diff mbox

full introspection support for QMP

Message ID 1371644677-11041-1-git-send-email-akong@redhat.com
State New
Headers show

Commit Message

Amos Kong June 19, 2013, 12:24 p.m. UTC
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>
---
 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(-)

Comments

Amos Kong June 19, 2013, 12:49 p.m. UTC | #1
On Wed, Jun 19, 2013 at 08:24:37PM +0800, Amos Kong wrote:
> 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.
 
I didn't implement to return complete schema in one go in this
version, will do it in next version. We have a recursive define
'DataObject', we only display one layer for it.

>>>> dict
  {'type': 't1', 'data': {'a1': 'b1'} }
  {'type': 't2', 'data': {'a2': 't1'} }

then we would retrieve t2 to:

   {'type': 't2', 'data': {'a2': {'a1': 'b1'} } }

>>>> list
  {'enum': 't1', 'data': ['a1', 'b1'] }
  {'type': 't2', 'data': {'a2': 't1'} }

then we would retrieve t2 to:

   {'type': 't2', 'data': {'a2': ['a1', 'b1'] } }

>>>> union
  {'union': 't1', 'data': {'a1':'v1', 'b1':'v2'} }
  {'type': 't2', 'data': {'a2': 't1'} }

then we would retrieve t2 to:

   {'type': 't2', 'data': {'a2': {'a2': ['a1', 'b1'] } } }

 
> Signed-off-by: Amos Kong <akong@redhat.com>
> ---
>  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(-)


You can find three kind of examples(string/list/dict) in the bottom.
Attached (query-qmp-schema--output.txt) the full output of execut query-qmp-schema command.

String:
{ 'command': 'query-name', 'returns': 'NameInfo' }
---------------------------------------------------------------------
        {
            "name": "NameInfo",
            "type": "Type",
            "data": [
                {
                    "name": "*name",
                    "type": "str"
                }
            ]
        },


List:
{ 'enum': 'ErrorClass',
  'data': [ 'GenericError', 'CommandNotFound', 'DeviceEncrypted',
            'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap' ] }
---------------------------------------------------------------------
        {
            "name": "ErrorClass",
            "type": "Enumeration",
            "data": [
                {
                    "type": "GenericError"
                },
                {
                    "type": "CommandNotFound"
                },
                {
                    "type": "DeviceEncrypted"
                },
                {
                    "type": "DeviceNotActive"
                },
                {
                    "type": "DeviceNotFound"
                },
                {
                    "type": "KVMMissingCap"
                }
            ]
        },


Dict:
{ 'type': 'VersionInfo',
  'data': {'qemu': {'major': 'int', 'minor': 'int', 'micro': 'int'},
           'package': 'str'} }
