Skip to content

Commit 609efcc

Browse files
author
Daan Hoogland
committed
Merge branch '4.19' into 4.20
2 parents ed6ee6b + a4263da commit 609efcc

File tree

24 files changed

+440
-282
lines changed

24 files changed

+440
-282
lines changed

api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public void execute() {
7272
response.setInstancesDisksStatsRetentionTime((Integer) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME));
7373
response.setSharedFsVmMinCpuCount((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT));
7474
response.setSharedFsVmMinRamSize((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE));
75+
response.setDynamicScalingEnabled((Boolean) capabilities.get(ApiConstants.DYNAMIC_SCALING_ENABLED));
7576
response.setObjectName("capability");
7677
response.setResponseName(getCommandName());
7778
this.setResponseObject(response);

api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ public class CapabilitiesResponse extends BaseResponse {
136136
@Param(description = "the min Ram size for the service offering used by the shared filesystem instance", since = "4.20.0")
137137
private Integer sharedFsVmMinRamSize;
138138

139+
@SerializedName(ApiConstants.DYNAMIC_SCALING_ENABLED)
140+
@Param(description = "true if dynamically scaling for instances is enabled", since = "4.21.0")
141+
private Boolean dynamicScalingEnabled;
142+
139143
public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
140144
this.securityGroupsEnabled = securityGroupsEnabled;
141145
}
@@ -247,4 +251,8 @@ public void setSharedFsVmMinCpuCount(Integer sharedFsVmMinCpuCount) {
247251
public void setSharedFsVmMinRamSize(Integer sharedFsVmMinRamSize) {
248252
this.sharedFsVmMinRamSize = sharedFsVmMinRamSize;
249253
}
254+
255+
public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) {
256+
this.dynamicScalingEnabled = dynamicScalingEnabled;
257+
}
250258
}

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: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,24 @@
3131
import com.cloud.resource.ResourceWrapper;
3232
import com.cloud.storage.Storage;
3333
import com.cloud.utils.exception.CloudRuntimeException;
34+
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
3435
import org.apache.cloudstack.utils.qemu.QemuImg;
3536
import org.apache.cloudstack.utils.qemu.QemuImgException;
3637
import org.apache.cloudstack.utils.qemu.QemuImgFile;
38+
import org.apache.commons.collections.MapUtils;
39+
import org.apache.commons.lang3.StringUtils;
3740
import org.libvirt.LibvirtException;
3841

42+
import java.util.Arrays;
43+
import java.util.HashMap;
44+
import java.util.List;
3945
import java.util.Map;
4046

4147
@ResourceWrapper(handles = CheckVolumeCommand.class)
4248
public final class LibvirtCheckVolumeCommandWrapper extends CommandWrapper<CheckVolumeCommand, Answer, LibvirtComputingResource> {
4349

50+
private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem);
51+
4452
@Override
4553
public Answer execute(final CheckVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
4654
String result = null;
@@ -50,34 +58,76 @@ public Answer execute(final CheckVolumeCommand command, final LibvirtComputingRe
5058
KVMStoragePool pool = poolMgr.getStoragePool(storageFilerTO.getType(), storageFilerTO.getUuid());
5159

5260
try {
53-
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
54-
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
61+
if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) {
5562
final KVMPhysicalDisk vol = pool.getPhysicalDisk(srcFile);
5663
final String path = vol.getPath();
57-
long size = getVirtualSizeFromFile(path);
58-
return new CheckVolumeAnswer(command, "", size);
64+
try {
65+
KVMPhysicalDisk.checkQcow2File(path);
66+
} catch (final CloudRuntimeException e) {
67+
return new CheckVolumeAnswer(command, false, "", 0, getVolumeDetails(pool, vol));
68+
}
69+
70+
long size = KVMPhysicalDisk.getVirtualSizeFromFile(path);
71+
return new CheckVolumeAnswer(command, true, "", size, getVolumeDetails(pool, vol));
5972
} else {
6073
return new Answer(command, false, "Unsupported Storage Pool");
6174
}
62-
6375
} catch (final Exception e) {
64-
logger.error("Error while locating disk: "+ e.getMessage());
76+
logger.error("Error while checking the disk: {}", e.getMessage());
6577
return new Answer(command, false, result);
6678
}
6779
}
6880

