mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-02-22 03:12:22 +00:00
Allow custom metadata in domain configuration XML
Applications can now insert custom nodes and hierarchies into domain configuration XML. Although currently not enforced, applications are required to use their own namespaces on every custom node they insert, with only one top-level element per namespace.
This commit is contained in:
parent
d19149dda8
commit
fa981fc945
1
AUTHORS
1
AUTHORS
@ -217,6 +217,7 @@ Patches have also been contributed by:
|
|||||||
Deepak C Shetty <deepakcs@linux.vnet.ibm.com>
|
Deepak C Shetty <deepakcs@linux.vnet.ibm.com>
|
||||||
Martin Kletzander <mkletzan@redhat.com>
|
Martin Kletzander <mkletzan@redhat.com>
|
||||||
Laszlo Ersek <lersek@redhat.com>
|
Laszlo Ersek <lersek@redhat.com>
|
||||||
|
Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
|
||||||
|
|
||||||
[....send patches to get your name here....]
|
[....send patches to get your name here....]
|
||||||
|
|
||||||
|
@ -3556,6 +3556,26 @@ qemu-kvm -net nic,model=? /dev/null
|
|||||||
sub-element <code>label</code> are supported.
|
sub-element <code>label</code> are supported.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3><a name="customMetadata">Custom metadata</a></h3>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
...
|
||||||
|
<metadata>
|
||||||
|
<app1:foo xmlns:app1="http://app1.org/app1/">..</app1:foo>
|
||||||
|
<app2:bar xmlns:app2="http://app1.org/app2/">..</app2:bar>
|
||||||
|
</metadata>
|
||||||
|
...</pre>
|
||||||
|
|
||||||
|
<dl>
|
||||||
|
<dt><code>metadata</code></dt>
|
||||||
|
<dd>The <code>metadata</code> node can be used by applications to
|
||||||
|
store custom metadata in the form of XML nodes/trees. Applications
|
||||||
|
must use custom namespaces on their XML nodes/trees, with only
|
||||||
|
one top-level element per namespace (if the application needs
|
||||||
|
structure, they should have sub-elements to their namespace
|
||||||
|
element). <span class="since">Since 0.9.10</span></dd>
|
||||||
|
</dl>
|
||||||
|
|
||||||
<h2><a name="examples">Example configs</a></h2>
|
<h2><a name="examples">Example configs</a></h2>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -25,6 +25,9 @@
|
|||||||
<optional>
|
<optional>
|
||||||
<ref name="description"/>
|
<ref name="description"/>
|
||||||
</optional>
|
</optional>
|
||||||
|
<optional>
|
||||||
|
<ref name="metadata"/>
|
||||||
|
</optional>
|
||||||
<optional>
|
<optional>
|
||||||
<ref name="cpu"/>
|
<ref name="cpu"/>
|
||||||
</optional>
|
</optional>
|
||||||
@ -2942,6 +2945,29 @@
|
|||||||
</element>
|
</element>
|
||||||
</define>
|
</define>
|
||||||
|
|
||||||
|
<define name="metadata">
|
||||||
|
<element name="metadata">
|
||||||
|
<zeroOrMore>
|
||||||
|
<ref name="customElement"/>
|
||||||
|
</zeroOrMore>
|
||||||
|
</element>
|
||||||
|
</define>
|
||||||
|
|
||||||
|
<define name="customElement">
|
||||||
|
<element>
|
||||||
|
<anyName/>
|
||||||
|
<zeroOrMore>
|
||||||
|
<choice>
|
||||||
|
<attribute>
|
||||||
|
<anyName/>
|
||||||
|
</attribute>
|
||||||
|
<text/>
|
||||||
|
<ref name="customElement"/>
|
||||||
|
</choice>
|
||||||
|
</zeroOrMore>
|
||||||
|
</element>
|
||||||
|
</define>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Type library
|
Type library
|
||||||
|
|
||||||
|
@ -1500,6 +1500,8 @@ void virDomainDefFree(virDomainDefPtr def)
|
|||||||
if (def->namespaceData && def->ns.free)
|
if (def->namespaceData && def->ns.free)
|
||||||
(def->ns.free)(def->namespaceData);
|
(def->ns.free)(def->namespaceData);
|
||||||
|
|
||||||
|
xmlFreeNode(def->metadata);
|
||||||
|
|
||||||
VIR_FREE(def);
|
VIR_FREE(def);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8072,6 +8074,11 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
|
|||||||
def->os.smbios_mode = VIR_DOMAIN_SMBIOS_NONE; /* not present */
|
def->os.smbios_mode = VIR_DOMAIN_SMBIOS_NONE; /* not present */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Extract custom metadata */
|
||||||
|
if ((node = virXPathNode("./metadata[1]", ctxt)) != NULL) {
|
||||||
|
def->metadata = xmlCopyNode(node, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/* we have to make a copy of all of the callback pointers here since
|
/* we have to make a copy of all of the callback pointers here since
|
||||||
* we won't have the virCaps structure available during free
|
* we won't have the virCaps structure available during free
|
||||||
*/
|
*/
|
||||||
@ -8211,6 +8218,7 @@ virDomainDefParse(const char *xmlStr,
|
|||||||
{
|
{
|
||||||
xmlDocPtr xml;
|
xmlDocPtr xml;
|
||||||
virDomainDefPtr def = NULL;
|
virDomainDefPtr def = NULL;
|
||||||
|
int keepBlanksDefault = xmlKeepBlanksDefault(0);
|
||||||
|
|
||||||
if ((xml = virXMLParse(filename, xmlStr, _("(domain_definition)")))) {
|
if ((xml = virXMLParse(filename, xmlStr, _("(domain_definition)")))) {
|
||||||
def = virDomainDefParseNode(caps, xml, xmlDocGetRootElement(xml),
|
def = virDomainDefParseNode(caps, xml, xmlDocGetRootElement(xml),
|
||||||
@ -8218,6 +8226,7 @@ virDomainDefParse(const char *xmlStr,
|
|||||||
xmlFreeDoc(xml);
|
xmlFreeDoc(xml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xmlKeepBlanksDefault(keepBlanksDefault);
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8311,6 +8320,7 @@ virDomainObjParseFile(virCapsPtr caps,
|
|||||||
{
|
{
|
||||||
xmlDocPtr xml;
|
xmlDocPtr xml;
|
||||||
virDomainObjPtr obj = NULL;
|
virDomainObjPtr obj = NULL;
|
||||||
|
int keepBlanksDefault = xmlKeepBlanksDefault(0);
|
||||||
|
|
||||||
if ((xml = virXMLParseFile(filename))) {
|
if ((xml = virXMLParseFile(filename))) {
|
||||||
obj = virDomainObjParseNode(caps, xml,
|
obj = virDomainObjParseNode(caps, xml,
|
||||||
@ -8319,6 +8329,7 @@ virDomainObjParseFile(virCapsPtr caps,
|
|||||||
xmlFreeDoc(xml);
|
xmlFreeDoc(xml);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xmlKeepBlanksDefault(keepBlanksDefault);
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11833,6 +11844,30 @@ virDomainDefFormatInternal(virDomainDefPtr def,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Custom metadata comes at the end */
|
||||||
|
if (def->metadata) {
|
||||||
|
xmlBufferPtr xmlbuf;
|
||||||
|
int oldIndentTreeOutput = xmlIndentTreeOutput;
|
||||||
|
|
||||||
|
/* Indentation on output requires that we previously set
|
||||||
|
* xmlKeepBlanksDefault to 0 when parsing; also, libxml does 2
|
||||||
|
* spaces per level of indentation of intermediate elements,
|
||||||
|
* but no leading indentation before the starting element.
|
||||||
|
* Thankfully, libxml maps what looks like globals into
|
||||||
|
* thread-local uses, so we are thread-safe. */
|
||||||
|
xmlIndentTreeOutput = 1;
|
||||||
|
xmlbuf = xmlBufferCreate();
|
||||||
|
if (xmlNodeDump(xmlbuf, def->metadata->doc, def->metadata,
|
||||||
|
virBufferGetIndent(buf, false) / 2 + 1, 1) < 0) {
|
||||||
|
xmlBufferFree(xmlbuf);
|
||||||
|
xmlIndentTreeOutput = oldIndentTreeOutput;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
virBufferAsprintf(buf, " %s\n", (char *) xmlBufferContent(xmlbuf));
|
||||||
|
xmlBufferFree(xmlbuf);
|
||||||
|
xmlIndentTreeOutput = oldIndentTreeOutput;
|
||||||
|
}
|
||||||
|
|
||||||
virBufferAddLit(buf, "</domain>\n");
|
virBufferAddLit(buf, "</domain>\n");
|
||||||
|
|
||||||
if (virBufferError(buf))
|
if (virBufferError(buf))
|
||||||
@ -12517,11 +12552,14 @@ virDomainSnapshotDefParseString(const char *xmlStr,
|
|||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
int active;
|
int active;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
|
int keepBlanksDefault = xmlKeepBlanksDefault(0);
|
||||||
|
|
||||||
xml = virXMLParseCtxt(NULL, xmlStr, _("(domain_snapshot)"), &ctxt);
|
xml = virXMLParseCtxt(NULL, xmlStr, _("(domain_snapshot)"), &ctxt);
|
||||||
if (!xml) {
|
if (!xml) {
|
||||||
|
xmlKeepBlanksDefault(keepBlanksDefault);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
xmlKeepBlanksDefault(keepBlanksDefault);
|
||||||
|
|
||||||
if (VIR_ALLOC(def) < 0) {
|
if (VIR_ALLOC(def) < 0) {
|
||||||
virReportOOMError();
|
virReportOOMError();
|
||||||
|
@ -1525,6 +1525,9 @@ struct _virDomainDef {
|
|||||||
|
|
||||||
void *namespaceData;
|
void *namespaceData;
|
||||||
virDomainXMLNamespace ns;
|
virDomainXMLNamespace ns;
|
||||||
|
|
||||||
|
/* Application-specific custom metadata */
|
||||||
|
xmlNodePtr metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum virDomainTaintFlags {
|
enum virDomainTaintFlags {
|
||||||
|
38
tests/domainsnapshotxml2xmlout/metadata.xml
Normal file
38
tests/domainsnapshotxml2xmlout/metadata.xml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<domainsnapshot>
|
||||||
|
<name>my snap name</name>
|
||||||
|
<description>!@#$%^</description>
|
||||||
|
<state>running</state>
|
||||||
|
<parent>
|
||||||
|
<name>earlier_snap</name>
|
||||||
|
</parent>
|
||||||
|
<creationTime>1272917631</creationTime>
|
||||||
|
<domain type='qemu'>
|
||||||
|
<name>QEMUGuest1</name>
|
||||||
|
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
|
||||||
|
<memory>219100</memory>
|
||||||
|
<currentMemory>219100</currentMemory>
|
||||||
|
<vcpu cpuset='1-4,8-20,525'>1</vcpu>
|
||||||
|
<os>
|
||||||
|
<type arch='i686' machine='pc'>hvm</type>
|
||||||
|
<boot dev='hd'/>
|
||||||
|
</os>
|
||||||
|
<clock offset='utc'/>
|
||||||
|
<on_poweroff>destroy</on_poweroff>
|
||||||
|
<on_reboot>restart</on_reboot>
|
||||||
|
<on_crash>destroy</on_crash>
|
||||||
|
<devices>
|
||||||
|
<emulator>/usr/bin/qemu</emulator>
|
||||||
|
<disk type='block' device='disk'>
|
||||||
|
<source dev='/dev/HostVG/QEMUGuest1'/>
|
||||||
|
<target dev='hda' bus='ide'/>
|
||||||
|
<address type='drive' controller='0' bus='0' unit='0'/>
|
||||||
|
</disk>
|
||||||
|
<controller type='ide' index='0'/>
|
||||||
|
<memballoon model='virtio'/>
|
||||||
|
</devices>
|
||||||
|
<metadata>
|
||||||
|
<app1:foo xmlns:app1="http://foo.org/">fooish</app1:foo>
|
||||||
|
<app2:bar xmlns:app2="http://bar.com/" maman="baz">barish</app2:bar>
|
||||||
|
</metadata>
|
||||||
|
</domain>
|
||||||
|
</domainsnapshot>
|
@ -109,6 +109,7 @@ mymain(void)
|
|||||||
DO_TEST("noparent_nodescription_noactive", NULL, 0);
|
DO_TEST("noparent_nodescription_noactive", NULL, 0);
|
||||||
DO_TEST("noparent_nodescription", NULL, 1);
|
DO_TEST("noparent_nodescription", NULL, 1);
|
||||||
DO_TEST("noparent", "9d37b878-a7cc-9f9a-b78f-49b3abad25a8", 0);
|
DO_TEST("noparent", "9d37b878-a7cc-9f9a-b78f-49b3abad25a8", 0);
|
||||||
|
DO_TEST("metadata", "c7a5fdbd-edaf-9455-926a-d65c16db1809", 0);
|
||||||
|
|
||||||
virCapabilitiesFree(driver.caps);
|
virCapabilitiesFree(driver.caps);
|
||||||
|
|
||||||
|
4
tests/qemuxml2argvdata/qemuxml2argv-metadata.args
Normal file
4
tests/qemuxml2argvdata/qemuxml2argv-metadata.args
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M \
|
||||||
|
pc -m 214 -smp 1 -name QEMUGuest1 -nographic -monitor unix:/tmp/test-monitor,\
|
||||||
|
server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial \
|
||||||
|
none -parallel none -usb
|
30
tests/qemuxml2argvdata/qemuxml2argv-metadata.xml
Normal file
30
tests/qemuxml2argvdata/qemuxml2argv-metadata.xml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<domain type='qemu'>
|
||||||
|
<name>QEMUGuest1</name>
|
||||||
|
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
|
||||||
|
<memory>219100</memory>
|
||||||
|
<currentMemory>219100</currentMemory>
|
||||||
|
<vcpu cpuset='1-4,8-20,525'>1</vcpu>
|
||||||
|
<os>
|
||||||
|
<type arch='i686' machine='pc'>hvm</type>
|
||||||
|
<boot dev='hd'/>
|
||||||
|
</os>
|
||||||
|
<clock offset='utc'/>
|
||||||
|
<on_poweroff>destroy</on_poweroff>
|
||||||
|
<on_reboot>restart</on_reboot>
|
||||||
|
<on_crash>destroy</on_crash>
|
||||||
|
<devices>
|
||||||
|
<emulator>/usr/bin/qemu</emulator>
|
||||||
|
<disk type='block' device='disk'>
|
||||||
|
<source dev='/dev/HostVG/QEMUGuest1'/>
|
||||||
|
<target dev='hda' bus='ide'/>
|
||||||
|
<address type='drive' controller='0' bus='0' unit='0'/>
|
||||||
|
</disk>
|
||||||
|
<controller type='ide' index='0'/>
|
||||||
|
<memballoon model='virtio'/>
|
||||||
|
</devices>
|
||||||
|
<!-- intentional mis-indentation -->
|
||||||
|
<metadata>
|
||||||
|
<app1:foo xmlns:app1="http://foo.org/">fooish</app1:foo>
|
||||||
|
<app2:bar xmlns:app2="http://bar.com/" maman="baz">barish</app2:bar>
|
||||||
|
</metadata>
|
||||||
|
</domain>
|
29
tests/qemuxml2xmloutdata/qemuxml2xmlout-metadata.xml
Normal file
29
tests/qemuxml2xmloutdata/qemuxml2xmlout-metadata.xml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<domain type='qemu'>
|
||||||
|
<name>QEMUGuest1</name>
|
||||||
|
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
|
||||||
|
<memory>219100</memory>
|
||||||
|
<currentMemory>219100</currentMemory>
|
||||||
|
<vcpu cpuset='1-4,8-20,525'>1</vcpu>
|
||||||
|
<os>
|
||||||
|
<type arch='i686' machine='pc'>hvm</type>
|
||||||
|
<boot dev='hd'/>
|
||||||
|
</os>
|
||||||
|
<clock offset='utc'/>
|
||||||
|
<on_poweroff>destroy</on_poweroff>
|
||||||
|
<on_reboot>restart</on_reboot>
|
||||||
|
<on_crash>destroy</on_crash>
|
||||||
|
<devices>
|
||||||
|
<emulator>/usr/bin/qemu</emulator>
|
||||||
|
<disk type='block' device='disk'>
|
||||||
|
<source dev='/dev/HostVG/QEMUGuest1'/>
|
||||||
|
<target dev='hda' bus='ide'/>
|
||||||
|
<address type='drive' controller='0' bus='0' unit='0'/>
|
||||||
|
</disk>
|
||||||
|
<controller type='ide' index='0'/>
|
||||||
|
<memballoon model='virtio'/>
|
||||||
|
</devices>
|
||||||
|
<metadata>
|
||||||
|
<app1:foo xmlns:app1="http://foo.org/">fooish</app1:foo>
|
||||||
|
<app2:bar xmlns:app2="http://bar.com/" maman="baz">barish</app2:bar>
|
||||||
|
</metadata>
|
||||||
|
</domain>
|
@ -210,6 +210,8 @@ mymain(void)
|
|||||||
DO_TEST_DIFFERENT("graphics-listen-network2");
|
DO_TEST_DIFFERENT("graphics-listen-network2");
|
||||||
DO_TEST_DIFFERENT("graphics-spice-timeout");
|
DO_TEST_DIFFERENT("graphics-spice-timeout");
|
||||||
|
|
||||||
|
DO_TEST_DIFFERENT("metadata");
|
||||||
|
|
||||||
virCapabilitiesFree(driver.caps);
|
virCapabilitiesFree(driver.caps);
|
||||||
|
|
||||||
return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
|
return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user