Blame SOURCES/libvirt-util-storage-Add-parser-for-qemu-s-json-backing-pseudo-protocol.patch

6ae9ed
From 296915a9c0a8de38ed83203f21811ef19465d69e Mon Sep 17 00:00:00 2001
6ae9ed
Message-Id: <296915a9c0a8de38ed83203f21811ef19465d69e@dist-git>
6ae9ed
From: Peter Krempa <pkrempa@redhat.com>
6ae9ed
Date: Tue, 2 Aug 2016 13:41:48 +0200
6ae9ed
Subject: [PATCH] util: storage: Add parser for qemu's json backing
6ae9ed
 pseudo-protocol
6ae9ed
6ae9ed
Add a modular parser that will allow to parse 'json' backing definitions
6ae9ed
that are supported by qemu. The initial implementation adds support for
6ae9ed
the 'file' driver.
6ae9ed
6ae9ed
Due to the approach qemu took to implement the JSON backing strings it's
6ae9ed
possible to specify them in two approaches.
6ae9ed
6ae9ed
The object approach:
6ae9ed
    json:{ "file" : { "driver":"file",
6ae9ed
                      "filename":"/path/to/file"
6ae9ed
                    }
6ae9ed
         }
6ae9ed
6ae9ed
And a partially flattened approach:
6ae9ed
    json:{"file.driver":"file"
6ae9ed
          "file.filename":"/path/to/file"
6ae9ed
         }
6ae9ed
6ae9ed
Both of the above are supported by qemu and by the code added in this
6ae9ed
commit. The current implementation de-flattens the first level ('file.')
6ae9ed
if possible and required. Other handling may be added later but
6ae9ed
currently only one level was possible anyways.
6ae9ed
6ae9ed
(cherry picked from commit e91f767c7433d9216c741f441196cd67c1934375)
6ae9ed
https://bugzilla.redhat.com/show_bug.cgi?id=1134878 [JSON backing]
6ae9ed
https://bugzilla.redhat.com/show_bug.cgi?id=1247521 [gluster multi-host]
6ae9ed
---
6ae9ed
 src/util/virstoragefile.c | 161 ++++++++++++++++++++++++++++++++++++++++++++--
6ae9ed
 tests/virstoragetest.c    |  15 +++++
6ae9ed
 2 files changed, 169 insertions(+), 7 deletions(-)
6ae9ed
6ae9ed
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
6ae9ed
index aa5f302..25f363d 100644
6ae9ed
--- a/src/util/virstoragefile.c
6ae9ed
+++ b/src/util/virstoragefile.c
6ae9ed
@@ -43,6 +43,7 @@
6ae9ed
 #include "viruri.h"
6ae9ed
 #include "dirname.h"
6ae9ed
 #include "virbuffer.h"
6ae9ed
+#include "virjson.h"
6ae9ed
 
6ae9ed
 #define VIR_FROM_THIS VIR_FROM_STORAGE
6ae9ed
 
6ae9ed
@@ -2523,10 +2524,154 @@ virStorageSourceParseBackingColon(virStorageSourcePtr src,
6ae9ed
 }
6ae9ed
 
6ae9ed
 
