]> xenbits.xensource.com Git - libvirt.git/commitdiff
Allow custom metadata in domain configuration XML
authorZeeshan Ali (Khattak) <zeeshanak@gnome.org>
Tue, 24 Jan 2012 02:26:18 +0000 (04:26 +0200)
committerEric Blake <eblake@redhat.com>
Wed, 25 Jan 2012 00:06:34 +0000 (17:06 -0700)
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.

AUTHORS
docs/formatdomain.html.in
docs/schemas/domaincommon.rng
src/conf/domain_conf.c
src/conf/domain_conf.h
tests/domainsnapshotxml2xmlout/metadata.xml [new file with mode: 0644]
tests/domainsnapshotxml2xmltest.c
tests/qemuxml2argvdata/qemuxml2argv-metadata.args [new file with mode: 0644]
tests/qemuxml2argvdata/qemuxml2argv-metadata.xml [new file with mode: 0644]
tests/qemuxml2xmloutdata/qemuxml2xmlout-metadata.xml [new file with mode: 0644]
tests/qemuxml2xmltest.c

diff --git a/AUTHORS b/AUTHORS
index 783c48a16ef087f7a6df5d49d66e3898fe479386..f383c660ab050fe92d8455a64b33ae2f0af7e789 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -217,6 +217,7 @@ Patches have also been contributed by:
   Deepak C Shetty      <deepakcs@linux.vnet.ibm.com>
   Martin Kletzander    <mkletzan@redhat.com>
   Laszlo Ersek         <lersek@redhat.com>
+  Zeeshan Ali (Khattak) <zeeshanak@gnome.org>
 
   [....send patches to get your name here....]
 
index 6667bed6b5d1eb466434d9669d9fe37f9f8a6bcd..6b025e856439f7f00482481f8703cfffb74ec823 100644 (file)
@@ -3556,6 +3556,26 @@ qemu-kvm -net nic,model=? /dev/null
       sub-element <code>label</code> are supported.
     </p>
 
+    <h3><a name="customMetadata">Custom metadata</a></h3>
+
+<pre>
+  ...
+  &lt;metadata&gt;
+    &lt;app1:foo xmlns:app1="http://app1.org/app1/"&gt;..&lt;/app1:foo&gt;
+    &lt;app2:bar xmlns:app2="http://app1.org/app2/"&gt;..&lt;/app2:bar&gt;
+  &lt;/metadata&gt;
+  ...</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>
 
     <p>
index 2041dfbbc2b4db3a805321097cdcf2bd11a36519..4fa968dc430c8a8755bfdec0bdd9f7eeb27270fd 100644 (file)
@@ -25,6 +25,9 @@
         <optional>
           <ref name="description"/>
         </optional>
+        <optional>
+          <ref name="metadata"/>
+        </optional>
         <optional>
           <ref name="cpu"/>
         </optional>
     </element>
   </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
 
index 8eed85b61a2e8ceb84d66b85e392cedaf07c1abe..f2c8d02171502c109103bc312d57a54c3233dbec 100644 (file)
@@ -1500,6 +1500,8 @@ void virDomainDefFree(virDomainDefPtr def)
     if (def->namespaceData && def->ns.free)
         (def->ns.free)(def->namespaceData);
 
+    xmlFreeNode(def->metadata);
+
     VIR_FREE(def);
 }
 
@@ -8072,6 +8074,11 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps,
         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 won't have the virCaps structure available during free
      */
