Skip to content

Commit

Permalink
qapi: introduce new cmd option "allow-preconfig"
Browse files Browse the repository at this point in the history
New option will be used to allow commands, which are prepared/need
to run, during preconfig state. Other commands that should be able
to run in preconfig state, should be amended to not expect machine
in initialized state or deal with it.

For compatibility reasons, commands that don't use new flag
'allow-preconfig' explicitly are not permitted to run in
preconfig state but allowed in all other states like they used
to be.

Within this patch allow following commands in preconfig state:
   qmp_capabilities
   query-qmp-schema
   query-commands
   query-command-line-options
   query-status
   exit-preconfig
to allow qmp connection, basic introspection and moving to the next
state.

PS:
set-numa-node and query-hotpluggable-cpus will be enabled later in
a separate patches.

Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Message-Id: <1526057503-39287-1-git-send-email-imammedo@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[ehabkost: Changed "since 2.13" to "since 3.0"]
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
  • Loading branch information
Igor Mammedov authored and ehabkost committed May 30, 2018
1 parent 71dc578 commit d6fe3d0
Show file tree
Hide file tree
Showing 11 changed files with 51 additions and 27 deletions.
11 changes: 10 additions & 1 deletion docs/devel/qapi-code-gen.txt
Expand Up @@ -559,7 +559,7 @@ following example objects:
Usage: { 'command': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
'*returns': TYPE-NAME, '*boxed': true,
'*gen': false, '*success-response': false,
'*allow-oob': true }
'*allow-oob': true, '*allow-preconfig': true }

Commands are defined by using a dictionary containing several members,
where three members are most common. The 'command' member is a
Expand Down Expand Up @@ -683,6 +683,15 @@ OOB command handlers must satisfy the following conditions:

If in doubt, do not implement OOB execution support.

A command may use the optional 'allow-preconfig' key to permit its execution
at early runtime configuration stage (preconfig runstate).
If not specified then a command defaults to 'allow-preconfig': false.

An example of declaring a command that is enabled during preconfig:
{ 'command': 'qmp_capabilities',
'data': { '*enable': [ 'QMPCapability' ] },
'allow-preconfig': true }

=== Events ===

Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
Expand Down
1 change: 1 addition & 0 deletions include/qapi/qmp/dispatch.h
Expand Up @@ -23,6 +23,7 @@ typedef enum QmpCommandOptions
QCO_NO_OPTIONS = 0x0,
QCO_NO_SUCCESS_RESP = (1U << 0),
QCO_ALLOW_OOB = (1U << 1),
QCO_ALLOW_PRECONFIG = (1U << 2),
} QmpCommandOptions;

typedef struct QmpCommand
Expand Down
5 changes: 2 additions & 3 deletions monitor.c
Expand Up @@ -1179,8 +1179,7 @@ static void monitor_init_qmp_commands(void)
qmp_init_marshal(&qmp_commands);

qmp_register_command(&qmp_commands, "query-qmp-schema",
qmp_query_qmp_schema,
QCO_NO_OPTIONS);
qmp_query_qmp_schema, QCO_ALLOW_PRECONFIG);
qmp_register_command(&qmp_commands, "device_add", qmp_device_add,
QCO_NO_OPTIONS);
qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add,
Expand All @@ -1190,7 +1189,7 @@ static void monitor_init_qmp_commands(void)

QTAILQ_INIT(&qmp_cap_negotiation_commands);
qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS);
qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
}

static bool qmp_cap_enabled(Monitor *mon, QMPCapability cap)
Expand Down
5 changes: 4 additions & 1 deletion qapi/introspect.json
Expand Up @@ -262,13 +262,16 @@
# @allow-oob: whether the command allows out-of-band execution.
# (Since: 2.12)
#
# @allow-preconfig: command can be executed in preconfig runstate,
# default: false (Since 3.0)
#
# 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', 'allow-preconfig': 'bool' } }

