Pass storage scope during KVM volume migration to avoid remotely moun… (#190)

* Use cryptsetup w/o zeroing for encrypted scaleio - faster

Signed-off-by: Marcus Sorensen <mls@apple.com>

* Pass storage scope during KVM volume migration to avoid remotely mounting local storage

Signed-off-by: Marcus Sorensen <mls@apple.com>

* Add method to choose template pool based on scope

Signed-off-by: Marcus Sorensen <mls@apple.com>

* Clean up null check when creating migration options

Signed-off-by: Marcus Sorensen <mls@apple.com>

* ScaleIO enhancements - thin/thick encrypted, online resize

Signed-off-by: Marcus Sorensen <mls@apple.com>
Co-authored-by: Marcus Sorensen <mls@apple.com>
This commit is contained in:
Marcus Sorensen 2022-08-26 09:47:04 -06:00 committed by GitHub
parent 557045bced
commit d4596ddc9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 343 additions and 109 deletions

View File

@ -16,6 +16,7 @@
// under the License.
package com.cloud.agent.api.to;
import com.cloud.agent.api.LogLevel;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.StoragePool;
@ -24,6 +25,7 @@ public class StorageFilerTO {
String uuid;
String host;
String path;
@LogLevel(LogLevel.Log4jLevel.Off)
String userInfo;
int port;
StoragePoolType type;

View File

@ -25,6 +25,7 @@ public class MigrationOptions implements Serializable {
private String srcPoolUuid;
private Storage.StoragePoolType srcPoolType;
private Type type;
private ScopeType scopeType;
private String srcBackingFilePath;
private boolean copySrcTemplate;
private String srcVolumeUuid;
@ -37,18 +38,20 @@ public class MigrationOptions implements Serializable {
public MigrationOptions() {
}
public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcBackingFilePath, boolean copySrcTemplate) {
public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcBackingFilePath, boolean copySrcTemplate, ScopeType scopeType) {
this.srcPoolUuid = srcPoolUuid;
this.srcPoolType = srcPoolType;
this.type = Type.LinkedClone;
this.scopeType = scopeType;
this.srcBackingFilePath = srcBackingFilePath;
this.copySrcTemplate = copySrcTemplate;
}
public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcVolumeUuid) {
public MigrationOptions(String srcPoolUuid, Storage.StoragePoolType srcPoolType, String srcVolumeUuid, ScopeType scopeType) {
this.srcPoolUuid = srcPoolUuid;
this.srcPoolType = srcPoolType;
this.type = Type.FullClone;
this.scopeType = scopeType;
this.srcVolumeUuid = srcVolumeUuid;
}
@ -60,6 +63,8 @@ public class MigrationOptions implements Serializable {
return srcPoolType;
}
public ScopeType getScopeType() { return scopeType; }
public String getSrcBackingFilePath() {
return srcBackingFilePath;
}

View File

@ -1738,20 +1738,16 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
*/
protected MigrationOptions createLinkedCloneMigrationOptions(VolumeInfo srcVolumeInfo, VolumeInfo destVolumeInfo, String srcVolumeBackingFile, String srcPoolUuid, Storage.StoragePoolType srcPoolType) {
VMTemplateStoragePoolVO ref = templatePoolDao.findByPoolTemplate(destVolumeInfo.getPoolId(), srcVolumeInfo.getTemplateId(), null);
// if template exists on destination, use it as the backing file
if (ref != null) {
return new MigrationOptions(destVolumeInfo.getDataStore().getUuid(), destVolumeInfo.getStoragePoolType(), ref.getInstallPath(), false);
} else {
return new MigrationOptions(srcPoolUuid, srcPoolType, srcVolumeBackingFile, true);
}
boolean updateBackingFileReference = ref == null;
String backingFile = !updateBackingFileReference ? ref.getInstallPath() : srcVolumeBackingFile;
return new MigrationOptions(srcPoolUuid, srcPoolType, backingFile, updateBackingFileReference, srcVolumeInfo.getDataStore().getScope().getScopeType());
}
/**
* Return expected MigrationOptions for a full clone volume live storage migration
*/
protected MigrationOptions createFullCloneMigrationOptions(VolumeInfo srcVolumeInfo, VirtualMachineTO vmTO, Host srcHost, String srcPoolUuid, Storage.StoragePoolType srcPoolType) {
return new MigrationOptions(srcPoolUuid, srcPoolType, srcVolumeInfo.getPath());
return new MigrationOptions(srcPoolUuid, srcPoolType, srcVolumeInfo.getPath(), srcVolumeInfo.getDataStore().getScope().getScopeType());
}
/**

View File

@ -24,7 +24,6 @@ import java.io.StringReader;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@ -55,7 +54,6 @@ import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.cloudstack.utils.cryptsetup.CryptSetup;
import org.apache.cloudstack.utils.cryptsetup.KeyFile;
import org.apache.cloudstack.utils.hypervisor.HypervisorUtils;
import org.apache.cloudstack.utils.linux.CPUStat;
import org.apache.cloudstack.utils.linux.KVMHostInfo;
@ -2901,6 +2899,9 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
pool.getUuid(), devId, diskBusType, DiskProtocol.RBD, DiskDef.DiskFmtType.RAW);
} else if (pool.getType() == StoragePoolType.PowerFlex) {
disk.defBlockBasedDisk(physicalDisk.getPath(), devId, diskBusTypeData);
if (physicalDisk.getFormat().equals(PhysicalDiskFormat.QCOW2)) {
disk.setDiskFormatType(DiskDef.DiskFmtType.QCOW2);
}
} else if (pool.getType() == StoragePoolType.Gluster) {
final String mountpoint = pool.getLocalPath();
final String path = physicalDisk.getPath();
@ -4661,28 +4662,21 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
Path testFile = Paths.get(javaTempDir, UUID.randomUUID().toString()).normalize().toAbsolutePath();
String objectName = "sec0";
Map<String, String> options = new HashMap<String, String>();
List<QemuObject> passphraseObjects = new ArrayList<>();
QemuImgFile file = new QemuImgFile(testFile.toString(), 64<<20, PhysicalDiskFormat.QCOW2);
try (KeyFile keyFile = new KeyFile(UUID.randomUUID().toString().getBytes())){
try {
QemuImg qemu = new QemuImg(0);
passphraseObjects.add(QemuObject.prepareSecretForQemuImg(PhysicalDiskFormat.QCOW2, QemuObject.EncryptFormat.LUKS, keyFile.toString(), "sec0", options));
qemu.create(file, null, options, passphraseObjects);
s_logger.info("Host's qemu install supports encryption");
} catch (QemuImgException | IOException | LibvirtException ex) {
if (!qemu.supportsImageFormat(PhysicalDiskFormat.LUKS)) {
return false;
}
} catch (QemuImgException | LibvirtException ex) {
s_logger.info("Host's qemu install doesn't support encryption", ex);
return false;
}
// cleanup
try {
Files.deleteIfExists(testFile);
} catch (IOException ex) {
s_logger.warn(String.format("Failed to clean up test file '%s'", testFile.toAbsolutePath()), ex);
}
// test cryptsetup
CryptSetup crypt = new CryptSetup();
if (!crypt.isSupported()) {

View File

@ -977,6 +977,8 @@ public class LibvirtVMDef {
return _diskFmtType;
}
public void setDiskFormatType(DiskFmtType type) { _diskFmtType = type; }
public void setBytesReadRate(Long bytesReadRate) {
_bytesReadRate = bytesReadRate;
}

View File

@ -24,12 +24,15 @@ import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.cloud.hypervisor.kvm.storage.ScaleIOStorageAdaptor;
import org.apache.cloudstack.utils.cryptsetup.KeyFile;
import org.apache.cloudstack.utils.qemu.QemuImageOptions;
import org.apache.cloudstack.utils.qemu.QemuImg;
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
import org.apache.cloudstack.utils.qemu.QemuImgException;
import org.apache.cloudstack.utils.qemu.QemuImgFile;
import org.apache.cloudstack.utils.qemu.QemuObject;
import org.apache.log4j.Logger;
import org.libvirt.Connect;
@ -63,7 +66,7 @@ public final class LibvirtResizeVolumeCommandWrapper extends CommandWrapper<Resi
@Override
public Answer execute(final ResizeVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
final String volumeId = command.getPath();
final long newSize = command.getNewSize();
long newSize = command.getNewSize();
final long currentSize = command.getCurrentSize();
final String vmInstanceName = command.getInstanceName();
final boolean shrinkOk = command.getShrinkOk();
@ -80,11 +83,34 @@ public final class LibvirtResizeVolumeCommandWrapper extends CommandWrapper<Resi
final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr();
KVMStoragePool pool = storagePoolMgr.getStoragePool(spool.getType(), spool.getUuid());
if (spool.getType().equals(StoragePoolType.PowerFlex)) {
pool.connectPhysicalDisk(volumeId, null);
}
final KVMPhysicalDisk vol = pool.getPhysicalDisk(volumeId);
final String path = vol.getPath();
String type = notifyOnlyType;
if (pool.getType() != StoragePoolType.RBD && pool.getType() != StoragePoolType.Linstor) {
if (spool.getType().equals(StoragePoolType.PowerFlex) && vol.getFormat().equals(PhysicalDiskFormat.QCOW2)) {
// PowerFlex QCOW2 sizing needs to consider overhead.
newSize = ScaleIOStorageAdaptor.getUsableBytesFromRawBytes(newSize);
} else if (spool.getType().equals(StoragePoolType.PowerFlex)) {
// PowerFlex RAW/LUKS is already resized, we just notify the domain based on new size (considering LUKS overhead)
try {
QemuImg qemu = new QemuImg(0);
QemuImgFile qemuFile = new QemuImgFile(path);
Map<String, String> info = qemu.info(qemuFile);
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
newSize = Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
} else {
throw new CloudRuntimeException("Unable to determine size of powerflex RAW volume for resize notification");
}
} catch (QemuImgException | LibvirtException ex) {
throw new CloudRuntimeException("Error when inspecting powerflex volume for size", ex);
}
}
if (pool.getType() != StoragePoolType.RBD && pool.getType() != StoragePoolType.Linstor && pool.getType() != StoragePoolType.PowerFlex) {
type = libvirtComputingResource.getResizeScriptType(pool, vol);
if (type.equals("QCOW2") && shrinkOk) {
return new ResizeVolumeAnswer(command, false, "Unable to shrink volumes of type " + type);
@ -96,8 +122,8 @@ public final class LibvirtResizeVolumeCommandWrapper extends CommandWrapper<Resi
s_logger.debug("Resizing volume: " + path + ", from: " + toHumanReadableSize(currentSize) + ", to: " + toHumanReadableSize(newSize) + ", type: " + type + ", name: " + vmInstanceName + ", shrinkOk: " + shrinkOk);
/* libvirt doesn't support resizing (C)LVM devices, and corrupts QCOW2 in some scenarios, so we have to do these via qemu-img */
if (pool.getType() != StoragePoolType.CLVM && pool.getType() != StoragePoolType.Linstor &&
vol.getFormat() != PhysicalDiskFormat.QCOW2) {
if (pool.getType() != StoragePoolType.CLVM && pool.getType() != StoragePoolType.Linstor && pool.getType() != StoragePoolType.PowerFlex
&& vol.getFormat() != PhysicalDiskFormat.QCOW2) {
s_logger.debug("Volume " + path + " can be resized by libvirt. Asking libvirt to resize the volume.");
try {
final LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();

View File

@ -394,11 +394,11 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor {
@Override
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) {
return copyPhysicalDisk(disk, name, destPool, timeout, null, null);
return copyPhysicalDisk(disk, name, destPool, timeout, null, null, null);
}
@Override
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk srcDisk, String destVolumeUuid, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] destPassphrase) {
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk srcDisk, String destVolumeUuid, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] destPassphrase, ProvisioningType provisioningType) {
QemuImgFile srcFile;

View File

@ -425,12 +425,12 @@ public class KVMStoragePoolManager {
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) {
StorageAdaptor adaptor = getStorageAdaptor(destPool.getType());
return adaptor.copyPhysicalDisk(disk, name, destPool, timeout, null, null);
return adaptor.copyPhysicalDisk(disk, name, destPool, timeout, null, null, null);
}
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] dstPassphrase) {
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] dstPassphrase, Storage.ProvisioningType provisioningType) {
StorageAdaptor adaptor = getStorageAdaptor(destPool.getType());
return adaptor.copyPhysicalDisk(disk, name, destPool, timeout, srcPassphrase, dstPassphrase);
return adaptor.copyPhysicalDisk(disk, name, destPool, timeout, srcPassphrase, dstPassphrase, provisioningType);
}
public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, String snapshotName, String name, KVMStoragePool destPool, int timeout) {

View File

@ -38,6 +38,7 @@ import java.util.UUID;
import javax.naming.ConfigurationException;
import com.cloud.storage.ScopeType;
import org.apache.cloudstack.agent.directdownload.DirectDownloadAnswer;
import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand;
import org.apache.cloudstack.agent.directdownload.HttpDirectDownloadCommand;
@ -415,7 +416,7 @@ public class KVMStorageProcessor implements StorageProcessor {
s_logger.warn("Failed to connect new volume at path: " + path + ", in storage pool id: " + primaryStore.getUuid());
}
vol = storagePoolMgr.copyPhysicalDisk(BaseVol, path != null ? path : volume.getUuid(), primaryPool, cmd.getWaitInMillSeconds(), null, volume.getPassphrase());
vol = storagePoolMgr.copyPhysicalDisk(BaseVol, path != null ? path : volume.getUuid(), primaryPool, cmd.getWaitInMillSeconds(), null, volume.getPassphrase(), volume.getProvisioningType());
storagePoolMgr.disconnectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), path);
} else {
@ -1366,6 +1367,11 @@ public class KVMStorageProcessor implements StorageProcessor {
final String glusterVolume = attachingPool.getSourceDir().replace("/", "");
diskdef.defNetworkBasedDisk(glusterVolume + path.replace(mountpoint, ""), attachingPool.getSourceHost(), attachingPool.getSourcePort(), null,
null, devId, busT, DiskProtocol.GLUSTER, DiskDef.DiskFmtType.QCOW2);
} else if (attachingPool.getType() == StoragePoolType.PowerFlex) {
diskdef.defBlockBasedDisk(attachingDisk.getPath(), devId, busT);
if (attachingDisk.getFormat() == PhysicalDiskFormat.QCOW2) {
diskdef.setDiskFormatType(DiskDef.DiskFmtType.QCOW2);
}
} else if (attachingDisk.getFormat() == PhysicalDiskFormat.QCOW2) {
diskdef.defFileBasedDisk(attachingDisk.getPath(), devId, busT, DiskDef.DiskFmtType.QCOW2);
} else if (attachingDisk.getFormat() == PhysicalDiskFormat.RAW) {
@ -1552,14 +1558,15 @@ public class KVMStorageProcessor implements StorageProcessor {
}
MigrationOptions migrationOptions = volume.getMigrationOptions();
if (isLinkedCloneMigration(migrationOptions)) {
String srcStoreUuid = migrationOptions.getSrcPoolUuid();
StoragePoolType srcPoolType = migrationOptions.getSrcPoolType();
KVMStoragePool srcPool = storagePoolMgr.getStoragePool(srcPoolType, srcStoreUuid);
if (migrationOptions != null) {
int timeout = migrationOptions.getTimeout();
vol = createLinkedCloneVolume(migrationOptions, srcPool, primaryPool, volume, format, timeout);
} else if (isFullCloneMigration(migrationOptions)) {
vol = createFullCloneVolume(migrationOptions, volume, primaryPool, format);
if (migrationOptions.getType() == MigrationOptions.Type.LinkedClone) {
KVMStoragePool srcPool = getTemplateSourcePoolUsingMigrationOptions(primaryPool, migrationOptions);
vol = createLinkedCloneVolume(migrationOptions, srcPool, primaryPool, volume, format, timeout);
} else if (migrationOptions.getType() == MigrationOptions.Type.FullClone) {
vol = createFullCloneVolume(migrationOptions, volume, primaryPool, format);
}
} else {
vol = primaryPool.createPhysicalDisk(volume.getUuid(), format,
volume.getProvisioningType(), disksize, volume.getPassphrase());
@ -1584,14 +1591,6 @@ public class KVMStorageProcessor implements StorageProcessor {
}
}
protected static boolean isLinkedCloneMigration(MigrationOptions options) {
return options != null && options.getType() == MigrationOptions.Type.LinkedClone;
}
protected static boolean isFullCloneMigration(MigrationOptions options) {
return options != null && options.getType() == MigrationOptions.Type.FullClone;
}
protected static final MessageFormat SnapshotXML = new MessageFormat(" <domainsnapshot>" + " <name>{0}</name>" + " <domain>"
+ " <uuid>{1}</uuid>" + " </domain>" + " </domainsnapshot>");
@ -2186,4 +2185,23 @@ public class KVMStorageProcessor implements StorageProcessor {
s_logger.info("SyncVolumePathCommand not currently applicable for KVMStorageProcessor");
return new Answer(cmd, false, "Not currently applicable for KVMStorageProcessor");
}
/**
* Determine if migration is using host-local source pool. If so, return this host's storage as the template source,
* rather than remote host's
* @param localPool The host-local storage pool being migrated to
* @param migrationOptions The migration options provided with a migrating volume
* @return
*/
public KVMStoragePool getTemplateSourcePoolUsingMigrationOptions(KVMStoragePool localPool, MigrationOptions migrationOptions) {
if (migrationOptions == null) {
throw new CloudRuntimeException("Migration options cannot be null when choosing a storage pool for migration");
}
if (migrationOptions.getScopeType().equals(ScopeType.HOST)) {
return localPool;
}
return storagePoolMgr.getStoragePool(migrationOptions.getSrcPoolType(), migrationOptions.getSrcPoolUuid());
}
}

View File

@ -1282,7 +1282,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
@Override
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) {
return copyPhysicalDisk(disk, name, destPool, timeout, null, null);
return copyPhysicalDisk(disk, name, destPool, timeout, null, null, null);
}
/**
@ -1292,7 +1292,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
* in ManagementServerImpl shows that the destPool is always a Secondary Storage Pool
*/
@Override
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] dstPassphrase) {
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] dstPassphrase, Storage.ProvisioningType provisioningType) {
/**
With RBD you can't run qemu-img convert with an existing RBD image as destination

View File

@ -404,11 +404,11 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
@Override
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) {
return copyPhysicalDisk(disk, name, destPool, timeout, null, null);
return copyPhysicalDisk(disk, name, destPool, timeout, null, null, null);
}
@Override
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout, byte[] srcPassphrase, byte[] destPassphrase)
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout, byte[] srcPassphrase, byte[] destPassphrase, Storage.ProvisioningType provisioningType)
{
s_logger.debug("Linstor: copyPhysicalDisk");
final QemuImg.PhysicalDiskFormat sourceFormat = disk.getFormat();

View File

@ -291,11 +291,11 @@ public class ManagedNfsStorageAdaptor implements StorageAdaptor {
@Override
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) {
return copyPhysicalDisk(disk, name, destPool, timeout, null, null);
return copyPhysicalDisk(disk, name, destPool, timeout, null, null, null);
}
@Override
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] destPassphrase) {
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[] destPassphrase, ProvisioningType provisioningType) {
throw new UnsupportedOperationException("Copying a disk is not supported in this configuration.");
}

View File

@ -153,6 +153,8 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
/**
* ScaleIO doesn't need to communicate with the hypervisor normally to create a volume. This is used only to prepare a ScaleIO data disk for encryption.
* Thin encrypted volumes are provisioned in QCOW2 format, which insulates the guest from zeroes/unallocated blocks in the block device that would
* otherwise show up as garbage data through the encryption layer. As a bonus, encrypted QCOW2 format handles discard.
* @param name disk path
* @param pool pool
* @param format disk format
@ -173,12 +175,32 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
KVMPhysicalDisk disk = getPhysicalDisk(name, pool);
try {
CryptSetup crypt = new CryptSetup();
crypt.luksFormat(passphrase, CryptSetup.LuksType.LUKS, disk.getPath());
if (provisioningType.equals(Storage.ProvisioningType.THIN)) {
disk.setFormat(QemuImg.PhysicalDiskFormat.QCOW2);
disk.setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS);
} catch (CryptSetupException ex) {
throw new CloudRuntimeException("Failed to set up encryption for block device " + disk.getPath(), ex);
try (KeyFile keyFile = new KeyFile(passphrase)){
QemuImg qemuImg = new QemuImg(0, true, true);
Map<String, String> options = new HashMap<>();
List<QemuObject> qemuObjects = new ArrayList<>();
long formattedSize = getUsableBytesFromRawBytes(disk.getSize());
options.put("preallocation", QemuImg.PreallocationType.Metadata.toString());
qemuObjects.add(QemuObject.prepareSecretForQemuImg(disk.getFormat(), disk.getQemuEncryptFormat(), keyFile.toString(), "sec0", options));
QemuImgFile file = new QemuImgFile(disk.getPath(), formattedSize, disk.getFormat());
qemuImg.create(file, null, options, qemuObjects);
LOGGER.debug(String.format("Successfully formatted %s as encrypted QCOW2", file.getFileName()));
} catch (QemuImgException | LibvirtException | IOException ex) {
throw new CloudRuntimeException("Failed to set up encrypted QCOW on block device " + disk.getPath(), ex);
}
} else {
try {
CryptSetup crypt = new CryptSetup();
crypt.luksFormat(passphrase, CryptSetup.LuksType.LUKS, disk.getPath());
disk.setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS);
disk.setFormat(QemuImg.PhysicalDiskFormat.RAW);
} catch (CryptSetupException ex) {
throw new CloudRuntimeException("Failed to set up encryption for block device " + disk.getPath(), ex);
}
}
return disk;
@ -295,16 +317,20 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
@Override
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout) {
return copyPhysicalDisk(disk, name, destPool, timeout, null, null);
return copyPhysicalDisk(disk, name, destPool, timeout, null, null, Storage.ProvisioningType.THIN);
}
@Override
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[]dstPassphrase) {
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPool, int timeout, byte[] srcPassphrase, byte[]dstPassphrase, Storage.ProvisioningType provisioningType) {
if (Strings.isNullOrEmpty(name) || disk == null || destPool == null) {
LOGGER.error("Unable to copy physical disk due to insufficient data");
throw new CloudRuntimeException("Unable to copy physical disk due to insufficient data");
}
if (provisioningType == null) {
provisioningType = Storage.ProvisioningType.THIN;
}
LOGGER.debug("Copy physical disk with size: " + disk.getSize() + ", virtualsize: " + disk.getVirtualSize()+ ", format: " + disk.getFormat());
KVMPhysicalDisk destDisk = destPool.getPhysicalDisk(name);
@ -313,21 +339,12 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
throw new CloudRuntimeException("Failed to find the disk: " + name + " of the storage pool: " + destPool.getUuid());
}
// golden copies of templates should be kept as qcow2 in the PowerFlex storage pool
if (disk.isTemplate()) {
LOGGER.debug("This is a template copy, storing in powerflex as QCOW2");
destDisk.setFormat(QemuImg.PhysicalDiskFormat.QCOW2);
} else {
LOGGER.debug("This is not a template copy, storing in powerflex as RAW");
destDisk.setFormat(QemuImg.PhysicalDiskFormat.RAW);
}
destDisk.setVirtualSize(disk.getVirtualSize());
destDisk.setSize(disk.getSize());
QemuImg qemu = null;
QemuImgFile srcFile = null;
QemuImgFile destFile = null;
QemuImgFile srcQemuFile = null;
QemuImgFile destQemuFile = null;
String srcKeyName = "sec0";
String destKeyName = "sec1";
List<QemuObject> qemuObjects = new ArrayList<>();
@ -335,46 +352,52 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
CryptSetup cryptSetup = null;
try (KeyFile srcKey = new KeyFile(srcPassphrase); KeyFile dstKey = new KeyFile(dstPassphrase)){
qemu = new QemuImg(timeout, true, true);
qemu = new QemuImg(timeout, provisioningType.equals(Storage.ProvisioningType.FAT), true);
String srcPath = disk.getPath();
String destPath = destDisk.getPath();
QemuImg.PhysicalDiskFormat destFormat = destDisk.getFormat();
QemuImageOptions qemuImageOpts = new QemuImageOptions(srcPath);
srcQemuFile = new QemuImgFile(srcPath, disk.getFormat());
destQemuFile = new QemuImgFile(destPath);
if (disk.isTemplate()) {
destQemuFile.setFormat(QemuImg.PhysicalDiskFormat.QCOW2);
}
if (srcKey.isSet()) {
qemuObjects.add(QemuObject.prepareSecretForQemuImg(disk.getFormat(), null , srcKey.toString(), srcKeyName, options));
qemuObjects.add(QemuObject.prepareSecretForQemuImg(disk.getFormat(), disk.getQemuEncryptFormat(), srcKey.toString(), srcKeyName, options));
qemuImageOpts = new QemuImageOptions(disk.getFormat(), srcPath, srcKeyName);
}
if (dstKey.isSet()) {
if (qemu.supportsSkipZeros()) {
// format and open luks device rather than letting qemu do a slow copy of full image
cryptSetup = new CryptSetup();
cryptSetup.luksFormat(dstPassphrase, CryptSetup.LuksType.LUKS, destDisk.getPath());
cryptSetup.open(dstPassphrase, CryptSetup.LuksType.LUKS, destDisk.getPath(), name);
destPath = String.format("/dev/mapper/%s", name);
if (!provisioningType.equals(Storage.ProvisioningType.FAT)) {
destDisk.setFormat(QemuImg.PhysicalDiskFormat.QCOW2);
destQemuFile.setFormat(QemuImg.PhysicalDiskFormat.QCOW2);
options.put("preallocation", QemuImg.PreallocationType.Metadata.toString());
} else {
qemuObjects.add(QemuObject.prepareSecretForQemuImg(destDisk.getFormat(), null, dstKey.toString(), destKeyName, options));
destFormat = QemuImg.PhysicalDiskFormat.LUKS;
qemu.setSkipZero(false);
destDisk.setFormat(QemuImg.PhysicalDiskFormat.RAW);
// qemu-img wants to treat RAW + encrypt formatting as LUKS
destQemuFile.setFormat(QemuImg.PhysicalDiskFormat.LUKS);
}
qemuObjects.add(QemuObject.prepareSecretForQemuImg(destDisk.getFormat(), QemuObject.EncryptFormat.LUKS, dstKey.toString(), destKeyName, options));
destDisk.setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS);
}
srcFile = new QemuImgFile(srcPath, disk.getFormat());
destFile = new QemuImgFile(destPath, destFormat);
boolean forceSourceFormat = srcQemuFile.getFormat() == QemuImg.PhysicalDiskFormat.RAW;
LOGGER.debug(String.format("Starting copy from source disk %s(%s) to PowerFlex volume %s(%s), forcing source format is %b", srcQemuFile.getFileName(), srcQemuFile.getFormat(), destQemuFile.getFileName(), destQemuFile.getFormat(), forceSourceFormat));
qemu.convert(srcQemuFile, destQemuFile, options, qemuObjects, qemuImageOpts,null, forceSourceFormat);
LOGGER.debug("Successfully converted source disk image " + srcQemuFile.getFileName() + " to PowerFlex volume: " + destDisk.getPath());
boolean forceSourceFormat = srcFile.getFormat() == QemuImg.PhysicalDiskFormat.RAW;
LOGGER.debug(String.format("Starting copy from source disk %s(%s) to PowerFlex volume %s(%s), forcing source format is %b", srcFile.getFileName(), srcFile.getFormat(), destFile.getFileName(), destFile.getFormat(), forceSourceFormat));
if (destFile.getFormat() == QemuImg.PhysicalDiskFormat.QCOW2) {
destFile.setSize(disk.getVirtualSize());
LOGGER.debug(String.format("Pre-formatting qcow2 block device %s to size %s", destFile.getFileName(), destFile.getSize()));
qemu.create(destFile);
if (destQemuFile.getFormat() == QemuImg.PhysicalDiskFormat.QCOW2 && !disk.isTemplate()) {
QemuImageOptions resizeOptions = new QemuImageOptions(destQemuFile.getFormat(), destPath, destKeyName);
resizeQcow2ToVolume(destPath, resizeOptions, qemuObjects, timeout);
LOGGER.debug("Resized volume at " + destPath);
}
qemu.convert(srcFile, destFile, options, qemuObjects, qemuImageOpts,null, forceSourceFormat);
LOGGER.debug("Succesfully converted source disk image " + srcFile.getFileName() + " to PowerFlex volume: " + destDisk.getPath());
} catch (QemuImgException | LibvirtException | IOException | CryptSetupException e) {
} catch (QemuImgException | LibvirtException | IOException e) {
try {
Map<String, String> srcInfo = qemu.info(srcFile);
Map<String, String> srcInfo = qemu.info(srcQemuFile);
LOGGER.debug("Source disk info: " + Arrays.asList(srcInfo));
} catch (Exception ignored) {
LOGGER.warn("Unable to get info from source disk: " + disk.getName());
@ -479,7 +502,7 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
srcFile = new QemuImgFile(srcTemplateFilePath, srcFileFormat);
qemu.info(srcFile);
/**
* Even though the disk itself is raw, we store templates on ScaleIO the raw volumes in qcow2 format.
* Even though the disk itself is raw, we store templates on ScaleIO in qcow2 format.
* This improves performance by reading/writing less data to volume, saves the unused space for encryption header, and
* nicely encapsulates VM images that might contain LUKS data (as opposed to converting to raw which would look like a LUKS volume).
*/
@ -516,4 +539,25 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
throw new CloudRuntimeException("Unable to extract template " + downloadedTemplateFile);
}
}
public void resizeQcow2ToVolume(String volumePath, QemuImageOptions options, List<QemuObject> objects, Integer timeout) throws QemuImgException, LibvirtException {
long rawSizeBytes = getPhysicalDiskSize(volumePath);
long usableSizeBytes = getUsableBytesFromRawBytes(rawSizeBytes);
QemuImg qemu = new QemuImg(timeout);
qemu.resize(options, objects, usableSizeBytes);
}
/**
* Calculates usable size from raw size, assuming qcow2 requires 192k/1GB for metadata
* We also remove 32MiB for potential encryption/safety factor.
* @param raw size in bytes
* @return usable size in bytesbytes
*/
public static long getUsableBytesFromRawBytes(Long raw) {
long usable = raw - (32 << 20) - ((raw >> 30) * 200704);
if (usable < 0) {
usable = 0L;
}
return usable;
}
}

