You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ah...@apache.org on 2013/07/04 00:25:54 UTC

[15/50] [abbrv] git commit: updated refs/heads/vmsync to 78fa5e1

CLOUDSTACK-3314: ListTemplates only show one entry for cross-zone
templates.


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/05f46807
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/05f46807
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/05f46807

Branch: refs/heads/vmsync
Commit: 05f46807355c902058b5651b1601e8c07373be29
Parents: 1f3a20c
Author: Min Chen <mi...@citrix.com>
Authored: Tue Jul 2 13:54:21 2013 -0700
Committer: Min Chen <mi...@citrix.com>
Committed: Tue Jul 2 13:54:40 2013 -0700

----------------------------------------------------------------------
 .../api/response/TemplateResponse.java          |  17 ++--
 .../com/cloud/api/query/QueryManagerImpl.java   |  85 +++++++++++-----
 .../cloud/api/query/dao/TemplateJoinDao.java    |   5 +-
 .../api/query/dao/TemplateJoinDaoImpl.java      | 102 ++++++-------------
 .../com/cloud/api/query/vo/TemplateJoinVO.java  |  32 ++++++
 setup/db/db/schema-410to420.sql                 |  22 ++--
 6 files changed, 142 insertions(+), 121 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/05f46807/api/src/org/apache/cloudstack/api/response/TemplateResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/TemplateResponse.java b/api/src/org/apache/cloudstack/api/response/TemplateResponse.java
index e6221f7..17ea037 100644
--- a/api/src/org/apache/cloudstack/api/response/TemplateResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/TemplateResponse.java
@@ -18,7 +18,6 @@ package org.apache.cloudstack.api.response;
 
 import java.util.Date;
 import java.util.LinkedHashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -137,8 +136,10 @@ public class TemplateResponse extends BaseResponse implements ControlledViewEnti
     @SerializedName(ApiConstants.DETAILS) @Param(description="additional key/value details tied with template")
     private Map details;
 
-    @SerializedName("zones")  @Param(description="list of zones associated with tempate", responseObject = TemplateZoneResponse.class)
-    private Set<TemplateZoneResponse> zones;
+    // To avoid breaking backwards compatibility, we still treat a template at different zones as different templates, so not embedding
+    // template_zone information in this TemplateZoneResponse set.
+    //    @SerializedName("zones")  @Param(description="list of zones associated with tempate", responseObject = TemplateZoneResponse.class)
+    //    private Set<TemplateZoneResponse> zones;
 
     @SerializedName(ApiConstants.TAGS)  @Param(description="the list of resource tags associated with tempate", responseObject = ResourceTagResponse.class)
     private Set<ResourceTagResponse> tags;
@@ -150,7 +151,7 @@ public class TemplateResponse extends BaseResponse implements ControlledViewEnti
     private Boolean isDynamicallyScalable;
 
     public TemplateResponse(){
-        zones = new LinkedHashSet<TemplateZoneResponse>();
+        //  zones = new LinkedHashSet<TemplateZoneResponse>();
         tags = new LinkedHashSet<ResourceTagResponse>();
     }
 
@@ -176,6 +177,7 @@ public class TemplateResponse extends BaseResponse implements ControlledViewEnti
         this.accountId = accountId;
     }
 
+    @Override
     public void setAccountName(String account) {
         this.account = account;
     }
@@ -312,13 +314,6 @@ public class TemplateResponse extends BaseResponse implements ControlledViewEnti
         this.tags.add(tag);
     }
 