69-
private long getVirtualSizeFromFile(String path) {
81+
private Map<VolumeOnStorageTO.Detail, String> getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) {
82+
Map<String, String> info = getDiskFileInfo(pool, disk, true);
83+
if (MapUtils.isEmpty(info)) {
84+
return null;
85+
}
86+
87+
Map<VolumeOnStorageTO.Detail, String> volumeDetails = new HashMap<>();
88+
89+
String backingFilePath = info.get(QemuImg.BACKING_FILE);
90+
if (StringUtils.isNotBlank(backingFilePath)) {
91+
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
92+
}
93+
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
94+
if (StringUtils.isNotBlank(backingFileFormat)) {
95+
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
96+
}
97+
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
98+
if (StringUtils.isNotBlank(clusterSize)) {
99+
volumeDetails.put(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
100+
}
101+
String fileFormat = info.get(QemuImg.FILE_FORMAT);
102+
if (StringUtils.isNotBlank(fileFormat)) {
103+
volumeDetails.put(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
104+
}
105+
String encrypted = info.get(QemuImg.ENCRYPTED);
106+
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
107+
volumeDetails.put(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
108+
}
109+
Boolean isLocked = isDiskFileLocked(pool, disk);
110+
volumeDetails.put(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
111+
112+
return volumeDetails;
113+
}
114+
115+
private Map<String, String> getDiskFileInfo(KVMStoragePool pool, KVMPhysicalDisk disk, boolean secure) {
116+
if (!STORAGE_POOL_TYPES_SUPPORTED.contains(pool.getType())) {
117+
return new HashMap<>(); // unknown
118+
}
70119
try {
71120
QemuImg qemu = new QemuImg(0);
72-
QemuImgFile qemuFile = new QemuImgFile(path);
73-
Map<String, String> info = qemu.info(qemuFile);
74-
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
75-
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
76-
} else {
77-
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
78-
}
121+
QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
122+
return qemu.info(qemuFile, secure);
79123
} catch (QemuImgException | LibvirtException ex) {
80-
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
124+
logger.error("Failed to get info of disk file: " + ex.getMessage());
125+
return null;
81126
}
82127
}
128+
129+
private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) {
130+
Map<String, String> info = getDiskFileInfo(pool, disk, false);
131+
return info == null;
132+
}
83133
}

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

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,22 @@
3131
import com.cloud.resource.ResourceWrapper;
3232
import com.cloud.storage.Storage;
3333
import com.cloud.utils.exception.CloudRuntimeException;
34+
import org.apache.cloudstack.storage.volume.VolumeOnStorageTO;
3435
import org.apache.cloudstack.utils.qemu.QemuImg;
3536
import org.apache.cloudstack.utils.qemu.QemuImgException;
3637
import org.apache.cloudstack.utils.qemu.QemuImgFile;
38+
import org.apache.commons.collections.MapUtils;
39+
import org.apache.commons.lang3.StringUtils;
3740
import org.libvirt.LibvirtException;
3841

42+
import java.util.Arrays;
43+
import java.util.HashMap;
44+
import java.util.List;
3945
import java.util.Map;
4046

4147
@ResourceWrapper(handles = CopyRemoteVolumeCommand.class)
4248
public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<CopyRemoteVolumeCommand, Answer, LibvirtComputingResource> {
49+
private static final List<Storage.StoragePoolType> STORAGE_POOL_TYPES_SUPPORTED = Arrays.asList(Storage.StoragePoolType.Filesystem, Storage.StoragePoolType.NetworkFilesystem);
4350

4451
@Override
4552
public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
@@ -55,14 +62,19 @@ public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComput
5562
int timeoutInSecs = command.getWait();
5663

5764
try {
58-
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
59-
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
65+
if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) {
6066
String filename = libvirtComputingResource.copyVolume(srcIp, username, password, dstPath, srcFile, tmpPath, timeoutInSecs);
6167
logger.debug("Volume " + srcFile + " copy successful, copied to file: " + filename);
6268
final KVMPhysicalDisk vol = pool.getPhysicalDisk(filename);
6369
final String path = vol.getPath();
64-
long size = getVirtualSizeFromFile(path);
65-
return new CopyRemoteVolumeAnswer(command, "", filename, size);
70+
try {
71+
KVMPhysicalDisk.checkQcow2File(path);
72+
} catch (final CloudRuntimeException e) {
73+
return new CopyRemoteVolumeAnswer(command, false, "", filename, 0, getVolumeDetails(pool, vol));
74+
}
75+
76+
long size = KVMPhysicalDisk.getVirtualSizeFromFile(path);
77+
return new CopyRemoteVolumeAnswer(command, true, "", filename, size, getVolumeDetails(pool, vol));
6678
} else {
6779
String msg = "Unsupported storage pool type: " + storageFilerTO.getType().toString() + ", only local and NFS pools are supported";
6880
return new Answer(command, false, msg);
@@ -74,18 +86,56 @@ public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComput
7486
}
7587
}
7688

