All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Daniel P. Berrange" <berrange@redhat.com>
To: qemu-devel@nongnu.org
Cc: qemu-block@nongnu.org, "Markus Armbruster" <armbru@redhat.com>,
	"Max Reitz" <mreitz@redhat.com>,
	"Paolo Bonzini" <pbonzini@redhat.com>,
	"Andreas Färber" <afaerber@suse.de>,
	"Eric Blake" <eblake@redhat.com>,
	"Daniel P. Berrange" <berrange@redhat.com>
Subject: [Qemu-devel] [PATCH v14 13/21] qdict: allow qdict_crumple to accept compound types as values
Date: Fri, 30 Sep 2016 15:45:36 +0100	[thread overview]
Message-ID: <1475246744-29302-14-git-send-email-berrange@redhat.com> (raw)
In-Reply-To: <1475246744-29302-1-git-send-email-berrange@redhat.com>

Currently qdict_crumple requires a totally flat QDict as its
input. i.e. all values in the QDict must be scalar types.

In order to have backwards compatibility with the OptsVisitor,
qemu_opt_to_qdict() has a new mode where it may return a QList
for values in the QDict, if there was a repeated key. We thus
need to allow compound types to appear as values in the input
dict given to qdict_crumple().

To avoid confusion, we sanity check that the user has not mixed
the old and new syntax at the same time. e.g. these are allowed

   foo=hello,foo=world,foo=wibble
   foo.0=hello,foo.1=world,foo.2=wibble

but this is forbidden

   foo=hello,foo=world,foo.2=wibble

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 qobject/qdict.c     |  45 +++++++++-----
 tests/check-qdict.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 204 insertions(+), 16 deletions(-)

