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 <sureshkumar.anapa...@gmail.com>
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;
 

Reply via email to