Skip to content

Commit bcbfe3b

Browse files
Likitha Shettyyadvr
authored andcommitted
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 15b3486) Signed-off-by: Rohit Yadav <[email protected]>
1 parent 12cbebc commit bcbfe3b

6 files changed

Lines changed: 80 additions & 5 deletions

File tree

core/src/com/cloud/agent/api/UnregisterVMCommand.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
public class UnregisterVMCommand extends Command {
2323
String vmName;
24+
boolean cleanupVmFiles = false;
2425

2526
public UnregisterVMCommand(String vmName) {
2627
this.vmName = vmName;
@@ -34,4 +35,12 @@ public boolean executeInSequence() {
3435
public String getVmName() {
3536
return vmName;
3637
}
38+
39+
public void setCleanupVmFiles(boolean cleanupVmFiles) {
40+
this.cleanupVmFiles = cleanupVmFiles;
41+
}
42+
43+
public boolean getCleanupVmFiles() {
44+
return this.cleanupVmFiles;
45+
}
3746
}

engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
import com.cloud.agent.api.StopCommand;
9696
import com.cloud.agent.api.UnPlugNicAnswer;
9797
import com.cloud.agent.api.UnPlugNicCommand;
98+
import com.cloud.agent.api.UnregisterVMCommand;
9899
import com.cloud.agent.api.to.DiskTO;
99100
import com.cloud.agent.api.to.GPUDeviceTO;
100101
import com.cloud.agent.api.to.NicTO;
@@ -1716,6 +1717,9 @@ else if (jobResult instanceof Throwable)
17161717

17171718
private void orchestrateStorageMigration(String vmUuid, StoragePool destPool) {
17181719
VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
1720+
Long srchostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId();
1721+
HostVO srcHost = _hostDao.findById(srchostId);
1722+
Long srcClusterId = srcHost.getClusterId();
17191723

17201724
try {
17211725
stateTransitTo(vm, VirtualMachine.Event.StorageMigrationRequested, null);
@@ -1741,6 +1745,29 @@ private void orchestrateStorageMigration(String vmUuid, StoragePool destPool) {
17411745
//when start the vm next time, don;'t look at last_host_id, only choose the host based on volume/storage pool
17421746
vm.setLastHostId(null);
17431747
vm.setPodIdToDeployIn(destPool.getPodId());
1748+
1749+
// If VM was cold migrated between clusters belonging to two different VMware DCs,
1750+
// unregister the VM from the source host and cleanup the associated VM files.
1751+
if (vm.getHypervisorType().equals(HypervisorType.VMware)) {
1752+
Long destClusterId = destPool.getClusterId();
1753+
if (srcClusterId != null && destClusterId != null && srcClusterId != destClusterId) {
1754+
String srcDcName = _clusterDetailsDao.getVmwareDcName(srcClusterId);
1755+
String destDcName = _clusterDetailsDao.getVmwareDcName(destClusterId);
1756+
if (srcDcName != null && destDcName != null && !srcDcName.equals(destDcName)) {
1757+
s_logger.debug("Since VM's storage was successfully migrated across VMware Datacenters, unregistering VM: " + vm.getInstanceName() +
1758+
" from source host: " + srcHost.getId());
1759+
UnregisterVMCommand uvc = new UnregisterVMCommand(vm.getInstanceName());
1760+
uvc.setCleanupVmFiles(true);
1761+
try {
1762+
_agentMgr.send(srcHost.getId(), uvc);
1763+
} catch (Exception e) {
1764+
throw new CloudRuntimeException("Failed to unregister VM: " + vm.getInstanceName() + " from source host: " + srcHost.getId() +
1765+
" after successfully migrating VM's storage across VMware Datacenters");
1766+
}
1767+
}
1768+
}
1769+
}
1770+
17441771
} else {
17451772
s_logger.debug("Storage migration failed");
17461773
}

engine/schema/src/com/cloud/dc/ClusterDetailsDao.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ public interface ClusterDetailsDao extends GenericDao<ClusterDetailsVO, Long> {
3030
ClusterDetailsVO findDetail(long clusterId, String name);
3131

3232
void deleteDetails(long clusterId);
33+
34+
String getVmwareDcName(Long clusterId);
3335
}

engine/schema/src/com/cloud/dc/ClusterDetailsDaoImpl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,14 @@ public String getConfigValue(long id, ConfigKey<?> key) {
139139
ClusterDetailsVO vo = findDetail(id, key.key());
140140
return vo == null ? null : vo.getValue();
141141
}
142+
143+
@Override
144+
public String getVmwareDcName(Long clusterId) {
145+
String dcName = null;
146+
String url = findDetail(clusterId, "url").getValue();
147+
String[] tokens = url.split("/"); // Cluster URL format is 'http://vcenter/dc/cluster'
148+
if (tokens != null && tokens.length > 3)
149+
dcName = tokens[3];
150+
return dcName;
151+
}
142152
}

plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,7 +1761,7 @@ protected StartAnswer execute(StartCommand cmd) {
17611761

17621762
// 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.
17631763
if (existingVmName != null && existingVmFileLayout != null) {
1764-
deleteUnregisteredVmFiles(existingVmFileLayout, dcMo);
1764+
deleteUnregisteredVmFiles(existingVmFileLayout, dcMo, true);
17651765
}
17661766

17671767
return startAnswer;
@@ -2242,7 +2242,7 @@ private void postDiskConfigBeforeStart(VirtualMachineMO vmMo, VirtualMachineTO v
22422242
}
22432243
}
22442244

2245-
private void deleteUnregisteredVmFiles(VirtualMachineFileLayoutEx vmFileLayout, DatacenterMO dcMo) throws Exception {
2245+
private void deleteUnregisteredVmFiles(VirtualMachineFileLayoutEx vmFileLayout, DatacenterMO dcMo, boolean deleteDisks) throws Exception {
22462246
s_logger.debug("Deleting files associated with an existing VM that was unregistered");
22472247
DatastoreFile vmFolder = null;
22482248
try {
@@ -2261,16 +2261,18 @@ else if (file.getType().equals("config"))
22612261
// Delete files that are present in the VM folder - this will take care of the VM disks as well.
22622262
DatastoreMO vmFolderDsMo = new DatastoreMO(dcMo.getContext(), dcMo.findDatastore(vmFolder.getDatastoreName()));
22632263
String[] files = vmFolderDsMo.listDirContent(vmFolder.getPath());
2264-
if (files.length != 0) {
2264+
if (deleteDisks) {
22652265
for (String file : files) {
22662266
String vmDiskFileFullPath = String.format("%s/%s", vmFolder.getPath(), file);
22672267
s_logger.debug("Deleting file: " + vmDiskFileFullPath);
22682268
vmFolderDsMo.deleteFile(vmDiskFileFullPath, dcMo.getMor(), true);
22692269
}
22702270
}
22712271
// Delete VM folder
2272-
s_logger.debug("Deleting folder: " + vmFolder.getPath());
2273-
vmFolderDsMo.deleteFolder(vmFolder.getPath(), dcMo.getMor());
2272+
if (deleteDisks || files.length == 0) {
2273+
s_logger.debug("Deleting folder: " + vmFolder.getPath());
2274+
vmFolderDsMo.deleteFolder(vmFolder.getPath(), dcMo.getMor());
2275+
}
22742276
} catch (Exception e) {
22752277
String message = "Failed to delete files associated with an existing VM that was unregistered due to " + VmwareHelper.getExceptionMessage(e);
22762278
s_logger.warn(message, e);
@@ -2768,6 +2770,7 @@ protected Answer execute(StopCommand cmd) {
27682770

27692771
try {
27702772
vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_NIC_MASK, "0");
2773+
vmMo.setCustomFieldValue(CustomFieldConstants.CLOUD_VM_INTERNAL_NAME, cmd.getVmName());
27712774

27722775
if (getVmPowerState(vmMo) != PowerState.PowerOff) {
27732776
if (vmMo.safePowerOff(_shutdownWaitMs)) {
@@ -3898,10 +3901,15 @@ protected Answer execute(UnregisterVMCommand cmd) {
38983901
VmwareContext context = getServiceContext();
38993902
VmwareHypervisorHost hyperHost = getHyperHost(context);
39003903
try {
3904+
DatacenterMO dataCenterMo = new DatacenterMO(getServiceContext(), hyperHost.getHyperHostDatacenter());
39013905
VirtualMachineMO vmMo = hyperHost.findVmOnHyperHost(cmd.getVmName());
39023906
if (vmMo != null) {
39033907
try {
3908+
VirtualMachineFileLayoutEx vmFileLayout = vmMo.getFileLayout();
39043909
context.getService().unregisterVM(vmMo.getMor());
3910+
if (cmd.getCleanupVmFiles()) {
3911+
deleteUnregisteredVmFiles(vmFileLayout, dataCenterMo, false);
3912+
}
39053913
return new Answer(cmd, true, "unregister succeeded");
39063914
} catch (Exception e) {
39073915
s_logger.warn("We are not able to unregister VM " + VmwareHelper.getExceptionMessage(e));

server/src/com/cloud/storage/VolumeApiServiceImpl.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
import com.cloud.configuration.Config;
8080
import com.cloud.configuration.ConfigurationManager;
8181
import com.cloud.configuration.Resource.ResourceType;
82+
import com.cloud.dc.ClusterDetailsDao;
8283
import com.cloud.dc.ClusterVO;
8384
import com.cloud.dc.DataCenter;
8485
import com.cloud.dc.DataCenterVO;
@@ -221,6 +222,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
221222
AsyncJobManager _jobMgr;
222223
@Inject
223224
VmWorkJobDao _workJobDao;
225+
@Inject
226+
ClusterDetailsDao _clusterDetailsDao;
224227

225228
private List<StoragePoolAllocator> _storagePoolAllocators;
226229

@@ -1764,6 +1767,22 @@ public Volume migrateVolume(MigrateVolumeCmd cmd) {
17641767
throw new InvalidParameterValueException("Cannot migrate a volume of a virtual machine to a storage pool in a different cluster");
17651768
}
17661769
}
1770+
// In case of VMware, if ROOT volume is being cold-migrated, then ensure destination storage pool is in the same Datacenter as the VM.
1771+
if (vm != null && vm.getHypervisorType().equals(HypervisorType.VMware)) {
1772+
if (!liveMigrateVolume && vol.volumeType.equals(Volume.Type.ROOT)) {
1773+
Long hostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId();
1774+
HostVO host = _hostDao.findById(hostId);
1775+
if (host != null)
1776+
srcClusterId = host.getClusterId();
1777+
if (srcClusterId != null && destPool.getClusterId() != null && !srcClusterId.equals(destPool.getClusterId())) {
1778+
String srcDcName = _clusterDetailsDao.getVmwareDcName(srcClusterId);
1779+
String destDcName = _clusterDetailsDao.getVmwareDcName(destPool.getClusterId());
1780+
if (srcDcName != null && destDcName != null && !srcDcName.equals(destDcName)) {
1781+
throw new InvalidParameterValueException("Cannot migrate ROOT volume of a stopped VM to a storage pool in a different VMware datacenter");
1782+
}
1783+
}
1784+
}
1785+
}
17671786
}
17681787
} else {
17691788
throw new InvalidParameterValueException("Migration of volume from local storage pool is not supported");

0 commit comments

Comments
 (0)