-    public void setZones(Set<TemplateZoneResponse> zones){
-        this.zones = zones;
-    }
-
-    public void addZone(TemplateZoneResponse zone){
-        this.zones.add(zone);
-    }
 
     public void setSshKeyEnabled(boolean sshKeyEnabled) {
         this.sshKeyEnabled = sshKeyEnabled;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/05f46807/server/src/com/cloud/api/query/QueryManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java
index 9a80082..18091aa 100644
--- a/server/src/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/com/cloud/api/query/QueryManagerImpl.java
@@ -27,7 +27,6 @@ import java.util.Set;
 import javax.ejb.Local;
 import javax.inject.Inject;
 
-import com.cloud.storage.*;
 import org.apache.cloudstack.affinity.AffinityGroupResponse;
 import org.apache.cloudstack.affinity.AffinityGroupVMMapVO;
 import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
@@ -56,7 +55,28 @@ import org.apache.cloudstack.api.command.user.vmgroup.ListVMGroupsCmd;
 import org.apache.cloudstack.api.command.user.volume.ListResourceDetailsCmd;
 import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
 import org.apache.cloudstack.api.command.user.zone.ListZonesByCmd;
-import org.apache.cloudstack.api.response.*;
+import org.apache.cloudstack.api.response.AccountResponse;
+import org.apache.cloudstack.api.response.AsyncJobResponse;
+import org.apache.cloudstack.api.response.DiskOfferingResponse;
+import org.apache.cloudstack.api.response.DomainRouterResponse;
+import org.apache.cloudstack.api.response.EventResponse;
+import org.apache.cloudstack.api.response.HostResponse;
+import org.apache.cloudstack.api.response.ImageStoreResponse;
+import org.apache.cloudstack.api.response.InstanceGroupResponse;
+import org.apache.cloudstack.api.response.ListResponse;
+import org.apache.cloudstack.api.response.ProjectAccountResponse;
+import org.apache.cloudstack.api.response.ProjectInvitationResponse;
+import org.apache.cloudstack.api.response.ProjectResponse;
+import org.apache.cloudstack.api.response.ResourceDetailResponse;
+import org.apache.cloudstack.api.response.ResourceTagResponse;
+import org.apache.cloudstack.api.response.SecurityGroupResponse;
+import org.apache.cloudstack.api.response.ServiceOfferingResponse;
+import org.apache.cloudstack.api.response.StoragePoolResponse;
+import org.apache.cloudstack.api.response.TemplateResponse;
+import org.apache.cloudstack.api.response.UserResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.api.response.VolumeResponse;
+import org.apache.cloudstack.api.response.ZoneResponse;
 import org.apache.cloudstack.engine.subsystem.api.storage.TemplateState;
 import org.apache.cloudstack.query.QueryService;
 import org.apache.log4j.Logger;
@@ -132,8 +152,14 @@ import com.cloud.server.ResourceTag.TaggedResourceType;
 import com.cloud.server.TaggedResourceService;
 import com.cloud.service.ServiceOfferingVO;
 import com.cloud.service.dao.ServiceOfferingDao;
+import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.ScopeType;
+import com.cloud.storage.Storage;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.Storage.TemplateType;
+import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.Volume;
+import com.cloud.storage.VolumeDetailVO;
 import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.storage.dao.VolumeDetailsDao;
 import com.cloud.template.VirtualMachineTemplate.TemplateFilter;
@@ -729,7 +755,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
         // first search distinct vm id by using query criteria and pagination
         SearchBuilder<UserVmJoinVO> sb = _userVmJoinDao.createSearchBuilder();
         sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct
-                                                             // ids
+        // ids
         _accountMgr.buildACLViewSearchBuilder(sb, domainId, isRecursive, permittedAccounts,
                 listProjectResourcesCriteria);
 
@@ -951,7 +977,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
                 cmd.getPageSizeVal());
         SearchBuilder<SecurityGroupJoinVO> sb = _securityGroupJoinDao.createSearchBuilder();
         sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct
-                                                             // ids
+        // ids
         _accountMgr.buildACLViewSearchBuilder(sb, domainId, isRecursive, permittedAccounts,
                 listProjectResourcesCriteria);
 
@@ -1474,7 +1500,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
 
         SearchBuilder<HostJoinVO> sb = _hostJoinDao.createSearchBuilder();
         sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct
-                                                             // ids
+        // ids
         sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
         sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE);
         sb.and("type", sb.entity().getType(), SearchCriteria.Op.LIKE);
