diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 2a201dba196..7841aa3e8cf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -326,6 +326,7 @@ public class ApiConstants { public static final String SESSIONKEY = "sessionkey"; public static final String SHOW_CAPACITIES = "showcapacities"; public static final String SHOW_REMOVED = "showremoved"; + public static final String SHOW_UNIQUE = "showunique"; public static final String SIGNATURE = "signature"; public static final String SIGNATURE_VERSION = "signatureversion"; public static final String SIZE = "size"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java index 80d46deb65c..b741ae7dd5f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/ListIsosCmd.java @@ -79,6 +79,9 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { @Parameter(name=ApiConstants.SHOW_REMOVED, type=CommandType.BOOLEAN, description="show removed ISOs as well") private Boolean showRemoved; + @Parameter(name = ApiConstants.SHOW_UNIQUE, type = CommandType.BOOLEAN, description = "If set to true, list only unique isos across zones", since = "4.13.2") + private Boolean showUnique; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -116,7 +119,11 @@ public class ListIsosCmd extends BaseListTaggedResourcesCmd implements UserCmd { } public Boolean getShowRemoved() { - return (showRemoved != null ? showRemoved : false); + return showRemoved != null && showRemoved; + } + + public Boolean getShowUnique() { + return showUnique != null && showUnique; } public boolean listInReadyState() { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java index 11e1425361b..5630d7fba42 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/template/ListTemplatesCmd.java @@ -76,6 +76,9 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User @Parameter(name = ApiConstants.SHOW_REMOVED, type = CommandType.BOOLEAN, description = "show removed templates as well") private Boolean showRemoved; + @Parameter(name = ApiConstants.SHOW_UNIQUE, type = CommandType.BOOLEAN, description = "If set to true, list only unique templates across zones", since = "4.13.2") + private Boolean showUnique; + @Parameter(name = ApiConstants.PARENT_TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "list datadisk templates by parent template id", since = "4.4") private Long parentTemplateId; @@ -104,7 +107,11 @@ public class ListTemplatesCmd extends BaseListTaggedResourcesCmd implements User } public Boolean getShowRemoved() { - return (showRemoved != null ? showRemoved : false); + return showRemoved != null && showRemoved; + } + + public Boolean getShowUnique() { + return showUnique != null && showUnique; } public Long getParentTemplateId() { diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index e2190881074..1847d511a66 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -3234,12 +3234,12 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor()); return searchForTemplatesInternal(id, cmd.getTemplateName(), cmd.getKeyword(), templateFilter, false, null, cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), hypervisorType, - showDomr, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId); + showDomr, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedTmpl, cmd.getIds(), parentTemplateId, cmd.getShowUnique()); } private Pair, Integer> searchForTemplatesInternal(Long templateId, String name, String keyword, TemplateFilter templateFilter, boolean isIso, Boolean bootable, Long pageSize, Long startIndex, Long zoneId, HypervisorType hyperType, boolean showDomr, boolean onlyReady, List permittedAccounts, Account caller, - ListProjectResourcesCriteria listProjectResourcesCriteria, Map tags, boolean showRemovedTmpl, List ids, Long parentTemplateId) { + ListProjectResourcesCriteria listProjectResourcesCriteria, Map tags, boolean showRemovedTmpl, List ids, Long parentTemplateId, Boolean showUnique) { // check if zone is configured, if not, just return empty list List hypers = null; @@ -3256,7 +3256,11 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q searchFilter.addOrderBy(TemplateJoinVO.class, "tempZonePair", SortKeyAscending.value()); SearchBuilder sb = _templateJoinDao.createSearchBuilder(); - sb.select(null, Func.DISTINCT, sb.entity().getTempZonePair()); // select distinct (templateId, zoneId) pair + if (showUnique) { + sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct templateId + } else { + sb.select(null, Func.DISTINCT, sb.entity().getTempZonePair()); // select distinct (templateId, zoneId) pair + } if (ids != null && !ids.isEmpty()) { sb.and("idIN", sb.entity().getId(), SearchCriteria.Op.IN); } @@ -3493,23 +3497,16 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q uniqueTmplPair = _templateJoinDao.searchIncludingRemovedAndCount(sc, searchFilter); } else { sc.addAnd("templateState", SearchCriteria.Op.IN, new State[] {State.Active, State.UploadAbandoned, State.UploadError, State.NotUploaded, State.UploadInProgress}); - final String[] distinctColumns = {"temp_zone_pair"}; - uniqueTmplPair = _templateJoinDao.searchAndDistinctCount(sc, searchFilter, distinctColumns); + if (showUnique) { + final String[] distinctColumns = {"id"}; + uniqueTmplPair = _templateJoinDao.searchAndDistinctCount(sc, searchFilter, distinctColumns); + } else { + final String[] distinctColumns = {"temp_zone_pair"}; + uniqueTmplPair = _templateJoinDao.searchAndDistinctCount(sc, searchFilter, distinctColumns); + } } - Integer count = uniqueTmplPair.second(); - if (count.intValue() == 0) { - // empty result - return uniqueTmplPair; - } - List uniqueTmpls = uniqueTmplPair.first(); - String[] tzIds = new String[uniqueTmpls.size()]; - int i = 0; - for (TemplateJoinVO v : uniqueTmpls) { - tzIds[i++] = v.getTempZonePair(); - } - List vrs = _templateJoinDao.searchByTemplateZonePair(showRemovedTmpl, tzIds); - return new Pair, Integer>(vrs, count); + return findTemplatesByIdOrTempZonePair(uniqueTmplPair, showRemovedTmpl, showUnique); // TODO: revisit the special logic for iso search in // VMTemplateDaoImpl.searchForTemplates and understand why we need to @@ -3518,6 +3515,25 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q } + // findTemplatesByIdOrTempZonePair returns the templates with the given ids if showUnique is true, or else by the TempZonePair + private Pair, Integer> findTemplatesByIdOrTempZonePair(Pair, Integer> templateDataPair, boolean showRemoved, boolean showUnique) { + Integer count = templateDataPair.second(); + if (count.intValue() == 0) { + // empty result + return templateDataPair; + } + List templateData = templateDataPair.first(); + List templates = null; + if (showUnique) { + Long[] templateIds = templateData.stream().map(template -> template.getId()).toArray(Long[]::new); + templates = _templateJoinDao.findByDistinctIds(templateIds); + } else { + String[] templateZonePairs = templateData.stream().map(template -> template.getTempZonePair()).toArray(String[]::new); + templates = _templateJoinDao.searchByTemplateZonePair(showRemoved, templateZonePairs); + } + return new Pair, Integer>(templates, count); + } + @Override public ListResponse listIsos(ListIsosCmd cmd) { Pair, Integer> result = searchForIsosInternal(cmd); @@ -3560,7 +3576,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q HypervisorType hypervisorType = HypervisorType.getType(cmd.getHypervisor()); return searchForTemplatesInternal(cmd.getId(), cmd.getIsoName(), cmd.getKeyword(), isoFilter, true, cmd.isBootable(), cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), - hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedISO, null, null); + hypervisorType, true, cmd.listInReadyState(), permittedAccounts, caller, listProjectResourcesCriteria, tags, showRemovedISO, null, null, cmd.getShowUnique()); } @Override diff --git a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDao.java b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDao.java index 298be4d6f01..c9d7eba48b2 100644 --- a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDao.java +++ b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDao.java @@ -48,4 +48,5 @@ public interface TemplateJoinDao extends GenericDao { Pair, Integer> searchIncludingRemovedAndCount(final SearchCriteria sc, final Filter filter); + List findByDistinctIds(Long... ids); } diff --git a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java index 54686f73df2..27380ffaa93 100644 --- a/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/TemplateJoinDaoImpl.java @@ -73,6 +73,8 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation tmpltIdSearch; + private final SearchBuilder tmpltIdsSearch; + private final SearchBuilder tmpltZoneSearch; private final SearchBuilder activeTmpltSearch; @@ -88,6 +90,11 @@ public class TemplateJoinDaoImpl extends GenericDaoBaseWithTagInformation, Integer>(objects, count); } + @Override + public List findByDistinctIds(Long... ids) { + if (ids == null || ids.length == 0) { + return new ArrayList(); + } + SearchCriteria sc = tmpltIdsSearch.create(); + sc.setParameters("idsIN", ids); + return searchIncludingRemoved(sc, null, null, false); + } + }