]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
snapshot: allow full domain xml in snapshot
authorEric Blake <eblake@redhat.com>
Sat, 13 Aug 2011 01:19:47 +0000 (19:19 -0600)
committerEric Blake <eblake@redhat.com>
Sat, 3 Sep 2011 14:09:35 +0000 (08:09 -0600)
Just like VM saved state images (virsh save), snapshots MUST
track the inactive domain xml to detect any ABI incompatibilities.

The indentation is not perfect, but functionality comes before form.

Later patches will actually supply a full domain; for now, this
wires up the storage to support one, but doesn't ever generate one
in dumpxml output.

Happily, libvirt.c was already rejecting use of VIR_DOMAIN_XML_SECURE
from read-only connections, even though before this patch, there was
no information to be secured by the use of that flag.

And while we're at it, mark the libvirt snapshot metadata files
as internal-use only.

* src/libvirt.c (virDomainSnapshotGetXMLDesc): Document flag.
* src/conf/domain_conf.h (_virDomainSnapshotDef): Add member.
(virDomainSnapshotDefParseString, virDomainSnapshotDefFormat):
Update signature.
* src/conf/domain_conf.c (virDomainSnapshotDefFree): Clean up.
(virDomainSnapshotDefParseString): Optionally parse domain.
(virDomainSnapshotDefFormat): Output full domain.
* src/esx/esx_driver.c (esxDomainSnapshotCreateXML)
(esxDomainSnapshotGetXMLDesc): Update callers.
* src/vbox/vbox_tmpl.c (vboxDomainSnapshotCreateXML)
(vboxDomainSnapshotGetXMLDesc): Likewise.
* src/qemu/qemu_driver.c (qemuDomainSnapshotCreateXML)
(qemuDomainSnapshotLoad, qemuDomainSnapshotGetXMLDesc)
(qemuDomainSnapshotWriteMetadata): Likewise.
* docs/formatsnapshot.html.in: Rework doc example.
Based on a patch by Philipp Hahn.

docs/formatsnapshot.html.in
src/conf/domain_conf.c
src/conf/domain_conf.h
src/esx/esx_driver.c
src/libvirt.c
src/qemu/qemu_driver.c
src/vbox/vbox_tmpl.c

index dc5873a8225e4de9a33562c38e506ebc05017ef1..91799b42c7c8f53edc8137dffdf9730a6815f9df 100644 (file)
         snapshots, as described above.  Readonly.
       </dd>
       <dt><code>domain</code></dt>
-      <dd>The domain that this snapshot was taken against.  This
-      element contains exactly one child element, uuid.  This
-      specifies the uuid of the domain that this snapshot was taken
-      against.  Readonly.
+      <dd>The domain that this snapshot was taken against.  Older
+        versions of libvirt stored only a single child element, uuid;
+        reverting to a snapshot like this is risky if the current
+        state of the domain differs from the state that the domain was
+        created in, and requires the use of the
+        <code>VIR_DOMAIN_SNAPSHOT_REVERT_FORCE</code> flag
+        in <code>virDomainRevertToSnapshot()</code>.  Newer versions
+        of libvirt store the entire
+        inactive <a href="formatdomain.html">domain configuration</a>
+        at the time of the snapshot (<span class="since">since
+        0.9.5</span>).  Readonly.
       </dd>
     </dl>
 
-    <h2><a name="example">Example</a></h2>
+    <h2><a name="example">Examples</a></h2>
 
+    <p>Using this XML on creation:</p>
     <pre>
 &lt;domainsnapshot&gt;
-  &lt;name&gt;os-updates&lt;/name&gt;
+  &lt;description&gt;Snapshot of OS install and updates&lt;/description&gt;
+&lt;/domainsnapshot&gt;</pre>
+
+    <p>will result in XML similar to this from
+    virDomainSnapshotGetXMLDesc:</p>
+    <pre>
+&lt;domainsnapshot&gt;
+  &lt;name&gt;1270477159&lt;/name&gt;
   &lt;description&gt;Snapshot of OS install and updates&lt;/description&gt;
   &lt;state&gt;running&lt;/state&gt;
   &lt;creationTime&gt;1270477159&lt;/creationTime&gt;
     &lt;name&gt;bare-os-install&lt;/name&gt;
   &lt;/parent&gt;
   &lt;domain&gt;
