Add support for hypervisor snapshots to CloudStack-managed storage (for XenServer and VMware)

This commit is contained in:
Mike Tutkowski 2013-10-02 00:07:48 -06:00
parent 66b853789c
commit bced4c7e8b
12 changed files with 177 additions and 112 deletions

View File

@ -25,6 +25,7 @@ public class AttachVolumeCommand extends Command {
private StoragePoolType pooltype;
private String volumePath;
private String volumeName;
private Long volumeSize;
private Long deviceId;
private String chainInfo;
private String poolUuid;
@ -45,13 +46,14 @@ public class AttachVolumeCommand extends Command {
public AttachVolumeCommand(boolean attach, boolean managed, String vmName,
StoragePoolType pooltype, String volumePath, String volumeName,
Long deviceId, String chainInfo) {
Long volumeSize, Long deviceId, String chainInfo) {
this.attach = attach;
this._managed = managed;
this.vmName = vmName;
this.pooltype = pooltype;
this.volumePath = volumePath;
this.volumeName = volumeName;
this.volumeSize = volumeSize;
this.deviceId = deviceId;
this.chainInfo = chainInfo;
}
@ -85,6 +87,10 @@ public class AttachVolumeCommand extends Command {
return volumeName;
}
public Long getVolumeSize() {
return volumeSize;
}
public Long getDeviceId() {
return deviceId;
}

View File

@ -27,7 +27,7 @@ import com.cloud.storage.Storage.StoragePoolType;
public class AttachVolumeAnswerTest {
AttachVolumeCommand avc = new AttachVolumeCommand(true, false, "vmname",
StoragePoolType.Filesystem, "vPath", "vName",
StoragePoolType.Filesystem, "vPath", "vName", 1073741824L,
123456789L, "chainInfo");
AttachVolumeAnswer ava1 = new AttachVolumeAnswer(avc);
String results = "";

View File

@ -26,7 +26,7 @@ import com.cloud.storage.Storage.StoragePoolType;
public class AttachVolumeCommandTest {
AttachVolumeCommand avc = new AttachVolumeCommand(true, false, "vmname",
StoragePoolType.Filesystem, "vPath", "vName",
StoragePoolType.Filesystem, "vPath", "vName", 1073741824L,
123456789L, "chainInfo");
@Test

View File

@ -17,6 +17,7 @@
package com.cloud.hypervisor.vmware.manager;
import com.cloud.agent.api.Command;
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
import com.cloud.hypervisor.vmware.util.VmwareContext;
import com.vmware.vim25.ManagedObjectReference;
@ -28,7 +29,8 @@ public interface VmwareHostService {
String getWorkerName(VmwareContext context, Command cmd, int workerSequence);
ManagedObjectReference handleDatastoreAndVmdkAttach(Command cmd, String iqn, String storageHost, int storagePort,
String initiatorUsername, String initiatorPassword, String targetUsername, String targetPassword) throws Exception;
ManagedObjectReference getVmfsDatastore(VmwareHypervisorHost hyperHost, String datastoreName, String storageIpAddress, int storagePortNumber,
String iqn, String initiatorChapName, String initiatorChapSecret, String mutualChapName, String mutualChapSecret) throws Exception;
void createVmdk(Command cmd, DatastoreMO dsMo, String volumeDatastorePath, Long volumeSize) throws Exception;
void handleDatastoreAndVmdkDetach(String iqn, String storageHost, int storagePort) throws Exception;
}

View File

@ -4439,7 +4439,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
return str.replace('/', '-');
}
private String trimIqn(String iqn) {
public static String trimIqn(String iqn) {
String[] tmp = iqn.split("/");
if (tmp.length != 3) {
@ -4454,36 +4454,23 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
}
@Override
public ManagedObjectReference handleDatastoreAndVmdkAttach(Command cmd, String iqn, String storageHost, int storagePort,
String initiatorUsername, String initiatorPassword, String targetUsername, String targetPassword) throws Exception {
public void createVmdk(Command cmd, DatastoreMO dsMo, String vmdkDatastorePath, Long volumeSize) throws Exception {
VmwareContext context = getServiceContext();
VmwareHypervisorHost hyperHost = getHyperHost(context);
ManagedObjectReference morDs = createVmfsDatastore(hyperHost, getDatastoreName(iqn),
storageHost, storagePort, trimIqn(iqn),
initiatorUsername, initiatorPassword,
targetUsername, targetPassword);
String dummyVmName = getWorkerName(context, cmd, 0);
DatastoreMO dsMo = new DatastoreMO(context, morDs);
VirtualMachineMO vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, dummyVmName);
String volumeDatastorePath = String.format("[%s] %s.vmdk", dsMo.getName(), dsMo.getName());
if (!dsMo.fileExists(volumeDatastorePath)) {
String dummyVmName = getWorkerName(context, cmd, 0);
VirtualMachineMO vmMo = HypervisorHostHelper.createWorkerVM(hyperHost, dsMo, dummyVmName);
if (vmMo == null) {
throw new Exception("Unable to create a dummy VM for volume creation");
}
vmMo.createDisk(volumeDatastorePath, getMBsFromBytes(dsMo.getSummary().getFreeSpace()),
morDs, vmMo.getScsiDeviceControllerKey());
vmMo.detachDisk(volumeDatastorePath, false);
vmMo.destroy();
if (vmMo == null) {
throw new Exception("Unable to create a dummy VM for volume creation");
}
return morDs;
Long volumeSizeToUse = volumeSize < dsMo.getSummary().getFreeSpace() ? volumeSize : dsMo.getSummary().getFreeSpace();
vmMo.createDisk(vmdkDatastorePath, getMBsFromBytes(volumeSizeToUse), dsMo.getMor(), vmMo.getScsiDeviceControllerKey());
vmMo.detachDisk(vmdkDatastorePath, false);
vmMo.destroy();
}
@Override
@ -4516,9 +4503,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
ManagedObjectReference morDs = null;
if (cmd.getAttach() && cmd.isManaged()) {
morDs = handleDatastoreAndVmdkAttach(cmd, cmd.get_iScsiName(), cmd.getStorageHost(), cmd.getStoragePort(),
cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword(),
cmd.getChapTargetUsername(), cmd.getChapTargetPassword());
morDs = getVmfsDatastore(hyperHost, getDatastoreName(cmd.get_iScsiName()), cmd.getStorageHost(), cmd.getStoragePort(), trimIqn(cmd.get_iScsiName()),
cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword(), cmd.getChapTargetUsername(), cmd.getChapTargetPassword());
DatastoreMO dsMo = new DatastoreMO(getServiceContext(), morDs);
String volumeDatastorePath = String.format("[%s] %s.vmdk", dsMo.getName(), dsMo.getName());
if (!dsMo.fileExists(volumeDatastorePath)) {
createVmdk(cmd, dsMo, VmwareResource.getDatastoreName(cmd.get_iScsiName()), cmd.getVolumeSize());
}
}
else {
morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, cmd.getPoolUuid());
@ -4531,10 +4525,18 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
}
DatastoreMO dsMo = new DatastoreMO(getServiceContext(), morDs);
VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder(dsMo.getOwnerDatacenter().first(), cmd.getVmName(),
dsMo, cmd.getVolumePath());
String datastoreVolumePath = dsMo.searchFileInSubFolders(cmd.getVolumePath() + ".vmdk", true);
String datastoreVolumePath = null;
if (cmd.isManaged()) {
datastoreVolumePath = dsMo.getDatastorePath(dsMo.getName() + ".vmdk");
}
else {
VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder(dsMo.getOwnerDatacenter().first(), cmd.getVmName(), dsMo, cmd.getVolumePath());
datastoreVolumePath = dsMo.searchFileInSubFolders(cmd.getVolumePath() + ".vmdk", true);
}
assert (datastoreVolumePath != null) : "Virtual disk file must exist in specified datastore for attach/detach operations.";
if (datastoreVolumePath == null) {
throw new CloudRuntimeException("Unable to find file " + cmd.getVolumePath() + ".vmdk in datastore " + dsMo.getName());
@ -4687,7 +4689,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
}
}
private ManagedObjectReference createVmfsDatastore(VmwareHypervisorHost hyperHost, String datastoreName, String storageIpAddress,
public ManagedObjectReference getVmfsDatastore(VmwareHypervisorHost hyperHost, String datastoreName, String storageIpAddress,
int storagePortNumber, String iqn, String chapName, String chapSecret, String mutualChapName, String mutualChapSecret) throws Exception {
VmwareContext context = getServiceContext();
ManagedObjectReference morCluster = hyperHost.getHyperHostCluster();

View File

@ -37,6 +37,7 @@ import com.cloud.hypervisor.vmware.manager.VmwareStorageManager;
import com.cloud.hypervisor.vmware.manager.VmwareStorageManagerImpl;
import com.cloud.hypervisor.vmware.manager.VmwareStorageMount;
import com.cloud.hypervisor.vmware.mo.ClusterMO;
import com.cloud.hypervisor.vmware.mo.DatastoreMO;
import com.cloud.hypervisor.vmware.mo.HostMO;
import com.cloud.hypervisor.vmware.mo.VmwareHostType;
import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
@ -347,8 +348,12 @@ public class VmwareSecondaryStorageResourceHandler implements SecondaryStorageRe
return true;
}
public ManagedObjectReference handleDatastoreAndVmdkAttach(Command cmd, String iqn, String storageHost, int storagePort,
String initiatorUsername, String initiatorPassword, String targetUsername, String targetPassword) throws Exception {
public ManagedObjectReference getVmfsDatastore(VmwareHypervisorHost hyperHost, String datastoreName, String storageIpAddress, int storagePortNumber,
String iqn, String initiatorChapName, String initiatorChapSecret, String mutualChapName, String mutualChapSecret) throws Exception {
throw new OperationNotSupportedException();
}
public void createVmdk(Command cmd, DatastoreMO dsMo, String volumeDatastorePath, Long volumeSize) throws Exception {
throw new OperationNotSupportedException();
}

View File

@ -1191,8 +1191,16 @@ public class VmwareStorageProcessor implements StorageProcessor {
ManagedObjectReference morDs = null;
if (isAttach && isManaged) {
morDs = hostService.handleDatastoreAndVmdkAttach(cmd, iScsiName, storageHost, storagePort,
initiatorUsername, initiatorPassword, targetUsername, targetPassword);
morDs = hostService.getVmfsDatastore(hyperHost, VmwareResource.getDatastoreName(iScsiName), storageHost, storagePort,
VmwareResource.trimIqn(iScsiName), initiatorUsername, initiatorPassword, targetUsername, targetPassword);
DatastoreMO dsMo = new DatastoreMO(hostService.getServiceContext(null), morDs);
String volumeDatastorePath = String.format("[%s] %s.vmdk", dsMo.getName(), dsMo.getName());
if (!dsMo.fileExists(volumeDatastorePath)) {
hostService.createVmdk(cmd, dsMo, VmwareResource.getDatastoreName(iScsiName), volumeTO.getSize());
}
}
else {
morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, isManaged ? VmwareResource.getDatastoreName(iScsiName) : primaryStore.getUuid());
@ -1207,24 +1215,35 @@ public class VmwareStorageProcessor implements StorageProcessor {
DatastoreMO dsMo = new DatastoreMO(this.hostService.getServiceContext(null), morDs);
String datastoreVolumePath;
if(isAttach) {
if(!isManaged)
datastoreVolumePath = VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder(dsMo.getOwnerDatacenter().first(), vmName,
dsMo, volumeTO.getPath());
else
if (isAttach) {
if (isManaged) {
datastoreVolumePath = dsMo.getDatastorePath(dsMo.getName() + ".vmdk");
} else {
datastoreVolumePath = VmwareStorageLayoutHelper.getLegacyDatastorePathFromVmdkFileName(dsMo, volumeTO.getPath() + ".vmdk");
if(!dsMo.fileExists(datastoreVolumePath))
datastoreVolumePath = VmwareStorageLayoutHelper.getVmwareDatastorePathFromVmdkFileName(dsMo, vmName, volumeTO.getPath() + ".vmdk");
}
else {
datastoreVolumePath = VmwareStorageLayoutHelper.syncVolumeToVmDefaultFolder(dsMo.getOwnerDatacenter().first(), vmName, dsMo, volumeTO.getPath());
}
}
else {
if (isManaged) {
datastoreVolumePath = dsMo.getDatastorePath(dsMo.getName() + ".vmdk");
}
else {
datastoreVolumePath = VmwareStorageLayoutHelper.getLegacyDatastorePathFromVmdkFileName(dsMo, volumeTO.getPath() + ".vmdk");
if (!dsMo.fileExists(datastoreVolumePath)) {
datastoreVolumePath = VmwareStorageLayoutHelper.getVmwareDatastorePathFromVmdkFileName(dsMo, vmName, volumeTO.getPath() + ".vmdk");
}
}
}
disk.setVdiUuid(datastoreVolumePath);
AttachAnswer answer = new AttachAnswer(disk);
if (isAttach) {
vmMo.attachDisk(new String[] { datastoreVolumePath }, morDs);
} else {
}
else {
vmMo.removeAllSnapshots();
vmMo.detachDisk(datastoreVolumePath, false);

View File

@ -5348,7 +5348,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
if (pool.getType() == StoragePoolType.NetworkFilesystem) {
getNfsSR(conn, pool);
} else if (pool.getType() == StoragePoolType.IscsiLUN) {
getIscsiSR(conn, pool.getUuid(), pool.getHost(), pool.getPath(), null, null, new Boolean[1]);
getIscsiSR(conn, pool.getUuid(), pool.getHost(), pool.getPath(), null, null);
} else if (pool.getType() == StoragePoolType.PreSetup) {
} else {
return new Answer(cmd, false, "The pool type: " + pool.getType().name() + " is not supported.");
@ -6166,17 +6166,27 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
}
protected VDI getVDIbyUuid(Connection conn, String uuid) {
return getVDIbyUuid(conn, uuid, true);
}
protected VDI getVDIbyUuid(Connection conn, String uuid, boolean throwExceptionIfNotFound) {
try {
return VDI.getByUuid(conn, uuid);
} catch (Exception e) {
String msg = "Catch Exception " + e.getClass().getName() + " :VDI getByUuid for uuid: " + uuid + " failed due to " + e.toString();
s_logger.debug(msg);
throw new CloudRuntimeException(msg, e);
if (throwExceptionIfNotFound) {
String msg = "Catch Exception " + e.getClass().getName() + " :VDI getByUuid for uuid: " + uuid + " failed due to " + e.toString();
s_logger.debug(msg);
throw new CloudRuntimeException(msg, e);
}
return null;
}
}
protected SR getIscsiSR(Connection conn, String srNameLabel, String target, String path,
String chapInitiatorUsername, String chapInitiatorPassword, Boolean[] created) {
String chapInitiatorUsername, String chapInitiatorPassword) {
synchronized (srNameLabel.intern()) {
Map<String, String> deviceConfig = new HashMap<String, String>();
try {
@ -6280,8 +6290,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
{
sr = SR.create(conn, host, deviceConfig, new Long(0), srNameLabel, srNameLabel, type, "user", true,
smConfig);
created[0] = true; // note that the SR was created (as opposed to introduced)
} else {
sr = SR.introduce(conn, pooluuid, srNameLabel, srNameLabel,
type, "user", true, smConfig);
@ -6459,54 +6467,41 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
}
}
protected VDI handleSrAndVdiAttach(String iqn, String storageHostName,
String chapInitiatorName, String chapInitiatorPassword) throws Types.XenAPIException, XmlRpcException {
protected VDI createVdi(SR sr, String vdiNameLabel, Long volumeSize) throws Types.XenAPIException, XmlRpcException {
VDI vdi = null;
Connection conn = getConnection();
Boolean[] created = { false };
VDI.Record vdir = new VDI.Record();
SR sr = getIscsiSR(conn, iqn,
storageHostName, iqn,
chapInitiatorName, chapInitiatorPassword, created);
vdir.nameLabel = vdiNameLabel;
vdir.SR = sr;
vdir.type = Types.VdiType.USER;
// if created[0] is true, this means the SR was actually created...as opposed to introduced
if (created[0]) {
VDI.Record vdir = new VDI.Record();
vdir.nameLabel = iqn;
vdir.SR = sr;
vdir.type = Types.VdiType.USER;
long totalSpace = sr.getPhysicalSize(conn);
long unavailableSpace = sr.getPhysicalUtilisation(conn);
vdir.virtualSize = totalSpace - unavailableSpace;
if (vdir.virtualSize < 0) {
throw new CloudRuntimeException("VDI virtual size cannot be less than 0.");
}
long maxNumberOfTries = (totalSpace / unavailableSpace >= 1) ? (totalSpace / unavailableSpace) : 1;
long tryNumber = 0;
while (tryNumber <= maxNumberOfTries) {
try {
vdi = VDI.create(conn, vdir);
break;
}
catch (Exception ex) {
tryNumber++;
vdir.virtualSize -= unavailableSpace;
}
}
long totalSrSpace = sr.getPhysicalSize(conn);
long unavailableSrSpace = sr.getPhysicalUtilisation(conn);
long availableSrSpace = totalSrSpace - unavailableSrSpace;
if (availableSrSpace < volumeSize) {
throw new CloudRuntimeException("Available space for SR cannot be less than " + volumeSize + ".");
}
else {
vdi = sr.getVDIs(conn).iterator().next();
vdir.virtualSize = volumeSize;
long maxNumberOfTries = (totalSrSpace / unavailableSrSpace >= 1) ? (totalSrSpace / unavailableSrSpace) : 1;
long tryNumber = 0;
while (tryNumber <= maxNumberOfTries) {
try {
vdi = VDI.create(conn, vdir);
break;
}
catch (Exception ex) {
tryNumber++;
vdir.virtualSize -= unavailableSrSpace;
}
}
return vdi;
@ -6534,12 +6529,17 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
}
try {
// Look up the VDI
VDI vdi = null;
if (cmd.getAttach() && cmd.isManaged()) {
vdi = handleSrAndVdiAttach(cmd.get_iScsiName(), cmd.getStorageHost(),
cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword());
SR sr = getIscsiSR(conn, cmd.get_iScsiName(), cmd.getStorageHost(), cmd.get_iScsiName(),
cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword());
vdi = getVDIbyUuid(conn, cmd.getVolumePath(), false);
if (vdi == null) {
vdi = createVdi(sr, cmd.get_iScsiName(), cmd.getVolumeSize());
}
}
else {
vdi = getVDIbyUuid(conn, cmd.getVolumePath());

View File

@ -167,12 +167,20 @@ public class XenServerStorageProcessor implements StorageProcessor {
try {
Connection conn = this.hypervisorResource.getConnection();
// Look up the VDI
VDI vdi = null;
if (cmd.isManaged()) {
vdi = this.hypervisorResource.handleSrAndVdiAttach(cmd.get_iScsiName(), cmd.getStorageHost(),
cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword());
SR sr = this.hypervisorResource.getIscsiSR(conn, cmd.get_iScsiName(), cmd.getStorageHost(), cmd.get_iScsiName(),
cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword());
vdi = this.hypervisorResource.getVDIbyUuid(conn, data.getPath(), false);
if (vdi == null) {
VolumeObjectTO volume = (VolumeObjectTO)data;
vdi = this.hypervisorResource.createVdi(sr, cmd.get_iScsiName(), volume.getSize());
}
}
else {
vdi = this.hypervisorResource.mount(conn, null, null, data.getPath());

View File

@ -277,8 +277,10 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), getDefaultBurstIops(storagePoolId, volumeInfo.getMaxIops()));
}
long volumeSize = volumeInfo.getSize() * 2; // in reality, use a multiplier that's at cluster-level scope
long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword,
getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeInfo.getSize(), true,
getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true, volumeInfo.getSize().toString(),
iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);

View File

@ -78,13 +78,13 @@ public class SolidFireUtil
public static final String USE_MUTUAL_CHAP_FOR_VMWARE = "useMutualChapForVMware";
public static long createSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword,
String strSfVolumeName, long lSfAccountId, long lTotalSize, boolean bEnable512e,
String strSfVolumeName, long lSfAccountId, long lTotalSize, boolean bEnable512e, final String strCloudStackVolumeSize,
long lMinIops, long lMaxIops, long lBurstIops)
{
final Gson gson = new GsonBuilder().create();
VolumeToCreate volumeToCreate = new VolumeToCreate(strSfVolumeName, lSfAccountId, lTotalSize, bEnable512e,
lMinIops, lMaxIops, lBurstIops);
strCloudStackVolumeSize, lMinIops, lMaxIops, lBurstIops);
String strVolumeToCreateJson = gson.toJson(volumeToCreate);
@ -443,10 +443,10 @@ public class SolidFireUtil
private final VolumeToCreateParams params;
private VolumeToCreate(final String strVolumeName, final long lAccountId, final long lTotalSize,
final boolean bEnable512e, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS)
final boolean bEnable512e, final String strCloudStackVolumeSize, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS)
{
params = new VolumeToCreateParams(strVolumeName, lAccountId, lTotalSize, bEnable512e,
lMinIOPS, lMaxIOPS, lBurstIOPS);
strCloudStackVolumeSize, lMinIOPS, lMaxIOPS, lBurstIOPS);
}
private static final class VolumeToCreateParams
@ -456,18 +456,30 @@ public class SolidFireUtil
private final long totalSize;
private final boolean enable512e;
private final VolumeToCreateParamsQoS qos;
private final VolumeToCreateParamsAttributes attributes;
private VolumeToCreateParams(final String strVolumeName, final long lAccountId, final long lTotalSize,
final boolean bEnable512e, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS)
final boolean bEnable512e, final String strCloudStackVolumeSize, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS)
{
name = strVolumeName;
accountID = lAccountId;
totalSize = lTotalSize;
enable512e = bEnable512e;
attributes = new VolumeToCreateParamsAttributes(strCloudStackVolumeSize);
qos = new VolumeToCreateParamsQoS(lMinIOPS, lMaxIOPS, lBurstIOPS);
}
private static final class VolumeToCreateParamsAttributes
{
private final String CloudStackVolumeSize;
private VolumeToCreateParamsAttributes(final String strCloudStackVolumeSize)
{
CloudStackVolumeSize = strCloudStackVolumeSize;
}
}
private static final class VolumeToCreateParamsQoS
{
private final long minIOPS;

View File

@ -1047,8 +1047,17 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
HypervisorType rootDiskHyperType = vm.getHypervisorType();
HypervisorType dataDiskHyperType = _volsDao.getHypervisorType(volume.getId());
if (dataDiskHyperType != HypervisorType.None && rootDiskHyperType != dataDiskHyperType) {
throw new InvalidParameterValueException("Can't attach a volume created by: " + dataDiskHyperType + " to a " + rootDiskHyperType + " vm");
VolumeVO dataDiskVol = _volsDao.findById(volume.getId());
StoragePoolVO dataDiskStoragePool = _storagePoolDao.findById(dataDiskVol.getPoolId());
// managed storage can be used for different types of hypervisors
// only perform this check if the volume's storage pool is not null and not managed
if (dataDiskStoragePool != null && !dataDiskStoragePool.isManaged()) {
if (dataDiskHyperType != HypervisorType.None && rootDiskHyperType != dataDiskHyperType) {
throw new InvalidParameterValueException("Can't attach a volume created by: " + dataDiskHyperType +
" to a " + rootDiskHyperType + " vm");
}
}
deviceId = getDeviceId(vmId, deviceId);