@@ -1935,7 +1961,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
 
         SearchBuilder<StoragePoolJoinVO> sb = _poolJoinDao.createSearchBuilder();
         sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct
-                                                             // ids
+        // ids
         sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
         sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
         sb.and("path", sb.entity().getPath(), SearchCriteria.Op.EQ);
@@ -2025,7 +2051,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
 
         SearchBuilder<ImageStoreJoinVO> sb = _imageStoreJoinDao.createSearchBuilder();
         sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct
-                                                             // ids
+        // ids
         sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
         sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
         sb.and("dataCenterId", sb.entity().getZoneId(), SearchCriteria.Op.EQ);
@@ -2105,7 +2131,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
 
         SearchBuilder<ImageStoreJoinVO> sb = _imageStoreJoinDao.createSearchBuilder();
         sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct
-                                                             // ids
+        // ids
         sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
         sb.and("name", sb.entity().getName(), SearchCriteria.Op.EQ);
         sb.and("dataCenterId", sb.entity().getZoneId(), SearchCriteria.Op.EQ);
@@ -2229,14 +2255,14 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
 
             spc.addOr("domainId", SearchCriteria.Op.IN, domainIds.toArray());
             spc.addOr("domainId", SearchCriteria.Op.NULL); // include public
-                                                           // offering as where
+            // offering as where
             sc.addAnd("domainId", SearchCriteria.Op.SC, spc);
             sc.addAnd("systemUse", SearchCriteria.Op.EQ, false); // non-root
-                                                                 // users should
-                                                                 // not see
-                                                                 // system
-                                                                 // offering at
-                                                                 // all
+            // users should
+            // not see
+            // system
+            // offering at
+            // all
 
         }
 
@@ -2356,7 +2382,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
 
             spc.addOr("domainId", SearchCriteria.Op.IN, domainIds.toArray());
             spc.addOr("domainId", SearchCriteria.Op.NULL); // include public
