QAPI patches for 2017-01-16

-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJYfJHZAAoJEDhwtADrkYZTrtsP/RRtEvFcNt267iQ54OqJxjLc
 on8YfbQsZa0PO+JfvfMBpQpoLOZpOmNIyC3YqWRftxpcB6rDXTgFCqsKjo0P2Czg
 MbPMJ8XOzzP3NmrJkenPW7d9ObvQxfUHQYiyEobIvmVIhREqsxbbLSYZw5MO1n5X
 Pe5pqmxSBi1rlw61G6uhk5x/6Xzo8fFlG46VnGsarfWZe+5q59VAoXDfs4nRxF9K
 fD/Nm5DjTszNc3YF8VteHnr+IlAGPUbbLJ2/mvnlCWcMlmVmzoSivBhi0PF7giG2
 qb1+p8b24s1OdpbkoN5b/K/naRA/n4WLp+pY/LJX4owWXOnRnysRuqCrfc4E+avP
 Mp0TatbSgfDamnF2LTdgkKDsjRniomxgWNDwyQbvWl1Z8PPgEGwyz8AQP8hjVfkj
 vJARYmUwqq+CoQq0mOmmLlN9WxcaqTw2iiBteNWwuNfntc0xSsiUrmbJuk7n9DOO
 f8cAjFwLq4/0mxHxZkGEMzDrOKvpIN7m8pS0I1gcJv42prV+5dkgO0ESe5xkfa8w
 djfjWv3lxyS41AMrJ7kIH1t2jYifhgWXnraAOR9Sc2Ipsb/I+3aBKbAjgFBoRVXt
 38i65fAlYfJuUUUJpqypapWNA4ILzJMYly7SfaASWp64Ic5p8q0/y0WBLuUVaeZM
 tVNbdSeCobEyLSmniYXN
 =+fD/
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2017-01-16' into staging

QAPI patches for 2017-01-16

# gpg: Signature made Mon 16 Jan 2017 09:26:49 GMT
# gpg:                using RSA key 0x3870B400EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>"
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-qapi-2017-01-16: (180 commits)
  build-sys: add qapi doc generation targets
  build-sys: add txt documentation rules
  build-sys: use a generic TEXI2MAN rule
  build-sys: remove dvi doc generation
  build-sys: use --no-split for info
  docs: add qemu logo to pdf
  qapi: add qapi2texi script
  qmp-events: move 'MIGRATION_PASS' doc to schema
  qmp-events: move 'DUMP_COMPLETED' doc to schema
  qmp-events: move 'MEM_UNPLUG_ERROR' doc to schema
  qmp-events: move 'VSERPORT_CHANGE' doc to schema
  qmp-events: move 'QUORUM_REPORT_BAD' doc to schema
  qmp-events: move 'QUORUM_FAILURE' doc to schema
  qmp-events: move 'GUEST_PANICKED' doc to schema
  qmp-events: move 'BALLOON_CHANGE' doc to schema
  qmp-events: move 'ACPI_DEVICE_OST' doc to schema
  qmp-events: move 'MIGRATION' doc to schema
  qmp-events: move 'SPICE_MIGRATE_COMPLETED' doc to schema
  qmp-events: move 'SPICE_DISCONNECTED' doc to schema
  qmp-events: move 'SPICE_INITIALIZED' doc to schema
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2017-01-17 13:53:50 +00:00
commit 23eb9e6b6d
297 changed files with 4751 additions and 5013 deletions

11
.gitignore vendored
View File

@ -40,6 +40,7 @@
/qmp-marshal.c
/qemu-doc.html
/qemu-doc.info
/qemu-doc.txt
/qemu-img
/qemu-nbd
/qemu-options.def
@ -60,7 +61,6 @@
*.a
*.aux
*.cp
*.dvi
*.exe
*.msi
*.dll
@ -105,6 +105,15 @@
/pc-bios/optionrom/kvmvapic.img
/pc-bios/s390-ccw/s390-ccw.elf
/pc-bios/s390-ccw/s390-ccw.img
/docs/qemu-ga-ref.html
/docs/qemu-ga-ref.txt
/docs/qemu-qmp-ref.html
/docs/qemu-qmp-ref.txt
docs/qemu-ga-ref.info*
docs/qemu-qmp-ref.info*
/qemu-ga-qapi.texi
/qemu-qapi.texi
*.tps
.stgit-*
cscope.*
tags

View File

