Features/QCFG: Difference between revisions
(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' } } | ||
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' } | { '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 | static void qcfg_handle_vnc(const char *value, Error **errp) | ||
{ | { | ||
VncConfig *config; | VncConfig *config; | ||
Error *local_err = NULL; | Error *local_err = NULL; | ||
config = | 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_VncConfig(config); | |||
} | } | ||
Line 51: | Line 111: | ||
} | } | ||
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. | |||
a function | |||
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 | 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: | |||
This | |||
{ 'device': 'isa-serial', 'config': 'ISASerialConfig' } | { 'device': 'isa-serial', 'config': 'ISASerialConfig' } | ||
This would generate the following | 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 | static DeviceState *qdev_marshal_isa_serial(const char *value, Error **errp) | ||
{ | { | ||
ISASerialConfig *config; | ISASerialConfig *config; | ||
Line 96: | Line 148: | ||
DeviceState *dev; | DeviceState *dev; | ||
config = | 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 | 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
- Name: Anthony Liguori
- Email: anthony@codemonkey.ws
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?