View File

@ -65,7 +65,7 @@ public interface StorageAdaptor {
public List<KVMPhysicalDisk> listPhysicalDisks(String storagePoolUuid, KVMStoragePool pool);
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout);
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout, byte[] srcPassphrase, byte[] dstPassphrase);
public KVMPhysicalDisk copyPhysicalDisk(KVMPhysicalDisk disk, String name, KVMStoragePool destPools, int timeout, byte[] srcPassphrase, byte[] dstPassphrase, Storage.ProvisioningType provisioningType);
public KVMPhysicalDisk createDiskFromSnapshot(KVMPhysicalDisk snapshot, String snapshotName, String name, KVMStoragePool destPool, int timeout);

View File

@ -22,6 +22,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang.StringUtils;
@ -32,6 +33,8 @@ import com.cloud.storage.Storage;
import com.cloud.utils.script.OutputInterpreter;
import com.cloud.utils.script.Script;
import static java.util.regex.Pattern.CASE_INSENSITIVE;
public class QemuImg {
public final static String BACKING_FILE = "backing_file";
public final static String BACKING_FILE_FORMAT = "backing_file_format";
@ -765,4 +768,29 @@ public class QemuImg {
public boolean supportsSkipZeros() {
return this.skipZero;
}
public void setSkipZero(boolean skipZero) {
this.skipZero = skipZero;
}
public boolean supportsImageFormat(QemuImg.PhysicalDiskFormat format) {
final Script s = new Script(_qemuImgPath, timeout);
s.add("--help");
final OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
String result = s.execute(parser);
String output = parser.getLines();
// Older Qemu returns output in result due to --help reporting error status
if (result != null) {
output = result;
}
return helpSupportsImageFormat(output, format);
}
protected static boolean helpSupportsImageFormat(String text, QemuImg.PhysicalDiskFormat format) {
Pattern pattern = Pattern.compile("Supported\\sformats:[a-zA-Z0-9-_\\s]*?\\b" + format + "\\b", CASE_INSENSITIVE);
return pattern.matcher(text).find();
}
}

View File

@ -4850,6 +4850,7 @@ public class LibvirtComputingResourceTest {
final DomainState state = DomainInfo.DomainState.VIR_DOMAIN_RUNNING;
info.state = state;
when(pool.getType()).thenReturn(StoragePoolType.RBD);
when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolMgr);
when(storagePoolMgr.getStoragePool(pool.getType(), pool.getUuid())).thenReturn(storagePool);
when(storagePool.getPhysicalDisk(path)).thenReturn(vol);
@ -4909,6 +4910,7 @@ public class LibvirtComputingResourceTest {
final DomainState state = DomainInfo.DomainState.VIR_DOMAIN_RUNNING;
info.state = state;
when(pool.getType()).thenReturn(StoragePoolType.Linstor);
when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolMgr);
when(storagePoolMgr.getStoragePool(pool.getType(), pool.getUuid())).thenReturn(storagePool);
when(storagePool.getPhysicalDisk(path)).thenReturn(vol);
@ -4976,6 +4978,7 @@ public class LibvirtComputingResourceTest {
final KVMStoragePool storagePool = Mockito.mock(KVMStoragePool.class);
final KVMPhysicalDisk vol = Mockito.mock(KVMPhysicalDisk.class);
when(pool.getType()).thenReturn(StoragePoolType.Filesystem);
when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolMgr);
when(storagePoolMgr.getStoragePool(pool.getType(), pool.getUuid())).thenReturn(storagePool);
when(storagePool.getPhysicalDisk(path)).thenReturn(vol);
@ -5006,6 +5009,7 @@ public class LibvirtComputingResourceTest {
final KVMPhysicalDisk vol = Mockito.mock(KVMPhysicalDisk.class);
final LibvirtUtilitiesHelper libvirtUtilitiesHelper = Mockito.mock(LibvirtUtilitiesHelper.class);
when(pool.getType()).thenReturn(StoragePoolType.RBD);
when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolMgr);
when(storagePoolMgr.getStoragePool(pool.getType(), pool.getUuid())).thenReturn(storagePool);
when(storagePool.getPhysicalDisk(path)).thenReturn(vol);
@ -5052,6 +5056,7 @@ public class LibvirtComputingResourceTest {
final KVMStoragePoolManager storagePoolMgr = Mockito.mock(KVMStoragePoolManager.class);
final KVMStoragePool storagePool = Mockito.mock(KVMStoragePool.class);
when(pool.getType()).thenReturn(StoragePoolType.RBD);
when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolMgr);
when(storagePoolMgr.getStoragePool(pool.getType(), pool.getUuid())).thenReturn(storagePool);
when(storagePool.getPhysicalDisk(path)).thenThrow(CloudRuntimeException.class);

View File

@ -0,0 +1,31 @@
// 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 com.cloud.hypervisor.kvm.storage;
import org.junit.Assert;
import org.junit.Test;
public class ScaleIOStorageAdaptorTest {
@Test
public void getUsableBytesFromRawBytesTest() {
Assert.assertEquals("Overhead calculated for 8Gi size", 8554774528L, ScaleIOStorageAdaptor.getUsableBytesFromRawBytes(8L << 30));
Assert.assertEquals("Overhead calculated for 4Ti size", 4294130925568L, ScaleIOStorageAdaptor.getUsableBytesFromRawBytes(4000L << 30));
Assert.assertEquals("Overhead calculated for 500Gi size", 536737005568L, ScaleIOStorageAdaptor.getUsableBytesFromRawBytes(500L << 30));
Assert.assertEquals("Unsupported small size", 0, ScaleIOStorageAdaptor.getUsableBytesFromRawBytes(1L));
}
}

View File

@ -32,6 +32,7 @@ import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
@ -349,4 +350,22 @@ public class QemuImgTest {
df.delete();
}
@Test
public void testHelpSupportsImageFormat() throws QemuImgException, LibvirtException {
String partialHelp = "Parameters to dd subcommand:\n" +
" 'bs=BYTES' read and write up to BYTES bytes at a time (default: 512)\n" +
" 'count=N' copy only N input blocks\n" +
" 'if=FILE' read from FILE\n" +
" 'of=FILE' write to FILE\n" +
" 'skip=N' skip N bs-sized blocks at the start of input\n" +
"\n" +
"Supported formats: cloop copy-on-read file ftp ftps host_cdrom host_device https iser luks nbd nvme parallels qcow qcow2 qed quorum raw rbd ssh throttle vdi vhdx vmdk vpc vvfat\n" +
"\n" +
"See <https://qemu.org/contribute/report-a-bug> for how to report bugs.\n" +
"More information on the QEMU project at <https://qemu.org>.";
Assert.assertTrue("should support luks", QemuImg.helpSupportsImageFormat(partialHelp, PhysicalDiskFormat.LUKS));
Assert.assertTrue("should support qcow2", QemuImg.helpSupportsImageFormat(partialHelp, PhysicalDiskFormat.QCOW2));
Assert.assertFalse("should not support http", QemuImg.helpSupportsImageFormat(partialHelp, PhysicalDiskFormat.SHEEPDOG));
}
}

