From b26ad3931eab27918433465d11a9769d0f42d450 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:04:22 +0000 Subject: [PATCH 1/4] Initial plan From b43afc02e8296fa48a6dbfc865231914ddd37440 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:20:54 +0000 Subject: [PATCH 2/4] Fix domain-dedicated resource eligibility in ImplicitDedicationPlanner (issue #5803) - Fix findAvoidSetForNonExplicitUserVM to check domain/account dedication before adding pods/clusters/hosts to the avoid list - Domain-dedicated resources are now accessible to users in the same domain - Add 4 unit tests covering same-domain, different-domain, same-account, different-account scenarios - Use mutable ArrayList in test return values to support list.clear() calls in production code --- .../deploy/DeploymentPlanningManagerImpl.java | 68 +++++- .../DeploymentPlanningManagerImplTest.java | 207 ++++++++++++++++++ 2 files changed, 274 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java index 230f97f6fd35..b830fbf8e01d 100644 --- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -963,8 +963,74 @@ private void findAvoiSetForRouterVM(ExcludeList avoids, VirtualMachine vm, List< } private void findAvoidSetForNonExplicitUserVM(ExcludeList avoids, VirtualMachine vm, List allPodsInDc, List allClustersInDc, List allHostsInDc) { + long vmAccountId = vm.getAccountId(); + long vmDomainId = vm.getDomainId(); + + List allPodsFromDedicatedID = new ArrayList<>(); + List allClustersFromDedicatedID = new ArrayList<>(); + List allHostsFromDedicatedID = new ArrayList<>(); + + // Check if the VM owner's domain has explicit dedication affinity group mappings. + // If so, resources dedicated to that domain are accessible to the VM owner (fixes + // issue where users in a domain-dedicated pod could not deploy with ImplicitDedicationPlanner). + List domainGroupMappings = _affinityGroupDomainMapDao.listByDomain(vmDomainId); + + List tempStorage; + + if (domainGroupMappings == null || domainGroupMappings.isEmpty()) { + // No domain-level affinity groups: search for resources dedicated to this specific account + tempStorage = _dedicatedDao.searchDedicatedPods(null, vmDomainId, vmAccountId, null, + new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first(); + for (DedicatedResourceVO vo : tempStorage) { + allPodsFromDedicatedID.add(vo.getPodId()); + } + + tempStorage.clear(); + tempStorage = _dedicatedDao.searchDedicatedClusters(null, vmDomainId, vmAccountId, null, + new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first(); + for (DedicatedResourceVO vo : tempStorage) { + allClustersFromDedicatedID.add(vo.getClusterId()); + } + + tempStorage.clear(); + tempStorage = _dedicatedDao.searchDedicatedHosts(null, vmDomainId, vmAccountId, null, + new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first(); + for (DedicatedResourceVO vo : tempStorage) { + allHostsFromDedicatedID.add(vo.getHostId()); + } + + allPodsInDc.removeAll(allPodsFromDedicatedID); + allClustersInDc.removeAll(allClustersFromDedicatedID); + allHostsInDc.removeAll(allHostsFromDedicatedID); + } else { + // Domain has explicit dedication affinity groups: search for resources dedicated to this domain + tempStorage = _dedicatedDao.searchDedicatedPods(null, vmDomainId, null, null, + new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first(); + for (DedicatedResourceVO vo : tempStorage) { + allPodsFromDedicatedID.add(vo.getPodId()); + } + + tempStorage.clear(); + tempStorage = _dedicatedDao.searchDedicatedClusters(null, vmDomainId, null, null, + new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first(); + for (DedicatedResourceVO vo : tempStorage) { + allClustersFromDedicatedID.add(vo.getClusterId()); + } + + tempStorage.clear(); + tempStorage = _dedicatedDao.searchDedicatedHosts(null, vmDomainId, null, null, + new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first(); + for (DedicatedResourceVO vo : tempStorage) { + allHostsFromDedicatedID.add(vo.getHostId()); + } + + allPodsInDc.removeAll(allPodsFromDedicatedID); + allClustersInDc.removeAll(allClustersFromDedicatedID); + allHostsInDc.removeAll(allHostsFromDedicatedID); + } + logger.debug(() -> LogUtils.logGsonWithoutException("Adding pods [%s], clusters [%s] and hosts [%s] to the avoid list in the deploy process of user VM [%s], " - + "because this VM is not explicitly dedicated to these components.", allPodsInDc, allClustersInDc, allHostsInDc, vm)); + + "because this VM is not dedicated to these components.", allPodsInDc, allClustersInDc, allHostsInDc, vm)); avoids.addPodList(allPodsInDc); avoids.addClusterList(allClustersInDc); avoids.addHostList(allHostsInDc); diff --git a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java index 5b03260d2d66..fd6f86339285 100644 --- a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java +++ b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java @@ -27,6 +27,7 @@ import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DedicatedResourceVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; @@ -73,6 +74,7 @@ import com.cloud.user.dao.AccountDao; import com.cloud.utils.Pair; import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.db.Filter; import com.cloud.vm.DiskProfile; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; @@ -82,6 +84,7 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDetailsDao; import com.cloud.vm.dao.VMInstanceDao; +import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO; import org.apache.cloudstack.affinity.AffinityGroupProcessor; import org.apache.cloudstack.affinity.AffinityGroupService; import org.apache.cloudstack.affinity.dao.AffinityGroupDao; @@ -227,6 +230,9 @@ public class DeploymentPlanningManagerImplTest { @Inject HostPodDao _podDao; + @Inject + AffinityGroupDomainMapDao _affinityGroupDomainMapDao; + private static final long dataCenterId = 1L; private static final long instanceId = 123L; private static final long hostId = 0L; @@ -937,6 +943,207 @@ private void assertAvoidIsEmpty(ExcludeList avoids, boolean isDcEmpty, boolean i Assert.assertEquals(isHostsEmpty, CollectionUtils.isEmpty(avoids.getHostsToAvoid())); } + /** + * Helper: set up mocks for checkForNonDedicatedResources tests. + * Sets up a non-root, non-explicit user VM with the given domain and account. + */ + private VMInstanceVO setupNonExplicitUserVmMocks(long vmDomainId, long vmAccountId, DataCenter mockDc) { + // Zone is not dedicated + Mockito.when(_dedicatedDao.findByZoneId(Mockito.anyLong())).thenReturn(null); + // Not root admin + Mockito.when(_accountMgr.isRootAdmin(Mockito.anyLong())).thenReturn(false); + // No explicit dedication affinity group for the VM + Mockito.when(_affinityGroupVMMapDao.findByVmIdType(Mockito.anyLong(), Mockito.eq("ExplicitDedication"))) + .thenReturn(new ArrayList<>()); + + VMInstanceVO mockVm = Mockito.mock(VMInstanceVO.class); + Mockito.when(mockVm.getType()).thenReturn(VirtualMachine.Type.User); + Mockito.when(mockVm.getDomainId()).thenReturn(vmDomainId); + Mockito.when(mockVm.getAccountId()).thenReturn(vmAccountId); + Mockito.when(mockVm.getId()).thenReturn(instanceId); + + // No dedicated clusters or hosts in DC + Mockito.when(_clusterDao.listAllClusterIds(Mockito.anyLong())).thenReturn(new ArrayList<>()); + Mockito.when(_dedicatedDao.listAllClusters()).thenReturn(new ArrayList<>()); + Mockito.when(_hostDao.listAllHosts(Mockito.anyLong())).thenReturn(new ArrayList<>()); + Mockito.when(_dedicatedDao.listAllHosts()).thenReturn(new ArrayList<>()); + Mockito.when(_dedicatedDao.searchDedicatedClusters(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(new Pair<>(new ArrayList<>(), 0)); + Mockito.when(_dedicatedDao.searchDedicatedHosts(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(new Pair<>(new ArrayList<>(), 0)); + + return mockVm; + } + + /** + * Issue #5803: A regular user in the same domain as a domain-dedicated pod + * must be allowed to deploy on that pod (i.e., the pod must NOT appear in the avoid list). + */ + @Test + public void checkForNonDedicatedResources_domainDedicatedPod_sameDomainUser_podNotInAvoidList() { + long vmDomainId = 10L; + long vmAccountId = 200L; + long dedicatedPodId = 42L; + + DataCenter mockDc = Mockito.mock(DataCenter.class); + Mockito.when(mockDc.getId()).thenReturn(dataCenterId); + + VMInstanceVO mockVm = setupNonExplicitUserVmMocks(vmDomainId, vmAccountId, mockDc); + + // Pod in DC and it is dedicated + List podsInDc = new ArrayList<>(Arrays.asList(dedicatedPodId)); + Mockito.when(_podDao.listAllPods(dataCenterId)).thenReturn(podsInDc); + Mockito.when(_dedicatedDao.listAllPods()).thenReturn(new ArrayList<>(Arrays.asList(dedicatedPodId))); + + // Domain has affinity group mappings (pod dedicated to this domain) + AffinityGroupDomainMapVO domainMap = Mockito.mock(AffinityGroupDomainMapVO.class); + Mockito.when(_affinityGroupDomainMapDao.listByDomain(vmDomainId)) + .thenReturn(Arrays.asList(domainMap)); + + // The pod is found when searching by domain (accountId = null) + DedicatedResourceVO dedicatedPod = Mockito.mock(DedicatedResourceVO.class); + Mockito.when(dedicatedPod.getPodId()).thenReturn(dedicatedPodId); + Mockito.when(_dedicatedDao.searchDedicatedPods(Mockito.isNull(), Mockito.eq(vmDomainId), + Mockito.isNull(), Mockito.isNull(), Mockito.any(Filter.class))) + .thenReturn(new Pair<>(new ArrayList<>(Arrays.asList(dedicatedPod)), 1)); + + VirtualMachineProfileImpl mockProfile = Mockito.mock(VirtualMachineProfileImpl.class); + Mockito.when(mockProfile.getVirtualMachine()).thenReturn(mockVm); + Mockito.when(mockProfile.getOwner()).thenReturn(Mockito.mock(com.cloud.user.Account.class)); + + ExcludeList avoids = new ExcludeList(); + _dpm.checkForNonDedicatedResources(mockProfile, mockDc, avoids); + + // The dedicated pod belongs to the same domain as the VM owner: must NOT be in avoid list + assertTrue("Domain-dedicated pod should not be in avoid list for same-domain user", + CollectionUtils.isEmpty(avoids.getPodsToAvoid()) || !avoids.getPodsToAvoid().contains(dedicatedPodId)); + } + + /** + * A user from a different domain must NOT be allowed to deploy on a pod dedicated to another domain. + */ + @Test + public void checkForNonDedicatedResources_domainDedicatedPod_differentDomainUser_podInAvoidList() { + long podDomainId = 10L; // domain the pod is dedicated to + long vmDomainId = 20L; // VM owner belongs to a DIFFERENT domain + long vmAccountId = 300L; + long dedicatedPodId = 43L; + + DataCenter mockDc = Mockito.mock(DataCenter.class); + Mockito.when(mockDc.getId()).thenReturn(dataCenterId); + + VMInstanceVO mockVm = setupNonExplicitUserVmMocks(vmDomainId, vmAccountId, mockDc); + + // Pod in DC and it is dedicated (to podDomainId, NOT to vmDomainId) + List podsInDc = new ArrayList<>(Arrays.asList(dedicatedPodId)); + Mockito.when(_podDao.listAllPods(dataCenterId)).thenReturn(podsInDc); + Mockito.when(_dedicatedDao.listAllPods()).thenReturn(new ArrayList<>(Arrays.asList(dedicatedPodId))); + + // VM owner's domain has NO affinity group mappings (no dedicated resources for vmDomainId) + Mockito.when(_affinityGroupDomainMapDao.listByDomain(vmDomainId)) + .thenReturn(new ArrayList<>()); + + // No pods dedicated to the VM owner's account + Mockito.when(_dedicatedDao.searchDedicatedPods(Mockito.isNull(), Mockito.eq(vmDomainId), + Mockito.eq(vmAccountId), Mockito.isNull(), Mockito.any(Filter.class))) + .thenReturn(new Pair<>(new ArrayList<>(), 0)); + + VirtualMachineProfileImpl mockProfile = Mockito.mock(VirtualMachineProfileImpl.class); + Mockito.when(mockProfile.getVirtualMachine()).thenReturn(mockVm); + Mockito.when(mockProfile.getOwner()).thenReturn(Mockito.mock(com.cloud.user.Account.class)); + + ExcludeList avoids = new ExcludeList(); + _dpm.checkForNonDedicatedResources(mockProfile, mockDc, avoids); + + // The pod is dedicated to a different domain: must be in avoid list + assertTrue("Domain-dedicated pod should be in avoid list for different-domain user", + avoids.getPodsToAvoid() != null && avoids.getPodsToAvoid().contains(dedicatedPodId)); + } + + /** + * An account-dedicated pod should be accessible to the owning account + * (no domain-level affinity groups, but account-level dedicated resource exists). + */ + @Test + public void checkForNonDedicatedResources_accountDedicatedPod_sameAccount_podNotInAvoidList() { + long vmDomainId = 10L; + long vmAccountId = 200L; + long dedicatedPodId = 44L; + + DataCenter mockDc = Mockito.mock(DataCenter.class); + Mockito.when(mockDc.getId()).thenReturn(dataCenterId); + + VMInstanceVO mockVm = setupNonExplicitUserVmMocks(vmDomainId, vmAccountId, mockDc); + + // Pod in DC and it is dedicated + List podsInDc = new ArrayList<>(Arrays.asList(dedicatedPodId)); + Mockito.when(_podDao.listAllPods(dataCenterId)).thenReturn(podsInDc); + Mockito.when(_dedicatedDao.listAllPods()).thenReturn(new ArrayList<>(Arrays.asList(dedicatedPodId))); + + // Domain has NO affinity group mappings (account-level dedication, not domain-level) + Mockito.when(_affinityGroupDomainMapDao.listByDomain(vmDomainId)) + .thenReturn(new ArrayList<>()); + + // The pod IS found when searching by account + DedicatedResourceVO dedicatedPod = Mockito.mock(DedicatedResourceVO.class); + Mockito.when(dedicatedPod.getPodId()).thenReturn(dedicatedPodId); + Mockito.when(_dedicatedDao.searchDedicatedPods(Mockito.isNull(), Mockito.eq(vmDomainId), + Mockito.eq(vmAccountId), Mockito.isNull(), Mockito.any(Filter.class))) + .thenReturn(new Pair<>(new ArrayList<>(Arrays.asList(dedicatedPod)), 1)); + + VirtualMachineProfileImpl mockProfile = Mockito.mock(VirtualMachineProfileImpl.class); + Mockito.when(mockProfile.getVirtualMachine()).thenReturn(mockVm); + Mockito.when(mockProfile.getOwner()).thenReturn(Mockito.mock(com.cloud.user.Account.class)); + + ExcludeList avoids = new ExcludeList(); + _dpm.checkForNonDedicatedResources(mockProfile, mockDc, avoids); + + // The pod is dedicated to this account: must NOT be in avoid list + assertTrue("Account-dedicated pod should not be in avoid list for same-account user", + CollectionUtils.isEmpty(avoids.getPodsToAvoid()) || !avoids.getPodsToAvoid().contains(dedicatedPodId)); + } + + /** + * A user whose account is different from the account a pod is dedicated to + * must NOT be able to deploy on that pod. + */ + @Test + public void checkForNonDedicatedResources_accountDedicatedPod_differentAccount_podInAvoidList() { + long vmDomainId = 10L; + long vmAccountId = 300L; // different account + long dedicatedPodId = 45L; + + DataCenter mockDc = Mockito.mock(DataCenter.class); + Mockito.when(mockDc.getId()).thenReturn(dataCenterId); + + VMInstanceVO mockVm = setupNonExplicitUserVmMocks(vmDomainId, vmAccountId, mockDc); + + // Pod in DC and it is dedicated (to a different account) + List podsInDc = new ArrayList<>(Arrays.asList(dedicatedPodId)); + Mockito.when(_podDao.listAllPods(dataCenterId)).thenReturn(podsInDc); + Mockito.when(_dedicatedDao.listAllPods()).thenReturn(new ArrayList<>(Arrays.asList(dedicatedPodId))); + + // Domain has NO affinity group mappings + Mockito.when(_affinityGroupDomainMapDao.listByDomain(vmDomainId)) + .thenReturn(new ArrayList<>()); + + // No pods dedicated to this account + Mockito.when(_dedicatedDao.searchDedicatedPods(Mockito.isNull(), Mockito.eq(vmDomainId), + Mockito.eq(vmAccountId), Mockito.isNull(), Mockito.any(Filter.class))) + .thenReturn(new Pair<>(new ArrayList<>(), 0)); + + VirtualMachineProfileImpl mockProfile = Mockito.mock(VirtualMachineProfileImpl.class); + Mockito.when(mockProfile.getVirtualMachine()).thenReturn(mockVm); + Mockito.when(mockProfile.getOwner()).thenReturn(Mockito.mock(com.cloud.user.Account.class)); + + ExcludeList avoids = new ExcludeList(); + _dpm.checkForNonDedicatedResources(mockProfile, mockDc, avoids); + + // The pod is dedicated to a different account: must be in avoid list + assertTrue("Account-dedicated pod should be in avoid list for different-account user", + avoids.getPodsToAvoid() != null && avoids.getPodsToAvoid().contains(dedicatedPodId)); + } + @Configuration @ComponentScan(basePackageClasses = {DeploymentPlanningManagerImpl.class}, includeFilters = {@Filter(value = TestConfiguration.Library.class, From 630c8616df6a4226de068a5f2e70eef1a82d85cd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:23:05 +0000 Subject: [PATCH 3/4] Refactor findAvoidSetForNonExplicitUserVM: remove pagination limit and tempStorage.clear() - Use Filter without offset/limit to retrieve all dedicated resources (not just the first) - Eliminate tempStorage variable and clear() calls to avoid potential side effects - Code is now cleaner and consistent with correct behavior --- .../deploy/DeploymentPlanningManagerImpl.java | 46 +++++-------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java index b830fbf8e01d..bea4d69f360a 100644 --- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -975,60 +975,36 @@ private void findAvoidSetForNonExplicitUserVM(ExcludeList avoids, VirtualMachine // issue where users in a domain-dedicated pod could not deploy with ImplicitDedicationPlanner). List domainGroupMappings = _affinityGroupDomainMapDao.listByDomain(vmDomainId); - List tempStorage; + Filter filter = new Filter(DedicatedResourceVO.class, "id", true); if (domainGroupMappings == null || domainGroupMappings.isEmpty()) { // No domain-level affinity groups: search for resources dedicated to this specific account - tempStorage = _dedicatedDao.searchDedicatedPods(null, vmDomainId, vmAccountId, null, - new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first(); - for (DedicatedResourceVO vo : tempStorage) { + for (DedicatedResourceVO vo : _dedicatedDao.searchDedicatedPods(null, vmDomainId, vmAccountId, null, filter).first()) { allPodsFromDedicatedID.add(vo.getPodId()); } - - tempStorage.clear(); - tempStorage = _dedicatedDao.searchDedicatedClusters(null, vmDomainId, vmAccountId, null, - new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first(); - for (DedicatedResourceVO vo : tempStorage) { + for (DedicatedResourceVO vo : _dedicatedDao.searchDedicatedClusters(null, vmDomainId, vmAccountId, null, filter).first()) { allClustersFromDedicatedID.add(vo.getClusterId()); } - - tempStorage.clear(); - tempStorage = _dedicatedDao.searchDedicatedHosts(null, vmDomainId, vmAccountId, null, - new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first(); - for (DedicatedResourceVO vo : tempStorage) { + for (DedicatedResourceVO vo : _dedicatedDao.searchDedicatedHosts(null, vmDomainId, vmAccountId, null, filter).first()) { allHostsFromDedicatedID.add(vo.getHostId()); } - - allPodsInDc.removeAll(allPodsFromDedicatedID); - allClustersInDc.removeAll(allClustersFromDedicatedID); - allHostsInDc.removeAll(allHostsFromDedicatedID); } else { // Domain has explicit dedication affinity groups: search for resources dedicated to this domain - tempStorage = _dedicatedDao.searchDedicatedPods(null, vmDomainId, null, null, - new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first(); - for (DedicatedResourceVO vo : tempStorage) { + for (DedicatedResourceVO vo : _dedicatedDao.searchDedicatedPods(null, vmDomainId, null, null, filter).first()) { allPodsFromDedicatedID.add(vo.getPodId()); } - - tempStorage.clear(); - tempStorage = _dedicatedDao.searchDedicatedClusters(null, vmDomainId, null, null, - new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first(); - for (DedicatedResourceVO vo : tempStorage) { + for (DedicatedResourceVO vo : _dedicatedDao.searchDedicatedClusters(null, vmDomainId, null, null, filter).first()) { allClustersFromDedicatedID.add(vo.getClusterId()); } - - tempStorage.clear(); - tempStorage = _dedicatedDao.searchDedicatedHosts(null, vmDomainId, null, null, - new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first(); - for (DedicatedResourceVO vo : tempStorage) { + for (DedicatedResourceVO vo : _dedicatedDao.searchDedicatedHosts(null, vmDomainId, null, null, filter).first()) { allHostsFromDedicatedID.add(vo.getHostId()); } - - allPodsInDc.removeAll(allPodsFromDedicatedID); - allClustersInDc.removeAll(allClustersFromDedicatedID); - allHostsInDc.removeAll(allHostsFromDedicatedID); } + allPodsInDc.removeAll(allPodsFromDedicatedID); + allClustersInDc.removeAll(allClustersFromDedicatedID); + allHostsInDc.removeAll(allHostsFromDedicatedID); + logger.debug(() -> LogUtils.logGsonWithoutException("Adding pods [%s], clusters [%s] and hosts [%s] to the avoid list in the deploy process of user VM [%s], " + "because this VM is not dedicated to these components.", allPodsInDc, allClustersInDc, allHostsInDc, vm)); avoids.addPodList(allPodsInDc); From b6eda5f983ceadafa0532a804d69009ace094080 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:24:00 +0000 Subject: [PATCH 4/4] Address code review: document filter purpose and clarify test mock intent --- .../java/com/cloud/deploy/DeploymentPlanningManagerImpl.java | 1 + .../com/cloud/deploy/DeploymentPlanningManagerImplTest.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java index bea4d69f360a..9ad8fab653db 100644 --- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -975,6 +975,7 @@ private void findAvoidSetForNonExplicitUserVM(ExcludeList avoids, VirtualMachine // issue where users in a domain-dedicated pod could not deploy with ImplicitDedicationPlanner). List domainGroupMappings = _affinityGroupDomainMapDao.listByDomain(vmDomainId); + // Sort by id ascending, no pagination — retrieve all dedicated resources for this domain/account Filter filter = new Filter(DedicatedResourceVO.class, "id", true); if (domainGroupMappings == null || domainGroupMappings.isEmpty()) { diff --git a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java index fd6f86339285..0bc6c6dd27c3 100644 --- a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java +++ b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java @@ -995,7 +995,8 @@ public void checkForNonDedicatedResources_domainDedicatedPod_sameDomainUser_podN Mockito.when(_podDao.listAllPods(dataCenterId)).thenReturn(podsInDc); Mockito.when(_dedicatedDao.listAllPods()).thenReturn(new ArrayList<>(Arrays.asList(dedicatedPodId))); - // Domain has affinity group mappings (pod dedicated to this domain) + // Domain has affinity group mappings (pod dedicated to this domain). + // The content of the list entries does not matter; only emptiness is checked. AffinityGroupDomainMapVO domainMap = Mockito.mock(AffinityGroupDomainMapVO.class); Mockito.when(_affinityGroupDomainMapDao.listByDomain(vmDomainId)) .thenReturn(Arrays.asList(domainMap));