Implement the XML parser and formatter for overriding of device
properties such as:
<qemu:override>
<qemu:device alias='ua-disk'>
<qemu:frontend>
<qemu:property name='prop1' type='string'
value='propval1'/>
<qemu:property name='prop2' type='signed'
value='-321'/>
<qemu:property name='prop3' type='unsigned'
value='123'/>
<qemu:property name='prop4' type='bool'
value='true'/>
<qemu:property name='prop5' type='bool'
value='false'/>
<qemu:property name='prop6' type='bool'
value='false'/>
<qemu:property name='prop6' type='remove'/>
</qemu:frontend>
</qemu:device>
</qemu:override>
Signed-off-by: Peter Krempa <pkrempa(a)redhat.com>
---
src/conf/schemas/domaincommon.rng | 42 ++++
src/qemu/qemu_domain.c | 218 ++++++++++++++++++
src/qemu/qemu_domain.h | 33 +++
.../qemu-ns.x86_64-4.0.0.args | 2 +-
.../qemu-ns.x86_64-latest.args | 2 +-
tests/qemuxml2argvdata/qemu-ns.xml | 14 ++
.../qemu-ns.x86_64-latest.xml | 14 ++
7 files changed, 323 insertions(+), 2 deletions(-)
diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng
index 9c1b64a644..8ca6bee81f 100644
--- a/src/conf/schemas/domaincommon.rng
+++ b/src/conf/schemas/domaincommon.rng
@@ -80,6 +80,9 @@
<optional>
<ref name="qemudeprecation"/>
</optional>
+ <optional>
+ <ref name="qemuoverride"/>
+ </optional>
<optional>
<ref name="lxcsharens"/>
</optional>
@@ -7553,6 +7556,45 @@
</element>
</define>
+ <define name="qemuoverrideproperty">
+ <element name="property"
ns="http://libvirt.org/schemas/domain/qemu/1.0">
+ <attribute name="name"/>
+ <attribute name="type">
+ <choice>
+ <value>string</value>
+ <value>signed</value>
+ <value>unsigned</value>
+ <value>bool</value>
+ <value>remove</value>
+ </choice>
+ </attribute>
+ <optional>
+ <attribute name="value"/>
+ </optional>
+ </element>
+ </define>
+
+ <define name="qemuoverride">
+ <element name="override"
ns="http://libvirt.org/schemas/domain/qemu/1.0">
+ <interleave>
+ <zeroOrMore>
+ <element name="device">
+ <attribute name="alias"/>
+ <interleave>
+ <optional>
+ <element name="frontend">
+ <zeroOrMore>
+ <ref name="qemuoverrideproperty"/>
+ </zeroOrMore>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </zeroOrMore>
+ </interleave>
+ </element>
+ </define>
+
<!--
Optional hypervisor extensions in their own namespace:
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 3bf864bc5d..636e58061e 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -3181,6 +3181,23 @@ qemuDomainXmlNsDefFree(qemuDomainXmlNsDef *def)
g_free(def->deprecationBehavior);
+ for (i = 0; i < def->ndeviceOverride; i++) {
+ size_t j;
+ g_free(def->deviceOverride[i].alias);
+
+ for (j = 0; j < def->deviceOverride[i].nfrontend; j++) {
+ qemuDomainXmlNsOverrideProperty *prop = def->deviceOverride[i].frontend +
j;
+
+ g_free(prop->name);
+ g_free(prop->value);
+ virJSONValueFree(prop->json);
+ }
+
+ g_free(def->deviceOverride[i].frontend);
+ }
+
+ g_free(def->deviceOverride);
+
g_free(def);
}
@@ -3319,6 +3336,159 @@ qemuDomainDefNamespaceParseCaps(qemuDomainXmlNsDef *nsdef,
return 0;
}
+VIR_ENUM_IMPL(qemuDomainXmlNsOverride,
+ QEMU_DOMAIN_XML_NS_OVERRIDE_LAST,
+ "",
+ "string",
+ "signed",
+ "unsigned",
+ "bool",
+ "remove",
+ );
+
+
+static int
+qemuDomainDefNamespaceParseOverrideProperties(qemuDomainXmlNsOverrideProperty *props,
+ xmlNodePtr *propnodes,
+ size_t npropnodes)
+{
+ size_t i;
+
+ for (i = 0; i < npropnodes; i++) {
+ qemuDomainXmlNsOverrideProperty *prop = props + i;
+ unsigned long long ull;
+ long long ll;
+ bool b;
+
+ if (!(prop->name = virXMLPropString(propnodes[i], "name"))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing 'name' attribute for
qemu:property"));
+ return -1;
+ }
+
+ if (STREQ(prop->name, "id")) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("property with name 'id' can't be
overriden"));
+ return -1;
+ }
+
+ if (virXMLPropEnum(propnodes[i], "type",
+ qemuDomainXmlNsOverrideTypeFromString,
+ VIR_XML_PROP_REQUIRED | VIR_XML_PROP_NONZERO,
+ &prop->type) < 0)
+ return -1;
+
+ if (!(prop->value = virXMLPropString(propnodes[i], "value"))) {
+ if (prop->type != QEMU_DOMAIN_XML_NS_OVERRIDE_REMOVE) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing 'value' attribute for
'qemu:property'"));
+ return -1;
+ }
+ }
+
+ switch (prop->type) {
+ case QEMU_DOMAIN_XML_NS_OVERRIDE_STRING:
+ prop->json = virJSONValueNewString(g_strdup(prop->value));
+ break;
+
+ case QEMU_DOMAIN_XML_NS_OVERRIDE_SIGNED:
+ if (virStrToLong_ll(prop->value, NULL, 10, &ll) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("invalid value '%s' of 'value'
attribute of 'qemu:property'"),
+ prop->value);
+ return -1;
+ }
+ prop->json = virJSONValueNewNumberLong(ll);
+ break;
+
+ case QEMU_DOMAIN_XML_NS_OVERRIDE_UNSIGNED:
+ if (virStrToLong_ullp(prop->value, NULL, 10, &ull) < 0) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("invalid value '%s' of 'value'
attribute of 'qemu:property'"),
+ prop->value);
+ return -1;
+ }
+ prop->json = virJSONValueNewNumberUlong(ull);
+ break;
+
+ case QEMU_DOMAIN_XML_NS_OVERRIDE_BOOL:
+ if (STRNEQ(prop->value, "true") &&
STRNEQ(prop->value, "false")) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("invalid value '%s' of 'value'
attribute of 'qemu:property'"),
+ prop->value);
+ return -1;
+ }
+
+ b = STREQ(prop->value, "true");
+ prop->json = virJSONValueNewBoolean(b);
+ break;
+
+ case QEMU_DOMAIN_XML_NS_OVERRIDE_REMOVE:
+ if (prop->value) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("setting 'value' attribute of
'qemu:property' doesn't make sense with 'remove' type"));
+ return -1;
+ }
+ break;
+
+ case QEMU_DOMAIN_XML_NS_OVERRIDE_NONE:
+ case QEMU_DOMAIN_XML_NS_OVERRIDE_LAST:
+ virReportEnumRangeError(qemuDomainXmlNsOverrideType, prop->type);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainDefNamespaceParseDeviceOverride(qemuDomainXmlNsDef *nsdef,
+ xmlXPathContextPtr ctxt)
+{
+ g_autofree xmlNodePtr *devicenodes = NULL;
+ ssize_t ndevicenodes;
+ size_t i;
+
+ if ((ndevicenodes = virXPathNodeSet("./qemu:override/qemu:device", ctxt,
&devicenodes)) < 0)
+ return -1;
+
+ if (ndevicenodes == 0)
+ return 0;
+
+ nsdef->deviceOverride = g_new0(qemuDomainXmlNsDeviceOverride, ndevicenodes);
+ nsdef->ndeviceOverride = ndevicenodes;
+
+ for (i = 0; i < ndevicenodes; i++) {
+ qemuDomainXmlNsDeviceOverride *n = nsdef->deviceOverride + i;
+ g_autofree xmlNodePtr *propnodes = NULL;
+ ssize_t npropnodes;
+ VIR_XPATH_NODE_AUTORESTORE(ctxt);
+
+ ctxt->node = devicenodes[i];
+
+ if (!(n->alias = virXMLPropString(devicenodes[i], "alias"))) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("missing 'alias' attribute for
qemu:device"));
+ return -1;
+ }
+
+ if ((npropnodes = virXPathNodeSet("./qemu:frontend/qemu:property",
ctxt, &propnodes)) < 0)
+ return -1;
+
+ if (npropnodes == 0)
+ continue;
+
+ n->frontend = g_new0(qemuDomainXmlNsOverrideProperty, npropnodes);
+ n->nfrontend = npropnodes;
+
+ if (qemuDomainDefNamespaceParseOverrideProperties(n->frontend, propnodes,
npropnodes) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
static int
qemuDomainDefNamespaceParse(xmlXPathContextPtr ctxt,
@@ -3331,6 +3501,7 @@ qemuDomainDefNamespaceParse(xmlXPathContextPtr ctxt,
if (qemuDomainDefNamespaceParseCommandlineArgs(nsdata, ctxt) < 0 ||
qemuDomainDefNamespaceParseCommandlineEnv(nsdata, ctxt) < 0 ||
+ qemuDomainDefNamespaceParseDeviceOverride(nsdata, ctxt) < 0 ||
qemuDomainDefNamespaceParseCaps(nsdata, ctxt) < 0)
goto cleanup;
@@ -3386,6 +3557,52 @@ qemuDomainDefNamespaceFormatXMLCaps(virBuffer *buf,
}
+static void
+qemuDomainDefNamespaceFormatXMLOverrideProperties(virBuffer *buf,
+ qemuDomainXmlNsOverrideProperty
*props,
+ size_t nprops)
+{
+ size_t i;
+
+ for (i = 0; i < nprops; i++) {
+ g_auto(virBuffer) propAttrBuf = VIR_BUFFER_INITIALIZER;
+ qemuDomainXmlNsOverrideProperty *prop = props + i;
+
+ virBufferEscapeString(&propAttrBuf, " name='%s'",
prop->name);
+ virBufferAsprintf(&propAttrBuf, " type='%s'",
+ qemuDomainXmlNsOverrideTypeToString(prop->type));
+ virBufferEscapeString(&propAttrBuf, " value='%s'",
prop->value);
+
+ virXMLFormatElement(buf, "qemu:property", &propAttrBuf, NULL);
+ }
+}
+
+
+static void
+qemuDomainDefNamespaceFormatXMLOverride(virBuffer *buf,
+ qemuDomainXmlNsDef *xmlns)
+{
+ g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
+ size_t i;
+
+ for (i = 0; i < xmlns->ndeviceOverride; i++) {
+ qemuDomainXmlNsDeviceOverride *device = xmlns->deviceOverride + i;
+ g_auto(virBuffer) deviceAttrBuf = VIR_BUFFER_INITIALIZER;
+ g_auto(virBuffer) deviceChildBuf = VIR_BUFFER_INIT_CHILD(&childBuf);
+ g_auto(virBuffer) frontendChildBuf = VIR_BUFFER_INIT_CHILD(&deviceChildBuf);
+
+ virBufferEscapeString(&deviceAttrBuf, " alias='%s'",
device->alias);
+
+ qemuDomainDefNamespaceFormatXMLOverrideProperties(&frontendChildBuf,
device->frontend, device->nfrontend);
+ virXMLFormatElement(&deviceChildBuf, "qemu:frontend", NULL,
&frontendChildBuf);
+
+ virXMLFormatElement(&childBuf, "qemu:device", &deviceAttrBuf,
&deviceChildBuf);
+ }
+
+ virXMLFormatElement(buf, "qemu:override", NULL, &childBuf);
+}
+
+
static int
qemuDomainDefNamespaceFormatXML(virBuffer *buf,
void *nsdata)
@@ -3394,6 +3611,7 @@ qemuDomainDefNamespaceFormatXML(virBuffer *buf,
qemuDomainDefNamespaceFormatXMLCommandline(buf, cmd);
qemuDomainDefNamespaceFormatXMLCaps(buf, cmd);
+ qemuDomainDefNamespaceFormatXMLOverride(buf, cmd);
virBufferEscapeString(buf, "<qemu:deprecation
behavior='%s'/>\n",
cmd->deprecationBehavior);
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index edafb585b3..85adb6a5ba 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -443,6 +443,36 @@ struct _qemuDomainXmlNsEnvTuple {
char *value;
};
+
+typedef enum {
+ QEMU_DOMAIN_XML_NS_OVERRIDE_NONE,
+ QEMU_DOMAIN_XML_NS_OVERRIDE_STRING,
+ QEMU_DOMAIN_XML_NS_OVERRIDE_SIGNED,
+ QEMU_DOMAIN_XML_NS_OVERRIDE_UNSIGNED,
+ QEMU_DOMAIN_XML_NS_OVERRIDE_BOOL,
+ QEMU_DOMAIN_XML_NS_OVERRIDE_REMOVE,
+
+ QEMU_DOMAIN_XML_NS_OVERRIDE_LAST
+} qemuDomainXmlNsOverrideType;
+VIR_ENUM_DECL(qemuDomainXmlNsOverride);
+
+typedef struct _qemuDomainXmlNsOverrideProperty qemuDomainXmlNsOverrideProperty;
+struct _qemuDomainXmlNsOverrideProperty {
+ char *name;
+ qemuDomainXmlNsOverrideType type;
+ char *value;
+ virJSONValue *json;
+};
+
+typedef struct _qemuDomainXmlNsDeviceOverride qemuDomainXmlNsDeviceOverride;
+struct _qemuDomainXmlNsDeviceOverride {
+ char *alias;
+
+ size_t nfrontend;
+ qemuDomainXmlNsOverrideProperty *frontend;
+};
+
+
typedef struct _qemuDomainXmlNsDef qemuDomainXmlNsDef;
struct _qemuDomainXmlNsDef {
char **args;
@@ -458,6 +488,9 @@ struct _qemuDomainXmlNsDef {
* starting the VM to avoid any form of errors in the parser or when
* changing qemu versions. The knob is mainly for development/CI purposes */
char *deprecationBehavior;
+
+ size_t ndeviceOverride;
+ qemuDomainXmlNsDeviceOverride *deviceOverride;
};
diff --git a/tests/qemuxml2argvdata/qemu-ns.x86_64-4.0.0.args
b/tests/qemuxml2argvdata/qemu-ns.x86_64-4.0.0.args
index 5105f410ce..236f984a90 100644
--- a/tests/qemuxml2argvdata/qemu-ns.x86_64-4.0.0.args
+++ b/tests/qemuxml2argvdata/qemu-ns.x86_64-4.0.0.args
@@ -31,7 +31,7 @@ BAR='' \
-device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 \
-blockdev
'{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest1","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}'
\
-blockdev
'{"node-name":"libvirt-1-format","read-only":false,"driver":"raw","file":"libvirt-1-storage"}'
\
--device ide-hd,bus=ide.0,unit=0,drive=libvirt-1-format,id=ide0-0-0,bootindex=1 \
+-device ide-hd,bus=ide.0,unit=0,drive=libvirt-1-format,id=ua-disk,bootindex=1 \
-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x2 \
-unknown parameter \
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
diff --git a/tests/qemuxml2argvdata/qemu-ns.x86_64-latest.args
b/tests/qemuxml2argvdata/qemu-ns.x86_64-latest.args
index 05ccade7e4..c0bf45000f 100644
--- a/tests/qemuxml2argvdata/qemu-ns.x86_64-latest.args
+++ b/tests/qemuxml2argvdata/qemu-ns.x86_64-latest.args
@@ -33,7 +33,7 @@ BAR='' \
-device
'{"driver":"piix3-usb-uhci","id":"usb","bus":"pci.0","addr":"0x1.0x2"}'
\
-blockdev
'{"driver":"host_device","filename":"/dev/HostVG/QEMUGuest1","node-name":"libvirt-1-storage","auto-read-only":true,"discard":"unmap"}'
\
-blockdev
'{"node-name":"libvirt-1-format","read-only":false,"driver":"raw","file":"libvirt-1-storage"}'
\
--device
'{"driver":"ide-hd","bus":"ide.0","unit":0,"drive":"libvirt-1-format","id":"ide0-0-0","bootindex":1}'
\
+-device
'{"driver":"ide-hd","bus":"ide.0","unit":0,"drive":"libvirt-1-format","id":"ua-disk","bootindex":1}'
\
-audiodev
'{"id":"audio1","driver":"none"}' \
-device
'{"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.0","addr":"0x2"}'
\
-unknown parameter \
diff --git a/tests/qemuxml2argvdata/qemu-ns.xml b/tests/qemuxml2argvdata/qemu-ns.xml
index 9d2f5502e9..36bf582dec 100644
--- a/tests/qemuxml2argvdata/qemu-ns.xml
+++ b/tests/qemuxml2argvdata/qemu-ns.xml
@@ -17,6 +17,7 @@
<disk type='block' device='disk'>
<source dev='/dev/HostVG/QEMUGuest1'/>
<target dev='hda' bus='ide'/>
+ <alias name='ua-disk'/>
<address type='drive' controller='0' bus='0'
target='0' unit='0'/>
</disk>
<controller type='ide' index='0'/>
@@ -32,5 +33,18 @@
<qemu:add capability="blockdev"/>
<qemu:del capability="name"/>
</qemu:capabilities>
+ <qemu:override>
+ <qemu:device alias='ua-disk'>
+ <qemu:frontend>
+ <qemu:property name='prop1' type='string'
value='propval1'/>
+ <qemu:property name='prop2' type='signed'
value='-321'/>
+ <qemu:property name='prop3' type='unsigned'
value='123'/>
+ <qemu:property name='prop4' type='bool'
value='true'/>
+ <qemu:property name='prop5' type='bool'
value='false'/>
+ <qemu:property name='prop6' type='bool'
value='false'/>
+ <qemu:property name='prop6' type='remove'/>
+ </qemu:frontend>
+ </qemu:device>
+ </qemu:override>
<qemu:deprecation behavior='crash'/>
</domain>
diff --git a/tests/qemuxml2xmloutdata/qemu-ns.x86_64-latest.xml
b/tests/qemuxml2xmloutdata/qemu-ns.x86_64-latest.xml
index 674525d033..a8998ae582 100644
--- a/tests/qemuxml2xmloutdata/qemu-ns.x86_64-latest.xml
+++ b/tests/qemuxml2xmloutdata/qemu-ns.x86_64-latest.xml
@@ -21,6 +21,7 @@
<driver name='qemu' type='raw'/>
<source dev='/dev/HostVG/QEMUGuest1'/>
<target dev='hda' bus='ide'/>
+ <alias name='ua-disk'/>
<address type='drive' controller='0' bus='0'
target='0' unit='0'/>
</disk>
<controller type='ide' index='0'>
@@ -48,5 +49,18 @@
<qemu:add capability='blockdev'/>
<qemu:del capability='name'/>
</qemu:capabilities>
+ <qemu:override>
+ <qemu:device alias='ua-disk'>
+ <qemu:frontend>
+ <qemu:property name='prop1' type='string'
value='propval1'/>
+ <qemu:property name='prop2' type='signed'
value='-321'/>
+ <qemu:property name='prop3' type='unsigned'
value='123'/>
+ <qemu:property name='prop4' type='bool'
value='true'/>
+ <qemu:property name='prop5' type='bool'
value='false'/>
+ <qemu:property name='prop6' type='bool'
value='false'/>
+ <qemu:property name='prop6' type='remove'/>
+ </qemu:frontend>
+ </qemu:device>
+ </qemu:override>
<qemu:deprecation behavior='crash'/>
</domain>
--
2.35.1