[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v6 4/7] qom: support arbitrary non-scalar proper
From: |
Marc-André Lureau |
Subject: |
Re: [Qemu-devel] [PATCH v6 4/7] qom: support arbitrary non-scalar properties with -object |
Date: |
Tue, 28 Jun 2016 18:09:08 +0200 |
Hi
On Tue, Jun 14, 2016 at 6:07 PM, Daniel P. Berrange <address@hidden> wrote:
> The current -object command line syntax only allows for
> creation of objects with scalar properties, or a list
> with a fixed scalar element type. Objects which have
> properties that are represented as structs in the QAPI
> schema cannot be created using -object.
>
> This is a design limitation of the way the OptsVisitor
> is written. It simply iterates over the QemuOpts values
> as a flat list. The support for lists is enabled by
> allowing the same key to be repeated in the opts string.
>
> It is not practical to extend the OptsVisitor to support
> more complex data structures while also maintaining
> the existing list handling behaviour that is relied upon
> by other areas of QEMU.
>
> Fortunately there is no existing object that implements
> the UserCreatable interface that relies on the list
> handling behaviour, so it is possible to swap out the
> OptsVisitor for a different visitor implementation, so
> -object supports non-scalar properties, thus leaving
> other users of OptsVisitor unaffected.
>
> The previously added qdict_crumple() method is able to
> take a qdict containing a flat set of properties and
> turn that into a arbitrarily nested set of dicts and
> lists. By combining qemu_opts_to_qdict and qdict_crumple()
> together, we can turn the opt string into a data structure
> that is practically identical to that passed over QMP
> when defining an object. The only difference is that all
> the scalar values are represented as strings, rather than
> strings, ints and bools. This is sufficient to let us
> replace the OptsVisitor with the QMPInputVisitor for
> use with -object.
>
> Thus -object can now support non-scalar properties,
> for example the QMP object
>
> {
> "execute": "object-add",
> "arguments": {
> "qom-type": "demo",
> "id": "demo0",
> "parameters": {
> "foo": [
> { "bar": "one", "wizz": "1" },
> { "bar": "two", "wizz": "2" }
> ]
> }
> }
> }
>
> Would be creatable via the CLI now using
>
> $QEMU \
> -object demo,id=demo0,\
> foo.0.bar=one,foo.0.wizz=1,\
> foo.1.bar=two,foo.1.wizz=2
>
> Notice that this syntax is intentionally compatible
> with that currently used by block drivers.
>
> This is also wired up to work for the 'object_add' command
> in the HMP monitor with the same syntax.
>
> (hmp) object_add demo,id=demo0,\
> foo.0.bar=one,foo.0.wizz=1,\
> foo.1.bar=two,foo.1.wizz=2
>
> NB indentation should not be used with HMP commands, this
> is just for convenient formatting in this commit message.
>
> Signed-off-by: Daniel P. Berrange <address@hidden>
The patch breaks parsing of size arguments:
-object memory-backend-file,id=mem,size=512M,mem-path=/tmp: Parameter
'size' expects a number
Looks like the previous patch needs type_size support
> ---
> hmp.c | 18 +--
> include/qom/object_interfaces.h | 10 +-
> qmp.c | 2 +-
> qom/object_interfaces.c | 49 ++++--
> tests/check-qom-proplist.c | 319
> +++++++++++++++++++++++++++++++++++++++-
> 5 files changed, 365 insertions(+), 33 deletions(-)
>
> diff --git a/hmp.c b/hmp.c
> index a4b1d3d..e7778e7 100644
> --- a/hmp.c
> +++ b/hmp.c
> @@ -25,7 +25,7 @@
> #include "qemu/sockets.h"
> #include "monitor/monitor.h"
> #include "monitor/qdev.h"
> -#include "qapi/opts-visitor.h"
> +#include "qapi/qmp-input-visitor.h"
> #include "qapi/qmp/qerror.h"
> #include "qapi/string-output-visitor.h"
> #include "qapi/util.h"
> @@ -1717,20 +1717,12 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict)
> void hmp_object_add(Monitor *mon, const QDict *qdict)
> {
> Error *err = NULL;
> - QemuOpts *opts;
> - OptsVisitor *ov;
> + QmpInputVisitor *qiv;
> Object *obj = NULL;
>
> - opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err);
> - if (err) {
> - hmp_handle_error(mon, &err);
> - return;
> - }
> -
> - ov = opts_visitor_new(opts);
> - obj = user_creatable_add(qdict, opts_get_visitor(ov), &err);
> - opts_visitor_cleanup(ov);
> - qemu_opts_del(opts);
> + qiv = qmp_string_input_visitor_new((QObject *)qdict, true);
> + obj = user_creatable_add(qdict, qmp_input_get_visitor(qiv), &err);
> + qmp_input_visitor_cleanup(qiv);
>
> if (err) {
> hmp_handle_error(mon, &err);
> diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h
> index 8b17f4d..997563a 100644
> --- a/include/qom/object_interfaces.h
> +++ b/include/qom/object_interfaces.h
> @@ -96,18 +96,24 @@ Object *user_creatable_add(const QDict *qdict,
> * user_creatable_add_type:
> * @type: the object type name
> * @id: the unique ID for the object
> + * @nested: whether to recurse into the visitor for properties
> * @qdict: the object properties
> * @v: the visitor
> * @errp: if an error occurs, a pointer to an area to store the error
> *
> * Create an instance of the user creatable object @type, placing
> * it in the object composition tree with name @id, initializing
> - * it with properties from @qdict
> + * it with properties from @qdict.
> + *
> + * If the visitor is already positioned to read the properties
> + * in @qdict, @nested should be false. Conversely, if it is
> + * neccessary to open/close a struct to read the properties in
> + * @qdict, @nested should be true.
> *
> * Returns: the newly created object or NULL on error
> */
> Object *user_creatable_add_type(const char *type, const char *id,
> - const QDict *qdict,
> + bool nested, const QDict *qdict,
> Visitor *v, Error **errp);
>
> /**
> diff --git a/qmp.c b/qmp.c
> index 7df6543..fc33661 100644
> --- a/qmp.c
> +++ b/qmp.c
> @@ -667,7 +667,7 @@ void qmp_object_add(const char *type, const char *id,
> }
>
> qiv = qmp_input_visitor_new(props, true);
> - obj = user_creatable_add_type(type, id, pdict,
> + obj = user_creatable_add_type(type, id, true, pdict,
> qmp_input_get_visitor(qiv), errp);
> qmp_input_visitor_cleanup(qiv);
> if (obj) {
> diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
> index 51e62e2..174a5f8 100644
> --- a/qom/object_interfaces.c
> +++ b/qom/object_interfaces.c
> @@ -3,6 +3,7 @@
> #include "qom/object_interfaces.h"
> #include "qemu/module.h"
> #include "qapi-visit.h"
> +#include "qapi/qmp-input-visitor.h"
> #include "qapi/qmp-output-visitor.h"
> #include "qapi/opts-visitor.h"
>
> @@ -63,12 +64,16 @@ Object *user_creatable_add(const QDict *qdict,
> if (local_err) {
> goto out_visit;
> }
> - visit_check_struct(v, &local_err);
> +
> + obj = user_creatable_add_type(type, id, false, pdict, v, &local_err);
> if (local_err) {
> goto out_visit;
> }
>
> - obj = user_creatable_add_type(type, id, pdict, v, &local_err);
> + visit_check_struct(v, &local_err);
> + if (local_err) {
> + goto out_visit;
> + }
>
> out_visit:
> visit_end_struct(v);
> @@ -87,7 +92,7 @@ out:
>
>
> Object *user_creatable_add_type(const char *type, const char *id,
> - const QDict *qdict,
> + bool nested, const QDict *qdict,
> Visitor *v, Error **errp)
> {
> Object *obj;
> @@ -114,9 +119,11 @@ Object *user_creatable_add_type(const char *type, const
> char *id,
>
> assert(qdict);
> obj = object_new(type);
> - visit_start_struct(v, NULL, NULL, 0, &local_err);
> - if (local_err) {
> - goto out;
> + if (nested) {
> + visit_start_struct(v, NULL, NULL, 0, &local_err);
> + if (local_err) {
> + goto out;
> + }
> }
> for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) {
> object_property_set(obj, v, e->key, &local_err);
> @@ -124,12 +131,14 @@ Object *user_creatable_add_type(const char *type, const
> char *id,
> break;
> }
> }
> - if (!local_err) {
> - visit_check_struct(v, &local_err);
> - }
> - visit_end_struct(v);
> - if (local_err) {
> - goto out;
> + if (nested) {
> + if (!local_err) {
> + visit_check_struct(v, &local_err);
> + }
> + visit_end_struct(v);
> + if (local_err) {
> + goto out;
> + }
> }
>
> object_property_add_child(object_get_objects_root(),
> @@ -156,15 +165,23 @@ out:
>
> Object *user_creatable_add_opts(QemuOpts *opts, Error **errp)
> {
> - OptsVisitor *ov;
> + QmpInputVisitor *qiv;
> QDict *pdict;
> + QObject *pobj;
> Object *obj = NULL;
>
> - ov = opts_visitor_new(opts);
> pdict = qemu_opts_to_qdict(opts, NULL);
>
> - obj = user_creatable_add(pdict, opts_get_visitor(ov), errp);
> - opts_visitor_cleanup(ov);
> + pobj = qdict_crumple(pdict, true, errp);
> + if (!pobj) {
> + goto cleanup;
> + }
> + qiv = qmp_string_input_visitor_new(pobj, true);
> +
> + obj = user_creatable_add((QDict *)pobj, qmp_input_get_visitor(qiv),
> errp);
> + qmp_input_visitor_cleanup(qiv);
> + qobject_decref(pobj);
> + cleanup:
> QDECREF(pdict);
> return obj;
> }
> diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
> index 42defe7..0fd104b 100644
> --- a/tests/check-qom-proplist.c
> +++ b/tests/check-qom-proplist.c
> @@ -23,6 +23,14 @@
> #include "qapi/error.h"
> #include "qom/object.h"
> #include "qemu/module.h"
> +#include "qapi/visitor.h"
> +#include "qom/object_interfaces.h"
> +#include "qemu/option.h"
> +#include "qemu/config-file.h"
> +#include "qapi/qmp/qjson.h"
> +#include "qapi/qmp-input-visitor.h"
> +#include "qapi-visit.h"
> +#include "qapi/dealloc-visitor.h"
>
>
> #define TYPE_DUMMY "qemu-dummy"
> @@ -30,6 +38,11 @@
> typedef struct DummyObject DummyObject;
> typedef struct DummyObjectClass DummyObjectClass;
>
> +typedef struct DummyPerson DummyPerson;
> +typedef struct DummyAddr DummyAddr;
> +typedef struct DummyAddrList DummyAddrList;
> +typedef struct DummySizeList DummySizeList;
> +
> #define DUMMY_OBJECT(obj) \
> OBJECT_CHECK(DummyObject, (obj), TYPE_DUMMY)
>
> @@ -50,12 +63,35 @@ static const char *const dummy_animal_map[DUMMY_LAST + 1]
> = {
> [DUMMY_LAST] = NULL,
> };
>
> +
> +struct DummyAddr {
> + char *ip;
> + int64_t prefix;
> + bool ipv6only;
> +};
> +
> +struct DummyAddrList {
> + DummyAddrList *next;
> + struct DummyAddr *value;
> +};
> +
> +struct DummyPerson {
> + char *name;
> + int64_t age;
> +};
> +
> struct DummyObject {
> Object parent_obj;
>
> bool bv;
> DummyAnimal av;
> char *sv;
> +
> + intList *sizes;
> +
> + DummyPerson *person;
> +
> + DummyAddrList *addrs;
> };
>
> struct DummyObjectClass {
> @@ -117,6 +153,159 @@ static char *dummy_get_sv(Object *obj,
> return g_strdup(dobj->sv);
> }
>
> +static void visit_type_DummyPerson_fields(Visitor *v, DummyPerson **obj,
> + Error **errp)
> +{
> + Error *err = NULL;
> +
> + visit_type_str(v, "name", &(*obj)->name, &err);
> + if (err) {
> + goto out;
> + }
> + visit_type_int(v, "age", &(*obj)->age, &err);
> + if (err) {
> + goto out;
> + }
> +
> +out:
> + error_propagate(errp, err);
> +}
> +
> +static void visit_type_DummyPerson(Visitor *v, const char *name,
> + DummyPerson **obj, Error **errp)
> +{
> + Error *err = NULL;
> +
> + visit_start_struct(v, name, (void **)obj, sizeof(DummyPerson), &err);
> + if (err) {
> + goto out;
> + }
> + if (!*obj) {
> + goto out_obj;
> + }
> + visit_type_DummyPerson_fields(v, obj, &err);
> + error_propagate(errp, err);
> + err = NULL;
> +out_obj:
> + visit_end_struct(v);
> +out:
> + error_propagate(errp, err);
> +}
> +
> +static void visit_type_DummyAddr_members(Visitor *v, DummyAddr **obj,
> + Error **errp)
> +{
> + Error *err = NULL;
> +
> + visit_type_str(v, "ip", &(*obj)->ip, &err);
> + if (err) {
> + goto out;
> + }
> + visit_type_int(v, "prefix", &(*obj)->prefix, &err);
> + if (err) {
> + goto out;
> + }
> + visit_type_bool(v, "ipv6only", &(*obj)->ipv6only, &err);
> + if (err) {
> + goto out;
> + }
> +
> +out:
> + error_propagate(errp, err);
> +}
> +
> +static void visit_type_DummyAddr(Visitor *v, const char *name,
> + DummyAddr **obj, Error **errp)
> +{
> + Error *err = NULL;
> +
> + visit_start_struct(v, name, (void **)obj, sizeof(DummyAddr), &err);
> + if (err) {
> + goto out;
> + }
> + if (!*obj) {
> + goto out_obj;
> + }
> + visit_type_DummyAddr_members(v, obj, &err);
> + error_propagate(errp, err);
> + err = NULL;
> +out_obj:
> + visit_end_struct(v);
> +out:
> + error_propagate(errp, err);
> +}
> +
> +static void qapi_free_DummyAddrList(DummyAddrList *obj);
> +
> +static void visit_type_DummyAddrList(Visitor *v, const char *name,
> + DummyAddrList **obj, Error **errp)
> +{
> + Error *err = NULL;
> + DummyAddrList *tail;
> + size_t size = sizeof(**obj);
> +
> + visit_start_list(v, name, (GenericList **)obj, size, &err);
> + if (err) {
> + goto out;
> + }
> +
> + for (tail = *obj; tail;
> + tail = (DummyAddrList *)visit_next_list(v,
> + (GenericList *)tail,
> + size)) {
> + visit_type_DummyAddr(v, NULL, &tail->value, &err);
> + if (err) {
> + break;
> + }
> + }
> +
> + visit_end_list(v);
> + if (err && visit_is_input(v)) {
> + qapi_free_DummyAddrList(*obj);
> + *obj = NULL;
> + }
> +out:
> + error_propagate(errp, err);
> +}
> +
> +static void qapi_free_DummyAddrList(DummyAddrList *obj)
> +{
> + QapiDeallocVisitor *qdv;
> + Visitor *v;
> +
> + if (!obj) {
> + return;
> + }
> +
> + qdv = qapi_dealloc_visitor_new();
> + v = qapi_dealloc_get_visitor(qdv);
> + visit_type_DummyAddrList(v, NULL, &obj, NULL);
> + qapi_dealloc_visitor_cleanup(qdv);
> +}
> +
> +static void dummy_set_sizes(Object *obj, Visitor *v, const char *name,
> + void *opaque, Error **errp)
> +{
> + DummyObject *dobj = DUMMY_OBJECT(obj);
> +
> + visit_type_intList(v, name, &dobj->sizes, errp);
> +}
> +
> +static void dummy_set_person(Object *obj, Visitor *v, const char *name,
> + void *opaque, Error **errp)
> +{
> + DummyObject *dobj = DUMMY_OBJECT(obj);
> +
> + visit_type_DummyPerson(v, name, &dobj->person, errp);
> +}
> +
> +static void dummy_set_addrs(Object *obj, Visitor *v, const char *name,
> + void *opaque, Error **errp)
> +{
> + DummyObject *dobj = DUMMY_OBJECT(obj);
> +
> + visit_type_DummyAddrList(v, name, &dobj->addrs, errp);
> +}
>
> static void dummy_init(Object *obj)
> {
> @@ -126,9 +315,16 @@ static void dummy_init(Object *obj)
> NULL);
> }
>
> +static void
> +dummy_complete(UserCreatable *uc, Error **errp)
> +{
> +}
>
> static void dummy_class_init(ObjectClass *cls, void *data)
> {
> + UserCreatableClass *ucc = USER_CREATABLE_CLASS(cls);
> + ucc->complete = dummy_complete;
> +
> object_class_property_add_bool(cls, "bv",
> dummy_get_bv,
> dummy_set_bv,
> @@ -143,12 +339,36 @@ static void dummy_class_init(ObjectClass *cls, void
> *data)
> dummy_get_av,
> dummy_set_av,
> NULL);
> + object_class_property_add(cls, "sizes",
> + "int[]",
> + NULL,
> + dummy_set_sizes,
> + NULL, NULL, NULL);
> + object_class_property_add(cls, "person",
> + "DummyPerson",
> + NULL,
> + dummy_set_person,
> + NULL, NULL, NULL);
> + object_class_property_add(cls, "addrs",
> + "DummyAddrList",
> + NULL,
> + dummy_set_addrs,
> + NULL, NULL, NULL);
> }
>
>
> static void dummy_finalize(Object *obj)
> {
> DummyObject *dobj = DUMMY_OBJECT(obj);
> + QapiDeallocVisitor *qdv;
> + Visitor *v;
> +
> + qdv = qapi_dealloc_visitor_new();
> + v = qapi_dealloc_get_visitor(qdv);
> + visit_type_intList(v, NULL, &dobj->sizes, NULL);
> + visit_type_DummyAddrList(v, NULL, &dobj->addrs, NULL);
> + visit_type_DummyPerson(v, NULL, &dobj->person, NULL);
> + qapi_dealloc_visitor_cleanup(qdv);
>
> g_free(dobj->sv);
> }
> @@ -162,6 +382,10 @@ static const TypeInfo dummy_info = {
> .instance_finalize = dummy_finalize,
> .class_size = sizeof(DummyObjectClass),
> .class_init = dummy_class_init,
> + .interfaces = (InterfaceInfo[]) {
> + { TYPE_USER_CREATABLE },
> + { }
> + }
> };
>
>
> @@ -457,7 +681,9 @@ static void test_dummy_iterator(void)
>
> ObjectProperty *prop;
> ObjectPropertyIterator iter;
> - bool seenbv = false, seensv = false, seenav = false, seentype;
> + bool seenbv = false, seensv = false, seenav = false,
> + seentype = false, seenaddrs = false, seenperson = false,
> + seensizes = false;
>
> object_property_iter_init(&iter, OBJECT(dobj));
> while ((prop = object_property_iter_next(&iter))) {
> @@ -470,6 +696,12 @@ static void test_dummy_iterator(void)
> } else if (g_str_equal(prop->name, "type")) {
> /* This prop comes from the base Object class */
> seentype = true;
> + } else if (g_str_equal(prop->name, "addrs")) {
> + seenaddrs = true;
> + } else if (g_str_equal(prop->name, "person")) {
> + seenperson = true;
> + } else if (g_str_equal(prop->name, "sizes")) {
> + seensizes = true;
> } else {
> g_printerr("Found prop '%s'\n", prop->name);
> g_assert_not_reached();
> @@ -479,6 +711,9 @@ static void test_dummy_iterator(void)
> g_assert(seenav);
> g_assert(seensv);
> g_assert(seentype);
> + g_assert(seenaddrs);
> + g_assert(seenperson);
> + g_assert(seensizes);
>
> object_unparent(OBJECT(dobj));
> }
> @@ -497,11 +732,91 @@ static void test_dummy_delchild(void)
> object_unparent(OBJECT(dev));
> }
>
> +
> +static QemuOptsList dummy_opts = {
> + .name = "object",
> + .implied_opt_name = "qom-type",
> + .head = QTAILQ_HEAD_INITIALIZER(dummy_opts.head),
> + .desc = {
> + { }
> + },
> +};
> +
> +static void test_dummy_create_complex(DummyObject *dummy)
> +{
> + g_assert(dummy->person != NULL);
> + g_assert_cmpstr(dummy->person->name, ==, "fred");
> + g_assert_cmpint(dummy->person->age, ==, 52);
> +
> + g_assert(dummy->sizes != NULL);
> + g_assert_cmpint(dummy->sizes->value, ==, 12);
> + g_assert_cmpint(dummy->sizes->next->value, ==, 65);
> + g_assert_cmpint(dummy->sizes->next->next->value, ==, 8139);
> +
> + g_assert(dummy->addrs != NULL);
> + g_assert_cmpstr(dummy->addrs->value->ip, ==, "127.0.0.1");
> + g_assert_cmpint(dummy->addrs->value->prefix, ==, 24);
> + g_assert(dummy->addrs->value->ipv6only);
> + g_assert_cmpstr(dummy->addrs->next->value->ip, ==, "0.0.0.0");
> + g_assert_cmpint(dummy->addrs->next->value->prefix, ==, 16);
> + g_assert(!dummy->addrs->next->value->ipv6only);
> +}
> +
> +
> +static void test_dummy_createopts(void)
> +{
> + const char *optstr = "qemu-dummy,id=dummy0,bv=yes,av=alligator,sv=hiss,"
> + "person.name=fred,person.age=52,sizes.0=12,sizes.1=65,sizes.2=8139,"
> + "addrs.0.ip=127.0.0.1,addrs.0.prefix=24,addrs.0.ipv6only=yes,"
> + "addrs.1.ip=0.0.0.0,addrs.1.prefix=16,addrs.1.ipv6only=no";
> + QemuOpts *opts;
> + DummyObject *dummy;
> +
> + opts = qemu_opts_parse_noisily(&dummy_opts,
> + optstr, true);
> + g_assert(opts != NULL);
> +
> + dummy = DUMMY_OBJECT(user_creatable_add_opts(opts, &error_abort));
> +
> + test_dummy_create_complex(dummy);
> +
> + object_unparent(OBJECT(dummy));
> + object_unref(OBJECT(dummy));
> +}
> +
> +
> +static void test_dummy_createqmp(void)
> +{
> + const char *jsonstr =
> + "{ 'qom-type': 'qemu-dummy', 'id': 'dummy0', "
> + " 'bv': true, 'av': 'alligator', 'sv': 'hiss', "
> + " 'person': { 'name': 'fred', 'age': 52 }, "
> + " 'sizes': [12, 65, 8139], "
> + " 'addrs': [ { 'ip': '127.0.0.1', 'prefix': 24, 'ipv6only': true },
> "
> + " { 'ip': '0.0.0.0', 'prefix': 16, 'ipv6only': false } ]
> }";
> + QObject *obj = qobject_from_json(jsonstr);
> + QmpInputVisitor *qiv = qmp_input_visitor_new(obj, true);
> + DummyObject *dummy;
> + g_assert(obj);
> + dummy = DUMMY_OBJECT(user_creatable_add(qobject_to_qdict(obj),
> + qmp_input_get_visitor(qiv),
> + &error_abort));
> +
> + test_dummy_create_complex(dummy);
> + qmp_input_visitor_cleanup(qiv);
> + object_unparent(OBJECT(dummy));
> + object_unref(OBJECT(dummy));
> + qobject_decref(obj);
> +}
> +
> int main(int argc, char **argv)
> {
> g_test_init(&argc, &argv, NULL);
>
> module_call_init(MODULE_INIT_QOM);
> +
> + qemu_add_opts(&dummy_opts);
> +
> type_register_static(&dummy_info);
> type_register_static(&dummy_dev_info);
> type_register_static(&dummy_bus_info);
> @@ -513,6 +828,8 @@ int main(int argc, char **argv)
> g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
> g_test_add_func("/qom/proplist/iterator", test_dummy_iterator);
> g_test_add_func("/qom/proplist/delchild", test_dummy_delchild);
> + g_test_add_func("/qom/proplist/createopts", test_dummy_createopts);
> + g_test_add_func("/qom/proplist/createqmp", test_dummy_createqmp);
>
> return g_test_run();
> }
> --
> 2.5.5
>
>
--
Marc-André Lureau
- [Qemu-devel] [PATCH v6 0/7] Provide a QOM-based authorization API, Daniel P. Berrange, 2016/06/14
- [Qemu-devel] [PATCH v6 1/7] qdict: implement a qdict_crumple method for un-flattening a dict, Daniel P. Berrange, 2016/06/14
- [Qemu-devel] [PATCH v6 3/7] qapi: add a QmpInputVisitor that does string conversion, Daniel P. Berrange, 2016/06/14
- [Qemu-devel] [PATCH v6 2/7] option: make parse_option_bool/number non-static, Daniel P. Berrange, 2016/06/14
- [Qemu-devel] [PATCH v6 4/7] qom: support arbitrary non-scalar properties with -object, Daniel P. Berrange, 2016/06/14
- Re: [Qemu-devel] [PATCH v6 4/7] qom: support arbitrary non-scalar properties with -object,
Marc-André Lureau <=
- [Qemu-devel] [PATCH v6 5/7] util: add QAuthZ object as an authorization base class, Daniel P. Berrange, 2016/06/14
- [Qemu-devel] [PATCH v6 6/7] util: add QAuthZSimple object type for a simple access control list, Daniel P. Berrange, 2016/06/14
- [Qemu-devel] [PATCH v6 7/7] acl: delete existing ACL implementation, Daniel P. Berrange, 2016/06/14
- Re: [Qemu-devel] [PATCH v6 0/7] Provide a QOM-based authorization API, Daniel P. Berrange, 2016/06/27