mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-28 05:50:37 +00:00
drop qapi nested structs
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVSPKIAAoJEDhwtADrkYZTtRcP/is2MqiBt7luwL6gtCPzL4xs ig3Y618dEfJYz3qp8B8fDhY9keth8Uip/rVSJN8ke12ZBfkuY/pzK/dFnyAh2sb5 QE5EWcX1yTyUAXbG7arNSQT7ruBQBlMKCQDJJIHvr5Dg4pP1AdlHScCSBrbSYSML DHOaO50n/UUXDtDOph9Ghjs+RCuxL/rPyHs3l7hJBOoSXdoMnpej+6SZbOuqqWcV J/v9xEYBYITlOItjTYzjnjHeA487AQx5WuJh9I+OPNeu5lMi/jt4YWUDuZfmSy8J 6E+t9aPFenlr3RnDsP6RERQmPgwPemqYVCAtI9KAqJOxI1zYoZQtJEp1Dnr6AS/I snz/2FOFM1dSWTokKx1e0fRRVbKc3Vv6PDK7tMFQk/etnJ2UWxzP3G7kJ8zC2FSI NXFaXH7zvsKUis9lBbWlWytiv2YfRlfyUQxL6zLEyhqeF/n3UhoxFJD8KE2C49DZ oLZZpesb1N6b5ddQ24pYK+n5BU/9XH3zaSfvSnELcLTMfukrl91/8LL3bgRETuwr hoepvp/r2eL1JhNFYYtWLhU8fF2POYZEwjxAOHXCnawCJFi3Vcq2luuSuZvC160+ lEtSAf+QjrkTMH6XZp1ClWUnglxDC0NNaXaeDB+KY5cU50UTiYeLy+ejsnjQonH1 dS8UuhwwSFAB8RVvI+SV =xvD0 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/armbru/tags/pull-qmp-2015-05-05' into staging drop qapi nested structs # gpg: Signature made Tue May 5 17:40:40 2015 BST using RSA key ID EB918653 # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" * remotes/armbru/tags/pull-qmp-2015-05-05: (40 commits) qapi: Check for member name conflicts with a base class qapi: Support (subset of) \u escapes in strings qapi: Tweak doc references to QMP when QGA is also meant qapi: Drop dead visitor code related to nested structs qapi: Drop support for inline nested types qapi: Drop inline nested structs in query-pci qapi: Drop inline nested struct in query-version qapi: Drop tests for inline nested structs qapi: Merge UserDefTwo and UserDefNested in tests qapi: Forbid 'type' in schema qapi: Use 'struct' instead of 'type' in schema qapi: Document 'struct' metatype qapi: Prefer 'struct' over 'type' in generator qapi: More rigorous checking for type safety bypass qapi: Whitelist commands that don't return dictionary qapi: Require valid names qapi: More rigourous checking of types qapi: Add some type check tests qapi: Unify type bypass and add tests qapi: Allow true, false and null in schema json ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
233353ec93
@ -1,61 +1,193 @@
|
||||
= How to use the QAPI code generator =
|
||||
|
||||
Copyright IBM Corp. 2011
|
||||
Copyright (C) 2012-2015 Red Hat, Inc.
|
||||
|
||||
This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
later. See the COPYING file in the top-level directory.
|
||||
|
||||
== Introduction ==
|
||||
|
||||
QAPI is a native C API within QEMU which provides management-level
|
||||
functionality to internal/external users. For external
|
||||
users/processes, this interface is made available by a JSON-based
|
||||
QEMU Monitor protocol that is provided by the QMP server.
|
||||
functionality to internal and external users. For external
|
||||
users/processes, this interface is made available by a JSON-based wire
|
||||
format for the QEMU Monitor Protocol (QMP) for controlling qemu, as
|
||||
well as the QEMU Guest Agent (QGA) for communicating with the guest.
|
||||
The remainder of this document uses "Client JSON Protocol" when
|
||||
referring to the wire contents of a QMP or QGA connection.
|
||||
|
||||
To map QMP-defined interfaces to the native C QAPI implementations,
|
||||
a JSON-based schema is used to define types and function
|
||||
signatures, and a set of scripts is used to generate types/signatures,
|
||||
and marshaling/dispatch code. The QEMU Guest Agent also uses these
|
||||
scripts, paired with a separate schema, to generate
|
||||
marshaling/dispatch code for the guest agent server running in the
|
||||
guest.
|
||||
|
||||
This document will describe how the schemas, scripts, and resulting
|
||||
code are used.
|
||||
To map Client JSON Protocol interfaces to the native C QAPI
|
||||
implementations, a JSON-based schema is used to define types and
|
||||
function signatures, and a set of scripts is used to generate types,
|
||||
signatures, and marshaling/dispatch code. This document will describe
|
||||
how the schemas, scripts, and resulting code are used.
|
||||
|
||||
|
||||
== QMP/Guest agent schema ==
|
||||
|
||||
This file defines the types, commands, and events used by QMP. It should
|
||||
fully describe the interface used by QMP.
|
||||
A QAPI schema file is designed to be loosely based on JSON
|
||||
(http://www.ietf.org/rfc/rfc7159.txt) with changes for quoting style
|
||||
and the use of comments; a QAPI schema file is then parsed by a python
|
||||
code generation program. A valid QAPI schema consists of a series of
|
||||
top-level expressions, with no commas between them. Where
|
||||
dictionaries (JSON objects) are used, they are parsed as python
|
||||
OrderedDicts so that ordering is preserved (for predictable layout of
|
||||
generated C structs and parameter lists). Ordering doesn't matter
|
||||
between top-level expressions or the keys within an expression, but
|
||||
does matter within dictionary values for 'data' and 'returns' members
|
||||
of a single expression. QAPI schema input is written using 'single
|
||||
quotes' instead of JSON's "double quotes" (in contrast, Client JSON
|
||||
Protocol uses no comments, and while input accepts 'single quotes' as
|
||||
an extension, output is strict JSON using only "double quotes"). As
|
||||
in JSON, trailing commas are not permitted in arrays or dictionaries.
|
||||
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.
|
||||
|
||||
This file is designed to be loosely based on JSON although it's technically
|
||||
executable Python. While dictionaries are used, they are parsed as
|
||||
OrderedDicts so that ordering is preserved.
|
||||
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 fields 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:
|
||||
|
||||
There are two basic syntaxes used, type definitions and command definitions.
|
||||
##
|
||||
# @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'} }
|
||||
|
||||
The first syntax defines a type and is represented by a dictionary. There are
|
||||
three kinds of user-defined types that are supported: complex types,
|
||||
enumeration types and union types.
|
||||
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
|
||||
scans in two passes, where the first pass learns all type names, and
|
||||
the second validates the schema and generates the code. This allows
|
||||
the definition of complex structs that can have mutually recursive
|
||||
types, and allows for indefinite nesting of Client JSON Protocol that
|
||||
satisfies the schema. A type name should not be defined more than
|
||||
once. It is permissible for the schema to contain additional types
|
||||
not used by any commands or events in the Client JSON Protocol, for
|
||||
the side effect of generated C code used internally.
|
||||
|
||||
Generally speaking, types definitions should always use CamelCase for the type
|
||||
names. Command names should be all lower case with words separated by a hyphen.
|
||||
There are seven top-level expressions recognized by the parser:
|
||||
'include', 'command', 'struct', 'enum', 'union', 'alternate', and
|
||||
'event'. There are several groups of types: simple types (a number of
|
||||
built-in types, such as 'int' and 'str'; as well as enumerations),
|
||||
complex types (structs and two flavors of unions), and alternate types
|
||||
(a choice between other types). The 'command' and 'event' expressions
|
||||
can refer to existing types by name, or list an anonymous type as a
|
||||
dictionary. Listing a type name inside an array refers to a
|
||||
single-dimension array of that type; multi-dimension arrays are not
|
||||
directly supported (although an array of a complex struct that
|
||||
contains an array member is possible).
|
||||
|
||||
Types, commands, and events share a common namespace. Therefore,
|
||||
generally speaking, type definitions should always use CamelCase for
|
||||
user-defined type names, while built-in types are lowercase. Type
|
||||
definitions should not end in 'Kind', as this namespace is used for
|
||||
creating implicit C enums for visiting union types. Command names,
|
||||
and field names within a type, should be all lower case with words
|
||||
separated by a hyphen. However, some existing older commands and
|
||||
complex types use underscore; when extending such expressions,
|
||||
consistency is preferred over blindly avoiding underscore. Event
|
||||
names should be ALL_CAPS with words separated by underscore. The
|
||||
special string '**' appears for some commands that manually perform
|
||||
their own type checking rather than relying on the type-safe code
|
||||
produced by the qapi code generators.
|
||||
|
||||
Any name (command, event, type, field, or enum value) beginning with
|
||||
"x-" is marked experimental, and may be withdrawn or changed
|
||||
incompatibly in a future release. Downstream vendors may add
|
||||
extensions; such extensions should begin with a prefix matching
|
||||
"__RFQDN_" (for the reverse-fully-qualified-domain-name of the
|
||||
vendor), even if the rest of the name uses dash (example:
|
||||
__com.redhat_drive-mirror). Other than downstream extensions (with
|
||||
leading underscore and the use of dots), all names should begin with a
|
||||
letter, and contain only ASCII letters, digits, dash, and underscore.
|
||||
It is okay to reuse names that match C keywords; the generator will
|
||||
rename a field named "default" in the QAPI to "q_default" in the
|
||||
generated C code.
|
||||
|
||||
In the rest of this document, usage lines are given for each
|
||||
expression type, with literal strings written in lower case and
|
||||
placeholders written in capitals. If a literal string includes a
|
||||
prefix of '*', that key/value pair can be omitted from the expression.
|
||||
For example, a usage statement that includes '*base':STRUCT-NAME
|
||||
means that an expression has an optional key 'base', which if present
|
||||
must have a value that forms a struct name.
|
||||
|
||||
|
||||
=== Built-in Types ===
|
||||
|
||||
The following types are built-in to the parser:
|
||||
'str' - arbitrary UTF-8 string
|
||||
'int' - 64-bit signed integer (although the C code may place further
|
||||
restrictions on acceptable range)
|
||||
'number' - floating point number
|
||||
'bool' - JSON value of true or false
|
||||
'int8', 'int16', 'int32', 'int64' - like 'int', but enforce maximum
|
||||
bit size
|
||||
'uint8', 'uint16', 'uint32', 'uint64' - unsigned counterparts
|
||||
'size' - like 'uint64', but allows scaled suffix from command line
|
||||
visitor
|
||||
|
||||
|
||||
=== Includes ===
|
||||
|
||||
Usage: { 'include': STRING }
|
||||
|
||||
The QAPI schema definitions can be modularized using the 'include' directive:
|
||||
|
||||
{ 'include': 'path/to/file.json'}
|
||||
{ 'include': 'path/to/file.json' }
|
||||
|
||||
The directive is evaluated recursively, and include paths are relative to the
|
||||
file using the directive. Multiple includes of the same file are safe.
|
||||
file using the directive. Multiple includes of the same file are
|
||||
safe. No other keys should appear in the expression, and the include
|
||||
value should be a string.
|
||||
|
||||
As a matter of style, it is a good idea to have all files be
|
||||
self-contained, but at the moment, nothing prevents an included file
|
||||
from making a forward reference to a type that is only introduced by
|
||||
an outer file. The parser may be made stricter in the future to
|
||||
prevent incomplete include files.
|
||||
|
||||
|
||||
=== Complex types ===
|
||||
=== Struct types ===
|
||||
|
||||
A complex type is a dictionary containing a single key whose value is a
|
||||
dictionary. This corresponds to a struct in C or an Object in JSON. An
|
||||
example of a complex type is:
|
||||
Usage: { 'struct': STRING, 'data': DICT, '*base': STRUCT-NAME }
|
||||
|
||||
{ 'type': 'MyType',
|
||||
A struct is a dictionary containing a single 'data' key whose
|
||||
value is a dictionary. This corresponds to a struct in C or an Object
|
||||
in JSON. Each value of the 'data' dictionary must be the name of a
|
||||
type, or a one-element array containing a type name. An example of a
|
||||
struct is:
|
||||
|
||||
{ 'struct': 'MyType',
|
||||
'data': { 'member1': 'str', 'member2': 'int', '*member3': 'str' } }
|
||||
|
||||
The use of '*' as a prefix to the name means the member is optional.
|
||||
The use of '*' as a prefix to the name means the member is optional in
|
||||
the corresponding JSON protocol usage.
|
||||
|
||||
The default initialization value of an optional argument should not be changed
|
||||
between versions of QEMU unless the new default maintains backward
|
||||
@ -84,13 +216,13 @@ A structure that is used in both input and output of various commands
|
||||
must consider the backwards compatibility constraints of both directions
|
||||
of use.
|
||||
|
||||
A complex type definition can specify another complex type as its base.
|
||||
A struct definition can specify another struct as its base.
|
||||
In this case, the fields of the base type are included as top-level fields
|
||||
of the new complex type's dictionary in the QMP wire format. An example
|
||||
definition is:
|
||||
of the new struct's dictionary in the Client JSON Protocol wire
|
||||
format. An example definition is:
|
||||
|
||||
{ 'type': 'BlockdevOptionsGenericFormat', 'data': { 'file': 'str' } }
|
||||
{ 'type': 'BlockdevOptionsGenericCOWFormat',
|
||||
{ 'struct': 'BlockdevOptionsGenericFormat', 'data': { 'file': 'str' } }
|
||||
{ 'struct': 'BlockdevOptionsGenericCOWFormat',
|
||||
'base': 'BlockdevOptionsGenericFormat',
|
||||
'data': { '*backing': 'str' } }
|
||||
|
||||
@ -100,97 +232,158 @@ both fields like this:
|
||||
{ "file": "/some/place/my-image",
|
||||
"backing": "/some/place/my-backing-file" }
|
||||
|
||||
|
||||
=== Enumeration types ===
|
||||
|
||||
An enumeration type is a dictionary containing a single key whose value is a
|
||||
list of strings. An example enumeration is:
|
||||
Usage: { 'enum': STRING, 'data': ARRAY-OF-STRING }
|
||||
|
||||
An enumeration type is a dictionary containing a single 'data' key
|
||||
whose value is a list of strings. An example enumeration is:
|
||||
|
||||
{ 'enum': 'MyEnum', 'data': [ 'value1', 'value2', 'value3' ] }
|
||||
|
||||
Nothing prevents an empty enumeration, although it is probably not
|
||||
useful. The list of strings should be lower case; if an enum name
|
||||
represents multiple words, use '-' between words. The string 'max' is
|
||||
not allowed as an enum value, and values should not be repeated.
|
||||
|
||||
The enumeration values are passed as strings over the Client JSON
|
||||
Protocol, but are encoded as C enum integral values in generated code.
|
||||
While the C code starts numbering at 0, it is better to use explicit
|
||||
comparisons to enum values than implicit comparisons to 0; the C code
|
||||
will also include a generated enum member ending in _MAX for tracking
|
||||
the size of the enum, useful when using common functions for
|
||||
converting between strings and enum values. Since the wire format
|
||||
always passes by name, it is acceptable to reorder or add new
|
||||
enumeration members in any location without breaking clients of Client
|
||||
JSON Protocol; however, removing enum values would break
|
||||
compatibility. For any struct that has a field that will only contain
|
||||
a finite set of string values, using an enum type for that field is
|
||||
better than open-coding the field to be type 'str'.
|
||||
|
||||
|
||||
=== Union types ===
|
||||
|
||||
Union types are used to let the user choose between several different data
|
||||
types. A union type is defined using a dictionary as explained in the
|
||||
following paragraphs.
|
||||
Usage: { 'union': STRING, 'data': DICT }
|
||||
or: { 'union': STRING, 'data': DICT, 'base': STRUCT-NAME,
|
||||
'discriminator': ENUM-MEMBER-OF-BASE }
|
||||
|
||||
Union types are used to let the user choose between several different
|
||||
variants for an object. There are two flavors: simple (no
|
||||
discriminator or base), flat (both discriminator and base). A union
|
||||
type is defined using a data dictionary as explained in the following
|
||||
paragraphs.
|
||||
|
||||
A simple union type defines a mapping from discriminator values to data types
|
||||
like in this example:
|
||||
A simple union type defines a mapping from automatic discriminator
|
||||
values to data types like in this example:
|
||||
|
||||
{ 'type': 'FileOptions', 'data': { 'filename': 'str' } }
|
||||
{ 'type': 'Qcow2Options',
|
||||
{ 'struct': 'FileOptions', 'data': { 'filename': 'str' } }
|
||||
{ 'struct': 'Qcow2Options',
|
||||
'data': { 'backing-file': 'str', 'lazy-refcounts': 'bool' } }
|
||||
|
||||
{ 'union': 'BlockdevOptions',
|
||||
'data': { 'file': 'FileOptions',
|
||||
'qcow2': 'Qcow2Options' } }
|
||||
|
||||
In the QMP wire format, a simple union is represented by a dictionary that
|
||||
contains the 'type' field as a discriminator, and a 'data' field that is of the
|
||||
specified data type corresponding to the discriminator value:
|
||||
In the Client JSON Protocol, a simple union is represented by a
|
||||
dictionary that contains the 'type' field as a discriminator, and a
|
||||
'data' field that is of the specified data type corresponding to the
|
||||
discriminator value, as in these examples:
|
||||
|
||||
{ "type": "file", "data" : { "filename": "/some/place/my-image" } }
|
||||
{ "type": "qcow2", "data" : { "backing-file": "/some/place/my-image",
|
||||
"lazy-refcounts": true } }
|
||||
|
||||
|
||||
A union definition can specify a complex type as its base. In this case, the
|
||||
fields of the complex type are included as top-level fields of the union
|
||||
dictionary in the QMP wire format. An example definition is:
|
||||
|
||||
{ 'type': 'BlockdevCommonOptions', 'data': { 'readonly': 'bool' } }
|
||||
{ 'union': 'BlockdevOptions',
|
||||
'base': 'BlockdevCommonOptions',
|
||||
'data': { 'raw': 'RawOptions',
|
||||
'qcow2': 'Qcow2Options' } }
|
||||
|
||||
And it looks like this on the wire:
|
||||
|
||||
{ "type": "qcow2",
|
||||
"readonly": false,
|
||||
"data" : { "backing-file": "/some/place/my-image",
|
||||
"lazy-refcounts": true } }
|
||||
The generated C code uses a struct containing a union. Additionally,
|
||||
an implicit C enum 'NameKind' is created, corresponding to the union
|
||||
'Name', for accessing the various branches of the union. No branch of
|
||||
the union can be named 'max', as this would collide with the implicit
|
||||
enum. The value for each branch can be of any type.
|
||||
|
||||
|
||||
Flat union types avoid the nesting on the wire. They are used whenever a
|
||||
specific field of the base type is declared as the discriminator ('type' is
|
||||
then no longer generated). The discriminator must be of enumeration type.
|
||||
The above example can then be modified as follows:
|
||||
A flat union definition specifies a struct as its base, and
|
||||
avoids nesting on the wire. All branches of the union must be
|
||||
complex types, and the top-level fields of the union dictionary on
|
||||
the wire will be combination of fields from both the base type and the
|
||||
appropriate branch type (when merging two dictionaries, there must be
|
||||
no keys in common). The 'discriminator' field must be the name of an
|
||||
enum-typed member of the base struct.
|
||||
|
||||
The following example enhances the above simple union example by
|
||||
adding a common field 'readonly', renaming the discriminator to
|
||||
something more applicable, and reducing the number of {} required on
|
||||
the wire:
|
||||
|
||||
{ 'enum': 'BlockdevDriver', 'data': [ 'raw', 'qcow2' ] }
|
||||
{ 'type': 'BlockdevCommonOptions',
|
||||
{ 'struct': 'BlockdevCommonOptions',
|
||||
'data': { 'driver': 'BlockdevDriver', 'readonly': 'bool' } }
|
||||
{ 'union': 'BlockdevOptions',
|
||||
'base': 'BlockdevCommonOptions',
|
||||
'discriminator': 'driver',
|
||||
'data': { 'raw': 'RawOptions',
|
||||
'data': { 'file': 'FileOptions',
|
||||
'qcow2': 'Qcow2Options' } }
|
||||
|
||||
Resulting in this JSON object:
|
||||
Resulting in these JSON objects:
|
||||
|
||||
{ "driver": "qcow2",
|
||||
"readonly": false,
|
||||
"backing-file": "/some/place/my-image",
|
||||
"lazy-refcounts": true }
|
||||
{ "driver": "file", "readonly": true,
|
||||
"filename": "/some/place/my-image" }
|
||||
{ "driver": "qcow2", "readonly": false,
|
||||
"backing-file": "/some/place/my-image", "lazy-refcounts": true }
|
||||
|
||||
Notice that in a flat union, the discriminator name is controlled by
|
||||
the user, but because it must map to a base member with enum type, the
|
||||
code generator can ensure that branches exist for all values of the
|
||||
enum (although the order of the keys need not match the declaration of
|
||||
the enum). In the resulting generated C data types, a flat union is
|
||||
represented as a struct with the base member fields included directly,
|
||||
and then a union of structures for each branch of the struct.
|
||||
|
||||
A simple union can always be re-written as a flat union where the base
|
||||
class has a single member named 'type', and where each branch of the
|
||||
union has a struct with a single member named 'data'. That is,
|
||||
|
||||
{ 'union': 'Simple', 'data': { 'one': 'str', 'two': 'int' } }
|
||||
|
||||
is identical on the wire to:
|
||||
|
||||
{ 'enum': 'Enum', 'data': ['one', 'two'] }
|
||||
{ 'struct': 'Base', 'data': { 'type': 'Enum' } }
|
||||
{ 'struct': 'Branch1', 'data': { 'data': 'str' } }
|
||||
{ 'struct': 'Branch2', 'data': { 'data': 'int' } }
|
||||
{ 'union': 'Flat': 'base': 'Base', 'discriminator': 'type',
|
||||
'data': { 'one': 'Branch1', 'two': 'Branch2' } }
|
||||
|
||||
|
||||
A special type of unions are anonymous unions. They don't form a dictionary in
|
||||
the wire format but allow the direct use of different types in their place. As
|
||||
they aren't structured, they don't have any explicit discriminator but use
|
||||
the (QObject) data type of their value as an implicit discriminator. This means
|
||||
that they are restricted to using only one discriminator value per QObject
|
||||
type. For example, you cannot have two different complex types in an anonymous
|
||||
union, or two different integer types.
|
||||
=== Alternate types ===
|
||||
|
||||
Anonymous unions are declared using an empty dictionary as their discriminator.
|
||||
The discriminator values never appear on the wire, they are only used in the
|
||||
generated C code. Anonymous unions cannot have a base type.
|
||||
Usage: { 'alternate': STRING, 'data': DICT }
|
||||
|
||||
{ 'union': 'BlockRef',
|
||||
'discriminator': {},
|
||||
An alternate type is one that allows a choice between two or more JSON
|
||||
data types (string, integer, number, or object, but currently not
|
||||
array) on the wire. The definition is similar to a simple union type,
|
||||
where each branch of the union names a QAPI type. For example:
|
||||
|
||||
{ 'alternate': 'BlockRef',
|
||||
'data': { 'definition': 'BlockdevOptions',
|
||||
'reference': 'str' } }
|
||||
|
||||
This example allows using both of the following example objects:
|
||||
Just like for a simple union, an implicit C enum 'NameKind' is created
|
||||
to enumerate the branches for the alternate 'Name'.
|
||||
|
||||
Unlike a union, the discriminator string is never passed on the wire
|
||||
for the Client JSON Protocol. Instead, the value's JSON type serves
|
||||
as an implicit discriminator, which in turn means that an alternate
|
||||
can only express a choice between types represented differently in
|
||||
JSON. If a branch is typed as the 'bool' built-in, the alternate
|
||||
accepts true and false; if it is typed as any of the various numeric
|
||||
built-ins, it accepts a JSON number; if it is typed as a 'str'
|
||||
built-in or named enum type, it accepts a JSON string; and if it is
|
||||
typed as a complex type (struct or union), it accepts a JSON object.
|
||||
Two different complex types, for instance, aren't permitted, because
|
||||
both are represented as a JSON object.
|
||||
|
||||
The example alternate declaration above allows using both of the
|
||||
following example objects:
|
||||
|
||||
{ "file": "my_existing_block_device_id" }
|
||||
{ "file": { "driver": "file",
|
||||
@ -200,23 +393,95 @@ This example allows using both of the following example objects:
|
||||
|
||||
=== Commands ===
|
||||
|
||||
Commands are defined by using a list containing three members. The first
|
||||
member is the command name, the second member is a dictionary containing
|
||||
arguments, and the third member is the return type.
|
||||
Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
|
||||
'*returns': TYPE-NAME-OR-DICT,
|
||||
'*gen': false, '*success-response': false }
|
||||
|
||||
An example command is:
|
||||
Commands are defined by using a dictionary containing several members,
|
||||
where three members are most common. The 'command' member is a
|
||||
mandatory string, and determines the "execute" value passed in a
|
||||
Client JSON Protocol command exchange.
|
||||
|
||||
The 'data' argument maps to the "arguments" dictionary passed in as
|
||||
part of a Client JSON Protocol command. The 'data' member is optional
|
||||
and defaults to {} (an empty dictionary). If present, it must be the
|
||||
string name of a complex type, a one-element array containing the name
|
||||
of a complex type, or a dictionary that declares an anonymous type
|
||||
with the same semantics as a 'struct' expression, with one exception
|
||||
noted below when 'gen' is used.
|
||||
|
||||
The 'returns' member describes what will appear in the "return" field
|
||||
of a Client JSON Protocol reply on successful completion of a command.
|
||||
The member is optional from the command declaration; if absent, the
|
||||
"return" field will be an empty dictionary. If 'returns' is present,
|
||||
it must be the string name of a complex or built-in type, a
|
||||
one-element array containing the name of a complex or built-in type,
|
||||
or a dictionary that declares an anonymous type with the same
|
||||
semantics as a 'struct' expression, with one exception noted below
|
||||
when 'gen' is used. Although it is permitted to have the 'returns'
|
||||
member name a built-in type or an array of built-in types, any command
|
||||
that does this cannot be extended to return additional information in
|
||||
the future; thus, new commands should strongly consider returning a
|
||||
dictionary-based type or an array of dictionaries, even if the
|
||||
dictionary only contains one field at the present.
|
||||
|
||||
All commands in Client JSON Protocol use a dictionary to report
|
||||
failure, with no way to specify that in QAPI. Where the error return
|
||||
is different than the usual GenericError class in order to help the
|
||||
client react differently to certain error conditions, it is worth
|
||||
documenting this in the comments before the command declaration.
|
||||
|
||||
Some example commands:
|
||||
|
||||
{ 'command': 'my-first-command',
|
||||
'data': { 'arg1': 'str', '*arg2': 'str' } }
|
||||
{ 'struct': 'MyType', 'data': { '*value': 'str' } }
|
||||
{ 'command': 'my-second-command',
|
||||
'returns': [ 'MyType' ] }
|
||||
|
||||
which would validate this Client JSON Protocol transaction:
|
||||
|
||||
=> { "execute": "my-first-command",
|
||||
"arguments": { "arg1": "hello" } }
|
||||
<= { "return": { } }
|
||||
=> { "execute": "my-second-command" }
|
||||
<= { "return": [ { "value": "one" }, { } ] }
|
||||
|
||||
In rare cases, QAPI cannot express a type-safe representation of a
|
||||
corresponding Client JSON Protocol command. In these cases, if the
|
||||
command expression includes the key 'gen' with boolean value false,
|
||||
then the 'data' or 'returns' member that intends to bypass generated
|
||||
type-safety and do its own manual validation should use an inline
|
||||
dictionary definition, with a value of '**' rather than a valid type
|
||||
name for the keys that the generated code will not validate. Please
|
||||
try to avoid adding new commands that rely on this, and instead use
|
||||
type-safe unions. For an example of bypass usage:
|
||||
|
||||
{ 'command': 'netdev_add',
|
||||
'data': {'type': 'str', 'id': 'str', '*props': '**'},
|
||||
'gen': false }
|
||||
|
||||
Normally, the QAPI schema is used to describe synchronous exchanges,
|
||||
where a response is expected. But in some cases, the action of a
|
||||
command is expected to change state in a way that a successful
|
||||
response is not possible (although the command will still return a
|
||||
normal dictionary error on failure). When a successful reply is not
|
||||
possible, the command expression should include the optional key
|
||||
'success-response' with boolean value false. So far, only QGA makes
|
||||
use of this field.
|
||||
|
||||
{ 'command': 'my-command',
|
||||
'data': { 'arg1': 'str', '*arg2': 'str' },
|
||||
'returns': 'str' }
|
||||
|
||||
=== Events ===
|
||||
|
||||
Events are defined with the keyword 'event'. When 'data' is also specified,
|
||||
additional info will be included in the event. Finally there will be C API
|
||||
generated in qapi-event.h; when called by QEMU code, a message with timestamp
|
||||
will be emitted on the wire. If timestamp is -1, it means failure to retrieve
|
||||
host time.
|
||||
Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT }
|
||||
|
||||
Events are defined with the keyword 'event'. It is not allowed to
|
||||
name an event 'MAX', since the generator also produces a C enumeration
|
||||
of all event names with a generated _MAX value at the end. When
|
||||
'data' is also specified, additional info will be included in the
|
||||
event, with similar semantics to a 'struct' expression. Finally there
|
||||
will be C API generated in qapi-event.h; when called by QEMU code, a
|
||||
message with timestamp will be emitted on the wire.
|
||||
|
||||
An example event is:
|
||||
|
||||
@ -234,9 +499,9 @@ Resulting in this JSON object:
|
||||
|
||||
Schemas are fed into 3 scripts to generate all the code/files that, paired
|
||||
with the core QAPI libraries, comprise everything required to take JSON
|
||||
commands read in by a QMP/guest agent server, unmarshal the arguments into
|
||||
commands read in by a Client JSON Protocol server, unmarshal the arguments into
|
||||
the underlying C types, call into the corresponding C function, and map the
|
||||
response back to a QMP/guest agent response to be returned to the user.
|
||||
response back to a Client JSON Protocol response to be returned to the user.
|
||||
|
||||
As an example, we'll use the following schema, which describes a single
|
||||
complex user-defined type (which will produce a C struct, along with a list
|
||||
@ -245,7 +510,7 @@ case we want to accept/return a list of this type with a command), and a
|
||||
command which takes that type as a parameter and returns the same type:
|
||||
|
||||
$ cat example-schema.json
|
||||
{ 'type': 'UserDefOne',
|
||||
{ 'struct': 'UserDefOne',
|
||||
'data': { 'integer': 'int', 'string': 'str' } }
|
||||
|
||||
{ 'command': 'my-command',
|
||||
@ -311,7 +576,7 @@ Example:
|
||||
#ifndef EXAMPLE_QAPI_TYPES_H
|
||||
#define EXAMPLE_QAPI_TYPES_H
|
||||
|
||||
[Builtin types omitted...]
|
||||
[Built-in types omitted...]
|
||||
|
||||
typedef struct UserDefOne UserDefOne;
|
||||
|
||||
@ -324,7 +589,7 @@ Example:
|
||||
struct UserDefOneList *next;
|
||||
} UserDefOneList;
|
||||
|
||||
[Functions on builtin types omitted...]
|
||||
[Functions on built-in types omitted...]
|
||||
|
||||
struct UserDefOne
|
||||
{
|
||||
@ -423,7 +688,7 @@ Example:
|
||||
#ifndef EXAMPLE_QAPI_VISIT_H
|
||||
#define EXAMPLE_QAPI_VISIT_H
|
||||
|
||||
[Visitors for builtin types omitted...]
|
||||
[Visitors for built-in types omitted...]
|
||||
|
||||
void visit_type_UserDefOne(Visitor *m, UserDefOne **obj, const char *name, Error **errp);
|
||||
void visit_type_UserDefOneList(Visitor *m, UserDefOneList **obj, const char *name, Error **errp);
|
||||
|
@ -1,10 +1,21 @@
|
||||
QEMU Machine Protocol Specification
|
||||
|
||||
0. About This Document
|
||||
======================
|
||||
|
||||
Copyright (C) 2009-2015 Red Hat, Inc.
|
||||
|
||||
This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
later. See the COPYING file in the top-level directory.
|
||||
|
||||
1. Introduction
|
||||
===============
|
||||
|
||||
This document specifies the QEMU Machine Protocol (QMP), a JSON-based protocol
|
||||
which is available for applications to operate QEMU at the machine-level.
|
||||
This document specifies the QEMU Machine Protocol (QMP), a JSON-based
|
||||
protocol which is available for applications to operate QEMU at the
|
||||
machine-level. It is also in use by the QEMU Guest Agent (QGA), which
|
||||
is available for host applications to interact with the guest
|
||||
operating system.
|
||||
|
||||
2. Protocol Specification
|
||||
=========================
|
||||
@ -18,14 +29,27 @@ following format:
|
||||
|
||||
json-DATA-STRUCTURE-NAME
|
||||
|
||||
Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined by
|
||||
the JSON standard:
|
||||
Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined
|
||||
by the JSON standard:
|
||||
|
||||
http://www.ietf.org/rfc/rfc4627.txt
|
||||
http://www.ietf.org/rfc/rfc7159.txt
|
||||
|
||||
For convenience, json-object members and json-array elements mentioned in
|
||||
this document will be in a certain order. However, in real protocol usage
|
||||
they can be in ANY order, thus no particular order should be assumed.
|
||||
The protocol is always encoded in UTF-8 except for synchronization
|
||||
bytes (documented below); although thanks to json-string escape
|
||||
sequences, the server will reply using only the strict ASCII subset.
|
||||
|
||||
For convenience, json-object members mentioned in this document will
|
||||
be in a certain order. However, in real protocol usage they can be in
|
||||
ANY order, thus no particular order should be assumed. On the other
|
||||
hand, use of json-array elements presumes that preserving order is
|
||||
important unless specifically documented otherwise. Repeating a key
|
||||
within a json-object gives unpredictable results.
|
||||
|
||||
Also for convenience, the server will accept an extension of
|
||||
'single-quoted' strings in place of the usual "double-quoted"
|
||||
json-string, and both input forms of strings understand an additional
|
||||
escape sequence of "\'" for a single quote. The server will only use
|
||||
double quoting on output.
|
||||
|
||||
2.1 General Definitions
|
||||
-----------------------
|
||||
@ -52,7 +76,16 @@ The greeting message format is:
|
||||
- The "version" member contains the Server's version information (the format
|
||||
is the same of the query-version command)
|
||||
- The "capabilities" member specify the availability of features beyond the
|
||||
baseline specification
|
||||
baseline specification; the order of elements in this array has no
|
||||
particular significance, so a client must search the entire array
|
||||
when looking for a particular capability
|
||||
|
||||
2.2.1 Capabilities
|
||||
------------------
|
||||
|
||||
As of the date this document was last revised, no server or client
|
||||
capability strings have been defined.
|
||||
|
||||
|
||||
2.3 Issuing Commands
|
||||
--------------------
|
||||
@ -65,10 +98,14 @@ The format for command execution is:
|
||||
|
||||
- The "execute" member identifies the command to be executed by the Server
|
||||
- The "arguments" member is used to pass any arguments required for the
|
||||
execution of the command, it is optional when no arguments are required
|
||||
execution of the command, it is optional when no arguments are
|
||||
required. Each command documents what contents will be considered
|
||||
valid when handling the json-argument
|
||||
- The "id" member is a transaction identification associated with the
|
||||
command execution, it is optional and will be part of the response if
|
||||
provided
|
||||
provided. The "id" member can be any json-value, although most
|
||||
clients merely use a json-number incremented for each successive
|
||||
command
|
||||
|
||||
2.4 Commands Responses
|
||||
----------------------
|
||||
@ -81,13 +118,15 @@ of a command execution: success or error.
|
||||
|
||||
The format of a success response is:
|
||||
|
||||
{ "return": json-object, "id": json-value }
|
||||
{ "return": json-value, "id": json-value }
|
||||
|
||||
Where,
|
||||
|
||||
- The "return" member contains the command returned data, which is defined
|
||||
in a per-command basis or an empty json-object if the command does not
|
||||
return data
|
||||
- The "return" member contains the data returned by the command, which
|
||||
is defined on a per-command basis (usually a json-object or
|
||||
json-array of json-objects, but sometimes a json-number, json-string,
|
||||
or json-array of json-strings); it is an empty json-object if the
|
||||
command does not return data
|
||||
- The "id" member contains the transaction identification associated
|
||||
with the command execution if issued by the Client
|
||||
|
||||
@ -114,7 +153,8 @@ if provided by the client.
|
||||
-----------------------
|
||||
|
||||
As a result of state changes, the Server may send messages unilaterally
|
||||
to the Client at any time. They are called "asynchronous events".
|
||||
to the Client at any time, when not in the middle of any other
|
||||
response. They are called "asynchronous events".
|
||||
|
||||
The format of asynchronous events is:
|
||||
|
||||
@ -126,13 +166,27 @@ The format of asynchronous events is:
|
||||
- The "event" member contains the event's name
|
||||
- The "data" member contains event specific data, which is defined in a
|
||||
per-event basis, it is optional
|
||||
- The "timestamp" member contains the exact time of when the event occurred
|
||||
in the Server. It is a fixed json-object with time in seconds and
|
||||
microseconds
|
||||
- The "timestamp" member contains the exact time of when the event
|
||||
occurred in the Server. It is a fixed json-object with time in
|
||||
seconds and microseconds relative to the Unix Epoch (1 Jan 1970); if
|
||||
there is a failure to retrieve host time, both members of the
|
||||
timestamp will be set to -1.
|
||||
|
||||
For a listing of supported asynchronous events, please, refer to the
|
||||
qmp-events.txt file.
|
||||
|
||||
2.5 QGA Synchronization
|
||||
-----------------------
|
||||
|
||||
When using QGA, an additional synchronization feature is built into
|
||||
the protocol. If the Client sends a raw 0xFF sentinel byte (not valid
|
||||
JSON), then the Server will reset its state and discard all pending
|
||||
data prior to the sentinel. Conversely, if the Client makes use of
|
||||
the 'guest-sync-delimited' command, the Server will send a raw 0xFF
|
||||
sentinel byte prior to its response, to aid the Client in discarding
|
||||
any data prior to the sentinel.
|
||||
|
||||
|
||||
3. QMP Examples
|
||||
===============
|
||||
|
||||
@ -145,32 +199,37 @@ This section provides some examples of real QMP usage, in all of them
|
||||
S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 },
|
||||
"package": ""}, "capabilities": []}}
|
||||
|
||||
3.2 Simple 'stop' execution
|
||||
3.2 Client QMP negotiation
|
||||
--------------------------
|
||||
C: { "execute": "qmp_capabilities" }
|
||||
S: { "return": {}}
|
||||
|
||||
3.3 Simple 'stop' execution
|
||||
---------------------------
|
||||
|
||||
C: { "execute": "stop" }
|
||||
S: { "return": {} }
|
||||
|
||||
3.3 KVM information
|
||||
3.4 KVM information
|
||||
-------------------
|
||||
|
||||
C: { "execute": "query-kvm", "id": "example" }
|
||||
S: { "return": { "enabled": true, "present": true }, "id": "example"}
|
||||
|
||||
3.4 Parsing error
|
||||
3.5 Parsing error
|
||||
------------------
|
||||
|
||||
C: { "execute": }
|
||||
S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } }
|
||||
|
||||
3.5 Powerdown event
|
||||
3.6 Powerdown event
|
||||
-------------------
|
||||
|
||||
S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 },
|
||||
"event": "POWERDOWN" }
|
||||
|
||||
4. Capabilities Negotiation
|
||||
----------------------------
|
||||
===========================
|
||||
|
||||
When a Client successfully establishes a connection, the Server is in
|
||||
Capabilities Negotiation mode.
|
||||
@ -189,7 +248,7 @@ effect, all commands (except qmp_capabilities) are allowed and asynchronous
|
||||
messages are delivered.
|
||||
|
||||
5 Compatibility Considerations
|
||||
------------------------------
|
||||
==============================
|
||||
|
||||
All protocol changes or new features which modify the protocol format in an
|
||||
incompatible way are disabled by default and will be advertised by the
|
||||
@ -213,12 +272,16 @@ However, Clients must not assume any particular:
|
||||
- Amount of errors generated by a command, that is, new errors can be added
|
||||
to any existing command in newer versions of the Server
|
||||
|
||||
Any command or field name beginning with "x-" is deemed experimental,
|
||||
and may be withdrawn or changed in an incompatible manner in a future
|
||||
release.
|
||||
|
||||
Of course, the Server does guarantee to send valid JSON. But apart from
|
||||
this, a Client should be "conservative in what they send, and liberal in
|
||||
what they accept".
|
||||
|
||||
6. Downstream extension of QMP
|
||||
------------------------------
|
||||
==============================
|
||||
|
||||
We recommend that downstream consumers of QEMU do *not* modify QMP.
|
||||
Management tools should be able to support both upstream and downstream
|
||||
|
28
hmp.c
28
hmp.c
@ -60,7 +60,7 @@ void hmp_info_version(Monitor *mon, const QDict *qdict)
|
||||
info = qmp_query_version(NULL);
|
||||
|
||||
monitor_printf(mon, "%" PRId64 ".%" PRId64 ".%" PRId64 "%s\n",
|
||||
info->qemu.major, info->qemu.minor, info->qemu.micro,
|
||||
info->qemu->major, info->qemu->minor, info->qemu->micro,
|
||||
info->package);
|
||||
|
||||
qapi_free_VersionInfo(info);
|
||||
@ -648,14 +648,14 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev)
|
||||
dev->slot, dev->function);
|
||||
monitor_printf(mon, " ");
|
||||
|
||||
if (dev->class_info.has_desc) {
|
||||
monitor_printf(mon, "%s", dev->class_info.desc);
|
||||
if (dev->class_info->has_desc) {
|
||||
monitor_printf(mon, "%s", dev->class_info->desc);
|
||||
} else {
|
||||
monitor_printf(mon, "Class %04" PRId64, dev->class_info.q_class);
|
||||
monitor_printf(mon, "Class %04" PRId64, dev->class_info->q_class);
|
||||
}
|
||||
|
||||
monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n",
|
||||
dev->id.vendor, dev->id.device);
|
||||
dev->id->vendor, dev->id->device);
|
||||
|
||||
if (dev->has_irq) {
|
||||
monitor_printf(mon, " IRQ %" PRId64 ".\n", dev->irq);
|
||||
@ -663,25 +663,25 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev)
|
||||
|
||||
if (dev->has_pci_bridge) {
|
||||
monitor_printf(mon, " BUS %" PRId64 ".\n",
|
||||
dev->pci_bridge->bus.number);
|
||||
dev->pci_bridge->bus->number);
|
||||
monitor_printf(mon, " secondary bus %" PRId64 ".\n",
|
||||
dev->pci_bridge->bus.secondary);
|
||||
dev->pci_bridge->bus->secondary);
|
||||
monitor_printf(mon, " subordinate bus %" PRId64 ".\n",
|
||||
dev->pci_bridge->bus.subordinate);
|
||||
dev->pci_bridge->bus->subordinate);
|
||||
|
||||
monitor_printf(mon, " IO range [0x%04"PRIx64", 0x%04"PRIx64"]\n",
|
||||
dev->pci_bridge->bus.io_range->base,
|
||||
dev->pci_bridge->bus.io_range->limit);
|
||||
dev->pci_bridge->bus->io_range->base,
|
||||
dev->pci_bridge->bus->io_range->limit);
|
||||
|
||||
monitor_printf(mon,
|
||||
" memory range [0x%08"PRIx64", 0x%08"PRIx64"]\n",
|
||||
dev->pci_bridge->bus.memory_range->base,
|
||||
dev->pci_bridge->bus.memory_range->limit);
|
||||
dev->pci_bridge->bus->memory_range->base,
|
||||
dev->pci_bridge->bus->memory_range->limit);
|
||||
|
||||
monitor_printf(mon, " prefetchable memory range "
|
||||
"[0x%08"PRIx64", 0x%08"PRIx64"]\n",
|
||||
dev->pci_bridge->bus.prefetchable_range->base,
|
||||
dev->pci_bridge->bus.prefetchable_range->limit);
|
||||
dev->pci_bridge->bus->prefetchable_range->base,
|
||||
dev->pci_bridge->bus->prefetchable_range->limit);
|
||||
}
|
||||
|
||||
for (region = dev->regions; region; region = region->next) {
|
||||
|
42
hw/pci/pci.c
42
hw/pci/pci.c
@ -1456,24 +1456,26 @@ static PciBridgeInfo *qmp_query_pci_bridge(PCIDevice *dev, PCIBus *bus,
|
||||
int bus_num)
|
||||
{
|
||||
PciBridgeInfo *info;
|
||||
PciMemoryRange *range;
|
||||
|
||||
info = g_malloc0(sizeof(*info));
|
||||
info = g_new0(PciBridgeInfo, 1);
|
||||
|
||||
info->bus.number = dev->config[PCI_PRIMARY_BUS];
|
||||
info->bus.secondary = dev->config[PCI_SECONDARY_BUS];
|
||||
info->bus.subordinate = dev->config[PCI_SUBORDINATE_BUS];
|
||||
info->bus = g_new0(PciBusInfo, 1);
|
||||
info->bus->number = dev->config[PCI_PRIMARY_BUS];
|
||||
info->bus->secondary = dev->config[PCI_SECONDARY_BUS];
|
||||
info->bus->subordinate = dev->config[PCI_SUBORDINATE_BUS];
|
||||
|
||||
info->bus.io_range = g_malloc0(sizeof(*info->bus.io_range));
|
||||
info->bus.io_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO);
|
||||
info->bus.io_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO);
|
||||
range = info->bus->io_range = g_new0(PciMemoryRange, 1);
|
||||
range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_IO);
|
||||
range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_IO);
|
||||
|
||||
info->bus.memory_range = g_malloc0(sizeof(*info->bus.memory_range));
|
||||
info->bus.memory_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
|
||||
info->bus.memory_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
|
||||
range = info->bus->memory_range = g_new0(PciMemoryRange, 1);
|
||||
range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
|
||||
range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY);
|
||||
|
||||
info->bus.prefetchable_range = g_malloc0(sizeof(*info->bus.prefetchable_range));
|
||||
info->bus.prefetchable_range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
|
||||
info->bus.prefetchable_range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
|
||||
range = info->bus->prefetchable_range = g_new0(PciMemoryRange, 1);
|
||||
range->base = pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
|
||||
range->limit = pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH);
|
||||
|
||||
if (dev->config[PCI_SECONDARY_BUS] != 0) {
|
||||
PCIBus *child_bus = pci_find_bus_nr(bus, dev->config[PCI_SECONDARY_BUS]);
|
||||
@ -1494,21 +1496,23 @@ static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus,
|
||||
uint8_t type;
|
||||
int class;
|
||||
|
||||
info = g_malloc0(sizeof(*info));
|
||||
info = g_new0(PciDeviceInfo, 1);
|
||||
info->bus = bus_num;
|
||||
info->slot = PCI_SLOT(dev->devfn);
|
||||
info->function = PCI_FUNC(dev->devfn);
|
||||
|
||||
info->class_info = g_new0(PciDeviceClass, 1);
|
||||
class = pci_get_word(dev->config + PCI_CLASS_DEVICE);
|
||||
info->class_info.q_class = class;
|
||||
info->class_info->q_class = class;
|
||||
desc = get_class_desc(class);
|
||||
if (desc->desc) {
|
||||
info->class_info.has_desc = true;
|
||||
info->class_info.desc = g_strdup(desc->desc);
|
||||
info->class_info->has_desc = true;
|
||||
info->class_info->desc = g_strdup(desc->desc);
|
||||
}
|
||||
|
||||
info->id.vendor = pci_get_word(dev->config + PCI_VENDOR_ID);
|
||||
info->id.device = pci_get_word(dev->config + PCI_DEVICE_ID);
|
||||
info->id = g_new0(PciDeviceId, 1);
|
||||
info->id->vendor = pci_get_word(dev->config + PCI_VENDOR_ID);
|
||||
info->id->device = pci_get_word(dev->config + PCI_DEVICE_ID);
|
||||
info->regions = qmp_query_pci_regions(dev);
|
||||
info->qdev_id = g_strdup(dev->qdev.id ? dev->qdev.id : "");
|
||||
|
||||
|
270
qapi-schema.json
270
qapi-schema.json
@ -71,7 +71,7 @@
|
||||
#
|
||||
# Since 0.14.0
|
||||
##
|
||||
{ 'type': 'NameInfo', 'data': {'*name': 'str'} }
|
||||
{ 'struct': 'NameInfo', 'data': {'*name': 'str'} }
|
||||
|
||||
##
|
||||
# @query-name:
|
||||
@ -95,7 +95,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'KvmInfo', 'data': {'enabled': 'bool', 'present': 'bool'} }
|
||||
{ 'struct': 'KvmInfo', 'data': {'enabled': 'bool', 'present': 'bool'} }
|
||||
|
||||
##
|
||||
# @query-kvm:
|
||||
@ -170,7 +170,7 @@
|
||||
#
|
||||
# Notes: @singlestep is enabled through the GDB stub
|
||||
##
|
||||
{ 'type': 'StatusInfo',
|
||||
{ 'struct': 'StatusInfo',
|
||||
'data': {'running': 'bool', 'singlestep': 'bool', 'status': 'RunState'} }
|
||||
|
||||
##
|
||||
@ -195,7 +195,7 @@
|
||||
#
|
||||
# Notes: If no UUID was specified for the guest, a null UUID is returned.
|
||||
##
|
||||
{ 'type': 'UuidInfo', 'data': {'UUID': 'str'} }
|
||||
{ 'struct': 'UuidInfo', 'data': {'UUID': 'str'} }
|
||||
|
||||
##
|
||||
# @query-uuid:
|
||||
@ -226,7 +226,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'ChardevInfo', 'data': {'label': 'str',
|
||||
{ 'struct': 'ChardevInfo', 'data': {'label': 'str',
|
||||
'filename': 'str',
|
||||
'frontend-open': 'bool'} }
|
||||
|
||||
@ -250,7 +250,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'ChardevBackendInfo', 'data': {'name': 'str'} }
|
||||
{ 'struct': 'ChardevBackendInfo', 'data': {'name': 'str'} }
|
||||
|
||||
##
|
||||
# @query-chardev-backends:
|
||||
@ -339,7 +339,7 @@
|
||||
#
|
||||
# Since: 1.2.0
|
||||
##
|
||||
{ 'type': 'EventInfo', 'data': {'name': 'str'} }
|
||||
{ 'struct': 'EventInfo', 'data': {'name': 'str'} }
|
||||
|
||||
##
|
||||
# @query-events:
|
||||
@ -380,7 +380,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'MigrationStats',
|
||||
{ 'struct': 'MigrationStats',
|
||||
'data': {'transferred': 'int', 'remaining': 'int', 'total': 'int' ,
|
||||
'duplicate': 'int', 'skipped': 'int', 'normal': 'int',
|
||||
'normal-bytes': 'int', 'dirty-pages-rate' : 'int',
|
||||
@ -405,7 +405,7 @@
|
||||
#
|
||||
# Since: 1.2
|
||||
##
|
||||
{ 'type': 'XBZRLECacheStats',
|
||||
{ 'struct': 'XBZRLECacheStats',
|
||||
'data': {'cache-size': 'int', 'bytes': 'int', 'pages': 'int',
|
||||
'cache-miss': 'int', 'cache-miss-rate': 'number',
|
||||
'overflow': 'int' } }
|
||||
@ -476,7 +476,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'MigrationInfo',
|
||||
{ 'struct': 'MigrationInfo',
|
||||
'data': {'*status': 'MigrationStatus', '*ram': 'MigrationStats',
|
||||
'*disk': 'MigrationStats',
|
||||
'*xbzrle-cache': 'XBZRLECacheStats',
|
||||
@ -534,7 +534,7 @@
|
||||
#
|
||||
# Since: 1.2
|
||||
##
|
||||
{ 'type': 'MigrationCapabilityStatus',
|
||||
{ 'struct': 'MigrationCapabilityStatus',
|
||||
'data': { 'capability' : 'MigrationCapability', 'state' : 'bool' } }
|
||||
|
||||
##
|
||||
@ -575,7 +575,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'MouseInfo',
|
||||
{ 'struct': 'MouseInfo',
|
||||
'data': {'name': 'str', 'index': 'int', 'current': 'bool',
|
||||
'absolute': 'bool'} }
|
||||
|
||||
@ -621,7 +621,7 @@
|
||||
# Notes: @halted is a transient state that changes frequently. By the time the
|
||||
# data is sent to the client, the guest may no longer be halted.
|
||||
##
|
||||
{ 'type': 'CpuInfo',
|
||||
{ 'struct': 'CpuInfo',
|
||||
'data': {'CPU': 'int', 'current': 'bool', 'halted': 'bool', '*pc': 'int',
|
||||
'*nip': 'int', '*npc': 'int', '*PC': 'int', 'thread_id': 'int'} }
|
||||
|
||||
@ -647,7 +647,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'IOThreadInfo',
|
||||
{ 'struct': 'IOThreadInfo',
|
||||
'data': {'id': 'str', 'thread-id': 'int'} }
|
||||
|
||||
##
|
||||
@ -700,7 +700,7 @@
|
||||
#
|
||||
# Since: 2.1
|
||||
##
|
||||
{ 'type': 'VncBasicInfo',
|
||||
{ 'struct': 'VncBasicInfo',
|
||||
'data': { 'host': 'str',
|
||||
'service': 'str',
|
||||
'family': 'NetworkAddressFamily',
|
||||
@ -715,7 +715,7 @@
|
||||
#
|
||||
# Since: 2.1
|
||||
##
|
||||
{ 'type': 'VncServerInfo',
|
||||
{ 'struct': 'VncServerInfo',
|
||||
'base': 'VncBasicInfo',
|
||||
'data': { '*auth': 'str' } }
|
||||
|
||||
@ -732,7 +732,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'VncClientInfo',
|
||||
{ 'struct': 'VncClientInfo',
|
||||
'base': 'VncBasicInfo',
|
||||
'data': { '*x509_dname': 'str', '*sasl_username': 'str' } }
|
||||
|
||||
@ -772,7 +772,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'VncInfo',
|
||||
{ 'struct': 'VncInfo',
|
||||
'data': {'enabled': 'bool', '*host': 'str',
|
||||
'*family': 'NetworkAddressFamily',
|
||||
'*service': 'str', '*auth': 'str', '*clients': ['VncClientInfo']} }
|
||||
@ -826,7 +826,7 @@
|
||||
#
|
||||
# Since: 2.3
|
||||
##
|
||||
{ 'type': 'VncInfo2',
|
||||
{ 'struct': 'VncInfo2',
|
||||
'data': { 'id' : 'str',
|
||||
'server' : ['VncBasicInfo'],
|
||||
'clients' : ['VncClientInfo'],
|
||||
@ -869,7 +869,7 @@
|
||||
#
|
||||
# Since: 2.1
|
||||
##
|
||||
{ 'type': 'SpiceBasicInfo',
|
||||
{ 'struct': 'SpiceBasicInfo',
|
||||
'data': { 'host': 'str',
|
||||
'port': 'str',
|
||||
'family': 'NetworkAddressFamily' } }
|
||||
@ -883,7 +883,7 @@
|
||||
#
|
||||
# Since: 2.1
|
||||
##
|
||||
{ 'type': 'SpiceServerInfo',
|
||||
{ 'struct': 'SpiceServerInfo',
|
||||
'base': 'SpiceBasicInfo',
|
||||
'data': { '*auth': 'str' } }
|
||||
|
||||
@ -907,7 +907,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'SpiceChannel',
|
||||
{ 'struct': 'SpiceChannel',
|
||||
'base': 'SpiceBasicInfo',
|
||||
'data': {'connection-id': 'int', 'channel-type': 'int', 'channel-id': 'int',
|
||||
'tls': 'bool'} }
|
||||
@ -965,7 +965,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'SpiceInfo',
|
||||
{ 'struct': 'SpiceInfo',
|
||||
'data': {'enabled': 'bool', 'migrated': 'bool', '*host': 'str', '*port': 'int',
|
||||
'*tls-port': 'int', '*auth': 'str', '*compiled-version': 'str',
|
||||
'mouse-mode': 'SpiceQueryMouseMode', '*channels': ['SpiceChannel']} }
|
||||
@ -991,7 +991,7 @@
|
||||
# Since: 0.14.0
|
||||
#
|
||||
##
|
||||
{ 'type': 'BalloonInfo', 'data': {'actual': 'int' } }
|
||||
{ 'struct': 'BalloonInfo', 'data': {'actual': 'int' } }
|
||||
|
||||
##
|
||||
# @query-balloon:
|
||||
@ -1018,7 +1018,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'PciMemoryRange', 'data': {'base': 'int', 'limit': 'int'} }
|
||||
{ 'struct': 'PciMemoryRange', 'data': {'base': 'int', 'limit': 'int'} }
|
||||
|
||||
##
|
||||
# @PciMemoryRegion
|
||||
@ -1036,41 +1036,80 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'PciMemoryRegion',
|
||||
{ 'struct': 'PciMemoryRegion',
|
||||
'data': {'bar': 'int', 'type': 'str', 'address': 'int', 'size': 'int',
|
||||
'*prefetch': 'bool', '*mem_type_64': 'bool' } }
|
||||
|
||||
##
|
||||
# @PciBusInfo:
|
||||
#
|
||||
# Information about a bus of a PCI Bridge device
|
||||
#
|
||||
# @number: primary bus interface number. This should be the number of the
|
||||
# bus the device resides on.
|
||||
#
|
||||
# @secondary: secondary bus interface number. This is the number of the
|
||||
# main bus for the bridge
|
||||
#
|
||||
# @subordinate: This is the highest number bus that resides below the
|
||||
# bridge.
|
||||
#
|
||||
# @io_range: The PIO range for all devices on this bridge
|
||||
#
|
||||
# @memory_range: The MMIO range for all devices on this bridge
|
||||
#
|
||||
# @prefetchable_range: The range of prefetchable MMIO for all devices on
|
||||
# this bridge
|
||||
#
|
||||
# Since: 2.4
|
||||
##
|
||||
{ 'struct': 'PciBusInfo',
|
||||
'data': {'number': 'int', 'secondary': 'int', 'subordinate': 'int',
|
||||
'io_range': 'PciMemoryRange',
|
||||
'memory_range': 'PciMemoryRange',
|
||||
'prefetchable_range': 'PciMemoryRange' } }
|
||||
|
||||
##
|
||||
# @PciBridgeInfo:
|
||||
#
|
||||
# Information about a PCI Bridge device
|
||||
#
|
||||
# @bus.number: primary bus interface number. This should be the number of the
|
||||
# bus the device resides on.
|
||||
#
|
||||
# @bus.secondary: secondary bus interface number. This is the number of the
|
||||
# main bus for the bridge
|
||||
#
|
||||
# @bus.subordinate: This is the highest number bus that resides below the
|
||||
# bridge.
|
||||
#
|
||||
# @bus.io_range: The PIO range for all devices on this bridge
|
||||
#
|
||||
# @bus.memory_range: The MMIO range for all devices on this bridge
|
||||
#
|
||||
# @bus.prefetchable_range: The range of prefetchable MMIO for all devices on
|
||||
# this bridge
|
||||
# @bus: information about the bus the device resides on
|
||||
#
|
||||
# @devices: a list of @PciDeviceInfo for each device on this bridge
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'PciBridgeInfo',
|
||||
'data': {'bus': { 'number': 'int', 'secondary': 'int', 'subordinate': 'int',
|
||||
'io_range': 'PciMemoryRange',
|
||||
'memory_range': 'PciMemoryRange',
|
||||
'prefetchable_range': 'PciMemoryRange' },
|
||||
'*devices': ['PciDeviceInfo']} }
|
||||
{ 'struct': 'PciBridgeInfo',
|
||||
'data': {'bus': 'PciBusInfo', '*devices': ['PciDeviceInfo']} }
|
||||
|
||||
##
|
||||
# @PciDeviceClass:
|
||||
#
|
||||
# Information about the Class of a PCI device
|
||||
#
|
||||
# @desc: #optional a string description of the device's class
|
||||
#
|
||||
# @class: the class code of the device
|
||||
#
|
||||
# Since: 2.4
|
||||
##
|
||||
{ 'struct': 'PciDeviceClass',
|
||||
'data': {'*desc': 'str', 'class': 'int'} }
|
||||
|
||||
##
|
||||
# @PciDeviceId:
|
||||
#
|
||||
# Information about the Id of a PCI device
|
||||
#
|
||||
# @device: the PCI device id
|
||||
#
|
||||
# @vendor: the PCI vendor id
|
||||
#
|
||||
# Since: 2.4
|
||||
##
|
||||
{ 'struct': 'PciDeviceId',
|
||||
'data': {'device': 'int', 'vendor': 'int'} }
|
||||
|
||||
##
|
||||
# @PciDeviceInfo:
|
||||
@ -1083,13 +1122,9 @@
|
||||
#
|
||||
# @function: the function of the slot used by the device
|
||||
#
|
||||
# @class_info.desc: #optional a string description of the device's class
|
||||
# @class_info: the class of the device
|
||||
#
|
||||
# @class_info.class: the class code of the device
|
||||
#
|
||||
# @id.device: the PCI device id
|
||||
#
|
||||
# @id.vendor: the PCI vendor id
|
||||
# @id: the PCI device id
|
||||
#
|
||||
# @irq: #optional if an IRQ is assigned to the device, the IRQ number
|
||||
#
|
||||
@ -1104,10 +1139,9 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'PciDeviceInfo',
|
||||
{ 'struct': 'PciDeviceInfo',
|
||||
'data': {'bus': 'int', 'slot': 'int', 'function': 'int',
|
||||
'class_info': {'*desc': 'str', 'class': 'int'},
|
||||
'id': {'device': 'int', 'vendor': 'int'},
|
||||
'class_info': 'PciDeviceClass', 'id': 'PciDeviceId',
|
||||
'*irq': 'int', 'qdev_id': 'str', '*pci_bridge': 'PciBridgeInfo',
|
||||
'regions': ['PciMemoryRegion']} }
|
||||
|
||||
@ -1122,7 +1156,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'PciInfo', 'data': {'bus': 'int', 'devices': ['PciDeviceInfo']} }
|
||||
{ 'struct': 'PciInfo', 'data': {'bus': 'int', 'devices': ['PciDeviceInfo']} }
|
||||
|
||||
##
|
||||
# @query-pci:
|
||||
@ -1341,7 +1375,7 @@
|
||||
#
|
||||
# Since: 1.6
|
||||
###
|
||||
{ 'type': 'Abort',
|
||||
{ 'struct': 'Abort',
|
||||
'data': { } }
|
||||
|
||||
##
|
||||
@ -1506,7 +1540,7 @@
|
||||
#
|
||||
# Since: 1.2
|
||||
##
|
||||
{ 'type': 'ObjectPropertyInfo',
|
||||
{ 'struct': 'ObjectPropertyInfo',
|
||||
'data': { 'name': 'str', 'type': 'str' } }
|
||||
|
||||
##
|
||||
@ -1561,8 +1595,8 @@
|
||||
##
|
||||
{ 'command': 'qom-get',
|
||||
'data': { 'path': 'str', 'property': 'str' },
|
||||
'returns': 'visitor',
|
||||
'gen': 'no' }
|
||||
'returns': '**',
|
||||
'gen': false }
|
||||
|
||||
##
|
||||
# @qom-set:
|
||||
@ -1579,8 +1613,8 @@
|
||||
# Since: 1.2
|
||||
##
|
||||
{ 'command': 'qom-set',
|
||||
'data': { 'path': 'str', 'property': 'str', 'value': 'visitor' },
|
||||
'gen': 'no' }
|
||||
'data': { 'path': 'str', 'property': 'str', 'value': '**' },
|
||||
'gen': false }
|
||||
|
||||
##
|
||||
# @set_password:
|
||||
@ -1691,7 +1725,7 @@
|
||||
#
|
||||
# Notes: This command is experimental and may change syntax in future releases.
|
||||
##
|
||||
{ 'type': 'ObjectTypeInfo',
|
||||
{ 'struct': 'ObjectTypeInfo',
|
||||
'data': { 'name': 'str' } }
|
||||
|
||||
##
|
||||
@ -1723,7 +1757,7 @@
|
||||
#
|
||||
# Since: 1.2
|
||||
##
|
||||
{ 'type': 'DevicePropertyInfo',
|
||||
{ 'struct': 'DevicePropertyInfo',
|
||||
'data': { 'name': 'str', 'type': 'str', '*description': 'str' } }
|
||||
|
||||
##
|
||||
@ -1903,7 +1937,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'DumpGuestMemoryCapability',
|
||||
{ 'struct': 'DumpGuestMemoryCapability',
|
||||
'data': {
|
||||
'formats': ['DumpGuestMemoryFormat'] } }
|
||||
|
||||
@ -1943,7 +1977,7 @@
|
||||
##
|
||||
{ 'command': 'netdev_add',
|
||||
'data': {'type': 'str', 'id': 'str', '*props': '**'},
|
||||
'gen': 'no' }
|
||||
'gen': false }
|
||||
|
||||
##
|
||||
# @netdev_del:
|
||||
@ -1976,8 +2010,8 @@
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'command': 'object-add',
|
||||
'data': {'qom-type': 'str', 'id': 'str', '*props': 'dict'},
|
||||
'gen': 'no' }
|
||||
'data': {'qom-type': 'str', 'id': 'str', '*props': '**'},
|
||||
'gen': false }
|
||||
|
||||
##
|
||||
# @object-del:
|
||||
@ -2000,7 +2034,7 @@
|
||||
#
|
||||
# Since 1.2
|
||||
##
|
||||
{ 'type': 'NetdevNoneOptions',
|
||||
{ 'struct': 'NetdevNoneOptions',
|
||||
'data': { } }
|
||||
|
||||
##
|
||||
@ -2020,7 +2054,7 @@
|
||||
#
|
||||
# Since 1.2
|
||||
##
|
||||
{ 'type': 'NetLegacyNicOptions',
|
||||
{ 'struct': 'NetLegacyNicOptions',
|
||||
'data': {
|
||||
'*netdev': 'str',
|
||||
'*macaddr': 'str',
|
||||
@ -2035,7 +2069,7 @@
|
||||
#
|
||||
# Since 1.2
|
||||
##
|
||||
{ 'type': 'String',
|
||||
{ 'struct': 'String',
|
||||
'data': {
|
||||
'str': 'str' } }
|
||||
|
||||
@ -2078,7 +2112,7 @@
|
||||
#
|
||||
# Since 1.2
|
||||
##
|
||||
{ 'type': 'NetdevUserOptions',
|
||||
{ 'struct': 'NetdevUserOptions',
|
||||
'data': {
|
||||
'*hostname': 'str',
|
||||
'*restrict': 'bool',
|
||||
@ -2130,7 +2164,7 @@
|
||||
#
|
||||
# Since 1.2
|
||||
##
|
||||
{ 'type': 'NetdevTapOptions',
|
||||
{ 'struct': 'NetdevTapOptions',
|
||||
'data': {
|
||||
'*ifname': 'str',
|
||||
'*fd': 'str',
|
||||
@ -2166,7 +2200,7 @@
|
||||
#
|
||||
# Since 1.2
|
||||
##
|
||||
{ 'type': 'NetdevSocketOptions',
|
||||
{ 'struct': 'NetdevSocketOptions',
|
||||
'data': {
|
||||
'*fd': 'str',
|
||||
'*listen': 'str',
|
||||
@ -2214,7 +2248,7 @@
|
||||
#
|
||||
# Since 2.1
|
||||
##
|
||||
{ 'type': 'NetdevL2TPv3Options',
|
||||
{ 'struct': 'NetdevL2TPv3Options',
|
||||
'data': {
|
||||
'src': 'str',
|
||||
'dst': 'str',
|
||||
@ -2246,7 +2280,7 @@
|
||||
#
|
||||
# Since 1.2
|
||||
##
|
||||
{ 'type': 'NetdevVdeOptions',
|
||||
{ 'struct': 'NetdevVdeOptions',
|
||||
'data': {
|
||||
'*sock': 'str',
|
||||
'*port': 'uint16',
|
||||
@ -2265,7 +2299,7 @@
|
||||
#
|
||||
# Since 1.2
|
||||
##
|
||||
{ 'type': 'NetdevDumpOptions',
|
||||
{ 'struct': 'NetdevDumpOptions',
|
||||
'data': {
|
||||
'*len': 'size',
|
||||
'*file': 'str' } }
|
||||
@ -2281,7 +2315,7 @@
|
||||
#
|
||||
# Since 1.2
|
||||
##
|
||||
{ 'type': 'NetdevBridgeOptions',
|
||||
{ 'struct': 'NetdevBridgeOptions',
|
||||
'data': {
|
||||
'*br': 'str',
|
||||
'*helper': 'str' } }
|
||||
@ -2295,7 +2329,7 @@
|
||||
#
|
||||
# Since 1.2
|
||||
##
|
||||
{ 'type': 'NetdevHubPortOptions',
|
||||
{ 'struct': 'NetdevHubPortOptions',
|
||||
'data': {
|
||||
'hubid': 'int32' } }
|
||||
|
||||
@ -2315,7 +2349,7 @@
|
||||
#
|
||||
# Since 2.0
|
||||
##
|
||||
{ 'type': 'NetdevNetmapOptions',
|
||||
{ 'struct': 'NetdevNetmapOptions',
|
||||
'data': {
|
||||
'ifname': 'str',
|
||||
'*devname': 'str' } }
|
||||
@ -2331,7 +2365,7 @@
|
||||
#
|
||||
# Since 2.1
|
||||
##
|
||||
{ 'type': 'NetdevVhostUserOptions',
|
||||
{ 'struct': 'NetdevVhostUserOptions',
|
||||
'data': {
|
||||
'chardev': 'str',
|
||||
'*vhostforce': 'bool' } }
|
||||
@ -2376,7 +2410,7 @@
|
||||
#
|
||||
# Since 1.2
|
||||
##
|
||||
{ 'type': 'NetLegacy',
|
||||
{ 'struct': 'NetLegacy',
|
||||
'data': {
|
||||
'*vlan': 'int32',
|
||||
'*id': 'str',
|
||||
@ -2394,7 +2428,7 @@
|
||||
#
|
||||
# Since 1.2
|
||||
##
|
||||
{ 'type': 'Netdev',
|
||||
{ 'struct': 'Netdev',
|
||||
'data': {
|
||||
'id': 'str',
|
||||
'opts': 'NetClientOptions' } }
|
||||
@ -2418,7 +2452,7 @@
|
||||
#
|
||||
# Since 1.3
|
||||
##
|
||||
{ 'type': 'InetSocketAddress',
|
||||
{ 'struct': 'InetSocketAddress',
|
||||
'data': {
|
||||
'host': 'str',
|
||||
'port': 'str',
|
||||
@ -2435,7 +2469,7 @@
|
||||
#
|
||||
# Since 1.3
|
||||
##
|
||||
{ 'type': 'UnixSocketAddress',
|
||||
{ 'struct': 'UnixSocketAddress',
|
||||
'data': {
|
||||
'path': 'str' } }
|
||||
|
||||
@ -2500,7 +2534,7 @@
|
||||
#
|
||||
# Since: 1.2.0
|
||||
##
|
||||
{ 'type': 'MachineInfo',
|
||||
{ 'struct': 'MachineInfo',
|
||||
'data': { 'name': 'str', '*alias': 'str',
|
||||
'*is-default': 'bool', 'cpu-max': 'int' } }
|
||||
|
||||
@ -2524,7 +2558,7 @@
|
||||
#
|
||||
# Since: 1.2.0
|
||||
##
|
||||
{ 'type': 'CpuDefinitionInfo',
|
||||
{ 'struct': 'CpuDefinitionInfo',
|
||||
'data': { 'name': 'str' } }
|
||||
|
||||
##
|
||||
@ -2549,7 +2583,7 @@
|
||||
#
|
||||
# Since: 1.2.0
|
||||
##
|
||||
{ 'type': 'AddfdInfo', 'data': {'fdset-id': 'int', 'fd': 'int'} }
|
||||
{ 'struct': 'AddfdInfo', 'data': {'fdset-id': 'int', 'fd': 'int'} }
|
||||
|
||||
##
|
||||
# @add-fd:
|
||||
@ -2605,7 +2639,7 @@
|
||||
#
|
||||
# Since: 1.2.0
|
||||
##
|
||||
{ 'type': 'FdsetFdInfo',
|
||||
{ 'struct': 'FdsetFdInfo',
|
||||
'data': {'fd': 'int', '*opaque': 'str'} }
|
||||
|
||||
##
|
||||
@ -2619,7 +2653,7 @@
|
||||
#
|
||||
# Since: 1.2.0
|
||||
##
|
||||
{ 'type': 'FdsetInfo',
|
||||
{ 'struct': 'FdsetInfo',
|
||||
'data': {'fdset-id': 'int', 'fds': ['FdsetFdInfo']} }
|
||||
|
||||
##
|
||||
@ -2645,7 +2679,7 @@
|
||||
#
|
||||
# Since: 1.2.0
|
||||
##
|
||||
{ 'type': 'TargetInfo',
|
||||
{ 'struct': 'TargetInfo',
|
||||
'data': { 'arch': 'str' } }
|
||||
|
||||
##
|
||||
@ -2745,7 +2779,7 @@
|
||||
#
|
||||
# Since: 1.4
|
||||
##
|
||||
{ 'type': 'ChardevFile', 'data': { '*in' : 'str',
|
||||
{ 'struct': 'ChardevFile', 'data': { '*in' : 'str',
|
||||
'out' : 'str' } }
|
||||
|
||||
##
|
||||
@ -2759,7 +2793,7 @@
|
||||
#
|
||||
# Since: 1.4
|
||||
##
|
||||
{ 'type': 'ChardevHostdev', 'data': { 'device' : 'str' } }
|
||||
{ 'struct': 'ChardevHostdev', 'data': { 'device' : 'str' } }
|
||||
|
||||
##
|
||||
# @ChardevSocket:
|
||||
@ -2781,7 +2815,7 @@
|
||||
#
|
||||
# Since: 1.4
|
||||
##
|
||||
{ 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress',
|
||||
{ 'struct': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress',
|
||||
'*server' : 'bool',
|
||||
'*wait' : 'bool',
|
||||
'*nodelay' : 'bool',
|
||||
@ -2798,7 +2832,7 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'type': 'ChardevUdp', 'data': { 'remote' : 'SocketAddress',
|
||||
{ 'struct': 'ChardevUdp', 'data': { 'remote' : 'SocketAddress',
|
||||
'*local' : 'SocketAddress' } }
|
||||
|
||||
##
|
||||
@ -2810,7 +2844,7 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'type': 'ChardevMux', 'data': { 'chardev' : 'str' } }
|
||||
{ 'struct': 'ChardevMux', 'data': { 'chardev' : 'str' } }
|
||||
|
||||
##
|
||||
# @ChardevStdio:
|
||||
@ -2823,7 +2857,7 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'type': 'ChardevStdio', 'data': { '*signal' : 'bool' } }
|
||||
{ 'struct': 'ChardevStdio', 'data': { '*signal' : 'bool' } }
|
||||
|
||||
##
|
||||
# @ChardevSpiceChannel:
|
||||
@ -2834,7 +2868,7 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'type': 'ChardevSpiceChannel', 'data': { 'type' : 'str' } }
|
||||
{ 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' } }
|
||||
|
||||
##
|
||||
# @ChardevSpicePort:
|
||||
@ -2845,7 +2879,7 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'type': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' } }
|
||||
{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' } }
|
||||
|
||||
##
|
||||
# @ChardevVC:
|
||||
@ -2859,7 +2893,7 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'type': 'ChardevVC', 'data': { '*width' : 'int',
|
||||
{ 'struct': 'ChardevVC', 'data': { '*width' : 'int',
|
||||
'*height' : 'int',
|
||||
'*cols' : 'int',
|
||||
'*rows' : 'int' } }
|
||||
@ -2873,7 +2907,7 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'type': 'ChardevRingbuf', 'data': { '*size' : 'int' } }
|
||||
{ 'struct': 'ChardevRingbuf', 'data': { '*size' : 'int' } }
|
||||
|
||||
##
|
||||
# @ChardevBackend:
|
||||
@ -2882,7 +2916,7 @@
|
||||
#
|
||||
# Since: 1.4 (testdev since 2.2)
|
||||
##
|
||||
{ 'type': 'ChardevDummy', 'data': { } }
|
||||
{ 'struct': 'ChardevDummy', 'data': { } }
|
||||
|
||||
{ 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile',
|
||||
'serial' : 'ChardevHostdev',
|
||||
@ -2915,7 +2949,7 @@
|
||||
#
|
||||
# Since: 1.4
|
||||
##
|
||||
{ 'type' : 'ChardevReturn', 'data': { '*pty' : 'str' } }
|
||||
{ 'struct' : 'ChardevReturn', 'data': { '*pty' : 'str' } }
|
||||
|
||||
##
|
||||
# @chardev-add:
|
||||
@ -3002,7 +3036,7 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'type': 'TPMPassthroughOptions', 'data': { '*path' : 'str',
|
||||
{ 'struct': 'TPMPassthroughOptions', 'data': { '*path' : 'str',
|
||||
'*cancel-path' : 'str'} }
|
||||
|
||||
##
|
||||
@ -3030,7 +3064,7 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'type': 'TPMInfo',
|
||||
{ 'struct': 'TPMInfo',
|
||||
'data': {'id': 'str',
|
||||
'model': 'TpmModel',
|
||||
'options': 'TpmTypeOptions' } }
|
||||
@ -3092,7 +3126,7 @@
|
||||
#
|
||||
# Since 1.5
|
||||
##
|
||||
{ 'type': 'AcpiTableOptions',
|
||||
{ 'struct': 'AcpiTableOptions',
|
||||
'data': {
|
||||
'*sig': 'str',
|
||||
'*rev': 'uint8',
|
||||
@ -3138,7 +3172,7 @@
|
||||
#
|
||||
# Since 1.5
|
||||
##
|
||||
{ 'type': 'CommandLineParameterInfo',
|
||||
{ 'struct': 'CommandLineParameterInfo',
|
||||
'data': { 'name': 'str',
|
||||
'type': 'CommandLineParameterType',
|
||||
'*help': 'str',
|
||||
@ -3155,7 +3189,7 @@
|
||||
#
|
||||
# Since 1.5
|
||||
##
|
||||
{ 'type': 'CommandLineOptionInfo',
|
||||
{ 'struct': 'CommandLineOptionInfo',
|
||||
'data': { 'option': 'str', 'parameters': ['CommandLineParameterInfo'] } }
|
||||
|
||||
##
|
||||
@ -3199,7 +3233,7 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'type': 'X86CPUFeatureWordInfo',
|
||||
{ 'struct': 'X86CPUFeatureWordInfo',
|
||||
'data': { 'cpuid-input-eax': 'int',
|
||||
'*cpuid-input-ecx': 'int',
|
||||
'cpuid-register': 'X86CPURegister32',
|
||||
@ -3252,7 +3286,7 @@
|
||||
# Since 1.6
|
||||
##
|
||||
|
||||
{ 'type': 'RxFilterInfo',
|
||||
{ 'struct': 'RxFilterInfo',
|
||||
'data': {
|
||||
'name': 'str',
|
||||
'promiscuous': 'bool',
|
||||
@ -3314,7 +3348,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type' : 'InputKeyEvent',
|
||||
{ 'struct' : 'InputKeyEvent',
|
||||
'data' : { 'key' : 'KeyValue',
|
||||
'down' : 'bool' } }
|
||||
|
||||
@ -3328,7 +3362,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type' : 'InputBtnEvent',
|
||||
{ 'struct' : 'InputBtnEvent',
|
||||
'data' : { 'button' : 'InputButton',
|
||||
'down' : 'bool' } }
|
||||
|
||||
@ -3343,7 +3377,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type' : 'InputMoveEvent',
|
||||
{ 'struct' : 'InputMoveEvent',
|
||||
'data' : { 'axis' : 'InputAxis',
|
||||
'value' : 'int' } }
|
||||
|
||||
@ -3426,7 +3460,7 @@
|
||||
#
|
||||
# Since: 2.1
|
||||
##
|
||||
{ 'type': 'NumaNodeOptions',
|
||||
{ 'struct': 'NumaNodeOptions',
|
||||
'data': {
|
||||
'*nodeid': 'uint16',
|
||||
'*cpus': ['uint16'],
|
||||
@ -3473,7 +3507,7 @@
|
||||
# Since: 2.1
|
||||
##
|
||||
|
||||
{ 'type': 'Memdev',
|
||||
{ 'struct': 'Memdev',
|
||||
'data': {
|
||||
'size': 'size',
|
||||
'merge': 'bool',
|
||||
@ -3516,7 +3550,7 @@
|
||||
#
|
||||
# Since: 2.1
|
||||
##
|
||||
{ 'type': 'PCDIMMDeviceInfo',
|
||||
{ 'struct': 'PCDIMMDeviceInfo',
|
||||
'data': { '*id': 'str',
|
||||
'addr': 'int',
|
||||
'size': 'int',
|
||||
@ -3570,7 +3604,7 @@
|
||||
#
|
||||
# Since: 2.1
|
||||
##
|
||||
{ 'type': 'ACPIOSTInfo',
|
||||
{ 'struct': 'ACPIOSTInfo',
|
||||
'data' : { '*device': 'str',
|
||||
'slot': 'str',
|
||||
'slot-type': 'ACPISlotType',
|
||||
|
@ -26,7 +26,7 @@
|
||||
#
|
||||
##
|
||||
|
||||
{ 'type': 'SnapshotInfo',
|
||||
{ 'struct': 'SnapshotInfo',
|
||||
'data': { 'id': 'str', 'name': 'str', 'vm-state-size': 'int',
|
||||
'date-sec': 'int', 'date-nsec': 'int',
|
||||
'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }
|
||||
@ -45,7 +45,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'ImageInfoSpecificQCow2',
|
||||
{ 'struct': 'ImageInfoSpecificQCow2',
|
||||
'data': {
|
||||
'compat': 'str',
|
||||
'*lazy-refcounts': 'bool',
|
||||
@ -66,7 +66,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'ImageInfoSpecificVmdk',
|
||||
{ 'struct': 'ImageInfoSpecificVmdk',
|
||||
'data': {
|
||||
'create-type': 'str',
|
||||
'cid': 'int',
|
||||
@ -126,7 +126,7 @@
|
||||
#
|
||||
##
|
||||
|
||||
{ 'type': 'ImageInfo',
|
||||
{ 'struct': 'ImageInfo',
|
||||
'data': {'filename': 'str', 'format': 'str', '*dirty-flag': 'bool',
|
||||
'*actual-size': 'int', 'virtual-size': 'int',
|
||||
'*cluster-size': 'int', '*encrypted': 'bool', '*compressed': 'bool',
|
||||
@ -178,7 +178,7 @@
|
||||
#
|
||||
##
|
||||
|
||||
{ 'type': 'ImageCheck',
|
||||
{ 'struct': 'ImageCheck',
|
||||
'data': {'filename': 'str', 'format': 'str', 'check-errors': 'int',
|
||||
'*image-end-offset': 'int', '*corruptions': 'int', '*leaks': 'int',
|
||||
'*corruptions-fixed': 'int', '*leaks-fixed': 'int',
|
||||
@ -196,7 +196,7 @@
|
||||
#
|
||||
# Since: 2.3
|
||||
##
|
||||
{ 'type': 'BlockdevCacheInfo',
|
||||
{ 'struct': 'BlockdevCacheInfo',
|
||||
'data': { 'writeback': 'bool',
|
||||
'direct': 'bool',
|
||||
'no-flush': 'bool' } }
|
||||
@ -267,7 +267,7 @@
|
||||
# Since: 0.14.0
|
||||
#
|
||||
##
|
||||
{ 'type': 'BlockDeviceInfo',
|
||||
{ 'struct': 'BlockDeviceInfo',
|
||||
'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str',
|
||||
'*backing_file': 'str', 'backing_file_depth': 'int',
|
||||
'encrypted': 'bool', 'encryption_key_missing': 'bool',
|
||||
@ -321,7 +321,7 @@
|
||||
#
|
||||
# Since 1.7
|
||||
##
|
||||
{ 'type': 'BlockDeviceMapEntry',
|
||||
{ 'struct': 'BlockDeviceMapEntry',
|
||||
'data': { 'start': 'int', 'length': 'int', 'depth': 'int', 'zero': 'bool',
|
||||
'data': 'bool', '*offset': 'int' } }
|
||||
|
||||
@ -340,7 +340,7 @@
|
||||
#
|
||||
# Since: 1.3
|
||||
##
|
||||
{ 'type': 'BlockDirtyInfo',
|
||||
{ 'struct': 'BlockDirtyInfo',
|
||||
'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32',
|
||||
'frozen': 'bool'} }
|
||||
|
||||
@ -375,7 +375,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'BlockInfo',
|
||||
{ 'struct': 'BlockInfo',
|
||||
'data': {'device': 'str', 'type': 'str', 'removable': 'bool',
|
||||
'locked': 'bool', '*inserted': 'BlockDeviceInfo',
|
||||
'*tray_open': 'bool', '*io-status': 'BlockDeviceIoStatus',
|
||||
@ -428,7 +428,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'BlockDeviceStats',
|
||||
{ 'struct': 'BlockDeviceStats',
|
||||
'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'rd_operations': 'int',
|
||||
'wr_operations': 'int', 'flush_operations': 'int',
|
||||
'flush_total_time_ns': 'int', 'wr_total_time_ns': 'int',
|
||||
@ -454,7 +454,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'BlockStats',
|
||||
{ 'struct': 'BlockStats',
|
||||
'data': {'*device': 'str', '*node-name': 'str',
|
||||
'stats': 'BlockDeviceStats',
|
||||
'*parent': 'BlockStats',
|
||||
@ -567,7 +567,7 @@
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'type': 'BlockJobInfo',
|
||||
{ 'struct': 'BlockJobInfo',
|
||||
'data': {'type': 'str', 'device': 'str', 'len': 'int',
|
||||
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
|
||||
'io-status': 'BlockDeviceIoStatus', 'ready': 'bool'} }
|
||||
@ -677,7 +677,7 @@
|
||||
# @mode: #optional whether and how QEMU should create a new image, default is
|
||||
# 'absolute-paths'.
|
||||
##
|
||||
{ 'type': 'BlockdevSnapshot',
|
||||
{ 'struct': 'BlockdevSnapshot',
|
||||
'data': { '*device': 'str', '*node-name': 'str',
|
||||
'snapshot-file': 'str', '*snapshot-node-name': 'str',
|
||||
'*format': 'str', '*mode': 'NewImageMode' } }
|
||||
@ -721,7 +721,7 @@
|
||||
#
|
||||
# Since: 1.6
|
||||
##
|
||||
{ 'type': 'DriveBackup',
|
||||
{ 'struct': 'DriveBackup',
|
||||
'data': { 'device': 'str', 'target': 'str', '*format': 'str',
|
||||
'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
|
||||
'*speed': 'int', '*bitmap': 'str',
|
||||
@ -756,7 +756,7 @@
|
||||
#
|
||||
# Since: 2.3
|
||||
##
|
||||
{ 'type': 'BlockdevBackup',
|
||||
{ 'struct': 'BlockdevBackup',
|
||||
'data': { 'device': 'str', 'target': 'str',
|
||||
'sync': 'MirrorSyncMode',
|
||||
'*speed': 'int',
|
||||
@ -977,7 +977,7 @@
|
||||
#
|
||||
# Since 2.4
|
||||
##
|
||||
{ 'type': 'BlockDirtyBitmap',
|
||||
{ 'struct': 'BlockDirtyBitmap',
|
||||
'data': { 'node': 'str', 'name': 'str' } }
|
||||
|
||||
##
|
||||
@ -992,7 +992,7 @@
|
||||
#
|
||||
# Since 2.4
|
||||
##
|
||||
{ 'type': 'BlockDirtyBitmapAdd',
|
||||
{ 'struct': 'BlockDirtyBitmapAdd',
|
||||
'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32' } }
|
||||
|
||||
##
|
||||
@ -1313,7 +1313,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevCacheOptions',
|
||||
{ 'struct': 'BlockdevCacheOptions',
|
||||
'data': { '*writeback': 'bool',
|
||||
'*direct': 'bool',
|
||||
'*no-flush': 'bool' } }
|
||||
@ -1360,7 +1360,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsBase',
|
||||
{ 'struct': 'BlockdevOptionsBase',
|
||||
'data': { 'driver': 'BlockdevDriver',
|
||||
'*id': 'str',
|
||||
'*node-name': 'str',
|
||||
@ -1382,7 +1382,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsFile',
|
||||
{ 'struct': 'BlockdevOptionsFile',
|
||||
'data': { 'filename': 'str' } }
|
||||
|
||||
##
|
||||
@ -1397,7 +1397,7 @@
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsNull',
|
||||
{ 'struct': 'BlockdevOptionsNull',
|
||||
'data': { '*size': 'int', '*latency-ns': 'uint64' } }
|
||||
|
||||
##
|
||||
@ -1413,7 +1413,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsVVFAT',
|
||||
{ 'struct': 'BlockdevOptionsVVFAT',
|
||||
'data': { 'dir': 'str', '*fat-type': 'int', '*floppy': 'bool',
|
||||
'*rw': 'bool' } }
|
||||
|
||||
@ -1427,7 +1427,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsGenericFormat',
|
||||
{ 'struct': 'BlockdevOptionsGenericFormat',
|
||||
'data': { 'file': 'BlockdevRef' } }
|
||||
|
||||
##
|
||||
@ -1443,7 +1443,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsGenericCOWFormat',
|
||||
{ 'struct': 'BlockdevOptionsGenericCOWFormat',
|
||||
'base': 'BlockdevOptionsGenericFormat',
|
||||
'data': { '*backing': 'BlockdevRef' } }
|
||||
|
||||
@ -1479,7 +1479,7 @@
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'Qcow2OverlapCheckFlags',
|
||||
{ 'struct': 'Qcow2OverlapCheckFlags',
|
||||
'data': { '*template': 'Qcow2OverlapCheckMode',
|
||||
'*main-header': 'bool',
|
||||
'*active-l1': 'bool',
|
||||
@ -1503,8 +1503,7 @@
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'union': 'Qcow2OverlapChecks',
|
||||
'discriminator': {},
|
||||
{ 'alternate': 'Qcow2OverlapChecks',
|
||||
'data': { 'flags': 'Qcow2OverlapCheckFlags',
|
||||
'mode': 'Qcow2OverlapCheckMode' } }
|
||||
|
||||
@ -1541,7 +1540,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsQcow2',
|
||||
{ 'struct': 'BlockdevOptionsQcow2',
|
||||
'base': 'BlockdevOptionsGenericCOWFormat',
|
||||
'data': { '*lazy-refcounts': 'bool',
|
||||
'*pass-discard-request': 'bool',
|
||||
@ -1576,7 +1575,7 @@
|
||||
# use the default value, 'archipelago'.
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsArchipelago',
|
||||
{ 'struct': 'BlockdevOptionsArchipelago',
|
||||
'data': { 'volume': 'str',
|
||||
'*mport': 'int',
|
||||
'*vport': 'int',
|
||||
@ -1628,7 +1627,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'BlkdebugInjectErrorOptions',
|
||||
{ 'struct': 'BlkdebugInjectErrorOptions',
|
||||
'data': { 'event': 'BlkdebugEvent',
|
||||
'*state': 'int',
|
||||
'*errno': 'int',
|
||||
@ -1651,7 +1650,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'BlkdebugSetStateOptions',
|
||||
{ 'struct': 'BlkdebugSetStateOptions',
|
||||
'data': { 'event': 'BlkdebugEvent',
|
||||
'*state': 'int',
|
||||
'new_state': 'int' } }
|
||||
@ -1673,7 +1672,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsBlkdebug',
|
||||
{ 'struct': 'BlockdevOptionsBlkdebug',
|
||||
'data': { 'image': 'BlockdevRef',
|
||||
'*config': 'str',
|
||||
'*align': 'int',
|
||||
@ -1691,7 +1690,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsBlkverify',
|
||||
{ 'struct': 'BlockdevOptionsBlkverify',
|
||||
'data': { 'test': 'BlockdevRef',
|
||||
'raw': 'BlockdevRef' } }
|
||||
|
||||
@ -1728,7 +1727,7 @@
|
||||
#
|
||||
# Since: 2.0
|
||||
##
|
||||
{ 'type': 'BlockdevOptionsQuorum',
|
||||
{ 'struct': 'BlockdevOptionsQuorum',
|
||||
'data': { '*blkverify': 'bool',
|
||||
'children': [ 'BlockdevRef' ],
|
||||
'vote-threshold': 'int',
|
||||
@ -1795,8 +1794,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'union': 'BlockdevRef',
|
||||
'discriminator': {},
|
||||
{ 'alternate': 'BlockdevRef',
|
||||
'data': { 'definition': 'BlockdevOptions',
|
||||
'reference': 'str' } }
|
||||
|
||||
|
@ -52,7 +52,7 @@
|
||||
#
|
||||
# Since: 1.7
|
||||
##
|
||||
{ 'type': 'BlockdevSnapshotInternal',
|
||||
{ 'struct': 'BlockdevSnapshotInternal',
|
||||
'data': { 'device': 'str', 'name': 'str' } }
|
||||
|
||||
##
|
||||
|
@ -28,16 +28,29 @@
|
||||
'data': [ 'GenericError', 'CommandNotFound', 'DeviceEncrypted',
|
||||
'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap' ] }
|
||||
|
||||
##
|
||||
# @VersionTriple
|
||||
#
|
||||
# A three-part version number.
|
||||
#
|
||||
# @qemu.major: The major version number.
|
||||
#
|
||||
# @qemu.minor: The minor version number.
|
||||
#
|
||||
# @qemu.micro: The micro version number.
|
||||
#
|
||||
# Since: 2.4
|
||||
##
|
||||
{ 'struct': 'VersionTriple',
|
||||
'data': {'major': 'int', 'minor': 'int', 'micro': 'int'} }
|
||||
|
||||
|
||||
##
|
||||
# @VersionInfo:
|
||||
#
|
||||
# A description of QEMU's version.
|
||||
#
|
||||
# @qemu.major: The major version of QEMU
|
||||
#
|
||||
# @qemu.minor: The minor version of QEMU
|
||||
#
|
||||
# @qemu.micro: The micro version of QEMU. By current convention, a micro
|
||||
# @qemu: The version of QEMU. By current convention, a micro
|
||||
# version of 50 signifies a development branch. A micro version
|
||||
# greater than or equal to 90 signifies a release candidate for
|
||||
# the next minor version. A micro version of less than 50
|
||||
@ -50,9 +63,8 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'VersionInfo',
|
||||
'data': {'qemu': {'major': 'int', 'minor': 'int', 'micro': 'int'},
|
||||
'package': 'str'} }
|
||||
{ 'struct': 'VersionInfo',
|
||||
'data': {'qemu': 'VersionTriple', 'package': 'str'} }
|
||||
|
||||
##
|
||||
# @query-version:
|
||||
@ -74,7 +86,7 @@
|
||||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'type': 'CommandInfo', 'data': {'name': 'str'} }
|
||||
{ 'struct': 'CommandInfo', 'data': {'name': 'str'} }
|
||||
|
||||
##
|
||||
# @query-commands:
|
||||
|
@ -32,7 +32,7 @@
|
||||
#
|
||||
# Since 2.2
|
||||
##
|
||||
{ 'type': 'TraceEventInfo',
|
||||
{ 'struct': 'TraceEventInfo',
|
||||
'data': {'name': 'str', 'state': 'TraceEventState'} }
|
||||
|
||||
##
|
||||
|
@ -150,7 +150,7 @@
|
||||
#
|
||||
# Since 1.1.0
|
||||
##
|
||||
{ 'type': 'GuestAgentCommandInfo',
|
||||
{ 'struct': 'GuestAgentCommandInfo',
|
||||
'data': { 'name': 'str', 'enabled': 'bool', 'success-response': 'bool' } }
|
||||
|
||||
##
|
||||
@ -164,7 +164,7 @@
|
||||
#
|
||||
# Since 0.15.0
|
||||
##
|
||||
{ 'type': 'GuestAgentInfo',
|
||||
{ 'struct': 'GuestAgentInfo',
|
||||
'data': { 'version': 'str',
|
||||
'supported_commands': ['GuestAgentCommandInfo'] } }
|
||||
##
|
||||
@ -195,7 +195,7 @@
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'command': 'guest-shutdown', 'data': { '*mode': 'str' },
|
||||
'success-response': 'no' }
|
||||
'success-response': false }
|
||||
|
||||
##
|
||||
# @guest-file-open:
|
||||
@ -242,7 +242,7 @@
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'type': 'GuestFileRead',
|
||||
{ 'struct': 'GuestFileRead',
|
||||
'data': { 'count': 'int', 'buf-b64': 'str', 'eof': 'bool' } }
|
||||
|
||||
##
|
||||
@ -274,7 +274,7 @@
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'type': 'GuestFileWrite',
|
||||
{ 'struct': 'GuestFileWrite',
|
||||
'data': { 'count': 'int', 'eof': 'bool' } }
|
||||
|
||||
##
|
||||
@ -309,7 +309,7 @@
|
||||
#
|
||||
# Since: 0.15.0
|
||||
##
|
||||
{ 'type': 'GuestFileSeek',
|
||||
{ 'struct': 'GuestFileSeek',
|
||||
'data': { 'position': 'int', 'eof': 'bool' } }
|
||||
|
||||
##
|
||||
@ -470,7 +470,7 @@
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'command': 'guest-suspend-disk', 'success-response': 'no' }
|
||||
{ 'command': 'guest-suspend-disk', 'success-response': false }
|
||||
|
||||
##
|
||||
# @guest-suspend-ram
|
||||
@ -502,7 +502,7 @@
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'command': 'guest-suspend-ram', 'success-response': 'no' }
|
||||
{ 'command': 'guest-suspend-ram', 'success-response': false }
|
||||
|
||||
##
|
||||
# @guest-suspend-hybrid
|
||||
@ -529,7 +529,7 @@
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'command': 'guest-suspend-hybrid', 'success-response': 'no' }
|
||||
{ 'command': 'guest-suspend-hybrid', 'success-response': false }
|
||||
|
||||
##
|
||||
# @GuestIpAddressType:
|
||||
@ -556,7 +556,7 @@
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'type': 'GuestIpAddress',
|
||||
{ 'struct': 'GuestIpAddress',
|
||||
'data': {'ip-address': 'str',
|
||||
'ip-address-type': 'GuestIpAddressType',
|
||||
'prefix': 'int'} }
|
||||
@ -572,7 +572,7 @@
|
||||
#
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'type': 'GuestNetworkInterface',
|
||||
{ 'struct': 'GuestNetworkInterface',
|
||||
'data': {'name': 'str',
|
||||
'*hardware-address': 'str',
|
||||
'*ip-addresses': ['GuestIpAddress'] } }
|
||||
@ -604,7 +604,7 @@
|
||||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'type': 'GuestLogicalProcessor',
|
||||
{ 'struct': 'GuestLogicalProcessor',
|
||||
'data': {'logical-id': 'int',
|
||||
'online': 'bool',
|
||||
'*can-offline': 'bool'} }
|
||||
@ -694,7 +694,7 @@
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'GuestPCIAddress',
|
||||
{ 'struct': 'GuestPCIAddress',
|
||||
'data': {'domain': 'int', 'bus': 'int',
|
||||
'slot': 'int', 'function': 'int'} }
|
||||
|
||||
@ -709,7 +709,7 @@
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'GuestDiskAddress',
|
||||
{ 'struct': 'GuestDiskAddress',
|
||||
'data': {'pci-controller': 'GuestPCIAddress',
|
||||
'bus-type': 'GuestDiskBusType',
|
||||
'bus': 'int', 'target': 'int', 'unit': 'int'} }
|
||||
@ -725,7 +725,7 @@
|
||||
#
|
||||
# Since: 2.2
|
||||
##
|
||||
{ 'type': 'GuestFilesystemInfo',
|
||||
{ 'struct': 'GuestFilesystemInfo',
|
||||
'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str',
|
||||
'disk': ['GuestDiskAddress']} }
|
||||
|
||||
@ -782,7 +782,7 @@
|
||||
#
|
||||
# Since: 2.3
|
||||
##
|
||||
{ 'type': 'GuestMemoryBlock',
|
||||
{ 'struct': 'GuestMemoryBlock',
|
||||
'data': {'phys-index': 'uint64',
|
||||
'online': 'bool',
|
||||
'*can-offline': 'bool'} }
|
||||
@ -835,7 +835,7 @@
|
||||
#
|
||||
# Since: 2.3
|
||||
##
|
||||
{ 'type': 'GuestMemoryBlockResponse',
|
||||
{ 'struct': 'GuestMemoryBlockResponse',
|
||||
'data': { 'phys-index': 'uint64',
|
||||
'response': 'GuestMemoryBlockResponseType',
|
||||
'*error-code': 'int' }}
|
||||
@ -876,7 +876,7 @@
|
||||
#
|
||||
# Since: 2.3
|
||||
##
|
||||
{ 'type': 'GuestMemoryBlockInfo',
|
||||
{ 'struct': 'GuestMemoryBlockInfo',
|
||||
'data': {'size': 'uint64'} }
|
||||
|
||||
##
|
||||
|
9
qmp.c
9
qmp.c
@ -45,15 +45,16 @@ NameInfo *qmp_query_name(Error **errp)
|
||||
|
||||
VersionInfo *qmp_query_version(Error **errp)
|
||||
{
|
||||
VersionInfo *info = g_malloc0(sizeof(*info));
|
||||
VersionInfo *info = g_new0(VersionInfo, 1);
|
||||
const char *version = QEMU_VERSION;
|
||||
char *tmp;
|
||||
|
||||
info->qemu.major = strtol(version, &tmp, 10);
|
||||
info->qemu = g_new0(VersionTriple, 1);
|
||||
info->qemu->major = strtol(version, &tmp, 10);
|
||||
tmp++;
|
||||
info->qemu.minor = strtol(tmp, &tmp, 10);
|
||||
info->qemu->minor = strtol(tmp, &tmp, 10);
|
||||
tmp++;
|
||||
info->qemu.micro = strtol(tmp, &tmp, 10);
|
||||
info->qemu->micro = strtol(tmp, &tmp, 10);
|
||||
info->package = g_strdup(QEMU_PKGVERSION);
|
||||
|
||||
return info;
|
||||
|
@ -2,7 +2,7 @@
|
||||
# QAPI command marshaller generator
|
||||
#
|
||||
# Copyright IBM, Corp. 2011
|
||||
# Copyright (C) 2014 Red Hat, Inc.
|
||||
# Copyright (C) 2014-2015 Red Hat, Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Anthony Liguori <aliguori@us.ibm.com>
|
||||
@ -28,7 +28,7 @@ def type_visitor(name):
|
||||
|
||||
def generate_command_decl(name, args, ret_type):
|
||||
arglist=""
|
||||
for argname, argtype, optional, structured in parse_args(args):
|
||||
for argname, argtype, optional in parse_args(args):
|
||||
argtype = c_type(argtype, is_param=True)
|
||||
if optional:
|
||||
arglist += "bool has_%s, " % c_var(argname)
|
||||
@ -53,7 +53,7 @@ def gen_sync_call(name, args, ret_type, indent=0):
|
||||
retval=""
|
||||
if ret_type:
|
||||
retval = "retval = "
|
||||
for argname, argtype, optional, structured in parse_args(args):
|
||||
for argname, argtype, optional in parse_args(args):
|
||||
if optional:
|
||||
arglist += "has_%s, " % c_var(argname)
|
||||
arglist += "%s, " % (c_var(argname))
|
||||
@ -96,7 +96,7 @@ Visitor *v;
|
||||
def gen_visitor_input_vars_decl(args):
|
||||
ret = ""
|
||||
push_indent()
|
||||
for argname, argtype, optional, structured in parse_args(args):
|
||||
for argname, argtype, optional in parse_args(args):
|
||||
if optional:
|
||||
ret += mcgen('''
|
||||
bool has_%(argname)s = false;
|
||||
@ -139,7 +139,7 @@ v = qapi_dealloc_get_visitor(md);
|
||||
v = qmp_input_get_visitor(mi);
|
||||
''')
|
||||
|
||||
for argname, argtype, optional, structured in parse_args(args):
|
||||
for argname, argtype, optional in parse_args(args):
|
||||
if optional:
|
||||
ret += mcgen('''
|
||||
visit_optional(v, &has_%(c_name)s, "%(name)s", %(errp)s);
|
||||
@ -293,17 +293,12 @@ out:
|
||||
|
||||
return ret
|
||||
|
||||
def option_value_matches(opt, val, cmd):
|
||||
if opt in cmd and cmd[opt] == val:
|
||||
return True
|
||||
return False
|
||||
|
||||
def gen_registry(commands):
|
||||
registry=""
|
||||
push_indent()
|
||||
for cmd in commands:
|
||||
options = 'QCO_NO_OPTIONS'
|
||||
if option_value_matches('success-response', 'no', cmd):
|
||||
if not cmd.get('success-response', True):
|
||||
options = 'QCO_NO_SUCCESS_RESP'
|
||||
|
||||
registry += mcgen('''
|
||||
|
@ -21,7 +21,7 @@ def _generate_event_api_name(event_name, params):
|
||||
l = len(api_name)
|
||||
|
||||
if params:
|
||||
for argname, argentry, optional, structured in parse_args(params):
|
||||
for argname, argentry, optional in parse_args(params):
|
||||
if optional:
|
||||
api_name += "bool has_%s,\n" % c_var(argname)
|
||||
api_name += "".ljust(l)
|
||||
@ -93,7 +93,7 @@ def generate_event_implement(api_name, event_name, params):
|
||||
""",
|
||||
event_name = event_name)
|
||||
|
||||
for argname, argentry, optional, structured in parse_args(params):
|
||||
for argname, argentry, optional in parse_args(params):
|
||||
if optional:
|
||||
ret += mcgen("""
|
||||
if (has_%(var)s) {
|
||||
|
@ -63,18 +63,13 @@ typedef struct %(name)sList
|
||||
def generate_struct_fields(members):
|
||||
ret = ''
|
||||
|
||||
for argname, argentry, optional, structured in parse_args(members):
|
||||
for argname, argentry, optional in parse_args(members):
|
||||
if optional:
|
||||
ret += mcgen('''
|
||||
bool has_%(c_name)s;
|
||||
''',
|
||||
c_name=c_var(argname))
|
||||
if structured:
|
||||
push_indent()
|
||||
ret += generate_struct({ "field": argname, "data": argentry})
|
||||
pop_indent()
|
||||
else:
|
||||
ret += mcgen('''
|
||||
ret += mcgen('''
|
||||
%(c_type)s %(c_name)s;
|
||||
''',
|
||||
c_type=c_type(argentry), c_name=c_var(argname))
|
||||
@ -83,7 +78,7 @@ def generate_struct_fields(members):
|
||||
|
||||
def generate_struct(expr):
|
||||
|
||||
structname = expr.get('type', "")
|
||||
structname = expr.get('struct', "")
|
||||
fieldname = expr.get('field', "")
|
||||
members = expr['data']
|
||||
base = expr.get('base')
|
||||
@ -170,9 +165,9 @@ typedef enum %(name)s
|
||||
|
||||
return lookup_decl + enum_decl
|
||||
|
||||
def generate_anon_union_qtypes(expr):
|
||||
def generate_alternate_qtypes(expr):
|
||||
|
||||
name = expr['union']
|
||||
name = expr['alternate']
|
||||
members = expr['data']
|
||||
|
||||
ret = mcgen('''
|
||||
@ -181,17 +176,8 @@ const int %(name)s_qtypes[QTYPE_MAX] = {
|
||||
name=name)
|
||||
|
||||
for key in members:
|
||||
qapi_type = members[key]
|
||||
if builtin_type_qtypes.has_key(qapi_type):
|
||||
qtype = builtin_type_qtypes[qapi_type]
|
||||
elif find_struct(qapi_type):
|
||||
qtype = "QTYPE_QDICT"
|
||||
elif find_union(qapi_type):
|
||||
qtype = "QTYPE_QDICT"
|
||||
elif find_enum(qapi_type):
|
||||
qtype = "QTYPE_QSTRING"
|
||||
else:
|
||||
assert False, "Invalid anonymous union member"
|
||||
qtype = find_alternate_member_qtype(members[key])
|
||||
assert qtype, "Invalid alternate member"
|
||||
|
||||
ret += mcgen('''
|
||||
[ %(qtype)s ] = %(abbrev)s_KIND_%(enum)s,
|
||||
@ -206,9 +192,9 @@ const int %(name)s_qtypes[QTYPE_MAX] = {
|
||||
return ret
|
||||
|
||||
|
||||
def generate_union(expr):
|
||||
def generate_union(expr, meta):
|
||||
|
||||
name = expr['union']
|
||||
name = expr[meta]
|
||||
typeinfo = expr['data']
|
||||
|
||||
base = expr.get('base')
|
||||
@ -242,10 +228,9 @@ struct %(name)s
|
||||
''')
|
||||
|
||||
if base:
|
||||
base_fields = find_struct(base)['data']
|
||||
if discriminator:
|
||||
base_fields = base_fields.copy()
|
||||
del base_fields[discriminator]
|
||||
assert discriminator
|
||||
base_fields = find_struct(base)['data'].copy()
|
||||
del base_fields[discriminator]
|
||||
ret += generate_struct_fields(base_fields)
|
||||
else:
|
||||
assert not discriminator
|
||||
@ -253,7 +238,7 @@ struct %(name)s
|
||||
ret += mcgen('''
|
||||
};
|
||||
''')
|
||||
if discriminator == {}:
|
||||
if meta == 'alternate':
|
||||
ret += mcgen('''
|
||||
extern const int %(name)s_qtypes[];
|
||||
''',
|
||||
@ -398,14 +383,14 @@ exprs = parse_schema(input_file)
|
||||
exprs = filter(lambda expr: not expr.has_key('gen'), exprs)
|
||||
|
||||
fdecl.write(guardstart("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
|
||||
for typename in builtin_types:
|
||||
for typename in builtin_types.keys():
|
||||
fdecl.write(generate_fwd_struct(typename, None, builtin_type=True))
|
||||
fdecl.write(guardend("QAPI_TYPES_BUILTIN_STRUCT_DECL"))
|
||||
|
||||
for expr in exprs:
|
||||
ret = "\n"
|
||||
if expr.has_key('type'):
|
||||
ret += generate_fwd_struct(expr['type'], expr['data'])
|
||||
if expr.has_key('struct'):
|
||||
ret += generate_fwd_struct(expr['struct'], expr['data'])
|
||||
elif expr.has_key('enum'):
|
||||
ret += generate_enum(expr['enum'], expr['data']) + "\n"
|
||||
ret += generate_fwd_enum_struct(expr['enum'], expr['data'])
|
||||
@ -417,8 +402,12 @@ for expr in exprs:
|
||||
ret += generate_enum('%sKind' % expr['union'], expr['data'].keys())
|
||||
fdef.write(generate_enum_lookup('%sKind' % expr['union'],
|
||||
expr['data'].keys()))
|
||||
if expr.get('discriminator') == {}:
|
||||
fdef.write(generate_anon_union_qtypes(expr))
|
||||
elif expr.has_key('alternate'):
|
||||
ret += generate_fwd_struct(expr['alternate'], expr['data']) + "\n"
|
||||
ret += generate_enum('%sKind' % expr['alternate'], expr['data'].keys())
|
||||
fdef.write(generate_enum_lookup('%sKind' % expr['alternate'],
|
||||
expr['data'].keys()))
|
||||
fdef.write(generate_alternate_qtypes(expr))
|
||||
else:
|
||||
continue
|
||||
fdecl.write(ret)
|
||||
@ -426,7 +415,7 @@ for expr in exprs:
|
||||
# to avoid header dependency hell, we always generate declarations
|
||||
# for built-in types in our header files and simply guard them
|
||||
fdecl.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
|
||||
for typename in builtin_types:
|
||||
for typename in builtin_types.keys():
|
||||
fdecl.write(generate_type_cleanup_decl(typename + "List"))
|
||||
fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
|
||||
|
||||
@ -435,24 +424,30 @@ fdecl.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DECL"))
|
||||
# over these cases
|
||||
if do_builtins:
|
||||
fdef.write(guardstart("QAPI_TYPES_BUILTIN_CLEANUP_DEF"))
|
||||
for typename in builtin_types:
|
||||
for typename in builtin_types.keys():
|
||||
fdef.write(generate_type_cleanup(typename + "List"))
|
||||
fdef.write(guardend("QAPI_TYPES_BUILTIN_CLEANUP_DEF"))
|
||||
|
||||
for expr in exprs:
|
||||
ret = "\n"
|
||||
if expr.has_key('type'):
|
||||
if expr.has_key('struct'):
|
||||
ret += generate_struct(expr) + "\n"
|
||||
ret += generate_type_cleanup_decl(expr['type'] + "List")
|
||||
fdef.write(generate_type_cleanup(expr['type'] + "List") + "\n")
|
||||
ret += generate_type_cleanup_decl(expr['type'])
|
||||
fdef.write(generate_type_cleanup(expr['type']) + "\n")
|
||||
ret += generate_type_cleanup_decl(expr['struct'] + "List")
|
||||
fdef.write(generate_type_cleanup(expr['struct'] + "List") + "\n")
|
||||
ret += generate_type_cleanup_decl(expr['struct'])
|
||||
fdef.write(generate_type_cleanup(expr['struct']) + "\n")
|
||||
elif expr.has_key('union'):
|
||||
ret += generate_union(expr)
|
||||
ret += generate_union(expr, 'union')
|
||||
ret += generate_type_cleanup_decl(expr['union'] + "List")
|
||||
fdef.write(generate_type_cleanup(expr['union'] + "List") + "\n")
|
||||
ret += generate_type_cleanup_decl(expr['union'])
|
||||
fdef.write(generate_type_cleanup(expr['union']) + "\n")
|
||||
elif expr.has_key('alternate'):
|
||||
ret += generate_union(expr, 'alternate')
|
||||
ret += generate_type_cleanup_decl(expr['alternate'] + "List")
|
||||
fdef.write(generate_type_cleanup(expr['alternate'] + "List") + "\n")
|
||||
ret += generate_type_cleanup_decl(expr['alternate'])
|
||||
fdef.write(generate_type_cleanup(expr['alternate']) + "\n")
|
||||
elif expr.has_key('enum'):
|
||||
ret += generate_type_cleanup_decl(expr['enum'] + "List")
|
||||
fdef.write(generate_type_cleanup(expr['enum'] + "List") + "\n")
|
||||
|
@ -43,79 +43,45 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error *
|
||||
''',
|
||||
c_type=type_name(type))
|
||||
|
||||
def generate_visit_struct_fields(name, field_prefix, fn_prefix, members, base = None):
|
||||
def generate_visit_struct_fields(name, members, base = None):
|
||||
substructs = []
|
||||
ret = ''
|
||||
if not fn_prefix:
|
||||
full_name = name
|
||||
else:
|
||||
full_name = "%s_%s" % (name, fn_prefix)
|
||||
|
||||
for argname, argentry, optional, structured in parse_args(members):
|
||||
if structured:
|
||||
if not fn_prefix:
|
||||
nested_fn_prefix = argname
|
||||
else:
|
||||
nested_fn_prefix = "%s_%s" % (fn_prefix, argname)
|
||||
|
||||
nested_field_prefix = "%s%s." % (field_prefix, argname)
|
||||
ret += generate_visit_struct_fields(name, nested_field_prefix,
|
||||
nested_fn_prefix, argentry)
|
||||
ret += mcgen('''
|
||||
|
||||
static void visit_type_%(full_name)s_field_%(c_name)s(Visitor *m, %(name)s **obj, Error **errp)
|
||||
{
|
||||
''',
|
||||
name=name, full_name=full_name, c_name=c_var(argname))
|
||||
ret += generate_visit_struct_body(full_name, argname, argentry)
|
||||
ret += mcgen('''
|
||||
}
|
||||
''')
|
||||
|
||||
if base:
|
||||
ret += generate_visit_implicit_struct(base)
|
||||
|
||||
ret += mcgen('''
|
||||
|
||||
static void visit_type_%(full_name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
|
||||
static void visit_type_%(name)s_fields(Visitor *m, %(name)s **obj, Error **errp)
|
||||
{
|
||||
Error *err = NULL;
|
||||
''',
|
||||
name=name, full_name=full_name)
|
||||
name=name)
|
||||
push_indent()
|
||||
|
||||
if base:
|
||||
ret += mcgen('''
|
||||
visit_type_implicit_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, &err);
|
||||
visit_type_implicit_%(type)s(m, &(*obj)->%(c_name)s, &err);
|
||||
if (err) {
|
||||
goto out;
|
||||
}
|
||||
''',
|
||||
c_prefix=c_var(field_prefix),
|
||||
type=type_name(base), c_name=c_var('base'))
|
||||
|
||||
for argname, argentry, optional, structured in parse_args(members):
|
||||
for argname, argentry, optional in parse_args(members):
|
||||
if optional:
|
||||
ret += mcgen('''
|
||||
visit_optional(m, &(*obj)->%(c_prefix)shas_%(c_name)s, "%(name)s", &err);
|
||||
if (!err && (*obj)->%(prefix)shas_%(c_name)s) {
|
||||
visit_optional(m, &(*obj)->has_%(c_name)s, "%(name)s", &err);
|
||||
if (!err && (*obj)->has_%(c_name)s) {
|
||||
''',
|
||||
c_prefix=c_var(field_prefix), prefix=field_prefix,
|
||||
c_name=c_var(argname), name=argname)
|
||||
push_indent()
|
||||
|
||||
if structured:
|
||||
ret += mcgen('''
|
||||
visit_type_%(full_name)s_field_%(c_name)s(m, obj, &err);
|
||||
ret += mcgen('''
|
||||
visit_type_%(type)s(m, &(*obj)->%(c_name)s, "%(name)s", &err);
|
||||
''',
|
||||
full_name=full_name, c_name=c_var(argname))
|
||||
else:
|
||||
ret += mcgen('''
|
||||
visit_type_%(type)s(m, &(*obj)->%(c_prefix)s%(c_name)s, "%(name)s", &err);
|
||||
''',
|
||||
c_prefix=c_var(field_prefix), prefix=field_prefix,
|
||||
type=type_name(argentry), c_name=c_var(argname),
|
||||
name=argname)
|
||||
type=type_name(argentry), c_name=c_var(argname),
|
||||
name=argname)
|
||||
|
||||
if optional:
|
||||
pop_indent()
|
||||
@ -141,29 +107,11 @@ out:
|
||||
return ret
|
||||
|
||||
|
||||
def generate_visit_struct_body(field_prefix, name, members):
|
||||
def generate_visit_struct_body(name, members):
|
||||
ret = mcgen('''
|
||||
Error *err = NULL;
|
||||
|
||||
''')
|
||||
|
||||
if not field_prefix:
|
||||
full_name = name
|
||||
else:
|
||||
full_name = "%s_%s" % (field_prefix, name)
|
||||
|
||||
if len(field_prefix):
|
||||
ret += mcgen('''
|
||||
visit_start_struct(m, NULL, "", "%(name)s", 0, &err);
|
||||
''',
|
||||
name=name)
|
||||
else:
|
||||
ret += mcgen('''
|
||||
visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), &err);
|
||||
''',
|
||||
name=name)
|
||||
|
||||
ret += mcgen('''
|
||||
if (!err) {
|
||||
if (*obj) {
|
||||
visit_type_%(name)s_fields(m, obj, errp);
|
||||
@ -172,17 +120,17 @@ def generate_visit_struct_body(field_prefix, name, members):
|
||||
}
|
||||
error_propagate(errp, err);
|
||||
''',
|
||||
name=full_name)
|
||||
name=name)
|
||||
|
||||
return ret
|
||||
|
||||
def generate_visit_struct(expr):
|
||||
|
||||
name = expr['type']
|
||||
name = expr['struct']
|
||||
members = expr['data']
|
||||
base = expr.get('base')
|
||||
|
||||
ret = generate_visit_struct_fields(name, "", "", members, base)
|
||||
ret = generate_visit_struct_fields(name, members, base)
|
||||
|
||||
ret += mcgen('''
|
||||
|
||||
@ -191,7 +139,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
|
||||
''',
|
||||
name=name)
|
||||
|
||||
ret += generate_visit_struct_body("", name, members)
|
||||
ret += generate_visit_struct_body(name, members)
|
||||
|
||||
ret += mcgen('''
|
||||
}
|
||||
@ -237,7 +185,7 @@ void visit_type_%(name)s(Visitor *m, %(name)s *obj, const char *name, Error **er
|
||||
''',
|
||||
name=name)
|
||||
|
||||
def generate_visit_anon_union(name, members):
|
||||
def generate_visit_alternate(name, members):
|
||||
ret = mcgen('''
|
||||
|
||||
void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **errp)
|
||||
@ -256,15 +204,15 @@ void visit_type_%(name)s(Visitor *m, %(name)s **obj, const char *name, Error **e
|
||||
''',
|
||||
name=name)
|
||||
|
||||
# For anon union, always use the default enum type automatically generated
|
||||
# For alternate, always use the default enum type automatically generated
|
||||
# as "'%sKind' % (name)"
|
||||
disc_type = '%sKind' % (name)
|
||||
|
||||
for key in members:
|
||||
assert (members[key] in builtin_types
|
||||
assert (members[key] in builtin_types.keys()
|
||||
or find_struct(members[key])
|
||||
or find_union(members[key])
|
||||
or find_enum(members[key])), "Invalid anonymous union member"
|
||||
or find_enum(members[key])), "Invalid alternate member"
|
||||
|
||||
enum_full_value = generate_enum_full_value(disc_type, key)
|
||||
ret += mcgen('''
|
||||
@ -300,27 +248,22 @@ def generate_visit_union(expr):
|
||||
base = expr.get('base')
|
||||
discriminator = expr.get('discriminator')
|
||||
|
||||
if discriminator == {}:
|
||||
assert not base
|
||||
return generate_visit_anon_union(name, members)
|
||||
|
||||
enum_define = discriminator_find_enum_define(expr)
|
||||
if enum_define:
|
||||
# Use the enum type as discriminator
|
||||
ret = ""
|
||||
disc_type = enum_define['enum_name']
|
||||
else:
|
||||
# There will always be a discriminator in the C switch code, by default it
|
||||
# is an enum type generated silently as "'%sKind' % (name)"
|
||||
# There will always be a discriminator in the C switch code, by default
|
||||
# it is an enum type generated silently as "'%sKind' % (name)"
|
||||
ret = generate_visit_enum('%sKind' % name, members.keys())
|
||||
disc_type = '%sKind' % (name)
|
||||
|
||||
if base:
|
||||
base_fields = find_struct(base)['data']
|
||||
if discriminator:
|
||||
base_fields = base_fields.copy()
|
||||
del base_fields[discriminator]
|
||||
ret += generate_visit_struct_fields(name, "", "", base_fields)
|
||||
assert discriminator
|
||||
base_fields = find_struct(base)['data'].copy()
|
||||
del base_fields[discriminator]
|
||||
ret += generate_visit_struct_fields(name, base_fields)
|
||||
|
||||
if discriminator:
|
||||
for key in members:
|
||||
@ -538,7 +481,7 @@ exprs = parse_schema(input_file)
|
||||
# to avoid header dependency hell, we always generate declarations
|
||||
# for built-in types in our header files and simply guard them
|
||||
fdecl.write(guardstart("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
|
||||
for typename in builtin_types:
|
||||
for typename in builtin_types.keys():
|
||||
fdecl.write(generate_declaration(typename, None, builtin_type=True))
|
||||
fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
|
||||
|
||||
@ -546,16 +489,16 @@ fdecl.write(guardend("QAPI_VISIT_BUILTIN_VISITOR_DECL"))
|
||||
# have the functions defined, so we use -b option to provide control
|
||||
# over these cases
|
||||
if do_builtins:
|
||||
for typename in builtin_types:
|
||||
for typename in builtin_types.keys():
|
||||
fdef.write(generate_visit_list(typename, None))
|
||||
|
||||
for expr in exprs:
|
||||
if expr.has_key('type'):
|
||||
if expr.has_key('struct'):
|
||||
ret = generate_visit_struct(expr)
|
||||
ret += generate_visit_list(expr['type'], expr['data'])
|
||||
ret += generate_visit_list(expr['struct'], expr['data'])
|
||||
fdef.write(ret)
|
||||
|
||||
ret = generate_declaration(expr['type'], expr['data'])
|
||||
ret = generate_declaration(expr['struct'], expr['data'])
|
||||
fdecl.write(ret)
|
||||
elif expr.has_key('union'):
|
||||
ret = generate_visit_union(expr)
|
||||
@ -569,6 +512,15 @@ for expr in exprs:
|
||||
expr['data'].keys())
|
||||
ret += generate_declaration(expr['union'], expr['data'])
|
||||
fdecl.write(ret)
|
||||
elif expr.has_key('alternate'):
|
||||
ret = generate_visit_alternate(expr['alternate'], expr['data'])
|
||||
ret += generate_visit_list(expr['alternate'], expr['data'])
|
||||
fdef.write(ret)
|
||||
|
||||
ret = generate_decl_enum('%sKind' % expr['alternate'],
|
||||
expr['data'].keys())
|
||||
ret += generate_declaration(expr['alternate'], expr['data'])
|
||||
fdecl.write(ret)
|
||||
elif expr.has_key('enum'):
|
||||
ret = generate_visit_list(expr['enum'], expr['data'])
|
||||
ret += generate_visit_enum(expr['enum'], expr['data'])
|
||||
|
528
scripts/qapi.py
528
scripts/qapi.py
@ -2,7 +2,7 @@
|
||||
# QAPI helper library
|
||||
#
|
||||
# Copyright IBM, Corp. 2011
|
||||
# Copyright (c) 2013 Red Hat Inc.
|
||||
# Copyright (c) 2013-2015 Red Hat Inc.
|
||||
#
|
||||
# Authors:
|
||||
# Anthony Liguori <aliguori@us.ibm.com>
|
||||
@ -16,13 +16,7 @@ from ordereddict import OrderedDict
|
||||
import os
|
||||
import sys
|
||||
|
||||
builtin_types = [
|
||||
'str', 'int', 'number', 'bool',
|
||||
'int8', 'int16', 'int32', 'int64',
|
||||
'uint8', 'uint16', 'uint32', 'uint64'
|
||||
]
|
||||
|
||||
builtin_type_qtypes = {
|
||||
builtin_types = {
|
||||
'str': 'QTYPE_QSTRING',
|
||||
'int': 'QTYPE_QINT',
|
||||
'number': 'QTYPE_QFLOAT',
|
||||
@ -35,8 +29,39 @@ builtin_type_qtypes = {
|
||||
'uint16': 'QTYPE_QINT',
|
||||
'uint32': 'QTYPE_QINT',
|
||||
'uint64': 'QTYPE_QINT',
|
||||
'size': 'QTYPE_QINT',
|
||||
}
|
||||
|
||||
# Whitelist of commands allowed to return a non-dictionary
|
||||
returns_whitelist = [
|
||||
# From QMP:
|
||||
'human-monitor-command',
|
||||
'query-migrate-cache-size',
|
||||
'query-tpm-models',
|
||||
'query-tpm-types',
|
||||
'ringbuf-read',
|
||||
|
||||
# From QGA:
|
||||
'guest-file-open',
|
||||
'guest-fsfreeze-freeze',
|
||||
'guest-fsfreeze-freeze-list',
|
||||
'guest-fsfreeze-status',
|
||||
'guest-fsfreeze-thaw',
|
||||
'guest-get-time',
|
||||
'guest-set-vcpus',
|
||||
'guest-sync',
|
||||
'guest-sync-delimited',
|
||||
|
||||
# From qapi-schema-test:
|
||||
'user_def_cmd3',
|
||||
]
|
||||
|
||||
enum_types = []
|
||||
struct_types = []
|
||||
union_types = []
|
||||
events = []
|
||||
all_names = {}
|
||||
|
||||
def error_path(parent):
|
||||
res = ""
|
||||
while parent:
|
||||
@ -148,7 +173,41 @@ class QAPISchema:
|
||||
raise QAPISchemaError(self,
|
||||
'Missing terminating "\'"')
|
||||
if esc:
|
||||
string += ch
|
||||
if ch == 'b':
|
||||
string += '\b'
|
||||
elif ch == 'f':
|
||||
string += '\f'
|
||||
elif ch == 'n':
|
||||
string += '\n'
|
||||
elif ch == 'r':
|
||||
string += '\r'
|
||||
elif ch == 't':
|
||||
string += '\t'
|
||||
elif ch == 'u':
|
||||
value = 0
|
||||
for x in range(0, 4):
|
||||
ch = self.src[self.cursor]
|
||||
self.cursor += 1
|
||||
if ch not in "0123456789abcdefABCDEF":
|
||||
raise QAPISchemaError(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')
|
||||
string += chr(value)
|
||||
elif ch in "\\/'\"":
|
||||
string += ch
|
||||
else:
|
||||
raise QAPISchemaError(self,
|
||||
"Unknown escape \\%s" %ch)
|
||||
esc = False
|
||||
elif ch == "\\":
|
||||
esc = True
|
||||
@ -157,6 +216,20 @@ class QAPISchema:
|
||||
return
|
||||
else:
|
||||
string += ch
|
||||
elif self.tok in "tfn":
|
||||
val = self.src[self.cursor - 1:]
|
||||
if val.startswith("true"):
|
||||
self.val = True
|
||||
self.cursor += 3
|
||||
return
|
||||
elif val.startswith("false"):
|
||||
self.val = False
|
||||
self.cursor += 4
|
||||
return
|
||||
elif val.startswith("null"):
|
||||
self.val = None
|
||||
self.cursor += 3
|
||||
return
|
||||
elif self.tok == '\n':
|
||||
if self.cursor == len(self.src):
|
||||
self.tok = None
|
||||
@ -196,8 +269,9 @@ class QAPISchema:
|
||||
if self.tok == ']':
|
||||
self.accept()
|
||||
return expr
|
||||
if not self.tok in [ '{', '[', "'" ]:
|
||||
raise QAPISchemaError(self, 'Expected "{", "[", "]" or string')
|
||||
if not self.tok in "{['tfn":
|
||||
raise QAPISchemaError(self, 'Expected "{", "[", "]", string, '
|
||||
'boolean or "null"')
|
||||
while True:
|
||||
expr.append(self.get_expr(True))
|
||||
if self.tok == ']':
|
||||
@ -216,7 +290,7 @@ class QAPISchema:
|
||||
elif self.tok == '[':
|
||||
self.accept()
|
||||
expr = self.get_values()
|
||||
elif self.tok == "'":
|
||||
elif self.tok in "'tfn":
|
||||
expr = self.val
|
||||
self.accept()
|
||||
else:
|
||||
@ -229,6 +303,18 @@ def find_base_fields(base):
|
||||
return None
|
||||
return base_struct_define['data']
|
||||
|
||||
# Return the qtype of an alternate branch, or None on error.
|
||||
def find_alternate_member_qtype(qapi_type):
|
||||
if builtin_types.has_key(qapi_type):
|
||||
return builtin_types[qapi_type]
|
||||
elif find_struct(qapi_type):
|
||||
return "QTYPE_QDICT"
|
||||
elif find_enum(qapi_type):
|
||||
return "QTYPE_QSTRING"
|
||||
elif find_union(qapi_type):
|
||||
return "QTYPE_QDICT"
|
||||
return None
|
||||
|
||||
# Return the discriminator enum define if discriminator is specified as an
|
||||
# enum type, otherwise return None.
|
||||
def discriminator_find_enum_define(expr):
|
||||
@ -248,56 +334,178 @@ def discriminator_find_enum_define(expr):
|
||||
|
||||
return find_enum(discriminator_type)
|
||||
|
||||
valid_name = re.compile('^[a-zA-Z_][a-zA-Z0-9_.-]*$')
|
||||
def check_name(expr_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)
|
||||
if name.startswith('*'):
|
||||
membername = name[1:]
|
||||
if not allow_optional:
|
||||
raise QAPIExprError(expr_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:
|
||||
membername = '_' + membername
|
||||
if not valid_name.match(membername):
|
||||
raise QAPIExprError(expr_info,
|
||||
"%s uses invalid name '%s'" % (source, name))
|
||||
|
||||
def check_type(expr_info, source, value, allow_array = False,
|
||||
allow_dict = False, allow_optional = False,
|
||||
allow_star = False, allow_metas = []):
|
||||
global all_names
|
||||
orig_value = value
|
||||
|
||||
if value is None:
|
||||
return
|
||||
|
||||
if allow_star and value == '**':
|
||||
return
|
||||
|
||||
# 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)
|
||||
if len(value) != 1 or not isinstance(value[0], str):
|
||||
raise QAPIExprError(expr_info,
|
||||
"%s: array type must contain single type name"
|
||||
% source)
|
||||
value = value[0]
|
||||
orig_value = "array of %s" %value
|
||||
|
||||
# Check if type name for value is okay
|
||||
if isinstance(value, str):
|
||||
if value == '**':
|
||||
raise QAPIExprError(expr_info,
|
||||
"%s uses '**' but did not request 'gen':false"
|
||||
% source)
|
||||
if not value in all_names:
|
||||
raise QAPIExprError(expr_info,
|
||||
"%s uses unknown type '%s'"
|
||||
% (source, orig_value))
|
||||
if not all_names[value] in allow_metas:
|
||||
raise QAPIExprError(expr_info,
|
||||
"%s cannot use %s type '%s'"
|
||||
% (source, all_names[value], orig_value))
|
||||
return
|
||||
|
||||
# value is a dictionary, check that each member is okay
|
||||
if not isinstance(value, OrderedDict):
|
||||
raise QAPIExprError(expr_info,
|
||||
"%s should be a dictionary" % source)
|
||||
if not allow_dict:
|
||||
raise QAPIExprError(expr_info,
|
||||
"%s should be a type name" % source)
|
||||
for (key, arg) in value.items():
|
||||
check_name(expr_info, "Member of %s" % source, key,
|
||||
allow_optional=allow_optional)
|
||||
# Todo: allow dictionaries to represent default values of
|
||||
# an optional argument.
|
||||
check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
|
||||
allow_array=True, allow_star=allow_star,
|
||||
allow_metas=['built-in', 'union', 'alternate', 'struct',
|
||||
'enum'])
|
||||
|
||||
def check_member_clash(expr_info, base_name, data, source = ""):
|
||||
base = find_struct(base_name)
|
||||
assert base
|
||||
base_members = base['data']
|
||||
for key in data.keys():
|
||||
if key.startswith('*'):
|
||||
key = key[1:]
|
||||
if key in base_members or "*" + key in base_members:
|
||||
raise QAPIExprError(expr_info,
|
||||
"Member name '%s'%s clashes with base '%s'"
|
||||
% (key, source, base_name))
|
||||
if base.get('base'):
|
||||
check_member_clash(expr_info, base['base'], data, source)
|
||||
|
||||
def check_command(expr, expr_info):
|
||||
name = expr['command']
|
||||
allow_star = expr.has_key('gen')
|
||||
|
||||
check_type(expr_info, "'data' for command '%s'" % name,
|
||||
expr.get('data'), allow_dict=True, allow_optional=True,
|
||||
allow_metas=['union', 'struct'], allow_star=allow_star)
|
||||
returns_meta = ['union', 'struct']
|
||||
if name in returns_whitelist:
|
||||
returns_meta += ['built-in', 'alternate', 'enum']
|
||||
check_type(expr_info, "'returns' for command '%s'" % name,
|
||||
expr.get('returns'), allow_array=True, allow_dict=True,
|
||||
allow_optional=True, allow_metas=returns_meta,
|
||||
allow_star=allow_star)
|
||||
|
||||
def check_event(expr, expr_info):
|
||||
global events
|
||||
name = expr['event']
|
||||
params = expr.get('data')
|
||||
if params:
|
||||
for argname, argentry, optional, structured in parse_args(params):
|
||||
if structured:
|
||||
raise QAPIExprError(expr_info,
|
||||
"Nested structure define in event is not "
|
||||
"supported, event '%s', argname '%s'"
|
||||
% (expr['event'], argname))
|
||||
|
||||
if name.upper() == 'MAX':
|
||||
raise QAPIExprError(expr_info, "Event name 'MAX' cannot be created")
|
||||
events.append(name)
|
||||
check_type(expr_info, "'data' for event '%s'" % name,
|
||||
expr.get('data'), allow_dict=True, allow_optional=True,
|
||||
allow_metas=['union', 'struct'])
|
||||
|
||||
def check_union(expr, expr_info):
|
||||
name = expr['union']
|
||||
base = expr.get('base')
|
||||
discriminator = expr.get('discriminator')
|
||||
members = expr['data']
|
||||
values = { 'MAX': '(automatic)' }
|
||||
|
||||
# If the object has a member 'base', its value must name a complex type.
|
||||
if base:
|
||||
base_fields = find_base_fields(base)
|
||||
if not base_fields:
|
||||
# If the object has a member 'base', its value must name a struct,
|
||||
# and there must be a discriminator.
|
||||
if base is not None:
|
||||
if discriminator is None:
|
||||
raise QAPIExprError(expr_info,
|
||||
"Base '%s' is not a valid type"
|
||||
% base)
|
||||
"Union '%s' requires a discriminator to go "
|
||||
"along with base" %name)
|
||||
|
||||
# If the union object has no member 'discriminator', it's an
|
||||
# ordinary union.
|
||||
if not discriminator:
|
||||
enum_define = None
|
||||
# Two types of unions, determined by discriminator.
|
||||
|
||||
# Else if the value of member 'discriminator' is {}, it's an
|
||||
# anonymous union.
|
||||
elif discriminator == {}:
|
||||
# With no discriminator it is a simple union.
|
||||
if discriminator is None:
|
||||
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)
|
||||
|
||||
# Else, it's a flat union.
|
||||
else:
|
||||
# The object must have a member 'base'.
|
||||
if not base:
|
||||
# The object must have a string member 'base'.
|
||||
if not isinstance(base, str):
|
||||
raise QAPIExprError(expr_info,
|
||||
"Flat union '%s' must have a base field"
|
||||
"Flat union '%s' must have a string base field"
|
||||
% name)
|
||||
# The value of member 'discriminator' must name a member of the
|
||||
# base type.
|
||||
base_fields = find_base_fields(base)
|
||||
if not base_fields:
|
||||
raise QAPIExprError(expr_info,
|
||||
"Base '%s' is not a valid struct"
|
||||
% base)
|
||||
|
||||
# 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,
|
||||
discriminator)
|
||||
discriminator_type = base_fields.get(discriminator)
|
||||
if not discriminator_type:
|
||||
raise QAPIExprError(expr_info,
|
||||
"Discriminator '%s' is not a member of base "
|
||||
"type '%s'"
|
||||
"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,
|
||||
@ -306,51 +514,196 @@ def check_union(expr, expr_info):
|
||||
|
||||
# Check every branch
|
||||
for (key, value) in members.items():
|
||||
# If this named member's value names an enum type, then all members
|
||||
check_name(expr_info, "Member of union '%s'" % name, key)
|
||||
|
||||
# Each value must name a known type; furthermore, in flat unions,
|
||||
# branches must be a struct with no overlapping member names
|
||||
check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
|
||||
value, allow_array=True, allow_metas=allow_metas)
|
||||
if base:
|
||||
branch_struct = find_struct(value)
|
||||
assert branch_struct
|
||||
check_member_clash(expr_info, base, branch_struct['data'],
|
||||
" of branch '%s'" % key)
|
||||
|
||||
# If the discriminator names an enum type, then all members
|
||||
# of 'data' must also be members of the enum type.
|
||||
if enum_define and not key in enum_define['enum_values']:
|
||||
if enum_define:
|
||||
if not key in enum_define['enum_values']:
|
||||
raise QAPIExprError(expr_info,
|
||||
"Discriminator value '%s' is not found in "
|
||||
"enum '%s'" %
|
||||
(key, enum_define["enum_name"]))
|
||||
|
||||
# Otherwise, check for conflicts in the generated enum
|
||||
else:
|
||||
c_key = _generate_enum_string(key)
|
||||
if c_key in values:
|
||||
raise QAPIExprError(expr_info,
|
||||
"Union '%s' member '%s' clashes with '%s'"
|
||||
% (name, key, values[c_key]))
|
||||
values[c_key] = key
|
||||
|
||||
def check_alternate(expr, expr_info):
|
||||
name = expr['alternate']
|
||||
members = expr['data']
|
||||
values = { 'MAX': '(automatic)' }
|
||||
types_seen = {}
|
||||
|
||||
# Check every branch
|
||||
for (key, value) in members.items():
|
||||
check_name(expr_info, "Member of alternate '%s'" % name, key)
|
||||
|
||||
# Check for conflicts in the generated enum
|
||||
c_key = _generate_enum_string(key)
|
||||
if c_key in values:
|
||||
raise QAPIExprError(expr_info,
|
||||
"Discriminator value '%s' is not found in "
|
||||
"enum '%s'" %
|
||||
(key, enum_define["enum_name"]))
|
||||
# Todo: add checking for values. Key is checked as above, value can be
|
||||
# also checked here, but we need more functions to handle array case.
|
||||
"Alternate '%s' member '%s' clashes with '%s'"
|
||||
% (name, key, values[c_key]))
|
||||
values[c_key] = key
|
||||
|
||||
# Ensure alternates have no type conflicts.
|
||||
check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
|
||||
value,
|
||||
allow_metas=['built-in', 'union', 'struct', 'enum'])
|
||||
qtype = find_alternate_member_qtype(value)
|
||||
assert qtype
|
||||
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]))
|
||||
types_seen[qtype] = key
|
||||
|
||||
def check_enum(expr, expr_info):
|
||||
name = expr['enum']
|
||||
members = expr.get('data')
|
||||
values = { 'MAX': '(automatic)' }
|
||||
|
||||
if not isinstance(members, list):
|
||||
raise QAPIExprError(expr_info,
|
||||
"Enum '%s' requires an array for 'data'" % name)
|
||||
for member in members:
|
||||
check_name(expr_info, "Member of enum '%s'" %name, member,
|
||||
enum_member=True)
|
||||
key = _generate_enum_string(member)
|
||||
if key in values:
|
||||
raise QAPIExprError(expr_info,
|
||||
"Enum '%s' member '%s' clashes with '%s'"
|
||||
% (name, member, values[key]))
|
||||
values[key] = member
|
||||
|
||||
def check_struct(expr, expr_info):
|
||||
name = expr['struct']
|
||||
members = expr['data']
|
||||
|
||||
check_type(expr_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'),
|
||||
allow_metas=['struct'])
|
||||
if expr.get('base'):
|
||||
check_member_clash(expr_info, expr['base'], expr['data'])
|
||||
|
||||
def check_exprs(schema):
|
||||
for expr_elem in schema.exprs:
|
||||
expr = expr_elem['expr']
|
||||
if expr.has_key('union'):
|
||||
check_union(expr, expr_elem['info'])
|
||||
if expr.has_key('event'):
|
||||
check_event(expr, expr_elem['info'])
|
||||
info = expr_elem['info']
|
||||
|
||||
if expr.has_key('enum'):
|
||||
check_enum(expr, info)
|
||||
elif expr.has_key('union'):
|
||||
check_union(expr, info)
|
||||
elif expr.has_key('alternate'):
|
||||
check_alternate(expr, info)
|
||||
elif expr.has_key('struct'):
|
||||
check_struct(expr, info)
|
||||
elif expr.has_key('command'):
|
||||
check_command(expr, info)
|
||||
elif expr.has_key('event'):
|
||||
check_event(expr, info)
|
||||
else:
|
||||
assert False, 'unexpected meta type'
|
||||
|
||||
def check_keys(expr_elem, meta, required, optional=[]):
|
||||
expr = expr_elem['expr']
|
||||
info = expr_elem['info']
|
||||
name = expr[meta]
|
||||
if not isinstance(name, str):
|
||||
raise QAPIExprError(info,
|
||||
"'%s' key must have a string value" % meta)
|
||||
required = required + [ meta ]
|
||||
for (key, value) in expr.items():
|
||||
if not key in required and not key in optional:
|
||||
raise QAPIExprError(info,
|
||||
"Unknown key '%s' in %s '%s'"
|
||||
% (key, meta, name))
|
||||
if (key == 'gen' or key == 'success-response') and value != False:
|
||||
raise QAPIExprError(info,
|
||||
"'%s' of %s '%s' should only use false value"
|
||||
% (key, meta, name))
|
||||
for key in required:
|
||||
if not expr.has_key(key):
|
||||
raise QAPIExprError(info,
|
||||
"Key '%s' is missing from %s '%s'"
|
||||
% (key, meta, name))
|
||||
|
||||
|
||||
def parse_schema(input_file):
|
||||
global all_names
|
||||
exprs = []
|
||||
|
||||
# First pass: read entire file into memory
|
||||
try:
|
||||
schema = QAPISchema(open(input_file, "r"))
|
||||
except (QAPISchemaError, QAPIExprError), e:
|
||||
print >>sys.stderr, e
|
||||
exit(1)
|
||||
|
||||
exprs = []
|
||||
|
||||
for expr_elem in schema.exprs:
|
||||
expr = expr_elem['expr']
|
||||
if expr.has_key('enum'):
|
||||
add_enum(expr['enum'], expr['data'])
|
||||
elif expr.has_key('union'):
|
||||
add_union(expr)
|
||||
elif expr.has_key('type'):
|
||||
add_struct(expr)
|
||||
exprs.append(expr)
|
||||
|
||||
# Try again for hidden UnionKind enum
|
||||
for expr_elem in schema.exprs:
|
||||
expr = expr_elem['expr']
|
||||
if expr.has_key('union'):
|
||||
if not discriminator_find_enum_define(expr):
|
||||
add_enum('%sKind' % expr['union'])
|
||||
|
||||
try:
|
||||
# Next pass: learn the types and check for valid expression keys. At
|
||||
# this point, top-level 'include' has already been flattened.
|
||||
for builtin in builtin_types.keys():
|
||||
all_names[builtin] = 'built-in'
|
||||
for expr_elem in schema.exprs:
|
||||
expr = expr_elem['expr']
|
||||
info = expr_elem['info']
|
||||
if expr.has_key('enum'):
|
||||
check_keys(expr_elem, 'enum', ['data'])
|
||||
add_enum(expr['enum'], info, expr['data'])
|
||||
elif expr.has_key('union'):
|
||||
check_keys(expr_elem, 'union', ['data'],
|
||||
['base', 'discriminator'])
|
||||
add_union(expr, info)
|
||||
elif expr.has_key('alternate'):
|
||||
check_keys(expr_elem, 'alternate', ['data'])
|
||||
add_name(expr['alternate'], info, 'alternate')
|
||||
elif expr.has_key('struct'):
|
||||
check_keys(expr_elem, 'struct', ['data'], ['base'])
|
||||
add_struct(expr, info)
|
||||
elif expr.has_key('command'):
|
||||
check_keys(expr_elem, 'command', [],
|
||||
['data', 'returns', 'gen', 'success-response'])
|
||||
add_name(expr['command'], info, 'command')
|
||||
elif expr.has_key('event'):
|
||||
check_keys(expr_elem, 'event', [], ['data'])
|
||||
add_name(expr['event'], info, 'event')
|
||||
else:
|
||||
raise QAPIExprError(expr_elem['info'],
|
||||
"Expression is missing metatype")
|
||||
exprs.append(expr)
|
||||
|
||||
# Try again for hidden UnionKind enum
|
||||
for expr_elem in schema.exprs:
|
||||
expr = expr_elem['expr']
|
||||
if expr.has_key('union'):
|
||||
if not discriminator_find_enum_define(expr):
|
||||
add_enum('%sKind' % expr['union'], expr_elem['info'],
|
||||
implicit=True)
|
||||
elif expr.has_key('alternate'):
|
||||
add_enum('%sKind' % expr['alternate'], expr_elem['info'],
|
||||
implicit=True)
|
||||
|
||||
# Final pass - validate that exprs make sense
|
||||
check_exprs(schema)
|
||||
except QAPIExprError, e:
|
||||
print >>sys.stderr, e
|
||||
@ -359,7 +712,7 @@ def parse_schema(input_file):
|
||||
return exprs
|
||||
|
||||
def parse_args(typeinfo):
|
||||
if isinstance(typeinfo, basestring):
|
||||
if isinstance(typeinfo, str):
|
||||
struct = find_struct(typeinfo)
|
||||
assert struct != None
|
||||
typeinfo = struct['data']
|
||||
@ -368,13 +721,12 @@ def parse_args(typeinfo):
|
||||
argname = member
|
||||
argentry = typeinfo[member]
|
||||
optional = False
|
||||
structured = False
|
||||
if member.startswith('*'):
|
||||
argname = member[1:]
|
||||
optional = True
|
||||
if isinstance(argentry, OrderedDict):
|
||||
structured = True
|
||||
yield (argname, argentry, optional, structured)
|
||||
# Todo: allow argentry to be OrderedDict, for providing the
|
||||
# value of an optional argument.
|
||||
yield (argname, argentry, optional)
|
||||
|
||||
def de_camel_case(name):
|
||||
new_name = ''
|
||||
@ -442,23 +794,36 @@ def type_name(name):
|
||||
return c_list_type(name[0])
|
||||
return name
|
||||
|
||||
enum_types = []
|
||||
struct_types = []
|
||||
union_types = []
|
||||
def add_name(name, info, meta, implicit = False):
|
||||
global all_names
|
||||
check_name(info, "'%s'" % meta, name)
|
||||
if name in all_names:
|
||||
raise QAPIExprError(info,
|
||||
"%s '%s' is already defined"
|
||||
% (all_names[name], name))
|
||||
if not implicit and name[-4:] == 'Kind':
|
||||
raise QAPIExprError(info,
|
||||
"%s '%s' should not end in 'Kind'"
|
||||
% (meta, name))
|
||||
all_names[name] = meta
|
||||
|
||||
def add_struct(definition):
|
||||
def add_struct(definition, info):
|
||||
global struct_types
|
||||
name = definition['struct']
|
||||
add_name(name, info, 'struct')
|
||||
struct_types.append(definition)
|
||||
|
||||
def find_struct(name):
|
||||
global struct_types
|
||||
for struct in struct_types:
|
||||
if struct['type'] == name:
|
||||
if struct['struct'] == name:
|
||||
return struct
|
||||
return None
|
||||
|
||||
def add_union(definition):
|
||||
def add_union(definition, info):
|
||||
global union_types
|
||||
name = definition['union']
|
||||
add_name(name, info, 'union')
|
||||
union_types.append(definition)
|
||||
|
||||
def find_union(name):
|
||||
@ -468,8 +833,9 @@ def find_union(name):
|
||||
return union
|
||||
return None
|
||||
|
||||
def add_enum(name, enum_values = None):
|
||||
def add_enum(name, info, enum_values = None, implicit = False):
|
||||
global enum_types
|
||||
add_name(name, info, 'enum', implicit)
|
||||
enum_types.append({"enum_name": name, "enum_values": enum_values})
|
||||
|
||||
def find_enum(name):
|
||||
@ -511,7 +877,7 @@ def c_type(name, is_param=False):
|
||||
return name
|
||||
elif name == None or len(name) == 0:
|
||||
return 'void'
|
||||
elif name == name.upper():
|
||||
elif name in events:
|
||||
return '%sEvent *%s' % (camel_case(name), eatspace)
|
||||
else:
|
||||
return '%s *%s' % (name, eatspace)
|
||||
|
@ -207,20 +207,44 @@ $(foreach target,$(SYSEMU_TARGET_LIST), \
|
||||
$(eval check-qtest-$(target)-y += tests/qom-test$(EXESUF))))
|
||||
|
||||
check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
|
||||
comments.json empty.json funny-char.json indented-expr.json \
|
||||
missing-colon.json missing-comma-list.json \
|
||||
missing-comma-object.json non-objects.json \
|
||||
comments.json empty.json enum-empty.json enum-missing-data.json \
|
||||
enum-wrong-data.json enum-int-member.json enum-dict-member.json \
|
||||
enum-clash-member.json enum-max-member.json enum-union-clash.json \
|
||||
enum-bad-name.json funny-char.json indented-expr.json \
|
||||
missing-type.json bad-ident.json ident-with-escape.json \
|
||||
escape-outside-string.json unknown-escape.json \
|
||||
escape-too-short.json escape-too-big.json unicode-str.json \
|
||||
double-type.json bad-base.json bad-type-bool.json bad-type-int.json \
|
||||
bad-type-dict.json double-data.json unknown-expr-key.json \
|
||||
redefined-type.json redefined-command.json redefined-builtin.json \
|
||||
redefined-event.json command-int.json bad-data.json event-max.json \
|
||||
type-bypass.json type-bypass-no-gen.json type-bypass-bad-gen.json \
|
||||
data-array-empty.json data-array-unknown.json data-int.json \
|
||||
data-unknown.json data-member-unknown.json data-member-array.json \
|
||||
data-member-array-bad.json returns-array-bad.json returns-int.json \
|
||||
returns-unknown.json returns-alternate.json returns-whitelist.json \
|
||||
missing-colon.json missing-comma-list.json missing-comma-object.json \
|
||||
nested-struct-data.json nested-struct-returns.json non-objects.json \
|
||||
qapi-schema-test.json quoted-structural-chars.json \
|
||||
trailing-comma-list.json trailing-comma-object.json \
|
||||
unclosed-list.json unclosed-object.json unclosed-string.json \
|
||||
duplicate-key.json union-invalid-base.json flat-union-no-base.json \
|
||||
flat-union-invalid-discriminator.json \
|
||||
duplicate-key.json union-invalid-base.json union-bad-branch.json \
|
||||
union-optional-branch.json union-unknown.json union-max.json \
|
||||
flat-union-optional-discriminator.json flat-union-no-base.json \
|
||||
flat-union-invalid-discriminator.json flat-union-inline.json \
|
||||
flat-union-invalid-branch-key.json flat-union-reverse-define.json \
|
||||
flat-union-string-discriminator.json \
|
||||
flat-union-string-discriminator.json union-base-no-discriminator.json \
|
||||
flat-union-bad-discriminator.json flat-union-bad-base.json \
|
||||
flat-union-base-star.json flat-union-int-branch.json \
|
||||
flat-union-base-union.json flat-union-branch-clash.json \
|
||||
alternate-nested.json alternate-unknown.json alternate-clash.json \
|
||||
alternate-good.json alternate-base.json alternate-array.json \
|
||||
alternate-conflict-string.json alternate-conflict-dict.json \
|
||||
include-simple.json include-relpath.json include-format-err.json \
|
||||
include-non-file.json include-no-file.json include-before-err.json \
|
||||
include-nested-err.json include-self-cycle.json include-cycle.json \
|
||||
include-repetition.json event-nest-struct.json)
|
||||
include-repetition.json event-nest-struct.json event-case.json \
|
||||
struct-base-clash.json struct-base-clash-deep.json )
|
||||
|
||||
GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h \
|
||||
tests/test-qmp-commands.h tests/test-qapi-event.h
|
||||
|
1
tests/qapi-schema/alternate-array.err
Normal file
1
tests/qapi-schema/alternate-array.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/alternate-array.json:5: Member 'two' of alternate 'Alt' cannot be an array
|
1
tests/qapi-schema/alternate-array.exit
Normal file
1
tests/qapi-schema/alternate-array.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
7
tests/qapi-schema/alternate-array.json
Normal file
7
tests/qapi-schema/alternate-array.json
Normal file
@ -0,0 +1,7 @@
|
||||
# we do not allow array branches in alternates
|
||||
# TODO: should we support this?
|
||||
{ 'struct': 'One',
|
||||
'data': { 'name': 'str' } }
|
||||
{ 'alternate': 'Alt',
|
||||
'data': { 'one': 'One',
|
||||
'two': [ 'int' ] } }
|
0
tests/qapi-schema/alternate-array.out
Normal file
0
tests/qapi-schema/alternate-array.out
Normal file
1
tests/qapi-schema/alternate-base.err
Normal file
1
tests/qapi-schema/alternate-base.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/alternate-base.json:4: Unknown key 'base' in alternate 'Alt'
|
1
tests/qapi-schema/alternate-base.exit
Normal file
1
tests/qapi-schema/alternate-base.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
6
tests/qapi-schema/alternate-base.json
Normal file
6
tests/qapi-schema/alternate-base.json
Normal file
@ -0,0 +1,6 @@
|
||||
# we reject alternate with base type
|
||||
{ 'struct': 'Base',
|
||||
'data': { 'string': 'str' } }
|
||||
{ 'alternate': 'Alt',
|
||||
'base': 'Base',
|
||||
'data': { 'number': 'int' } }
|
0
tests/qapi-schema/alternate-base.out
Normal file
0
tests/qapi-schema/alternate-base.out
Normal file
1
tests/qapi-schema/alternate-clash.err
Normal file
1
tests/qapi-schema/alternate-clash.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/alternate-clash.json:2: Alternate 'Alt1' member 'ONE' clashes with 'one'
|
1
tests/qapi-schema/alternate-clash.exit
Normal file
1
tests/qapi-schema/alternate-clash.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/alternate-clash.json
Normal file
3
tests/qapi-schema/alternate-clash.json
Normal file
@ -0,0 +1,3 @@
|
||||
# we detect C enum collisions in an alternate
|
||||
{ 'alternate': 'Alt1',
|
||||
'data': { 'one': 'str', 'ONE': 'int' } }
|
0
tests/qapi-schema/alternate-clash.out
Normal file
0
tests/qapi-schema/alternate-clash.out
Normal file
1
tests/qapi-schema/alternate-conflict-dict.err
Normal file
1
tests/qapi-schema/alternate-conflict-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/alternate-conflict-dict.json:6: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
|
1
tests/qapi-schema/alternate-conflict-dict.exit
Normal file
1
tests/qapi-schema/alternate-conflict-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
8
tests/qapi-schema/alternate-conflict-dict.json
Normal file
8
tests/qapi-schema/alternate-conflict-dict.json
Normal file
@ -0,0 +1,8 @@
|
||||
# we reject alternates with multiple object branches
|
||||
{ 'struct': 'One',
|
||||
'data': { 'name': 'str' } }
|
||||
{ 'struct': 'Two',
|
||||
'data': { 'value': 'int' } }
|
||||
{ 'alternate': 'Alt',
|
||||
'data': { 'one': 'One',
|
||||
'two': 'Two' } }
|
0
tests/qapi-schema/alternate-conflict-dict.out
Normal file
0
tests/qapi-schema/alternate-conflict-dict.out
Normal file
1
tests/qapi-schema/alternate-conflict-string.err
Normal file
1
tests/qapi-schema/alternate-conflict-string.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/alternate-conflict-string.json:4: Alternate 'Alt' member 'two' can't be distinguished from member 'one'
|
1
tests/qapi-schema/alternate-conflict-string.exit
Normal file
1
tests/qapi-schema/alternate-conflict-string.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
6
tests/qapi-schema/alternate-conflict-string.json
Normal file
6
tests/qapi-schema/alternate-conflict-string.json
Normal file
@ -0,0 +1,6 @@
|
||||
# we reject alternates with multiple string-like branches
|
||||
{ 'enum': 'Enum',
|
||||
'data': [ 'hello', 'world' ] }
|
||||
{ 'alternate': 'Alt',
|
||||
'data': { 'one': 'str',
|
||||
'two': 'Enum' } }
|
0
tests/qapi-schema/alternate-conflict-string.out
Normal file
0
tests/qapi-schema/alternate-conflict-string.out
Normal file
0
tests/qapi-schema/alternate-good.err
Normal file
0
tests/qapi-schema/alternate-good.err
Normal file
1
tests/qapi-schema/alternate-good.exit
Normal file
1
tests/qapi-schema/alternate-good.exit
Normal file
@ -0,0 +1 @@
|
||||
0
|
9
tests/qapi-schema/alternate-good.json
Normal file
9
tests/qapi-schema/alternate-good.json
Normal file
@ -0,0 +1,9 @@
|
||||
# Working example of alternate
|
||||
{ 'struct': 'Data',
|
||||
'data': { '*number': 'int', '*name': 'str' } }
|
||||
{ 'enum': 'Enum',
|
||||
'data': [ 'hello', 'world' ] }
|
||||
{ 'alternate': 'Alt',
|
||||
'data': { 'value': 'int',
|
||||
'string': 'Enum',
|
||||
'struct': 'Data' } }
|
6
tests/qapi-schema/alternate-good.out
Normal file
6
tests/qapi-schema/alternate-good.out
Normal file
@ -0,0 +1,6 @@
|
||||
[OrderedDict([('struct', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))]),
|
||||
OrderedDict([('enum', 'Enum'), ('data', ['hello', 'world'])]),
|
||||
OrderedDict([('alternate', 'Alt'), ('data', OrderedDict([('value', 'int'), ('string', 'Enum'), ('struct', 'Data')]))])]
|
||||
[{'enum_name': 'Enum', 'enum_values': ['hello', 'world']},
|
||||
{'enum_name': 'AltKind', 'enum_values': None}]
|
||||
[OrderedDict([('struct', 'Data'), ('data', OrderedDict([('*number', 'int'), ('*name', 'str')]))])]
|
1
tests/qapi-schema/alternate-nested.err
Normal file
1
tests/qapi-schema/alternate-nested.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/alternate-nested.json:4: Member 'nested' of alternate 'Alt2' cannot use alternate type 'Alt1'
|
1
tests/qapi-schema/alternate-nested.exit
Normal file
1
tests/qapi-schema/alternate-nested.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
5
tests/qapi-schema/alternate-nested.json
Normal file
5
tests/qapi-schema/alternate-nested.json
Normal file
@ -0,0 +1,5 @@
|
||||
# we reject a nested alternate branch
|
||||
{ 'alternate': 'Alt1',
|
||||
'data': { 'name': 'str', 'value': 'int' } }
|
||||
{ 'alternate': 'Alt2',
|
||||
'data': { 'nested': 'Alt1' } }
|
0
tests/qapi-schema/alternate-nested.out
Normal file
0
tests/qapi-schema/alternate-nested.out
Normal file
1
tests/qapi-schema/alternate-unknown.err
Normal file
1
tests/qapi-schema/alternate-unknown.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/alternate-unknown.json:2: Member 'unknown' of alternate 'Alt' uses unknown type 'MissingType'
|
1
tests/qapi-schema/alternate-unknown.exit
Normal file
1
tests/qapi-schema/alternate-unknown.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/alternate-unknown.json
Normal file
3
tests/qapi-schema/alternate-unknown.json
Normal file
@ -0,0 +1,3 @@
|
||||
# we reject an alternate with unknown type in branch
|
||||
{ 'alternate': 'Alt',
|
||||
'data': { 'unknown': 'MissingType' } }
|
0
tests/qapi-schema/alternate-unknown.out
Normal file
0
tests/qapi-schema/alternate-unknown.out
Normal file
1
tests/qapi-schema/bad-base.err
Normal file
1
tests/qapi-schema/bad-base.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/bad-base.json:3: 'base' for struct 'MyType' cannot use union type 'Union'
|
1
tests/qapi-schema/bad-base.exit
Normal file
1
tests/qapi-schema/bad-base.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/bad-base.json
Normal file
3
tests/qapi-schema/bad-base.json
Normal file
@ -0,0 +1,3 @@
|
||||
# we reject a base that is not a struct
|
||||
{ 'union': 'Union', 'data': { 'a': 'int', 'b': 'str' } }
|
||||
{ 'struct': 'MyType', 'base': 'Union', 'data': { 'c': 'int' } }
|
0
tests/qapi-schema/bad-base.out
Normal file
0
tests/qapi-schema/bad-base.out
Normal file
1
tests/qapi-schema/bad-data.err
Normal file
1
tests/qapi-schema/bad-data.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/bad-data.json:2: 'data' for command 'oops' cannot be an array
|
1
tests/qapi-schema/bad-data.exit
Normal file
1
tests/qapi-schema/bad-data.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/bad-data.json
Normal file
2
tests/qapi-schema/bad-data.json
Normal file
@ -0,0 +1,2 @@
|
||||
# we ensure 'data' is a dictionary for all but enums
|
||||
{ 'command': 'oops', 'data': [ ] }
|
0
tests/qapi-schema/bad-data.out
Normal file
0
tests/qapi-schema/bad-data.out
Normal file
1
tests/qapi-schema/bad-ident.err
Normal file
1
tests/qapi-schema/bad-ident.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/bad-ident.json:2: 'struct' does not allow optional name '*oops'
|
1
tests/qapi-schema/bad-ident.exit
Normal file
1
tests/qapi-schema/bad-ident.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/bad-ident.json
Normal file
2
tests/qapi-schema/bad-ident.json
Normal file
@ -0,0 +1,2 @@
|
||||
# we reject creating a type name with bad name
|
||||
{ 'struct': '*oops', 'data': { 'i': 'int' } }
|
0
tests/qapi-schema/bad-ident.out
Normal file
0
tests/qapi-schema/bad-ident.out
Normal file
1
tests/qapi-schema/bad-type-bool.err
Normal file
1
tests/qapi-schema/bad-type-bool.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/bad-type-bool.json:2: 'struct' key must have a string value
|
1
tests/qapi-schema/bad-type-bool.exit
Normal file
1
tests/qapi-schema/bad-type-bool.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/bad-type-bool.json
Normal file
2
tests/qapi-schema/bad-type-bool.json
Normal file
@ -0,0 +1,2 @@
|
||||
# we reject an expression with a metatype that is not a string
|
||||
{ 'struct': true, 'data': { } }
|
0
tests/qapi-schema/bad-type-bool.out
Normal file
0
tests/qapi-schema/bad-type-bool.out
Normal file
1
tests/qapi-schema/bad-type-dict.err
Normal file
1
tests/qapi-schema/bad-type-dict.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/bad-type-dict.json:2: 'command' key must have a string value
|
1
tests/qapi-schema/bad-type-dict.exit
Normal file
1
tests/qapi-schema/bad-type-dict.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/bad-type-dict.json
Normal file
2
tests/qapi-schema/bad-type-dict.json
Normal file
@ -0,0 +1,2 @@
|
||||
# we reject an expression with a metatype that is not a string
|
||||
{ 'command': { } }
|
0
tests/qapi-schema/bad-type-dict.out
Normal file
0
tests/qapi-schema/bad-type-dict.out
Normal file
1
tests/qapi-schema/bad-type-int.err
Normal file
1
tests/qapi-schema/bad-type-int.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/bad-type-int.json:3:13: Stray "1"
|
1
tests/qapi-schema/bad-type-int.exit
Normal file
1
tests/qapi-schema/bad-type-int.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/bad-type-int.json
Normal file
3
tests/qapi-schema/bad-type-int.json
Normal file
@ -0,0 +1,3 @@
|
||||
# we reject an expression with a metatype that is not a string
|
||||
# FIXME: once the parser understands integer inputs, improve the error message
|
||||
{ 'struct': 1, 'data': { } }
|
0
tests/qapi-schema/bad-type-int.out
Normal file
0
tests/qapi-schema/bad-type-int.out
Normal file
1
tests/qapi-schema/command-int.err
Normal file
1
tests/qapi-schema/command-int.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/command-int.json:2: built-in 'int' is already defined
|
1
tests/qapi-schema/command-int.exit
Normal file
1
tests/qapi-schema/command-int.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
3
tests/qapi-schema/command-int.json
Normal file
3
tests/qapi-schema/command-int.json
Normal file
@ -0,0 +1,3 @@
|
||||
# we reject collisions between commands and types
|
||||
{ 'command': 'int', 'data': { 'character': 'str' },
|
||||
'returns': { 'value': 'int' } }
|
0
tests/qapi-schema/command-int.out
Normal file
0
tests/qapi-schema/command-int.out
Normal file
1
tests/qapi-schema/data-array-empty.err
Normal file
1
tests/qapi-schema/data-array-empty.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/data-array-empty.json:2: Member 'empty' of 'data' for command 'oops': array type must contain single type name
|
1
tests/qapi-schema/data-array-empty.exit
Normal file
1
tests/qapi-schema/data-array-empty.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/data-array-empty.json
Normal file
2
tests/qapi-schema/data-array-empty.json
Normal file
@ -0,0 +1,2 @@
|
||||
# we reject an array for data if it does not contain a known type
|
||||
{ 'command': 'oops', 'data': { 'empty': [ ] } }
|
0
tests/qapi-schema/data-array-empty.out
Normal file
0
tests/qapi-schema/data-array-empty.out
Normal file
1
tests/qapi-schema/data-array-unknown.err
Normal file
1
tests/qapi-schema/data-array-unknown.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/data-array-unknown.json:2: Member 'array' of 'data' for command 'oops' uses unknown type 'array of NoSuchType'
|
1
tests/qapi-schema/data-array-unknown.exit
Normal file
1
tests/qapi-schema/data-array-unknown.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/data-array-unknown.json
Normal file
2
tests/qapi-schema/data-array-unknown.json
Normal file
@ -0,0 +1,2 @@
|
||||
# we reject an array for data if it does not contain a known type
|
||||
{ 'command': 'oops', 'data': { 'array': [ 'NoSuchType' ] } }
|
0
tests/qapi-schema/data-array-unknown.out
Normal file
0
tests/qapi-schema/data-array-unknown.out
Normal file
1
tests/qapi-schema/data-int.err
Normal file
1
tests/qapi-schema/data-int.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/data-int.json:2: 'data' for command 'oops' cannot use built-in type 'int'
|
1
tests/qapi-schema/data-int.exit
Normal file
1
tests/qapi-schema/data-int.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/data-int.json
Normal file
2
tests/qapi-schema/data-int.json
Normal file
@ -0,0 +1,2 @@
|
||||
# we reject commands where data is not an array or complex type
|
||||
{ 'command': 'oops', 'data': 'int' }
|
0
tests/qapi-schema/data-int.out
Normal file
0
tests/qapi-schema/data-int.out
Normal file
1
tests/qapi-schema/data-member-array-bad.err
Normal file
1
tests/qapi-schema/data-member-array-bad.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/data-member-array-bad.json:2: Member 'member' of 'data' for command 'oops': array type must contain single type name
|
1
tests/qapi-schema/data-member-array-bad.exit
Normal file
1
tests/qapi-schema/data-member-array-bad.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/data-member-array-bad.json
Normal file
2
tests/qapi-schema/data-member-array-bad.json
Normal file
@ -0,0 +1,2 @@
|
||||
# we reject data if it does not contain a valid array type
|
||||
{ 'command': 'oops', 'data': { 'member': [ { 'nested': 'str' } ] } }
|
0
tests/qapi-schema/data-member-array-bad.out
Normal file
0
tests/qapi-schema/data-member-array-bad.out
Normal file
0
tests/qapi-schema/data-member-array.err
Normal file
0
tests/qapi-schema/data-member-array.err
Normal file
1
tests/qapi-schema/data-member-array.exit
Normal file
1
tests/qapi-schema/data-member-array.exit
Normal file
@ -0,0 +1 @@
|
||||
0
|
4
tests/qapi-schema/data-member-array.json
Normal file
4
tests/qapi-schema/data-member-array.json
Normal file
@ -0,0 +1,4 @@
|
||||
# valid array members
|
||||
{ 'enum': 'abc', 'data': [ 'a', 'b', 'c' ] }
|
||||
{ 'struct': 'def', 'data': { 'array': [ 'abc' ] } }
|
||||
{ 'command': 'okay', 'data': { 'member1': [ 'int' ], 'member2': [ 'def' ] } }
|
5
tests/qapi-schema/data-member-array.out
Normal file
5
tests/qapi-schema/data-member-array.out
Normal file
@ -0,0 +1,5 @@
|
||||
[OrderedDict([('enum', 'abc'), ('data', ['a', 'b', 'c'])]),
|
||||
OrderedDict([('struct', 'def'), ('data', OrderedDict([('array', ['abc'])]))]),
|
||||
OrderedDict([('command', 'okay'), ('data', OrderedDict([('member1', ['int']), ('member2', ['def'])]))])]
|
||||
[{'enum_name': 'abc', 'enum_values': ['a', 'b', 'c']}]
|
||||
[OrderedDict([('struct', 'def'), ('data', OrderedDict([('array', ['abc'])]))])]
|
1
tests/qapi-schema/data-member-unknown.err
Normal file
1
tests/qapi-schema/data-member-unknown.err
Normal file
@ -0,0 +1 @@
|
||||
tests/qapi-schema/data-member-unknown.json:2: Member 'member' of 'data' for command 'oops' uses unknown type 'NoSuchType'
|
1
tests/qapi-schema/data-member-unknown.exit
Normal file
1
tests/qapi-schema/data-member-unknown.exit
Normal file
@ -0,0 +1 @@
|
||||
1
|
2
tests/qapi-schema/data-member-unknown.json
Normal file
2
tests/qapi-schema/data-member-unknown.json
Normal file
@ -0,0 +1,2 @@
|
||||
# we reject data if it does not contain a known type
|
||||
{ 'command': 'oops', 'data': { 'member': 'NoSuchType' } }
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user