From bcbfe3bdeefedf4f53e1b5bade4e220fb8d23a57 Mon Sep 17 00:00:00 2001 From: Likitha Shetty Date: Thu, 18 Dec 2014 19:38:14 +0530 Subject: [PATCH] CLOUDSTACK-8129. Cold migration of VM across VMware DCs leaves the VM behind in the source host. If VM has been cold migrated across different VMware DCs, then unregister the VM from source host. (cherry picked from commit 15b348632df2049347f58c87830be2c02eee3b61) Signed-off-by: Rohit Yadav --- .../cloud/agent/api/UnregisterVMCommand.java | 9 +++++++ .../cloud/vm/VirtualMachineManagerImpl.java | 27 +++++++++++++++++++ .../src/com/cloud/dc/ClusterDetailsDao.java | 2 ++ .../com/cloud/dc/ClusterDetailsDaoImpl.java | 10 +++++++ .../vmware/resource/VmwareResource.java | 18 +++++++++---- .../cloud/storage/VolumeApiServiceImpl.java | 19 +++++++++++++ 6 files changed, 80 insertions(+), 5 deletions(-) diff --git a/core/src/com/cloud/agent/api/UnregisterVMCommand.java b/core/src/com/cloud/agent/api/UnregisterVMCommand.java index a0085e0ca02..16eb4bac0e8 100644 --- a/core/src/com/cloud/agent/api/UnregisterVMCommand.java +++ b/core/src/com/cloud/agent/api/UnregisterVMCommand.java @@ -21,6 +21,7 @@ package com.cloud.agent.api; public class UnregisterVMCommand extends Command { String vmName; + boolean cleanupVmFiles = false; public UnregisterVMCommand(String vmName) { this.vmName = vmName; @@ -34,4 +35,12 @@ public class UnregisterVMCommand extends Command { public String getVmName() { return vmName; } + + public void setCleanupVmFiles(boolean cleanupVmFiles) { + this.cleanupVmFiles = cleanupVmFiles; + } + + public boolean getCleanupVmFiles() { + return this.cleanupVmFiles; + } } diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java index 8c7388d342a..eed0963449e 100755 --- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -95,6 +95,7 @@ import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.StopCommand; import com.cloud.agent.api.UnPlugNicAnswer; import com.cloud.agent.api.UnPlugNicCommand; +import com.cloud.agent.api.UnregisterVMCommand; import com.cloud.agent.api.to.DiskTO; import com.cloud.agent.api.to.GPUDeviceTO; import com.cloud.agent.api.to.NicTO; @@ -1716,6 +1717,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac private void orchestrateStorageMigration(String vmUuid, StoragePool destPool) { VMInstanceVO vm = _vmDao.findByUuid(vmUuid); + Long srchostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId(); + HostVO srcHost = _hostDao.findById(srchostId); + Long srcClusterId = srcHost.getClusterId(); try { stateTransitTo(vm, VirtualMachine.Event.StorageMigrationRequested, null); @@ -1741,6 +1745,29 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac //when start the vm next time, don;'t look at last_host_id, only choose the host based on volume/storage pool vm.setLastHostId(null); vm.setPodIdToDeployIn(destPool.getPodId()); + + // If VM was cold migrated between clusters belonging to two different VMware DCs, + // unregister the VM from the source host and cleanup the associated VM files. + if (vm.getHypervisorType().equals(HypervisorType.VMware)) { + Long destClusterId = destPool.getClusterId(); + if (srcClusterId != null && destClusterId != null && srcClusterId != destClusterId) { + String srcDcName = _clusterDetailsDao.getVmwareDcName(srcClusterId); + String destDcName = _clusterDetailsDao.getVmwareDcName(destClusterId); + if (srcDcName != null && destDcName != null && !srcDcName.equals(destDcName)) { + s_logger.debug("Since VM's storage was successfully migrated across VMware Datacenters, unregistering VM: " + vm.getInstanceName() + + " from source host: " + srcHost.getId()); + UnregisterVMCommand uvc = new UnregisterVMCommand(vm.getInstanceName()); + uvc.setCleanupVmFiles(true); + try { + _agentMgr.send(srcHost.getId(), uvc); + } catch (Exception e) { + throw new CloudRuntimeException("Failed to unregister VM: " + vm.getInstanceName() + " from source host: " + srcHost.getId() + + " after successfully migrating VM's storage across VMware Datacenters"); + } + } + } + } + } else { s_logger.debug("Storage migration failed"); } diff --git a/engine/schema/src/com/cloud/dc/ClusterDetailsDao.java b/engine/schema/src/com/cloud/dc/ClusterDetailsDao.java index 49250d94be6..06c9c525504 100644 --- a/engine/schema/src/com/cloud/dc/ClusterDetailsDao.java +++ b/engine/schema/src/com/cloud/dc/ClusterDetailsDao.java @@ -30,4 +30,6 @@ public interface ClusterDetailsDao extends GenericDao { ClusterDetailsVO findDetail(long clusterId, String name); void deleteDetails(long clusterId); + + String getVmwareDcName(Long clusterId); } diff --git a/engine/schema/src/com/cloud/dc/ClusterDetailsDaoImpl.java b/engine/schema/src/com/cloud/dc/ClusterDetailsDaoImpl.java index 0d6b833b399..c9397c2574f 100755 --- a/engine/schema/src/com/cloud/dc/ClusterDetailsDaoImpl.java +++ b/engine/schema/src/com/cloud/dc/ClusterDetailsDaoImpl.java @@ -139,4 +139,14 @@ public class ClusterDetailsDaoImpl extends GenericDaoBase 3) + dcName = tokens[3]; + return dcName; + } } diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 89e7d3f766c..315b1619f4a 100755 --- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -1761,7 +1761,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa // Since VM was successfully powered-on, if there was an existing VM in a different cluster that was unregistered, delete all the files associated with it. if (existingVmName != null && existingVmFileLayout != null) { - deleteUnregisteredVmFiles(existingVmFileLayout, dcMo); + deleteUnregisteredVmFiles(existingVmFileLayout, dcMo, true); } return startAnswer; @@ -2242,7 +2242,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } - private void deleteUnregisteredVmFiles(VirtualMachineFileLayoutEx vmFileLayout, DatacenterMO dcMo) throws Exception { + private void deleteUnregisteredVmFiles(VirtualMachineFileLayoutEx vmFileLayout, DatacenterMO dcMo, boolean deleteDisks) throws Exception { s_logger.debug("Deleting files associated with an existing VM that was unregistered"); DatastoreFile vmFolder = null; try { @@ -2261,7 +2261,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa // Delete files that are present in the VM folder - this will take care of the VM disks as well. DatastoreMO vmFolderDsMo = new DatastoreMO(dcMo.getContext(), dcMo.findDatastore(vmFolder.getDatastoreName())); String[] files = vmFolderDsMo.listDirContent(vmFolder.getPath()); - if (files.length != 0) { + if (deleteDisks) { for (String file : files) { String vmDiskFileFullPath = String.format("%s/%s", vmFolder.getPath(), file); s_logger.debug("Deleting file: " + vmDiskFileFullPath); @@ -2269,8 +2269,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } // Delete VM folder - s_logger.debug("Deleting folder: " + vmFolder.getPath()); - vmFolderDsMo.deleteFolder(vmFolder.getPath(), dcMo.getMor()); + if (deleteDisks || files.length == 0) { + s_logger.debug("Deleting folder: " + vmFolder.getPath()); + vmFolderDsMo.deleteFolder(vmFolder.getPath(), dcMo.getMor()); + } } catch (Exception e) { String message = "Failed to delete files associated with an existing VM that was unregistered due to " + VmwareHelper.getExceptionMessage(e); s_logger.warn(message, e); @@ -2768,6 +2770,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa try { vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK, "0"); + vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_VM_INTERNAL_NAME, cmd.getVmName()); if (getVmPowerState(vmMo) != PowerState.PowerOff) { if (vmMo.safePowerOff(_shutdownWaitMs)) { @@ -3898,10 +3901,15 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa VmwareContext context = getServiceContext(); VmwareHypervisorHost hyperHost = getHyperHost(context); try { + DatacenterMO dataCenterMo = new DatacenterMO(getServiceContext(), hyperHost.getHyperHostDatacenter()); VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(cmd.getVmName()); if (vmMo != null) { try { + VirtualMachineFileLayoutEx vmFileLayout = vmMo.getFileLayout(); context.getService().unregisterVM(vmMo.getMor()); + if (cmd.getCleanupVmFiles()) { + deleteUnregisteredVmFiles(vmFileLayout, dataCenterMo, false); + } return new Answer(cmd, true, "unregister succeeded"); } catch (Exception e) { s_logger.warn("We are not able to unregister VM " + VmwareHelper.getExceptionMessage(e)); diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index 40b957446af..71042c50dba 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -79,6 +79,7 @@ import com.cloud.api.ApiDBUtils; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.Resource.ResourceType; +import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; @@ -221,6 +222,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic AsyncJobManager _jobMgr; @Inject VmWorkJobDao _workJobDao; + @Inject + ClusterDetailsDao _clusterDetailsDao; private List _storagePoolAllocators; @@ -1764,6 +1767,22 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic throw new InvalidParameterValueException("Cannot migrate a volume of a virtual machine to a storage pool in a different cluster"); } } + // In case of VMware, if ROOT volume is being cold-migrated, then ensure destination storage pool is in the same Datacenter as the VM. + if (vm != null && vm.getHypervisorType().equals(HypervisorType.VMware)) { + if (!liveMigrateVolume && vol.volumeType.equals(Volume.Type.ROOT)) { + Long hostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId(); + HostVO host = _hostDao.findById(hostId); + if (host != null) + srcClusterId = host.getClusterId(); + if (srcClusterId != null && destPool.getClusterId() != null && !srcClusterId.equals(destPool.getClusterId())) { + String srcDcName = _clusterDetailsDao.getVmwareDcName(srcClusterId); + String destDcName = _clusterDetailsDao.getVmwareDcName(destPool.getClusterId()); + if (srcDcName != null && destDcName != null && !srcDcName.equals(destDcName)) { + throw new InvalidParameterValueException("Cannot migrate ROOT volume of a stopped VM to a storage pool in a different VMware datacenter"); + } + } + } + } } } else { throw new InvalidParameterValueException("Migration of volume from local storage pool is not supported");