437 changes: 248 additions & 189 deletions qapi/ui.json

Large diffs are not rendered by default.

84 changes: 30 additions & 54 deletions qapi/virtio.json
Expand Up @@ -16,7 +16,6 @@
# @name: Name of the VirtIODevice
#
# Since: 7.2
#
##
{ 'struct': 'VirtioInfo',
'data': { 'path': 'str',
Expand All @@ -28,6 +27,7 @@
# Returns a list of all realized VirtIODevices
#
# Features:
#
# @unstable: This command is meant for debugging.
#
# Returns: List of gathered VirtIODevices
Expand Down Expand Up @@ -60,17 +60,15 @@
# }
# ]
# }
#
##

{ 'command': 'x-query-virtio',
'returns': [ 'VirtioInfo' ],
'features': [ 'unstable' ] }

##
# @VhostStatus:
#
# Information about a vhost device. This information will only be
# Information about a vhost device. This information will only be
# displayed if the vhost device is active.
#
# @n-mem-sections: vhost_dev n_mem_sections
Expand Down Expand Up @@ -98,9 +96,7 @@
# @log-size: vhost_dev log_size
#
# Since: 7.2
#
##

{ 'struct': 'VhostStatus',
'data': { 'n-mem-sections': 'int',
'n-tmp-sections': 'int',
Expand All @@ -119,8 +115,8 @@
# @VirtioStatus:
#
# Full status of the virtio device with most VirtIODevice members.
# Also includes the full status of the corresponding vhost device
# if the vhost device is active.
# Also includes the full status of the corresponding vhost device if
# the vhost device is active.
#
# @name: VirtIODevice name
#
Expand All @@ -136,8 +132,8 @@
#
# @device-endian: VirtIODevice device_endian
#
# @num-vqs: VirtIODevice virtqueue count. This is the number of active
# virtqueues being used by the VirtIODevice.
# @num-vqs: VirtIODevice virtqueue count. This is the number of
# active virtqueues being used by the VirtIODevice.
#
# @status: VirtIODevice configuration status (VirtioDeviceStatus)
#
Expand All @@ -163,14 +159,12 @@
#
# @use-guest-notifier-mask: VirtIODevice use_guest_notifier_mask flag
#
# @vhost-dev: Corresponding vhost device info for a given VirtIODevice.
# Present if the given VirtIODevice has an active vhost
# device.
# @vhost-dev: Corresponding vhost device info for a given
# VirtIODevice. Present if the given VirtIODevice has an active
# vhost device.
#
# Since: 7.2
#
##

{ 'struct': 'VirtioStatus',
'data': { 'name': 'str',
'device-id': 'uint16',
Expand Down Expand Up @@ -202,6 +196,7 @@
# @path: Canonical QOM path of the VirtIODevice
#
# Features:
#
# @unstable: This command is meant for debugging.
#
# Returns: VirtioStatus of the virtio device
Expand Down Expand Up @@ -433,9 +428,7 @@
# "use-started": true
# }
# }
#
##

{ 'command': 'x-query-virtio-status',
'data': { 'path': 'str' },
'returns': 'VirtioStatus',
Expand All @@ -448,13 +441,13 @@
# device
#
# @statuses: List of decoded configuration statuses of the virtio
# device
# device
#
# @unknown-statuses: Virtio device statuses bitmap that have not been decoded
# @unknown-statuses: Virtio device statuses bitmap that have not been
# decoded
#
# Since: 7.2
##

{ 'struct': 'VirtioDeviceStatus',
'data': { 'statuses': [ 'str' ],
'*unknown-statuses': 'uint8' } }
Expand All @@ -466,35 +459,33 @@
# Vhost User device
#
# @protocols: List of decoded vhost user protocol features of a vhost
# user device
# user device
#
# @unknown-protocols: Vhost user device protocol features bitmap that
# have not been decoded
# have not been decoded
#
# Since: 7.2
##

{ 'struct': 'VhostDeviceProtocols',
'data': { 'protocols': [ 'str' ],
'*unknown-protocols': 'uint64' } }

##
# @VirtioDeviceFeatures:
#
# The common fields that apply to most Virtio devices. Some devices
# The common fields that apply to most Virtio devices. Some devices
# may not have their own device-specific features (e.g. virtio-rng).
#
# @transports: List of transport features of the virtio device
#
# @dev-features: List of device-specific features (if the device has
# unique features)
# unique features)
#
# @unknown-dev-features: Virtio device features bitmap that have not
# been decoded
# been decoded
#
# Since: 7.2
##

{ 'struct': 'VirtioDeviceFeatures',
'data': { 'transports': [ 'str' ],
'*dev-features': [ 'str' ],
Expand Down Expand Up @@ -525,7 +516,7 @@
# @vring-used: VirtQueue vring.used (device area)
#
# @last-avail-idx: VirtQueue last_avail_idx or return of vhost_dev
# vhost_get_vring_base (if vhost active)
# vhost_get_vring_base (if vhost active)
#
# @shadow-avail-idx: VirtQueue shadow_avail_idx
#
Expand All @@ -536,9 +527,7 @@
# @signalled-used-valid: VirtQueue signalled_used_valid flag
#
# Since: 7.2
#
##

{ 'struct': 'VirtQueueStatus',
'data': { 'name': 'str',
'queue-index': 'uint16',
Expand All @@ -565,16 +554,17 @@
# @queue: VirtQueue index to examine
#
# Features:
#
# @unstable: This command is meant for debugging.
#
# Returns: VirtQueueStatus of the VirtQueue
#
# Notes: last_avail_idx will not be displayed in the case where
# the selected VirtIODevice has a running vhost device and
# the VirtIODevice VirtQueue index (queue) does not exist for
# the corresponding vhost device vhost_virtqueue. Also,
# shadow_avail_idx will not be displayed in the case where
# the selected VirtIODevice has a running vhost device.
# Notes: last_avail_idx will not be displayed in the case where the
# selected VirtIODevice has a running vhost device and the
# VirtIODevice VirtQueue index (queue) does not exist for the
# corresponding vhost device vhost_virtqueue. Also,
# shadow_avail_idx will not be displayed in the case where the
# selected VirtIODevice has a running vhost device.
#
# Since: 7.2
#
Expand Down Expand Up @@ -626,9 +616,7 @@
# "vring-num": 128
# }
# }
#
##

{ 'command': 'x-query-virtio-queue-status',
'data': { 'path': 'str', 'queue': 'uint16' },
'returns': 'VirtQueueStatus',
Expand Down Expand Up @@ -667,9 +655,7 @@
# @used-size: vhost_virtqueue used_size
#
# Since: 7.2
#
##

{ 'struct': 'VirtVhostQueueStatus',
'data': { 'name': 'str',
'kick': 'int',
Expand All @@ -695,6 +681,7 @@
# @queue: vhost_virtqueue index to examine
#
# Features:
#
# @unstable: This command is meant for debugging.
#
# Returns: VirtVhostQueueStatus of the vhost_virtqueue
Expand Down Expand Up @@ -748,9 +735,7 @@
# "kick": 0
# }
# }
#
##

{ 'command': 'x-query-virtio-vhost-queue-status',
'data': { 'path': 'str', 'queue': 'uint16' },
'returns': 'VirtVhostQueueStatus',
Expand All @@ -768,9 +753,7 @@
# @flags: List of descriptor flags
#
# Since: 7.2
#
##

{ 'struct': 'VirtioRingDesc',
'data': { 'addr': 'uint64',
'len': 'uint32',
Expand All @@ -788,9 +771,7 @@
# @ring: VRingAvail ring[] entry at provided index
#
# Since: 7.2
#
##

{ 'struct': 'VirtioRingAvail',
'data': { 'flags': 'uint16',
'idx': 'uint16',
Expand All @@ -806,9 +787,7 @@
# @idx: VRingUsed index
#
# Since: 7.2
#
##

{ 'struct': 'VirtioRingUsed',
'data': { 'flags': 'uint16',
'idx': 'uint16' } }
Expand All @@ -830,9 +809,7 @@
# @used: VRingUsed info
#
# Since: 7.2
#
##

{ 'struct': 'VirtioQueueElement',
'data': { 'name': 'str',
'index': 'uint32',
Expand All @@ -849,10 +826,11 @@
#
# @queue: VirtQueue index to examine
#
# @index: Index of the element in the queue
# (default: head of the queue)
# @index: Index of the element in the queue (default: head of the
# queue)
#
# Features:
#
# @unstable: This command is meant for debugging.
#
# Returns: VirtioQueueElement information
Expand Down Expand Up @@ -945,9 +923,7 @@
# }
# }
# }
#
##

{ 'command': 'x-query-virtio-queue-element',
'data': { 'path': 'str', 'queue': 'uint16', '*index': 'uint16' },
'returns': 'VirtioQueueElement',
Expand Down
42 changes: 20 additions & 22 deletions qapi/yank.json
Expand Up @@ -9,7 +9,7 @@
##
# @YankInstanceType:
#
# An enumeration of yank instance types. See @YankInstance for more
# An enumeration of yank instance types. See @YankInstance for more
# information.
#
# Since: 6.0
Expand All @@ -20,8 +20,8 @@
##
# @YankInstanceBlockNode:
#
# Specifies which block graph node to yank. See @YankInstance for more
# information.
# Specifies which block graph node to yank. See @YankInstance for
# more information.
#
# @node-name: the name of the block graph node
#
Expand All @@ -33,8 +33,8 @@
##
# @YankInstanceChardev:
#
# Specifies which character device to yank. See @YankInstance for more
# information.
# Specifies which character device to yank. See @YankInstance for
# more information.
#
# @id: the chardev's ID
#
Expand All @@ -46,21 +46,18 @@
##
# @YankInstance:
#
# A yank instance can be yanked with the @yank qmp command to recover from a
# hanging QEMU.
# A yank instance can be yanked with the @yank qmp command to recover
# from a hanging QEMU.
#
# Currently implemented yank instances:
#
# - nbd block device:
# Yanking it will shut down the connection to the nbd server without
# attempting to reconnect.
# - socket chardev:
# Yanking it will shut down the connected socket.
# - migration:
# Yanking it will shut down all migration connections. Unlike
# @migrate_cancel, it will not notify the migration process, so migration
# will go into @failed state, instead of @cancelled state. @yank should be
# used to recover from hangs.
# - nbd block device: Yanking it will shut down the connection to the
# nbd server without attempting to reconnect.
# - socket chardev: Yanking it will shut down the connected socket.
# - migration: Yanking it will shut down all migration connections.
# Unlike @migrate_cancel, it will not notify the migration process,
# so migration will go into @failed state, instead of @cancelled
# state. @yank should be used to recover from hangs.
#
# Since: 6.0
##
Expand All @@ -74,13 +71,14 @@
##
# @yank:
#
# Try to recover from hanging QEMU by yanking the specified instances. See
# @YankInstance for more information.
# Try to recover from hanging QEMU by yanking the specified instances.
# See @YankInstance for more information.
#
# Takes a list of @YankInstance as argument.
#
# Returns: - Nothing on success
# - @DeviceNotFound error, if any of the YankInstances doesn't exist
# Returns:
# - Nothing on success
# - @DeviceNotFound error, if any of the YankInstances doesn't exist
#
# Example:
#
Expand All @@ -101,7 +99,7 @@
##
# @query-yank:
#
# Query yank instances. See @YankInstance for more information.
# Query yank instances. See @YankInstance for more information.
#
# Returns: list of @YankInstance
#
Expand Down
682 changes: 389 additions & 293 deletions qga/qapi-schema.json

Large diffs are not rendered by default.

141 changes: 51 additions & 90 deletions scripts/qapi/parser.py
Expand Up @@ -346,7 +346,7 @@ def accept(self, skip_comment: bool = True) -> None:
elif not self.tok.isspace():
# Show up to next structural, whitespace or quote
# character
match = must_match('[^[\\]{}:,\\s\'"]+',
match = must_match('[^[\\]{}:,\\s\']+',
self.src[self.cursor-1:])
raise QAPIParseError(self, "stray '%s'" % match.group(0))

Expand Down Expand Up @@ -468,34 +468,39 @@ class QAPIDoc:
class Section:
# pylint: disable=too-few-public-methods
def __init__(self, parser: QAPISchemaParser,
name: Optional[str] = None, indent: int = 0):

name: Optional[str] = None):
# parser, for error messages about indentation
self._parser = parser
# optional section name (argument/member or section name)
self.name = name
# section text without section name
self.text = ''
# the expected indent level of the text of this section
self._indent = indent
# indentation to strip (None means indeterminate)
self._indent = None if self.name else 0

def append(self, line: str) -> None:
# Strip leading spaces corresponding to the expected indent level
# Blank lines are always OK.
line = line.rstrip()

if line:
indent = must_match(r'\s*', line).end()
if indent < self._indent:
if self._indent is None:
# indeterminate indentation
if self.text != '':
# non-blank, non-first line determines indentation
self._indent = indent
elif indent < self._indent:
raise QAPIParseError(
self._parser,
"unexpected de-indent (expected at least %d spaces)" %
self._indent)
line = line[self._indent:]

self.text += line.rstrip() + '\n'
self.text += line + '\n'

class ArgSection(Section):
def __init__(self, parser: QAPISchemaParser,
name: str, indent: int = 0):
super().__init__(parser, name, indent)
name: str):
super().__init__(parser, name)
self.member: Optional['QAPISchemaMember'] = None

def connect(self, member: 'QAPISchemaMember') -> None:
Expand Down Expand Up @@ -558,12 +563,12 @@ def end_comment(self) -> None:
self._switch_section(QAPIDoc.NullSection(self._parser))

@staticmethod
def _is_section_tag(name: str) -> bool:
return name in ('Returns:', 'Since:',
# those are often singular or plural
'Note:', 'Notes:',
'Example:', 'Examples:',
'TODO:')
def _match_at_name_colon(string: str) -> re.Match:
return re.match(r'@([^:]*): *', string)

@staticmethod
def _match_section_tag(string: str) -> re.Match:
return re.match(r'(Returns|Since|Notes?|Examples?|TODO): *', string)

def _append_body_line(self, line: str) -> None:
"""
Expand All @@ -579,7 +584,6 @@ def _append_body_line(self, line: str) -> None:
Else, append the line to the current section.
"""
name = line.split(' ', 1)[0]
# FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
# recognized, and get silently treated as ordinary text
if not self.symbol and not self.body.text and line.startswith('@'):
Expand All @@ -593,12 +597,12 @@ def _append_body_line(self, line: str) -> None:
self._parser, "name required after '@'")
elif self.symbol:
# This is a definition documentation block
if name.startswith('@') and name.endswith(':'):
if self._match_at_name_colon(line):
self._append_line = self._append_args_line
self._append_args_line(line)
elif line == 'Features:':
self._append_line = self._append_features_line
elif self._is_section_tag(name):
elif self._match_section_tag(line):
self._append_line = self._append_various_line
self._append_various_line(line)
else:
Expand All @@ -619,25 +623,11 @@ def _append_args_line(self, line: str) -> None:
Else, append the line to the current section.
"""
name = line.split(' ', 1)[0]

if name.startswith('@') and name.endswith(':'):
# If line is "@arg: first line of description", find
# the index of 'f', which is the indent we expect for any
# following lines. We then remove the leading "@arg:"
# from line and replace it with spaces so that 'f' has the
# same index as it did in the original line and can be
# handled the same way we will handle following lines.
indent = must_match(r'@\S*:\s*', line).end()
line = line[indent:]
if not line:
# Line was just the "@arg:" header; following lines
# are not indented
indent = 0
else:
line = ' ' * indent + line
self._start_args_section(name[1:-1], indent)
elif self._is_section_tag(name):
match = self._match_at_name_colon(line)
if match:
line = line[match.end():]
self._start_args_section(match.group(1))
elif self._match_section_tag(line):
self._append_line = self._append_various_line
self._append_various_line(line)
return
Expand All @@ -654,25 +644,11 @@ def _append_args_line(self, line: str) -> None:
self._append_freeform(line)

def _append_features_line(self, line: str) -> None:
name = line.split(' ', 1)[0]

if name.startswith('@') and name.endswith(':'):
# If line is "@arg: first line of description", find
# the index of 'f', which is the indent we expect for any
# following lines. We then remove the leading "@arg:"
# from line and replace it with spaces so that 'f' has the
# same index as it did in the original line and can be
# handled the same way we will handle following lines.
indent = must_match(r'@\S*:\s*', line).end()
line = line[indent:]
if not line:
# Line was just the "@arg:" header; following lines
# are not indented
indent = 0
else:
line = ' ' * indent + line
self._start_features_section(name[1:-1], indent)
elif self._is_section_tag(name):
match = self._match_at_name_colon(line)
if match:
line = line[match.end():]
self._start_features_section(match.group(1))
elif self._match_section_tag(line):
self._append_line = self._append_various_line
self._append_various_line(line)
return
Expand All @@ -696,64 +672,49 @@ def _append_various_line(self, line: str) -> None:
Else, append the line to the current section.
"""
name = line.split(' ', 1)[0]

if name.startswith('@') and name.endswith(':'):
match = self._match_at_name_colon(line)
if match:
raise QAPIParseError(self._parser,
"'%s' can't follow '%s' section"
% (name, self.sections[0].name))
if self._is_section_tag(name):
# If line is "Section: first line of description", find
# the index of 'f', which is the indent we expect for any
# following lines. We then remove the leading "Section:"
# from line and replace it with spaces so that 'f' has the
# same index as it did in the original line and can be
# handled the same way we will handle following lines.
indent = must_match(r'\S*:\s*', line).end()
line = line[indent:]
if not line:
# Line was just the "Section:" header; following lines
# are not indented
indent = 0
else:
line = ' ' * indent + line
self._start_section(name[:-1], indent)
"'@%s:' can't follow '%s' section"
% (match.group(1), self.sections[0].name))
match = self._match_section_tag(line)
if match:
line = line[match.end():]
self._start_section(match.group(1))

self._append_freeform(line)

def _start_symbol_section(
self,
symbols_dict: Dict[str, 'QAPIDoc.ArgSection'],
name: str,
indent: int) -> None:
name: str) -> None:
# FIXME invalid names other than the empty string aren't flagged
if not name:
raise QAPIParseError(self._parser, "invalid parameter name")
if name in symbols_dict:
raise QAPIParseError(self._parser,
"'%s' parameter name duplicated" % name)
assert not self.sections
new_section = QAPIDoc.ArgSection(self._parser, name, indent)
new_section = QAPIDoc.ArgSection(self._parser, name)
self._switch_section(new_section)
symbols_dict[name] = new_section

def _start_args_section(self, name: str, indent: int) -> None:
self._start_symbol_section(self.args, name, indent)
def _start_args_section(self, name: str) -> None:
self._start_symbol_section(self.args, name)

def _start_features_section(self, name: str, indent: int) -> None:
self._start_symbol_section(self.features, name, indent)
def _start_features_section(self, name: str) -> None:
self._start_symbol_section(self.features, name)

def _start_section(self, name: Optional[str] = None,
indent: int = 0) -> None:
def _start_section(self, name: Optional[str] = None) -> None:
if name in ('Returns', 'Since') and self.has_section(name):
raise QAPIParseError(self._parser,
"duplicated '%s' section" % name)
new_section = QAPIDoc.Section(self._parser, name, indent)
new_section = QAPIDoc.Section(self._parser, name)
self._switch_section(new_section)
self.sections.append(new_section)

def _switch_section(self, new_section: 'QAPIDoc.Section') -> None:
text = self._section.text = self._section.text.strip()
text = self._section.text = self._section.text.strip('\n')

# Only the 'body' section is allowed to have an empty body.
# All other sections, including anonymous ones, must have text.
Expand Down
2 changes: 1 addition & 1 deletion tests/qapi-schema/doc-bad-indent.err
@@ -1 +1 @@
doc-bad-indent.json:6:1: unexpected de-indent (expected at least 4 spaces)
doc-bad-indent.json:7:1: unexpected de-indent (expected at least 2 spaces)
3 changes: 2 additions & 1 deletion tests/qapi-schema/doc-bad-indent.json
Expand Up @@ -3,6 +3,7 @@
##
# @foo:
# @a: line one
# line two is wrongly indented
# line two
# line three is wrongly indented
##
{ 'command': 'foo', 'data': { 'a': 'int' } }
20 changes: 13 additions & 7 deletions tests/qapi-schema/doc-good.json
Expand Up @@ -54,7 +54,7 @@
##
# @Enum:
#
# @one: The _one_ {and only}
# @one: The _one_ {and only}, description on the same line
#
# Features:
# @enum-feat: Also _one_ {and only}
Expand All @@ -73,7 +73,8 @@
# @Base:
#
# @base1:
# the first member
# description starts on a new line,
# not indented
##
{ 'struct': 'Base', 'data': { 'base1': 'Enum' },
'if': { 'all': ['IFALL1', 'IFALL2'] } }
Expand All @@ -83,7 +84,9 @@
#
# A paragraph
#
# Another paragraph (but no @var: line)
# Another paragraph
#
# @var1 is undocumented
#
# Features:
# @variant1-feat: a feature
Expand Down Expand Up @@ -118,7 +121,8 @@
##
# @Alternate:
#
# @i: an integer
# @i: description starts on the same line
# remainder indented the same
# @b is undocumented
#
# Features:
Expand All @@ -136,10 +140,12 @@
##
# @cmd:
#
# @arg1: the first argument
# @arg1:
# description starts on a new line,
# indented
#
# @arg2: the second
# argument
# @arg2: description starts on the same line
# remainder indented differently
#
# Features:
# @cmd-feat1: a feature
Expand Down
19 changes: 12 additions & 7 deletions tests/qapi-schema/doc-good.out
Expand Up @@ -104,7 +104,7 @@ doc symbol=Enum
body=

arg=one
The _one_ {and only}
The _one_ {and only}, description on the same line
arg=two

feature=enum-feat
Expand All @@ -117,12 +117,15 @@ doc symbol=Base
body=

arg=base1
the first member
description starts on a new line,
not indented
doc symbol=Variant1
body=
A paragraph

Another paragraph (but no @var: line)
Another paragraph

@var1 is undocumented
arg=var1

feature=variant1-feat
Expand All @@ -141,7 +144,8 @@ doc symbol=Alternate
body=

arg=i
an integer
description starts on the same line
remainder indented the same
@b is undocumented
arg=b

Expand All @@ -154,10 +158,11 @@ doc symbol=cmd
body=

arg=arg1
the first argument
description starts on a new line,
indented
arg=arg2
the second
argument
description starts on the same line
remainder indented differently
arg=arg3

feature=cmd-feat1
Expand Down