Skip to content

Commit bebacdc

Browse files
Validate qcow2 file during import operation
1 parent 3fc02dd commit bebacdc

File tree

6 files changed

+66
-70
lines changed

6 files changed

+66
-70
lines changed

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckVolumeCommandWrapper.java

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,7 @@
3030
import com.cloud.resource.CommandWrapper;
3131
import com.cloud.resource.ResourceWrapper;
3232
import com.cloud.storage.Storage;
33-
import com.cloud.utils.exception.CloudRuntimeException;
34-
import org.apache.cloudstack.utils.qemu.QemuImg;
35-
import org.apache.cloudstack.utils.qemu.QemuImgException;
36-
import org.apache.cloudstack.utils.qemu.QemuImgFile;
3733
import org.apache.log4j.Logger;
38-
import org.libvirt.LibvirtException;
39-
40-
import java.util.Map;
4134

4235
@ResourceWrapper(handles = CheckVolumeCommand.class)
4336
public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<CheckVolumeCommand, Answer, LibvirtComputingResource> {
@@ -57,7 +50,8 @@ public Answer execute(final CheckVolumeCommand command, final LibvirtComputingRe
5750
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
5851
final KVMPhysicalDisk vol = pool.getPhysicalDisk(srcFile);
5952
final String path = vol.getPath();
60-
long size = getVirtualSizeFromFile(path);
53+
KVMPhysicalDisk.checkQcow2File(path);
54+
long size = KVMPhysicalDisk.getVirtualSizeFromFile(path);
6155
return new CheckVolumeAnswer(command, "", size);
6256
} else {
6357
return new Answer(command, false, "Unsupported Storage Pool");
@@ -68,19 +62,4 @@ public Answer execute(final CheckVolumeCommand command, final LibvirtComputingRe
6862
return new Answer(command, false, result);
6963
}
7064
}
71-
72-
private long getVirtualSizeFromFile(String path) {
73-
try {
74-
QemuImg qemu = new QemuImg(0);
75-
QemuImgFile qemuFile = new QemuImgFile(path);
76-
Map<String, String> info = qemu.info(qemuFile);
77-
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
78-
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
79-
} else {
80-
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
81-
}
82-
} catch (QemuImgException | LibvirtException ex) {
83-
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
84-
}
85-
}
8665
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCopyRemoteVolumeCommandWrapper.java

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,7 @@
3030
import com.cloud.resource.CommandWrapper;
3131
import com.cloud.resource.ResourceWrapper;
3232
import com.cloud.storage.Storage;
33-
import com.cloud.utils.exception.CloudRuntimeException;
34-
import org.apache.cloudstack.utils.qemu.QemuImg;
35-
import org.apache.cloudstack.utils.qemu.QemuImgException;
36-
import org.apache.cloudstack.utils.qemu.QemuImgFile;
3733
import org.apache.log4j.Logger;
38-
import org.libvirt.LibvirtException;
39-
40-
import java.util.Map;
4134

