CLOUDSTACK-8318. Storage vMotion support for VMFS.

MigrateVMWithVolumes-
1. If ESXi host version is below 5.1, ensure destination datastore(s) is mounted on the source host, then migrate the storage and then finally migrate the VM.
If destination storage(s) is not mounted on the source host,
- In case of NFS storage mount the storage(s).
- In case of VMFS storage fail the request for migration.
2. If EXi host version is 5.1 or above, simultaneously migrate the VM and its storage to the destination host and storage(s) respectively for both NFS and VMFS storage.
This commit is contained in:
Likitha Shetty 2015-02-26 15:42:40 +05:30
parent c27c69438b
commit adc836cc5e
2 changed files with 94 additions and 43 deletions

View File

@ -3018,15 +3018,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
ManagedObjectReference morDc = null; ManagedObjectReference morDc = null;
ManagedObjectReference morDcOfTargetHost = null; ManagedObjectReference morDcOfTargetHost = null;
ManagedObjectReference morTgtHost = new ManagedObjectReference(); ManagedObjectReference morTgtHost = new ManagedObjectReference();
ManagedObjectReference morTgtDatastore = new ManagedObjectReference();
VirtualMachineRelocateSpec relocateSpec = new VirtualMachineRelocateSpec(); VirtualMachineRelocateSpec relocateSpec = new VirtualMachineRelocateSpec();
List<VirtualMachineRelocateSpecDiskLocator> diskLocators = new ArrayList<VirtualMachineRelocateSpecDiskLocator>(); List<VirtualMachineRelocateSpecDiskLocator> diskLocators = new ArrayList<VirtualMachineRelocateSpecDiskLocator>();
VirtualMachineRelocateSpecDiskLocator diskLocator = null; VirtualMachineRelocateSpecDiskLocator diskLocator = null;
boolean isFirstDs = true; boolean isFirstDs = true;
String tgtDsName = ""; String tgtDsName = "";
String tgtDsNfsHost; String tgtDsHost;
String tgtDsNfsPath; String tgtDsPath;
int tgtDsNfsPort; int tgtDsPort;
VolumeTO volume; VolumeTO volume;
StorageFilerTO filerTo; StorageFilerTO filerTo;
Set<String> mountedDatastoresAtSource = new HashSet<String>(); Set<String> mountedDatastoresAtSource = new HashSet<String>();
@ -3049,6 +3050,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
throw new CloudRuntimeException(msg); throw new CloudRuntimeException(msg);
} }
VmwareManager mgr = tgtHyperHost.getContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME); VmwareManager mgr = tgtHyperHost.getContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
String srcHostApiVersion = ((HostMO)srcHyperHost).getHostAboutInfo().getApiVersion();
// find VM through datacenter (VM is not at the target host yet) // find VM through datacenter (VM is not at the target host yet)
vmMo = srcHyperHost.findVmOnPeerHyperHost(vmName); vmMo = srcHyperHost.findVmOnPeerHyperHost(vmName);
@ -3059,47 +3061,68 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
} }
vmName = vmMo.getName(); vmName = vmMo.getName();
// Get details of each target datastore & attach to source host. // Specify destination datastore location for each volume
for (Entry<VolumeTO, StorageFilerTO> entry : volToFiler.entrySet()) { for (Entry<VolumeTO, StorageFilerTO> entry : volToFiler.entrySet()) {
volume = entry.getKey(); volume = entry.getKey();
filerTo = entry.getValue(); filerTo = entry.getValue();
tgtDsName = filerTo.getUuid().replace("-", "");
tgtDsNfsHost = filerTo.getHost();
tgtDsNfsPath = filerTo.getPath();
tgtDsNfsPort = filerTo.getPort();
s_logger.debug("Preparing spec for volume : " + volume.getName()); s_logger.debug("Preparing spec for volume : " + volume.getName());
morDsAtTarget = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(tgtHyperHost, filerTo.getUuid()); morDsAtTarget = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(tgtHyperHost, filerTo.getUuid());
morDsAtSource = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(srcHyperHost, filerTo.getUuid());
if (morDsAtTarget == null) { if (morDsAtTarget == null) {
String msg = "Unable to find the mounted datastore with uuid " + morDsAtTarget + " to execute MigrateWithStorageCommand"; String msg = "Unable to find the mounted datastore with uuid " + morDsAtTarget + " to execute MigrateWithStorageCommand";
s_logger.error(msg); s_logger.error(msg);
throw new Exception(msg); throw new Exception(msg);
} }
morDsAtSource = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(srcHyperHost, filerTo.getUuid()); morTgtDatastore = morDsAtTarget;
// If host version is below 5.1 then simultaneous change of VM's datastore and host is not supported.
// So since only the datastore will be changed first, ensure the target datastore is mounted on source host.
if (srcHostApiVersion.compareTo("5.1") < 0) {
tgtDsName = filerTo.getUuid().replace("-", "");
tgtDsHost = filerTo.getHost();
tgtDsPath = filerTo.getPath();
tgtDsPort = filerTo.getPort();
// If datastore is NFS and target datastore is not already mounted on source host then mount the datastore.
if (filerTo.getType().equals(StoragePoolType.NetworkFilesystem)) {
if (morDsAtSource == null) { if (morDsAtSource == null) {
morDsAtSource = srcHyperHost.mountDatastore(false, tgtDsNfsHost, tgtDsNfsPort, tgtDsNfsPath, tgtDsName); morDsAtSource = srcHyperHost.mountDatastore(false, tgtDsHost, tgtDsPort, tgtDsPath, tgtDsName);
if (morDsAtSource == null) { if (morDsAtSource == null) {
throw new Exception("Unable to mount datastore " + tgtDsNfsHost + ":/" + tgtDsNfsPath + " on " + _hostName); throw new Exception("Unable to mount NFS datastore " + tgtDsHost + ":/" + tgtDsPath + " on " + _hostName);
} }
mountedDatastoresAtSource.add(tgtDsName); mountedDatastoresAtSource.add(tgtDsName);
s_logger.debug("Mounted datastore " + tgtDsNfsHost + ":/" + tgtDsNfsPath + " on " + _hostName); s_logger.debug("Mounted datastore " + tgtDsHost + ":/" + tgtDsPath + " on " + _hostName);
}
}
// If datastore is VMFS and target datastore is not mounted or accessible to source host then fail migration.
if (filerTo.getType().equals(StoragePoolType.VMFS)) {
if (morDsAtSource == null) {
s_logger.warn("If host version is below 5.1, then target VMFS datastore(s) need to manually mounted on source host for a successful live storage migration.");
throw new Exception("Target VMFS datastore: " + tgtDsPath + " is not mounted on source host: " + _hostName);
}
DatastoreMO dsAtSourceMo = new DatastoreMO(getServiceContext(), morDsAtSource);
String srcHostValue = srcHyperHost.getMor().getValue();
if(!dsAtSourceMo.isAccessibleToHost(srcHostValue)) {
s_logger.warn("If host version is below 5.1, then target VMFS datastore(s) need to accessible to source host for a successful live storage migration.");
throw new Exception("Target VMFS datastore: " + tgtDsPath + " is not accessible on source host: " + _hostName);
}
}
morTgtDatastore = morDsAtSource;
} }
if (isFirstDs) { if (isFirstDs) {
relocateSpec.setDatastore(morDsAtSource); relocateSpec.setDatastore(morTgtDatastore);
isFirstDs = false; isFirstDs = false;
} }
VmwareStorageLayoutHelper.getVmwareDatastorePathFromVmdkFileName(new DatastoreMO(srcHyperHost.getContext(), morDsAtSource), vmName, volume.getPath() +
".vmdk");
diskLocator = new VirtualMachineRelocateSpecDiskLocator(); diskLocator = new VirtualMachineRelocateSpecDiskLocator();
diskLocator.setDatastore(morDsAtSource); diskLocator.setDatastore(morTgtDatastore);
int diskId = getVirtualDiskInfo(vmMo, volume.getPath() + ".vmdk"); int diskId = getVirtualDiskInfo(vmMo, volume.getPath() + ".vmdk");
diskLocator.setDiskId(diskId); diskLocator.setDiskId(diskId);
diskLocators.add(diskLocator); diskLocators.add(diskLocator);
volumeDeviceKey.put(volume.getId(), diskId); volumeDeviceKey.put(volume.getId(), diskId);
} }
relocateSpec.getDisk().addAll(diskLocators); relocateSpec.getDisk().addAll(diskLocators);
@ -3123,24 +3146,40 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
throw new Exception(msg); throw new Exception(msg);
} }
// Change datastore if (srcHostApiVersion.compareTo("5.1") < 0) {
// Migrate VM's volumes to target datastore(s).
if (!vmMo.changeDatastore(relocateSpec)) { if (!vmMo.changeDatastore(relocateSpec)) {
throw new Exception("Change datastore operation failed during storage migration"); throw new Exception("Change datastore operation failed during storage migration");
} else { } else {
s_logger.debug("Successfully migrated storage of VM " + vmName + " to target datastore(s)"); s_logger.debug("Successfully migrated storage of VM " + vmName + " to target datastore(s)");
} }
// Migrate VM to target host.
ManagedObjectReference morPool = tgtHyperHost.getHyperHostOwnerResourcePool();
if (!vmMo.migrate(morPool, tgtHyperHost.getMor())) {
throw new Exception("VM migration to target host failed during storage migration");
} else {
s_logger.debug("Successfully migrated VM " + vmName + " from " + _hostName + " to " + tgtHyperHost.getHyperHostName());
}
} else {
// Simultaneously migrate VM's volumes to target datastore and VM to target host.
relocateSpec.setHost(tgtHyperHost.getMor());
relocateSpec.setPool(tgtHyperHost.getHyperHostOwnerResourcePool());
if (!vmMo.changeDatastore(relocateSpec)) {
throw new Exception("Change datastore operation failed during storage migration");
} else {
s_logger.debug("Successfully migrated VM " + vmName + " from " + _hostName + " to " + tgtHyperHost.getHyperHostName() +
" and its storage to target datastore(s)");
}
}
// Consolidate VM disks. // Consolidate VM disks.
// In case of a linked clone VM, if VM's disks are not consolidated, // In case of a linked clone VM, if VM's disks are not consolidated, further VM operations such as volume snapshot, VM snapshot etc. will result in DB inconsistencies.
// further VM operations such as volume snapshot, VM snapshot etc. will result in DB inconsistencies.
String apiVersion = HypervisorHostHelper.getVcenterApiVersion(vmMo.getContext());
if (apiVersion.compareTo("5.0") >= 0) {
if (!vmMo.consolidateVmDisks()) { if (!vmMo.consolidateVmDisks()) {
s_logger.warn("VM disk consolidation failed after storage migration. Yet proceeding with VM migration."); s_logger.warn("VM disk consolidation failed after storage migration. Yet proceeding with VM migration.");
} else { } else {
s_logger.debug("Successfully consolidated disks of VM " + vmName + "."); s_logger.debug("Successfully consolidated disks of VM " + vmName + ".");
} }
}
// Update and return volume path for every disk because that could have changed after migration // Update and return volume path for every disk because that could have changed after migration
for (Entry<VolumeTO, StorageFilerTO> entry : volToFiler.entrySet()) { for (Entry<VolumeTO, StorageFilerTO> entry : volToFiler.entrySet()) {
@ -3158,14 +3197,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
} }
} }
// Change host
ManagedObjectReference morPool = tgtHyperHost.getHyperHostOwnerResourcePool();
if (!vmMo.migrate(morPool, tgtHyperHost.getMor())) {
throw new Exception("Change datastore operation failed during storage migration");
} else {
s_logger.debug("Successfully relocated VM " + vmName + " from " + _hostName + " to " + tgtHyperHost.getHyperHostName());
}
return new MigrateWithStorageAnswer(cmd, volumeToList); return new MigrateWithStorageAnswer(cmd, volumeToList);
} catch (Throwable e) { } catch (Throwable e) {
if (e instanceof RemoteException) { if (e instanceof RemoteException) {
@ -3215,7 +3246,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
try { try {
srcHyperHost = getHyperHost(getServiceContext()); srcHyperHost = getHyperHost(getServiceContext());
morDc = srcHyperHost.getHyperHostDatacenter(); morDc = srcHyperHost.getHyperHostDatacenter();
tgtDsName = poolTo.getUuid().replace("-", ""); tgtDsName = poolTo.getUuid();
// find VM in this datacenter not just in this cluster. // find VM in this datacenter not just in this cluster.
DatacenterMO dcMo = new DatacenterMO(getServiceContext(), morDc); DatacenterMO dcMo = new DatacenterMO(getServiceContext(), morDc);

View File

@ -21,9 +21,11 @@ import java.util.List;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import com.vmware.vim25.DatastoreHostMount;
import com.vmware.vim25.DatastoreSummary; import com.vmware.vim25.DatastoreSummary;
import com.vmware.vim25.FileInfo; import com.vmware.vim25.FileInfo;
import com.vmware.vim25.HostDatastoreBrowserSearchResults; import com.vmware.vim25.HostDatastoreBrowserSearchResults;
import com.vmware.vim25.HostMountInfo;
import com.vmware.vim25.ManagedObjectReference; import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.ObjectContent; import com.vmware.vim25.ObjectContent;
import com.vmware.vim25.ObjectSpec; import com.vmware.vim25.ObjectSpec;
@ -66,6 +68,10 @@ public class DatastoreMO extends BaseMO {
return new HostDatastoreBrowserMO(_context, (ManagedObjectReference)_context.getVimClient().getDynamicProperty(_mor, "browser")); return new HostDatastoreBrowserMO(_context, (ManagedObjectReference)_context.getVimClient().getDynamicProperty(_mor, "browser"));
} }
public List<DatastoreHostMount> getHostMounts() throws Exception {
return _context.getVimClient().getDynamicProperty(_mor, "host");
}
public String getInventoryPath() throws Exception { public String getInventoryPath() throws Exception {
Pair<DatacenterMO, String> dcInfo = getOwnerDatacenter(); Pair<DatacenterMO, String> dcInfo = getOwnerDatacenter();
return dcInfo.second() + "/" + getName(); return dcInfo.second() + "/" + getName();
@ -380,4 +386,18 @@ public class DatastoreMO extends BaseMO {
} }
return absoluteFileName; return absoluteFileName;
} }
public boolean isAccessibleToHost(String hostValue) throws Exception {
boolean isAccessible = true;
List<DatastoreHostMount> hostMounts = getHostMounts();
for (DatastoreHostMount hostMount : hostMounts) {
String hostMountValue = hostMount.getKey().getValue();
if (hostMountValue.equalsIgnoreCase(hostValue)) {
HostMountInfo mountInfo = hostMount.getMountInfo();
isAccessible = mountInfo.isAccessible();
break;
}
}
return isAccessible;
}
} }