Message ID | 1371644677-11041-1-git-send-email-akong@redhat.com |
---|---|
State | New |
Headers | show |
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" } ] }
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" } ] } ] } ] } ] }
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:
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)
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.
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
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.
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
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" } ]
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.
> > 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
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
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.
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?
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
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
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
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
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.
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
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
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
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 >
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
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
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 >>
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
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
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 >>> > > >
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
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
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 --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:
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(-)