Skip to content

Commit 137f3be

Browse files
Check imported volume details, throw appropriate error for locked or encrypted volume, and volume with backing file
1 parent 7fdc82b commit 137f3be

File tree

6 files changed

+249
-38
lines changed

6 files changed

+249
-38
lines changed

core/src/main/java/com/cloud/agent/api/CheckVolumeAnswer.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,33 @@
1717

1818
package com.cloud.agent.api;
1919

20+
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
21+
22+
import java.util.Map;
23+
2024
public class CheckVolumeAnswer extends Answer {
2125

2226
private long size;
27+
private Map<VolumeOnStorageTO.Detail, String> volumeDetails;
2328

2429
CheckVolumeAnswer() {
2530
}
2631

27-
public CheckVolumeAnswer(CheckVolumeCommand cmd, String details, long size) {
28-
super(cmd, true, details);
32+
public CheckVolumeAnswer(CheckVolumeCommand cmd, final boolean success, String details, long size,
33+
Map<VolumeOnStorageTO.Detail, String> volumeDetails) {
34+
super(cmd, success, details);
2935
this.size = size;
36+
this.volumeDetails = volumeDetails;
3037
}
3138

3239
public long getSize() {
3340
return size;
3441
}
3542

43+
public Map<VolumeOnStorageTO.Detail, String> getVolumeDetails() {
44+
return volumeDetails;
45+
}
46+
3647
public String getString() {
3748
return "CheckVolumeAnswer [size=" + size + "]";
3849
}

core/src/main/java/com/cloud/agent/api/CopyRemoteVolumeAnswer.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,28 @@
1717

1818
package com.cloud.agent.api;
1919

20+
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
21+
22+
import java.util.Map;
23+
2024
public class CopyRemoteVolumeAnswer extends Answer {
2125

2226
private String remoteIp;
2327
private String filename;
2428

2529
private long size;
30+
private Map<VolumeOnStorageTO.Detail, String> volumeDetails;
2631

2732
CopyRemoteVolumeAnswer() {
2833
}
2934

30-
public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, String details, String filename, long size) {
31-
super(cmd, true, details);
35+
public CopyRemoteVolumeAnswer(CopyRemoteVolumeCommand cmd, final boolean success, String details, String filename, long size,
36+
Map<VolumeOnStorageTO.Detail, String> volumeDetails) {
37+
super(cmd, success, details);
3238
this.remoteIp = cmd.getRemoteIp();
3339
this.filename = filename;
3440
this.size = size;
41+
this.volumeDetails = volumeDetails;
3542
}
3643

3744
public String getRemoteIp() {
@@ -54,6 +61,10 @@ public long getSize() {
5461
return size;
5562
}
5663

64+
public Map<VolumeOnStorageTO.Detail, String> getVolumeDetails() {
65+
return volumeDetails;
66+
}
67+
5768
public String getString() {
5869
return "CopyRemoteVolumeAnswer [remoteIp=" + remoteIp + "]";
5970
}

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

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,26 @@
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.storage.volume.VolumeOnStorageTO;
35+
import org.apache.cloudstack.utils.qemu.QemuImg;
36+
import org.apache.cloudstack.utils.qemu.QemuImgException;
37+
import org.apache.cloudstack.utils.qemu.QemuImgFile;
38+
import org.apache.commons.collections.MapUtils;
39+
import org.apache.commons.lang3.StringUtils;
3340
import org.apache.log4j.Logger;
41+
import org.libvirt.LibvirtException;
42+
43+
import java.util.Arrays;
44+
import java.util.HashMap;
45+
import java.util.List;
46+
import java.util.Map;
3447

3548
@ResourceWrapper(handles = CheckVolumeCommand.class)
3649
public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<CheckVolumeCommand, Answer, LibvirtComputingResource> {
3750

3851
private static final Logger s_logger = Logger.getLogger(LibvirtCheckVolumeCommandWrapper.class);
52+
private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem);
3953

4054
@Override
4155
public Answer execute(final CheckVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
@@ -46,20 +60,76 @@ public Answer execute(final CheckVolumeCommand command, final LibvirtComputingRe
4660
KVMStoragePool pool = poolMgr.getStoragePool(storageFilerTO.getType(), storageFilerTO.getUuid());
4761

4862
try {
49-
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
50-
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
63+
if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) {
5164
final KVMPhysicalDisk vol = pool.getPhysicalDisk(srcFile);
5265
final String path = vol.getPath();
53-
KVMPhysicalDisk.checkQcow2File(path);
66+
try {
67+
KVMPhysicalDisk.checkQcow2File(path);
68+
} catch (final CloudRuntimeException e) {
69+
return new CheckVolumeAnswer(command, false, "", 0, getVolumeDetails(pool, vol));
70+
}
71+
5472
long size = KVMPhysicalDisk.getVirtualSizeFromFile(path);
55-
return new CheckVolumeAnswer(command, "", size);
73+
return new CheckVolumeAnswer(command, true, "", size, getVolumeDetails(pool, vol));
5674
} else {
5775
return new Answer(command, false, "Unsupported Storage Pool");
5876
}
59-
6077
} catch (final Exception e) {
61-
s_logger.error("Error while locating disk: "+ e.getMessage());
78+
s_logger.error("Error while checking the disk: " + e.getMessage());
6279
return new Answer(command, false, result);
6380
}
6481
}
82+
83+
private Map<VolumeOnStorageTO.Detail, String> getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) {
84+
Map<String, String> info = getDiskFileInfo(pool, disk, true);
85+
if (MapUtils.isEmpty(info)) {
86+
return null;
87+
}
88+
89+
Map<VolumeOnStorageTO.Detail, String> volumeDetails = new HashMap<>();
90+
91+
String backingFilePath = info.get(QemuImg.BACKING_FILE);
92+
if (StringUtils.isNotBlank(backingFilePath)) {
93+
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
94+
}
95+
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
96+
if (StringUtils.isNotBlank(backingFileFormat)) {
97+
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
98+
}
99+
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
100+
if (StringUtils.isNotBlank(clusterSize)) {
101+
volumeDetails.put(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
102+
}
103+
String fileFormat = info.get(QemuImg.FILE_FORMAT);
104+
if (StringUtils.isNotBlank(fileFormat)) {
105+
volumeDetails.put(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
106+
}
107+
String encrypted = info.get(QemuImg.ENCRYPTED);
108+
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
109+
volumeDetails.put(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
110+
}
111+
Boolean isLocked = isDiskFileLocked(pool, disk);
112+
volumeDetails.put(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
113+
114+
return volumeDetails;
115+
}
116+
117+
private Map<String, String> getDiskFileInfo(KVMStoragePool pool, KVMPhysicalDisk disk, boolean secure) {
118+
if (!STORAGE_POOL_TYPES_SUPPORTED.contains(pool.getType())) {
119+
return new HashMap<>(); // unknown
120+
}
121+
try {
122+
QemuImg qemu = new QemuImg(0);
123+
QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
124+
return qemu.info(qemuFile, secure);
125+
} catch (QemuImgException | LibvirtException ex) {
126+
logger.error("Failed to get info of disk file: " + ex.getMessage());
127+
return null;
128+
}
129+
}
130+
131+
private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) {
132+
Map<String, String> info = getDiskFileInfo(pool, disk, false);
133+
return info == null;
134+
}
65135
}

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

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,26 @@
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.storage.volume.VolumeOnStorageTO;
35+
import org.apache.cloudstack.utils.qemu.QemuImg;
36+
import org.apache.cloudstack.utils.qemu.QemuImgException;
37+
import org.apache.cloudstack.utils.qemu.QemuImgFile;
38+
import org.apache.commons.collections.MapUtils;
39+
import org.apache.commons.lang3.StringUtils;
3340
import org.apache.log4j.Logger;
41+
import org.libvirt.LibvirtException;
42+
43+
import java.util.Arrays;
44+
import java.util.HashMap;
45+
import java.util.List;
46+
import java.util.Map;
3447

3548
@ResourceWrapper(handles = CopyRemoteVolumeCommand.class)
3649
public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<CopyRemoteVolumeCommand, Answer, LibvirtComputingResource> {
3750

3851
private static final Logger s_logger = Logger.getLogger(LibvirtCopyRemoteVolumeCommandWrapper.class);
52+
private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem);
3953

4054
@Override
4155
public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
@@ -51,15 +65,19 @@ public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComput
5165
int timeoutInSecs = command.getWait();
5266

5367
try {
54-
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
55-
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
68+
if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) {
5669
String filename = libvirtComputingResource.copyVolume(srcIp, username, password, dstPath, srcFile, tmpPath, timeoutInSecs);
5770
s_logger.debug("Volume " + srcFile + " copy successful, copied to file: " + filename);
5871
final KVMPhysicalDisk vol = pool.getPhysicalDisk(filename);
5972
final String path = vol.getPath();
60-
KVMPhysicalDisk.checkQcow2File(path);
73+
try {
74+
KVMPhysicalDisk.checkQcow2File(path);
75+
} catch (final CloudRuntimeException e) {
76+
return new CopyRemoteVolumeAnswer(command, false, "", filename, 0, getVolumeDetails(pool, vol));
77+
}
78+
6179
long size = KVMPhysicalDisk.getVirtualSizeFromFile(path);
62-
return new CopyRemoteVolumeAnswer(command, "", filename, size);
80+
return new CopyRemoteVolumeAnswer(command, true, "", filename, size, getVolumeDetails(pool, vol));
6381
} else {
6482
String msg = "Unsupported storage pool type: " + storageFilerTO.getType().toString() + ", only local and NFS pools are supported";
6583
return new Answer(command, false, msg);
@@ -70,4 +88,57 @@ public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComput
7088
return new Answer(command, false, msg);
7189
}
7290
}
91+
92+
private Map<VolumeOnStorageTO.Detail, String> getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) {
93+
Map<String, String> info = getDiskFileInfo(pool, disk, true);
94+
if (MapUtils.isEmpty(info)) {
95+
return null;
96+
}
97+
98+
Map<VolumeOnStorageTO.Detail, String> volumeDetails = new HashMap<>();
99+
100+
String backingFilePath = info.get(QemuImg.BACKING_FILE);
101+
if (StringUtils.isNotBlank(backingFilePath)) {
102+
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
103+
}
104+
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
105+
if (StringUtils.isNotBlank(backingFileFormat)) {
106+
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
107+
}
108+
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
109+
if (StringUtils.isNotBlank(clusterSize)) {
110+
volumeDetails.put(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
111+
}
112+
String fileFormat = info.get(QemuImg.FILE_FORMAT);
113+
if (StringUtils.isNotBlank(fileFormat)) {
114+
volumeDetails.put(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
115+
}
116+
String encrypted = info.get(QemuImg.ENCRYPTED);
117+
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
118+
volumeDetails.put(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
119+
}
120+
Boolean isLocked = isDiskFileLocked(pool, disk);
121+
volumeDetails.put(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
122+
123+
return volumeDetails;
124+
}
125+
126+
private Map<String, String> getDiskFileInfo(KVMStoragePool pool, KVMPhysicalDisk disk, boolean secure) {
127+
if (!STORAGE_POOL_TYPES_SUPPORTED.contains(pool.getType())) {
128+
return new HashMap<>(); // unknown
129+
}
130+
try {
131+
QemuImg qemu = new QemuImg(0);
132+
QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
133+
return qemu.info(qemuFile, secure);
134+
} catch (QemuImgException | LibvirtException ex) {
135+
logger.error("Failed to get info of disk file: " + ex.getMessage());
136+
return null;
137+
}
138+
}
139+
140+
private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) {
141+
Map<String, String> info = getDiskFileInfo(pool, disk, false);
142+
return info == null;
143+
}
73144
}

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

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
3737
import org.apache.cloudstack.utils.qemu.QemuImgException;
3838
import org.apache.cloudstack.utils.qemu.QemuImgFile;
39+
import org.apache.commons.collections.MapUtils;
3940
import org.apache.commons.lang3.StringUtils;
4041
import org.libvirt.LibvirtException;
4142

@@ -91,37 +92,46 @@ private GetVolumesOnStorageAnswer addVolumeByVolumePath(final GetVolumesOnStorag
9192
if (disk.getQemuEncryptFormat() != null) {
9293
volumeOnStorageTO.setQemuEncryptFormat(disk.getQemuEncryptFormat().toString());
9394
}
94-
String backingFilePath = info.get(QemuImg.BACKING_FILE);
95-
if (StringUtils.isNotBlank(backingFilePath)) {
96-
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
97-
}
98-
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
99-
if (StringUtils.isNotBlank(backingFileFormat)) {
100-
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
101-
}
102-
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
103-
if (StringUtils.isNotBlank(clusterSize)) {
104-
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
105-
}
10695
String fileFormat = info.get(QemuImg.FILE_FORMAT);
107-
if (StringUtils.isNotBlank(fileFormat)) {
108-
if (!fileFormat.equalsIgnoreCase(disk.getFormat().toString())) {
109-
return new GetVolumesOnStorageAnswer(command, false, String.format("The file format is %s, but expected to be %s", fileFormat, disk.getFormat()));
110-
}
111-
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
96+
if (StringUtils.isNotBlank(fileFormat) && !fileFormat.equalsIgnoreCase(disk.getFormat().toString())) {
97+
return new GetVolumesOnStorageAnswer(command, false, String.format("The file format is %s, but expected to be %s", fileFormat, disk.getFormat()));
11298
}
113-
String encrypted = info.get(QemuImg.ENCRYPTED);
114-
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
115-
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
116-
}
117-
Boolean isLocked = isDiskFileLocked(storagePool, disk);
118-
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
99+
addDetailsToVolumeOnStorageTO(volumeOnStorageTO, info, storagePool, disk);
119100

120101
volumes.add(volumeOnStorageTO);
121102
}
122103
return new GetVolumesOnStorageAnswer(command, volumes);
123104
}
124105

106+
private void addDetailsToVolumeOnStorageTO(VolumeOnStorageTO volumeOnStorageTO, final Map<String, String> info, final KVMStoragePool storagePool, final KVMPhysicalDisk disk) {
107+
if (MapUtils.isEmpty(info)) {
108+
return;
109+
}
110+
111+
String backingFilePath = info.get(QemuImg.BACKING_FILE);
112+
if (StringUtils.isNotBlank(backingFilePath)) {
113+
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
114+
}
115+
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
116+
if (StringUtils.isNotBlank(backingFileFormat)) {
117+
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
118+
}
119+
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
120+
if (StringUtils.isNotBlank(clusterSize)) {
121+
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
122+
}
123+
String fileFormat = info.get(QemuImg.FILE_FORMAT);
124+
if (StringUtils.isNotBlank(fileFormat)) {
125+
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
126+
}
127+
String encrypted = info.get(QemuImg.ENCRYPTED);
128+
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
129+
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
130+
}
131+
Boolean isLocked = isDiskFileLocked(storagePool, disk);
132+
volumeOnStorageTO.addDetail(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
133+
}
134+
125135
private GetVolumesOnStorageAnswer addAllVolumes(final GetVolumesOnStorageCommand command, final KVMStoragePool storagePool, String keyword) {
126136
List<VolumeOnStorageTO> volumes = new ArrayList<>();
127137

@@ -134,11 +144,21 @@ private GetVolumesOnStorageAnswer addAllVolumes(final GetVolumesOnStorageCommand
134144
if (!isDiskFormatSupported(disk)) {
135145
continue;
136146
}
147+
Map<String, String> info = getDiskFileInfo(storagePool, disk, true);
148+
if (info == null) {
149+
continue;
150+
}
137151
VolumeOnStorageTO volumeOnStorageTO = new VolumeOnStorageTO(Hypervisor.HypervisorType.KVM, disk.getName(), disk.getName(), disk.getPath(),
138152
disk.getFormat().toString(), disk.getSize(), disk.getVirtualSize());
139153
if (disk.getQemuEncryptFormat() != null) {
140154
volumeOnStorageTO.setQemuEncryptFormat(disk.getQemuEncryptFormat().toString());
141155
}
156+
String fileFormat = info.get(QemuImg.FILE_FORMAT);
157+
if (StringUtils.isNotBlank(fileFormat) && !fileFormat.equalsIgnoreCase(disk.getFormat().toString())) {
158+
continue;
159+
}
160+
addDetailsToVolumeOnStorageTO(volumeOnStorageTO, info, storagePool, disk);
161+
142162
volumes.add(volumeOnStorageTO);
143163
}
144164
return new GetVolumesOnStorageAnswer(command, volumes);

0 commit comments

Comments
 (0)