@ -80,8 +80,8 @@ GENERATED_HEADERS += module_block.h
Makefile: ;
configure: ;
.PHONY: all clean cscope distclean dvi html info install install-doc \
pdf recurse-all speed test dist msi FORCE
.PHONY: all clean cscope distclean html info install install-doc \
pdf txt recurse-all speed test dist msi FORCE
$(call set-vpath, $(SRC_PATH))
@ -90,7 +90,9 @@ LIBS+=-lz $(LIBS_TOOLS)
HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
ifdef BUILD_DOCS
DOCS=qemu-doc.html qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
DOCS+=docs/qemu-qmp-ref.html docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7
DOCS+=docs/qemu-ga-ref.html docs/qemu-ga-ref.txt docs/qemu-ga-ref.7
ifdef CONFIG_VIRTFS
DOCS+=fsdev/virtfs-proxy-helper.1
endif
@ -265,6 +267,7 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
gen-out-type = $(subst .,-,$(suffix $@))
qapi-py = $(SRC_PATH)/scripts/qapi.py $(SRC_PATH)/scripts/ordereddict.py
qapi-py += $(SRC_PATH)/scripts/qapi2texi.py
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h :\
$(SRC_PATH)/qga/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py $(qapi-py)
@ -387,12 +390,17 @@ distclean: clean
rm -f config-all-devices.mak config-all-disas.mak config.status
rm -f po/*.mo tests/qemu-iotests/common.env
rm -f roms/seabios/config.mak roms/vgabios/config.mak
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps qemu-doc.dvi
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps
rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys
rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp
rm -f qemu-doc.vr
rm -f config.log
rm -f linux-headers/asm
rm -f qemu-ga-qapi.texi qemu-qapi.texi
rm -f docs/qemu-qmp-ref.7 docs/qemu-ga-ref.7
rm -f docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
rm -f docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
rm -f docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
for d in $(TARGET_DIRS); do \
rm -rf $$d || exit 1 ; \
done
@ -429,10 +437,14 @@ endif
install-doc: $(DOCS)
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) $(SRC_PATH)/docs/qmp-commands.txt "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
ifdef CONFIG_POSIX
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
$(INSTALL_DATA) docs/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
ifneq ($(TOOLS),)
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
@ -440,6 +452,9 @@ ifneq ($(TOOLS),)
endif
ifneq (,$(findstring qemu-ga,$(TOOLS)))
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
$(INSTALL_DATA) docs/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
endif
endif
ifdef CONFIG_VIRTFS
@ -527,21 +542,23 @@ ui/console-gl.o: $(SRC_PATH)/ui/console-gl.c \
ui/shader/texture-blit-vert.h ui/shader/texture-blit-frag.h
# documentation
MAKEINFO=makeinfo
MAKEINFOFLAGS=--no-headers --no-split --number-sections
TEXIFLAG=$(if $(V),,--quiet)
%.dvi: %.texi
$(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<,"GEN","$@")
MAKEINFO=makeinfo -D 'VERSION $(VERSION)'
MAKEINFOFLAGS=--no-split --number-sections
TEXIFLAG=$(if $(V),,--quiet) --command='@set VERSION $(VERSION)'
%.html: %.texi
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --html $< -o $@, \
"GEN","$@")
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
--html $< -o $@,"GEN","$@")
%.info: %.texi
$(call quiet-command,$(MAKEINFO) $< -o $@,"GEN","$@")
$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) $< -o $@,"GEN","$@")
%.txt: %.texi
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --no-headers \
--plaintext $< -o $@,"GEN","$@")
%.pdf: %.texi
$(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<,"GEN","$@")
$(call quiet-command,texi2pdf $(TEXIFLAG) -I $(SRC_PATH) -I . $< -o $@,"GEN","$@")
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
@ -555,47 +572,36 @@ qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxt
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
qemu-qapi.texi: $(qapi-modules) $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN" "$@")
qemu-ga-qapi.texi: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi2texi.py $< > $@,"GEN","$@")
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
$(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \
$(POD2MAN) --section=1 --center=" " --release=" " qemu.pod > $@, \
"GEN","$@")
qemu.1: qemu-option-trace.texi
qemu-img.1: qemu-img.texi qemu-option-trace.texi qemu-img-cmds.texi
$(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-img.pod && \
$(POD2MAN) --section=1 --center=" " --release=" " qemu-img.pod > $@, \
"GEN","$@")
fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
$(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< fsdev/virtfs-proxy-helper.pod && \
$(POD2MAN) --section=1 --center=" " --release=" " fsdev/virtfs-proxy-helper.pod > $@, \
"GEN","$@")
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
$(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-nbd.pod && \
$(POD2MAN) --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \
"GEN","$@")
qemu-ga.8: qemu-ga.texi
$(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-ga.pod && \
$(POD2MAN) --section=8 --center=" " --release=" " qemu-ga.pod > $@, \
"GEN","$@")
dvi: qemu-doc.dvi
html: qemu-doc.html
info: qemu-doc.info
pdf: qemu-doc.pdf
html: qemu-doc.html docs/qemu-qmp-ref.html docs/qemu-ga-ref.html
info: qemu-doc.info docs/qemu-qmp-ref.info docs/qemu-ga-ref.info
pdf: qemu-doc.pdf docs/qemu-qmp-ref.pdf docs/qemu-ga-ref.pdf
txt: qemu-doc.txt docs/qemu-qmp-ref.txt docs/qemu-ga-ref.txt
qemu-doc.dvi qemu-doc.html qemu-doc.info qemu-doc.pdf: \
qemu-doc.html qemu-doc.info qemu-doc.pdf: \
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
qemu-monitor-info.texi
docs/qemu-ga-ref.dvi docs/qemu-ga-ref.html docs/qemu-ga-ref.info docs/qemu-ga-ref.pdf docs/qemu-ga-ref.txt docs/qemu-ga-ref.7: \
docs/qemu-ga-ref.texi qemu-ga-qapi.texi
docs/qemu-qmp-ref.dvi docs/qemu-qmp-ref.html docs/qemu-qmp-ref.info docs/qemu-qmp-ref.pdf docs/qemu-qmp-ref.txt docs/qemu-qmp-ref.7: \
docs/qemu-qmp-ref.texi qemu-qapi.texi
ifdef CONFIG_WIN32
INSTALLER = qemu-setup-$(VERSION)$(EXESUF)
@ -688,7 +694,7 @@ help:
@echo ' docker - Help about targets running tests inside Docker containers'
@echo ''
@echo 'Documentation targets:'
@echo ' dvi html info pdf'
@echo ' html info pdf txt'
@echo ' - Build documentation in specified format'
@echo ''
ifdef CONFIG_WIN32

2
configure vendored
View File

@ -6198,7 +6198,7 @@ fi
# build tree in object directory in case the source is not in the current directory
DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests"
DIRS="$DIRS fsdev"
DIRS="$DIRS docs fsdev"
DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw"
DIRS="$DIRS roms/seabios roms/vgabios"
DIRS="$DIRS qapi-generated"

View File

@ -44,40 +44,154 @@ Input must be ASCII (although QMP supports full Unicode strings, the
QAPI parser does not). At present, there is no place where a QAPI
schema requires the use of JSON numbers or null.
Comments are allowed; anything between an unquoted # and the following
newline is ignored. Although there is not yet a documentation
generator, a form of stylized comments has developed for consistently
documenting details about an expression and when it was added to the
schema. The documentation is delimited between two lines of ##, then
the first line names the expression, an optional overview is provided,
then individual documentation about each member of 'data' is provided,
and finally, a 'Since: x.y.z' tag lists the release that introduced
the expression. Optional members are tagged with the phrase
'#optional', often with their default value; and extensions added
after the expression was first released are also given a '(since
x.y.z)' comment. For example:
##
# @BlockStats:
#
# Statistics of a virtual block device or a block backing device.
#
# @device: #optional If the stats are for a virtual block device, the name
# corresponding to the virtual block device.
#
# @stats: A @BlockDeviceStats for the device.
#
# @parent: #optional This describes the file block device if it has one.
#
# @backing: #optional This describes the backing block device if it has one.
# (Since 2.0)
#
# Since: 0.14.0
##
{ 'struct': 'BlockStats',
'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
'*parent': 'BlockStats',
'*backing': 'BlockStats'} }
=== Comments ===
Comments are allowed; anything between an unquoted # and the following
newline is ignored.
A multi-line comment that starts and ends with a '##' line is a
documentation comment. These are parsed by the documentation
generator, which recognizes certain markup detailed below.
==== Documentation markup ====
Comment text starting with '=' is a section title:
# = Section title
Double the '=' for a subsection title:
# == Subection title
'|' denotes examples:
# | Text of the example, may span
# | multiple lines
'*' starts an itemized list:
# * First item, may span
# multiple lines
# * Second item
You can also use '-' instead of '*'.
A decimal number followed by '.' starts a numbered list:
# 1. First item, may span
# multiple lines
# 2. Second item
The actual number doesn't matter. You could even use '*' instead of
'2.' for the second item.
Lists can't be nested. Blank lines are currently not supported within
lists.
Additional whitespace between the initial '#' and the comment text is
permitted.
*foo* and _foo_ are for strong and emphasis styles respectively (they
do not work over multiple lines). @foo is used to reference a name in
the schema.
Example:
##
# = Section
# == Subsection
#
# Some text foo with *strong* and _emphasis_
# 1. with a list
# 2. like that
#
# And some code:
# | $ echo foo
# | -> do this
# | <- get that
#
##
==== Expression documentation ====
Each expression that isn't an include directive must be preceded by a
documentation block. Such blocks are called expression documentation
blocks.
The documentation block consists of a first line naming the
expression, an optional overview, a description of each argument (for
commands and events) or member (for structs, unions and alternates),
and optional tagged sections.
FIXME: the parser accepts these things in almost any order.
Optional arguments / members are tagged with the phrase '#optional',
often with their default value; and extensions added after the
expression was first released are also given a '(since x.y.z)'
comment.
A tagged section starts with one of the following words:
"Note:"/"Notes:", "Since:", "Example"/"Examples", "Returns:", "TODO:".
The section ends with the start of a new section.
A 'Since: x.y.z' tagged section lists the release that introduced the
expression.
For example:
##
# @BlockStats:
#
# Statistics of a virtual block device or a block backing device.
#
# @device: #optional If the stats are for a virtual block device, the name
# corresponding to the virtual block device.
#
# @node-name: #optional The node name of the device. (since 2.3)
#
# ... more members ...
#
# Since: 0.14.0
##
{ 'struct': 'BlockStats',
'data': {'*device': 'str', '*node-name': 'str',
... more members ... } }
##
# @query-blockstats:
#
# Query the @BlockStats for all virtual block devices.
#
# @query-nodes: #optional If true, the command will query all the
# block nodes ... explain, explain ... (since 2.3)
#
# Returns: A list of @BlockStats for each virtual block devices.
#
# Since: 0.14.0
#
# Example:
#
# -> { "execute": "query-blockstats" }
# <- {
# ... lots of output ...
# }
#
##
{ 'command': 'query-blockstats',
'data': { '*query-nodes': 'bool' },
'returns': ['BlockStats'] }
==== Free-form documentation ====
A documentation block that isn't an expression documentation block is
a free-form documentation block. These may be used to provide
additional text and structuring content.
=== Schema overview ===
The schema sets up a series of types, as well as commands and events
that will use those types. Forward references are allowed: the parser

78
docs/qemu-ga-ref.texi Normal file
View File

@ -0,0 +1,78 @@
\input texinfo
@setfilename qemu-ga-ref.info
@exampleindent 0
@paragraphindent 0
@settitle QEMU Guest Agent Protocol Reference
@iftex
@center @image{docs/qemu_logo}
@end iftex
@copying
This is the QEMU Guest Agent Protocol reference manual.
Copyright @copyright{} 2016 The QEMU Project developers
@quotation
This manual is free documentation: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 2 of the
License, or (at your option) any later version.
This manual is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this manual. If not, see http://www.gnu.org/licenses/.
@end quotation
@end copying
@dircategory QEMU
@direntry
* QEMU-GA-Ref: (qemu-ga-ref). QEMU Guest Agent Protocol Reference
@end direntry
@titlepage
@title Guest Agent Protocol Reference Manual
@subtitle QEMU version @value{VERSION}
@page
@vskip 0pt plus 1filll
@insertcopying
@end titlepage
@contents
@ifnottex
@node Top
@top QEMU Guest Agent protocol reference
@end ifnottex
@menu
* API Reference::
* Commands and Events Index::
* Data Types Index::
@end menu
@node API Reference
@chapter API Reference
@c for texi2pod:
@c man begin DESCRIPTION
@include qemu-ga-qapi.texi
@c man end
@node Commands and Events Index
@unnumbered Commands and Events Index
@printindex fn
@node Data Types Index
@unnumbered Data Types Index
@printindex tp
@bye

78
docs/qemu-qmp-ref.texi Normal file
View File

@ -0,0 +1,78 @@
\input texinfo
@setfilename qemu-qmp-ref.info
@exampleindent 0
@paragraphindent 0
@settitle QEMU QMP Reference Manual
@iftex
@center @image{docs/qemu_logo}
@end iftex
@copying
This is the QEMU QMP reference manual.
Copyright @copyright{} 2016 The QEMU Project developers
@quotation
This manual is free documentation: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 2 of the
License, or (at your option) any later version.
This manual is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this manual. If not, see http://www.gnu.org/licenses/.
@end quotation
@end copying
@dircategory QEMU
@direntry
* QEMU-QMP-Ref: (qemu-qmp-ref). QEMU QMP Reference Manual
@end direntry
@titlepage
@title QMP Reference Manual
@subtitle QEMU version @value{VERSION}
@page
@vskip 0pt plus 1filll
@insertcopying
@end titlepage
@contents
@ifnottex
@node Top
@top QEMU QMP reference
@end ifnottex
@menu
* API Reference::
* Commands and Events Index::
* Data Types Index::
@end menu
@node API Reference
@chapter API Reference
@c for texi2pod:
@c man begin DESCRIPTION
@include qemu-qapi.texi
@c man end
@node Commands and Events Index
@unnumbered Commands and Events Index
@printindex fn
@node Data Types Index
@unnumbered Data Types Index
@printindex tp
@bye

BIN
docs/qemu_logo.pdf Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,731 +0,0 @@
QEMU Machine Protocol Events
============================
ACPI_DEVICE_OST
---------------
Emitted when guest executes ACPI _OST method.
- data: ACPIOSTInfo type as described in qapi-schema.json
{ "event": "ACPI_DEVICE_OST",
"data": { "device": "d1", "slot": "0", "slot-type": "DIMM", "source": 1, "status": 0 } }
BALLOON_CHANGE
--------------
Emitted when the guest changes the actual BALLOON level. This
value is equivalent to the 'actual' field return by the
'query-balloon' command
Data:
- "actual": actual level of the guest memory balloon in bytes (json-number)
Example:
{ "event": "BALLOON_CHANGE",
"data": { "actual": 944766976 },
"timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
Note: this event is rate-limited.
BLOCK_IMAGE_CORRUPTED
---------------------
Emitted when a disk image is being marked corrupt. The image can be
identified by its device or node name. The 'device' field is always
present for compatibility reasons, but it can be empty ("") if the
image does not have a device name associated.
Data:
- "device": Device name (json-string)
- "node-name": Node name (json-string, optional)
- "msg": Informative message (e.g., reason for the corruption)
(json-string)
- "offset": If the corruption resulted from an image access, this
is the host's access offset into the image
(json-int, optional)
- "size": If the corruption resulted from an image access, this
is the access size (json-int, optional)
Example:
{ "event": "BLOCK_IMAGE_CORRUPTED",
"data": { "device": "ide0-hd0", "node-name": "node0",
"msg": "Prevented active L1 table overwrite", "offset": 196608,
"size": 65536 },
"timestamp": { "seconds": 1378126126, "microseconds": 966463 } }
BLOCK_IO_ERROR
--------------
Emitted when a disk I/O error occurs.
Data:
- "device": device name. This is always present for compatibility
reasons, but it can be empty ("") if the image does not
have a device name associated. (json-string)
- "node-name": node name. Note that errors may be reported for the root node
that is directly attached to a guest device rather than for the
node where the error occurred. (json-string)
- "operation": I/O operation (json-string, "read" or "write")
- "action": action that has been taken, it's one of the following (json-string):
"ignore": error has been ignored
"report": error has been reported to the device
"stop": the VM is going to stop because of the error
Example:
{ "event": "BLOCK_IO_ERROR",
"data": { "device": "ide0-hd1",
"node-name": "#block212",
"operation": "write",
"action": "stop" },
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
Note: If action is "stop", a STOP event will eventually follow the
BLOCK_IO_ERROR event.
BLOCK_JOB_CANCELLED
-------------------
Emitted when a block job has been cancelled.
Data:
- "type": Job type (json-string; "stream" for image streaming
"commit" for block commit)
- "device": Job identifier. Originally the device name but other
values are allowed since QEMU 2.7 (json-string)
- "len": Maximum progress value (json-int)
- "offset": Current progress value (json-int)
On success this is equal to len.
On failure this is less than len.
- "speed": Rate limit, bytes per second (json-int)
Example:
{ "event": "BLOCK_JOB_CANCELLED",
"data": { "type": "stream", "device": "virtio-disk0",
"len": 10737418240, "offset": 134217728,
"speed": 0 },
"timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
BLOCK_JOB_COMPLETED
-------------------
Emitted when a block job has completed.
Data:
- "type": Job type (json-string; "stream" for image streaming
"commit" for block commit)
- "device": Job identifier. Originally the device name but other
values are allowed since QEMU 2.7 (json-string)
- "len": Maximum progress value (json-int)
- "offset": Current progress value (json-int)
On success this is equal to len.
On failure this is less than len.
- "speed": Rate limit, bytes per second (json-int)
- "error": Error message (json-string, optional)
Only present on failure. This field contains a human-readable
error message. There are no semantics other than that streaming
has failed and clients should not try to interpret the error
string.
Example:
{ "event": "BLOCK_JOB_COMPLETED",
"data": { "type": "stream", "device": "virtio-disk0",
"len": 10737418240, "offset": 10737418240,
"speed": 0 },
"timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
BLOCK_JOB_ERROR
---------------
Emitted when a block job encounters an error.
Data:
- "device": Job identifier. Originally the device name but other
values are allowed since QEMU 2.7 (json-string)
- "operation": I/O operation (json-string, "read" or "write")
- "action": action that has been taken, it's one of the following (json-string):
"ignore": error has been ignored, the job may fail later
"report": error will be reported and the job canceled
"stop": error caused job to be paused
Example:
{ "event": "BLOCK_JOB_ERROR",
"data": { "device": "ide0-hd1",
"operation": "write",
"action": "stop" },
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
BLOCK_JOB_READY
---------------
Emitted when a block job is ready to complete.
Data:
- "type": Job type (json-string; "stream" for image streaming
"commit" for block commit)
- "device": Job identifier. Originally the device name but other
values are allowed since QEMU 2.7 (json-string)
- "len": Maximum progress value (json-int)
- "offset": Current progress value (json-int)
On success this is equal to len.
On failure this is less than len.
- "speed": Rate limit, bytes per second (json-int)
Example:
{ "event": "BLOCK_JOB_READY",
"data": { "device": "drive0", "type": "mirror", "speed": 0,
"len": 2097152, "offset": 2097152 }
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
Note: The "ready to complete" status is always reset by a BLOCK_JOB_ERROR
event.
DEVICE_DELETED
--------------
Emitted whenever the device removal completion is acknowledged
by the guest.
At this point, it's safe to reuse the specified device ID.
Device removal can be initiated by the guest or by HMP/QMP commands.
Data:
- "device": device name (json-string, optional)
- "path": device path (json-string)
{ "event": "DEVICE_DELETED",
"data": { "device": "virtio-net-pci-0",
"path": "/machine/peripheral/virtio-net-pci-0" },
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
DEVICE_TRAY_MOVED
-----------------
It's emitted whenever the tray of a removable device is moved by the guest
or by HMP/QMP commands.
Data:
- "device": Block device name. This is always present for compatibility
reasons, but it can be empty ("") if the image does not have a
device name associated. (json-string)
- "id": The name or QOM path of the guest device (json-string)
- "tray-open": true if the tray has been opened or false if it has been closed
(json-bool)
{ "event": "DEVICE_TRAY_MOVED",
"data": { "device": "ide1-cd0",
"id": "/machine/unattached/device[22]",
"tray-open": true
},
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
DUMP_COMPLETED
--------------
Emitted when the guest has finished one memory dump.
Data:
- "result": DumpQueryResult type described in qapi-schema.json
- "error": Error message when dump failed. This is only a
human-readable string provided when dump failed. It should not be
parsed in any way (json-string, optional)
Example:
{ "event": "DUMP_COMPLETED",
"data": {"result": {"total": 1090650112, "status": "completed",
"completed": 1090650112} } }
GUEST_PANICKED
--------------
Emitted when guest OS panic is detected.
Data:
- "action": Action that has been taken (json-string, currently always "pause").
Example:
{ "event": "GUEST_PANICKED",
"data": { "action": "pause" } }
MEM_UNPLUG_ERROR
--------------------
Emitted when memory hot unplug error occurs.
Data:
- "device": device name (json-string)
- "msg": Informative message (e.g., reason for the error) (json-string)
Example:
{ "event": "MEM_UNPLUG_ERROR"
"data": { "device": "dimm1",
"msg": "acpi: device unplug for unsupported device"
},
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
NIC_RX_FILTER_CHANGED
---------------------
The event is emitted once until the query command is executed,
the first event will always be emitted.
Data:
- "name": net client name (json-string)
- "path": device path (json-string)
{ "event": "NIC_RX_FILTER_CHANGED",
"data": { "name": "vnet0",
"path": "/machine/peripheral/vnet0/virtio-backend" },
"timestamp": { "seconds": 1368697518, "microseconds": 326866 } }
}
POWERDOWN
---------
Emitted when the Virtual Machine is powered down through the power
control system, such as via ACPI.
Data: None.
Example:
{ "event": "POWERDOWN",
"timestamp": { "seconds": 1267040730, "microseconds": 682951 } }
QUORUM_FAILURE
--------------
Emitted by the Quorum block driver if it fails to establish a quorum.
Data:
- "reference": device name if defined else node name.
- "sector-num": Number of the first sector of the failed read operation.
- "sectors-count": Failed read operation sector count.
Example:
{ "event": "QUORUM_FAILURE",
"data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 },
"timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
Note: this event is rate-limited.
QUORUM_REPORT_BAD
-----------------
Emitted to report a corruption of a Quorum file.
Data:
- "type": Quorum operation type
- "error": Error message (json-string, optional)
Only present on failure. This field contains a human-readable
error message. There are no semantics other than that the
block layer reported an error and clients should not try to
interpret the error string.
- "node-name": The graph node name of the block driver state.
- "sector-num": Number of the first sector of the failed read operation.
- "sectors-count": Failed read operation sector count.
Example:
Read operation:
{ "event": "QUORUM_REPORT_BAD",
"data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5,
"type": "read" },
"timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
Flush operation:
{ "event": "QUORUM_REPORT_BAD",
"data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120,
"type": "flush", "error": "Broken pipe" },
"timestamp": { "seconds": 1456406829, "microseconds": 291763 } }
Note: this event is rate-limited.
RESET
-----
Emitted when the Virtual Machine is reset.
Data: None.
Example:
{ "event": "RESET",
"timestamp": { "seconds": 1267041653, "microseconds": 9518 } }
RESUME
------
Emitted when the Virtual Machine resumes execution.
Data: None.
Example:
{ "event": "RESUME",
"timestamp": { "seconds": 1271770767, "microseconds": 582542 } }
RTC_CHANGE
----------
Emitted when the guest changes the RTC time.
Data:
- "offset": Offset between base RTC clock (as specified by -rtc base), and
new RTC clock value (json-number)
Example:
{ "event": "RTC_CHANGE",
"data": { "offset": 78 },
"timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
Note: this event is rate-limited.
SHUTDOWN
--------
Emitted when the Virtual Machine has shut down, indicating that qemu
is about to exit.
Data: None.
Example:
{ "event": "SHUTDOWN",
"timestamp": { "seconds": 1267040730, "microseconds": 682951 } }
Note: If the command-line option "-no-shutdown" has been specified, a STOP
event will eventually follow the SHUTDOWN event.
SPICE_CONNECTED
---------------
Emitted when a SPICE client connects.
Data:
- "server": Server information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "client": Client information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
Example:
{ "timestamp": {"seconds": 1290688046, "microseconds": 388707},
"event": "SPICE_CONNECTED",
"data": {
"server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
"client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
}}
SPICE_DISCONNECTED
------------------
Emitted when a SPICE client disconnects.
Data:
- "server": Server information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "client": Client information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
Example:
{ "timestamp": {"seconds": 1290688046, "microseconds": 388707},
"event": "SPICE_DISCONNECTED",
"data": {
"server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
"client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
}}
SPICE_INITIALIZED
-----------------
Emitted after initial handshake and authentication takes place (if any)
and the SPICE channel is up and running
Data:
- "server": Server information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "auth": authentication method (json-string, optional)
- "client": Client information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "connection-id": spice connection id. All channels with the same id
belong to the same spice session (json-int)
- "channel-type": channel type. "1" is the main control channel, filter for
this one if you want track spice sessions only (json-int)
- "channel-id": channel id. Usually "0", might be different needed when
multiple channels of the same type exist, such as multiple
display channels in a multihead setup (json-int)
- "tls": whevener the channel is encrypted (json-bool)
Example:
{ "timestamp": {"seconds": 1290688046, "microseconds": 417172},
"event": "SPICE_INITIALIZED",
"data": {"server": {"auth": "spice", "port": "5921",
"family": "ipv4", "host": "127.0.0.1"},
"client": {"port": "49004", "family": "ipv4", "channel-type": 3,
"connection-id": 1804289383, "host": "127.0.0.1",
"channel-id": 0, "tls": true}
}}
SPICE_MIGRATE_COMPLETED
-----------------------
Emitted when SPICE migration has completed
Data: None.
Example:
{ "timestamp": {"seconds": 1290688046, "microseconds": 417172},
"event": "SPICE_MIGRATE_COMPLETED" }
MIGRATION
---------
Emitted when a migration event happens
Data: None.
- "status": migration status
See MigrationStatus in ~/qapi-schema.json for possible values
Example:
{"timestamp": {"seconds": 1432121972, "microseconds": 744001},
"event": "MIGRATION", "data": {"status": "completed"}}
MIGRATION_PASS
--------------
Emitted from the source side of a migration at the start of each pass
(when it syncs the dirty bitmap)
Data: None.
- "pass": An incrementing count (starting at 1 on the first pass)
Example:
{"timestamp": {"seconds": 1449669631, "microseconds": 239225},
"event": "MIGRATION_PASS", "data": {"pass": 2}}
STOP
----
Emitted when the Virtual Machine is stopped.
Data: None.
Example:
{ "event": "STOP",
"timestamp": { "seconds": 1267041730, "microseconds": 281295 } }
SUSPEND
-------
Emitted when guest enters S3 state.
Data: None.
Example:
{ "event": "SUSPEND",
"timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
SUSPEND_DISK
------------
Emitted when the guest makes a request to enter S4 state.
Data: None.
Example:
{ "event": "SUSPEND_DISK",
"timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
Note: QEMU shuts down when entering S4 state.
VNC_CONNECTED
-------------
Emitted when a VNC client establishes a connection.
Data:
- "server": Server information (json-object)
- "host": IP address (json-string)
- "service": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "auth": authentication method (json-string, optional)
- "client": Client information (json-object)
- "host": IP address (json-string)
- "service": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
Example:
{ "event": "VNC_CONNECTED",
"data": {
"server": { "auth": "sasl", "family": "ipv4",
"service": "5901", "host": "0.0.0.0" },
"client": { "family": "ipv4", "service": "58425",
"host": "127.0.0.1" } },
"timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
Note: This event is emitted before any authentication takes place, thus
the authentication ID is not provided.
VNC_DISCONNECTED
----------------
Emitted when the connection is closed.
Data:
- "server": Server information (json-object)
- "host": IP address (json-string)
- "service": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "auth": authentication method (json-string, optional)
- "client": Client information (json-object)
- "host": IP address (json-string)
- "service": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "x509_dname": TLS dname (json-string, optional)
- "sasl_username": SASL username (json-string, optional)
Example:
{ "event": "VNC_DISCONNECTED",
"data": {
"server": { "auth": "sasl", "family": "ipv4",
"service": "5901", "host": "0.0.0.0" },
"client": { "family": "ipv4", "service": "58425",
"host": "127.0.0.1", "sasl_username": "luiz" } },
"timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
VNC_INITIALIZED
---------------
Emitted after authentication takes place (if any) and the VNC session is
made active.
Data:
- "server": Server information (json-object)
- "host": IP address (json-string)
- "service": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "auth": authentication method (json-string, optional)
- "client": Client information (json-object)
- "host": IP address (json-string)
- "service": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "x509_dname": TLS dname (json-string, optional)
- "sasl_username": SASL username (json-string, optional)
Example:
{ "event": "VNC_INITIALIZED",
"data": {
"server": { "auth": "sasl", "family": "ipv4",
"service": "5901", "host": "0.0.0.0"},
"client": { "family": "ipv4", "service": "46089",
"host": "127.0.0.1", "sasl_username": "luiz" } },
"timestamp": { "seconds": 1263475302, "microseconds": 150772 } }
VSERPORT_CHANGE
---------------
Emitted when the guest opens or closes a virtio-serial port.
Data:
- "id": device identifier of the virtio-serial port (json-string)
- "open": true if the guest has opened the virtio-serial port (json-bool)
Example:
{ "event": "VSERPORT_CHANGE",
"data": { "id": "channel0", "open": true },
"timestamp": { "seconds": 1401385907, "microseconds": 422329 } }
Note: this event is rate-limited separately for each "id".
WAKEUP
------
Emitted when the guest has woken up from S3 and is running.
Data: None.
Example:
{ "event": "WAKEUP",
"timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
WATCHDOG
--------
Emitted when the watchdog device's timer is expired.
Data:
- "action": Action that has been taken, it's one of the following (json-string):
"reset", "shutdown", "poweroff", "pause", "debug", or "none"
Example:
{ "event": "WATCHDOG",
"data": { "action": "reset" },
"timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
followed respectively by the RESET, SHUTDOWN, or STOP events.
Note: this event is rate-limited.

View File

@ -16,8 +16,7 @@ QMP is JSON[1] based and features the following:
For detailed information on QMP's usage, please, refer to the following files:
o qmp-spec.txt QEMU Machine Protocol current specification
o qmp-commands.txt QMP supported commands (auto-generated at build-time)
o qmp-events.txt List of available asynchronous events
o qemu-qmp-ref.html QEMU QMP commands and events (auto-generated at build-time)
[1] http://www.json.org

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,8 @@
# -*- Mode: Python -*-
#
# QAPI block core definitions (vm unrelated)
##
# == QAPI block core definitions (vm unrelated)
##
# QAPI common definitions
{ 'include': 'common.json' }
@ -467,6 +469,87 @@
# Returns: a list of @BlockInfo describing each virtual block device
#
# Since: 0.14.0
#
# Example:
#
# -> { "execute": "query-block" }
# <- {
# "return":[
# {
# "io-status": "ok",
# "device":"ide0-hd0",
# "locked":false,
# "removable":false,
# "inserted":{
# "ro":false,
# "drv":"qcow2",
# "encrypted":false,
# "file":"disks/test.qcow2",
# "backing_file_depth":1,
# "bps":1000000,
# "bps_rd":0,
# "bps_wr":0,
# "iops":1000000,
# "iops_rd":0,
# "iops_wr":0,
# "bps_max": 8000000,
# "bps_rd_max": 0,
# "bps_wr_max": 0,
# "iops_max": 0,
# "iops_rd_max": 0,
# "iops_wr_max": 0,
# "iops_size": 0,
# "detect_zeroes": "on",
# "write_threshold": 0,
# "image":{
# "filename":"disks/test.qcow2",
# "format":"qcow2",
# "virtual-size":2048000,
# "backing_file":"base.qcow2",
# "full-backing-filename":"disks/base.qcow2",
# "backing-filename-format":"qcow2",
# "snapshots":[
# {
# "id": "1",
# "name": "snapshot1",
# "vm-state-size": 0,
# "date-sec": 10000200,
# "date-nsec": 12,
# "vm-clock-sec": 206,
# "vm-clock-nsec": 30
# }
# ],
# "backing-image":{
# "filename":"disks/base.qcow2",
# "format":"qcow2",
# "virtual-size":2048000
# }
# }
# },
# "type":"unknown"
# },
# {
# "io-status": "ok",
# "device":"ide1-cd0",
# "locked":false,
# "removable":true,
# "type":"unknown"
# },
# {
# "device":"floppy0",
# "locked":false,
# "removable":true,
# "type":"unknown"
# },
# {
# "device":"sd0",
# "locked":false,
# "removable":true,
# "type":"unknown"
# }
# ]
# }
#
##
{ 'command': 'query-block', 'returns': ['BlockInfo'] }
@ -614,6 +697,9 @@
# @stats: A @BlockDeviceStats for the device.
#
# @parent: #optional This describes the file block device if it has one.
# Contains recursively the statistics of the underlying
# protocol (e.g. the host file for a qcow2 image). If there is
# no underlying protocol, this field is omitted
#
# @backing: #optional This describes the backing block device if it has one.
# (Since 2.0)
@ -641,6 +727,106 @@
# Returns: A list of @BlockStats for each virtual block devices.
#
# Since: 0.14.0
#
# Example:
#
# -> { "execute": "query-blockstats" }
# <- {
# "return":[
# {
# "device":"ide0-hd0",
# "parent":{
# "stats":{
# "wr_highest_offset":3686448128,
# "wr_bytes":9786368,
# "wr_operations":751,
# "rd_bytes":122567168,
# "rd_operations":36772
# "wr_total_times_ns":313253456
# "rd_total_times_ns":3465673657
# "flush_total_times_ns":49653
# "flush_operations":61,
# "rd_merged":0,
# "wr_merged":0,
# "idle_time_ns":2953431879,
# "account_invalid":true,
# "account_failed":false
# }
# },
# "stats":{
# "wr_highest_offset":2821110784,
# "wr_bytes":9786368,
# "wr_operations":692,
# "rd_bytes":122739200,
# "rd_operations":36604
# "flush_operations":51,
# "wr_total_times_ns":313253456
# "rd_total_times_ns":3465673657
# "flush_total_times_ns":49653,
# "rd_merged":0,
# "wr_merged":0,
# "idle_time_ns":2953431879,
# "account_invalid":true,
# "account_failed":false
# }
# },
# {
# "device":"ide1-cd0",
# "stats":{
# "wr_highest_offset":0,
# "wr_bytes":0,
# "wr_operations":0,
# "rd_bytes":0,
# "rd_operations":0
# "flush_operations":0,
# "wr_total_times_ns":0
# "rd_total_times_ns":0
# "flush_total_times_ns":0,
# "rd_merged":0,
# "wr_merged":0,
# "account_invalid":false,
# "account_failed":false
# }
# },
# {
# "device":"floppy0",
# "stats":{
# "wr_highest_offset":0,
# "wr_bytes":0,
# "wr_operations":0,
# "rd_bytes":0,
# "rd_operations":0
# "flush_operations":0,
# "wr_total_times_ns":0
# "rd_total_times_ns":0
# "flush_total_times_ns":0,
# "rd_merged":0,
# "wr_merged":0,
# "account_invalid":false,
# "account_failed":false
# }
# },
# {
# "device":"sd0",
# "stats":{
# "wr_highest_offset":0,
# "wr_bytes":0,
# "wr_operations":0,
# "rd_bytes":0,
# "rd_operations":0
# "flush_operations":0,
# "wr_total_times_ns":0
# "rd_total_times_ns":0
# "flush_total_times_ns":0,
# "rd_merged":0,
# "wr_merged":0,
# "account_invalid":false,
# "account_failed":false
# }
# }
# ]
# }
#
##
{ 'command': 'query-blockstats',
'data': { '*query-nodes': 'bool' },
@ -785,6 +971,13 @@
# occur if an invalid password is specified.
#
# Since: 0.14.0
#
# Example:
#
# -> { "execute": "block_passwd", "arguments": { "device": "ide0-hd0",
# "password": "12345" } }
# <- { "return": {} }
#
##
{ 'command': 'block_passwd', 'data': {'*device': 'str',
'*node-name': 'str', 'password': 'str'} }
@ -806,6 +999,13 @@
# If @device is not a valid block device, DeviceNotFound
#
# Since: 0.14.0
#
# Example:
#
# -> { "execute": "block_resize",
# "arguments": { "device": "scratch", "size": 1073741824 } }
# <- { "return": {} }
#
##
{ 'command': 'block_resize', 'data': { '*device': 'str',
'*node-name': 'str',
@ -837,7 +1037,9 @@
#
# @node-name: #optional graph node name to generate the snapshot from (Since 2.0)
#
# @snapshot-file: the target of the new image. A new file will be created.
# @snapshot-file: the target of the new image. If the file exists, or
# if it is a device, the snapshot will be created in the existing
# file/device. Otherwise, a new file will be created.
#
# @snapshot-node-name: #optional the graph node name of the new image (Since 2.0)
#
@ -971,6 +1173,16 @@
# If @device is not a valid block device, DeviceNotFound
#
# Since: 0.14.0
#
# Example:
#
# -> { "execute": "blockdev-snapshot-sync",
# "arguments": { "device": "ide-hd0",
# "snapshot-file":
# "/some/place/my-image",
# "format": "qcow2" } }
# <- { "return": {} }
#
##
{ 'command': 'blockdev-snapshot-sync',
'data': 'BlockdevSnapshotSync' }
@ -981,9 +1193,31 @@
#
# Generates a snapshot of a block device.
#
# Create a snapshot, by installing 'node' as the backing image of
# 'overlay'. Additionally, if 'node' is associated with a block
# device, the block device changes to using 'overlay' as its new active
# image.
#
# For the arguments, see the documentation of BlockdevSnapshot.
#
# Since: 2.5
#
# Example:
#
# -> { "execute": "blockdev-add",
# "arguments": { "options": { "driver": "qcow2",
# "node-name": "node1534",
# "file": { "driver": "file",
# "filename": "hd1.qcow2" },
# "backing": "" } } }
#
# <- { "return": {} }
#
# -> { "execute": "blockdev-snapshot",
# "arguments": { "node": "ide-hd0",
# "overlay": "node1534" } }
# <- { "return": {} }
#
##
{ 'command': 'blockdev-snapshot',
'data': 'BlockdevSnapshot' }
@ -999,7 +1233,9 @@
# updated.
#
# @image-node-name: The name of the block driver state node of the
# image to modify.
# image to modify. The "device" argument is used
# to verify "image-node-name" is in the chain
# described by "device".
#
# @device: The device name or node-name of the root node that owns
# image-node-name.
@ -1009,6 +1245,10 @@
# when specifying the string or the image chain may
# not be able to be reopened again.
#
# Returns: Nothing on success
#
# If "device" does not exist or cannot be determined, DeviceNotFound
#
# Since: 2.1
##
{ 'command': 'change-backing-file',
@ -1027,7 +1267,7 @@
# @device: the device name or node-name of a root node
#
# @base: #optional The file name of the backing image to write data into.
# If not specified, this is the deepest backing image
# If not specified, this is the deepest backing image.
#
# @top: #optional The file name of the backing image within the image chain,
# which contains the topmost data to be committed down. If
@ -1073,6 +1313,13 @@
#
# Since: 1.3
#
# Example:
#
# -> { "execute": "block-commit",
# "arguments": { "device": "virtio0",
# "top": "/tmp/snap1.qcow2" } }
# <- { "return": {} }
#
##
{ 'command': 'block-commit',
'data': { '*job-id': 'str', 'device': 'str', '*base': 'str', '*top': 'str',
@ -1093,6 +1340,15 @@
# If @device is not a valid block device, GenericError
#
# Since: 1.6
#
# Example:
#
# -> { "execute": "drive-backup",
# "arguments": { "device": "drive0",
# "sync": "full",
# "target": "backup.img" } }
# <- { "return": {} }
#
##
{ 'command': 'drive-backup', 'boxed': true,
'data': 'DriveBackup' }
@ -1112,6 +1368,14 @@
# If @device is not a valid block device, DeviceNotFound
#
# Since: 2.3
#
# Example:
# -> { "execute": "blockdev-backup",
# "arguments": { "device": "src-id",
# "sync": "full",
# "target": "tgt-id" } }
# <- { "return": {} }
#
##
{ 'command': 'blockdev-backup', 'boxed': true,
'data': 'BlockdevBackup' }
@ -1125,13 +1389,67 @@
# Returns: the list of BlockDeviceInfo
#
# Since: 2.0
#
# Example:
#
# -> { "execute": "query-named-block-nodes" }
# <- { "return": [ { "ro":false,
# "drv":"qcow2",
# "encrypted":false,
# "file":"disks/test.qcow2",
# "node-name": "my-node",
# "backing_file_depth":1,
# "bps":1000000,
# "bps_rd":0,
# "bps_wr":0,
# "iops":1000000,
# "iops_rd":0,
# "iops_wr":0,
# "bps_max": 8000000,
# "bps_rd_max": 0,
# "bps_wr_max": 0,
# "iops_max": 0,
# "iops_rd_max": 0,
# "iops_wr_max": 0,
# "iops_size": 0,
# "write_threshold": 0,
# "image":{
# "filename":"disks/test.qcow2",
# "format":"qcow2",
# "virtual-size":2048000,
# "backing_file":"base.qcow2",
# "full-backing-filename":"disks/base.qcow2",
# "backing-filename-format":"qcow2",
# "snapshots":[
# {
# "id": "1",
# "name": "snapshot1",
# "vm-state-size": 0,
# "date-sec": 10000200,
# "date-nsec": 12,
# "vm-clock-sec": 206,
# "vm-clock-nsec": 30
# }
# ],
# "backing-image":{
# "filename":"disks/base.qcow2",
# "format":"qcow2",
# "virtual-size":2048000
# }
# } } ] }
#
##
{ 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ] }
##
# @drive-mirror:
#
# Start mirroring a block device's writes to a new destination.
# Start mirroring a block device's writes to a new destination. target
# specifies the target of the new image. If the file exists, or if it
# is a device, it will be used as the new destination for writes. If
# it does not exist, a new file will be created. format specifies the
# format of the mirror image, default is to probe if mode='existing',
# else the format of the source.
#
# See DriveMirror for parameter descriptions
#
@ -1139,6 +1457,16 @@
# If @device is not a valid block device, GenericError
#
# Since: 1.3
#
# Example:
#
# -> { "execute": "drive-mirror",
# "arguments": { "device": "ide-hd0",
# "target": "/some/place/my-image",
# "sync": "full",
# "format": "qcow2" } }
# <- { "return": {} }
#
##
{ 'command': 'drive-mirror', 'boxed': true,
'data': 'DriveMirror' }
@ -1239,13 +1567,20 @@
##
# @block-dirty-bitmap-add:
#
# Create a dirty bitmap with a name on the node
# Create a dirty bitmap with a name on the node, and start tracking the writes.
#
# Returns: nothing on success
# If @node is not a valid block device or node, DeviceNotFound
# If @name is already taken, GenericError with an explanation
#
# Since: 2.4
#
# Example:
#
# -> { "execute": "block-dirty-bitmap-add",
# "arguments": { "node": "drive0", "name": "bitmap0" } }
# <- { "return": {} }
#
##
{ 'command': 'block-dirty-bitmap-add',
'data': 'BlockDirtyBitmapAdd' }
@ -1253,7 +1588,8 @@
##
# @block-dirty-bitmap-remove:
#
# Remove a dirty bitmap on the node
# Stop write tracking and remove the dirty bitmap that was created
# with block-dirty-bitmap-add.
#
# Returns: nothing on success
# If @node is not a valid block device or node, DeviceNotFound
@ -1261,6 +1597,13 @@
# if @name is frozen by an operation, GenericError
#
# Since: 2.4
#
# Example:
#
# -> { "execute": "block-dirty-bitmap-remove",
# "arguments": { "node": "drive0", "name": "bitmap0" } }
# <- { "return": {} }
#
##
{ 'command': 'block-dirty-bitmap-remove',
'data': 'BlockDirtyBitmap' }
@ -1268,13 +1611,22 @@
##
# @block-dirty-bitmap-clear:
#
# Clear (reset) a dirty bitmap on the device
# Clear (reset) a dirty bitmap on the device, so that an incremental
# backup from this point in time forward will only backup clusters
# modified after this clear operation.
#
# Returns: nothing on success
# If @node is not a valid block device, DeviceNotFound
# If @name is not found, GenericError with an explanation
#
# Since: 2.4
#
# Example:
#
# -> { "execute": "block-dirty-bitmap-clear",
# "arguments": { "node": "drive0", "name": "bitmap0" } }
# <- { "return": {} }
#
##
{ 'command': 'block-dirty-bitmap-clear',
'data': 'BlockDirtyBitmap' }
@ -1322,6 +1674,15 @@
# Returns: nothing on success.
#
# Since: 2.6
#
# Example:
#
# -> { "execute": "blockdev-mirror",
# "arguments": { "device": "ide-hd0",
# "target": "target0",
# "sync": "full" } }
# <- { "return": {} }
#
##
{ 'command': 'blockdev-mirror',
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
@ -1363,6 +1724,26 @@
# If @device is not a valid block device, DeviceNotFound
#
# Since: 1.1
#
# Example:
#
# -> { "execute": "block_set_io_throttle",
# "arguments": { "id": "ide0-1-0",
# "bps": 1000000,
# "bps_rd": 0,
# "bps_wr": 0,
# "iops": 0,
# "iops_rd": 0,
# "iops_wr": 0,
# "bps_max": 8000000,
# "bps_rd_max": 0,
# "bps_wr_max": 0,
# "iops_max": 0,
# "iops_rd_max": 0,
# "iops_wr_max": 0,
# "bps_max_length": 60,
# "iops_size": 0 } }
# <- { "return": {} }
##
{ 'command': 'block_set_io_throttle', 'boxed': true,
'data': 'BlockIOThrottle' }
@ -1511,7 +1892,17 @@
# 'stop' and 'enospc' can only be used if the block device
# supports io-status (see BlockInfo). Since 1.3.
#
# Returns: Nothing on success. If @device does not exist, DeviceNotFound.
#
# Since: 1.1
#
# Example:
#
# -> { "execute": "block-stream",
# "arguments": { "device": "virtio0",
# "base": "/tmp/master.qcow2" } }
# <- { "return": {} }
#
##
{ 'command': 'block-stream',
'data': { '*job-id': 'str', 'device': 'str', '*base': 'str',
@ -2441,13 +2832,52 @@
# BlockBackend will be created; otherwise, @node-name is mandatory at the top
# level and no BlockBackend will be created.
#
# This command is still a work in progress. It doesn't support all
# For the arguments, see the documentation of BlockdevOptions.
#
# Note: This command is still a work in progress. It doesn't support all
# block drivers among other things. Stay away from it unless you want
# to help with its development.
#
# For the arguments, see the documentation of BlockdevOptions.
#
# Since: 1.7
#
# Example:
#
# 1.
# -> { "execute": "blockdev-add",
# "arguments": {
# "options" : { "driver": "qcow2",
# "file": { "driver": "file",
# "filename": "test.qcow2" } } } }
# <- { "return": {} }
#
# 2.
# -> { "execute": "blockdev-add",
# "arguments": {
# "options": {
# "driver": "qcow2",
# "node-name": "node0",
# "discard": "unmap",
# "cache": {
# "direct": true,
# "writeback": true
# },
# "file": {
# "driver": "file",
# "filename": "/tmp/test.qcow2"
# },
# "backing": {
# "driver": "raw",
# "file": {
# "driver": "file",
# "filename": "/dev/fdset/4"
# }
# }
# }
# }
# }
#
# <- { "return": {} }
#
##
{ 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true }
@ -2458,13 +2888,35 @@
# The command will fail if the node is attached to a device or is
# otherwise being used.
#
# This command is still a work in progress and is considered
# @node-name: Name of the graph node to delete.
#
# Note: This command is still a work in progress and is considered
# experimental. Stay away from it unless you want to help with its
# development.
#
# @node-name: Name of the graph node to delete.
#
# Since: 2.5
#
# Example:
#
# -> { "execute": "blockdev-add",
# "arguments": {
# "options": {
# "driver": "qcow2",
# "node-name": "node0",
# "file": {
# "driver": "file",
# "filename": "test.qcow2"
# }
# }
# }
# }
# <- { "return": {} }
#
# -> { "execute": "x-blockdev-del",
# "arguments": { "node-name": "node0" }
# }
# <- { "return": {} }
#
##
{ 'command': 'x-blockdev-del', 'data': { 'node-name': 'str' } }
@ -2496,6 +2948,21 @@
# it is locked
#
# Since: 2.5
#
# Example:
#
# -> { "execute": "blockdev-open-tray",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "timestamp": { "seconds": 1418751016,
# "microseconds": 716996 },
# "event": "DEVICE_TRAY_MOVED",
# "data": { "device": "ide1-cd0",
# "id": "ide0-1-0",
# "tray-open": true } }
#
# <- { "return": {} }
#
##
{ 'command': 'blockdev-open-tray',
'data': { '*device': 'str',
@ -2516,6 +2983,21 @@
# @id: #optional The name or QOM path of the guest device (since: 2.8)
#
# Since: 2.5
#
# Example:
#
# -> { "execute": "blockdev-close-tray",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "timestamp": { "seconds": 1418751345,
# "microseconds": 272147 },
# "event": "DEVICE_TRAY_MOVED",
# "data": { "device": "ide1-cd0",
# "id": "ide0-1-0",
# "tray-open": false } }
#
# <- { "return": {} }
#
##
{ 'command': 'blockdev-close-tray',
'data': { '*device': 'str',
@ -2530,14 +3012,40 @@
#
# If the tray is open and there is no medium inserted, this will be a no-op.
#
# This command is still a work in progress and is considered experimental.
# Stay away from it unless you want to help with its development.
#
# @device: #optional Block device name (deprecated, use @id instead)
#
# @id: #optional The name or QOM path of the guest device (since: 2.8)
#
# Note: This command is still a work in progress and is considered experimental.
# Stay away from it unless you want to help with its development.
#
# Since: 2.5
#
# Example:
#
# -> { "execute": "x-blockdev-remove-medium",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "error": { "class": "GenericError",
# "desc": "Tray of device 'ide0-1-0' is not open" } }
#
# -> { "execute": "blockdev-open-tray",
# "arguments": { "id": "ide0-1-0" } }
#
# <- { "timestamp": { "seconds": 1418751627,
# "microseconds": 549958 },
# "event": "DEVICE_TRAY_MOVED",
# "data": { "device": "ide1-cd0",
# "id": "ide0-1-0",
# "tray-open": true } }
#
# <- { "return": {} }
#
# -> { "execute": "x-blockdev-remove-medium",
# "arguments": { "device": "ide0-1-0" } }
#
# <- { "return": {} }
#
##
{ 'command': 'x-blockdev-remove-medium',
'data': { '*device': 'str',
@ -2550,16 +3058,33 @@
# device's tray must currently be open (unless there is no attached guest
# device) and there must be no medium inserted already.
#
# This command is still a work in progress and is considered experimental.
# Stay away from it unless you want to help with its development.
#
# @device: #optional Block device name (deprecated, use @id instead)
#
# @id: #optional The name or QOM path of the guest device (since: 2.8)
#
# @node-name: name of a node in the block driver state graph
#
# Note: This command is still a work in progress and is considered experimental.
# Stay away from it unless you want to help with its development.
#
# Since: 2.5
#
# Example:
#
# -> { "execute": "blockdev-add",
# "arguments": {
# "options": { "node-name": "node0",
# "driver": "raw",
# "file": { "driver": "file",
# "filename": "fedora.iso" } } } }
# <- { "return": {} }
#
# -> { "execute": "x-blockdev-insert-medium",
# "arguments": { "id": "ide0-1-0",
# "node-name": "node0" } }
#
# <- { "return": {} }
#
##
{ 'command': 'x-blockdev-insert-medium',
'data': { '*device': 'str',
@ -2580,6 +3105,7 @@
# @read-write: Makes the device writable
#
# Since: 2.3
#
##
{ 'enum': 'BlockdevChangeReadOnlyMode',
'data': ['retain', 'read-only', 'read-write'] }
@ -2607,6 +3133,37 @@
# to 'retain'
#
# Since: 2.5
#
# Examples:
#
# 1. Change a removable medium
#
# -> { "execute": "blockdev-change-medium",
# "arguments": { "id": "ide0-1-0",
# "filename": "/srv/images/Fedora-12-x86_64-DVD.iso",
# "format": "raw" } }
# <- { "return": {} }
#
# 2. Load a read-only medium into a writable drive
#
# -> { "execute": "blockdev-change-medium",
# "arguments": { "id": "floppyA",
# "filename": "/srv/images/ro.img",
# "format": "raw",
# "read-only-mode": "retain" } }
#
# <- { "error":
# { "class": "GenericError",
# "desc": "Could not open '/srv/images/ro.img': Permission denied" } }
#
# -> { "execute": "blockdev-change-medium",
# "arguments": { "id": "floppyA",
# "filename": "/srv/images/ro.img",
# "format": "raw",
# "read-only-mode": "read-only" } }
#
# <- { "return": {} }
#
##
{ 'command': 'blockdev-change-medium',
'data': { '*device': 'str',
@ -2636,7 +3193,10 @@
##
# @BLOCK_IMAGE_CORRUPTED:
#
# Emitted when a corruption has been detected in a disk image
# Emitted when a disk image is being marked corrupt. The image can be
# identified by its device or node name. The 'device' field is always
# present for compatibility reasons, but it can be empty ("") if the
# image does not have a device name associated.
#
# @device: device name. This is always present for compatibility
# reasons, but it can be empty ("") if the image does not
@ -2654,10 +3214,21 @@
# @size: #optional, if the corruption resulted from an image access, this is
# the access size
#
# fatal: if set, the image is marked corrupt and therefore unusable after this
# @fatal: if set, the image is marked corrupt and therefore unusable after this
# event and must be repaired (Since 2.2; before, every
# BLOCK_IMAGE_CORRUPTED event was fatal)
#
# Note: If action is "stop", a STOP event will eventually follow the
# BLOCK_IO_ERROR event.
#
# Example:
#
# <- { "event": "BLOCK_IMAGE_CORRUPTED",
# "data": { "device": "ide0-hd0", "node-name": "node0",
# "msg": "Prevented active L1 table overwrite", "offset": 196608,
# "size": 65536 },
# "timestamp": { "seconds": 1378126126, "microseconds": 966463 } }
#
# Since: 1.7
##
{ 'event': 'BLOCK_IMAGE_CORRUPTED',
@ -2698,6 +3269,16 @@
# BLOCK_IO_ERROR event
#
# Since: 0.13.0
#
# Example:
#
# <- { "event": "BLOCK_IO_ERROR",
# "data": { "device": "ide0-hd1",
# "node-name": "#block212",
# "operation": "write",
# "action": "stop" },
# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
#
##
{ 'event': 'BLOCK_IO_ERROR',
'data': { 'device': 'str', 'node-name': 'str', 'operation': 'IoOperationType',
@ -2727,6 +3308,15 @@
# interpret the error string
#
# Since: 1.1
#
# Example:
#
# <- { "event": "BLOCK_JOB_COMPLETED",
# "data": { "type": "stream", "device": "virtio-disk0",
# "len": 10737418240, "offset": 10737418240,
# "speed": 0 },
# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
#
##
{ 'event': 'BLOCK_JOB_COMPLETED',
'data': { 'type' : 'BlockJobType',
@ -2754,6 +3344,15 @@
# @speed: rate limit, bytes per second
#
# Since: 1.1
#
# Example:
#
# <- { "event": "BLOCK_JOB_CANCELLED",
# "data": { "type": "stream", "device": "virtio-disk0",
# "len": 10737418240, "offset": 134217728,
# "speed": 0 },
# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
#
##
{ 'event': 'BLOCK_JOB_CANCELLED',
'data': { 'type' : 'BlockJobType',
@ -2775,6 +3374,15 @@
# @action: action that has been taken
#
# Since: 1.3
#
# Example:
#
# <- { "event": "BLOCK_JOB_ERROR",
# "data": { "device": "ide0-hd1",
# "operation": "write",
# "action": "stop" },
# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
#
##
{ 'event': 'BLOCK_JOB_ERROR',
'data': { 'device' : 'str',
@ -2802,6 +3410,14 @@
# event
#
# Since: 1.3
#
# Example:
#
# <- { "event": "BLOCK_JOB_READY",
# "data": { "device": "drive0", "type": "mirror", "speed": 0,
# "len": 2097152, "offset": 2097152 }
# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
#
##
{ 'event': 'BLOCK_JOB_READY',
'data': { 'type' : 'BlockJobType',
@ -2854,8 +3470,12 @@
##
# @block-set-write-threshold:
#
# Change the write threshold for a block drive. An event will be delivered
# if a write to this block drive crosses the configured threshold.
# Change the write threshold for a block drive. An event will be
# delivered if a write to this block drive crosses the configured
# threshold. The threshold is an offset, thus must be
# non-negative. Default is no write threshold. Setting the threshold
# to zero disables it.
#
# This is useful to transparently resize thin-provisioned drives without
# the guest OS noticing.
#
@ -2865,6 +3485,14 @@
# Use 0 to disable the threshold.
#
# Since: 2.3
#
# Example:
#
# -> { "execute": "block-set-write-threshold",
# "arguments": { "node-name": "mydev",
# "write-threshold": 17179869184 } }
# <- { "return": {} }
#
##
{ 'command': 'block-set-write-threshold',
'data': { 'node-name': 'str', 'write-threshold': 'uint64' } }
@ -2895,6 +3523,28 @@
# the rest of the array.
#
# Since: 2.7
#
# Example:
#
# 1. Add a new node to a quorum
# -> { "execute": "blockdev-add",
# "arguments": {
# "options": { "driver": "raw",
# "node-name": "new_node",
# "file": { "driver": "file",
# "filename": "test.raw" } } } }
# <- { "return": {} }
# -> { "execute": "x-blockdev-change",
# "arguments": { "parent": "disk1",
# "node": "new_node" } }
# <- { "return": {} }
#
# 2. Delete a quorum's node
# -> { "execute": "x-blockdev-change",
# "arguments": { "parent": "disk1",
# "child": "children.1" } }
# <- { "return": {} }
#
##
{ 'command': 'x-blockdev-change',
'data' : { 'parent': 'str',

View File

@ -1,10 +1,16 @@
# -*- Mode: Python -*-
#
# QAPI block definitions (vm related)
##
# = QAPI block definitions
##
# QAPI block core definitions
{ 'include': 'block-core.json' }
##
# == QAPI block definitions (vm unrelated)
##
##
# @BiosAtaTranslation:
#
@ -75,19 +81,33 @@
##
# @blockdev-snapshot-internal-sync:
#
# Synchronously take an internal snapshot of a block device, when the format
# of the image used supports it.
# Synchronously take an internal snapshot of a block device, when the
# format of the image used supports it. If the name is an empty
# string, or a snapshot with name already exists, the operation will
# fail.
#
# For the arguments, see the documentation of BlockdevSnapshotInternal.
#
# Returns: nothing on success
#
# If @device is not a valid block device, GenericError
#
# If any snapshot matching @name exists, or @name is empty,
# GenericError
#
# If the format of the image used does not support it,
# BlockFormatFeatureNotSupported
#
# Since: 1.7
#
# Example:
#
# -> { "execute": "blockdev-snapshot-internal-sync",
# "arguments": { "device": "ide-hd0",
# "name": "snapshot0" }
# }
# <- { "return": {} }
#
##
{ 'command': 'blockdev-snapshot-internal-sync',
'data': 'BlockdevSnapshotInternal' }
@ -115,6 +135,24 @@
# If @id and @name are both not specified, GenericError
#
# Since: 1.7
#
# Example:
#
# -> { "execute": "blockdev-snapshot-delete-internal-sync",
# "arguments": { "device": "ide-hd0",
# "name": "snapshot0" }
# }
# <- { "return": {
# "id": "1",
# "name": "snapshot0",
# "vm-state-size": 0,
# "date-sec": 1000012,
# "date-nsec": 10,
# "vm-clock-sec": 100,
# "vm-clock-nsec": 20
# }
# }
#
##
{ 'command': 'blockdev-snapshot-delete-internal-sync',
'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
@ -129,15 +167,21 @@
#
# @id: #optional The name or QOM path of the guest device (since: 2.8)
#
# @force: @optional If true, eject regardless of whether the drive is locked.
# @force: #optional If true, eject regardless of whether the drive is locked.
# If not specified, the default value is false.
#
# Returns: Nothing on success
#
# If @device is not a valid block device, DeviceNotFound
#
# Notes: Ejecting a device will no media results in success
# Notes: Ejecting a device with no media results in success
#
# Since: 0.14.0
#
# Example:
#
# -> { "execute": "eject", "arguments": { "device": "ide1-0-1" } }
# <- { "return": {} }
##
{ 'command': 'eject',
'data': { '*device': 'str',
@ -204,6 +248,16 @@
# @tray-open: true if the tray has been opened or false if it has been closed
#
# Since: 1.1
#
# Example:
#
# <- { "event": "DEVICE_TRAY_MOVED",
# "data": { "device": "ide1-cd0",
# "id": "/machine/unattached/device[22]",
# "tray-open": true
# },
# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
#
##
{ 'event': 'DEVICE_TRAY_MOVED',
'data': { 'device': 'str', 'id': 'str', 'tray-open': 'bool' } }

View File

@ -1,6 +1,8 @@
# -*- Mode: Python -*-
#
# QAPI common definitions
##
# = QAPI common definitions
##
##
# @QapiErrorClass:
@ -75,6 +77,21 @@
# Returns: A @VersionInfo object describing the current version of QEMU.
#
# Since: 0.14.0
#
# Example:
#
# -> { "execute": "query-version" }
# <- {
# "return":{
# "qemu":{
# "major":0,
# "minor":11,
# "micro":5
# },
# "package":""
# }
# }
#
##
{ 'command': 'query-version', 'returns': 'VersionInfo' }
@ -97,6 +114,23 @@
# Returns: A list of @CommandInfo for all supported commands
#
# Since: 0.14.0
#
# Example:
#
# -> { "execute": "query-commands" }
# <- {
# "return":[
# {
# "name":"query-balloon"
# },
# {
# "name":"system_powerdown"
# }
# ]
# }
#
# Note: This example has been shortened as the real response is too long.
#
##
{ 'command': 'query-commands', 'returns': ['CommandInfo'] }

View File

@ -1,6 +1,9 @@
# -*- Mode: Python -*-
#
# QAPI crypto definitions
##
# = QAPI crypto definitions
##
##
# @QCryptoTLSCredsEndpoint:

View File

@ -1,3 +1,9 @@
# -*- Mode: Python -*-
##
# = Other events
##
##
# @SHUTDOWN:
#
@ -8,6 +14,12 @@
# not exit, and a STOP event will eventually follow the SHUTDOWN event
#
# Since: 0.12.0
#
# Example:
#
# <- { "event": "SHUTDOWN",
# "timestamp": { "seconds": 1267040730, "microseconds": 682951 } }
#
##
{ 'event': 'SHUTDOWN' }
@ -18,6 +30,12 @@
# system, such as via ACPI.
#
# Since: 0.12.0
#
# Example:
#
# <- { "event": "POWERDOWN",
# "timestamp": { "seconds": 1267040730, "microseconds": 682951 } }
#
##
{ 'event': 'POWERDOWN' }
@ -27,6 +45,12 @@
# Emitted when the virtual machine is reset
#
# Since: 0.12.0
#
# Example:
#
# <- { "event": "RESET",
# "timestamp": { "seconds": 1267041653, "microseconds": 9518 } }
#
##
{ 'event': 'RESET' }
@ -36,6 +60,12 @@
# Emitted when the virtual machine is stopped
#
# Since: 0.12.0
#
# Example:
#
# <- { "event": "STOP",
# "timestamp": { "seconds": 1267041730, "microseconds": 281295 } }
#
##
{ 'event': 'STOP' }
@ -45,6 +75,12 @@
# Emitted when the virtual machine resumes execution
#
# Since: 0.12.0
#
# Example:
#
# <- { "event": "RESUME",
# "timestamp": { "seconds": 1271770767, "microseconds": 582542 } }
#
##
{ 'event': 'RESUME' }
@ -55,6 +91,12 @@
# which is sometimes called standby state
#
# Since: 1.1
#
# Example:
#
# <- { "event": "SUSPEND",
# "timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
#
##
{ 'event': 'SUSPEND' }
@ -67,6 +109,12 @@
# Note: QEMU shuts down (similar to event @SHUTDOWN) when entering this state
#
# Since: 1.2
#
# Example:
#
# <- { "event": "SUSPEND_DISK",
# "timestamp": { "seconds": 1344456160, "microseconds": 309119 } }
#
##
{ 'event': 'SUSPEND_DISK' }
@ -76,6 +124,12 @@
# Emitted when the guest has woken up from suspend state and is running
#
# Since: 1.1
#
# Example:
#
# <- { "event": "WAKEUP",
# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
#
##
{ 'event': 'WAKEUP' }
@ -87,7 +141,16 @@
# @offset: offset between base RTC clock (as specified by -rtc base), and
# new RTC clock value
#
# Note: This event is rate-limited.
#
# Since: 0.13.0
#
# Example:
#
# <- { "event": "RTC_CHANGE",
# "data": { "offset": 78 },
# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
#
##
{ 'event': 'RTC_CHANGE',
'data': { 'offset': 'int' } }
@ -102,7 +165,16 @@
# Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
# followed respectively by the RESET, SHUTDOWN, or STOP events
#
# Note: This event is rate-limited.
#
# Since: 0.13.0
#
# Example:
#
# <- { "event": "WATCHDOG",
# "data": { "action": "reset" },
# "timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
#
##
{ 'event': 'WATCHDOG',
'data': { 'action': 'WatchdogExpirationAction' } }
@ -119,6 +191,14 @@
# @path: device path
#
# Since: 1.5
#
# Example:
#
# <- { "event": "DEVICE_DELETED",
# "data": { "device": "virtio-net-pci-0",
# "path": "/machine/peripheral/virtio-net-pci-0" },
# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
#
##
{ 'event': 'DEVICE_DELETED',
'data': { '*device': 'str', 'path': 'str' } }
@ -134,6 +214,15 @@
# @path: device path
#
# Since: 1.6
#
# Example:
#
# <- { "event": "NIC_RX_FILTER_CHANGED",
# "data": { "name": "vnet0",
# "path": "/machine/peripheral/vnet0/virtio-backend" },
# "timestamp": { "seconds": 1368697518, "microseconds": 326866 } }
# }
#
##
{ 'event': 'NIC_RX_FILTER_CHANGED',
'data': { '*name': 'str', 'path': 'str' } }
@ -151,6 +240,17 @@
# the authentication ID is not provided
#
# Since: 0.13.0
#
# Example:
#
# <- { "event": "VNC_CONNECTED",
# "data": {
# "server": { "auth": "sasl", "family": "ipv4",
# "service": "5901", "host": "0.0.0.0" },
# "client": { "family": "ipv4", "service": "58425",
# "host": "127.0.0.1" } },
# "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
#
##
{ 'event': 'VNC_CONNECTED',
'data': { 'server': 'VncServerInfo',
@ -167,6 +267,17 @@
# @client: client information
#
# Since: 0.13.0
#
# Example:
#
# <- { "event": "VNC_INITIALIZED",
# "data": {
# "server": { "auth": "sasl", "family": "ipv4",
# "service": "5901", "host": "0.0.0.0"},
# "client": { "family": "ipv4", "service": "46089",
# "host": "127.0.0.1", "sasl_username": "luiz" } },
# "timestamp": { "seconds": 1263475302, "microseconds": 150772 } }
#
##
{ 'event': 'VNC_INITIALIZED',
'data': { 'server': 'VncServerInfo',
@ -182,6 +293,17 @@
# @client: client information
#
# Since: 0.13.0
#
# Example:
#
# <- { "event": "VNC_DISCONNECTED",
# "data": {
# "server": { "auth": "sasl", "family": "ipv4",
# "service": "5901", "host": "0.0.0.0" },
# "client": { "family": "ipv4", "service": "58425",
# "host": "127.0.0.1", "sasl_username": "luiz" } },
# "timestamp": { "seconds": 1262976601, "microseconds": 975795 } }
#
##
{ 'event': 'VNC_DISCONNECTED',
'data': { 'server': 'VncServerInfo',
@ -197,6 +319,16 @@
# @client: client information
#
# Since: 0.14.0
#
# Example:
#
# <- { "timestamp": {"seconds": 1290688046, "microseconds": 388707},
# "event": "SPICE_CONNECTED",
# "data": {
# "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
# "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
# }}
#
##
{ 'event': 'SPICE_CONNECTED',
'data': { 'server': 'SpiceBasicInfo',
@ -213,6 +345,18 @@
# @client: client information
#
# Since: 0.14.0
#
# Example:
#
# <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172},
# "event": "SPICE_INITIALIZED",
# "data": {"server": {"auth": "spice", "port": "5921",
# "family": "ipv4", "host": "127.0.0.1"},
# "client": {"port": "49004", "family": "ipv4", "channel-type": 3,
# "connection-id": 1804289383, "host": "127.0.0.1",
# "channel-id": 0, "tls": true}
# }}
#
##
{ 'event': 'SPICE_INITIALIZED',
'data': { 'server': 'SpiceServerInfo',
@ -228,6 +372,16 @@
# @client: client information
#
# Since: 0.14.0
#
# Example:
#
# <- { "timestamp": {"seconds": 1290688046, "microseconds": 388707},
# "event": "SPICE_DISCONNECTED",
# "data": {
# "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
# "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
# }}
#
##
{ 'event': 'SPICE_DISCONNECTED',
'data': { 'server': 'SpiceBasicInfo',
@ -239,6 +393,12 @@
# Emitted when SPICE migration has completed
#
# Since: 1.3
#
# Example:
#
# <- { "timestamp": {"seconds": 1290688046, "microseconds": 417172},
# "event": "SPICE_MIGRATE_COMPLETED" }
#
##
{ 'event': 'SPICE_MIGRATE_COMPLETED' }
@ -250,6 +410,13 @@
# @status: @MigrationStatus describing the current migration status.
#
# Since: 2.4
#
# Example:
#
# <- {"timestamp": {"seconds": 1432121972, "microseconds": 744001},
# "event": "MIGRATION",
# "data": {"status": "completed"} }
#
##
{ 'event': 'MIGRATION',
'data': {'status': 'MigrationStatus'}}
@ -263,6 +430,12 @@
# @pass: An incrementing count (starting at 1 on the first pass)
#
# Since: 2.6
#
# Example:
#
# { "timestamp": {"seconds": 1449669631, "microseconds": 239225},
# "event": "MIGRATION_PASS", "data": {"pass": 2} }
#
##
{ 'event': 'MIGRATION_PASS',
'data': { 'pass': 'int' } }
@ -272,9 +445,16 @@
#
# Emitted when guest executes ACPI _OST method.
#
# @info: ACPIOSTInfo type as described in qapi-schema.json
#
# Since: 2.1
#
# @info: ACPIOSTInfo type as described in qapi-schema.json
# Example:
#
# <- { "event": "ACPI_DEVICE_OST",
# "data": { "device": "d1", "slot": "0",
# "slot-type": "DIMM", "source": 1, "status": 0 } }
#
##
{ 'event': 'ACPI_DEVICE_OST',
'data': { 'info': 'ACPIOSTInfo' } }
@ -287,7 +467,16 @@
#
# @actual: actual level of the guest memory balloon in bytes
#
# Note: this event is rate-limited.
#
# Since: 1.2
#
# Example:
#
# <- { "event": "BALLOON_CHANGE",
# "data": { "actual": 944766976 },
# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
#
##
{ 'event': 'BALLOON_CHANGE',
'data': { 'actual': 'int' } }
@ -300,6 +489,12 @@
# @action: action that has been taken, currently always "pause"
#
# Since: 1.5
#
# Example:
#
# <- { "event": "GUEST_PANICKED",
# "data": { "action": "pause" } }
#
##
{ 'event': 'GUEST_PANICKED',
'data': { 'action': 'GuestPanicAction' } }
@ -315,7 +510,16 @@
#
# @sectors-count: failed read operation sector count
#
# Note: This event is rate-limited.
#
# Since: 2.0
#
# Example:
#
# <- { "event": "QUORUM_FAILURE",
# "data": { "reference": "usr1", "sector-num": 345435, "sectors-count": 5 },
# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
#
##
{ 'event': 'QUORUM_FAILURE',
'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } }
@ -338,7 +542,26 @@
#
# @sectors-count: failed read operation sector count
#
# Note: This event is rate-limited.
#
# Since: 2.0
#
# Example:
#
# 1. Read operation
#
# { "event": "QUORUM_REPORT_BAD",
# "data": { "node-name": "node0", "sector-num": 345435, "sectors-count": 5,
# "type": "read" },
# "timestamp": { "seconds": 1344522075, "microseconds": 745528 } }
#
# 2. Flush operation
#
# { "event": "QUORUM_REPORT_BAD",
# "data": { "node-name": "node0", "sector-num": 0, "sectors-count": 2097120,
# "type": "flush", "error": "Broken pipe" },
# "timestamp": { "seconds": 1456406829, "microseconds": 291763 } }
#
##
{ 'event': 'QUORUM_REPORT_BAD',
'data': { 'type': 'QuorumOpType', '*error': 'str', 'node-name': 'str',
@ -354,6 +577,13 @@
# @open: true if the guest has opened the virtio-serial port
#
# Since: 2.1
#
# Example:
#
# <- { "event": "VSERPORT_CHANGE",
# "data": { "id": "channel0", "open": true },
# "timestamp": { "seconds": 1401385907, "microseconds": 422329 } }
#
##
{ 'event': 'VSERPORT_CHANGE',
'data': { 'id': 'str', 'open': 'bool' } }
@ -368,6 +598,15 @@
# @msg: Informative message
#
# Since: 2.4
#
# Example:
#
# <- { "event": "MEM_UNPLUG_ERROR"
# "data": { "device": "dimm1",
# "msg": "acpi: device unplug for unsupported device"
# },
# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
#
##
{ 'event': 'MEM_UNPLUG_ERROR',
'data': { 'device': 'str', 'msg': 'str' } }
@ -384,6 +623,13 @@
# user should not try to interpret the error string.
#
# Since: 2.6
#
# Example:
#
# { "event": "DUMP_COMPLETED",
# "data": {"result": {"total": 1090650112, "status": "completed",
# "completed": 1090650112} } }
#
##
{ 'event': 'DUMP_COMPLETED' ,
'data': { 'result': 'DumpQueryResult', '*error': 'str' } }

View File

@ -78,14 +78,13 @@
# @SchemaInfo:
#
# @name: the entity's name, inherited from @base.
# The SchemaInfo is always referenced by this name.
# Commands and events have the name defined in the QAPI schema.
# Unlike command and event names, type names are not part of
# the wire ABI. Consequently, type names are meaningless
# strings here, although they are still guaranteed unique
# regardless of @meta-type.
#
# All references to other SchemaInfo are by name.
#
# @meta-type: the entity's meta type, inherited from @base.
#
# Additional members depend on the value of @meta-type.
@ -258,7 +257,7 @@
#
# @ret-type: the name of the command's result type.
#
# TODO @success-response (currently irrelevant, because it's QGA, not QMP)
# TODO: @success-response (currently irrelevant, because it's QGA, not QMP)
#
# Since: 2.5
##

View File

@ -1,3 +1,7 @@
##
# = Rocker switch device
##
##
# @RockerSwitch:
#
@ -22,6 +26,12 @@
# Returns: @Rocker information
#
# Since: 2.4
#
# Example:
#
# -> { "execute": "query-rocker", "arguments": { "name": "sw1" } }
# <- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}}
#
##
{ 'command': 'query-rocker',
'data': { 'name': 'str' },
@ -80,11 +90,21 @@
##
# @query-rocker-ports:
#
# Return rocker switch information.
# Return rocker switch port information.
#
# Returns: @Rocker information
# Returns: a list of @RockerPort information
#
# Since: 2.4
#
# Example:
#
# -> { "execute": "query-rocker-ports", "arguments": { "name": "sw1" } }
# <- { "return": [ {"duplex": "full", "enabled": true, "name": "sw1.1",
# "autoneg": "off", "link-up": true, "speed": 10000},
# {"duplex": "full", "enabled": true, "name": "sw1.2",
# "autoneg": "off", "link-up": true, "speed": 10000}
# ]}
#
##
{ 'command': 'query-rocker-ports',
'data': { 'name': 'str' },
@ -215,9 +235,23 @@
# @tbl-id: #optional flow table ID. If tbl-id is not specified, returns
# flow information for all tables.
#
# Returns: @Rocker OF-DPA flow information
# Returns: rocker OF-DPA flow information
#
# Since: 2.4
#
# Example:
#
# -> { "execute": "query-rocker-of-dpa-flows",
# "arguments": { "name": "sw1" } }
# <- { "return": [ {"key": {"in-pport": 0, "priority": 1, "tbl-id": 0},
# "hits": 138,
# "cookie": 0,
# "action": {"goto-tbl": 10},
# "mask": {"in-pport": 4294901760}
# },
# {...more...},
# ]}
#
##
{ 'command': 'query-rocker-of-dpa-flows',
'data': { 'name': 'str', '*tbl-id': 'uint32' },
@ -277,9 +311,28 @@
# @type: #optional group type. If type is not specified, returns
# group information for all group types.
#
# Returns: @Rocker OF-DPA group information
# Returns: rocker OF-DPA group information
#
# Since: 2.4
#
# Example:
#
# -> { "execute": "query-rocker-of-dpa-groups",
# "arguments": { "name": "sw1" } }
# <- { "return": [ {"type": 0, "out-pport": 2,
# "pport": 2, "vlan-id": 3841,
# "pop-vlan": 1, "id": 251723778},
# {"type": 0, "out-pport": 0,
# "pport": 0, "vlan-id": 3841,
# "pop-vlan": 1, "id": 251723776},
# {"type": 0, "out-pport": 1,
# "pport": 1, "vlan-id": 3840,
# "pop-vlan": 1, "id": 251658241},
# {"type": 0, "out-pport": 0,
# "pport": 0, "vlan-id": 3840,
# "pop-vlan": 1, "id": 251658240}
# ]}
#
##
{ 'command': 'query-rocker-of-dpa-groups',
'data': { 'name': 'str', '*type': 'uint8' },

View File

@ -5,6 +5,9 @@
# This work is licensed under the terms of the GNU GPL, version 2 or later.
# See the COPYING file in the top-level directory.
##
# = Tracing commands
##
##
# @TraceEventState:
@ -59,6 +62,13 @@
# an error is returned.
#
# Since: 2.2
#
# Example:
#
# -> { "execute": "trace-event-get-state",
# "arguments": { "name": "qemu_memalign" } }
# <- { "return": [ { "name": "qemu_memalign", "state": "disabled" } ] }
#
##
{ 'command': 'trace-event-get-state',
'data': {'name': 'str', '*vcpu': 'int'},
@ -84,6 +94,13 @@
# error is returned.
#
# Since: 2.2
#
# Example:
#
# -> { "execute": "trace-event-set-state",
# "arguments": { "name": "qemu_memalign", "enable": "true" } }
# <- { "return": {} }
#
##
{ 'command': 'trace-event-set-state',
'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool',

View File

@ -697,21 +697,18 @@
# Returns: The length of the initial sublist that has been successfully
# processed. The guest agent maximizes this value. Possible cases:
#
# 0: if the @vcpus list was empty on input. Guest state
# - 0: if the @vcpus list was empty on input. Guest state
# has not been changed. Otherwise,
#
# Error: processing the first node of @vcpus failed for the
# - Error: processing the first node of @vcpus failed for the
# reason returned. Guest state has not been changed.
# Otherwise,
#
# < length(@vcpus): more than zero initial nodes have been processed,
# - < length(@vcpus): more than zero initial nodes have been processed,
# but not the entire @vcpus list. Guest state has
# changed accordingly. To retrieve the error
# (assuming it persists), repeat the call with the
# successfully processed initial sublist removed.
# Otherwise,
#
# length(@vcpus): call successful.
# - length(@vcpus): call successful.
#
# Since: 1.5
##

View File

@ -363,3 +363,15 @@ define unnest-vars
$(eval -include $(patsubst %.o,%.d,$(patsubst %.mo,%.d,$($v))))
$(eval $v := $(filter-out %/,$($v))))
endef
TEXI2MAN = $(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< $@.pod && \
$(POD2MAN) --section=$(subst .,,$(suffix $@)) --center=" " --release=" " $@.pod > $@, \
"GEN","$@")
%.1:
$(call TEXI2MAN)
%.7:
$(call TEXI2MAN)
%.8:
$(call TEXI2MAN)

View File

@ -91,35 +91,154 @@ def error_path(parent):
return res
class QAPISchemaError(Exception):
def __init__(self, schema, msg):
class QAPIError(Exception):
def __init__(self, fname, line, col, incl_info, msg):
Exception.__init__(self)
self.fname = schema.fname
self.fname = fname
self.line = line
self.col = col
self.info = incl_info
self.msg = msg
self.col = 1
self.line = schema.line
for ch in schema.src[schema.line_pos:schema.pos]:
def __str__(self):
loc = "%s:%d" % (self.fname, self.line)
if self.col is not None:
loc += ":%s" % self.col
return error_path(self.info) + "%s: %s" % (loc, self.msg)
class QAPIParseError(QAPIError):
def __init__(self, parser, msg):
col = 1
for ch in parser.src[parser.line_pos:parser.pos]:
if ch == '\t':
self.col = (self.col + 7) % 8 + 1
col = (col + 7) % 8 + 1
else:
self.col += 1
self.info = schema.incl_info
def __str__(self):
return error_path(self.info) + \
"%s:%d:%d: %s" % (self.fname, self.line, self.col, self.msg)
col += 1
QAPIError.__init__(self, parser.fname, parser.line, col,
parser.incl_info, msg)
class QAPIExprError(Exception):
def __init__(self, expr_info, msg):
Exception.__init__(self)
assert expr_info
self.info = expr_info
self.msg = msg
class QAPISemError(QAPIError):
def __init__(self, info, msg):
QAPIError.__init__(self, info['file'], info['line'], None,
info['parent'], msg)
def __str__(self):
return error_path(self.info['parent']) + \
"%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
class QAPIDoc(object):
class Section(object):
def __init__(self, name=None):
# optional section name (argument/member or section name)
self.name = name
# the list of lines for this section
self.content = []
def append(self, line):
self.content.append(line)
def __repr__(self):
return "\n".join(self.content).strip()
class ArgSection(Section):
pass
def __init__(self, parser, info):
# self.parser is used to report errors with QAPIParseError. The
# resulting error position depends on the state of the parser.
# It happens to be the beginning of the comment. More or less
# servicable, but action at a distance.
self.parser = parser
self.info = info
self.symbol = None
self.body = QAPIDoc.Section()
# dict mapping parameter name to ArgSection
self.args = OrderedDict()
# a list of Section
self.sections = []
# the current section
self.section = self.body
# associated expression (to be set by expression parser)
self.expr = None
def has_section(self, name):
"""Return True if we have a section with this name."""
for i in self.sections:
if i.name == name:
return True
return False
def append(self, line):
"""Parse a comment line and add it to the documentation."""
line = line[1:]
if not line:
self._append_freeform(line)
return
if line[0] != ' ':
raise QAPIParseError(self.parser, "Missing space after #")
line = line[1:]
# FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
# recognized, and get silently treated as ordinary text
if self.symbol:
self._append_symbol_line(line)
elif not self.body.content and line.startswith("@"):
if not line.endswith(":"):
raise QAPIParseError(self.parser, "Line should end with :")
self.symbol = line[1:-1]
# FIXME invalid names other than the empty string aren't flagged
if not self.symbol:
raise QAPIParseError(self.parser, "Invalid name")
else:
self._append_freeform(line)
def _append_symbol_line(self, line):
name = line.split(' ', 1)[0]
if name.startswith("@") and name.endswith(":"):
line = line[len(name)+1:]
self._start_args_section(name[1:-1])
elif name in ("Returns:", "Since:",
# those are often singular or plural
"Note:", "Notes:",
"Example:", "Examples:",
"TODO:"):
line = line[len(name)+1:]
self._start_section(name[:-1])
self._append_freeform(line)
def _start_args_section(self, name):
# FIXME invalid names other than the empty string aren't flagged
if not name:
raise QAPIParseError(self.parser, "Invalid parameter name")
if name in self.args:
raise QAPIParseError(self.parser,
"'%s' parameter name duplicated" % name)
if self.sections:
raise QAPIParseError(self.parser,
"'@%s:' can't follow '%s' section"
% (name, self.sections[0].name))
self.section = QAPIDoc.ArgSection(name)
self.args[name] = self.section
def _start_section(self, name=""):
if name in ("Returns", "Since") and self.has_section(name):
raise QAPIParseError(self.parser,
"Duplicated '%s' section" % name)
self.section = QAPIDoc.Section(name)
self.sections.append(self.section)
def _append_freeform(self, line):
in_arg = isinstance(self.section, QAPIDoc.ArgSection)
if (in_arg and self.section.content
and not self.section.content[-1]
and line and not line[0].isspace()):
self._start_section()
if (in_arg or not self.section.name
or not self.section.name.startswith("Example")):
line = line.strip()
self.section.append(line)
class QAPISchemaParser(object):
@ -137,46 +256,58 @@ def __init__(self, fp, previously_included=[], incl_info=None):
self.line = 1
self.line_pos = 0
self.exprs = []
self.docs = []
self.accept()
while self.tok is not None:
expr_info = {'file': fname, 'line': self.line,
'parent': self.incl_info}
info = {'file': fname, 'line': self.line,
'parent': self.incl_info}
if self.tok == '#':
doc = self.get_doc(info)
self.docs.append(doc)
continue
expr = self.get_expr(False)
if isinstance(expr, dict) and "include" in expr:
if len(expr) != 1:
raise QAPIExprError(expr_info,
"Invalid 'include' directive")
raise QAPISemError(info, "Invalid 'include' directive")
include = expr["include"]
if not isinstance(include, str):
raise QAPIExprError(expr_info,
"Value of 'include' must be a string")
raise QAPISemError(info,
"Value of 'include' must be a string")
incl_abs_fname = os.path.join(os.path.dirname(abs_fname),
include)
# catch inclusion cycle
inf = expr_info
inf = info
while inf:
if incl_abs_fname == os.path.abspath(inf['file']):
raise QAPIExprError(expr_info, "Inclusion loop for %s"
% include)
raise QAPISemError(info, "Inclusion loop for %s"
% include)
inf = inf['parent']
# skip multiple include of the same file
if incl_abs_fname in previously_included:
continue
try:
fobj = open(incl_abs_fname, 'r')
except IOError as e:
raise QAPIExprError(expr_info,
'%s: %s' % (e.strerror, include))
raise QAPISemError(info, '%s: %s' % (e.strerror, include))
exprs_include = QAPISchemaParser(fobj, previously_included,
expr_info)
info)
self.exprs.extend(exprs_include.exprs)
self.docs.extend(exprs_include.docs)
else:
expr_elem = {'expr': expr,
'info': expr_info}
'info': info}
if (self.docs
and self.docs[-1].info['file'] == fname
and not self.docs[-1].expr):
self.docs[-1].expr = expr
expr_elem['doc'] = self.docs[-1]
self.exprs.append(expr_elem)
def accept(self):
def accept(self, skip_comment=True):
while True:
self.tok = self.src[self.cursor]
self.pos = self.cursor
@ -184,7 +315,13 @@ def accept(self):
self.val = None
if self.tok == '#':
if self.src[self.cursor] == '#':
# Start of doc comment
skip_comment = False
self.cursor = self.src.find('\n', self.cursor)
if not skip_comment:
self.val = self.src[self.pos:self.cursor]
return
elif self.tok in "{}:,[]":
return
elif self.tok == "'":
@ -194,8 +331,7 @@ def accept(self):
ch = self.src[self.cursor]
self.cursor += 1
if ch == '\n':
raise QAPISchemaError(self,
'Missing terminating "\'"')
raise QAPIParseError(self, 'Missing terminating "\'"')
if esc:
if ch == 'b':
string += '\b'
@ -213,25 +349,25 @@ def accept(self):
ch = self.src[self.cursor]
self.cursor += 1
if ch not in "0123456789abcdefABCDEF":
raise QAPISchemaError(self,
'\\u escape needs 4 '
'hex digits')
raise QAPIParseError(self,
'\\u escape needs 4 '
'hex digits')
value = (value << 4) + int(ch, 16)
# If Python 2 and 3 didn't disagree so much on
# how to handle Unicode, then we could allow
# Unicode string defaults. But most of QAPI is
# ASCII-only, so we aren't losing much for now.
if not value or value > 0x7f:
raise QAPISchemaError(self,
'For now, \\u escape '
'only supports non-zero '
'values up to \\u007f')
raise QAPIParseError(self,
'For now, \\u escape '
'only supports non-zero '
'values up to \\u007f')
string += chr(value)
elif ch in "\\/'\"":
string += ch
else:
raise QAPISchemaError(self,
"Unknown escape \\%s" % ch)
raise QAPIParseError(self,
"Unknown escape \\%s" % ch)
esc = False
elif ch == "\\":
esc = True
@ -259,7 +395,7 @@ def accept(self):
self.line += 1
self.line_pos = self.cursor
elif not self.tok.isspace():
raise QAPISchemaError(self, 'Stray "%s"' % self.tok)
raise QAPIParseError(self, 'Stray "%s"' % self.tok)
def get_members(self):
expr = OrderedDict()
@ -267,24 +403,24 @@ def get_members(self):
self.accept()
return expr
if self.tok != "'":
raise QAPISchemaError(self, 'Expected string or "}"')
raise QAPIParseError(self, 'Expected string or "}"')
while True:
key = self.val
self.accept()
if self.tok != ':':
raise QAPISchemaError(self, 'Expected ":"')
raise QAPIParseError(self, 'Expected ":"')
self.accept()
if key in expr:
raise QAPISchemaError(self, 'Duplicate key "%s"' % key)
raise QAPIParseError(self, 'Duplicate key "%s"' % key)
expr[key] = self.get_expr(True)
if self.tok == '}':
self.accept()
return expr
if self.tok != ',':
raise QAPISchemaError(self, 'Expected "," or "}"')
raise QAPIParseError(self, 'Expected "," or "}"')
self.accept()
if self.tok != "'":
raise QAPISchemaError(self, 'Expected string')
raise QAPIParseError(self, 'Expected string')
def get_values(self):
expr = []
@ -292,20 +428,20 @@ def get_values(self):
self.accept()
return expr
if self.tok not in "{['tfn":
raise QAPISchemaError(self, 'Expected "{", "[", "]", string, '
'boolean or "null"')
raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
'boolean or "null"')
while True:
expr.append(self.get_expr(True))
if self.tok == ']':
self.accept()
return expr
if self.tok != ',':
raise QAPISchemaError(self, 'Expected "," or "]"')
raise QAPIParseError(self, 'Expected "," or "]"')
self.accept()
def get_expr(self, nested):
if self.tok != '{' and not nested:
raise QAPISchemaError(self, 'Expected "{"')
raise QAPIParseError(self, 'Expected "{"')
if self.tok == '{':
self.accept()
expr = self.get_members()
@ -316,9 +452,31 @@ def get_expr(self, nested):
expr = self.val
self.accept()
else:
raise QAPISchemaError(self, 'Expected "{", "[" or string')
raise QAPIParseError(self, 'Expected "{", "[" or string')
return expr
def get_doc(self, info):
if self.val != '##':
raise QAPIParseError(self, "Junk after '##' at start of "
"documentation comment")
doc = QAPIDoc(self, info)
self.accept(False)
while self.tok == '#':
if self.val.startswith('##'):
# End of doc comment
if self.val != '##':
raise QAPIParseError(self, "Junk after '##' at end of "
"documentation comment")
self.accept()
return doc
else:
doc.append(self.val)
self.accept(False)
raise QAPIParseError(self, "Documentation comment must end with '##'")
#
# Semantic analysis of schema expressions
# TODO fold into QAPISchema
@ -375,20 +533,18 @@ def discriminator_find_enum_define(expr):
'[a-zA-Z][a-zA-Z0-9_-]*$')
def check_name(expr_info, source, name, allow_optional=False,
def check_name(info, source, name, allow_optional=False,
enum_member=False):
global valid_name
membername = name
if not isinstance(name, str):
raise QAPIExprError(expr_info,
"%s requires a string name" % source)
raise QAPISemError(info, "%s requires a string name" % source)
if name.startswith('*'):
membername = name[1:]
if not allow_optional:
raise QAPIExprError(expr_info,
"%s does not allow optional name '%s'"
% (source, name))
raise QAPISemError(info, "%s does not allow optional name '%s'"
% (source, name))
# Enum members can start with a digit, because the generated C
# code always prefixes it with the enum name
if enum_member and membername[0].isdigit():
@ -397,8 +553,7 @@ def check_name(expr_info, source, name, allow_optional=False,
# and 'q_obj_*' implicit type names.
if not valid_name.match(membername) or \
c_name(membername, False).startswith('q_'):
raise QAPIExprError(expr_info,
"%s uses invalid name '%s'" % (source, name))
raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
def add_name(name, info, meta, implicit=False):
@ -407,13 +562,11 @@ def add_name(name, info, meta, implicit=False):
# FIXME should reject names that differ only in '_' vs. '.'
# vs. '-', because they're liable to clash in generated C.
if name in all_names:
raise QAPIExprError(info,
"%s '%s' is already defined"
% (all_names[name], name))
raise QAPISemError(info, "%s '%s' is already defined"
% (all_names[name], name))
if not implicit and (name.endswith('Kind') or name.endswith('List')):
raise QAPIExprError(info,
"%s '%s' should not end in '%s'"
% (meta, name, name[-4:]))
raise QAPISemError(info, "%s '%s' should not end in '%s'"
% (meta, name, name[-4:]))
all_names[name] = meta
@ -465,7 +618,7 @@ def is_enum(name):
return find_enum(name) is not None
def check_type(expr_info, source, value, allow_array=False,
def check_type(info, source, value, allow_array=False,
allow_dict=False, allow_optional=False,
allow_metas=[]):
global all_names
@ -476,69 +629,64 @@ def check_type(expr_info, source, value, allow_array=False,
# Check if array type for value is okay
if isinstance(value, list):
if not allow_array:
raise QAPIExprError(expr_info,
"%s cannot be an array" % source)
raise QAPISemError(info, "%s cannot be an array" % source)
if len(value) != 1 or not isinstance(value[0], str):
raise QAPIExprError(expr_info,
"%s: array type must contain single type name"
% source)
raise QAPISemError(info,
"%s: array type must contain single type name" %
source)
value = value[0]
# Check if type name for value is okay
if isinstance(value, str):
if value not in all_names:
raise QAPIExprError(expr_info,
"%s uses unknown type '%s'"
% (source, value))
raise QAPISemError(info, "%s uses unknown type '%s'"
% (source, value))
if not all_names[value] in allow_metas:
raise QAPIExprError(expr_info,
"%s cannot use %s type '%s'"
% (source, all_names[value], value))
raise QAPISemError(info, "%s cannot use %s type '%s'" %
(source, all_names[value], value))
return
if not allow_dict:
raise QAPIExprError(expr_info,
"%s should be a type name" % source)
raise QAPISemError(info, "%s should be a type name" % source)
if not isinstance(value, OrderedDict):
raise QAPIExprError(expr_info,
"%s should be a dictionary or type name" % source)
raise QAPISemError(info,
"%s should be a dictionary or type name" % source)
# value is a dictionary, check that each member is okay
for (key, arg) in value.items():
check_name(expr_info, "Member of %s" % source, key,
check_name(info, "Member of %s" % source, key,
allow_optional=allow_optional)
if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
raise QAPIExprError(expr_info,
"Member of %s uses reserved name '%s'"
% (source, key))
raise QAPISemError(info, "Member of %s uses reserved name '%s'"
% (source, key))
# Todo: allow dictionaries to represent default values of
# an optional argument.
check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
check_type(info, "Member '%s' of %s" % (key, source), arg,
allow_array=True,
allow_metas=['built-in', 'union', 'alternate', 'struct',
'enum'])
def check_command(expr, expr_info):
def check_command(expr, info):
name = expr['command']
boxed = expr.get('boxed', False)
args_meta = ['struct']
if boxed:
args_meta += ['union', 'alternate']
check_type(expr_info, "'data' for command '%s'" % name,
check_type(info, "'data' for command '%s'" % name,
expr.get('data'), allow_dict=not boxed, allow_optional=True,
allow_metas=args_meta)
returns_meta = ['union', 'struct']
if name in returns_whitelist:
returns_meta += ['built-in', 'alternate', 'enum']
check_type(expr_info, "'returns' for command '%s'" % name,
check_type(info, "'returns' for command '%s'" % name,
expr.get('returns'), allow_array=True,
allow_optional=True, allow_metas=returns_meta)
def check_event(expr, expr_info):
def check_event(expr, info):
global events
name = expr['event']
boxed = expr.get('boxed', False)
@ -547,12 +695,12 @@ def check_event(expr, expr_info):
if boxed:
meta += ['union', 'alternate']
events.append(name)
check_type(expr_info, "'data' for event '%s'" % name,
check_type(info, "'data' for event '%s'" % name,
expr.get('data'), allow_dict=not boxed, allow_optional=True,
allow_metas=meta)
def check_union(expr, expr_info):
def check_union(expr, info):
name = expr['union']
base = expr.get('base')
discriminator = expr.get('discriminator')
@ -565,123 +713,117 @@ def check_union(expr, expr_info):
enum_define = None
allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
if base is not None:
raise QAPIExprError(expr_info,
"Simple union '%s' must not have a base"
% name)
raise QAPISemError(info, "Simple union '%s' must not have a base" %
name)
# Else, it's a flat union.
else:
# The object must have a string or dictionary 'base'.
check_type(expr_info, "'base' for union '%s'" % name,
check_type(info, "'base' for union '%s'" % name,
base, allow_dict=True, allow_optional=True,
allow_metas=['struct'])
if not base:
raise QAPIExprError(expr_info,
"Flat union '%s' must have a base"
% name)
raise QAPISemError(info, "Flat union '%s' must have a base"
% name)
base_members = find_base_members(base)
assert base_members
# The value of member 'discriminator' must name a non-optional
# member of the base struct.
check_name(expr_info, "Discriminator of flat union '%s'" % name,
check_name(info, "Discriminator of flat union '%s'" % name,
discriminator)
discriminator_type = base_members.get(discriminator)
if not discriminator_type:
raise QAPIExprError(expr_info,
"Discriminator '%s' is not a member of base "
"struct '%s'"
% (discriminator, base))
raise QAPISemError(info,
"Discriminator '%s' is not a member of base "
"struct '%s'"
% (discriminator, base))
enum_define = find_enum(discriminator_type)
allow_metas = ['struct']
# Do not allow string discriminator
if not enum_define:
raise QAPIExprError(expr_info,
"Discriminator '%s' must be of enumeration "
"type" % discriminator)
raise QAPISemError(info,
"Discriminator '%s' must be of enumeration "
"type" % discriminator)
# Check every branch; don't allow an empty union
if len(members) == 0:
raise QAPIExprError(expr_info,
"Union '%s' cannot have empty 'data'" % name)
raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
for (key, value) in members.items():
check_name(expr_info, "Member of union '%s'" % name, key)
check_name(info, "Member of union '%s'" % name, key)
# Each value must name a known type
check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
check_type(info, "Member '%s' of union '%s'" % (key, name),
value, allow_array=not base, allow_metas=allow_metas)
# If the discriminator names an enum type, then all members
# of 'data' must also be members of the enum type.
if enum_define:
if key not in enum_define['enum_values']:
raise QAPIExprError(expr_info,
"Discriminator value '%s' is not found in "
"enum '%s'" %
(key, enum_define["enum_name"]))
raise QAPISemError(info,
"Discriminator value '%s' is not found in "
"enum '%s'"
% (key, enum_define["enum_name"]))
# If discriminator is user-defined, ensure all values are covered
if enum_define:
for value in enum_define['enum_values']:
if value not in members.keys():
raise QAPIExprError(expr_info,
"Union '%s' data missing '%s' branch"
% (name, value))
raise QAPISemError(info, "Union '%s' data missing '%s' branch"
% (name, value))
def check_alternate(expr, expr_info):
def check_alternate(expr, info):
name = expr['alternate']
members = expr['data']
types_seen = {}
# Check every branch; require at least two branches
if len(members) < 2:
raise QAPIExprError(expr_info,
"Alternate '%s' should have at least two branches "
"in 'data'" % name)
raise QAPISemError(info,
"Alternate '%s' should have at least two branches "
"in 'data'" % name)
for (key, value) in members.items():
check_name(expr_info, "Member of alternate '%s'" % name, key)
check_name(info, "Member of alternate '%s'" % name, key)
# Ensure alternates have no type conflicts.
check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
check_type(info, "Member '%s' of alternate '%s'" % (key, name),
value,
allow_metas=['built-in', 'union', 'struct', 'enum'])
qtype = find_alternate_member_qtype(value)
if not qtype:
raise QAPIExprError(expr_info,
"Alternate '%s' member '%s' cannot use "
"type '%s'" % (name, key, value))
raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
"type '%s'" % (name, key, value))
if qtype in types_seen:
raise QAPIExprError(expr_info,
"Alternate '%s' member '%s' can't "
"be distinguished from member '%s'"
% (name, key, types_seen[qtype]))
raise QAPISemError(info, "Alternate '%s' member '%s' can't "
"be distinguished from member '%s'"
% (name, key, types_seen[qtype]))
types_seen[qtype] = key
def check_enum(expr, expr_info):
def check_enum(expr, info):
name = expr['enum']
members = expr.get('data')
prefix = expr.get('prefix')
if not isinstance(members, list):
raise QAPIExprError(expr_info,
"Enum '%s' requires an array for 'data'" % name)
raise QAPISemError(info,
"Enum '%s' requires an array for 'data'" % name)
if prefix is not None and not isinstance(prefix, str):
raise QAPIExprError(expr_info,
"Enum '%s' requires a string for 'prefix'" % name)
raise QAPISemError(info,
"Enum '%s' requires a string for 'prefix'" % name)
for member in members:
check_name(expr_info, "Member of enum '%s'" % name, member,
check_name(info, "Member of enum '%s'" % name, member,
enum_member=True)
def check_struct(expr, expr_info):
def check_struct(expr, info):
name = expr['struct']
members = expr['data']
check_type(expr_info, "'data' for struct '%s'" % name, members,
check_type(info, "'data' for struct '%s'" % name, members,
allow_dict=True, allow_optional=True)
check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'),
check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
allow_metas=['struct'])
@ -690,27 +832,24 @@ def check_keys(expr_elem, meta, required, optional=[]):
info = expr_elem['info']
name = expr[meta]
if not isinstance(name, str):
raise QAPIExprError(info,
"'%s' key must have a string value" % meta)
raise QAPISemError(info, "'%s' key must have a string value" % meta)
required = required + [meta]
for (key, value) in expr.items():
if key not in required and key not in optional:
raise QAPIExprError(info,
"Unknown key '%s' in %s '%s'"
% (key, meta, name))
raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
% (key, meta, name))
if (key == 'gen' or key == 'success-response') and value is not False:
raise QAPIExprError(info,
"'%s' of %s '%s' should only use false value"
% (key, meta, name))
raise QAPISemError(info,
"'%s' of %s '%s' should only use false value"
% (key, meta, name))
if key == 'boxed' and value is not True:
raise QAPIExprError(info,
"'%s' of %s '%s' should only use true value"
% (key, meta, name))
raise QAPISemError(info,
"'%s' of %s '%s' should only use true value"
% (key, meta, name))
for key in required:
if key not in expr:
raise QAPIExprError(info,
"Key '%s' is missing from %s '%s'"
% (key, meta, name))
raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
% (key, meta, name))
def check_exprs(exprs):
@ -722,6 +861,11 @@ def check_exprs(exprs):
for expr_elem in exprs:
expr = expr_elem['expr']
info = expr_elem['info']
if 'doc' not in expr_elem:
raise QAPISemError(info,
"Expression missing documentation comment")
if 'enum' in expr:
check_keys(expr_elem, 'enum', ['data'], ['prefix'])
add_enum(expr['enum'], info, expr['data'])
@ -743,8 +887,8 @@ def check_exprs(exprs):
check_keys(expr_elem, 'event', [], ['data', 'boxed'])
add_name(expr['event'], info, 'event')
else:
raise QAPIExprError(expr_elem['info'],
"Expression is missing metatype")
raise QAPISemError(expr_elem['info'],
"Expression is missing metatype")
# Try again for hidden UnionKind enum
for expr_elem in exprs:
@ -780,6 +924,88 @@ def check_exprs(exprs):
return exprs
def check_freeform_doc(doc):
if doc.symbol:
raise QAPISemError(doc.info,
"Documention for '%s' is not followed"
" by the definition" % doc.symbol)
body = str(doc.body)
if re.search(r'@\S+:', body, re.MULTILINE):
raise QAPISemError(doc.info,
"Free-form documentation block must not contain"
" @NAME: sections")
def check_definition_doc(doc, expr, info):
for i in ('enum', 'union', 'alternate', 'struct', 'command', 'event'):
if i in expr:
meta = i
break
name = expr[meta]
if doc.symbol != name:
raise QAPISemError(info, "Definition of '%s' follows documentation"
" for '%s'" % (name, doc.symbol))
if doc.has_section('Returns') and 'command' not in expr:
raise QAPISemError(info, "'Returns:' is only valid for commands")
if meta == 'union':
args = expr.get('base', [])
else:
args = expr.get('data', [])
if isinstance(args, str):
return
if isinstance(args, dict):
args = args.keys()
assert isinstance(args, list)
if (meta == 'alternate'
or (meta == 'union' and not expr.get('discriminator'))):
args.append('type')
for arg in args:
if arg[0] == '*':
opt = True
desc = doc.args.get(arg[1:])
else:
opt = False
desc = doc.args.get(arg)
if not desc:
continue
desc_opt = "#optional" in str(desc)
if desc_opt and not opt:
raise QAPISemError(info, "Description has #optional, "
"but the declaration doesn't")
if not desc_opt and opt:
# silently fix the doc
# TODO either fix the schema and make this an error,
# or drop #optional entirely
desc.append("#optional")
doc_args = set(doc.args.keys())
args = set([name.strip('*') for name in args])
if not doc_args.issubset(args):
raise QAPISemError(info, "The following documented members are not in "
"the declaration: %s" % ", ".join(doc_args - args))
def check_docs(docs):
for doc in docs:
for section in doc.args.values() + doc.sections:
content = str(section)
if not content or content.isspace():
raise QAPISemError(doc.info,
"Empty doc section '%s'" % section.name)
if not doc.expr:
check_freeform_doc(doc)
else:
check_definition_doc(doc, doc.expr, doc.info)
return docs
#
# Schema compiler frontend
#
@ -978,8 +1204,8 @@ def __init__(self, name, info, base, local_members, variants):
def check(self, schema):
if self.members is False: # check for cycles
raise QAPIExprError(self.info,
"Object %s contains itself" % self.name)
raise QAPISemError(self.info,
"Object %s contains itself" % self.name)
if self.members:
return
self.members = False # mark as being checked
@ -1051,12 +1277,11 @@ def set_owner(self, name):
def check_clash(self, info, seen):
cname = c_name(self.name)
if cname.lower() != cname and self.owner not in case_whitelist:
raise QAPIExprError(info,
"%s should not use uppercase" % self.describe())
raise QAPISemError(info,
"%s should not use uppercase" % self.describe())
if cname in seen:
raise QAPIExprError(info,
"%s collides with %s"
% (self.describe(), seen[cname].describe()))
raise QAPISemError(info, "%s collides with %s" %
(self.describe(), seen[cname].describe()))
seen[cname] = self
def _pretty_owner(self):
@ -1201,14 +1426,13 @@ def check(self, schema):
self.arg_type.check(schema)
if self.boxed:
if self.arg_type.is_empty():
raise QAPIExprError(self.info,
"Cannot use 'boxed' with empty type")
raise QAPISemError(self.info,
"Cannot use 'boxed' with empty type")
else:
assert not isinstance(self.arg_type, QAPISchemaAlternateType)
assert not self.arg_type.variants
elif self.boxed:
raise QAPIExprError(self.info,
"Use of 'boxed' requires 'data'")
raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
if self._ret_type_name:
self.ret_type = schema.lookup_type(self._ret_type_name)
assert isinstance(self.ret_type, QAPISchemaType)
@ -1235,14 +1459,13 @@ def check(self, schema):
self.arg_type.check(schema)
if self.boxed:
if self.arg_type.is_empty():
raise QAPIExprError(self.info,
"Cannot use 'boxed' with empty type")
raise QAPISemError(self.info,
"Cannot use 'boxed' with empty type")
else:
assert not isinstance(self.arg_type, QAPISchemaAlternateType)
assert not self.arg_type.variants
elif self.boxed:
raise QAPIExprError(self.info,
"Use of 'boxed' requires 'data'")
raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
def visit(self, visitor):
visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
@ -1251,14 +1474,16 @@ def visit(self, visitor):
class QAPISchema(object):
def __init__(self, fname):
try:
self.exprs = check_exprs(QAPISchemaParser(open(fname, "r")).exprs)
parser = QAPISchemaParser(open(fname, "r"))
self.exprs = check_exprs(parser.exprs)
self.docs = check_docs(parser.docs)
self._entity_dict = {}
self._predefining = True
self._def_predefineds()
self._predefining = False
self._def_exprs()
self.check()
except (QAPISchemaError, QAPIExprError) as err:
except QAPIError as err:
print >>sys.stderr, err
exit(1)

271
scripts/qapi2texi.py Executable file
View File

@ -0,0 +1,271 @@
#!/usr/bin/env python
# QAPI texi generator
#
# This work is licensed under the terms of the GNU LGPL, version 2+.
# See the COPYING file in the top-level directory.
"""This script produces the documentation of a qapi schema in texinfo format"""
import re
import sys
import qapi
COMMAND_FMT = """
@deftypefn {type} {{}} {name}
{body}
@end deftypefn
""".format
ENUM_FMT = """
@deftp Enum {name}
{body}
@end deftp
""".format
STRUCT_FMT = """
@deftp {{{type}}} {name}
{body}
@end deftp
""".format
EXAMPLE_FMT = """@example
{code}
@end example
""".format
def subst_strong(doc):
"""Replaces *foo* by @strong{foo}"""
return re.sub(r'\*([^*\n]+)\*', r'@emph{\1}', doc)
def subst_emph(doc):
"""Replaces _foo_ by @emph{foo}"""
return re.sub(r'\b_([^_\n]+)_\b', r' @emph{\1} ', doc)
def subst_vars(doc):
"""Replaces @var by @code{var}"""
return re.sub(r'@([\w-]+)', r'@code{\1}', doc)
def subst_braces(doc):
"""Replaces {} with @{ @}"""
return doc.replace("{", "@{").replace("}", "@}")
def texi_example(doc):
"""Format @example"""
# TODO: Neglects to escape @ characters.
# We should probably escape them in subst_braces(), and rename the
# function to subst_special() or subs_texi_special(). If we do that, we
# need to delay it until after subst_vars() in texi_format().
doc = subst_braces(doc).strip('\n')
return EXAMPLE_FMT(code=doc)
def texi_format(doc):
"""
Format documentation
Lines starting with:
- |: generates an @example
- =: generates @section
- ==: generates @subsection
- 1. or 1): generates an @enumerate @item
- */-: generates an @itemize list
"""
lines = []
doc = subst_braces(doc)
doc = subst_vars(doc)
doc = subst_emph(doc)
doc = subst_strong(doc)
inlist = ""
lastempty = False
for line in doc.split('\n'):
empty = line == ""
# FIXME: Doing this in a single if / elif chain is
# problematic. For instance, a line without markup terminates
# a list if it follows a blank line (reaches the final elif),
# but a line with some *other* markup, such as a = title
# doesn't.
#
# Make sure to update section "Documentation markup" in
# docs/qapi-code-gen.txt when fixing this.
if line.startswith("| "):
line = EXAMPLE_FMT(code=line[2:])
elif line.startswith("= "):
line = "@section " + line[2:]
elif line.startswith("== "):
line = "@subsection " + line[3:]
elif re.match(r'^([0-9]*\.) ', line):
if not inlist:
lines.append("@enumerate")
inlist = "enumerate"
line = line[line.find(" ")+1:]
lines.append("@item")
elif re.match(r'^[*-] ', line):
if not inlist:
lines.append("@itemize %s" % {'*': "@bullet",
'-': "@minus"}[line[0]])
inlist = "itemize"
lines.append("@item")
line = line[2:]
elif lastempty and inlist:
lines.append("@end %s\n" % inlist)
inlist = ""
lastempty = empty
lines.append(line)
if inlist:
lines.append("@end %s\n" % inlist)
return "\n".join(lines)
def texi_body(doc):
"""
Format the body of a symbol documentation:
- main body
- table of arguments
- followed by "Returns/Notes/Since/Example" sections
"""
body = texi_format(str(doc.body)) + "\n"
if doc.args:
body += "@table @asis\n"
for arg, section in doc.args.iteritems():
desc = str(section)
opt = ''
if "#optional" in desc:
desc = desc.replace("#optional", "")
opt = ' (optional)'
body += "@item @code{'%s'}%s\n%s\n" % (arg, opt,
texi_format(desc))
body += "@end table\n"
for section in doc.sections:
name, doc = (section.name, str(section))
func = texi_format
if name.startswith("Example"):
func = texi_example
if name:
# FIXME the indentation produced by @quotation in .txt and
# .html output is confusing
body += "\n@quotation %s\n%s\n@end quotation" % \
(name, func(doc))
else:
body += func(doc)
return body
def texi_alternate(expr, doc):
"""Format an alternate to texi"""
body = texi_body(doc)
return STRUCT_FMT(type="Alternate",
name=doc.symbol,
body=body)
def texi_union(expr, doc):
"""Format a union to texi"""
discriminator = expr.get("discriminator")
if discriminator:
union = "Flat Union"
else:
union = "Simple Union"
body = texi_body(doc)
return STRUCT_FMT(type=union,
name=doc.symbol,
body=body)
def texi_enum(expr, doc):
"""Format an enum to texi"""
for i in expr['data']:
if i not in doc.args:
doc.args[i] = ''
body = texi_body(doc)
return ENUM_FMT(name=doc.symbol,
body=body)
def texi_struct(expr, doc):
"""Format a struct to texi"""
body = texi_body(doc)
return STRUCT_FMT(type="Struct",
name=doc.symbol,
body=body)
def texi_command(expr, doc):
"""Format a command to texi"""
body = texi_body(doc)
return COMMAND_FMT(type="Command",
name=doc.symbol,
body=body)
def texi_event(expr, doc):
"""Format an event to texi"""
body = texi_body(doc)
return COMMAND_FMT(type="Event",
name=doc.symbol,
body=body)
def texi_expr(expr, doc):
"""Format an expr to texi"""
(kind, _) = expr.items()[0]
fmt = {"command": texi_command,
"struct": texi_struct,
"enum": texi_enum,
"union": texi_union,
"alternate": texi_alternate,
"event": texi_event}[kind]
return fmt(expr, doc)
def texi(docs):
"""Convert QAPI schema expressions to texi documentation"""
res = []
for doc in docs:
expr = doc.expr
if not expr:
res.append(texi_body(doc))
continue
try:
doc = texi_expr(expr, doc)
res.append(doc)
except:
print >>sys.stderr, "error at @%s" % doc.info
raise
return '\n'.join(res)
def main(argv):
"""Takes schema argument, prints result to stdout"""
if len(argv) != 2:
print >>sys.stderr, "%s: need exactly 1 argument: SCHEMA" % argv[0]
sys.exit(1)
schema = qapi.QAPISchema(argv[1])
print texi(schema.docs)
if __name__ == "__main__":
main(sys.argv)

View File

@ -37,6 +37,7 @@ $inf = "";
$ibase = "";
@ipath = ();
$encoding = undef;
@args = ();
while ($_ = shift) {
if (/^-D(.*)$/) {
@ -162,7 +163,8 @@ while(<$inf>) {
if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) {
$skipping = pop @skstack;
next;
} elsif ($ended =~ /^(?:example|smallexample|display)$/) {
} elsif ($ended =~ /^(?:example|smallexample|display
|quotation|deftp|deftypefn)$/x) {
$shift = "";
$_ = ""; # need a paragraph break
} elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) {
@ -303,6 +305,7 @@ while(<$inf>) {
$ic =~ s/\@(?:code|kbd)/C/;
$ic =~ s/\@(?:dfn|var|emph|cite|i)/I/;
$ic =~ s/\@(?:file)/F/;
$ic =~ s/\@(?:asis)//;
$_ = "\n=over 4\n";
};
@ -323,10 +326,54 @@ while(<$inf>) {
$_ = "\n=item ".join (" : ", @columns)."\n";
};
/^\@(quotation)\s*(.+)?$/ and do {
push @endwstack, $endw;
$endw = $1;
$_ = "\n$2:"
};
/^{(.*)}$|^(.*)$/ and $#args > 0 and do {
$kind = $args[0];
$arguments = $1 // "";
if ($endw eq "deftypefn") {
$ret = $args[1];
$fname = "B<$args[2]>";
$_ = $ret ? "$ret " : "";
$_ .= "$fname $arguments ($kind)";
} else {
$_ = "B<$args[1]> ($kind)\n\n$arguments";
}
@args = ();
};
/^\@(deftp)\s*(.+)?$/ and do {
push @endwstack, $endw;
$endw = $1;
$arg = $2;
$arg =~ s/{([^}]*)}/$1/g;
$arg =~ s/\@$//;
@args = split (/ /, $arg);
$_ = "";
};
/^\@(deftypefn)\s*(.+)?$/ and do {
push @endwstack, $endw;
$endw = $1;
$arg = $2;
$arg =~ s/{([^}]*)}/$1/g;
$arg =~ s/\@$//;
@args = split (/ /, $arg);
$_ = "";
};
/^\@itemx?\s*(.+)?$/ and do {
if (defined $1) {
# Entity escapes prevent munging by the <> processing below.
$_ = "\n=item $ic\&LT;$1\&GT;\n";
if ($ic eq "") {
$_ = "\n=item $1\n";
} else {
# Entity escapes prevent munging by the <> processing below.
$_ = "\n=item $ic\&LT;$1\&GT;\n";
}
} else {
$_ = "\n=item $ic\n";
$ic =~ y/A-Ya-y/B-Zb-z/;
@ -388,6 +435,7 @@ sub postprocess
s/\@sc\{([^\}]*)\}/\U$1/g;
s/\@file\{([^\}]*)\}/F<$1>/g;
s/\@w\{([^\}]*)\}/S<$1>/g;
s/\@t\{([^\}]*)\}/$1/g;
s/\@(?:dmn|math)\{([^\}]*)\}/$1/g;
# keep references of the form @ref{...}, print them bold

View File

@ -352,6 +352,24 @@ qapi-schema += base-cycle-direct.json
qapi-schema += base-cycle-indirect.json
qapi-schema += command-int.json
qapi-schema += comments.json
qapi-schema += doc-bad-args.json
qapi-schema += doc-bad-symbol.json
qapi-schema += doc-duplicated-arg.json
qapi-schema += doc-duplicated-return.json
qapi-schema += doc-duplicated-since.json
qapi-schema += doc-empty-arg.json
qapi-schema += doc-empty-section.json
qapi-schema += doc-empty-symbol.json
qapi-schema += doc-interleaved-section.json
qapi-schema += doc-invalid-end.json
qapi-schema += doc-invalid-end2.json
qapi-schema += doc-invalid-return.json
qapi-schema += doc-invalid-section.json
qapi-schema += doc-invalid-start.json
qapi-schema += doc-missing-colon.json
qapi-schema += doc-missing-expr.json
qapi-schema += doc-missing-space.json
qapi-schema += doc-optional.json
qapi-schema += double-data.json
qapi-schema += double-type.json
qapi-schema += duplicate-key.json
@ -445,6 +463,8 @@ qapi-schema += union-optional-branch.json
qapi-schema += union-unknown.json
qapi-schema += unknown-escape.json
qapi-schema += unknown-expr-key.json
check-qapi-schema-y := $(addprefix tests/qapi-schema/, $(qapi-schema))
GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \

View File

@ -1 +1 @@
tests/qapi-schema/alternate-any.json:2: Alternate 'Alt' member 'one' cannot use type 'any'
tests/qapi-schema/alternate-any.json:6: Alternate 'Alt' member 'one' cannot use type 'any'

View File

@ -1,4 +1,8 @@
# we do not allow the 'any' type as an alternate branch
##
# @Alt:
##
{ 'alternate': 'Alt',
'data': { 'one': 'any',
'two': 'int' } }

View File

@ -1 +1 @@
tests/qapi-schema/alternate-array.json:5: Member 'two' of alternate 'Alt' cannot be an array
tests/qapi-schema/alternate-array.json:12: Member 'two' of alternate 'Alt' cannot be an array

View File

@ -1,7 +1,14 @@
# we do not allow array branches in alternates
##
# @One:
##
# TODO: should we support this?
{ 'struct': 'One',
'data': { 'name': 'str' } }
##
# @Alt:
##
{ 'alternate': 'Alt',
'data': { 'one': 'One',
'two': [ 'int' ] } }

View File

@ -1 +1 @@
tests/qapi-schema/alternate-base.json:4: Unknown key 'base' in alternate 'Alt'
tests/qapi-schema/alternate-base.json:11: Unknown key 'base' in alternate 'Alt'

View File

@ -1,6 +1,13 @@
# we reject alternate with base type
##
# @Base:
##
{ 'struct': 'Base',
'data': { 'string': 'str' } }
##
# @Alt:
##
{ 'alternate': 'Alt',
'base': 'Base',
'data': { 'number': 'int' } }

View File

@ -1 +1 @@
tests/qapi-schema/alternate-clash.json:7: 'a_b' (branch of Alt1) collides with 'a-b' (branch of Alt1)
tests/qapi-schema/alternate-clash.json:11: 'a_b' (branch of Alt1) collides with 'a-b' (branch of Alt1)

View File

@ -4,5 +4,9 @@
# TODO: In the future, if alternates are simplified to not generate
# the implicit Alt1Kind enum, we would still have a collision with the
# resulting C union trying to have two members named 'a_b'.
##
# @Alt1:
##
{ 'alternate': 'Alt1',
'data': { 'a-b': 'str', 'a_b': 'int' } }

View File

@ -1 +1 @@
tests/qapi-schema/alternate-conflict-dict.json:6: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
tests/qapi-schema/alternate-conflict-dict.json:16: Alternate 'Alt' member 'two' can't be distinguished from member 'one'

View File

@ -1,8 +1,18 @@
# we reject alternates with multiple object branches
##
# @One:
##
{ 'struct': 'One',
'data': { 'name': 'str' } }
##
# @Two:
##
{ 'struct': 'Two',
'data': { 'value': 'int' } }
##
# @Alt:
##
{ 'alternate': 'Alt',
'data': { 'one': 'One',
'two': 'Two' } }

View File

@ -1 +1 @@
tests/qapi-schema/alternate-conflict-string.json:4: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
tests/qapi-schema/alternate-conflict-string.json:11: Alternate 'Alt' member 'two' can't be distinguished from member 'one'

View File

@ -1,6 +1,13 @@
# we reject alternates with multiple string-like branches
##
# @Enum:
##
{ 'enum': 'Enum',
'data': [ 'hello', 'world' ] }
##
# @Alt:
##
{ 'alternate': 'Alt',
'data': { 'one': 'str',
'two': 'Enum' } }

View File

@ -1 +1 @@
tests/qapi-schema/alternate-empty.json:2: Alternate 'Alt' should have at least two branches in 'data'
tests/qapi-schema/alternate-empty.json:6: Alternate 'Alt' should have at least two branches in 'data'

View File

@ -1,2 +1,6 @@
# alternates must list at least two types to be useful
##
# @Alt:
##
{ 'alternate': 'Alt', 'data': { 'i': 'int' } }

View File

@ -1 +1 @@
tests/qapi-schema/alternate-nested.json:4: Member 'nested' of alternate 'Alt2' cannot use alternate type 'Alt1'
tests/qapi-schema/alternate-nested.json:11: Member 'nested' of alternate 'Alt2' cannot use alternate type 'Alt1'

View File

@ -1,5 +1,12 @@
# we reject a nested alternate branch
##
# @Alt1:
##
{ 'alternate': 'Alt1',
'data': { 'name': 'str', 'value': 'int' } }
##
# @Alt2:
##
{ 'alternate': 'Alt2',
'data': { 'nested': 'Alt1', 'b': 'bool' } }

View File

@ -1 +1 @@
tests/qapi-schema/alternate-unknown.json:2: Member 'unknown' of alternate 'Alt' uses unknown type 'MissingType'
tests/qapi-schema/alternate-unknown.json:6: Member 'unknown' of alternate 'Alt' uses unknown type 'MissingType'

View File

@ -1,3 +1,7 @@
# we reject an alternate with unknown type in branch
##
# @Alt:
##
{ 'alternate': 'Alt',
'data': { 'unknown': 'MissingType', 'i': 'int' } }

View File

@ -1 +1 @@
tests/qapi-schema/args-alternate.json:3: 'data' for command 'oops' cannot use alternate type 'Alt'
tests/qapi-schema/args-alternate.json:11: 'data' for command 'oops' cannot use alternate type 'Alt'

View File

@ -1,3 +1,11 @@
# we do not allow alternate arguments
##
# @Alt:
##
{ 'alternate': 'Alt', 'data': { 'case1': 'int', 'case2': 'str' } }
##
# @oops:
##
{ 'command': 'oops', 'data': 'Alt' }

View File

@ -1 +1 @@
tests/qapi-schema/args-any.json:2: 'data' for command 'oops' cannot use built-in type 'any'
tests/qapi-schema/args-any.json:6: 'data' for command 'oops' cannot use built-in type 'any'

View File

@ -1,2 +1,6 @@
# we do not allow an 'any' argument
##
# @oops:
##
{ 'command': 'oops', 'data': 'any' }

View File

@ -1 +1 @@
tests/qapi-schema/args-array-empty.json:2: Member 'empty' of 'data' for command 'oops': array type must contain single type name
tests/qapi-schema/args-array-empty.json:6: Member 'empty' of 'data' for command 'oops': array type must contain single type name

View File

@ -1,2 +1,6 @@
# we reject an array for data if it does not contain a known type
##
# @oops:
##
{ 'command': 'oops', 'data': { 'empty': [ ] } }

View File

@ -1 +1 @@
tests/qapi-schema/args-array-unknown.json:2: Member 'array' of 'data' for command 'oops' uses unknown type 'NoSuchType'
tests/qapi-schema/args-array-unknown.json:6: Member 'array' of 'data' for command 'oops' uses unknown type 'NoSuchType'

View File

@ -1,2 +1,6 @@
# we reject an array for data if it does not contain a known type
##
# @oops:
##
{ 'command': 'oops', 'data': { 'array': [ 'NoSuchType' ] } }

View File

@ -1 +1 @@
tests/qapi-schema/args-bad-boxed.json:2: 'boxed' of command 'foo' should only use true value
tests/qapi-schema/args-bad-boxed.json:6: 'boxed' of command 'foo' should only use true value

View File

@ -1,2 +1,6 @@
# 'boxed' should only appear with value true
##
# @foo:
##
{ 'command': 'foo', 'boxed': false }

View File

@ -1 +1 @@
tests/qapi-schema/args-boxed-anon.json:2: 'data' for command 'foo' should be a type name
tests/qapi-schema/args-boxed-anon.json:6: 'data' for command 'foo' should be a type name

View File

@ -1,2 +1,6 @@
# 'boxed' can only be used with named types
##
# @foo:
##
{ 'command': 'foo', 'boxed': true, 'data': { 'string': 'str' } }

View File

@ -1 +1 @@
tests/qapi-schema/args-boxed-empty.json:3: Cannot use 'boxed' with empty type
tests/qapi-schema/args-boxed-empty.json:11: Cannot use 'boxed' with empty type

View File

@ -1,3 +1,11 @@
# 'boxed' requires a non-empty type
##
# @Empty:
##
{ 'struct': 'Empty', 'data': {} }
##
# @foo:
##
{ 'command': 'foo', 'boxed': true, 'data': 'Empty' }

View File

@ -1 +1 @@
tests/qapi-schema/args-boxed-string.json:2: 'data' for command 'foo' cannot use built-in type 'str'
tests/qapi-schema/args-boxed-string.json:6: 'data' for command 'foo' cannot use built-in type 'str'

View File

@ -1,2 +1,6 @@
# 'boxed' requires a complex (not built-in) type
##
# @foo:
##
{ 'command': 'foo', 'boxed': true, 'data': 'str' }

View File

@ -1 +1 @@
tests/qapi-schema/args-int.json:2: 'data' for command 'oops' cannot use built-in type 'int'
tests/qapi-schema/args-int.json:6: 'data' for command 'oops' cannot use built-in type 'int'

View File

@ -1,2 +1,6 @@
# we reject commands where data is not an array or complex type
##
# @oops:
##
{ 'command': 'oops', 'data': 'int' }

View File

@ -1 +1 @@
tests/qapi-schema/args-invalid.json:1: 'data' for command 'foo' should be a dictionary or type name
tests/qapi-schema/args-invalid.json:4: 'data' for command 'foo' should be a dictionary or type name

View File

@ -1,2 +1,5 @@
##
# @foo:
##
{ 'command': 'foo',
'data': false }

View File

@ -1 +1 @@
tests/qapi-schema/args-member-array-bad.json:2: Member 'member' of 'data' for command 'oops': array type must contain single type name
tests/qapi-schema/args-member-array-bad.json:6: Member 'member' of 'data' for command 'oops': array type must contain single type name

View File

@ -1,2 +1,6 @@
# we reject data if it does not contain a valid array type
##
# @oops:
##
{ 'command': 'oops', 'data': { 'member': [ { 'nested': 'str' } ] } }

View File

@ -1 +1 @@
tests/qapi-schema/args-member-case.json:2: 'Arg' (parameter of no-way-this-will-get-whitelisted) should not use uppercase
tests/qapi-schema/args-member-case.json:6: 'Arg' (parameter of no-way-this-will-get-whitelisted) should not use uppercase

View File

@ -1,2 +1,6 @@
# Member names should be 'lower-case' unless the struct/command is whitelisted
##
# @no-way-this-will-get-whitelisted:
##
{ 'command': 'no-way-this-will-get-whitelisted', 'data': { 'Arg': 'int' } }

View File

@ -1 +1 @@
tests/qapi-schema/args-member-unknown.json:2: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType'
tests/qapi-schema/args-member-unknown.json:6: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType'

View File

@ -1,2 +1,6 @@
# we reject data if it does not contain a known type
##
# @oops:
##
{ 'command': 'oops', 'data': { 'member': 'NoSuchType' } }

View File

@ -1 +1 @@
tests/qapi-schema/args-name-clash.json:4: 'a_b' (parameter of oops) collides with 'a-b' (parameter of oops)
tests/qapi-schema/args-name-clash.json:8: 'a_b' (parameter of oops) collides with 'a-b' (parameter of oops)

View File

@ -1,4 +1,8 @@
# C member name collision
# Reject members that clash when mapped to C names (we would have two 'a_b'
# members).
##
# @oops:
##
{ 'command': 'oops', 'data': { 'a-b': 'str', 'a_b': 'str' } }

View File

@ -1 +1 @@
tests/qapi-schema/args-union.json:3: 'data' for command 'oops' cannot use union type 'Uni'
tests/qapi-schema/args-union.json:10: 'data' for command 'oops' cannot use union type 'Uni'

View File

@ -1,3 +1,10 @@
# use of union arguments requires 'boxed':true
##
# @Uni:
##
{ 'union': 'Uni', 'data': { 'case1': 'int', 'case2': 'str' } }
##
# oops:
##
{ 'command': 'oops', 'data': 'Uni' }

View File

@ -1 +1 @@
tests/qapi-schema/args-unknown.json:2: 'data' for command 'oops' uses unknown type 'NoSuchType'
tests/qapi-schema/args-unknown.json:6: 'data' for command 'oops' uses unknown type 'NoSuchType'

View File

@ -1,2 +1,6 @@
# we reject data if it does not contain a known type
##
# @oops:
##
{ 'command': 'oops', 'data': 'NoSuchType' }

View File

@ -1 +1 @@
tests/qapi-schema/bad-base.json:3: 'base' for struct 'MyType' cannot use union type 'Union'
tests/qapi-schema/bad-base.json:10: 'base' for struct 'MyType' cannot use union type 'Union'

View File

@ -1,3 +1,10 @@
# we reject a base that is not a struct
##
# @Union:
##
{ 'union': 'Union', 'data': { 'a': 'int', 'b': 'str' } }
##
# @MyType:
##
{ 'struct': 'MyType', 'base': 'Union', 'data': { 'c': 'int' } }

View File

@ -1 +1 @@
tests/qapi-schema/bad-data.json:2: 'data' for command 'oops' cannot be an array
tests/qapi-schema/bad-data.json:6: 'data' for command 'oops' cannot be an array

View File

@ -1,2 +1,6 @@
# we ensure 'data' is a dictionary for all but enums
##
# @oops:
##
{ 'command': 'oops', 'data': [ ] }

View File

@ -1 +1 @@
tests/qapi-schema/bad-ident.json:2: 'struct' does not allow optional name '*oops'
tests/qapi-schema/bad-ident.json:6: 'struct' does not allow optional name '*oops'

View File

@ -1,2 +1,6 @@
# we reject creating a type name with bad name
##
# @*oops:
##
{ 'struct': '*oops', 'data': { 'i': 'int' } }

View File

@ -1 +1 @@
tests/qapi-schema/bad-type-bool.json:2: 'struct' key must have a string value
tests/qapi-schema/bad-type-bool.json:6: 'struct' key must have a string value

View File

@ -1,2 +1,6 @@
# we reject an expression with a metatype that is not a string
##
# @true:
##
{ 'struct': true, 'data': { } }

View File

@ -1 +1 @@
tests/qapi-schema/bad-type-dict.json:2: 'command' key must have a string value
tests/qapi-schema/bad-type-dict.json:6: 'command' key must have a string value

View File

@ -1,2 +1,6 @@
# we reject an expression with a metatype that is not a string
##
# @foo:
##
{ 'command': { } }

View File

@ -1 +1 @@
tests/qapi-schema/base-cycle-direct.json:2: Object Loopy contains itself
tests/qapi-schema/base-cycle-direct.json:6: Object Loopy contains itself

View File

@ -1,2 +1,6 @@
# we reject a loop in base classes
##
# @Loopy:
##
{ 'struct': 'Loopy', 'base': 'Loopy', 'data': {} }

View File

@ -1 +1 @@
tests/qapi-schema/base-cycle-indirect.json:2: Object Base1 contains itself
tests/qapi-schema/base-cycle-indirect.json:6: Object Base1 contains itself

View File

@ -1,3 +1,10 @@
# we reject a loop in base classes
##
# @Base1:
##
{ 'struct': 'Base1', 'base': 'Base2', 'data': {} }
##
# @Base2:
##
{ 'struct': 'Base2', 'base': 'Base1', 'data': {} }

View File

@ -1 +1 @@
tests/qapi-schema/command-int.json:2: built-in 'int' is already defined
tests/qapi-schema/command-int.json:6: built-in 'int' is already defined

View File

@ -1,2 +1,6 @@
# we reject collisions between commands and types
##
# @int:
##
{ 'command': 'int', 'data': { 'character': 'str' } }

View File

@ -1,4 +1,8 @@
# Unindented comment
##
# @Status:
##
{ 'enum': 'Status', # Comment to the right of code
# Indented comment
'data': [ 'good', 'bad', 'ugly' ] }

View File

@ -2,3 +2,4 @@ enum QType ['none', 'qnull', 'qint', 'qstring', 'qdict', 'qlist', 'qfloat', 'qbo
prefix QTYPE
enum Status ['good', 'bad', 'ugly']
object q_empty
doc symbol=Status expr=('enum', 'Status')

View File

@ -0,0 +1 @@
tests/qapi-schema/doc-bad-args.json:3: The following documented members are not in the declaration: b

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,8 @@
# Arguments listed in the doc comment must exist in the actual schema
##
# @foo:
# @a: a
# @b: b
##
{ 'command': 'foo', 'data': {'a': 'int'} }

View File

View File

@ -0,0 +1 @@
tests/qapi-schema/doc-bad-symbol.json:3: Definition of 'foo' follows documentation for 'food'

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,6 @@
# Documentation symbol mismatch with expression
##
# @food:
##
{ 'command': 'foo', 'data': {'a': 'int'} }

Some files were not shown because too many files have changed in this diff Show More