mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-24 03:59:52 +00:00
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:
commit
23eb9e6b6d
11
.gitignore
vendored
11
.gitignore
vendored
@ -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
|
||||
|
94
Makefile
94
Makefile
@ -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
2
configure
vendored
@ -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"
|
||||
|
@ -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
78
docs/qemu-ga-ref.texi
Normal 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
78
docs/qemu-qmp-ref.texi
Normal 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
BIN
docs/qemu_logo.pdf
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -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.
|
@ -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
|
||||
|
||||
|
1339
qapi-schema.json
1339
qapi-schema.json
File diff suppressed because it is too large
Load Diff
@ -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',
|
||||
|
@ -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' } }
|
||||
|
@ -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'] }
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
# -*- Mode: Python -*-
|
||||
#
|
||||
# QAPI crypto definitions
|
||||
|
||||
##
|
||||
# = QAPI crypto definitions
|
||||
##
|
||||
|
||||
##
|
||||
# @QCryptoTLSCredsEndpoint:
|
||||
|
248
qapi/event.json
248
qapi/event.json
@ -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' } }
|
||||
|
@ -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
|
||||
##
|
||||
|
@ -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' },
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
##
|
||||
|
12
rules.mak
12
rules.mak
@ -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)
|
||||
|
585
scripts/qapi.py
585
scripts/qapi.py
@ -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 @@ class QAPISchemaParser(object):
|
||||
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 @@ class QAPISchemaParser(object):
|
||||
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 @@ class QAPISchemaParser(object):
|
||||
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 @@ class QAPISchemaParser(object):
|
||||
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 @@ class QAPISchemaParser(object):
|
||||
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 @@ class QAPISchemaParser(object):
|
||||
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 @@ class QAPISchemaParser(object):
|
||||
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 @@ class QAPISchemaParser(object):
|
||||
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 @@ valid_name = re.compile('^(__[a-zA-Z0-9.-]+_)?'
|
||||
'[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 @@ class QAPISchemaObjectType(QAPISchemaType):
|
||||
|
||||
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 @@ class QAPISchemaMember(object):
|
||||
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 @@ class QAPISchemaCommand(QAPISchemaEntity):
|
||||
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 @@ class QAPISchemaEvent(QAPISchemaEntity):
|
||||
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 @@ class QAPISchemaEvent(QAPISchemaEntity):
|
||||
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
271
scripts/qapi2texi.py
Executable 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)
|
@ -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\<$1\>\n";
|
||||
if ($ic eq "") {
|
||||
$_ = "\n=item $1\n";
|
||||
} else {
|
||||
# Entity escapes prevent munging by the <> processing below.
|
||||
$_ = "\n=item $ic\<$1\>\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
|
||||
|
@ -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 \
|
||||
|
@ -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'
|
||||
|
@ -1,4 +1,8 @@
|
||||
# we do not allow the 'any' type as an alternate branch
|
||||
|
||||
##
|
||||
# @Alt:
|
||||
##
|
||||
{ 'alternate': 'Alt',
|
||||
'data': { 'one': 'any',
|
||||
'two': 'int' } }
|
||||
|
@ -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
|
||||
|
@ -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' ] } }
|
||||
|
@ -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'
|
||||
|
@ -1,6 +1,13 @@
|
||||
# we reject alternate with base type
|
||||
|
||||
##
|
||||
# @Base:
|
||||
##
|
||||
{ 'struct': 'Base',
|
||||
'data': { 'string': 'str' } }
|
||||
##
|
||||
# @Alt:
|
||||
##
|
||||
{ 'alternate': 'Alt',
|
||||
'base': 'Base',
|
||||
'data': { 'number': 'int' } }
|
||||
|
@ -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)
|
||||
|
@ -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' } }
|
||||
|
@ -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'
|
||||
|
@ -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' } }
|
||||
|
@ -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'
|
||||
|
@ -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' } }
|
||||
|
@ -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'
|
||||
|
@ -1,2 +1,6 @@
|
||||
# alternates must list at least two types to be useful
|
||||
|
||||
##
|
||||
# @Alt:
|
||||
##
|
||||
{ 'alternate': 'Alt', 'data': { 'i': 'int' } }
|
||||
|
@ -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'
|
||||
|
@ -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' } }
|
||||
|
@ -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'
|
||||
|
@ -1,3 +1,7 @@
|
||||
# we reject an alternate with unknown type in branch
|
||||
|
||||
##
|
||||
# @Alt:
|
||||
##
|
||||
{ 'alternate': 'Alt',
|
||||
'data': { 'unknown': 'MissingType', 'i': 'int' } }
|
||||
|
@ -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'
|
||||
|
@ -1,3 +1,11 @@
|
||||
# we do not allow alternate arguments
|
||||
|
||||
##
|
||||
# @Alt:
|
||||
##
|
||||
{ 'alternate': 'Alt', 'data': { 'case1': 'int', 'case2': 'str' } }
|
||||
|
||||
##
|
||||
# @oops:
|
||||
##
|
||||
{ 'command': 'oops', 'data': 'Alt' }
|
||||
|
@ -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'
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we do not allow an 'any' argument
|
||||
|
||||
##
|
||||
# @oops:
|
||||
##
|
||||
{ 'command': 'oops', 'data': 'any' }
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we reject an array for data if it does not contain a known type
|
||||
|
||||
##
|
||||
# @oops:
|
||||
##
|
||||
{ 'command': 'oops', 'data': { 'empty': [ ] } }
|
||||
|
@ -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'
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we reject an array for data if it does not contain a known type
|
||||
|
||||
##
|
||||
# @oops:
|
||||
##
|
||||
{ 'command': 'oops', 'data': { 'array': [ 'NoSuchType' ] } }
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,6 @@
|
||||
# 'boxed' should only appear with value true
|
||||
|
||||
##
|
||||
# @foo:
|
||||
##
|
||||
{ 'command': 'foo', 'boxed': false }
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,6 @@
|
||||
# 'boxed' can only be used with named types
|
||||
|
||||
##
|
||||
# @foo:
|
||||
##
|
||||
{ 'command': 'foo', 'boxed': true, 'data': { 'string': 'str' } }
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,11 @@
|
||||
# 'boxed' requires a non-empty type
|
||||
|
||||
##
|
||||
# @Empty:
|
||||
##
|
||||
{ 'struct': 'Empty', 'data': {} }
|
||||
|
||||
##
|
||||
# @foo:
|
||||
##
|
||||
{ 'command': 'foo', 'boxed': true, 'data': 'Empty' }
|
||||
|
@ -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'
|
||||
|
@ -1,2 +1,6 @@
|
||||
# 'boxed' requires a complex (not built-in) type
|
||||
|
||||
##
|
||||
# @foo:
|
||||
##
|
||||
{ 'command': 'foo', 'boxed': true, 'data': 'str' }
|
||||
|
@ -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'
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we reject commands where data is not an array or complex type
|
||||
|
||||
##
|
||||
# @oops:
|
||||
##
|
||||
{ 'command': 'oops', 'data': 'int' }
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,5 @@
|
||||
##
|
||||
# @foo:
|
||||
##
|
||||
{ 'command': 'foo',
|
||||
'data': false }
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we reject data if it does not contain a valid array type
|
||||
|
||||
##
|
||||
# @oops:
|
||||
##
|
||||
{ 'command': 'oops', 'data': { 'member': [ { 'nested': 'str' } ] } }
|
||||
|
@ -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
|
||||
|
@ -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' } }
|
||||
|
@ -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'
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we reject data if it does not contain a known type
|
||||
|
||||
##
|
||||
# @oops:
|
||||
##
|
||||
{ 'command': 'oops', 'data': { 'member': 'NoSuchType' } }
|
||||
|
@ -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)
|
||||
|
@ -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' } }
|
||||
|
@ -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'
|
||||
|
@ -1,3 +1,10 @@
|
||||
# use of union arguments requires 'boxed':true
|
||||
|
||||
##
|
||||
# @Uni:
|
||||
##
|
||||
{ 'union': 'Uni', 'data': { 'case1': 'int', 'case2': 'str' } }
|
||||
##
|
||||
# oops:
|
||||
##
|
||||
{ 'command': 'oops', 'data': 'Uni' }
|
||||
|
@ -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'
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we reject data if it does not contain a known type
|
||||
|
||||
##
|
||||
# @oops:
|
||||
##
|
||||
{ 'command': 'oops', 'data': 'NoSuchType' }
|
||||
|
@ -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'
|
||||
|
@ -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' } }
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we ensure 'data' is a dictionary for all but enums
|
||||
|
||||
##
|
||||
# @oops:
|
||||
##
|
||||
{ 'command': 'oops', 'data': [ ] }
|
||||
|
@ -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'
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we reject creating a type name with bad name
|
||||
|
||||
##
|
||||
# @*oops:
|
||||
##
|
||||
{ 'struct': '*oops', 'data': { 'i': 'int' } }
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we reject an expression with a metatype that is not a string
|
||||
|
||||
##
|
||||
# @true:
|
||||
##
|
||||
{ 'struct': true, 'data': { } }
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we reject an expression with a metatype that is not a string
|
||||
|
||||
##
|
||||
# @foo:
|
||||
##
|
||||
{ 'command': { } }
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we reject a loop in base classes
|
||||
|
||||
##
|
||||
# @Loopy:
|
||||
##
|
||||
{ 'struct': 'Loopy', 'base': 'Loopy', 'data': {} }
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,10 @@
|
||||
# we reject a loop in base classes
|
||||
|
||||
##
|
||||
# @Base1:
|
||||
##
|
||||
{ 'struct': 'Base1', 'base': 'Base2', 'data': {} }
|
||||
##
|
||||
# @Base2:
|
||||
##
|
||||
{ 'struct': 'Base2', 'base': 'Base1', 'data': {} }
|
||||
|
@ -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
|
||||
|
@ -1,2 +1,6 @@
|
||||
# we reject collisions between commands and types
|
||||
|
||||
##
|
||||
# @int:
|
||||
##
|
||||
{ 'command': 'int', 'data': { 'character': 'str' } }
|
||||
|
@ -1,4 +1,8 @@
|
||||
# Unindented comment
|
||||
|
||||
##
|
||||
# @Status:
|
||||
##
|
||||
{ 'enum': 'Status', # Comment to the right of code
|
||||
# Indented comment
|
||||
'data': [ 'good', 'bad', 'ugly' ] }
|
||||
|
@ -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')
|
||||
|
1
tests/qapi-schema/doc-bad-args.err
Normal file
1
tests/qapi-schema/doc-bad-args.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/doc-bad-args.json:3: The following documented members are not in the declaration: b
|
1
tests/qapi-schema/doc-bad-args.exit
Normal file
1
tests/qapi-schema/doc-bad-args.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
8
tests/qapi-schema/doc-bad-args.json
Normal file
8
tests/qapi-schema/doc-bad-args.json
Normal 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'} }
|
0
tests/qapi-schema/doc-bad-args.out
Normal file
0
tests/qapi-schema/doc-bad-args.out
Normal file
1
tests/qapi-schema/doc-bad-symbol.err
Normal file
1
tests/qapi-schema/doc-bad-symbol.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/doc-bad-symbol.json:3: Definition of 'foo' follows documentation for 'food'
|
1
tests/qapi-schema/doc-bad-symbol.exit
Normal file
1
tests/qapi-schema/doc-bad-symbol.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
6
tests/qapi-schema/doc-bad-symbol.json
Normal file
6
tests/qapi-schema/doc-bad-symbol.json
Normal 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
Loading…
Reference in New Issue
Block a user