+    &lt;name&gt;fedora&lt;/name&gt;
     &lt;uuid&gt;93a5c045-6457-2c09-e56c-927cdf34e178&lt;/uuid&gt;
+    &lt;memory&gt;1048576&lt;/memory&gt;
+    ...
+    &lt;/devices&gt;
   &lt;/domain&gt;
 &lt;/domainsnapshot&gt;</pre>
   </body>
index 5a13e43dd1497dd66547bb085c121802ac96f462..1b57a555529d45e3b6f5ba89391785fce4bb9458 100644 (file)
@@ -11377,12 +11377,19 @@ void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def)
     VIR_FREE(def->name);
     VIR_FREE(def->description);
     VIR_FREE(def->parent);
+    virDomainDefFree(def->dom);
     VIR_FREE(def);
 }
 
-/* flags are from virDomainSnapshotParseFlags */
+/* flags is bitwise-or of virDomainSnapshotParseFlags.
+ * If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then
+ * caps and expectedVirtTypes are ignored.
+ */
 virDomainSnapshotDefPtr
-virDomainSnapshotDefParseString(const char *xmlStr, unsigned int flags)
+virDomainSnapshotDefParseString(const char *xmlStr,
+                                virCapsPtr caps,
+                                unsigned int expectedVirtTypes,
+                                unsigned int flags)
 {
     xmlXPathContextPtr ctxt = NULL;
     xmlDocPtr xml = NULL;
@@ -11391,6 +11398,7 @@ virDomainSnapshotDefParseString(const char *xmlStr, unsigned int flags)
     char *creation = NULL, *state = NULL;
     struct timeval tv;
     int active;
+    char *tmp;
 
     xml = virXMLParseCtxt(NULL, xmlStr, "domainsnapshot.xml", &ctxt);
     if (!xml) {
@@ -11454,6 +11462,29 @@ virDomainSnapshotDefParseString(const char *xmlStr, unsigned int flags)
                                  state);
             goto cleanup;
         }
+
+        /* Older snapshots were created with just <domain>/<uuid>, and
+         * lack domain/@type.  In that case, leave dom NULL, and
+         * clients will have to decide between best effort
+         * initialization or outright failure.  */
+        if ((tmp = virXPathString("string(./domain/@type)", ctxt))) {
+            xmlNodePtr domainNode = virXPathNode("./domain", ctxt);
+
+            VIR_FREE(tmp);
+            if (!domainNode) {
+                virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                     _("missing domain in snapshot"));
+                goto cleanup;
+            }
+            def->dom = virDomainDefParseNode(caps, xml, domainNode,
+                                             expectedVirtTypes,
+                                             (VIR_DOMAIN_XML_INACTIVE |
+                                              VIR_DOMAIN_XML_SECURE));
+            if (!def->dom)
+                goto cleanup;
+        } else {
+            VIR_WARN("parsing older snapshot that lacks domain");
+        }
     } else {
         def->creationTime = tv.tv_sec;
     }
