Repository: cloudstack Updated Branches: refs/heads/master 7b32b8a26 -> 974b0180d
CLOUDSTACK-8126. Cold Migration of VM is not working as expected. In case a VM is cold migrated across clusters then VM fails to start. 1. If a VM by the same name exists on a different cluster in the VMware DC, unregister the existing VM and continue with the VM start. 2. If VM start succeeds, delete VM files associated with the unregistered VM. 3. If VM start fails, re-register the unregistered VM. Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/974b0180 Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/974b0180 Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/974b0180 Branch: refs/heads/master Commit: 974b0180dd67f19fea921092105161f849891ac5 Parents: 7b32b8a Author: Likitha Shetty <likitha.she...@citrix.com> Authored: Mon Dec 8 18:59:51 2014 +0530 Committer: Likitha Shetty <likitha.she...@citrix.com> Committed: Wed Dec 24 15:07:08 2014 +0530 ---------------------------------------------------------------------- .../vmware/resource/VmwareResource.java | 77 +++++++++++++++++++- .../hypervisor/vmware/mo/VirtualMachineMO.java | 36 +++++++++ 2 files changed, 111 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/974b0180/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java ---------------------------------------------------------------------- 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 53cdb99..0dfde45 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 @@ -84,6 +84,9 @@ import com.vmware.vim25.VirtualEthernetCard; import com.vmware.vim25.VirtualEthernetCardDistributedVirtualPortBackingInfo; import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo; import com.vmware.vim25.VirtualMachineConfigSpec; +import com.vmware.vim25.VirtualMachineFileInfo; +import com.vmware.vim25.VirtualMachineFileLayoutEx; +import com.vmware.vim25.VirtualMachineFileLayoutExFileInfo; import com.vmware.vim25.VirtualMachineGuestOsIdentifier; import com.vmware.vim25.VirtualMachinePowerState; import com.vmware.vim25.VirtualMachineRelocateSpec; @@ -1326,17 +1329,22 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa VirtualMachineTO vmSpec = cmd.getVirtualMachine(); boolean vmAlreadyExistsInVcenter = false; + String existingVmName = null; + VirtualMachineFileInfo existingVmFileInfo = null; + VirtualMachineFileLayoutEx existingVmFileLayout = null; + Pair<String, String> names = composeVmNames(vmSpec); String vmInternalCSName = names.first(); String vmNameOnVcenter = names.second(); // Thus, vmInternalCSName always holds i-x-y, the cloudstack generated internal VM name. VmwareContext context = getServiceContext(); + DatacenterMO dcMo = null; try { VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME); VmwareHypervisorHost hyperHost = getHyperHost(context); - DatacenterMO dcMo = new DatacenterMO(hyperHost.getContext(), hyperHost.getHyperHostDatacenter()); + dcMo = new DatacenterMO(hyperHost.getContext(), hyperHost.getHyperHostDatacenter()); // Validate VM name is unique in Datacenter VirtualMachineMO vmInVcenter = dcMo.checkIfVmAlreadyExistsInVcenter(vmNameOnVcenter, vmInternalCSName); @@ -1404,6 +1412,15 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa vmMo.tearDownDevices(new Class<?>[] {VirtualEthernetCard.class}); vmMo.ensureScsiDeviceController(); } else { + // If a VM with the same name is found in a different cluster in the DC, unregister the old VM and configure a new VM (cold-migration). + VirtualMachineMO existingVmInDc = dcMo.findVm(vmInternalCSName); + if (existingVmInDc != null) { + s_logger.debug("Found VM: " + vmInternalCSName + " on a host in a different cluster. Unregistering the exisitng VM."); + existingVmName = existingVmInDc.getName(); + existingVmFileInfo = existingVmInDc.getFileInfo(); + existingVmFileLayout = existingVmInDc.getFileLayout(); + existingVmInDc.unregisterVm(); + } Pair<ManagedObjectReference, DatastoreMO> rootDiskDataStoreDetails = null; for (DiskTO vol : disks) { if (vol.getType() == Volume.Type.ROOT) { @@ -1429,7 +1446,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa assert (vmSpec.getMinSpeed() != null) && (rootDiskDataStoreDetails != null); - if (rootDiskDataStoreDetails.second().folderExists(String.format("[%s]", rootDiskDataStoreDetails.second().getName()), vmNameOnVcenter)) { + boolean vmFolderExists = rootDiskDataStoreDetails.second().folderExists(String.format("[%s]", rootDiskDataStoreDetails.second().getName()), vmNameOnVcenter); + String vmxFileFullPath = dsRootVolumeIsOn.searchFileInSubFolders(vmNameOnVcenter + ".vmx", false); + if (vmFolderExists && vmxFileFullPath != null) { // VM can be registered only if .vmx is present. registerVm(vmNameOnVcenter, dsRootVolumeIsOn); vmMo = hyperHost.findVmOnHyperHost(vmInternalCSName); tearDownVm(vmMo); @@ -1740,6 +1759,11 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa startAnswer.setIqnToPath(iqnToPath); + // 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); + } + return startAnswer; } catch (Throwable e) { if (e instanceof RemoteException) { @@ -1753,6 +1777,20 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa if(vmAlreadyExistsInVcenter) { startAnswer.setContextParam("stopRetry", "true"); } + + // Since VM start failed, if there was an existing VM in a different cluster that was unregistered, register it back. + if (existingVmName != null && existingVmFileInfo != null) { + s_logger.debug("Since VM start failed, registering back an existing VM: " + existingVmName + " that was unregistered"); + try { + DatastoreFile fileInDatastore = new DatastoreFile(existingVmFileInfo.getVmPathName()); + DatastoreMO existingVmDsMo = new DatastoreMO(dcMo.getContext(), dcMo.findDatastore(fileInDatastore.getDatastoreName())); + registerVm(existingVmName, existingVmDsMo); + } catch (Exception ex){ + String message = "Failed to register an existing VM: " + existingVmName + " due to " + VmwareHelper.getExceptionMessage(ex); + s_logger.warn(message, ex); + } + } + return startAnswer; } finally { } @@ -2201,6 +2239,41 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } + private void deleteUnregisteredVmFiles(VirtualMachineFileLayoutEx vmFileLayout, DatacenterMO dcMo) throws Exception { + s_logger.debug("Deleting files associated with an existing VM that was unregistered"); + DatastoreFile vmFolder = null; + try { + List<VirtualMachineFileLayoutExFileInfo> fileInfo = vmFileLayout.getFile(); + for (VirtualMachineFileLayoutExFileInfo file : fileInfo) { + DatastoreFile fileInDatastore = new DatastoreFile(file.getName()); + // In case of linked clones, VM file layout includes the base disk so don't delete all disk files. + if (file.getType().startsWith("disk") || file.getType().startsWith("digest")) + continue; + else if (file.getType().equals("config")) + vmFolder = new DatastoreFile(fileInDatastore.getDatastoreName(), fileInDatastore.getDir()); + DatastoreMO dsMo = new DatastoreMO(dcMo.getContext(), dcMo.findDatastore(fileInDatastore.getDatastoreName())); + s_logger.debug("Deleting file: " + file.getName()); + dsMo.deleteFile(file.getName(), dcMo.getMor(), true); + } + // 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) { + for (String file : files) { + String vmDiskFileFullPath = String.format("%s/%s", vmFolder.getPath(), file); + s_logger.debug("Deleting file: " + vmDiskFileFullPath); + vmFolderDsMo.deleteFile(vmDiskFileFullPath, dcMo.getMor(), true); + } + } + // Delete VM folder + 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); + } + } + private static VolumeObjectTO getVolumeInSpec(VirtualMachineTO vmSpec, VolumeObjectTO srcVol) { for (DiskTO disk : vmSpec.getDisks()) { VolumeObjectTO vol = (VolumeObjectTO)disk.getData(); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/974b0180/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java ---------------------------------------------------------------------- diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 286aedd..5f180e1 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -86,6 +86,7 @@ import com.vmware.vim25.VirtualMachineConfigOption; import com.vmware.vim25.VirtualMachineConfigSpec; import com.vmware.vim25.VirtualMachineConfigSummary; import com.vmware.vim25.VirtualMachineFileInfo; +import com.vmware.vim25.VirtualMachineFileLayoutEx; import com.vmware.vim25.VirtualMachineMessage; import com.vmware.vim25.VirtualMachineMovePriority; import com.vmware.vim25.VirtualMachinePowerState; @@ -740,6 +741,41 @@ public class VirtualMachineMO extends BaseMO { return (VirtualMachineFileInfo)_context.getVimClient().getDynamicProperty(_mor, "config.files"); } + public VirtualMachineFileLayoutEx getFileLayout() throws Exception { + VirtualMachineFileLayoutEx fileLayout = null; + PropertySpec pSpec = new PropertySpec(); + pSpec.setType("VirtualMachine"); + pSpec.getPathSet().add("layoutEx"); + + ObjectSpec oSpec = new ObjectSpec(); + oSpec.setObj(_mor); + oSpec.setSkip(Boolean.FALSE); + + PropertyFilterSpec pfSpec = new PropertyFilterSpec(); + pfSpec.getPropSet().add(pSpec); + pfSpec.getObjectSet().add(oSpec); + List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>(); + pfSpecArr.add(pfSpec); + + List<ObjectContent> ocs = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr); + + if (ocs != null) { + for (ObjectContent oc : ocs) { + List<DynamicProperty> props = oc.getPropSet(); + if (props != null) { + for (DynamicProperty prop : props) { + if (prop.getName().equals("layoutEx")) { + fileLayout = (VirtualMachineFileLayoutEx)prop.getVal(); + break; + } + } + } + } + } + + return fileLayout; + } + @Override public ManagedObjectReference getParentMor() throws Exception { return (ManagedObjectReference)_context.getVimClient().getDynamicProperty(_mor, "parent");