mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
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>
This commit is contained in:
parent
c0ecc34f4c
commit
a755ecfce8
@ -26,15 +26,18 @@ import org.apache.cloudstack.framework.config.ConfigKey;
|
|||||||
|
|
||||||
import com.cloud.agent.api.to.NicTO;
|
import com.cloud.agent.api.to.NicTO;
|
||||||
import com.cloud.agent.api.to.VirtualMachineTO;
|
import com.cloud.agent.api.to.VirtualMachineTO;
|
||||||
|
import com.cloud.deploy.DataCenterDeployment;
|
||||||
import com.cloud.deploy.DeployDestination;
|
import com.cloud.deploy.DeployDestination;
|
||||||
import com.cloud.deploy.DeploymentPlan;
|
import com.cloud.deploy.DeploymentPlan;
|
||||||
import com.cloud.deploy.DeploymentPlanner;
|
import com.cloud.deploy.DeploymentPlanner;
|
||||||
|
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
|
||||||
import com.cloud.exception.AgentUnavailableException;
|
import com.cloud.exception.AgentUnavailableException;
|
||||||
import com.cloud.exception.ConcurrentOperationException;
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
import com.cloud.exception.InsufficientCapacityException;
|
import com.cloud.exception.InsufficientCapacityException;
|
||||||
import com.cloud.exception.InsufficientServerCapacityException;
|
import com.cloud.exception.InsufficientServerCapacityException;
|
||||||
import com.cloud.exception.OperationTimedoutException;
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
import com.cloud.exception.ResourceUnavailableException;
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.host.Host;
|
||||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||||
import com.cloud.network.Network;
|
import com.cloud.network.Network;
|
||||||
import com.cloud.offering.DiskOffering;
|
import com.cloud.offering.DiskOffering;
|
||||||
@ -246,6 +249,10 @@ public interface VirtualMachineManager extends Manager {
|
|||||||
|
|
||||||
UserVm restoreVirtualMachine(long vmId, Long newTemplateId) throws ResourceUnavailableException, InsufficientCapacityException;
|
UserVm restoreVirtualMachine(long vmId, Long newTemplateId) throws ResourceUnavailableException, InsufficientCapacityException;
|
||||||
|
|
||||||
|
boolean checkIfVmHasClusterWideVolumes(Long vmId);
|
||||||
|
|
||||||
|
DataCenterDeployment getMigrationDeployment(VirtualMachine vm, Host host, Long poolId, ExcludeList excludes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the VM's Root volume is allocated at a local storage pool
|
* Returns true if the VM's Root volume is allocated at a local storage pool
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -135,6 +135,7 @@ import com.cloud.capacity.CapacityManager;
|
|||||||
import com.cloud.configuration.Resource.ResourceType;
|
import com.cloud.configuration.Resource.ResourceType;
|
||||||
import com.cloud.dc.ClusterDetailsDao;
|
import com.cloud.dc.ClusterDetailsDao;
|
||||||
import com.cloud.dc.ClusterDetailsVO;
|
import com.cloud.dc.ClusterDetailsVO;
|
||||||
|
import com.cloud.dc.ClusterVO;
|
||||||
import com.cloud.dc.DataCenter;
|
import com.cloud.dc.DataCenter;
|
||||||
import com.cloud.dc.DataCenterVO;
|
import com.cloud.dc.DataCenterVO;
|
||||||
import com.cloud.dc.HostPodVO;
|
import com.cloud.dc.HostPodVO;
|
||||||
@ -250,6 +251,8 @@ import com.cloud.vm.snapshot.VMSnapshotVO;
|
|||||||
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
|
import static com.cloud.configuration.ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS;
|
||||||
|
|
||||||
public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, VmWorkJobHandler, Listener, Configurable {
|
public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, VmWorkJobHandler, Listener, Configurable {
|
||||||
private static final Logger s_logger = Logger.getLogger(VirtualMachineManagerImpl.class);
|
private static final Logger s_logger = Logger.getLogger(VirtualMachineManagerImpl.class);
|
||||||
|
|
||||||
@ -3368,9 +3371,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, poolId, null);
|
|
||||||
final ExcludeList excludes = new ExcludeList();
|
final ExcludeList excludes = new ExcludeList();
|
||||||
excludes.addHost(hostId);
|
excludes.addHost(hostId);
|
||||||
|
DataCenterDeployment plan = getMigrationDeployment(vm, host, poolId, excludes);
|
||||||
|
|
||||||
DeployDestination dest = null;
|
DeployDestination dest = null;
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -3382,16 +3385,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||||||
s_logger.warn("Unable to create deployment, affinity rules associted to the VM conflict", e2);
|
s_logger.warn("Unable to create deployment, affinity rules associted to the VM conflict", e2);
|
||||||
throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict");
|
throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict");
|
||||||
}
|
}
|
||||||
|
if (dest == null) {
|
||||||
if (dest != null) {
|
s_logger.warn("Unable to find destination for migrating the vm " + profile);
|
||||||
if (s_logger.isDebugEnabled()) {
|
throw new InsufficientServerCapacityException("Unable to find a server to migrate to.", DataCenter.class, host.getDataCenterId());
|
||||||
s_logger.debug("Found destination " + dest + " for migrating to.");
|
}
|
||||||
}
|
if (s_logger.isDebugEnabled()) {
|
||||||
} else {
|
s_logger.debug("Found destination " + dest + " for migrating to.");
|
||||||
if (s_logger.isDebugEnabled()) {
|
|
||||||
s_logger.debug("Unable to find destination for migrating the vm " + profile);
|
|
||||||
}
|
|
||||||
throw new InsufficientServerCapacityException("Unable to find a server to migrate to.", host.getClusterId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
excludes.addHost(dest.getHost().getId());
|
excludes.addHost(dest.getHost().getId());
|
||||||
@ -3420,6 +3419,41 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the virtual machine has any volume in cluster-wide pool
|
||||||
|
* @param vmId id of the virtual machine
|
||||||
|
* @return true if volume exists on cluster-wide pool else false
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean checkIfVmHasClusterWideVolumes(Long vmId) {
|
||||||
|
final List<VolumeVO> volumesList = _volsDao.findCreatedByInstance(vmId);
|
||||||
|
|
||||||
|
return volumesList.parallelStream()
|
||||||
|
.anyMatch(vol -> _storagePoolDao.findById(vol.getPoolId()).getScope().equals(ScopeType.CLUSTER));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DataCenterDeployment getMigrationDeployment(final VirtualMachine vm, final Host host, final Long poolId, final ExcludeList excludes) {
|
||||||
|
if (MIGRATE_VM_ACROSS_CLUSTERS.valueIn(host.getDataCenterId()) &&
|
||||||
|
(HypervisorType.VMware.equals(host.getHypervisorType()) || !checkIfVmHasClusterWideVolumes(vm.getId()))) {
|
||||||
|
s_logger.info("Searching for hosts in the zone for vm migration");
|
||||||
|
List<Long> clustersToExclude = _clusterDao.listAllClusters(host.getDataCenterId());
|
||||||
|
List<ClusterVO> clusterList = _clusterDao.listByDcHyType(host.getDataCenterId(), host.getHypervisorType().toString());
|
||||||
|
for (ClusterVO cluster : clusterList) {
|
||||||
|
clustersToExclude.remove(cluster.getId());
|
||||||
|
}
|
||||||
|
for (Long clusterId : clustersToExclude) {
|
||||||
|
excludes.addCluster(clusterId);
|
||||||
|
}
|
||||||
|
if (VirtualMachine.systemVMs.contains(vm.getType())) {
|
||||||
|
return new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), null, null, poolId, null);
|
||||||
|
}
|
||||||
|
return new DataCenterDeployment(host.getDataCenterId(), null, null, null, poolId, null);
|
||||||
|
}
|
||||||
|
return new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, poolId, null);
|
||||||
|
}
|
||||||
|
|
||||||
protected class CleanupTask extends ManagedContextRunnable {
|
protected class CleanupTask extends ManagedContextRunnable {
|
||||||
@Override
|
@Override
|
||||||
protected void runInContext() {
|
protected void runInContext() {
|
||||||
|
|||||||
@ -27,7 +27,7 @@ import com.cloud.utils.db.GenericDao;
|
|||||||
public interface CapacityDao extends GenericDao<CapacityVO, Long> {
|
public interface CapacityDao extends GenericDao<CapacityVO, Long> {
|
||||||
CapacityVO findByHostIdType(Long hostId, short capacityType);
|
CapacityVO findByHostIdType(Long hostId, short capacityType);
|
||||||
|
|
||||||
List<Long> listClustersInZoneOrPodByHostCapacities(long id, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone);
|
List<Long> listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone);
|
||||||
|
|
||||||
List<Long> listHostsWithEnoughCapacity(int requiredCpu, long requiredRam, Long clusterId, String hostType);
|
List<Long> listHostsWithEnoughCapacity(int requiredCpu, long requiredRam, Long clusterId, String hostType);
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ public interface CapacityDao extends GenericDao<CapacityVO, Long> {
|
|||||||
|
|
||||||
List<SummedCapacity> findNonSharedStorageForClusterPodZone(Long zoneId, Long podId, Long clusterId);
|
List<SummedCapacity> findNonSharedStorageForClusterPodZone(Long zoneId, Long podId, Long clusterId);
|
||||||
|
|
||||||
Pair<List<Long>, Map<Long, Double>> orderClustersByAggregateCapacity(long id, short capacityType, boolean isZone);
|
Pair<List<Long>, Map<Long, Double>> orderClustersByAggregateCapacity(long id, long vmId, short capacityType, boolean isZone);
|
||||||
|
|
||||||
List<SummedCapacity> findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId);
|
List<SummedCapacity> findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId);
|
||||||
|
|
||||||
|
|||||||
@ -75,13 +75,24 @@ public class CapacityDaoImpl extends GenericDaoBase<CapacityVO, Long> implements
|
|||||||
+ " 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 ) >= ?)) ";
|
+ " 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 ) >= ?)) ";
|
||||||
|
|
||||||
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART1 =
|
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART1 =
|
||||||
"SELECT capacity.cluster_id, SUM(used_capacity+reserved_capacity)/SUM(total_capacity ) FROM `cloud`.`op_host_capacity` capacity WHERE ";
|
"SELECT capacity.cluster_id, SUM(used_capacity+reserved_capacity)/SUM(total_capacity ) FROM `cloud`.`op_host_capacity` capacity ";
|
||||||
|
|
||||||
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART2 =
|
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART2 =
|
||||||
" 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";
|
" AND capacity_type = ? GROUP BY capacity.cluster_id ORDER BY SUM(used_capacity+reserved_capacity)/SUM(total_capacity) ASC";
|
||||||
|
|
||||||
|
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_JOIN_1 =
|
||||||
|
"JOIN host ON capacity.host_id = host.id " +
|
||||||
|
"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 = ? " +
|
||||||
|
"LEFT JOIN dedicated_resources dr_pod ON dr_pod.pod_id IS NOT NULL AND dr_pod.pod_id = host.pod_id " +
|
||||||
|
"LEFT JOIN dedicated_resources dr_cluster ON dr_cluster.cluster_id IS NOT NULL AND dr_cluster.cluster_id = host.cluster_id " +
|
||||||
|
"LEFT JOIN dedicated_resources dr_host ON dr_host.host_id IS NOT NULL AND dr_host.host_id = host.id ";
|
||||||
|
|
||||||
|
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_JOIN_2 =
|
||||||
|
" 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 " +
|
||||||
|
"(dr_pod.affinity_group_id = ag.id OR dr_cluster.affinity_group_id = ag.id OR dr_host.affinity_group_id = ag.id))";
|
||||||
|
|
||||||
private static final String ORDER_CLUSTERS_BY_AGGREGATE_OVERCOMMIT_CAPACITY_PART1 =
|
private static final String ORDER_CLUSTERS_BY_AGGREGATE_OVERCOMMIT_CAPACITY_PART1 =
|
||||||
"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 ";
|
"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) ";
|
||||||
|
|
||||||
private static final String ORDER_CLUSTERS_BY_AGGREGATE_OVERCOMMIT_CAPACITY_PART2 =
|
private static final String ORDER_CLUSTERS_BY_AGGREGATE_OVERCOMMIT_CAPACITY_PART2 =
|
||||||
" 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";
|
" 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 class CapacityDaoImpl extends GenericDaoBase<CapacityVO, Long> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Long> listClustersInZoneOrPodByHostCapacities(long id, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone) {
|
public List<Long> listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone) {
|
||||||
TransactionLegacy txn = TransactionLegacy.currentTxn();
|
TransactionLegacy txn = TransactionLegacy.currentTxn();
|
||||||
PreparedStatement pstmt = null;
|
PreparedStatement pstmt = null;
|
||||||
List<Long> result = new ArrayList<Long>();
|
List<Long> result = new ArrayList<Long>();
|
||||||
@ -854,7 +865,7 @@ public class CapacityDaoImpl extends GenericDaoBase<CapacityVO, Long> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Pair<List<Long>, Map<Long, Double>> orderClustersByAggregateCapacity(long id, short capacityTypeForOrdering, boolean isZone) {
|
public Pair<List<Long>, Map<Long, Double>> orderClustersByAggregateCapacity(long id, long vmId, short capacityTypeForOrdering, boolean isZone) {
|
||||||
TransactionLegacy txn = TransactionLegacy.currentTxn();
|
TransactionLegacy txn = TransactionLegacy.currentTxn();
|
||||||
PreparedStatement pstmt = null;
|
PreparedStatement pstmt = null;
|
||||||
List<Long> result = new ArrayList<Long>();
|
List<Long> result = new ArrayList<Long>();
|
||||||
@ -866,11 +877,14 @@ public class CapacityDaoImpl extends GenericDaoBase<CapacityVO, Long> implements
|
|||||||
sql.append(ORDER_CLUSTERS_BY_AGGREGATE_OVERCOMMIT_CAPACITY_PART1);
|
sql.append(ORDER_CLUSTERS_BY_AGGREGATE_OVERCOMMIT_CAPACITY_PART1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_JOIN_1);
|
||||||
if (isZone) {
|
if (isZone) {
|
||||||
sql.append(" data_center_id = ?");
|
sql.append("WHERE capacity.capacity_state = 'Enabled' AND capacity.data_center_id = ?");
|
||||||
} else {
|
} else {
|
||||||
sql.append(" pod_id = ?");
|
sql.append("WHERE capacity.capacity_state = 'Enabled' AND capacity.pod_id = ?");
|
||||||
}
|
}
|
||||||
|
sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_JOIN_2);
|
||||||
|
|
||||||
if (capacityTypeForOrdering != Capacity.CAPACITY_TYPE_CPU && capacityTypeForOrdering != Capacity.CAPACITY_TYPE_MEMORY) {
|
if (capacityTypeForOrdering != Capacity.CAPACITY_TYPE_CPU && capacityTypeForOrdering != Capacity.CAPACITY_TYPE_MEMORY) {
|
||||||
sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART2);
|
sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART2);
|
||||||
} else {
|
} else {
|
||||||
@ -879,13 +893,14 @@ public class CapacityDaoImpl extends GenericDaoBase<CapacityVO, Long> implements
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
pstmt = txn.prepareAutoCloseStatement(sql.toString());
|
pstmt = txn.prepareAutoCloseStatement(sql.toString());
|
||||||
pstmt.setLong(1, id);
|
pstmt.setLong(1, vmId);
|
||||||
pstmt.setShort(2, capacityTypeForOrdering);
|
pstmt.setLong(2, id);
|
||||||
|
pstmt.setShort(3, capacityTypeForOrdering);
|
||||||
|
|
||||||
if (capacityTypeForOrdering == Capacity.CAPACITY_TYPE_CPU) {
|
if (capacityTypeForOrdering == Capacity.CAPACITY_TYPE_CPU) {
|
||||||
pstmt.setString(3, "cpuOvercommitRatio");
|
pstmt.setString(4, "cpuOvercommitRatio");
|
||||||
} else if (capacityTypeForOrdering == Capacity.CAPACITY_TYPE_MEMORY) {
|
} else if (capacityTypeForOrdering == Capacity.CAPACITY_TYPE_MEMORY) {
|
||||||
pstmt.setString(3, "memoryOvercommitRatio");
|
pstmt.setString(4, "memoryOvercommitRatio");
|
||||||
}
|
}
|
||||||
|
|
||||||
ResultSet rs = pstmt.executeQuery();
|
ResultSet rs = pstmt.executeQuery();
|
||||||
|
|||||||
@ -382,7 +382,7 @@ public class ImplicitPlannerTest {
|
|||||||
clustersWithEnoughCapacity.add(2L);
|
clustersWithEnoughCapacity.add(2L);
|
||||||
clustersWithEnoughCapacity.add(3L);
|
clustersWithEnoughCapacity.add(3L);
|
||||||
when(
|
when(
|
||||||
capacityDao.listClustersInZoneOrPodByHostCapacities(dataCenterId, noOfCpusInOffering * cpuSpeedInOffering, ramInOffering * 1024L * 1024L,
|
capacityDao.listClustersInZoneOrPodByHostCapacities(dataCenterId, 12L, noOfCpusInOffering * cpuSpeedInOffering, ramInOffering * 1024L * 1024L,
|
||||||
Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersWithEnoughCapacity);
|
Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersWithEnoughCapacity);
|
||||||
|
|
||||||
Map<Long, Double> clusterCapacityMap = new HashMap<Long, Double>();
|
Map<Long, Double> clusterCapacityMap = new HashMap<Long, Double>();
|
||||||
@ -390,7 +390,7 @@ public class ImplicitPlannerTest {
|
|||||||
clusterCapacityMap.put(2L, 2048D);
|
clusterCapacityMap.put(2L, 2048D);
|
||||||
clusterCapacityMap.put(3L, 2048D);
|
clusterCapacityMap.put(3L, 2048D);
|
||||||
Pair<List<Long>, Map<Long, Double>> clustersOrderedByCapacity = new Pair<List<Long>, Map<Long, Double>>(clustersWithEnoughCapacity, clusterCapacityMap);
|
Pair<List<Long>, Map<Long, Double>> clustersOrderedByCapacity = new Pair<List<Long>, Map<Long, Double>>(clustersWithEnoughCapacity, clusterCapacityMap);
|
||||||
when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersOrderedByCapacity);
|
when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, 12L, Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersOrderedByCapacity);
|
||||||
|
|
||||||
List<Long> disabledClusters = new ArrayList<Long>();
|
List<Long> disabledClusters = new ArrayList<Long>();
|
||||||
List<Long> clustersWithDisabledPods = new ArrayList<Long>();
|
List<Long> clustersWithDisabledPods = new ArrayList<Long>();
|
||||||
|
|||||||
@ -439,6 +439,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
|
|
||||||
public static final ConfigKey<Integer> VM_USERDATA_MAX_LENGTH = new ConfigKey<Integer>("Advanced", Integer.class, VM_USERDATA_MAX_LENGTH_STRING, "32768",
|
public static final ConfigKey<Integer> VM_USERDATA_MAX_LENGTH = new ConfigKey<Integer>("Advanced", Integer.class, VM_USERDATA_MAX_LENGTH_STRING, "32768",
|
||||||
"Max length of vm userdata after base64 decoding. Default is 32768 and maximum is 1048576", true);
|
"Max length of vm userdata after base64 decoding. Default is 32768 and maximum is 1048576", true);
|
||||||
|
public static final ConfigKey<Boolean> MIGRATE_VM_ACROSS_CLUSTERS = new ConfigKey<Boolean>(Boolean.class, "migrate.vm.across.clusters", "Advanced", "false",
|
||||||
|
"Indicates whether the VM can be migrated to different cluster if no host is found in same cluster",true, ConfigKey.Scope.Zone, null);
|
||||||
|
|
||||||
private static final String IOPS_READ_RATE = "IOPS Read";
|
private static final String IOPS_READ_RATE = "IOPS Read";
|
||||||
private static final String IOPS_WRITE_RATE = "IOPS Write";
|
private static final String IOPS_WRITE_RATE = "IOPS Write";
|
||||||
@ -6535,6 +6537,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
|||||||
public ConfigKey<?>[] getConfigKeys() {
|
public ConfigKey<?>[] getConfigKeys() {
|
||||||
return new ConfigKey<?>[] {SystemVMUseLocalStorage, IOPS_MAX_READ_LENGTH, IOPS_MAX_WRITE_LENGTH,
|
return new ConfigKey<?>[] {SystemVMUseLocalStorage, IOPS_MAX_READ_LENGTH, IOPS_MAX_WRITE_LENGTH,
|
||||||
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,
|
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,
|
||||||
VM_SERVICE_OFFERING_MAX_RAM_SIZE, VM_USERDATA_MAX_LENGTH};
|
VM_SERVICE_OFFERING_MAX_RAM_SIZE, VM_USERDATA_MAX_LENGTH, MIGRATE_VM_ACROSS_CLUSTERS};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -383,7 +383,7 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla
|
|||||||
long requiredRam = offering.getRamSize() * 1024L * 1024L;
|
long requiredRam = offering.getRamSize() * 1024L * 1024L;
|
||||||
|
|
||||||
//list clusters under this zone by cpu and ram capacity
|
//list clusters under this zone by cpu and ram capacity
|
||||||
Pair<List<Long>, Map<Long, Double>> clusterCapacityInfo = listClustersByCapacity(id, requiredCpu, requiredRam, avoid, isZone);
|
Pair<List<Long>, Map<Long, Double>> clusterCapacityInfo = listClustersByCapacity(id, vmProfile.getId(), requiredCpu, requiredRam, avoid, isZone);
|
||||||
List<Long> prioritizedClusterIds = clusterCapacityInfo.first();
|
List<Long> prioritizedClusterIds = clusterCapacityInfo.first();
|
||||||
if (!prioritizedClusterIds.isEmpty()) {
|
if (!prioritizedClusterIds.isEmpty()) {
|
||||||
if (avoid.getClustersToAvoid() != null) {
|
if (avoid.getClustersToAvoid() != null) {
|
||||||
@ -441,7 +441,7 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla
|
|||||||
return podIdsByCapacity;
|
return podIdsByCapacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Pair<List<Long>, Map<Long, Double>> listClustersByCapacity(long id, int requiredCpu, long requiredRam, ExcludeList avoid, boolean isZone) {
|
protected Pair<List<Long>, Map<Long, Double>> listClustersByCapacity(long id, long vmId, int requiredCpu, long requiredRam, ExcludeList avoid, boolean isZone) {
|
||||||
//look at the aggregate available cpu and ram per cluster
|
//look at the aggregate available cpu and ram per cluster
|
||||||
//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
|
//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
|
||||||
|
|
||||||
@ -456,11 +456,11 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla
|
|||||||
capacityType = Capacity.CAPACITY_TYPE_MEMORY;
|
capacityType = Capacity.CAPACITY_TYPE_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Long> clusterIdswithEnoughCapacity = capacityDao.listClustersInZoneOrPodByHostCapacities(id, requiredCpu, requiredRam, capacityType, isZone);
|
List<Long> clusterIdswithEnoughCapacity = capacityDao.listClustersInZoneOrPodByHostCapacities(id, vmId, requiredCpu, requiredRam, capacityType, isZone);
|
||||||
if (s_logger.isTraceEnabled()) {
|
if (s_logger.isTraceEnabled()) {
|
||||||
s_logger.trace("ClusterId List having enough CPU and RAM capacity: " + clusterIdswithEnoughCapacity);
|
s_logger.trace("ClusterId List having enough CPU and RAM capacity: " + clusterIdswithEnoughCapacity);
|
||||||
}
|
}
|
||||||
Pair<List<Long>, Map<Long, Double>> result = capacityDao.orderClustersByAggregateCapacity(id, capacityType, isZone);
|
Pair<List<Long>, Map<Long, Double>> result = capacityDao.orderClustersByAggregateCapacity(id, vmId, capacityType, isZone);
|
||||||
List<Long> clusterIdsOrderedByAggregateCapacity = result.first();
|
List<Long> clusterIdsOrderedByAggregateCapacity = result.first();
|
||||||
//only keep the clusters that have enough capacity to host this VM
|
//only keep the clusters that have enough capacity to host this VM
|
||||||
if (s_logger.isTraceEnabled()) {
|
if (s_logger.isTraceEnabled()) {
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.resource;
|
package com.cloud.resource;
|
||||||
|
|
||||||
|
import static com.cloud.configuration.ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS;
|
||||||
import static com.cloud.configuration.ConfigurationManagerImpl.SET_HOST_DOWN_TO_MAINTENANCE;
|
import static com.cloud.configuration.ConfigurationManagerImpl.SET_HOST_DOWN_TO_MAINTENANCE;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@ -1315,7 +1316,14 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<HostVO> hosts = listAllUpAndEnabledHosts(Host.Type.Routing, host.getClusterId(), host.getPodId(), host.getDataCenterId());
|
List<HostVO> hosts = listAllUpAndEnabledHosts(Host.Type.Routing, host.getClusterId(), host.getPodId(), host.getDataCenterId());
|
||||||
|
if (CollectionUtils.isEmpty(hosts)) {
|
||||||
|
s_logger.warn("Unable to find a host for vm migration in cluster: " + host.getClusterId());
|
||||||
|
if (! isClusterWideMigrationPossible(host, vms, hosts)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (final VMInstanceVO vm : vms) {
|
for (final VMInstanceVO vm : vms) {
|
||||||
if (hosts == null || hosts.isEmpty() || !answer.getMigrate()
|
if (hosts == null || hosts.isEmpty() || !answer.getMigrate()
|
||||||
|| _serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.vgpuType.toString()) != null) {
|
|| _serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.vgpuType.toString()) != null) {
|
||||||
@ -1345,6 +1353,41 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isClusterWideMigrationPossible(Host host, List<VMInstanceVO> vms, List<HostVO> hosts) {
|
||||||
|
if (MIGRATE_VM_ACROSS_CLUSTERS.valueIn(host.getDataCenterId())) {
|
||||||
|
s_logger.info("Looking for hosts across different clusters in zone: " + host.getDataCenterId());
|
||||||
|
Long podId = null;
|
||||||
|
for (final VMInstanceVO vm : vms) {
|
||||||
|
if (VirtualMachine.systemVMs.contains(vm.getType())) {
|
||||||
|
// SystemVMs can only be migrated to same pod
|
||||||
|
podId = host.getPodId();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hosts.addAll(listAllUpAndEnabledHosts(Host.Type.Routing, null, podId, host.getDataCenterId()));
|
||||||
|
if (CollectionUtils.isEmpty(hosts)) {
|
||||||
|
s_logger.warn("Unable to find a host for vm migration in zone: " + host.getDataCenterId());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
s_logger.info("Found hosts in the zone for vm migration: " + hosts);
|
||||||
|
if (HypervisorType.VMware.equals(host.getHypervisorType())) {
|
||||||
|
s_logger.debug("Skipping pool check of volumes on VMware environment because across-cluster vm migration is supported by vMotion");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Don't migrate vm if it has volumes on cluster-wide pool
|
||||||
|
for (final VMInstanceVO vm : vms) {
|
||||||
|
if (_vmMgr.checkIfVmHasClusterWideVolumes(vm.getId())) {
|
||||||
|
s_logger.warn(String.format("VM %s cannot be migrated across cluster as it has volumes on cluster-wide pool", vm));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_logger.warn(String.format("VMs cannot be migrated across cluster since %s is false for zone ID: %d", MIGRATE_VM_ACROSS_CLUSTERS.key(), host.getDataCenterId()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks for Hosts able to allocate the VM and migrates the VM with its volume.
|
* Looks for Hosts able to allocate the VM and migrates the VM with its volume.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -6025,9 +6025,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
|||||||
final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm, null, offering, null, null);
|
final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm, null, offering, null, null);
|
||||||
final Long srcHostId = srcHost.getId();
|
final Long srcHostId = srcHost.getId();
|
||||||
final Host host = _hostDao.findById(srcHostId);
|
final Host host = _hostDao.findById(srcHostId);
|
||||||
final DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, null, null);
|
|
||||||
ExcludeList excludes = new ExcludeList();
|
ExcludeList excludes = new ExcludeList();
|
||||||
excludes.addHost(srcHostId);
|
excludes.addHost(srcHostId);
|
||||||
|
final DataCenterDeployment plan = _itMgr.getMigrationDeployment(vm, host, null, excludes);
|
||||||
try {
|
try {
|
||||||
return _planningMgr.planDeployment(profile, plan, excludes, null);
|
return _planningMgr.planDeployment(profile, plan, excludes, null);
|
||||||
} catch (final AffinityConflictException e2) {
|
} catch (final AffinityConflictException e2) {
|
||||||
|
|||||||
@ -291,7 +291,7 @@ public class FirstFitPlannerTest {
|
|||||||
clustersWithEnoughCapacity.add(6L);
|
clustersWithEnoughCapacity.add(6L);
|
||||||
|
|
||||||
when(
|
when(
|
||||||
capacityDao.listClustersInZoneOrPodByHostCapacities(dataCenterId, noOfCpusInOffering * cpuSpeedInOffering, ramInOffering * 1024L * 1024L,
|
capacityDao.listClustersInZoneOrPodByHostCapacities(dataCenterId, 12L, noOfCpusInOffering * cpuSpeedInOffering, ramInOffering * 1024L * 1024L,
|
||||||
Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersWithEnoughCapacity);
|
Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersWithEnoughCapacity);
|
||||||
|
|
||||||
Map<Long, Double> clusterCapacityMap = new HashMap<Long, Double>();
|
Map<Long, Double> clusterCapacityMap = new HashMap<Long, Double>();
|
||||||
@ -303,7 +303,7 @@ public class FirstFitPlannerTest {
|
|||||||
clusterCapacityMap.put(6L, 2048D);
|
clusterCapacityMap.put(6L, 2048D);
|
||||||
|
|
||||||
Pair<List<Long>, Map<Long, Double>> clustersOrderedByCapacity = new Pair<List<Long>, Map<Long, Double>>(clustersWithEnoughCapacity, clusterCapacityMap);
|
Pair<List<Long>, Map<Long, Double>> clustersOrderedByCapacity = new Pair<List<Long>, Map<Long, Double>>(clustersWithEnoughCapacity, clusterCapacityMap);
|
||||||
when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersOrderedByCapacity);
|
when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, 12L, Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersOrderedByCapacity);
|
||||||
|
|
||||||
List<Long> disabledClusters = new ArrayList<Long>();
|
List<Long> disabledClusters = new ArrayList<Long>();
|
||||||
List<Long> clustersWithDisabledPods = new ArrayList<Long>();
|
List<Long> clustersWithDisabledPods = new ArrayList<Long>();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user