This policy makes deprecated commands fail like this: ---> {"execute": "query-cpus"} <--- {"error": {"class": "CommandNotFound", "desc": "Deprecated command query-cpus disabled by policy"}}
When the command is removed, the error will change to <--- {"error": {"class": "CommandNotFound", "desc": "The command query-cpus has not been found"}} The policy thus permits "testing the future". Signed-off-by: Markus Armbruster <arm...@redhat.com> --- qapi/common.json | 4 ++-- include/qapi/qmp/dispatch.h | 1 + qapi/qmp-dispatch.c | 13 +++++++++++++ tests/test-qmp-cmds.c | 23 +++++++++++++++++++++++ qemu-options.hx | 4 +++- scripts/qapi/commands.py | 10 +++++++--- 6 files changed, 49 insertions(+), 6 deletions(-) diff --git a/qapi/common.json b/qapi/common.json index 9fc3e6400c..3e9d12c90f 100644 --- a/qapi/common.json +++ b/qapi/common.json @@ -151,7 +151,7 @@ # Policy for handling "funny" input. # # @accept: Accept silently -# TODO @reject: Reject with an error +# @reject: Reject with an error # TODO @crash: abort() the process # # FIXME Guidance on intended use. @@ -159,7 +159,7 @@ # Since: 4.2 ## { 'enum': 'CompatPolicyInput', - 'data': [ 'accept' ] } + 'data': [ 'accept', 'reject' ] } ## # @CompatPolicyOutput: diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 9aa426a398..ef256f2bb7 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -24,6 +24,7 @@ typedef enum QmpCommandOptions QCO_NO_SUCCESS_RESP = (1U << 0), QCO_ALLOW_OOB = (1U << 1), QCO_ALLOW_PRECONFIG = (1U << 2), + QCO_DEPRECATED = (1U << 3), } QmpCommandOptions; typedef struct QmpCommand diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 8fe59cf54d..b079db85d2 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -132,6 +132,19 @@ QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, "The command %s has not been found", command); goto out; } + if (cmd->options & QCO_DEPRECATED) { + switch (qapi_compat_policy.deprecated_input) { + case COMPAT_POLICY_INPUT_ACCEPT: + break; + case COMPAT_POLICY_INPUT_REJECT: + error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, + "Deprecated command %s disabled by policy", + command); + goto out; + default: + abort(); + } + } if (!cmd->enabled) { error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND, "The command %s has been disabled for this instance", diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index cf4fa1a091..005ea24a27 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "qapi/compat-policy.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qnum.h" @@ -235,6 +236,26 @@ static void test_dispatch_cmd_success_response(void) qobject_unref(req); } +static void test_dispatch_cmd_deprecated(void) +{ + const char *cmd = "{ 'execute': 'test-command-features1' }"; + QDict *ret; + + /* accept */ + ret = qobject_to(QDict, do_qmp_dispatch(false, cmd)); + assert(ret && qdict_size(ret) == 0); + qobject_unref(ret); + + qapi_compat_policy.has_deprecated_input = true; + qapi_compat_policy.deprecated_input = COMPAT_POLICY_INPUT_ACCEPT; + ret = qobject_to(QDict, do_qmp_dispatch(false, cmd)); + assert(ret && qdict_size(ret) == 0); + qobject_unref(ret); + + qapi_compat_policy.deprecated_input = COMPAT_POLICY_INPUT_REJECT; + do_qmp_dispatch_error(false, ERROR_CLASS_COMMAND_NOT_FOUND, cmd); +} + /* test commands that involve both input parameters and return values */ static void test_dispatch_cmd_io(void) { @@ -344,6 +365,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_success_response", test_dispatch_cmd_success_response); + g_test_add_func("/qmp/dispatch_cmd_deprecated", + test_dispatch_cmd_deprecated); g_test_add_func("/qmp/dealloc_types", test_dealloc_types); g_test_add_func("/qmp/dealloc_partial", test_dealloc_partial); diff --git a/qemu-options.hx b/qemu-options.hx index c43f768a15..f107a57c81 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3320,7 +3320,7 @@ STEXI ETEXI DEF("compat", HAS_ARG, QEMU_OPTION_compat, - "-compat [deprecated-input=accept][,deprecated-output=accept]\n" + "-compat [deprecated-input=accept|reject][,deprecated-output=accept]\n" " Policy for handling deprecated management interfaces\n", QEMU_ARCH_ALL) STEXI @@ -3331,6 +3331,8 @@ Set policy for handling deprecated management interfaces: @table @option @item deprecated-input=accept (default) Accept deprecated commands +@item deprecated-input=reject +Reject deprecated commands @item deprecated-output=accept (default) Emit deprecated events @end table diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 11e9a6c095..df2a132915 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -194,9 +194,13 @@ out: return ret -def gen_register_command(name, success_response, allow_oob, allow_preconfig): +def gen_register_command(name, features, + success_response, allow_oob, allow_preconfig): options = [] + if 'deprecated' in [f.name for f in features]: + options += ['QCO_DEPRECATED'] + if not success_response: options += ['QCO_NO_SUCCESS_RESP'] if allow_oob: @@ -295,8 +299,8 @@ void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds); self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type)) self._genh.add(gen_marshal_decl(name)) self._genc.add(gen_marshal(name, arg_type, boxed, ret_type)) - self._regy.add(gen_register_command(name, success_response, - allow_oob, allow_preconfig)) + self._regy.add(gen_register_command( + name, features, success_response, allow_oob, allow_preconfig)) def gen_commands(schema, output_dir, prefix): -- 2.21.0