From: Anthony Liguori <aliguori@us.ibm.com>
To: Amos Kong <akong@redhat.com>, qemu-devel@nongnu.org
Cc: pbonzini@redhat.com, qiaonuohan@cn.fujitsu.com,
armbru@redhat.com, lcapitulino@redhat.com
Subject: Re: [Qemu-devel] [PATCH] full introspection support for QMP
Date: Tue, 02 Jul 2013 09:51:55 -0500 [thread overview]
Message-ID: <878v1pqak4.fsf@codemonkey.ws> (raw)
In-Reply-To: <1371644677-11041-1-git-send-email-akong@redhat.com>
Amos Kong <akong@redhat.com> writes:
> Introduces new monitor command to query QMP schema information,
> the return data is a nested dict/list, it contains the useful
> metadata.
>
> we can add events definations to qapi-schema.json, then it can
> also be queried.
>
> Signed-off-by: Amos Kong <akong@redhat.com>
Maybe I'm being too meta here, but why not just return qapi-schema.json
as a string and call it as day?
It's JSON already and since QMP is JSON, the client already has a JSON
parser. Adding another level of complexity doesn't add much value IMHO.
It's pretty simple too.
Regards,
Anthony Liguori
> ---
> Makefile | 4 +-
> qapi-schema.json | 68 +++++++++++++++++++
> qmp-commands.hx | 39 +++++++++++
> qmp.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++
> scripts/qapi-commands.py | 2 +-
> scripts/qapi-types.py | 34 +++++++++-
> scripts/qapi-visit.py | 2 +-
> scripts/qapi.py | 7 +-
> 8 files changed, 320 insertions(+), 6 deletions(-)
>
> diff --git a/Makefile b/Makefile
> index 3cfa7d0..42713ef 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -38,7 +38,7 @@ endif
> endif
>
> GENERATED_HEADERS = config-host.h qemu-options.def
> -GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h
> +GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h qmp-schema.h
> GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c
>
> GENERATED_HEADERS += trace/generated-events.h
> @@ -213,7 +213,7 @@ qga/qapi-generated/qga-qmp-commands.h qga/qapi-generated/qga-qmp-marshal.c :\
> $(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py $(qapi-py)
> $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o qga/qapi-generated -p "qga-" < $<, " GEN $@")
>
> -qapi-types.c qapi-types.h :\
> +qapi-types.c qapi-types.h qmp-schema.h:\
> $(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
> $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "." -b < $<, " GEN $@")
> qapi-visit.c qapi-visit.h :\
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 6cc07c2..43abe57 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -3608,3 +3608,71 @@
> '*cpuid-input-ecx': 'int',
> 'cpuid-register': 'X86CPURegister32',
> 'features': 'int' } }
> +
> +##
> +# @DataObject
> +#
> +# Details of a data object, it can be nested dictionary/list
> +#
> +# @name: #optional the string key of dictionary
> +#
> +# @type: the string value of dictionary or list
> +#
> +# @data: #optional a list of @DataObject, dictionary's value is nested
> +# dictionary/list
> +#
> +# Since: 1.6
> +##
> +{ 'type': 'DataObject',
> + 'data': { '*name': 'str', '*type': 'str', '*data': ['DataObject'] } }
> +
> +##
> +# @SchemaMetatype
> +#
> +# Possible meta types of a schema entry
> +#
> +# @Command: QMP monitor command to control guest
> +#
> +# @Type: defined new data type
> +#
> +# @Enumeration: enumeration data type
> +#
> +# @Union: union data type
> +#
> +# @Event: QMP event to notify QMP clients
> +#
> +# Since: 1.6
> +##
> +{ 'enum': 'SchemaMetatype',
> + 'data': ['Command', 'Type', 'Enumeration', 'Union', 'Event'] }
> +
> +##
> +# @SchemaData
> +#
> +# Details of schema items
> +#
> +# @type: dict's value, list's value
> +#
> +# @name: dict's key
> +#
> +# @data: #optional list of @DataObject, arguments data of executing
> +# QMP command
> +#
> +# @returns: #optional list of DataObject, return data after executing
> +# QMP command
> +#
> +# Since: 1.6
> +##
> +{ 'type': 'SchemaData', 'data': { 'type': 'SchemaMetatype',
> + 'name': 'str', '*data': ['DataObject'], '*returns': ['DataObject'] } }
> +
> +##
> +# @query-qmp-schema
> +#
> +# Query QMP schema information
> +#
> +# Returns: list of @SchemaData. Returns an error if json string is invalid.
> +#
> +# Since: 1.6
> +##
> +{ 'command': 'query-qmp-schema', 'returns': ['SchemaData'] }
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index 8cea5e5..667d9ab 100644
> --- a/qmp-commands.hx
> +++ b/qmp-commands.hx
> @@ -2997,3 +2997,42 @@ Example:
> <- { "return": {} }
>
> 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', 'event'
> +- "data": schema data (json-object, optional)
> +- "returns": return data of qmp command (json-object, optional)
> +
> +Example:
> +
> +-> { "execute": "query-qmp-schema" }
> +<- { "return": [
> + {
> + "name": "NameInfo",
> + "type": "Type",
> + "data": [
> + {
> + "name": "*name",
> + "type": "str"
> + }
> + ]
> + }
> + ]
> + }
> +
> +EQMP
> diff --git a/qmp.c b/qmp.c
> index 4c149b3..3a7c403 100644
> --- a/qmp.c
> +++ b/qmp.c
> @@ -25,6 +25,8 @@
> #include "sysemu/blockdev.h"
> #include "qom/qom-qobject.h"
> #include "hw/boards.h"
> +#include "qmp-schema.h"
> +#include "qapi/qmp/qjson.h"
>
> NameInfo *qmp_query_name(Error **errp)
> {
> @@ -486,6 +488,174 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
> return arch_query_cpu_definitions(errp);
> }
>
> +static DataObjectList *visit_qobj_dict(QObject *data);
> +
> +static DataObjectList *visit_qobj_list(QObject *data)
> +{
> + DataObjectList *obj_list = NULL, *obj_last_entry, *obj_entry;
> + DataObject *obj_info;
> + const QListEntry *ent;
> + QList *qlist;
> +
> + qlist = qobject_to_qlist(data);
> + if (!qlist) {
> + return NULL;
> + }
> +
> + for (ent = qlist_first(qlist); ent; ent = qlist_next(ent)) {
> + obj_info = g_malloc0(sizeof(*obj_info));
> + obj_entry = g_malloc0(sizeof(*obj_entry));
> + obj_entry->value = obj_info;
> + obj_entry->next = NULL;
> +
> + if (!obj_list) {
> + obj_list = obj_entry;
> + } else {
> + obj_last_entry->next = obj_entry;
> + }
> + obj_last_entry = obj_entry;
> +
> + if (qobject_to_qstring(ent->value)) {
> + obj_info->has_type = true;
> + obj_info->type = g_strdup_printf("%s",
> + qstring_get_str(qobject_to_qstring(ent->value)));
> + continue;
> + }
> +
> + obj_info->has_data = true;
> + if (ent->value->type->code == QTYPE_QDICT) {
> + obj_info->data = visit_qobj_dict(ent->value);
> + } else if (ent->value->type->code == QTYPE_QLIST) {
> + obj_info->data = visit_qobj_list(ent->value);
> + }
> + }
> +
> + return obj_list;
> +}
> +
> +static DataObjectList *visit_qobj_dict(QObject *data)
> +{
> + DataObjectList *obj_list = NULL, *obj_last_entry, *obj_entry;
> + DataObject *obj_info;
> + const QDictEntry *ent;
> + QDict *qdict;
> +
> + qdict = qobject_to_qdict(data);
> + if (!qdict) {
> + return NULL;
> + }
> +
> + for (ent = qdict_first(qdict); ent; ent = qdict_next(qdict, ent)) {
> + obj_info = g_malloc0(sizeof(*obj_info));
> + obj_entry = g_malloc0(sizeof(*obj_entry));
> + obj_entry->value = obj_info;
> + obj_entry->next = NULL;
> +
> + if (!obj_list) {
> + obj_list = obj_entry;
> + } else {
> + obj_last_entry->next = obj_entry;
> + }
> + obj_last_entry = obj_entry;
> +
> + obj_info->name = ent->key;
> + obj_info->has_name = true;
> + if (qobject_to_qstring(ent->value)) {
> + obj_info->has_type = true;
> + obj_info->type = g_strdup_printf("%s",
> + qstring_get_str(qobject_to_qstring(ent->value)));
> + continue;
> + }
> +
> + obj_info->has_data = true;
> + if (ent->value->type->code == QTYPE_QDICT) {
> + obj_info->data = visit_qobj_dict(ent->value);
> + } else if (ent->value->type->code == QTYPE_QLIST) {
> + obj_info->data = visit_qobj_list(ent->value);
> + }
> + }
> +
> + return obj_list;
> +}
> +
> +SchemaDataList *qmp_query_qmp_schema(Error **errp)
> +{
> + SchemaDataList *list = NULL, *last_entry, *entry;
> + SchemaData *info;
> + int i;
> +
> + DataObjectList *obj_entry;
> + DataObject *obj_info;
> +
> + QObject *data;
> + QDict *qdict;
> + const QDictEntry *ent;
> +
> + for (i = 0; qmp_schema_table[i]; i++) {
> + data = qobject_from_json(qmp_schema_table[i]);
> + if (!data) {
> + error_setg(errp, "Can't convert json string to valid qobject");
> + return NULL;
> + }
> +
> + qdict = qobject_to_qdict(data);
> + if (!qdict) {
> + error_setg(errp, "Can't convert qobject to valid qdict");
> + return NULL;
> + }
> +
> + ent = qdict_first(qdict);
> + info = g_malloc0(sizeof(*info));
> +
> + if (!strcmp(ent->key, "command")) {
> + info->type = SCHEMA_METATYPE_COMMAND;
> + } else if (!strcmp(ent->key, "type")) {
> + info->type = SCHEMA_METATYPE_TYPE;
> + } else if (!strcmp(ent->key, "enum")) {
> + info->type = SCHEMA_METATYPE_ENUMERATION;
> + } else if (!strcmp(ent->key, "union")) {
> + info->type = SCHEMA_METATYPE_UNION;
> + } else if (!strcmp(ent->key, "event")) {
> + info->type = SCHEMA_METATYPE_EVENT;
> + }
> +
> + info->name = g_strdup_printf("%s",
> + qstring_get_str(qobject_to_qstring(ent->value)));
> +
> + data = qdict_get(qdict, "data");
> + if (data) {
> + if (qobject_to_qstring(data)) {
> + obj_info = g_malloc0(sizeof(*obj_info));
> + obj_entry = g_malloc0(sizeof(*obj_entry));
> + obj_entry->value = obj_info;
> + obj_info->name = g_strdup_printf("%s",
> + qstring_get_str(qobject_to_qstring(data)));
> + obj_info->has_type = true;
> + obj_info->type = g_strdup_printf("%s",
> + qstring_get_str(qobject_to_qstring(data)));
> + } else if (data->type->code == QTYPE_QLIST) {
> + info->has_data = true;
> + info->data = visit_qobj_list(data);
> + } else if (data->type->code == QTYPE_QDICT) {
> + info->has_data = true;
> + info->data = visit_qobj_dict(data);
> + }
> + }
> +
> + entry = g_malloc0(sizeof(DataObjectList *));
> + entry->value = info;
> + entry->next = NULL;
> + if (!list) {
> + list = entry;
> + } else {
> + last_entry->next = entry;
> + }
> + last_entry = entry;
> + }
> +
> + return list;
> +}
> +
> void qmp_add_client(const char *protocol, const char *fdname,
> bool has_skipauth, bool skipauth, bool has_tls, bool tls,
> Error **errp)
> diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
> index e06332b..d15d04f 100644
> --- a/scripts/qapi-commands.py
> +++ b/scripts/qapi-commands.py
> @@ -437,7 +437,7 @@ except os.error, e:
> if e.errno != errno.EEXIST:
> raise
>
> -exprs = parse_schema(sys.stdin)
> +exprs = parse_schema(sys.stdin)[0]
> commands = filter(lambda expr: expr.has_key('command'), exprs)
> commands = filter(lambda expr: not expr.has_key('gen'), commands)
>
> diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py
> index ddcfed9..eb59a5a 100644
> --- a/scripts/qapi-types.py
> +++ b/scripts/qapi-types.py
> @@ -15,6 +15,7 @@ import sys
> import os
> import getopt
> import errno
> +import re
>
> def generate_fwd_struct(name, members, builtin_type=False):
> if builtin_type:
> @@ -303,7 +304,38 @@ fdecl.write(mcgen('''
> ''',
> guard=guardname(h_file)))
>
> -exprs = parse_schema(sys.stdin)
> +exprs_all = parse_schema(sys.stdin)
> +
> +schema_table = """/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
> +
> +/*
> + * Schema json string table converted from qapi-schema.json
> + *
> + * Copyright (c) 2013 Red Hat, Inc.
> + *
> + * Authors:
> + * Amos Kong <akong@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
> + * See the COPYING.LIB file in the top-level directory.
> + *
> + */
> +
> +const char *qmp_schema_table[] = {
> +"""
> +
> +for line in exprs_all[1]:
> + line = re.sub(r'\n', ' ', line.strip())
> + line = re.sub(r' +', ' ', line)
> + schema_table += '"%s",\n' % (line)
> +
> +schema_table += 'NULL};\n'
> +
> +f = open("qmp-schema.h", "w")
> +f.write(schema_table)
> +f.close()
> +
> +exprs = exprs_all[0]
> exprs = filter(lambda expr: not expr.has_key('gen'), exprs)
>
> fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 6cac05a..70f80eb 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -334,7 +334,7 @@ fdecl.write(mcgen('''
> ''',
> prefix=prefix, guard=guardname(h_file)))
>
> -exprs = parse_schema(sys.stdin)
> +exprs = parse_schema(sys.stdin)[0]
>
> # to avoid header dependency hell, we always generate declarations
> # for built-in types in our header files and simply guard them
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index 02ad668..fe7648c 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -80,6 +80,7 @@ def evaluate(string):
>
> def parse_schema(fp):
> exprs = []
> + raw_exprs = []
> expr = ''
> expr_eval = None
>
> @@ -91,6 +92,8 @@ def parse_schema(fp):
> expr += line
> elif expr:
> expr_eval = evaluate(expr)
> + raw_exprs.append(expr)
> +
> if expr_eval.has_key('enum'):
> add_enum(expr_eval['enum'])
> elif expr_eval.has_key('union'):
> @@ -102,13 +105,15 @@ def parse_schema(fp):
>
> if expr:
> expr_eval = evaluate(expr)
> + raw_exprs.append(expr)
> +
> if expr_eval.has_key('enum'):
> add_enum(expr_eval['enum'])
> elif expr_eval.has_key('union'):
> add_enum('%sKind' % expr_eval['union'])
> exprs.append(expr_eval)
>
> - return exprs
> + return exprs, raw_exprs
>
> def parse_args(typeinfo):
> for member in typeinfo:
> --
> 1.8.1.4
next prev parent reply other threads:[~2013-07-02 14:52 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-06-19 12:24 [Qemu-devel] [PATCH] full introspection support for QMP Amos Kong
2013-06-19 12:49 ` Amos Kong
2013-06-20 10:16 ` Amos Kong
2013-07-02 16:39 ` Eric Blake
2013-06-21 3:20 ` Luiz Capitulino
2013-07-02 8:37 ` Amos Kong
2013-07-02 14:20 ` Luiz Capitulino
2013-07-16 10:52 ` Amos Kong
2013-07-02 14:51 ` Anthony Liguori [this message]
2013-07-02 15:28 ` Eric Blake
2013-07-02 15:39 ` Daniel P. Berrange
2013-07-02 16:44 ` Eric Blake
2013-07-02 17:01 ` Paolo Bonzini
2013-07-02 17:06 ` Eric Blake
2013-07-02 18:27 ` Anthony Liguori
2013-07-04 3:54 ` Amos Kong
2013-07-02 18:21 ` Anthony Liguori
2013-07-02 20:00 ` Paolo Bonzini
2013-07-02 20:08 ` Eric Blake
2013-07-02 20:58 ` Anthony Liguori
2013-07-03 5:52 ` Paolo Bonzini
2013-07-03 12:54 ` Anthony Liguori
2013-07-03 14:45 ` Paolo Bonzini
2013-07-03 16:06 ` Anthony Liguori
2013-07-04 7:53 ` Paolo Bonzini
2013-07-11 13:37 ` Amos Kong
2013-07-02 17:06 ` Anthony Liguori
2013-07-02 17:11 ` Eric Blake
2013-07-02 18:28 ` Anthony Liguori
2013-07-03 15:08 ` Kevin Wolf
2013-07-03 15:59 ` Anthony Liguori
2013-07-04 7:42 ` Kevin Wolf
2013-07-04 7:55 ` Paolo Bonzini
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=878v1pqak4.fsf@codemonkey.ws \
--to=aliguori@us.ibm.com \
--cc=akong@redhat.com \
--cc=armbru@redhat.com \
--cc=lcapitulino@redhat.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=qiaonuohan@cn.fujitsu.com \
/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.