@@ -8211,6 +8218,7 @@ virDomainDefParse(const char *xmlStr,
 {
     xmlDocPtr xml;
     virDomainDefPtr def = NULL;
+    int keepBlanksDefault = xmlKeepBlanksDefault(0);
 
     if ((xml = virXMLParse(filename, xmlStr, _("(domain_definition)")))) {
         def = virDomainDefParseNode(caps, xml, xmlDocGetRootElement(xml),
@@ -8218,6 +8226,7 @@ virDomainDefParse(const char *xmlStr,
         xmlFreeDoc(xml);
     }
 
+    xmlKeepBlanksDefault(keepBlanksDefault);
     return def;
 }
 
@@ -8311,6 +8320,7 @@ virDomainObjParseFile(virCapsPtr caps,
 {
     xmlDocPtr xml;
     virDomainObjPtr obj = NULL;
+    int keepBlanksDefault = xmlKeepBlanksDefault(0);
 
     if ((xml = virXMLParseFile(filename))) {
         obj = virDomainObjParseNode(caps, xml,
@@ -8319,6 +8329,7 @@ virDomainObjParseFile(virCapsPtr caps,
         xmlFreeDoc(xml);
     }
 
+    xmlKeepBlanksDefault(keepBlanksDefault);
     return obj;
 }
 
@@ -11833,6 +11844,30 @@ virDomainDefFormatInternal(virDomainDefPtr def,
             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");
 
     if (virBufferError(buf))
@@ -12517,11 +12552,14 @@ virDomainSnapshotDefParseString(const char *xmlStr,
     struct timeval tv;
     int active;
     char *tmp;
+    int keepBlanksDefault = xmlKeepBlanksDefault(0);
 
     xml = virXMLParseCtxt(NULL, xmlStr, _("(domain_snapshot)"), &ctxt);
     if (!xml) {
+        xmlKeepBlanksDefault(keepBlanksDefault);
         return NULL;
     }
+    xmlKeepBlanksDefault(keepBlanksDefault);
 
     if (VIR_ALLOC(def) < 0) {
         virReportOOMError();
index a49795cdda330062d6aa03429f02861da3c2fd0e..3b522a9a28113ddf7b366ef28a6ba4b4abcab4da 100644 (file)
@@ -1525,6 +1525,9 @@ struct _virDomainDef {
 
     void *namespaceData;
     virDomainXMLNamespace ns;
+
+    /* Application-specific custom metadata */
+    xmlNodePtr metadata;
 };
 
 enum virDomainTaintFlags {
diff --git a/tests/domainsnapshotxml2xmlout/metadata.xml b/tests/domainsnapshotxml2xmlout/metadata.xml
new file mode 100644 (file)
index 0000000..45180c9
--- /dev/null
@@ -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>
index 90ff9bb372dafe2505b9054caa9cc2a6bbed0e34..5c2e670231abf491507fd2e58738c1f23b61319d 100644 (file)
@@ -109,6 +109,7 @@ mymain(void)
     DO_TEST("noparent_nodescription_noactive", NULL, 0);
     DO_TEST("noparent_nodescription", NULL, 1);
     DO_TEST("noparent", "9d37b878-a7cc-9f9a-b78f-49b3abad25a8", 0);
+    DO_TEST("metadata", "c7a5fdbd-edaf-9455-926a-d65c16db1809", 0);
 
     virCapabilitiesFree(driver.caps);
 
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-metadata.args b/tests/qemuxml2argvdata/qemuxml2argv-metadata.args
new file mode 100644 (file)
index 0000000..651793d
--- /dev/null
@@ -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
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-metadata.xml b/tests/qemuxml2argvdata/qemuxml2argv-metadata.xml
new file mode 100644 (file)
index 0000000..b0f90dd
--- /dev/null
@@ -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>
diff --git a/tests/qemuxml2xmloutdata/qemuxml2xmlout-metadata.xml b/tests/qemuxml2xmloutdata/qemuxml2xmlout-metadata.xml
new file mode 100644 (file)
index 0000000..a6888ee
--- /dev/null
@@ -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>
index 293c2a7d5f3e9692a1dbd0e39d35ee712618e08e..df317fd538e3d9f9c46b105f888eefa76f6f40af 100644 (file)
@@ -210,6 +210,8 @@ mymain(void)
     DO_TEST_DIFFERENT("graphics-listen-network2");
     DO_TEST_DIFFERENT("graphics-spice-timeout");
 
+    DO_TEST_DIFFERENT("metadata");
+
     virCapabilitiesFree(driver.caps);
 
     return (ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);