qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH v9 17/17] qapi: Restore autocast behavior in 'netdev


From: Eric Blake
Subject: [Qemu-devel] [PATCH v9 17/17] qapi: Restore autocast behavior in 'netdev_add'
Date: Wed, 13 Jul 2016 21:50:28 -0600

When we converted 'netdev_add' to a fully-introspectible QMP,
we temporarily lost the ability to parse strings in place of
integers, the way the old QemuOpts code had done.  But now that
we have autocast, we can restore that behavior, by adding a
new 'autocast':true marker in QAPI.

Signed-off-by: Eric Blake <address@hidden>

---
v9: new patch

If we like this patch, one alternative would be to rearrange
the series to get autocast working first, prior to converting
netdev_add to drop 'gen':false, so that we don't have any
temporary regression.
---
 qapi-schema.json               |  6 +++++-
 scripts/qapi.py                | 16 ++++++++++------
 scripts/qapi-commands.py       | 11 ++++++-----
 scripts/qapi-introspect.py     |  2 +-
 docs/qapi-code-gen.txt         | 10 +++++++++-
 tests/qapi-schema/test-qapi.py |  2 +-
 6 files changed, 32 insertions(+), 15 deletions(-)

diff --git a/qapi-schema.json b/qapi-schema.json
index 7ec01e1..4dccc9b 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2367,12 +2367,16 @@
 #
 # @id: the name of the new network backend
 #
+# For backward compatibility, this command accepts strings where introspection
+# says that integers are expected; however, clients should not rely on this
+# and should obey the types visible in introspection.
+#
 # Since: 0.14.0
 #
 # Returns: Nothing on success
 #          If @type is not a valid network backend, DeviceNotFound
 ##
-{ 'command': 'netdev_add', 'data': 'Netdev', 'boxed': true }
+{ 'command': 'netdev_add', 'data': 'Netdev', 'boxed': true, 'autocast': true }

 ##
 # @netdev_del:
diff --git a/scripts/qapi.py b/scripts/qapi.py
index 21bc32f..0e33f72 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -702,7 +702,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
             raise QAPIExprError(info,
                                 "'%s' of %s '%s' should only use false value"
                                 % (key, meta, name))
-        if key == 'boxed' and value is not True:
+        if (key == 'boxed' or key == 'autocast') and value is not True:
             raise QAPIExprError(info,
                                 "'%s' of %s '%s' should only use true value"
                                 % (key, meta, name))
@@ -737,7 +737,8 @@ def check_exprs(exprs):
             add_struct(expr, info)
         elif 'command' in expr:
             check_keys(expr_elem, 'command', [],
-                       ['data', 'returns', 'gen', 'success-response', 'boxed'])
+                       ['data', 'returns', 'gen', 'success-response',
+                        'boxed', 'autocast'])
             add_name(expr['command'], info, 'command')
         elif 'event' in expr:
             check_keys(expr_elem, 'event', [], ['data', 'boxed'])
@@ -838,7 +839,7 @@ class QAPISchemaVisitor(object):
         pass

     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, boxed):
+                      gen, success_response, boxed, autocast):
         pass

     def visit_event(self, name, info, arg_type, boxed):
@@ -1181,7 +1182,7 @@ class QAPISchemaAlternateType(QAPISchemaType):

 class QAPISchemaCommand(QAPISchemaEntity):
     def __init__(self, name, info, arg_type, ret_type, gen, success_response,
-                 boxed):
+                 boxed, autocast):
         QAPISchemaEntity.__init__(self, name, info)
         assert not arg_type or isinstance(arg_type, str)
         assert not ret_type or isinstance(ret_type, str)
@@ -1192,6 +1193,7 @@ class QAPISchemaCommand(QAPISchemaEntity):
         self.gen = gen
         self.success_response = success_response
         self.boxed = boxed
+        self.autocast = autocast

     def check(self, schema):
         if self._arg_type_name:
@@ -1216,7 +1218,8 @@ class QAPISchemaCommand(QAPISchemaEntity):
     def visit(self, visitor):
         visitor.visit_command(self.name, self.info,
                               self.arg_type, self.ret_type,
-                              self.gen, self.success_response, self.boxed)
+                              self.gen, self.success_response, self.boxed,
+                              self.autocast)


 class QAPISchemaEvent(QAPISchemaEntity):
@@ -1422,6 +1425,7 @@ class QAPISchema(object):
         gen = expr.get('gen', True)
         success_response = expr.get('success-response', True)
         boxed = expr.get('boxed', False)
+        autocast = expr.get('autocast', False)
         if isinstance(data, OrderedDict):
             data = self._make_implicit_object_type(
                 name, info, 'arg', self._make_members(data, info))
