Skip to content

Commit 51c99bf

Browse files
Merge branch '4.19' into 4.19-fix-deployvm-without-networkid
2 parents 43479a1 + de5188e commit 51c99bf

File tree

44 files changed

+507
-151
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+507
-151
lines changed

api/src/main/java/org/apache/cloudstack/api/command/admin/resource/ListCapacityCmd.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,14 +127,16 @@ public void execute() {
127127
Collections.sort(capacityResponses, new Comparator<CapacityResponse>() {
128128
public int compare(CapacityResponse resp1, CapacityResponse resp2) {
129129
int res = resp1.getZoneName().compareTo(resp2.getZoneName());
130+
// Group by zone
130131
if (res != 0) {
131132
return res;
132-
} else {
133-
return resp1.getCapacityType().compareTo(resp2.getCapacityType());
134133
}
134+
// Sort by capacity type only if not already sorted by usage
135+
return (getSortBy() != null) ? 0 : resp1.getCapacityType().compareTo(resp2.getCapacityType());
135136
}
136137
});
137138

139+
138140
response.setResponses(capacityResponses);
139141
response.setResponseName(getCommandName());
140142
this.setResponseObject(response);

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
@@ -71,6 +71,7 @@ public void execute() {
7171
response.setInstancesStatsUserOnly((Boolean) capabilities.get(ApiConstants.INSTANCES_STATS_USER_ONLY));
7272
response.setInstancesDisksStatsRetentionEnabled((Boolean) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_ENABLED));
7373
response.setInstancesDisksStatsRetentionTime((Integer) capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME));
74+
response.setDynamicScalingEnabled((Boolean) capabilities.get(ApiConstants.DYNAMIC_SCALING_ENABLED));
7475
response.setObjectName("capability");
7576
response.setResponseName(getCommandName());
7677
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
@@ -124,6 +124,10 @@ public class CapabilitiesResponse extends BaseResponse {
124124
@Param(description = "the retention time for Instances disks stats", since = "4.18.0")
125125
private Integer instancesDisksStatsRetentionTime;
126126

127+
@SerializedName(ApiConstants.DYNAMIC_SCALING_ENABLED)
128+
@Param(description = "true if dynamically scaling for instances is enabled", since = "4.21.0")
129+
private Boolean dynamicScalingEnabled;
130+
127131
public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
128132
this.securityGroupsEnabled = securityGroupsEnabled;
129133
}
@@ -223,4 +227,8 @@ public void setInstancesDisksStatsRetentionTime(Integer instancesDisksStatsReten
223227
public void setCustomHypervisorDisplayName(String customHypervisorDisplayName) {
224228
this.customHypervisorDisplayName = customHypervisorDisplayName;
225229
}
230+
231+
public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) {
232+
this.dynamicScalingEnabled = dynamicScalingEnabled;
233+
}
226234
}

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
}

engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDao.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,6 @@ public interface AutoScaleVmGroupVmMapDao extends GenericDao<AutoScaleVmGroupVmM
3535
public boolean removeByVm(long vmId);
3636

3737
public boolean removeByGroup(long vmGroupId);
38+
39+
int getErroredInstanceCount(long vmGroupId);
3840
}

engine/schema/src/main/java/com/cloud/network/as/dao/AutoScaleVmGroupVmMapDaoImpl.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,13 @@ public boolean removeByGroup(long vmGroupId) {
115115
sc.setParameters("vmGroupId", vmGroupId);
116116
return remove(sc) >= 0;
117117
}
118+
119+
@Override
120+
public int getErroredInstanceCount(long vmGroupId) {
121+
SearchCriteria<Integer> sc = CountBy.create();
122+
sc.setParameters("vmGroupId", vmGroupId);
123+
sc.setJoinParameters("vmSearch", "states", State.Error);
124+
final List<Integer> results = customSearch(sc, null);
125+
return results.get(0);
126+
}
118127
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,15 @@ public LibvirtVMDef.InterfaceDef plug(NicTO nic, String guestOsType, String nicA
252252
intf.defBridgeNet(_bridges.get("private"), null, nic.getMac(), getGuestNicModel(guestOsType, nicAdapter));
253253
} else if (nic.getType() == Networks.TrafficType.Storage) {
254254
String storageBrName = nic.getName() == null ? _bridges.get("private") : nic.getName();
255+
if (nic.getBroadcastType() == Networks.BroadcastDomainType.Storage) {
256+
vNetId = Networks.BroadcastDomainType.getValue(nic.getBroadcastUri());
257+
protocol = Networks.BroadcastDomainType.Vlan.scheme();
258+
}
259+
if (isValidProtocolAndVnetId(vNetId, protocol)) {
260+
s_logger.debug(String.format("creating a vNet dev and bridge for %s traffic per traffic label %s",
261+
Networks.TrafficType.Storage.name(), trafficLabel));
262+
storageBrName = createVnetBr(vNetId, storageBrName, protocol);
263+
}
255264
intf.defBridgeNet(storageBrName, null, nic.getMac(), getGuestNicModel(guestOsType, nicAdapter));
256265
}
257266
if (nic.getPxeDisable()) {

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

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,25 @@
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.apache.log4j.Logger;
3841
import org.libvirt.LibvirtException;
3942

43+
import java.util.Arrays;
44+
import java.util.HashMap;
45+
import java.util.List;
4046
import java.util.Map;
4147

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

4551
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);
4653