##
# @SchemaInfoEvent:
Expand Down
9 changes: 6 additions & 3 deletions qapi/misc.json
Expand Up @@ -37,7 +37,8 @@
#
##
{ 'command': 'qmp_capabilities',
'data': { '*enable': [ 'QMPCapability' ] } }
'data': { '*enable': [ 'QMPCapability' ] },
'allow-preconfig': true }

##
# @QMPCapability:
Expand Down Expand Up @@ -155,7 +156,8 @@
# Note: This example has been shortened as the real response is too long.
#
##
{ 'command': 'query-commands', 'returns': ['CommandInfo'] }
{ 'command': 'query-commands', 'returns': ['CommandInfo'],
'allow-preconfig': true }

##
# @LostTickPolicy:
Expand Down Expand Up @@ -2648,7 +2650,8 @@
#
##
{'command': 'query-command-line-options', 'data': { '*option': 'str' },
'returns': ['CommandLineOptionInfo'] }
'returns': ['CommandLineOptionInfo'],
'allow-preconfig': true }

##
# @X86CPURegister32:
Expand Down
3 changes: 2 additions & 1 deletion qapi/run-state.json
Expand Up @@ -94,7 +94,8 @@
# "status": "running" } }
#
##
{ 'command': 'query-status', 'returns': 'StatusInfo' }
{ 'command': 'query-status', 'returns': 'StatusInfo',
'allow-preconfig': true }

##
# @SHUTDOWN:
Expand Down
11 changes: 7 additions & 4 deletions scripts/qapi/commands.py
Expand Up @@ -193,13 +193,15 @@ def gen_marshal(name, arg_type, boxed, ret_type):
return ret


def gen_register_command(name, success_response, allow_oob):
def gen_register_command(name, success_response, allow_oob, allow_preconfig):
options = []

if not success_response:
options += ['QCO_NO_SUCCESS_RESP']
if allow_oob:
options += ['QCO_ALLOW_OOB']
if allow_preconfig:
options += ['QCO_ALLOW_PRECONFIG']

if not options:
options = ['QCO_NO_OPTIONS']
Expand Down Expand Up @@ -275,8 +277,8 @@ def visit_end(self):
c_prefix=c_name(self._prefix, protect=False)))
genc.add(gen_registry(self._regy, self._prefix))

def visit_command(self, name, info, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
def visit_command(self, name, info, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
if not gen:
return
self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type))
Expand All @@ -285,7 +287,8 @@ def visit_command(self, name, info, arg_type, ret_type,
self._genc.add(gen_marshal_output(ret_type))
self._genh.add(gen_marshal_decl(name))
self._genc.add(gen_marshal(name, arg_type, boxed, ret_type))
self._regy += gen_register_command(name, success_response, allow_oob)
self._regy += gen_register_command(name, success_response, allow_oob,
allow_preconfig)


def gen_commands(schema, output_dir, prefix):
Expand Down
18 changes: 11 additions & 7 deletions scripts/qapi/common.py
Expand Up @@ -872,7 +872,8 @@ def check_keys(expr_elem, meta, required, optional=[]):
raise QAPISemError(info,
"'%s' of %s '%s' should only use false value"
% (key, meta, name))
if (key == 'boxed' or key == 'allow-oob') and value is not True:
if (key == 'boxed' or key == 'allow-oob' or
key == 'allow-preconfig') and value is not True:
raise QAPISemError(info,
"'%s' of %s '%s' should only use true value"
% (key, meta, name))
Expand Down Expand Up @@ -922,7 +923,7 @@ def check_exprs(exprs):
meta = 'command'
check_keys(expr_elem, 'command', [],
['data', 'returns', 'gen', 'success-response',
'boxed', 'allow-oob'])
'boxed', 'allow-oob', 'allow-preconfig'])
elif 'event' in expr:
meta = 'event'
check_keys(expr_elem, 'event', [], ['data', 'boxed'])
Expand Down Expand Up @@ -1044,8 +1045,8 @@ def visit_object_type_flat(self, name, info, members, variants):
def visit_alternate_type(self, name, info, variants):
pass