@@ -11482,10 +11513,15 @@ cleanup:
 
 char *virDomainSnapshotDefFormat(char *domain_uuid,
                                  virDomainSnapshotDefPtr def,
+                                 unsigned int flags,
                                  int internal)
 {
     virBuffer buf = VIR_BUFFER_INITIALIZER;
 
+    virCheckFlags(VIR_DOMAIN_XML_SECURE, NULL);
+
+    flags |= VIR_DOMAIN_XML_INACTIVE;
+
     virBufferAddLit(&buf, "<domainsnapshot>\n");
     virBufferAsprintf(&buf, "  <name>%s</name>\n", def->name);
     if (def->description)
@@ -11500,9 +11536,13 @@ char *virDomainSnapshotDefFormat(char *domain_uuid,
     }
     virBufferAsprintf(&buf, "  <creationTime>%lld</creationTime>\n",
                       def->creationTime);
-    virBufferAddLit(&buf, "  <domain>\n");
-    virBufferAsprintf(&buf, "    <uuid>%s</uuid>\n", domain_uuid);
-    virBufferAddLit(&buf, "  </domain>\n");
+    if (def->dom) {
+        virDomainDefFormatInternal(def->dom, flags, &buf);
+    } else {
+        virBufferAddLit(&buf, "  <domain>\n");
+        virBufferAsprintf(&buf, "    <uuid>%s</uuid>\n", domain_uuid);
+        virBufferAddLit(&buf, "  </domain>\n");
+    }
     if (internal)
         virBufferAsprintf(&buf, "  <active>%d</active>\n", def->current);
     virBufferAddLit(&buf, "</domainsnapshot>\n");
index cef0d9ee93ab8628c1bfdbd5753705ad87bba420..e218a302444ba1e6db332291de0bf9bc216044dd 100644 (file)
@@ -1385,6 +1385,7 @@ struct _virDomainSnapshotDef {
     char *parent;
     long long creationTime; /* in seconds */
     int state;
+    virDomainDefPtr dom;
 
     /* Internal use.  */
     bool current; /* At most one snapshot in the list should have this set */
@@ -1413,10 +1414,13 @@ typedef enum {
 } virDomainSnapshotParseFlags;
 
 virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr,
+                                                        virCapsPtr caps,
+                                                        unsigned int expectedVirtTypes,
                                                         unsigned int flags);
 void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def);
 char *virDomainSnapshotDefFormat(char *domain_uuid,
                                  virDomainSnapshotDefPtr def,
+                                 unsigned int flags,
                                  int internal);
 virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots,
                                                    const virDomainSnapshotDefPtr def);
index 2bdb5d0cf2a8047caa2670e2ed1e23d9164dd452..18edc454fd0765028d915a494c95357f616edf9f 100644 (file)
@@ -4220,7 +4220,7 @@ esxDomainSnapshotCreateXML(virDomainPtr domain, const char *xmlDesc,
         return NULL;
     }
 
