diff --git a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java index 96cf333cb19..b9a45f0dcdc 100644 --- a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java +++ b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java @@ -19,7 +19,6 @@ package com.cloud.storage; import java.math.BigDecimal; import java.util.List; -import com.cloud.agent.api.ModifyStoragePoolAnswer; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener; import org.apache.cloudstack.framework.config.ConfigKey; @@ -27,6 +26,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import com.cloud.agent.api.Answer; import com.cloud.agent.api.Command; +import com.cloud.agent.api.ModifyStoragePoolAnswer; import com.cloud.agent.api.StoragePoolInfo; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DiskTO; @@ -252,7 +252,7 @@ public interface StorageManager extends StorageService { boolean storagePoolCompatibleWithVolumePool(StoragePool pool, Volume volume); - boolean isStoragePoolComplaintWithStoragePolicy(List volumes, StoragePool pool) throws StorageUnavailableException; + boolean isStoragePoolCompliantWithStoragePolicy(List volumes, StoragePool pool) throws StorageUnavailableException; boolean registerHostListener(String providerUuid, HypervisorHostListener listener); diff --git a/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDao.java b/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDao.java index 45c88060496..9255fae0c9e 100644 --- a/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDao.java +++ b/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDao.java @@ -38,5 +38,5 @@ public interface HypervisorCapabilitiesDao extends GenericDao getHypervisorsWithDefaultEntries(); - Boolean isStorageMotionSupported(HypervisorType hypervisorType, String hypervisorVersion); + boolean isStorageMotionSupported(HypervisorType hypervisorType, String hypervisorVersion); } diff --git a/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java b/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java index 09b39749ec2..35f765b554a 100644 --- a/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/hypervisor/dao/HypervisorCapabilitiesDaoImpl.java @@ -121,7 +121,7 @@ public class HypervisorCapabilitiesDaoImpl extends GenericDaoBase hypervisorCapabilitiesList = listAllByHypervisorType(HypervisorType.KVM); diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java index 7de40e924de..10d39ee88fa 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java @@ -241,7 +241,7 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement } try { - boolean isStoragePoolStoragepolicyComplaince = storageMgr.isStoragePoolComplaintWithStoragePolicy(requestVolumes, pool); + boolean isStoragePoolStoragepolicyComplaince = storageMgr.isStoragePoolCompliantWithStoragePolicy(requestVolumes, pool); if (!isStoragePoolStoragepolicyComplaince) { return false; } diff --git a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java index 7a939ae263e..afcaacf0143 100644 --- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -1411,7 +1411,7 @@ StateListener, Configurable { hostCanAccessPool = true; if (potentialHost.getHypervisorType() == HypervisorType.VMware) { try { - boolean isStoragePoolStoragepolicyComplaince = _storageMgr.isStoragePoolComplaintWithStoragePolicy(allVolumes, storagePool); + boolean isStoragePoolStoragepolicyComplaince = _storageMgr.isStoragePoolCompliantWithStoragePolicy(allVolumes, storagePool); if (!isStoragePoolStoragepolicyComplaince) { continue; } @@ -1450,7 +1450,7 @@ StateListener, Configurable { if (potentialHost.getHypervisorType() == HypervisorType.VMware) { try { - boolean isStoragePoolStoragepolicyComplaince = _storageMgr.isStoragePoolComplaintWithStoragePolicy(requestVolumes, potentialSPool); + boolean isStoragePoolStoragepolicyComplaince = _storageMgr.isStoragePoolCompliantWithStoragePolicy(requestVolumes, potentialSPool); if (!isStoragePoolStoragepolicyComplaince) { continue; } diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 9ae7ddfd207..1d8c74053b1 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -1291,7 +1291,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe boolean canMigrateWithStorage = false; if (VirtualMachine.Type.User.equals(vm.getType()) || HypervisorType.VMware.equals(vm.getHypervisorType())) { - canMigrateWithStorage = Boolean.TRUE.equals(_hypervisorCapabilitiesDao.isStorageMotionSupported(srcHost.getHypervisorType(), srcHostVersion)); + canMigrateWithStorage = _hypervisorCapabilitiesDao.isStorageMotionSupported(srcHost.getHypervisorType(), srcHostVersion); } // Check if the vm is using any disks on local storage. @@ -1345,11 +1345,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe // source volume. iterator.remove(); } else { - boolean hostSupportsStorageMigration = false; - if ((srcHostVersion != null && srcHostVersion.equals(hostVersion)) || - Boolean.TRUE.equals(_hypervisorCapabilitiesDao.isStorageMotionSupported(host.getHypervisorType(), hostVersion))) { - hostSupportsStorageMigration = true; - } + boolean hostSupportsStorageMigration = (srcHostVersion != null && srcHostVersion.equals(hostVersion)) || + _hypervisorCapabilitiesDao.isStorageMotionSupported(host.getHypervisorType(), hostVersion); if (hostSupportsStorageMigration && hasSuitablePoolsForVolume(volume, host, vmProfile)) { requiresStorageMotion.put(host, true); } else { diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index 7bf69244252..743e90f4eae 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.storage; +import static com.cloud.utils.NumbersUtil.toHumanReadableSize; + import java.math.BigDecimal; import java.net.URI; import java.net.URISyntaxException; @@ -29,11 +31,11 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; -import java.util.LinkedHashSet; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -41,11 +43,6 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; -import com.cloud.agent.api.to.StorageFilerTO; -import com.cloud.dc.VsphereStoragePolicyVO; -import com.cloud.dc.dao.VsphereStoragePolicyDao; -import com.cloud.service.dao.ServiceOfferingDetailsDao; -import com.cloud.utils.StringUtils; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.admin.storage.CancelPrimaryStorageMaintenanceCmd; import org.apache.cloudstack.api.command.admin.storage.CreateSecondaryStagingStoreCmd; @@ -53,8 +50,8 @@ import org.apache.cloudstack.api.command.admin.storage.CreateStoragePoolCmd; import org.apache.cloudstack.api.command.admin.storage.DeleteImageStoreCmd; import org.apache.cloudstack.api.command.admin.storage.DeletePoolCmd; import org.apache.cloudstack.api.command.admin.storage.DeleteSecondaryStagingStoreCmd; -import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd; import org.apache.cloudstack.api.command.admin.storage.SyncStoragePoolCmd; +import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; @@ -121,12 +118,13 @@ import com.cloud.agent.api.GetStorageStatsAnswer; import com.cloud.agent.api.GetStorageStatsCommand; import com.cloud.agent.api.GetVolumeStatsAnswer; import com.cloud.agent.api.GetVolumeStatsCommand; +import com.cloud.agent.api.ModifyStoragePoolAnswer; +import com.cloud.agent.api.ModifyStoragePoolCommand; import com.cloud.agent.api.StoragePoolInfo; import com.cloud.agent.api.VolumeStatsEntry; import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DiskTO; -import com.cloud.agent.api.ModifyStoragePoolCommand; -import com.cloud.agent.api.ModifyStoragePoolAnswer; +import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.manager.Commands; import com.cloud.api.ApiDBUtils; import com.cloud.api.query.dao.TemplateJoinDao; @@ -143,8 +141,10 @@ import com.cloud.configuration.ConfigurationManagerImpl; import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenterVO; +import com.cloud.dc.VsphereStoragePolicyVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.VsphereStoragePolicyDao; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; import com.cloud.exception.AgentUnavailableException; @@ -172,6 +172,7 @@ import com.cloud.org.Grouping.AllocationState; import com.cloud.resource.ResourceState; import com.cloud.server.ConfigurationServer; import com.cloud.server.ManagementServer; +import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Volume.Type; @@ -195,6 +196,7 @@ import com.cloud.user.dao.UserDao; import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; +import com.cloud.utils.StringUtils; import com.cloud.utils.UriUtils; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.component.ManagerBase; @@ -218,8 +220,6 @@ import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.VMInstanceDao; -import static com.cloud.utils.NumbersUtil.toHumanReadableSize; - @Component public class StorageManagerImpl extends ManagerBase implements StorageManager, ClusterManagerListener, Configurable { private static final Logger s_logger = Logger.getLogger(StorageManagerImpl.class); @@ -2392,8 +2392,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C } @Override - public boolean isStoragePoolComplaintWithStoragePolicy(List volumes, StoragePool pool) throws StorageUnavailableException { - if (volumes == null || volumes.isEmpty()) { + public boolean isStoragePoolCompliantWithStoragePolicy(List volumes, StoragePool pool) throws StorageUnavailableException { + if (CollectionUtils.isEmpty(volumes)) { return false; } List> answers = new ArrayList>(); diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index b9fa40c4633..f77a007e120 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -2441,7 +2441,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic HypervisorType hypervisorType = _volsDao.getHypervisorType(volumeId); if (hypervisorType.equals(HypervisorType.VMware)) { try { - boolean isStoragePoolStoragepolicyComplaince = storageMgr.isStoragePoolComplaintWithStoragePolicy(Arrays.asList(vol), destPool); + boolean isStoragePoolStoragepolicyComplaince = storageMgr.isStoragePoolCompliantWithStoragePolicy(Arrays.asList(vol), destPool); if (!isStoragePoolStoragepolicyComplaince) { throw new CloudRuntimeException(String.format("Storage pool %s is not storage policy compliance with the volume %s", poolUuid, vol.getUuid())); } diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 99548f97a38..cd4e67abb52 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -582,6 +582,13 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private static final ConfigKey VmDestroyForcestop = new ConfigKey("Advanced", Boolean.class, "vm.destroy.forcestop", "false", "On destroy, force-stop takes this value ", true); + public static final List VM_STORAGE_MIGRATION_SUPPORTING_HYPERVISORS = new ArrayList<>(Arrays.asList( + HypervisorType.KVM, + HypervisorType.VMware, + HypervisorType.XenServer, + HypervisorType.Simulator + )); + @Override public UserVmVO getVirtualMachine(long vmId) { return _vmDao.findById(vmId); @@ -6266,56 +6273,18 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir return true; } - @Override - @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true) - public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinationHost, Map volumeToPool) throws ResourceUnavailableException, - ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { - // Access check - only root administrator can migrate VM. - Account caller = CallContext.current().getCallingAccount(); - if (!_accountMgr.isRootAdmin(caller.getId())) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Caller is not a root admin, permission denied to migrate the VM"); - } - throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!"); - } - - VMInstanceVO vm = _vmInstanceDao.findById(vmId); - if (vm == null) { - throw new InvalidParameterValueException("Unable to find the vm by id " + vmId); - } - - // OfflineVmwareMigration: this would be it ;) if multiple paths exist: unify - if (vm.getState() != State.Running) { - // OfflineVmwareMigration: and not vmware - if (s_logger.isDebugEnabled()) { - s_logger.debug("VM is not Running, unable to migrate the vm " + vm); - } - CloudRuntimeException ex = new CloudRuntimeException("VM is not Running, unable to migrate the vm with" + " specified id"); - ex.addProxyObject(vm.getUuid(), "vmId"); - throw ex; - } - - if(serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) { - throw new InvalidParameterValueException("Live Migration of GPU enabled VM is not supported"); - } - - // OfflineVmwareMigration: this condition is to complicated. (already a method somewhere) - if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM) - && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Hyperv) - && !vm.getHypervisorType().equals(HypervisorType.Simulator)) { - throw new InvalidParameterValueException("Unsupported hypervisor type for vm migration, we support" + " XenServer/VMware/KVM only"); - } - + private Pair getHostsForMigrateVmWithStorage(VMInstanceVO vm, Host destinationHost) throws VirtualMachineMigrationException { long srcHostId = vm.getHostId(); Host srcHost = _resourceMgr.getHost(srcHostId); if (srcHost == null) { - throw new InvalidParameterValueException("Cannot migrate VM, host with id: " + srcHostId + " for VM not found"); + throw new InvalidParameterValueException("Cannot migrate VM, host with ID: " + srcHostId + " for VM not found"); } // Check if source and destination hosts are valid and migrating to same host if (destinationHost.getId() == srcHostId) { - throw new InvalidParameterValueException("Cannot migrate VM, VM is already present on this host, please" + " specify valid destination host to migrate the VM"); + throw new InvalidParameterValueException(String.format("Cannot migrate VM as it is already present on host %s (ID: %s), please specify valid destination host to migrate the VM", + destinationHost.getName(), destinationHost.getUuid())); } String srcHostVersion = srcHost.getHypervisorVersion(); @@ -6345,39 +6314,45 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } - if (!Boolean.TRUE.equals(_hypervisorCapabilitiesDao.isStorageMotionSupported(srcHost.getHypervisorType(), srcHostVersion))) { - throw new CloudRuntimeException("Migration with storage isn't supported for source host ID: " + srcHost.getUuid() + " on hypervisor " + srcHost.getHypervisorType() + " of version " + srcHost.getHypervisorVersion()); + if (!_hypervisorCapabilitiesDao.isStorageMotionSupported(srcHost.getHypervisorType(), srcHostVersion)) { + throw new CloudRuntimeException(String.format("Migration with storage isn't supported for source host %s (ID: %s) on hypervisor %s with version %s", srcHost.getName(), srcHost.getUuid(), srcHost.getHypervisorType(), srcHost.getHypervisorVersion())); } if (srcHostVersion == null || !srcHostVersion.equals(destHostVersion)) { - if (!Boolean.TRUE.equals(_hypervisorCapabilitiesDao.isStorageMotionSupported(destinationHost.getHypervisorType(), destHostVersion))) { - throw new CloudRuntimeException("Migration with storage isn't supported for target host ID: " + srcHost.getUuid() + " on hypervisor " + srcHost.getHypervisorType() + " of version " + srcHost.getHypervisorVersion()); + if (!_hypervisorCapabilitiesDao.isStorageMotionSupported(destinationHost.getHypervisorType(), destHostVersion)) { + throw new CloudRuntimeException(String.format("Migration with storage isn't supported for target host %s (ID: %s) on hypervisor %s with version %s", destinationHost.getName(), destinationHost.getUuid(), destinationHost.getHypervisorType(), destinationHost.getHypervisorVersion())); } } // Check if destination host is up. if (destinationHost.getState() != com.cloud.host.Status.Up || destinationHost.getResourceState() != ResourceState.Enabled) { - throw new CloudRuntimeException("Cannot migrate VM, destination host is not in correct state, has " + "status: " + destinationHost.getState() + ", state: " - + destinationHost.getResourceState()); + throw new CloudRuntimeException(String.format("Cannot migrate VM, destination host %s (ID: %s) is not in correct state, has status: %s, state: %s", + destinationHost.getName(), destinationHost.getUuid(), destinationHost.getState(), destinationHost.getResourceState())); } - // Check that Vm does not have VM Snapshots - if (_vmSnapshotDao.findByVm(vmId).size() > 0) { - throw new InvalidParameterValueException("VM with VM Snapshots cannot be migrated with storage, please remove all VM snapshots"); + // Check max guest vm limit for the destinationHost. + if (_capacityMgr.checkIfHostReachMaxGuestLimit(destinationHost)) { + throw new VirtualMachineMigrationException(String.format("Cannot migrate VM as destination host %s (ID: %s) already has max running vms (count includes system VMs)", + destinationHost.getName(), destinationHost.getUuid())); } + return new Pair<>(srcHost, destinationHost); + } + + private List getVmVolumesForMigrateVmWithStorage(VMInstanceVO vm) { List vmVolumes = _volsDao.findUsableVolumesForInstance(vm.getId()); - Map volToPoolObjectMap = new HashMap(); - if (!isVMUsingLocalStorage(vm) && MapUtils.isEmpty(volumeToPool) - && (destinationHost.getClusterId().equals(srcHost.getClusterId()) || isVmVolumesOnZoneWideStore(vm))){ - // If volumes do not have to be migrated - // call migrateVirtualMachine for non-user VMs else throw exception - if (!VirtualMachine.Type.User.equals(vm.getType())) { - return migrateVirtualMachine(vmId, destinationHost); + for (VolumeVO volume : vmVolumes) { + if (volume.getState() != Volume.State.Ready) { + throw new CloudRuntimeException(String.format("Volume %s (ID: %s) of the VM is not in Ready state. Cannot migrate the VM %s (ID: %s) with its volumes", volume.getName(), volume.getUuid(), vm.getInstanceName(), vm.getUuid())); } - throw new InvalidParameterValueException("Migration of the vm " + vm + "from host " + srcHost + " to destination host " + destinationHost - + " doesn't involve migrating the volumes."); } + return vmVolumes; + } + + private Map getVolumePoolMappingForMigrateVmWithStorage(VMInstanceVO vm, Map volumeToPool) { + Map volToPoolObjectMap = new HashMap(); + + List vmVolumes = getVmVolumesForMigrateVmWithStorage(vm); if (MapUtils.isNotEmpty(volumeToPool)) { // Check if all the volumes and pools passed as parameters are valid. @@ -6394,15 +6369,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } else { // Verify the volume given belongs to the vm. if (!vmVolumes.contains(volume)) { - throw new InvalidParameterValueException("There volume " + volume + " doesn't belong to " + "the virtual machine " + vm + " that has to be migrated"); + throw new InvalidParameterValueException(String.format("Volume " + volume + " doesn't belong to the VM %s (ID: %s) that has to be migrated", vm.getInstanceName(), vm.getUuid())); } - volToPoolObjectMap.put(Long.valueOf(volume.getId()), Long.valueOf(pool.getId())); + volToPoolObjectMap.put(volume.getId(), pool.getId()); } HypervisorType hypervisorType = _volsDao.getHypervisorType(volume.getId()); if (hypervisorType.equals(HypervisorType.VMware)) { try { - boolean isStoragePoolStoragepolicyComplaince = storageManager.isStoragePoolComplaintWithStoragePolicy(Arrays.asList(volume), pool); - if (!isStoragePoolStoragepolicyComplaince) { + boolean isStoragePoolStoragepolicyCompliance = storageManager.isStoragePoolCompliantWithStoragePolicy(Arrays.asList(volume), pool); + if (!isStoragePoolStoragepolicyCompliance) { throw new CloudRuntimeException(String.format("Storage pool %s is not storage policy compliance with the volume %s", pool.getUuid(), volume.getUuid())); } } catch (StorageUnavailableException e) { @@ -6411,24 +6386,69 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } } + return volToPoolObjectMap; + } - // Check if all the volumes are in the correct state. - for (VolumeVO volume : vmVolumes) { - if (volume.getState() != Volume.State.Ready) { - throw new CloudRuntimeException("Volume " + volume + " of the VM is not in Ready state. Cannot " + "migrate the vm with its volumes."); + @Override + @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true) + public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinationHost, Map volumeToPool) throws ResourceUnavailableException, + ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException { + // Access check - only root administrator can migrate VM. + Account caller = CallContext.current().getCallingAccount(); + if (!_accountMgr.isRootAdmin(caller.getId())) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Caller is not a root admin, permission denied to migrate the VM"); } + throw new PermissionDeniedException("No permission to migrate VM, Only Root Admin can migrate a VM!"); } - // Check max guest vm limit for the destinationHost. - HostVO destinationHostVO = _hostDao.findById(destinationHost.getId()); - if (_capacityMgr.checkIfHostReachMaxGuestLimit(destinationHostVO)) { - throw new VirtualMachineMigrationException("Host name: " + destinationHost.getName() + ", hostId: " + destinationHost.getId() - + " already has max running vms (count includes system VMs). Cannot" + " migrate to this host"); + VMInstanceVO vm = _vmInstanceDao.findById(vmId); + if (vm == null) { + throw new InvalidParameterValueException("Unable to find the VM by ID " + vmId); } - checkHostsDedication(vm, srcHostId, destinationHost.getId()); + // OfflineVmwareMigration: this would be it ;) if multiple paths exist: unify + if (vm.getState() != State.Running) { + // OfflineVmwareMigration: and not vmware + if (s_logger.isDebugEnabled()) { + s_logger.debug("VM is not Running, unable to migrate the vm " + vm); + } + CloudRuntimeException ex = new CloudRuntimeException(String.format("Unable to migrate the VM %s (ID: %s) as it is not in Running state", vm.getInstanceName(), vm.getUuid())); + ex.addProxyObject(vm.getUuid(), "vmId"); + throw ex; + } - _itMgr.migrateWithStorage(vm.getUuid(), srcHostId, destinationHost.getId(), volToPoolObjectMap); + if(serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) { + throw new InvalidParameterValueException("Live Migration of GPU enabled VM is not supported"); + } + + if (VM_STORAGE_MIGRATION_SUPPORTING_HYPERVISORS.contains(vm.getHypervisorType())) { + throw new InvalidParameterValueException(String.format("Unsupported hypervisor: %s for VM migration, we support XenServer/VMware/KVM only", vm.getHypervisorType())); + } + + if (_vmSnapshotDao.findByVm(vmId).size() > 0) { + throw new InvalidParameterValueException("VM with VM Snapshots cannot be migrated with storage, please remove all VM snapshots"); + } + + Pair sourceDestinationHosts = getHostsForMigrateVmWithStorage(vm, destinationHost); + Host srcHost = sourceDestinationHosts.first(); + + if (!isVMUsingLocalStorage(vm) && MapUtils.isEmpty(volumeToPool) + && (destinationHost.getClusterId().equals(srcHost.getClusterId()) || isVmVolumesOnZoneWideStore(vm))){ + // If volumes do not have to be migrated + // call migrateVirtualMachine for non-user VMs else throw exception + if (!VirtualMachine.Type.User.equals(vm.getType())) { + return migrateVirtualMachine(vmId, destinationHost); + } + throw new InvalidParameterValueException(String.format("Migration of the VM: %s (ID: %s) from host %s (ID: %s) to destination host %s (ID: %s) doesn't involve migrating the volumes", + vm.getInstanceName(), vm.getUuid(), srcHost.getName(), srcHost.getUuid(), destinationHost.getName(), destinationHost.getUuid())); + } + + Map volToPoolObjectMap = getVolumePoolMappingForMigrateVmWithStorage(vm, volumeToPool); + + checkHostsDedication(vm, srcHost.getId(), destinationHost.getId()); + + _itMgr.migrateWithStorage(vm.getUuid(), srcHost.getId(), destinationHost.getId(), volToPoolObjectMap); return findMigratedVm(vm.getId(), vm.getType()); }