Skip to content

Commit fdefee7

Browse files
authored
vmware: fix inter-cluster stopped vm and volume migration (#4895)
Fixes #4838 For inter-cluster migration without shared storage, VMware needs a host to be specified. Fix is to specify an appropriate host in the target cluster during a stopped VM migration. Also, find target datastore using the host in the target cluster. Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
1 parent 99a9063 commit fdefee7

File tree

9 files changed

+407
-172
lines changed

9 files changed

+407
-172
lines changed

core/src/main/java/com/cloud/agent/api/MigrateVmToPoolCommand.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
//
1919
package com.cloud.agent.api;
2020

21-
import com.cloud.agent.api.to.VolumeTO;
22-
2321
import java.util.Collection;
2422

23+
import com.cloud.agent.api.to.VolumeTO;
24+
2525
/**
2626
* used to tell the agent to migrate a vm to a different primary storage pool.
2727
* It is for now only implemented on Vmware and is supposed to work irrespective of whether the VM is started or not.
@@ -32,6 +32,7 @@ public class MigrateVmToPoolCommand extends Command {
3232
private String vmName;
3333
private String destinationPool;
3434
private boolean executeInSequence = false;
35+
private String hostGuidInTargetCluster;
3536

3637
protected MigrateVmToPoolCommand() {
3738
}
@@ -41,15 +42,22 @@ protected MigrateVmToPoolCommand() {
4142
* @param vmName the name of the VM to migrate
4243
* @param volumes used to supply feedback on vmware generated names
4344
* @param destinationPool the primary storage pool to migrate the VM to
45+
* @param hostGuidInTargetCluster GUID of host in target cluster when migrating across clusters
4446
* @param executeInSequence
4547
*/
46-
public MigrateVmToPoolCommand(String vmName, Collection<VolumeTO> volumes, String destinationPool, boolean executeInSequence) {
48+
public MigrateVmToPoolCommand(String vmName, Collection<VolumeTO> volumes, String destinationPool,
49+
String hostGuidInTargetCluster, boolean executeInSequence) {
4750
this.vmName = vmName;
4851
this.volumes = volumes;
4952
this.destinationPool = destinationPool;
53+
this.hostGuidInTargetCluster = hostGuidInTargetCluster;
5054
this.executeInSequence = executeInSequence;
5155
}
5256

57+
public MigrateVmToPoolCommand(String vmName, Collection<VolumeTO> volumes, String destinationPool, boolean executeInSequence) {
58+
this(vmName, volumes, destinationPool, null, executeInSequence);
59+
}
60+
5361
public Collection<VolumeTO> getVolumes() {
5462
return volumes;
5563
}
@@ -62,6 +70,10 @@ public String getVmName() {
6270
return vmName;
6371
}
6472

73+
public String getHostGuidInTargetCluster() {
74+
return hostGuidInTargetCluster;
75+
}
76+
6577
@Override
6678
public boolean executeInSequence() {
6779
return executeInSequence;

core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public class MigrateVolumeCommand extends Command {
3434
StorageFilerTO sourcePool;
3535
String attachedVmName;
3636
Volume.Type volumeType;
37+
private String hostGuidInTargetCluster;
3738

3839
private DataTO srcData;
3940
private DataTO destData;
@@ -54,9 +55,10 @@ public MigrateVolumeCommand(long volumeId, String volumePath, StoragePool pool,
5455
this.setWait(timeout);
5556
}
5657

57-
public MigrateVolumeCommand(long volumeId, String volumePath, StoragePool sourcePool, StoragePool targetPool) {
58+
public MigrateVolumeCommand(long volumeId, String volumePath, StoragePool sourcePool, StoragePool targetPool, String hostGuidInTargetCluster) {
5859
this(volumeId,volumePath,targetPool, null, Volume.Type.UNKNOWN, -1);
5960
this.sourcePool = new StorageFilerTO(sourcePool);
61+
this.hostGuidInTargetCluster = hostGuidInTargetCluster;
6062
}
6163

6264
public MigrateVolumeCommand(DataTO srcData, DataTO destData, Map<String, String> srcDetails, Map<String, String> destDetails, int timeout) {
@@ -101,6 +103,10 @@ public Volume.Type getVolumeType() {
101103
return volumeType;
102104
}
103105

106+
public String getHostGuidInTargetCluster() {
107+
return hostGuidInTargetCluster;
108+
}
109+
104110
public DataTO getSrcData() {
105111
return srcData;
106112
}

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

Lines changed: 64 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import javax.inject.Inject;
4141
import javax.naming.ConfigurationException;
4242

43-
import com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDao;
4443
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
4544
import org.apache.cloudstack.api.ApiConstants;
4645
import org.apache.cloudstack.api.command.admin.vm.MigrateVMCmd;
@@ -142,6 +141,7 @@
142141
import com.cloud.deploy.DeploymentPlanner;
143142
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
144143
import com.cloud.deploy.DeploymentPlanningManager;
144+
import com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDao;
145145
import com.cloud.event.EventTypes;
146146
import com.cloud.event.UsageEventUtils;
147147
import com.cloud.event.UsageEventVO;
@@ -2210,23 +2210,17 @@ private void orchestrateStorageMigration(final String vmUuid, final StoragePool
22102210
}
22112211
}
22122212

2213-
private Answer[] attemptHypervisorMigration(StoragePool destPool, VMInstanceVO vm) {
2213+
private Answer[] attemptHypervisorMigration(StoragePool destPool, VMInstanceVO vm, Long hostId) {
2214+
if (hostId == null) {
2215+
return null;
2216+
}
22142217
final HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vm.getHypervisorType());
22152218
// OfflineVmwareMigration: in case of vmware call vcenter to do it for us.
22162219
// OfflineVmwareMigration: should we check the proximity of source and destination
22172220
// OfflineVmwareMigration: if we are in the same cluster/datacentre/pool or whatever?
22182221
// OfflineVmwareMigration: we are checking on success to optionally delete an old vm if we are not
22192222
List<Command> commandsToSend = hvGuru.finalizeMigrate(vm, destPool);
22202223

2221-
Long hostId = vm.getHostId();
2222-
// OfflineVmwareMigration: probably this is null when vm is stopped
2223-
if(hostId == null) {
2224-
hostId = vm.getLastHostId();
2225-
if (s_logger.isDebugEnabled()) {
2226-
s_logger.debug(String.format("host id is null, using last host id %d", hostId) );
2227-
}
2228-
}
2229-
22302224
if(CollectionUtils.isNotEmpty(commandsToSend)) {
22312225
Commands commandsContainer = new Commands(Command.OnError.Stop);
22322226
commandsContainer.addCommands(commandsToSend);
@@ -2241,7 +2235,7 @@ private Answer[] attemptHypervisorMigration(StoragePool destPool, VMInstanceVO v
22412235
return null;
22422236
}
22432237

2244-
private void afterHypervisorMigrationCleanup(StoragePool destPool, VMInstanceVO vm, HostVO srcHost, Long srcClusterId, Answer[] hypervisorMigrationResults) throws InsufficientCapacityException {
2238+
private void afterHypervisorMigrationCleanup(StoragePool destPool, VMInstanceVO vm, Long srcClusterId, Answer[] hypervisorMigrationResults) throws InsufficientCapacityException {
22452239
boolean isDebugEnabled = s_logger.isDebugEnabled();
22462240
if(isDebugEnabled) {
22472241
String msg = String.format("cleaning up after hypervisor pool migration volumes for VM %s(%s) to pool %s(%s)", vm.getInstanceName(), vm.getUuid(), destPool.getName(), destPool.getUuid());
@@ -2250,18 +2244,23 @@ private void afterHypervisorMigrationCleanup(StoragePool destPool, VMInstanceVO
22502244
setDestinationPoolAndReallocateNetwork(destPool, vm);
22512245
// OfflineVmwareMigration: don't set this to null or have another way to address the command; twice migrating will lead to an NPE
22522246
Long destPodId = destPool.getPodId();
2253-
Long vmPodId = vm.getPodIdToDeployIn();
2254-
if (destPodId == null || ! destPodId.equals(vmPodId)) {
2247+
2248+
if (destPodId == null || !destPodId.equals(vm.getPodIdToDeployIn())) {
22552249
if(isDebugEnabled) {
22562250
String msg = String.format("resetting lasHost for VM %s(%s) as pod (%s) is no good.", vm.getInstanceName(), vm.getUuid(), destPodId);
22572251
s_logger.debug(msg);
22582252
}
2259-
22602253
vm.setLastHostId(null);
22612254
vm.setPodIdToDeployIn(destPodId);
22622255
// OfflineVmwareMigration: a consecutive migration will fail probably (no host not pod)
2263-
}// else keep last host set for this vm
2264-
markVolumesInPool(vm,destPool, hypervisorMigrationResults);
2256+
} else if (srcClusterId != null && destPool.getClusterId() != null && !srcClusterId.equals(destPool.getClusterId())) {
2257+
if(isDebugEnabled) {
2258+
String msg = String.format("resetting lasHost for VM %s(%s) as cluster changed", vm.getInstanceName(), vm.getUuid());
2259+
s_logger.debug(msg);
2260+
}
2261+
vm.setLastHostId(null);
2262+
} // else keep last host set for this vm
2263+
markVolumesInPool(vm, destPool, hypervisorMigrationResults);
22652264
// OfflineVmwareMigration: deal with answers, if (hypervisorMigrationResults.length > 0)
22662265
// OfflineVmwareMigration: iterate over the volumes for data updates
22672266
}
@@ -2295,23 +2294,60 @@ private void markVolumesInPool(VMInstanceVO vm, StoragePool destPool, Answer[] h
22952294
}
22962295
}
22972296

2297+
private Pair<Long, Long> findClusterAndHostIdForVm(VMInstanceVO vm) {
2298+
Long hostId = vm.getHostId();
2299+
Long clusterId = null;
2300+
// OfflineVmwareMigration: probably this is null when vm is stopped
2301+
if(hostId == null) {
2302+
hostId = vm.getLastHostId();
2303+
if (s_logger.isDebugEnabled()) {
2304+
s_logger.debug(String.format("host id is null, using last host id %d", hostId) );
2305+
}
2306+
}
2307+
if (hostId == null) {
2308+
List<VolumeVO> volumes = _volsDao.findByInstanceAndType(vm.getId(), Type.ROOT);
2309+
if (CollectionUtils.isNotEmpty(volumes)) {
2310+
for (VolumeVO rootVolume : volumes) {
2311+
if (rootVolume.getPoolId() != null) {
2312+
StoragePoolVO pool = _storagePoolDao.findById(rootVolume.getPoolId());
2313+
if (pool != null && pool.getClusterId() != null) {
2314+
clusterId = pool.getClusterId();
2315+
List<HostVO> hosts = _hostDao.findHypervisorHostInCluster(pool.getClusterId());
2316+
if (CollectionUtils.isNotEmpty(hosts)) {
2317+
hostId = hosts.get(0).getId();
2318+
break;
2319+
}
2320+
}
2321+
}
2322+
}
2323+
}
2324+
}
2325+
if (clusterId == null && hostId != null) {
2326+
HostVO host = _hostDao.findById(hostId);
2327+
if (host != null) {
2328+
clusterId = host.getClusterId();
2329+
}
2330+
}
2331+
return new Pair<>(clusterId, hostId);
2332+
}
2333+
22982334
private void migrateThroughHypervisorOrStorage(StoragePool destPool, VMInstanceVO vm) throws StorageUnavailableException, InsufficientCapacityException {
22992335
final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm);
2300-
final Long srchostId = vm.getHostId() != null ? vm.getHostId() : vm.getLastHostId();
2301-
final HostVO srcHost = _hostDao.findById(srchostId);
2302-
final Long srcClusterId = srcHost.getClusterId();
2303-
Answer[] hypervisorMigrationResults = attemptHypervisorMigration(destPool, vm);
2336+
Pair<Long, Long> vmClusterAndHost = findClusterAndHostIdForVm(vm);
2337+
final Long sourceClusterId = vmClusterAndHost.first();
2338+
final Long sourceHostId = vmClusterAndHost.second();
2339+
Answer[] hypervisorMigrationResults = attemptHypervisorMigration(destPool, vm, sourceHostId);
23042340
boolean migrationResult = false;
23052341
if (hypervisorMigrationResults == null) {
23062342
// OfflineVmwareMigration: if the HypervisorGuru can't do it, let the volume manager take care of it.
23072343
migrationResult = volumeMgr.storageMigration(profile, destPool);
23082344
if (migrationResult) {
2309-
afterStorageMigrationCleanup(destPool, vm, srcHost, srcClusterId);
2345+
afterStorageMigrationCleanup(destPool, vm, sourceHostId, sourceClusterId);
23102346
} else {
23112347
s_logger.debug("Storage migration failed");
23122348
}
23132349
} else {
2314-
afterHypervisorMigrationCleanup(destPool, vm, srcHost, srcClusterId, hypervisorMigrationResults);
2350+
afterHypervisorMigrationCleanup(destPool, vm, sourceClusterId, hypervisorMigrationResults);
23152351
}
23162352
}
23172353

@@ -2366,7 +2402,7 @@ static boolean matches(List<String> volumeTags, List<String> storagePoolTags) {
23662402
}
23672403

23682404

2369-
private void afterStorageMigrationCleanup(StoragePool destPool, VMInstanceVO vm, HostVO srcHost, Long srcClusterId) throws InsufficientCapacityException {
2405+
private void afterStorageMigrationCleanup(StoragePool destPool, VMInstanceVO vm, Long srcHostId, Long srcClusterId) throws InsufficientCapacityException {
23702406
setDestinationPoolAndReallocateNetwork(destPool, vm);
23712407

23722408
//when start the vm next time, don;'t look at last_host_id, only choose the host based on volume/storage pool
@@ -2376,7 +2412,7 @@ private void afterStorageMigrationCleanup(StoragePool destPool, VMInstanceVO vm,
23762412
// If VM was cold migrated between clusters belonging to two different VMware DCs,
23772413
// unregister the VM from the source host and cleanup the associated VM files.
23782414
if (vm.getHypervisorType().equals(HypervisorType.VMware)) {
2379-
afterStorageMigrationVmwareVMcleanup(destPool, vm, srcHost, srcClusterId);
2415+
afterStorageMigrationVmwareVMCleanup(destPool, vm, srcHostId, srcClusterId);
23802416
}
23812417
}
23822418

@@ -2394,14 +2430,14 @@ private void setDestinationPoolAndReallocateNetwork(StoragePool destPool, VMInst
23942430
}
23952431
}
23962432

2397-
private void afterStorageMigrationVmwareVMcleanup(StoragePool destPool, VMInstanceVO vm, HostVO srcHost, Long srcClusterId) {
2433+
private void afterStorageMigrationVmwareVMCleanup(StoragePool destPool, VMInstanceVO vm, Long srcHostId, Long srcClusterId) {
23982434
// OfflineVmwareMigration: this should only happen on storage migration, else the guru would already have issued the command
23992435
final Long destClusterId = destPool.getClusterId();
2400-
if (srcClusterId != null && destClusterId != null && ! srcClusterId.equals(destClusterId)) {
2436+
if (srcHostId != null && srcClusterId != null && destClusterId != null && ! srcClusterId.equals(destClusterId)) {
24012437
final String srcDcName = _clusterDetailsDao.getVmwareDcName(srcClusterId);
24022438
final String destDcName = _clusterDetailsDao.getVmwareDcName(destClusterId);
24032439
if (srcDcName != null && destDcName != null && !srcDcName.equals(destDcName)) {
2404-
removeStaleVmFromSource(vm, srcHost);
2440+
removeStaleVmFromSource(vm, _hostDao.findById(srcHostId));
24052441
}
24062442
}
24072443
}

0 commit comments

Comments
 (0)