snapshot: add support for qemu transaction command

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.
This commit is contained in:
Eric Blake 2012-03-16 22:17:28 -06:00
parent 4c4cc1b96d
commit 64d5e815b7
5 changed files with 100 additions and 25 deletions

View File

@ -9883,7 +9883,7 @@ qemuDomainSnapshotCreateSingleDiskActive(struct qemud_driver *driver,
origdriver = NULL; origdriver = NULL;
/* create the actual snapshot */ /* 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); virDomainAuditDisk(vm, disk->src, source, "snapshot", ret >= 0);
if (ret < 0) if (ret < 0)
goto cleanup; goto cleanup;

View File

@ -2656,12 +2656,13 @@ int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name)
* device into a read-only backing file of a new qcow2 image located * device into a read-only backing file of a new qcow2 image located
* at file. */ * at file. */
int int
qemuMonitorDiskSnapshot(qemuMonitorPtr mon, const char *device, qemuMonitorDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions,
const char *file) const char *device, const char *file)
{ {
int ret; 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) { if (!mon) {
qemuReportError(VIR_ERR_INVALID_ARG, "%s", qemuReportError(VIR_ERR_INVALID_ARG, "%s",
@ -2669,10 +2670,32 @@ qemuMonitorDiskSnapshot(qemuMonitorPtr mon, const char *device,
return -1; return -1;
} }
if (mon->json) if (mon->json) {
ret = qemuMonitorJSONDiskSnapshot(mon, device, file); ret = qemuMonitorJSONDiskSnapshot(mon, actions, device, file);
else } else {
if (actions) {
qemuReportError(VIR_ERR_INVALID_ARG, "%s",
_("actions not supported with text monitor"));
return -1;
}
ret = qemuMonitorTextDiskSnapshot(mon, device, file); 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 = qemuMonitorJSONTransaction(mon, actions);
else
qemuReportError(VIR_ERR_INVALID_ARG, "%s",
_("transaction requires JSON monitor"));
return ret; return ret;
} }

View File

@ -31,6 +31,7 @@
# include "qemu_conf.h" # include "qemu_conf.h"
# include "bitmap.h" # include "bitmap.h"
# include "virhash.h" # include "virhash.h"
# include "json.h"
typedef struct _qemuMonitor qemuMonitor; typedef struct _qemuMonitor qemuMonitor;
typedef qemuMonitor *qemuMonitorPtr; typedef qemuMonitor *qemuMonitorPtr;
@ -502,8 +503,11 @@ int qemuMonitorLoadSnapshot(qemuMonitorPtr mon, const char *name);
int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorDeleteSnapshot(qemuMonitorPtr mon, const char *name);
int qemuMonitorDiskSnapshot(qemuMonitorPtr mon, int qemuMonitorDiskSnapshot(qemuMonitorPtr mon,
virJSONValuePtr actions,
const char *device, const char *device,
const char *file); const char *file);
int qemuMonitorTransaction(qemuMonitorPtr mon, virJSONValuePtr actions)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
int qemuMonitorArbitraryCommand(qemuMonitorPtr mon, int qemuMonitorArbitraryCommand(qemuMonitorPtr mon,
const char *cmd, const char *cmd,

View File

@ -375,9 +375,10 @@ qemuMonitorJSONHasError(virJSONValuePtr reply,
return STREQ(klass, thisklass); 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 static virJSONValuePtr ATTRIBUTE_SENTINEL
qemuMonitorJSONMakeCommand(const char *cmdname, qemuMonitorJSONMakeCommandRaw(bool wrap, const char *cmdname, ...)
...)
{ {
virJSONValuePtr obj; virJSONValuePtr obj;
virJSONValuePtr jargs = NULL; virJSONValuePtr jargs = NULL;
@ -389,7 +390,8 @@ qemuMonitorJSONMakeCommand(const char *cmdname,
if (!(obj = virJSONValueNewObject())) if (!(obj = virJSONValueNewObject()))
goto no_memory; goto no_memory;
if (virJSONValueObjectAppendString(obj, "execute", cmdname) < 0) if (virJSONValueObjectAppendString(obj, wrap ? "type" : "execute",
cmdname) < 0)
goto no_memory; goto no_memory;
while ((key = va_arg(args, char *)) != NULL) { while ((key = va_arg(args, char *)) != NULL) {
@ -411,8 +413,7 @@ qemuMonitorJSONMakeCommand(const char *cmdname,
!(jargs = virJSONValueNewObject())) !(jargs = virJSONValueNewObject()))
goto no_memory; goto no_memory;
/* This doesn't supports maps/arrays. This hasn't /* This doesn't support maps, but no command uses those. */
* proved to be a problem..... yet :-) */
switch (type) { switch (type) {
case 's': { case 's': {
char *val = va_arg(args, char *); char *val = va_arg(args, char *);
@ -450,6 +451,10 @@ qemuMonitorJSONMakeCommand(const char *cmdname,
case 'n': { case 'n': {
ret = virJSONValueObjectAppendNull(jargs, key); ret = virJSONValueObjectAppendNull(jargs, key);
} break; } break;
case 'a': {
virJSONValuePtr val = va_arg(args, virJSONValuePtr);
ret = virJSONValueObjectAppend(jargs, key, val);
} break;
default: default:
qemuReportError(VIR_ERR_INTERNAL_ERROR, qemuReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported data type '%c' for arg '%s'"), type, key - 2); _("unsupported data type '%c' for arg '%s'"), type, key - 2);
@ -460,7 +465,7 @@ qemuMonitorJSONMakeCommand(const char *cmdname,
} }
if (jargs && if (jargs &&
virJSONValueObjectAppend(obj, "arguments", jargs) < 0) virJSONValueObjectAppend(obj, wrap ? "data" : "arguments", jargs) < 0)
goto no_memory; goto no_memory;
va_end(args); va_end(args);
@ -476,6 +481,8 @@ error:
return NULL; return NULL;
} }
#define qemuMonitorJSONMakeCommand(cmdname, ...) \
qemuMonitorJSONMakeCommandRaw(false, cmdname, __VA_ARGS__)
static void static void
qemuFreeKeywords(int nkeywords, char **keywords, char **values) qemuFreeKeywords(int nkeywords, char **keywords, char **values)
@ -3097,30 +3104,69 @@ cleanup:
} }
int int
qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, const char *device, qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions,
const char *file) const char *device, const char *file)
{ {
int ret; int ret;
virJSONValuePtr cmd; virJSONValuePtr cmd;
virJSONValuePtr reply = NULL; virJSONValuePtr reply = NULL;
cmd = qemuMonitorJSONMakeCommand("blockdev-snapshot-sync", cmd = qemuMonitorJSONMakeCommandRaw(actions != NULL,
"s:device", device, "blockdev-snapshot-sync",
"s:snapshot-file", file, "s:device", device,
NULL); "s:snapshot-file", file,
NULL);
if (!cmd) if (!cmd)
return -1; 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; goto cleanup;
if (qemuMonitorJSONHasError(reply, "CommandNotFound") && if (qemuMonitorJSONHasError(reply, "CommandNotFound") &&
qemuMonitorCheckHMP(mon, "snapshot_blkdev")) { qemuMonitorCheckHMP(mon, "snapshot_blkdev")) {
VIR_DEBUG("blockdev-snapshot-sync command not found, trying HMP"); VIR_DEBUG("blockdev-snapshot-sync command not found, trying HMP");
ret = qemuMonitorTextDiskSnapshot(mon, device, file); ret = qemuMonitorTextDiskSnapshot(mon, device, file);
goto cleanup; 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); ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup: cleanup:

View File

@ -226,8 +226,10 @@ int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name);
int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name);
int qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, int qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon,
virJSONValuePtr actions,
const char *device, const char *device,
const char *file); const char *file);
int qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions);
int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
const char *cmd_str, const char *cmd_str,