This is an automated email from the ASF dual-hosted git repository.
rohit pushed a commit to branch 4.15
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.15 by this push:
new a30d518 vmware: fix stopped VM volume migration (#4758)
a30d518 is described below
commit a30d518e8a62cddeafeff0402bdf36c0cf615b8f
Author: Abhishek Kumar <[email protected]>
AuthorDate: Sat Apr 24 18:55:25 2021 +0530
vmware: fix stopped VM volume migration (#4758)
* prevent other vm disks getting deleted
Signed-off-by: Abhishek Kumar <[email protected]>
* vmware: fix inter-cluster stopped vm migration
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.
Signed-off-by: Abhishek Kumar <[email protected]>
* fix detached volume inter-cluster migration
Signed-off-by: Abhishek Kumar <[email protected]>
* cleanup unused method
Signed-off-by: Abhishek Kumar <[email protected]>
* review changes
Signed-off-by: Abhishek Kumar <[email protected]>
* changes
Signed-off-by: Abhishek Kumar <[email protected]>
* vmware: allow attached volume migration using VmwareStorageMotionStrategy
Signed-off-by: Abhishek Kumar <[email protected]>
* find vm clusterid with multiple ROOT volumes
VM can have multiple ROOT volumes and some can be on zone-wide store
therefore iterate over all of them till a cluster ID is found.
Signed-off-by: Abhishek Kumar <[email protected]>
* fix successive storage migration
Signed-off-by: Abhishek Kumar <[email protected]>
* fix intercluster check
Signed-off-by: Abhishek Kumar <[email protected]>
* refactor vm cluster, host method
Signed-off-by: Abhishek Kumar <[email protected]>
* remove inter-pod check
Added by mistake, VMware won't have pods
Signed-off-by: Abhishek Kumar <[email protected]>
* address review comment
Signed-off-by: Abhishek Kumar <[email protected]>
---
.../agent/api/storage/MigrateVolumeCommand.java | 4 +-
.../java/com/cloud/vm/VirtualMachineManager.java | 3 +
.../com/cloud/vm/VirtualMachineManagerImpl.java | 86 ++++++++-------
.../java/com/cloud/hypervisor/guru/VMwareGuru.java | 47 +-------
.../storage/resource/VmwareStorageProcessor.java | 19 ++--
.../motion/VmwareStorageMotionStrategy.java | 119 +++++++++++++--------
.../motion/VmwareStorageMotionStrategyTest.java | 45 ++++----
.../com/cloud/storage/VolumeApiServiceImpl.java | 20 ++--
8 files changed, 184 insertions(+), 159 deletions(-)
diff --git
a/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java
b/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java
index 5443ad1..0550e8d 100644
--- a/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/storage/MigrateVolumeCommand.java
@@ -55,8 +55,8 @@ public class MigrateVolumeCommand extends Command {
this.setWait(timeout);
}
- public MigrateVolumeCommand(long volumeId, String volumePath, StoragePool
sourcePool, StoragePool targetPool, String hostGuidInTargetCluster) {
- this(volumeId,volumePath,targetPool, null, Volume.Type.UNKNOWN, -1);
+ public MigrateVolumeCommand(long volumeId, String volumePath, String
attachedVmName, StoragePool sourcePool, StoragePool targetPool, String
hostGuidInTargetCluster) {
+ this(volumeId,volumePath,targetPool, attachedVmName,
Volume.Type.UNKNOWN, -1);
this.sourcePool = new StorageFilerTO(sourcePool);
this.hostGuidInTargetCluster = hostGuidInTargetCluster;
}
diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java
b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java
index 49b13e1..40777b38 100644
--- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java
+++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java
@@ -44,6 +44,7 @@ import com.cloud.storage.StoragePool;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.uservm.UserVm;
+import com.cloud.utils.Pair;
import com.cloud.utils.component.Manager;
import com.cloud.utils.fsm.NoTransitionException;
@@ -255,4 +256,6 @@ public interface VirtualMachineManager extends Manager {
boolean unmanage(String vmUuid);
UserVm restoreVirtualMachine(long vmId, Long newTemplateId) throws
ResourceUnavailableException, InsufficientCapacityException;
+
+ Pair<Long, Long> findClusterAndHostIdForVm(long vmId);
}
diff --git
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
index 6faeeb5..765bb90 100755
---
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -2294,43 +2294,6 @@ public class VirtualMachineManagerImpl extends
ManagerBase implements VirtualMac
}
}
- private Pair<Long, Long> findClusterAndHostIdForVm(VMInstanceVO vm) {
- Long hostId = vm.getHostId();
- Long clusterId = null;
- // OfflineVmwareMigration: probably this is null when vm is stopped
- if(hostId == null) {
- hostId = vm.getLastHostId();
- if (s_logger.isDebugEnabled()) {
- s_logger.debug(String.format("host id is null, using last host
id %d", hostId) );
- }
- }
- if (hostId == null) {
- List<VolumeVO> volumes =
_volsDao.findByInstanceAndType(vm.getId(), Type.ROOT);
- if (CollectionUtils.isNotEmpty(volumes)) {
- for (VolumeVO rootVolume : volumes) {
- if (rootVolume.getPoolId() != null) {
- StoragePoolVO pool =
_storagePoolDao.findById(rootVolume.getPoolId());
- if (pool != null && pool.getClusterId() != null) {
- clusterId = pool.getClusterId();
- List<HostVO> hosts =
_hostDao.findHypervisorHostInCluster(pool.getClusterId());
- if (CollectionUtils.isNotEmpty(hosts)) {
- hostId = hosts.get(0).getId();
- break;
- }
- }
- }
- }
- }
- }
- if (clusterId == null && hostId != null) {
- HostVO host = _hostDao.findById(hostId);
- if (host != null) {
- clusterId = host.getClusterId();
- }
- }
- return new Pair<>(clusterId, hostId);
- }
-
private void migrateThroughHypervisorOrStorage(StoragePool destPool,
VMInstanceVO vm) throws StorageUnavailableException,
InsufficientCapacityException {
final VirtualMachineProfile profile = new
VirtualMachineProfileImpl(vm);
Pair<Long, Long> vmClusterAndHost = findClusterAndHostIdForVm(vm);
@@ -5913,4 +5876,53 @@ public class VirtualMachineManagerImpl extends
ManagerBase implements VirtualMac
_jobMgr.marshallResultObject(result));
}
+ private Pair<Long, Long> findClusterAndHostIdForVmFromVolumes(long vmId) {
+ Long clusterId = null;
+ Long hostId = null;
+ List<VolumeVO> volumes = _volsDao.findByInstance(vmId);
+ for (VolumeVO volume : volumes) {
+ if (Volume.State.Ready.equals(volume.getState()) &&
+ volume.getPoolId() != null) {
+ StoragePoolVO pool =
_storagePoolDao.findById(volume.getPoolId());
+ if (pool != null && pool.getClusterId() != null) {
+ clusterId = pool.getClusterId();
+ // hostId to be used only for sending commands, capacity
check skipped
+ List<HostVO> hosts =
_hostDao.findHypervisorHostInCluster(pool.getClusterId());
+ if (CollectionUtils.isNotEmpty(hosts)) {
+ hostId = hosts.get(0).getId();
+ break;
+ }
+ }
+ }
+ }
+ return new Pair<>(clusterId, hostId);
+ }
+
+ private Pair<Long, Long> findClusterAndHostIdForVm(VirtualMachine vm) {
+ Long hostId = vm.getHostId();
+ Long clusterId = null;
+ if(hostId == null) {
+ hostId = vm.getLastHostId();
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug(String.format("host id is null, using last host
id %d", hostId) );
+ }
+ }
+ if (hostId == null) {
+ return findClusterAndHostIdForVmFromVolumes(vm.getId());
+ }
+ HostVO host = _hostDao.findById(hostId);
+ if (host != null) {
+ clusterId = host.getClusterId();
+ }
+ return new Pair<>(clusterId, hostId);
+ }
+
+ @Override
+ public Pair<Long, Long> findClusterAndHostIdForVm(long vmId) {
+ VMInstanceVO vm = _vmDao.findById(vmId);
+ if (vm == null) {
+ return new Pair<>(null, null);
+ }
+ return findClusterAndHostIdForVm(vm);
+ }
}
diff --git
a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
index edb3b88..48347d4 100644
---
a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
+++
b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
@@ -206,48 +206,7 @@ public class VMwareGuru extends HypervisorGuruBase
implements HypervisorGuru, Co
@Override public VirtualMachineTO implement(VirtualMachineProfile vm) {
vmwareVmImplementer.setGlobalNestedVirtualisationEnabled(VmwareEnableNestedVirtualization.value());
vmwareVmImplementer.setGlobalNestedVPerVMEnabled(VmwareEnableNestedVirtualizationPerVM.value());
- return vmwareVmImplementer.implement(vm, toVirtualMachineTO(vm),
getClusterId(vm.getId()));
- }
-
- private Long getClusterIdFromVmVolume(long vmId) {
- Long clusterId = null;
- List<VolumeVO> volumes = _volumeDao.findByInstanceAndType(vmId,
Volume.Type.ROOT);
- if (CollectionUtils.isNotEmpty(volumes)) {
- for (VolumeVO rootVolume : volumes) {
- if (rootVolume.getPoolId() != null) {
- StoragePoolVO pool =
_storagePoolDao.findById(rootVolume.getPoolId());
- if (pool != null && pool.getClusterId() != null) {
- clusterId = pool.getClusterId();
- break;
- }
- }
- }
- }
- return clusterId;
- }
-
- private Long getClusterId(long vmId) {
- Long clusterId = null;
- Long hostId = null;
- VMInstanceVO vm = _vmDao.findById(vmId);
- if (vm != null) {
- hostId = _vmDao.findById(vmId).getHostId();
- }
- if (vm != null && hostId == null) {
- // If VM is in stopped state then hostId would be undefined. Hence
read last host's Id instead.
- hostId = _vmDao.findById(vmId).getLastHostId();
- }
- HostVO host = null;
- if (hostId != null) {
- host = _hostDao.findById(hostId);
- }
- if (host != null) {
- clusterId = host.getClusterId();
- } else {
- clusterId = getClusterIdFromVmVolume(vmId);
- }
-
- return clusterId;
+ return vmwareVmImplementer.implement(vm, toVirtualMachineTO(vm),
vmManager.findClusterAndHostIdForVm(vm.getId()).first());
}
@Override @DB public Pair<Boolean, Long> getCommandHostDelegation(long
hostId, Command cmd) {
@@ -445,7 +404,7 @@ public class VMwareGuru extends HypervisorGuruBase
implements HypervisorGuru, Co
@Override public Map<String, String> getClusterSettings(long vmId) {
Map<String, String> details = new HashMap<String, String>();
- Long clusterId = getClusterId(vmId);
+ Long clusterId = vmManager.findClusterAndHostIdForVm(vmId).first();
if (clusterId != null) {
details.put(VmwareReserveCpu.key(),
VmwareReserveCpu.valueIn(clusterId).toString());
details.put(VmwareReserveMemory.key(),
VmwareReserveMemory.valueIn(clusterId).toString());
@@ -1120,7 +1079,7 @@ public class VMwareGuru extends HypervisorGuruBase
implements HypervisorGuru, Co
}
final Long destClusterId = destination.getClusterId();
- final Long srcClusterId = getClusterId(vm.getId());
+ final Long srcClusterId =
vmManager.findClusterAndHostIdForVm(vm.getId()).first();
final boolean isInterClusterMigration =
isInterClusterMigration(destClusterId, srcClusterId);
MigrateVmToPoolCommand migrateVmToPoolCommand = new
MigrateVmToPoolCommand(vm.getInstanceName(),
vols, destination.getUuid(),
getHostGuidInTargetCluster(isInterClusterMigration, destClusterId), true);
diff --git
a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java
b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java
index 1584bd0..a2aee6b 100644
---
a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java
+++
b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java
@@ -34,11 +34,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
-import com.cloud.hypervisor.vmware.mo.VirtualStorageObjectManagerMO;
-import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfoBuilder;
-import com.vmware.vim25.BaseConfigInfoDiskFileBackingInfo;
-import com.vmware.vim25.VStorageObject;
-import com.vmware.vim25.VirtualDiskType;
import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand;
import org.apache.cloudstack.storage.command.AttachAnswer;
import org.apache.cloudstack.storage.command.AttachCommand;
@@ -84,7 +79,9 @@ import com.cloud.hypervisor.vmware.mo.HostMO;
import com.cloud.hypervisor.vmware.mo.HostStorageSystemMO;
import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
import com.cloud.hypervisor.vmware.mo.NetworkDetails;
+import com.cloud.hypervisor.vmware.mo.VirtualMachineDiskInfoBuilder;
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
+import com.cloud.hypervisor.vmware.mo.VirtualStorageObjectManagerMO;
import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
import com.cloud.hypervisor.vmware.resource.VmwareResource;
import com.cloud.hypervisor.vmware.util.VmwareContext;
@@ -104,6 +101,7 @@ import com.cloud.vm.VirtualMachine.PowerState;
import com.cloud.vm.VmDetailConstants;
import com.google.common.base.Strings;
import com.google.gson.Gson;
+import com.vmware.vim25.BaseConfigInfoDiskFileBackingInfo;
import com.vmware.vim25.DatastoreHostMount;
import com.vmware.vim25.HostHostBusAdapter;
import com.vmware.vim25.HostInternetScsiHba;
@@ -122,11 +120,13 @@ import com.vmware.vim25.HostUnresolvedVmfsResignatureSpec;
import com.vmware.vim25.HostUnresolvedVmfsVolume;
import com.vmware.vim25.InvalidStateFaultMsg;
import com.vmware.vim25.ManagedObjectReference;
+import com.vmware.vim25.VStorageObject;
import com.vmware.vim25.VirtualDeviceBackingInfo;
import com.vmware.vim25.VirtualDeviceConfigSpec;
import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
import com.vmware.vim25.VirtualDisk;
import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
+import com.vmware.vim25.VirtualDiskType;
import com.vmware.vim25.VirtualMachineConfigSpec;
import com.vmware.vim25.VmConfigInfo;
import com.vmware.vim25.VmfsDatastoreExpandSpec;
@@ -2683,15 +2683,14 @@ public class VmwareStorageProcessor implements
StorageProcessor {
List<VirtualDisk> virtualDisks =
vmMo.getVirtualDisks();
List<String> managedDatastoreNames =
getManagedDatastoreNamesFromVirtualDisks(virtualDisks);
+ // Preserve other disks of the VM
+ List<String> detachedDisks =
vmMo.detachAllDisksExcept(vol.getPath(), diskInfo != null ?
diskInfo.getDiskDeviceBusName() : null);
+ VmwareStorageLayoutHelper.moveVolumeToRootFolder(new
DatacenterMO(context, morDc), detachedDisks);
// let vmMo.destroy to delete volume for us
// vmMo.tearDownDevices(new Class<?>[] {
VirtualDisk.class, VirtualEthernetCard.class });
-
if (isManaged) {
- List<String> detachedDisks =
vmMo.detachAllDisksExcept(vol.getPath(), diskInfo != null ?
diskInfo.getDiskDeviceBusName() : null);
-
VmwareStorageLayoutHelper.moveVolumeToRootFolder(new DatacenterMO(context,
morDc), detachedDisks);
vmMo.unregisterVm();
- }
- else {
+ } else {
vmMo.destroy();
}
diff --git
a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java
b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java
index 2854a7c..04111bc 100644
---
a/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java
+++
b/plugins/hypervisors/vmware/src/main/java/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategy.java
@@ -31,11 +31,9 @@ import
org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
-import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
-import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.log4j.Logger;
@@ -67,6 +65,8 @@ import com.cloud.storage.dao.VolumeDao;
import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.VMInstanceDao;
@Component
@@ -77,13 +77,13 @@ public class VmwareStorageMotionStrategy implements
DataMotionStrategy {
@Inject
VolumeDao volDao;
@Inject
- VolumeDataFactory volFactory;
- @Inject
PrimaryDataStoreDao storagePoolDao;
@Inject
VMInstanceDao instanceDao;
@Inject
- private HostDao hostDao;
+ HostDao hostDao;
+ @Inject
+ VirtualMachineManager vmManager;
@Override
public StrategyPriority canHandle(DataObject srcData, DataObject destData)
{
@@ -91,8 +91,7 @@ public class VmwareStorageMotionStrategy implements
DataMotionStrategy {
if (isOnVmware(srcData, destData)
&& isOnPrimary(srcData, destData)
&& isVolumesOnly(srcData, destData)
- && isIntraPodOrZoneWideStoreInvolved(srcData, destData)
- && isDettached(srcData)) {
+ && isDetachedOrAttachedToStoppedVM(srcData)) {
if (s_logger.isDebugEnabled()) {
String msg = String.format("%s can handle the request because
%d(%s) and %d(%s) share the pod"
, this.getClass()
@@ -107,20 +106,14 @@ public class VmwareStorageMotionStrategy implements
DataMotionStrategy {
return StrategyPriority.CANT_HANDLE;
}
- private boolean isIntraPodOrZoneWideStoreInvolved(DataObject srcData,
DataObject destData) {
- DataStore srcStore = srcData.getDataStore();
- StoragePoolVO srcPool = storagePoolDao.findById(srcStore.getId());
- DataStore destStore = destData.getDataStore();
- StoragePoolVO destPool = storagePoolDao.findById(destStore.getId());
- if (srcPool.getPodId() != null && destPool.getPodId() != null) {
- return srcPool.getPodId().equals(destPool.getPodId());
- }
- return (ScopeType.ZONE.equals(srcPool.getScope()) ||
ScopeType.ZONE.equals(destPool.getScope()));
+ private boolean isAttachedToStoppedVM(Volume volume) {
+ VMInstanceVO vm = instanceDao.findById(volume.getInstanceId());
+ return vm != null &&
VirtualMachine.State.Stopped.equals(vm.getState());
}
- private boolean isDettached(DataObject srcData) {
+ private boolean isDetachedOrAttachedToStoppedVM(DataObject srcData) {
VolumeVO volume = volDao.findById(srcData.getId());
- return volume.getInstanceId() == null;
+ return volume.getInstanceId() == null || isAttachedToStoppedVM(volume);
}
private boolean isVolumesOnly(DataObject srcData, DataObject destData) {
@@ -138,11 +131,46 @@ public class VmwareStorageMotionStrategy implements
DataMotionStrategy {
&&
HypervisorType.VMware.equals(destData.getTO().getHypervisorType());
}
- private Pair<Long, String>
getHostIdForVmAndHostGuidInTargetCluster(DataObject srcData, DataObject
destData) {
- StoragePool sourcePool = (StoragePool) srcData.getDataStore();
- ScopeType sourceScopeType =
srcData.getDataStore().getScope().getScopeType();
- StoragePool targetPool = (StoragePool) destData.getDataStore();
- ScopeType targetScopeType =
destData.getDataStore().getScope().getScopeType();
+ private String getHostGuidInTargetCluster (Long sourceClusterId,
+ StoragePool targetPool,
+ ScopeType targetScopeType) {
+ String hostGuidInTargetCluster = null;
+ if (ScopeType.CLUSTER.equals(targetScopeType) &&
!sourceClusterId.equals(targetPool.getClusterId())) {
+ // Without host vMotion might fail between non-shared storages
with error similar to,
+ // https://kb.vmware.com/s/article/1003795
+ List<HostVO> hosts =
hostDao.findHypervisorHostInCluster(targetPool.getClusterId());
+ if (CollectionUtils.isNotEmpty(hosts)) {
+ hostGuidInTargetCluster = hosts.get(0).getGuid();
+ }
+ if (hostGuidInTargetCluster == null) {
+ throw new CloudRuntimeException("Offline Migration failed,
unable to find suitable target host for VM placement while migrating between
storage pools of different cluster without shared storages");
+ }
+ }
+ return hostGuidInTargetCluster;
+ }
+
+ private VirtualMachine getVolumeVm(DataObject srcData) {
+ if (srcData instanceof VolumeInfo) {
+ return ((VolumeInfo)srcData).getAttachedVM();
+ }
+ VolumeVO volume = volDao.findById(srcData.getId());
+ return volume.getInstanceId() == null ? null :
instanceDao.findById(volume.getInstanceId());
+ }
+
+ private Pair<Long, String>
getHostIdForVmAndHostGuidInTargetClusterForAttachedVm(VirtualMachine vm,
+
StoragePool targetPool,
+
ScopeType targetScopeType) {
+ Pair<Long, Long> clusterAndHostId =
vmManager.findClusterAndHostIdForVm(vm.getId());
+ if (clusterAndHostId.second() == null) {
+ throw new CloudRuntimeException(String.format("Offline Migration
failed, unable to find host for VM: %s", vm.getUuid()));
+ }
+ return new Pair<>(clusterAndHostId.second(),
getHostGuidInTargetCluster(clusterAndHostId.first(), targetPool,
targetScopeType));
+ }
+
+ private Pair<Long, String>
getHostIdForVmAndHostGuidInTargetClusterForWorkerVm(StoragePool sourcePool,
+
ScopeType sourceScopeType,
+
StoragePool targetPool,
+
ScopeType targetScopeType) {
Long hostId = null;
String hostGuidInTargetCluster = null;
if (ScopeType.CLUSTER.equals(sourceScopeType)) {
@@ -151,17 +179,7 @@ public class VmwareStorageMotionStrategy implements
DataMotionStrategy {
if (hostId == null) {
throw new CloudRuntimeException("Offline Migration failed,
unable to find suitable host for worker VM placement in the cluster of storage
pool: " + sourcePool.getName());
}
- if (ScopeType.CLUSTER.equals(targetScopeType) &&
!sourcePool.getClusterId().equals(targetPool.getClusterId())) {
- // Without host vMotion might fail between non-shared storages
with error similar to,
- // https://kb.vmware.com/s/article/1003795
- List<HostVO> hosts =
hostDao.findHypervisorHostInCluster(targetPool.getClusterId());
- if (CollectionUtils.isNotEmpty(hosts)) {
- hostGuidInTargetCluster = hosts.get(0).getGuid();
- }
- if (hostGuidInTargetCluster == null) {
- throw new CloudRuntimeException("Offline Migration failed,
unable to find suitable target host for worker VM placement while migrating
between storage pools of different cluster without shared storages");
- }
- }
+ hostGuidInTargetCluster =
getHostGuidInTargetCluster(sourcePool.getClusterId(), targetPool,
sourceScopeType);
} else if (ScopeType.CLUSTER.equals(targetScopeType)) {
hostId =
findSuitableHostIdForWorkerVmPlacement(targetPool.getClusterId());
if (hostId == null) {
@@ -171,6 +189,19 @@ public class VmwareStorageMotionStrategy implements
DataMotionStrategy {
return new Pair<>(hostId, hostGuidInTargetCluster);
}
+ private Pair<Long, String>
getHostIdForVmAndHostGuidInTargetCluster(VirtualMachine vm,
+
DataObject srcData,
+
StoragePool sourcePool,
+
DataObject destData,
+
StoragePool targetPool) {
+ ScopeType sourceScopeType =
srcData.getDataStore().getScope().getScopeType();
+ ScopeType targetScopeType =
destData.getDataStore().getScope().getScopeType();
+ if (vm != null) {
+ return getHostIdForVmAndHostGuidInTargetClusterForAttachedVm(vm,
targetPool, targetScopeType);
+ }
+ return getHostIdForVmAndHostGuidInTargetClusterForWorkerVm(sourcePool,
sourceScopeType, targetPool, targetScopeType);
+ }
+
@Override
public StrategyPriority canHandle(Map<VolumeInfo, DataStore> volumeMap,
Host srcHost, Host destHost) {
if (srcHost.getHypervisorType() == HypervisorType.VMware &&
destHost.getHypervisorType() == HypervisorType.VMware) {
@@ -205,17 +236,18 @@ public class VmwareStorageMotionStrategy implements
DataMotionStrategy {
// OfflineVmwareMigration: we shouldn't be here as we would have
refused in the canHandle call
throw new UnsupportedOperationException();
}
- Pair<Long, String> hostIdForVmAndHostGuidInTargetCluster =
getHostIdForVmAndHostGuidInTargetCluster(srcData, destData);
- Long hostId = hostIdForVmAndHostGuidInTargetCluster.first();
- String hostGuidInTargetCluster =
hostIdForVmAndHostGuidInTargetCluster.second();
+ VirtualMachine vm = getVolumeVm(srcData);
StoragePool sourcePool = (StoragePool) srcData.getDataStore();
StoragePool targetPool = (StoragePool) destData.getDataStore();
+ Pair<Long, String> hostIdForVmAndHostGuidInTargetCluster =
+ getHostIdForVmAndHostGuidInTargetCluster(vm, srcData,
sourcePool, destData, targetPool);
+ Long hostId = hostIdForVmAndHostGuidInTargetCluster.first();
MigrateVolumeCommand cmd = new MigrateVolumeCommand(srcData.getId()
, srcData.getTO().getPath()
+ , vm != null ? vm.getInstanceName() : null
, sourcePool
, targetPool
- , hostGuidInTargetCluster);
- // OfflineVmwareMigration: should be
((StoragePool)srcData.getDataStore()).getHypervisor() but that is NULL, so
hardcoding
+ , hostIdForVmAndHostGuidInTargetCluster.second());
Answer answer;
if (hostId != null) {
answer = agentMgr.easySend(hostId, cmd);
@@ -255,9 +287,11 @@ public class VmwareStorageMotionStrategy implements
DataMotionStrategy {
VolumeVO sourceVO = volDao.findById(srcData.getId());
sourceVO.setState(Volume.State.Ready);
volDao.update(sourceVO.getId(), sourceVO);
- destinationVO.setState(Volume.State.Expunged);
- destinationVO.setRemoved(new Date());
- volDao.update(destinationVO.getId(), destinationVO);
+ if (destinationVO.getId() != sourceVO.getId()) {
+ destinationVO.setState(Volume.State.Expunged);
+ destinationVO.setRemoved(new Date());
+ volDao.update(destinationVO.getId(), destinationVO);
+ }
throw new CloudRuntimeException("unexpected answer from hypervisor
agent: " + answer.getDetails());
}
MigrateVolumeAnswer ans = (MigrateVolumeAnswer) answer;
@@ -266,6 +300,7 @@ public class VmwareStorageMotionStrategy implements
DataMotionStrategy {
s_logger.debug(String.format(format, ans.getVolumePath(),
destData.getId()));
}
// OfflineVmwareMigration: update the volume with new pool/volume path
+ destinationVO.setPoolId(destData.getDataStore().getId());
destinationVO.setPath(ans.getVolumePath());
volDao.update(destinationVO.getId(), destinationVO);
}
diff --git
a/plugins/hypervisors/vmware/src/test/java/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategyTest.java
b/plugins/hypervisors/vmware/src/test/java/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategyTest.java
index 4cc3a77..e59c797 100644
---
a/plugins/hypervisors/vmware/src/test/java/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategyTest.java
+++
b/plugins/hypervisors/vmware/src/test/java/org/apache/cloudstack/storage/motion/VmwareStorageMotionStrategyTest.java
@@ -16,6 +16,13 @@
// under the License.
package org.apache.cloudstack.storage.motion;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@@ -23,17 +30,6 @@ import java.util.Map;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
-import com.cloud.agent.AgentManager;
-import com.cloud.agent.api.MigrateWithStorageAnswer;
-import com.cloud.agent.api.MigrateWithStorageCommand;
-import com.cloud.agent.api.to.VirtualMachineTO;
-import com.cloud.host.Host;
-import com.cloud.host.dao.HostDao;
-import com.cloud.hypervisor.Hypervisor.HypervisorType;
-import com.cloud.storage.dao.VolumeDao;
-import com.cloud.utils.component.ComponentContext;
-import com.cloud.vm.VMInstanceVO;
-import com.cloud.vm.dao.VMInstanceDao;
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
@@ -63,12 +59,18 @@ import
org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.MigrateWithStorageAnswer;
+import com.cloud.agent.api.MigrateWithStorageCommand;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.host.Host;
+import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.utils.component.ComponentContext;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachineManager;
+import com.cloud.vm.dao.VMInstanceDao;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
@@ -87,7 +89,9 @@ public class VmwareStorageMotionStrategyTest {
@Inject
VMInstanceDao instanceDao;
@Inject
- private HostDao hostDao;
+ HostDao hostDao;
+ @Inject
+ VirtualMachineManager vmManager;
CopyCommandResult result;
@@ -268,6 +272,11 @@ public class VmwareStorageMotionStrategyTest {
return Mockito.mock(HostDao.class);
}
+ @Bean
+ public VirtualMachineManager vmManager() {
+ return Mockito.mock(VirtualMachineManager.class);
+ }
+
public static class Library implements TypeFilter {
@Override
public boolean match(MetadataReader mdr, MetadataReaderFactory
arg1) throws IOException {
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index 05ce543..74b65b8 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -31,7 +31,6 @@ import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
-import com.cloud.dc.Pod;
import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
@@ -100,6 +99,7 @@ import com.cloud.configuration.Resource.ResourceType;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.Pod;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.domain.Domain;
import com.cloud.event.ActionEvent;
@@ -2227,13 +2227,13 @@ public class VolumeApiServiceImpl extends ManagerBase
implements VolumeApiServic
}
// Check that Vm to which this volume is attached does not have VM
Snapshots
- // OfflineVmwareMigration: considder if this is needed and desirable
+ // OfflineVmwareMigration: consider if this is needed and desirable
if (vm != null && _vmSnapshotDao.findByVm(vm.getId()).size() > 0) {
throw new InvalidParameterValueException("Volume cannot be
migrated, please remove all VM snapshots for VM to which this volume is
attached");
}
// OfflineVmwareMigration: extract this block as method and check if
it is subject to regression
- if (vm != null && vm.getState() == State.Running) {
+ if (vm != null && State.Running.equals(vm.getState())) {
// Check if the VM is GPU enabled.
if
(_serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(),
GPU.Keys.pciDevice.toString()) != null) {
throw new InvalidParameterValueException("Live Migration of
GPU enabled VM is not supported");
@@ -2263,10 +2263,17 @@ public class VolumeApiServiceImpl extends ManagerBase
implements VolumeApiServic
if (!liveMigrateVolume) {
throw new InvalidParameterValueException("Volume needs to be
detached from VM");
}
+
+ if (!cmd.isLiveMigrate()) {
+ throw new InvalidParameterValueException("The volume " + vol +
"is attached to a vm and for migrating it " + "the parameter livemigrate should
be specified");
+ }
}
- if (liveMigrateVolume && !cmd.isLiveMigrate()) {
- throw new InvalidParameterValueException("The volume " + vol + "is
attached to a vm and for migrating it " + "the parameter livemigrate should be
specified");
+ if (vm != null &&
+ HypervisorType.VMware.equals(vm.getHypervisorType()) &&
+ State.Stopped.equals(vm.getState())) {
+ // For VMware, use liveMigrateVolume=true so that it follows
VmwareStorageMotionStrategy
+ liveMigrateVolume = true;
}
StoragePool destPool =
(StoragePool)dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
@@ -2299,7 +2306,8 @@ public class VolumeApiServiceImpl extends ManagerBase
implements VolumeApiServic
getStoragePoolTags(destPool), vol.getName(),
vol.getUuid(), diskOffering.getTags()));
}
- if (liveMigrateVolume && destPool.getClusterId() != null &&
srcClusterId != null) {
+ if (liveMigrateVolume && State.Running.equals(vm.getState()) &&
+ destPool.getClusterId() != null && srcClusterId != null) {
if (!srcClusterId.equals(destPool.getClusterId())) {
throw new InvalidParameterValueException("Cannot migrate a
volume of a virtual machine to a storage pool in a different cluster");
}