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