def visit_command(self, name, info, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
def visit_command(self, name, info, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
pass

def visit_event(self, name, info, arg_type, boxed):
Expand Down Expand Up @@ -1422,7 +1423,7 @@ def is_empty(self):

class QAPISchemaCommand(QAPISchemaEntity):
def __init__(self, name, info, doc, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
gen, success_response, boxed, allow_oob, allow_preconfig):
QAPISchemaEntity.__init__(self, name, info, doc)
assert not arg_type or isinstance(arg_type, str)
assert not ret_type or isinstance(ret_type, str)
Expand All @@ -1434,6 +1435,7 @@ def __init__(self, name, info, doc, arg_type, ret_type,
self.success_response = success_response
self.boxed = boxed
self.allow_oob = allow_oob
self.allow_preconfig = allow_preconfig

def check(self, schema):
if self._arg_type_name:
Expand All @@ -1458,7 +1460,8 @@ 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.allow_oob)
self.boxed, self.allow_oob,
self.allow_preconfig)


class QAPISchemaEvent(QAPISchemaEntity):
Expand Down Expand Up @@ -1678,6 +1681,7 @@ def _def_command(self, expr, info, doc):
success_response = expr.get('success-response', True)
boxed = expr.get('boxed', False)
allow_oob = expr.get('allow-oob', False)
allow_preconfig = expr.get('allow-preconfig', False)
if isinstance(data, OrderedDict):
data = self._make_implicit_object_type(
name, info, doc, 'arg', self._make_members(data, info))
Expand All @@ -1686,7 +1690,7 @@ def _def_command(self, expr, info, doc):
rets = self._make_array_type(rets[0], info)
self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
gen, success_response,
boxed, allow_oob))
boxed, allow_oob, allow_preconfig))

def _def_event(self, expr, info, doc):
name = expr['event']
Expand Down
4 changes: 2 additions & 2 deletions scripts/qapi/doc.py
Expand Up @@ -226,8 +226,8 @@ def visit_alternate_type(self, name, info, variants):
name=doc.symbol,
body=texi_entity(doc, 'Members')))

def visit_command(self, name, info, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
def visit_command(self, name, info, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
doc = self.cur_doc
if boxed:
body = texi_body(doc)
Expand Down
7 changes: 4 additions & 3 deletions scripts/qapi/introspect.py
Expand Up @@ -171,14 +171,15 @@ def visit_alternate_type(self, name, info, variants):
{'members': [{'type': self._use_type(m.type)}
for m in variants.variants]})

def visit_command(self, name, info, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
def visit_command(self, name, info, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
arg_type = arg_type or self._schema.the_empty_object_type
ret_type = ret_type or self._schema.the_empty_object_type
self._gen_qlit(name, 'command',
{'arg-type': self._use_type(arg_type),
'ret-type': self._use_type(ret_type),
'allow-oob': allow_oob})
'allow-oob': allow_oob,
'allow-preconfig': allow_preconfig})

def visit_event(self, name, info, arg_type, boxed):
arg_type = arg_type or self._schema.the_empty_object_type
Expand Down
4 changes: 2 additions & 2 deletions tests/qapi-schema/test-qapi.py
Expand Up @@ -41,8 +41,8 @@ def visit_alternate_type(self, name, info, variants):
print('alternate %s' % name)
self._print_variants(variants)

def visit_command(self, name, info, arg_type, ret_type,
gen, success_response, boxed, allow_oob):
def visit_command(self, name, info, arg_type, ret_type, gen,
success_response, boxed, allow_oob, allow_preconfig):
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 oob=%s' % \
Expand Down

0 comments on commit d6fe3d0

Please sign in to comment.