diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartBackupCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartBackupCommandWrapper.java index 2e7c8c5ae98..cf599916d9b 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartBackupCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartBackupCommandWrapper.java @@ -19,12 +19,18 @@ package com.cloud.hypervisor.kvm.resource.wrapper; import java.io.File; import java.io.FileWriter; +import java.util.HashMap; import java.util.Map; import org.apache.cloudstack.backup.StartBackupAnswer; import org.apache.cloudstack.backup.StartBackupCommand; +import org.apache.cloudstack.utils.qemu.QemuCommand; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.json.JSONArray; +import org.json.JSONObject; +import org.libvirt.Domain; +import org.libvirt.LibvirtException; import com.cloud.agent.api.Answer; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; import com.cloud.resource.CommandWrapper; @@ -150,31 +156,47 @@ public class LibvirtStartBackupCommandWrapper extends CommandWrapper\n"); - if (StringUtils.isNotBlank(fromCheckpointId)) { - xml.append(" ").append(fromCheckpointId).append("\n"); - } - xml.append(String.format(" \n", socket)); xml.append(" \n"); Map diskPathUuidMap = cmd.getDiskPathUuidMap(); Map diskPathLabelMap = resource.getDiskPathLabelMap(cmd.getVmName()); + Map diskPathHasFromCheckpointMap = new HashMap<>(); + if (StringUtils.isNotBlank(fromCheckpointId)) { + Domain vm = null; + try { + vm = resource.getDomain(resource.getLibvirtUtilitiesHelper().getConnection(), cmd.getVmName()); + if (vm != null) { + diskPathHasFromCheckpointMap = getVmDiskPathHasFromCheckpointMap(vm, fromCheckpointId); + } else { + logger.warn("Failed to get domain for VM [{}] while evaluating export bitmap [{}]. Falling back to full Backup", + cmd.getVmName(), fromCheckpointId); + } + } finally { + if (vm != null) { + vm.free(); + } + } + } for (Map.Entry entry : diskPathLabelMap.entrySet()) { - if (!diskPathUuidMap.containsKey(entry.getKey())) { + String diskPath = entry.getKey(); + if (!diskPathUuidMap.containsKey(diskPath)) { continue; } String diskName = entry.getValue(); - String export = diskPathUuidMap.get(entry.getKey()); + String export = diskPathUuidMap.get(diskPath); String scratchFile = "/var/tmp/scratch-" + export + ".qcow2"; xml.append(" \n"); xml.append(" \n"); @@ -194,7 +216,6 @@ public class LibvirtStartBackupCommandWrapper extends CommandWrapper diskPathUuidMap = cmd.getDiskPathUuidMap(); for (Map.Entry entry : diskPathUuidMap.entrySet()) { String diskPath = entry.getKey(); @@ -218,4 +239,43 @@ public class LibvirtStartBackupCommandWrapper extends CommandWrapper getVmDiskPathHasFromCheckpointMap(Domain vm, String fromCheckpointId) throws LibvirtException { + Map diskPathHasFromCheckpointMap = new HashMap<>(); + String queryBlock = vm.qemuMonitorCommand(QemuCommand.buildQemuCommand("query-block", null), 0); + JSONObject response = new JSONObject(queryBlock); + JSONArray blocks = response.optJSONArray("return"); + if (blocks == null) { + logger.warn("Couldn't get bitmap information for the VM [{}]. Falling back to full Backup", vm.getName()); + return diskPathHasFromCheckpointMap; + } + for (int i = 0; i < blocks.length(); i++) { + JSONObject block = blocks.getJSONObject(i); + JSONObject inserted = block.optJSONObject("inserted"); + if (inserted == null) { + continue; + } + String file = inserted.optString("file"); + if (StringUtils.isBlank(file)) { + continue; + } + JSONArray dirtyBitmaps = inserted.optJSONArray("dirty-bitmaps"); + boolean hasFromCheckpointBitmap = false; + if (dirtyBitmaps != null) { + for (int j = 0; j < dirtyBitmaps.length(); j++) { + JSONObject dirtyBitmap = dirtyBitmaps.optJSONObject(j); + if (dirtyBitmap == null) { + continue; + } + String bitmapName = dirtyBitmap.optString("name"); + if (fromCheckpointId.equals(bitmapName)) { + hasFromCheckpointBitmap = true; + break; + } + } + } + diskPathHasFromCheckpointMap.put(file, hasFromCheckpointBitmap); + } + return diskPathHasFromCheckpointMap; + } } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartNBDServerCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartNBDServerCommandWrapper.java index 56d5945ced1..5318dc7b6dc 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartNBDServerCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtStartNBDServerCommandWrapper.java @@ -23,6 +23,8 @@ import org.apache.cloudstack.backup.StartNBDServerAnswer; import org.apache.cloudstack.backup.StartNBDServerCommand; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.json.JSONArray; +import org.json.JSONObject; import com.cloud.agent.api.Answer; import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource; @@ -68,6 +70,11 @@ public class LibvirtStartNBDServerCommandWrapper extends CommandWrapper