QEmu 1.1 is adding a 'transaction' command to the JSON monitor. Each element of a transaction corresponds to a top-level command, with the additional guarantee that the transaction flushes all pending I/O, then guarantees that all actions will be successful as a group or that failure will roll back the state to what it was before the monitor command. The difference between a top-level command: { "execute": "blockdev-snapshot-sync", "arguments": { "device": "virtio0", ... } } and a transaction: { "execute": "transaction", "arguments": { "actions": [ { "type" "blockdev-snapshot-sync", "data": { "device": "virtio0", ... } } ] } } is just a couple of changed key names and nesting the shorter command inside a JSON array to the longer command. This patch just adds the framework; the next patch will actually use a transaction. * src/qemu/qemu_monitor_json.c (qemuMonitorJSONMakeCommand): Move guts... (qemuMonitorJSONMakeCommandRaw): ...into new helper. Add support for array element. (qemuMonitorJSONTransaction): New command. (qemuMonitorJSONDiskSnapshot): Support use in a transaction. * src/qemu/qemu_monitor_json.h (qemuMonitorJSONDiskSnapshot): Add argument. (qemuMonitorJSONTransaction): New declaration. * src/qemu/qemu_monitor.h (qemuMonitorTransaction): Likewise. (qemuMonitorDiskSnapshot): Add argument. * src/qemu/qemu_monitor.c (qemuMonitorTransaction): New wrapper. (qemuMonitorDiskSnapshot): Pass argument on. * src/qemu/qemu_driver.c (qemuDomainSnapshotCreateSingleDiskActive): Update caller. --- src/qemu/qemu_driver.c | 2 +- src/qemu/qemu_monitor.c | 33 ++++++++++++++--- src/qemu/qemu_monitor.h | 4 ++ src/qemu/qemu_monitor_json.c | 82 ++++++++++++++++++++++++++++++++--------- src/qemu/qemu_monitor_json.h | 2 + 5 files changed, 99 insertions(+), 24 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c2c3b92..c3bbc3f 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9882,7 +9882,7 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver, origdriver = NULL; /* create the actual snapshot */ - ret = qemuMonitorDiskSnapshot(priv->mon, device, source); + ret = qemuMonitorDiskSnapshot(priv->mon, NULL, device, source); virDomainAuditDisk(vm, disk->src, source, "snapshot", ret >= 0); if (ret < 0) goto cleanup; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 78eb492..1d54249 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2623,12 +2623,13 @@ int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name) * device into a read-only backing file of a new qcow2 image located * at file. */ int -qemuMonitorDiskSnapshot(qemuMonitorPtr mon, const char *device, - const char *file) +qemuMonitorDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions, + const char *device, const char *file) { int ret; - VIR_DEBUG("mon=%p, device=%s, file=%s", mon, device, file); + VIR_DEBUG("mon=%p, actions=%p, device=%s, file=%s", + mon, actions, device, file); if (!mon) { qemuReportError(VIR_ERR_INVALID_ARG, "%s", @@ -2636,10 +2637,32 @@ qemuMonitorDiskSnapshot(qemuMonitorPtr mon, const char *device, return -1; } + if (mon->json) { + ret = qemuMonitorJSONDiskSnapshot(mon, actions, device, file); + } else { + if (actions) { + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("actions not supported with text monitor")); + return -1; + } + ret = qemuMonitorTextDiskSnapshot(mon, device, file); + } + return ret; +} + +/* Use the transaction QMP command to run atomic snapshot commands. */ +int +qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr actions) +{ + int ret = -1; + + VIR_DEBUG("mon=%p, actions=%p", mon, actions); + if (mon->json) - ret = qemuMonitorJSONDiskSnapshot(mon, device, file); + ret = qemuMonitorJSONTransaction(mon, actions); else - ret = qemuMonitorTextDiskSnapshot(mon, device, file); + qemuReportError(VIR_ERR_INVALID_ARG, "%s", + _("transaction requires JSON monitor")); return ret; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 654d9bd..7e91951 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -31,6 +31,7 @@ # include "qemu_conf.h" # include "bitmap.h" # include "virhash.h" +# include "json.h" typedef struct _qemuMonitor qemuMonitor; typedef qemuMonitor *qemuMonitorPtr; @@ -492,8 +493,11 @@ int qemuMonitorLoadSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorDiskSnapshot(qemuMonitorPtr mon, + virJSONValuePtr actions, const char *device, const char *file); +int qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr actions) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, const char *cmd, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 4dd6924..ba07e84 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -369,9 +369,10 @@ qemuMonitorJSONHasError(virJSONValuePtr reply, return STREQ(klass, thisklass); } +/* Top-level commands and nested transaction list elements share a + * common structure for everything except the dictionary names. */ static virJSONValuePtr ATTRIBUTE_SENTINEL -qemuMonitorJSONMakeCommand(const char *cmdname, - ...) +qemuMonitorJSONMakeCommandRaw(bool wrap, const char *cmdname, ...) { virJSONValuePtr obj; virJSONValuePtr jargs = NULL; @@ -383,7 +384,8 @@ qemuMonitorJSONMakeCommand(const char *cmdname, if (!(obj = virJSONValueNewObject())) goto no_memory; - if (virJSONValueObjectAppendString(obj, "execute", cmdname) < 0) + if (virJSONValueObjectAppendString(obj, wrap ? "type" : "execute", + cmdname) < 0) goto no_memory; while ((key = va_arg(args, char *)) != NULL) { @@ -405,8 +407,7 @@ qemuMonitorJSONMakeCommand(const char *cmdname, !(jargs = virJSONValueNewObject())) goto no_memory; - /* This doesn't supports maps/arrays. This hasn't - * proved to be a problem..... yet :-) */ + /* This doesn't support maps, but no command uses those. */ switch (type) { case 's': { char *val = va_arg(args, char *); @@ -444,6 +445,10 @@ qemuMonitorJSONMakeCommand(const char *cmdname, case 'n': { ret = virJSONValueObjectAppendNull(jargs, key); } break; + case 'a': { + virJSONValuePtr val = va_arg(args, virJSONValuePtr); + ret = virJSONValueObjectAppend(jargs, key, val); + } break; default: qemuReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported data type '%c' for arg '%s'"), type, key - 2); @@ -454,7 +459,7 @@ qemuMonitorJSONMakeCommand(const char *cmdname, } if (jargs && - virJSONValueObjectAppend(obj, "arguments", jargs) < 0) + virJSONValueObjectAppend(obj, wrap ? "data" : "arguments", jargs) < 0) goto no_memory; va_end(args); @@ -470,6 +475,8 @@ error: return NULL; } +#define qemuMonitorJSONMakeCommand(cmdname, ...) \ + qemuMonitorJSONMakeCommandRaw(false, cmdname, __VA_ARGS__) static void qemuFreeKeywords(int nkeywords, char **keywords, char **values) @@ -3052,30 +3059,69 @@ cleanup: } int -qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, const char *device, - const char *file) +qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions, + const char *device, const char *file) { int ret; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; - cmd = qemuMonitorJSONMakeCommand("blockdev-snapshot-sync", - "s:device", device, - "s:snapshot-file", file, - NULL); + cmd = qemuMonitorJSONMakeCommandRaw(actions != NULL, + "blockdev-snapshot-sync", + "s:device", device, + "s:snapshot-file", file, + NULL); if (!cmd) return -1; - if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0) + if (actions) { + if (virJSONValueArrayAppend(actions, cmd) < 0) { + virReportOOMError(); + ret = -1; + } else { + ret = 0; + cmd = NULL; + } + } else { + if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0) goto cleanup; - if (qemuMonitorJSONHasError(reply, "CommandNotFound") && - qemuMonitorCheckHMP(mon, "snapshot_blkdev")) { - VIR_DEBUG("blockdev-snapshot-sync command not found, trying HMP"); - ret = qemuMonitorTextDiskSnapshot(mon, device, file); - goto cleanup; + if (qemuMonitorJSONHasError(reply, "CommandNotFound") && + qemuMonitorCheckHMP(mon, "snapshot_blkdev")) { + VIR_DEBUG("blockdev-snapshot-sync command not found, trying HMP"); + ret = qemuMonitorTextDiskSnapshot(mon, device, file); + goto cleanup; + } + + ret = qemuMonitorJSONCheckError(cmd, reply); + } + +cleanup: + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + +/* Note that this call frees actions regardless of whether the call + * succeeds. */ +int +qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions) +{ + int ret; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + cmd = qemuMonitorJSONMakeCommand("transaction", + "a:actions", actions, + NULL); + if (!cmd) { + virJSONValueFree(actions); + return -1; } + if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0) + goto cleanup; + ret = qemuMonitorJSONCheckError(cmd, reply); cleanup: diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 04e9d86..e127870 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -226,8 +226,10 @@ int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, + virJSONValuePtr actions, const char *device, const char *file); +int qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions); int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, const char *cmd_str, -- 1.7.7.6 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list