Repository: cloudstack
Updated Branches:
  refs/heads/master 5227ae227 -> 15b348632


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.


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/15b34863
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/15b34863
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/15b34863

Branch: refs/heads/master
Commit: 15b348632df2049347f58c87830be2c02eee3b61
Parents: 5227ae2
Author: Likitha Shetty <likitha.she...@citrix.com>
Authored: Thu Dec 18 19:38:14 2014 +0530
Committer: Likitha Shetty <likitha.she...@citrix.com>
Committed: Fri Dec 26 11:07:30 2014 +0530

----------------------------------------------------------------------
 .../cloud/agent/api/UnregisterVMCommand.java    |  9 +++++++
 .../com/cloud/vm/VirtualMachineManagerImpl.java | 27 ++++++++++++++++++++
 .../src/com/cloud/dc/ClusterDetailsDao.java     |  2 ++
 .../src/com/cloud/dc/ClusterDetailsDaoImpl.java | 10 ++++++++
 .../vmware/resource/VmwareResource.java         | 18 +++++++++----
 .../com/cloud/storage/VolumeApiServiceImpl.java | 19 ++++++++++++++
 6 files changed, 80 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/15b34863/core/src/com/cloud/agent/api/UnregisterVMCommand.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/UnregisterVMCommand.java 
b/core/src/com/cloud/agent/api/UnregisterVMCommand.java
index a0085e0..16eb4ba 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;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/15b34863/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
----------------------------------------------------------------------
diff --git 
a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java 
b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
index be5ea63..e9cd79c 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");
             }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/15b34863/engine/schema/src/com/cloud/dc/ClusterDetailsDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/dc/ClusterDetailsDao.java 
b/engine/schema/src/com/cloud/dc/ClusterDetailsDao.java
index 49250d9..06c9c52 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, Long> {
     ClusterDetailsVO findDetail(long clusterId, String name);
 
     void deleteDetails(long clusterId);
+
+    String getVmwareDcName(Long clusterId);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/15b34863/engine/schema/src/com/cloud/dc/ClusterDetailsDaoImpl.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/dc/ClusterDetailsDaoImpl.java 
b/engine/schema/src/com/cloud/dc/ClusterDetailsDaoImpl.java
index 0d6b833..c9397c2 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<ClusterDetailsVO, Long
         ClusterDetailsVO vo = findDetail(id, key.key());
         return vo == null ? null : vo.getValue();
     }
+
+    @Override
+    public String getVmwareDcName(Long clusterId) {
+        String dcName = null;
+        String url = findDetail(clusterId, "url").getValue();
+        String[] tokens = url.split("/"); // Cluster URL format is 
'http://vcenter/dc/cluster'
+        if (tokens != null && tokens.length > 3)
+            dcName = tokens[3];
+        return dcName;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/15b34863/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 0dfde45..b1a4380 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));

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/15b34863/server/src/com/cloud/storage/VolumeApiServiceImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java 
b/server/src/com/cloud/storage/VolumeApiServiceImpl.java
index 7fa600a..ca83890 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<StoragePoolAllocator> _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");

Reply via email to