4754
@Override
4855
public Answer execute(final CheckVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
@@ -53,34 +60,76 @@ public Answer execute(final CheckVolumeCommand command, final LibvirtComputingRe
5360
KVMStoragePool pool = poolMgr.getStoragePool(storageFilerTO.getType(), storageFilerTO.getUuid());
5461

5562
try {
56-
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
57-
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
63+
if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) {
5864
final KVMPhysicalDisk vol = pool.getPhysicalDisk(srcFile);
5965
final String path = vol.getPath();
60-
long size = getVirtualSizeFromFile(path);
61-
return new CheckVolumeAnswer(command, "", size);
66+
try {
67+
KVMPhysicalDisk.checkQcow2File(path);
68+
} catch (final CloudRuntimeException e) {
69+
return new CheckVolumeAnswer(command, false, "", 0, getVolumeDetails(pool, vol));
70+
}
71+
72+
long size = KVMPhysicalDisk.getVirtualSizeFromFile(path);
73+
return new CheckVolumeAnswer(command, true, "", size, getVolumeDetails(pool, vol));
6274
} else {
6375
return new Answer(command, false, "Unsupported Storage Pool");
6476
}
65-
6677
} catch (final Exception e) {
67-
s_logger.error("Error while locating disk: "+ e.getMessage());
78+
s_logger.error("Error while checking the disk: " + e.getMessage());
6879
return new Answer(command, false, result);
6980
}
7081
}
7182

72-
private long getVirtualSizeFromFile(String path) {
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+
}
73121
try {
74122
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-
}
123+
QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
124+
return qemu.info(qemuFile, secure);
82125
} catch (QemuImgException | LibvirtException ex) {
83-
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
126+
logger.error("Failed to get info of disk file: " + ex.getMessage());
127+
return null;
84128
}
85129
}
130+
131+
private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) {
132+
Map<String, String> info = getDiskFileInfo(pool, disk, false);
133+
return info == null;
134+
}
86135
}

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,18 +31,25 @@
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.apache.log4j.Logger;
3841
import org.libvirt.LibvirtException;
3942

43+
import java.util.Arrays;
44+
import java.util.HashMap;
45+
import java.util.List;
4046
import java.util.Map;
4147

4248
@ResourceWrapper(handles = CopyRemoteVolumeCommand.class)
4349
public final class LibvirtCopyRemoteVolumeCommandWrapper extends CommandWrapper<CopyRemoteVolumeCommand, Answer, LibvirtComputingResource> {
4450

4551
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);
4653

4754
@Override
4855
public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
@@ -58,14 +65,19 @@ public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComput
5865
int timeoutInSecs = command.getWait();
5966

6067
try {
61-
if (storageFilerTO.getType() == Storage.StoragePoolType.Filesystem ||
62-
storageFilerTO.getType() == Storage.StoragePoolType.NetworkFilesystem) {
68+
if (STORAGE_POOL_TYPES_SUPPORTED.contains(storageFilerTO.getType())) {
6369
String filename = libvirtComputingResource.copyVolume(srcIp, username, password, dstPath, srcFile, tmpPath, timeoutInSecs);
6470
s_logger.debug("Volume " + srcFile + " copy successful, copied to file: " + filename);
6571
final KVMPhysicalDisk vol = pool.getPhysicalDisk(filename);
6672
final String path = vol.getPath();
67-
long size = getVirtualSizeFromFile(path);
68-
return new CopyRemoteVolumeAnswer(command, "", filename, size);
73+
try {
74+
KVMPhysicalDisk.checkQcow2File(path);
75+
} catch (final CloudRuntimeException e) {
76+
return new CopyRemoteVolumeAnswer(command, false, "", filename, 0, getVolumeDetails(pool, vol));
77+
}
78+
79+
long size = KVMPhysicalDisk.getVirtualSizeFromFile(path);
80+
return new CopyRemoteVolumeAnswer(command, true, "", filename, size, getVolumeDetails(pool, vol));
6981
} else {
7082
String msg = "Unsupported storage pool type: " + storageFilerTO.getType().toString() + ", only local and NFS pools are supported";
7183
return new Answer(command, false, msg);
@@ -77,18 +89,56 @@ public Answer execute(final CopyRemoteVolumeCommand command, final LibvirtComput
7789
}
7890
}
7991

80-
private long getVirtualSizeFromFile(String path) {
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+
}
81130
try {
82131
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-
}
132+
QemuImgFile qemuFile = new QemuImgFile(disk.getPath(), disk.getFormat());
133+
return qemu.info(qemuFile, secure);
90134
} catch (QemuImgException | LibvirtException ex) {
91-
throw new CloudRuntimeException("Error when inspecting volume at path " + path, ex);
135+
logger.error("Failed to get info of disk file: " + ex.getMessage());
136+
return null;
92137
}
93138
}
139+
140+
private boolean isDiskFileLocked(KVMStoragePool pool, KVMPhysicalDisk disk) {
141+
Map<String, String> info = getDiskFileInfo(pool, disk, false);
142+
return info == null;
143+
}
94144
}

0 commit comments

Comments
 (0)