View File

@ -22,6 +22,12 @@ import java.util.Map;
import javax.inject.Inject;
import com.cloud.agent.api.storage.ResizeVolumeCommand;
import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.host.HostVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.dao.VMInstanceDao;
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
@ -119,6 +125,8 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
private ConfigurationDao configDao;
@Inject
private HostDao hostDao;
@Inject
private VMInstanceDao vmInstanceDao;
public ScaleIOPrimaryDataStoreDriver() {
@ -475,9 +483,9 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
CreateObjectAnswer answer = new CreateObjectAnswer(createdObject.getTO());
// if volume needs to be set up with encryption, do it now.
if (anyVolumeRequiresEncryption(volumeInfo)) {
LOGGER.debug(String.format("Setting up encryption for volume %s", volumeInfo));
// if volume needs to be set up with encryption, do it now if it's not a root disk (which gets done during template copy)
if (anyVolumeRequiresEncryption(volumeInfo) && !volumeInfo.getVolumeType().equals(Volume.Type.ROOT)) {
LOGGER.debug(String.format("Setting up encryption for volume %s", volumeInfo.getId()));
VolumeObjectTO prepVolume = (VolumeObjectTO) createdObject.getTO();
prepVolume.setPath(volumePath);
prepVolume.setUuid(volumePath);
@ -899,6 +907,7 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
try {
String scaleIOVolumeId = ScaleIOUtil.getVolumePath(volumeInfo.getPath());
Long storagePoolId = volumeInfo.getPoolId();
final ScaleIOGatewayClient client = getScaleIOClient(storagePoolId);
ResizeVolumePayload payload = (ResizeVolumePayload)volumeInfo.getpayload();
long newSizeInBytes = payload.newSize != null ? payload.newSize : volumeInfo.getSize();
@ -907,13 +916,69 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
throw new CloudRuntimeException("Only increase size is allowed for volume: " + volumeInfo.getName());
}
org.apache.cloudstack.storage.datastore.api.Volume scaleIOVolume = null;
org.apache.cloudstack.storage.datastore.api.Volume scaleIOVolume = client.getVolume(scaleIOVolumeId);
long newSizeInGB = newSizeInBytes / (1024 * 1024 * 1024);
long newSizeIn8gbBoundary = (long) (Math.ceil(newSizeInGB / 8.0) * 8.0);
final ScaleIOGatewayClient client = getScaleIOClient(storagePoolId);
scaleIOVolume = client.resizeVolume(scaleIOVolumeId, (int) newSizeIn8gbBoundary);
if (scaleIOVolume == null) {
throw new CloudRuntimeException("Failed to resize volume: " + volumeInfo.getName());
if (scaleIOVolume.getSizeInKb() == newSizeIn8gbBoundary << 20) {
LOGGER.debug("No resize necessary at API");
} else {
scaleIOVolume = client.resizeVolume(scaleIOVolumeId, (int) newSizeIn8gbBoundary);
if (scaleIOVolume == null) {
throw new CloudRuntimeException("Failed to resize volume: " + volumeInfo.getName());
}
}
StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
boolean attachedRunning = false;
long hostId = 0;
if (payload.instanceName != null) {
VMInstanceVO instance = vmInstanceDao.findVMByInstanceName(payload.instanceName);
if (instance.getState().equals(VirtualMachine.State.Running)) {
hostId = instance.getHostId();
attachedRunning = true;
}
}
if (volumeInfo.getFormat().equals(Storage.ImageFormat.QCOW2) || attachedRunning) {
LOGGER.debug("Volume needs to be resized at the hypervisor host");
if (hostId == 0) {
hostId = selector.select(volumeInfo, true).getId();
}
HostVO host = hostDao.findById(hostId);
if (host == null) {
throw new CloudRuntimeException("Found no hosts to run resize command on");
}
EndPoint ep = RemoteHostEndPoint.getHypervisorHostEndPoint(host);
ResizeVolumeCommand resizeVolumeCommand = new ResizeVolumeCommand(
volumeInfo.getPath(), new StorageFilerTO(storagePool), volumeInfo.getSize(), newSizeInBytes,
payload.shrinkOk, payload.instanceName, volumeInfo.getChainInfo(),
volumeInfo.getPassphrase(), volumeInfo.getEncryptFormat());
try {
if (!attachedRunning) {
grantAccess(volumeInfo, ep, volumeInfo.getDataStore());
}
Answer answer = ep.sendMessage(resizeVolumeCommand);
if (!answer.getResult() && volumeInfo.getFormat().equals(Storage.ImageFormat.QCOW2)) {
throw new CloudRuntimeException("Failed to resize at host: " + answer.getDetails());
} else if (!answer.getResult()) {
// for non-qcow2, notifying the running VM is going to be best-effort since we can't roll back
// or avoid VM seeing a successful change at the PowerFlex volume after e.g. reboot
LOGGER.warn("Resized raw volume, but failed to notify. VM will see change on reboot. Error:" + answer.getDetails());
} else {
LOGGER.debug("Resized volume at host: " + answer.getDetails());
}
} finally {
if (!attachedRunning) {
revokeAccess(volumeInfo, ep, volumeInfo.getDataStore());
}
}
}
VolumeVO volume = volumeDao.findById(volumeInfo.getId());
@ -921,7 +986,6 @@ public class ScaleIOPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
volume.setSize(scaleIOVolume.getSizeInKb() * 1024);
volumeDao.update(volume.getId(), volume);
StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId);
long capacityBytes = storagePool.getCapacityBytes();
long usedBytes = storagePool.getUsedBytes();

View File

@ -1142,7 +1142,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
if (storagePoolId != null) {
StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId);
if (storagePoolVO.isManaged()) {
if (storagePoolVO.isManaged() && !storagePoolVO.getPoolType().equals(Storage.StoragePoolType.PowerFlex)) {
Long instanceId = volume.getInstanceId();
if (instanceId != null) {