@@ -1429,7 +1433,7 @@ class QAPISchema(object):
             assert len(rets) == 1
             rets = self._make_array_type(rets[0], info)
         self._def_entity(QAPISchemaCommand(name, info, data, rets, gen,
-                                           success_response, boxed))
+                                           success_response, boxed, autocast))

     def _def_event(self, expr, info):
         name = expr['event']
diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py
index f526c13..bf99d60 100644
--- a/scripts/qapi-commands.py
+++ b/scripts/qapi-commands.py
@@ -97,7 +97,7 @@ def gen_marshal_decl(name):
                  proto=gen_marshal_proto(name))


-def gen_marshal(name, arg_type, boxed, ret_type):
+def gen_marshal(name, arg_type, boxed, autocast, ret_type):
     ret = mcgen('''

 %(proto)s
@@ -117,7 +117,7 @@ def gen_marshal(name, arg_type, boxed, ret_type):
     Visitor *v;
     %(c_name)s arg = {0};

-    v = qmp_input_visitor_new(QOBJECT(args), true, false);
+    v = qmp_input_visitor_new(QOBJECT(args), true, %(autocast)s);
     visit_start_struct(v, NULL, NULL, 0, &err);
     if (err) {
         goto out;
@@ -131,7 +131,8 @@ def gen_marshal(name, arg_type, boxed, ret_type):
         goto out;
     }
 ''',
-                     c_name=arg_type.c_name())
+                     c_name=arg_type.c_name(),
+                     autocast='true' if autocast else 'false')

     else:
         ret += mcgen('''
@@ -215,7 +216,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
         self._visited_ret_types = None

     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, boxed):
+                      gen, success_response, boxed, autocast):
         if not gen:
             return
         self.decl += gen_command_decl(name, arg_type, boxed, ret_type)
@@ -224,7 +225,7 @@ class QAPISchemaGenCommandVisitor(QAPISchemaVisitor):
             self.defn += gen_marshal_output(ret_type)
         if middle_mode:
             self.decl += gen_marshal_decl(name)
-        self.defn += gen_marshal(name, arg_type, boxed, ret_type)
+        self.defn += gen_marshal(name, arg_type, boxed, autocast, ret_type)
         if not middle_mode:
             self._regy += gen_register_command(name, success_response)

diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py
index 60e9877..0e878e2 100644
--- a/scripts/qapi-introspect.py
+++ b/scripts/qapi-introspect.py
@@ -154,7 +154,7 @@ const char %(c_name)s[] = %(c_string)s;
                                     for m in variants.variants]})

     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, box):
+                      gen, success_response, box, autocast):
         arg_type = arg_type or self._schema.the_empty_object_type
         ret_type = ret_type or self._schema.the_empty_object_type
         self._gen_json(name, 'command',
diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
index 961015c..fc462bf 100644
--- a/docs/qapi-code-gen.txt
+++ b/docs/qapi-code-gen.txt
@@ -410,7 +410,7 @@ following example objects:
 === Commands ===

 Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
-         '*returns': TYPE-NAME, '*boxed': true,
+         '*returns': TYPE-NAME, '*boxed': true, '*autocast': true,
          '*gen': false, '*success-response': false }

 Commands are defined by using a dictionary containing several members,
@@ -475,6 +475,14 @@ arguments for the user's function out of an input QDict, 
calls the
 user's function, and if it succeeded, builds an output QObject from
 its return value.

+By default, the marshalling function enforces that the QDict built
+from the Client JSON Protocol is strictly typed (if the QAPI calls for
+an 'int', the client must have passed a JSON number).  But if the
+command definition includes a key 'autocast' with the boolean value
+true, the command will also accept JSON strings that can be intepreted
+as the corresponding scalar type.  Autocast should only be used for
+preserving backwards compatibility, and avoided on new commands.
+
 In rare cases, QAPI cannot express a type-safe representation of a
 corresponding Client JSON Protocol command.  You then have to suppress
 generation of a marshalling function by including a key 'gen' with
diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py
index ef74e2c..d5f675f 100644
--- a/tests/qapi-schema/test-qapi.py
+++ b/tests/qapi-schema/test-qapi.py
@@ -36,7 +36,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
         self._print_variants(variants)

     def visit_command(self, name, info, arg_type, ret_type,
-                      gen, success_response, boxed):
+                      gen, success_response, boxed, autocast):
         print 'command %s %s -> %s' % \
             (name, arg_type and arg_type.name, ret_type and ret_type.name)
         print '   gen=%s success_response=%s boxed=%s' % \
-- 
2.5.5




reply via email to

[Prev in Thread] Current Thread [Next in Thread]