4235
@ResourceWrapper(handles = CopyRemoteVolumeCommand.class)
4336
public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<CopyRemoteVolumeCommand, Answer, LibvirtComputingResource> {
@@ -64,7 +57,8 @@ public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComput
6457
s_logger.debug("Volume " + srcFile + " copy successful, copied to file: " + filename);
6558
final KVMPhysicalDisk vol = pool.getPhysicalDisk(filename);
6659
final String path = vol.getPath();
67-
long size = getVirtualSizeFromFile(path);
60+
KVMPhysicalDisk.checkQcow2File(path);
61+
long size = KVMPhysicalDisk.getVirtualSizeFromFile(path);
6862
return new CopyRemoteVolumeAnswer(command, "", filename, size);
6963
} else {
7064
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
7670
return new Answer(command, false, msg);
7771
}
7872
}
79-
80-
private long getVirtualSizeFromFile(String path) {
81-
try {
82-
QemuImg qemu = new QemuImg(0);
83-
QemuImgFile qemuFile = new QemuImgFile(path);
84-
Map<String, String> info = qemu.info(qemuFile);
85-
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
86-
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
87-
} else {
88-
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
89-
}
90-
} catch (QemuImgException | LibvirtException ex) {
91-
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
92-
}
93-
}
9473
}

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapper.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import com.cloud.resource.CommandWrapper;
3232
import com.cloud.resource.ResourceWrapper;
3333
import com.cloud.storage.Storage.StoragePoolType;
34+
import com.cloud.utils.exception.CloudRuntimeException;
3435
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
3536
import org.apache.cloudstack.utils.qemu.QemuImg;
3637
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
@@ -82,6 +83,13 @@ private GetVolumesOnStorageAnswer addVolumeByVolumePath(final GetVolumesOnStorag
8283
if (!isDiskFormatSupported(disk)) {
8384
return new GetVolumesOnStorageAnswer(command, false, String.format("disk format %s is unsupported", disk.getFormat()));
8485
}
86+
if (PhysicalDiskFormat.QCOW2.equals(disk.getFormat())) {
87+
try {
88+
KVMPhysicalDisk.checkQcow2File(disk.getPath());
89+
} catch (CloudRuntimeException e) {
90+
return new GetVolumesOnStorageAnswer(command, false, e.getMessage());
91+
}
92+
}
8593
Map<String, String> info = getDiskFileInfo(storagePool, disk, true);
8694
if (info == null) {
8795
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
134142
if (!isDiskFormatSupported(disk)) {
135143
continue;
136144
}
145+
if (PhysicalDiskFormat.QCOW2.equals(disk.getFormat())) {
146+
try {
147+
KVMPhysicalDisk.checkQcow2File(disk.getPath());
148+
} catch (CloudRuntimeException e) {
149+
logger.error("Error while checking for qcow2 volume file: " + e.getMessage());
150+
continue;
151+
}
152+
}
137153
VolumeOnStorageTO volumeOnStorageTO = new VolumeOnStorageTO(Hypervisor.HypervisorType.KVM, disk.getName(), disk.getName(), disk.getPath(),
138154
disk.getFormat().toString(), disk.getSize(), disk.getVirtualSize());
139155
if (disk.getQemuEncryptFormat() != null) {

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtResizeVolumeCommandWrapper.java

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,13 @@
2525
import java.util.ArrayList;
2626
import java.util.Arrays;
2727
import java.util.List;
28-
import java.util.Map;
2928

3029
import com.cloud.hypervisor.kvm.storage.ScaleIOStorageAdaptor;
3130
import org.apache.cloudstack.utils.cryptsetup.KeyFile;
3231
import org.apache.cloudstack.utils.qemu.QemuImageOptions;
3332
import org.apache.cloudstack.utils.qemu.QemuImg;
3433
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
3534
import org.apache.cloudstack.utils.qemu.QemuImgException;
36-
import org.apache.cloudstack.utils.qemu.QemuImgFile;
3735
import org.apache.cloudstack.utils.qemu.QemuObject;
3836
import org.apache.log4j.Logger;
3937
import org.libvirt.Connect;
@@ -102,7 +100,7 @@ public Answer execute(final ResizeVolumeCommand command, final LibvirtComputingR
102100
newSize = ScaleIOStorageAdaptor.getUsableBytesFromRawBytes(newSize);
103101
} else if (spool.getType().equals(StoragePoolType.PowerFlex)) {
104102
// PowerFlex RAW/LUKS is already resized, we just notify the domain based on new size (considering LUKS overhead)
105-
newSize = getVirtualSizeFromFile(path);
103+
newSize = KVMPhysicalDisk.getVirtualSizeFromFile(path);
106104
}
107105

108106
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
216214
}
217215
}
218216

219-
private long getVirtualSizeFromFile(String path) {
220-
try {
221-
QemuImg qemu = new QemuImg(0);
222-
QemuImgFile qemuFile = new QemuImgFile(path);
223-
Map<String, String> info = qemu.info(qemuFile);
224-
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
225-
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
226-
} else {
227-
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
228-
}
229-
} catch (QemuImgException | LibvirtException ex) {
230-
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
231-
}
232-
}
233-
234217
private Answer handleMultipathSCSIResize(ResizeVolumeCommand command, KVMStoragePool pool) {
235218
((MultipathSCSIPool)pool).resize(command.getPath(), command.getInstanceName(), command.getNewSize());
236219
return new ResizeVolumeAnswer(command, true, "");

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,21 @@
1616
// under the License.
1717
package com.cloud.hypervisor.kvm.storage;
1818

19+
import com.cloud.utils.exception.CloudRuntimeException;
20+
import org.apache.cloudstack.storage.formatinspector.Qcow2Inspector;
21+
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
22+
import org.apache.cloudstack.utils.qemu.QemuImg;
1923
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
24+
import org.apache.cloudstack.utils.qemu.QemuImgException;
25+
import org.apache.cloudstack.utils.qemu.QemuImgFile;
2026
import org.apache.cloudstack.utils.qemu.QemuObject;
2127
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
2228
import org.apache.commons.lang3.StringUtils;
29+
import org.libvirt.LibvirtException;
2330

2431
import java.util.ArrayList;
2532
import java.util.List;
33+
import java.util.Map;
2634

2735
public class KVMPhysicalDisk {
2836
private String path;
@@ -71,6 +79,31 @@ private static String replaceHostAddress(String hostIp) {
7179
return hostIp;
7280
}
7381

82+
public static long getVirtualSizeFromFile(String path) {
83+
try {
84+
QemuImg qemu = new QemuImg(0);
85+
QemuImgFile qemuFile = new QemuImgFile(path);
86+
Map<String, String> info = qemu.info(qemuFile);
87+
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
88+
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
89+
} else {
90+
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
91+
}
92+
} catch (QemuImgException | LibvirtException ex) {
93+
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
94+
}
95+
}
96+
97+
public static void checkQcow2File(String path) {
98+
if (ImageStoreUtil.isCorrectExtension(path, "qcow2")) {
99+
try {
100+
Qcow2Inspector.validateQcow2File(path);
101+
} catch (RuntimeException e) {
102+
throw new CloudRuntimeException("The volume file at path " + path + " is not a valid QCOW2");
103+
}
104+
}
105+
}
106+
74107
private PhysicalDiskFormat format;
75108
private long size;
76109
private long virtualSize;

plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumesOnStorageCommandWrapperTest.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.junit.Test;
3939
import org.junit.runner.RunWith;
4040
import org.mockito.Mock;
41+
import org.mockito.MockedStatic;
4142
import org.mockito.MockedConstruction;
4243
import org.mockito.Mockito;
4344
import org.mockito.Spy;
@@ -116,12 +117,17 @@ public void testLibvirtGetVolumesOnStorageCommandWrapperForAllVolumes() {
116117
}
117118
Mockito.when(storagePool.listPhysicalDisks()).thenReturn(physicalDisks);
118119

119-
Answer answer = libvirtGetVolumesOnStorageCommandWrapper.execute(command, libvirtComputingResource);
120-
Assert.assertTrue(answer instanceof GetVolumesOnStorageAnswer);
121-
Assert.assertTrue(answer.getResult());
122-
List<VolumeOnStorageTO> volumes = ((GetVolumesOnStorageAnswer) answer).getVolumes();
123-
Assert.assertEquals(numberDisks, volumes.size());
124-
volumeOnStorageTOMock.constructed().forEach(s -> Mockito.verify(s, times(1)).setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS.toString()));
120+
try (MockedStatic<KVMPhysicalDisk> mockedKVMPhysicalDisk = Mockito.mockStatic(KVMPhysicalDisk.class)) {
121+
mockedKVMPhysicalDisk.when(() -> KVMPhysicalDisk.checkQcow2File(Mockito.anyString()))
122+
.thenAnswer(invocation -> null);
123+
124+
Answer answer = libvirtGetVolumesOnStorageCommandWrapper.execute(command, libvirtComputingResource);
125+
Assert.assertTrue(answer instanceof GetVolumesOnStorageAnswer);
126+
Assert.assertTrue(answer.getResult());
127+
List<VolumeOnStorageTO> volumes = ((GetVolumesOnStorageAnswer) answer).getVolumes();
128+
Assert.assertEquals(numberDisks, volumes.size());
129+
volumeOnStorageTOMock.constructed().forEach(s -> Mockito.verify(s, times(1)).setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS.toString()));
130+
}
125131
}
126132

127133
@Test

0 commit comments

Comments
 (0)