:p
atchew
Login
These patches make use of QMP recently added snapshot-save/delete commands and reaps HMP savevm/deletevm. The usage of HMP commands are highly discouraged by QEMU. Nikolai Barybin (4): qemu monitor: add snaphot-save/delete QMP commands qemu blockjob: add snapshot-save/delete job types qemu snapshot: use QMP snapshot-save/delete for internal snapshots qemu monitor: reap qemu_monitor_text po/POTFILES | 1 - po/libvirt.pot | 18 ---- src/qemu/meson.build | 1 - src/qemu/qemu_block.c | 2 + src/qemu/qemu_blockjob.c | 6 +- src/qemu/qemu_blockjob.h | 2 + src/qemu/qemu_domain.c | 4 + src/qemu/qemu_monitor.c | 23 +++-- src/qemu/qemu_monitor.h | 16 +++- src/qemu/qemu_monitor_json.c | 66 +++++++++++++++ src/qemu/qemu_monitor_json.h | 13 +++ src/qemu/qemu_monitor_text.c | 88 ------------------- src/qemu/qemu_monitor_text.h | 29 ------- src/qemu/qemu_snapshot.c | 158 ++++++++++++++++++++++++++++++++--- 14 files changed, 267 insertions(+), 160 deletions(-) delete mode 100644 src/qemu/qemu_monitor_text.c delete mode 100644 src/qemu/qemu_monitor_text.h -- 2.43.5
Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- src/qemu/qemu_monitor.c | 30 ++++++++++++++++ src/qemu/qemu_monitor.h | 13 +++++++ src/qemu/qemu_monitor_json.c | 66 ++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 13 +++++++ 4 files changed, 122 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -XXX,XX +XXX,XX @@ qemuMonitorDeleteSnapshot(qemuMonitor *mon, const char *name) } +int +qemuMonitorSnapshotSave(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + const char *vmstateDev, + char **wrdevs) +{ + VIR_DEBUG("jobname=%s, snapshotName=%s, vmstateDev=%s, wrdevs=%p", + jobname, snapshotName, vmstateDev, wrdevs); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONSnapshotSave(mon, jobname, snapshotName, vmstateDev, wrdevs); +} + + +int +qemuMonitorSnapshotDelete(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + char **wrdevs) +{ + VIR_DEBUG("jobname=%s, snapshotName=%s, wrdevs=%p", jobname, snapshotName, wrdevs); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONSnapshotDelete(mon, jobname, snapshotName, wrdevs); +} + + int qemuMonitorBlockdevMirror(qemuMonitor *mon, const char *jobname, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -XXX,XX +XXX,XX @@ int qemuMonitorDisplayReload(qemuMonitor *mon, const char *type, bool tlsCerts); + +int +qemuMonitorSnapshotSave(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + const char *vmstateDev, + char **wrdevs); + +int +qemuMonitorSnapshotDelete(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + char **wrdevs); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -XXX,XX +XXX,XX @@ int qemuMonitorJSONDisplayReload(qemuMonitor *mon, return 0; } + +int +qemuMonitorJSONSnapshotSave(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + const char *vmstateDev, + char **wrdevs) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + g_autoptr(virJSONValue) wrdev_list = NULL; + size_t i = 0; + + if (wrdevs) { + wrdev_list = virJSONValueNewArray(); + + for (i = 0; wrdevs[i]; i++) + if (virJSONValueArrayAppendString(wrdev_list, wrdevs[i]) < 0) + return -1; + } + + if (!(cmd = qemuMonitorJSONMakeCommand("snapshot-save", + "s:job-id", jobname, + "s:tag", snapshotName, + "s:vmstate", vmstateDev, + "A:devices", &wrdev_list, + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + return qemuMonitorJSONCheckError(cmd, reply); +} + +int +qemuMonitorJSONSnapshotDelete(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + char **wrdevs) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + g_autoptr(virJSONValue) wrdev_list = NULL; + size_t i = 0; + + if (wrdevs) { + wrdev_list = virJSONValueNewArray(); + + for (i = 0; wrdevs[i]; i++) + if (virJSONValueArrayAppendString(wrdev_list, wrdevs[i]) < 0) + return -1; + } + + if (!(cmd = qemuMonitorJSONMakeCommand("snapshot-delete", + "s:job-id", jobname, + "s:tag", snapshotName, + "A:devices", &wrdev_list, + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + return qemuMonitorJSONCheckError(cmd, reply); +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -XXX,XX +XXX,XX @@ qemuMonitorJSONQueryStats(qemuMonitor *mon, int qemuMonitorJSONDisplayReload(qemuMonitor *mon, const char *type, bool tlsCerts); + +int +qemuMonitorJSONSnapshotSave(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + const char *vmstateDev, + char **wrdevs); + +int +qemuMonitorJSONSnapshotDelete(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + char **wrdevs); -- 2.43.5
Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- src/qemu/qemu_block.c | 2 ++ src/qemu/qemu_blockjob.c | 6 +++++- src/qemu/qemu_blockjob.h | 2 ++ src/qemu/qemu_domain.c | 4 ++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -XXX,XX +XXX,XX @@ qemuBlockPivot(virDomainObj *vm, case QEMU_BLOCKJOB_TYPE_INTERNAL: case QEMU_BLOCKJOB_TYPE_CREATE: case QEMU_BLOCKJOB_TYPE_BROKEN: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_SAVE: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_DELETE: virReportError(VIR_ERR_OPERATION_INVALID, _("job type '%1$s' does not support pivot"), qemuBlockjobTypeToString(job->type)); diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -XXX,XX +XXX,XX @@ VIR_ENUM_IMPL(qemuBlockjob, "backup", "", "create", - "broken"); + "broken", + "snapshot-save", + "snapshot-delete"); static virClass *qemuBlockJobDataClass; @@ -XXX,XX +XXX,XX @@ qemuBlockJobEventProcessConcludedTransition(qemuBlockJobData *job, break; case QEMU_BLOCKJOB_TYPE_BROKEN: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_SAVE: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_DELETE: case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_INTERNAL: case QEMU_BLOCKJOB_TYPE_LAST: diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -XXX,XX +XXX,XX @@ typedef enum { QEMU_BLOCKJOB_TYPE_INTERNAL, QEMU_BLOCKJOB_TYPE_CREATE, QEMU_BLOCKJOB_TYPE_BROKEN, + QEMU_BLOCKJOB_TYPE_SNAPSHOT_SAVE, + QEMU_BLOCKJOB_TYPE_SNAPSHOT_DELETE, QEMU_BLOCKJOB_TYPE_LAST } qemuBlockJobType; G_STATIC_ASSERT((int)QEMU_BLOCKJOB_TYPE_INTERNAL == VIR_DOMAIN_BLOCK_JOB_TYPE_LAST); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -XXX,XX +XXX,XX @@ qemuDomainObjPrivateXMLFormatBlockjobIterator(void *payload, break; case QEMU_BLOCKJOB_TYPE_BROKEN: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_SAVE: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_DELETE: case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_INTERNAL: case QEMU_BLOCKJOB_TYPE_LAST: @@ -XXX,XX +XXX,XX @@ qemuDomainObjPrivateXMLParseBlockjobDataSpecific(qemuBlockJobData *job, break; case QEMU_BLOCKJOB_TYPE_BROKEN: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_SAVE: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_DELETE: case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_INTERNAL: case QEMU_BLOCKJOB_TYPE_LAST: -- 2.43.5
The usage of HMP commands are highly discouraged by qemu. Moreover, current snapshot creation routine does not provide flexibility in choosing target device for VM state snapshot. This patch makes use of QMP commands snapshot-save/delete and by default chooses first writable disk (if present) as target for VM state, NVRAM - otherwise. Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- src/qemu/qemu_snapshot.c | 158 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 148 insertions(+), 10 deletions(-) diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -XXX,XX +XXX,XX @@ qemuSnapshotCreateInactiveExternal(virQEMUDriver *driver, return ret; } +static int +qemuSnapshotActiveInternalGetWrdevListHelper(virDomainObj *vm, + char **wrdevs) +{ + size_t wrdevCount = 0; + size_t i = 0; + + for (i = 0; i < vm->def->ndisks; i++) { + virDomainDiskDef *disk = vm->def->disks[i]; + if (!disk->src->readonly) { + wrdevs[wrdevCount] = g_strdup(disk->src->nodenameformat); + wrdevCount++; + } + } + + if (wrdevCount == 0) { + if (vm->def->os.loader->nvram) { + wrdevs[0] = g_strdup(vm->def->os.loader->nvram->nodenameformat); + } else { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("no writable device for internal snapshot creation/deletion")); + return -1; + } + } + + return 0; +} + + +static int +qemuSnapshotCreateActiveInternalDone(virDomainObj *vm) +{ + qemuBlockJobData *job = NULL; + qemuDomainObjPrivate *priv = vm->privateData; + + if (!(job = virHashLookup(priv->blockjobs, g_strdup_printf("snapsave%d", vm->def->id)))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to lookup blockjob 'snapsave%1$d'"), vm->def->id); + return -1; + } + + qemuBlockJobUpdate(vm, job, VIR_ASYNC_JOB_NONE); + if (job->state == VIR_DOMAIN_BLOCK_JOB_FAILED) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("snapshot-save job failed: %1$s"), NULLSTR(job->errmsg)); + return -1; + } + + return job->state == VIR_DOMAIN_BLOCK_JOB_COMPLETED ? 1 : 0; +} + + +static int +qemuSnapshotCreateActiveInternalStart(virDomainObj *vm, + const char *name) +{ + qemuBlockJobData *job = NULL; + g_autofree char** wrdevs = NULL; + int ret = -1; + int rc = 0; + + wrdevs = g_new0(char *, vm->def->ndisks + 1); + if (qemuSnapshotActiveInternalGetWrdevListHelper(vm, wrdevs) < 0) + return -1; + + if (!(job = qemuBlockJobDiskNew(vm, NULL, QEMU_BLOCKJOB_TYPE_SNAPSHOT_SAVE, + g_strdup_printf("snapsave%d", vm->def->id)))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to create new blockjob")); + return -1; + } + + qemuBlockJobSyncBegin(job); + if (qemuDomainObjEnterMonitorAsync(vm, VIR_ASYNC_JOB_SNAPSHOT) < 0) { + ret = -1; + goto cleanup; + } + + rc = qemuMonitorSnapshotSave(qemuDomainGetMonitor(vm), job->name, + name, wrdevs[0], wrdevs); + qemuDomainObjExitMonitor(vm); + if (rc == 0) { + qemuBlockJobStarted(job, vm); + ret = 0; + } + + cleanup: + qemuBlockJobStartupFinalize(vm, job); + return ret; +} + /* The domain is expected to be locked and active. */ static int @@ -XXX,XX +XXX,XX @@ qemuSnapshotCreateActiveInternal(virQEMUDriver *driver, virDomainMomentObj *snap, unsigned int flags) { - qemuDomainObjPrivate *priv = vm->privateData; virObjectEvent *event = NULL; bool resume = false; virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap); int ret = -1; + int rv = 0; if (!qemuMigrationSrcIsAllowed(vm, false, VIR_ASYNC_JOB_SNAPSHOT, 0)) goto cleanup; @@ -XXX,XX +XXX,XX @@ qemuSnapshotCreateActiveInternal(virQEMUDriver *driver, } } - if (qemuDomainObjEnterMonitorAsync(vm, VIR_ASYNC_JOB_SNAPSHOT) < 0) { + if ((ret = qemuSnapshotCreateActiveInternalStart(vm, snap->def->name)) < 0) { resume = false; goto cleanup; } - ret = qemuMonitorCreateSnapshot(priv->mon, snap->def->name); - qemuDomainObjExitMonitor(vm); - if (ret < 0) - goto cleanup; + while ((rv = qemuSnapshotCreateActiveInternalDone(vm)) != 1) { + if (rv < 0 || qemuDomainObjWait(vm) < 0) { + ret = -1; + goto cleanup; + } + } if (!(snapdef->cookie = (virObject *) qemuDomainSaveCookieNew(vm))) goto cleanup; @@ -XXX,XX +XXX,XX @@ qemuSnapshotDiscardMetadata(virDomainObj *vm, } +static int +qemuSnapshotDiscardActiveInternal(virDomainObj *vm, + const char *name) +{ + qemuBlockJobData *job = NULL; + g_autofree char** wrdevs = NULL; + int ret = -1; + int rc = 0; + + wrdevs = g_new0(char *, vm->def->ndisks + 1); + if (qemuSnapshotActiveInternalGetWrdevListHelper(vm, wrdevs) < 0) + return -1; + + if (!(job = qemuBlockJobDiskNew(vm, NULL, QEMU_BLOCKJOB_TYPE_SNAPSHOT_DELETE, + g_strdup_printf("snapdelete%d", vm->def->id)))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to create new blockjob")); + return -1; + } + + qemuBlockJobSyncBegin(job); + if (qemuDomainObjEnterMonitorAsync(vm, VIR_ASYNC_JOB_SNAPSHOT) < 0) + goto cleanup; + + rc = qemuMonitorSnapshotDelete(qemuDomainGetMonitor(vm), job->name, name, wrdevs); + qemuDomainObjExitMonitor(vm); + if (rc == 0) { + qemuBlockJobStarted(job, vm); + ret = 0; + } else { + goto cleanup; + } + + while (job->state != VIR_DOMAIN_BLOCK_JOB_COMPLETED) { + qemuDomainObjWait(vm); + qemuBlockJobUpdate(vm, job, VIR_ASYNC_JOB_NONE); + if (job->state == VIR_DOMAIN_BLOCK_JOB_FAILED) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("snapshot-delete job failed: %1$s"), NULLSTR(job->errmsg)); + ret = -1; + break; + } + } + + cleanup: + qemuBlockJobStartupFinalize(vm, job); + return ret; +} + + /* Discard one snapshot (or its metadata), without reparenting any children. */ static int qemuSnapshotDiscardImpl(virQEMUDriver *driver, @@ -XXX,XX +XXX,XX @@ qemuSnapshotDiscardImpl(virQEMUDriver *driver, /* Similarly as internal snapshot creation we would use a regular job * here so set a mask to forbid any other job. */ qemuDomainObjSetAsyncJobMask(vm, VIR_JOB_NONE); - if (qemuDomainObjEnterMonitorAsync(vm, VIR_ASYNC_JOB_SNAPSHOT) < 0) - return -1; /* we continue on even in the face of error */ - qemuMonitorDeleteSnapshot(qemuDomainGetMonitor(vm), snap->def->name); - qemuDomainObjExitMonitor(vm); + qemuSnapshotDiscardActiveInternal(vm, snap->def->name); qemuDomainObjSetAsyncJobMask(vm, VIR_JOB_DEFAULT_MASK); } } -- 2.43.5
Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- po/POTFILES | 1 - po/libvirt.pot | 18 -------- src/qemu/meson.build | 1 - src/qemu/qemu_monitor.c | 25 ---------- src/qemu/qemu_monitor.h | 3 -- src/qemu/qemu_monitor_text.c | 88 ------------------------------------ src/qemu/qemu_monitor_text.h | 29 ------------ 7 files changed, 165 deletions(-) delete mode 100644 src/qemu/qemu_monitor_text.c delete mode 100644 src/qemu/qemu_monitor_text.h diff --git a/po/POTFILES b/po/POTFILES index XXXXXXX..XXXXXXX 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -XXX,XX +XXX,XX @@ src/qemu/qemu_migration_cookie.c src/qemu/qemu_migration_params.c src/qemu/qemu_monitor.c src/qemu/qemu_monitor_json.c -src/qemu/qemu_monitor_text.c src/qemu/qemu_namespace.c src/qemu/qemu_nbdkit.c src/qemu/qemu_passt.c diff --git a/po/libvirt.pot b/po/libvirt.pot index XXXXXXX..XXXXXXX 100644 --- a/po/libvirt.pot +++ b/po/libvirt.pot @@ -XXX,XX +XXX,XX @@ msgstr "" msgid "Failed to delete snapshot %1$s" msgstr "" -#: src/qemu/qemu_monitor_text.c:83 -#, c-format -msgid "Failed to delete snapshot: %1$s" -msgstr "" - #: src/bhyve/bhyve_driver.c:389 src/libxl/libxl_driver.c:4670 #: src/lxc/lxc_driver.c:2495 src/network/bridge_driver.c:3618 #: src/qemu/qemu_driver.c:7793 src/storage/storage_driver.c:1399 @@ -XXX,XX +XXX,XX @@ msgstr "" msgid "Failed to symlink device %1$s to %2$s" msgstr "" -#: src/qemu/qemu_monitor_text.c:52 -#, c-format -msgid "Failed to take snapshot: %1$s" -msgstr "" - #: src/util/virprocess.c:424 src/util/virprocess.c:435 #, c-format msgid "Failed to terminate process %1$lld with SIG%2$s" @@ -XXX,XX +XXX,XX @@ msgstr "" msgid "this disk doesn't support update" msgstr "" -#: src/qemu/qemu_monitor_text.c:74 -msgid "this domain does not have a device to delete snapshots" -msgstr "" - -#: src/qemu/qemu_monitor_text.c:56 -msgid "this domain does not have a device to take snapshots" -msgstr "" - #: src/util/virerror.c:1042 msgid "this domain exists already" msgstr "" diff --git a/src/qemu/meson.build b/src/qemu/meson.build index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/meson.build +++ b/src/qemu/meson.build @@ -XXX,XX +XXX,XX @@ qemu_driver_sources = [ 'qemu_migration_params.c', 'qemu_monitor.c', 'qemu_monitor_json.c', - 'qemu_monitor_text.c', 'qemu_namespace.c', 'qemu_nbdkit.c', 'qemu_passt.c', diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -XXX,XX +XXX,XX @@ #include "qemu_alias.h" #include "qemu_monitor.h" -#include "qemu_monitor_text.h" #include "qemu_monitor_json.h" #include "qemu_domain.h" #include "qemu_capabilities.h" @@ -XXX,XX +XXX,XX @@ qemuMonitorDelObject(qemuMonitor *mon, } -int -qemuMonitorCreateSnapshot(qemuMonitor *mon, const char *name) -{ - VIR_DEBUG("name=%s", name); - - QEMU_CHECK_MONITOR(mon); - - /* there won't ever be a direct QMP replacement for this function */ - return qemuMonitorTextCreateSnapshot(mon, name); -} - - -int -qemuMonitorDeleteSnapshot(qemuMonitor *mon, const char *name) -{ - VIR_DEBUG("name=%s", name); - - QEMU_CHECK_MONITOR(mon); - - /* there won't ever be a direct QMP replacement for this function */ - return qemuMonitorTextDeleteSnapshot(mon, name); -} - - int qemuMonitorSnapshotSave(qemuMonitor *mon, const char *jobname, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -XXX,XX +XXX,XX @@ int qemuMonitorDelObject(qemuMonitor *mon, const char *objalias, bool report_error); -int qemuMonitorCreateSnapshot(qemuMonitor *mon, const char *name); -int qemuMonitorDeleteSnapshot(qemuMonitor *mon, const char *name); - int qemuMonitorTransaction(qemuMonitor *mon, virJSONValue **actions) ATTRIBUTE_NONNULL(2); int qemuMonitorBlockdevMirror(qemuMonitor *mon, diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c deleted file mode 100644 index XXXXXXX..XXXXXXX --- a/src/qemu/qemu_monitor_text.c +++ /dev/null @@ -XXX,XX +XXX,XX @@ -/* - * qemu_monitor_text.c: interaction with QEMU monitor console - * - * Copyright (C) 2006-2014 Red Hat, Inc. - * Copyright (C) 2006 Daniel P. Berrange - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * <http://www.gnu.org/licenses/>. - */ - -#include <config.h> - - -#include "qemu_monitor_text.h" -#include "qemu_monitor_json.h" -#include "virlog.h" -#include "virerror.h" - -#define VIR_FROM_THIS VIR_FROM_QEMU - -VIR_LOG_INIT("qemu.qemu_monitor_text"); - -int -qemuMonitorTextCreateSnapshot(qemuMonitor *mon, - const char *name) -{ - g_autofree char *cmd = NULL; - g_autofree char *reply = NULL; - - cmd = g_strdup_printf("savevm \"%s\"", name); - - if (qemuMonitorJSONHumanCommand(mon, cmd, -1, &reply)) - return -1; - - if (strstr(reply, "Error while creating snapshot") || - strstr(reply, "Could not open VM state file") || - strstr(reply, "State blocked by non-migratable device") || - strstr(reply, "Error: ") || - (strstr(reply, "Error") && strstr(reply, "while writing VM"))) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("Failed to take snapshot: %1$s"), reply); - return -1; - } else if (strstr(reply, "No block device can accept snapshots")) { - virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("this domain does not have a device to take snapshots")); - return -1; - } - - return 0; -} - -int qemuMonitorTextDeleteSnapshot(qemuMonitor *mon, const char *name) -{ - g_autofree char *cmd = NULL; - g_autofree char *reply = NULL; - - cmd = g_strdup_printf("delvm \"%s\"", name); - if (qemuMonitorJSONHumanCommand(mon, cmd, -1, &reply)) - return -1; - - if (strstr(reply, "No block device supports snapshots")) { - virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("this domain does not have a device to delete snapshots")); - return -1; - } else if (strstr(reply, "Snapshots not supported on device")) { - virReportError(VIR_ERR_OPERATION_INVALID, "%s", reply); - return -1; - } else if (strstr(reply, "Error: ") || - (strstr(reply, "Error") && - strstr(reply, "while deleting snapshot"))) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("Failed to delete snapshot: %1$s"), reply); - return -1; - } - - return 0; -} diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h deleted file mode 100644 index XXXXXXX..XXXXXXX --- a/src/qemu/qemu_monitor_text.h +++ /dev/null @@ -XXX,XX +XXX,XX @@ -/* - * qemu_monitor_text.h: interaction with QEMU monitor console - * - * Copyright (C) 2006-2009, 2011-2012 Red Hat, Inc. - * Copyright (C) 2006 Daniel P. Berrange - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see - * <http://www.gnu.org/licenses/>. - */ - -#pragma once - -#include "internal.h" - -#include "qemu_monitor.h" - -int qemuMonitorTextCreateSnapshot(qemuMonitor *mon, const char *name); -int qemuMonitorTextDeleteSnapshot(qemuMonitor *mon, const char *name); -- 2.43.5
Den, Peter, Daniel thank you for your comments! I'm sending v2 of this patchset. Changes since last revision: - dropped [PATCH 4/4] qemu monitor: reap qemu_monitor_text - added new patch: qemu capabilities: add QEMU_CAPS_SNAPSHOT_SAVE/_DELETE - preserved old-style snapshotting (HMP savevm) in case we have QEMU < 6.0 - enhanced requirements for allowing snapshotting. All writable disks should be qcow2, non-shared. If such disks exist and we have qcow2 NVRAM, add NVRAM device to the list of wrdevs. But never save vmstate to NVRAM - make char** wrdevs list allocation inside qemuSnapshotActiveInternalGetWrdevListHelper() Nikolai Barybin (4): qemu monitor: add snaphot-save/delete QMP commands qemu blockjob: add snapshot-save/delete job types qemu capabilities: add QEMU_CAPS_SNAPSHOT_SAVE/_DELETE qemu snapshot: use QMP snapshot-save/delete for internal snapshots src/qemu/qemu_block.c | 2 + src/qemu/qemu_blockjob.c | 6 +- src/qemu/qemu_blockjob.h | 2 + src/qemu/qemu_capabilities.c | 4 + src/qemu/qemu_capabilities.h | 2 + src/qemu/qemu_domain.c | 4 + src/qemu/qemu_monitor.c | 30 +++ src/qemu/qemu_monitor.h | 13 ++ src/qemu/qemu_monitor_json.c | 66 ++++++ src/qemu/qemu_monitor_json.h | 13 ++ src/qemu/qemu_snapshot.c | 207 ++++++++++++++++-- .../caps_6.0.0_aarch64.xml | 2 + .../qemucapabilitiesdata/caps_6.0.0_s390x.xml | 2 + .../caps_6.0.0_x86_64.xml | 2 + .../caps_6.1.0_x86_64.xml | 2 + .../caps_6.2.0_aarch64.xml | 2 + .../qemucapabilitiesdata/caps_6.2.0_ppc64.xml | 2 + .../caps_6.2.0_x86_64.xml | 2 + .../caps_7.0.0_aarch64+hvf.xml | 2 + .../caps_7.0.0_aarch64.xml | 2 + .../qemucapabilitiesdata/caps_7.0.0_ppc64.xml | 2 + .../caps_7.0.0_x86_64.xml | 2 + .../qemucapabilitiesdata/caps_7.1.0_ppc64.xml | 2 + .../caps_7.1.0_x86_64.xml | 2 + tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml | 2 + .../caps_7.2.0_x86_64+hvf.xml | 2 + .../caps_7.2.0_x86_64.xml | 2 + .../caps_8.0.0_riscv64.xml | 2 + .../caps_8.0.0_x86_64.xml | 2 + .../qemucapabilitiesdata/caps_8.1.0_s390x.xml | 2 + .../caps_8.1.0_x86_64.xml | 2 + .../caps_8.2.0_aarch64.xml | 2 + .../caps_8.2.0_armv7l.xml | 2 + .../caps_8.2.0_loongarch64.xml | 2 + .../qemucapabilitiesdata/caps_8.2.0_s390x.xml | 2 + .../caps_8.2.0_x86_64.xml | 2 + .../qemucapabilitiesdata/caps_9.0.0_sparc.xml | 2 + .../caps_9.0.0_x86_64.xml | 2 + .../caps_9.1.0_x86_64.xml | 2 + 39 files changed, 391 insertions(+), 14 deletions(-) -- 2.43.5
Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- src/qemu/qemu_monitor.c | 30 ++++++++++++++++ src/qemu/qemu_monitor.h | 13 +++++++ src/qemu/qemu_monitor_json.c | 66 ++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 13 +++++++ 4 files changed, 122 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -XXX,XX +XXX,XX @@ qemuMonitorDeleteSnapshot(qemuMonitor *mon, const char *name) } +int +qemuMonitorSnapshotSave(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + const char *vmstateDev, + char **wrdevs) +{ + VIR_DEBUG("jobname=%s, snapshotName=%s, vmstateDev=%s, wrdevs=%p", + jobname, snapshotName, vmstateDev, wrdevs); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONSnapshotSave(mon, jobname, snapshotName, vmstateDev, wrdevs); +} + + +int +qemuMonitorSnapshotDelete(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + char **wrdevs) +{ + VIR_DEBUG("jobname=%s, snapshotName=%s, wrdevs=%p", jobname, snapshotName, wrdevs); + + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONSnapshotDelete(mon, jobname, snapshotName, wrdevs); +} + + int qemuMonitorBlockdevMirror(qemuMonitor *mon, const char *jobname, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -XXX,XX +XXX,XX @@ int qemuMonitorDisplayReload(qemuMonitor *mon, const char *type, bool tlsCerts); + +int +qemuMonitorSnapshotSave(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + const char *vmstateDev, + char **wrdevs); + +int +qemuMonitorSnapshotDelete(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + char **wrdevs); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -XXX,XX +XXX,XX @@ int qemuMonitorJSONDisplayReload(qemuMonitor *mon, return 0; } + +int +qemuMonitorJSONSnapshotSave(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + const char *vmstateDev, + char **wrdevs) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + g_autoptr(virJSONValue) wrdev_list = NULL; + size_t i = 0; + + if (wrdevs) { + wrdev_list = virJSONValueNewArray(); + + for (i = 0; wrdevs[i]; i++) + if (virJSONValueArrayAppendString(wrdev_list, wrdevs[i]) < 0) + return -1; + } + + if (!(cmd = qemuMonitorJSONMakeCommand("snapshot-save", + "s:job-id", jobname, + "s:tag", snapshotName, + "s:vmstate", vmstateDev, + "A:devices", &wrdev_list, + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + return qemuMonitorJSONCheckError(cmd, reply); +} + +int +qemuMonitorJSONSnapshotDelete(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + char **wrdevs) +{ + g_autoptr(virJSONValue) cmd = NULL; + g_autoptr(virJSONValue) reply = NULL; + g_autoptr(virJSONValue) wrdev_list = NULL; + size_t i = 0; + + if (wrdevs) { + wrdev_list = virJSONValueNewArray(); + + for (i = 0; wrdevs[i]; i++) + if (virJSONValueArrayAppendString(wrdev_list, wrdevs[i]) < 0) + return -1; + } + + if (!(cmd = qemuMonitorJSONMakeCommand("snapshot-delete", + "s:job-id", jobname, + "s:tag", snapshotName, + "A:devices", &wrdev_list, + NULL))) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + return qemuMonitorJSONCheckError(cmd, reply); +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -XXX,XX +XXX,XX @@ qemuMonitorJSONQueryStats(qemuMonitor *mon, int qemuMonitorJSONDisplayReload(qemuMonitor *mon, const char *type, bool tlsCerts); + +int +qemuMonitorJSONSnapshotSave(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + const char *vmstateDev, + char **wrdevs); + +int +qemuMonitorJSONSnapshotDelete(qemuMonitor *mon, + const char *jobname, + const char *snapshotName, + char **wrdevs); -- 2.43.5
Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- src/qemu/qemu_block.c | 2 ++ src/qemu/qemu_blockjob.c | 6 +++++- src/qemu/qemu_blockjob.h | 2 ++ src/qemu/qemu_domain.c | 4 ++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/qemu/qemu_block.c b/src/qemu/qemu_block.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_block.c +++ b/src/qemu/qemu_block.c @@ -XXX,XX +XXX,XX @@ qemuBlockPivot(virDomainObj *vm, case QEMU_BLOCKJOB_TYPE_INTERNAL: case QEMU_BLOCKJOB_TYPE_CREATE: case QEMU_BLOCKJOB_TYPE_BROKEN: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_SAVE: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_DELETE: virReportError(VIR_ERR_OPERATION_INVALID, _("job type '%1$s' does not support pivot"), qemuBlockjobTypeToString(job->type)); diff --git a/src/qemu/qemu_blockjob.c b/src/qemu/qemu_blockjob.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_blockjob.c +++ b/src/qemu/qemu_blockjob.c @@ -XXX,XX +XXX,XX @@ VIR_ENUM_IMPL(qemuBlockjob, "backup", "", "create", - "broken"); + "broken", + "snapshot-save", + "snapshot-delete"); static virClass *qemuBlockJobDataClass; @@ -XXX,XX +XXX,XX @@ qemuBlockJobEventProcessConcludedTransition(qemuBlockJobData *job, break; case QEMU_BLOCKJOB_TYPE_BROKEN: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_SAVE: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_DELETE: case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_INTERNAL: case QEMU_BLOCKJOB_TYPE_LAST: diff --git a/src/qemu/qemu_blockjob.h b/src/qemu/qemu_blockjob.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_blockjob.h +++ b/src/qemu/qemu_blockjob.h @@ -XXX,XX +XXX,XX @@ typedef enum { QEMU_BLOCKJOB_TYPE_INTERNAL, QEMU_BLOCKJOB_TYPE_CREATE, QEMU_BLOCKJOB_TYPE_BROKEN, + QEMU_BLOCKJOB_TYPE_SNAPSHOT_SAVE, + QEMU_BLOCKJOB_TYPE_SNAPSHOT_DELETE, QEMU_BLOCKJOB_TYPE_LAST } qemuBlockJobType; G_STATIC_ASSERT((int)QEMU_BLOCKJOB_TYPE_INTERNAL == VIR_DOMAIN_BLOCK_JOB_TYPE_LAST); diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -XXX,XX +XXX,XX @@ qemuDomainObjPrivateXMLFormatBlockjobIterator(void *payload, break; case QEMU_BLOCKJOB_TYPE_BROKEN: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_SAVE: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_DELETE: case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_INTERNAL: case QEMU_BLOCKJOB_TYPE_LAST: @@ -XXX,XX +XXX,XX @@ qemuDomainObjPrivateXMLParseBlockjobDataSpecific(qemuBlockJobData *job, break; case QEMU_BLOCKJOB_TYPE_BROKEN: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_SAVE: + case QEMU_BLOCKJOB_TYPE_SNAPSHOT_DELETE: case QEMU_BLOCKJOB_TYPE_NONE: case QEMU_BLOCKJOB_TYPE_INTERNAL: case QEMU_BLOCKJOB_TYPE_LAST: -- 2.43.5
The 'snapshot-save/delete' QMP commands were introduced in QEMU 6.0.0, so we add a compatible capability to check if target QEMU binary supports it. Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- src/qemu/qemu_capabilities.c | 4 ++++ src/qemu/qemu_capabilities.h | 2 ++ tests/qemucapabilitiesdata/caps_6.0.0_aarch64.xml | 2 ++ tests/qemucapabilitiesdata/caps_6.0.0_s390x.xml | 2 ++ tests/qemucapabilitiesdata/caps_6.0.0_x86_64.xml | 2 ++ tests/qemucapabilitiesdata/caps_6.1.0_x86_64.xml | 2 ++ tests/qemucapabilitiesdata/caps_6.2.0_aarch64.xml | 2 ++ tests/qemucapabilitiesdata/caps_6.2.0_ppc64.xml | 2 ++ tests/qemucapabilitiesdata/caps_6.2.0_x86_64.xml | 2 ++ tests/qemucapabilitiesdata/caps_7.0.0_aarch64+hvf.xml | 2 ++ tests/qemucapabilitiesdata/caps_7.0.0_aarch64.xml | 2 ++ tests/qemucapabilitiesdata/caps_7.0.0_ppc64.xml | 2 ++ tests/qemucapabilitiesdata/caps_7.0.0_x86_64.xml | 2 ++ tests/qemucapabilitiesdata/caps_7.1.0_ppc64.xml | 2 ++ tests/qemucapabilitiesdata/caps_7.1.0_x86_64.xml | 2 ++ tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml | 2 ++ tests/qemucapabilitiesdata/caps_7.2.0_x86_64+hvf.xml | 2 ++ tests/qemucapabilitiesdata/caps_7.2.0_x86_64.xml | 2 ++ tests/qemucapabilitiesdata/caps_8.0.0_riscv64.xml | 2 ++ tests/qemucapabilitiesdata/caps_8.0.0_x86_64.xml | 2 ++ tests/qemucapabilitiesdata/caps_8.1.0_s390x.xml | 2 ++ tests/qemucapabilitiesdata/caps_8.1.0_x86_64.xml | 2 ++ tests/qemucapabilitiesdata/caps_8.2.0_aarch64.xml | 2 ++ tests/qemucapabilitiesdata/caps_8.2.0_armv7l.xml | 2 ++ tests/qemucapabilitiesdata/caps_8.2.0_loongarch64.xml | 2 ++ tests/qemucapabilitiesdata/caps_8.2.0_s390x.xml | 2 ++ tests/qemucapabilitiesdata/caps_8.2.0_x86_64.xml | 2 ++ tests/qemucapabilitiesdata/caps_9.0.0_sparc.xml | 2 ++ tests/qemucapabilitiesdata/caps_9.0.0_x86_64.xml | 2 ++ tests/qemucapabilitiesdata/caps_9.1.0_x86_64.xml | 2 ++ 30 files changed, 62 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -XXX,XX +XXX,XX @@ VIR_ENUM_IMPL(virQEMUCaps, /* 460 */ "sev-snp-guest", /* QEMU_CAPS_SEV_SNP_GUEST */ "netdev.user", /* QEMU_CAPS_NETDEV_USER */ + "snapshot-save", /* QEMU_CAPS_SNAPSHOT_SAVE */ + "snapshot-delete", /* QEMU_CAPS_SNAPSHOT_DELETE */ ); @@ -XXX,XX +XXX,XX @@ struct virQEMUCapsStringFlags virQEMUCapsCommands[] = { { "query-stats", QEMU_CAPS_QUERY_STATS }, { "query-stats-schemas", QEMU_CAPS_QUERY_STATS_SCHEMAS }, { "display-reload", QEMU_CAPS_DISPLAY_RELOAD }, + { "snapshot-save", QEMU_CAPS_SNAPSHOT_SAVE }, + { "snapshot-delete", QEMU_CAPS_SNAPSHOT_DELETE }, }; struct virQEMUCapsStringFlags virQEMUCapsMigration[] = { diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -XXX,XX +XXX,XX @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ /* 460 */ QEMU_CAPS_SEV_SNP_GUEST, /* -object sev-snp-guest */ QEMU_CAPS_NETDEV_USER, /* -netdev user */ + QEMU_CAPS_SNAPSHOT_SAVE, /* 'snapshot-save' qmp command is supported */ + QEMU_CAPS_SNAPSHOT_DELETE, /* 'snapshot-delete' qmp command is supported */ 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 XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_6.0.0_aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_6.0.0_aarch64.xml @@ -XXX,XX +XXX,XX @@ <flag name='usb-mtp'/> <flag name='machine.virt.ras'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>6000000</version> <microcodeVersion>61700242</microcodeVersion> <package>v6.0.0</package> diff --git a/tests/qemucapabilitiesdata/caps_6.0.0_s390x.xml b/tests/qemucapabilitiesdata/caps_6.0.0_s390x.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_6.0.0_s390x.xml +++ b/tests/qemucapabilitiesdata/caps_6.0.0_s390x.xml @@ -XXX,XX +XXX,XX @@ <flag name='virtio-crypto'/> <flag name='display-reload'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>6000000</version> <microcodeVersion>39100242</microcodeVersion> <package>qemu-6.0.0-20210517.1.4ff77070.fc33</package> diff --git a/tests/qemucapabilitiesdata/caps_6.0.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_6.0.0_x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_6.0.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_6.0.0_x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>6000000</version> <microcodeVersion>43100242</microcodeVersion> <package>v6.0.0</package> diff --git a/tests/qemucapabilitiesdata/caps_6.1.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_6.1.0_x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_6.1.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_6.1.0_x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>6001000</version> <microcodeVersion>43100243</microcodeVersion> <package>v6.1.0</package> diff --git a/tests/qemucapabilitiesdata/caps_6.2.0_aarch64.xml b/tests/qemucapabilitiesdata/caps_6.2.0_aarch64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_6.2.0_aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_6.2.0_aarch64.xml @@ -XXX,XX +XXX,XX @@ <flag name='usb-mtp'/> <flag name='machine.virt.ras'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>6001050</version> <microcodeVersion>61700244</microcodeVersion> <package></package> diff --git a/tests/qemucapabilitiesdata/caps_6.2.0_ppc64.xml b/tests/qemucapabilitiesdata/caps_6.2.0_ppc64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_6.2.0_ppc64.xml +++ b/tests/qemucapabilitiesdata/caps_6.2.0_ppc64.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>6002000</version> <microcodeVersion>42900244</microcodeVersion> <package>v6.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_6.2.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_6.2.0_x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_6.2.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_6.2.0_x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>6002000</version> <microcodeVersion>43100244</microcodeVersion> <package>v6.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_7.0.0_aarch64+hvf.xml b/tests/qemucapabilitiesdata/caps_7.0.0_aarch64+hvf.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_7.0.0_aarch64+hvf.xml +++ b/tests/qemucapabilitiesdata/caps_7.0.0_aarch64+hvf.xml @@ -XXX,XX +XXX,XX @@ <flag name='usb-mtp'/> <flag name='machine.virt.ras'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>6002092</version> <microcodeVersion>61700243</microcodeVersion> <package>v7.0.0-rc2</package> diff --git a/tests/qemucapabilitiesdata/caps_7.0.0_aarch64.xml b/tests/qemucapabilitiesdata/caps_7.0.0_aarch64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_7.0.0_aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_7.0.0_aarch64.xml @@ -XXX,XX +XXX,XX @@ <flag name='usb-mtp'/> <flag name='machine.virt.ras'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>6002092</version> <microcodeVersion>61700243</microcodeVersion> <package>v7.0.0-rc2</package> diff --git a/tests/qemucapabilitiesdata/caps_7.0.0_ppc64.xml b/tests/qemucapabilitiesdata/caps_7.0.0_ppc64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_7.0.0_ppc64.xml +++ b/tests/qemucapabilitiesdata/caps_7.0.0_ppc64.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>7000000</version> <microcodeVersion>42900243</microcodeVersion> <package>v7.0.0</package> diff --git a/tests/qemucapabilitiesdata/caps_7.0.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_7.0.0_x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_7.0.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_7.0.0_x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>7000000</version> <microcodeVersion>43100243</microcodeVersion> <package>v7.0.0</package> diff --git a/tests/qemucapabilitiesdata/caps_7.1.0_ppc64.xml b/tests/qemucapabilitiesdata/caps_7.1.0_ppc64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_7.1.0_ppc64.xml +++ b/tests/qemucapabilitiesdata/caps_7.1.0_ppc64.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>7001000</version> <microcodeVersion>42900244</microcodeVersion> <package>v7.1.0</package> diff --git a/tests/qemucapabilitiesdata/caps_7.1.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_7.1.0_x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_7.1.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_7.1.0_x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>7001000</version> <microcodeVersion>43100244</microcodeVersion> <package>v7.1.0</package> diff --git a/tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml b/tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml +++ b/tests/qemucapabilitiesdata/caps_7.2.0_ppc.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>7002000</version> <microcodeVersion>0</microcodeVersion> <package>qemu-7.2.0-6.fc37</package> diff --git a/tests/qemucapabilitiesdata/caps_7.2.0_x86_64+hvf.xml b/tests/qemucapabilitiesdata/caps_7.2.0_x86_64+hvf.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_7.2.0_x86_64+hvf.xml +++ b/tests/qemucapabilitiesdata/caps_7.2.0_x86_64+hvf.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>7002000</version> <microcodeVersion>43100245</microcodeVersion> <package>v7.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_7.2.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_7.2.0_x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_7.2.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_7.2.0_x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>7002000</version> <microcodeVersion>43100245</microcodeVersion> <package>v7.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_8.0.0_riscv64.xml b/tests/qemucapabilitiesdata/caps_8.0.0_riscv64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.0.0_riscv64.xml +++ b/tests/qemucapabilitiesdata/caps_8.0.0_riscv64.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>7002050</version> <microcodeVersion>0</microcodeVersion> <package>v7.2.0-333-g222059a0fc</package> diff --git a/tests/qemucapabilitiesdata/caps_8.0.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_8.0.0_x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.0.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_8.0.0_x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>8000000</version> <microcodeVersion>43100244</microcodeVersion> <package>v8.0.0</package> diff --git a/tests/qemucapabilitiesdata/caps_8.1.0_s390x.xml b/tests/qemucapabilitiesdata/caps_8.1.0_s390x.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.1.0_s390x.xml +++ b/tests/qemucapabilitiesdata/caps_8.1.0_s390x.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>8001000</version> <microcodeVersion>39100245</microcodeVersion> <package>v8.1.0</package> diff --git a/tests/qemucapabilitiesdata/caps_8.1.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_8.1.0_x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.1.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_8.1.0_x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='display-reload'/> <flag name='usb-mtp'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>8001000</version> <microcodeVersion>43100245</microcodeVersion> <package>v8.1.0</package> diff --git a/tests/qemucapabilitiesdata/caps_8.2.0_aarch64.xml b/tests/qemucapabilitiesdata/caps_8.2.0_aarch64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.2.0_aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_8.2.0_aarch64.xml @@ -XXX,XX +XXX,XX @@ <flag name='machine.virt.ras'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>8002000</version> <microcodeVersion>61700246</microcodeVersion> <package>v8.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_8.2.0_armv7l.xml b/tests/qemucapabilitiesdata/caps_8.2.0_armv7l.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.2.0_armv7l.xml +++ b/tests/qemucapabilitiesdata/caps_8.2.0_armv7l.xml @@ -XXX,XX +XXX,XX @@ <flag name='machine.virt.ras'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>8002000</version> <microcodeVersion>0</microcodeVersion> <package>qemu-8.2.0-7.fc39</package> diff --git a/tests/qemucapabilitiesdata/caps_8.2.0_loongarch64.xml b/tests/qemucapabilitiesdata/caps_8.2.0_loongarch64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.2.0_loongarch64.xml +++ b/tests/qemucapabilitiesdata/caps_8.2.0_loongarch64.xml @@ -XXX,XX +XXX,XX @@ <flag name='usb-mtp'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>8002000</version> <microcodeVersion>106300246</microcodeVersion> <package>v8.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_8.2.0_s390x.xml b/tests/qemucapabilitiesdata/caps_8.2.0_s390x.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.2.0_s390x.xml +++ b/tests/qemucapabilitiesdata/caps_8.2.0_s390x.xml @@ -XXX,XX +XXX,XX @@ <flag name='usb-mtp'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>8002000</version> <microcodeVersion>39100246</microcodeVersion> <package>v8.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_8.2.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_8.2.0_x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_8.2.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_8.2.0_x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='usb-mtp'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>8002000</version> <microcodeVersion>43100246</microcodeVersion> <package>v8.2.0</package> diff --git a/tests/qemucapabilitiesdata/caps_9.0.0_sparc.xml b/tests/qemucapabilitiesdata/caps_9.0.0_sparc.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_9.0.0_sparc.xml +++ b/tests/qemucapabilitiesdata/caps_9.0.0_sparc.xml @@ -XXX,XX +XXX,XX @@ <flag name='blockjob.backing-mask-protocol'/> <flag name='display-reload'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>9000000</version> <microcodeVersion>0</microcodeVersion> <package>qemu-9.0.0-1.fc40</package> diff --git a/tests/qemucapabilitiesdata/caps_9.0.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_9.0.0_x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_9.0.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_9.0.0_x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='usb-mtp'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>9000000</version> <microcodeVersion>43100245</microcodeVersion> <package>v9.0.0</package> diff --git a/tests/qemucapabilitiesdata/caps_9.1.0_x86_64.xml b/tests/qemucapabilitiesdata/caps_9.1.0_x86_64.xml index XXXXXXX..XXXXXXX 100644 --- a/tests/qemucapabilitiesdata/caps_9.1.0_x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_9.1.0_x86_64.xml @@ -XXX,XX +XXX,XX @@ <flag name='usb-mtp'/> <flag name='virtio-sound'/> <flag name='netdev.user'/> + <flag name='snapshot-save'/> + <flag name='snapshot-delete'/> <version>9000050</version> <microcodeVersion>43100246</microcodeVersion> <package>v9.0.0-1388-g80e8f06021-dirty</package> -- 2.43.5
The usage of HMP commands are highly discouraged by qemu. Moreover, current snapshot creation routine does not provide flexibility in choosing target device for VM state snapshot. This patch makes use of QMP commands snapshot-save/delete and by default chooses first writable non-shared qcow2 disk (if present) as target for VM state. Signed-off-by: Nikolai Barybin <nikolai.barybin@virtuozzo.com> --- src/qemu/qemu_snapshot.c | 207 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 194 insertions(+), 13 deletions(-) diff --git a/src/qemu/qemu_snapshot.c b/src/qemu/qemu_snapshot.c index XXXXXXX..XXXXXXX 100644 --- a/src/qemu/qemu_snapshot.c +++ b/src/qemu/qemu_snapshot.c @@ -XXX,XX +XXX,XX @@ qemuSnapshotCreateInactiveExternal(virQEMUDriver *driver, return ret; } +static int +qemuSnapshotActiveInternalGetWrdevListHelper(virDomainObj *vm, + char ***wrdevs) +{ + size_t wrdevCount = 0; + size_t i = 0; + g_auto(GStrv) wrdevsInternal = NULL; + + *wrdevs = NULL; + for (i = 0; i < vm->def->ndisks; i++) { + virDomainDiskDef *disk = vm->def->disks[i]; + if (!disk->src->readonly && disk->src->shared) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("found shared writable disk, VM snapshotting has no sense")); + return -1; + } + + if (!disk->src->readonly && !disk->src->shared && + disk->src->format != VIR_STORAGE_FILE_QCOW2) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("found writable non-qcow2 disk, snapshotting forbidden")); + return -1; + } + + if (!disk->src->readonly && !disk->src->shared && + disk->src->format == VIR_STORAGE_FILE_QCOW2) { + if (wrdevCount == 0) + wrdevsInternal = g_new0(char *, vm->def->ndisks + 2); + + wrdevsInternal[wrdevCount++] = g_strdup(disk->src->nodenameformat); + } + } + + if (wrdevCount > 0 && vm->def->os.loader && vm->def->os.loader->nvram && + vm->def->os.loader->nvram->format == VIR_STORAGE_FILE_QCOW2) + wrdevsInternal[wrdevCount] = g_strdup(vm->def->os.loader->nvram->nodenameformat); + + if (wrdevCount == 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("no writable device found for internal snapshot creation/deletion")); + return -1; + } + + *wrdevs = g_steal_pointer(&wrdevsInternal); + return 0; +} + + +static int +qemuSnapshotCreateActiveInternalDone(virDomainObj *vm) +{ + qemuBlockJobData *job = NULL; + qemuDomainObjPrivate *priv = vm->privateData; + + if (!(job = virHashLookup(priv->blockjobs, g_strdup_printf("snapsave%d", vm->def->id)))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("failed to lookup blockjob 'snapsave%1$d'"), vm->def->id); + return -1; + } + + qemuBlockJobUpdate(vm, job, VIR_ASYNC_JOB_NONE); + if (job->state == VIR_DOMAIN_BLOCK_JOB_FAILED) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("snapshot-save job failed: %1$s"), NULLSTR(job->errmsg)); + return -1; + } + + return job->state == VIR_DOMAIN_BLOCK_JOB_COMPLETED ? 1 : 0; +} + + +static int +qemuSnapshotCreateActiveInternalStart(virDomainObj *vm, + const char *name) +{ + qemuBlockJobData *job = NULL; + g_auto(GStrv) wrdevs = NULL; + int ret = -1; + int rc = 0; + + if (qemuSnapshotActiveInternalGetWrdevListHelper(vm, &wrdevs) < 0) + return -1; + + if (!(job = qemuBlockJobDiskNew(vm, NULL, QEMU_BLOCKJOB_TYPE_SNAPSHOT_SAVE, + g_strdup_printf("snapsave%d", vm->def->id)))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to create new blockjob")); + return -1; + } + + qemuBlockJobSyncBegin(job); + if (qemuDomainObjEnterMonitorAsync(vm, VIR_ASYNC_JOB_SNAPSHOT) < 0) { + ret = -1; + goto cleanup; + } + + rc = qemuMonitorSnapshotSave(qemuDomainGetMonitor(vm), job->name, + name, wrdevs[0], wrdevs); + qemuDomainObjExitMonitor(vm); + if (rc == 0) { + qemuBlockJobStarted(job, vm); + ret = 0; + } + + cleanup: + qemuBlockJobStartupFinalize(vm, job); + return ret; +} + /* The domain is expected to be locked and active. */ static int @@ -XXX,XX +XXX,XX @@ qemuSnapshotCreateActiveInternal(virQEMUDriver *driver, bool resume = false; virDomainSnapshotDef *snapdef = virDomainSnapshotObjGetDef(snap); int ret = -1; + int rv = 0; if (!qemuMigrationSrcIsAllowed(vm, false, VIR_ASYNC_JOB_SNAPSHOT, 0)) goto cleanup; @@ -XXX,XX +XXX,XX @@ qemuSnapshotCreateActiveInternal(virQEMUDriver *driver, } } - if (qemuDomainObjEnterMonitorAsync(vm, VIR_ASYNC_JOB_SNAPSHOT) < 0) { - resume = false; - goto cleanup; - } + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_SNAPSHOT_SAVE)) { + if ((ret = qemuSnapshotCreateActiveInternalStart(vm, snap->def->name)) < 0) { + resume = false; + goto cleanup; + } - ret = qemuMonitorCreateSnapshot(priv->mon, snap->def->name); - qemuDomainObjExitMonitor(vm); - if (ret < 0) - goto cleanup; + while ((rv = qemuSnapshotCreateActiveInternalDone(vm)) != 1) { + if (rv < 0 || qemuDomainObjWait(vm) < 0) { + ret = -1; + goto cleanup; + } + } + /* Legacy support for QEMU versions < 6.0. */ + } else { + if (qemuDomainObjEnterMonitorAsync(vm, VIR_ASYNC_JOB_SNAPSHOT) < 0) { + resume = false; + goto cleanup; + } + + ret = qemuMonitorCreateSnapshot(priv->mon, snap->def->name); + qemuDomainObjExitMonitor(vm); + if (ret < 0) + goto cleanup; + } if (!(snapdef->cookie = (virObject *) qemuDomainSaveCookieNew(vm))) goto cleanup; @@ -XXX,XX +XXX,XX @@ qemuSnapshotDiscardMetadata(virDomainObj *vm, } +static int +qemuSnapshotDiscardActiveInternal(virDomainObj *vm, + const char *name) +{ + qemuBlockJobData *job = NULL; + g_auto(GStrv) wrdevs = NULL; + int ret = -1; + int rc = 0; + + if (qemuSnapshotActiveInternalGetWrdevListHelper(vm, &wrdevs) < 0) + return -1; + + if (!(job = qemuBlockJobDiskNew(vm, NULL, QEMU_BLOCKJOB_TYPE_SNAPSHOT_DELETE, + g_strdup_printf("snapdelete%d", vm->def->id)))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to create new blockjob")); + return -1; + } + + qemuBlockJobSyncBegin(job); + if (qemuDomainObjEnterMonitorAsync(vm, VIR_ASYNC_JOB_SNAPSHOT) < 0) + goto cleanup; + + rc = qemuMonitorSnapshotDelete(qemuDomainGetMonitor(vm), job->name, name, wrdevs); + qemuDomainObjExitMonitor(vm); + if (rc == 0) { + qemuBlockJobStarted(job, vm); + ret = 0; + } else { + goto cleanup; + } + + while (job->state != VIR_DOMAIN_BLOCK_JOB_COMPLETED) { + qemuDomainObjWait(vm); + qemuBlockJobUpdate(vm, job, VIR_ASYNC_JOB_NONE); + if (job->state == VIR_DOMAIN_BLOCK_JOB_FAILED) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("snapshot-delete job failed: %1$s"), NULLSTR(job->errmsg)); + ret = -1; + break; + } + } + + cleanup: + qemuBlockJobStartupFinalize(vm, job); + return ret; +} + + /* Discard one snapshot (or its metadata), without reparenting any children. */ static int qemuSnapshotDiscardImpl(virQEMUDriver *driver, @@ -XXX,XX +XXX,XX @@ qemuSnapshotDiscardImpl(virQEMUDriver *driver, if (qemuSnapshotDiscardExternal(vm, snap, externalData) < 0) return -1; } else { + qemuDomainObjPrivate *priv = vm->privateData; + /* Similarly as internal snapshot creation we would use a regular job * here so set a mask to forbid any other job. */ qemuDomainObjSetAsyncJobMask(vm, VIR_JOB_NONE); - if (qemuDomainObjEnterMonitorAsync(vm, VIR_ASYNC_JOB_SNAPSHOT) < 0) - return -1; - /* we continue on even in the face of error */ - qemuMonitorDeleteSnapshot(qemuDomainGetMonitor(vm), snap->def->name); - qemuDomainObjExitMonitor(vm); + + if (virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_SNAPSHOT_DELETE)) { + qemuSnapshotDiscardActiveInternal(vm, snap->def->name); + /* Legacy support for QEMU versions < 6.0. */ + } else { + if (qemuDomainObjEnterMonitorAsync(vm, VIR_ASYNC_JOB_SNAPSHOT) < 0) + return -1; + /* we continue on even in the face of error */ + qemuMonitorDeleteSnapshot(qemuDomainGetMonitor(vm), snap->def->name); + qemuDomainObjExitMonitor(vm); + } + qemuDomainObjSetAsyncJobMask(vm, VIR_JOB_DEFAULT_MASK); } } -- 2.43.5