qapi: Have each QAPI schema declare its returns white-list

qapi.py has a hardcoded white-list of command names that may violate
the rules on permitted return types.  Add a new pragma directive
'returns-whitelist', and use it to replace the hard-coded white-list.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1489582656-31133-6-git-send-email-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
Markus Armbruster 2017-03-15 13:56:54 +01:00
parent 700dc9f503
commit 1554a8fae9
12 changed files with 61 additions and 28 deletions

View File

@ -318,6 +318,9 @@ pragma to different values in parts of the schema doesn't work.
Pragma 'doc-required' takes a boolean value. If true, documentation
is required. Default is false.
Pragma 'returns-whitelist' takes a list of command names that may
violate the rules on permitted return types. Default is none.
=== Struct types ===
@ -566,12 +569,10 @@ The member is optional from the command declaration; if absent, the
"return" member 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.
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
member at the present.
To return anything else, you have to list the command in pragma
'returns-whitelist'. If you do this, the command cannot be extended
to return additional information in the future. Use of
'returns-whitelist' for new commands is strongly discouraged.
All commands in Client JSON Protocol use a dictionary to report
failure, with no way to specify that in QAPI. Where the error return

View File

@ -51,6 +51,18 @@
{ 'pragma': { 'doc-required': true } }
# Whitelists to permit QAPI rule violations; think twice before you
# add to them!
{ 'pragma': {
# Commands allowed to return a non-dictionary:
'returns-whitelist': [
'human-monitor-command',
'qom-get',
'query-migrate-cache-size',
'query-tpm-models',
'query-tpm-types',
'ringbuf-read' ] } }
# QAPI common definitions
{ 'include': 'qapi/common.json' }

View File

@ -13,6 +13,21 @@
{ 'pragma': { 'doc-required': true } }
# Whitelists to permit QAPI rule violations; think twice before you
# add to them!
{ 'pragma': {
# Commands allowed to return a non-dictionary:
'returns-whitelist': [
'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' ] } }
##
# @guest-sync-delimited:
#

View File

@ -41,26 +41,7 @@ builtin_types = {
doc_required = False
# Whitelist of commands allowed to return a non-dictionary
returns_whitelist = [
# From QMP:
'human-monitor-command',
'qom-get',
'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',
]
returns_whitelist = []
# Whitelist of entities allowed to violate case conventions
case_whitelist = [
@ -321,12 +302,19 @@ class QAPISchemaParser(object):
self.docs.extend(exprs_include.docs)
def _pragma(self, name, value, info):
global doc_required
global doc_required, returns_whitelist
if name == 'doc-required':
if not isinstance(value, bool):
raise QAPISemError(info,
"Pragma 'doc-required' must be boolean")
doc_required = value
elif name == 'returns-whitelist':
if (not isinstance(value, list)
or any([not isinstance(elt, str) for elt in value])):
raise QAPISemError(info,
"Pragma returns-whitelist must be"
" a list of strings")
returns_whitelist = value
else:
raise QAPISemError(info, "Unknown pragma '%s'" % name)

View File

@ -444,6 +444,7 @@ qapi-schema += non-objects.json
qapi-schema += pragma-doc-required-crap.json
qapi-schema += pragma-extra-junk.json
qapi-schema += pragma-non-dict.json
qapi-schema += pragma-returns-whitelist-crap.json
qapi-schema += qapi-schema-test.json
qapi-schema += quoted-structural-chars.json
qapi-schema += redefined-builtin.json

View File

@ -0,0 +1 @@
tests/qapi-schema/pragma-returns-whitelist-crap.json:3: Pragma returns-whitelist must be a list of strings

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
# 'returns-whitelist' must be list of strings
{ 'pragma': { 'returns-whitelist': [ 'good', [ 'bad' ] ] } }

View File

@ -3,6 +3,13 @@
# This file is a stress test of supported qapi constructs that must
# parse and compile correctly.
# Whitelists to permit QAPI rule violations
{ 'pragma': {
# Commands allowed to return a non-dictionary:
'returns-whitelist': [
'guest-get-time',
'guest-sync' ] } }
{ 'struct': 'TestStruct',
'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } }

View File

@ -1 +1 @@
tests/qapi-schema/returns-whitelist.json:10: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'int'
tests/qapi-schema/returns-whitelist.json:14: 'returns' for command 'no-way-this-will-get-whitelisted' cannot use built-in type 'int'

View File

@ -1,4 +1,8 @@
# we enforce that 'returns' be a dict or array of dict unless whitelisted
{ 'pragma': { 'returns-whitelist': [
'human-monitor-command', 'query-tpm-models', 'guest-get-time' ] } }
{ 'command': 'human-monitor-command',
'data': {'command-line': 'str', '*cpu-index': 'int'},
'returns': 'str' }