On Fri, Jun 19, 2026 at 12:28:13PM +0200, Peter Krempa via Devel wrote:
> From: Peter Krempa <[email protected]>
> 
> 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'/>

This is again not especially friendly to non-C languages.

In both Golang and Rust bindings we don't have any concept of typed
parameters, and instead just have an extensible struct. So these
param names don't map to anything meaningful for the developer at the
Go/Rust level API

Is there an example of where knowing the typed parameters ahead
of time was needed / important ?


>       </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 <[email protected]>
> ---
>  scripts/genintrospection.py | 125 ++++++++++++++++++++++++++++++++++++
>  src/util/virintrospection.c |  45 +++++++++++++
>  src/util/virintrospection.h |   3 +
>  3 files changed, 173 insertions(+)
> 
> diff --git a/scripts/genintrospection.py b/scripts/genintrospection.py
> index c3ac5c940a..95469fc15a 100755
> --- 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,13 @@ 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")
> +
>          outfile.write("    },\n")
> 
>      epilogue = """    { .api = NULL }
> @@ -216,5 +333,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
> 

With regards,
Daniel
-- 
|: https://berrange.com       ~~        https://hachyderm.io/@berrange :|
|: https://libvirt.org          ~~          https://entangle-photo.org :|
|: https://pixelfed.art/berrange   ~~    https://fstop138.berrange.com :|

Reply via email to