From 57331aca2fc4b0c7051a94a52b18969ac6920fd4 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Wed, 7 Jan 2026 09:25:11 +0530 Subject: [PATCH] Skip removal of offerings if in use during domain removal (#11780) This PR fixes #11502 - Prevent service offering update to specific domains if any instance for the offering are outside of those - Removal of offerings is skipped if it is in use by any Instance. --- .../java/com/cloud/storage/dao/VolumeDao.java | 2 + .../com/cloud/storage/dao/VolumeDaoImpl.java | 14 +++++++ .../java/com/cloud/vm/dao/VMInstanceDao.java | 3 ++ .../com/cloud/vm/dao/VMInstanceDaoImpl.java | 37 +++++++++++++++++++ .../ConfigurationManagerImpl.java | 8 +++- .../com/cloud/user/DomainManagerImpl.java | 25 +++++++++++-- 6 files changed, 84 insertions(+), 5 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java index e6ffca06f9e..4936af3caab 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java @@ -162,4 +162,6 @@ public interface VolumeDao extends GenericDao, StateDao searchRemovedByVms(List vmIds, Long batchSize); VolumeVO findOneByIScsiName(String iScsiName); + + int getVolumeCountByOfferingId(long diskOfferingId); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java index 750dbf2bee0..5ef64b04664 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java @@ -77,6 +77,7 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol protected GenericSearchBuilder primaryStorageSearch2; protected GenericSearchBuilder secondaryStorageSearch; private final SearchBuilder poolAndPathSearch; + final GenericSearchBuilder CountByOfferingId; @Inject ReservationDao reservationDao; @@ -504,6 +505,11 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol poolAndPathSearch.and("poolId", poolAndPathSearch.entity().getPoolId(), Op.EQ); poolAndPathSearch.and("path", poolAndPathSearch.entity().getPath(), Op.EQ); poolAndPathSearch.done(); + + CountByOfferingId = createSearchBuilder(Integer.class); + CountByOfferingId.select(null, Func.COUNT, CountByOfferingId.entity().getId()); + CountByOfferingId.and("diskOfferingId", CountByOfferingId.entity().getDiskOfferingId(), Op.EQ); + CountByOfferingId.done(); } @Override @@ -909,4 +915,12 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol sc.setParameters("iScsiName", iScsiName); return findOneIncludingRemovedBy(sc); } + + @Override + public int getVolumeCountByOfferingId(long diskOfferingId) { + SearchCriteria sc = CountByOfferingId.create(); + sc.setParameters("diskOfferingId", diskOfferingId); + List results = customSearch(sc, null); + return results.get(0); + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java index 823642d8c3d..56e16ddd871 100755 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java @@ -187,4 +187,7 @@ public interface VMInstanceDao extends GenericDao, StateDao< Map getNameIdMapForVmIds(Collection ids); + int getVmCountByOfferingId(Long serviceOfferingId); + + int getVmCountByOfferingNotInDomain(Long serviceOfferingId, List domainIds); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java index 29ab74dfbfd..518bc3cf497 100755 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -104,6 +104,8 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem protected SearchBuilder LastHostAndStatesSearch; protected SearchBuilder VmsNotInClusterUsingPool; protected SearchBuilder IdsPowerStateSelectSearch; + GenericSearchBuilder CountByOfferingId; + GenericSearchBuilder CountUserVmNotInDomain; @Inject ResourceTagDao tagsDao; @@ -344,6 +346,18 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem IdsPowerStateSelectSearch.entity().getPowerStateUpdateCount(), IdsPowerStateSelectSearch.entity().getPowerStateUpdateTime()); IdsPowerStateSelectSearch.done(); + + CountByOfferingId = createSearchBuilder(Integer.class); + CountByOfferingId.select(null, Func.COUNT, CountByOfferingId.entity().getId()); + CountByOfferingId.and("serviceOfferingId", CountByOfferingId.entity().getServiceOfferingId(), Op.EQ); + CountByOfferingId.done(); + + CountUserVmNotInDomain = createSearchBuilder(Integer.class); + CountUserVmNotInDomain.select(null, Func.COUNT, CountUserVmNotInDomain.entity().getId()); + CountUserVmNotInDomain.and("serviceOfferingId", CountUserVmNotInDomain.entity().getServiceOfferingId(), Op.EQ); + CountUserVmNotInDomain.and("domainIdsNotIn", CountUserVmNotInDomain.entity().getDomainId(), Op.NIN); + CountUserVmNotInDomain.done(); + } @Override @@ -1224,4 +1238,27 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem return vms.stream() .collect(Collectors.toMap(VMInstanceVO::getInstanceName, VMInstanceVO::getId)); } + + @Override + public int getVmCountByOfferingId(Long serviceOfferingId) { + if (serviceOfferingId == null) { + return 0; + } + SearchCriteria sc = CountByOfferingId.create(); + sc.setParameters("serviceOfferingId", serviceOfferingId); + List count = customSearch(sc, null); + return count.get(0); + } + + @Override + public int getVmCountByOfferingNotInDomain(Long serviceOfferingId, List domainIds) { + if (serviceOfferingId == null || CollectionUtils.isEmpty(domainIds)) { + return 0; + } + SearchCriteria sc = CountUserVmNotInDomain.create(); + sc.setParameters("serviceOfferingId", serviceOfferingId); + sc.setParameters("domainIdsNotIn", domainIds.toArray()); + List count = customSearch(sc, null); + return count.get(0); + } } diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index 246681f7585..62b3c23d27e 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -50,7 +50,7 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; - +import com.cloud.exception.UnsupportedServiceException; import com.cloud.network.as.AutoScaleManager; import com.cloud.user.AccountManagerImpl; import org.apache.cloudstack.acl.RoleType; @@ -3722,6 +3722,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati List filteredDomainIds = filterChildSubDomains(domainIds); Collections.sort(filteredDomainIds); + // avoid domain update of service offering if any instance is associated to it + int instanceCount = _vmInstanceDao.getVmCountByOfferingNotInDomain(offeringHandle.getId(), filteredDomainIds); + if (instanceCount > 0) { + throw new UnsupportedServiceException("There are Instances associated to this service offering outside of the specified domains."); + } + List filteredZoneIds = new ArrayList<>(); if (CollectionUtils.isNotEmpty(zoneIds)) { filteredZoneIds.addAll(zoneIds); diff --git a/server/src/main/java/com/cloud/user/DomainManagerImpl.java b/server/src/main/java/com/cloud/user/DomainManagerImpl.java index 6fc9c6f5ef5..28f9bd3ab39 100644 --- a/server/src/main/java/com/cloud/user/DomainManagerImpl.java +++ b/server/src/main/java/com/cloud/user/DomainManagerImpl.java @@ -34,6 +34,8 @@ import com.cloud.api.query.vo.NetworkOfferingJoinVO; import com.cloud.api.query.vo.VpcOfferingJoinVO; import com.cloud.configuration.Resource; import com.cloud.domain.dao.DomainDetailsDao; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.vpc.dao.VpcDao; import com.cloud.network.vpc.dao.VpcOfferingDao; import com.cloud.network.vpc.dao.VpcOfferingDetailsDao; import com.cloud.offerings.dao.NetworkOfferingDao; @@ -85,6 +87,7 @@ import com.cloud.projects.dao.ProjectDao; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.VolumeDao; import com.cloud.user.dao.AccountDao; import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; @@ -101,6 +104,8 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.vm.ReservationContext; import com.cloud.vm.ReservationContextImpl; +import com.cloud.vm.dao.VMInstanceDao; + import org.apache.commons.lang3.StringUtils; @Component @@ -141,6 +146,14 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom @Inject private ProjectDao _projectDao; @Inject + private VMInstanceDao vmInstanceDao; + @Inject + private NetworkDao networkDao; + @Inject + private VolumeDao volumeDao; + @Inject + private VpcDao vpcDao; + @Inject private ProjectManager _projectMgr; @Inject private RegionManager _regionMgr; @@ -543,7 +556,8 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom List vpcOfferingsDetailsToRemove = new ArrayList<>(); List vpcOfferingsForThisDomain = vpcOfferingJoinDao.findByDomainId(domainId); for (VpcOfferingJoinVO vpcOffering : vpcOfferingsForThisDomain) { - if (domainIdString.equals(vpcOffering.getDomainId())) { + int vpcCount = vpcDao.getVpcCountByOfferingId(vpcOffering.getId()); + if (vpcCount == 0) { vpcOfferingDao.remove(vpcOffering.getId()); } else { vpcOfferingsDetailsToRemove.add(vpcOffering.getId()); @@ -558,7 +572,8 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom List networkOfferingsDetailsToRemove = new ArrayList<>(); List networkOfferingsForThisDomain = networkOfferingJoinDao.findByDomainId(domainId, false); for (NetworkOfferingJoinVO networkOffering : networkOfferingsForThisDomain) { - if (domainIdString.equals(networkOffering.getDomainId())) { + int networkCount = networkDao.getNetworkCountByNetworkOffId(networkOffering.getId()); + if (networkCount == 0) { networkOfferingDao.remove(networkOffering.getId()); } else { networkOfferingsDetailsToRemove.add(networkOffering.getId()); @@ -573,7 +588,8 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom List serviceOfferingsDetailsToRemove = new ArrayList<>(); List serviceOfferingsForThisDomain = serviceOfferingJoinDao.findByDomainId(domainId); for (ServiceOfferingJoinVO serviceOffering : serviceOfferingsForThisDomain) { - if (domainIdString.equals(serviceOffering.getDomainId())) { + int vmCount = vmInstanceDao.getVmCountByOfferingId(serviceOffering.getId()); + if (vmCount == 0) { serviceOfferingDao.remove(serviceOffering.getId()); } else { serviceOfferingsDetailsToRemove.add(serviceOffering.getId()); @@ -588,7 +604,8 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom List diskOfferingsDetailsToRemove = new ArrayList<>(); List diskOfferingsForThisDomain = diskOfferingJoinDao.findByDomainId(domainId); for (DiskOfferingJoinVO diskOffering : diskOfferingsForThisDomain) { - if (domainIdString.equals(diskOffering.getDomainId())) { + int volumeCount = volumeDao.getVolumeCountByOfferingId(diskOffering.getId()); + if (volumeCount == 0) { diskOfferingDao.remove(diskOffering.getId()); } else { diskOfferingsDetailsToRemove.add(diskOffering.getId());