Skip to content

Commit 3fb4bff

Browse files
committed
api,server,kvm: get total physical size for qcow2
Related #12734 Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
1 parent e93ae1a commit 3fb4bff

6 files changed

Lines changed: 131 additions & 10 deletions

File tree

api/src/main/java/com/cloud/storage/VolumeApiService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,4 +203,6 @@ Volume updateVolume(long volumeId, String path, String state, Long storageId,
203203
Pair<String, String> checkAndRepairVolume(CheckAndRepairVolumeCmd cmd) throws ResourceAllocationException;
204204

205205
Long getVolumePhysicalSize(Storage.ImageFormat format, String path, String chainInfo);
206+
207+
Long getVolumeTotalPhysicalSize(Storage.ImageFormat format, String path, String chainInfo);
206208
}

api/src/main/java/com/cloud/storage/VolumeStats.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,6 @@ public interface VolumeStats {
2626
* @return bytes allocated
2727
*/
2828
long getPhysicalSize();
29+
30+
Long getTotalPhysicalSize();
2931
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public class VolumeStatsEntry implements VolumeStats {
2525
String volumeUuid;
2626
long physicalsize = 0;
2727
long virtualSize = 0;
28+
Long totalPhysicalSize = null;
2829

2930
public VolumeStatsEntry(String volumeUuid, long physicalsize, long virtualSize) {
3031
this.volumeUuid = volumeUuid;
@@ -56,6 +57,23 @@ public void setVirtualSize(long virtualSize) {
5657
this.virtualSize = virtualSize;
5758
}
5859

60+
public long getPhysicalsize() {
61+
return physicalsize;
62+
}
63+
64+
public void setPhysicalsize(long physicalsize) {
65+
this.physicalsize = physicalsize;
66+
}
67+
68+
@Override
69+
public Long getTotalPhysicalSize() {
70+
return totalPhysicalSize;
71+
}
72+
73+
public void setTotalPhysicalSize(Long totalPhysicalSize) {
74+
this.totalPhysicalSize = totalPhysicalSize;
75+
}
76+
5977
@Override
6078
public String toString() {
6179
return "VolumeStatsEntry [volumeUuid=" + volumeUuid + ", size=" + physicalsize + ", virtualSize=" + virtualSize + "]";

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

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,18 @@
2121

2222
import java.util.HashMap;
2323

24+
import org.apache.cloudstack.utils.qemu.QemuImg;
25+
import org.apache.cloudstack.utils.qemu.QemuImgException;
26+
import org.apache.cloudstack.utils.qemu.QemuImgFile;
27+
import org.apache.commons.lang3.StringUtils;
28+
import org.apache.logging.log4j.Logger;
2429
import org.libvirt.Connect;
2530
import org.libvirt.LibvirtException;
2631

2732
import com.cloud.agent.api.Answer;
33+
import com.cloud.agent.api.GetVolumeStatsAnswer;
34+
import com.cloud.agent.api.GetVolumeStatsCommand;
35+
import com.cloud.agent.api.VolumeStatsEntry;
2836
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
2937
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
3038
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
@@ -33,9 +41,10 @@
3341
import com.cloud.resource.ResourceWrapper;
3442
import com.cloud.storage.Storage.StoragePoolType;
3543
import com.cloud.utils.exception.CloudRuntimeException;
36-
import com.cloud.agent.api.GetVolumeStatsAnswer;
37-
import com.cloud.agent.api.GetVolumeStatsCommand;
38-
import com.cloud.agent.api.VolumeStatsEntry;
44+
import com.google.gson.JsonArray;
45+
import com.google.gson.JsonElement;
46+
import com.google.gson.JsonObject;
47+
import com.google.gson.JsonParser;
3948

4049
@ResourceWrapper(handles = GetVolumeStatsCommand.class)
4150
public final class LibvirtGetVolumeStatsCommandWrapper extends CommandWrapper<GetVolumeStatsCommand, Answer, LibvirtComputingResource> {
@@ -72,6 +81,61 @@ private VolumeStatsEntry getVolumeStat(final LibvirtComputingResource libvirtCom
7281
return null;
7382
}
7483

75-
return new VolumeStatsEntry(volumeUuid, sourceKVMVolume.getSize(), sourceKVMVolume.getVirtualSize());
84+
VolumeStatsEntry entry = new VolumeStatsEntry(volumeUuid, sourceKVMVolume.getSize(),
85+
sourceKVMVolume.getVirtualSize());
86+
entry.setTotalPhysicalSize(retrieveTotalPhysicalSize(volumeUuid, sourceKVMVolume.getPath(), logger));
87+
88+
return entry;
89+
}
90+
91+
92+
private static Long retrieveTotalPhysicalSize(String volumeUuid, String volumePath, Logger logger) {
93+
logger.trace("Retrieving total physical size for volume: {} with path: {}", volumeUuid, volumePath);
94+
Long totalPhysicalSize = null;
95+
try {
96+
QemuImg qemu = new QemuImg(10000);
97+
QemuImgFile file = new QemuImgFile(volumePath);
98+
String info = qemu.infoBackingJson(file);
99+
if (StringUtils.isBlank(info)) {
100+
logger.debug("Failed to get qemu info for volume: {} as the output is empty");
101+
return null;
102+
}
103+
totalPhysicalSize = parseTotalActualSize(info, logger);
104+
if (totalPhysicalSize != null) {
105+
logger.trace("Total physical size for volume {} is: {}", volumeUuid, totalPhysicalSize);
106+
}
107+
} catch (LibvirtException | QemuImgException e) {
108+
logger.debug("Failed to get qemu info for volume: {} due to: {}", volumeUuid, e.getMessage());
109+
}
110+
return totalPhysicalSize;
111+
}
112+
113+
private static Long parseTotalActualSize(String json, Logger logger) {
114+
JsonElement root = JsonParser.parseString(json);
115+
116+
Long total = null;
117+
118+
if (root.isJsonArray()) {
119+
JsonArray arr = root.getAsJsonArray();
120+
for (JsonElement elem : arr) {
121+
JsonObject obj = elem.getAsJsonObject();
122+
if (!obj.has("actual-size")) {
123+
continue;
124+
}
125+
if (total == null) {
126+
total = 0L;
127+
}
128+
total += obj.get("actual-size").getAsLong();
129+
}
130+
} else if (root.isJsonObject()) {
131+
JsonObject obj = root.getAsJsonObject();
132+
if (obj.has("actual-size")) {
133+
total = obj.get("actual-size").getAsLong();
134+
}
135+
} else {
136+
logger.debug("Unexpected JSON format when parsing qemu info: {}", json);
137+
}
138+
139+
return total;
76140
}
77141
}

plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/qemu/QemuImg.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
// under the License.
1717
package org.apache.cloudstack.utils.qemu;
1818

19+
import static java.util.regex.Pattern.CASE_INSENSITIVE;
20+
1921
import java.nio.file.Files;
2022
import java.nio.file.Paths;
2123
import java.util.HashMap;
@@ -28,16 +30,14 @@
2830
import org.apache.commons.collections.MapUtils;
2931
import org.apache.commons.lang.NotImplementedException;
3032
import org.apache.commons.lang3.StringUtils;
33+
import org.apache.logging.log4j.LogManager;
34+
import org.apache.logging.log4j.Logger;
3135
import org.libvirt.LibvirtException;
3236

3337
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
3438
import com.cloud.storage.Storage;
3539
import com.cloud.utils.script.OutputInterpreter;
3640
import com.cloud.utils.script.Script;
37-
import org.apache.logging.log4j.Logger;
38-
import org.apache.logging.log4j.LogManager;
39-
40-
import static java.util.regex.Pattern.CASE_INSENSITIVE;
4141

4242
public class QemuImg {
4343
private Logger logger = LogManager.getLogger(this.getClass());
@@ -661,6 +661,24 @@ public Map<String, String> info(final QemuImgFile file, boolean secure) throws Q
661661
return info;
662662
}
663663

664+
public String infoBackingJson(final QemuImgFile file) throws QemuImgException {
665+
final Script s = new Script(_qemuImgPath);
666+
s.add("info");
667+
s.add("--backing-chain");
668+
s.add("--output=json");
669+
if (this.version >= QEMU_2_10) {
670+
s.add("-U");
671+
}
672+
s.add(file.getFileName());
673+
674+
final OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
675+
final String result = s.execute(parser);
676+
if (result != null) {
677+
throw new QemuImgException(result);
678+
}
679+
return parser.getLines().trim();
680+
}
681+
664682
/* create snapshots in image */
665683
public void snapshot(final QemuImageOptions srcImageOpts, final String snapshotName, final List<QemuObject> qemuObjects) throws QemuImgException {
666684
final Script s = new Script(_qemuImgPath, timeout);

server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5348,8 +5348,7 @@ private VmWorkJobVO createPlaceHolderWork(long instanceId) {
53485348
return workJob;
53495349
}
53505350

5351-
@Override
5352-
public Long getVolumePhysicalSize(ImageFormat format, String path, String chainInfo) {
5351+
protected VolumeStats getVolumeStats(ImageFormat format, String path, String chainInfo) {
53535352
VolumeStats vs = null;
53545353
if (format == ImageFormat.VHD || format == ImageFormat.QCOW2 || format == ImageFormat.RAW) {
53555354
if (path != null) {
@@ -5360,9 +5359,27 @@ public Long getVolumePhysicalSize(ImageFormat format, String path, String chainI
53605359
vs = statsCollector.getVolumeStats(chainInfo);
53615360
}
53625361
}
5362+
return vs;
5363+
}
5364+
5365+
@Override
5366+
public Long getVolumePhysicalSize(ImageFormat format, String path, String chainInfo) {
5367+
VolumeStats vs = getVolumeStats(format, path, chainInfo);
53635368
return (vs == null) ? null : vs.getPhysicalSize();
53645369
}
53655370

5371+
@Override
5372+
public Long getVolumeTotalPhysicalSize(ImageFormat format, String path, String chainInfo) {
5373+
VolumeStats vs = getVolumeStats(format, path, chainInfo);
5374+
if (vs == null) {
5375+
return null;
5376+
}
5377+
if (vs.getTotalPhysicalSize() == null) {
5378+
return vs.getPhysicalSize();
5379+
}
5380+
return vs.getTotalPhysicalSize();
5381+
}
5382+
53665383
@Override
53675384
public String getConfigComponentName() {
53685385
return VolumeApiService.class.getSimpleName();

0 commit comments

Comments
 (0)