[PATCH 00/11] qemu: switch to modern QEMU's snapshot-* API for

This series applied on top of [1]. In this series I still use old API to load snapshot if snapshot was done using old API. The thing is snapshot-load require "vmstate" parameter to be set. And with old snapshot the disk which holds vmstate is not known to libvirt. This is unfortunate as we will need to use old API for a long time even after we drop support for QEMU older then 6.0 (where snapshot-* API is introduced). I guess the best will be to allow omiting "vmstate" for snapshot-load and thus falling back to default disk for vmstate. The code for recovering on libvirtd crash/restart is missing. I'll work on it too if the overall approach is accepted. [PATCH v2 REBASE 0/3] qemu: don't pause VM when creating internal snapshot https://listman.redhat.com/archives/libvir-list/2022-March/229719.html Nikolay Shirokovskiy (11): qemu: add QEMU_CAPS_SNAPSHOT_SAVE capability qemu: add monitor commands to snapshot-{save, load, delete} qemu: move snapshot related funcs from domain.c to snapshot.c qemu: introduce QEMU_ASYNC_JOB_SNAPSHOT_DELETE qemu: snapshot: prepare for async snapshot deletion conf: add memory state disk for internal snapshots qemu: check internal snaphshot memory disk qemu: add snapshot job types to qemuMonitorJobType qemu: use snapshot-save for modern qemu to create snapshot qemu: use snapshot-load for modern QEMU to revert to snapshot qemu: use snapshot-delete for modern QEMU to delete snapshot src/conf/snapshot_conf.c | 13 + src/conf/snapshot_conf.h | 1 + src/hypervisor/domain_job.c | 1 + src/hypervisor/domain_job.h | 1 + src/qemu/qemu_capabilities.c | 2 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_domain.c | 241 +------- src/qemu/qemu_domain.h | 19 - src/qemu/qemu_domainjob.c | 2 + src/qemu/qemu_driver.c | 14 +- src/qemu/qemu_migration.c | 2 + src/qemu/qemu_monitor.c | 56 ++ src/qemu/qemu_monitor.h | 26 + src/qemu/qemu_monitor_json.c | 106 ++++ src/qemu/qemu_monitor_json.h | 23 + src/qemu/qemu_process.c | 22 +- src/qemu/qemu_snapshot.c | 583 +++++++++++++++++- src/qemu/qemu_snapshot.h | 10 + .../caps_6.0.0.aarch64.xml | 1 + .../qemucapabilitiesdata/caps_6.0.0.s390x.xml | 1 + .../caps_6.0.0.x86_64.xml | 1 + .../caps_6.1.0.x86_64.xml | 1 + .../caps_6.2.0.aarch64.xml | 1 + .../qemucapabilitiesdata/caps_6.2.0.ppc64.xml | 1 + .../caps_6.2.0.x86_64.xml | 1 + .../qemucapabilitiesdata/caps_7.0.0.ppc64.xml | 1 + .../caps_7.0.0.x86_64.xml | 1 + 27 files changed, 844 insertions(+), 288 deletions(-) -- 2.35.1

