Message ID | 20130614095244.GA24720@t430s.nay.redhat.com |
---|---|
State | New |
Headers | show |
On 06/14/2013 10:52 AM, Amos Kong wrote: > On Fri, Jun 07, 2013 at 06:17:26PM +0800, Amos Kong wrote: >> On Fri, Jun 07, 2013 at 06:12:30PM +0800, Amos Kong wrote: >>> Sent out a draft patch in the end of this week. It doesn't support: >>> * output all stuffs in one shot. >>> * introspect event >>> * provide metadata date >>> >>> How can we define a dynamic dict in qmp-schema.json ? >>> >>> Currently I just output the raw json dict by a string, Libvirt needs >>> parse two times, convert the string to json format. >>> >>> qmp-schema.h: auto generated head file by qapi script >>> >>> Attached some examples. > > > Hello all, > > I defined a new type 'SchemaData', it contains 4 keys (type, name, data, returns) > > | { 'type': 'SchemaData', > | 'data': { 'type': 'str', 'name': 'str', '*data': 'str', '*returns': 'str' } } It seems like 'type' should be an enum rather than an open-coded 'str'. Returning 'data' and 'returns' as an open-coded 'str' is not nice - it requires the client to do further parsing. I was serious when I suggested adding additional layers of formalized structure to the result. My suggestion was something like: { 'type': 'SchemaDataMember', 'data': { 'option': 'str', 'type': 'str', '*optional': 'bool' } } { 'enum': 'SchemaMetatype', 'data': [ 'command', 'type', 'event' ] } { 'type': 'SchemaData', 'data': { 'name': 'str', 'metatype': 'SchemaMetatype', '*returns': 'str', '*data': [ 'SchemaDataMember' ] } } > | > | { 'command': 'query-qmp-schema', 'data': { '*type': 'str', '*name': 'str' }, > | 'returns': ['SchemaData'] } I'm still not sure whether optional filtering will be used by libvirt, or if it adds any complexity on your end (I'm not opposed to it, but I know that there hasn't been universal agreement on filtering queries yet). Again, it seems like 'type' should be an enum. Something like: { 'command': 'query-qmp-schema', 'data': { '*type': 'SchemaMetatype', '*name': 'str' }, 'returns': ['SchemaData'] } > > Then we can provice meaningful result to Libvirt. Currently I set a string > for SchemaData['data']. > > > I tried to define a dynamical dict for 'data', but it's failed. > > | { 'type': 'SchemaData', > | 'data': { 'type': 'str', 'name': 'str', '*data': '**', '*returns': 'str' } } (Failed!!) > > > Does qapi support to define a dynamical dict, then I can convert dict string > and set to SchemaData['data'] ? Rather, you want to take an arbitrary dict, and turn it into an array describing each key of the dict. That is, given the .json file containing: { 'command': 'add_client', 'data': { 'protocol': 'str', 'fdname': 'str', '*skipauth': 'bool', '*tls': 'bool' } } you would create an array of four members, describing each of the four dict members, resulting in this exchange: => { "execute": "query-qmp-schema", "arguments": { "name": "add_client" } } <= { "return": [ { "name": "add_client", "type": "command", "data": [ { "name": "protocol", "type": "str" }, { "name": "fdname", "type": "str" }, { "name": "skipauth", "type": "bool", "optional": true }, { "name": "tls", "type": "bool", "optional": true } ] } ] }
On 06/14/2013 10:52 AM, Amos Kong wrote: >>From 2b39fe0f380eea6a96de3589b3d148673d28c1ff Mon Sep 17 00:00:00 2001 > From: Amos Kong <akong@redhat.com> > Date: Fri, 7 Jun 2013 18:02:21 +0800 > Subject: [PATCH] full introspection support for QMP > > Signed-off-by: Amos Kong <akong@redhat.com> > --- > qapi-schema.json | 6 ++++++ > qmp-commands.hx | 23 +++++++++++++++++++++++ > qmp.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ > scripts/qapi-commands.py | 2 +- > scripts/qapi-types.py | 42 +++++++++++++++++++++++++++++++++++++++++- > scripts/qapi-visit.py | 2 +- > scripts/qapi.py | 13 ++++++++++++- > 7 files changed, 130 insertions(+), 4 deletions(-) > > diff --git a/qapi-schema.json b/qapi-schema.json > index ef1f657..128cc58 100644 > --- a/qapi-schema.json > +++ b/qapi-schema.json > @@ -3618,3 +3618,9 @@ > '*cpuid-input-ecx': 'int', > 'cpuid-register': 'X86CPURegister32', > 'features': 'int' } } > + > +{ 'type': 'SchemaData', > + 'data': { 'type': 'str', 'name': 'str', '*data': 'str', '*returns': 'str' } } > + > +{ 'command': 'query-qmp-schema', 'data': { '*type': 'str', '*name': 'str' }, > + 'returns': ['SchemaData'] } Needs documentation, and a since 1.6 notation in the docs (assuming that we still make it in time for 1.6). Also, see my other mail complaining that this is not structured enough, yet. > diff --git a/qmp-commands.hx b/qmp-commands.hx > index ffd130e..fc56fba 100644 > --- a/qmp-commands.hx > +++ b/qmp-commands.hx > @@ -2932,3 +2932,26 @@ Example: > + > +EQMP > \ No newline at end of file Fix that. > } > > +SchemaDataList * qmp_query_qmp_schema(bool has_type, const char * type, No space after '*' when declaring a pointer return or pointer argument. Should be: SchemaDataList *qmp_query_qmp_schema(bool has_type, const char *type, > +++ b/scripts/qapi-types.py > + > +struct qmp_schem { Aren't structs supposed to be named in CamelCase? > +const char *type; > +const char *name; > +const char *data; > +const char *returns; > +} qmp_schema_table[] = { > +""" > + > +for i in exprs_all[1]: > + print i > + > + data = returns = "" > + type = i.keys()[0] > + name = i[type] > + for k in i.keys(): > + if isinstance(i[k], OrderedDict): > + ret = {} > + for key in i[k]: > + ret[key] = i[k][key] > + i[k] = ret > + > + if i.has_key('data'): > + data = i['data'] I think this is where you'd need to do more processing of data to spit it out in a more structured format, but my python is too weak to actually write that conversion.
On Fri, Jun 14, 2013 at 12:09:33PM +0100, Eric Blake wrote: > On 06/14/2013 10:52 AM, Amos Kong wrote: > > +const char *type; > > +const char *name; > > +const char *data; > > +const char *returns; > > +} qmp_schema_table[] = { > > +""" > > + > > +for i in exprs_all[1]: > > + print i > > + > > + data = returns = "" > > + type = i.keys()[0] > > + name = i[type] > > + for k in i.keys(): > > + if isinstance(i[k], OrderedDict): > > + ret = {} > > + for key in i[k]: > > + ret[key] = i[k][key] > > + i[k] = ret > > + > > + if i.has_key('data'): > > + data = i['data'] > > I think this is where you'd need to do more processing of data to spit > it out in a more structured format, but my python is too weak to > actually write that conversion. I found a solution :-) Generate a string array in C head file to record the raw json string; Convert each json string to QObject, and convert the QObject to QDict for traversal by recursion; Define a new type and uion [1] to describe dynamical date for QMP. and allocate & connect nested members in recursion function. Finally, we can provide dynamical data with metadate according the definition in json file. I will send a v1 patch later. ---- [1] ---- { 'union': 'DataValue', 'data': { 'string': 'str', 'obj': 'DataObj' } } { 'type': 'DataObj', 'data': { '*key': 'str', 'value': 'DataValue' } } { 'type': 'SchemaData', 'data': { 'type': 'str', 'name': 'str', '*data': ['DataObj'], '*returns': ['DataObj'] } }
diff --git a/qapi-schema.json b/qapi-schema.json index ef1f657..128cc58 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3618,3 +3618,9 @@ '*cpuid-input-ecx': 'int', 'cpuid-register': 'X86CPURegister32', 'features': 'int' } } + +{ 'type': 'SchemaData', + 'data': { 'type': 'str', 'name': 'str', '*data': 'str', '*returns': 'str' } } + +{ 'command': 'query-qmp-schema', 'data': { '*type': 'str', '*name': 'str' }, + 'returns': ['SchemaData'] } diff --git a/qmp-commands.hx b/qmp-commands.hx index ffd130e..fc56fba 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -2932,3 +2932,26 @@ Example: <- { "return": {} } EQMP + + { + .name = "query-qmp-schema", + .args_type = "type:s?,name:s?", + .mhandler.cmd_new = qmp_marshal_input_query_qmp_schema, + }, + + +SQMP +query-qmp-schema +---------------- + +query qmp schema information + +Example: + +-> { "execute": "query-qmp-schema", "arguments": { "name" : "query-name" }} +<- { "return": [ + "{ 'command': 'query-name', 'returns': 'NameInfo' }" + ] + } + +EQMP \ No newline at end of file diff --git a/qmp.c b/qmp.c index 4c149b3..c062f88 100644 --- a/qmp.c +++ b/qmp.c @@ -25,6 +25,7 @@ #include "sysemu/blockdev.h" #include "qom/qom-qobject.h" #include "hw/boards.h" +#include "qmp-schema.h" NameInfo *qmp_query_name(Error **errp) { @@ -486,6 +487,51 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) return arch_query_cpu_definitions(errp); } +SchemaDataList * qmp_query_qmp_schema(bool has_type, const char * type, + bool has_name, const char * name, + Error **errp) +{ + SchemaDataList *list = NULL, *last_entry, *entry; + SchemaData *info; + int i; + + for (i = 0; qmp_schema_table[i].type; i++) { + if (has_type && strcmp(type, qmp_schema_table[i].type)) { + continue; + } + if (has_name && strcmp(name, qmp_schema_table[i].name)) { + continue; + } + + info = g_malloc0(sizeof(*info)); + info->type = g_strdup(qmp_schema_table[i].type); + info->name = g_strdup(qmp_schema_table[i].name); + + if (qmp_schema_table[i].data && strlen(qmp_schema_table[i].data) > 0) { + info->has_data = true; + } + info->data = g_strdup(qmp_schema_table[i].data); + + if (qmp_schema_table[i].returns && strlen(qmp_schema_table[i].returns) > 0) { + info->has_returns = true; + } + info->returns = g_strdup(qmp_schema_table[i].returns); + + entry = malloc(sizeof(SchemaDataList *)); + 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..30e7e56 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,46 @@ fdecl.write(mcgen(''' ''', guard=guardname(h_file))) -exprs = parse_schema(sys.stdin) +exprs_all = parse_schema(sys.stdin) + +schema_table = """ +/* convert qapi-schema.json to a string table */ + +struct qmp_schem { +const char *type; +const char *name; +const char *data; +const char *returns; +} qmp_schema_table[] = { +""" + +for i in exprs_all[1]: + print i + + data = returns = "" + type = i.keys()[0] + name = i[type] + for k in i.keys(): + if isinstance(i[k], OrderedDict): + ret = {} + for key in i[k]: + ret[key] = i[k][key] + i[k] = ret + + if i.has_key('data'): + data = i['data'] + if i.has_key('returns'): + returns = i['returns'] + + schema_table += '{"%s", "%s", "%s", "%s"},\n' % (type, name, data, returns) + +schema_table += '{NULL, NULL, 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..076b5dc 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,11 @@ def parse_schema(fp): expr += line elif expr: expr_eval = evaluate(expr) + + for name in ['command', 'type', 'enum', 'union']: + if expr_eval.has_key(name): + raw_exprs.append(expr_eval) + if expr_eval.has_key('enum'): add_enum(expr_eval['enum']) elif expr_eval.has_key('union'): @@ -102,13 +108,18 @@ def parse_schema(fp): if expr: expr_eval = evaluate(expr) + + for name in ['command', 'type', 'enum', 'union']: + if expr_eval.has_key(name): + raw_exprs.append(expr_eval) + 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: