Skip to content

Commit c6c5689

Browse files
committed
Optimise DRS plan generation
1 parent 1f28df8 commit c6c5689

File tree

7 files changed

+795
-52
lines changed

7 files changed

+795
-52
lines changed

api/src/main/java/com/cloud/server/ManagementService.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@
7171
import com.cloud.capacity.Capacity;
7272
import com.cloud.dc.Pod;
7373
import com.cloud.dc.Vlan;
74+
import com.cloud.deploy.DeploymentPlan;
75+
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
7476
import com.cloud.exception.ConcurrentOperationException;
7577
import com.cloud.exception.ManagementServerException;
7678
import com.cloud.exception.ResourceUnavailableException;
@@ -91,6 +93,7 @@
9193
import com.cloud.vm.InstanceGroup;
9294
import com.cloud.vm.VirtualMachine;
9395
import com.cloud.vm.VirtualMachine.Type;
96+
import com.cloud.vm.VirtualMachineProfile;
9497

9598
/**
9699
* Hopefully this is temporary.
@@ -452,6 +455,32 @@ public interface ManagementService {
452455

453456
Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>> listHostsForMigrationOfVM(VirtualMachine vm, Long startIndex, Long pageSize, String keyword, List<VirtualMachine> vmList);
454457

458+
/**
459+
* Get technically compatible hosts for VM migration (storage, hypervisor, UEFI filtering).
460+
* This is a helper method that can be used independently for caching in DRS planning.
461+
*
462+
* @param vm The virtual machine to migrate
463+
* @param startIndex Starting index for pagination
464+
* @param pageSize Page size for pagination
465+
* @param keyword Keyword filter for host search
466+
* @return Ternary containing: (all hosts with count, filtered compatible hosts, storage motion requirements map)
467+
*/
468+
Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>> getTechnicallyCompatibleHosts(
469+
VirtualMachine vm, Long startIndex, Long pageSize, String keyword);
470+
471+
/**
472+
* Apply affinity group constraints and other exclusion rules for VM migration.
473+
* This is a helper method that can be used independently for per-iteration affinity checks in DRS.
474+
*
475+
* @param vm The virtual machine to migrate
476+
* @param vmProfile The VM profile
477+
* @param plan The deployment plan
478+
* @param vmList List of VMs with current/simulated placements for affinity processing
479+
* @return ExcludeList containing hosts to avoid
480+
*/
481+
ExcludeList applyAffinityConstraints(VirtualMachine vm, VirtualMachineProfile vmProfile,
482+
DeploymentPlan plan, List<VirtualMachine> vmList);
483+
455484
/**
456485
* List storage pools for live migrating of a volume. The API returns list of all pools in the cluster to which the
457486
* volume can be migrated. Current pool is not included in the list. In case of vSphere datastore cluster storage pools,

api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,38 @@ Ternary<Double, Double, Double> getMetrics(Cluster cluster, VirtualMachine vm, S
8585
Map<Long, Ternary<Long, Long, Long>> hostMemoryMap,
8686
Boolean requiresStorageMotion) throws ConfigurationException;
8787

88+
/**
89+
* Determines the metrics for a given virtual machine and destination host in a DRS cluster.
90+
* This overloaded version accepts a pre-calculated pre-imbalance value to avoid recalculating
91+
* it for every VM-host combination within the same iteration.
92+
*
93+
* @param cluster
94+
* the cluster to check
95+
* @param vm
96+
* the virtual machine to check
97+
* @param serviceOffering
98+
* the service offering for the virtual machine
99+
* @param destHost
100+
* the destination host for the virtual machine
101+
* @param hostCpuMap
102+
* a map of host IDs to the Ternary of used, reserved and total CPU on each host
103+
* @param hostMemoryMap
104+
* a map of host IDs to the Ternary of used, reserved and total memory on each host
105+
* @param requiresStorageMotion
106+
* whether storage motion is required for the virtual machine
107+
* @param preImbalance
108+
* the pre-calculated cluster imbalance before migration (null to calculate it)
109+
*
110+
* @return a ternary containing improvement, cost, benefit
111+
*/
112+
default Ternary<Double, Double, Double> getMetrics(Cluster cluster, VirtualMachine vm, ServiceOffering serviceOffering,
113+
Host destHost, Map<Long, Ternary<Long, Long, Long>> hostCpuMap,
114+
Map<Long, Ternary<Long, Long, Long>> hostMemoryMap,
115+
Boolean requiresStorageMotion, Double preImbalance) throws ConfigurationException {
116+
// Default implementation delegates to the original method for backward compatibility
117+
return getMetrics(cluster, vm, serviceOffering, destHost, hostCpuMap, hostMemoryMap, requiresStorageMotion);
118+
}
119+
88120
/**
89121
* Calculates the imbalance of the cluster after a virtual machine migration.
90122
*

plugins/drs/cluster/balanced/src/main/java/org/apache/cloudstack/cluster/Balanced.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,21 @@ public Ternary<Double, Double, Double> getMetrics(Cluster cluster, VirtualMachin
7474
Map<Long, Ternary<Long, Long, Long>> hostCpuMap, Map<Long, Ternary<Long, Long, Long>> hostMemoryMap,
7575
Boolean requiresStorageMotion) throws ConfigurationException {
7676
Double preImbalance = ClusterDrsAlgorithm.getClusterImbalance(cluster.getId(), new ArrayList<>(hostCpuMap.values()), new ArrayList<>(hostMemoryMap.values()), null);
77+
return getMetrics(cluster, vm, serviceOffering, destHost, hostCpuMap, hostMemoryMap, requiresStorageMotion, preImbalance);
78+
}
79+
80+
@Override
81+
public Ternary<Double, Double, Double> getMetrics(Cluster cluster, VirtualMachine vm,
82+
ServiceOffering serviceOffering, Host destHost,
83+
Map<Long, Ternary<Long, Long, Long>> hostCpuMap, Map<Long, Ternary<Long, Long, Long>> hostMemoryMap,
84+
Boolean requiresStorageMotion, Double preImbalance) throws ConfigurationException {
85+
// Use provided pre-imbalance if available, otherwise calculate it
86+
if (preImbalance == null) {
87+
preImbalance = ClusterDrsAlgorithm.getClusterImbalance(cluster.getId(), new ArrayList<>(hostCpuMap.values()), new ArrayList<>(hostMemoryMap.values()), null);
88+
}
7789
Double postImbalance = getImbalancePostMigration(serviceOffering, vm, destHost, hostCpuMap, hostMemoryMap);
7890

79-
logger.debug("Cluster {} pre-imbalance: {} post-imbalance: {} Algorithm: {} VM: {} srcHost: {} destHost: {}",
91+
logger.trace("Cluster {} pre-imbalance: {} post-imbalance: {} Algorithm: {} VM: {} srcHost: {} destHost: {}",
8092
cluster, preImbalance, postImbalance, getName(), vm, vm.getHostId(), destHost);
8193

8294
// This needs more research to determine the cost and benefit of a migration

plugins/drs/cluster/condensed/src/main/java/org/apache/cloudstack/cluster/Condensed.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,22 @@ public Ternary<Double, Double, Double> getMetrics(Cluster cluster, VirtualMachin
7878
Boolean requiresStorageMotion) throws ConfigurationException {
7979
Double preImbalance = ClusterDrsAlgorithm.getClusterImbalance(cluster.getId(), new ArrayList<>(hostCpuMap.values()),
8080
new ArrayList<>(hostMemoryMap.values()), null);
81+
return getMetrics(cluster, vm, serviceOffering, destHost, hostCpuMap, hostMemoryMap, requiresStorageMotion, preImbalance);
82+
}
83+
84+
@Override
85+
public Ternary<Double, Double, Double> getMetrics(Cluster cluster, VirtualMachine vm,
86+
ServiceOffering serviceOffering, Host destHost,
87+
Map<Long, Ternary<Long, Long, Long>> hostCpuMap, Map<Long, Ternary<Long, Long, Long>> hostMemoryMap,
88+
Boolean requiresStorageMotion, Double preImbalance) throws ConfigurationException {
89+
// Use provided pre-imbalance if available, otherwise calculate it
90+
if (preImbalance == null) {
91+
preImbalance = ClusterDrsAlgorithm.getClusterImbalance(cluster.getId(), new ArrayList<>(hostCpuMap.values()),
92+
new ArrayList<>(hostMemoryMap.values()), null);
93+
}
8194
Double postImbalance = getImbalancePostMigration(serviceOffering, vm, destHost, hostCpuMap, hostMemoryMap);
8295

83-
logger.debug("Cluster {} pre-imbalance: {} post-imbalance: {} Algorithm: {} VM: {} srcHost: {} destHost: {}",
96+
logger.trace("Cluster {} pre-imbalance: {} post-imbalance: {} Algorithm: {} VM: {} srcHost: {} destHost: {}",
8497
cluster, preImbalance, postImbalance, getName(), vm, vm.getHostId(), destHost);
8598

8699
// This needs more research to determine the cost and benefit of a migration

server/src/main/java/com/cloud/server/ManagementServerImpl.java

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@
692692
import com.cloud.dc.dao.PodVlanMapDao;
693693
import com.cloud.dc.dao.VlanDao;
694694
import com.cloud.deploy.DataCenterDeployment;
695+
import com.cloud.deploy.DeploymentPlan;
695696
import com.cloud.deploy.DeploymentPlanner;
696697
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
697698
import com.cloud.deploy.DeploymentPlanningManager;
@@ -1465,7 +1466,8 @@ protected boolean zoneWideVolumeRequiresStorageMotion(PrimaryDataStore volumeDat
14651466
* @param keyword Keyword filter for host search
14661467
* @return Ternary containing: (all hosts with count, filtered compatible hosts, storage motion requirements map)
14671468
*/
1468-
protected Ternary<Pair<List<HostVO>, Integer>, List<HostVO>, Map<Host, Boolean>> getTechnicallyCompatibleHosts(
1469+
@Override
1470+
public Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>> getTechnicallyCompatibleHosts(
14691471
final VirtualMachine vm,
14701472
final Long startIndex,
14711473
final Long pageSize,
@@ -1474,7 +1476,8 @@ protected Ternary<Pair<List<HostVO>, Integer>, List<HostVO>, Map<Host, Boolean>>
14741476
// GPU check
14751477
if (_serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) {
14761478
logger.info(" Live Migration of GPU enabled VM : " + vm.getInstanceName() + " is not supported");
1477-
return new Ternary<>(new Pair<>(new ArrayList<>(), 0), new ArrayList<>(), new HashMap<>());
1479+
return new Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>>(
1480+
new Pair<>(new ArrayList<>(), 0), new ArrayList<>(), new HashMap<>());
14781481
}
14791482

14801483
final long srcHostId = vm.getHostId();
@@ -1571,7 +1574,8 @@ protected Ternary<Pair<List<HostVO>, Integer>, List<HostVO>, Map<Host, Boolean>>
15711574
}
15721575

15731576
if (CollectionUtils.isEmpty(filteredHosts)) {
1574-
return new Ternary<>(new Pair<>(allHosts, allHostsPair.second()), new ArrayList<>(), new HashMap<>());
1577+
return new Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>>(
1578+
new Pair<>(allHosts, allHostsPair.second()), new ArrayList<>(), new HashMap<>());
15751579
}
15761580
} else {
15771581
final Long cluster = srcHost.getClusterId();
@@ -1584,14 +1588,16 @@ protected Ternary<Pair<List<HostVO>, Integer>, List<HostVO>, Map<Host, Boolean>>
15841588
filteredHosts = allHosts;
15851589
}
15861590

