This is an automated email from the ASF dual-hosted git repository. DaanHoogland pushed a commit to branch fix-vm-deployment-issue in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit f603e1b33881fa30490788e816fd70ae99f39d5f Author: Daan Hoogland <[email protected]> AuthorDate: Sat Aug 22 20:33:09 2026 +0200 Scope host tag-rule matches to deployment plan in allocators --- .../src/main/java/com/cloud/host/dao/HostDao.java | 1 + .../main/java/com/cloud/host/dao/HostDaoImpl.java | 15 ++++ .../manager/allocator/impl/RandomAllocator.java | 2 +- .../allocator/impl/RandomAllocatorTest.java | 42 +++++++++++ .../manager/allocator/impl/FirstFitAllocator.java | 4 +- .../allocator/impl/FirstFitAllocatorTest.java | 85 ++++++++++++++++++++++ 6 files changed, 146 insertions(+), 3 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java index 57ac35d3eb1..f9e0288cba0 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java @@ -182,6 +182,7 @@ public interface HostDao extends GenericDao<HostVO, Long>, StateDao<Status, Stat List<String> listOrderedHostsHypervisorVersionsInDatacenter(long datacenterId, HypervisorType hypervisorType); List<HostVO> findHostsWithTagRuleThatMatchComputeOferringTags(String computeOfferingTags); + List<HostVO> findHostsWithTagRuleThatMatchComputeOferringTags(String computeOfferingTags, Long clusterId, Long podId, Long dcId); List<Long> findClustersThatMatchHostTagRule(String computeOfferingTags); diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java index 8c3604d352b..cca57129de5 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java @@ -1456,6 +1456,7 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao } } + @Override public List<HostVO> findHostsWithTagRuleThatMatchComputeOferringTags(String computeOfferingTags) { List<HostTagVO> hostTagVOList = _hostTagsDao.findHostRuleTags(); List<HostVO> result = new ArrayList<>(); @@ -1468,6 +1469,20 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao return result; } + @Override + public List<HostVO> findHostsWithTagRuleThatMatchComputeOferringTags(String computeOfferingTags, Long clusterId, Long podId, Long dcId) { + List<HostVO> hosts = findHostsWithTagRuleThatMatchComputeOferringTags(computeOfferingTags); + if (dcId == null && podId == null && clusterId == null) { + return hosts; + } + + return hosts.stream() + .filter(host -> host != null && (dcId == null || host.getDataCenterId() == dcId)) + .filter(host -> podId == null || Objects.equals(host.getPodId(), podId)) + .filter(host -> clusterId == null || Objects.equals(host.getClusterId(), clusterId)) + .collect(Collectors.toList()); + } + public List<Long> findClustersThatMatchHostTagRule(String computeOfferingTags) { Set<Long> result = new HashSet<>(); List<HostVO> hosts = findHostsWithTagRuleThatMatchComputeOferringTags(computeOfferingTags); diff --git a/plugins/host-allocators/random/src/main/java/com/cloud/agent/manager/allocator/impl/RandomAllocator.java b/plugins/host-allocators/random/src/main/java/com/cloud/agent/manager/allocator/impl/RandomAllocator.java index 42129944a19..d8455737cfb 100644 --- a/plugins/host-allocators/random/src/main/java/com/cloud/agent/manager/allocator/impl/RandomAllocator.java +++ b/plugins/host-allocators/random/src/main/java/com/cloud/agent/manager/allocator/impl/RandomAllocator.java @@ -122,7 +122,7 @@ public class RandomAllocator extends AdapterBase implements HostAllocator { hostsCopy = _hostDao.listAllHostsThatHaveNoRuleTag(type, clusterId, podId, dcId); } } - hostsCopy = ListUtils.union(hostsCopy, _hostDao.findHostsWithTagRuleThatMatchComputeOferringTags(offeringHostTag)); + hostsCopy = ListUtils.union(hostsCopy, _hostDao.findHostsWithTagRuleThatMatchComputeOferringTags(offeringHostTag, clusterId, podId, dcId)); if (hostsCopy.isEmpty()) { logger.info("No suitable host found for VM [{}] in {}.", vmProfile, hostTag); diff --git a/plugins/host-allocators/random/src/test/java/com/cloud/agent/manager/allocator/impl/RandomAllocatorTest.java b/plugins/host-allocators/random/src/test/java/com/cloud/agent/manager/allocator/impl/RandomAllocatorTest.java index 538d7157184..6dbde0131e1 100644 --- a/plugins/host-allocators/random/src/test/java/com/cloud/agent/manager/allocator/impl/RandomAllocatorTest.java +++ b/plugins/host-allocators/random/src/test/java/com/cloud/agent/manager/allocator/impl/RandomAllocatorTest.java @@ -28,15 +28,24 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; +import com.cloud.capacity.CapacityManager; +import com.cloud.offering.ServiceOffering; +import com.cloud.storage.VMTemplateVO; +import com.cloud.utils.Pair; +import com.cloud.vm.VirtualMachineProfile; @RunWith(MockitoJUnitRunner.class) public class RandomAllocatorTest { @Mock HostDao hostDao; + @Mock + CapacityManager capacityManager; @InjectMocks RandomAllocator randomAllocator; @@ -77,4 +86,37 @@ public class RandomAllocatorTest { Assert.assertFalse(CollectionUtils.isEmpty(result)); Assert.assertEquals(1, result.size()); } + + @Test + public void testAllocateToUsesScopedRuleTagLookup() { + Host.Type type = Host.Type.Routing; + long dcId = 1L; + Long podId = 2L; + Long clusterId = 3L; + String offeringTag = "compute"; + + DeploymentPlan plan = Mockito.mock(DeploymentPlan.class); + Mockito.when(plan.getDataCenterId()).thenReturn(dcId); + Mockito.when(plan.getPodId()).thenReturn(podId); + Mockito.when(plan.getClusterId()).thenReturn(clusterId); + + VirtualMachineProfile vmProfile = Mockito.mock(VirtualMachineProfile.class); + ServiceOffering offering = Mockito.mock(ServiceOffering.class); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + Mockito.when(vmProfile.getServiceOffering()).thenReturn(offering); + Mockito.when(vmProfile.getTemplate()).thenReturn(template); + Mockito.when(offering.getHostTag()).thenReturn(offeringTag); + + HostVO host = Mockito.mock(HostVO.class); + List<Host> hosts = List.of(host); + Mockito.when(hostDao.listByHostTag(type, clusterId, podId, dcId, offeringTag)).thenReturn(List.of(host)); + Mockito.when(hostDao.findHostsWithTagRuleThatMatchComputeOferringTags(offeringTag, clusterId, podId, dcId)).thenReturn(new ArrayList<>()); + Mockito.when(capacityManager.checkIfHostHasCpuCapabilityAndCapacity(host, offering, true)).thenReturn(new Pair<>(true, true)); + + List<Host> result = randomAllocator.allocateTo(vmProfile, plan, type, new ExcludeList(), hosts, 1, true); + + Assert.assertEquals(1, result.size()); + Mockito.verify(hostDao).findHostsWithTagRuleThatMatchComputeOferringTags(offeringTag, clusterId, podId, dcId); + Mockito.verify(hostDao, Mockito.never()).findHostsWithTagRuleThatMatchComputeOferringTags(offeringTag); + } } diff --git a/server/src/main/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java b/server/src/main/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java index 590db3406c2..17b9834ec71 100644 --- a/server/src/main/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java +++ b/server/src/main/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java @@ -198,7 +198,7 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { clusterHosts.retainAll(hostsMatchingUefiTag); } - clusterHosts.addAll(_hostDao.findHostsWithTagRuleThatMatchComputeOferringTags(hostTagOnOffering)); + clusterHosts.addAll(_hostDao.findHostsWithTagRuleThatMatchComputeOferringTags(hostTagOnOffering, clusterId, podId, dcId)); if (clusterHosts.isEmpty()) { @@ -274,7 +274,7 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { } } - hostsCopy.addAll(_hostDao.findHostsWithTagRuleThatMatchComputeOferringTags(hostTagOnOffering)); + hostsCopy.addAll(_hostDao.findHostsWithTagRuleThatMatchComputeOferringTags(hostTagOnOffering, clusterId, podId, dcId)); if (!hostsCopy.isEmpty()) { suitableHosts = allocateTo(plan, offering, template, avoid, hostsCopy, returnUpTo, considerReservedCapacity, account); diff --git a/server/src/test/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocatorTest.java b/server/src/test/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocatorTest.java new file mode 100644 index 00000000000..4fed8662ef7 --- /dev/null +++ b/server/src/test/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocatorTest.java @@ -0,0 +1,85 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package com.cloud.agent.manager.allocator.impl; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +import com.cloud.deploy.DeploymentPlan; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDao; +import com.cloud.offering.ServiceOffering; +import com.cloud.storage.VMTemplateVO; +import com.cloud.user.Account; +import com.cloud.vm.VirtualMachineProfile; + +@RunWith(MockitoJUnitRunner.class) +public class FirstFitAllocatorTest { + + @Mock + HostDao hostDao; + @Spy + @InjectMocks + FirstFitAllocator firstFitAllocator; + + @Test + public void testAllocateToWithHostsUsesScopedRuleTagLookup() { + Host.Type type = Host.Type.Routing; + long dcId = 1L; + Long podId = 2L; + Long clusterId = 3L; + String offeringTag = "compute"; + + DeploymentPlan plan = Mockito.mock(DeploymentPlan.class); + Mockito.when(plan.getDataCenterId()).thenReturn(dcId); + Mockito.when(plan.getPodId()).thenReturn(podId); + Mockito.when(plan.getClusterId()).thenReturn(clusterId); + + VirtualMachineProfile vmProfile = Mockito.mock(VirtualMachineProfile.class); + ServiceOffering offering = Mockito.mock(ServiceOffering.class); + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); + Account account = Mockito.mock(Account.class); + Mockito.when(vmProfile.getServiceOffering()).thenReturn(offering); + Mockito.when(vmProfile.getTemplate()).thenReturn(template); + Mockito.when(vmProfile.getOwner()).thenReturn(account); + Mockito.when(offering.getHostTag()).thenReturn(offeringTag); + + HostVO host = Mockito.mock(HostVO.class); + List<Host> selectedHosts = List.of(host); + Mockito.when(hostDao.listByHostTag(type, clusterId, podId, dcId, offeringTag)).thenReturn(List.of(host)); + Mockito.when(hostDao.findHostsWithTagRuleThatMatchComputeOferringTags(offeringTag, clusterId, podId, dcId)).thenReturn(new ArrayList<>()); + Mockito.doReturn(selectedHosts).when(firstFitAllocator).allocateTo( + Mockito.eq(plan), Mockito.eq(offering), Mockito.eq(template), Mockito.any(ExcludeList.class), Mockito.anyList(), Mockito.eq(1), Mockito.eq(true), Mockito.eq(account)); + + List<Host> result = firstFitAllocator.allocateTo(vmProfile, plan, type, new ExcludeList(), selectedHosts, 1, true); + + Assert.assertEquals(1, result.size()); + Mockito.verify(hostDao).findHostsWithTagRuleThatMatchComputeOferringTags(offeringTag, clusterId, podId, dcId); + Mockito.verify(hostDao, Mockito.never()).findHostsWithTagRuleThatMatchComputeOferringTags(offeringTag); + } +}
