Skip to content

Commit a755ecf

Browse files
ustcweizhouRakesh VenkateshSina Kashipazhaweizhouapachesoreana
authored
Migrate vm across clusters (#4534)
* server: Optional destination host when migrate a vm * #4378: migrate systemvms/routers with optional host * Migrate vms across clusters After enabling maintenance mode on host, if no suitable hosts are found in the same cluster then search for hosts in different clusters having the same hypervisor type set global setting migrate.vm.across.clusters to true * search all clusters in zone when migrate vm across clusters if applicable * Honor migrate.vm.across.clusters when migrate vm without destination * Check MIGRATE_VM_ACROSS_CLUSTERS in zone setting * #4534 Fix Vms are migrated to same clusters in CloudStack caused by dedicated resources. * #4534 extract some codes to methods * fix #4534: an error in 'git merge' * fix #4534: remove useless methods in FirstFitPlanner.java * fix #4534: vms are stopped in host maintenance * fix #4534: across-cluster migration of vms with cluster-scoped pools is supported by vmware vmotion * fix #4534: migrate systemvms is only possible across clusters in same pod to avoid potential network errors. * fix #4534: code optimization Co-authored-by: Rakesh Venkatesh <r.venkatesh@global.leaseweb.com> Co-authored-by: Sina Kashipazha <s.kashipazha@global.leaseweb.com> Co-authored-by: Wei Zhou <weizhou@apache.org> Co-authored-by: Sina Kashipazha <soreana@users.noreply.github.com>
1 parent c0ecc34 commit a755ecf

File tree

10 files changed

+136
-35
lines changed

10 files changed

+136
-35
lines changed

engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,18 @@
2626

2727
import com.cloud.agent.api.to.NicTO;
2828
import com.cloud.agent.api.to.VirtualMachineTO;
29+
import com.cloud.deploy.DataCenterDeployment;
2930
import com.cloud.deploy.DeployDestination;
3031
import com.cloud.deploy.DeploymentPlan;
3132
import com.cloud.deploy.DeploymentPlanner;
33+
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
3234
import com.cloud.exception.AgentUnavailableException;
3335
import com.cloud.exception.ConcurrentOperationException;
3436
import com.cloud.exception.InsufficientCapacityException;
3537
import com.cloud.exception.InsufficientServerCapacityException;
3638
import com.cloud.exception.OperationTimedoutException;
3739
import com.cloud.exception.ResourceUnavailableException;
40+
import com.cloud.host.Host;
3841
import com.cloud.hypervisor.Hypervisor.HypervisorType;
3942
import com.cloud.network.Network;
4043
import com.cloud.offering.DiskOffering;
@@ -246,6 +249,10 @@ static String getHypervisorHostname(String name) {
246249

247250
UserVm restoreVirtualMachine(long vmId, Long newTemplateId) throws ResourceUnavailableException, InsufficientCapacityException;
248251

252+
boolean checkIfVmHasClusterWideVolumes(Long vmId);
253+
254+
DataCenterDeployment getMigrationDeployment(VirtualMachine vm, Host host, Long poolId, ExcludeList excludes);
255+
249256
/**
250257
* Returns true if the VM's Root volume is allocated at a local storage pool
251258
*/

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@
135135
import com.cloud.configuration.Resource.ResourceType;
136136
import com.cloud.dc.ClusterDetailsDao;
137137
import com.cloud.dc.ClusterDetailsVO;
138+
import com.cloud.dc.ClusterVO;
138139
import com.cloud.dc.DataCenter;
139140
import com.cloud.dc.DataCenterVO;
140141
import com.cloud.dc.HostPodVO;
@@ -250,6 +251,8 @@
250251
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
251252
import com.google.common.base.Strings;
252253

254+
import static com.cloud.configuration.ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS;
255+
253256
public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, VmWorkJobHandler, Listener, Configurable {
254257
private static final Logger s_logger = Logger.getLogger(VirtualMachineManagerImpl.class);
255258

@@ -3368,9 +3371,9 @@ private void orchestrateMigrateAway(final String vmUuid, final long srcHostId, f
33683371
}
33693372
}
33703373

3371-
final DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, poolId, null);
33723374
final ExcludeList excludes = new ExcludeList();
33733375
excludes.addHost(hostId);
3376+
DataCenterDeployment plan = getMigrationDeployment(vm, host, poolId, excludes);
33743377

33753378
DeployDestination dest = null;
33763379
while (true) {
@@ -3382,16 +3385,12 @@ private void orchestrateMigrateAway(final String vmUuid, final long srcHostId, f
33823385
s_logger.warn("Unable to create deployment, affinity rules associted to the VM conflict", e2);
33833386
throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict");
33843387
}
3385-
3386-
if (dest != null) {
3387-
if (s_logger.isDebugEnabled()) {
3388-
s_logger.debug("Found destination " + dest + " for migrating to.");
3389-
}
3390-
} else {
3391-
if (s_logger.isDebugEnabled()) {
3392-
s_logger.debug("Unable to find destination for migrating the vm " + profile);
3393-
}
3394-
throw new InsufficientServerCapacityException("Unable to find a server to migrate to.", host.getClusterId());
3388+
if (dest == null) {
3389+
s_logger.warn("Unable to find destination for migrating the vm " + profile);
3390+
throw new InsufficientServerCapacityException("Unable to find a server to migrate to.", DataCenter.class, host.getDataCenterId());
3391+
}
3392+
if (s_logger.isDebugEnabled()) {
3393+
s_logger.debug("Found destination " + dest + " for migrating to.");
33953394
}
33963395

33973396
excludes.addHost(dest.getHost().getId());
@@ -3420,6 +3419,41 @@ private void orchestrateMigrateAway(final String vmUuid, final long srcHostId, f
34203419
}
34213420
}
34223421

3422+
/**
3423+
* Check if the virtual machine has any volume in cluster-wide pool
3424+
* @param vmId id of the virtual machine
3425+
* @return true if volume exists on cluster-wide pool else false
3426+
*/
3427+
@Override
3428+
public boolean checkIfVmHasClusterWideVolumes(Long vmId) {
3429+
final List<VolumeVO> volumesList = _volsDao.findCreatedByInstance(vmId);
3430+
3431+
return volumesList.parallelStream()
3432+
.anyMatch(vol -> _storagePoolDao.findById(vol.getPoolId()).getScope().equals(ScopeType.CLUSTER));
3433+
3434+
}
3435+
3436+
@Override
3437+
public DataCenterDeployment getMigrationDeployment(final VirtualMachine vm, final Host host, final Long poolId, final ExcludeList excludes) {
3438+
if (MIGRATE_VM_ACROSS_CLUSTERS.valueIn(host.getDataCenterId()) &&
3439+
(HypervisorType.VMware.equals(host.getHypervisorType()) || !checkIfVmHasClusterWideVolumes(vm.getId()))) {
3440+
s_logger.info("Searching for hosts in the zone for vm migration");
3441+
List<Long> clustersToExclude = _clusterDao.listAllClusters(host.getDataCenterId());
3442+
List<ClusterVO> clusterList = _clusterDao.listByDcHyType(host.getDataCenterId(), host.getHypervisorType().toString());
3443+
for (ClusterVO cluster : clusterList) {
3444+
clustersToExclude.remove(cluster.getId());
3445+
}
3446+
for (Long clusterId : clustersToExclude) {
3447+
excludes.addCluster(clusterId);
3448+
}
3449+
if (VirtualMachine.systemVMs.contains(vm.getType())) {
3450+
return new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), null, null, poolId, null);
3451+
}
3452+
return new DataCenterDeployment(host.getDataCenterId(), null, null, null, poolId, null);
3453+
}
3454+
return new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, poolId, null);
3455+
}
3456+
34233457
protected class CleanupTask extends ManagedContextRunnable {
34243458
@Override
34253459
protected void runInContext() {

engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
public interface CapacityDao extends GenericDao<CapacityVO, Long> {
2828
CapacityVO findByHostIdType(Long hostId, short capacityType);
2929

30-
List<Long> listClustersInZoneOrPodByHostCapacities(long id, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone);
30+
List<Long> listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone);
3131

3232
List<Long> listHostsWithEnoughCapacity(int requiredCpu, long requiredRam, Long clusterId, String hostType);
3333

@@ -37,7 +37,7 @@ public interface CapacityDao extends GenericDao<CapacityVO, Long> {
3737

3838
List<SummedCapacity> findNonSharedStorageForClusterPodZone(Long zoneId, Long podId, Long clusterId);
3939

40-
Pair<List<Long>, Map<Long, Double>> orderClustersByAggregateCapacity(long id, short capacityType, boolean isZone);
40+
Pair<List<Long>, Map<Long, Double>> orderClustersByAggregateCapacity(long id, long vmId, short capacityType, boolean isZone);
4141

4242
List<SummedCapacity> findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId);
4343

engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,24 @@ public class CapacityDaoImpl extends GenericDaoBase<CapacityVO, Long> implements
7575
+ " AND host_capacity.host_id IN (SELECT capacity.host_id FROM `cloud`.`op_host_capacity` capacity JOIN `cloud`.`cluster_details` cluster_details ON (capacity.cluster_id= cluster_details.cluster_id) where capacity_type='0' AND cluster_details.name='memoryOvercommitRatio' AND ((total_capacity* cluster_details.value) - used_capacity ) >= ?)) ";
7676

7777
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART1 =
78-
"SELECT capacity.cluster_id, SUM(used_capacity+reserved_capacity)/SUM(total_capacity ) FROM `cloud`.`op_host_capacity` capacity WHERE ";
78+
"SELECT capacity.cluster_id, SUM(used_capacity+reserved_capacity)/SUM(total_capacity ) FROM `cloud`.`op_host_capacity` capacity ";
7979

8080
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART2 =
81-
" AND capacity_type = ? AND cluster_details.name =? GROUP BY capacity.cluster_id ORDER BY SUM(used_capacity+reserved_capacity)/SUM(total_capacity * cluster_details.value) ASC";
81+
" AND capacity_type = ? GROUP BY capacity.cluster_id ORDER BY SUM(used_capacity+reserved_capacity)/SUM(total_capacity) ASC";
82+
83+
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_JOIN_1 =
84+
"JOIN host ON capacity.host_id = host.id " +
85+
"LEFT JOIN (SELECT affinity_group.id, agvm.instance_id FROM affinity_group_vm_map agvm JOIN affinity_group ON agvm.affinity_group_id = affinity_group.id AND affinity_group.type='ExplicitDedication') AS ag ON ag.instance_id = ? " +
86+
"LEFT JOIN dedicated_resources dr_pod ON dr_pod.pod_id IS NOT NULL AND dr_pod.pod_id = host.pod_id " +
87+
"LEFT JOIN dedicated_resources dr_cluster ON dr_cluster.cluster_id IS NOT NULL AND dr_cluster.cluster_id = host.cluster_id " +
88+
"LEFT JOIN dedicated_resources dr_host ON dr_host.host_id IS NOT NULL AND dr_host.host_id = host.id ";
89+
90+
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_JOIN_2 =
91+
" AND ((ag.id IS NULL AND dr_pod.pod_id IS NULL AND dr_cluster.cluster_id IS NULL AND dr_host.host_id IS NULL) OR " +
92+
"(dr_pod.affinity_group_id = ag.id OR dr_cluster.affinity_group_id = ag.id OR dr_host.affinity_group_id = ag.id))";
8293

8394
private static final String ORDER_CLUSTERS_BY_AGGREGATE_OVERCOMMIT_CAPACITY_PART1 =
84-
"SELECT capacity.cluster_id, SUM(used_capacity+reserved_capacity)/SUM(total_capacity * cluster_details.value) FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`cluster_details` cluster_details ON (capacity.cluster_id = cluster_details.cluster_id) WHERE ";
95+
"SELECT capacity.cluster_id, SUM(used_capacity+reserved_capacity)/SUM(total_capacity * cluster_details.value) FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`cluster_details` cluster_details ON (capacity.cluster_id = cluster_details.cluster_id) ";
8596

8697
private static final String ORDER_CLUSTERS_BY_AGGREGATE_OVERCOMMIT_CAPACITY_PART2 =
8798
" AND capacity_type = ? AND cluster_details.name =? GROUP BY capacity.cluster_id ORDER BY SUM(used_capacity+reserved_capacity)/SUM(total_capacity * cluster_details.value) ASC";
@@ -572,7 +583,7 @@ public CapacityVO findByHostIdType(Long hostId, short capacityType) {
572583
}
573584

574585
@Override
575-
public List<Long> listClustersInZoneOrPodByHostCapacities(long id, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone) {
586+
public List<Long> listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone) {
576587
TransactionLegacy txn = TransactionLegacy.currentTxn();
577588
PreparedStatement pstmt = null;
578589
List<Long> result = new ArrayList<Long>();
@@ -854,7 +865,7 @@ public boolean removeBy(Short capacityType, Long zoneId, Long podId, Long cluste
854865
}
855866

856867
@Override
857-
public Pair<List<Long>, Map<Long, Double>> orderClustersByAggregateCapacity(long id, short capacityTypeForOrdering, boolean isZone) {
868+
public Pair<List<Long>, Map<Long, Double>> orderClustersByAggregateCapacity(long id, long vmId, short capacityTypeForOrdering, boolean isZone) {
858869
TransactionLegacy txn = TransactionLegacy.currentTxn();
859870
PreparedStatement pstmt = null;
860871
List<Long> result = new ArrayList<Long>();
@@ -866,11 +877,14 @@ public Pair<List<Long>, Map<Long, Double>> orderClustersByAggregateCapacity(long
866877
sql.append(ORDER_CLUSTERS_BY_AGGREGATE_OVERCOMMIT_CAPACITY_PART1);
867878
}
868879

880+
sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_JOIN_1);
869881
if (isZone) {
870-
sql.append(" data_center_id = ?");
882+
sql.append("WHERE capacity.capacity_state = 'Enabled' AND capacity.data_center_id = ?");
871883
} else {
872-
sql.append(" pod_id = ?");
884+
sql.append("WHERE capacity.capacity_state = 'Enabled' AND capacity.pod_id = ?");
873885
}
886+
sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_JOIN_2);
887+
874888
if (capacityTypeForOrdering != Capacity.CAPACITY_TYPE_CPU && capacityTypeForOrdering != Capacity.CAPACITY_TYPE_MEMORY) {
875889
sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART2);
876890
} else {
@@ -879,13 +893,14 @@ public Pair<List<Long>, Map<Long, Double>> orderClustersByAggregateCapacity(long
879893

880894
try {
881895
pstmt = txn.prepareAutoCloseStatement(sql.toString());
882-
pstmt.setLong(1, id);
883-
pstmt.setShort(2, capacityTypeForOrdering);
896+
pstmt.setLong(1, vmId);
897+
pstmt.setLong(2, id);
898+
pstmt.setShort(3, capacityTypeForOrdering);
884899

885900
if (capacityTypeForOrdering == Capacity.CAPACITY_TYPE_CPU) {
886-
pstmt.setString(3, "cpuOvercommitRatio");
901+
pstmt.setString(4, "cpuOvercommitRatio");
887902
} else if (capacityTypeForOrdering == Capacity.CAPACITY_TYPE_MEMORY) {
888-
pstmt.setString(3, "memoryOvercommitRatio");
903+
pstmt.setString(4, "memoryOvercommitRatio");
889904
}
890905

891906
ResultSet rs = pstmt.executeQuery();

plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,15 +382,15 @@ private void initializeForTest(VirtualMachineProfileImpl vmProfile, DataCenterDe
382382
clustersWithEnoughCapacity.add(2L);
383383
clustersWithEnoughCapacity.add(3L);
384384
when(
385-
capacityDao.listClustersInZoneOrPodByHostCapacities(dataCenterId, noOfCpusInOffering * cpuSpeedInOffering, ramInOffering * 1024L * 1024L,
385+
capacityDao.listClustersInZoneOrPodByHostCapacities(dataCenterId, 12L, noOfCpusInOffering * cpuSpeedInOffering, ramInOffering * 1024L * 1024L,
386386
Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersWithEnoughCapacity);
387387

388388
Map<Long, Double> clusterCapacityMap = new HashMap<Long, Double>();
389389
clusterCapacityMap.put(1L, 2048D);
390390
clusterCapacityMap.put(2L, 2048D);
391391
clusterCapacityMap.put(3L, 2048D);
392392
Pair<List<Long>, Map<Long, Double>> clustersOrderedByCapacity = new Pair<List<Long>, Map<Long, Double>>(clustersWithEnoughCapacity, clusterCapacityMap);
393-
when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersOrderedByCapacity);
393+
when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, 12L, Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersOrderedByCapacity);
394394

395395
List<Long> disabledClusters = new ArrayList<Long>();
396396
List<Long> clustersWithDisabledPods = new ArrayList<Long>();

server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
439439

440440
public static final ConfigKey<Integer> VM_USERDATA_MAX_LENGTH = new ConfigKey<Integer>("Advanced", Integer.class, VM_USERDATA_MAX_LENGTH_STRING, "32768",
441441
"Max length of vm userdata after base64 decoding. Default is 32768 and maximum is 1048576", true);
442+
public static final ConfigKey<Boolean> MIGRATE_VM_ACROSS_CLUSTERS = new ConfigKey<Boolean>(Boolean.class, "migrate.vm.across.clusters", "Advanced", "false",
443+
"Indicates whether the VM can be migrated to different cluster if no host is found in same cluster",true, ConfigKey.Scope.Zone, null);
442444

443445
private static final String IOPS_READ_RATE = "IOPS Read";
444446
private static final String IOPS_WRITE_RATE = "IOPS Write";
@@ -6535,6 +6537,6 @@ public String getConfigComponentName() {
65356537
public ConfigKey<?>[] getConfigKeys() {
65366538
return new ConfigKey<?>[] {SystemVMUseLocalStorage, IOPS_MAX_READ_LENGTH, IOPS_MAX_WRITE_LENGTH,
65376539
BYTES_MAX_READ_LENGTH, BYTES_MAX_WRITE_LENGTH, ADD_HOST_ON_SERVICE_RESTART_KVM, SET_HOST_DOWN_TO_MAINTENANCE, VM_SERVICE_OFFERING_MAX_CPU_CORES,
6538-
VM_SERVICE_OFFERING_MAX_RAM_SIZE, VM_USERDATA_MAX_LENGTH};
6540+
VM_SERVICE_OFFERING_MAX_RAM_SIZE, VM_USERDATA_MAX_LENGTH, MIGRATE_VM_ACROSS_CLUSTERS};
65396541
}
65406542
}

server/src/main/java/com/cloud/deploy/FirstFitPlanner.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ private List<Long> scanClustersForDestinationInZoneOrPod(long id, boolean isZone
383383
long requiredRam = offering.getRamSize() * 1024L * 1024L;
384384

385385
//list clusters under this zone by cpu and ram capacity
386-
Pair<List<Long>, Map<Long, Double>> clusterCapacityInfo = listClustersByCapacity(id, requiredCpu, requiredRam, avoid, isZone);
386+
Pair<List<Long>, Map<Long, Double>> clusterCapacityInfo = listClustersByCapacity(id, vmProfile.getId(), requiredCpu, requiredRam, avoid, isZone);
387387
List<Long> prioritizedClusterIds = clusterCapacityInfo.first();
388388
if (!prioritizedClusterIds.isEmpty()) {
389389
if (avoid.getClustersToAvoid() != null) {
@@ -441,7 +441,7 @@ protected List<Long> reorderPods(Pair<List<Long>, Map<Long, Double>> podCapacity
441441
return podIdsByCapacity;
442442
}
443443

444-
protected Pair<List<Long>, Map<Long, Double>> listClustersByCapacity(long id, int requiredCpu, long requiredRam, ExcludeList avoid, boolean isZone) {
444+
protected Pair<List<Long>, Map<Long, Double>> listClustersByCapacity(long id, long vmId, int requiredCpu, long requiredRam, ExcludeList avoid, boolean isZone) {
445445
//look at the aggregate available cpu and ram per cluster
446446
//although an aggregate value may be false indicator that a cluster can host a vm, it will at the least eliminate those clusters which definitely cannot
447447

@@ -456,11 +456,11 @@ protected Pair<List<Long>, Map<Long, Double>> listClustersByCapacity(long id, in
456456
capacityType = Capacity.CAPACITY_TYPE_MEMORY;
457457
}
458458

459-
List<Long> clusterIdswithEnoughCapacity = capacityDao.listClustersInZoneOrPodByHostCapacities(id, requiredCpu, requiredRam, capacityType, isZone);
459+
List<Long> clusterIdswithEnoughCapacity = capacityDao.listClustersInZoneOrPodByHostCapacities(id, vmId, requiredCpu, requiredRam, capacityType, isZone);
460460
if (s_logger.isTraceEnabled()) {
461461
s_logger.trace("ClusterId List having enough CPU and RAM capacity: " + clusterIdswithEnoughCapacity);
462462
}
463-
Pair<List<Long>, Map<Long, Double>> result = capacityDao.orderClustersByAggregateCapacity(id, capacityType, isZone);
463+
Pair<List<Long>, Map<Long, Double>> result = capacityDao.orderClustersByAggregateCapacity(id, vmId, capacityType, isZone);
464464
List<Long> clusterIdsOrderedByAggregateCapacity = result.first();
465465
//only keep the clusters that have enough capacity to host this VM
466466
if (s_logger.isTraceEnabled()) {

0 commit comments

Comments
 (0)