[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[libvirt] [PATCH 1/2] 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.
---
 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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]