Features/QCFG: Difference between revisions

From QEMU
(Created page with '== Summary == Replace QemuOpts infrastructure with code generation leveraging QAPI. == Owner == * '''Name:''' Anthony Liguori * '''Email:''' anthony@co…')
 
No edit summary
Line 21: Line 21:
             '*password': 'bool', '*tls': 'bool', '*sasl': 'bool' } }
             '*password': 'bool', '*tls': 'bool', '*sasl': 'bool' } }


No major code generation changes are needed since this is a normal QMP schema
We can generate an unmarshalling function that will convert a string of comma
typeWe just need to add a new type of command for configuration entries:
encoded options to a proper structureIt will generate the following code:


// qemu/qcfg-marshal.h
static VncConfig *qcfg_unmarshal_VncConfig(const char *value, Error **errp)
{
    VncConfig *config;
    Error *local_err = NULL;
    const char *ptr = value;
    char *key, *value;
    bool has_addr = false;
    config = qmp_alloc_VncConfig();
    while (qcfg_strsep(value, ',', &ptr, &key, &value)) {
        if (strequals(key, "addr")) {
            has_addr = true;
            config->addr = qcfg_unmarshal_str(value, &local_err);
        } else if (strequals(key, "x509")) {
            config->has_x509 = true;
            config->x509 = qcfg_unmarshal_str(value, &local_err);
        } else if (strequals(key, "x509verify")) {
            config->has_x509verify = true;
            config->x509verify = qcfg_unmarshal_str(value, &local_err);
        } else if (strequals(key, "password")) {
            config->has_password = true;
            config->password = qcfg_unmarshal_bool(value, &local_err);
        } else if (strequals(key, "tls")) {
            config->has_tls = true;
            config->tls = qcfg_unmarshal_bool(value, &local_err);
        } else if (strequals(key, "sasl")) {
            config->has_sasl = true;
            config->sasl = qcfg_unmarshal_bool(value, &local_err);
        } else {
            error_set(&local_err, QERR_INVALID_PARAMETER, key);
        }
        if (local_err) {
            goto out;
        }
    }
    if (!has_addr) {
        error_set(&local_err, QERR_MISSING_PARAMETER, "addr");
        goto out;
    }
    return config;
out:
    qmp_free_vnc_config(config);
    error_propagate(errp, local_err);
    return NULL;
}
There will still be the normal code generated in qmp-types and qmp-marshal-types
which means that this type can also be encoded and decode via QMP.
In order to bridge this type to a function that is called to process a command
line argument, we just need to add the following stanza:
// qemu/qcfg-schema.json
  { 'config': 'vnc', 'type': 'VncConfig' }
  { 'config': 'vnc', 'type': 'VncConfig' }


Which generates:
Which generates:


// qemu/qcfg.h
  void qcfg_vnc_config(VncConfig * config, Error **errp);
  void qcfg_vnc_config(VncConfig * config, Error **errp);
   
   
  static void qcfg_marshal_vnc(const QDict *args, Error **errp)
  static void qcfg_handle_vnc(const char *value, Error **errp)
  {
  {
     VncConfig *config;
     VncConfig *config;
     Error *local_err = NULL;
     Error *local_err = NULL;
   
   
     config = qmp_unmarshal_type_VncConfig(args, &local_err);
     config = qcfg_unmarshal_VncConfig(value, &local_err);
     if (local_err) {
     if (local_err) {
         error_propagate(errp, local_err);
         error_propagate(errp, local_err);
Line 43: Line 103:
     qcfg_vnc(config, errp);
     qcfg_vnc(config, errp);
   
   
     qmp_free_vnc_config(config);
     qmp_free_VncConfig(config);
  }
  }
   
   
Line 51: Line 111:
  }
  }


We just need to take a command line string, marshal it into a QDict.  The effect
The effect is that we can call a function that takes a string argument and
is automatic translation of ''-vnc :1'' into a strongly typed function call to
will decode that to a strongly typed call to a function.
a function ''qcfg_vnc''.


This function can also be reused by QMP trivially without going through multiple
This function can also be reused by QMP trivially without going through multiple
Line 72: Line 131:
             'chardev': 'CharDriverState' } }
             'chardev': 'CharDriverState' } }


This would translate to:
This would generate a standard qmp-types entry and corresponding marshalling
 
functions. This means the type is usuable via both QMP and through the
  typedef struct ISASerialConfig {
command line.  To generate a device factory interface, we just use the
    int64_t index;
following schema entry:
    int64_t iobase;
    int64_t irq;
    struct CharDriverState * chr
} ISASerialConfig;
 
This requires no modifications to the current code generation.  To use this in
qdev, we would add:


  { 'device': 'isa-serial', 'config': 'ISASerialConfig' }
  { 'device': 'isa-serial', 'config': 'ISASerialConfig' }


This would generate the following function:
This would generate the following code:


  DeviceState *qdev_ctor_isa_serial(ISASerialConfig *config, Error **errp);
  DeviceState *qdev_ctor_isa_serial(ISASerialConfig *config, Error **errp);
   
   
  static DeviceState *qdev_marshal_isa_serial(const QDict *args, Error **errp)
  static DeviceState *qdev_marshal_isa_serial(const char *value, Error **errp)
  {
  {
     ISASerialConfig *config;
     ISASerialConfig *config;
Line 96: Line 148:
     DeviceState *dev;
     DeviceState *dev;
   
   
     config = qmp_unmarshal_type_ISASerialConfig(args, &local_err);
     config = qcfg_unmarshal_ISASerialConfig(value, &local_err);
     if (local_err) {
     if (local_err) {
         error_propagate(errp, local_err);
         error_propagate(errp, local_err);
Line 109: Line 161:
  }
  }


This lets QMP invoke qdev constructors directly without going remarshaling to
This lets QMP invoke qdev constructors directly without remarshaling to
strings. It also provides a convenient interface for QEMU to consume internally
strings. It also provides a convenient interface for QEMU to consume internally
to create devices which is fully type safe.
to create devices which is fully type safe.

Revision as of 02:42, 12 March 2011

Summary

Replace QemuOpts infrastructure with code generation leveraging QAPI.

Owner

Detailed Summary

Use code generator to generate demarshalling routines for command line arguments into structures. Pass structures to function calls to process QEMU command line.

For instance, consider the -vnc option. We would first add a type corresponding to the options -vnc takes:

{ 'type': 'VncConfig',
  'data': { 'addr': 'str', '*x509': 'str', '*x509verify': 'str',
            '*password': 'bool', '*tls': 'bool', '*sasl': 'bool' } }

We can generate an unmarshalling function that will convert a string of comma encoded options to a proper structure. It will generate the following code:

// qemu/qcfg-marshal.h
static VncConfig *qcfg_unmarshal_VncConfig(const char *value, Error **errp)
{
    VncConfig *config;
    Error *local_err = NULL;
    const char *ptr = value;
    char *key, *value;
    bool has_addr = false;

    config = qmp_alloc_VncConfig();

    while (qcfg_strsep(value, ',', &ptr, &key, &value)) {
        if (strequals(key, "addr")) {
            has_addr = true;
            config->addr = qcfg_unmarshal_str(value, &local_err);
        } else if (strequals(key, "x509")) {
            config->has_x509 = true;
            config->x509 = qcfg_unmarshal_str(value, &local_err);
        } else if (strequals(key, "x509verify")) {
            config->has_x509verify = true;
            config->x509verify = qcfg_unmarshal_str(value, &local_err);
        } else if (strequals(key, "password")) {
            config->has_password = true;
            config->password = qcfg_unmarshal_bool(value, &local_err);
        } else if (strequals(key, "tls")) {
            config->has_tls = true;
            config->tls = qcfg_unmarshal_bool(value, &local_err);
        } else if (strequals(key, "sasl")) {
            config->has_sasl = true;
            config->sasl = qcfg_unmarshal_bool(value, &local_err);
        } else {
            error_set(&local_err, QERR_INVALID_PARAMETER, key);
        }

        if (local_err) {
            goto out;
        }
    }

    if (!has_addr) {
        error_set(&local_err, QERR_MISSING_PARAMETER, "addr");
        goto out;
    }

    return config;

out:
    qmp_free_vnc_config(config);
    error_propagate(errp, local_err);
    return NULL;
}

There will still be the normal code generated in qmp-types and qmp-marshal-types which means that this type can also be encoded and decode via QMP.

In order to bridge this type to a function that is called to process a command line argument, we just need to add the following stanza:

// qemu/qcfg-schema.json
{ 'config': 'vnc', 'type': 'VncConfig' }

Which generates:

// qemu/qcfg.h
void qcfg_vnc_config(VncConfig * config, Error **errp);

static void qcfg_handle_vnc(const char *value, Error **errp)
{
    VncConfig *config;
    Error *local_err = NULL;

    config = qcfg_unmarshal_VncConfig(value, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        return;
    }

    qcfg_vnc(config, errp);

    qmp_free_VncConfig(config);
}

void qcfg_init(void)
{
    qcfg_register_handler("vnc", &qcfg_marshal_vnc);
}

The effect is that we can call a function that takes a string argument and will decode that to a strongly typed call to a function.

This function can also be reused by QMP trivially without going through multiple levels of marshaling and unmarshaling.

This provides a consistent way to document all options including the parameters that they take. It also provides a consistent way to do introspection of command line arguments.

This mechanism can also be applied to qdev which will allow for strongly typed qdev initialization functions that can also be trivially called from within QEMU.

For instance, for the serial device, we would do:

{ 'type': 'ISASerialConfig',
  'data': { '*index': 'int', '*iobase': 'int', '*irq': 'int',
            'chardev': 'CharDriverState' } }

This would generate a standard qmp-types entry and corresponding marshalling functions. This means the type is usuable via both QMP and through the command line. To generate a device factory interface, we just use the following schema entry:

{ 'device': 'isa-serial', 'config': 'ISASerialConfig' }

This would generate the following code:

DeviceState *qdev_ctor_isa_serial(ISASerialConfig *config, Error **errp);

static DeviceState *qdev_marshal_isa_serial(const char *value, Error **errp)
{
    ISASerialConfig *config;
    Error *local_err = NULL;
    DeviceState *dev;

    config = qcfg_unmarshal_ISASerialConfig(value, &local_err);
    if (local_err) {
        error_propagate(errp, local_err);
        return;
    }

    dev = qdev_ctor_isa_serial(config, errp);

    qmp_free_isa_serial_config(config);

    return dev;
}

This lets QMP invoke qdev constructors directly without remarshaling to strings. It also provides a convenient interface for QEMU to consume internally to create devices which is fully type safe.

It also provides a mechanism to do full introspection of qdev and a consistent way to provide documentation.

Open Questions

  • Should we use a structure or marshal parameters as function calls?