diff --git a/qobject/qdict.c b/qobject/qdict.c
index c38e90e..83384b2 100644
--- a/qobject/qdict.c
+++ b/qobject/qdict.c
@@ -814,15 +814,16 @@ static int qdict_is_list(QDict *maybe_list, Error **errp)
 
 /**
  * qdict_crumple:
- * @src: the original flat dictionary (only scalar values) to crumple
+ * @src: the original dictionary to crumple
  * @recursive: true to recursively crumple nested dictionaries
  *
- * Takes a flat dictionary whose keys use '.' separator to indicate
- * nesting, and values are scalars, and crumples it into a nested
- * structure. If the @recursive parameter is false, then only the
- * first level of structure implied by the keys will be crumpled. If
- * @recursive is true, then the input will be recursively crumpled to
- * expand all levels of structure in the keys.
+ * Takes a dictionary whose keys use '.' separator to indicate
+ * nesting, crumples it into a nested structure. The values in
+ * @src are permitted to be scalars or compound typee. If the
+ * @recursive parameter is false, then only the first level of
+ * structure implied by the keys will be crumpled. If @recursive
+ * is true, then the input will be recursively crumpled to expand
+ * all levels of structure in the keys.
  *
  * To include a literal '.' in a key name, it must be escaped as '..'
  *
@@ -843,7 +844,10 @@ static int qdict_is_list(QDict *maybe_list, Error **errp)
  * The following scenarios in the input dict will result in an
  * error being returned:
  *
- *  - Any values in @src are non-scalar types
+ *  - If a key in @src has a value that is non-scalar, and
+ *    a later key implies creation of a compound type. e.g.
+ *    if 'foo' point to a 'QList' or 'QDict', then it is
+ *    not permitted to also have 'foo.NNN' keys later.
  *  - If keys in @src imply that a particular level is both a
  *    list and a dict. e.g., "foo.0.bar" and "foo.eek.bar".
  *  - If keys in @src imply that a particular level is a list,
@@ -864,21 +868,26 @@ QObject *qdict_crumple(const QDict *src, bool recursive, Error **errp)
     char *prefix = NULL;
     const char *suffix = NULL;
     int is_list;
+    QDict *compound;
 
+    compound = qdict_new();
     two_level = qdict_new();
 
     /* Step 1: split our totally flat dict into a two level dict */
     for (ent = qdict_first(src); ent != NULL; ent = qdict_next(src, ent)) {
-        if (qobject_type(ent->value) == QTYPE_QDICT ||
-            qobject_type(ent->value) == QTYPE_QLIST) {
-            error_setg(errp, "Value %s is not a scalar",
-                       ent->key);
-            goto error;
-        }
-
         qdict_split_flat_key(ent->key, &prefix, &suffix);
 
         child = qdict_get(two_level, prefix);
+
+        if (qdict_haskey(compound, prefix)) {
+            error_setg(errp, "Key %s is already set as a list/dict", prefix);
+            goto error;
+        }
+        if (qobject_type(ent->value) == QTYPE_QLIST ||
+            qobject_type(ent->value) == QTYPE_QDICT) {
+            qobject_incref(ent->value);
+            qdict_put_obj(compound, prefix, ent->value);
+        }
         if (suffix) {
             if (child) {
                 if (qobject_type(child) != QTYPE_QDICT) {
@@ -913,7 +922,8 @@ QObject *qdict_crumple(const QDict *src, bool recursive, Error **errp)
         for (ent = qdict_first(two_level); ent != NULL;
              ent = qdict_next(two_level, ent)) {
 
-            if (qobject_type(ent->value) == QTYPE_QDICT) {
+            if (qobject_type(ent->value) == QTYPE_QDICT &&
+                !qdict_haskey(compound, ent->key)) {
                 child = qdict_crumple(qobject_to_qdict(ent->value),
                                       recursive, errp);
                 if (!child) {
@@ -961,10 +971,13 @@ QObject *qdict_crumple(const QDict *src, bool recursive, Error **errp)
         dst = QOBJECT(multi_level);
     }
 
+    QDECREF(compound);
+
     return dst;
 
  error:
     g_free(prefix);
+    QDECREF(compound);
     QDECREF(multi_level);
     QDECREF(two_level);
     qobject_decref(dst);
diff --git a/tests/check-qdict.c b/tests/check-qdict.c
index 64c33ab..29d7362 100644
--- a/tests/check-qdict.c
+++ b/tests/check-qdict.c
@@ -844,6 +844,173 @@ static void qdict_crumple_test_bad_inputs(void)
     QDECREF(src);
 }
 
+static void qdict_crumple_test_non_flat_list_ok(void)
+{
+    QDict *src, *dst, *vnc, *listen;
+    QObject *child, *res;
+    QList *list;
+    QString *match;
+
+    src = qdict_new();
+    list = qlist_new();
+    qlist_append(list, qstring_from_str("fred"));
+    qlist_append(list, qstring_from_str("bob"));
+
+    qdict_put(src, "vnc.listen.addr", qstring_from_str("127.0.0.1"));
+    qdict_put(src, "vnc.listen.port", qstring_from_str("5901"));
+    qdict_put(src, "match", list);
+
+    res = qdict_crumple(src, true, &error_abort);
+
+    g_assert_cmpint(qobject_type(res), ==, QTYPE_QDICT);
+
+    dst = qobject_to_qdict(res);
+
+    g_assert_cmpint(qdict_size(dst), ==, 2);
+
+    child = qdict_get(dst, "vnc");
+    g_assert_cmpint(qobject_type(child), ==, QTYPE_QDICT);
+    vnc = qobject_to_qdict(child);
+
+    child = qdict_get(vnc, "listen");
+    g_assert_cmpint(qobject_type(child), ==, QTYPE_QDICT);
+    listen = qobject_to_qdict(child);
+    g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(listen, "addr"));
+    g_assert_cmpstr("5901", ==, qdict_get_str(listen, "port"));
+
+    child = qdict_get(dst, "match");
+    g_assert_cmpint(qobject_type(child), ==, QTYPE_QLIST);
+    list = qobject_to_qlist(child);
+
+    g_assert_cmpint(qlist_size(list), ==, 2);
+
+    child = qlist_pop(list);
+    g_assert(child);
+    match = qobject_to_qstring(child);
+    g_assert_cmpstr("fred", ==, qstring_get_str(match));
+    qobject_decref(child);
+
+    child = qlist_pop(list);
+    g_assert(child);
+    match = qobject_to_qstring(child);
+    g_assert_cmpstr("bob", ==, qstring_get_str(match));
+    qobject_decref(child);
+
+    child = qlist_pop(list);
+    g_assert(!child);
+
+    QDECREF(src);
+    QDECREF(dst);
+}
+
+
+static void qdict_crumple_test_non_flat_list_bad(void)
+{
+    QDict *src;
+    QObject *res;
+    QList *list;
+    Error *err = NULL;
+
+    src = qdict_new();
+    list = qlist_new();
+    qlist_append(list, qstring_from_str("fred"));
+    qlist_append(list, qstring_from_str("bob"));
+
+    qdict_put(src, "vnc.listen.addr", qstring_from_str("127.0.0.1"));
+    qdict_put(src, "vnc.listen.port", qstring_from_str("5901"));
+    qdict_put(src, "match.0", qstring_from_str("frank"));
+    qdict_put(src, "match", list);
+
+    res = qdict_crumple(src, false, &err);
+    g_assert(!res);
+    error_free_or_abort(&err);
+
+    QDECREF(src);
+}
+
+
+static void qdict_crumple_test_non_flat_dict_ok(void)
+{
+    QDict *src, *dst, *rule, *vnc, *acl;
+    QObject *child, *res;
+    QList *rules;
+
+    vnc = qdict_new();
+    qdict_put(vnc, "listen.addr", qstring_from_str("127.0.0.1"));
+    qdict_put(vnc, "listen.port", qstring_from_str("5901"));
+
+    src = qdict_new();
+    qdict_put(src, "vnc", vnc);
+    qdict_put(src, "acl.rules.0.match", qstring_from_str("fred"));
+    qdict_put(src, "acl.rules.0.policy", qstring_from_str("allow"));
+    qdict_put(src, "acl.rules.1.match", qstring_from_str("bob"));
+    qdict_put(src, "acl.rules.1.policy", qstring_from_str("deny"));
+
+    res = qdict_crumple(src, true, &error_abort);
+
+    g_assert_cmpint(qobject_type(res), ==, QTYPE_QDICT);
+
+    dst = qobject_to_qdict(res);
+
+    g_assert_cmpint(qdict_size(dst), ==, 2);
+
+    child = qdict_get(dst, "vnc");
+    g_assert_cmpint(qobject_type(child), ==, QTYPE_QDICT);
+    vnc = qobject_to_qdict(child);
+
+    g_assert_cmpstr("127.0.0.1", ==, qdict_get_str(vnc, "listen.addr"));
+    g_assert_cmpstr("5901", ==, qdict_get_str(vnc, "listen.port"));
+
+    child = qdict_get(dst, "acl");
+    g_assert_cmpint(qobject_type(child), ==, QTYPE_QDICT);
+    acl = qobject_to_qdict(child);
+
+    child = qdict_get(acl, "rules");
+    g_assert_cmpint(qobject_type(child), ==, QTYPE_QLIST);
+    rules = qobject_to_qlist(child);
+    g_assert_cmpint(qlist_size(rules), ==, 2);
+
+    rule = qobject_to_qdict(qlist_pop(rules));
+    g_assert_cmpint(qdict_size(rule), ==, 2);
+    g_assert_cmpstr("fred", ==, qdict_get_str(rule, "match"));
+    g_assert_cmpstr("allow", ==, qdict_get_str(rule, "policy"));
+    QDECREF(rule);
+
+    rule = qobject_to_qdict(qlist_pop(rules));
+    g_assert_cmpint(qdict_size(rule), ==, 2);
+    g_assert_cmpstr("bob", ==, qdict_get_str(rule, "match"));
+    g_assert_cmpstr("deny", ==, qdict_get_str(rule, "policy"));
+    QDECREF(rule);
+
+    QDECREF(src);
+    QDECREF(dst);
+}
+
+
+static void qdict_crumple_test_non_flat_dict_bad(void)
+{
+    QDict *src, *vnc;
+    QObject *res;
+    Error *err = NULL;
+
+    vnc = qdict_new();
+    qdict_put(vnc, "listen.addr", qstring_from_str("127.0.0.1"));
+
+    src = qdict_new();
+    qdict_put(src, "vnc", vnc);
+    qdict_put(src, "vnc.listen.port", qstring_from_str("5901"));
+    qdict_put(src, "acl.rules.0.match", qstring_from_str("fred"));
+    qdict_put(src, "acl.rules.0.policy", qstring_from_str("allow"));
+    qdict_put(src, "acl.rules.1.match", qstring_from_str("bob"));
+    qdict_put(src, "acl.rules.1.policy", qstring_from_str("deny"));
+
+    res = qdict_crumple(src, true, &err);
+    g_assert(!res);
+    error_free_or_abort(&err);
+
+    QDECREF(src);
+}
+
 
 /*
  * Errors test-cases
@@ -1002,6 +1169,14 @@ int main(int argc, char **argv)
                     qdict_crumple_test_empty);
     g_test_add_func("/public/crumple/bad_inputs",
                     qdict_crumple_test_bad_inputs);
+    g_test_add_func("/public/crumple/non-flat-list-ok",
+                    qdict_crumple_test_non_flat_list_ok);
+    g_test_add_func("/public/crumple/non-flat-list-bad",
+                    qdict_crumple_test_non_flat_list_bad);
+    g_test_add_func("/public/crumple/non-flat-dict-ok",
+                    qdict_crumple_test_non_flat_dict_ok);
+    g_test_add_func("/public/crumple/non-flat-dict-bad",
+                    qdict_crumple_test_non_flat_dict_bad);
 
     /* The Big one */
     if (g_test_slow()) {
-- 
2.7.4

  parent reply	other threads:[~2016-09-30 14:46 UTC|newest]

Thread overview: 109+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-09-30 14:45 [Qemu-devel] [PATCH v14 00/21] QAPI/QOM work for non-scalar object properties Daniel P. Berrange
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 01/21] option: make parse_option_bool/number non-static Daniel P. Berrange
2016-10-21 16:55   ` Markus Armbruster
2016-10-21 17:12     ` Eric Blake
2016-10-21 17:51       ` Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 02/21] qdict: implement a qdict_crumple method for un-flattening a dict Daniel P. Berrange
2016-10-18 14:32   ` Markus Armbruster
2016-10-20 14:11     ` Daniel P. Berrange
2016-10-21  9:58       ` Markus Armbruster
2016-10-21 18:31         ` Max Reitz
2016-10-24  9:18           ` Markus Armbruster
2016-10-24 10:28             ` Daniel P. Berrange
2016-10-24 12:38               ` Markus Armbruster
2016-10-25 10:03           ` Markus Armbruster
2016-10-25 10:20             ` Daniel P. Berrange
2016-10-25 12:29               ` Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 03/21] qapi: add trace events for visitor Daniel P. Berrange
2016-09-30 15:49   ` Eric Blake
2016-10-06 14:39   ` [Qemu-devel] [Qemu-block] " Kevin Wolf
2016-10-07 13:59   ` [Qemu-devel] " Markus Armbruster
2016-10-07 14:16     ` Daniel P. Berrange
2016-10-21 10:52       ` Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 04/21] qapi: rename QmpInputVisitor to QObjectInputVisitor Daniel P. Berrange
2016-10-25 13:41   ` Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 05/21] qapi: rename QmpOutputVisitor to QObjectOutputVisitor Daniel P. Berrange
2016-10-25 13:36   ` Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 06/21] qapi: don't pass two copies of TestInputVisitorData to tests Daniel P. Berrange
2016-09-30 17:43   ` Eric Blake
2016-10-06 14:39   ` [Qemu-devel] [Qemu-block] " Kevin Wolf
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 07/21] qapi: permit scalar type conversions in QObjectInputVisitor Daniel P. Berrange
2016-09-30 17:48   ` Eric Blake
2016-10-11 16:20   ` Markus Armbruster
2016-10-12 14:51     ` Markus Armbruster
2016-10-12 15:05       ` Markus Armbruster
2016-10-12 15:26         ` Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 08/21] qapi: allow QObjectInputVisitor to be created with QemuOpts Daniel P. Berrange
2016-09-30 17:55   ` Eric Blake
2016-10-06 14:56   ` [Qemu-devel] [Qemu-block] " Kevin Wolf
2016-10-12  8:08   ` [Qemu-devel] " Markus Armbruster
2016-10-13  7:23     ` Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 09/21] qapi: permit auto-creating single element lists Daniel P. Berrange
2016-09-30 17:59   ` Eric Blake
2016-10-12  9:18   ` Markus Armbruster
2016-10-20 14:23     ` Daniel P. Berrange
2016-10-21 11:58       ` Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 10/21] qapi: permit auto-creating nested structs Daniel P. Berrange
2016-09-30 18:23   ` Eric Blake
2016-10-06 15:10   ` [Qemu-devel] [Qemu-block] " Kevin Wolf
2016-10-06 15:18     ` Daniel P. Berrange
2016-10-06 15:30       ` Kevin Wolf
2016-10-06 15:39         ` Daniel P. Berrange
2016-10-06 15:51           ` Kevin Wolf
2016-10-06 15:57             ` Daniel P. Berrange
2016-10-12 14:00               ` Markus Armbruster
2016-10-06 17:54         ` Eric Blake
2016-10-12 14:12   ` [Qemu-devel] " Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 11/21] qapi: add integer range support for QObjectInputVisitor Daniel P. Berrange
2016-09-30 19:45   ` Eric Blake
2016-10-12 15:50   ` Markus Armbruster
2016-10-12 16:03     ` [Qemu-devel] [Qemu-block] " Kevin Wolf
2016-10-12 18:14       ` Markus Armbruster
2016-10-20 14:28     ` [Qemu-devel] " Daniel P. Berrange
2016-10-21 10:58       ` Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 12/21] option: allow qemu_opts_to_qdict to merge repeated options Daniel P. Berrange
2016-09-30 20:08   ` Eric Blake
2016-10-12 17:46   ` Markus Armbruster
2016-10-13  9:21     ` Markus Armbruster
2016-10-20 14:29     ` Daniel P. Berrange
2016-10-21 11:09       ` Markus Armbruster
2016-10-21 11:14         ` Daniel P. Berrange
2016-10-13  8:31   ` Markus Armbruster
2016-10-20 14:37     ` Daniel P. Berrange
2016-10-21 11:28       ` Markus Armbruster
2016-09-30 14:45 ` Daniel P. Berrange [this message]
2016-10-13 12:35   ` [Qemu-devel] [PATCH v14 13/21] qdict: allow qdict_crumple to accept compound types as values Markus Armbruster
2016-10-13 14:46     ` Kevin Wolf
2016-10-17 14:50       ` Markus Armbruster
2016-10-17 15:43         ` Paolo Bonzini
2016-10-17 17:48           ` Markus Armbruster
2016-10-17 17:38         ` Eric Blake
2016-10-18 10:59           ` Markus Armbruster
2016-10-18  9:34         ` Kevin Wolf
2016-10-18 15:35           ` Markus Armbruster
2016-10-19  9:25             ` Kevin Wolf
2016-10-19  9:42               ` Daniel P. Berrange
2016-10-19 13:31             ` [Qemu-devel] [Qemu-block] " Eric Blake
2016-10-20 14:46     ` [Qemu-devel] " Daniel P. Berrange
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 14/21] qapi: allow repeated opts with qobject_input_visitor_new_opts Daniel P. Berrange
2016-10-18 17:13   ` Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 15/21] qom: support non-scalar properties with -object Daniel P. Berrange
2016-10-19 16:54   ` Markus Armbruster
2017-07-10 19:30   ` Manos Pitsidianakis
2017-07-11  8:10     ` Markus Armbruster
2017-07-11 14:44       ` Markus Armbruster
2017-07-11 14:49         ` Daniel P. Berrange
2017-07-12 17:56           ` Manos Pitsidianakis
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 16/21] hmp: support non-scalar properties with object_add Daniel P. Berrange
2016-10-20  6:43   ` Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 17/21] numa: convert to use QObjectInputVisitor for -numa Daniel P. Berrange
2016-10-20  6:57   ` Markus Armbruster
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 18/21] block: convert crypto driver to use QObjectInputVisitor Daniel P. Berrange
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 19/21] acpi: convert to QObjectInputVisitor for -acpi parsing Daniel P. Berrange
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 20/21] net: convert to QObjectInputVisitor for -net/-netdev parsing Daniel P. Berrange
2016-10-20  7:38   ` Markus Armbruster
2016-10-20 13:43     ` [Qemu-devel] [Qemu-block] " Eric Blake
2016-09-30 14:45 ` [Qemu-devel] [PATCH v14 21/21] qapi: delete unused OptsVisitor code Daniel P. Berrange
2016-09-30 15:45 ` [Qemu-devel] [PATCH v14 00/21] QAPI/QOM work for non-scalar object properties no-reply
2016-09-30 18:50   ` Eric Blake
2016-10-21 18:30 ` Markus Armbruster

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1475246744-29302-14-git-send-email-berrange@redhat.com \
    --to=berrange@redhat.com \
    --cc=afaerber@suse.de \
    --cc=armbru@redhat.com \
    --cc=eblake@redhat.com \
    --cc=mreitz@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.