[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v4 4/5] qmp: full introspection support for QMP
From: |
Fam Zheng |
Subject: |
Re: [Qemu-devel] [PATCH v4 4/5] qmp: full introspection support for QMP |
Date: |
Fri, 24 Jan 2014 18:48:31 +0800 |
User-agent: |
Mutt/1.5.22 (2013-10-16) |
On Thu, 01/23 22:46, Amos Kong wrote:
> This patch introduces a new monitor command to query QMP schema
> information, the return data is a range of schema structs, which
> contains the useful metadata to help management to check supported
> features, QMP commands detail, etc.
>
> We use qapi-introspect.py to parse all json definition in
> qapi-schema.json, and generate a range of dictionaries with metadata.
> The query command will visit the dictionaries and fill the data
> to allocated struct tree. Then QMP infrastructure will convert
> the tree to json string and return to QMP client.
>
> TODO:
> Wenchao Xia is working to convert QMP events to qapi-schema.json,
> then event can also be queried by this interface.
>
> I will introduce another command 'query-qga-schema' to query QGA
> schema information, it's easy to add this support based on this
> patch.
>
> Signed-off-by: Amos Kong <address@hidden>
> ---
> qapi-schema.json | 11 +++
> qmp-commands.hx | 42 +++++++++++
> qmp.c | 215
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 268 insertions(+)
>
> diff --git a/qapi-schema.json b/qapi-schema.json
> index c63f0ca..6033383 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -4411,3 +4411,14 @@
> 'reference-type': 'String',
> 'type': 'DataObjectType',
> 'unionobj': 'DataObjectUnion' } }
> +
> +##
> +# @query-qmp-schema
> +#
> +# Query QMP schema information
> +#
> +# @returns: list of @DataObject
> +#
> +# Since: 1.8
> +##
> +{ 'command': 'query-qmp-schema', 'returns': ['DataObject'] }
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 02cc815..b83762d 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -3291,6 +3291,48 @@ Example:
> }
>
> EQMP
> + {
> + .name = "query-qmp-schema",
> + .args_type = "",
> + .mhandler.cmd_new = qmp_marshal_input_query_qmp_schema,
> + },
> +
> +
> +SQMP
> +query-qmp-schema
> +----------------
> +
> +query qmp schema information
> +
> +Return a json-object with the following information:
> +
> +- "name": qmp schema name (json-string)
> +- "type": qmp schema type, it can be 'comand', 'type', 'enum', 'union'
> +- "returns": return data of qmp command (json-object, optional)
> +
> +Example:
> +
> +-> { "execute": "query-qmp-schema" }
> +-> { "return": [
> + {
> + "name": "query-name",
> + "type": "command",
> + "returns": {
> + "name": "NameInfo",
> + "type": "type",
> + "data": [
> + {
> + "name": "name",
> + "optional": true,
> + "recursive": false,
> + "type": "str"
> + }
> + ]
> + }
> + }
> + }
> +
> +EQMP
>
> {
> .name = "blockdev-add",
> diff --git a/qmp.c b/qmp.c
> index 0f46171..a64ae6d 100644
> --- a/qmp.c
> +++ b/qmp.c
> @@ -27,6 +27,8 @@
> #include "qapi/qmp/qobject.h"
> #include "qapi/qmp-input-visitor.h"
> #include "hw/boards.h"
> +#include "qapi/qmp/qjson.h"
> +#include "qapi-introspect.h"
>
> NameInfo *qmp_query_name(Error **errp)
> {
> @@ -488,6 +490,219 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error
> **errp)
> return arch_query_cpu_definitions(errp);
> }
>
> +static strList *qobject_to_strlist(QObject *data)
> +{
> + strList *list = NULL;
> + strList **plist = &list;
> + QList *qlist;
> + const QListEntry *lent;
> +
> + qlist = qobject_to_qlist(data);
> + for (lent = qlist_first(qlist); lent; lent = qlist_next(lent)) {
> + strList *entry = g_malloc0(sizeof(strList));
> + entry->value = g_strdup(qobject_get_str(lent->value));
> + *plist = entry;
> + plist = &entry->next;
> + }
> +
> + return list;
> +}
> +
> +static DataObject *qobject_to_dataobj(QObject *data);
> +
> +static DataObjectMember *qobject_to_dataobjmem(QObject *data)
> +{
> +
> + DataObjectMember *member = g_malloc0(sizeof(DataObjectMember));
> +
> + member->type = g_malloc0(sizeof(DataObjectMemberType));
> + if (data->type->code == QTYPE_QDICT) {
> + member->type->kind = DATA_OBJECT_MEMBER_TYPE_KIND_EXTEND;
> + member->type->extend = qobject_to_dataobj(data);
> + } else {
> + member->type->kind = DATA_OBJECT_MEMBER_TYPE_KIND_REFERENCE;
> + member->type->reference = g_strdup(qobject_get_str(data));
> + }
> +
> + return member;
> +}
> +
> +static DataObjectMemberList *qobject_to_dict_memlist(QObject *data)
> +{
> + DataObjectMemberList *list = NULL;
> + DataObjectMemberList **plist = &list;
> + QDict *qdict = qobject_to_qdict(data);
> + const QDictEntry *dent;
> +
> + for (dent = qdict_first(qdict); dent; dent = qdict_next(qdict, dent)) {
> + DataObjectMemberList *entry =
> g_malloc0(sizeof(DataObjectMemberList));
> + entry->value = qobject_to_dataobjmem(dent->value);
> +
> + entry->value->has_optional = true;
> + entry->value->has_name = true;
> + if (dent->key[0] == '*') {
> + entry->value->optional = true;
> + entry->value->name = g_strdup(dent->key + 1);
> + } else {
> + entry->value->name = g_strdup(dent->key);
> + }
> + *plist = entry;
> + plist = &entry->next;
> + }
> +
> + return list;
> +}
> +
> +static DataObjectMemberList *qobject_to_list_memlist(QObject *data)
> +{
> + const QListEntry *lent;
> + DataObjectMemberList *list = NULL;
> + DataObjectMemberList **plist = &list;
> + QList *qlist = qobject_to_qlist(data);
> +
> + for (lent = qlist_first(qlist); lent; lent = qlist_next(lent)) {
> + DataObjectMemberList *entry =
> g_malloc0(sizeof(DataObjectMemberList));
> + entry->value = qobject_to_dataobjmem(lent->value);
> + entry->value->has_optional = true;
> + entry->value->has_name = true;
> + *plist = entry;
> + plist = &entry->next;
> + }
> +
> + return list;
> +}
> +
> +static DataObjectMemberList *qobject_to_memlist(QObject *data)
This whole converting is cumbersome. You already did all the traversing through
the type jungle in python when generating this, it's not necessary to do the
similar thing again here.
Alternatively, I think we have a good reason to extend QMP framework as
necessary here, as we are doing "QMP introspection", which is a part of the
framework:
* Define final output into qmp_schema_table[], no need to box it like:
"{'_obj_member': 'False', '_obj_type': 'enum', '_obj_name':
'ErrorClass', '_obj_data': {'data': ...
just put it content of "qmp-introspection.output.txt" as a long string in
the header, like you would generate in qobject_to_memlist:
const char *qmp_schema_table =
"{ 'name': 'ErrorClass', 'type': 'enumeration', 'data': [...]},"
"{ 'name': '...', ...},"
...
;
* Add a new type of qmp command, that returns a QString as a json literal.
query-qmp-schema is defined as this type. (This wouldn't be much code, but
may be abused in the future, I'm afraid. However we can review, limit its
use to introspection only)
* And return qmp_schema_table from query-qmp-shema, which will be copied to
the wire.
Feel free to disagree, it's not a perfect solution. But I really think we need
to avoid duplicating "enum", "base", "type", "union", "discriminator", ...
Fam
> +{
> + DataObjectMemberList *list = NULL;
> + QDict *qdict = qobject_to_qdict(data);
> + QObject *subdata = qdict_get(qdict, "_obj_data");
> +
> + list = NULL;
> + if (subdata->type->code == QTYPE_QDICT) {
> + list = qobject_to_dict_memlist(subdata);
> + } else if (subdata->type->code == QTYPE_QLIST) {
> + list = qobject_to_list_memlist(subdata);
> + }
> +
> + return list;
> +}
> +
> +static DataObject *qobject_to_dataobj(QObject *data)
> +{
> + QObject *subdata;
> + QDict *qdict;
> + const char *obj_type, *obj_recursive;
> + DataObject *obj = g_malloc0(sizeof(DataObject));
> +
> + if (data->type->code == QTYPE_QSTRING) {
> + obj->kind = DATA_OBJECT_KIND_REFERENCE_TYPE;
> + obj->reference_type = g_malloc0(sizeof(String));
> + obj->reference_type->str = g_strdup(qobject_get_str(data));
> + return obj;
> + }
> +
> + qdict = qobject_to_qdict(data);
> + assert(qdict != NULL);
> +
> + obj_type = qobject_get_str(qdict_get(qdict, "_obj_type"));
> + obj_recursive = qobject_get_str(qdict_get(qdict, "_obj_recursive"));
> + if (!strcmp(obj_recursive, "True")) {
> + obj->has_recursive = true;
> + obj->recursive = true;
> + }
> +
> + obj->has_name = true;
> + obj->name = g_strdup(qobject_get_str(qdict_get(qdict, "_obj_name")));
> +
> + subdata = qdict_get(qdict, "_obj_data");
> + qdict = qobject_to_qdict(subdata);
> +
> + if (!strcmp(obj_type, "command")) {
> + obj->kind = DATA_OBJECT_KIND_COMMAND;
> + obj->command = g_malloc0(sizeof(DataObjectCommand));
> + subdata = qdict_get(qobject_to_qdict(subdata), "data");
> +
> + if (subdata && subdata->type->code == QTYPE_QDICT) {
> + obj->command->has_data = true;
> + obj->command->data = qobject_to_memlist(subdata);
> + } else if (subdata && subdata->type->code == QTYPE_QLIST) {
> + abort();
> + }
> +
> + subdata = qdict_get(qdict, "returns");
> + if (subdata) {
> + obj->command->has_returns = true;
> + obj->command->returns = qobject_to_dataobj(subdata);
> + }
> +
> + subdata = qdict_get(qdict, "gen");
> + if (subdata && subdata->type->code == QTYPE_QSTRING) {
> + obj->command->has_gen = true;
> + if (!strcmp(qobject_get_str(subdata), "no")) {
> + obj->command->gen = false;
> + } else {
> + obj->command->gen = true;
> + }
> + }
> + } else if (!strcmp(obj_type, "union")) {
> + obj->kind = DATA_OBJECT_KIND_UNIONOBJ;
> + obj->unionobj = g_malloc0(sizeof(DataObjectUnion));
> + subdata = qdict_get(qdict, "data");
> + obj->unionobj->data = qobject_to_memlist(subdata);
> +
> + subdata = qdict_get(qdict, "base");
> + if (subdata) {
> + obj->unionobj->has_base = true;
> + obj->unionobj->base = qobject_to_dataobj(subdata);
> + }
> +
> + subdata = qdict_get(qdict, "discriminator");
> + if (subdata) {
> + obj->unionobj->has_discriminator = true;
> + obj->unionobj->discriminator = qobject_to_dataobj(subdata);
> + }
> + } else if (!strcmp(obj_type, "type")) {
> + obj->kind = DATA_OBJECT_KIND_TYPE;
> + obj->type = g_malloc0(sizeof(DataObjectType));
> + subdata = qdict_get(qdict, "data");
> + if (subdata) {
> + obj->type->data = qobject_to_memlist(subdata);
> + }
> + } else if (!strcmp(obj_type, "enum")) {
> + obj->kind = DATA_OBJECT_KIND_ENUMERATION;
> + obj->enumeration = g_malloc0(sizeof(DataObjectEnumeration));
> + subdata = qdict_get(qdict, "data");
> + obj->enumeration->data = qobject_to_strlist(subdata);
> + } else {
> + obj->has_name = false;
> + obj->kind = DATA_OBJECT_KIND_ANONYMOUS_STRUCT;
> + obj->anonymous_struct = g_malloc0(sizeof(DataObjectAnonymousStruct));
> + obj->anonymous_struct->data = qobject_to_memlist(data);
> + }
> +
> + return obj;
> +}
> +
> +DataObjectList *qmp_query_qmp_schema(Error **errp)
> +{
> + DataObjectList *list = NULL;
> + DataObjectList **plist = &list;
> + QObject *data;
> + int i;
> +
> + for (i = 0; qmp_schema_table[i]; i++) {
> + data = qobject_from_json(qmp_schema_table[i]);
> + assert(data != NULL);
> + DataObjectList *entry = g_malloc0(sizeof(DataObjectList));
> + entry->value = qobject_to_dataobj(data);
> + *plist = entry;
> + plist = &entry->next;
> + }
> +
> + return list;
> +}
> +
> void qmp_add_client(const char *protocol, const char *fdname,
> bool has_skipauth, bool skipauth, bool has_tls, bool tls,
> Error **errp)
> --
> 1.8.4.2
>
>
- [Qemu-devel] [PATCH v4 0/5] QMP full introspection, Amos Kong, 2014/01/23
- [Qemu-devel] [PATCH v4 1/5] qapi: introduce DataObject to describe dynamic structs, Amos Kong, 2014/01/23
- [Qemu-devel] [PATCH v4 4/5] qmp: full introspection support for QMP, Amos Kong, 2014/01/23
- Re: [Qemu-devel] [PATCH v4 4/5] qmp: full introspection support for QMP,
Fam Zheng <=
- Re: [Qemu-devel] [PATCH v4 4/5] qmp: full introspection support for QMP, Fam Zheng, 2014/01/27
- Re: [Qemu-devel] [PATCH v4 4/5] qmp: full introspection support for QMP, Amos Kong, 2014/01/28
- Re: [Qemu-devel] [PATCH v4 4/5] qmp: full introspection support for QMP, Paolo Bonzini, 2014/01/28
- Re: [Qemu-devel] [PATCH v4 4/5] qmp: full introspection support for QMP, Eric Blake, 2014/01/28
- Re: [Qemu-devel] [PATCH v4 4/5] qmp: full introspection support for QMP, Fam Zheng, 2014/01/29
- Re: [Qemu-devel] [PATCH v4 4/5] qmp: full introspection support for QMP, Amos Kong, 2014/01/27