1587-
final Pair<List<HostVO>, Integer> allHostsPairResult = new Pair<>(allHosts, allHostsPair.second());
1591+
final Pair<List<? extends Host>, Integer> allHostsPairResult = new Pair<>(allHosts, allHostsPair.second());
15881592
Pair<Boolean, List<HostVO>> uefiFilteredResult = filterUefiHostsForMigration(allHosts, filteredHosts, vm);
15891593
if (!uefiFilteredResult.first()) {
1590-
return new Ternary<>(allHostsPairResult, new ArrayList<>(), new HashMap<>());
1594+
return new Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>>(
1595+
allHostsPairResult, new ArrayList<>(), new HashMap<>());
15911596
}
15921597
filteredHosts = uefiFilteredResult.second();
15931598

1594-
return new Ternary<>(allHostsPairResult, filteredHosts, requiresStorageMotion);
1599+
return new Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>>(
1600+
allHostsPairResult, filteredHosts, requiresStorageMotion);
15951601
}
15961602

15971603
/**
@@ -1604,17 +1610,15 @@ protected Ternary<Pair<List<HostVO>, Integer>, List<HostVO>, Map<Host, Boolean>>
16041610
* @param vmList List of VMs with current/simulated placements for affinity processing
16051611
* @return ExcludeList containing hosts to avoid
16061612
*/
1607-
protected ExcludeList applyAffinityConstraints(
1608-
final VirtualMachine vm,
1609-
final VirtualMachineProfile vmProfile,
1610-
final DataCenterDeployment plan,
1611-
final List<VirtualMachine> vmList) {
1612-
1613+
@Override
1614+
public ExcludeList applyAffinityConstraints(VirtualMachine vm, VirtualMachineProfile vmProfile, DeploymentPlan plan, List<VirtualMachine> vmList) {
16131615
final ExcludeList excludes = new ExcludeList();
16141616
excludes.addHost(vm.getHostId());
16151617

16161618
if (dpdkHelper.isVMDpdkEnabled(vm.getId())) {
1617-
excludeNonDPDKEnabledHosts(plan, excludes);
1619+
if (plan instanceof DataCenterDeployment) {
1620+
excludeNonDPDKEnabledHosts((DataCenterDeployment) plan, excludes);
1621+
}
16181622
}
16191623

16201624
// call affinitygroup chain
@@ -1649,7 +1653,7 @@ protected List<Host> getCapableSuitableHosts(
16491653
final VirtualMachine vm,
16501654
final VirtualMachineProfile vmProfile,
16511655
final DataCenterDeployment plan,
1652-
final List<HostVO> compatibleHosts,
1656+
final List<? extends Host> compatibleHosts,
16531657
final ExcludeList excludes,
16541658
final Host srcHost) {
16551659

@@ -1693,11 +1697,11 @@ public Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Ho
16931697
validateVmForHostMigration(vm);
16941698

16951699
// Get technically compatible hosts (storage, hypervisor, UEFI)
1696-
Ternary<Pair<List<HostVO>, Integer>, List<HostVO>, Map<Host, Boolean>> compatibilityResult =
1700+
Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>> compatibilityResult =
16971701
getTechnicallyCompatibleHosts(vm, startIndex, pageSize, keyword);
16981702

1699-
Pair<List<HostVO>, Integer> allHostsPair = compatibilityResult.first();
1700-
List<HostVO> filteredHosts = compatibilityResult.second();
1703+
Pair<List<? extends Host>, Integer> allHostsPair = compatibilityResult.first();
1704+
List<? extends Host> filteredHosts = compatibilityResult.second();
17011705
Map<Host, Boolean> requiresStorageMotion = compatibilityResult.third();
17021706

17031707
// If no compatible hosts, return early

0 commit comments

Comments
 (0)