mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added VmwareStorageMotionStrategy to deal with storage motion tasks. Added target host parameter to MigrateWithStorageCommand.
Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added Resource changes to perform VM live migration along with virtual disks across the clusters in a zone. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added VmwareStorageMotionStrategy to application context. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> Support for volume live migration across datastores Unit tests for vmware storage motion. These test the VmwareStorageMotionStrategy. CLOUDSTACK-659 Fixing migrate volume. CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added Resource changes to perform VM live migration along with virtual disks across the clusters in a zone. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> Support for volume live migration across datastores CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added Resource changes to perform VM live migration along with virtual disks across the clusters in a zone. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> Support for volume live migration across datastores CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added Resource changes to perform VM live migration along with virtual disks across the clusters in a zone. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> Support for volume live migration across datastores Added vm type to prepareNetworkFromNicInfo in MigrateWithStorageCommand implementation. CLOUDSTACK-2701 - Enable storage migration for VMware resources Fixing attach volume and delete volume cases for volumes that are moved off original path in datastore when created. If volume is not found in root directory or datastore, do search in sub folders. CLOUDSTACK-2701 - Enable storage migration for VMware resources Sending command MigrateWithStorageCommand to source host instead of target host for the case of migration of VM within cluster. CLOUDSTACK-2701 - Enable storage migration for VMware resources Searching for virtual disk during device tear down. Adding dependency of 'cloud-engine-storage' to vmware hypervisor plugin. Add hypervisor capability storage_motion_supported for VMware 5.0 and 5.1 CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added VmwareStorageMotionStrategy to deal with storage motion tasks. Added target host parameter to MigrateWithStorageCommand. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added Resource changes to perform VM live migration along with virtual disks across the clusters in a zone. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added VmwareStorageMotionStrategy to application context. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> Support for volume live migration across datastores Unit tests for vmware storage motion. These test the VmwareStorageMotionStrategy. CLOUDSTACK-659 Fixing migrate volume. CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added Resource changes to perform VM live migration along with virtual disks across the clusters in a zone. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> Support for volume live migration across datastores Added vm type to prepareNetworkFromNicInfo in MigrateWithStorageCommand implementation. Adding dependency of 'cloud-engine-storage' to vmware hypervisor plugin. Add hypervisor capability storage_motion_supported for VMware 5.0 and 5.1 CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added VmwareStorageMotionStrategy to deal with storage motion tasks. Added target host parameter to MigrateWithStorageCommand. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added Resource changes to perform VM live migration along with virtual disks across the clusters in a zone. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added VmwareStorageMotionStrategy to application context. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> Support for volume live migration across datastores Unit tests for vmware storage motion. These test the VmwareStorageMotionStrategy. CLOUDSTACK-659 Fixing migrate volume. Adding dependency of 'cloud-engine-storage' to vmware hypervisor plugin. Add hypervisor capability storage_motion_supported for VMware 5.0 and 5.1 Adding dependency of 'cloud-engine-storage' to vmware hypervisor plugin. CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added VmwareStorageMotionStrategy to deal with storage motion tasks. Added target host parameter to MigrateWithStorageCommand. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added Resource changes to perform VM live migration along with virtual disks across the clusters in a zone. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> CLOUDSTACK-659 Support for storage migration in Cloudstack deployment over VMware Added VmwareStorageMotionStrategy to application context. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org> Support for volume live migration across datastores CLOUDSTACK-659 Fixing migrate volume. Unit tests for vmware storage motion. These test the VmwareStorageMotionStrategy. CLOUDSTACK-2701 - Enable storage migration for VMware resources Sending command MigrateWithStorageCommand to source host instead of target host for the case of migration of VM within cluster. CLOUDSTACK-2701 - Enable storage migration for VMware resources Moved 2 methods that are not specific to VMware but Volume are moved to VolumeManager from VmwareManager. Moved datastore volume path constructing code to separate method. Added check for source and target host, if they are from different DCs/vCenter instances. Updated error message & removed stale comment CLOUDSTACK-2701 - Enable storage migration for VMware resources Injecting component VolumeManager into VmwareResource. Signed-off-by: Sateesh Chodapuneedi <sateesh@apache.org>
This commit is contained in:
parent
5c523eb892
commit
f24e81fe0d
@ -733,6 +733,7 @@
|
||||
<bean id="agentMonitor" class="com.cloud.agent.manager.AgentMonitor" />
|
||||
<bean id="alertGenerator" class="com.cloud.event.AlertGenerator" />
|
||||
<bean id="ancientDataMotionStrategy" class="org.apache.cloudstack.storage.motion.AncientDataMotionStrategy" />
|
||||
<bean id="vmwareStorageMotionStrategy" class="org.apache.cloudstack.storage.motion.VmwareStorageMotionStrategy"/>
|
||||
<bean id="xenserverStorageMotionStrategy" class="org.apache.cloudstack.storage.motion.XenServerStorageMotionStrategy" />
|
||||
<bean id="ancientImageDataStoreProvider" class="org.apache.cloudstack.storage.image.store.AncientImageDataStoreProvider" />
|
||||
<bean id="ancientSnapshotStrategy" class="org.apache.cloudstack.storage.snapshot.strategy.AncientSnapshotStrategy" />
|
||||
|
||||
@ -24,10 +24,18 @@ import com.cloud.agent.api.to.StorageFilerTO;
|
||||
public class MigrateWithStorageCommand extends Command {
|
||||
VirtualMachineTO vm;
|
||||
Map<VolumeTO, StorageFilerTO> volumeToFiler;
|
||||
String tgtHost;
|
||||
|
||||
public MigrateWithStorageCommand(VirtualMachineTO vm, Map<VolumeTO, StorageFilerTO> volumeToFiler) {
|
||||
this.vm = vm;
|
||||
this.volumeToFiler = volumeToFiler;
|
||||
this.tgtHost = null;
|
||||
}
|
||||
|
||||
public MigrateWithStorageCommand(VirtualMachineTO vm, Map<VolumeTO, StorageFilerTO> volumeToFiler, String tgtHost) {
|
||||
this.vm = vm;
|
||||
this.volumeToFiler = volumeToFiler;
|
||||
this.tgtHost = tgtHost;
|
||||
}
|
||||
|
||||
public VirtualMachineTO getVirtualMachine() {
|
||||
@ -38,6 +46,10 @@ public class MigrateWithStorageCommand extends Command {
|
||||
return volumeToFiler;
|
||||
}
|
||||
|
||||
public String getTargetHost() {
|
||||
return tgtHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean executeInSequence() {
|
||||
return true;
|
||||
|
||||
@ -20,6 +20,7 @@ import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.agent.api.to.VolumeTO;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.hypervisor.vmware.manager.VmwareStorageManager;
|
||||
import com.cloud.hypervisor.vmware.mo.HostMO;
|
||||
|
||||
@ -38,6 +38,8 @@ import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.cloudstack.api.command.admin.zone.AddVmwareDcCmd;
|
||||
import org.apache.cloudstack.api.command.admin.zone.RemoveVmwareDcCmd;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreRole;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
@ -48,6 +50,8 @@ import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.StartupCommand;
|
||||
import com.cloud.agent.api.StartupRoutingCommand;
|
||||
import com.cloud.agent.api.storage.MigrateVolumeAnswer;
|
||||
import com.cloud.agent.api.to.VolumeTO;
|
||||
import com.cloud.cluster.ClusterManager;
|
||||
import com.cloud.configuration.Config;
|
||||
import com.cloud.configuration.dao.ConfigurationDao;
|
||||
@ -99,6 +103,9 @@ import com.cloud.serializer.GsonHelper;
|
||||
import com.cloud.server.ConfigurationServer;
|
||||
import com.cloud.storage.JavaStorageLayer;
|
||||
import com.cloud.storage.StorageLayer;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.storage.secondary.SecondaryStorageVmManager;
|
||||
import com.cloud.utils.FileUtil;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
@ -114,6 +121,8 @@ import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.script.Script;
|
||||
import com.cloud.utils.ssh.SshHelper;
|
||||
import com.cloud.vm.DomainRouterVO;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.google.gson.Gson;
|
||||
import com.vmware.vim25.AboutInfo;
|
||||
import com.vmware.vim25.HostConnectSpec;
|
||||
@ -150,6 +159,9 @@ public class VmwareManagerImpl extends ManagerBase implements VmwareManager, Vmw
|
||||
@Inject VmwareDatacenterDao _vmwareDcDao;
|
||||
@Inject VmwareDatacenterZoneMapDao _vmwareDcZoneMapDao;
|
||||
@Inject LegacyZoneDao _legacyZoneDao;
|
||||
@Inject VMInstanceDao _vmDao;
|
||||
@Inject VolumeDao _volDao;
|
||||
@Inject DataStoreManager dataStoreMgr;
|
||||
|
||||
String _mountParent;
|
||||
StorageLayer _storage;
|
||||
|
||||
@ -328,7 +328,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
|
||||
workerVm = vmMo;
|
||||
|
||||
// attach volume to worker VM
|
||||
String datastoreVolumePath = String.format("[%s] %s.vmdk", dsMo.getName(), volumePath);
|
||||
String datastoreVolumePath = getVolumePathInDatastore(dsMo, volumePath + ".vmdk");
|
||||
vmMo.attachDisk(new String[] { datastoreVolumePath }, morDs);
|
||||
}
|
||||
}
|
||||
@ -1060,7 +1060,7 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
|
||||
}
|
||||
|
||||
//attach volume to worker VM
|
||||
String datastoreVolumePath = String.format("[%s] %s.vmdk", dsMo.getName(), volumePath);
|
||||
String datastoreVolumePath = getVolumePathInDatastore(dsMo, volumePath + ".vmdk");
|
||||
workerVm.attachDisk(new String[] { datastoreVolumePath }, morDs);
|
||||
vmMo = workerVm;
|
||||
}
|
||||
@ -1081,6 +1081,12 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
|
||||
}
|
||||
}
|
||||
|
||||
private String getVolumePathInDatastore(DatastoreMO dsMo, String volumeFileName) throws Exception {
|
||||
String datastoreVolumePath = dsMo.searchFileInSubFolders(volumeFileName, true);
|
||||
assert (datastoreVolumePath != null) : "Virtual disk file missing from datastore.";
|
||||
return datastoreVolumePath;
|
||||
}
|
||||
|
||||
private Pair<String, String> copyVolumeFromSecStorage(VmwareHypervisorHost hyperHost, long volumeId,
|
||||
DatastoreMO dsMo, String secStorageUrl, String exportName) throws Exception {
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ import java.util.Random;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
@ -73,6 +74,9 @@ import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
|
||||
import com.cloud.agent.api.DeleteStoragePoolCommand;
|
||||
import com.cloud.agent.api.DeleteVMSnapshotAnswer;
|
||||
import com.cloud.agent.api.DeleteVMSnapshotCommand;
|
||||
import com.cloud.agent.api.MigrateWithStorageAnswer;
|
||||
import com.cloud.agent.api.MigrateWithStorageCommand;
|
||||
import com.cloud.agent.api.UnregisterVMCommand;
|
||||
import com.cloud.agent.api.GetDomRVersionAnswer;
|
||||
import com.cloud.agent.api.GetDomRVersionCmd;
|
||||
import com.cloud.agent.api.GetHostStatsAnswer;
|
||||
@ -162,6 +166,12 @@ import com.cloud.agent.api.routing.VmDataCommand;
|
||||
import com.cloud.agent.api.routing.VpnUsersCfgCommand;
|
||||
import com.cloud.agent.api.storage.CopyVolumeAnswer;
|
||||
import com.cloud.agent.api.storage.CopyVolumeCommand;
|
||||
import com.cloud.agent.api.storage.CreateVolumeOVACommand;
|
||||
import com.cloud.agent.api.storage.CreateVolumeOVAAnswer;
|
||||
import com.cloud.agent.api.storage.MigrateVolumeAnswer;
|
||||
import com.cloud.agent.api.storage.MigrateVolumeCommand;
|
||||
import com.cloud.agent.api.storage.PrepareOVAPackingAnswer;
|
||||
import com.cloud.agent.api.storage.PrepareOVAPackingCommand;
|
||||
import com.cloud.agent.api.storage.CreateAnswer;
|
||||
import com.cloud.agent.api.storage.CreateCommand;
|
||||
import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
|
||||
@ -185,6 +195,7 @@ import com.cloud.agent.api.to.VolumeTO;
|
||||
import com.cloud.dc.DataCenter.NetworkType;
|
||||
import com.cloud.dc.Vlan;
|
||||
import com.cloud.exception.InternalErrorException;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.host.Host.Type;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.hypervisor.vmware.manager.VmwareHostService;
|
||||
@ -195,10 +206,12 @@ import com.cloud.hypervisor.vmware.mo.CustomFieldsManagerMO;
|
||||
import com.cloud.hypervisor.vmware.mo.DatacenterMO;
|
||||
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
|
||||
import com.cloud.hypervisor.vmware.mo.DiskControllerType;
|
||||
import com.cloud.hypervisor.vmware.mo.HostDatastoreSystemMO;
|
||||
import com.cloud.hypervisor.vmware.mo.HostFirewallSystemMO;
|
||||
import com.cloud.hypervisor.vmware.mo.HostMO;
|
||||
import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
|
||||
import com.cloud.hypervisor.vmware.mo.NetworkDetails;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualDiskManagerMO;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
|
||||
import com.cloud.hypervisor.vmware.mo.VirtualSwitchType;
|
||||
@ -219,13 +232,17 @@ import com.cloud.network.rules.FirewallRule;
|
||||
import com.cloud.resource.ServerResource;
|
||||
import com.cloud.serializer.GsonHelper;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.Storage.StoragePoolType;
|
||||
import com.cloud.storage.Volume;
|
||||
import com.cloud.storage.VolumeManager;
|
||||
import com.cloud.storage.VolumeManagerImpl;
|
||||
import com.cloud.storage.resource.StoragePoolResource;
|
||||
import com.cloud.storage.template.TemplateInfo;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.StringUtils;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.db.DB;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.exception.ExceptionUtil;
|
||||
@ -246,10 +263,12 @@ import com.vmware.vim25.DatastoreSummary;
|
||||
import com.vmware.vim25.DynamicProperty;
|
||||
import com.vmware.vim25.GuestInfo;
|
||||
import com.vmware.vim25.HostCapability;
|
||||
import com.vmware.vim25.HostDatastoreBrowserSearchResults;
|
||||
import com.vmware.vim25.HostFirewallInfo;
|
||||
import com.vmware.vim25.HostFirewallRuleset;
|
||||
import com.vmware.vim25.HostNetworkTrafficShapingPolicy;
|
||||
import com.vmware.vim25.HostPortGroupSpec;
|
||||
import com.vmware.vim25.ManagedObjectNotFound;
|
||||
import com.vmware.vim25.ManagedObjectReference;
|
||||
import com.vmware.vim25.ObjectContent;
|
||||
import com.vmware.vim25.OptionValue;
|
||||
@ -265,9 +284,13 @@ import com.vmware.vim25.RuntimeFaultFaultMsg;
|
||||
import com.vmware.vim25.ToolsUnavailableFaultMsg;
|
||||
import com.vmware.vim25.VimPortType;
|
||||
import com.vmware.vim25.VirtualDevice;
|
||||
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.VirtualDiskMode;
|
||||
import com.vmware.vim25.VirtualDiskType;
|
||||
import com.vmware.vim25.VirtualEthernetCard;
|
||||
import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo;
|
||||
import com.vmware.vim25.VirtualLsiLogicController;
|
||||
@ -275,9 +298,38 @@ import com.vmware.vim25.VirtualMachineConfigSpec;
|
||||
import com.vmware.vim25.VirtualMachineFileInfo;
|
||||
import com.vmware.vim25.VirtualMachineGuestOsIdentifier;
|
||||
import com.vmware.vim25.VirtualMachinePowerState;
|
||||
import com.vmware.vim25.VirtualMachineRelocateSpec;
|
||||
import com.vmware.vim25.VirtualMachineRelocateSpecDiskLocator;
|
||||
import com.vmware.vim25.VirtualMachineRuntimeInfo;
|
||||
import com.vmware.vim25.VirtualSCSISharing;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.log4j.NDC;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.rmi.RemoteException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Random;
|
||||
import java.util.TimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
public class VmwareResource implements StoragePoolResource, ServerResource, VmwareHostService {
|
||||
private static final Logger s_logger = Logger.getLogger(VmwareResource.class);
|
||||
@ -288,6 +340,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
||||
|
||||
protected final int _shutdown_waitMs = 300000; // wait up to 5 minutes for shutdown
|
||||
|
||||
@Inject
|
||||
protected VolumeManager volMgr;
|
||||
|
||||
// out an operation
|
||||
protected final int _retry = 24;
|
||||
protected final int _sleep = 10000;
|
||||
@ -398,6 +453,10 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
||||
answer = execute((PrepareForMigrationCommand) cmd);
|
||||
} else if (clz == MigrateCommand.class) {
|
||||
answer = execute((MigrateCommand) cmd);
|
||||
} else if (clz == MigrateWithStorageCommand.class) {
|
||||
answer = execute((MigrateWithStorageCommand) cmd);
|
||||
} else if (clz == MigrateVolumeCommand.class) {
|
||||
answer = execute((MigrateVolumeCommand) cmd);
|
||||
} else if (clz == DestroyCommand.class) {
|
||||
answer = execute((DestroyCommand) cmd);
|
||||
} else if (clz == CreateStoragePoolCommand.class) {
|
||||
@ -2506,7 +2565,9 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
||||
Pair<ManagedObjectReference, DatastoreMO> volumeDsDetails = dataStoresDetails.get(vol.getPoolUuid());
|
||||
assert (volumeDsDetails != null);
|
||||
VirtualDevice device;
|
||||
datastoreDiskPath = String.format("[%s] %s.vmdk", volumeDsDetails.second().getName(), vol.getPath());
|
||||
|
||||
datastoreDiskPath = volumeDsDetails.second().searchFileInSubFolders(vol.getPath() + ".vmdk", true);
|
||||
|
||||
String chainInfo = vol.getChainInfo();
|
||||
|
||||
if (chainInfo != null && !chainInfo.isEmpty()) {
|
||||
@ -3371,6 +3432,254 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
||||
}
|
||||
}
|
||||
|
||||
protected Answer execute(MigrateWithStorageCommand cmd) {
|
||||
|
||||
if (s_logger.isInfoEnabled()) {
|
||||
s_logger.info("Executing resource MigrateWithStorageCommand: " + _gson.toJson(cmd));
|
||||
}
|
||||
|
||||
VirtualMachineTO vmTo = cmd.getVirtualMachine();
|
||||
final String vmName = vmTo.getName();
|
||||
|
||||
State state = null;
|
||||
synchronized (_vms) {
|
||||
state = _vms.get(vmName);
|
||||
_vms.put(vmName, State.Stopping);
|
||||
}
|
||||
|
||||
VmwareHypervisorHost srcHyperHost = null;
|
||||
VmwareHypervisorHost tgtHyperHost = null;
|
||||
VirtualMachineMO vmMo = null;
|
||||
|
||||
ManagedObjectReference morDsAtTarget = null;
|
||||
ManagedObjectReference morDsAtSource = null;
|
||||
ManagedObjectReference morDc = null;
|
||||
ManagedObjectReference morDcOfTargetHost = null;
|
||||
ManagedObjectReference morTgtHost = new ManagedObjectReference();
|
||||
VirtualMachineRelocateSpec relocateSpec = new VirtualMachineRelocateSpec();
|
||||
List<VirtualMachineRelocateSpecDiskLocator> diskLocators = new ArrayList<VirtualMachineRelocateSpecDiskLocator>();
|
||||
VirtualMachineRelocateSpecDiskLocator diskLocator = null;
|
||||
|
||||
boolean isFirstDs = true;
|
||||
String srcDiskName = "";
|
||||
String srcDsName = "";
|
||||
String tgtDsName = "";
|
||||
String tgtDsNfsHost;
|
||||
String tgtDsNfsPath;
|
||||
int tgtDsNfsPort;
|
||||
VolumeTO volume;
|
||||
StorageFilerTO filerTo;
|
||||
Set<String> mountedDatastoresAtSource = new HashSet<String>();
|
||||
|
||||
Map<VolumeTO, StorageFilerTO> volToFiler = cmd.getVolumeToFiler();
|
||||
String tgtHost = cmd.getTargetHost();
|
||||
String tgtHostMorInfo = tgtHost.split("@")[0];
|
||||
morTgtHost.setType(tgtHostMorInfo.split(":")[0]);
|
||||
morTgtHost.setValue(tgtHostMorInfo.split(":")[1]);
|
||||
|
||||
try {
|
||||
srcHyperHost = getHyperHost(getServiceContext());
|
||||
tgtHyperHost = new HostMO(getServiceContext(), morTgtHost);
|
||||
morDc = srcHyperHost.getHyperHostDatacenter();
|
||||
morDcOfTargetHost = tgtHyperHost.getHyperHostDatacenter();
|
||||
if (morDc != morDcOfTargetHost) {
|
||||
String msg = "Source host & target host are in different datacentesr";
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
VmwareManager mgr = tgtHyperHost.getContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
|
||||
|
||||
// find VM through datacenter (VM is not at the target host yet)
|
||||
vmMo = srcHyperHost.findVmOnPeerHyperHost(vmName);
|
||||
if (vmMo == null) {
|
||||
String msg = "VM " + vmName + " does not exist in VMware datacenter " + morDc.getValue();
|
||||
s_logger.error(msg);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
|
||||
// Get details of each target datastore & attach to source host.
|
||||
for (Entry<VolumeTO, StorageFilerTO> entry : volToFiler.entrySet()) {
|
||||
volume = entry.getKey();
|
||||
filerTo = entry.getValue();
|
||||
|
||||
srcDsName = volume.getPoolUuid().replace("-", "");
|
||||
tgtDsName = filerTo.getUuid().replace("-", "");
|
||||
tgtDsNfsHost = filerTo.getHost();
|
||||
tgtDsNfsPath = filerTo.getPath();
|
||||
tgtDsNfsPort = filerTo.getPort();
|
||||
|
||||
s_logger.debug("Preparing spec for volume : " + volume.getName());
|
||||
morDsAtTarget = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(tgtHyperHost, filerTo.getUuid());
|
||||
if (morDsAtTarget == null) {
|
||||
String msg = "Unable to find the mounted datastore with uuid " + morDsAtTarget + " to execute MigrateWithStorageCommand";
|
||||
s_logger.error(msg);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
morDsAtSource = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(srcHyperHost, filerTo.getUuid());
|
||||
if (morDsAtSource == null) {
|
||||
morDsAtSource = srcHyperHost.mountDatastore(false, tgtDsNfsHost, tgtDsNfsPort, tgtDsNfsPath, tgtDsName);
|
||||
if (morDsAtSource == null) {
|
||||
throw new Exception("Unable to mount datastore " + tgtDsNfsHost + ":/" + tgtDsNfsPath + " on " + _hostName);
|
||||
}
|
||||
mountedDatastoresAtSource.add(tgtDsName);
|
||||
s_logger.debug("Mounted datastore " + tgtDsNfsHost + ":/" + tgtDsNfsPath + " on " + _hostName);
|
||||
}
|
||||
|
||||
if (isFirstDs) {
|
||||
relocateSpec.setDatastore(morDsAtSource);
|
||||
isFirstDs = false;
|
||||
}
|
||||
srcDiskName = String.format("[%s] %s.vmdk", srcDsName, volume.getPath());
|
||||
diskLocator = new VirtualMachineRelocateSpecDiskLocator();
|
||||
diskLocator.setDatastore(morDsAtSource);
|
||||
diskLocator.setDiskId(getVirtualDiskInfo(vmMo, srcDiskName));
|
||||
|
||||
diskLocators.add(diskLocator);
|
||||
|
||||
}
|
||||
relocateSpec.getDisk().addAll(diskLocators);
|
||||
|
||||
// Prepare network at target before migration
|
||||
NicTO[] nics = vmTo.getNics();
|
||||
for (NicTO nic : nics) {
|
||||
// prepare network on the host
|
||||
prepareNetworkFromNicInfo(new HostMO(getServiceContext(), morTgtHost), nic, false, vmTo.getType());
|
||||
}
|
||||
|
||||
// Ensure secondary storage mounted on target host
|
||||
String secStoreUrl = mgr.getSecondaryStorageStoreUrl(Long.parseLong(_dcId));
|
||||
if(secStoreUrl == null) {
|
||||
String msg = "secondary storage for dc " + _dcId + " is not ready yet?";
|
||||
throw new Exception(msg);
|
||||
}
|
||||
mgr.prepareSecondaryStorageStore(secStoreUrl);
|
||||
ManagedObjectReference morSecDs = prepareSecondaryDatastoreOnHost(secStoreUrl);
|
||||
if (morSecDs == null) {
|
||||
String msg = "Failed to prepare secondary storage on host, secondary store url: " + secStoreUrl;
|
||||
throw new Exception(msg);
|
||||
}
|
||||
|
||||
// Change datastore
|
||||
if (!vmMo.changeDatastore(relocateSpec)) {
|
||||
throw new Exception("Change datastore operation failed during storage migration");
|
||||
} else {
|
||||
s_logger.debug("Successfully migrated storage of VM " + vmName + " to target datastore(s)");
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
state = State.Stopping;
|
||||
List<VolumeTO> volumeToList = null;
|
||||
return new MigrateWithStorageAnswer(cmd, volumeToList);
|
||||
} catch (Throwable e) {
|
||||
if (e instanceof RemoteException) {
|
||||
s_logger.warn("Encountered remote exception at vCenter, invalidating VMware session context");
|
||||
invalidateServiceContext();
|
||||
}
|
||||
|
||||
String msg = "MigrationCommand failed due to " + VmwareHelper.getExceptionMessage(e);
|
||||
s_logger.warn(msg, e);
|
||||
return new MigrateWithStorageAnswer(cmd, (Exception) e);
|
||||
} finally {
|
||||
// Cleanup datastores mounted on source host
|
||||
for(String mountedDatastore : mountedDatastoresAtSource) {
|
||||
s_logger.debug("Attempting to unmount datastore " + mountedDatastore + " at " + _hostName);
|
||||
try {
|
||||
srcHyperHost.unmountDatastore(mountedDatastore);
|
||||
} catch (Exception unmountEx) {
|
||||
s_logger.debug("Failed to unmount datastore " + mountedDatastore + " at " + _hostName +
|
||||
". Seems the datastore is still being used by " + _hostName +
|
||||
". Please unmount manually to cleanup.");
|
||||
}
|
||||
s_logger.debug("Successfully unmounted datastore " + mountedDatastore + " at " + _hostName);
|
||||
}
|
||||
synchronized (_vms) {
|
||||
_vms.put(vmName, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Answer execute(MigrateVolumeCommand cmd) {
|
||||
String volumePath = cmd.getVolumePath();
|
||||
StorageFilerTO poolTo = cmd.getPool();
|
||||
|
||||
if (s_logger.isInfoEnabled()) {
|
||||
s_logger.info("Executing resource MigrateVolumeCommand: " + _gson.toJson(cmd));
|
||||
}
|
||||
|
||||
VmwareContext context = getServiceContext();
|
||||
VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
|
||||
final String vmName = volMgr.getVmNameFromVolumeId(cmd.getVolumeId());
|
||||
|
||||
VirtualMachineMO vmMo = null;
|
||||
VmwareHypervisorHost srcHyperHost = null;
|
||||
|
||||
ManagedObjectReference morDs = null;
|
||||
ManagedObjectReference morDc = null;
|
||||
VirtualMachineRelocateSpec relocateSpec = new VirtualMachineRelocateSpec();
|
||||
List<VirtualMachineRelocateSpecDiskLocator> diskLocators = new ArrayList<VirtualMachineRelocateSpecDiskLocator>();
|
||||
VirtualMachineRelocateSpecDiskLocator diskLocator = null;
|
||||
|
||||
String srcDiskName = "";
|
||||
String srcDsName = "";
|
||||
String tgtDsName = "";
|
||||
|
||||
try {
|
||||
srcHyperHost = getHyperHost(getServiceContext());
|
||||
morDc = srcHyperHost.getHyperHostDatacenter();
|
||||
srcDsName = volMgr.getStoragePoolOfVolume(cmd.getVolumeId());
|
||||
tgtDsName = poolTo.getUuid().replace("-", "");
|
||||
|
||||
// find VM through datacenter (VM is not at the target host yet)
|
||||
vmMo = srcHyperHost.findVmOnPeerHyperHost(vmName);
|
||||
if (vmMo == null) {
|
||||
String msg = "VM " + vmName + " does not exist in VMware datacenter " + morDc.getValue();
|
||||
s_logger.error(msg);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(srcHyperHost, tgtDsName);
|
||||
if (morDs == null) {
|
||||
String msg = "Unable to find the mounted datastore with name " + tgtDsName + " to execute MigrateVolumeCommand";
|
||||
s_logger.error(msg);
|
||||
throw new Exception(msg);
|
||||
}
|
||||
|
||||
srcDiskName = String.format("[%s] %s.vmdk", srcDsName, volumePath);
|
||||
diskLocator = new VirtualMachineRelocateSpecDiskLocator();
|
||||
diskLocator.setDatastore(morDs);
|
||||
diskLocator.setDiskId(getVirtualDiskInfo(vmMo, srcDiskName));
|
||||
|
||||
diskLocators.add(diskLocator);
|
||||
relocateSpec.getDisk().add(diskLocator);
|
||||
|
||||
// Change datastore
|
||||
if (!vmMo.changeDatastore(relocateSpec)) {
|
||||
throw new Exception("Change datastore operation failed during volume migration");
|
||||
} else {
|
||||
s_logger.debug("Successfully migrated volume " + volumePath + " to target datastore " + tgtDsName);
|
||||
}
|
||||
|
||||
return new MigrateVolumeAnswer(cmd, true, null, volumePath);
|
||||
} catch (Exception e) {
|
||||
String msg = "Catch Exception " + e.getClass().getName() + " due to " + e.toString();
|
||||
s_logger.error(msg, e);
|
||||
return new MigrateVolumeAnswer(cmd, false, msg, null);
|
||||
}
|
||||
}
|
||||
|
||||
private int getVirtualDiskInfo(VirtualMachineMO vmMo, String srcDiskName) throws Exception {
|
||||
Pair<VirtualDisk, String> deviceInfo = vmMo.getDiskDevice(srcDiskName, false);
|
||||
if(deviceInfo == null) {
|
||||
throw new Exception("No such disk device: " + srcDiskName);
|
||||
}
|
||||
return deviceInfo.first().getKey();
|
||||
}
|
||||
|
||||
private VmwareHypervisorHost getTargetHyperHost(DatacenterMO dcMo, String destIp) throws Exception {
|
||||
|
||||
VmwareManager mgr = dcMo.getContext().getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
|
||||
@ -3483,7 +3792,8 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
||||
}
|
||||
|
||||
DatastoreMO dsMo = new DatastoreMO(getServiceContext(), morDs);
|
||||
String datastoreVolumePath = String.format("[%s] %s.vmdk", dsMo.getName(), cmd.getVolumePath());
|
||||
String datastoreVolumePath = dsMo.searchFileInSubFolders(cmd.getVolumePath() + ".vmdk", true);
|
||||
assert (datastoreVolumePath != null) : "Virtual disk file must exist in specified datastore for attach/detach operations.";
|
||||
|
||||
AttachVolumeAnswer answer = new AttachVolumeAnswer(cmd, cmd.getDeviceId());
|
||||
if (cmd.getAttach()) {
|
||||
@ -5286,6 +5596,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
|
||||
_guestTrafficInfo = (VmwareTrafficLabel) params.get("guestTrafficInfo");
|
||||
_publicTrafficInfo = (VmwareTrafficLabel) params.get("publicTrafficInfo");
|
||||
VmwareContext context = getServiceContext();
|
||||
volMgr = ComponentContext.inject(VolumeManagerImpl.class);
|
||||
try {
|
||||
VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
|
||||
mgr.setupResourceStartupParams(params);
|
||||
|
||||
@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cloudstack.storage.motion;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
|
||||
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.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.log4j.Logger;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.agent.AgentManager;
|
||||
import com.cloud.agent.api.Answer;
|
||||
import com.cloud.agent.api.MigrateWithStorageAnswer;
|
||||
import com.cloud.agent.api.MigrateWithStorageCommand;
|
||||
import com.cloud.agent.api.MigrateWithStorageCompleteAnswer;
|
||||
import com.cloud.agent.api.MigrateWithStorageCompleteCommand;
|
||||
import com.cloud.agent.api.MigrateWithStorageReceiveAnswer;
|
||||
import com.cloud.agent.api.MigrateWithStorageReceiveCommand;
|
||||
import com.cloud.agent.api.MigrateWithStorageSendAnswer;
|
||||
import com.cloud.agent.api.MigrateWithStorageSendCommand;
|
||||
import com.cloud.agent.api.to.StorageFilerTO;
|
||||
import com.cloud.agent.api.to.VirtualMachineTO;
|
||||
import com.cloud.agent.api.to.VolumeTO;
|
||||
import com.cloud.exception.AgentUnavailableException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.host.Host;
|
||||
import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.storage.StoragePool;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.vm.VMInstanceVO;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
|
||||
@Component
|
||||
public class VmwareStorageMotionStrategy implements DataMotionStrategy {
|
||||
private static final Logger s_logger = Logger.getLogger(VmwareStorageMotionStrategy.class);
|
||||
@Inject AgentManager agentMgr;
|
||||
@Inject VolumeDao volDao;
|
||||
@Inject VolumeDataFactory volFactory;
|
||||
@Inject PrimaryDataStoreDao storagePoolDao;
|
||||
@Inject VMInstanceDao instanceDao;
|
||||
|
||||
@Override
|
||||
public boolean canHandle(DataObject srcData, DataObject destData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost) {
|
||||
if (srcHost.getHypervisorType() == HypervisorType.VMware && destHost.getHypervisorType() == HypervisorType.VMware) {
|
||||
s_logger.debug(this.getClass() + " can handle the request because the hosts have VMware hypervisor");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void copyAsync(DataObject srcData, DataObject destData,
|
||||
AsyncCompletionCallback<CopyCommandResult> callback) {
|
||||
CopyCommandResult result = new CopyCommandResult(null, null);
|
||||
result.setResult("Unsupported operation requested for copying data.");
|
||||
callback.complete(result);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost,
|
||||
AsyncCompletionCallback<CopyCommandResult> callback) {
|
||||
Answer answer = null;
|
||||
String errMsg = null;
|
||||
try {
|
||||
VMInstanceVO instance = instanceDao.findById(vmTo.getId());
|
||||
if (instance != null) {
|
||||
if (srcHost.getClusterId() == destHost.getClusterId()) {
|
||||
answer = migrateVmWithVolumesWithinCluster(instance, vmTo, srcHost, destHost, volumeMap);
|
||||
} else {
|
||||
answer = migrateVmWithVolumesAcrossCluster(instance, vmTo, srcHost, destHost, volumeMap);
|
||||
}
|
||||
} else {
|
||||
throw new CloudRuntimeException("Unsupported operation requested for moving data.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
s_logger.error("copy failed", e);
|
||||
errMsg = e.toString();
|
||||
}
|
||||
|
||||
CopyCommandResult result = new CopyCommandResult(null, answer);
|
||||
result.setResult(errMsg);
|
||||
callback.complete(result);
|
||||
return null;
|
||||
}
|
||||
|
||||
private Answer migrateVmWithVolumesAcrossCluster(VMInstanceVO vm, VirtualMachineTO to, Host srcHost,
|
||||
Host destHost, Map<VolumeInfo, DataStore> volumeToPool) throws AgentUnavailableException {
|
||||
|
||||
// Initiate migration of a virtual machine with it's volumes.
|
||||
try {
|
||||
Map<VolumeTO, StorageFilerTO> volumeToFilerto = new HashMap<VolumeTO, StorageFilerTO>();
|
||||
for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
|
||||
VolumeInfo volume = entry.getKey();
|
||||
VolumeTO volumeTo = new VolumeTO(volume, storagePoolDao.findById(volume.getPoolId()));
|
||||
StorageFilerTO filerTo = new StorageFilerTO((StoragePool)entry.getValue());
|
||||
volumeToFilerto.put(volumeTo, filerTo);
|
||||
}
|
||||
|
||||
// Migration across cluster needs to be done in three phases.
|
||||
// 1. Send a migrate command to source resource to initiate migration
|
||||
// Run validations against target!!
|
||||
// 2. Complete the process. Update the volume details.
|
||||
MigrateWithStorageCommand migrateWithStorageCmd = new MigrateWithStorageCommand(to, volumeToFilerto, destHost.getGuid());
|
||||
MigrateWithStorageAnswer migrateWithStorageAnswer = (MigrateWithStorageAnswer) agentMgr.send(
|
||||
srcHost.getId(), migrateWithStorageCmd);
|
||||
if (migrateWithStorageAnswer == null) {
|
||||
s_logger.error("Migration with storage of vm " + vm+ " to host " + destHost + " failed.");
|
||||
throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
|
||||
} else if (!migrateWithStorageAnswer.getResult()) {
|
||||
s_logger.error("Migration with storage of vm " + vm+ " failed. Details: " + migrateWithStorageAnswer.getDetails());
|
||||
throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost +
|
||||
". " + migrateWithStorageAnswer.getDetails());
|
||||
} else {
|
||||
// Update the volume details after migration.
|
||||
updateVolumesAfterMigration(volumeToPool);
|
||||
}
|
||||
s_logger.debug("Storage migration of VM " + vm.getInstanceName() + " completed successfully. Migrated to host " + destHost.getName());
|
||||
|
||||
return migrateWithStorageAnswer;
|
||||
} catch (OperationTimedoutException e) {
|
||||
s_logger.error("Error while migrating vm " + vm + " to host " + destHost, e);
|
||||
throw new AgentUnavailableException("Operation timed out on storage motion for " + vm, destHost.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private Answer migrateVmWithVolumesWithinCluster(VMInstanceVO vm, VirtualMachineTO to, Host srcHost,
|
||||
Host destHost, Map<VolumeInfo, DataStore> volumeToPool) throws AgentUnavailableException {
|
||||
|
||||
// Initiate migration of a virtual machine with it's volumes.
|
||||
try {
|
||||
Map<VolumeTO, StorageFilerTO> volumeToFilerto = new HashMap<VolumeTO, StorageFilerTO>();
|
||||
for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
|
||||
VolumeInfo volume = entry.getKey();
|
||||
VolumeTO volumeTo = new VolumeTO(volume, storagePoolDao.findById(volume.getPoolId()));
|
||||
StorageFilerTO filerTo = new StorageFilerTO((StoragePool)entry.getValue());
|
||||
volumeToFilerto.put(volumeTo, filerTo);
|
||||
}
|
||||
|
||||
MigrateWithStorageCommand command = new MigrateWithStorageCommand(to, volumeToFilerto, destHost.getGuid());
|
||||
MigrateWithStorageAnswer answer = (MigrateWithStorageAnswer) agentMgr.send(srcHost.getId(), command);
|
||||
if (answer == null) {
|
||||
s_logger.error("Migration with storage of vm " + vm + " failed.");
|
||||
throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost);
|
||||
} else if (!answer.getResult()) {
|
||||
s_logger.error("Migration with storage of vm " + vm+ " failed. Details: " + answer.getDetails());
|
||||
throw new CloudRuntimeException("Error while migrating the vm " + vm + " to host " + destHost +
|
||||
". " + answer.getDetails());
|
||||
} else {
|
||||
// Update the volume details after migration.
|
||||
updateVolumesAfterMigration(volumeToPool);
|
||||
}
|
||||
|
||||
return answer;
|
||||
} catch (OperationTimedoutException e) {
|
||||
s_logger.error("Error while migrating vm " + vm + " to host " + destHost, e);
|
||||
throw new AgentUnavailableException("Operation timed out on storage motion for " + vm, destHost.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateVolumesAfterMigration(Map<VolumeInfo, DataStore> volumeToPool) {
|
||||
for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
|
||||
VolumeInfo volume = entry.getKey();
|
||||
StoragePool pool = (StoragePool)entry.getValue();
|
||||
|
||||
VolumeVO volumeVO = volDao.findById(volume.getId());
|
||||
Long oldPoolId = volumeVO.getPoolId();
|
||||
volumeVO.setLastPoolId(oldPoolId);
|
||||
volumeVO.setFolder(pool.getPath());
|
||||
volumeVO.setPodId(pool.getPodId());
|
||||
volumeVO.setPoolId(pool.getId());
|
||||
|
||||
volDao.update(volume.getId(), volumeVO);
|
||||
s_logger.debug("Volume path was successfully updated for volume " + volume.getName() + " after it was migrated.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,271 @@
|
||||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// 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;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.CommandResult;
|
||||
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.VolumeDataFactory;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
|
||||
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
||||
import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher;
|
||||
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||
import org.apache.cloudstack.framework.async.AsyncRpcConext;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.test.utils.SpringUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
|
||||
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.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;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
|
||||
public class VmwareStorageMotionStrategyTest {
|
||||
|
||||
@Inject VmwareStorageMotionStrategy strategy = new VmwareStorageMotionStrategy();
|
||||
@Inject AgentManager agentMgr;
|
||||
@Inject VolumeDao volDao;
|
||||
@Inject VolumeDataFactory volFactory;
|
||||
@Inject PrimaryDataStoreDao storagePoolDao;
|
||||
@Inject VMInstanceDao instanceDao;
|
||||
|
||||
CopyCommandResult result;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws ConfigurationException {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void testSetUp() {
|
||||
ComponentContext.initComponentsLifeCycle();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStrategyHandlesVmwareHosts() throws Exception {
|
||||
Host srcHost = mock(Host.class);
|
||||
Host destHost = mock(Host.class);
|
||||
when(srcHost.getHypervisorType()).thenReturn(HypervisorType.VMware);
|
||||
when(destHost.getHypervisorType()).thenReturn(HypervisorType.VMware);
|
||||
Map<VolumeInfo, DataStore> volumeMap = new HashMap<VolumeInfo, DataStore>();
|
||||
boolean canHandle = strategy.canHandle(volumeMap, srcHost, destHost);
|
||||
assertTrue("The strategy is only supposed to handle vmware hosts", canHandle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStrategyDoesnotHandlesNonVmwareHosts() throws Exception {
|
||||
Host srcHost = mock(Host.class);
|
||||
Host destHost = mock(Host.class);
|
||||
when(srcHost.getHypervisorType()).thenReturn(HypervisorType.XenServer);
|
||||
when(destHost.getHypervisorType()).thenReturn(HypervisorType.XenServer);
|
||||
Map<VolumeInfo, DataStore> volumeMap = new HashMap<VolumeInfo, DataStore>();
|
||||
boolean canHandle = strategy.canHandle(volumeMap, srcHost, destHost);
|
||||
assertFalse("The strategy is only supposed to handle vmware hosts", canHandle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigrateWithinClusterSuccess() throws Exception {
|
||||
Host srcHost = mock(Host.class);
|
||||
Host destHost = mock(Host.class);
|
||||
when(srcHost.getClusterId()).thenReturn(1L);
|
||||
when(destHost.getClusterId()).thenReturn(1L);
|
||||
Map<VolumeInfo, DataStore> volumeMap = new HashMap<VolumeInfo, DataStore>();
|
||||
VirtualMachineTO to = mock(VirtualMachineTO.class);
|
||||
when(to.getId()).thenReturn(6L);
|
||||
VMInstanceVO instance = mock(VMInstanceVO.class);
|
||||
when(instanceDao.findById(6L)).thenReturn(instance);
|
||||
|
||||
MockContext<CommandResult> context = new MockContext<CommandResult>(null, null, volumeMap);
|
||||
AsyncCallbackDispatcher<VmwareStorageMotionStrategyTest, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
|
||||
caller.setCallback(caller.getTarget().mockCallBack(null, null)).setContext(context);
|
||||
|
||||
MigrateWithStorageAnswer migAnswerMock = mock(MigrateWithStorageAnswer.class);
|
||||
when(migAnswerMock.getResult()).thenReturn(true);
|
||||
when(agentMgr.send(anyLong(), isA(MigrateWithStorageCommand.class))).thenReturn(migAnswerMock);
|
||||
|
||||
strategy.copyAsync(volumeMap, to, srcHost, destHost, caller);
|
||||
assertTrue("Migration within cluster isn't successful.", this.result.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigrateWithinClusterFailure() throws Exception {
|
||||
Host srcHost = mock(Host.class);
|
||||
Host destHost = mock(Host.class);
|
||||
when(srcHost.getClusterId()).thenReturn(1L);
|
||||
when(destHost.getClusterId()).thenReturn(1L);
|
||||
Map<VolumeInfo, DataStore> volumeMap = new HashMap<VolumeInfo, DataStore>();
|
||||
VirtualMachineTO to = mock(VirtualMachineTO.class);
|
||||
when(to.getId()).thenReturn(6L);
|
||||
VMInstanceVO instance = mock(VMInstanceVO.class);
|
||||
when(instanceDao.findById(6L)).thenReturn(instance);
|
||||
|
||||
MockContext<CommandResult> context = new MockContext<CommandResult>(null, null, volumeMap);
|
||||
AsyncCallbackDispatcher<VmwareStorageMotionStrategyTest, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
|
||||
caller.setCallback(caller.getTarget().mockCallBack(null, null)).setContext(context);
|
||||
|
||||
MigrateWithStorageAnswer migAnswerMock = mock(MigrateWithStorageAnswer.class);
|
||||
when(migAnswerMock.getResult()).thenReturn(false);
|
||||
when(agentMgr.send(anyLong(), isA(MigrateWithStorageCommand.class))).thenReturn(migAnswerMock);
|
||||
|
||||
strategy.copyAsync(volumeMap, to, srcHost, destHost, caller);
|
||||
assertFalse("Migration within cluster didn't fail.", this.result.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigrateAcrossClusterSuccess() throws Exception {
|
||||
Host srcHost = mock(Host.class);
|
||||
Host destHost = mock(Host.class);
|
||||
when(srcHost.getClusterId()).thenReturn(1L);
|
||||
when(destHost.getClusterId()).thenReturn(2L);
|
||||
Map<VolumeInfo, DataStore> volumeMap = new HashMap<VolumeInfo, DataStore>();
|
||||
VirtualMachineTO to = mock(VirtualMachineTO.class);
|
||||
when(to.getId()).thenReturn(6L);
|
||||
VMInstanceVO instance = mock(VMInstanceVO.class);
|
||||
when(instanceDao.findById(6L)).thenReturn(instance);
|
||||
|
||||
MockContext<CommandResult> context = new MockContext<CommandResult>(null, null, volumeMap);
|
||||
AsyncCallbackDispatcher<VmwareStorageMotionStrategyTest, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
|
||||
caller.setCallback(caller.getTarget().mockCallBack(null, null)).setContext(context);
|
||||
|
||||
MigrateWithStorageAnswer migAnswerMock = mock(MigrateWithStorageAnswer.class);
|
||||
when(migAnswerMock.getResult()).thenReturn(true);
|
||||
when(agentMgr.send(anyLong(), isA(MigrateWithStorageCommand.class))).thenReturn(migAnswerMock);
|
||||
|
||||
strategy.copyAsync(volumeMap, to, srcHost, destHost, caller);
|
||||
assertTrue("Migration across cluster isn't successful.", this.result.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMigrateAcrossClusterFailure() throws Exception {
|
||||
Host srcHost = mock(Host.class);
|
||||
Host destHost = mock(Host.class);
|
||||
when(srcHost.getClusterId()).thenReturn(1L);
|
||||
when(destHost.getClusterId()).thenReturn(2L);
|
||||
Map<VolumeInfo, DataStore> volumeMap = new HashMap<VolumeInfo, DataStore>();
|
||||
VirtualMachineTO to = mock(VirtualMachineTO.class);
|
||||
when(to.getId()).thenReturn(6L);
|
||||
VMInstanceVO instance = mock(VMInstanceVO.class);
|
||||
when(instanceDao.findById(6L)).thenReturn(instance);
|
||||
|
||||
MockContext<CommandResult> context = new MockContext<CommandResult>(null, null, volumeMap);
|
||||
AsyncCallbackDispatcher<VmwareStorageMotionStrategyTest, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
|
||||
caller.setCallback(caller.getTarget().mockCallBack(null, null)).setContext(context);
|
||||
|
||||
MigrateWithStorageAnswer migAnswerMock = mock(MigrateWithStorageAnswer.class);
|
||||
when(migAnswerMock.getResult()).thenReturn(false);
|
||||
when(agentMgr.send(anyLong(), isA(MigrateWithStorageCommand.class))).thenReturn(migAnswerMock);
|
||||
|
||||
strategy.copyAsync(volumeMap, to, srcHost, destHost, caller);
|
||||
assertFalse("Migration across cluster didn't fail.", this.result.isSuccess());
|
||||
}
|
||||
|
||||
private class MockContext<T> extends AsyncRpcConext<T> {
|
||||
final Map<VolumeInfo, DataStore> volumeToPool;
|
||||
final AsyncCallFuture<CommandResult> future;
|
||||
/**
|
||||
* @param callback
|
||||
*/
|
||||
public MockContext(AsyncCompletionCallback<T> callback, AsyncCallFuture<CommandResult> future,
|
||||
Map<VolumeInfo, DataStore> volumeToPool) {
|
||||
super(callback);
|
||||
this.volumeToPool = volumeToPool;
|
||||
this.future = future;
|
||||
}
|
||||
}
|
||||
|
||||
protected Void mockCallBack(AsyncCallbackDispatcher<VmwareStorageMotionStrategyTest,
|
||||
CopyCommandResult> callback, MockContext<CommandResult> context) {
|
||||
this.result = callback.getResult();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan(basePackageClasses = { VmwareStorageMotionStrategy.class },
|
||||
includeFilters = {@Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)},
|
||||
useDefaultFilters = false)
|
||||
public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration {
|
||||
|
||||
@Bean
|
||||
public VolumeDao volumeDao() {
|
||||
return Mockito.mock(VolumeDao.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VolumeDataFactory volumeDataFactory() {
|
||||
return Mockito.mock(VolumeDataFactory.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PrimaryDataStoreDao primaryDataStoreDao() {
|
||||
return Mockito.mock(PrimaryDataStoreDao.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VMInstanceDao vmInstanceDao() {
|
||||
return Mockito.mock(VMInstanceDao.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AgentManager agentManager() {
|
||||
return Mockito.mock(AgentManager.class);
|
||||
}
|
||||
|
||||
public static class Library implements TypeFilter {
|
||||
@Override
|
||||
public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException {
|
||||
ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class);
|
||||
return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,4 +105,8 @@ public interface VolumeManager extends VolumeApiService {
|
||||
DiskProfile allocateTemplatedVolume(Type type, String name,
|
||||
DiskOfferingVO offering, VMTemplateVO template, VMInstanceVO vm,
|
||||
Account owner);
|
||||
|
||||
String getVmNameFromVolumeId(long volumeId);
|
||||
|
||||
String getStoragePoolOfVolume(long volumeId);
|
||||
}
|
||||
|
||||
@ -2604,4 +2604,17 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getVmNameFromVolumeId(long volumeId) {
|
||||
Long instanceId;
|
||||
VolumeVO volume = _volsDao.findById(volumeId);
|
||||
return getVmNameOnVolume(volume);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStoragePoolOfVolume(long volumeId) {
|
||||
VolumeVO vol = _volsDao.findById(volumeId);
|
||||
return dataStoreMgr.getPrimaryDataStore(vol.getPoolId()).getUuid();
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,8 +133,14 @@ public class DatastoreMO extends BaseMO {
|
||||
fullPath = String.format("[%s] %s", datastoreName, path);
|
||||
|
||||
try {
|
||||
if(testExistence && !fileExists(fullPath))
|
||||
return true;
|
||||
if(testExistence && !fileExists(fullPath)) {
|
||||
String searchResult = searchFileInSubFolders(fullPath.split(" ")[1], true);
|
||||
if (searchResult == null) {
|
||||
return true;
|
||||
} else {
|
||||
fullPath = searchResult;
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
s_logger.info("Unable to test file existence due to exception " + e.getClass().getName() + ", skip deleting of it");
|
||||
return true;
|
||||
@ -315,4 +321,38 @@ public class DatastoreMO extends BaseMO {
|
||||
s_logger.info("Folder " + folderName + " does not exist on datastore");
|
||||
return false;
|
||||
}
|
||||
|
||||
public String searchFileInSubFolders(String fileName, boolean caseInsensitive) throws Exception {
|
||||
String datastorePath = "[" + getName() + "]";
|
||||
String rootDirectoryFilePath = String.format("%s %s", datastorePath, fileName);
|
||||
if(fileExists(rootDirectoryFilePath)) {
|
||||
return rootDirectoryFilePath;
|
||||
}
|
||||
|
||||
String parentFolderPath = null;
|
||||
String absoluteFileName = null;
|
||||
s_logger.info("Searching file " + fileName + " in " + datastorePath);
|
||||
|
||||
HostDatastoreBrowserMO browserMo = getHostDatastoreBrowserMO();
|
||||
ArrayList<HostDatastoreBrowserSearchResults> results = browserMo.searchDatastoreSubFolders("[" + getName() + "]", fileName, caseInsensitive);
|
||||
if (results.size() > 1) {
|
||||
s_logger.warn("Multiple files with name " + fileName + " exists in datastore " + datastorePath + ". Trying to choose first file found in search attempt.");
|
||||
}
|
||||
for (HostDatastoreBrowserSearchResults result : results) {
|
||||
if (result != null) {
|
||||
List<FileInfo> info = result.getFile();
|
||||
if (info != null && info.size() > 0) {
|
||||
for (FileInfo fi : info) {
|
||||
absoluteFileName = parentFolderPath = result.getFolderPath();
|
||||
s_logger.info("Found file " + fileName + " in datastore at " + absoluteFileName);
|
||||
if(parentFolderPath.endsWith("]"))
|
||||
absoluteFileName += " ";
|
||||
absoluteFileName += fi.getPath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return absoluteFileName;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
// under the License.
|
||||
package com.cloud.hypervisor.vmware.mo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.hypervisor.vmware.util.VmwareContext;
|
||||
@ -76,7 +78,8 @@ public class HostDatastoreBrowserMO extends BaseMO {
|
||||
return searchDatastore(datastorePath, spec);
|
||||
}
|
||||
|
||||
public HostDatastoreBrowserSearchResults searchDatastoreSubFolders(String datastorePath, HostDatastoreBrowserSearchSpec searchSpec) throws Exception {
|
||||
@SuppressWarnings("unchecked")
|
||||
public ArrayList<HostDatastoreBrowserSearchResults> searchDatastoreSubFolders(String datastorePath, HostDatastoreBrowserSearchSpec searchSpec) throws Exception {
|
||||
if(s_logger.isTraceEnabled())
|
||||
s_logger.trace("vCenter API trace - searchDatastoreSubFolders(). target mor: " + _mor.getValue() + ", file datastore path: " + datastorePath);
|
||||
|
||||
@ -87,7 +90,7 @@ public class HostDatastoreBrowserMO extends BaseMO {
|
||||
if(result) {
|
||||
_context.waitForTaskProgressDone(morTask);
|
||||
|
||||
return (HostDatastoreBrowserSearchResults)_context.getVimClient().getDynamicProperty(morTask, "info.result");
|
||||
return (ArrayList<HostDatastoreBrowserSearchResults>) _context.getVimClient().getDynamicProperty(morTask, "info.result");
|
||||
} else {
|
||||
s_logger.error("VMware searchDaastoreSubFolders_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
|
||||
}
|
||||
@ -99,12 +102,11 @@ public class HostDatastoreBrowserMO extends BaseMO {
|
||||
return null;
|
||||
}
|
||||
|
||||
public HostDatastoreBrowserSearchResults searchDatastoreSubFolders(String datastorePath, String folderName, boolean caseInsensitive) throws Exception {
|
||||
HostDatastoreBrowserSearchSpec spec = new HostDatastoreBrowserSearchSpec();
|
||||
spec.setSearchCaseInsensitive(caseInsensitive);
|
||||
spec.getMatchPattern().add(folderName);
|
||||
public ArrayList<HostDatastoreBrowserSearchResults> searchDatastoreSubFolders(String datastorePath, String fileName, boolean caseInsensitive) throws Exception {
|
||||
HostDatastoreBrowserSearchSpec spec = new HostDatastoreBrowserSearchSpec();
|
||||
spec.setSearchCaseInsensitive(caseInsensitive);
|
||||
spec.getMatchPattern().add(fileName);
|
||||
|
||||
return searchDatastore(datastorePath, spec);
|
||||
}
|
||||
return searchDatastoreSubFolders(datastorePath, spec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -70,6 +70,7 @@ import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
|
||||
import com.vmware.vim25.VirtualLsiLogicController;
|
||||
import com.vmware.vim25.VirtualMachineConfigSpec;
|
||||
import com.vmware.vim25.VirtualMachineFileInfo;
|
||||
import com.vmware.vim25.VirtualMachineRelocateSpec;
|
||||
import com.vmware.vim25.VirtualMachineVideoCard;
|
||||
import com.vmware.vim25.VirtualSCSISharing;
|
||||
import com.vmware.vim25.VmwareDistributedVirtualSwitchPvlanSpec;
|
||||
|
||||
@ -26,6 +26,10 @@ import com.vmware.vim25.VirtualDiskSpec;
|
||||
public class VirtualDiskManagerMO extends BaseMO {
|
||||
private static final Logger s_logger = Logger.getLogger(VirtualDiskManagerMO.class);
|
||||
|
||||
public VirtualDiskManagerMO(VmwareContext context) {
|
||||
super(context, context.getServiceContent().getVirtualDiskManager());
|
||||
}
|
||||
|
||||
public VirtualDiskManagerMO(VmwareContext context, ManagedObjectReference morDiskMgr) {
|
||||
super(context, morDiskMgr);
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@ import com.vmware.vim25.GuestInfo;
|
||||
import com.vmware.vim25.HttpNfcLeaseDeviceUrl;
|
||||
import com.vmware.vim25.HttpNfcLeaseInfo;
|
||||
import com.vmware.vim25.HttpNfcLeaseState;
|
||||
import com.vmware.vim25.InvalidStateFaultMsg;
|
||||
import com.vmware.vim25.ManagedObjectReference;
|
||||
import com.vmware.vim25.ObjectContent;
|
||||
import com.vmware.vim25.ObjectSpec;
|
||||
@ -57,6 +58,7 @@ import com.vmware.vim25.OvfCreateDescriptorResult;
|
||||
import com.vmware.vim25.OvfFile;
|
||||
import com.vmware.vim25.PropertyFilterSpec;
|
||||
import com.vmware.vim25.PropertySpec;
|
||||
import com.vmware.vim25.RuntimeFaultFaultMsg;
|
||||
import com.vmware.vim25.SelectionSpec;
|
||||
import com.vmware.vim25.TraversalSpec;
|
||||
import com.vmware.vim25.VirtualCdrom;
|
||||
@ -335,6 +337,30 @@ public class VirtualMachineMO extends BaseMO {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean changeDatastore(VirtualMachineRelocateSpec relocateSpec) throws Exception {
|
||||
ManagedObjectReference morTask = _context.getVimClient().getService().relocateVMTask(_mor, relocateSpec, VirtualMachineMovePriority.DEFAULT_PRIORITY);
|
||||
boolean result = _context.getVimClient().waitForTask(morTask);
|
||||
if(result) {
|
||||
_context.waitForTaskProgressDone(morTask);
|
||||
return true;
|
||||
} else {
|
||||
s_logger.error("VMware RelocateVM_Task to change datastore failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean changeHost(VirtualMachineRelocateSpec relocateSpec) throws Exception {
|
||||
ManagedObjectReference morTask = _context.getService().relocateVMTask(_mor, relocateSpec, VirtualMachineMovePriority.DEFAULT_PRIORITY);
|
||||
boolean result = _context.getVimClient().waitForTask(morTask);
|
||||
if (result) {
|
||||
_context.waitForTaskProgressDone(morTask);
|
||||
return true;
|
||||
} else {
|
||||
s_logger.error("VMware RelocateVM_Task to change host failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean relocate(ManagedObjectReference morTargetHost) throws Exception {
|
||||
VirtualMachineRelocateSpec relocateSpec = new VirtualMachineRelocateSpec();
|
||||
relocateSpec.setHost(morTargetHost);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user