77-
private long getVirtualSizeFromFile(String path) {
89+
private Map<VolumeOnStorageTO.Detail, String> getVolumeDetails(KVMStoragePool pool, KVMPhysicalDisk disk) {
90+
Map<String, String> info = getDiskFileInfo(pool, disk, true);
91+
if (MapUtils.isEmpty(info)) {
92+
return null;
93+
}
94+
95+
Map<VolumeOnStorageTO.Detail, String> volumeDetails = new HashMap<>();
96+
97+
String backingFilePath = info.get(QemuImg.BACKING_FILE);
98+
if (StringUtils.isNotBlank(backingFilePath)) {
99+
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE, backingFilePath);
100+
}
101+
String backingFileFormat = info.get(QemuImg.BACKING_FILE_FORMAT);
102+
if (StringUtils.isNotBlank(backingFileFormat)) {
103+
volumeDetails.put(VolumeOnStorageTO.Detail.BACKING_FILE_FORMAT, backingFileFormat);
104+
}
105+
String clusterSize = info.get(QemuImg.CLUSTER_SIZE);
106+
if (StringUtils.isNotBlank(clusterSize)) {
107+
volumeDetails.put(VolumeOnStorageTO.Detail.CLUSTER_SIZE, clusterSize);
108+
}
109+
String fileFormat = info.get(QemuImg.FILE_FORMAT);
110+
if (StringUtils.isNotBlank(fileFormat)) {
111+
volumeDetails.put(VolumeOnStorageTO.Detail.FILE_FORMAT, fileFormat);
112+
}
113+
String encrypted = info.get(QemuImg.ENCRYPTED);
114+
if (StringUtils.isNotBlank(encrypted) && encrypted.equalsIgnoreCase("yes")) {
115+
volumeDetails.put(VolumeOnStorageTO.Detail.IS_ENCRYPTED, String.valueOf(Boolean.TRUE));
116+
}
117+
Boolean isLocked = isDiskFileLocked(pool, disk);
118+
volumeDetails.put(VolumeOnStorageTO.Detail.IS_LOCKED, String.valueOf(isLocked));
119+
120+
return volumeDetails;
121+
}
122+
123+
private Map<String, String> getDiskFileInfo(KVMStoragePool pool, KVMPhysicalDisk disk, boolean secure) {
124+
if (!STORAGE_POOL_TYPES_SUPPORTED.contains(pool.getType())) {
125+
return new HashMap<>(); // unknown
126+
}
78127
try {
79128
QemuImg qemu = new QemuImg(0);
80-
QemuImgFile qemuFile = new QemuImgFile(path);
81-
Map<String, String> info = qemu.info(qemuFile);
82-
if (info.containsKey(QemuImg.VIRTUAL_SIZE)) {
83-
return Long.parseLong(info.get(QemuImg.VIRTUAL_SIZE));
84-
} else {
85-
throw new CloudRuntimeException("Unable to determine virtual size of volume at path " + path);
86-
}
129+
QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
130+
return qemu.info(qemuFile, secure);
87131
} catch (QemuImgException | LibvirtException ex) {
88-
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
132+
logger.error("Failed to get info of disk file: " + ex.getMessage());
133+
return null;
89134
}
90135
}
136+
137+
private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) {
138+
Map<String, String> info = getDiskFileInfo(pool, disk, false);
139+
return info == null;
140+
}
91141
}

0 commit comments

Comments
 (0)