You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by da...@apache.org on 2026/08/22 18:34:32 UTC
(cloudstack) 01/01: Scope host tag-rule matches to deployment plan in allocators
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 <da...@apache.org>
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);
+ }
+}