-                                                           // offering as where
+            // offering as where
             sc.addAnd("domainId", SearchCriteria.Op.SC, spc);
 
         } else {
@@ -2439,8 +2465,9 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
         Filter searchFilter = new Filter(DataCenterJoinVO.class, null, false, cmd.getStartIndex(), cmd.getPageSizeVal());
         SearchCriteria<DataCenterJoinVO> sc = _dcJoinDao.createSearchCriteria();
 
-        if (networkType != null)
+        if (networkType != null) {
             sc.addAnd("networkType", SearchCriteria.Op.EQ, networkType);
+        }
 
         if (id != null) {
             sc.addAnd("id", SearchCriteria.Op.EQ, id);
@@ -2550,8 +2577,8 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
             if (account != null) {
                 if ((available != null) && Boolean.FALSE.equals(available)) {
                     Set<Long> dcIds = new HashSet<Long>(); // data centers with
-                                                           // at least one VM
-                                                           // running
+                    // at least one VM
+                    // running
                     List<DomainRouterVO> routers = _routerDao.listBy(account.getId());
                     for (DomainRouterVO router : routers) {
                         dcIds.add(router.getDataCenterId());
@@ -2664,6 +2691,9 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
         Boolean isAscending = Boolean.parseBoolean(_configDao.getValue("sortkey.algorithm"));
         isAscending = (isAscending == null ? true : isAscending);
         Filter searchFilter = new Filter(TemplateJoinVO.class, "sortKey", isAscending, startIndex, pageSize);
+
+        SearchBuilder<TemplateJoinVO> sb = _templateJoinDao.createSearchBuilder();
+        sb.select(null, Func.DISTINCT, sb.entity().getTempZonePair()); // select distinct (templateId, zoneId) pair
         SearchCriteria<TemplateJoinVO> sc = _templateJoinDao.createSearchCriteria();
 
         // verify templateId parameter and specially handle it
@@ -2846,7 +2876,14 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
             }
 
             if (zoneId != null) {
-                sc.addAnd("dataCenterId", SearchCriteria.Op.EQ, zoneId);
+                SearchCriteria<TemplateJoinVO> zoneSc = _templateJoinDao.createSearchCriteria();
+                zoneSc.addAnd("dataCenterId", SearchCriteria.Op.EQ, zoneId);
+                // handle the case where xs-tools.iso and vmware-tools.iso do not have data_center information in template_view
+                SearchCriteria<TemplateJoinVO> isoPerhostSc = _templateJoinDao.createSearchCriteria();
+                isoPerhostSc.addAnd("format", SearchCriteria.Op.EQ, ImageFormat.ISO);
+                isoPerhostSc.addAnd("templateType", SearchCriteria.Op.EQ, TemplateType.PERHOST);
+                zoneSc.addOr("templateType", SearchCriteria.Op.SC, isoPerhostSc);
+                sc.addAnd("dataCenterId", SearchCriteria.Op.SC, zoneSc);
             }
 
             if (!showDomr) {
@@ -2867,12 +2904,12 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
             return uniqueTmplPair;
         }
         List<TemplateJoinVO> uniqueTmpls = uniqueTmplPair.first();
-        Long[] vrIds = new Long[uniqueTmpls.size()];
+        String[] tzIds = new String[uniqueTmpls.size()];
         int i = 0;
         for (TemplateJoinVO v : uniqueTmpls) {
-            vrIds[i++] = v.getId();
+            tzIds[i++] = v.getTempZonePair();
         }
-        List<TemplateJoinVO> vrs = _templateJoinDao.searchByIds(vrIds);
+        List<TemplateJoinVO> vrs = _templateJoinDao.searchByTemplateZonePair(tzIds);
         return new Pair<List<TemplateJoinVO>, Integer>(vrs, count);
 
         // TODO: revisit the special logic for iso search in
@@ -2926,6 +2963,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
                 cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags);
     }
 
+    @Override
     public ListResponse<AffinityGroupResponse> listAffinityGroups(Long affinityGroupId, String affinityGroupName,
             String affinityGroupType, Long vmId, String accountName, Long domainId, boolean isRecursive,
             boolean listAll, Long startIndex, Long pageSize) {
@@ -2971,7 +3009,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
                 listProjectResourcesCriteria);
 
         groupSearch.select(null, Func.DISTINCT, groupSearch.entity().getId()); // select
-                                                                               // distinct
+        // distinct
 
         SearchCriteria<AffinityGroupJoinVO> sc = groupSearch.create();
         _accountMgr.buildACLViewSearchCriteria(sc, domainId, isRecursive, permittedAccounts,
@@ -3026,6 +3064,7 @@ public class QueryManagerImpl extends ManagerBase implements QueryService {
         return new Pair<List<AffinityGroupJoinVO>, Integer>(ags, count);
     }
 
+    @Override
     public List<ResourceDetailResponse> listResource(ListResourceDetailsCmd cmd) {
 
         String key = cmd.getKey();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/05f46807/server/src/com/cloud/api/query/dao/TemplateJoinDao.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/TemplateJoinDao.java b/server/src/com/cloud/api/query/dao/TemplateJoinDao.java
index 4d1de90..f73f5bd 100644
--- a/server/src/com/cloud/api/query/dao/TemplateJoinDao.java
+++ b/server/src/com/cloud/api/query/dao/TemplateJoinDao.java
@@ -19,6 +19,7 @@ package com.cloud.api.query.dao;
 import java.util.List;
 
 import org.apache.cloudstack.api.response.TemplateResponse;
+
 import com.cloud.api.query.vo.TemplateJoinVO;
 import com.cloud.template.VirtualMachineTemplate;
 import com.cloud.utils.db.GenericDao;
@@ -37,8 +38,8 @@ public interface TemplateJoinDao extends GenericDao<TemplateJoinVO, Long> {
 
     List<TemplateJoinVO> newTemplateView(VirtualMachineTemplate tmpl, long zoneId, boolean readyOnly);
 
-    List<TemplateJoinVO> searchByIds(Long... ids);
+    List<TemplateJoinVO> searchByTemplateZonePair(String... pairs);
 
     List<TemplateJoinVO> listActiveTemplates(long storeId);
 
- }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/05f46807/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java
index a31a036..f5b7218 100644
--- a/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/TemplateJoinDaoImpl.java
@@ -26,7 +26,6 @@ import javax.inject.Inject;
 
 import org.apache.cloudstack.api.BaseCmd;
 import org.apache.cloudstack.api.response.TemplateResponse;
-import org.apache.cloudstack.api.response.TemplateZoneResponse;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
 import org.apache.cloudstack.engine.subsystem.api.storage.TemplateState;
 import org.apache.log4j.Logger;
@@ -60,7 +59,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBase<TemplateJoinVO, Long> im
     @Inject
     private ConfigurationDao  _configDao;
 
-    private final SearchBuilder<TemplateJoinVO> tmpltSearch;
+    private final SearchBuilder<TemplateJoinVO> tmpltIdPairSearch;
 
     private final SearchBuilder<TemplateJoinVO> tmpltIdSearch;
 
@@ -71,9 +70,9 @@ public class TemplateJoinDaoImpl extends GenericDaoBase<TemplateJoinVO, Long> im
 
     protected TemplateJoinDaoImpl() {
 
-        tmpltSearch = createSearchBuilder();
-        tmpltSearch.and("idIN", tmpltSearch.entity().getId(), SearchCriteria.Op.IN);
-        tmpltSearch.done();
+        tmpltIdPairSearch = createSearchBuilder();
+        tmpltIdPairSearch.and("tempZonePairIN", tmpltIdPairSearch.entity().getTempZonePair(), SearchCriteria.Op.IN);
+        tmpltIdPairSearch.done();
 
         tmpltIdSearch = createSearchBuilder();
         tmpltIdSearch.and("id", tmpltIdSearch.entity().getId(), SearchCriteria.Op.EQ);
@@ -91,7 +90,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBase<TemplateJoinVO, Long> im
         activeTmpltSearch.done();
 
         // select distinct pair (template_id, zone_id)
-        this._count = "select count(distinct id) from template_view WHERE ";
+        this._count = "select count(distinct temp_zone_pair) from template_view WHERE ";
     }
 
 
@@ -172,6 +171,11 @@ public class TemplateJoinDaoImpl extends GenericDaoBase<TemplateJoinVO, Long> im
             templateResponse.setStatus(templateStatus);
         }
 
+        if ( template.getDataCenterId() > 0 ){
+            templateResponse.setZoneId(template.getDataCenterUuid());
+            templateResponse.setZoneName(template.getDataCenterName());
+        }
+
         Long templateSize = template.getSize();
         if (templateSize > 0) {
             templateResponse.setSize(templateSize);
@@ -183,26 +187,6 @@ public class TemplateJoinDaoImpl extends GenericDaoBase<TemplateJoinVO, Long> im
         }
         templateResponse.setTemplateTag(template.getTemplateTag());
 
-        // set template zone information
-        if (template.getDataCenterId() > 0 ){
-            TemplateZoneResponse tmplZoneResp = new TemplateZoneResponse(template.getDataCenterUuid(), template.getDataCenterName());
-            tmplZoneResp.setCreated(template.getCreatedOnStore());
-            if ( template.getFormat() == Storage.ImageFormat.BAREMETAL ){
-                // for baremetal template, we didn't download, but is ready to use.
-                tmplZoneResp.setReady(true);
-            }
-            else{
-                tmplZoneResp.setReady(template.getState() == ObjectInDataStoreStateMachine.State.Ready);
-            }
-            if ( templateStatus != null ){
-                tmplZoneResp.setStatus(templateStatus);
-            }
-            templateResponse.addZone(tmplZoneResp);
-            // set the first found associated zone directly in TemplateResponse
-            templateResponse.setZoneId(template.getDataCenterUuid());
-            templateResponse.setZoneName(template.getDataCenterName());
-        }
-
         // set details map
         if (template.getDetailName() != null){
             Map<String, String> details = new HashMap<String, String>();
@@ -274,30 +258,6 @@ public class TemplateJoinDaoImpl extends GenericDaoBase<TemplateJoinVO, Long> im
     @Override
     public TemplateResponse setTemplateResponse(TemplateResponse templateResponse, TemplateJoinVO template) {
 
-        // update template zone information
-        if (template.getDataCenterId() > 0 ){
-            TemplateZoneResponse tmplZoneResp = new TemplateZoneResponse(template.getDataCenterUuid(), template.getDataCenterName());
-            tmplZoneResp.setCreated(template.getCreatedOnStore());
-            if ( template.getFormat() == Storage.ImageFormat.BAREMETAL ){
-                // for baremetal template, we didn't download, but is ready to use.
-                tmplZoneResp.setReady(true);
-            }
-            else{
-                tmplZoneResp.setReady(template.getState() == ObjectInDataStoreStateMachine.State.Ready);
-            }
-            String templateStatus = getTemplateStatus(template);
-            if ( templateStatus != null ){
-                tmplZoneResp.setStatus(templateStatus);
-            }
-            templateResponse.addZone(tmplZoneResp);
-            if (templateResponse.getZoneId() == null) {
-                // set the first found associated zone directly in
-                // TemplateResponse
-                templateResponse.setZoneId(template.getDataCenterUuid());
-                templateResponse.setZoneName(template.getDataCenterName());
-            }
-        }
-
         // update details map
         if (template.getDetailName() != null){
             Map<String, String> details = templateResponse.getDetails();
@@ -354,15 +314,6 @@ public class TemplateJoinDaoImpl extends GenericDaoBase<TemplateJoinVO, Long> im
         isoResponse.setDomainId(iso.getDomainUuid());
         isoResponse.setDomainName(iso.getDomainName());
 
-        // set template zone information
-        if (iso.getDataCenterId() > 0 ){
-            TemplateZoneResponse tmplZoneResp = new TemplateZoneResponse(iso.getDataCenterUuid(), iso.getDataCenterName());
-            isoResponse.addZone(tmplZoneResp);
-            // set the first found associated zone directly in TemplateResponse
-            isoResponse.setZoneId(iso.getDataCenterUuid());
-            isoResponse.setZoneName(iso.getDataCenterName());
-        }
-
 
         Account caller = UserContext.current().getCaller();
         boolean isAdmin = false;
@@ -393,6 +344,12 @@ public class TemplateJoinDaoImpl extends GenericDaoBase<TemplateJoinVO, Long> im
             }
         }
 
+        if ( iso.getDataCenterId() > 0 ){
+            isoResponse.setZoneId(iso.getDataCenterUuid());
+            isoResponse.setZoneName(iso.getDataCenterName());
+        }
+
+
         Long isoSize = iso.getSize();
         if (isoSize > 0) {
             isoResponse.setSize(isoSize);
@@ -433,7 +390,7 @@ public class TemplateJoinDaoImpl extends GenericDaoBase<TemplateJoinVO, Long> im
 
 
     @Override
-    public List<TemplateJoinVO> searchByIds(Long... tmplIds) {
+    public List<TemplateJoinVO> searchByTemplateZonePair(String... idPairs) {
         // set detail batch query size
         int DETAILS_BATCH_SIZE = 2000;
         String batchCfg = _configDao.getValue("detail.batch.query.size");
@@ -444,14 +401,14 @@ public class TemplateJoinDaoImpl extends GenericDaoBase<TemplateJoinVO, Long> im
         List<TemplateJoinVO> uvList = new ArrayList<TemplateJoinVO>();
         // query details by batches
         int curr_index = 0;
-        if ( tmplIds.length > DETAILS_BATCH_SIZE ){
-            while ( (curr_index + DETAILS_BATCH_SIZE ) <= tmplIds.length ) {
-                Long[] ids = new Long[DETAILS_BATCH_SIZE];
+        if ( idPairs.length > DETAILS_BATCH_SIZE ){
+            while ( (curr_index + DETAILS_BATCH_SIZE ) <= idPairs.length ) {
+                String[] labels = new String[DETAILS_BATCH_SIZE];
                 for (int k = 0, j = curr_index; j < curr_index + DETAILS_BATCH_SIZE; j++, k++) {
-                    ids[k] = tmplIds[j];
+                    labels[k] = idPairs[j];
                 }
-                SearchCriteria<TemplateJoinVO> sc = tmpltSearch.create();
-                sc.setParameters("idIN", ids);
+                SearchCriteria<TemplateJoinVO> sc = tmpltIdPairSearch.create();
+                sc.setParameters("tempZonePairIN", labels);
                 List<TemplateJoinVO> vms = searchIncludingRemoved(sc, null, null, false);
                 if (vms != null) {
                     uvList.addAll(vms);
@@ -459,15 +416,14 @@ public class TemplateJoinDaoImpl extends GenericDaoBase<TemplateJoinVO, Long> im
                 curr_index += DETAILS_BATCH_SIZE;
             }
         }
-        if (curr_index < tmplIds.length) {
-            int batch_size = (tmplIds.length - curr_index);
-            // set the ids value
-            Long[] ids = new Long[batch_size];
+        if (curr_index < idPairs.length) {
+            int batch_size = (idPairs.length - curr_index);
+            String[] labels = new String[batch_size];
             for (int k = 0, j = curr_index; j < curr_index + batch_size; j++, k++) {
-                ids[k] = tmplIds[j];
+                labels[k] = idPairs[j];
             }
-            SearchCriteria<TemplateJoinVO> sc = tmpltSearch.create();
-            sc.setParameters("idIN", ids);
+            SearchCriteria<TemplateJoinVO> sc = tmpltIdPairSearch.create();
+            sc.setParameters("tempZonePairIN", labels);
             List<TemplateJoinVO> vms = searchIncludingRemoved(sc, null, null, false);
             if (vms != null) {
                 uvList.addAll(vms);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/05f46807/server/src/com/cloud/api/query/vo/TemplateJoinVO.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/vo/TemplateJoinVO.java b/server/src/com/cloud/api/query/vo/TemplateJoinVO.java
index 03dce99..1f2afe9 100644
--- a/server/src/com/cloud/api/query/vo/TemplateJoinVO.java
+++ b/server/src/com/cloud/api/query/vo/TemplateJoinVO.java
@@ -17,6 +17,7 @@
 package com.cloud.api.query.vo;
 
 import java.util.Date;
+
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.EnumType;
@@ -27,8 +28,10 @@ import javax.persistence.Temporal;
 import javax.persistence.TemporalType;
 
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
+
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.server.ResourceTag.TaggedResourceType;
+import com.cloud.storage.ScopeType;
 import com.cloud.storage.Storage;
 import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
 import com.cloud.utils.db.GenericDao;
@@ -175,6 +178,10 @@ public class TemplateJoinVO extends BaseViewVO implements ControlledViewEntity {
     @Column(name="data_center_name")
     private String dataCenterName;
 
+    @Column(name="store_scope")
+    @Enumerated(value = EnumType.STRING)
+    private ScopeType dataStoreScope;
+
     @Column(name="store_id")
     private Long dataStoreId; // this can be null for baremetal templates
 
@@ -239,6 +246,9 @@ public class TemplateJoinVO extends BaseViewVO implements ControlledViewEntity {
     @Enumerated(EnumType.STRING)
     ObjectInDataStoreStateMachine.State state;
 
+    @Column(name="temp_zone_pair")
+    private String tempZonePair; // represent a distinct (templateId, data_center_id) pair
+
     public TemplateJoinVO() {
     }
 
@@ -1027,4 +1037,26 @@ public class TemplateJoinVO extends BaseViewVO implements ControlledViewEntity {
     }
 
 
+
+    public ScopeType getDataStoreScope() {
+        return dataStoreScope;
+    }
+
+
+    public void setDataStoreScope(ScopeType dataStoreScope) {
+        this.dataStoreScope = dataStoreScope;
+    }
+
+
+    public String getTempZonePair() {
+        return tempZonePair;
+    }
+
+
+
+    public void setTempZonePair(String tempZonePair) {
+        this.tempZonePair = tempZonePair;
+    }
+
+
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/05f46807/setup/db/db/schema-410to420.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql
index f5b79e9..e179a88 100644
--- a/setup/db/db/schema-410to420.sql
+++ b/setup/db/db/schema-410to420.sql
@@ -1883,7 +1883,6 @@ CREATE VIEW `cloud`.`template_view` AS
         vm_template.display_text,
         vm_template.enable_password,
         vm_template.guest_os_id,
-        -- vm_template.state,
         guest_os.uuid guest_os_uuid,
         guest_os.display_name guest_os_name,
         vm_template.bootable,
@@ -1913,6 +1912,7 @@ CREATE VIEW `cloud`.`template_view` AS
         data_center.name data_center_name,
         launch_permission.account_id lp_account_id,
         template_store_ref.store_id,
+		image_store.scope as store_scope,
         template_store_ref.state,
         template_store_ref.download_state,
         template_store_ref.download_pct,
@@ -1931,7 +1931,8 @@ CREATE VIEW `cloud`.`template_view` AS
         resource_tags.resource_id tag_resource_id,
         resource_tags.resource_uuid tag_resource_uuid,
         resource_tags.resource_type tag_resource_type,
-        resource_tags.customer tag_customer
+        resource_tags.customer tag_customer,
+		CONCAT(vm_template.id, '_', IFNULL(data_center.id, 0)) as temp_zone_pair
     from
         `cloud`.`vm_template`
             inner join
@@ -1945,24 +1946,21 @@ CREATE VIEW `cloud`.`template_view` AS
             left join
         `cloud`.`vm_template_details` ON vm_template_details.template_id = vm_template.id         
             left join
-        `cloud`.`vm_template` source_template ON source_template.id = vm_template.source_template_id            
-            left join
-        `cloud`.`template_zone_ref` ON template_zone_ref.template_id = vm_template.id
-            AND template_zone_ref.removed is null
+        `cloud`.`vm_template` source_template ON source_template.id = vm_template.source_template_id    
             left join
-        `cloud`.`data_center` ON template_zone_ref.zone_id = data_center.id
+        `cloud`.`template_store_ref` ON template_store_ref.template_id = vm_template.id
             left join
-        `cloud`.`image_store` ON image_store.removed is NULL AND (image_store.data_center_id = data_center.id OR image_store.scope = 'REGION')
+        `cloud`.`image_store` ON image_store.removed is NULL AND template_store_ref.store_id is not NULL AND image_store.id = template_store_ref.store_id 
+        	left join
+        `cloud`.`template_zone_ref` ON template_zone_ref.template_id = vm_template.id AND template_store_ref.store_id is NULL AND template_zone_ref.removed is null    
             left join
-        `cloud`.`template_store_ref` ON template_store_ref.template_id = vm_template.id AND template_store_ref.store_id = image_store.id
+        `cloud`.`data_center` ON (image_store.data_center_id = data_center.id OR template_zone_ref.zone_id = data_center.id)
             left join
         `cloud`.`launch_permission` ON launch_permission.template_id = vm_template.id
             left join
         `cloud`.`resource_tags` ON resource_tags.resource_id = vm_template.id
             and (resource_tags.resource_type = 'Template' or resource_tags.resource_type='ISO');
-
-
-
+            
 INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'midonet.apiserver.address', 'http://localhost:8081', 'Specify the address at which the Midonet API server can be contacted (if using Midonet)');
 INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'midonet.providerrouter.id', 'd7c5e6a3-e2f4-426b-b728-b7ce6a0448e5', 'Specifies the UUID of the Midonet provider router (if using Midonet)');