6ae9ed
+static int
6ae9ed
+virStorageSourceParseBackingJSONPath(virStorageSourcePtr src,
6ae9ed
+                                     virJSONValuePtr json,
6ae9ed
+                                     int type)
6ae9ed
+{
6ae9ed
+    const char *path;
6ae9ed
+
6ae9ed
+    if (!(path = virJSONValueObjectGetString(json, "filename"))) {
6ae9ed
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
6ae9ed
+                       _("missing 'filename' field in JSON backing volume "
6ae9ed
+                         "definition"));
6ae9ed
+        return -1;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (VIR_STRDUP(src->path, path) < 0)
6ae9ed
+        return -1;
6ae9ed
+
6ae9ed
+    src->type = type;
6ae9ed
+    return 0;
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
+struct virStorageSourceJSONDriverParser {
6ae9ed
+    const char *drvname;
6ae9ed
+    int (*func)(virStorageSourcePtr src, virJSONValuePtr json, int opaque);
6ae9ed
+    int opaque;
6ae9ed
+};
6ae9ed
+
6ae9ed
+static const struct virStorageSourceJSONDriverParser jsonParsers[] = {
6ae9ed
+    {"file", virStorageSourceParseBackingJSONPath, VIR_STORAGE_TYPE_FILE},
6ae9ed
+};
6ae9ed
+
6ae9ed
+
6ae9ed
+static int
6ae9ed
+virStorageSourceParseBackingJSONDeflattenWorker(const char *key,
6ae9ed
+                                                const virJSONValue *value,
6ae9ed
+                                                void *opaque)
6ae9ed
+{
6ae9ed
+    virJSONValuePtr retobj = opaque;
6ae9ed
+    virJSONValuePtr newval = NULL;
6ae9ed
+    const char *newkey;
6ae9ed
+
6ae9ed
+    if (!(newkey = STRSKIP(key, "file."))) {
6ae9ed
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
6ae9ed
+                       _("JSON backing file syntax is neither nested nor "
6ae9ed
+                         "flattened"));
6ae9ed
+        return -1;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (!(newval = virJSONValueCopy(value)))
6ae9ed
+        return -1;
6ae9ed
+
6ae9ed
+    if (virJSONValueObjectAppend(retobj, newkey, newval) < 0) {
6ae9ed
+        virJSONValueFree(newval);
6ae9ed
+        return -1;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    return 0;
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
+/**
6ae9ed
+ * virStorageSourceParseBackingJSONDeflatten:
6ae9ed
+ *
6ae9ed
+ * The json: pseudo-protocol syntax in qemu allows multiple approaches to
6ae9ed
+ * describe nesting of the values. This is due to the lax handling of the string
6ae9ed
+ * in qemu and the fact that internally qemu is flattening the values using '.'.
6ae9ed
+ *
6ae9ed
+ * This allows to specify nested json strings either using nested json objects
6ae9ed
+ * or prefixing object members with the parent object name followed by the dot.
6ae9ed
+ *
6ae9ed
+ * This function will attempt to reverse the process and provide a nested json
6ae9ed
+ * hierarchy so that the parsers can be kept simple and we still can use the
6ae9ed
+ * weird syntax some users might use.
6ae9ed
+ *
6ae9ed
+ * Currently this function will flatten out just the 'file.' prefix into a new
6ae9ed
+ * tree. Any other syntax will be rejected.
6ae9ed
+ */
6ae9ed
+static virJSONValuePtr
6ae9ed
+virStorageSourceParseBackingJSONDeflatten(virJSONValuePtr json)
6ae9ed
+{
6ae9ed
+    virJSONValuePtr ret;
6ae9ed
+
6ae9ed
+    if (!(ret = virJSONValueNewObject()))
6ae9ed
+        return NULL;
6ae9ed
+
6ae9ed
+    if (virJSONValueObjectForeachKeyValue(json,
6ae9ed
+                                          virStorageSourceParseBackingJSONDeflattenWorker,
6ae9ed
+                                          ret) < 0) {
6ae9ed
+        virJSONValueFree(ret);
6ae9ed
+        return NULL;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    return ret;
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
+static int
6ae9ed
+virStorageSourceParseBackingJSON(virStorageSourcePtr src,
6ae9ed
+                                 const char *json)
6ae9ed
+{
6ae9ed
+    virJSONValuePtr root = NULL;
6ae9ed
+    virJSONValuePtr fixedroot = NULL;
6ae9ed
+    virJSONValuePtr file;
6ae9ed
+    const char *drvname;
6ae9ed
+    size_t i;
6ae9ed
+    int ret = -1;
6ae9ed
+
6ae9ed
+    if (!(root = virJSONValueFromString(json)))
6ae9ed
+        return -1;
6ae9ed
+
6ae9ed
+    if (!(file = virJSONValueObjectGetObject(root, "file"))) {
6ae9ed
+        if (!(fixedroot = virStorageSourceParseBackingJSONDeflatten(root)))
6ae9ed
+            goto cleanup;
6ae9ed
+
6ae9ed
+        file = fixedroot;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    if (!(drvname = virJSONValueObjectGetString(file, "driver"))) {
6ae9ed
+        virReportError(VIR_ERR_INVALID_ARG, _("JSON backing volume defintion "
6ae9ed
+                                              "'%s' lacks driver name"), json);
6ae9ed
+        goto cleanup;
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    for (i = 0; i < ARRAY_CARDINALITY(jsonParsers); i++) {
6ae9ed
+        if (STREQ(drvname, jsonParsers[i].drvname)) {
6ae9ed
+            ret = jsonParsers[i].func(src, file, jsonParsers[i].opaque);
6ae9ed
+            goto cleanup;
6ae9ed
+        }
6ae9ed
+    }
6ae9ed
+
6ae9ed
+    virReportError(VIR_ERR_INTERNAL_ERROR,
6ae9ed
+                   _("missing parser implementation for JSON backing volume "
6ae9ed
+                     "driver '%s'"), drvname);
6ae9ed
+
6ae9ed
+ cleanup:
6ae9ed
+    virJSONValueFree(root);
6ae9ed
+    virJSONValueFree(fixedroot);
6ae9ed
+    return ret;
6ae9ed
+}
6ae9ed
+
6ae9ed
+
6ae9ed
 virStorageSourcePtr
6ae9ed
 virStorageSourceNewFromBackingAbsolute(const char *path)
6ae9ed
 {
6ae9ed
+    const char *json;
6ae9ed
     virStorageSourcePtr ret;
6ae9ed
+    int rc;
6ae9ed
 
6ae9ed
     if (VIR_ALLOC(ret) < 0)
6ae9ed
         return NULL;
6ae9ed
@@ -2540,13 +2685,15 @@ virStorageSourceNewFromBackingAbsolute(const char *path)
6ae9ed
         ret->type = VIR_STORAGE_TYPE_NETWORK;
6ae9ed
 
6ae9ed
         /* handle URI formatted backing stores */
6ae9ed
-        if (strstr(path, "://")) {
6ae9ed
-            if (virStorageSourceParseBackingURI(ret, path) < 0)
6ae9ed
-                goto error;
6ae9ed
-        } else {
6ae9ed
-            if (virStorageSourceParseBackingColon(ret, path) < 0)
6ae9ed
-                goto error;
6ae9ed
-        }
6ae9ed
+        if ((json = STRSKIP(path, "json:")))
6ae9ed
+            rc = virStorageSourceParseBackingJSON(ret, json);
6ae9ed
+        else if (strstr(path, "://"))
6ae9ed
+            rc = virStorageSourceParseBackingURI(ret, path);
6ae9ed
+        else
6ae9ed
+            rc = virStorageSourceParseBackingColon(ret, path);
6ae9ed
+
6ae9ed
+        if (rc < 0)
6ae9ed
+            goto error;
6ae9ed
     }
6ae9ed
 
6ae9ed
     return ret;
6ae9ed
diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c
6ae9ed
index 6016a3b..04575f2 100644
6ae9ed
--- a/tests/virstoragetest.c
6ae9ed
+++ b/tests/virstoragetest.c
6ae9ed
@@ -1355,6 +1355,21 @@ mymain(void)
6ae9ed
                        "<source protocol='nbd' name='blah'>\n"
6ae9ed
                        "  <host name='example.org' port='6000'/>\n"
6ae9ed
                        "</source>\n");
6ae9ed
+    TEST_BACKING_PARSE("json:", NULL);
6ae9ed
+    TEST_BACKING_PARSE("json:asdgsdfg", NULL);
6ae9ed
+    TEST_BACKING_PARSE("json:{}", NULL);
6ae9ed
+    TEST_BACKING_PARSE("json: { \"file.driver\":\"blah\"}", NULL);
6ae9ed
+    TEST_BACKING_PARSE("json:{\"file.driver\":\"file\"}", NULL);
6ae9ed
+    TEST_BACKING_PARSE("json:{\"file.driver\":\"file\", "
6ae9ed
+                             "\"file.filename\":\"/path/to/file\"}",
6ae9ed
+                       "<source file='/path/to/file'/>\n");
6ae9ed
+    TEST_BACKING_PARSE("json:{\"file.driver\":\"file\", "
6ae9ed
+                             "\"filename\":\"/path/to/file\"}", NULL);
6ae9ed
+    TEST_BACKING_PARSE("json:{\"file\" : { \"driver\":\"file\","
6ae9ed
+                                          "\"filename\":\"/path/to/file\""
6ae9ed
+                                        "}"
6ae9ed
+                            "}",
6ae9ed
+                       "<source file='/path/to/file'/>\n");
6ae9ed
 
6ae9ed
  cleanup:
6ae9ed
     /* Final cleanup */
6ae9ed
-- 
6ae9ed
2.9.2
6ae9ed