diff --git a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDetailsDao.java b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDetailsDao.java index 10ba8d574a1..1645f2dc448 100644 --- a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDetailsDao.java @@ -27,4 +27,6 @@ public interface ServiceOfferingDetailsDao extends GenericDao findDomainIds(final long resourceId); List findZoneIds(final long resourceId); String getDetail(Long diskOfferingId, String key); -} \ No newline at end of file + List findOfferingIdsByDomainIds(List domainIds); +} + diff --git a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDetailsDaoImpl.java index 0080174874d..0d14882fa29 100644 --- a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDetailsDaoImpl.java @@ -19,6 +19,7 @@ package com.cloud.service.dao; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.resourcedetail.ResourceDetailsDaoBase; @@ -67,4 +68,10 @@ public class ServiceOfferingDetailsDaoImpl extends ResourceDetailsDaoBase findOfferingIdsByDomainIds(List domainIds) { + Object[] dIds = domainIds.stream().map(s -> String.valueOf(s)).collect(Collectors.toList()).toArray(); + return findResouceIdsByNameAndValueIn("domainid", dIds); + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java index 52d901293ad..bde71b350ac 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDao.java @@ -96,4 +96,5 @@ public interface ResourceDetailsDao extends GenericDao public void addDetail(long resourceId, String key, String value, boolean display); + public List findResouceIdsByNameAndValueIn(String name, Object[] values); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDaoBase.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDaoBase.java index 0f153dbbf08..b33a1fc2599 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDaoBase.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/ResourceDetailsDaoBase.java @@ -23,9 +23,11 @@ import java.util.Map; import org.apache.cloudstack.api.ResourceDetail; import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.db.SearchCriteria.Op; public abstract class ResourceDetailsDaoBase extends GenericDaoBase implements ResourceDetailsDao { private SearchBuilder AllFieldsSearch; @@ -182,4 +184,21 @@ public abstract class ResourceDetailsDaoBase extends G List results = search(sc, null); return results; } + + @Override + public List findResouceIdsByNameAndValueIn(String name, Object[] values) { + GenericSearchBuilder sb = createSearchBuilder(Long.class); + sb.selectFields(sb.entity().getResourceId()); + sb.and("name", sb.entity().getName(), Op.EQ); + sb.and().op("value", sb.entity().getValue(), Op.IN); + sb.or("valueNull", sb.entity().getValue(), Op.NULL); + sb.cp(); + sb.done(); + + SearchCriteria sc = sb.create(); + sc.setParameters("name", name); + sc.setParameters("value", values); + + return customSearch(sc, null); + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDao.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDao.java index 815f1693ee5..812102a70aa 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDao.java @@ -27,4 +27,6 @@ public interface DiskOfferingDetailsDao extends GenericDao findDomainIds(final long resourceId); List findZoneIds(final long resourceId); String getDetail(Long diskOfferingId, String key); -} \ No newline at end of file + List findOfferingIdsByDomainIds(List domainIds); +} + diff --git a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDaoImpl.java index 5408f2d7f03..4d29dda560f 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/resourcedetail/dao/DiskOfferingDetailsDaoImpl.java @@ -19,6 +19,7 @@ package org.apache.cloudstack.resourcedetail.dao; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO; @@ -66,4 +67,11 @@ public class DiskOfferingDetailsDaoImpl extends ResourceDetailsDaoBase findOfferingIdsByDomainIds(List domainIds) { + Object[] dIds = domainIds.stream().map(s -> String.valueOf(s)).collect(Collectors.toList()).toArray(); + return findResouceIdsByNameAndValueIn("domainid", dIds); + } +} + 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 a13720f17e2..2041e9207f6 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -23,7 +23,6 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -121,7 +120,6 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; -import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -207,6 +205,7 @@ import com.cloud.server.ResourceTag.ResourceObjectType; import com.cloud.server.TaggedResourceService; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.DataStoreRole; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.ScopeType; @@ -350,7 +349,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q private DiskOfferingJoinDao _diskOfferingJoinDao; @Inject - private DiskOfferingDetailsDao diskOfferingDetailsDao; + private DiskOfferingDetailsDao _diskOfferingDetailsDao; @Inject private ServiceOfferingJoinDao _srvOfferingJoinDao; @@ -358,6 +357,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Inject private ServiceOfferingDao _srvOfferingDao; + @Inject + private ServiceOfferingDetailsDao _srvOfferingDetailsDao; + @Inject private DataCenterJoinDao _dcJoinDao; @@ -2869,75 +2871,41 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sc.addAnd("zoneId", SearchCriteria.Op.SC, zoneSC); } - // FIXME: disk offerings should search back up the hierarchy for - // available disk offerings... - /* - * sb.addAnd("domainId", sb.entity().getDomainId(), - * SearchCriteria.Op.EQ); if (domainId != null) { - * SearchBuilder domainSearch = - * _domainDao.createSearchBuilder(); domainSearch.addAnd("path", - * domainSearch.entity().getPath(), SearchCriteria.Op.LIKE); - * sb.join("domainSearch", domainSearch, sb.entity().getDomainId(), - * domainSearch.entity().getId()); } - */ + // Filter offerings that are not associated with caller's domain + // Fetch the offering ids from the details table since theres no smart way to filter them in the join ... yet! + Account caller = CallContext.current().getCallingAccount(); + if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { + Domain callerDomain = _domainDao.findById(caller.getDomainId()); + List domainIds = findRelatedDomainIds(callerDomain, isRecursive); - // FIXME: disk offerings should search back up the hierarchy for - // available disk offerings... - /* - * if (domainId != null) { sc.setParameters("domainId", domainId); // - * //DomainVO domain = _domainDao.findById((Long)domainId); // // I want - * to join on user_vm.domain_id = domain.id where domain.path like - * 'foo%' //sc.setJoinParameters("domainSearch", "path", - * domain.getPath() + "%"); // } - */ + List ids = _diskOfferingDetailsDao.findOfferingIdsByDomainIds(domainIds); + SearchBuilder sb = _diskOfferingJoinDao.createSearchBuilder(); + if (ids != null && !ids.isEmpty()) { + sb.and("id", sb.entity().getId(), Op.IN); + } + sb.or("domainId", sb.entity().getDomainId(), Op.NULL); + sb.done(); + + SearchCriteria scc = sb.create(); + if (ids != null && !ids.isEmpty()) { + scc.setParameters("id", ids.toArray()); + } + sc.addAnd("domainId", SearchCriteria.Op.SC, scc); + } Pair, Integer> result = _diskOfferingJoinDao.searchAndCount(sc, searchFilter); - // Remove offerings that are not associated with caller's domain - if (account.getType() != Account.ACCOUNT_TYPE_ADMIN && CollectionUtils.isNotEmpty(result.first())) { - ListIterator it = result.first().listIterator(); - while (it.hasNext()) { - DiskOfferingJoinVO offering = it.next(); - if(!Strings.isNullOrEmpty(offering.getDomainId())) { - boolean toRemove = true; - String[] domainIdsArray = offering.getDomainId().split(","); - for (String domainIdString : domainIdsArray) { - Long dId = Long.valueOf(domainIdString.trim()); - if (isRecursive) { - if (_domainDao.isChildDomain(account.getDomainId(), dId)) { - toRemove = false; - break; - } - } else { - if (_domainDao.isChildDomain(dId, account.getDomainId())) { - toRemove = false; - break; - } - } - } - if (toRemove) { - it.remove(); - } - } - } - } return new Pair<>(result.first(), result.second()); } - private List filterOfferingsOnCurrentTags(List offerings, ServiceOfferingVO currentVmOffering) { - if (currentVmOffering == null) { - return offerings; + private List findRelatedDomainIds(Domain domain, boolean isRecursive) { + List domainIds = _domainDao.getDomainParentIds(domain.getId()) + .stream().collect(Collectors.toList()); + if (isRecursive) { + List childrenIds = _domainDao.getDomainChildrenIds(domain.getPath()); + if (childrenIds != null && !childrenIds.isEmpty()) + domainIds.addAll(childrenIds); } - List currentTagsList = StringUtils.csvTagsToList(currentVmOffering.getTags()); - - // New service offering should have all the tags of the current service offering. - List filteredOfferings = new ArrayList<>(); - for (ServiceOfferingJoinVO offering : offerings) { - List newTagsList = StringUtils.csvTagsToList(offering.getTags()); - if (newTagsList.containsAll(currentTagsList)) { - filteredOfferings.add(offering); - } - } - return filteredOfferings; + return domainIds; } @Override @@ -3111,39 +3079,60 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q sc.addAnd("cpuspeedconstraints", SearchCriteria.Op.SC, cpuSpeedSearchCriteria); } - Pair, Integer> result = _srvOfferingJoinDao.searchAndCount(sc, searchFilter); + // Filter offerings that are not associated with caller's domain + // Fetch the offering ids from the details table since theres no smart way to filter them in the join ... yet! + if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN) { + Domain callerDomain = _domainDao.findById(caller.getDomainId()); + List domainIds = findRelatedDomainIds(callerDomain, isRecursive); - //Couldn't figure out a smart way to filter offerings based on tags in sql so doing it in Java. - List filteredOfferings = filterOfferingsOnCurrentTags(result.first(), currentVmOffering); - // Remove offerings that are not associated with caller's domain - if (caller.getType() != Account.ACCOUNT_TYPE_ADMIN && CollectionUtils.isNotEmpty(filteredOfferings)) { - ListIterator it = filteredOfferings.listIterator(); - while (it.hasNext()) { - ServiceOfferingJoinVO offering = it.next(); - if(!Strings.isNullOrEmpty(offering.getDomainId())) { - boolean toRemove = true; - String[] domainIdsArray = offering.getDomainId().split(","); - for (String domainIdString : domainIdsArray) { - Long dId = Long.valueOf(domainIdString.trim()); - if (isRecursive) { - if (_domainDao.isChildDomain(caller.getDomainId(), dId)) { - toRemove = false; - break; - } - } else { - if (_domainDao.isChildDomain(dId, caller.getDomainId())) { - toRemove = false; - break; - } - } - } - if (toRemove) { - it.remove(); - } + List ids = _srvOfferingDetailsDao.findOfferingIdsByDomainIds(domainIds); + SearchBuilder sb = _srvOfferingJoinDao.createSearchBuilder(); + if (ids != null && !ids.isEmpty()) { + sb.and("id", sb.entity().getId(), Op.IN); + } + sb.or("domainId", sb.entity().getDomainId(), Op.NULL); + sb.done(); + + SearchCriteria scc = sb.create(); + if (ids != null && !ids.isEmpty()) { + scc.setParameters("id", ids.toArray()); + } + sc.addAnd("domainId", SearchCriteria.Op.SC, scc); + } + + if (currentVmOffering != null) { + List storageTags = StringUtils.csvTagsToList(currentVmOffering.getTags()); + if (!storageTags.isEmpty()) { + SearchBuilder sb = _srvOfferingJoinDao.createSearchBuilder(); + for(String tag : storageTags) { + sb.and(tag, sb.entity().getTags(), Op.FIND_IN_SET); } + sb.done(); + + SearchCriteria scc = sb.create(); + for(String tag : storageTags) { + scc.setParameters(tag, tag); + } + sc.addAnd("storageTags", SearchCriteria.Op.SC, scc); + } + + List hostTags = StringUtils.csvTagsToList(currentVmOffering.getHostTag()); + if (!hostTags.isEmpty()) { + SearchBuilder sb = _srvOfferingJoinDao.createSearchBuilder(); + for(String tag : hostTags) { + sb.and(tag, sb.entity().getHostTag(), Op.FIND_IN_SET); + } + sb.done(); + + SearchCriteria scc = sb.create(); + for(String tag : hostTags) { + scc.setParameters(tag, tag); + } + sc.addAnd("hostTags", SearchCriteria.Op.SC, scc); } } - return new Pair<>(filteredOfferings, result.second()); + + return _srvOfferingJoinDao.searchAndCount(sc, searchFilter); } @Override diff --git a/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java index 5e2959d5235..ff24b222951 100644 --- a/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java @@ -145,5 +145,4 @@ public class ServiceOfferingJoinDaoImpl extends GenericDaoBase