This is an automated email from the ASF dual-hosted git repository.
dahn pushed a commit to branch 4.19
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.19 by this push:
new 75a2b3cc54c Validate qcow2 file during import operation (#11264)
75a2b3cc54c is described below
commit 75a2b3cc54cf6fadda3e137c2cd7f5b8a502803e
Author: Suresh Kumar Anaparti <[email protected]>
AuthorDate: Fri Jul 25 14:47:14 2025 +0530
Validate qcow2 file during import operation (#11264)
---
.../com/cloud/agent/api/CheckVolumeAnswer.java | 15 +++-
.../cloud/agent/api/CopyRemoteVolumeAnswer.java | 15 +++-
.../wrapper/LibvirtCheckVolumeCommandWrapper.java | 79 ++++++++++++++++++----
.../LibvirtCopyRemoteVolumeCommandWrapper.java | 76 +++++++++++++++++----
.../LibvirtGetVolumesOnStorageCommandWrapper.java | 66 +++++++++++-------
.../wrapper/LibvirtResizeVolumeCommandWrapper.java | 19 +-----
.../hypervisor/kvm/storage/KVMPhysicalDisk.java | 33 +++++++++
.../cloudstack/vm/UnmanagedVMsManagerImpl.java | 37 +++++++++-
8 files changed, 265 insertions(+), 75 deletions(-)
diff --git a/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java
b/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java
index 5a32ab59a7a..07b7e102df9 100644
--- a/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java
+++ b/core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java
@@ -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 + "]";
}
diff --git a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java
b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java
index e79005be71b..4aec0b26581 100644
--- a/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java
+++ b/core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java
@@ -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 + "]";
}
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVolumeCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVolumeCommandWrapper.java
index 8b0a5aab461..2caf8da2914 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVolumeCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVolumeCommandWrapper.java
@@ -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;
+ }
}
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCopyRemoteVolumeCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCopyRemoteVolumeCommandWrapper.java
index a5e1716da2e..6edf5cbd906 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCopyRemoteVolumeCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCopyRemoteVolumeCommandWrapper.java
@@ -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;
+ }
}
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapper.java
index 821a80f5cca..6facf169602 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapper.java
@@ -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);
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java
index 4f1ad728b5d..aa1a0f41f1b 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java
@@ -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, "");
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java
index 9d9a6415e27..c43f5101fbe 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java
@@ -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;
diff --git
a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
index abb0e6b63c5..df87aff276d 100644
--- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
@@ -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;