From: Eric Blake <eblake@redhat.com>
To: Amos Kong <akong@redhat.com>
Cc: "kwolf@redhat.com" <kwolf@redhat.com>,
aliguori@us.ibm.com, armbru@redhat.com, qemu-devel@nongnu.org,
lcapitulino@redhat.com, pbonzini@redhat.com
Subject: Re: [Qemu-devel] [PATCH v2 2/2] full introspection support for QMP
Date: Fri, 19 Jul 2013 16:05:16 -0600 [thread overview]
Message-ID: <51E9B81C.2090105@redhat.com> (raw)
In-Reply-To: <1373971062-28909-3-git-send-email-akong@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 13690 bytes --]
On 07/16/2013 04:37 AM, Amos Kong wrote:
> Introduces new monitor command to query QMP schema information,
> the return data is a dynamical and nested dict/list, it contains
s/dynamical/dynamic/
> the useful metadata to help management to check feature support,
> QMP commands detail, etc.
>
> I added a document for QMP introspection support.
> (docs/qmp-full-introspection.txt)
Yay - docs make a world of difference.
>
> We need to parse all commands json definition, and generated a
mixing tense ("need" present, "generated" past); for commit messages,
present tense is generally appropriate. Thus:
s/generated/generate/
> dynamical tree, QMP infrastructure will convert the tree to
s/dynamical/dynamic/
> json string and return to QMP client.
>
> So here I defined a 'DataObject' type in qapi-schema.json,
> it's used to describe the dynamical dictionary/list/string.
s/dynamical/dynamic/
>
> { 'type': 'DataObject',
> 'data': { '*key': 'str', '*type': 'str', '*data': ['DataObject'] } }
>
> Not all the keys in data will be used.
> # List: type
> # Dict: key, type
> # nested List: type, data
> # nested Dict: key, type, data
I wonder if we can take advantage of Kevin's work on unions to make this
MUCH easier to use:
https://lists.gnu.org/archive/html/qemu-devel/2013-07/msg01407.html
{ 'enum': 'DataObjectType',
'data': [ 'command', 'struct', 'union', 'enum' ] }
# extend to add 'event' later
{ 'type': 'DataObjectBase',
'data': { 'name': 'str' } }
{ 'union': 'DataObjectMemberType',
'discriminator': {},
'data': { 'reference': 'str',
'inline': 'DataObject' } }
{ 'type': DataObjectMember',
'data': { 'name': 'str', 'type': 'DataObjectMemberType',
'*optional': 'bool', '*recursive': 'bool' } }
{ 'type': 'DataObjectStruct',
'data': { 'data': [ 'DataObjectMember' ] } }
{ 'type': 'DataObjectEnum',
'data': { 'data': [ 'str' ] } }
{ 'type': 'DataObjectUnion',
'data': { 'data': [ 'DataObjectMember' ], '*base': 'str',
'*discriminator': 'str' } }
{ 'type': 'DataObjectCommand',
'data': { 'data': [ 'DataObjectMember' ], '*returns': 'DataObject' } }
{ 'union': 'DataObject',
'base': 'DataObjectBase',
'discriminator': 'type',
'data': {
'struct': 'DataObjectStruct',
'enum': 'DataObjectEnum',
'union': 'DataObjectUnion',
'command': 'DataObjectCommand',
'array': {} }
>
> The DataObject is described in docs/qmp-full-introspection.txt in
> detail.
>
> The following content gives an example of query-tpm-types:
>
> ## Define example in qapi-schema.json:
>
> { 'enum': 'TpmType', 'data': [ 'passthrough' ] }
> { 'command': 'query-tpm-types', 'returns': ['TpmType'] }
It might be more useful to have a (made-up) example that shows as many
details as possible - a command that takes arguments and has a return
type will exercise more of the code paths than a query command with only
a return.
>
> ## Returned description:
>
> {
> "name": "query-tpm-types",
> "type": "Command",
> "returns": [
> {
> "type": "TpmType",
> "data": [
> {
> "type": "passthrough"
> }
> ]
I need a way to know the difference between a TpmType returned directly
in a dict, vs. a return containing an array of TpmType.
> }
> ]
> },
Thus, using the discriminated union I set out above, I would return
something like:
{ "name": "TpmType", "type": "enum",
"data": [ "passthrough" ] },
{ "name": "query-tpm-types", "type": "command",
"data": [ ],
"returns": { "name": "TpmType", "type": "array" } }
>
> 'TpmType' is a defined type, it will be extended in returned
> description. [ 'passthrough' ] is a list, so 'type' and 'data'
> will be used.
>
> TODO:
> We can add events definations to qapi-schema.json by another
s/definations/definitions/
> patch, then event can also be queried.
>
> Introduce another command 'query-qga-schema' to query QGA schema
> information, it's easy to add this support with current current
> patch.
Such a command would be part of the QGA interface, not a QMP command.
But yes, it is needed, and the ideal patch series from you will include
that patch as part of the series. But I don't mind waiting until we get
the interface nailed down before you actually implement the QGA repeat
of the interface.
>
> Signed-off-by: Amos Kong <akong@redhat.com>
> ---
> docs/qmp-full-introspection.txt | 143 +++++++++++++++++++
> qapi-schema.json | 69 +++++++++
> qmp-commands.hx | 39 +++++
> qmp.c | 307 ++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 558 insertions(+)
> create mode 100644 docs/qmp-full-introspection.txt
>
> diff --git a/docs/qmp-full-introspection.txt b/docs/qmp-full-introspection.txt
> new file mode 100644
> index 0000000..cc0fb80
> --- /dev/null
> +++ b/docs/qmp-full-introspection.txt
> @@ -0,0 +1,143 @@
> += full introspection support for QMP =
> +
Is it worth merging this into the existing qapi-code-gen.txt, or at
least having the two documents refer to one another, since they deal
with related concepts (turning the .json file into generated code)?
> +== Purpose ==
> +
> +Add a new interface to provide QMP schema information to management,
s/a new/an/ - after a year, the interface won't be new, but this doc
will still be relevant.
> +the return data is a dynamical and nested dict/list, it contains
s/dynamical/dynamic/
> +the useful metadata to help management to check feature support,
> +QMP commands detail, etc.
> +
> +== Usage ==
> +
> +Execute QMP command:
> +
> + { "execute": "query-qmp-schema" }
> +
> +Returns:
> +
> + { "return": [
> + {
> + "name": "query-name",
> + "type": "Command",
> + "returns": [
> + {
> + "key": "*name",
> + "type": "str"
> + }
> + ]
Are we trying to use struct names where they exist for compactness, or
trying to inline-expand everything as far as possible until we run into
self-referential recursion?
> + },
> + ...
> + }
> +
> +The whole schema information will be returned in one go, it contains
> +commands and event. It doesn't support to be filtered by type or name.
s/event/events/
s/It/At present, it/
s/to be filtered/filtering/
> +
> +We have four types (ImageInfo, BlockStats, PciDeviceInfo, SchemaData)
This list will get out of date quickly. I'd just word it generically:
We have several self-referential types
> +that uses themself in their own define data directly or indirectly,
s/uses themself/use themselves/
> +we will not repeatedly extend them to avoid dead loop.
Would it be worth a flag in the QMP output when a type is not being
further expanded because it is a nested occurrence of itself? That is,
given my proposed layout above, ImageInfo would turn into something like:
{ "name": "ImageInfo", "type": "struct",
"data": [ { "name": "filename", "type", "str" },
...
{ "name": "backing-image", "type": "ImageInfo",
"optional": true, "recursive": true } ] },
> +
> +== more description of 'DataObject' type ==
> +
> +We use 'DataObject' to describe dynamical data struct, it might be
s/dynamical/dynamic/
> +nested dictionary, list or string.
> +
> +'DataObject' itself is a arbitrary and nested dictionary, the
> +dictionary has three keys ('key', 'type', 'data'), 'key' and
spacing is odd.
> +'data' are optional.
> +
> +* For describing Dictionary, we set the key to 'key', and set the
> + value to 'type'
> +* For describing List, we don't set 'key', just set the value to
> + 'type'
Again, if you use the idea of a discriminated union, you don't need
quite the complexity in describing this: dictionaries are listed with
key/type/optional tuples, lists (enums) are listed with just an array of
strings, and the QAPI perfectly described that difference by the
discriminator telling you 'struct' vs. 'enum'.
> +* If the value of dictionary or list is non-native type, we extend
> + the non-native type to dictionary, set it to 'data', and set the
> + non-native type's name to 'type'.
Again, I tried to set up the QAPI that describes something that uses an
anonymous union that either gives a string (the name of a primitive or
already-defined type) or a struct (a recursion into another layer of
struct describing the structure of that type in line).
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 7b9fef1..cf03391 100644
> --- a/qapi-schema.json
> +++ b/qapi-schema.json
> @@ -3679,3 +3679,72 @@
> '*cpuid-input-ecx': 'int',
> 'cpuid-register': 'X86CPURegister32',
> 'features': 'int' } }
> +
> +##
> +# @DataObject
> +#
> +# Details of a data object, it can be nested dictionary/list
> +#
> +# @key: #optional Data object key
> +#
> +# @type: Data object type name
> +#
> +# @optional: #optional whether the data object is optional
mention that the default is false.
> +#
> +# @data: #optional DataObject list, can be a dictionary or list type
so if 'data' is present, we are describing @type in more detail; if it
is absent, then @type is primitive.
> +#
> +# Since: 1.6
> +##
> +{ 'type': 'DataObject',
> + 'data': { '*key': 'str', '*type': 'str', '*optional': 'bool', '*data': ['DataObject'] } }
> +
'*type' should be an enum type, not open-coded string.
> +##
> +# @SchemaMetaType
> +#
> +# Possible meta types of a schema entry
> +#
> +# @Command: QMP command
> +#
> +# @Type: defined new data type
> +#
> +# @Enumeration: enumeration data type
> +#
> +# @Union: union data type
> +#
> +# Since: 1.6
> +##
> +{ 'enum': 'SchemaMetaType',
> + 'data': ['Command', 'Type', 'Enumeration', 'Union'] }
Do we need to start these with a capital? On the other hand, having
them as capitals may make it easier to ensure we are outputting correct
information.
> +
> +##
> +# @SchemaEntry
> +#
> +# Details of schema items
> +#
> +# @type: Entry's type in string format
> +#
> +# @name: Entry name
> +#
> +# @data: #optional list of DataObject. This can have different meaning
> +# depending on the 'type' value. For example, for a QMP command,
> +# this member contains an argument listing. For an enumeration,
> +# it contains the enum's values and so on
This argues for making it a union type.
> +#
> +# @returns: #optional list of DataObject, return data after executing
> +# QMP command
> +#
> +# Since: 1.6
> +##
> +{ 'type': 'SchemaEntry', 'data': { 'type': 'SchemaMetaType',
> + 'name': 'str', '*data': ['DataObject'], '*returns': ['DataObject'] } }
> +
> +##
> +# @query-qmp-schema
> +#
> +# Query QMP schema information
> +#
> +# Returns: list of @SchemaEntry. Returns an error if json string is invalid.
If you don't take any arguments, then the "returns an error" statement
is impossible.
> +#
> +# Since: 1.6
> +##
> +{ 'command': 'query-qmp-schema', 'returns': ['SchemaEntry'] }
> diff --git a/qmp-commands.hx b/qmp-commands.hx
> index e075df4..e3cbe93 100644
> --- a/qmp-commands.hx
>
> +/*
> + * Use a string to record the visit path, type index of each node
> + * will be saved to the string, indexes are split by ':'.
> + */
> +static char visit_path_str[1024];
Is a fixed width buffer really the best solution, or does glib offer us
something better? For example, a hash table, or even a growable string.
> +
> + for (i = 0; qmp_schema_table[i]; i++) {
> + data = qobject_from_json(qmp_schema_table[i]);
> + assert(data != NULL);
> +
> + qdict = qobject_to_qdict(data);
> + assert(qdict != NULL);
> +
> + ent = qdict_first(qdict);
> + if (!qdict_get(qdict, "enum") && !qdict_get(qdict, "type")
> + && !qdict_get(qdict, "union")) {
> + continue;
> + }
Why are we doing the work in C code, every time the command is called?
Wouldn't it be more efficient to do the work in python code, at the time
the qmp_schema_table C code is first generated, so that the generated
code is already in the right format?
> +SchemaEntryList *qmp_query_qmp_schema(Error **errp)
> +{
> + SchemaEntryList *list, *last_entry, *entry;
> + SchemaEntry *info;
> + DataObjectList *obj_entry;
> + DataObject *obj_info;
> + QObject *data;
> + QDict *qdict;
> + int i;
> +
> + list = NULL;
> + for (i = 0; qmp_schema_table[i]; i++) {
> + data = qobject_from_json(qmp_schema_table[i]);
> + assert(data != NULL);
> +
> + qdict = qobject_to_qdict(data);
> + assert(qdict != NULL);
> +
> + if (qdict_get(qdict, "command")) {
> + info = g_malloc0(sizeof(*info));
> + info->type = SCHEMA_META_TYPE_COMMAND;
> + info->name = strdup(qdict_get_str(qdict, "command"));
> + } else {
> + continue;
> + }
> +
Don't we want to also output types (struct, union, enum) and eventually
events, not just commands?
I hope we're converging, but I'm starting to worry that we won't have a
design in place in time for the 1.6 release.
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 621 bytes --]
next prev parent reply other threads:[~2013-07-19 22:05 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-07-16 10:37 [Qemu-devel] [PATCH v2 0/2] QMP full introspection Amos Kong
2013-07-16 10:37 ` [Qemu-devel] [PATCH v2 1/2] qapi: change qapi to convert schema json Amos Kong
2013-07-17 20:09 ` Luiz Capitulino
2013-07-26 3:39 ` Amos Kong
2013-07-19 12:27 ` Eric Blake
2013-07-26 6:53 ` Amos Kong
2013-07-16 10:37 ` [Qemu-devel] [PATCH v2 2/2] full introspection support for QMP Amos Kong
2013-07-16 10:48 ` Paolo Bonzini
2013-07-16 11:04 ` Amos Kong
2013-07-16 11:08 ` Paolo Bonzini
2013-07-16 12:04 ` Amos Kong
2013-07-16 12:18 ` Paolo Bonzini
2013-07-26 7:03 ` Amos Kong
2013-07-17 20:36 ` Luiz Capitulino
2013-07-19 20:26 ` Eric Blake
2013-07-26 7:21 ` Amos Kong
2013-07-19 22:05 ` Eric Blake [this message]
2013-07-26 7:51 ` Amos Kong
2013-07-26 11:52 ` Eric Blake
2013-11-27 2:32 ` Amos Kong
2013-11-27 9:51 ` Kevin Wolf
[not found] ` <20131220110001.GC2890@amosk.info>
2013-12-20 11:57 ` Amos Kong
2013-12-20 18:03 ` Paolo Bonzini
2013-12-23 8:11 ` Amos Kong
2013-12-23 6:32 ` Wenchao Xia
2013-12-23 7:15 ` Amos Kong
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=51E9B81C.2090105@redhat.com \
--to=eblake@redhat.com \
--cc=akong@redhat.com \
--cc=aliguori@us.ibm.com \
--cc=armbru@redhat.com \
--cc=kwolf@redhat.com \
--cc=lcapitulino@redhat.com \
--cc=pbonzini@redhat.com \
--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.