-    def = virDomainSnapshotDefParseString(xmlDesc, 0);
+    def = virDomainSnapshotDefParseString(xmlDesc, NULL, 0, 0);
 
     if (def == NULL) {
         return NULL;
@@ -4316,7 +4316,7 @@ esxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot,
 
     virUUIDFormat(snapshot->domain->uuid, uuid_string);
 
-    xml = virDomainSnapshotDefFormat(uuid_string, &def, 0);
+    xml = virDomainSnapshotDefFormat(uuid_string, &def, flags, 0);
 
   cleanup:
     esxVI_VirtualMachineSnapshotTree_Free(&rootSnapshotList);
index 0b8f1d328cf41016665488cec1c2ea47a8e888af..4d80e2fe2f8d02d7dd1ff887de1094cf15aadd61 100644 (file)
@@ -15698,10 +15698,15 @@ error:
 /**
  * virDomainSnapshotGetXMLDesc:
  * @snapshot: a domain snapshot object
- * @flags: unused flag parameters; callers should pass 0
+ * @flags: bitwise-OR of subset of virDomainXMLFlags
  *
  * Provide an XML description of the domain snapshot.
  *
+ * No security-sensitive data will be included unless @flags contains
+ * VIR_DOMAIN_XML_SECURE; this flag is rejected on read-only
+ * connections.  For this API, @flags should not contain either
+ * VIR_DOMAIN_XML_INACTIVE or VIR_DOMAIN_XML_UPDATE_CPU.
+ *
  * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case of error.
  *         the caller must free() the returned value.
  */
index 0e022ad4aab91dd6b509a5839c65ec5e0b9694fe..a1557b1198a8fc9ee8e1b104fffc859d1a273345 100644 (file)
@@ -340,7 +340,9 @@ static void qemuDomainSnapshotLoad(void *payload,
             continue;
         }
 
-        def = virDomainSnapshotDefParseString(xmlStr, flags);
+        def = virDomainSnapshotDefParseString(xmlStr, qemu_driver->caps,
+                                              QEMU_EXPECTED_VIRT_TYPES,
+                                              flags);
         if (def == NULL) {
             /* Nothing we can do here, skip this one */
             VIR_ERROR(_("Failed to parse snapshot XML from file '%s'"),
@@ -1610,9 +1612,11 @@ qemuDomainSnapshotWriteMetadata(virDomainObjPtr vm,
     char *snapDir = NULL;
     char *snapFile = NULL;
     char uuidstr[VIR_UUID_STRING_BUFLEN];
+    char *tmp;
 
     virUUIDFormat(vm->def->uuid, uuidstr);
-    newxml = virDomainSnapshotDefFormat(uuidstr, snapshot->def, 1);
+    newxml = virDomainSnapshotDefFormat(uuidstr, snapshot->def,
+                                        VIR_DOMAIN_XML_SECURE, 1);
     if (newxml == NULL) {
         virReportOOMError();
         return -1;
@@ -1638,6 +1642,14 @@ qemuDomainSnapshotWriteMetadata(virDomainObjPtr vm,
                         _("failed to create snapshot file '%s'"), snapFile);
         goto cleanup;
     }
+
+    if (virAsprintf(&tmp, "snapshot-edit %s", vm->def->name) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    virEmitXMLWarning(fd, snapshot->def->name, tmp);
+    VIR_FREE(tmp);
+
     if (safewrite(fd, newxml, strlen(newxml)) != strlen(newxml)) {
         virReportSystemError(errno, _("Failed to write snapshot data to %s"),
                              snapFile);
@@ -8781,7 +8793,9 @@ static virDomainSnapshotPtr qemuDomainSnapshotCreateXML(virDomainPtr domain,
     if (!qemuDomainSnapshotIsAllowed(vm))
         goto cleanup;
 
-    if (!(def = virDomainSnapshotDefParseString(xmlDesc, parse_flags)))
+    if (!(def = virDomainSnapshotDefParseString(xmlDesc, driver->caps,
+                                                QEMU_EXPECTED_VIRT_TYPES,
+                                                parse_flags)))
         goto cleanup;
 
     if (flags & VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE) {
@@ -9083,8 +9097,6 @@ static char *qemuDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot,
     virDomainSnapshotObjPtr snap = NULL;
     char uuidstr[VIR_UUID_STRING_BUFLEN];
 
-    /* XXX Actually wire this up once we return domain xml; for now,
-     * it is trivially safe to ignore this flag. */
     virCheckFlags(VIR_DOMAIN_XML_SECURE, NULL);
 
     qemuDriverLock(driver);
@@ -9104,7 +9116,7 @@ static char *qemuDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot,
         goto cleanup;
     }
 
-    xml = virDomainSnapshotDefFormat(uuidstr, snap->def, 0);
+    xml = virDomainSnapshotDefFormat(uuidstr, snap->def, flags, 0);
 
 cleanup:
     if (vm)
index ebed4d9071214eaaa52ccc30e5da9ac350a37304..cbe34e8f5eb2b444c3956c1547c4370a71e82964 100644 (file)
@@ -5658,7 +5658,7 @@ vboxDomainSnapshotCreateXML(virDomainPtr dom,
     /* VBox has no snapshot metadata, so this flag is trivial.  */
     virCheckFlags(VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, NULL);
 
-    if (!(def = virDomainSnapshotDefParseString(xmlDesc, 0)))
+    if (!(def = virDomainSnapshotDefParseString(xmlDesc, NULL, 0, 0)))
         goto cleanup;
 
     vboxIIDFromUUID(&domiid, dom->uuid);
@@ -5840,7 +5840,7 @@ vboxDomainSnapshotGetXMLDesc(virDomainSnapshotPtr snapshot,
         def->state = VIR_DOMAIN_SHUTOFF;
 
     virUUIDFormat(dom->uuid, uuidstr);
-    ret = virDomainSnapshotDefFormat(uuidstr, def, 0);
+    ret = virDomainSnapshotDefFormat(uuidstr, def, flags, 0);
 
 cleanup:
     virDomainSnapshotDefFree(def);