Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/src/main/java/com/cloud/storage/VolumeApiService.java
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,6 @@ Volume updateVolume(long volumeId, String path, String state, Long storageId,
Pair<String, String> checkAndRepairVolume(CheckAndRepairVolumeCmd cmd) throws ResourceAllocationException;

Long getVolumePhysicalSize(Storage.ImageFormat format, String path, String chainInfo);

Long getVolumeTotalPhysicalSize(Storage.ImageFormat format, String path, String chainInfo);
}
2 changes: 2 additions & 0 deletions api/src/main/java/com/cloud/storage/VolumeStats.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ public interface VolumeStats {
* @return bytes allocated
*/
long getPhysicalSize();

Long getTotalPhysicalSize();
}
18 changes: 18 additions & 0 deletions core/src/main/java/com/cloud/agent/api/VolumeStatsEntry.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class VolumeStatsEntry implements VolumeStats {
String volumeUuid;
long physicalsize = 0;
long virtualSize = 0;
Long totalPhysicalSize = null;

public VolumeStatsEntry(String volumeUuid, long physicalsize, long virtualSize) {
this.volumeUuid = volumeUuid;
Expand Down Expand Up @@ -56,6 +57,23 @@ public void setVirtualSize(long virtualSize) {
this.virtualSize = virtualSize;
}

public long getPhysicalsize() {
return physicalsize;
}

public void setPhysicalsize(long physicalsize) {
this.physicalsize = physicalsize;
}

@Override
public Long getTotalPhysicalSize() {
return totalPhysicalSize;
}

public void setTotalPhysicalSize(Long totalPhysicalSize) {
this.totalPhysicalSize = totalPhysicalSize;
}

@Override
public String toString() {
return "VolumeStatsEntry [volumeUuid=" + volumeUuid + ", size=" + physicalsize + ", virtualSize=" + virtualSize + "]";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,18 @@

import java.util.HashMap;

import org.apache.cloudstack.utils.qemu.QemuImg;
import org.apache.cloudstack.utils.qemu.QemuImgException;
import org.apache.cloudstack.utils.qemu.QemuImgFile;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Logger;
import org.libvirt.Connect;
import org.libvirt.LibvirtException;

import com.cloud.agent.api.Answer;
import com.cloud.agent.api.GetVolumeStatsAnswer;
import com.cloud.agent.api.GetVolumeStatsCommand;
import com.cloud.agent.api.VolumeStatsEntry;
import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
Expand All @@ -33,9 +41,10 @@
import com.cloud.resource.ResourceWrapper;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.agent.api.GetVolumeStatsAnswer;
import com.cloud.agent.api.GetVolumeStatsCommand;
import com.cloud.agent.api.VolumeStatsEntry;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

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

return new VolumeStatsEntry(volumeUuid, sourceKVMVolume.getSize(), sourceKVMVolume.getVirtualSize());
VolumeStatsEntry entry = new VolumeStatsEntry(volumeUuid, sourceKVMVolume.getSize(),
sourceKVMVolume.getVirtualSize());
entry.setTotalPhysicalSize(retrieveTotalPhysicalSize(volumeUuid, sourceKVMVolume.getPath(), logger));

return entry;
}


private static Long retrieveTotalPhysicalSize(String volumeUuid, String volumePath, Logger logger) {
logger.trace("Retrieving total physical size for volume: {} with path: {}", volumeUuid, volumePath);
Long totalPhysicalSize = null;
try {
QemuImg qemu = new QemuImg(10000);
QemuImgFile file = new QemuImgFile(volumePath);
String info = qemu.infoBackingJson(file);
if (StringUtils.isBlank(info)) {
logger.debug("Failed to get qemu info for volume: {} as the output is empty");
return null;
}
totalPhysicalSize = parseTotalActualSize(info, logger);
if (totalPhysicalSize != null) {
logger.trace("Total physical size for volume {} is: {}", volumeUuid, totalPhysicalSize);
}
} catch (LibvirtException | QemuImgException e) {
logger.debug("Failed to get qemu info for volume: {} due to: {}", volumeUuid, e.getMessage());
}
return totalPhysicalSize;
}

private static Long parseTotalActualSize(String json, Logger logger) {
JsonElement root = JsonParser.parseString(json);

Long total = null;

if (root.isJsonArray()) {
JsonArray arr = root.getAsJsonArray();
for (JsonElement elem : arr) {
JsonObject obj = elem.getAsJsonObject();
if (!obj.has("actual-size")) {
continue;
}
if (total == null) {
total = 0L;
}
total += obj.get("actual-size").getAsLong();
}
} else if (root.isJsonObject()) {
JsonObject obj = root.getAsJsonObject();
if (obj.has("actual-size")) {
total = obj.get("actual-size").getAsLong();
}
} else {
logger.debug("Unexpected JSON format when parsing qemu info: {}", json);
}

return total;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
// under the License.
package org.apache.cloudstack.utils.qemu;

import static java.util.regex.Pattern.CASE_INSENSITIVE;

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
Expand All @@ -28,16 +30,14 @@
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.libvirt.LibvirtException;

import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
import com.cloud.storage.Storage;
import com.cloud.utils.script.OutputInterpreter;
import com.cloud.utils.script.Script;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

import static java.util.regex.Pattern.CASE_INSENSITIVE;

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

public String infoBackingJson(final QemuImgFile file) throws QemuImgException {
final Script s = new Script(_qemuImgPath);
s.add("info");
s.add("--backing-chain");
s.add("--output=json");
if (this.version >= QEMU_2_10) {
s.add("-U");
}
s.add(file.getFileName());

final OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
final String result = s.execute(parser);
if (result != null) {
throw new QemuImgException(result);
}
return parser.getLines().trim();
}

/* create snapshots in image */
public void snapshot(final QemuImageOptions srcImageOpts, final String snapshotName, final List<QemuObject> qemuObjects) throws QemuImgException {
final Script s = new Script(_qemuImgPath, timeout);
Expand Down
21 changes: 19 additions & 2 deletions server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -5348,8 +5348,7 @@ private VmWorkJobVO createPlaceHolderWork(long instanceId) {
return workJob;
}

@Override
public Long getVolumePhysicalSize(ImageFormat format, String path, String chainInfo) {
protected VolumeStats getVolumeStats(ImageFormat format, String path, String chainInfo) {
VolumeStats vs = null;
if (format == ImageFormat.VHD || format == ImageFormat.QCOW2 || format == ImageFormat.RAW) {
if (path != null) {
Expand All @@ -5360,9 +5359,27 @@ public Long getVolumePhysicalSize(ImageFormat format, String path, String chainI
vs = statsCollector.getVolumeStats(chainInfo);
}
}
return vs;
}

@Override
public Long getVolumePhysicalSize(ImageFormat format, String path, String chainInfo) {
VolumeStats vs = getVolumeStats(format, path, chainInfo);
return (vs == null) ? null : vs.getPhysicalSize();
}

@Override
public Long getVolumeTotalPhysicalSize(ImageFormat format, String path, String chainInfo) {
VolumeStats vs = getVolumeStats(format, path, chainInfo);
if (vs == null) {
return null;
}
if (vs.getTotalPhysicalSize() == null) {
return vs.getPhysicalSize();
}
return vs.getTotalPhysicalSize();
}

@Override
public String getConfigComponentName() {
return VolumeApiService.class.getSimpleName();
Expand Down
Loading