---------------------------------------------------------------------
        {
            "name": "VersionInfo",
            "type": "Type",
            "data": [
                {
                    "name": "qemu",
                    "data": [
                        {
                            "name": "micro",
                            "type": "int"
                        },
                        {
                            "name": "minor",
                            "type": "int"
                        },
                        {
                            "name": "major",
                            "type": "int"
                        }
                    ]
                },
                {
                    "name": "package",
                    "type": "str"
                }
            ]
        },
{
    "QMP": {
        "version": {
            "qemu": {
                "micro": 50, 
                "minor": 5, 
                "major": 1
            }, 
            "package": ""
        }, 
        "capabilities": [
        ]
    }
}
{
    "return": {
    }
}
{
    "return": [
        {
            "name": "ErrorClass", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "GenericError"
                }, 
                {
                    "type": "CommandNotFound"
                }, 
                {
                    "type": "DeviceEncrypted"
                }, 
                {
                    "type": "DeviceNotActive"
                }, 
                {
                    "type": "DeviceNotFound"
                }, 
                {
                    "type": "KVMMissingCap"
                }
            ]
        }, 
        {
            "name": "add_client", 
            "type": "Command", 
            "data": [
                {
                    "name": "*skipauth", 
                    "type": "bool"
                }, 
                {
                    "name": "protocol", 
                    "type": "str"
                }, 
                {
                    "name": "fdname", 
                    "type": "str"
                }, 
                {
                    "name": "*tls", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "NameInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "*name", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-name", 
            "type": "Command"
        }, 
        {
            "name": "VersionInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "qemu", 
                    "data": [
                        {
                            "name": "micro", 
                            "type": "int"
                        }, 
                        {
                            "name": "minor", 
                            "type": "int"
                        }, 
                        {
                            "name": "major", 
                            "type": "int"
                        }
                    ]
                }, 
                {
                    "name": "package", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-version", 
            "type": "Command"
        }, 
        {
            "name": "KvmInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "enabled", 
                    "type": "bool"
                }, 
                {
                    "name": "present", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "query-kvm", 
            "type": "Command"
        }, 
        {
            "name": "RunState", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "debug"
                }, 
                {
                    "type": "inmigrate"
                }, 
                {
                    "type": "internal-error"
                }, 
                {
                    "type": "io-error"
                }, 
                {
                    "type": "paused"
                }, 
                {
                    "type": "postmigrate"
                }, 
                {
                    "type": "prelaunch"
                }, 
                {
                    "type": "finish-migrate"
                }, 
                {
                    "type": "restore-vm"
                }, 
                {
                    "type": "running"
                }, 
                {
                    "type": "save-vm"
                }, 
                {
                    "type": "shutdown"
                }, 
                {
                    "type": "suspended"
                }, 
                {
                    "type": "watchdog"
                }, 
                {
                    "type": "guest-panicked"
                }
            ]
        }, 
        {
            "name": "SnapshotInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "vm-clock-nsec", 
                    "type": "int"
                }, 
                {
                    "name": "name", 
                    "type": "str"
                }, 
                {
                    "name": "date-sec", 
                    "type": "int"
                }, 
                {
                    "name": "date-nsec", 
                    "type": "int"
                }, 
                {
                    "name": "vm-clock-sec", 
                    "type": "int"
                }, 
                {
                    "name": "id", 
                    "type": "str"
                }, 
                {
                    "name": "vm-state-size", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "ImageInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "*backing-filename", 
                    "type": "str"
                }, 
                {
                    "name": "*actual-size", 
                    "type": "int"
                }, 
                {
                    "name": "*backing-image", 
                    "type": "ImageInfo"
                }, 
                {
                    "name": "*full-backing-filename", 
                    "type": "str"
                }, 
                {
                    "name": "*cluster-size", 
                    "type": "int"
                }, 
                {
                    "name": "virtual-size", 
                    "type": "int"
                }, 
                {
                    "name": "filename", 
                    "type": "str"
                }, 
                {
                    "name": "*encrypted", 
                    "type": "bool"
                }, 
                {
                    "name": "format", 
                    "type": "str"
                }, 
                {
                    "name": "*snapshots", 
                    "data": [
                        {
                            "type": "SnapshotInfo"
                        }
                    ]
                }, 
                {
                    "name": "*backing-filename-format", 
                    "type": "str"
                }, 
                {
                    "name": "*dirty-flag", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "ImageCheck", 
            "type": "Type", 
            "data": [
                {
                    "name": "*allocated-clusters", 
                    "type": "int"
                }, 
                {
                    "name": "*image-end-offset", 
                    "type": "int"
                }, 
                {
                    "name": "*leaks-fixed", 
                    "type": "int"
                }, 
                {
                    "name": "check-errors", 
                    "type": "int"
                }, 
                {
                    "name": "*corruptions-fixed", 
                    "type": "int"
                }, 
                {
                    "name": "*fragmented-clusters", 
                    "type": "int"
                }, 
                {
                    "name": "*compressed-clusters", 
                    "type": "int"
                }, 
                {
                    "name": "filename", 
                    "type": "str"
                }, 
                {
                    "name": "*leaks", 
                    "type": "int"
                }, 
                {
                    "name": "format", 
                    "type": "str"
                }, 
                {
                    "name": "*total-clusters", 
                    "type": "int"
                }, 
                {
                    "name": "*corruptions", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "StatusInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "status", 
                    "type": "RunState"
                }, 
                {
                    "name": "singlestep", 
                    "type": "bool"
                }, 
                {
                    "name": "running", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "query-status", 
            "type": "Command"
        }, 
        {
            "name": "UuidInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "UUID", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-uuid", 
            "type": "Command"
        }, 
        {
            "name": "ChardevInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "filename", 
                    "type": "str"
                }, 
                {
                    "name": "label", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-chardev", 
            "type": "Command"
        }, 
        {
            "name": "DataFormat", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "utf8"
                }, 
                {
                    "type": "base64"
                }
            ]
        }, 
        {
            "name": "ringbuf-write", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*format", 
                    "type": "DataFormat"
                }, 
                {
                    "name": "data", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "ringbuf-read", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*format", 
                    "type": "DataFormat"
                }, 
                {
                    "name": "size", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "CommandInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "name", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-commands", 
            "type": "Command"
        }, 
        {
            "name": "EventInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "name", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-events", 
            "type": "Command"
        }, 
        {
            "name": "MigrationStats", 
            "type": "Type", 
            "data": [
                {
                    "name": "total", 
                    "type": "int"
                }, 
                {
                    "name": "remaining", 
                    "type": "int"
                }, 
                {
                    "name": "transferred", 
                    "type": "int"
                }, 
                {
                    "name": "duplicate", 
                    "type": "int"
                }, 
                {
                    "name": "dirty-pages-rate", 
                    "type": "int"
                }, 
                {
                    "name": "skipped", 
                    "type": "int"
                }, 
                {
                    "name": "normal-bytes", 
                    "type": "int"
                }, 
                {
                    "name": "normal", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "XBZRLECacheStats", 
            "type": "Type", 
            "data": [
                {
                    "name": "bytes", 
                    "type": "int"
                }, 
                {
                    "name": "cache-size", 
                    "type": "int"
                }, 
                {
                    "name": "pages", 
                    "type": "int"
                }, 
                {
                    "name": "overflow", 
                    "type": "int"
                }, 
                {
                    "name": "cache-miss", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "MigrationInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "*disk", 
                    "type": "MigrationStats"
                }, 
                {
                    "name": "*xbzrle-cache", 
                    "type": "XBZRLECacheStats"
                }, 
                {
                    "name": "*downtime", 
                    "type": "int"
                }, 
                {
                    "name": "*expected-downtime", 
                    "type": "int"
                }, 
                {
                    "name": "*status", 
                    "type": "str"
                }, 
                {
                    "name": "*ram", 
                    "type": "MigrationStats"
                }, 
                {
                    "name": "*total-time", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "query-migrate", 
            "type": "Command"
        }, 
        {
            "name": "MigrationCapability", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "xbzrle"
                }
            ]
        }, 
        {
            "name": "MigrationCapabilityStatus", 
            "type": "Type", 
            "data": [
                {
                    "name": "state", 
                    "type": "bool"
                }, 
                {
                    "name": "capability", 
                    "type": "MigrationCapability"
                }
            ]
        }, 
        {
            "name": "migrate-set-capabilities", 
            "type": "Command", 
            "data": [
                {
                    "name": "capabilities", 
                    "data": [
                        {
                            "type": "MigrationCapabilityStatus"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-migrate-capabilities", 
            "type": "Command"
        }, 
        {
            "name": "MouseInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "index", 
                    "type": "int"
                }, 
                {
                    "name": "name", 
                    "type": "str"
                }, 
                {
                    "name": "current", 
                    "type": "bool"
                }, 
                {
                    "name": "absolute", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "query-mice", 
            "type": "Command"
        }, 
        {
            "name": "CpuInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "*PC", 
                    "type": "int"
                }, 
                {
                    "name": "*pc", 
                    "type": "int"
                }, 
                {
                    "name": "current", 
                    "type": "bool"
                }, 
                {
                    "name": "CPU", 
                    "type": "int"
                }, 
                {
                    "name": "halted", 
                    "type": "bool"
                }, 
                {
                    "name": "*npc", 
                    "type": "int"
                }, 
                {
                    "name": "*nip", 
                    "type": "int"
                }, 
                {
                    "name": "thread_id", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "query-cpus", 
            "type": "Command"
        }, 
        {
            "name": "BlockDeviceInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "iops_rd", 
                    "type": "int"
                }, 
                {
                    "name": "*backing_file", 
                    "type": "str"
                }, 
                {
                    "name": "image", 
                    "type": "ImageInfo"
                }, 
                {
                    "name": "iops_wr", 
                    "type": "int"
                }, 
                {
                    "name": "ro", 
                    "type": "bool"
                }, 
                {
                    "name": "backing_file_depth", 
                    "type": "int"
                }, 
                {
                    "name": "drv", 
                    "type": "str"
                }, 
                {
                    "name": "iops", 
                    "type": "int"
                }, 
                {
                    "name": "bps_wr", 
                    "type": "int"
                }, 
                {
                    "name": "encrypted", 
                    "type": "bool"
                }, 
                {
                    "name": "bps", 
                    "type": "int"
                }, 
                {
                    "name": "bps_rd", 
                    "type": "int"
                }, 
                {
                    "name": "file", 
                    "type": "str"
                }, 
                {
                    "name": "encryption_key_missing", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "BlockDeviceIoStatus", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "ok"
                }, 
                {
                    "type": "failed"
                }, 
                {
                    "type": "nospace"
                }
            ]
        }, 
        {
            "name": "BlockDirtyInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "granularity", 
                    "type": "int"
                }, 
                {
                    "name": "count", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "BlockInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "locked", 
                    "type": "bool"
                }, 
                {
                    "name": "removable", 
                    "type": "bool"
                }, 
                {
                    "name": "*dirty", 
                    "type": "BlockDirtyInfo"
                }, 
                {
                    "name": "*io-status", 
                    "type": "BlockDeviceIoStatus"
                }, 
                {
                    "name": "*tray_open", 
                    "type": "bool"
                }, 
                {
                    "name": "type", 
                    "type": "str"
                }, 
                {
                    "name": "*inserted", 
                    "type": "BlockDeviceInfo"
                }
            ]
        }, 
        {
            "name": "query-block", 
            "type": "Command"
        }, 
        {
            "name": "BlockDeviceStats", 
            "type": "Type", 
            "data": [
                {
                    "name": "flush_total_time_ns", 
                    "type": "int"
                }, 
                {
                    "name": "wr_highest_offset", 
                    "type": "int"
                }, 
                {
                    "name": "wr_total_time_ns", 
                    "type": "int"
                }, 
                {
                    "name": "wr_bytes", 
                    "type": "int"
                }, 
                {
                    "name": "rd_total_time_ns", 
                    "type": "int"
                }, 
                {
                    "name": "flush_operations", 
                    "type": "int"
                }, 
                {
                    "name": "wr_operations", 
                    "type": "int"
                }, 
                {
                    "name": "rd_bytes", 
                    "type": "int"
                }, 
                {
                    "name": "rd_operations", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "BlockStats", 
            "type": "Type", 
            "data": [
                {
                    "name": "stats", 
                    "type": "BlockDeviceStats"
                }, 
                {
                    "name": "*device", 
                    "type": "str"
                }, 
                {
                    "name": "*parent", 
                    "type": "BlockStats"
                }
            ]
        }, 
        {
            "name": "query-blockstats", 
            "type": "Command"
        }, 
        {
            "name": "VncClientInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "family", 
                    "type": "str"
                }, 
                {
                    "name": "*sasl_username", 
                    "type": "str"
                }, 
                {
                    "name": "service", 
                    "type": "str"
                }, 
                {
                    "name": "host", 
                    "type": "str"
                }, 
                {
                    "name": "*x509_dname", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "VncInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "*clients", 
                    "data": [
                        {
                            "type": "VncClientInfo"
                        }
                    ]
                }, 
                {
                    "name": "*family", 
                    "type": "str"
                }, 
                {
                    "name": "enabled", 
                    "type": "bool"
                }, 
                {
                    "name": "*service", 
                    "type": "str"
                }, 
                {
                    "name": "*host", 
                    "type": "str"
                }, 
                {
                    "name": "*auth", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-vnc", 
            "type": "Command"
        }, 
        {
            "name": "SpiceChannel", 
            "type": "Type", 
            "data": [
                {
                    "name": "port", 
                    "type": "str"
                }, 
                {
                    "name": "family", 
                    "type": "str"
                }, 
                {
                    "name": "channel-type", 
                    "type": "int"
                }, 
                {
                    "name": "connection-id", 
                    "type": "int"
                }, 
                {
                    "name": "host", 
                    "type": "str"
                }, 
                {
                    "name": "channel-id", 
                    "type": "int"
                }, 
                {
                    "name": "tls", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "SpiceQueryMouseMode", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "client"
                }, 
                {
                    "type": "server"
                }, 
                {
                    "type": "unknown"
                }
            ]
        }, 
        {
            "name": "SpiceInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "migrated", 
                    "type": "bool"
                }, 
                {
                    "name": "enabled", 
                    "type": "bool"
                }, 
                {
                    "name": "*port", 
                    "type": "int"
                }, 
                {
                    "name": "*channels", 
                    "data": [
                        {
                            "type": "SpiceChannel"
                        }
                    ]
                }, 
                {
                    "name": "*compiled-version", 
                    "type": "str"
                }, 
                {
                    "name": "mouse-mode", 
                    "type": "SpiceQueryMouseMode"
                }, 
                {
                    "name": "*tls-port", 
                    "type": "int"
                }, 
                {
                    "name": "*host", 
                    "type": "str"
                }, 
                {
                    "name": "*auth", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-spice", 
            "type": "Command"
        }, 
        {
            "name": "BalloonInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "actual", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "query-balloon", 
            "type": "Command"
        }, 
        {
            "name": "PciMemoryRange", 
            "type": "Type", 
            "data": [
                {
                    "name": "limit", 
                    "type": "int"
                }, 
                {
                    "name": "base", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "PciMemoryRegion", 
            "type": "Type", 
            "data": [
                {
                    "name": "*prefetch", 
                    "type": "bool"
                }, 
                {
                    "name": "bar", 
                    "type": "int"
                }, 
                {
                    "name": "size", 
                    "type": "int"
                }, 
                {
                    "name": "*mem_type_64", 
                    "type": "bool"
                }, 
                {
                    "name": "address", 
                    "type": "int"
                }, 
                {
                    "name": "type", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "PciBridgeInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "bus", 
                    "data": [
                        {
                            "name": "prefetchable_range", 
                            "type": "PciMemoryRange"
                        }, 
                        {
                            "name": "memory_range", 
                            "type": "PciMemoryRange"
                        }, 
                        {
                            "name": "secondary", 
                            "type": "int"
                        }, 
                        {
                            "name": "io_range", 
                            "type": "PciMemoryRange"
                        }, 
                        {
                            "name": "number", 
                            "type": "int"
                        }, 
                        {
                            "name": "subordinate", 
                            "type": "int"
                        }
                    ]
                }, 
                {
                    "name": "*devices", 
                    "data": [
                        {
                            "type": "PciDeviceInfo"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "PciDeviceInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "bus", 
                    "type": "int"
                }, 
                {
                    "name": "*irq", 
                    "type": "int"
                }, 
                {
                    "name": "qdev_id", 
                    "type": "str"
                }, 
                {
                    "name": "slot", 
                    "type": "int"
                }, 
                {
                    "name": "*pci_bridge", 
                    "type": "PciBridgeInfo"
                }, 
                {
                    "name": "class_info", 
                    "data": [
                        {
                            "name": "*desc", 
                            "type": "str"
                        }, 
                        {
                            "name": "class", 
                            "type": "int"
                        }
                    ]
                }, 
                {
                    "name": "id", 
                    "data": [
                        {
                            "name": "device", 
                            "type": "int"
                        }, 
                        {
                            "name": "vendor", 
                            "type": "int"
                        }
                    ]
                }, 
                {
                    "name": "function", 
                    "type": "int"
                }, 
                {
                    "name": "regions", 
                    "data": [
                        {
                            "type": "PciMemoryRegion"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "PciInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "bus", 
                    "type": "int"
                }, 
                {
                    "name": "devices", 
                    "data": [
                        {
                            "type": "PciDeviceInfo"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-pci", 
            "type": "Command"
        }, 
        {
            "name": "BlockdevOnError", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "report"
                }, 
                {
                    "type": "ignore"
                }, 
                {
                    "type": "enospc"
                }, 
                {
                    "type": "stop"
                }
            ]
        }, 
        {
            "name": "MirrorSyncMode", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "top"
                }, 
                {
                    "type": "full"
                }, 
                {
                    "type": "none"
                }
            ]
        }, 
        {
            "name": "BlockJobInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "io-status", 
                    "type": "BlockDeviceIoStatus"
                }, 
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "busy", 
                    "type": "bool"
                }, 
                {
                    "name": "len", 
                    "type": "int"
                }, 
                {
                    "name": "offset", 
                    "type": "int"
                }, 
                {
                    "name": "paused", 
                    "type": "bool"
                }, 
                {
                    "name": "speed", 
                    "type": "int"
                }, 
                {
                    "name": "type", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-block-jobs", 
            "type": "Command"
        }, 
        {
            "name": "quit", 
            "type": "Command"
        }, 
        {
            "name": "stop", 
            "type": "Command"
        }, 
        {
            "name": "system_reset", 
            "type": "Command"
        }, 
        {
            "name": "system_powerdown", 
            "type": "Command"
        }, 
        {
            "name": "cpu", 
            "type": "Command", 
            "data": [
                {
                    "name": "index", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "cpu-add", 
            "type": "Command", 
            "data": [
                {
                    "name": "id", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "memsave", 
            "type": "Command", 
            "data": [
                {
                    "name": "filename", 
                    "type": "str"
                }, 
                {
                    "name": "size", 
                    "type": "int"
                }, 
                {
                    "name": "val", 
                    "type": "int"
                }, 
                {
                    "name": "*cpu-index", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "pmemsave", 
            "type": "Command", 
            "data": [
                {
                    "name": "filename", 
                    "type": "str"
                }, 
                {
                    "name": "size", 
                    "type": "int"
                }, 
                {
                    "name": "val", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "cont", 
            "type": "Command"
        }, 
        {
            "name": "system_wakeup", 
            "type": "Command"
        }, 
        {
            "name": "inject-nmi", 
            "type": "Command"
        }, 
        {
            "name": "set_link", 
            "type": "Command", 
            "data": [
                {
                    "name": "name", 
                    "type": "str"
                }, 
                {
                    "name": "up", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "block_passwd", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "password", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "balloon", 
            "type": "Command", 
            "data": [
                {
                    "name": "value", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "block_resize", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "size", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "NewImageMode", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "existing"
                }, 
                {
                    "type": "absolute-paths"
                }
            ]
        }, 
        {
            "name": "BlockdevSnapshot", 
            "type": "Type", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*mode", 
                    "type": "NewImageMode"
                }, 
                {
                    "name": "*format", 
                    "type": "str"
                }, 
                {
                    "name": "snapshot-file", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "TransactionAction", 
            "type": "Union", 
            "data": [
                {
                    "name": "blockdev-snapshot-sync", 
                    "type": "BlockdevSnapshot"
                }
            ]
        }, 
        {
            "name": "transaction", 
            "type": "Command", 
            "data": [
                {
                    "name": "actions", 
                    "data": [
                        {
                            "type": "TransactionAction"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "blockdev-snapshot-sync", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*mode", 
                    "type": "NewImageMode"
                }, 
                {
                    "name": "*format", 
                    "type": "str"
                }, 
                {
                    "name": "snapshot-file", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "human-monitor-command", 
            "type": "Command", 
            "data": [
                {
                    "name": "command-line", 
                    "type": "str"
                }, 
                {
                    "name": "*cpu-index", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "block-commit", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*speed", 
                    "type": "int"
                }, 
                {
                    "name": "*base", 
                    "type": "str"
                }, 
                {
                    "name": "top", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "drive-mirror", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*speed", 
                    "type": "int"
                }, 
                {
                    "name": "*mode", 
                    "type": "NewImageMode"
                }, 
                {
                    "name": "*buf-size", 
                    "type": "int"
                }, 
                {
                    "name": "*format", 
                    "type": "str"
                }, 
                {
                    "name": "*on-source-error", 
                    "type": "BlockdevOnError"
                }, 
                {
                    "name": "*on-target-error", 
                    "type": "BlockdevOnError"
                }, 
                {
                    "name": "sync", 
                    "type": "MirrorSyncMode"
                }, 
                {
                    "name": "*granularity", 
                    "type": "uint32"
                }, 
                {
                    "name": "target", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "migrate_cancel", 
            "type": "Command"
        }, 
        {
            "name": "migrate_set_downtime", 
            "type": "Command", 
            "data": [
                {
                    "name": "value", 
                    "type": "number"
                }
            ]
        }, 
        {
            "name": "migrate_set_speed", 
            "type": "Command", 
            "data": [
                {
                    "name": "value", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "migrate-set-cache-size", 
            "type": "Command", 
            "data": [
                {
                    "name": "value", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "query-migrate-cache-size", 
            "type": "Command"
        }, 
        {
            "name": "ObjectPropertyInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "name", 
                    "type": "str"
                }, 
                {
                    "name": "type", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "qom-list", 
            "type": "Command", 
            "data": [
                {
                    "name": "path", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "qom-get", 
            "type": "Command", 
            "data": [
                {
                    "name": "path", 
                    "type": "str"
                }, 
                {
                    "name": "property", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "qom-set", 
            "type": "Command", 
            "data": [
                {
                    "name": "path", 
                    "type": "str"
                }, 
                {
                    "name": "property", 
                    "type": "str"
                }, 
                {
                    "name": "value", 
                    "type": "visitor"
                }
            ]
        }, 
        {
            "name": "set_password", 
            "type": "Command", 
            "data": [
                {
                    "name": "*connected", 
                    "type": "str"
                }, 
                {
                    "name": "protocol", 
                    "type": "str"
                }, 
                {
                    "name": "password", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "expire_password", 
            "type": "Command", 
            "data": [
                {
                    "name": "protocol", 
                    "type": "str"
                }, 
                {
                    "name": "time", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "eject", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*force", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "change-vnc-password", 
            "type": "Command", 
            "data": [
                {
                    "name": "password", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "change", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*arg", 
                    "type": "str"
                }, 
                {
                    "name": "target", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "block_set_io_throttle", 
            "type": "Command", 
            "data": [
                {
                    "name": "iops_rd", 
                    "type": "int"
                }, 
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "iops_wr", 
                    "type": "int"
                }, 
                {
                    "name": "iops", 
                    "type": "int"
                }, 
                {
                    "name": "bps_wr", 
                    "type": "int"
                }, 
                {
                    "name": "bps", 
                    "type": "int"
                }, 
                {
                    "name": "bps_rd", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "block-stream", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*speed", 
                    "type": "int"
                }, 
                {
                    "name": "*on-error", 
                    "type": "BlockdevOnError"
                }, 
                {
                    "name": "*base", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "block-job-set-speed", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "speed", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "block-job-cancel", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*force", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "block-job-pause", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "block-job-resume", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "block-job-complete", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "ObjectTypeInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "name", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "qom-list-types", 
            "type": "Command", 
            "data": [
                {
                    "name": "*implements", 
                    "type": "str"
                }, 
                {
                    "name": "*abstract", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "DevicePropertyInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "name", 
                    "type": "str"
                }, 
                {
                    "name": "type", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "device-list-properties", 
            "type": "Command", 
            "data": [
                {
                    "name": "typename", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "migrate", 
            "type": "Command", 
            "data": [
                {
                    "name": "uri", 
                    "type": "str"
                }, 
                {
                    "name": "*inc", 
                    "type": "bool"
                }, 
                {
                    "name": "*detach", 
                    "type": "bool"
                }, 
                {
                    "name": "*blk", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "xen-save-devices-state", 
            "type": "Command", 
            "data": [
                {
                    "name": "filename", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "xen-set-global-dirty-log", 
            "type": "Command", 
            "data": [
                {
                    "name": "enable", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "device_del", 
            "type": "Command", 
            "data": [
                {
                    "name": "id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "dump-guest-memory", 
            "type": "Command", 
            "data": [
                {
                    "name": "*length", 
                    "type": "int"
                }, 
                {
                    "name": "protocol", 
                    "type": "str"
                }, 
                {
                    "name": "*begin", 
                    "type": "int"
                }, 
                {
                    "name": "paging", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "netdev_add", 
            "type": "Command", 
            "data": [
                {
                    "name": "*props", 
                    "type": "**"
                }, 
                {
                    "name": "type", 
                    "type": "str"
                }, 
                {
                    "name": "id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "netdev_del", 
            "type": "Command", 
            "data": [
                {
                    "name": "id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "NetdevNoneOptions", 
            "type": "Type", 
            "data": [
            ]
        }, 
        {
            "name": "NetLegacyNicOptions", 
            "type": "Type", 
            "data": [
                {
                    "name": "*model", 
                    "type": "str"
                }, 
                {
                    "name": "*netdev", 
                    "type": "str"
                }, 
                {
                    "name": "*macaddr", 
                    "type": "str"
                }, 
                {
                    "name": "*vectors", 
                    "type": "uint32"
                }, 
                {
                    "name": "*addr", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "String", 
            "type": "Type", 
            "data": [
                {
                    "name": "str", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "NetdevUserOptions", 
            "type": "Type", 
            "data": [
                {
                    "name": "*tftp", 
                    "type": "str"
                }, 
                {
                    "name": "*guestfwd", 
                    "data": [
                        {
                            "type": "String"
                        }
                    ]
                }, 
                {
                    "name": "*hostfwd", 
                    "data": [
                        {
                            "type": "String"
                        }
                    ]
                }, 
                {
                    "name": "*smb", 
                    "type": "str"
                }, 
                {
                    "name": "*ip", 
                    "type": "str"
                }, 
                {
                    "name": "*hostname", 
                    "type": "str"
                }, 
                {
                    "name": "*smbserver", 
                    "type": "str"
                }, 
                {
                    "name": "*host", 
                    "type": "str"
                }, 
                {
                    "name": "*net", 
                    "type": "str"
                }, 
                {
                    "name": "*bootfile", 
                    "type": "str"
                }, 
                {
                    "name": "*dns", 
                    "type": "str"
                }, 
                {
                    "name": "*dhcpstart", 
                    "type": "str"
                }, 
                {
                    "name": "*restrict", 
                    "type": "bool"
                }, 
                {
                    "name": "*dnssearch", 
                    "data": [
                        {
                            "type": "String"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "NetdevTapOptions", 
            "type": "Type", 
            "data": [
                {
                    "name": "*ifname", 
                    "type": "str"
                }, 
                {
                    "name": "*fd", 
                    "type": "str"
                }, 
                {
                    "name": "*fds", 
                    "type": "str"
                }, 
                {
                    "name": "*vhostfd", 
                    "type": "str"
                }, 
                {
                    "name": "*vnet_hdr", 
                    "type": "bool"
                }, 
                {
                    "name": "*vhostfds", 
                    "type": "str"
                }, 
                {
                    "name": "*queues", 
                    "type": "uint32"
                }, 
                {
                    "name": "*downscript", 
                    "type": "str"
                }, 
                {
                    "name": "*sndbuf", 
                    "type": "size"
                }, 
                {
                    "name": "*script", 
                    "type": "str"
                }, 
                {
                    "name": "*helper", 
                    "type": "str"
                }, 
                {
                    "name": "*vhost", 
                    "type": "bool"
                }, 
                {
                    "name": "*vhostforce", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "NetdevSocketOptions", 
            "type": "Type", 
            "data": [
                {
                    "name": "*listen", 
                    "type": "str"
                }, 
                {
                    "name": "*fd", 
                    "type": "str"
                }, 
                {
                    "name": "*localaddr", 
                    "type": "str"
                }, 
                {
                    "name": "*udp", 
                    "type": "str"
                }, 
                {
                    "name": "*mcast", 
                    "type": "str"
                }, 
                {
                    "name": "*connect", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "NetdevVdeOptions", 
            "type": "Type", 
            "data": [
                {
                    "name": "*mode", 
                    "type": "uint16"
                }, 
                {
                    "name": "*port", 
                    "type": "uint16"
                }, 
                {
                    "name": "*sock", 
                    "type": "str"
                }, 
                {
                    "name": "*group", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "NetdevDumpOptions", 
            "type": "Type", 
            "data": [
                {
                    "name": "*file", 
                    "type": "str"
                }, 
                {
                    "name": "*len", 
                    "type": "size"
                }
            ]
        }, 
        {
            "name": "NetdevBridgeOptions", 
            "type": "Type", 
            "data": [
                {
                    "name": "*br", 
                    "type": "str"
                }, 
                {
                    "name": "*helper", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "NetdevHubPortOptions", 
            "type": "Type", 
            "data": [
                {
                    "name": "hubid", 
                    "type": "int32"
                }
            ]
        }, 
        {
            "name": "NetClientOptions", 
            "type": "Union", 
            "data": [
                {
                    "name": "tap", 
                    "type": "NetdevTapOptions"
                }, 
                {
                    "name": "socket", 
                    "type": "NetdevSocketOptions"
                }, 
                {
                    "name": "nic", 
                    "type": "NetLegacyNicOptions"
                }, 
                {
                    "name": "bridge", 
                    "type": "NetdevBridgeOptions"
                }, 
                {
                    "name": "none", 
                    "type": "NetdevNoneOptions"
                }, 
                {
                    "name": "dump", 
                    "type": "NetdevDumpOptions"
                }, 
                {
                    "name": "vde", 
                    "type": "NetdevVdeOptions"
                }, 
                {
                    "name": "user", 
                    "type": "NetdevUserOptions"
                }, 
                {
                    "name": "hubport", 
                    "type": "NetdevHubPortOptions"
                }
            ]
        }, 
        {
            "name": "NetLegacy", 
            "type": "Type", 
            "data": [
                {
                    "name": "*id", 
                    "type": "str"
                }, 
                {
                    "name": "*vlan", 
                    "type": "int32"
                }, 
                {
                    "name": "opts", 
                    "type": "NetClientOptions"
                }, 
                {
                    "name": "*name", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "Netdev", 
            "type": "Type", 
            "data": [
                {
                    "name": "opts", 
                    "type": "NetClientOptions"
                }, 
                {
                    "name": "id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "InetSocketAddress", 
            "type": "Type", 
            "data": [
                {
                    "name": "port", 
                    "type": "str"
                }, 
                {
                    "name": "*ipv6", 
                    "type": "bool"
                }, 
                {
                    "name": "*ipv4", 
                    "type": "bool"
                }, 
                {
                    "name": "host", 
                    "type": "str"
                }, 
                {
                    "name": "*to", 
                    "type": "uint16"
                }
            ]
        }, 
        {
            "name": "UnixSocketAddress", 
            "type": "Type", 
            "data": [
                {
                    "name": "path", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "SocketAddress", 
            "type": "Union", 
            "data": [
                {
                    "name": "fd", 
                    "type": "String"
                }, 
                {
                    "name": "unix", 
                    "type": "UnixSocketAddress"
                }, 
                {
                    "name": "inet", 
                    "type": "InetSocketAddress"
                }
            ]
        }, 
        {
            "name": "getfd", 
            "type": "Command", 
            "data": [
                {
                    "name": "fdname", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "closefd", 
            "type": "Command", 
            "data": [
                {
                    "name": "fdname", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "MachineInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "*alias", 
                    "type": "str"
                }, 
                {
                    "name": "name", 
                    "type": "str"
                }, 
                {
                    "name": "*is-default", 
                    "type": "bool"
                }, 
                {
                    "name": "cpu-max", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "query-machines", 
            "type": "Command"
        }, 
        {
            "name": "CpuDefinitionInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "name", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-cpu-definitions", 
            "type": "Command"
        }, 
        {
            "name": "AddfdInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "fd", 
                    "type": "int"
                }, 
                {
                    "name": "fdset-id", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "add-fd", 
            "type": "Command", 
            "data": [
                {
                    "name": "*fdset-id", 
                    "type": "int"
                }, 
                {
                    "name": "*opaque", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "remove-fd", 
            "type": "Command", 
            "data": [
                {
                    "name": "*fd", 
                    "type": "int"
                }, 
                {
                    "name": "fdset-id", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "FdsetFdInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "fd", 
                    "type": "int"
                }, 
                {
                    "name": "*opaque", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "FdsetInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "fds", 
                    "data": [
                        {
                            "type": "FdsetFdInfo"
                        }
                    ]
                }, 
                {
                    "name": "fdset-id", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "query-fdsets", 
            "type": "Command"
        }, 
        {
            "name": "TargetInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "arch", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-target", 
            "type": "Command"
        }, 
        {
            "name": "QKeyCode", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "shift"
                }, 
                {
                    "type": "shift_r"
                }, 
                {
                    "type": "alt"
                }, 
                {
                    "type": "alt_r"
                }, 
                {
                    "type": "altgr"
                }, 
                {
                    "type": "altgr_r"
                }, 
                {
                    "type": "ctrl"
                }, 
                {
                    "type": "ctrl_r"
                }, 
                {
                    "type": "menu"
                }, 
                {
                    "type": "esc"
                }, 
                {
                    "type": "1"
                }, 
                {
                    "type": "2"
                }, 
                {
                    "type": "3"
                }, 
                {
                    "type": "4"
                }, 
                {
                    "type": "5"
                }, 
                {
                    "type": "6"
                }, 
                {
                    "type": "7"
                }, 
                {
                    "type": "8"
                }, 
                {
                    "type": "9"
                }, 
                {
                    "type": "0"
                }, 
                {
                    "type": "minus"
                }, 
                {
                    "type": "equal"
                }, 
                {
                    "type": "backspace"
                }, 
                {
                    "type": "tab"
                }, 
                {
                    "type": "q"
                }, 
                {
                    "type": "w"
                }, 
                {
                    "type": "e"
                }, 
                {
                    "type": "r"
                }, 
                {
                    "type": "t"
                }, 
                {
                    "type": "y"
                }, 
                {
                    "type": "u"
                }, 
                {
                    "type": "i"
                }, 
                {
                    "type": "o"
                }, 
                {
                    "type": "p"
                }, 
                {
                    "type": "bracket_left"
                }, 
                {
                    "type": "bracket_right"
                }, 
                {
                    "type": "ret"
                }, 
                {
                    "type": "a"
                }, 
                {
                    "type": "s"
                }, 
                {
                    "type": "d"
                }, 
                {
                    "type": "f"
                }, 
                {
                    "type": "g"
                }, 
                {
                    "type": "h"
                }, 
                {
                    "type": "j"
                }, 
                {
                    "type": "k"
                }, 
                {
                    "type": "l"
                }, 
                {
                    "type": "semicolon"
                }, 
                {
                    "type": "apostrophe"
                }, 
                {
                    "type": "grave_accent"
                }, 
                {
                    "type": "backslash"
                }, 
                {
                    "type": "z"
                }, 
                {
                    "type": "x"
                }, 
                {
                    "type": "c"
                }, 
                {
                    "type": "v"
                }, 
                {
                    "type": "b"
                }, 
                {
                    "type": "n"
                }, 
                {
                    "type": "m"
                }, 
                {
                    "type": "comma"
                }, 
                {
                    "type": "dot"
                }, 
                {
                    "type": "slash"
                }, 
                {
                    "type": "asterisk"
                }, 
                {
                    "type": "spc"
                }, 
                {
                    "type": "caps_lock"
                }, 
                {
                    "type": "f1"
                }, 
                {
                    "type": "f2"
                }, 
                {
                    "type": "f3"
                }, 
                {
                    "type": "f4"
                }, 
                {
                    "type": "f5"
                }, 
                {
                    "type": "f6"
                }, 
                {
                    "type": "f7"
                }, 
                {
                    "type": "f8"
                }, 
                {
                    "type": "f9"
                }, 
                {
                    "type": "f10"
                }, 
                {
                    "type": "num_lock"
                }, 
                {
                    "type": "scroll_lock"
                }, 
                {
                    "type": "kp_divide"
                }, 
                {
                    "type": "kp_multiply"
                }, 
                {
                    "type": "kp_subtract"
                }, 
                {
                    "type": "kp_add"
                }, 
                {
                    "type": "kp_enter"
                }, 
                {
                    "type": "kp_decimal"
                }, 
                {
                    "type": "sysrq"
                }, 
                {
                    "type": "kp_0"
                }, 
                {
                    "type": "kp_1"
                }, 
                {
                    "type": "kp_2"
                }, 
                {
                    "type": "kp_3"
                }, 
                {
                    "type": "kp_4"
                }, 
                {
                    "type": "kp_5"
                }, 
                {
                    "type": "kp_6"
                }, 
                {
                    "type": "kp_7"
                }, 
                {
                    "type": "kp_8"
                }, 
                {
                    "type": "kp_9"
                }, 
                {
                    "type": "less"
                }, 
                {
                    "type": "f11"
                }, 
                {
                    "type": "f12"
                }, 
                {
                    "type": "print"
                }, 
                {
                    "type": "home"
                }, 
                {
                    "type": "pgup"
                }, 
                {
                    "type": "pgdn"
                }, 
                {
                    "type": "end"
                }, 
                {
                    "type": "left"
                }, 
                {
                    "type": "up"
                }, 
                {
                    "type": "down"
                }, 
                {
                    "type": "right"
                }, 
                {
                    "type": "insert"
                }, 
                {
                    "type": "delete"
                }, 
                {
                    "type": "stop"
                }, 
                {
                    "type": "again"
                }, 
                {
                    "type": "props"
                }, 
                {
                    "type": "undo"
                }, 
                {
                    "type": "front"
                }, 
                {
                    "type": "copy"
                }, 
                {
                    "type": "open"
                }, 
                {
                    "type": "paste"
                }, 
                {
                    "type": "find"
                }, 
                {
                    "type": "cut"
                }, 
                {
                    "type": "lf"
                }, 
                {
                    "type": "help"
                }, 
                {
                    "type": "meta_l"
                }, 
                {
                    "type": "meta_r"
                }, 
                {
                    "type": "compose"
                }
            ]
        }, 
        {
            "name": "KeyValue", 
            "type": "Union", 
            "data": [
                {
                    "name": "qcode", 
                    "type": "QKeyCode"
                }, 
                {
                    "name": "number", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "send-key", 
            "type": "Command", 
            "data": [
                {
                    "name": "keys", 
                    "data": [
                        {
                            "type": "KeyValue"
                        }
                    ]
                }, 
                {
                    "name": "*hold-time", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "screendump", 
            "type": "Command", 
            "data": [
                {
                    "name": "filename", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "nbd-server-start", 
            "type": "Command", 
            "data": [
                {
                    "name": "addr", 
                    "type": "SocketAddress"
                }
            ]
        }, 
        {
            "name": "nbd-server-add", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*writable", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "nbd-server-stop", 
            "type": "Command"
        }, 
        {
            "name": "ChardevFile", 
            "type": "Type", 
            "data": [
                {
                    "name": "*in", 
                    "type": "str"
                }, 
                {
                    "name": "out", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "ChardevHostdev", 
            "type": "Type", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "ChardevSocket", 
            "type": "Type", 
            "data": [
                {
                    "name": "*wait", 
                    "type": "bool"
                }, 
                {
                    "name": "*server", 
                    "type": "bool"
                }, 
                {
                    "name": "addr", 
                    "type": "SocketAddress"
                }, 
                {
                    "name": "*nodelay", 
                    "type": "bool"
                }, 
                {
                    "name": "*telnet", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "ChardevUdp", 
            "type": "Type", 
            "data": [
                {
                    "name": "remote", 
                    "type": "SocketAddress"
                }, 
                {
                    "name": "*local", 
                    "type": "SocketAddress"
                }
            ]
        }, 
        {
            "name": "ChardevMux", 
            "type": "Type", 
            "data": [
                {
                    "name": "chardev", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "ChardevStdio", 
            "type": "Type", 
            "data": [
                {
                    "name": "*signal", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "ChardevSpiceChannel", 
            "type": "Type", 
            "data": [
                {
                    "name": "type", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "ChardevSpicePort", 
            "type": "Type", 
            "data": [
                {
                    "name": "fqdn", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "ChardevVC", 
            "type": "Type", 
            "data": [
                {
                    "name": "*cols", 
                    "type": "int"
                }, 
                {
                    "name": "*width", 
                    "type": "int"
                }, 
                {
                    "name": "*rows", 
                    "type": "int"
                }, 
                {
                    "name": "*height", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "ChardevMemory", 
            "type": "Type", 
            "data": [
                {
                    "name": "*size", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "ChardevDummy", 
            "type": "Type", 
            "data": [
            ]
        }, 
        {
            "name": "ChardevBackend", 
            "type": "Union", 
            "data": [
                {
                    "name": "serial", 
                    "type": "ChardevHostdev"
                }, 
                {
                    "name": "spiceport", 
                    "type": "ChardevSpicePort"
                }, 
                {
                    "name": "stdio", 
                    "type": "ChardevStdio"
                }, 
                {
                    "name": "memory", 
                    "type": "ChardevMemory"
                }, 
                {
                    "name": "socket", 
                    "type": "ChardevSocket"
                }, 
                {
                    "name": "msmouse", 
                    "type": "ChardevDummy"
                }, 
                {
                    "name": "pty", 
                    "type": "ChardevDummy"
                }, 
                {
                    "name": "braille", 
                    "type": "ChardevDummy"
                }, 
                {
                    "name": "udp", 
                    "type": "ChardevUdp"
                }, 
                {
                    "name": "mux", 
                    "type": "ChardevMux"
                }, 
                {
                    "name": "console", 
                    "type": "ChardevDummy"
                }, 
                {
                    "name": "parallel", 
                    "type": "ChardevHostdev"
                }, 
                {
                    "name": "null", 
                    "type": "ChardevDummy"
                }, 
                {
                    "name": "file", 
                    "type": "ChardevFile"
                }, 
                {
                    "name": "spicevmc", 
                    "type": "ChardevSpiceChannel"
                }, 
                {
                    "name": "vc", 
                    "type": "ChardevVC"
                }, 
                {
                    "name": "pipe", 
                    "type": "ChardevHostdev"
                }
            ]
        }, 
        {
            "name": "ChardevReturn", 
            "type": "Type", 
            "data": [
                {
                    "name": "*pty", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "chardev-add", 
            "type": "Command", 
            "data": [
                {
                    "name": "backend", 
                    "type": "ChardevBackend"
                }, 
                {
                    "name": "id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "chardev-remove", 
            "type": "Command", 
            "data": [
                {
                    "name": "id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "TpmModel", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "tpm-tis"
                }
            ]
        }, 
        {
            "name": "query-tpm-models", 
            "type": "Command"
        }, 
        {
            "name": "TpmType", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "passthrough"
                }
            ]
        }, 
        {
            "name": "query-tpm-types", 
            "type": "Command"
        }, 
        {
            "name": "TPMPassthroughOptions", 
            "type": "Type", 
            "data": [
                {
                    "name": "*path", 
                    "type": "str"
                }, 
                {
                    "name": "*cancel-path", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "TpmTypeOptions", 
            "type": "Union", 
            "data": [
                {
                    "name": "passthrough", 
                    "type": "TPMPassthroughOptions"
                }
            ]
        }, 
        {
            "name": "TPMInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "model", 
                    "type": "TpmModel"
                }, 
                {
                    "name": "options", 
                    "type": "TpmTypeOptions"
                }, 
                {
                    "name": "id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-tpm", 
            "type": "Command"
        }, 
        {
            "name": "AcpiTableOptions", 
            "type": "Type", 
            "data": [
                {
                    "name": "*data", 
                    "type": "str"
                }, 
                {
                    "name": "*oem_id", 
                    "type": "str"
                }, 
                {
                    "name": "*oem_table_id", 
                    "type": "str"
                }, 
                {
                    "name": "*sig", 
                    "type": "str"
                }, 
                {
                    "name": "*oem_rev", 
                    "type": "uint32"
                }, 
                {
                    "name": "*file", 
                    "type": "str"
                }, 
                {
                    "name": "*rev", 
                    "type": "uint8"
                }, 
                {
                    "name": "*asl_compiler_rev", 
                    "type": "uint32"
                }, 
                {
                    "name": "*asl_compiler_id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "CommandLineParameterType", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "string"
                }, 
                {
                    "type": "boolean"
                }, 
                {
                    "type": "number"
                }, 
                {
                    "type": "size"
                }
            ]
        }, 
        {
            "name": "CommandLineParameterInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "name", 
                    "type": "str"
                }, 
                {
                    "name": "*help", 
                    "type": "str"
                }, 
                {
                    "name": "type", 
                    "type": "CommandLineParameterType"
                }
            ]
        }, 
        {
            "name": "CommandLineOptionInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "parameters", 
                    "data": [
                        {
                            "type": "CommandLineParameterInfo"
                        }
                    ]
                }, 
                {
                    "name": "option", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-command-line-options", 
            "type": "Command", 
            "data": [
                {
                    "name": "*option", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "X86CPURegister32", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "EAX"
                }, 
                {
                    "type": "EBX"
                }, 
                {
                    "type": "ECX"
                }, 
                {
                    "type": "EDX"
                }, 
                {
                    "type": "ESP"
                }, 
                {
                    "type": "EBP"
                }, 
                {
                    "type": "ESI"
                }, 
                {
                    "type": "EDI"
                }
            ]
        }, 
        {
            "name": "X86CPUFeatureWordInfo", 
            "type": "Type", 
            "data": [
                {
                    "name": "*cpuid-input-ecx", 
                    "type": "int"
                }, 
                {
                    "name": "cpuid-register", 
                    "type": "X86CPURegister32"
                }, 
                {
                    "name": "cpuid-input-eax", 
                    "type": "int"
                }, 
                {
                    "name": "features", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "DataObject", 
            "type": "Type", 
            "data": [
                {
                    "name": "*data", 
                    "data": [
                        {
                            "type": "DataObject"
                        }
                    ]
                }, 
                {
                    "name": "*type", 
                    "type": "str"
                }, 
                {
                    "name": "*name", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "SchemaMetatype", 
            "type": "Enumeration", 
            "data": [
                {
                    "type": "Command"
                }, 
                {
                    "type": "Type"
                }, 
                {
                    "type": "Enumeration"
                }, 
                {
                    "type": "Union"
                }, 
                {
                    "type": "Event"
                }
            ]
        }, 
        {
            "name": "SchemaData", 
            "type": "Type", 
            "data": [
                {
                    "name": "*data", 
                    "data": [
                        {
                            "type": "DataObject"
                        }
                    ]
                }, 
                {
                    "name": "name", 
                    "type": "str"
                }, 
                {
                    "name": "*returns", 
                    "data": [
                        {
                            "type": "DataObject"
                        }
                    ]
                }, 
                {
                    "name": "type", 
                    "type": "SchemaMetatype"
                }
            ]
        }, 
        {
            "name": "query-qmp-schema", 
            "type": "Command"
        }
    ]
}
Amos Kong June 20, 2013, 10:16 a.m. UTC | #2
On Wed, Jun 19, 2013 at 08:49:14PM +0800, Amos Kong wrote:
> On Wed, Jun 19, 2013 at 08:24:37PM +0800, Amos Kong wrote:
> > 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.
>  
> I didn't implement to return complete schema in one go in this
> version, will do it in next version. We have a recursive define
> 'DataObject', we only display one layer for it.


I have implemented the return whole schema in one go.
Attached the whole output [query-qmp-schema--whole.txt]
I will send the patch out after cleanup.

DataObject description might cause confuse, I also plan
to add a detail document [docs/qmp-full-introspection.txt] (uncompleted)

Welcome your comments :)

============== full introspection support for QMP ===================

== Purpose ==

Add a new interface to provide QMP schema information to management,

== Usage ==

Execute QMP command:

  { "execute": "query-qmp-schema" }

Returns:

  { "return": [
      {
          "name": "query-name",
          "type": "Command",
          "returns": [
              {
                  "name": "*name",
                  "type": "str"
              }
          ]
      }  
   }

The whole schema information will be returned in one go, it contains
commands and event. It doesn't support to filter information by type
or name.

We have four types (ImageInfo, BlockStats, PciDeviceInfo,
SchemaData) that uses themself in their own define data
directly or indirectly, we will not repeatedly extend them.

== more description of 'DataObject' type ==

We use 'DataObject' to describe dynamical data struct, it might be
nested dictionary, list or string.

'DataObject' itself is a arbitrary and nested dictionary, the
dictionary has three keys ('name', 'type', 'data'), 'name' and
'data' are optional.

* For describing Dictionary, we set the key to 'name', and set the
  value to 'type'
* For describing List, we don't set 'name', just set the value to
  'type'
* 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'.


1) Dict, value is native type
{ 'id': 'str', ... }
--------------------
[
    {
        "name": "id", 
        "type": "str"
    },
    .....
]

2) Dict, value is defined types
{ 'options': 'TpmTypeOptions' }
-------------------------------
[
    {
        "name": "options", 
        "type": "TpmTypeOptions", 
        "data": [
            {
                "name": "passthrough", 
                "type": "str", 
            }
        ]
    },
    .....
]

3) List, value is native type
['str', ... ]
-------------
[
    {
        "type": "str"
    },
    ....
]

4) List, value is defined types
['TpmTypeOptions', ... ]
------------------------
[
    {
        "type": "TpmTypeOptions", 
        "data": [
            {
                "name": "passthrough", 
                "type": "str", 
            }
        ]
    },
    .....
]
{
    "QMP": {
        "version": {
            "qemu": {
                "micro": 50, 
                "minor": 5, 
                "major": 1
            }, 
            "package": ""
        }, 
        "capabilities": [
        ]
    }
}
{
    "return": {
    }
}
{
    "return": [
        {
            "name": "add_client", 
            "type": "Command", 
            "data": [
                {
                    "name": "*skipauth", 
                    "type": "bool"
                }, 
                {
                    "name": "protocol", 
                    "type": "str"
                }, 
                {
                    "name": "fdname", 
                    "type": "str"
                }, 
                {
                    "name": "*tls", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "query-name", 
            "type": "Command", 
            "returns": [
                {
                    "name": "*name", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-version", 
            "type": "Command", 
            "returns": [
                {
                    "name": "qemu", 
                    "data": [
                        {
                            "name": "micro", 
                            "type": "int"
                        }, 
                        {
                            "name": "minor", 
                            "type": "int"
                        }, 
                        {
                            "name": "major", 
                            "type": "int"
                        }
                    ]
                }, 
                {
                    "name": "package", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-kvm", 
            "type": "Command", 
            "returns": [
                {
                    "name": "enabled", 
                    "type": "bool"
                }, 
                {
                    "name": "present", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "query-status", 
            "type": "Command", 
            "returns": [
                {
                    "name": "status", 
                    "type": "RunState", 
                    "data": [
                        {
                            "type": "debug"
                        }, 
                        {
                            "type": "inmigrate"
                        }, 
                        {
                            "type": "internal-error"
                        }, 
                        {
                            "type": "io-error"
                        }, 
                        {
                            "type": "paused"
                        }, 
                        {
                            "type": "postmigrate"
                        }, 
                        {
                            "type": "prelaunch"
                        }, 
                        {
                            "type": "finish-migrate"
                        }, 
                        {
                            "type": "restore-vm"
                        }, 
                        {
                            "type": "running"
                        }, 
                        {
                            "type": "save-vm"
                        }, 
                        {
                            "type": "shutdown"
                        }, 
                        {
                            "type": "suspended"
                        }, 
                        {
                            "type": "watchdog"
                        }, 
                        {
                            "type": "guest-panicked"
                        }
                    ]
                }, 
                {
                    "name": "singlestep", 
                    "type": "bool"
                }, 
                {
                    "name": "running", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "query-uuid", 
            "type": "Command", 
            "returns": [
                {
                    "name": "UUID", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-chardev", 
            "type": "Command", 
            "returns": [
                {
                    "type": "ChardevInfo", 
                    "data": [
                        {
                            "name": "filename", 
                            "type": "str"
                        }, 
                        {
                            "name": "label", 
                            "type": "str"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "ringbuf-write", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*format", 
                    "type": "DataFormat", 
                    "data": [
                        {
                            "type": "utf8"
                        }, 
                        {
                            "type": "base64"
                        }
                    ]
                }, 
                {
                    "name": "data", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "ringbuf-read", 
            "type": "Command", 
            "returns": [
                {
                    "type": "str"
                }
            ], 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*format", 
                    "type": "DataFormat", 
                    "data": [
                        {
                            "type": "utf8"
                        }, 
                        {
                            "type": "base64"
                        }
                    ]
                }, 
                {
                    "name": "size", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "query-commands", 
            "type": "Command", 
            "returns": [
                {
                    "type": "CommandInfo", 
                    "data": [
                        {
                            "name": "name", 
                            "type": "str"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-events", 
            "type": "Command", 
            "returns": [
                {
                    "type": "EventInfo", 
                    "data": [
                        {
                            "name": "name", 
                            "type": "str"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-migrate", 
            "type": "Command", 
            "returns": [
                {
                    "name": "*disk", 
                    "type": "MigrationStats", 
                    "data": [
                        {
                            "name": "total", 
                            "type": "int"
                        }, 
                        {
                            "name": "remaining", 
                            "type": "int"
                        }, 
                        {
                            "name": "transferred", 
                            "type": "int"
                        }, 
                        {
                            "name": "duplicate", 
                            "type": "int"
                        }, 
                        {
                            "name": "dirty-pages-rate", 
                            "type": "int"
                        }, 
                        {
                            "name": "skipped", 
                            "type": "int"
                        }, 
                        {
                            "name": "normal-bytes", 
                            "type": "int"
                        }, 
                        {
                            "name": "normal", 
                            "type": "int"
                        }
                    ]
                }, 
                {
                    "name": "*xbzrle-cache", 
                    "type": "XBZRLECacheStats", 
                    "data": [
                        {
                            "name": "bytes", 
                            "type": "int"
                        }, 
                        {
                            "name": "cache-size", 
                            "type": "int"
                        }, 
                        {
                            "name": "pages", 
                            "type": "int"
                        }, 
                        {
                            "name": "overflow", 
                            "type": "int"
                        }, 
                        {
                            "name": "cache-miss", 
                            "type": "int"
                        }
                    ]
                }, 
                {
                    "name": "*downtime", 
                    "type": "int"
                }, 
                {
                    "name": "*expected-downtime", 
                    "type": "int"
                }, 
                {
                    "name": "*status", 
                    "type": "str"
                }, 
                {
                    "name": "*ram", 
                    "type": "MigrationStats", 
                    "data": [
                        {
                            "name": "total", 
                            "type": "int"
                        }, 
                        {
                            "name": "remaining", 
                            "type": "int"
                        }, 
                        {
                            "name": "transferred", 
                            "type": "int"
                        }, 
                        {
                            "name": "duplicate", 
                            "type": "int"
                        }, 
                        {
                            "name": "dirty-pages-rate", 
                            "type": "int"
                        }, 
                        {
                            "name": "skipped", 
                            "type": "int"
                        }, 
                        {
                            "name": "normal-bytes", 
                            "type": "int"
                        }, 
                        {
                            "name": "normal", 
                            "type": "int"
                        }
                    ]
                }, 
                {
                    "name": "*total-time", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "migrate-set-capabilities", 
            "type": "Command", 
            "data": [
                {
                    "name": "capabilities", 
                    "data": [
                        {
                            "type": "MigrationCapabilityStatus", 
                            "data": [
                                {
                                    "name": "state", 
                                    "type": "bool"
                                }, 
                                {
                                    "name": "capability", 
                                    "type": "MigrationCapability", 
                                    "data": [
                                        {
                                            "type": "xbzrle"
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-migrate-capabilities", 
            "type": "Command", 
            "returns": [
                {
                    "type": "MigrationCapabilityStatus", 
                    "data": [
                        {
                            "name": "state", 
                            "type": "bool"
                        }, 
                        {
                            "name": "capability", 
                            "type": "MigrationCapability", 
                            "data": [
                                {
                                    "type": "xbzrle"
                                }
                            ]
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-mice", 
            "type": "Command", 
            "returns": [
                {
                    "type": "MouseInfo", 
                    "data": [
                        {
                            "name": "index", 
                            "type": "int"
                        }, 
                        {
                            "name": "name", 
                            "type": "str"
                        }, 
                        {
                            "name": "current", 
                            "type": "bool"
                        }, 
                        {
                            "name": "absolute", 
                            "type": "bool"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-cpus", 
            "type": "Command", 
            "returns": [
                {
                    "type": "CpuInfo", 
                    "data": [
                        {
                            "name": "*PC", 
                            "type": "int"
                        }, 
                        {
                            "name": "*pc", 
                            "type": "int"
                        }, 
                        {
                            "name": "current", 
                            "type": "bool"
                        }, 
                        {
                            "name": "CPU", 
                            "type": "int"
                        }, 
                        {
                            "name": "halted", 
                            "type": "bool"
                        }, 
                        {
                            "name": "*npc", 
                            "type": "int"
                        }, 
                        {
                            "name": "*nip", 
                            "type": "int"
                        }, 
                        {
                            "name": "thread_id", 
                            "type": "int"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-block", 
            "type": "Command", 
            "returns": [
                {
                    "type": "BlockInfo", 
                    "data": [
                        {
                            "name": "device", 
                            "type": "str"
                        }, 
                        {
                            "name": "locked", 
                            "type": "bool"
                        }, 
                        {
                            "name": "removable", 
                            "type": "bool"
                        }, 
                        {
                            "name": "*dirty", 
                            "type": "BlockDirtyInfo", 
                            "data": [
                                {
                                    "name": "granularity", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "count", 
                                    "type": "int"
                                }
                            ]
                        }, 
                        {
                            "name": "*io-status", 
                            "type": "BlockDeviceIoStatus", 
                            "data": [
                                {
                                    "type": "ok"
                                }, 
                                {
                                    "type": "failed"
                                }, 
                                {
                                    "type": "nospace"
                                }
                            ]
                        }, 
                        {
                            "name": "*tray_open", 
                            "type": "bool"
                        }, 
                        {
                            "name": "type", 
                            "type": "str"
                        }, 
                        {
                            "name": "*inserted", 
                            "type": "BlockDeviceInfo", 
                            "data": [
                                {
                                    "name": "iops_rd", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "*backing_file", 
                                    "type": "str"
                                }, 
                                {
                                    "name": "image", 
                                    "type": "ImageInfo", 
                                    "data": [
                                        {
                                            "name": "*backing-filename", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "*actual-size", 
                                            "type": "int"
                                        }, 
                                        {
                                            "name": "*backing-image", 
                                            "type": "ImageInfo"
                                        }, 
                                        {
                                            "name": "*full-backing-filename", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "*cluster-size", 
                                            "type": "int"
                                        }, 
                                        {
                                            "name": "virtual-size", 
                                            "type": "int"
                                        }, 
                                        {
                                            "name": "filename", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "*encrypted", 
                                            "type": "bool"
                                        }, 
                                        {
                                            "name": "format", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "*snapshots", 
                                            "data": [
                                                {
                                                    "type": "SnapshotInfo", 
                                                    "data": [
                                                        {
                                                            "name": "vm-clock-nsec", 
                                                            "type": "int"
                                                        }, 
                                                        {
                                                            "name": "name", 
                                                            "type": "str"
                                                        }, 
                                                        {
                                                            "name": "date-sec", 
                                                            "type": "int"
                                                        }, 
                                                        {
                                                            "name": "date-nsec", 
                                                            "type": "int"
                                                        }, 
                                                        {
                                                            "name": "vm-clock-sec", 
                                                            "type": "int"
                                                        }, 
                                                        {
                                                            "name": "id", 
                                                            "type": "str"
                                                         }, 
                                                        {
                                                            "name": "vm-state-size", 
                                                            "type": "int"
                                                        }
                                                    ]
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "*backing-filename-format", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "*dirty-flag", 
                                            "type": "bool"
                                        }
                                    ]
                                }, 
                                {
                                    "name": "iops_wr", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "ro", 
                                    "type": "bool"
                                }, 
                                {
                                    "name": "backing_file_depth", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "drv", 
                                    "type": "str"
                                }, 
                                {
                                    "name": "iops", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "bps_wr", 
                                    "type" : "int"
                                }, 
                                {
                                    "name": "encrypted", 
                                    "type": "bool"
                                }, 
                                {
                                    "name": "bps", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "bps_rd", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "file", 
                                    "type": "str"
                                }, 
                                {
                                    "name": "encryption_key_missing", 
                                    "type": "bool"
                                }
                            ]
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-blockstats", 
            "type": "Command", 
            "returns": [
                {
                    "type": "BlockStats", 
                    "data": [
                        {
                            "name": "stats", 
                            "type": "BlockDeviceStats", 
                            "data": [
                                {
                                    "name": "flush_total_time_ns", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "wr_highest_offset", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "wr_total_time_ns", 
                                    "type": "int"
                                }, 
                                 {
                                    "name": "wr_bytes", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "rd_total_time_ns", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "flush_operations", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "wr_operations", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "rd_bytes", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "rd_operations", 
                                    "type": "int"
                                }
                            ]
                        }, 
                        {
                            "name": "*device", 
                            "type": "str"
                        }, 
                        {
                            "name": "*parent", 
                            "type": "BlockStats"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-vnc", 
            "type": "Command", 
            "returns": [
                {
                    "name": "*clients", 
                    "data": [
                        {
                            "type": "VncClientInfo", 
                            "data": [
                                {
                                    "name": "family", 
                                    "type": "str"
                                }, 
                                 {
                                    "name": "*sasl_username", 
                                    "type": "str"
                                }, 
                                {
                                    "name": "service", 
                                    "type": "str"
                                }, 
                                {
                                    "name": "host", 
                                    "type": "str"
                                }, 
                                {
                                    "name": "*x509_dname", 
                                    "type": "str"
                                }
                            ]
                        }
                    ]
                }, 
                {
                    "name": "*family", 
                    "type": "str"
                }, 
                {
                    "name": "enabled", 
                    "type": "bool"
                }, 
                {
                    "name": "*service", 
                    "type": "str"
                }, 
                {
                    "name": "*host", 
                    "type": "str"
                }, 
                {
                    "name": "*auth", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-spice", 
            "type": "Command", 
            "returns": [
                {
                    "name": "migrated", 
                    "type": "bool"
                }, 
                {
                    "name": "enabled", 
                    "type": "bool"
                }, 
                {
                    "name": "*port", 
                    "type": "int"
                }, 
                {
                    "name": "*channels", 
                    "data": [
                        {
                            "type": "SpiceChannel", 
                             "data": [
                                {
                                    "name": "port", 
                                    "type": "str"
                                }, 
                                {
                                    "name": "family", 
                                    "type": "str"
                                }, 
                                {
                                    "name": "channel-type", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "connection-id", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "host", 
                                    "type": "str"
                                }, 
                                {
                                    "name": "channel-id", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "tls", 
                                    "type": "bool"
                                }
                            ]
                        }
                    ]
                }, 
                {
                    "name": "*compiled-version", 
                    "type": "str"
                }, 
                {
                    "name": "mouse-mode", 
                    "type": "SpiceQueryMouseMode", 
                    "data": [
                        {
                            "type": "client"
                        }, 
                        {
                            "type": "server"
                        }, 
                        {
                            "type": "unknown"
                        }
                    ]
                }, 
                {
                     "name": "*tls-port", 
                    "type": "int"
                }, 
                {
                    "name": "*host", 
                    "type": "str"
                }, 
                {
                    "name": "*auth", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-balloon", 
            "type": "Command", 
            "returns": [
                {
                    "name": "actual", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "query-pci", 
            "type": "Command", 
            "returns": [
                {
                    "type": "PciInfo", 
                    "data": [
                        {
                            "name": "bus", 
                            "type": "int"
                        }, 
                        {
                            "name": "devices", 
                            "data": [
                                {
                                    "type": "PciDeviceInfo", 
                                    "data": [
                                        {
                                            "name": "bus", 
                                            "type": "int"
                                        }, 
                                        {
                                            "name": "*irq", 
                                            "type": "int"
                                        }, 
                                        {
                                            "name": "qdev_id", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "slot", 
                                            "type": "int"
                                        }, 
                                         {
                                            "name": "*pci_bridge", 
                                            "type": "PciBridgeInfo", 
                                            "data": [
                                                {
                                                    "name": "bus", 
                                                    "data": [
                                                        {
                                                            "name": "prefetchable_range", 
                                                            "type": "PciMemoryRange", 
                                                            "data": [
                                                                {
                                                                    "name": "limit", 
                                                                    "type": "int"
                                                                }, 
                                                                {
                                                                    "name": "base", 
                                                                    "type": "int"
                                                                }
                                                            ]
                                                        }, 
                                                        {
                                                            "name": "memory_range", 
                                                            "type": "PciMemoryRange", 
                                                            "data": [
                                                                {
                                                                    "name": "limit", 
                                                                    "type": "int"
                                                                },  
                                                                {
                                                                    "name": "base", 
                                                                    "type": "int"
                                                                }
                                                            ]
                                                        }, 
                                                        {
                                                            "name": "secondary", 
                                                            "type": "int"
                                                        }, 
                                                        {
                                                            "name": "io_range", 
                                                            "type": "PciMemoryRange", 
                                                            "data": [
                                                                {
                                                                    "name": "limit", 
                                                                    "type": "int"
                                                                }, 
                                                                {
                                                                    "name": "base", 
                                                                    "type": "int"
                                                                }
                                                            ]
                                                        }, 
                                                        {
                                                            "name": "number", 
                                                            "type": "int"
                                                        }, 
                                                         {
                                                            "name": "subordinate", 
                                                            "type": "int"
                                                        }
                                                    ]
                                                }, 
                                                {
                                                    "name": "*devices", 
                                                    "data": [
                                                        {
                                                            "type": "PciDeviceInfo"
                                                        }
                                                    ]
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "class_info", 
                                            "data": [
                                                {
                                                    "name": "*desc", 
                                                    "type": "str"
                                                }, 
                                                {
                                                    "name": "class", 
                                                    "type": "int"
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "id", 
                                            "data": [
                                                {
                                                    "name": "device", 
                                                    "type": "int"
                                                 }, 
                                                {
                                                    "name": "vendor", 
                                                    "type": "int"
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "function", 
                                            "type": "int"
                                        }, 
                                        {
                                            "name": "regions", 
                                            "data": [
                                                {
                                                    "type": "PciMemoryRegion", 
                                                    "data": [
                                                        {
                                                            "name": "*prefetch", 
                                                            "type": "bool"
                                                        }, 
                                                        {
                                                            "name": "bar", 
                                                            "type": "int"
                                                        }, 
                                                        {
                                                            "name": "size", 
                                                            "type": "int"
                                                        }, 
                                                        {
                                                            "name": "*mem_type_64", 
                                                            "type": "bool"
                                                        }, 
                                                         {
                                                            "name": "address", 
                                                            "type": "int"
                                                        }, 
                                                        {
                                                            "name": "type", 
                                                            "type": "str"
                                                        }
                                                    ]
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-block-jobs", 
            "type": "Command", 
            "returns": [
                {
                    "type": "BlockJobInfo", 
                    "data": [
                        {
                            "name": "io-status", 
                            "type": "BlockDeviceIoStatus", 
                            "data": [
                                {
                                    "type": "ok"
                                }, 
                                {
                                    "type": "failed"
                                }, 
                                {
                                    "type": "nospace"
                                }
                            ]
                        }, 
                        {
                            "name": "device", 
                            "type": "str"
                        }, 
                        {
                            "name": "busy", 
                            "type": "bool"
                         }, 
                        {
                            "name": "len", 
                            "type": "int"
                        }, 
                        {
                            "name": "offset", 
                            "type": "int"
                        }, 
                        {
                            "name": "paused", 
                            "type": "bool"
                        }, 
                        {
                            "name": "speed", 
                            "type": "int"
                        }, 
                        {
                            "name": "type", 
                            "type": "str"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "quit", 
            "type": "Command"
        }, 
        {
            "name": "stop", 
            "type": "Command"
        }, 
        {
            "name": "system_reset", 
            "type": "Command"
        }, 
        {
            "name": "system_powerdown", 
            "type": "Command"
        }, 
        {
            "name": "cpu", 
            "type": "Command", 
            "data": [
                {
                    "name": "index", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "cpu-add", 
            "type": "Command", 
            "data": [
                {
                    "name": "id", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "memsave", 
            "type": "Command", 
            "data": [
                {
                    "name": "filename", 
                    "type": "str"
                }, 
                {
                    "name": "size", 
                    "type": "int"
                }, 
                {
                    "name": "val", 
                    "type ": "int"
                }, 
                {
                    "name": "*cpu-index", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "pmemsave", 
            "type": "Command", 
            "data": [
                {
                    "name": "filename", 
                    "type": "str"
                }, 
                {
                    "name": "size", 
                    "type": "int"
                }, 
                {
                    "name": "val", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "cont", 
            "type": "Command"
        }, 
        {
            "name": "system_wakeup", 
            "type": "Command"
        }, 
        {
            "name": "inject-nmi", 
            "type": "Command"
        }, 
        {
            "name": "set_link", 
            "type": "Command", 
            "data": [
                {
                    "name": "name", 
                    "type": "str"
                }, 
                {
                    "name": "up", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "block_passwd", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "password", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "balloon", 
            "type": "Command", 
            "data": [
                {
                    "name": "value", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "block_resize", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                     "type": "str"
                }, 
                {
                    "name": "size", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "transaction", 
            "type": "Command", 
            "data": [
                {
                    "name": "actions", 
                    "data": [
                        {
                            "type": "TransactionAction", 
                            "data": [
                                {
                                    "name": "blockdev-snapshot-sync", 
                                    "type": "BlockdevSnapshot", 
                                    "data": [
                                        {
                                            "name": "device", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "*mode", 
                                            "type": "NewImageMode", 
                                            "data": [
                                                {
                                                    "type": "existing"
                                                }, 
                                                {
                                                    "type": "absolute-paths"
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "*format", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "snapshot-file", 
                                            "type": "str"
                                        }
                                     ]
                                }
                            ]
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "blockdev-snapshot-sync", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*mode", 
                    "type": "NewImageMode", 
                    "data": [
                        {
                            "type": "existing"
                        }, 
                        {
                            "type": "absolute-paths"
                        }
                    ]
                }, 
                {
                    "name": "*format", 
                    "type": "str"
                }, 
                {
                    "name": "snapshot-file", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "human-monitor-command", 
            "type": "Command", 
            "returns": [
                {
                    "type": "str"
                }
            ], 
            "data": [
                {
                    "name": "command-line", 
                    "type": "str"
                }, 
                {
                    "name": "*cpu-index", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "block-commit", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*speed", 
                    "type": "int"
                }, 
                {
                    "name": "*base", 
                    "type": "str"
                }, 
                {
                     "name": "top", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "drive-mirror", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*speed", 
                    "type": "int"
                }, 
                {
                    "name": "*mode", 
                    "type": "NewImageMode", 
                    "data": [
                        {
                            "type": "existing"
                        }, 
                        {
                            "type": "absolute-paths"
                        }
                    ]
                }, 
                {
                    "name": "*buf-size", 
                    "type": "int"
                }, 
                {
                    "name": "*format", 
                    "type": "str"
                }, 
                {
                    "name": "*on-source-error", 
                    "type": "BlockdevOnError", 
                    "data": [
                        {
                            "type": "report"
                        }, 
                        {
                            "type": "ignore"
                        }, 
                        {
                            "type": "enospc"
                        }, 
                        {
                            "type": "stop"
                        }
                    ]
                }, 
                {
                    "name": "*on-target-error", 
                    "type": "BlockdevOnError", 
                    "data": [
                        {
                            "type": "report"
                        }, 
                        {
                            "type": "ignore"
                        }, 
                        { 
                            "type": "enospc"
                        }, 
                        {
                            "type": "stop"
                        }
                    ]
                }, 
                {
                    "name": "sync", 
                    "type": "MirrorSyncMode", 
                    "data": [
                        {
                            "type": "top"
                        }, 
                        {
                            "type": "full"
                        }, 
                        {
                            "type": "none"
                        }
                    ]
                }, 
                {
                    "name": "*granularity", 
                    "type": "uint32"
                }, 
                {
                    "name": "target", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "migrate_cancel", 
            "type": "Command"
        }, 
        {
            "name": "migrate_set_downtime", 
            "type": "Command", 
            "data": [
                {
                    "name": "value", 
                    "type": "number"
                }
            ]
        }, 
        {
            "name": "migrate_set_speed", 
            "type": "Command", 
            "data": [
                {
                    "name": "value", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "migrate-set-cache-size", 
            "type": "Command", 
            "data": [
                {
                    "name": "value", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "query-migrate-cache-size", 
            "type": "Command", 
            "returns": [
                {
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "qom-list", 
            "type": "Command", 
            "returns": [
                {
                    "type": "ObjectPropertyInfo", 
                    "data": [
                        {
                            "name": "name", 
                            "type": "str"
                        }, 
                        {
                            "name": "type", 
                            "type": "str"
                        }
                    ]
                }
            ], 
            "data": [
                {
                    "name": "path", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "qom-get", 
            "type": "Command", 
            "returns": [
                {
                    "type": "visitor"
                }
            ], 
            "data": [
                {
                    "name": "path", 
                    "type": "str"
                }, 
                {
                    "name": "property", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "qom-set", 
            "type": "Command", 
            "data": [
                {
                    "name": "path", 
                    "type": "str"
                }, 
                {
                    "name": "property", 
                    "type": "str"
                }, 
                {
                    "name": "value", 
                    "type": "visitor"
                }
            ]
        }, 
        {
            "name": "set_password", 
            "type": "Command", 
            "data": [
                {
                    "name": "*connected", 
                    "type": "str"
                }, 
                {
                    "name": "protocol", 
                    "type": "str"
                }, 
                {
                    "name": "pass word", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "expire_password", 
            "type": "Command", 
            "data": [
                {
                    "name": "protocol", 
                    "type": "str"
                }, 
                {
                    "name": "time", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "eject", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*force", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "change-vnc-password", 
            "type": "Command", 
            "data": [
                {
                    "name": "password", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "change", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*arg", 
                    "type": "str"
                }, 
                {
                    "name": "target", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "block_set_io_throttle", 
            "type": "Command", 
            "data": [
                {
                    "name": "iops_rd", 
                    "type": "int"
                }, 
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "iops_wr", 
                    "type": "int"
                }, 
                {
                    "name": "iops",  
                    "type": "int"
                }, 
                {
                    "name": "bps_wr", 
                    "type": "int"
                }, 
                {
                    "name": "bps", 
                    "type": "int"
                }, 
                {
                    "name": "bps_rd", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "block-stream", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*speed", 
                    "type": "int"
                }, 
                {
                    "name": "*on-error", 
                    "type": "BlockdevOnError", 
                    "data": [
                        {
                            "type": "report"
                        }, 
                        {
                            "type": "ignore"
                        }, 
                        {
                            "type": "enospc"
                        }, 
                        {
                            "type": "stop"
                        }
                    ]
                }, 
                {
                    "name": "*base", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "block-job-set-speed", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "speed", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "block-job-cancel", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    " type": "str"
                }, 
                {
                    "name": "*force", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "block-job-pause", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "block-job-resume", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "block-job-complete", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "qom-list-types", 
            "type": "Command", 
            "returns": [
                {
                    "type": "ObjectTypeInfo", 
                    "data": [
                        {
                            "name": "name", 
                            "type": "str"
                        }
                    ]
                }
            ], 
            "data": [
                {
                    "name": "*implements", 
                    "type": "str"
                }, 
                {
                    "name": "*abstract", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "device-list-properties", 
            "type": "Command", 
            "returns": [
                {
                    "type": "DevicePropertyInfo", 
                    "data": [
                        {
                            "name": "name", 
                            "type": "str"
                        }, 
                        {
                            "n ame": "type", 
                            "type": "str"
                        }
                    ]
                }
            ], 
            "data": [
                {
                    "name": "typename", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "migrate", 
            "type": "Command", 
            "data": [
                {
                    "name": "uri", 
                    "type": "str"
                }, 
                {
                    "name": "*inc", 
                    "type": "bool"
                }, 
                {
                    "name": "*detach", 
                    "type": "bool"
                }, 
                {
                    "name": "*blk", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "xen-save-devices-state", 
            "type": "Command", 
            "data": [
                {
                    "name": "filename", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "xen-set-global-dirty-log", 
            "type": "Command", 
            "data": [
                {
                    "name": "enable", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "device_del", 
            "type": "Command", 
            "data": [
                {
                    "name": "id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "dump-guest-memory", 
            "type": "Command", 
            "data": [
                {
                    "name": "*length", 
                    "type": "int"
                }, 
                {
                    "name": "protocol", 
                    "type": "str"
                }, 
                {
                    "name": "*begin ", 
                    "type": "int"
                }, 
                {
                    "name": "paging", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "netdev_add", 
            "type": "Command", 
            "data": [
                {
                    "name": "*props", 
                    "type": "**"
                }, 
                {
                    "name": "type", 
                    "type": "str"
                }, 
                {
                    "name": "id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "netdev_del", 
            "type": "Command", 
            "data": [
                {
                    "name": "id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "getfd", 
            "type": "Command", 
            "data": [
                {
                    "name": "fdname", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "closefd", 
            "type": "Command", 
            "data": [
                {
                    "name": "fdname", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-machines", 
            "type": "Command", 
            "returns": [
                {
                    "type": "MachineInfo", 
                    "data": [
                        {
                            "name": "*alias", 
                            "type": "str"
                        }, 
                        {
                            "name": "name", 
                            "type": "str"
                        }, 
                        {
                            "name": "*is-default", 
                            "type": "bool"
                        }, 
                         {
                            "name": "cpu-max", 
                            "type": "int"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-cpu-definitions", 
            "type": "Command", 
            "returns": [
                {
                    "type": "CpuDefinitionInfo", 
                    "data": [
                        {
                            "name": "name", 
                            "type": "str"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "add-fd", 
            "type": "Command", 
            "returns": [
                {
                    "name": "fd", 
                    "type": "int"
                }, 
                {
                    "name": "fdset-id", 
                    "type": "int"
                }
            ], 
            "data": [
                {
                    "name": "*fdset-id", 
                    "type": "int"
                }, 
                {
                    "name": "*opaque", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "remove-fd", 
            "type": "Command", 
            "data": [
                {
                    "name": "*fd", 
                    "type": "int"
                }, 
                {
                    "name": "fdset-id", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "query-fdsets", 
            "type": "Command", 
            "returns": [
                {
                    "type": "FdsetInfo", 
                    "data": [
                        {
                            "name": "fds", 
                            "data": [
                                {
                                    "type": "FdsetFdInfo", 
                                     "data": [
                                        {
                                            "name": "fd", 
                                            "type": "int"
                                        }, 
                                        {
                                            "name": "*opaque", 
                                            "type": "str"
                                        }
                                    ]
                                }
                            ]
                        }, 
                        {
                            "name": "fdset-id", 
                            "type": "int"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-target", 
            "type": "Command", 
            "returns": [
                {
                    "name": "arch", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "send-key", 
            "type": "Command", 
            "data": [
                {
                    "name": "keys", 
                    "data": [
                        {
                            "type": "KeyValue", 
                            "data": [
                                {
                                    "name": "qcode", 
                                    "type": "QKeyCode", 
                                    "data": [
                                        {
                                            "type": "shift"
                                        }, 
                                        {
                                            "type": "shift_r"
                                        }, 
                                        {
                                            "type": "alt"
                                        }, 
                                        {
                                             "type": "alt_r"
                                        }, 
                                        {
                                            "type": "altgr"
                                        }, 
                                        {
                                            "type": "altgr_r"
                                        }, 
                                        {
                                            "type": "ctrl"
                                        }, 
                                        {
                                            "type": "ctrl_r"
                                        }, 
                                        {
                                            "type": "menu"
                                        }, 
                                        {
                                            "type": "esc"
                                        }, 
                                        {
                                            "type": "1"
                                        }, 
                                        {
                                            "type": "2"
                                        }, 
                                        {
                                            "type": "3"
                                        }, 
                                        {
                                            "type": "4"
                                        }, 
                                        {
                                            "type": "5"
                                        }, 
                                        {
                                            "type": "6"
                                        }, 
                                        {
                                            "type": "7"
                                        }, 
                                        {
                                             "type": "8"
                                        }, 
                                        {
                                            "type": "9"
                                        }, 
                                        {
                                            "type": "0"
                                        }, 
                                        {
                                            "type": "minus"
                                        }, 
                                        {
                                            "type": "equal"
                                        }, 
                                        {
                                            "type": "backspace"
                                        }, 
                                        {
                                            "type": "tab"
                                        }, 
                                        {
                                            "type": "q"
                                        }, 
                                        {
                                            "type": "w"
                                        }, 
                                        {
                                            "type": "e"
                                        }, 
                                        {
                                            "type": "r"
                                        }, 
                                        {
                                            "type": "t"
                                        }, 
                                        {
                                            "type": "y"
                                        }, 
                                        {
                                            "type": "u"
                                        }, 
                                        {
                                             "type": "i"
                                        }, 
                                        {
                                            "type": "o"
                                        }, 
                                        {
                                            "type": "p"
                                        }, 
                                        {
                                            "type": "bracket_left"
                                        }, 
                                        {
                                            "type": "bracket_right"
                                        }, 
                                        {
                                            "type": "ret"
                                        }, 
                                        {
                                            "type": "a"
                                        }, 
                                        {
                                            "type": "s"
                                        }, 
                                        {
                                            "type": "d"
                                        }, 
                                        {
                                            "type": "f"
                                        }, 
                                        {
                                            "type": "g"
                                        }, 
                                        {
                                            "type": "h"
                                        }, 
                                        {
                                            "type": "j"
                                        }, 
                                        {
                                            "type": "k"
                                        }, 
                                         {
                                            "type": "l"
                                        }, 
                                        {
                                            "type": "semicolon"
                                        }, 
                                        {
                                            "type": "apostrophe"
                                        }, 
                                        {
                                            "type": "grave_accent"
                                        }, 
                                        {
                                            "type": "backslash"
                                        }, 
                                        {
                                            "type": "z"
                                        }, 
                                        {
                                            "type": "x"
                                        }, 
                                        {
                                            "type": "c"
                                        }, 
                                        {
                                            "type": "v"
                                        }, 
                                        {
                                            "type": "b"
                                        }, 
                                        {
                                            "type": "n"
                                        }, 
                                        {
                                            "type": "m"
                                        }, 
                                        {
                                            "type": "comma"
                                        }, 
                                        {
                                            "type": "dot"
                                        }, 
                                         {
                                            "type": "slash"
                                        }, 
                                        {
                                            "type": "asterisk"
                                        }, 
                                        {
                                            "type": "spc"
                                        }, 
                                        {
                                            "type": "caps_lock"
                                        }, 
                                        {
                                            "type": "f1"
                                        }, 
                                        {
                                            "type": "f2"
                                        }, 
                                        {
                                            "type": "f3"
                                        }, 
                                        {
                                            "type": "f4"
                                        }, 
                                        {
                                            "type": "f5"
                                        }, 
                                        {
                                            "type": "f6"
                                        }, 
                                        {
                                            "type": "f7"
                                        }, 
                                        {
                                            "type": "f8"
                                        }, 
                                        {
                                            "type": "f9"
                                        }, 
                                        {
                                            "type": "f10"
                                        }, 
                                         {
                                            "type": "num_lock"
                                        }, 
                                        {
                                            "type": "scroll_lock"
                                        }, 
                                        {
                                            "type": "kp_divide"
                                        }, 
                                        {
                                            "type": "kp_multiply"
                                        }, 
                                        {
                                            "type": "kp_subtract"
                                        }, 
                                        {
                                            "type": "kp_add"
                                        }, 
                                        {
                                            "type": "kp_enter"
                                        }, 
                                        {
                                            "type": "kp_decimal"
                                        }, 
                                        {
                                            "type": "sysrq"
                                        }, 
                                        {
                                            "type": "kp_0"
                                        }, 
                                        {
                                            "type": "kp_1"
                                        }, 
                                        {
                                            "type": "kp_2"
                                        }, 
                                        {
                                            "type": "kp_3"
                                        }, 
                                        {
                                             "type": "kp_4"
                                        }, 
                                        {
                                            "type": "kp_5"
                                        }, 
                                        {
                                            "type": "kp_6"
                                        }, 
                                        {
                                            "type": "kp_7"
                                        }, 
                                        {
                                            "type": "kp_8"
                                        }, 
                                        {
                                            "type": "kp_9"
                                        }, 
                                        {
                                            "type": "less"
                                        }, 
                                        {
                                            "type": "f11"
                                        }, 
                                        {
                                            "type": "f12"
                                        }, 
                                        {
                                            "type": "print"
                                        }, 
                                        {
                                            "type": "home"
                                        }, 
                                        {
                                            "type": "pgup"
                                        }, 
                                        {
                                            "type": "pgdn"
                                        }, 
                                        {
                                            "type": "end"
                                        }, 
                                        {
                                            "type": "left"
                                        }, 
                                        {
                                            "type": "up"
                                        }, 
                                        {
                                            "type": "down"
                                        }, 
                                        {
                                            "type": "right"
                                        }, 
                                        {
                                            "type": "insert"
                                        }, 
                                        {
                                            "type": "delete"
                                        }, 
                                        {
                                            "type": "stop"
                                        }, 
                                        {
                                            "type": "again"
                                        }, 
                                         {
                                            "type": "props"
                                        }, 
                                        {
                                            "type": "undo"
                                        }, 
                                        {
                                            "type": "front"
                                        }, 
                                        {
                                            "type": "copy"
                                        }, 
                                        {
                                            "type": "open"
                                        }, 
                                        {
                                            "type": "paste"
                                        }, 
                                        {
                                            "type": "find"
                                        }, 
                                        {
                                            "type": "cut"
                                        }, 
                                        {
                                            "type": "lf"
                                        }, 
                                        {
                                            "type": "help"
                                        }, 
                                        {
                                            "type": "meta_l"
                                        }, 
                                        {
                                            "type": "meta_r"
                                        }, 
                                        {
                                            "type": "compose"
                                        }
                                    ]
                                }, 
                                {
                                     "name": "number", 
                                    "type": "int"
                                }
                            ]
                        }
                    ]
                }, 
                {
                    "name": "*hold-time", 
                    "type": "int"
                }
            ]
        }, 
        {
            "name": "screendump", 
            "type": "Command", 
            "data": [
                {
                    "name": "filename", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "nbd-server-start", 
            "type": "Command", 
            "data": [
                {
                    "name": "addr", 
                    "type": "SocketAddress", 
                    "data": [
                        {
                            "name": "fd", 
                            "type": "String", 
                            "data": [
                                {
                                    "name": "str", 
                                    "type": "str"
                                }
                            ]
                        }, 
                        {
                            "name": "unix", 
                            "type": "UnixSocketAddress", 
                            "data": [
                                {
                                    "name": "path", 
                                    "type": "str"
                                }
                            ]
                        }, 
                        {
                            "name": "inet", 
                            "type": "InetSocketAddress", 
                            "data": [
                                {
                                    "name": "port", 
                                    "type": "str"
                                }, 
                                {
                                    "name": "*ipv6", 
                                    "type": "bool"
                                }, 
                                {
                                    "name": "*ipv4", 
                                    "type": "bool"
                                }, 
                                {
                                    "name": "host", 
                                    "type": "str"
                                }, 
                                {
                                    "name": "*to", 
                                    "type": "uint16"
                                }
                            ]
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "nbd-server-add", 
            "type": "Command", 
            "data": [
                {
                    "name": "device", 
                    "type": "str"
                }, 
                {
                    "name": "*writable", 
                    "type": "bool"
                }
            ]
        }, 
        {
            "name": "nbd-server-stop", 
            "type": "Command"
        }, 
        {
            "name": "chardev-add", 
            "type": "Command", 
            "returns": [
                {
                    "name": "*pty", 
                    "type": "str"
                }
            ], 
            "data": [
                {
                    "name": "backend", 
                    "type": "ChardevBackend", 
                    "data": [
                        {
                            "name": "serial", 
                            "type": "ChardevHostdev", 
                            "data": [
                                {
                                    "name": "device", 
                                    "type": "str"
                                }
                            ]
                        }, 
                        {
                            "name": "spiceport", 
                            "type": "ChardevSpicePort", 
                            "data": [
                                {
                                    "name": "fqdn", 
                                    "type": "str"
                                }
                            ]
                        }, 
                        {
                            "name": "stdio", 
                            "type": "ChardevStdio", 
                            "data": [
                                {
                                    "name": "*signal", 
                                    "type": "bool"
                                }
                            ]
                        }, 
                        {
                            "name": "memory", 
                            "type": "ChardevMemory", 
                            "data": [
                                {
                                    "name": "*size", 
                                    "type": "int"
                                }
                            ]
                        }, 
                        {
                            "name": "socket", 
                            "type": "ChardevSocket", 
                            "data": [
                                {
                                    "name": "*wait", 
                                    "type": "bool"
                                }, 
                                {
                                    "name": "*server", 
                                    "type": "bool"
                                }, 
                                {
                                    "name": "addr", 
                                    "type": "SocketAddress", 
                                    "data": [
                                        {
                                            "name": "fd", 
                                            "type": "String", 
                                            "data": [
                                                {
                                                    "name": "str", 
                                                    "type": "str"
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "unix", 
                                            "type": "UnixSocketAddress", 
                                            "data": [
                                                {
                                                    "name": "path", 
                                                    "type": "str"
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "inet", 
                                            "type": "InetSocketAddress", 
                                            "data": [
                                                {
                                                    "name": "port", 
                                                    "type": "str"
                                                }, 
                                                {
                                                    "name": "*ipv6", 
                                                    "type": "bool"
                                                }, 
                                                {
                                                    "name": "*ipv4", 
                                                    "type": "bool"
                                                }, 
                                                {
                                                    "name": "host", 
                                                    "type": "str"
                                                }, 
                                                {
                                                    "name": "*to", 
                                                    "type": "uint16"
                                                }
                                            ]
                                        }
                                    ]
                                }, 
                                {
                                    "name": "*nodelay", 
                                    "type": "bool"
                                }, 
                                {
                                    "name": "*telnet", 
                                    "type": "bool"
                                }
                            ]
                        }, 
                        {
                            "name": "msmouse", 
                            "type": "ChardevDummy", 
                            "data": [
                            ]
                        }, 
                        {
                            "name": "pty", 
                            "type": "ChardevDummy", 
                            "data": [
                            ]
                        }, 
                        {
                            "name": "braille", 
                            "type": "ChardevDummy", 
                            "data": [
                            ]
                        }, 
                        {
                            "name": "udp", 
                            "type": "ChardevUdp", 
                            "data": [
                                {
                                    "name": "remote", 
                                    "type": "SocketAddress", 
                                    "data": [
                                        {
                                            "name": "fd", 
                                            "type": "String", 
                                            "data": [
                                                {
                                                    "name": "str", 
                                                    "type": "str"
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "unix", 
                                            "type": "UnixSocketAddress", 
                                            "data": [
                                                {
                                                    "name": "path", 
                                                    "type": "str"
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "inet", 
                                            "type": "InetSocketAddress", 
                                            "data": [
                                                {
                                                    "name": "port", 
                                                    "type": "str"
                                                }, 
                                                {
                                                    "name": "*ipv6", 
                                                    "type": "bool"
                                                }, 
                                                {
                                                    "name": "*ipv4", 
                                                    "type": "bool"
                                                }, 
                                                {
                                                    "name": "host", 
                                                    "type": "str"
                                                }, 
                                                {
                                                    "name": "*to", 
                                                    "type": "uint16"
                                                }
                                            ]
                                        }
                                    ]
                                }, 
                                {
                                    "name": "*local", 
                                    "type": "SocketAddress", 
                                    "data": [
                                        {
                                            "name": "fd", 
                                            "type": "String", 
                                            "data": [
                                                {
                                                    "name": "str", 
                                                    "type": "str"
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "unix", 
                                            "type": "UnixSocketAddress", 
                                            "data": [
                                                {
                                                    "name": "path", 
                                                    "type": "str"
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "inet", 
                                            "type": "InetSocketAddress", 
                                            "data": [
                                                {
                                                    "name": "port", 
                                                    "type": "str"
                                                }, 
                                                {
                                                    "name": "*ipv6", 
                                                    "type": "bool"
                                                }, 
                                                {
                                                    "name": "*ipv4", 
                                                    "type": "bool"
                                                }, 
                                                {
                                                    "name": "host", 
                                                    "type": "str"
                                                }, 
                                                {
                                                    "name": "*to", 
                                                    "type": "uint16"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }, 
                        {
                            "name": "mux", 
                            "type": "ChardevMux", 
                            "data": [
                                {
                                    "name": "chardev", 
                                    "type": "str"
                                }
                            ]
                        }, 
                        {
                            "name": "console", 
                            "type": "ChardevDummy", 
                            "data": [
                            ]
                        }, 
                        {
                            "name": "parallel", 
                            "type": "ChardevHostdev", 
                            "data": [
                                {
                                    "name": "device", 
                                    "type": "str"
                                }
                            ]
                        }, 
                        {
                            "name": "null", 
                            "type": "ChardevDummy", 
                            "data": [
                            ]
                        }, 
                        {
                            "name": "file", 
                            "type": "ChardevFile", 
                            "data": [
                                {
                                    "name": "*in", 
                                    "type": "str"
                                }, 
                                {
                                    "name": "out", 
                                    "type": "str"
                                }
                            ]
                        }, 
                        {
                            "name": "spicevmc", 
                            "type": "ChardevSpiceChannel", 
                            "data": [
                                {
                                    "name": "type", 
                                    "type": "str"
                                }
                            ]
                        }, 
                        {
                            "name": "vc", 
                            "type": "ChardevVC", 
                            "data": [
                                {
                                    "name": "*cols", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "*width", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "*rows", 
                                    "type": "int"
                                }, 
                                {
                                    "name": "*height", 
                                    "type": "int"
                                }
                            ]
                        }, 
                        {
                            "name": "pipe", 
                            "type": "ChardevHostdev", 
                            "data": [
                                {
                                    "name": "device", 
                                    "type": "str"
                                }
                            ]
                        }
                    ]
                }, 
                {
                    "name": "id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "chardev-remove", 
            "type": "Command", 
            "data": [
                {
                    "name": "id", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-tpm-models", 
            "type": "Command", 
            "returns": [
                {
                    "type": "TpmModel", 
                    "data": [
                        {
                            "type": "tpm-tis"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-tpm-types", 
            "type": "Command", 
            "returns": [
                {
                    "type": "TpmType", 
                    "data": [
                        {
                            "type": "passthrough"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-tpm", 
            "type": "Command", 
            "returns": [
                {
                    "type": "TPMInfo", 
                    "data": [
                        {
                            "name": "model", 
                            "type": "TpmModel", 
                            "data": [
                                {
                                    "type": "tpm-tis"
                                }
                            ]
                        }, 
                        {
                            "name": "options", 
                            "type": "TpmTypeOptions", 
                            "data": [
                                {
                                    "name": "passthrough", 
                                    "type": "TPMPassthroughOptions", 
                                    "data": [
                                        {
                                            "name": "*path", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "*cancel-path", 
                                            "type": "str"
                                        }
                                    ]
                                }
                            ]
                        }, 
                        {
                            "name": "id", 
                            "type": "str"
                        }
                    ]
                }
            ]
        }, 
        {
            "name": "query-command-line-options", 
            "type": "Command", 
            "returns": [
                {
                    "type": "CommandLineOptionInfo", 
                    "data": [
                        {
                            "name": "parameters", 
                            "data": [
                                {
                                    "type": "CommandLineParameterInfo", 
                                    "data": [
                                        {
                                            "name": "name", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "*help", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "type", 
                                            "type": "CommandLineParameterType", 
                                            "data": [
                                                {
                                                    "type": "string"
                                                }, 
                                                {
                                                    "type": "boolean"
                                                }, 
                                                {
                                                    "type": "number"
                                                }, 
                                                {
                                                    "type": "size"
                                                }
                                            ]
                                        }
                                    ]
                                }
                            ]
                        }, 
                        {
                            "name": "option", 
                            "type": "str"
                        }
                    ]
                }
            ], 
            "data": [
                {
                    "name": "*option", 
                    "type": "str"
                }
            ]
        }, 
        {
            "name": "query-qmp-schema", 
            "type": "Command", 
            "returns": [
                {
                    "type": "SchemaData", 
                    "data": [
                        {
                            "name": "*data", 
                            "data": [
                                {
                                    "type": "DataObject", 
                                    "data": [
                                        {
                                            "name": "*data", 
                                            "data": [
                                                {
                                                    "type": "DataObject"
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "*type", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "*name", 
                                            "type": "str"
                                        }
                                    ]
                                }
                            ]
                        }, 
                        {
                            "name": "name", 
                            "type": "str"
                        }, 
                        {
                            "name": "*returns", 
                            "data": [
                                {
                                    "type": "DataObject", 
                                    "data": [
                                        {
                                            "name": "*data", 
                                            "data": [
                                                {
                                                    "type": "DataObject"
                                                }
                                            ]
                                        }, 
                                        {
                                            "name": "*type", 
                                            "type": "str"
                                        }, 
                                        {
                                            "name": "*name", 
                                            "type": "str"
                                        }
                                    ]
                                }
                            ]
                        }, 
                        {
                            "name": "type", 
                            "type": "SchemaMetatype", 
                            "data": [
                                {
                                    "type": "Command"
                                }, 
                                {
                                    "type": "Type"
                                }, 
                                {
                                    "type": "Enumeration"
                                }, 
                                {
                                    "type": "Union"
                                }, 
                                {
                                    "type": "Event"
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}
Luiz Capitulino June 21, 2013, 3:20 a.m. UTC | #3
On Wed, 19 Jun 2013 20:24:37 +0800
Amos Kong <akong@redhat.com> wrote:

> Introduces new monitor command to query QMP schema information,
> the return data is a nested dict/list, it contains the useful
> metadata.

Thanks for the good work, Amos!

When testing this though I actually get qemu-ga's schema, not
qmp's. Did you test this with qemu-ga build enabled?

This bug shows that we need to handle qemu-ga properly, which
means having query-guest-agent-schema for qemu-ga.

It's also a good idea to start the commit log with some json
examples btw.

More comments below.

> we can add events definations to qapi-schema.json, then it can
> also be queried.
> 
> Signed-off-by: Amos Kong <akong@redhat.com>
> ---
>  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

Data object name if it has one?

> +#
> +# @type: the string value of dictionary or list

data type name?

> +#
> +# @data: #optional a list of @DataObject, dictionary's value is nested
> +#        dictionary/list

#optional DataObject list, can be a dictionary or list type?

> +#
> +# Since: 1.6
> +##
> +{ 'type': 'DataObject',
> +  'data': { '*name': 'str', '*type': 'str', '*data': ['DataObject'] } }
> +
> +##
> +# @SchemaMetatype

As we're doing CamelCase, this should be SchemaMetaType. Or maybe just
SchemaType?

> +#
> +# Possible meta types of a schema entry
> +#
> +# @Command: QMP monitor command to control guest

"QMP command" is good enough.

> +#
> +# @Type: defined new data type
> +#
> +# @Enumeration: enumeration data type
> +#
> +# @Union: union data type
> +#
> +# @Event: QMP event to notify QMP clients

I'm not sure we should have events listed here as they are not
supported yet.

> +#
> +# Since: 1.6
> +##
> +{ 'enum': 'SchemaMetatype',
> +  'data': ['Command', 'Type', 'Enumeration', 'Union', 'Event'] }
> +
> +##
> +# @SchemaData

Sorry for the bikeshed, but SchemaEntry maybe?

> +#
> +# Details of schema items
> +#
> +# @type: dict's value, list's value

Entry's type in string format.

> +#
> +# @name: dict's key

Entry name.

> +#
> +# @data: #optional list of @DataObject, arguments data of executing
> +#        QMP command

"#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"

> +#
> +# @returns: #optional list of DataObject, return data after executing
> +#           QMP command

I don't parse what's after the coma.

> +#
> +# 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.

I don't think you should return errors, see below.

> +#
> +# 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"
> +                }

Should we have an 'optional' bool field instead of having the *
in name?

> +            ]
> +        }
> +      ]
> +   }
> +
> +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);

I don't think this is needed.

> +
> +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;

No need for spaces between declarations.

> +
> +    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;
> +        }

This should only fail if we have a bug in the qapi scripts, right? In this
case we should abort instead.

> +
> +        qdict = qobject_to_qdict(data);
> +        if (!qdict) {
> +            error_setg(errp, "Can't convert qobject to valid qdict");
> +            return NULL;
> +        }

Same here.

> +
> +        ent = qdict_first(qdict);

I don't think this is fully correct. This works probably because the
command/type/enum/union key is the first in the dict, but we shouldn't
count on that.

I think you should use qdict_get() here, like:

> +        info = g_malloc0(sizeof(*info));
> +
> +        if (!strcmp(ent->key, "command")) {

if (qdict_get(qdict, "command")) {
	...
}

This is safer I think.

> +            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)));

Please, add a new method to qstring, like qstring_copy_str() and
use that instead.

> +                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[] = {

I think you want const char *const qmp_schema_table[].

Btw, I find your approach interesting but I'm wondering if it's going to
be a good thing to keep all the schema in memory. Do you have an idea
on its size?

> +"""
> +
> +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:
Amos Kong July 2, 2013, 8:37 a.m. UTC | #4
On Thu, Jun 20, 2013 at 11:20:21PM -0400, Luiz Capitulino wrote:
> On Wed, 19 Jun 2013 20:24:37 +0800
> Amos Kong <akong@redhat.com> wrote:
> 
> > Introduces new monitor command to query QMP schema information,
> > the return data is a nested dict/list, it contains the useful
> > metadata.

Thanks your comments.
 
> Thanks for the good work, Amos!
> 
> When testing this though I actually get qemu-ga's schema, not
> qmp's. Did you test this with qemu-ga build enabled?

Currently qmp-schema.h will be generated two times. QMP's schema
will cover qga's schema. I will add an option '-i' / '--instrospec'
for qapi-types.py to set the name of generated file.

> This bug shows that we need to handle qemu-ga properly, which
> means having query-guest-agent-schema for qemu-ga.

I will generate two schema tables for qmp & qga
  qmp-schema.h qga-schema.h

> It's also a good idea to start the commit log with some json
> examples btw.
 
OK, I will give some example in commitlog, and add a doc to
describe dynamical DataObject.

> More comments below.
> 
> > we can add events definations to qapi-schema.json, then it can
> > also be queried.
> > 
> > Signed-off-by: Amos Kong <akong@redhat.com>
> > ---
> >  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
> 
> Data object name if it has one?
> 
> > +#
> > +# @type: the string value of dictionary or list
> 
> data type name?
> 
> > +#
> > +# @data: #optional a list of @DataObject, dictionary's value is nested
> > +#        dictionary/list
> 
> #optional DataObject list, can be a dictionary or list type?
> 
> > +#
> > +# Since: 1.6
> > +##
> > +{ 'type': 'DataObject',
> > +  'data': { '*name': 'str', '*type': 'str', '*data': ['DataObject'] } }
> > +
> > +##
> > +# @SchemaMetatype
> 
> As we're doing CamelCase, this should be SchemaMetaType. Or maybe just
> SchemaType?
> 
> > +#
> > +# Possible meta types of a schema entry
> > +#
> > +# @Command: QMP monitor command to control guest
> 
> "QMP command" is good enough.
> 
> > +#
> > +# @Type: defined new data type
> > +#
> > +# @Enumeration: enumeration data type
> > +#
> > +# @Union: union data type
> > +#
> > +# @Event: QMP event to notify QMP clients
> 
> I'm not sure we should have events listed here as they are not
> supported yet.

Will remove it first.
 
> > +#
> > +# Since: 1.6
> > +##
> > +{ 'enum': 'SchemaMetatype',
> > +  'data': ['Command', 'Type', 'Enumeration', 'Union', 'Event'] }
> > +
> > +##
> > +# @SchemaData
> 
> Sorry for the bikeshed, but SchemaEntry maybe?
> 
> > +#
> > +# Details of schema items
> > +#
> > +# @type: dict's value, list's value
> 
> Entry's type in string format.
> 
> > +#
> > +# @name: dict's key
> 
> Entry name.
> 
> > +#
> > +# @data: #optional list of @DataObject, arguments data of executing
> > +#        QMP command
> 
> "#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"
> 
> > +#
> > +# @returns: #optional list of DataObject, return data after executing
> > +#           QMP command
>
> I don't parse what's after the coma.

#optional list of DataObject. This is the return date of executing a 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.
> 
> I don't think you should return errors, see below.
> 
> > +#
> > +# 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"
> > +                }
> 
> Should we have an 'optional' bool field instead of having the *
> in name?

OK.
 
> > +            ]
> > +        }
> > +      ]
> > +   }
> > +
> > +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);
> 
> I don't think this is needed.

visit_qobj_dict() and visit_qobj_list() call each other.
the declaration is necessary.
 
> > +
> > +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;
> 
> No need for spaces between declarations.
> 
> > +
> > +    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;
> > +        }
> 
> This should only fail if we have a bug in the qapi scripts, right? In this
> case we should abort instead.

Yes, qapi script didn't provide valid schema table.
Ok, will replace the error by abort.
 
> > +
> > +        qdict = qobject_to_qdict(data);
> > +        if (!qdict) {
> > +            error_setg(errp, "Can't convert qobject to valid qdict");
> > +            return NULL;
> > +        }
> 
> Same here.
> 
> > +
> > +        ent = qdict_first(qdict);
> 
> I don't think this is fully correct. This works probably because the
> command/type/enum/union key is the first in the dict, but we shouldn't
> count on that.
> 
> I think you should use qdict_get() here, like:
> 
> > +        info = g_malloc0(sizeof(*info));
> > +
> > +        if (!strcmp(ent->key, "command")) {
> 
> if (qdict_get(qdict, "command")) {
> 	...
> }
> 
> This is safer I think.

OK.

> > +            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)));
> 
> Please, add a new method to qstring, like qstring_copy_str() and
> use that instead.
 
OK.

> > +                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[] = {
> 
> I think you want const char *const qmp_schema_table[].
> 
> Btw, I find your approach interesting but I'm wondering if it's going to
> be a good thing to keep all the schema in memory. Do you have an idea
> on its size?

The size of qmp_schema_table[] is 1528 bytes.

method 2rd:
  save the raw json strings to qmp-schema.txt, allocate dynamical
  qmp_schema_table[] in qmp_query_qmp_schema(), and restore the
  file content to the table, free it at the end of qmp_query_qmp_schema()

qmp-schema.txt:
# Schema json strings converted from qapi-schema.json
# comments ....
# comments ....
{ 'type': 'NameInfo', 'data': {'*name': 'str'} }
{ 'command': 'query-name', 'returns': 'NameInfo' }
....

 
> > +"""
> > +
> > +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)
Luiz Capitulino July 2, 2013, 2:20 p.m. UTC | #5
On Tue, 2 Jul 2013 16:37:29 +0800
Amos Kong <akong@redhat.com> wrote:

> > Btw, I find your approach interesting but I'm wondering if it's going to
> > be a good thing to keep all the schema in memory. Do you have an idea
> > on its size?
> 
> The size of qmp_schema_table[] is 1528 bytes.
> 
> method 2rd:
>   save the raw json strings to qmp-schema.txt, allocate dynamical
>   qmp_schema_table[] in qmp_query_qmp_schema(), and restore the
>   file content to the table, free it at the end of qmp_query_qmp_schema()

Well, keeping a file around like that doesn't look like a good solution.

Let's do your approach for now. If having this in memory turns out to
be a problem, we could try compacting it.
Anthony Liguori July 2, 2013, 2:51 p.m. UTC | #6
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
Eric Blake July 2, 2013, 3:28 p.m. UTC | #7
On 07/02/2013 08:51 AM, Anthony Liguori wrote:
> 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?

Because qapi-schema.json requires further parsing.  For example, how is
a client supposed to know that '*foo':'int' means that there is an
argument named 'foo' but it is optional?  The rule of thumb with QMP is
that if you have to post-process JSON output, then the JSON was not
designed correctly.

> 
> 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.

qapi-schema.json is not quite JSON, in that it has #comments that we'd
have to strip before we attempted a trick like this.  I've also been the
one arguing that the additional complexity (an array of
{"name":"str","type":"str","optional":bool"}) is better for libvirt in
that the JSON is then well-suited for scanning (it is easier to scan
through an array where the key is a constant "name", and looking for the
value that we are interested in, than it is to scan through a dictionary
where the keys of the dictionary are the names we are interested in).
That is, the JSON in qapi-schema.json is a nice compact representation
that works for humans, but may be a bit TOO compact for handling via
machines.
Daniel P. Berrangé July 2, 2013, 3:39 p.m. UTC | #8
On Tue, Jul 02, 2013 at 09:28:51AM -0600, Eric Blake wrote:
> On 07/02/2013 08:51 AM, Anthony Liguori wrote:
> > 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?
> 
> Because qapi-schema.json requires further parsing.  For example, how is
> a client supposed to know that '*foo':'int' means that there is an
> argument named 'foo' but it is optional?  The rule of thumb with QMP is
> that if you have to post-process JSON output, then the JSON was not
> designed correctly.

Arguably that rule of thumb would apply equally to the QEMU
build scripts which already parse qapi-schema.json. It could
be possible to normalize qapi-schema.json somewhat to remove
this 2-stage parsing if we went down this route.

> > 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.
> 
> qapi-schema.json is not quite JSON, in that it has #comments that we'd
> have to strip before we attempted a trick like this.  I've also been the
> one arguing that the additional complexity (an array of
> {"name":"str","type":"str","optional":bool"}) is better for libvirt in
> that the JSON is then well-suited for scanning (it is easier to scan
> through an array where the key is a constant "name", and looking for the
> value that we are interested in, than it is to scan through a dictionary
> where the keys of the dictionary are the names we are interested in).
> That is, the JSON in qapi-schema.json is a nice compact representation
> that works for humans, but may be a bit TOO compact for handling via
> machines.

I'm finding it hard to clearly see what the 2 different proposed
data formats look like against each other. Can someone give some
examples, showing the data that would need to be parsed in each
format, for a couple of examples.


Daniel
Eric Blake July 2, 2013, 4:39 p.m. UTC | #9
On 06/19/2013 06:49 AM, Amos Kong wrote:
> On Wed, Jun 19, 2013 at 08:24:37PM +0800, Amos Kong wrote:
>> 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.
>  
> I didn't implement to return complete schema in one go in this
> version, will do it in next version. We have a recursive define
> 'DataObject', we only display one layer for it.

Yes, for recursive definitions, we have to stop somewhere.


> 
> You can find three kind of examples(string/list/dict) in the bottom.
> Attached (query-qmp-schema--output.txt) the full output of execut query-qmp-schema command.
> 
> String:
> { 'command': 'query-name', 'returns': 'NameInfo' }
> ---------------------------------------------------------------------
>         {
>             "name": "NameInfo",
>             "type": "Type",
>             "data": [
>                 {
>                     "name": "*name",
>                     "type": "str"
>                 }
>             ]
>         },

This output still requires post-processing - the user has to parse the
key "*name" to learn two bits of information - whether the parameter is
optional, and the fact that it is named "name" not "*name".  I _still_
argue that we want to return 3 things, not two, as in:

            "data": [
                {
                    "name": "name",
                    "optional": true,
                    "type": "str"
                }
            ]
Eric Blake July 2, 2013, 4:44 p.m. UTC | #10
On 07/02/2013 09:39 AM, Daniel P. Berrange wrote:
>>> Maybe I'm being too meta here, but why not just return qapi-schema.json
>>> as a string and call it as day?
>>
>> Because qapi-schema.json requires further parsing.  For example, how is
>> a client supposed to know that '*foo':'int' means that there is an
>> argument named 'foo' but it is optional?  The rule of thumb with QMP is
>> that if you have to post-process JSON output, then the JSON was not
>> designed correctly.
> 
> Arguably that rule of thumb would apply equally to the QEMU
> build scripts which already parse qapi-schema.json. It could
> be possible to normalize qapi-schema.json somewhat to remove
> this 2-stage parsing if we went down this route.

Indeed, I wouldn't mind a one-time pass over qapi-schema.json to make it
follow a more rigid format if that made it easier to use it as-is with
less post-processing.  It won't be very nice to backport such a
conversion, but I don't know how much distros are planning on
backporting introspection in the first place.

> 
> I'm finding it hard to clearly see what the 2 different proposed
> data formats look like against each other. Can someone give some
> examples, showing the data that would need to be parsed in each
> format, for a couple of examples.

My proposal (but not quite what was implemented in _this_ version of
Amos' patch) has been:

qapi-schema.json:

{ 'command': 'add_client',
  'data': { 'protocol': 'str', 'fdname': 'str', '*skipauth': 'bool',
            '*tls': 'bool' } }

My proposal:


=> { "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 }
       ] } ] }

Note that one other benefit of breaking out "optional" into its own dict
member: we are free to expand the dict to add new members, such as a
'*default':'str' member (present when "optional":true, and with the
stringized value of the default value used if "name" is not present).
Of course, to be able to express a default value, we already have to
modify the syntax stored in qapi-schema.json in the first place, which
takes us back to the argument of a one-time conversion of the .json file
to actually use a more formal typing system in the first place.
Paolo Bonzini July 2, 2013, 5:01 p.m. UTC | #11
> > Arguably that rule of thumb would apply equally to the QEMU
> > build scripts which already parse qapi-schema.json. It could
> > be possible to normalize qapi-schema.json somewhat to remove
> > this 2-stage parsing if we went down this route.
> 
> Indeed, I wouldn't mind a one-time pass over qapi-schema.json to make it
> follow a more rigid format if that made it easier to use it as-is with
> less post-processing.  It won't be very nice to backport such a
> conversion, but I don't know how much distros are planning on
> backporting introspection in the first place.

How would the schema look like after this "one-time pass"?

This:

> [
>          { "name": "protocol",
>            "type": "str" },
>          { "name": "fdname",
>            "type": "str" },
>          { "name": "skipauth",
>            "type": "bool",
>            "optional": true },
>          { "name": "tls",
>            "type": "bool",
>            "optional": true }
> ]

Looks quite awful for a human to write and read.

Paolo
Anthony Liguori July 2, 2013, 5:06 p.m. UTC | #12
Eric Blake <eblake@redhat.com> writes:

> On 07/02/2013 08:51 AM, Anthony Liguori wrote:
>> 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?
>
> Because qapi-schema.json requires further parsing.  For example, how is
> a client supposed to know that '*foo':'int' means that there is an
> argument named 'foo' but it is optional?  The rule of thumb with QMP is
> that if you have to post-process JSON output, then the JSON was not
> designed correctly.

Then we should fix qapi-schema.json.

>> 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.
>
> qapi-schema.json is not quite JSON, in that it has #comments that we'd
> have to strip before we attempted a trick like this.  

Pretty easy to filter that out...

> I've also been the one arguing that the additional complexity (an array of
> {"name":"str","type":"str","optional":bool"}) is better for libvirt in
> that the JSON is then well-suited for scanning (it is easier to scan
> through an array where the key is a constant "name", and looking for the
> value that we are interested in, than it is to scan through a dictionary
> where the keys of the dictionary are the names we are interested in).
> That is, the JSON in qapi-schema.json is a nice compact representation
> that works for humans, but may be a bit TOO compact for handling via
> machines.

But adding a bunch of code to do JSON translation just adds a bunch of
additional complexity.

One reasonable compromise would be:

{ "command": "foo", "arguments": { "name": "str", "id": "int" },
                    "optional": { "bar": "bool" } }

Then libvirt only has to test 'does command["optional"] contain key
"bar"' to test for an optional parameter.

Although to be honest, '*bar' in command['arguments'] is reasonable too
IMHO.

Regards,

Anthony Liguori


>
> -- 
> Eric Blake   eblake redhat com    +1-919-301-3266
> Libvirt virtualization library http://libvirt.org
Eric Blake July 2, 2013, 5:06 p.m. UTC | #13
On 07/02/2013 11:01 AM, Paolo Bonzini wrote:
>>> Arguably that rule of thumb would apply equally to the QEMU
>>> build scripts which already parse qapi-schema.json. It could
>>> be possible to normalize qapi-schema.json somewhat to remove
>>> this 2-stage parsing if we went down this route.
>>
>> Indeed, I wouldn't mind a one-time pass over qapi-schema.json to make it
>> follow a more rigid format if that made it easier to use it as-is with
>> less post-processing.  It won't be very nice to backport such a
>> conversion, but I don't know how much distros are planning on
>> backporting introspection in the first place.
> 
> How would the schema look like after this "one-time pass"?
> 
> This:
> 
>> [
>>          { "name": "protocol",
>>            "type": "str" },
>>          { "name": "fdname",
>>            "type": "str" },
>>          { "name": "skipauth",
>>            "type": "bool",
>>            "optional": true },
>>          { "name": "tls",
>>            "type": "bool",
>>            "optional": true }
>> ]
> 
> Looks quite awful for a human to write and read.

Which puts us back in favor of my original argument that keeping
qapi-schema.json compact for human use, while expanding the QMP output
to be verbose for machine use, is probably what we'll have to live with.
 While it is easy to document shortcuts that the qapi parser can use
when converting .json to code, it is harder to require that all other
QMP clients must implement those same shortcuts, instead of having
things already directly represented.
Eric Blake July 2, 2013, 5:11 p.m. UTC | #14
On 07/02/2013 11:06 AM, Anthony Liguori wrote:
>> Because qapi-schema.json requires further parsing.  For example, how is
>> a client supposed to know that '*foo':'int' means that there is an
>> argument named 'foo' but it is optional?  The rule of thumb with QMP is
>> that if you have to post-process JSON output, then the JSON was not
>> designed correctly.
> 
> Then we should fix qapi-schema.json.
> 

> One reasonable compromise would be:
> 
> { "command": "foo", "arguments": { "name": "str", "id": "int" },
>                     "optional": { "bar": "bool" } }
> 
> Then libvirt only has to test 'does command["optional"] contain key
> "bar"' to test for an optional parameter.

Yes, that might be a reasonable compromise - at least that way, the
optional parameter names are listed directly, and libvirt doesn't have
to probe every single parameter to name to see which begin with '*'.

> 
> Although to be honest, '*bar' in command['arguments'] is reasonable too
> IMHO.

It may be reasonable, but it forces every QMP client to reimplement the
post-processing step, rather than presenting the parameter names already
in isolation.

Food for thought - suppose we wanted to start expressing in the .json
file what the default value is for any parameter that is listed as
optional?  What representation is most compact for that purpose, while
still being valid JSON and not requiring QMP clients to reimplement
post-processing?
Anthony Liguori July 2, 2013, 6:21 p.m. UTC | #15
Eric Blake <eblake@redhat.com> writes:

> On 07/02/2013 09:39 AM, Daniel P. Berrange wrote:
>>>> Maybe I'm being too meta here, but why not just return qapi-schema.json
>>>> as a string and call it as day?
>>>
>>> Because qapi-schema.json requires further parsing.  For example, how is
>>> a client supposed to know that '*foo':'int' means that there is an
>>> argument named 'foo' but it is optional?  The rule of thumb with QMP is
>>> that if you have to post-process JSON output, then the JSON was not
>>> designed correctly.
>> 
>> Arguably that rule of thumb would apply equally to the QEMU
>> build scripts which already parse qapi-schema.json. It could
>> be possible to normalize qapi-schema.json somewhat to remove
>> this 2-stage parsing if we went down this route.
>
> Indeed, I wouldn't mind a one-time pass over qapi-schema.json to make it
> follow a more rigid format if that made it easier to use it as-is with
> less post-processing.  It won't be very nice to backport such a
> conversion, but I don't know how much distros are planning on
> backporting introspection in the first place.

We consume the schema in QEMU.  No reason for us to consume it in a
different format than libvirt.

We're all after the same thing.

Regards,

Anthony Liguori

>
>> 
>> I'm finding it hard to clearly see what the 2 different proposed
>> data formats look like against each other. Can someone give some
>> examples, showing the data that would need to be parsed in each
>> format, for a couple of examples.
>
> My proposal (but not quite what was implemented in _this_ version of
> Amos' patch) has been:
>
> qapi-schema.json:
>
> { 'command': 'add_client',
>   'data': { 'protocol': 'str', 'fdname': 'str', '*skipauth': 'bool',
>             '*tls': 'bool' } }
>
> My proposal:
>
>
> => { "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 }
>        ] } ] }
>
> Note that one other benefit of breaking out "optional" into its own dict
> member: we are free to expand the dict to add new members, such as a
> '*default':'str' member (present when "optional":true, and with the
> stringized value of the default value used if "name" is not present).
> Of course, to be able to express a default value, we already have to
> modify the syntax stored in qapi-schema.json in the first place, which
> takes us back to the argument of a one-time conversion of the .json file
> to actually use a more formal typing system in the first place.

I really think that we want schema introspection to return the exact
content of qapi-schema.json.

I don't think we need an overly verbose schema though and I don't really
understand why { 'name': 'tls', 'type': 'bool', 'optional': true } is
better than { '*name': 'bool' }

Regards,

Anthony Liguori

>
> -- 
> Eric Blake   eblake redhat com    +1-919-301-3266
> Libvirt virtualization library http://libvirt.org
Anthony Liguori July 2, 2013, 6:27 p.m. UTC | #16
Eric Blake <eblake@redhat.com> writes:

> On 07/02/2013 11:01 AM, Paolo Bonzini wrote:
>>>> Arguably that rule of thumb would apply equally to the QEMU
>>>> build scripts which already parse qapi-schema.json. It could
>>>> be possible to normalize qapi-schema.json somewhat to remove
>>>> this 2-stage parsing if we went down this route.
>>>
>>> Indeed, I wouldn't mind a one-time pass over qapi-schema.json to make it
>>> follow a more rigid format if that made it easier to use it as-is with
>>> less post-processing.  It won't be very nice to backport such a
>>> conversion, but I don't know how much distros are planning on
>>> backporting introspection in the first place.
>> 
>> How would the schema look like after this "one-time pass"?
>> 
>> This:
>> 
>>> [
>>>          { "name": "protocol",
>>>            "type": "str" },
>>>          { "name": "fdname",
>>>            "type": "str" },
>>>          { "name": "skipauth",
>>>            "type": "bool",
>>>            "optional": true },
>>>          { "name": "tls",
>>>            "type": "bool",
>>>            "optional": true }
>>> ]
>> 
>> Looks quite awful for a human to write and read.
>
> Which puts us back in favor of my original argument that keeping
> qapi-schema.json compact for human use,

qapi-schema.json is not "for human use".  It's consumed by QAPI.  It
happens to be reasonably human readable but that's just a happy
coincidence and because we document and format the heck out of it.

> while expanding the QMP output
> to be verbose for machine use, is probably what we'll have to live with.
>  While it is easy to document shortcuts that the qapi parser can use
> when converting .json to code, it is harder to require that all other
> QMP clients must implement those same shortcuts, instead of having
> things already directly represented.

qapi-schema.json is valid JSON except for the # style comments.  Those
are trivial to remove and we could simply post process it.

Heck, if you use json_parser to parse the file, you'd end up with a list
of QObjects that could be returned directly via QMP.

I don't understand what all this talk of "post-processing" is.

Regards,

Anthony Liguori

>
> -- 
> Eric Blake   eblake redhat com    +1-919-301-3266
> Libvirt virtualization library http://libvirt.org
Anthony Liguori July 2, 2013, 6:28 p.m. UTC | #17
Eric Blake <eblake@redhat.com> writes:

> On 07/02/2013 11:06 AM, Anthony Liguori wrote:
>>> Because qapi-schema.json requires further parsing.  For example, how is
>>> a client supposed to know that '*foo':'int' means that there is an
>>> argument named 'foo' but it is optional?  The rule of thumb with QMP is
>>> that if you have to post-process JSON output, then the JSON was not
>>> designed correctly.
>> 
>> Then we should fix qapi-schema.json.
>> 
>
>> One reasonable compromise would be:
>> 
>> { "command": "foo", "arguments": { "name": "str", "id": "int" },
>>                     "optional": { "bar": "bool" } }
>> 
>> Then libvirt only has to test 'does command["optional"] contain key
>> "bar"' to test for an optional parameter.
>
> Yes, that might be a reasonable compromise - at least that way, the
> optional parameter names are listed directly, and libvirt doesn't have
> to probe every single parameter to name to see which begin with '*'.
>
>> 
>> Although to be honest, '*bar' in command['arguments'] is reasonable too
>> IMHO.
>
> It may be reasonable, but it forces every QMP client to reimplement the
> post-processing step, rather than presenting the parameter names already
> in isolation.
>
> Food for thought - suppose we wanted to start expressing in the .json
> file what the default value is for any parameter that is listed as
> optional?  What representation is most compact for that purpose, while
> still being valid JSON and not requiring QMP clients to reimplement
> post-processing?

We won't do that.  It would be way too hard to retrofit it for very
little value.

Regards,

Anthony Liguori

>
> -- 
> Eric Blake   eblake redhat com    +1-919-301-3266
> Libvirt virtualization library http://libvirt.org
Paolo Bonzini July 2, 2013, 8 p.m. UTC | #18
Il 02/07/2013 20:21, Anthony Liguori ha scritto:
>> >
>> > Indeed, I wouldn't mind a one-time pass over qapi-schema.json to make it
>> > follow a more rigid format if that made it easier to use it as-is with
>> > less post-processing.  It won't be very nice to backport such a
>> > conversion, but I don't know how much distros are planning on
>> > backporting introspection in the first place.
> We consume the schema in QEMU.  No reason for us to consume it in a
> different format than libvirt.

One reason could be that qapi-schema.json, as written, lacks a schema
that can be expressed itself using QAPI.

Paolo
Eric Blake July 2, 2013, 8:08 p.m. UTC | #19
On 07/02/2013 02:00 PM, Paolo Bonzini wrote:
> Il 02/07/2013 20:21, Anthony Liguori ha scritto:
>>>>
>>>> Indeed, I wouldn't mind a one-time pass over qapi-schema.json to make it
>>>> follow a more rigid format if that made it easier to use it as-is with
>>>> less post-processing.  It won't be very nice to backport such a
>>>> conversion, but I don't know how much distros are planning on
>>>> backporting introspection in the first place.
>> We consume the schema in QEMU.  No reason for us to consume it in a
>> different format than libvirt.
> 
> One reason could be that qapi-schema.json, as written, lacks a schema
> that can be expressed itself using QAPI.

Indeed - it was my attempt to write a structured set of QAPI that led to
the more rigid and more verbose layout, rather than shorthand.  On the
other hand, there's nothing stopping us from stating that QAPI itself
can be extended to express new concepts (after all, we want to express
the full structure of events as part of qapi-schema.json someday); we
even have the 'qmp_capabilities' handshake to advertise whether use of
such extensions will be understood by the client.
Anthony Liguori July 2, 2013, 8:58 p.m. UTC | #20
Paolo Bonzini <pbonzini@redhat.com> writes:

> Il 02/07/2013 20:21, Anthony Liguori ha scritto:
>>> >
>>> > Indeed, I wouldn't mind a one-time pass over qapi-schema.json to make it
>>> > follow a more rigid format if that made it easier to use it as-is with
>>> > less post-processing.  It won't be very nice to backport such a
>>> > conversion, but I don't know how much distros are planning on
>>> > backporting introspection in the first place.
>> We consume the schema in QEMU.  No reason for us to consume it in a
>> different format than libvirt.
>
> One reason could be that qapi-schema.json, as written, lacks a schema
> that can be expressed itself using QAPI.

Yup, but how much does that matter in practice?

At any rate, if we wanted to solve this problem--a self-describing
schema--we should do it in qapi-schema.json too.

Regards,

Anthony Liguori

>
> Paolo
Paolo Bonzini July 3, 2013, 5:52 a.m. UTC | #21
Il 02/07/2013 22:58, Anthony Liguori ha scritto:
> > > We consume the schema in QEMU.  No reason for us to consume it in a
> > > different format than libvirt.
> >
> > One reason could be that qapi-schema.json, as written, lacks a schema
> > that can be expressed itself using QAPI.
>
> Yup, but how much does that matter in practice?

It matters little because we do not provide a library of QAPI
parsers/visitors, so clients have to invent their own anyway.

But if we did, clients would be completely oblivious of the fact that
QMP is based on JSON.  Sending qapi-schema.json down the wire as a JSON
string would break the abstraction that we provide to the clients.

> At any rate, if we wanted to solve this problem--a self-describing
> schema--we should do it in qapi-schema.json too.

I disagree.  I also disagree that qapi-schema.json, as written, is a
format designed for machine consumption.

So, qapi-schema.json has to be readable/writable _mostly_ by humans.
That it is valid JSON is little more than a curious accident, because
overall the syntax greatly favors humans rather than computers.  A
format designed for computers would have a schema such that no parsing
tasks (however small---I'm thinking of the "list of" and "optional"
syntaxes) would be left after parsing the JSON.

The example that Eric sent is not something that I would find easy to
read/write.  qapi-schema.json instead is more than acceptable.

Paolo
Anthony Liguori July 3, 2013, 12:54 p.m. UTC | #22
Paolo Bonzini <pbonzini@redhat.com> writes:

> Il 02/07/2013 22:58, Anthony Liguori ha scritto:
>> > > We consume the schema in QEMU.  No reason for us to consume it in a
>> > > different format than libvirt.
>> >
>> > One reason could be that qapi-schema.json, as written, lacks a schema
>> > that can be expressed itself using QAPI.
>>
>> Yup, but how much does that matter in practice?
>
> It matters little because we do not provide a library of QAPI
> parsers/visitors, so clients have to invent their own anyway.
>
> But if we did, clients would be completely oblivious of the fact that
> QMP is based on JSON.  Sending qapi-schema.json down the wire as a JSON
> string would break the abstraction that we provide to the clients.
>
>> At any rate, if we wanted to solve this problem--a self-describing
>> schema--we should do it in qapi-schema.json too.
>
> I disagree.  I also disagree that qapi-schema.json, as written, is a
> format designed for machine consumption.
>
> So, qapi-schema.json has to be readable/writable _mostly_ by humans.
> That it is valid JSON is little more than a curious accident, because

I can assure you that it wasn't an accident.  The plan had been to start
with what the output of a "human friendly" parser would be and then
eventually introduce a more IDL like syntax.

qapi-schema.json is valid JSON.  It's a stream of objects.  It's a
stream of objects instead of a list to favor readability but that's
really the only compromise.

The only reason we don't use json.loads() is because we want to provide
stable ordering for generated command line arguments and struct
members.  We can't get that guarantee with the json module.

But this doesn't matter for a client.  QMP doesn't have a notion of
argument ordering.

> overall the syntax greatly favors humans rather than computers.  A
> format designed for computers would have a schema such that no parsing
> tasks (however small---I'm thinking of the "list of" and "optional"
> syntaxes) would be left after parsing the JSON.

Here is how I would handle "processing" qapi-schema.json:

1) Put all types, unions, and enums in their own dictionary
2) Put commands in a dictionary

To answer:

A) Is 'type' valid?
   - bool('type' in type_dict)

B) Does 'type' have optional parameter 'foo':
   - bool('*foo' in type_dict['data'])

C) Does 'enum' have 'value'
   - bool('value' in enum_dict['data'])

D) Does 'command' have 'parameter'
   - bool('parameter' in command_dict['data'])

Now we could certainly return dictionaries instead of a list but that's
a trivial post-processing step.

> The example that Eric sent is not something that I would find easy to
> read/write.  qapi-schema.json instead is more than acceptable.

I don't think the example Eric sent is any easier to parse
programmatically.  That's the problem I have here.  I don't see why we
can't have both a human readable and machine readable syntax.
Furthermore, qapi.py is an existence proof that we do :-)

Regards,

Anthony Liguori

>
> Paolo
Paolo Bonzini July 3, 2013, 2:45 p.m. UTC | #23
Il 03/07/2013 14:54, Anthony Liguori ha scritto:
>> So, qapi-schema.json has to be readable/writable _mostly_ by humans.
>> That it is valid JSON is little more than a curious accident, because
> 
> I can assure you that it wasn't an accident.

Sure, it is not.  But when designing the right API for a QMP client, it
doesn't matter if it is or not.  If QMP used ASN.1 or something like
that as the wire protocol, we would not use JSON just for the schema,
would we?

> The plan had been to start
> with what the output of a "human friendly" parser would be and then
> eventually introduce a more IDL like syntax.
> 
> qapi-schema.json is valid JSON.  It's a stream of objects.  It's a
> stream of objects instead of a list to favor readability but that's
> really the only compromise.

So far so good.

>> overall the syntax greatly favors humans rather than computers.  A
>> format designed for computers would have a schema such that no parsing
>> tasks (however small---I'm thinking of the "list of" and "optional"
>> syntaxes) would be left after parsing the JSON.
> 
> Here is how I would handle "processing" qapi-schema.json:
> 
> 1) Put all types, unions, and enums in their own dictionary
> 2) Put commands in a dictionary

Agreed.

> To answer:
> 
> A) Is 'type' valid?
>    - bool('type' in type_dict)
> 
> B) Does 'type' have optional parameter 'foo':
>    - bool('*foo' in type_dict['data'])

Does 'type' have argument 'foo':

   bool('foo' in type_dict['data']) or
     bool('*foo' in type_dict['data'])

(as a QMP client I want to send the argument, I don't care if it is
optional or not) and here the abstraction is already falling, IMHO.  It
should be one of these:

  'foo' in type_dict['data'].fields
  'foo' in type_dict['data']['fields']

> C) Does 'enum' have 'value'
>    - bool('value' in enum_dict['data'])
> 
> D) Does 'command' have 'parameter'
>    - bool('parameter' in command_dict['data'])

What is the type of 'parameter' in command:

    command_dict['data']['parameter'] or
       command_dict['data']['*parameter']

It should be something like these:

    command_dict['data'].arguments['parameter'].type
    command_dict['data']['arguments']['parameter']['type']

>> The example that Eric sent is not something that I would find easy to
>> read/write.  qapi-schema.json instead is more than acceptable.
> 
> I don't think the example Eric sent is any easier to parse
> programmatically.

It is, see the above examples.

> That's the problem I have here.  I don't see why we
> can't have both a human readable and machine readable syntax.

It is machine readable, but that doesn't mean it constitutes a nice API.

Paolo

> Furthermore, qapi.py is an existence proof that we do :-)
> 
> Regards,
> 
> Anthony Liguori
> 
>>
>> Paolo
>
Kevin Wolf July 3, 2013, 3:08 p.m. UTC | #24
Am 02.07.2013 um 19:06 hat Anthony Liguori geschrieben:
> Eric Blake <eblake@redhat.com> writes:
> > On 07/02/2013 08:51 AM, Anthony Liguori wrote:
> >> 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?

I know you don't agree with this, but as I mentioned several times
before, I think the schema as returned by the introspection functions
shouldn't contain what a qemu of this version _could_ in theory provide,
but what this specific build actually _does_ provide. It shouldn't
include things that are compiled out.

> > I've also been the one arguing that the additional complexity (an array of
> > {"name":"str","type":"str","optional":bool"}) is better for libvirt in
> > that the JSON is then well-suited for scanning (it is easier to scan
> > through an array where the key is a constant "name", and looking for the
> > value that we are interested in, than it is to scan through a dictionary
> > where the keys of the dictionary are the names we are interested in).
> > That is, the JSON in qapi-schema.json is a nice compact representation
> > that works for humans, but may be a bit TOO compact for handling via
> > machines.
> 
> But adding a bunch of code to do JSON translation just adds a bunch of
> additional complexity.
> 
> One reasonable compromise would be:
> 
> { "command": "foo", "arguments": { "name": "str", "id": "int" },
>                     "optional": { "bar": "bool" } }

This assumes that optional vs. mandatory is the only property we ever
want to describe for fields. Eric's approach is much more future-proof.
Let's keep the format of qapi-schema.json an implementation detail that
we can change and extend when necessary.

Kevin
Anthony Liguori July 3, 2013, 3:59 p.m. UTC | #25
Kevin Wolf <kwolf@redhat.com> writes:

> Am 02.07.2013 um 19:06 hat Anthony Liguori geschrieben:
>> Eric Blake <eblake@redhat.com> writes:
>> > On 07/02/2013 08:51 AM, Anthony Liguori wrote:
>> >> 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?
>
> I know you don't agree with this, but as I mentioned several times
> before, I think the schema as returned by the introspection functions
> shouldn't contain what a qemu of this version _could_ in theory provide,
> but what this specific build actually _does_ provide. It shouldn't
> include things that are compiled out.

I really don't disagree with you here.  I just don't like having two
formats for the schema.

>> > I've also been the one arguing that the additional complexity (an array of
>> > {"name":"str","type":"str","optional":bool"}) is better for libvirt in
>> > that the JSON is then well-suited for scanning (it is easier to scan
>> > through an array where the key is a constant "name", and looking for the
>> > value that we are interested in, than it is to scan through a dictionary
>> > where the keys of the dictionary are the names we are interested in).
>> > That is, the JSON in qapi-schema.json is a nice compact representation
>> > that works for humans, but may be a bit TOO compact for handling via
>> > machines.
>> 
>> But adding a bunch of code to do JSON translation just adds a bunch of
>> additional complexity.
>> 
>> One reasonable compromise would be:
>> 
>> { "command": "foo", "arguments": { "name": "str", "id": "int" },
>>                     "optional": { "bar": "bool" } }
>
> This assumes that optional vs. mandatory is the only property we ever
> want to describe for fields. Eric's approach is much more future-proof.
> Let's keep the format of qapi-schema.json an implementation detail that
> we can change and extend when necessary.

It's always possible to add another argument that describes additional
information.

For instance:

{ "command": "foo",
  "arguments": { "name": "str", "id": "int" },
  "optional": { "bar": "bool" },
  "defaults": { "bar": false } }

That doesn't mean I think exposing defaults is good, but rather that
it's still possible to do this in a compact form.

Regards,

Anthony Liguori

>
> Kevin
Anthony Liguori July 3, 2013, 4:06 p.m. UTC | #26
Paolo Bonzini <pbonzini@redhat.com> writes:

> Il 03/07/2013 14:54, Anthony Liguori ha scritto:
>>> So, qapi-schema.json has to be readable/writable _mostly_ by humans.
>>> That it is valid JSON is little more than a curious accident, because
>> 
>> I can assure you that it wasn't an accident.
>
> Sure, it is not.  But when designing the right API for a QMP client, it
> doesn't matter if it is or not.  If QMP used ASN.1 or something like
> that as the wire protocol, we would not use JSON just for the schema,
> would we?

JSON is a pretty good representation of Python data structures and the
intention was for qapi-schema.json to be generated by another tool.

But I understand the point you're trying to make.  The thing is, QMP is
JSON now so it's somewhat academic.

>> The plan had been to start
>> with what the output of a "human friendly" parser would be and then
>> eventually introduce a more IDL like syntax.
>> 
>> qapi-schema.json is valid JSON.  It's a stream of objects.  It's a
>> stream of objects instead of a list to favor readability but that's
>> really the only compromise.
>
> So far so good.
>
>>> overall the syntax greatly favors humans rather than computers.  A
>>> format designed for computers would have a schema such that no parsing
>>> tasks (however small---I'm thinking of the "list of" and "optional"
>>> syntaxes) would be left after parsing the JSON.
>> 
>> Here is how I would handle "processing" qapi-schema.json:
>> 
>> 1) Put all types, unions, and enums in their own dictionary
>> 2) Put commands in a dictionary
>
> Agreed.
>
>> To answer:
>> 
>> A) Is 'type' valid?
>>    - bool('type' in type_dict)
>> 
>> B) Does 'type' have optional parameter 'foo':
>>    - bool('*foo' in type_dict['data'])
>
> Does 'type' have argument 'foo':
>
>    bool('foo' in type_dict['data']) or
>      bool('*foo' in type_dict['data'])
>
> (as a QMP client I want to send the argument, I don't care if it is
> optional or not) and here the abstraction is already falling, IMHO.  It
> should be one of these:

Whether 'type' is in 'foo' is a static property.  We would never add
non-optional arguments to a function so the first part of the clause is
a constant expression.

>   'foo' in type_dict['data'].fields
>   'foo' in type_dict['data']['fields']

Not sure I see why the extra layer of indirection is needed.

>
>> C) Does 'enum' have 'value'
>>    - bool('value' in enum_dict['data'])
>> 
>> D) Does 'command' have 'parameter'
>>    - bool('parameter' in command_dict['data'])
>
> What is the type of 'parameter' in command:
>
>     command_dict['data']['parameter'] or
>        command_dict['data']['*parameter']

That's a fair point.  But again, this is a constant expression.  Type
values never change.

What are we really optimizing here for?  What are the things that
someone is expected to use the schema to do in real life?

Regards,

Anthony Liguori

> It should be something like these:
>
>     command_dict['data'].arguments['parameter'].type
>     command_dict['data']['arguments']['parameter']['type']
>
>>> The example that Eric sent is not something that I would find easy to
>>> read/write.  qapi-schema.json instead is more than acceptable.
>> 
>> I don't think the example Eric sent is any easier to parse
>> programmatically.
>
> It is, see the above examples.
>
>> That's the problem I have here.  I don't see why we
>> can't have both a human readable and machine readable syntax.
>
> It is machine readable, but that doesn't mean it constitutes a nice API.
>
> Paolo
>
>> Furthermore, qapi.py is an existence proof that we do :-)
>> 
>> Regards,
>> 
>> Anthony Liguori
>> 
>>>
>>> Paolo
>>
Amos Kong July 4, 2013, 3:54 a.m. UTC | #27
On Tue, Jul 02, 2013 at 01:27:32PM -0500, Anthony Liguori wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
> > On 07/02/2013 11:01 AM, Paolo Bonzini wrote:
> >>>> Arguably that rule of thumb would apply equally to the QEMU
> >>>> build scripts which already parse qapi-schema.json. It could
> >>>> be possible to normalize qapi-schema.json somewhat to remove
> >>>> this 2-stage parsing if we went down this route.
> >>>
> >>> Indeed, I wouldn't mind a one-time pass over qapi-schema.json to make it
> >>> follow a more rigid format if that made it easier to use it as-is with
> >>> less post-processing.  It won't be very nice to backport such a
> >>> conversion, but I don't know how much distros are planning on
> >>> backporting introspection in the first place.
> >> 
> >> How would the schema look like after this "one-time pass"?
> >> 
> >> This:
> >> 
> >>> [
> >>>          { "name": "protocol",
> >>>            "type": "str" },
> >>>          { "name": "fdname",
> >>>            "type": "str" },
> >>>          { "name": "skipauth",
> >>>            "type": "bool",
> >>>            "optional": true },
> >>>          { "name": "tls",
> >>>            "type": "bool",
> >>>            "optional": true }
> >>> ]
> >> 
> >> Looks quite awful for a human to write and read.
> >
> > Which puts us back in favor of my original argument that keeping
> > qapi-schema.json compact for human use,
> 
> qapi-schema.json is not "for human use".  It's consumed by QAPI.  It
> happens to be reasonably human readable but that's just a happy
> coincidence and because we document and format the heck out of it.
> 
> > while expanding the QMP output
> > to be verbose for machine use, is probably what we'll have to live with.
> >  While it is easy to document shortcuts that the qapi parser can use
> > when converting .json to code, it is harder to require that all other
> > QMP clients must implement those same shortcuts, instead of having
> > things already directly represented.
> 
> qapi-schema.json is valid JSON except for the # style comments.  Those
> are trivial to remove and we could simply post process it.
> 
> Heck, if you use json_parser to parse the file, you'd end up with a list
> of QObjects that could be returned directly via QMP.
 
I like this solution, but QObject is not a defined type in
qapi-schema.json, so we can't assign qobj to return data.
We also need to do "post-processing" to provide prolific
meta-data to management.

# { 'command': 'query-qmp-schema', 'returns': ['QObject'] }

> I don't understand what all this talk of "post-processing" is.

When management gets the raw jaso string, they could not directly use,
post-processing might be:
1) revert the whole schema by itself, replace defined type by complex json
2) process the '*' before typename
3) process json string to get type(command/event/type...),name(command
name, type name, event name), data, returns one by one.
4) etc

> Regards,
> 
> Anthony Liguori
Kevin Wolf July 4, 2013, 7:42 a.m. UTC | #28
Am 03.07.2013 um 17:59 hat Anthony Liguori geschrieben:
> Kevin Wolf <kwolf@redhat.com> writes:
> 
> > Am 02.07.2013 um 19:06 hat Anthony Liguori geschrieben:
> >> Eric Blake <eblake@redhat.com> writes:
> >> > On 07/02/2013 08:51 AM, Anthony Liguori wrote:
> >> >> 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?
> >
> > I know you don't agree with this, but as I mentioned several times
> > before, I think the schema as returned by the introspection functions
> > shouldn't contain what a qemu of this version _could_ in theory provide,
> > but what this specific build actually _does_ provide. It shouldn't
> > include things that are compiled out.
> 
> I really don't disagree with you here.  I just don't like having two
> formats for the schema.

So you agree that we have to postprocess at least in the sense that we
leave out things that aren't available?

In this case, I think you already have most of the postprocessing code
(and this diffstat of this patch seems to show that it's not that much
code anyway), so code size isn't a valid point any more. Then we can
concentrate on getting the optimal wire format and do whatever is needed
to implement it.

> >> > I've also been the one arguing that the additional complexity (an array of
> >> > {"name":"str","type":"str","optional":bool"}) is better for libvirt in
> >> > that the JSON is then well-suited for scanning (it is easier to scan
> >> > through an array where the key is a constant "name", and looking for the
> >> > value that we are interested in, than it is to scan through a dictionary
> >> > where the keys of the dictionary are the names we are interested in).
> >> > That is, the JSON in qapi-schema.json is a nice compact representation
> >> > that works for humans, but may be a bit TOO compact for handling via
> >> > machines.
> >> 
> >> But adding a bunch of code to do JSON translation just adds a bunch of
> >> additional complexity.
> >> 
> >> One reasonable compromise would be:
> >> 
> >> { "command": "foo", "arguments": { "name": "str", "id": "int" },
> >>                     "optional": { "bar": "bool" } }
> >
> > This assumes that optional vs. mandatory is the only property we ever
> > want to describe for fields. Eric's approach is much more future-proof.
> > Let's keep the format of qapi-schema.json an implementation detail that
> > we can change and extend when necessary.
> 
> It's always possible to add another argument that describes additional
> information.
> 
> For instance:
> 
> { "command": "foo",
>   "arguments": { "name": "str", "id": "int" },
>   "optional": { "bar": "bool" },
>   "defaults": { "bar": false } }
> 
> That doesn't mean I think exposing defaults is good, but rather that
> it's still possible to do this in a compact form.

Yeah, it's possible, but it feels kind of backwards to have the
properties on the top level and repeat the field names for each property
that they have.

How does it work for nested structs? There you don't have the
"arguments" substructure, so you'd have to have "optional" as a child of
all the other fields or something like that. It becomes ugly quite
quickly.

Kevin
Paolo Bonzini July 4, 2013, 7:53 a.m. UTC | #29
Il 03/07/2013 18:06, Anthony Liguori ha scritto:
> Paolo Bonzini <pbonzini@redhat.com> writes:
> 
>> Il 03/07/2013 14:54, Anthony Liguori ha scritto:
>>>> So, qapi-schema.json has to be readable/writable _mostly_ by humans.
>>>> That it is valid JSON is little more than a curious accident, because
>>>
>>> I can assure you that it wasn't an accident.
>>
>> Sure, it is not.  But when designing the right API for a QMP client, it
>> doesn't matter if it is or not.  If QMP used ASN.1 or something like
>> that as the wire protocol, we would not use JSON just for the schema,
>> would we?
> 
> JSON is a pretty good representation of Python data structures and the
> intention was for qapi-schema.json to be generated by another tool.
> 
> But I understand the point you're trying to make.  The thing is, QMP is
> JSON now so it's somewhat academic.

If we generated a Python or C API based on the schema, should the client
care (or know) that QMP is JSON?

>> Does 'type' have argument 'foo':
>>
>>    bool('foo' in type_dict['data']) or
>>      bool('*foo' in type_dict['data'])
>>
>> (as a QMP client I want to send the argument, I don't care if it is
>> optional or not) and here the abstraction is already falling, IMHO.  It
>> should be one of these:
> 
> Whether 'type' is in 'foo' is a static property.  We would never add
> non-optional arguments to a function so the first part of the clause is
> a constant expression.

What about returned types?  I'm not sure we've never added non-optional
arguments, even though in principle it was not the right thing to do.

>>> C) Does 'enum' have 'value'
>>>    - bool('value' in enum_dict['data'])
>>>
>>> D) Does 'command' have 'parameter'
>>>    - bool('parameter' in command_dict['data'])
>>
>> What is the type of 'parameter' in command:
>>
>>     command_dict['data']['parameter'] or
>>        command_dict['data']['*parameter']
> 
> That's a fair point.  But again, this is a constant expression.  Type
> values never change.

Not necessarily, a type that is currently used in two places can be
split in two different types, with different optional fields.

I understand though that command_dict['data']['parameter'] is either
always true or always false, because new parameters are always added as
optional.  Still, for something that targets a new-enough QEMU only,
there is no need to know if the parameter has always been there, or was
added as optional.

> What are we really optimizing here for?

I think we should optimize for the clients, not for ourselves.

Paolo

> Regards,
> 
> Anthony Liguori
> 
>> It should be something like these:
>>
>>     command_dict['data'].arguments['parameter'].type
>>     command_dict['data']['arguments']['parameter']['type']
>>
>>>> The example that Eric sent is not something that I would find easy to
>>>> read/write.  qapi-schema.json instead is more than acceptable.
>>>
>>> I don't think the example Eric sent is any easier to parse
>>> programmatically.
>>
>> It is, see the above examples.
>>
>>> That's the problem I have here.  I don't see why we
>>> can't have both a human readable and machine readable syntax.
>>
>> It is machine readable, but that doesn't mean it constitutes a nice API.
>>
>> Paolo
>>
>>> Furthermore, qapi.py is an existence proof that we do :-)
>>>
>>> Regards,
>>>
>>> Anthony Liguori
>>>
>>>>
>>>> Paolo
>>>
> 
> 
>
Paolo Bonzini July 4, 2013, 7:55 a.m. UTC | #30
Il 03/07/2013 17:59, Anthony Liguori ha scritto:
> For instance:
> 
> { "command": "foo",
>   "arguments": { "name": "str", "id": "int" },
>   "optional": { "bar": "bool" },
>   "defaults": { "bar": false } }

This is still not a dictionary that QAPI is able to describe.

Paolo
Amos Kong July 11, 2013, 1:37 p.m. UTC | #31
On Wed, Jul 03, 2013 at 07:54:47AM -0500, Anthony Liguori wrote:
> Paolo Bonzini <pbonzini@redhat.com> writes:
> 
> > Il 02/07/2013 22:58, Anthony Liguori ha scritto:
> >> > > We consume the schema in QEMU.  No reason for us to consume it in a
> >> > > different format than libvirt.
> >> >
> >> > One reason could be that qapi-schema.json, as written, lacks a schema
> >> > that can be expressed itself using QAPI.
> >>
> >> Yup, but how much does that matter in practice?
> >
> > It matters little because we do not provide a library of QAPI
> > parsers/visitors, so clients have to invent their own anyway.
> >
> > But if we did, clients would be completely oblivious of the fact that
> > QMP is based on JSON.  Sending qapi-schema.json down the wire as a JSON
> > string would break the abstraction that we provide to the clients.
> >
> >> At any rate, if we wanted to solve this problem--a self-describing
> >> schema--we should do it in qapi-schema.json too.
> >
> > I disagree.  I also disagree that qapi-schema.json, as written, is a
> > format designed for machine consumption.
> >
> > So, qapi-schema.json has to be readable/writable _mostly_ by humans.
> > That it is valid JSON is little more than a curious accident, because
> 
> I can assure you that it wasn't an accident.  The plan had been to start
> with what the output of a "human friendly" parser would be and then
> eventually introduce a more IDL like syntax.
> 
> qapi-schema.json is valid JSON.  It's a stream of objects.  It's a
> stream of objects instead of a list to favor readability but that's
> really the only compromise.
> 
> The only reason we don't use json.loads() is because we want to provide
> stable ordering for generated command line arguments and struct
> members.  We can't get that guarantee with the json module.
> 
> But this doesn't matter for a client.  QMP doesn't have a notion of
> argument ordering.
> 
> > overall the syntax greatly favors humans rather than computers.  A
> > format designed for computers would have a schema such that no parsing
> > tasks (however small---I'm thinking of the "list of" and "optional"
> > syntaxes) would be left after parsing the JSON.
> 
> Here is how I would handle "processing" qapi-schema.json:
> 
> 1) Put all types, unions, and enums in their own dictionary
> 2) Put commands in a dictionary

We need to parse the json strings in qapi-schema.json, and
generate a dynamical & nested dictionary. When qmp clients
get the dictionary, they have to parse the dictionary's values
layer by layer. If we provide the meta-data, it will be easier.

meta-data:
  the type of dictionary's value (dictionary, list, string)
  if it's optional


Eric, can you give some input? If Anthony's suggestion satisfies
your request?

However, I will send my latest version out latter. let's discuss
on it.
 
> To answer:
> 
> A) Is 'type' valid?
>    - bool('type' in type_dict)
> 
> B) Does 'type' have optional parameter 'foo':
>    - bool('*foo' in type_dict['data'])
> 
> C) Does 'enum' have 'value'
>    - bool('value' in enum_dict['data'])
> 
> D) Does 'command' have 'parameter'
>    - bool('parameter' in command_dict['data'])
> 
> Now we could certainly return dictionaries instead of a list but that's
> a trivial post-processing step.
> 
> > The example that Eric sent is not something that I would find easy to
> > read/write.  qapi-schema.json instead is more than acceptable.
> 
> I don't think the example Eric sent is any easier to parse
> programmatically.  That's the problem I have here.  I don't see why we
> can't have both a human readable and machine readable syntax.
> Furthermore, qapi.py is an existence proof that we do :-)
> 
> Regards,
> 
> Anthony Liguori
> 
> >
> > Paolo
Amos Kong July 16, 2013, 10:52 a.m. UTC | #32
On Tue, Jul 02, 2013 at 10:20:39AM -0400, Luiz Capitulino wrote:
> On Tue, 2 Jul 2013 16:37:29 +0800
> Amos Kong <akong@redhat.com> wrote:
> 
> > > Btw, I find your approach interesting but I'm wondering if it's going to
> > > be a good thing to keep all the schema in memory. Do you have an idea
> > > on its size?
> > 
> > The size of qmp_schema_table[] is 1528 bytes.
> > 
> > method 2rd:
> >   save the raw json strings to qmp-schema.txt, allocate dynamical
> >   qmp_schema_table[] in qmp_query_qmp_schema(), and restore the
> >   file content to the table, free it at the end of qmp_query_qmp_schema()
> 
> Well, keeping a file around like that doesn't look like a good solution.

If we keep it in a file, we need to install the file to system.
If the file is changed after installing, it's risk.
 
> Let's do your approach for now. If having this in memory turns out to
> be a problem, we could try compacting it.

Ok, I will still define a big table in head file.
Current 1528 bytes is not very big.
diff mbox

Patch

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: