mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 01:32:18 +02:00
Validate qcow2 file during import operation (#11264)
This commit is contained in:
parent
264e404108
commit
75a2b3cc54
@ -17,22 +17,33 @@
|
||||
|
||||
package com.cloud.agent.api;
|
||||
|
||||
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class CheckVolumeAnswer extends Answer {
|
||||
|
||||
private long size;
|
||||
private Map<VolumeOnStorageTO.Detail, String> volumeDetails;
|
||||
|
||||
CheckVolumeAnswer() {
|
||||
}
|
||||
|
||||
public CheckVolumeAnswer(CheckVolumeCommand cmd, String details, long size) {
|
||||
super(cmd, true, details);
|
||||
public CheckVolumeAnswer(CheckVolumeCommand cmd, final boolean success, String details, long size,
|
||||
Map<VolumeOnStorageTO.Detail, String> volumeDetails) {
|
||||
super(cmd, success, details);
|
||||
this.size = size;
|
||||
this.volumeDetails = volumeDetails;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Map<VolumeOnStorageTO.Detail, String> getVolumeDetails() {
|
||||
return volumeDetails;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return "CheckVolumeAnswer [size=" + size + "]";
|
||||
}
|
||||
|
||||
@ -17,21 +17,28 @@
|
||||
|
||||
package com.cloud.agent.api;
|
||||
|
||||
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class CopyRemoteVolumeAnswer extends Answer {
|
||||
|
||||
private String remoteIp;
|
||||
private String filename;
|
||||
|
||||
private long size;
|
||||
private Map<VolumeOnStorageTO.Detail, String> volumeDetails;
|
||||
|
||||
CopyRemoteVolumeAnswer() {
|
||||
}
|
||||
|
||||
public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, String details, String filename, long size) {
|
||||
super(cmd, true, details);
|
||||
public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, final boolean success, String details, String filename, long size,
|
||||
Map<VolumeOnStorageTO.Detail, String> volumeDetails) {
|
||||
super(cmd, success, details);
|
||||
this.remoteIp = cmd.getRemoteIp();
|
||||
this.filename = filename;
|
||||
this.size = size;
|
||||
this.volumeDetails = volumeDetails;
|
||||
}
|
||||
|
||||
public String getRemoteIp() {
|
||||
@ -54,6 +61,10 @@ public class CopyRemoteVolumeAnswer extends Answer {
|
||||
return size;
|
||||
}
|
||||
|
||||
public Map<VolumeOnStorageTO.Detail, String> getVolumeDetails() {
|
||||
return volumeDetails;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return "CopyRemoteVolumeAnswer [remoteIp=" + remoteIp + "]";
|
||||
}
|
||||
|
||||
@ -31,18 +31,25 @@ import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImg;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgException;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgFile;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ResourceWrapper(handles = CheckVolumeCommand.class)
|
||||
public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<CheckVolumeCommand, Answer, LibvirtComputingResource> {
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(LibvirtCheckVolumeCommandWrapper.class);
|
||||
private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem);
|
||||
|
||||
@Override
|
||||
public Answer execute(final CheckVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
|
||||
@ -53,34 +60,76 @@ public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<Check
|
||||
KVMStoragePool pool = poolMgr.getStoragePool(storageFilerTO.getType(), storageFilerTO.getUuid());
|
||||
|
||||
try {
|
||||
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
|
||||
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
|
||||
if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) {
|
||||
final KVMPhysicalDisk vol = pool.getPhysicalDisk(srcFile);
|
||||
final String path = vol.getPath();
|
||||
long size = getVirtualSizeFromFile(path);
|
||||
return new CheckVolumeAnswer(command, "", size);
|
||||
try {
|
||||
KVMPhysicalDisk.checkQcow2File(path);
|
||||
} catch (final CloudRuntimeException e) {
|
||||
return new CheckVolumeAnswer(command, false, "", 0, getVolumeDetails(pool, vol));
|
||||
}
|
||||
|
||||
long size = KVMPhysicalDisk.getVirtualSizeFromFile(path);
|
||||
return new CheckVolumeAnswer(command, true, "", size, getVolumeDetails(pool, vol));
|
||||
} else {
|
||||
return new Answer(command, false, "Unsupported Storage Pool");
|
||||
}
|
||||
|
||||
} catch (final Exception e) {
|
||||
s_logger.error("Error while locating disk: "+ e.getMessage());
|
||||
s_logger.error("Error while checking the disk: " + e.getMessage());
|
||||
return new Answer(command, false, result);
|
||||
}
|
||||
}
|
||||
|
||||
private long getVirtualSizeFromFile(String path) {
|
||||
private Map<VolumeOnStorageTO.Detail, String> getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) {
|
||||
Map<String, String> info = getDiskFileInfo(pool, disk, true);
|
||||
if (MapUtils.isEmpty(info)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<VolumeOnStorageTO.Detail, String> volumeDetails = new HashMap<>();
|
||||
|
||||
String backingFilePath = info.get(QemuImg.BACKING_FILE);
|
||||
if (StringUtils.isNotBlank(backingFilePath)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
|
||||
}
|
||||
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(backingFileFormat)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
|
||||
}
|
||||
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
|
||||
if (StringUtils.isNotBlank(clusterSize)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
|
||||
}
|
||||
String fileFormat = info.get(QemuImg.FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(fileFormat)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
|
||||
}
|
||||
String encrypted = info.get(QemuImg.ENCRYPTED);
|
||||
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
|
||||
}
|
||||
Boolean isLocked = isDiskFileLocked(pool, disk);
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
|
||||
|
||||
return volumeDetails;
|
||||
}
|
||||
|
||||
private Map<String, String> getDiskFileInfo(KVMStoragePool pool, KVMPhysicalDisk disk, boolean secure) {
|
||||
if (!STORAGE_POOL_TYPES_SUPPORTED.contains(pool.getType())) {
|
||||
return new HashMap<>(); // unknown
|
||||
}
|
||||
try {
|
||||
QemuImg qemu = new QemuImg(0);
|
||||
QemuImgFile qemuFile = new QemuImgFile(path);
|
||||
Map<String, String> info = qemu.info(qemuFile);
|
||||
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
|
||||
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
|
||||
} else {
|
||||
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
|
||||
}
|
||||
QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
|
||||
return qemu.info(qemuFile, secure);
|
||||
} catch (QemuImgException | LibvirtException ex) {
|
||||
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
|
||||
logger.error("Failed to get info of disk file: " + ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) {
|
||||
Map<String, String> info = getDiskFileInfo(pool, disk, false);
|
||||
return info == null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,18 +31,25 @@ import com.cloud.resource.CommandWrapper;
|
||||
import com.cloud.resource.ResourceWrapper;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImg;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgException;
|
||||
import org.apache.cloudstack.utils.qemu.QemuImgFile;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ResourceWrapper(handles = CopyRemoteVolumeCommand.class)
|
||||
public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<CopyRemoteVolumeCommand, Answer, LibvirtComputingResource> {
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(LibvirtCopyRemoteVolumeCommandWrapper.class);
|
||||
private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem);
|
||||
|
||||
@Override
|
||||
public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
|
||||
@ -58,14 +65,19 @@ public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<
|
||||
int timeoutInSecs = command.getWait();
|
||||
|
||||
try {
|
||||
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
|
||||
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
|
||||
if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) {
|
||||
String filename = libvirtComputingResource.copyVolume(srcIp, username, password, dstPath, srcFile, tmpPath, timeoutInSecs);
|
||||
s_logger.debug("Volume " + srcFile + " copy successful, copied to file: " + filename);
|
||||
final KVMPhysicalDisk vol = pool.getPhysicalDisk(filename);
|
||||
final String path = vol.getPath();
|
||||
long size = getVirtualSizeFromFile(path);
|
||||
return new CopyRemoteVolumeAnswer(command, "", filename, size);
|
||||
try {
|
||||
KVMPhysicalDisk.checkQcow2File(path);
|
||||
} catch (final CloudRuntimeException e) {
|
||||
return new CopyRemoteVolumeAnswer(command, false, "", filename, 0, getVolumeDetails(pool, vol));
|
||||
}
|
||||
|
||||
long size = KVMPhysicalDisk.getVirtualSizeFromFile(path);
|
||||
return new CopyRemoteVolumeAnswer(command, true, "", filename, size, getVolumeDetails(pool, vol));
|
||||
} else {
|
||||
String msg = "Unsupported storage pool type: " + storageFilerTO.getType().toString() + ", only local and NFS pools are supported";
|
||||
return new Answer(command, false, msg);
|
||||
@ -77,18 +89,56 @@ public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<
|
||||
}
|
||||
}
|
||||
|
||||
private long getVirtualSizeFromFile(String path) {
|
||||
private Map<VolumeOnStorageTO.Detail, String> getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) {
|
||||
Map<String, String> info = getDiskFileInfo(pool, disk, true);
|
||||
if (MapUtils.isEmpty(info)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<VolumeOnStorageTO.Detail, String> volumeDetails = new HashMap<>();
|
||||
|
||||
String backingFilePath = info.get(QemuImg.BACKING_FILE);
|
||||
if (StringUtils.isNotBlank(backingFilePath)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
|
||||
}
|
||||
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(backingFileFormat)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
|
||||
}
|
||||
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
|
||||
if (StringUtils.isNotBlank(clusterSize)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
|
||||
}
|
||||
String fileFormat = info.get(QemuImg.FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(fileFormat)) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
|
||||
}
|
||||
String encrypted = info.get(QemuImg.ENCRYPTED);
|
||||
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
|
||||
}
|
||||
Boolean isLocked = isDiskFileLocked(pool, disk);
|
||||
volumeDetails.put(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
|
||||
|
||||
return volumeDetails;
|
||||
}
|
||||
|
||||
private Map<String, String> getDiskFileInfo(KVMStoragePool pool, KVMPhysicalDisk disk, boolean secure) {
|
||||
if (!STORAGE_POOL_TYPES_SUPPORTED.contains(pool.getType())) {
|
||||
return new HashMap<>(); // unknown
|
||||
}
|
||||
try {
|
||||
QemuImg qemu = new QemuImg(0);
|
||||
QemuImgFile qemuFile = new QemuImgFile(path);
|
||||
Map<String, String> info = qemu.info(qemuFile);
|
||||
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
|
||||
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
|
||||
} else {
|
||||
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
|
||||
}
|
||||
QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
|
||||
return qemu.info(qemuFile, secure);
|
||||
} catch (QemuImgException | LibvirtException ex) {
|
||||
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
|
||||
logger.error("Failed to get info of disk file: " + ex.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) {
|
||||
Map<String, String> info = getDiskFileInfo(pool, disk, false);
|
||||
return info == null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ 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.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
@ -91,37 +92,46 @@ public final class LibvirtGetVolumesOnStorageCommandWrapper extends CommandWrapp
|
||||
if (disk.getQemuEncryptFormat() != null) {
|
||||
volumeOnStorageTO.setQemuEncryptFormat(disk.getQemuEncryptFormat().toString());
|
||||
}
|
||||
String backingFilePath = info.get(QemuImg.BACKING_FILE);
|
||||
if (StringUtils.isNotBlank(backingFilePath)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
|
||||
}
|
||||
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(backingFileFormat)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
|
||||
}
|
||||
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
|
||||
if (StringUtils.isNotBlank(clusterSize)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
|
||||
}
|
||||
String fileFormat = info.get(QemuImg.FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(fileFormat)) {
|
||||
if (!fileFormat.equalsIgnoreCase(disk.getFormat().toString())) {
|
||||
return new GetVolumesOnStorageAnswer(command, false, String.format("The file format is %s, but expected to be %s", fileFormat, disk.getFormat()));
|
||||
}
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
|
||||
if (StringUtils.isNotBlank(fileFormat) && !fileFormat.equalsIgnoreCase(disk.getFormat().toString())) {
|
||||
return new GetVolumesOnStorageAnswer(command, false, String.format("The file format is %s, but expected to be %s", fileFormat, disk.getFormat()));
|
||||
}
|
||||
String encrypted = info.get(QemuImg.ENCRYPTED);
|
||||
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
|
||||
}
|
||||
Boolean isLocked = isDiskFileLocked(storagePool, disk);
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
|
||||
addDetailsToVolumeOnStorageTO(volumeOnStorageTO, info, storagePool, disk);
|
||||
|
||||
volumes.add(volumeOnStorageTO);
|
||||
}
|
||||
return new GetVolumesOnStorageAnswer(command, volumes);
|
||||
}
|
||||
|
||||
private void addDetailsToVolumeOnStorageTO(VolumeOnStorageTO volumeOnStorageTO, final Map<String, String> info, final KVMStoragePool storagePool, final KVMPhysicalDisk disk) {
|
||||
if (MapUtils.isEmpty(info)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String backingFilePath = info.get(QemuImg.BACKING_FILE);
|
||||
if (StringUtils.isNotBlank(backingFilePath)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
|
||||
}
|
||||
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(backingFileFormat)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
|
||||
}
|
||||
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
|
||||
if (StringUtils.isNotBlank(clusterSize)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
|
||||
}
|
||||
String fileFormat = info.get(QemuImg.FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(fileFormat)) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
|
||||
}
|
||||
String encrypted = info.get(QemuImg.ENCRYPTED);
|
||||
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
|
||||
}
|
||||
Boolean isLocked = isDiskFileLocked(storagePool, disk);
|
||||
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
|
||||
}
|
||||
|
||||
private GetVolumesOnStorageAnswer addAllVolumes(final GetVolumesOnStorageCommand command, final KVMStoragePool storagePool, String keyword) {
|
||||
List<VolumeOnStorageTO> volumes = new ArrayList<>();
|
||||
|
||||
@ -134,11 +144,21 @@ public final class LibvirtGetVolumesOnStorageCommandWrapper extends CommandWrapp
|
||||
if (!isDiskFormatSupported(disk)) {
|
||||
continue;
|
||||
}
|
||||
Map<String, String> info = getDiskFileInfo(storagePool, disk, true);
|
||||
if (info == null) {
|
||||
continue;
|
||||
}
|
||||
VolumeOnStorageTO volumeOnStorageTO = new VolumeOnStorageTO(Hypervisor.HypervisorType.KVM, disk.getName(), disk.getName(), disk.getPath(),
|
||||
disk.getFormat().toString(), disk.getSize(), disk.getVirtualSize());
|
||||
if (disk.getQemuEncryptFormat() != null) {
|
||||
volumeOnStorageTO.setQemuEncryptFormat(disk.getQemuEncryptFormat().toString());
|
||||
}
|
||||
String fileFormat = info.get(QemuImg.FILE_FORMAT);
|
||||
if (StringUtils.isNotBlank(fileFormat) && !fileFormat.equalsIgnoreCase(disk.getFormat().toString())) {
|
||||
continue;
|
||||
}
|
||||
addDetailsToVolumeOnStorageTO(volumeOnStorageTO, info, storagePool, disk);
|
||||
|
||||
volumes.add(volumeOnStorageTO);
|
||||
}
|
||||
return new GetVolumesOnStorageAnswer(command, volumes);
|
||||
|
||||
@ -25,7 +25,6 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.hypervisor.kvm.storage.ScaleIOStorageAdaptor;
|
||||
import org.apache.cloudstack.utils.cryptsetup.KeyFile;
|
||||
@ -33,7 +32,6 @@ 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;
|
||||
@ -102,7 +100,7 @@ public final class LibvirtResizeVolumeCommandWrapper extends CommandWrapper<Resi
|
||||
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)
|
||||
newSize = getVirtualSizeFromFile(path);
|
||||
newSize = KVMPhysicalDisk.getVirtualSizeFromFile(path);
|
||||
}
|
||||
|
||||
if (pool.getType() != StoragePoolType.RBD && pool.getType() != StoragePoolType.Linstor && pool.getType() != StoragePoolType.PowerFlex) {
|
||||
@ -216,21 +214,6 @@ public final class LibvirtResizeVolumeCommandWrapper extends CommandWrapper<Resi
|
||||
}
|
||||
}
|
||||
|
||||
private long getVirtualSizeFromFile(String path) {
|
||||
try {
|
||||
QemuImg qemu = new QemuImg(0);
|
||||
QemuImgFile qemuFile = new QemuImgFile(path);
|
||||
Map<String, String> info = qemu.info(qemuFile);
|
||||
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
|
||||
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
|
||||
} else {
|
||||
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
|
||||
}
|
||||
} catch (QemuImgException | LibvirtException ex) {
|
||||
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Answer handleMultipathSCSIResize(ResizeVolumeCommand command, KVMStoragePool pool) {
|
||||
((MultipathSCSIPool)pool).resize(command.getPath(), command.getInstanceName(), command.getNewSize());
|
||||
return new ResizeVolumeAnswer(command, true, "");
|
||||
|
||||
@ -16,13 +16,21 @@
|
||||
// under the License.
|
||||
package com.cloud.hypervisor.kvm.storage;
|
||||
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.cloudstack.storage.formatinspector.Qcow2Inspector;
|
||||
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
|
||||
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.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class KVMPhysicalDisk {
|
||||
private String path;
|
||||
@ -71,6 +79,31 @@ public class KVMPhysicalDisk {
|
||||
return hostIp;
|
||||
}
|
||||
|
||||
public static long getVirtualSizeFromFile(String path) {
|
||||
try {
|
||||
QemuImg qemu = new QemuImg(0);
|
||||
QemuImgFile qemuFile = new QemuImgFile(path);
|
||||
Map<String, String> info = qemu.info(qemuFile);
|
||||
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
|
||||
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
|
||||
} else {
|
||||
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
|
||||
}
|
||||
} catch (QemuImgException | LibvirtException ex) {
|
||||
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkQcow2File(String path) {
|
||||
if (ImageStoreUtil.isCorrectExtension(path, "qcow2")) {
|
||||
try {
|
||||
Qcow2Inspector.validateQcow2File(path);
|
||||
} catch (RuntimeException e) {
|
||||
throw new CloudRuntimeException("The volume file at path " + path + " is not a valid QCOW2. Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PhysicalDiskFormat format;
|
||||
private long size;
|
||||
private long virtualSize;
|
||||
|
||||
@ -170,6 +170,7 @@ import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
|
||||
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
|
||||
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
@ -812,7 +813,8 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
throw new CloudRuntimeException("Error while copying volume of remote instance: " + answer.getDetails());
|
||||
}
|
||||
CopyRemoteVolumeAnswer copyRemoteVolumeAnswer = (CopyRemoteVolumeAnswer) answer;
|
||||
if(!copyRemoteVolumeAnswer.getResult()) {
|
||||
checkVolume(copyRemoteVolumeAnswer.getVolumeDetails());
|
||||
if (!copyRemoteVolumeAnswer.getResult()) {
|
||||
throw new CloudRuntimeException("Unable to copy volume of remote instance");
|
||||
}
|
||||
diskProfile.setSize(copyRemoteVolumeAnswer.getSize());
|
||||
@ -2653,7 +2655,13 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
throw new CloudRuntimeException("Disk not found or is invalid");
|
||||
}
|
||||
CheckVolumeAnswer checkVolumeAnswer = (CheckVolumeAnswer) answer;
|
||||
if(!checkVolumeAnswer.getResult()) {
|
||||
try {
|
||||
checkVolume(checkVolumeAnswer.getVolumeDetails());
|
||||
} catch (CloudRuntimeException e) {
|
||||
cleanupFailedImportVM(userVm);
|
||||
throw e;
|
||||
}
|
||||
if (!checkVolumeAnswer.getResult()) {
|
||||
cleanupFailedImportVM(userVm);
|
||||
throw new CloudRuntimeException("Disk not found or is invalid");
|
||||
}
|
||||
@ -2679,6 +2687,31 @@ public class UnmanagedVMsManagerImpl implements UnmanagedVMsManager {
|
||||
return userVm;
|
||||
}
|
||||
|
||||
private void checkVolume(Map<VolumeOnStorageTO.Detail, String> volumeDetails) {
|
||||
if (MapUtils.isEmpty(volumeDetails)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (volumeDetails.containsKey(VolumeOnStorageTO.Detail.IS_LOCKED)) {
|
||||
String isLocked = volumeDetails.get(VolumeOnStorageTO.Detail.IS_LOCKED);
|
||||
if (Boolean.parseBoolean(isLocked)) {
|
||||
logFailureAndThrowException("Locked volume cannot be imported or unmanaged.");
|
||||
}
|
||||
}
|
||||
if (volumeDetails.containsKey(VolumeOnStorageTO.Detail.IS_ENCRYPTED)) {
|
||||
String isEncrypted = volumeDetails.get(VolumeOnStorageTO.Detail.IS_ENCRYPTED);
|
||||
if (Boolean.parseBoolean(isEncrypted)) {
|
||||
logFailureAndThrowException("Encrypted volume cannot be imported or unmanaged.");
|
||||
}
|
||||
}
|
||||
if (volumeDetails.containsKey(VolumeOnStorageTO.Detail.BACKING_FILE)) {
|
||||
String backingFile = volumeDetails.get(VolumeOnStorageTO.Detail.BACKING_FILE);
|
||||
if (StringUtils.isNotBlank(backingFile)) {
|
||||
logFailureAndThrowException("Volume with backing file cannot be imported or unmanaged.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private NetworkVO getDefaultNetwork(DataCenter zone, Account owner, boolean selectAny) throws InsufficientCapacityException, ResourceAllocationException {
|
||||
NetworkVO defaultNetwork = null;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user