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 be5ea63139f..e9cd79cc017 100644 --- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -96,6 +96,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; @@ -1727,6 +1728,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); @@ -1752,6 +1756,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 100644 --- 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 0dfde45946d..b1a438019d1 100644 --- 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; @@ -2239,7 +2239,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 { @@ -2258,7 +2258,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); @@ -2266,8 +2266,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); @@ -2765,6 +2767,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)) { @@ -3895,10 +3898,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 7fa600a8f36..ca838901706 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; @@ -1761,6 +1764,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");