From: Peter Krempa <pkrempa@redhat.com> Extract information about typed parameters from calls to 'virTypedParamsValidate'/'virTypedParamsValidateTemplate' and expose them in the introspection XML: <api name='virDomainMigrate3'> <flags dec='2097151' hex='0x1fffff'/> <typed-parameters type='input' name='params'> <param name='migrate_uri' type='string'/> <param name='destination_name' type='string'/> <param name='destination_xml' type='string'/> <param name='bandwidth' type='ullong'/> <param name='graphics_uri' type='string'/> <param name='listen_address' type='string'/> <param name='migrate_disks' type='string' multiple='yes'/> <param name='migrate_disks_detect_zeroes' type='string' multiple='yes'/> <param name='migrate_disks_target_zero' type='string' multiple='yes'/> <param name='disks_port' type='int'/> <param name='compression' type='string' multiple='yes'/> <param name='compression.mt.level' type='int'/> <param name='compression.mt.threads' type='int'/> <param name='compression.mt.dthreads' type='int'/> <param name='compression.xbzrle.cache' type='ullong'/> <param name='persistent_xml' type='string'/> <param name='auto_converge.initial' type='int'/> <param name='auto_converge.increment' type='int'/> <param name='bandwidth.postcopy' type='ullong'/> <param name='parallel.connections' type='int'/> <param name='compression.zlib.level' type='int'/> <param name='compression.zstd.level' type='int'/> <param name='tls.destination' type='string'/> <param name='disks_uri' type='string'/> <param name='bandwidth.avail.switchover' type='ullong'/> </typed-parameters> </api> Migration APIs once again required special handling as some typed params are supported even if the backing APIs using typed parameters arend supported because they can be converted to legacy parameters for the lesser APIs. Signed-off-by: Peter Krempa <pkrempa@redhat.com> --- scripts/genintrospection.py | 127 ++++++++++++++++++++++++++++++++++++ src/util/virintrospection.c | 45 +++++++++++++ src/util/virintrospection.h | 3 + 3 files changed, 175 insertions(+) diff --git a/scripts/genintrospection.py b/scripts/genintrospection.py index 25a17e2b0c..ac51634d10 100644 --- a/scripts/genintrospection.py +++ b/scripts/genintrospection.py @@ -7,6 +7,21 @@ import re import sys +# APIs which fill a user-supplied virTypedParameter pointer -- use it as output +input_params_exceptions = [ + "virDomainGetBlkioParameters", + "virDomainGetMemoryParameters", + "virDomainGetNumaParameters", + "virDomainGetSchedulerParametersFlags", + "virDomainGetSchedulerParameters", + "virDomainBlockStatsFlags", + "virDomainGetInterfaceParameters", + "virDomainGetBlockIoTune", + "virDomainGetCPUStats", + "virNodeGetMemoryParameters", +] + + # driver callbacks needed to infer the introspection for public migration APIs # which do not map directly to any driver API callback. We need a representative # sample of APIs supporting typed parameters and flags @@ -27,6 +42,15 @@ migration_public_api = { "virDomainMigrateToURI3": {"params": True}, } +# list of parameters supported by 'virDomainMigrate3'/'virDomainMigrateToURI3' +# if any migration protocol version is supported by extracting the parameters +migration_public_api_fallback_params = [ + ("VIR_MIGRATE_PARAM_URI", "VIR_TYPED_PARAM_STRING"), + ("VIR_MIGRATE_PARAM_DEST_NAME", "VIR_TYPED_PARAM_STRING"), + ("VIR_MIGRATE_PARAM_DEST_XML", "VIR_TYPED_PARAM_STRING"), + ("VIR_MIGRATE_PARAM_BANDWIDTH", "VIR_TYPED_PARAM_ULLONG"), +] + def load_public_symbols(filename): """load the public symbol file and return all APIs""" @@ -121,8 +145,13 @@ def parse_api(filename, syms): "callback": f.group("name"), "flags_arg": False, "flags_supported": None, + "input_params": False, + "input_params_supported": None, + "input_params_template": None, } + # find APIs having 'flags' argument and find the corresponding + # virCheckFlags flagarg = re.search(r"\bflags\b", f.group("args")) if flagarg: data["flags_arg"] = True @@ -136,6 +165,54 @@ def parse_api(filename, syms): if flagcheck: data["flags_supported"] = re.sub(r"\s+", " ", flagcheck.group("flags")) + # find APIs supporting typed parameters as input and find the + # corresponding supported flags by matching 'virTypedParamsValidate' + # We're looking for APIs which take typed parameters as input, which + # excludes any API taking a double-pointer. From the rest + # the 'input_params_exceptions' array has APIs which output typed + # parameters into a pre-allocated array and thus are excluded + paramarg = re.search( + r"virTypedParameter(?P<ptrtype>Ptr)?(?P<ptrstar>[ *]+)", f.group("args") + ) + param_nptrs = 0 + + if paramarg: + if paramarg.group("ptrtype"): + param_nptrs += 1 + + for c in paramarg.group("ptrstar"): + if c == "*": + param_nptrs += 1 + + if param_nptrs == 1 and apiname not in input_params_exceptions: + data["input_params"] = True + + paramscheck = re.search( + r"virTypedParamsValidate(?P<template>Template)?(?:Deferred)?\([^,]+,\s*[^,]+,\s*(?P<params>[^)]+)\)", + f.group("impl"), + flags=re.DOTALL | re.MULTILINE, + ) + + if paramscheck: + if paramscheck.group("template"): + data["input_params_template"] = paramscheck.group("params") + + else: + data["input_params_supported"] = [] + + idx = 0 + params = re.sub(r"\s+", "", paramscheck.group("params")).split(",") + + while idx < len(params): + if params[idx] == "NULL": + break + + data["input_params_supported"].append( + (params[idx], params[idx + 1]) + ) + + idx += 2 + if migrationapi: migr_data[apiname] = data else: @@ -148,6 +225,18 @@ def parse_api(filename, syms): if m in migr_data: for apiname, pub in migration_public_api.items(): data = migr_data[m].copy() + + if pub.get("params", False): + if not data["input_params"]: + data["input_params"] = True + data["input_params_supported"] = ( + migration_public_api_fallback_params + ) + else: + data["input_params"] = False + data["input_params_supported"] = None + data["input_params_template"] = None + flagmap[apiname] = data break @@ -184,6 +273,27 @@ with open(args.output, "w") as outfile: """ ) + for api in sorted(introspection.keys()): + data = introspection[api] + + if data.get("input_params_supported", None) is None: + continue + + outfile.write( + f""" +const virTypedParamValidationTemplate {data["callback"]}InputParamValidation[] = {{ +""" + ) + + for param, flag in data["input_params_supported"]: + outfile.write(f" {{ {param}, {flag} }},\n") + + outfile.write( + """ { "", 0 } +}; +""" + ) + outfile.write( """ static const virIntrospectionData driver_api_introspection[] = @@ -201,6 +311,15 @@ static const virIntrospectionData driver_api_introspection[] = else: outfile.write(" .flags_arg = false,\n") + if data.get("input_params_supported", None) is not None: + outfile.write( + f" .input_params = {data["callback"]}InputParamValidation,\n" + ) + elif data.get("input_params_template", None) is not None: + outfile.write(f" .input_params = {data["input_params_template"]},\n") + else: + outfile.write(" .input_params = NULL,\n") + outfile.write(" },\n") epilogue = """ { .api = NULL } @@ -216,5 +335,13 @@ for api, data in introspection.items(): print(f"failed to parse flags for '{api}' in '{data.get("callback", "")}'") fail = True + if data.get("input_params", False) is True and ( + data.get("input_params_supported", None) is None + and data.get("input_params_template", None) is None + ): + print( + f"failed to parse typed params for '{api}' in '{data.get("callback", "")}'" + ) + fail = True if fail: sys.exit(1) diff --git a/src/util/virintrospection.c b/src/util/virintrospection.c index b9fb5beda8..2862e60924 100644 --- a/src/util/virintrospection.c +++ b/src/util/virintrospection.c @@ -8,6 +8,24 @@ #include "virxml.h" #include "virbuffer.h" +/* These strings are exported int he XML */ +VIR_ENUM_DECL(virIntrospectionTypedParam); + +VIR_ENUM_IMPL(virIntrospectionTypedParam, + VIR_TYPED_PARAM_UNSIGNED + 1, + "", + "int", + "uint", + "llong", + "ullong", + "double", + "boolean", + "string", + "", /* VIR_TYPED_PARAM_LAST */ + "ullong", /* VIR_TYPED_PARAM_UNSIGNED */ +); + + char * virIntrospectionGetXML(const virIntrospectionData *d) { @@ -27,6 +45,33 @@ virIntrospectionGetXML(const virIntrospectionData *d) d[i].flags, d[i].flags); } + if (d[i].input_params) { + g_auto(virBuffer) typedparam_attr = VIR_BUFFER_INITIALIZER; + g_auto(virBuffer) typedparam_elem = VIR_BUFFER_INIT_CHILD(&api_elem); + size_t j; + + virBufferAddLit(&typedparam_attr, " type='input' name='params'"); + + for (j = 0; d[i].input_params[j].name[0] != '\0'; j++) { + unsigned int typeval = d[i].input_params[j].typeflags & ~VIR_TYPED_PARAM_MULTIPLE; + const char *type = virIntrospectionTypedParamTypeToString(typeval); + + virBufferAsprintf(&typedparam_elem, "<param name='%s'", + d[i].input_params[j].name); + + if (type && type[0] != '\0') + virBufferAsprintf(&typedparam_elem, " type='%s'", type); + + if (d[i].input_params[j].typeflags & VIR_TYPED_PARAM_MULTIPLE) + virBufferAddLit(&typedparam_elem, " multiple='yes'"); + + virBufferAddLit(&typedparam_elem, "/>\n"); + } + + virXMLFormatElement(&api_elem, "typed-parameters", + &typedparam_attr, &typedparam_elem); + } + virXMLFormatElement(&apis, "api", &api_attr, &api_elem); } diff --git a/src/util/virintrospection.h b/src/util/virintrospection.h index f996ce0f07..8d259e36ce 100644 --- a/src/util/virintrospection.h +++ b/src/util/virintrospection.h @@ -6,10 +6,13 @@ #include <stdbool.h> +#include "virtypedparam.h" + struct _virIntrospectionData { const char *api; bool flags_arg; unsigned int flags; + const virTypedParamValidationTemplate *input_params; }; typedef struct _virIntrospectionData virIntrospectionData; -- 2.54.0