qapi: Implement deprecated-output=hide for QMP command results
This policy suppresses deprecated bits in output, and thus permits "testing the future". Implement it for QMP command results. Example: when QEMU is run with -compat deprecated-output=hide, then {"execute": "query-cpus-fast"} yields {"return": [{"thread-id": 9805, "props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "qom-path": "/machine/unattached/device[0]", "cpu-index": 0, "target": "x86_64"}]} instead of {"return": [{"arch": "x86", "thread-id": 22436, "props": {"core-id": 0, "thread-id": 0, "socket-id": 0}, "qom-path": "/machine/unattached/device[0]", "cpu-index": 0, "target": "x86_64"}]} Note the suppression of deprecated member "arch". Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-Id: <20210318155519.1224118-4-armbru@redhat.com>
This commit is contained in:
parent
6dd75472d5
commit
91fa93e516
|
@ -17,4 +17,13 @@
|
||||||
|
|
||||||
extern CompatPolicy compat_policy;
|
extern CompatPolicy compat_policy;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a QObject output visitor for @obj for use with QMP
|
||||||
|
*
|
||||||
|
* This is like qobject_output_visitor_new(), except it obeys the
|
||||||
|
* policy for handling deprecated management interfaces set with
|
||||||
|
* -compat.
|
||||||
|
*/
|
||||||
|
Visitor *qobject_output_visitor_new_qmp(QObject **result);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#define QOBJECT_OUTPUT_VISITOR_H
|
#define QOBJECT_OUTPUT_VISITOR_H
|
||||||
|
|
||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
|
#include "qapi/qapi-types-compat.h"
|
||||||
|
|
||||||
typedef struct QObjectOutputVisitor QObjectOutputVisitor;
|
typedef struct QObjectOutputVisitor QObjectOutputVisitor;
|
||||||
|
|
||||||
|
@ -53,4 +54,7 @@ typedef struct QObjectOutputVisitor QObjectOutputVisitor;
|
||||||
*/
|
*/
|
||||||
Visitor *qobject_output_visitor_new(QObject **result);
|
Visitor *qobject_output_visitor_new(QObject **result);
|
||||||
|
|
||||||
|
void qobject_output_visitor_set_policy(Visitor *v,
|
||||||
|
CompatPolicyOutput deprecated);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -113,6 +113,9 @@ struct Visitor
|
||||||
The core takes care of the return type in the public interface. */
|
The core takes care of the return type in the public interface. */
|
||||||
void (*optional)(Visitor *v, const char *name, bool *present);
|
void (*optional)(Visitor *v, const char *name, bool *present);
|
||||||
|
|
||||||
|
/* Optional */
|
||||||
|
bool (*deprecated)(Visitor *v, const char *name);
|
||||||
|
|
||||||
/* Must be set */
|
/* Must be set */
|
||||||
VisitorType type;
|
VisitorType type;
|
||||||
|
|
||||||
|
|
|
@ -459,6 +459,15 @@ void visit_end_alternate(Visitor *v, void **obj);
|
||||||
*/
|
*/
|
||||||
bool visit_optional(Visitor *v, const char *name, bool *present);
|
bool visit_optional(Visitor *v, const char *name, bool *present);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Should we visit deprecated member @name?
|
||||||
|
*
|
||||||
|
* @name must not be NULL. This function is only useful between
|
||||||
|
* visit_start_struct() and visit_end_struct(), since only objects
|
||||||
|
* have deprecated members.
|
||||||
|
*/
|
||||||
|
bool visit_deprecated(Visitor *v, const char *name);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Visit an enum value.
|
* Visit an enum value.
|
||||||
*
|
*
|
||||||
|
|
|
@ -135,6 +135,15 @@ bool visit_optional(Visitor *v, const char *name, bool *present)
|
||||||
return *present;
|
return *present;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit_deprecated(Visitor *v, const char *name)
|
||||||
|
{
|
||||||
|
trace_visit_deprecated(v, name);
|
||||||
|
if (v->deprecated) {
|
||||||
|
return v->deprecated(v, name);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool visit_is_input(Visitor *v)
|
bool visit_is_input(Visitor *v)
|
||||||
{
|
{
|
||||||
return v->type == VISITOR_INPUT;
|
return v->type == VISITOR_INPUT;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "qapi/qmp/dispatch.h"
|
#include "qapi/qmp/dispatch.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qjson.h"
|
#include "qapi/qmp/qjson.h"
|
||||||
|
#include "qapi/qobject-output-visitor.h"
|
||||||
#include "sysemu/runstate.h"
|
#include "sysemu/runstate.h"
|
||||||
#include "qapi/qmp/qbool.h"
|
#include "qapi/qmp/qbool.h"
|
||||||
#include "qemu/coroutine.h"
|
#include "qemu/coroutine.h"
|
||||||
|
@ -26,6 +27,14 @@
|
||||||
|
|
||||||
CompatPolicy compat_policy;
|
CompatPolicy compat_policy;
|
||||||
|
|
||||||
|
Visitor *qobject_output_visitor_new_qmp(QObject **result)
|
||||||
|
{
|
||||||
|
Visitor *v = qobject_output_visitor_new(result);
|
||||||
|
|
||||||
|
qobject_output_visitor_set_policy(v, compat_policy.deprecated_output);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob,
|
static QDict *qmp_dispatch_check_obj(QDict *dict, bool allow_oob,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qapi/compat-policy.h"
|
||||||
#include "qapi/qobject-output-visitor.h"
|
#include "qapi/qobject-output-visitor.h"
|
||||||
#include "qapi/visitor-impl.h"
|
#include "qapi/visitor-impl.h"
|
||||||
#include "qemu/queue.h"
|
#include "qemu/queue.h"
|
||||||
|
@ -31,6 +32,8 @@ typedef struct QStackEntry {
|
||||||
|
|
||||||
struct QObjectOutputVisitor {
|
struct QObjectOutputVisitor {
|
||||||
Visitor visitor;
|
Visitor visitor;
|
||||||
|
CompatPolicyOutput deprecated_policy;
|
||||||
|
|
||||||
QSLIST_HEAD(, QStackEntry) stack; /* Stack of unfinished containers */
|
QSLIST_HEAD(, QStackEntry) stack; /* Stack of unfinished containers */
|
||||||
QObject *root; /* Root of the output visit */
|
QObject *root; /* Root of the output visit */
|
||||||
QObject **result; /* User's storage location for result */
|
QObject **result; /* User's storage location for result */
|
||||||
|
@ -207,6 +210,13 @@ static bool qobject_output_type_null(Visitor *v, const char *name,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool qobject_output_deprecated(Visitor *v, const char *name)
|
||||||
|
{
|
||||||
|
QObjectOutputVisitor *qov = to_qov(v);
|
||||||
|
|
||||||
|
return qov->deprecated_policy != COMPAT_POLICY_OUTPUT_HIDE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Finish building, and return the root object.
|
/* Finish building, and return the root object.
|
||||||
* The root object is never null. The caller becomes the object's
|
* The root object is never null. The caller becomes the object's
|
||||||
* owner, and should use qobject_unref() when done with it. */
|
* owner, and should use qobject_unref() when done with it. */
|
||||||
|
@ -256,6 +266,7 @@ Visitor *qobject_output_visitor_new(QObject **result)
|
||||||
v->visitor.type_number = qobject_output_type_number;
|
v->visitor.type_number = qobject_output_type_number;
|
||||||
v->visitor.type_any = qobject_output_type_any;
|
v->visitor.type_any = qobject_output_type_any;
|
||||||
v->visitor.type_null = qobject_output_type_null;
|
v->visitor.type_null = qobject_output_type_null;
|
||||||
|
v->visitor.deprecated = qobject_output_deprecated;
|
||||||
v->visitor.complete = qobject_output_complete;
|
v->visitor.complete = qobject_output_complete;
|
||||||
v->visitor.free = qobject_output_free;
|
v->visitor.free = qobject_output_free;
|
||||||
|
|
||||||
|
@ -264,3 +275,11 @@ Visitor *qobject_output_visitor_new(QObject **result)
|
||||||
|
|
||||||
return &v->visitor;
|
return &v->visitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qobject_output_visitor_set_policy(Visitor *v,
|
||||||
|
CompatPolicyOutput deprecated)
|
||||||
|
{
|
||||||
|
QObjectOutputVisitor *qov = to_qov(v);
|
||||||
|
|
||||||
|
qov->deprecated_policy = deprecated;
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ visit_start_alternate(void *v, const char *name, void *obj, size_t size) "v=%p n
|
||||||
visit_end_alternate(void *v, void *obj) "v=%p obj=%p"
|
visit_end_alternate(void *v, void *obj) "v=%p obj=%p"
|
||||||
|
|
||||||
visit_optional(void *v, const char *name, bool *present) "v=%p name=%s present=%p"
|
visit_optional(void *v, const char *name, bool *present) "v=%p name=%s present=%p"
|
||||||
|
visit_deprecated(void *v, const char *name) "v=%p name=%s"
|
||||||
|
|
||||||
visit_type_enum(void *v, const char *name, int *obj) "v=%p name=%s obj=%p"
|
visit_type_enum(void *v, const char *name, int *obj) "v=%p name=%s obj=%p"
|
||||||
visit_type_int(void *v, const char *name, int64_t *obj) "v=%p name=%s obj=%p"
|
visit_type_int(void *v, const char *name, int64_t *obj) "v=%p name=%s obj=%p"
|
||||||
|
|
|
@ -96,7 +96,7 @@ static void qmp_marshal_output_%(c_name)s(%(c_type)s ret_in,
|
||||||
{
|
{
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
|
|
||||||
v = qobject_output_visitor_new(ret_out);
|
v = qobject_output_visitor_new_qmp(ret_out);
|
||||||
if (visit_type_%(c_name)s(v, "unused", &ret_in, errp)) {
|
if (visit_type_%(c_name)s(v, "unused", &ret_in, errp)) {
|
||||||
visit_complete(v, ret_out);
|
visit_complete(v, ret_out);
|
||||||
}
|
}
|
||||||
|
@ -251,9 +251,9 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
|
||||||
visit = self._module_basename('qapi-visit', name)
|
visit = self._module_basename('qapi-visit', name)
|
||||||
self._genc.add(mcgen('''
|
self._genc.add(mcgen('''
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qapi/compat-policy.h"
|
||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qobject-output-visitor.h"
|
|
||||||
#include "qapi/qobject-input-visitor.h"
|
#include "qapi/qobject-input-visitor.h"
|
||||||
#include "qapi/dealloc-visitor.h"
|
#include "qapi/dealloc-visitor.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
|
|
@ -77,6 +77,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
||||||
c_type=base.c_name())
|
c_type=base.c_name())
|
||||||
|
|
||||||
for memb in members:
|
for memb in members:
|
||||||
|
deprecated = 'deprecated' in [f.name for f in memb.features]
|
||||||
ret += gen_if(memb.ifcond)
|
ret += gen_if(memb.ifcond)
|
||||||
if memb.optional:
|
if memb.optional:
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
|
@ -84,6 +85,12 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
||||||
''',
|
''',
|
||||||
name=memb.name, c_name=c_name(memb.name))
|
name=memb.name, c_name=c_name(memb.name))
|
||||||
indent.increase()
|
indent.increase()
|
||||||
|
if deprecated:
|
||||||
|
ret += mcgen('''
|
||||||
|
if (visit_deprecated(v, "%(name)s")) {
|
||||||
|
''',
|
||||||
|
name=memb.name)
|
||||||
|
indent.increase()
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) {
|
if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -91,6 +98,11 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
|
||||||
''',
|
''',
|
||||||
c_type=memb.type.c_name(), name=memb.name,
|
c_type=memb.type.c_name(), name=memb.name,
|
||||||
c_name=c_name(memb.name))
|
c_name=c_name(memb.name))
|
||||||
|
if deprecated:
|
||||||
|
indent.decrease()
|
||||||
|
ret += mcgen('''
|
||||||
|
}
|
||||||
|
''')
|
||||||
if memb.optional:
|
if memb.optional:
|
||||||
indent.decrease()
|
indent.decrease()
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
|
|
|
@ -299,14 +299,15 @@
|
||||||
'features': [ 'feature1' ] }
|
'features': [ 'feature1' ] }
|
||||||
|
|
||||||
{ 'command': 'test-features0',
|
{ 'command': 'test-features0',
|
||||||
'data': { 'fs0': 'FeatureStruct0',
|
'data': { '*fs0': 'FeatureStruct0',
|
||||||
'fs1': 'FeatureStruct1',
|
'*fs1': 'FeatureStruct1',
|
||||||
'fs2': 'FeatureStruct2',
|
'*fs2': 'FeatureStruct2',
|
||||||
'fs3': 'FeatureStruct3',
|
'*fs3': 'FeatureStruct3',
|
||||||
'fs4': 'FeatureStruct4',
|
'*fs4': 'FeatureStruct4',
|
||||||
'cfs1': 'CondFeatureStruct1',
|
'*cfs1': 'CondFeatureStruct1',
|
||||||
'cfs2': 'CondFeatureStruct2',
|
'*cfs2': 'CondFeatureStruct2',
|
||||||
'cfs3': 'CondFeatureStruct3' },
|
'*cfs3': 'CondFeatureStruct3' },
|
||||||
|
'returns': 'FeatureStruct1',
|
||||||
'features': [] }
|
'features': [] }
|
||||||
|
|
||||||
{ 'command': 'test-command-features1',
|
{ 'command': 'test-command-features1',
|
||||||
|
|
|
@ -409,15 +409,15 @@ alternate FeatureAlternate1
|
||||||
case eins: FeatureStruct1
|
case eins: FeatureStruct1
|
||||||
feature feature1
|
feature feature1
|
||||||
object q_obj_test-features0-arg
|
object q_obj_test-features0-arg
|
||||||
member fs0: FeatureStruct0 optional=False
|
member fs0: FeatureStruct0 optional=True
|
||||||
member fs1: FeatureStruct1 optional=False
|
member fs1: FeatureStruct1 optional=True
|
||||||
member fs2: FeatureStruct2 optional=False
|
member fs2: FeatureStruct2 optional=True
|
||||||
member fs3: FeatureStruct3 optional=False
|
member fs3: FeatureStruct3 optional=True
|
||||||
member fs4: FeatureStruct4 optional=False
|
member fs4: FeatureStruct4 optional=True
|
||||||
member cfs1: CondFeatureStruct1 optional=False
|
member cfs1: CondFeatureStruct1 optional=True
|
||||||
member cfs2: CondFeatureStruct2 optional=False
|
member cfs2: CondFeatureStruct2 optional=True
|
||||||
member cfs3: CondFeatureStruct3 optional=False
|
member cfs3: CondFeatureStruct3 optional=True
|
||||||
command test-features0 q_obj_test-features0-arg -> None
|
command test-features0 q_obj_test-features0-arg -> FeatureStruct1
|
||||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||||
command test-command-features1 None -> None
|
command test-command-features1 None -> None
|
||||||
gen=True success_response=True boxed=False oob=False preconfig=False
|
gen=True success_response=True boxed=False oob=False preconfig=False
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
|
#include "qapi/compat-policy.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
#include "qapi/qmp/qjson.h"
|
#include "qapi/qmp/qjson.h"
|
||||||
#include "qapi/qmp/qnum.h"
|
#include "qapi/qmp/qnum.h"
|
||||||
|
@ -49,12 +50,17 @@ void qmp_user_def_cmd1(UserDefOne * ud1, Error **errp)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_test_features0(FeatureStruct0 *fs0, FeatureStruct1 *fs1,
|
FeatureStruct1 *qmp_test_features0(bool has_fs0, FeatureStruct0 *fs0,
|
||||||
FeatureStruct2 *fs2, FeatureStruct3 *fs3,
|
bool has_fs1, FeatureStruct1 *fs1,
|
||||||
FeatureStruct4 *fs4, CondFeatureStruct1 *cfs1,
|
bool has_fs2, FeatureStruct2 *fs2,
|
||||||
CondFeatureStruct2 *cfs2, CondFeatureStruct3 *cfs3,
|
bool has_fs3, FeatureStruct3 *fs3,
|
||||||
Error **errp)
|
bool has_fs4, FeatureStruct4 *fs4,
|
||||||
|
bool has_cfs1, CondFeatureStruct1 *cfs1,
|
||||||
|
bool has_cfs2, CondFeatureStruct2 *cfs2,
|
||||||
|
bool has_cfs3, CondFeatureStruct3 *cfs3,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
return g_new0(FeatureStruct1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_test_command_features1(Error **errp)
|
void qmp_test_command_features1(Error **errp)
|
||||||
|
@ -275,6 +281,30 @@ static void test_dispatch_cmd_io(void)
|
||||||
qobject_unref(ret3);
|
qobject_unref(ret3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_dispatch_cmd_ret_deprecated(void)
|
||||||
|
{
|
||||||
|
const char *cmd = "{ 'execute': 'test-features0' }";
|
||||||
|
QDict *ret;
|
||||||
|
|
||||||
|
memset(&compat_policy, 0, sizeof(compat_policy));
|
||||||
|
|
||||||
|
/* default accept */
|
||||||
|
ret = qobject_to(QDict, do_qmp_dispatch(false, cmd));
|
||||||
|
assert(ret && qdict_size(ret) == 1);
|
||||||
|
qobject_unref(ret);
|
||||||
|
|
||||||
|
compat_policy.has_deprecated_output = true;
|
||||||
|
compat_policy.deprecated_output = COMPAT_POLICY_OUTPUT_ACCEPT;
|
||||||
|
ret = qobject_to(QDict, do_qmp_dispatch(false, cmd));
|
||||||
|
assert(ret && qdict_size(ret) == 1);
|
||||||
|
qobject_unref(ret);
|
||||||
|
|
||||||
|
compat_policy.deprecated_output = COMPAT_POLICY_OUTPUT_HIDE;
|
||||||
|
ret = qobject_to(QDict, do_qmp_dispatch(false, cmd));
|
||||||
|
assert(ret && qdict_size(ret) == 0);
|
||||||
|
qobject_unref(ret);
|
||||||
|
}
|
||||||
|
|
||||||
/* test generated dealloc functions for generated types */
|
/* test generated dealloc functions for generated types */
|
||||||
static void test_dealloc_types(void)
|
static void test_dealloc_types(void)
|
||||||
{
|
{
|
||||||
|
@ -349,6 +379,8 @@ int main(int argc, char **argv)
|
||||||
g_test_add_func("/qmp/dispatch_cmd_io", test_dispatch_cmd_io);
|
g_test_add_func("/qmp/dispatch_cmd_io", test_dispatch_cmd_io);
|
||||||
g_test_add_func("/qmp/dispatch_cmd_success_response",
|
g_test_add_func("/qmp/dispatch_cmd_success_response",
|
||||||
test_dispatch_cmd_success_response);
|
test_dispatch_cmd_success_response);
|
||||||
|
g_test_add_func("/qmp/dispatch_cmd_ret_deprecated",
|
||||||
|
test_dispatch_cmd_ret_deprecated);
|
||||||
g_test_add_func("/qmp/dealloc_types", test_dealloc_types);
|
g_test_add_func("/qmp/dealloc_types", test_dealloc_types);
|
||||||
g_test_add_func("/qmp/dealloc_partial", test_dealloc_partial);
|
g_test_add_func("/qmp/dealloc_partial", test_dealloc_partial);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue