From bebacdceed734eda2cdbf2f0bff699fb96a008ca Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Wed, 23 Jul 2025 09:33:28 +0530 Subject: [PATCH 1/4] Validate qcow2 file during import operation --- .../LibvirtCheckVolumeCommandWrapper.java | 25 ++------------ ...LibvirtCopyRemoteVolumeCommandWrapper.java | 25 ++------------ ...virtGetVolumesOnStorageCommandWrapper.java | 16 +++++++++ .../LibvirtResizeVolumeCommandWrapper.java | 19 +---------- .../kvm/storage/KVMPhysicalDisk.java | 33 +++++++++++++++++++ ...GetVolumesOnStorageCommandWrapperTest.java | 18 ++++++---- 6 files changed, 66 insertions(+), 70 deletions(-) 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 8b0a5aab4619..4329e0a01035 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 @@ -30,14 +30,7 @@ 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.utils.qemu.QemuImg; -import org.apache.cloudstack.utils.qemu.QemuImgException; -import org.apache.cloudstack.utils.qemu.QemuImgFile; import org.apache.log4j.Logger; -import org.libvirt.LibvirtException; - -import java.util.Map; @ResourceWrapper(handles = CheckVolumeCommand.class) public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper { @@ -57,7 +50,8 @@ public Answer execute(final CheckVolumeCommand command, final LibvirtComputingRe storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) { final KVMPhysicalDisk vol = pool.getPhysicalDisk(srcFile); final String path = vol.getPath(); - long size = getVirtualSizeFromFile(path); + KVMPhysicalDisk.checkQcow2File(path); + long size = KVMPhysicalDisk.getVirtualSizeFromFile(path); return new CheckVolumeAnswer(command, "", size); } else { return new Answer(command, false, "Unsupported Storage Pool"); @@ -68,19 +62,4 @@ public Answer execute(final CheckVolumeCommand command, final LibvirtComputingRe return new Answer(command, false, result); } } - - private long getVirtualSizeFromFile(String path) { - try { - QemuImg qemu = new QemuImg(0); - QemuImgFile qemuFile = new QemuImgFile(path); - Map 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); - } - } } 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 a5e1716da2eb..fcef2110ec02 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 @@ -30,14 +30,7 @@ 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.utils.qemu.QemuImg; -import org.apache.cloudstack.utils.qemu.QemuImgException; -import org.apache.cloudstack.utils.qemu.QemuImgFile; import org.apache.log4j.Logger; -import org.libvirt.LibvirtException; - -import java.util.Map; @ResourceWrapper(handles = CopyRemoteVolumeCommand.class) public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper { @@ -64,7 +57,8 @@ public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComput 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); + KVMPhysicalDisk.checkQcow2File(path); + long size = KVMPhysicalDisk.getVirtualSizeFromFile(path); return new CopyRemoteVolumeAnswer(command, "", filename, size); } else { String msg = "Unsupported storage pool type: " + storageFilerTO.getType().toString() + ", only local and NFS pools are supported"; @@ -76,19 +70,4 @@ public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComput return new Answer(command, false, msg); } } - - private long getVirtualSizeFromFile(String path) { - try { - QemuImg qemu = new QemuImg(0); - QemuImgFile qemuFile = new QemuImgFile(path); - Map 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); - } - } } 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 821a80f5ccac..2b2f5c1adaa4 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 @@ -31,6 +31,7 @@ import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.storage.Storage.StoragePoolType; +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.QemuImg.PhysicalDiskFormat; @@ -82,6 +83,13 @@ private GetVolumesOnStorageAnswer addVolumeByVolumePath(final GetVolumesOnStorag if (!isDiskFormatSupported(disk)) { return new GetVolumesOnStorageAnswer(command, false, String.format("disk format %s is unsupported", disk.getFormat())); } + if (PhysicalDiskFormat.QCOW2.equals(disk.getFormat())) { + try { + KVMPhysicalDisk.checkQcow2File(disk.getPath()); + } catch (CloudRuntimeException e) { + return new GetVolumesOnStorageAnswer(command, false, e.getMessage()); + } + } Map info = getDiskFileInfo(storagePool, disk, true); if (info == null) { return new GetVolumesOnStorageAnswer(command, false, "failed to get information of disk file. The disk might be locked or unsupported"); @@ -134,6 +142,14 @@ private GetVolumesOnStorageAnswer addAllVolumes(final GetVolumesOnStorageCommand if (!isDiskFormatSupported(disk)) { continue; } + if (PhysicalDiskFormat.QCOW2.equals(disk.getFormat())) { + try { + KVMPhysicalDisk.checkQcow2File(disk.getPath()); + } catch (CloudRuntimeException e) { + logger.error("Error while checking for qcow2 volume file: " + e.getMessage()); + 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) { 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 4f1ad728b5d9..aa1a0f41f1b2 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.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.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 Answer execute(final ResizeVolumeCommand command, final LibvirtComputingR 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 @@ private void resizeEncryptedQcowFile(final KVMPhysicalDisk vol, final QemuObject } } - private long getVirtualSizeFromFile(String path) { - try { - QemuImg qemu = new QemuImg(0); - QemuImgFile qemuFile = new QemuImgFile(path); - Map 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 9d9a6415e27f..1c6a954f9de3 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 @@ private static String replaceHostAddress(String hostIp) { return hostIp; } + public static long getVirtualSizeFromFile(String path) { + try { + QemuImg qemu = new QemuImg(0); + QemuImgFile qemuFile = new QemuImgFile(path); + Map 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"); + } + } + } + private PhysicalDiskFormat format; private long size; private long virtualSize; diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapperTest.java index 4e039f318928..f1c35d86698c 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapperTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapperTest.java @@ -38,6 +38,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.MockedConstruction; import org.mockito.Mockito; import org.mockito.Spy; @@ -116,12 +117,17 @@ public void testLibvirtGetVolumesOnStorageCommandWrapperForAllVolumes() { } Mockito.when(storagePool.listPhysicalDisks()).thenReturn(physicalDisks); - Answer answer = libvirtGetVolumesOnStorageCommandWrapper.execute(command, libvirtComputingResource); - Assert.assertTrue(answer instanceof GetVolumesOnStorageAnswer); - Assert.assertTrue(answer.getResult()); - List volumes = ((GetVolumesOnStorageAnswer) answer).getVolumes(); - Assert.assertEquals(numberDisks, volumes.size()); - volumeOnStorageTOMock.constructed().forEach(s -> Mockito.verify(s, times(1)).setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS.toString())); + try (MockedStatic mockedKVMPhysicalDisk = Mockito.mockStatic(KVMPhysicalDisk.class)) { + mockedKVMPhysicalDisk.when(() -> KVMPhysicalDisk.checkQcow2File(Mockito.anyString())) + .thenAnswer(invocation -> null); + + Answer answer = libvirtGetVolumesOnStorageCommandWrapper.execute(command, libvirtComputingResource); + Assert.assertTrue(answer instanceof GetVolumesOnStorageAnswer); + Assert.assertTrue(answer.getResult()); + List volumes = ((GetVolumesOnStorageAnswer) answer).getVolumes(); + Assert.assertEquals(numberDisks, volumes.size()); + volumeOnStorageTOMock.constructed().forEach(s -> Mockito.verify(s, times(1)).setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS.toString())); + } } @Test From a4c479b85a9ac0740caff4002fec9f1a5feed565 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Wed, 23 Jul 2025 09:47:27 +0530 Subject: [PATCH 2/4] Remove qcow2 check while listing volumes (using GetVolumesOnStorageCommand) --- ...bvirtGetVolumesOnStorageCommandWrapper.java | 16 ---------------- ...tGetVolumesOnStorageCommandWrapperTest.java | 18 ++++++------------ 2 files changed, 6 insertions(+), 28 deletions(-) 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 2b2f5c1adaa4..821a80f5ccac 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 @@ -31,7 +31,6 @@ import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.storage.Storage.StoragePoolType; -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.QemuImg.PhysicalDiskFormat; @@ -83,13 +82,6 @@ private GetVolumesOnStorageAnswer addVolumeByVolumePath(final GetVolumesOnStorag if (!isDiskFormatSupported(disk)) { return new GetVolumesOnStorageAnswer(command, false, String.format("disk format %s is unsupported", disk.getFormat())); } - if (PhysicalDiskFormat.QCOW2.equals(disk.getFormat())) { - try { - KVMPhysicalDisk.checkQcow2File(disk.getPath()); - } catch (CloudRuntimeException e) { - return new GetVolumesOnStorageAnswer(command, false, e.getMessage()); - } - } Map info = getDiskFileInfo(storagePool, disk, true); if (info == null) { return new GetVolumesOnStorageAnswer(command, false, "failed to get information of disk file. The disk might be locked or unsupported"); @@ -142,14 +134,6 @@ private GetVolumesOnStorageAnswer addAllVolumes(final GetVolumesOnStorageCommand if (!isDiskFormatSupported(disk)) { continue; } - if (PhysicalDiskFormat.QCOW2.equals(disk.getFormat())) { - try { - KVMPhysicalDisk.checkQcow2File(disk.getPath()); - } catch (CloudRuntimeException e) { - logger.error("Error while checking for qcow2 volume file: " + e.getMessage()); - 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) { diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapperTest.java index f1c35d86698c..4e039f318928 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapperTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapperTest.java @@ -38,7 +38,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockedStatic; import org.mockito.MockedConstruction; import org.mockito.Mockito; import org.mockito.Spy; @@ -117,17 +116,12 @@ public void testLibvirtGetVolumesOnStorageCommandWrapperForAllVolumes() { } Mockito.when(storagePool.listPhysicalDisks()).thenReturn(physicalDisks); - try (MockedStatic mockedKVMPhysicalDisk = Mockito.mockStatic(KVMPhysicalDisk.class)) { - mockedKVMPhysicalDisk.when(() -> KVMPhysicalDisk.checkQcow2File(Mockito.anyString())) - .thenAnswer(invocation -> null); - - Answer answer = libvirtGetVolumesOnStorageCommandWrapper.execute(command, libvirtComputingResource); - Assert.assertTrue(answer instanceof GetVolumesOnStorageAnswer); - Assert.assertTrue(answer.getResult()); - List volumes = ((GetVolumesOnStorageAnswer) answer).getVolumes(); - Assert.assertEquals(numberDisks, volumes.size()); - volumeOnStorageTOMock.constructed().forEach(s -> Mockito.verify(s, times(1)).setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS.toString())); - } + Answer answer = libvirtGetVolumesOnStorageCommandWrapper.execute(command, libvirtComputingResource); + Assert.assertTrue(answer instanceof GetVolumesOnStorageAnswer); + Assert.assertTrue(answer.getResult()); + List volumes = ((GetVolumesOnStorageAnswer) answer).getVolumes(); + Assert.assertEquals(numberDisks, volumes.size()); + volumeOnStorageTOMock.constructed().forEach(s -> Mockito.verify(s, times(1)).setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS.toString())); } @Test From 7fdc82b4771560bc8591805233bdddaa3b66dc89 Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Wed, 23 Jul 2025 09:53:26 +0530 Subject: [PATCH 3/4] review --- .../java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1c6a954f9de3..c43f5101fbe5 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 @@ -99,7 +99,7 @@ public static void checkQcow2File(String path) { try { Qcow2Inspector.validateQcow2File(path); } catch (RuntimeException e) { - throw new CloudRuntimeException("The volume file at path " + path + " is not a valid QCOW2"); + throw new CloudRuntimeException("The volume file at path " + path + " is not a valid QCOW2. Error: " + e.getMessage()); } } } From 210a3f2ec51da8e306e7d524bdb2f18c662d6bfa Mon Sep 17 00:00:00 2001 From: Suresh Kumar Anaparti Date: Thu, 24 Jul 2025 13:26:59 +0530 Subject: [PATCH 4/4] Check imported volume details, throw appropriate error for locked or encrypted volume, and volume with backing file --- .../cloud/agent/api/CheckVolumeAnswer.java | 15 +++- .../agent/api/CopyRemoteVolumeAnswer.java | 15 +++- .../LibvirtCheckVolumeCommandWrapper.java | 82 +++++++++++++++++-- ...LibvirtCopyRemoteVolumeCommandWrapper.java | 79 +++++++++++++++++- ...virtGetVolumesOnStorageCommandWrapper.java | 66 +++++++++------ .../vm/UnmanagedVMsManagerImpl.java | 37 ++++++++- 6 files changed, 255 insertions(+), 39 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 5a32ab59a7a2..07b7e102df96 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 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 volumeDetails) { + super(cmd, success, details); this.size = size; + this.volumeDetails = volumeDetails; } public long getSize() { return size; } + public Map 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 e79005be71b9..4aec0b26581b 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 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 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 long getSize() { return size; } + public Map 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 4329e0a01035..2caf8da29140 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 @@ -30,12 +30,26 @@ 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 { private static final Logger s_logger = Logger.getLogger(LibvirtCheckVolumeCommandWrapper.class); + private static final List STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem); @Override public Answer execute(final CheckVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) { @@ -46,20 +60,76 @@ public Answer execute(final CheckVolumeCommand command, final LibvirtComputingRe 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(); - KVMPhysicalDisk.checkQcow2File(path); + 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, "", size); + 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 Map getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) { + Map info = getDiskFileInfo(pool, disk, true); + if (MapUtils.isEmpty(info)) { + return null; + } + + Map 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 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(disk.getPath(), disk.getFormat()); + return qemu.info(qemuFile, secure); + } catch (QemuImgException | LibvirtException ex) { + logger.error("Failed to get info of disk file: " + ex.getMessage()); + return null; + } + } + + private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) { + Map 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 fcef2110ec02..6edf5cbd906a 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 @@ -30,12 +30,26 @@ 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 { private static final Logger s_logger = Logger.getLogger(LibvirtCopyRemoteVolumeCommandWrapper.class); + private static final List STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem); @Override public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) { @@ -51,15 +65,19 @@ public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComput 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(); - KVMPhysicalDisk.checkQcow2File(path); + 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, "", filename, size); + 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); @@ -70,4 +88,57 @@ public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComput return new Answer(command, false, msg); } } + + private Map getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) { + Map info = getDiskFileInfo(pool, disk, true); + if (MapUtils.isEmpty(info)) { + return null; + } + + Map 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 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(disk.getPath(), disk.getFormat()); + return qemu.info(qemuFile, secure); + } catch (QemuImgException | LibvirtException ex) { + logger.error("Failed to get info of disk file: " + ex.getMessage()); + return null; + } + } + + private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) { + Map 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 821a80f5ccac..6facf1696025 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.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 @@ private GetVolumesOnStorageAnswer addVolumeByVolumePath(final GetVolumesOnStorag 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 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 volumes = new ArrayList<>(); @@ -134,11 +144,21 @@ private GetVolumesOnStorageAnswer addAllVolumes(final GetVolumesOnStorageCommand if (!isDiskFormatSupported(disk)) { continue; } + Map 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/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index abb0e6b63c53..df87aff276d2 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.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 @@ private Pair importExternalDisk(UnmanagedInstanceTO.Di 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 @@ private UserVm importKvmVirtualMachineFromDisk(final ImportSource importSource, 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 @@ private UserVm importKvmVirtualMachineFromDisk(final ImportSource importSource, return userVm; } + private void checkVolume(Map 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;