d6fe3d02e9
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>
193 lines
6.6 KiB
Python
193 lines
6.6 KiB
Python
"""
|
|
QAPI introspection generator
|
|
|
|
Copyright (C) 2015-2018 Red Hat, Inc.
|
|
|
|
Authors:
|
|
Markus Armbruster <armbru@redhat.com>
|
|
|
|
This work is licensed under the terms of the GNU GPL, version 2.
|
|
See the COPYING file in the top-level directory.
|
|
"""
|
|
|
|
from qapi.common import *
|
|
|
|
|
|
def to_qlit(obj, level=0, suppress_first_indent=False):
|
|
|
|
def indent(level):
|
|
return level * 4 * ' '
|
|
|
|
ret = ''
|
|
if not suppress_first_indent:
|
|
ret += indent(level)
|
|
if obj is None:
|
|
ret += 'QLIT_QNULL'
|
|
elif isinstance(obj, str):
|
|
ret += 'QLIT_QSTR(' + to_c_string(obj) + ')'
|
|
elif isinstance(obj, list):
|
|
elts = [to_qlit(elt, level + 1)
|
|
for elt in obj]
|
|
elts.append(indent(level + 1) + "{}")
|
|
ret += 'QLIT_QLIST(((QLitObject[]) {\n'
|
|
ret += ',\n'.join(elts) + '\n'
|
|
ret += indent(level) + '}))'
|
|
elif isinstance(obj, dict):
|
|
elts = []
|
|
for key, value in sorted(obj.items()):
|
|
elts.append(indent(level + 1) + '{ %s, %s }' %
|
|
(to_c_string(key), to_qlit(value, level + 1, True)))
|
|
elts.append(indent(level + 1) + '{}')
|
|
ret += 'QLIT_QDICT(((QLitDictEntry[]) {\n'
|
|
ret += ',\n'.join(elts) + '\n'
|
|
ret += indent(level) + '}))'
|
|
elif isinstance(obj, bool):
|
|
ret += 'QLIT_QBOOL(%s)' % ('true' if obj else 'false')
|
|
else:
|
|
assert False # not implemented
|
|
return ret
|
|
|
|
|
|
def to_c_string(string):
|
|
return '"' + string.replace('\\', r'\\').replace('"', r'\"') + '"'
|
|
|
|
|
|
class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor):
|
|
|
|
def __init__(self, prefix, unmask):
|
|
QAPISchemaMonolithicCVisitor.__init__(
|
|
self, prefix, 'qapi-introspect',
|
|
' * QAPI/QMP schema introspection', __doc__)
|
|
self._unmask = unmask
|
|
self._schema = None
|
|
self._qlits = []
|
|
self._used_types = []
|
|
self._name_map = {}
|
|
self._genc.add(mcgen('''
|
|
#include "qemu/osdep.h"
|
|
#include "%(prefix)sqapi-introspect.h"
|
|
|
|
''',
|
|
prefix=prefix))
|
|
|
|
def visit_begin(self, schema):
|
|
self._schema = schema
|
|
|
|
def visit_end(self):
|
|
# visit the types that are actually used
|
|
qlits = self._qlits
|
|
self._qlits = []
|
|
for typ in self._used_types:
|
|
typ.visit(self)
|
|
# generate C
|
|
# TODO can generate awfully long lines
|
|
qlits.extend(self._qlits)
|
|
name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit'
|
|
self._genh.add(mcgen('''
|
|
#include "qapi/qmp/qlit.h"
|
|
|
|
extern const QLitObject %(c_name)s;
|
|
''',
|
|
c_name=c_name(name)))
|
|
self._genc.add(mcgen('''
|
|
const QLitObject %(c_name)s = %(c_string)s;
|
|
''',
|
|
c_name=c_name(name),
|
|
c_string=to_qlit(qlits)))
|
|
self._schema = None
|
|
self._qlits = []
|
|
self._used_types = []
|
|
self._name_map = {}
|
|
|
|
def visit_needed(self, entity):
|
|
# Ignore types on first pass; visit_end() will pick up used types
|
|
return not isinstance(entity, QAPISchemaType)
|
|
|
|
def _name(self, name):
|
|
if self._unmask:
|
|
return name
|
|
if name not in self._name_map:
|
|
self._name_map[name] = '%d' % len(self._name_map)
|
|
return self._name_map[name]
|
|
|
|
def _use_type(self, typ):
|
|
# Map the various integer types to plain int
|
|
if typ.json_type() == 'int':
|
|
typ = self._schema.lookup_type('int')
|
|
elif (isinstance(typ, QAPISchemaArrayType) and
|
|
typ.element_type.json_type() == 'int'):
|
|
typ = self._schema.lookup_type('intList')
|
|
# Add type to work queue if new
|
|
if typ not in self._used_types:
|
|
self._used_types.append(typ)
|
|
# Clients should examine commands and events, not types. Hide
|
|
# type names to reduce the temptation. Also saves a few
|
|
# characters.
|
|
if isinstance(typ, QAPISchemaBuiltinType):
|
|
return typ.name
|
|
if isinstance(typ, QAPISchemaArrayType):
|
|
return '[' + self._use_type(typ.element_type) + ']'
|
|
return self._name(typ.name)
|
|
|
|
def _gen_qlit(self, name, mtype, obj):
|
|
if mtype not in ('command', 'event', 'builtin', 'array'):
|
|
name = self._name(name)
|
|
obj['name'] = name
|
|
obj['meta-type'] = mtype
|
|
self._qlits.append(obj)
|
|
|
|
def _gen_member(self, member):
|
|
ret = {'name': member.name, 'type': self._use_type(member.type)}
|
|
if member.optional:
|
|
ret['default'] = None
|
|
return ret
|
|
|
|
def _gen_variants(self, tag_name, variants):
|
|
return {'tag': tag_name,
|
|
'variants': [self._gen_variant(v) for v in variants]}
|
|
|
|
def _gen_variant(self, variant):
|
|
return {'case': variant.name, 'type': self._use_type(variant.type)}
|
|
|
|
def visit_builtin_type(self, name, info, json_type):
|
|
self._gen_qlit(name, 'builtin', {'json-type': json_type})
|
|
|
|
def visit_enum_type(self, name, info, values, prefix):
|
|
self._gen_qlit(name, 'enum', {'values': values})
|
|
|
|
def visit_array_type(self, name, info, element_type):
|
|
element = self._use_type(element_type)
|
|
self._gen_qlit('[' + element + ']', 'array', {'element-type': element})
|
|
|
|
def visit_object_type_flat(self, name, info, members, variants):
|
|
obj = {'members': [self._gen_member(m) for m in members]}
|
|
if variants:
|
|
obj.update(self._gen_variants(variants.tag_member.name,
|
|
variants.variants))
|
|
self._gen_qlit(name, 'object', obj)
|
|
|
|
def visit_alternate_type(self, name, info, variants):
|
|
self._gen_qlit(name, 'alternate',
|
|
{'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, 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-preconfig': allow_preconfig})
|
|
|
|
def visit_event(self, name, info, arg_type, boxed):
|
|
arg_type = arg_type or self._schema.the_empty_object_type
|
|
self._gen_qlit(name, 'event', {'arg-type': self._use_type(arg_type)})
|
|
|
|
|
|
def gen_introspect(schema, output_dir, prefix, opt_unmask):
|
|
vis = QAPISchemaGenIntrospectVisitor(prefix, opt_unmask)
|
|
schema.visit(vis)
|
|
vis.write(output_dir)
|