diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java index 68183ad2add..c192f876aac 100644 --- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java +++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java @@ -26,15 +26,18 @@ import org.apache.cloudstack.framework.config.ConfigKey; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.Network; import com.cloud.offering.DiskOffering; @@ -246,6 +249,10 @@ public interface VirtualMachineManager extends Manager { 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 */ diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index 1c77295a75a..4c498d114f9 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -135,6 +135,7 @@ import com.cloud.capacity.CapacityManager; import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterDetailsVO; +import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; @@ -250,6 +251,8 @@ import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; 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 { 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(); excludes.addHost(hostId); + DataCenterDeployment plan = getMigrationDeployment(vm, host, poolId, excludes); DeployDestination dest = null; 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); throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict"); } - - if (dest != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Found destination " + dest + " for migrating to."); - } - } else { - 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()); + if (dest == null) { + s_logger.warn("Unable to find destination for migrating the vm " + profile); + throw new InsufficientServerCapacityException("Unable to find a server to migrate to.", DataCenter.class, host.getDataCenterId()); + } + if (s_logger.isDebugEnabled()) { + s_logger.debug("Found destination " + dest + " for migrating to."); } 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 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 clustersToExclude = _clusterDao.listAllClusters(host.getDataCenterId()); + List 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 { @Override protected void runInContext() { diff --git a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java index f2735b819a9..dc04f76f2f2 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java +++ b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java @@ -27,7 +27,7 @@ import com.cloud.utils.db.GenericDao; public interface CapacityDao extends GenericDao { CapacityVO findByHostIdType(Long hostId, short capacityType); - List listClustersInZoneOrPodByHostCapacities(long id, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone); + List listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone); List listHostsWithEnoughCapacity(int requiredCpu, long requiredRam, Long clusterId, String hostType); @@ -37,7 +37,7 @@ public interface CapacityDao extends GenericDao { List findNonSharedStorageForClusterPodZone(Long zoneId, Long podId, Long clusterId); - Pair, Map> orderClustersByAggregateCapacity(long id, short capacityType, boolean isZone); + Pair, Map> orderClustersByAggregateCapacity(long id, long vmId, short capacityType, boolean isZone); List findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId); diff --git a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java index 72d5b070761..fe6f2f4ce7b 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java @@ -75,13 +75,24 @@ public class CapacityDaoImpl extends GenericDaoBase 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 ) >= ?)) "; 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 = - " 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 = - "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 = " 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 implements } @Override - public List listClustersInZoneOrPodByHostCapacities(long id, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone) { + public List listClustersInZoneOrPodByHostCapacities(long id, long vmId, int requiredCpu, long requiredRam, short capacityTypeForOrdering, boolean isZone) { TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; List result = new ArrayList(); @@ -854,7 +865,7 @@ public class CapacityDaoImpl extends GenericDaoBase implements } @Override - public Pair, Map> orderClustersByAggregateCapacity(long id, short capacityTypeForOrdering, boolean isZone) { + public Pair, Map> orderClustersByAggregateCapacity(long id, long vmId, short capacityTypeForOrdering, boolean isZone) { TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; List result = new ArrayList(); @@ -866,11 +877,14 @@ public class CapacityDaoImpl extends GenericDaoBase implements sql.append(ORDER_CLUSTERS_BY_AGGREGATE_OVERCOMMIT_CAPACITY_PART1); } + sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_JOIN_1); if (isZone) { - sql.append(" data_center_id = ?"); + sql.append("WHERE capacity.capacity_state = 'Enabled' AND capacity.data_center_id = ?"); } 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) { sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART2); } else { @@ -879,13 +893,14 @@ public class CapacityDaoImpl extends GenericDaoBase implements try { pstmt = txn.prepareAutoCloseStatement(sql.toString()); - pstmt.setLong(1, id); - pstmt.setShort(2, capacityTypeForOrdering); + pstmt.setLong(1, vmId); + pstmt.setLong(2, id); + pstmt.setShort(3, capacityTypeForOrdering); if (capacityTypeForOrdering == Capacity.CAPACITY_TYPE_CPU) { - pstmt.setString(3, "cpuOvercommitRatio"); + pstmt.setString(4, "cpuOvercommitRatio"); } else if (capacityTypeForOrdering == Capacity.CAPACITY_TYPE_MEMORY) { - pstmt.setString(3, "memoryOvercommitRatio"); + pstmt.setString(4, "memoryOvercommitRatio"); } ResultSet rs = pstmt.executeQuery(); diff --git a/plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java b/plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java index 1a3aed0ab61..a25ad9fd308 100644 --- a/plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java +++ b/plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java @@ -382,7 +382,7 @@ public class ImplicitPlannerTest { clustersWithEnoughCapacity.add(2L); clustersWithEnoughCapacity.add(3L); 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); Map clusterCapacityMap = new HashMap(); @@ -390,7 +390,7 @@ public class ImplicitPlannerTest { clusterCapacityMap.put(2L, 2048D); clusterCapacityMap.put(3L, 2048D); Pair, Map> clustersOrderedByCapacity = new Pair, Map>(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 disabledClusters = new ArrayList(); List clustersWithDisabledPods = new ArrayList(); diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index afabf1bfd9e..7c18c600b04 100755 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -439,6 +439,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati public static final ConfigKey VM_USERDATA_MAX_LENGTH = new ConfigKey("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); + public static final ConfigKey MIGRATE_VM_ACROSS_CLUSTERS = new ConfigKey(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_WRITE_RATE = "IOPS Write"; @@ -6535,6 +6537,6 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati public ConfigKey[] getConfigKeys() { 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, - VM_SERVICE_OFFERING_MAX_RAM_SIZE, VM_USERDATA_MAX_LENGTH}; + VM_SERVICE_OFFERING_MAX_RAM_SIZE, VM_USERDATA_MAX_LENGTH, MIGRATE_VM_ACROSS_CLUSTERS}; } } diff --git a/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java b/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java index f10dde6ac71..f5263e21045 100644 --- a/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java +++ b/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java @@ -383,7 +383,7 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla long requiredRam = offering.getRamSize() * 1024L * 1024L; //list clusters under this zone by cpu and ram capacity - Pair, Map> clusterCapacityInfo = listClustersByCapacity(id, requiredCpu, requiredRam, avoid, isZone); + Pair, Map> clusterCapacityInfo = listClustersByCapacity(id, vmProfile.getId(), requiredCpu, requiredRam, avoid, isZone); List prioritizedClusterIds = clusterCapacityInfo.first(); if (!prioritizedClusterIds.isEmpty()) { if (avoid.getClustersToAvoid() != null) { @@ -441,7 +441,7 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla return podIdsByCapacity; } - protected Pair, Map> listClustersByCapacity(long id, int requiredCpu, long requiredRam, ExcludeList avoid, boolean isZone) { + protected Pair, Map> listClustersByCapacity(long id, long vmId, int requiredCpu, long requiredRam, ExcludeList avoid, boolean isZone) { //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 @@ -456,11 +456,11 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla capacityType = Capacity.CAPACITY_TYPE_MEMORY; } - List clusterIdswithEnoughCapacity = capacityDao.listClustersInZoneOrPodByHostCapacities(id, requiredCpu, requiredRam, capacityType, isZone); + List clusterIdswithEnoughCapacity = capacityDao.listClustersInZoneOrPodByHostCapacities(id, vmId, requiredCpu, requiredRam, capacityType, isZone); if (s_logger.isTraceEnabled()) { s_logger.trace("ClusterId List having enough CPU and RAM capacity: " + clusterIdswithEnoughCapacity); } - Pair, Map> result = capacityDao.orderClustersByAggregateCapacity(id, capacityType, isZone); + Pair, Map> result = capacityDao.orderClustersByAggregateCapacity(id, vmId, capacityType, isZone); List clusterIdsOrderedByAggregateCapacity = result.first(); //only keep the clusters that have enough capacity to host this VM if (s_logger.isTraceEnabled()) { diff --git a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java index 01c1070db54..2b4e2334871 100755 --- a/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java +++ b/server/src/main/java/com/cloud/resource/ResourceManagerImpl.java @@ -16,6 +16,7 @@ // under the License. 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 java.net.URI; @@ -1315,7 +1316,14 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, return true; } - final List hosts = listAllUpAndEnabledHosts(Host.Type.Routing, host.getClusterId(), host.getPodId(), host.getDataCenterId()); + List 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) { if (hosts == null || hosts.isEmpty() || !answer.getMigrate() || _serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.vgpuType.toString()) != null) { @@ -1345,6 +1353,41 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager, return true; } + private boolean isClusterWideMigrationPossible(Host host, List vms, List 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. */ diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 31c138cd2ba..cac5bc9ebff 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -6025,9 +6025,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm, null, offering, null, null); final Long srcHostId = srcHost.getId(); final Host host = _hostDao.findById(srcHostId); - final DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, null, null); ExcludeList excludes = new ExcludeList(); excludes.addHost(srcHostId); + final DataCenterDeployment plan = _itMgr.getMigrationDeployment(vm, host, null, excludes); try { return _planningMgr.planDeployment(profile, plan, excludes, null); } catch (final AffinityConflictException e2) { diff --git a/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java b/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java index 41deea2823f..bfca1fe5d98 100644 --- a/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java +++ b/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java @@ -291,7 +291,7 @@ public class FirstFitPlannerTest { clustersWithEnoughCapacity.add(6L); 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); Map clusterCapacityMap = new HashMap(); @@ -303,7 +303,7 @@ public class FirstFitPlannerTest { clusterCapacityMap.put(6L, 2048D); Pair, Map> clustersOrderedByCapacity = new Pair, Map>(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 disabledClusters = new ArrayList(); List clustersWithDisabledPods = new ArrayList();