If it is present then QEMU supports snaphot-{save, load, delete} set of QMP commands. Signed-off-by: Nikolay Shirokovskiy <nikolay.shirokovskiy@openvz.org> --- src/qemu/qemu_capabilities.c | 2 ++ src/qemu/qemu_capabilities.h | 1 + tests/qemucapabilitiesdata/caps_6.0.0.aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_6.0.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_6.1.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_6.2.0.aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_6.2.0.ppc64.xml | 1 + tests/qemucapabilitiesdata/caps_6.2.0.x86_64.xml | 1 + tests/qemucapabilitiesdata/caps_7.0.0.ppc64.xml | 1 + tests/qemucapabilitiesdata/caps_7.0.0.x86_64.xml | 1 + 11 files changed, 12 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 6b4ed08499..3acc735e99 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -669,6 +669,7 @@ VIR_ENUM_IMPL(virQEMUCaps, /* 425 */ "blockdev.nbd.tls-hostname", /* QEMU_CAPS_BLOCKDEV_NBD_TLS_HOSTNAME */ "memory-backend-file.prealloc-threads", /* QEMU_CAPS_MEMORY_BACKEND_PREALLOC_THREADS */ + "snapshot-save", /* QEMU_CAPS_SNAPSHOT_SAVE */ ); @@ -1236,6 +1237,7 @@ struct virQEMUCapsStringFlags virQEMUCapsCommands[] = { { "query-dirty-rate", QEMU_CAPS_QUERY_DIRTY_RATE }, { "sev-inject-launch-secret", QEMU_CAPS_SEV_INJECT_LAUNCH_SECRET }, { "calc-dirty-rate", QEMU_CAPS_CALC_DIRTY_RATE }, + { "snapshot-save", QEMU_CAPS_SNAPSHOT_SAVE }, }; struct virQEMUCapsStringFlags virQEMUCapsMigration[] = { diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 948029d60d..ff5301a1b4 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -644,6 +644,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ /* 425 */ QEMU_CAPS_BLOCKDEV_NBD_TLS_HOSTNAME, /* tls hostname can be overriden for NBD clients */ QEMU_CAPS_MEMORY_BACKEND_PREALLOC_THREADS, /* -object memory-backend-*.prealloc-threads */ + QEMU_CAPS_SNAPSHOT_SAVE, /* supports snapshot-save qmp command */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/tests/qemucapabilitiesdata/caps_6.0.0.aarch64.xml b/tests/qemucapabilitiesdata/caps_6.0.0.aarch64.xml index 7557e6ad71..7d1de6c1f7 100644 --- a/tests/qemucapabilitiesdata/caps_6.0.0.aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_6.0.0.aarch64.xml @@ -193,6 +193,7 @@ <flag name='query-dirty-rate'/> <flag name='calc-dirty-rate'/> <flag name='memory-backend-file.prealloc-threads'/> + <flag name='snapshot-save'/> <version>6000000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>61700242</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_6.0.0.s390x.xml b/tests/qemucapabilitiesdata/caps_6.0.0.s390x.xml index 00009be3dc..7e52c2bad5 100644 --- a/tests/qemucapabilitiesdata/caps_6.0.0.s390x.xml +++ b/tests/qemucapabilitiesdata/caps_6.0.0.s390x.xml @@ -150,6 +150,7 @@ <flag name='query-dirty-rate'/> <flag name='calc-dirty-rate'/> <flag name='memory-backend-file.prealloc-threads'/> + <flag name='snapshot-save'/> <version>6000000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>39100242</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml index 61d561dc69..e3ea706b2c 100644 --- a/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_6.0.0.x86_64.xml @@ -236,6 +236,7 @@ <flag name='sev-inject-launch-secret'/> <flag name='calc-dirty-rate'/> <flag name='memory-backend-file.prealloc-threads'/> + <flag name='snapshot-save'/> <version>6000000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>43100242</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_6.1.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_6.1.0.x86_64.xml index 0b58210335..2167bfd5ed 100644 --- a/tests/qemucapabilitiesdata/caps_6.1.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_6.1.0.x86_64.xml @@ -240,6 +240,7 @@ <flag name='sev-inject-launch-secret'/> <flag name='calc-dirty-rate'/> <flag name='memory-backend-file.prealloc-threads'/> + <flag name='snapshot-save'/> <version>6001000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>43100243</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_6.2.0.aarch64.xml b/tests/qemucapabilitiesdata/caps_6.2.0.aarch64.xml index d08b2c0213..e2778e6d34 100644 --- a/tests/qemucapabilitiesdata/caps_6.2.0.aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_6.2.0.aarch64.xml @@ -204,6 +204,7 @@ <flag name='rbd-encryption'/> <flag name='calc-dirty-rate'/> <flag name='memory-backend-file.prealloc-threads'/> + <flag name='snapshot-save'/> <version>6001050</version> <kvmVersion>0</kvmVersion> <microcodeVersion>61700244</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_6.2.0.ppc64.xml b/tests/qemucapabilitiesdata/caps_6.2.0.ppc64.xml index 8c52964ec0..7ffdd4d5fa 100644 --- a/tests/qemucapabilitiesdata/caps_6.2.0.ppc64.xml +++ b/tests/qemucapabilitiesdata/caps_6.2.0.ppc64.xml @@ -202,6 +202,7 @@ <flag name='calc-dirty-rate'/> <flag name='dirtyrate-param.mode'/> <flag name='memory-backend-file.prealloc-threads'/> + <flag name='snapshot-save'/> <version>6002000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>42900244</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_6.2.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_6.2.0.x86_64.xml index cdf72b9ebf..ae0fb90df9 100644 --- a/tests/qemucapabilitiesdata/caps_6.2.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_6.2.0.x86_64.xml @@ -242,6 +242,7 @@ <flag name='calc-dirty-rate'/> <flag name='dirtyrate-param.mode'/> <flag name='memory-backend-file.prealloc-threads'/> + <flag name='snapshot-save'/> <version>6002000</version> <kvmVersion>0</kvmVersion> <microcodeVersion>43100244</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_7.0.0.ppc64.xml b/tests/qemucapabilitiesdata/caps_7.0.0.ppc64.xml index 8aba3329ad..e19eef6f8e 100644 --- a/tests/qemucapabilitiesdata/caps_7.0.0.ppc64.xml +++ b/tests/qemucapabilitiesdata/caps_7.0.0.ppc64.xml @@ -203,6 +203,7 @@ <flag name='calc-dirty-rate'/> <flag name='dirtyrate-param.mode'/> <flag name='memory-backend-file.prealloc-threads'/> + <flag name='snapshot-save'/> <version>6002050</version> <kvmVersion>0</kvmVersion> <microcodeVersion>42900243</microcodeVersion> diff --git a/tests/qemucapabilitiesdata/caps_7.0.0.x86_64.xml b/tests/qemucapabilitiesdata/caps_7.0.0.x86_64.xml index 5227e3ee0b..5c97fe71b8 100644 --- a/tests/qemucapabilitiesdata/caps_7.0.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_7.0.0.x86_64.xml @@ -245,6 +245,7 @@ <flag name='dirtyrate-param.mode'/> <flag name='blockdev.nbd.tls-hostname'/> <flag name='memory-backend-file.prealloc-threads'/> + <flag name='snapshot-save'/> <version>6002050</version> <kvmVersion>0</kvmVersion> <microcodeVersion>43100243</microcodeVersion> -- 2.35.1

Signed-off-by: Nikolay Shirokovskiy <nikolay.shirokovskiy@openvz.org> --- src/qemu/qemu_monitor.c | 53 ++++++++++++++++++ src/qemu/qemu_monitor.h | 23 ++++++++ src/qemu/qemu_monitor_json.c | 106 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 23 ++++++++ 4 files changed, 205 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 316cff5b9b..4b33407e50 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4566,3 +4566,56 @@ qemuMonitorChangeMemoryRequestedSize(qemuMonitor *mon, return qemuMonitorJSONChangeMemoryRequestedSize(mon, alias, requestedsize); } + + +int +qemuMonitorSnapshotSave(qemuMonitor *mon, + const char *jobname, + const char *snapid, + const char *vmstateDevice, + const char **devices, + size_t ndevices) +{ + VIR_DEBUG("jobname=%s, snapid=%s, vmstateDevice=%s, devices=%p, ndevices=%zd", + jobname, snapid, vmstateDevice, devices, ndevices); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONSnapshotSave(mon, jobname, snapid, vmstateDevice, + devices, ndevices); +} + + +int +qemuMonitorSnapshotLoad(qemuMonitor *mon, + const char *jobname, + const char *snapid, + const char *vmstateDevice, + const char **devices, + size_t ndevices) +{ + VIR_DEBUG("jobname=%s, snapid=%s, vmstateDevice=%s, devices=%p, ndevices=%zd", + jobname, snapid, vmstateDevice, devices, ndevices); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONSnapshotLoad(mon, jobname, snapid, vmstateDevice, + devices, ndevices); +} + + +int +qemuMonitorSnapshotDelete(qemuMonitor *mon, + const char *jobname, + const char *snapid, + const char **devices, + size_t ndevices) +{ + VIR_DEBUG("jobname=%s, snapid=%s, devices=%p, ndevices=%zd", + jobname, snapid, devices, ndevices); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONSnapshotDelete(mon, jobname, snapid, + devices, ndevices); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 5c2a749282..8067236693 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1588,3 +1588,26 @@ int qemuMonitorChangeMemoryRequestedSize(qemuMonitor *mon, const char *alias, unsigned long long requestedsize); + +int +qemuMonitorSnapshotSave(qemuMonitor *mon, + const char *jobname, + const char *snapid, + const char *vmstateDevice, + const char **devices, + size_t ndevices); + +int +qemuMonitorSnapshotLoad(qemuMonitor *mon, + const char *jobname, + const char *snapid, + const char *vmstateDevice, + const char **devices, + size_t ndevices); + +int +qemuMonitorSnapshotDelete(qemuMonitor *mon, + const char *jobname, + const char *snapid, + const char **devices, + size_t ndevices); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index d5622bd6d9..864be427c3 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -8966,3 +8966,109 @@ qemuMonitorJSONChangeMemoryRequestedSize(qemuMonitor *mon, return qemuMonitorJSONSetObjectProperty(mon, path, "requested-size", &prop); } + + +int +qemuMonitorJSONSnapshotSave(qemuMonitor *mon, + const char *jobname, + const char *snapid, + const char *vmstateDevice, + const char **devices, + size_t ndevices) +{ + g_autoptr(virJSONValue) devicesJSON = virJSONValueNewArray(); + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + size_t i; + + for (i = 0; i < ndevices; i++) + if (virJSONValueArrayAppendString(devicesJSON, devices[i]) < 0) + return -1; + + cmd = qemuMonitorJSONMakeCommand("snapshot-save", + "s:job-id", jobname, + "s:tag", snapid, + "s:vmstate", vmstateDevice, + "a:devices", &devicesJSON, + NULL); + if (!cmd) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + return -1; + + return 0; +} + + +int +qemuMonitorJSONSnapshotLoad(qemuMonitor *mon, + const char *jobname, + const char *snapid, + const char *vmstateDevice, + const char **devices, + size_t ndevices) +{ + g_autoptr(virJSONValue) devicesJSON = virJSONValueNewArray(); + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + size_t i; + + for (i = 0; i < ndevices; i++) + if (virJSONValueArrayAppendString(devicesJSON, devices[i]) < 0) + return -1; + + cmd = qemuMonitorJSONMakeCommand("snapshot-load", + "s:job-id", jobname, + "s:tag", snapid, + "s:vmstate", vmstateDevice, + "a:devices", &devicesJSON, + NULL); + if (!cmd) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + return -1; + + return 0; +} + + +int +qemuMonitorJSONSnapshotDelete(qemuMonitor *mon, + const char *jobname, + const char *snapid, + const char **devices, + size_t ndevices) +{ + g_autoptr(virJSONValue) devicesJSON = virJSONValueNewArray(); + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + size_t i; + + for (i = 0; i < ndevices; i++) + if (virJSONValueArrayAppendString(devicesJSON, devices[i]) < 0) + return -1; + + cmd = qemuMonitorJSONMakeCommand("snapshot-delete", + "s:job-id", jobname, + "s:tag", snapid, + "a:devices", &devicesJSON, + NULL); + if (!cmd) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + return -1; + + return 0; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 982fbad44e..73849c4a4b 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -873,3 +873,26 @@ int qemuMonitorJSONChangeMemoryRequestedSize(qemuMonitor *mon, const char *alias, unsigned long long requestedsize); + +int +qemuMonitorJSONSnapshotSave(qemuMonitor *mon, + const char *jobname, + const char *snapid, + const char *vmstateDevice, + const char **devices, + size_t ndevices); + +int +qemuMonitorJSONSnapshotLoad(qemuMonitor *mon, + const char *jobname, + const char *snapid, + const char *vmstateDevice, + const char **devices, + size_t ndevices); + +int +qemuMonitorJSONSnapshotDelete(qemuMonitor *mon, + const char *jobname, + const char *snapid, + const char **devices, + size_t ndevices); -- 2.35.1

These functions mostly used from the qemu_snapshot.c and also semantically they belong to this source file. At the same time rename qemuDomainSnapshot* to qemuSnaphot*. Signed-off-by: Nikolay Shirokovskiy <nikolay.shirokovskiy@openvz.org> --- src/qemu/qemu_domain.c | 241 +---------------------------------- src/qemu/qemu_domain.h | 19 --- src/qemu/qemu_driver.c | 8 +- src/qemu/qemu_snapshot.c | 268 ++++++++++++++++++++++++++++++++++++--- src/qemu/qemu_snapshot.h | 10 ++ 5 files changed, 269 insertions(+), 277 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 18d403e099..56cf0c3f68 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -38,6 +38,7 @@ #include "qemu_checkpoint.h" #include "qemu_validate.h" #include "qemu_namespace.h" +#include "qemu_snapshot.h" #include "viralloc.h" #include "virlog.h" #include "virerror.h" @@ -64,7 +65,6 @@ #include "virsecret.h" #include "logging/log_manager.h" #include "locking/domain_lock.h" -#include "virdomainsnapshotobjlist.h" #include "virdomaincheckpointobjlist.h" #include "backup_conf.h" #include "virutil.h" @@ -7041,243 +7041,6 @@ qemuFindQemuImgBinary(virQEMUDriver *driver) return driver->qemuImgBinary; } -int -qemuDomainSnapshotWriteMetadata(virDomainObj *vm, - virDomainMomentObj *snapshot, - virDomainXMLOption *xmlopt, - const char *snapshotDir) -{ - g_autofree char *newxml = NULL; - g_autofree char *snapDir = NULL; - g_autofree char *snapFile = NULL; - char uuidstr[VIR_UUID_STRING_BUFLEN]; - unsigned int flags = VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE | - VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL; - virDomainSnapshotDef *def = virDomainSnapshotObjGetDef(snapshot); - - if (virDomainSnapshotGetCurrent(vm->snapshots) == snapshot) - flags |= VIR_DOMAIN_SNAPSHOT_FORMAT_CURRENT; - virUUIDFormat(vm->def->uuid, uuidstr); - newxml = virDomainSnapshotDefFormat(uuidstr, def, xmlopt, flags); - if (newxml == NULL) - return -1; - - snapDir = g_strdup_printf("%s/%s", snapshotDir, vm->def->name); - if (g_mkdir_with_parents(snapDir, 0777) < 0) { - virReportSystemError(errno, _("cannot create snapshot directory '%s'"), - snapDir); - return -1; - } - - snapFile = g_strdup_printf("%s/%s.xml", snapDir, def->parent.name); - - return virXMLSaveFile(snapFile, NULL, "snapshot-edit", newxml); -} - - -/* The domain is expected to be locked and inactive. Return -1 on normal - * failure, 1 if we skipped a disk due to try_all. */ -static int -qemuDomainSnapshotForEachQcow2Raw(virQEMUDriver *driver, - virDomainDef *def, - virDomainMomentObj *snap, - const char *op, - bool try_all, - int ndisks) -{ - virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap); - const char *qemuimgbin; - size_t i; - bool skipped = false; - - qemuimgbin = qemuFindQemuImgBinary(driver); - if (qemuimgbin == NULL) { - /* qemuFindQemuImgBinary set the error */ - return -1; - } - - for (i = 0; i < ndisks; i++) { - g_autoptr(virCommand) cmd = virCommandNewArgList(qemuimgbin, "snapshot", - op, snap->def->name, NULL); - int format = virDomainDiskGetFormat(def->disks[i]); - - /* FIXME: we also need to handle LVM here */ - if (def->disks[i]->device != VIR_DOMAIN_DISK_DEVICE_DISK || - snapdef->disks[i].snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL) - continue; - - if (!virStorageSourceIsLocalStorage(def->disks[i]->src)) { - virReportError(VIR_ERR_OPERATION_INVALID, - _("can't manipulate inactive snapshots of disk '%s'"), - def->disks[i]->dst); - return -1; - } - - if (format > 0 && format != VIR_STORAGE_FILE_QCOW2) { - if (try_all) { - /* Continue on even in the face of error, since other - * disks in this VM may have the same snapshot name. - */ - VIR_WARN("skipping snapshot action on %s", - def->disks[i]->dst); - skipped = true; - continue; - } else if (STREQ(op, "-c") && i) { - /* We must roll back partial creation by deleting - * all earlier snapshots. */ - qemuDomainSnapshotForEachQcow2Raw(driver, def, snap, - "-d", false, i); - } - virReportError(VIR_ERR_OPERATION_INVALID, - _("Disk device '%s' does not support snapshotting"), - def->disks[i]->dst); - return -1; - } - - virCommandAddArg(cmd, virDomainDiskGetSource(def->disks[i])); - - if (virCommandRun(cmd, NULL) < 0) { - if (try_all) { - VIR_WARN("skipping snapshot action on %s", - def->disks[i]->dst); - skipped = true; - continue; - } else if (STREQ(op, "-c") && i) { - /* We must roll back partial creation by deleting - * all earlier snapshots. */ - qemuDomainSnapshotForEachQcow2Raw(driver, def, snap, - "-d", false, i); - } - return -1; - } - } - - return skipped ? 1 : 0; -} - -/* The domain is expected to be locked and inactive. Return -1 on normal - * failure, 1 if we skipped a disk due to try_all. */ -int -qemuDomainSnapshotForEachQcow2(virQEMUDriver *driver, - virDomainDef *def, - virDomainMomentObj *snap, - const char *op, - bool try_all) -{ - return qemuDomainSnapshotForEachQcow2Raw(driver, def, snap, - op, try_all, def->ndisks); -} - -/* Discard one snapshot (or its metadata), without reparenting any children. */ -int -qemuDomainSnapshotDiscard(virQEMUDriver *driver, - virDomainObj *vm, - virDomainMomentObj *snap, - bool update_parent, - bool metadata_only) -{ - g_autofree char *snapFile = NULL; - qemuDomainObjPrivate *priv; - virDomainMomentObj *parentsnap = NULL; - g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); - - if (!metadata_only) { - if (!virDomainObjIsActive(vm)) { - size_t i; - /* Ignore any skipped disks */ - - /* Prefer action on the disks in use at the time the snapshot was - * created; but fall back to current definition if dealing with a - * snapshot created prior to libvirt 0.9.5. */ - virDomainDef *def = snap->def->dom; - - if (!def) - def = vm->def; - - for (i = 0; i < def->ndisks; i++) { - if (virDomainDiskTranslateSourcePool(def->disks[i]) < 0) - return -1; - } - - if (qemuDomainSnapshotForEachQcow2(driver, def, snap, "-d", true) < 0) - return -1; - } else { - priv = vm->privateData; - qemuDomainObjEnterMonitor(driver, vm); - /* we continue on even in the face of error */ - qemuMonitorDeleteSnapshot(priv->mon, snap->def->name); - qemuDomainObjExitMonitor(vm); - } - } - - snapFile = g_strdup_printf("%s/%s/%s.xml", cfg->snapshotDir, vm->def->name, - snap->def->name); - - if (snap == virDomainSnapshotGetCurrent(vm->snapshots)) { - virDomainSnapshotSetCurrent(vm->snapshots, NULL); - if (update_parent && snap->def->parent_name) { - parentsnap = virDomainSnapshotFindByName(vm->snapshots, - snap->def->parent_name); - if (!parentsnap) { - VIR_WARN("missing parent snapshot matching name '%s'", - snap->def->parent_name); - } else { - virDomainSnapshotSetCurrent(vm->snapshots, parentsnap); - if (qemuDomainSnapshotWriteMetadata(vm, parentsnap, - driver->xmlopt, - cfg->snapshotDir) < 0) { - VIR_WARN("failed to set parent snapshot '%s' as current", - snap->def->parent_name); - virDomainSnapshotSetCurrent(vm->snapshots, NULL); - } - } - } - } - - if (unlink(snapFile) < 0) - VIR_WARN("Failed to unlink %s", snapFile); - if (update_parent) - virDomainMomentDropParent(snap); - virDomainSnapshotObjListRemove(vm->snapshots, snap); - - return 0; -} - -/* Hash iterator callback to discard multiple snapshots. */ -int qemuDomainMomentDiscardAll(void *payload, - const char *name G_GNUC_UNUSED, - void *data) -{ - virDomainMomentObj *moment = payload; - virQEMUMomentRemove *curr = data; - int err; - - if (!curr->found && curr->current == moment) - curr->found = true; - err = curr->momentDiscard(curr->driver, curr->vm, moment, false, - curr->metadata_only); - if (err && !curr->err) - curr->err = err; - return 0; -} - -int -qemuDomainSnapshotDiscardAllMetadata(virQEMUDriver *driver, - virDomainObj *vm) -{ - virQEMUMomentRemove rem = { - .driver = driver, - .vm = vm, - .metadata_only = true, - .momentDiscard = qemuDomainSnapshotDiscard, - }; - - virDomainSnapshotForEach(vm->snapshots, qemuDomainMomentDiscardAll, &rem); - virDomainSnapshotObjListRemoveAll(vm->snapshots); - - return rem.err; -} - static void qemuDomainRemoveInactiveCommon(virQEMUDriver *driver, @@ -7288,7 +7051,7 @@ qemuDomainRemoveInactiveCommon(virQEMUDriver *driver, g_autofree char *chkDir = NULL; /* Remove any snapshot metadata prior to removing the domain */ - if (qemuDomainSnapshotDiscardAllMetadata(driver, vm) < 0) { + if (qemuSnapshotDiscardAllMetadata(driver, vm) < 0) { VIR_WARN("unable to remove all snapshots for domain %s", vm->def->name); } else { diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index a41d8308e3..494a276212 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -633,22 +633,6 @@ int qemuDomainLogAppendMessage(virQEMUDriver *driver, const char *qemuFindQemuImgBinary(virQEMUDriver *driver); -int qemuDomainSnapshotWriteMetadata(virDomainObj *vm, - virDomainMomentObj *snapshot, - virDomainXMLOption *xmlopt, - const char *snapshotDir); - -int qemuDomainSnapshotForEachQcow2(virQEMUDriver *driver, - virDomainDef *def, - virDomainMomentObj *snap, - const char *op, - bool try_all); - -int qemuDomainSnapshotDiscard(virQEMUDriver *driver, - virDomainObj *vm, - virDomainMomentObj *snap, - bool update_current, - bool metadata_only); typedef struct _virQEMUMomentRemove virQEMUMomentRemove; struct _virQEMUMomentRemove { @@ -666,9 +650,6 @@ int qemuDomainMomentDiscardAll(void *payload, const char *name, void *data); -int qemuDomainSnapshotDiscardAllMetadata(virQEMUDriver *driver, - virDomainObj *vm); - void qemuDomainRemoveInactive(virQEMUDriver *driver, virDomainObj *vm); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 77012eb527..74bc2c7bf4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -6568,7 +6568,7 @@ qemuDomainUndefineFlags(virDomainPtr dom, nsnapshots); goto endjob; } - if (qemuDomainSnapshotDiscardAllMetadata(driver, vm) < 0) + if (qemuSnapshotDiscardAllMetadata(driver, vm) < 0) goto endjob; } if (!virDomainObjIsActive(vm) && @@ -19132,9 +19132,9 @@ qemuDomainSnapshotWriteMetadataIter(void *payload, virQEMUDriverConfig *cfg = virQEMUDriverGetConfig(data->driver); int ret; - ret = qemuDomainSnapshotWriteMetadata(data->vm, payload, - data->driver->xmlopt, - cfg->snapshotDir); + ret = qemuSnapshotWriteMetadata(data->vm, payload, + data->driver->xmlopt, + cfg->snapshotDir); virObjectUnref(cfg); return ret; diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index 5d622592c3..878a0abb34 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -49,6 +49,175 @@ VIR_LOG_INIT("qemu.qemu_snapshot"); +/* The domain is expected to be locked and inactive. Return -1 on normal + * failure, 1 if we skipped a disk due to try_all. */ +static int +qemuSnapshotForEachQcow2Raw(virQEMUDriver *driver, + virDomainDef *def, + virDomainMomentObj *snap, + const char *op, + bool try_all, + int ndisks) +{ + virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap); + const char *qemuimgbin; + size_t i; + bool skipped = false; + + qemuimgbin = qemuFindQemuImgBinary(driver); + if (qemuimgbin == NULL) { + /* qemuFindQemuImgBinary set the error */ + return -1; + } + + for (i = 0; i < ndisks; i++) { + g_autoptr(virCommand) cmd = virCommandNewArgList(qemuimgbin, "snapshot", + op, snap->def->name, NULL); + int format = virDomainDiskGetFormat(def->disks[i]); + + /* FIXME: we also need to handle LVM here */ + if (def->disks[i]->device != VIR_DOMAIN_DISK_DEVICE_DISK || + snapdef->disks[i].snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL) + continue; + + if (!virStorageSourceIsLocalStorage(def->disks[i]->src)) { + virReportError(VIR_ERR_OPERATION_INVALID, + _("can't manipulate inactive snapshots of disk '%s'"), + def->disks[i]->dst); + return -1; + } + + if (format > 0 && format != VIR_STORAGE_FILE_QCOW2) { + if (try_all) { + /* Continue on even in the face of error, since other + * disks in this VM may have the same snapshot name. + */ + VIR_WARN("skipping snapshot action on %s", + def->disks[i]->dst); + skipped = true; + continue; + } else if (STREQ(op, "-c") && i) { + /* We must roll back partial creation by deleting + * all earlier snapshots. */ + qemuSnapshotForEachQcow2Raw(driver, def, snap, "-d", false, i); + } + virReportError(VIR_ERR_OPERATION_INVALID, + _("Disk device '%s' does not support snapshotting"), + def->disks[i]->dst); + return -1; + } + + virCommandAddArg(cmd, virDomainDiskGetSource(def->disks[i])); + + if (virCommandRun(cmd, NULL) < 0) { + if (try_all) { + VIR_WARN("skipping snapshot action on %s", + def->disks[i]->dst); + skipped = true; + continue; + } else if (STREQ(op, "-c") && i) { + /* We must roll back partial creation by deleting + * all earlier snapshots. */ + qemuSnapshotForEachQcow2Raw(driver, def, snap, "-d", false, i); + } + return -1; + } + } + + return skipped ? 1 : 0; +} + + +/* The domain is expected to be locked and inactive. Return -1 on normal + * failure, 1 if we skipped a disk due to try_all. */ +static int +qemuSnapshotForEachQcow2(virQEMUDriver *driver, + virDomainDef *def, + virDomainMomentObj *snap, + const char *op, + bool try_all) +{ + return qemuSnapshotForEachQcow2Raw(driver, def, snap, + op, try_all, def->ndisks); +} + + +/* Discard one snapshot (or its metadata), without reparenting any children. */ +static int +qemuSnapshotDiscard(virQEMUDriver *driver, + virDomainObj *vm, + virDomainMomentObj *snap, + bool update_parent, + bool metadata_only) +{ + g_autofree char *snapFile = NULL; + qemuDomainObjPrivate *priv; + virDomainMomentObj *parentsnap = NULL; + g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); + + if (!metadata_only) { + if (!virDomainObjIsActive(vm)) { + size_t i; + /* Ignore any skipped disks */ + + /* Prefer action on the disks in use at the time the snapshot was + * created; but fall back to current definition if dealing with a + * snapshot created prior to libvirt 0.9.5. */ + virDomainDef *def = snap->def->dom; + + if (!def) + def = vm->def; + + for (i = 0; i < def->ndisks; i++) { + if (virDomainDiskTranslateSourcePool(def->disks[i]) < 0) + return -1; + } + + if (qemuSnapshotForEachQcow2(driver, def, snap, "-d", true) < 0) + return -1; + } else { + priv = vm->privateData; + qemuDomainObjEnterMonitor(driver, vm); + /* we continue on even in the face of error */ + qemuMonitorDeleteSnapshot(priv->mon, snap->def->name); + qemuDomainObjExitMonitor(vm); + } + } + + snapFile = g_strdup_printf("%s/%s/%s.xml", cfg->snapshotDir, vm->def->name, + snap->def->name); + + if (snap == virDomainSnapshotGetCurrent(vm->snapshots)) { + virDomainSnapshotSetCurrent(vm->snapshots, NULL); + if (update_parent && snap->def->parent_name) { + parentsnap = virDomainSnapshotFindByName(vm->snapshots, + snap->def->parent_name); + if (!parentsnap) { + VIR_WARN("missing parent snapshot matching name '%s'", + snap->def->parent_name); + } else { + virDomainSnapshotSetCurrent(vm->snapshots, parentsnap); + if (qemuSnapshotWriteMetadata(vm, parentsnap, + driver->xmlopt, + cfg->snapshotDir) < 0) { + VIR_WARN("failed to set parent snapshot '%s' as current", + snap->def->parent_name); + virDomainSnapshotSetCurrent(vm->snapshots, NULL); + } + } + } + } + + if (unlink(snapFile) < 0) + VIR_WARN("Failed to unlink %s", snapFile); + if (update_parent) + virDomainMomentDropParent(snap); + virDomainSnapshotObjListRemove(vm->snapshots, snap); + + return 0; +} + + /** * qemuSnapshotSetCurrent: Set currently active snapshot * @@ -73,7 +242,7 @@ qemuSnapshotSetCurrent(virDomainObj *vm, * 'active' property */ if (oldcurrent && oldcurrent != newcurrent) { - if (qemuDomainSnapshotWriteMetadata(vm, oldcurrent, driver->xmlopt, cfg->snapshotDir) < 0) + if (qemuSnapshotWriteMetadata(vm, oldcurrent, driver->xmlopt, cfg->snapshotDir) < 0) VIR_WARN("failed to update old current snapshot"); } } @@ -167,7 +336,7 @@ qemuSnapshotCreateInactiveInternal(virQEMUDriver *driver, virDomainObj *vm, virDomainMomentObj *snap) { - return qemuDomainSnapshotForEachQcow2(driver, vm->def, snap, "-c", false); + return qemuSnapshotForEachQcow2(driver, vm->def, snap, "-c", false); } @@ -1691,9 +1860,8 @@ qemuSnapshotCreateWriteMetadata(virDomainObj *vm, virQEMUDriver *driver, virQEMUDriverConfig *cfg) { - if (qemuDomainSnapshotWriteMetadata(vm, snap, - driver->xmlopt, - cfg->snapshotDir) < 0) { + if (qemuSnapshotWriteMetadata(vm, snap, driver->xmlopt, + cfg->snapshotDir) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unable to save metadata for snapshot %s"), snap->def->name); @@ -2005,9 +2173,8 @@ qemuSnapshotRevertWriteMetadata(virDomainObj *vm, bool defined) { qemuSnapshotSetCurrent(vm, snap); - if (qemuDomainSnapshotWriteMetadata(vm, snap, - driver->xmlopt, - cfg->snapshotDir) < 0) { + if (qemuSnapshotWriteMetadata(vm, snap, driver->xmlopt, + cfg->snapshotDir) < 0) { virDomainSnapshotSetCurrent(vm->snapshots, NULL); return -1; } @@ -2146,7 +2313,7 @@ qemuSnapshotInternalRevertInactive(virQEMUDriver *driver, } /* Try all disks, but report failure if we skipped any. */ - if (qemuDomainSnapshotForEachQcow2(driver, def, snap, "-a", true) != 0) + if (qemuSnapshotForEachQcow2(driver, def, snap, "-a", true) != 0) return -1; return 0; @@ -2415,7 +2582,7 @@ qemuSnapshotDelete(virDomainObj *vm, rem.err = 0; rem.current = virDomainSnapshotGetCurrent(vm->snapshots); rem.found = false; - rem.momentDiscard = qemuDomainSnapshotDiscard; + rem.momentDiscard = qemuSnapshotDiscard; virDomainMomentForEachDescendant(snap, qemuDomainMomentDiscardAll, &rem); if (rem.err < 0) @@ -2424,9 +2591,8 @@ qemuSnapshotDelete(virDomainObj *vm, qemuSnapshotSetCurrent(vm, snap); if (flags & VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY) { - if (qemuDomainSnapshotWriteMetadata(vm, snap, - driver->xmlopt, - cfg->snapshotDir) < 0) { + if (qemuSnapshotWriteMetadata(vm, snap, driver->xmlopt, + cfg->snapshotDir) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to set snapshot '%s' as current"), snap->def->name); @@ -2441,7 +2607,7 @@ qemuSnapshotDelete(virDomainObj *vm, rep.vm = vm; rep.err = 0; rep.xmlopt = driver->xmlopt; - rep.writeMetadata = qemuDomainSnapshotWriteMetadata; + rep.writeMetadata = qemuSnapshotWriteMetadata; virDomainMomentForEachChild(snap, qemuSnapshotChildrenReparent, &rep); @@ -2454,7 +2620,7 @@ qemuSnapshotDelete(virDomainObj *vm, virDomainMomentDropChildren(snap); ret = 0; } else { - ret = qemuDomainSnapshotDiscard(driver, vm, snap, true, metadata_only); + ret = qemuSnapshotDiscard(driver, vm, snap, true, metadata_only); } endjob: @@ -2462,3 +2628,75 @@ qemuSnapshotDelete(virDomainObj *vm, return ret; } + + +int +qemuSnapshotWriteMetadata(virDomainObj *vm, + virDomainMomentObj *snapshot, + virDomainXMLOption *xmlopt, + const char *snapshotDir) +{ + g_autofree char *newxml = NULL; + g_autofree char *snapDir = NULL; + g_autofree char *snapFile = NULL; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + unsigned int flags = VIR_DOMAIN_SNAPSHOT_FORMAT_SECURE | + VIR_DOMAIN_SNAPSHOT_FORMAT_INTERNAL; + virDomainSnapshotDef *def = virDomainSnapshotObjGetDef(snapshot); + + if (virDomainSnapshotGetCurrent(vm->snapshots) == snapshot) + flags |= VIR_DOMAIN_SNAPSHOT_FORMAT_CURRENT; + virUUIDFormat(vm->def->uuid, uuidstr); + newxml = virDomainSnapshotDefFormat(uuidstr, def, xmlopt, flags); + if (newxml == NULL) + return -1; + + snapDir = g_strdup_printf("%s/%s", snapshotDir, vm->def->name); + if (g_mkdir_with_parents(snapDir, 0777) < 0) { + virReportSystemError(errno, _("cannot create snapshot directory '%s'"), + snapDir); + return -1; + } + + snapFile = g_strdup_printf("%s/%s.xml", snapDir, def->parent.name); + + return virXMLSaveFile(snapFile, NULL, "snapshot-edit", newxml); +} + + +/* Hash iterator callback to discard multiple snapshots. */ +int +qemuDomainMomentDiscardAll(void *payload, + const char *name G_GNUC_UNUSED, + void *data) +{ + virDomainMomentObj *moment = payload; + virQEMUMomentRemove *curr = data; + int err; + + if (!curr->found && curr->current == moment) + curr->found = true; + err = curr->momentDiscard(curr->driver, curr->vm, moment, false, + curr->metadata_only); + if (err && !curr->err) + curr->err = err; + return 0; +} + + +int +qemuSnapshotDiscardAllMetadata(virQEMUDriver *driver, + virDomainObj *vm) +{ + virQEMUMomentRemove rem = { + .driver = driver, + .vm = vm, + .metadata_only = true, + .momentDiscard = qemuSnapshotDiscard, + }; + + virDomainSnapshotForEach(vm->snapshots, qemuDomainMomentDiscardAll, &rem); + virDomainSnapshotObjListRemoveAll(vm->snapshots); + + return rem.err; +} diff --git a/src/qemu/qemu_snapshot.h b/src/qemu/qemu_snapshot.h index 0cc38c0039..016d18449a 100644 --- a/src/qemu/qemu_snapshot.h +++ b/src/qemu/qemu_snapshot.h @@ -81,3 +81,13 @@ qemuSnapshotDiskCreate(qemuSnapshotDiskContext *snapctxt); virDomainSnapshotDiskDef * qemuSnapshotGetTransientDiskDef(virDomainDiskDef *domdisk, const char *suffix); + +int +qemuSnapshotWriteMetadata(virDomainObj *vm, + virDomainMomentObj *snapshot, + virDomainXMLOption *xmlopt, + const char *snapshotDir); + +int +qemuSnapshotDiscardAllMetadata(virQEMUDriver *driver, + virDomainObj *vm); -- 2.35.1

We are going to use this job for snapshot delete API. Currently snapshot delete API uses QEMU_JOB_MODIFY. Signed-off-by: Nikolay Shirokovskiy <nikolay.shirokovskiy@openvz.org> --- src/hypervisor/domain_job.c | 1 + src/hypervisor/domain_job.h | 1 + src/qemu/qemu_domainjob.c | 2 ++ src/qemu/qemu_driver.c | 6 ++++++ src/qemu/qemu_migration.c | 2 ++ src/qemu/qemu_process.c | 3 +++ 6 files changed, 15 insertions(+) diff --git a/src/hypervisor/domain_job.c b/src/hypervisor/domain_job.c index ff4e008cb5..ad71a77150 100644 --- a/src/hypervisor/domain_job.c +++ b/src/hypervisor/domain_job.c @@ -37,6 +37,7 @@ VIR_ENUM_IMPL(virDomainAsyncJob, "save", "dump", "snapshot", + "snapshot delete", "start", "backup", ); diff --git a/src/hypervisor/domain_job.h b/src/hypervisor/domain_job.h index db8b8b1390..cb24e3f743 100644 --- a/src/hypervisor/domain_job.h +++ b/src/hypervisor/domain_job.h @@ -63,6 +63,7 @@ typedef enum { VIR_ASYNC_JOB_SAVE, VIR_ASYNC_JOB_DUMP, VIR_ASYNC_JOB_SNAPSHOT, + VIR_ASYNC_JOB_SNAPSHOT_DELETE, VIR_ASYNC_JOB_START, VIR_ASYNC_JOB_BACKUP, diff --git a/src/qemu/qemu_domainjob.c b/src/qemu/qemu_domainjob.c index 56a3f6cb19..89f721abc9 100644 --- a/src/qemu/qemu_domainjob.c +++ b/src/qemu/qemu_domainjob.c @@ -85,6 +85,7 @@ virDomainAsyncJobPhaseToString(virDomainAsyncJob job, case VIR_ASYNC_JOB_SAVE: case VIR_ASYNC_JOB_DUMP: case VIR_ASYNC_JOB_SNAPSHOT: + case VIR_ASYNC_JOB_SNAPSHOT_DELETE: case VIR_ASYNC_JOB_START: case VIR_ASYNC_JOB_NONE: case VIR_ASYNC_JOB_BACKUP: @@ -111,6 +112,7 @@ virDomainAsyncJobPhaseFromString(virDomainAsyncJob job, case VIR_ASYNC_JOB_SAVE: case VIR_ASYNC_JOB_DUMP: case VIR_ASYNC_JOB_SNAPSHOT: + case VIR_ASYNC_JOB_SNAPSHOT_DELETE: case VIR_ASYNC_JOB_START: case VIR_ASYNC_JOB_NONE: case VIR_ASYNC_JOB_BACKUP: diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 74bc2c7bf4..547cffa1bc 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -12741,6 +12741,12 @@ static int qemuDomainAbortJob(virDomainPtr dom) ret = 0; break; + case VIR_ASYNC_JOB_SNAPSHOT_DELETE: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("snapshot deletion expected to be exclusive job")); + goto endjob; + break; + case VIR_ASYNC_JOB_LAST: default: virReportEnumRangeError(virDomainAsyncJob, priv->job.asyncJob); diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 3e653543c6..cb4c0e544a 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1717,6 +1717,8 @@ qemuMigrationJobName(virDomainObj *vm) return _("snapshot job"); case VIR_ASYNC_JOB_START: return _("start job"); + case VIR_ASYNC_JOB_SNAPSHOT_DELETE: + return _("snapshot delete job"); case VIR_ASYNC_JOB_BACKUP: return _("backup job"); case VIR_ASYNC_JOB_LAST: diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index f110e4f3dd..9918423701 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -3648,6 +3648,9 @@ qemuProcessRecoverJob(virQEMUDriver *driver, /* Already handled in VIR_DOMAIN_PAUSED_STARTING_UP check. */ break; + case VIR_ASYNC_JOB_SNAPSHOT_DELETE: + break; + case VIR_ASYNC_JOB_BACKUP: ignore_value(virTimeMillisNow(&now)); -- 2.35.1

Use async job instead of regular one as we are going to use async job harness to wait for snapshot-delete job completion in QEMU. We can use VIR_DOMAIN_JOB_OPERATION_UNKNOWN because it is used only when reporting job progress and getting job progress is disabled now for snapshot deletion. It is not clear whether we should add VIR_DOMAIN_JOB_OPERATION_SNAPSHOT_DELETE and report job progress in future as snapshot-deletion should not take much time I guess. Yet using VIR_DOMAIN_JOB_OPERATION_UNKNOWN seems to be brittle. Signed-off-by: Nikolay Shirokovskiy <nikolay.shirokovskiy@openvz.org> --- src/qemu/qemu_snapshot.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index 878a0abb34..579d744c60 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -177,10 +177,12 @@ qemuSnapshotDiscard(virQEMUDriver *driver, return -1; } else { priv = vm->privateData; - qemuDomainObjEnterMonitor(driver, vm); - /* we continue on even in the face of error */ - qemuMonitorDeleteSnapshot(priv->mon, snap->def->name); - qemuDomainObjExitMonitor(vm); + if (qemuDomainObjEnterMonitorAsync(driver, vm, + VIR_ASYNC_JOB_SNAPSHOT) == 0) { + /* we continue on even in the face of error */ + qemuMonitorDeleteSnapshot(priv->mon, snap->def->name); + qemuDomainObjExitMonitor(vm); + } } } @@ -2551,8 +2553,14 @@ qemuSnapshotDelete(virDomainObj *vm, VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY | VIR_DOMAIN_SNAPSHOT_DELETE_CHILDREN_ONLY, -1); - if (qemuDomainObjBeginJob(driver, vm, VIR_JOB_MODIFY) < 0) + /* + * For snapshot deletion quering stats/aborting is not supported yet + * thus disable any parallels jobs. + */ + if (qemuDomainObjBeginAsyncJob(driver, vm, VIR_ASYNC_JOB_SNAPSHOT_DELETE, + VIR_DOMAIN_JOB_OPERATION_UNKNOWN, flags) < 0) return -1; + qemuDomainObjSetAsyncJobMask(vm, VIR_JOB_NONE); if (!(snap = qemuSnapObjFromSnapshot(vm, snapshot))) goto endjob; @@ -2624,7 +2632,7 @@ qemuSnapshotDelete(virDomainObj *vm, } endjob: - qemuDomainObjEndJob(vm); + qemuDomainObjEndAsyncJob(vm); return ret; } -- 2.35.1

This can be used to specify disk where to place vmstate data. Signed-off-by: Nikolay Shirokovskiy <nikolay.shirokovskiy@openvz.org> --- src/conf/snapshot_conf.c | 13 +++++++++++++ src/conf/snapshot_conf.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c index 80946beba9..8c9ee03942 100644 --- a/src/conf/snapshot_conf.c +++ b/src/conf/snapshot_conf.c @@ -117,6 +117,7 @@ virDomainSnapshotDefDispose(void *obj) size_t i; g_free(def->memorysnapshotfile); + g_free(def->memorydisk); for (i = 0; i < def->ndisks; i++) virDomainSnapshotDiskDefClear(&def->disks[i]); g_free(def->disks); @@ -305,6 +306,7 @@ virDomainSnapshotDefParse(xmlXPathContextPtr ctxt, if ((memoryNode = virXPathNode("./memory", ctxt))) { def->memorysnapshotfile = virXMLPropString(memoryNode, "file"); + def->memorydisk = virXMLPropString(memoryNode, "disk"); if (virXMLPropEnumDefault(memoryNode, "snapshot", virDomainSnapshotLocationTypeFromString, @@ -323,6 +325,8 @@ virDomainSnapshotDefParse(xmlXPathContextPtr ctxt, if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT) { if (def->memorysnapshotfile) { def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_EXTERNAL; + } else if (def->memorydisk) { + def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL; } else if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) { if (offline) { def->memory = VIR_DOMAIN_SNAPSHOT_LOCATION_NO; @@ -347,6 +351,14 @@ virDomainSnapshotDefParse(xmlXPathContextPtr ctxt, return NULL; } + if (def->memorydisk && + def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL) { + virReportError(VIR_ERR_XML_ERROR, + _("memory disk '%s' requires internal snapshot"), + def->memorydisk); + return NULL; + } + if (offline && def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_DEFAULT && def->memory != VIR_DOMAIN_SNAPSHOT_LOCATION_NO) { @@ -844,6 +856,7 @@ virDomainSnapshotDefFormatInternal(virBuffer *buf, virBufferAsprintf(buf, "<memory snapshot='%s'", virDomainSnapshotLocationTypeToString(def->memory)); virBufferEscapeString(buf, " file='%s'", def->memorysnapshotfile); + virBufferEscapeString(buf, " disk='%s'", def->memorydisk); virBufferAddLit(buf, "/>\n"); } diff --git a/src/conf/snapshot_conf.h b/src/conf/snapshot_conf.h index 1f787f1a94..b7b448a071 100644 --- a/src/conf/snapshot_conf.h +++ b/src/conf/snapshot_conf.h @@ -72,6 +72,7 @@ struct _virDomainSnapshotDef { virDomainSnapshotLocation memory; char *memorysnapshotfile; /* memory state file when snapshot is external */ + char *memorydisk; /* memory state disk when snapshot is internal */ size_t ndisks; /* should not exceed dom->ndisks */ virDomainSnapshotDiskDef *disks; -- 2.35.1

- check it is supported by the QEMU - check disk itself has internal snapshot - for modern QEMU select it if it is not specified explicitly Signed-off-by: Nikolay Shirokovskiy <nikolay.shirokovskiy@openvz.org> --- src/qemu/qemu_snapshot.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index 579d744c60..54eafb5020 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -1852,6 +1852,38 @@ qemuSnapshotCreateAlignDisks(virDomainObj *vm, if (virDomainSnapshotAlignDisks(def, NULL, align_location, true) < 0) return -1; + if (def->memory == VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL) { + bool modern = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_SNAPSHOT_SAVE); + + if (modern) { + size_t i; + + for (i = 0; i < def->ndisks; i++) { + if (def->disks[i].snapshot != VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL) + continue; + + if (def->memorydisk) { + if (STREQ(def->memorydisk, def->disks[i].name)) + break; + } else { + def->memorydisk = g_strdup(def->disks[i].name); + break; + } + } + + if (i == def->ndisks && def->memorydisk) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, + _("memory disk '%s' should itself have internal snapshot"), + def->memorydisk); + return -1; + } + } else if (def->memorydisk) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("specifing memory disk is not supported by this QEMU")); + return -1; + } + } + return 0; } -- 2.35.1

Signed-off-by: Nikolay Shirokovskiy <nikolay.shirokovskiy@openvz.org> --- src/qemu/qemu_monitor.c | 3 +++ src/qemu/qemu_monitor.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 4b33407e50..77682f817c 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -119,6 +119,9 @@ VIR_ENUM_IMPL(qemuMonitorJob, "mirror", "backup", "create", + "snapshot-save", + "snapshot-load", + "snapshot-delete", ); VIR_ENUM_IMPL(qemuMonitorJobStatus, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 8067236693..67715ef252 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -139,6 +139,9 @@ typedef enum { QEMU_MONITOR_JOB_TYPE_MIRROR, QEMU_MONITOR_JOB_TYPE_BACKUP, QEMU_MONITOR_JOB_TYPE_CREATE, + QEMU_MONITOR_JOB_TYPE_SNAPSHOT_SAVE, + QEMU_MONITOR_JOB_TYPE_SNAPSHOT_LOAD, + QEMU_MONITOR_JOB_TYPE_SNAPSHOT_DELETE, QEMU_MONITOR_JOB_TYPE_LAST } qemuMonitorJobType; -- 2.35.1

Signed-off-by: Nikolay Shirokovskiy <nikolay.shirokovskiy@openvz.org> --- src/qemu/qemu_process.c | 11 +++ src/qemu/qemu_snapshot.c | 179 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 182 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 9918423701..6ed7eaaa83 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -958,6 +958,17 @@ qemuProcessHandleJobStatusChange(qemuMonitor *mon G_GNUC_UNUSED, jobname, vm, vm->def->name, qemuMonitorJobStatusTypeToString(status), status); + if (STREQ(jobname, "snapshot-save") || + STREQ(jobname, "snapshot-delete") || + STREQ(jobname, "snapshot-load")) { + if (status == QEMU_MONITOR_JOB_STATUS_CONCLUDED && priv->job.current) { + priv->job.current->status = VIR_DOMAIN_JOB_STATUS_COMPLETED; + virDomainObjBroadcast(vm); + } + + goto cleanup; + } + if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV)) { VIR_DEBUG("job '%s' handled by old blockjob handler", jobname); goto cleanup; diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index 54eafb5020..9f81befe85 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -142,6 +142,131 @@ qemuSnapshotForEachQcow2(virQEMUDriver *driver, } +static GPtrArray * +qemuSnapshotGetDisksNodes(virDomainSnapshotDef *snapdef, + virDomainDef *def, + const char **memoryNode) +{ + g_autoptr(GPtrArray) devices = g_ptr_array_new(); + size_t i; + + if (memoryNode) + *memoryNode = NULL; + + for (i = 0; i < snapdef->ndisks; i++) { + if (snapdef->disks[i].snapshot == VIR_DOMAIN_SNAPSHOT_LOCATION_INTERNAL) + g_ptr_array_add(devices, def->disks[i]->src->nodeformat); + + if (memoryNode && STREQ(snapdef->memorydisk, snapdef->disks[i].name)) + *memoryNode = def->disks[i]->src->nodeformat; + } + + if (memoryNode && !*memoryNode) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find vmstate disk '%s'"), snapdef->memorydisk); + return NULL; + } + + return g_steal_pointer(&devices); +} + + +static int +qemuSnapshotDismissJob(virQEMUDriver *driver, + virDomainObj *vm, + virDomainAsyncJob asyncJob, + const char *jobid) +{ + qemuDomainObjPrivate *priv = vm->privateData; + int rc; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + return -1; + + rc = qemuMonitorJobDismiss(priv->mon, jobid); + qemuDomainObjExitMonitor(vm); + if (rc < 0) + return -1; + + return 0; +} + + +static int +qemuSnapshotWaitJob(virQEMUDriver *driver, + virDomainObj *vm, + virDomainAsyncJob asyncJob, + const char *jobid) +{ + qemuDomainObjPrivate *priv = vm->privateData; + qemuMonitorJobInfo **jobs = NULL; + size_t njobs = 0; + qemuMonitorJobInfo *job = NULL; + int ret = -1; + size_t i; + int rc; + + while (priv->job.current->status != VIR_DOMAIN_JOB_STATUS_COMPLETED) { + /* + * We can't do much if wait fails and if domain is still active as in + * order to cleanup we need to call job-cancel and again wait for + * concluded state. + */ + if (virDomainObjWait(vm) < 0) + return -1; + } + + if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) < 0) + goto cleanup; + rc = qemuMonitorGetJobInfo(priv->mon, &jobs, &njobs); + qemuDomainObjExitMonitor(vm); + if (rc < 0) + goto cleanup; + + for (i = 0; i < njobs; i++) { + if (STREQ_NULLABLE(jobs[i]->id, jobid)) { + job = jobs[i]; + break; + } + } + + if (!job) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot obtain status of '%s' job"), jobid); + goto cleanup; + } + if (job->status != QEMU_MONITOR_JOB_STATUS_CONCLUDED) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected '%s' job status '%s'"), jobid, + qemuMonitorJobStatusTypeToString(job->status)); + goto cleanup; + } + if (job->error) { + virReportError(VIR_ERR_OPERATION_FAILED, + _("'%s' job failed '%s'"), jobid, job->error); + goto cleanup; + } + + ret = 0; + + cleanup: + if (virDomainObjIsActive(vm)) { + virErrorPtr err; + + virErrorPreserveLast(&err); + if (qemuSnapshotDismissJob(driver, vm, asyncJob, jobid) < 0) + VIR_WARN("failed to dismiss job '%s'", jobid); + virErrorRestore(&err); + } + + for (i = 0; i < njobs; i++) + qemuMonitorJobInfoFree(jobs[i]); + g_free(jobs); + + return ret; +} + + /* Discard one snapshot (or its metadata), without reparenting any children. */ static int qemuSnapshotDiscard(virQEMUDriver *driver, @@ -453,6 +578,38 @@ qemuSnapshotCreateInactiveExternal(virQEMUDriver *driver, } +static int +qemuSnapshotCreateActiveInternalRun(virQEMUDriver *driver, + virDomainObj *vm, + virDomainMomentObj *snap) +{ + qemuDomainObjPrivate *priv = vm->privateData; + virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap); + g_autoptr(GPtrArray) devices = g_ptr_array_new(); + const char *memoryNode; + int rc; + + if (!(devices = qemuSnapshotGetDisksNodes(snapdef, vm->def, &memoryNode))) + return -1; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, + VIR_ASYNC_JOB_SNAPSHOT) < 0) + return -1; + rc = qemuMonitorSnapshotSave(priv->mon, + "snapshot-save", + snap->def->name, + memoryNode, + (const char **)devices->pdata, + devices->len); + qemuDomainObjExitMonitor(vm); + if (rc < 0) + return -1; + + return qemuSnapshotWaitJob(driver, vm, VIR_ASYNC_JOB_SNAPSHOT, + "snapshot-save"); +} + + /* The domain is expected to be locked and active. */ static int qemuSnapshotCreateActiveInternal(virQEMUDriver *driver, @@ -465,6 +622,7 @@ qemuSnapshotCreateActiveInternal(virQEMUDriver *driver, bool resume = false; virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap); bool halt = !!(flags & VIR_DOMAIN_SNAPSHOT_CREATE_HALT); + bool modern = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_SNAPSHOT_SAVE); int ret = -1; if (!qemuMigrationSrcIsAllowed(driver, vm, false, 0)) @@ -478,14 +636,19 @@ qemuSnapshotCreateActiveInternal(virQEMUDriver *driver, resume = true; } - if (qemuDomainObjEnterMonitorAsync(driver, vm, - VIR_ASYNC_JOB_SNAPSHOT) < 0) - return -1; - - ret = qemuMonitorCreateSnapshot(priv->mon, snap->def->name); - qemuDomainObjExitMonitor(vm); - if (ret < 0) - goto cleanup; + if (modern) { + if (qemuSnapshotCreateActiveInternalRun(driver, vm, snap) < 0) + goto cleanup; + ret = 0; + } else { + if (qemuDomainObjEnterMonitorAsync(driver, vm, + VIR_ASYNC_JOB_SNAPSHOT) < 0) + return -1; + ret = qemuMonitorCreateSnapshot(priv->mon, snap->def->name); + qemuDomainObjExitMonitor(vm); + if (ret < 0) + goto cleanup; + } if (!(snapdef->cookie = (virObject *) qemuDomainSaveCookieNew(vm))) goto cleanup; -- 2.35.1

Note that if snapshot was done using old QEMU API then it is loaded using old QEMU API as well as we don't know on which disk vmstate is. Signed-off-by: Nikolay Shirokovskiy <nikolay.shirokovskiy@openvz.org> --- src/qemu/qemu_process.c | 8 +++++- src/qemu/qemu_snapshot.c | 54 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 6ed7eaaa83..eac6b00ff4 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -98,6 +98,7 @@ #include "virutil.h" #include "storage_source.h" #include "backup_conf.h" +#include "virdomainsnapshotobjlist.h" #include "logging/log_manager.h" #include "logging/log_protocol.h" @@ -7389,6 +7390,7 @@ qemuProcessLaunch(virConnectPtr conn, size_t nnicindexes = 0; g_autofree int *nicindexes = NULL; unsigned long long maxMemLock = 0; + bool backcompatSnapshot; VIR_DEBUG("conn=%p driver=%p vm=%p name=%s if=%d asyncJob=%d " "incoming.launchURI=%s incoming.deferredURI=%s " @@ -7444,11 +7446,15 @@ qemuProcessLaunch(virConnectPtr conn, if (qemuExtDevicesStart(driver, vm, incoming != NULL) < 0) goto cleanup; + if (snapshot) + backcompatSnapshot = !virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_SNAPSHOT_SAVE) || + !virDomainSnapshotObjGetDef(snapshot)->memorydisk; + VIR_DEBUG("Building emulator command line"); if (!(cmd = qemuBuildCommandLine(driver, vm, incoming ? incoming->launchURI : NULL, - snapshot, vmop, + snapshot && backcompatSnapshot ? snapshot : NULL, vmop, false, qemuCheckFips(vm), &nnicindexes, &nicindexes, 0))) diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index 9f81befe85..605288f6c5 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -2395,6 +2395,54 @@ qemuSnapshotRevertWriteMetadata(virDomainObj *vm, } +static int +qemuSnapshotLoadState(virQEMUDriver *driver, + virDomainObj *vm, + virDomainMomentObj *snap) +{ + qemuDomainObjPrivate *priv = vm->privateData; + virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap); + g_autoptr(GPtrArray) devices = g_ptr_array_new(); + const char *memoryNode; + int ret = -1; + int rc; + + if (!(devices = qemuSnapshotGetDisksNodes(snapdef, vm->def, &memoryNode))) + goto cleanup; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, + VIR_ASYNC_JOB_START) < 0) + goto cleanup; + rc = qemuMonitorSnapshotLoad(priv->mon, + "snapshot-load", + snap->def->name, + memoryNode, + (const char **)devices->pdata, + devices->len); + qemuDomainObjExitMonitor(vm); + if (rc < 0) + goto cleanup; + + if (qemuSnapshotWaitJob(driver, vm, VIR_ASYNC_JOB_START, + "snapshot-load") < 0) + goto cleanup; + + ret = 0; + + cleanup: + if (ret < 0 && virDomainObjIsActive(vm)) { + virErrorPtr err; + + virErrorPreserveLast(&err); + qemuProcessStop(driver, vm, + VIR_DOMAIN_SHUTOFF_FAILED, + VIR_ASYNC_JOB_START, 0); + virErrorRestore(&err); + } + return ret; +} + + static int qemuSnapshotRevertActive(virDomainObj *vm, virDomainSnapshotPtr snapshot, @@ -2407,6 +2455,7 @@ qemuSnapshotRevertActive(virDomainObj *vm, unsigned int start_flags, unsigned int flags) { + qemuDomainObjPrivate *priv = vm->privateData; virObjectEvent *event = NULL; virObjectEvent *event2 = NULL; int detail; @@ -2458,6 +2507,11 @@ qemuSnapshotRevertActive(virDomainObj *vm, if (rc < 0) return -1; + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_SNAPSHOT_SAVE) && + snapdef->memorydisk && + qemuSnapshotLoadState(driver, vm, snap) < 0) + return -1; + /* Touch up domain state. */ if (!(flags & VIR_DOMAIN_SNAPSHOT_REVERT_RUNNING) && (snapdef->state == VIR_DOMAIN_SNAPSHOT_PAUSED || -- 2.35.1

Signed-off-by: Nikolay Shirokovskiy <nikolay.shirokovskiy@openvz.org> --- src/qemu/qemu_snapshot.c | 54 ++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index 605288f6c5..fb34c21495 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -267,6 +267,50 @@ qemuSnapshotWaitJob(virQEMUDriver *driver, } +static int +qemuSnapshotDiscardDataActive(virQEMUDriver *driver, + virDomainObj *vm, + virDomainMomentObj *snap) +{ + qemuDomainObjPrivate *priv = vm->privateData; + bool modern = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_SNAPSHOT_SAVE); + virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap); + g_autoptr(GPtrArray) devices = g_ptr_array_new(); + int rc; + + if (!modern) { + if (qemuDomainObjEnterMonitorAsync(driver, vm, + VIR_ASYNC_JOB_SNAPSHOT_DELETE) < 0) + return -1; + + rc = qemuMonitorDeleteSnapshot(priv->mon, snap->def->name); + qemuDomainObjExitMonitor(vm); + if (rc < 0) + return -1; + + return 0; + } + + if (!(devices = qemuSnapshotGetDisksNodes(snapdef, vm->def, NULL))) + return -1; + + if (qemuDomainObjEnterMonitorAsync(driver, vm, + VIR_ASYNC_JOB_SNAPSHOT_DELETE) < 0) + return -1; + rc = qemuMonitorSnapshotDelete(priv->mon, + "snapshot-delete", + snap->def->name, + (const char **)devices->pdata, + devices->len); + qemuDomainObjExitMonitor(vm); + if (rc < 0) + return -1; + + return qemuSnapshotWaitJob(driver, vm, VIR_ASYNC_JOB_SNAPSHOT_DELETE, + "snapshot-delete"); +} + + /* Discard one snapshot (or its metadata), without reparenting any children. */ static int qemuSnapshotDiscard(virQEMUDriver *driver, @@ -276,7 +320,6 @@ qemuSnapshotDiscard(virQEMUDriver *driver, bool metadata_only) { g_autofree char *snapFile = NULL; - qemuDomainObjPrivate *priv; virDomainMomentObj *parentsnap = NULL; g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver); @@ -301,13 +344,8 @@ qemuSnapshotDiscard(virQEMUDriver *driver, if (qemuSnapshotForEachQcow2(driver, def, snap, "-d", true) < 0) return -1; } else { - priv = vm->privateData; - if (qemuDomainObjEnterMonitorAsync(driver, vm, - VIR_ASYNC_JOB_SNAPSHOT) == 0) { - /* we continue on even in the face of error */ - qemuMonitorDeleteSnapshot(priv->mon, snap->def->name); - qemuDomainObjExitMonitor(vm); - } + /* we continue on even in the face of error */ + qemuSnapshotDiscardDataActive(driver, vm, snap); } } -- 2.35.1
participants (1)
-
Nikolay Shirokovskiy