aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Krempa <pkrempa@redhat.com>2019-10-18 10:14:51 +0200
committerMarkus Armbruster <armbru@redhat.com>2019-10-22 13:54:13 +0200
commit23394b4c393c832aa3891533587ff97e04c70883 (patch)
tree191585355f00fd01fa66cb5d9da57aa7df96620f
parent758f272b6de428fcd523067f7a507cc7257d4ab0 (diff)
downloadqemu-23394b4c393c832aa3891533587ff97e04c70883.zip
qemu-23394b4c393c832aa3891533587ff97e04c70883.tar.gz
qemu-23394b4c393c832aa3891533587ff97e04c70883.tar.bz2
qapi: Add feature flags to commands
Similarly to features for struct types introduce the feature flags also for commands. This will allow notifying management layers of fixes and compatible changes in the behaviour of a command which may not be detectable any other way. The changes were heavily inspired by commit 6a8c0b51025. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Markus Armbruster <armbru@redhat.com> Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20191018081454.21369-3-armbru@redhat.com>
-rw-r--r--docs/devel/qapi-code-gen.txt10
-rw-r--r--qapi/introspect.json6
-rw-r--r--scripts/qapi/commands.py3
-rw-r--r--scripts/qapi/doc.py4
-rw-r--r--scripts/qapi/expr.py35
-rw-r--r--scripts/qapi/introspect.py7
-rw-r--r--scripts/qapi/schema.py22
-rwxr-xr-xtests/qapi-schema/test-qapi.py3
8 files changed, 62 insertions, 28 deletions
diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index 64d9e4c..45c93a4 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -457,7 +457,8 @@ Syntax:
'*gen': false,
'*allow-oob': true,
'*allow-preconfig': true,
- '*if': COND }
+ '*if': COND,
+ '*features': FEATURES }
Member 'command' names the command.
@@ -640,9 +641,10 @@ change in the QMP syntax (usually by allowing values or operations
that previously resulted in an error). QMP clients may still need to
know whether the extension is available.
-For this purpose, a list of features can be specified for a struct type.
-This is exposed to the client as a list of string, where each string
-signals that this build of QEMU shows a certain behaviour.
+For this purpose, a list of features can be specified for a command or
+struct type. This is exposed to the client as a list of strings,
+where each string signals that this build of QEMU shows a certain
+behaviour.
Each member of the 'features' array defines a feature. It can either
be { 'name': STRING, '*if': COND }, or STRING, which is shorthand for
diff --git a/qapi/introspect.json b/qapi/introspect.json
index 1843c1c..031a954 100644
--- a/qapi/introspect.json
+++ b/qapi/introspect.json
@@ -266,13 +266,17 @@
# @allow-oob: whether the command allows out-of-band execution,
# defaults to false (Since: 2.12)
#
+# @features: names of features associated with the command, in no particular
+# order. (since 4.2)
+#
# TODO: @success-response (currently irrelevant, because it's QGA, not QMP)
#
# Since: 2.5
##
{ 'struct': 'SchemaInfoCommand',
'data': { 'arg-type': 'str', 'ret-type': 'str',
- '*allow-oob': 'bool' } }
+ '*allow-oob': 'bool',
+ '*features': [ 'str' ] } }
##
# @SchemaInfoEvent:
diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py
index 898516b..ab98e50 100644
--- a/scripts/qapi/commands.py
+++ b/scripts/qapi/commands.py
@@ -277,7 +277,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
genc.add(gen_registry(self._regy.get_content(), self._prefix))
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
- success_response, boxed, allow_oob, allow_preconfig):
+ success_response, boxed, allow_oob, allow_preconfig,
+ features):
if not gen:
return
# FIXME: If T is a user-defined type, the user is responsible
diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py
index dc8919b..6d5726c 100644
--- a/scripts/qapi/doc.py
+++ b/scripts/qapi/doc.py
@@ -249,12 +249,14 @@ class QAPISchemaGenDocVisitor(QAPISchemaVisitor):
body=texi_entity(doc, 'Members', ifcond)))
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
- success_response, boxed, allow_oob, allow_preconfig):
+ success_response, boxed, allow_oob, allow_preconfig,
+ features):
doc = self.cur_doc
if boxed:
body = texi_body(doc)
body += ('\n@b{Arguments:} the members of @code{%s}\n'
% arg_type.name)
+ body += texi_features(doc)
body += texi_sections(doc, ifcond)
else:
body = texi_entity(doc, 'Arguments', ifcond)
diff --git a/scripts/qapi/expr.py b/scripts/qapi/expr.py
index 67cb2c2..7c7394f 100644
--- a/scripts/qapi/expr.py
+++ b/scripts/qapi/expr.py
@@ -185,6 +185,22 @@ def normalize_features(features):
for f in features]
+def check_features(features, info):
+ if features is None:
+ return
+ if not isinstance(features, list):
+ raise QAPISemError(info, "'features' must be an array")
+ for f in features:
+ source = "'features' member"
+ assert isinstance(f, dict)
+ check_keys(f, info, source, ['name'], ['if'])
+ check_name_is_str(f['name'], info, source)
+ source = "%s '%s'" % (source, f['name'])
+ check_name_str(f['name'], info, source)
+ check_if(f, info, source)
+ normalize_if(f)
+
+
def normalize_enum(expr):
if isinstance(expr['data'], list):
expr['data'] = [m if isinstance(m, dict) else {'name': m}
@@ -217,23 +233,10 @@ def check_enum(expr, info):
def check_struct(expr, info):
name = expr['struct']
members = expr['data']
- features = expr.get('features')
check_type(members, info, "'data'", allow_dict=name)
check_type(expr.get('base'), info, "'base'")
-
- if features:
- if not isinstance(features, list):
- raise QAPISemError(info, "'features' must be an array")
- for f in features:
- source = "'features' member"
- assert isinstance(f, dict)
- check_keys(f, info, source, ['name'], ['if'])
- check_name_is_str(f['name'], info, source)
- source = "%s '%s'" % (source, f['name'])
- check_name_str(f['name'], info, source)
- check_if(f, info, source)
- normalize_if(f)
+ check_features(expr.get('features'), info)
def check_union(expr, info):
@@ -283,6 +286,7 @@ def check_command(expr, info):
raise QAPISemError(info, "'boxed': true requires 'data'")
check_type(args, info, "'data'", allow_dict=not boxed)
check_type(rets, info, "'returns'", allow_array=True)
+ check_features(expr.get('features'), info)
def check_event(expr, info):
@@ -358,10 +362,11 @@ def check_exprs(exprs):
elif meta == 'command':
check_keys(expr, info, meta,
['command'],
- ['data', 'returns', 'boxed', 'if',
+ ['data', 'returns', 'boxed', 'if', 'features',
'gen', 'success-response', 'allow-oob',
'allow-preconfig'])
normalize_members(expr.get('data'))
+ normalize_features(expr.get('features'))
check_command(expr, info)
elif meta == 'event':
check_keys(expr, info, meta,
diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py
index 4f25759..b3a463d 100644
--- a/scripts/qapi/introspect.py
+++ b/scripts/qapi/introspect.py
@@ -211,13 +211,18 @@ const QLitObject %(c_name)s = %(c_string)s;
for m in variants.variants]}, ifcond)
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
- success_response, boxed, allow_oob, allow_preconfig):
+ success_response, boxed, allow_oob, allow_preconfig,
+ features):
arg_type = arg_type or self._schema.the_empty_object_type
ret_type = ret_type or self._schema.the_empty_object_type
obj = {'arg-type': self._use_type(arg_type),
'ret-type': self._use_type(ret_type)}
if allow_oob:
obj['allow-oob'] = allow_oob
+
+ if features:
+ obj['features'] = [(f.name, {'if': f.ifcond}) for f in features]
+
self._gen_qlit(name, 'command', obj, ifcond)
def visit_event(self, name, info, ifcond, arg_type, boxed):
diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py
index 2913a0f..f7d68a3 100644
--- a/scripts/qapi/schema.py
+++ b/scripts/qapi/schema.py
@@ -110,7 +110,8 @@ class QAPISchemaVisitor(object):
pass
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
- success_response, boxed, allow_oob, allow_preconfig):
+ success_response, boxed, allow_oob, allow_preconfig,
+ features):
pass
def visit_event(self, name, info, ifcond, arg_type, boxed):
@@ -659,10 +660,14 @@ class QAPISchemaCommand(QAPISchemaEntity):
meta = 'command'
def __init__(self, name, info, doc, ifcond, arg_type, ret_type,
- gen, success_response, boxed, allow_oob, allow_preconfig):
+ gen, success_response, boxed, allow_oob, allow_preconfig,
+ features):
QAPISchemaEntity.__init__(self, name, info, doc, ifcond)
assert not arg_type or isinstance(arg_type, str)
assert not ret_type or isinstance(ret_type, str)
+ for f in features:
+ assert isinstance(f, QAPISchemaFeature)
+ f.set_defined_in(name)
self._arg_type_name = arg_type
self.arg_type = None
self._ret_type_name = ret_type
@@ -672,6 +677,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
self.boxed = boxed
self.allow_oob = allow_oob
self.allow_preconfig = allow_preconfig
+ self.features = features
def check(self, schema):
QAPISchemaEntity.check(self, schema)
@@ -701,13 +707,19 @@ class QAPISchemaCommand(QAPISchemaEntity):
"command's 'returns' cannot take %s"
% self.ret_type.describe())
+ # Features are in a name space separate from members
+ seen = {}
+ for f in self.features:
+ f.check_clash(self.info, seen)
+
def visit(self, visitor):
QAPISchemaEntity.visit(self, visitor)
visitor.visit_command(self.name, self.info, self.ifcond,
self.arg_type, self.ret_type,
self.gen, self.success_response,
self.boxed, self.allow_oob,
- self.allow_preconfig)
+ self.allow_preconfig,
+ self.features)
class QAPISchemaEvent(QAPISchemaEntity):
@@ -984,6 +996,7 @@ class QAPISchema(object):
allow_oob = expr.get('allow-oob', False)
allow_preconfig = expr.get('allow-preconfig', False)
ifcond = expr.get('if')
+ features = expr.get('features', [])
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
name, info, doc, ifcond, 'arg', self._make_members(data, info))
@@ -992,7 +1005,8 @@ class QAPISchema(object):
rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, doc, ifcond, data, rets,
gen, success_response,
- boxed, allow_oob, allow_preconfig))
+ boxed, allow_oob, allow_preconfig,
+ self._make_features(features, info)))
def _def_event(self, expr, info, doc):
name = expr['event']
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index 29d9435..d31ac4b 100755
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -72,7 +72,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
self._print_if(ifcond)
def visit_command(self, name, info, ifcond, arg_type, ret_type, gen,
- success_response, boxed, allow_oob, allow_preconfig):
+ success_response, boxed, allow_oob, allow_preconfig,
+ features):
print('command %s %s -> %s'
% (name, arg_type and arg_type.name,
ret_type and ret_type.name))