[libvirt] [PATCH 34/26] snapshot: add <disks> to snapshot xml

Eric Blake eblake at redhat.com
Fri Aug 19 21:58:40 UTC 2011


Adds an optional element to <domainsnapshot>, which will be used
to give user control over external snapshot filenames on input,
and specify generated filenames on output.

<domainsnapshot>
  ...
  <disks>
    <disk name='vda' snapshot='no'/>
    <disk name='vdb' snapshot='internal'/>
    <disk name='vdc' snapshot='external'>
      <driver type='qcow2'/>
      <source file='/path/to/new'/>
    </disk>
  </disks>
  <domain>
    ...
    <devices>
      <disk ...>
        <driver name='qemu' type='raw'/>
        <target dev='vdc'/>
        <source file='/path/to/old'/>
      </disk>
    </devices>
  </domain>
</domainsnapshot>

* src/conf/domain_conf.h (_virDomainSnapshotDiskDef): New type.
(_virDomainSnapshotDef): Add new elements.
(virDomainSnapshotAlignDisks): New prototype.
* src/conf/domain_conf.c (virDomainSnapshotDiskDefClear)
(virDomainSnapshotDiskDefParseXML, disksorter)
(virDomainSnapshotAlignDisks): New functions.
(virDomainSnapshotDefParseString): Parse new fields.
(virDomainSnapshotDefFree): Clean them up.
(virDomainSnapshotDefFormat): Output them.
* src/libvirt_private.syms (domain_conf.h): Export new function.
* docs/schemas/domainsnapshot.rng (domainsnapshot, disksnapshot):
Add more xml.
* docs/formatsnapshot.html.in: Document it.
* tests/domainsnapshotxml2xmlin/disk_snapshot.xml: New test.
* tests/domainsnapshotxml2xmlout/disk_snapshot.xml: Update.
---

Sorry this one's so big in comparison to some of the earlier ones;
lots of juicy stuff in here.  Still no clients of the new domain_conf
features, but that comes next.

 docs/formatsnapshot.html.in                      |  123 ++++++++++-
 docs/schemas/domainsnapshot.rng                  |   52 +++++
 src/conf/domain_conf.c                           |  239 ++++++++++++++++++++++
 src/conf/domain_conf.h                           |   22 ++-
 src/libvirt_private.syms                         |    1 +
 tests/domainsnapshotxml2xmlin/disk_snapshot.xml  |   16 ++
 tests/domainsnapshotxml2xmlout/disk_snapshot.xml |   42 ++++
 7 files changed, 484 insertions(+), 11 deletions(-)
 create mode 100644 tests/domainsnapshotxml2xmlin/disk_snapshot.xml

diff --git a/docs/formatsnapshot.html.in b/docs/formatsnapshot.html.in
index 4158a63..5aebd72 100644
--- a/docs/formatsnapshot.html.in
+++ b/docs/formatsnapshot.html.in
@@ -64,10 +64,10 @@
     <p>
       Attributes of libvirt snapshots are stored as child elements of
       the <code>domainsnapshot</code> element.  At snapshot creation
-      time, only the <code>name</code> and <code>description</code>
-      elements are settable; the rest of the fields are informational
-      (and readonly) and will be filled in by libvirt when the
-      snapshot is created.
+      time, only the <code>name</code>, <code>description</code>,
+      and <code>disks</code> elements are settable; the rest of the
+      fields are informational (and readonly) and will be filled in by
+      libvirt when the snapshot is created.
     </p>
     <p>
       The top-level <code>domainsnapshot</code> element may contain
@@ -86,6 +86,55 @@
         description is omitted when initially creating the snapshot,
         then this field will be empty.
       </dd>
+      <dt><code>disks</code></dt>
+      <dd>On input, this is an optional listing of specific
+        instructions for disk snapshots; it is needed when making a
+        snapshot of only a subset of the disks associated with a
+        domain, or when overriding the domain defaults for how to
+        snapshot each disk, or for providing specific control over
+        what file name is created in an external snapshot.  On output,
+        this is fully populated to show the state of each disk in the
+        snapshot, including any properties that were generated by the
+        hypervisor defaults.  For system checkpoints, this field is
+        ignored on input and omitted on output (a system checkpoint
+        implies that all disks participate in the snapshot process,
+        and since the current implementation only does internal system
+        checkpoints, there are no extra details to add); a future
+        release may allow the use of <code>disks</code> with a system
+        checkpoint.  This element has a list of <code>disk</code>
+        sub-elements, describing anywhere from zero to all of the
+        disks associated with the domain.  <span class="since">Since
+        0.9.5</span>
+        <dl>
+          <dt><code>disk</code></dt>
+          <dd>This sub-element describes the snapshot properties of a
+            specific disk.  The attribute <code>name</code> is
+            mandatory, and must match the <code><target
+            dev='name'/></code> of one of
+            the <a href="formatdomain.html#elementsDisks">disk
+            devices</a> specified for the domain at the time of the
+            snapshot.  The attribute <code>snapshot</code> is
+            optional, and has the same values of the disk device
+            element for a domain
+            (<code>no</code>, <code>internal</code>,
+            or <code>external</code>).  Some hypervisors like ESX
+            require that if specified, the snapshot mode must not
+            override any snapshot mode attached to the corresponding
+            domain disk, while others like qemu allow this field to
+            override the domain default.  If the snapshot mode is
+            external (whether specified or inherited), then there is
+            an optional sub-element <code>source</code>, with an
+            attribute <code>file</code> giving the name, and an
+            optional sub-element <code>driver</code>, with an
+            attribute <code>type</code> giving the driver type (such
+            as qcow2), of the new file created by the external
+            snapshot of the new file.  Remember that with external
+            snapshots, the original file name becomes the read-only
+            snapshot, and the new file name contains the read-write
+            delta of all disk changes since the snapshot.
+          </dd>
+        </dl>
+      </dd>
       <dt><code>creationTime</code></dt>
       <dd>The time this snapshot was created.  The time is specified
         in seconds since the Epoch, UTC (i.e. Unix time).  Readonly.
@@ -124,14 +173,21 @@

     <h2><a name="example">Examples</a></h2>

-    <p>Using this XML on creation:</p>
+    <p>Using this XML to create a disk snapshot of just vda on a qemu
+      domain with two disks:</p>
     <pre>
-      <domainsnapshot>
-         <description>Snapshot of OS install and updates</description>
-      </domainsnapshot></pre>
+<domainsnapshot>
+  <description>Snapshot of OS install and updates</description>
+  <disks>
+    <disk name='vda'>
+      <source file='/path/to/new'/>
+    </disk>
+    <disk name='vdb' snapshot='no'/>
+  </disks>
+</domainsnapshot></pre>

-    <p>Will result in XML similar to this from
-    virDomainSnapshotGetXMLDesc:</p>
+    <p>will result in XML similar to this from
+      <code>virDomainSnapshotGetXMLDesc()</code>:</p>
     <pre>
 <domainsnapshot>
   <name>1270477159</name>
@@ -141,14 +197,61 @@
   <parent>
     <name>bare-os-install</name>
   </parent>
+  <disks>
+    <disk name='vda' snapshot='external'>
+      <driver type='qcow2'/>
+      <b><source file='/path/to/new'/></b>
+    </disk>
+    <disk name='vdb' snapshot='no'/>
+  </disks>
   <domain>
     <name>fedora</name>
     <uuid>93a5c045-6457-2c09-e56c-927cdf34e178</uuid>
     <memory>1048576</memory>
     ...
+    <devices>
+      <disk type='file' device='disk'>
+        <driver name='qemu' type='raw'/>
+        <b><source file='/path/to/old'/></b>
+        <target dev='vda' bus='virtio'/>
+      </disk>
+      <disk type='file' device='disk' snapshot='external'>
+        <driver name='qemu' type='raw'/>
+        <source file='/path/to/old2'/>
+        <target dev='vdb' bus='virtio'/>
+      </disk>
+      ...
     </devices>
   </domain>
 </domainsnapshot></pre>
+    <p>With that snapshot created, <code>/path/to/old</code> is the
+      read-only backing file to the new active
+      file <code>/path/to/new</code>.  The <code><domain></code>
+      element within the snapshot xml records the state of the domain
+      just before the snapshot; a call
+      to <code>virDomainGetXMLDesc()</code> will show that the domain
+      has been changed to reflect the snapshot:
+    </p>
+    <pre>
+<domain>
+  <name>fedora</name>
+  <uuid>93a5c045-6457-2c09-e56c-927cdf34e178</uuid>
+  <memory>1048576</memory>
+  ...
+  <devices>
+    <disk type='file' device='disk'>
+      <driver name='qemu' type='qcow2'/>
+      <b><source file='/path/to/new'/></b>
+      <target dev='vda' bus='virtio'/>
+    </disk>
+    <disk type='file' device='disk' snapshot='external'>
+      <driver name='qemu' type='raw'/>
+      <source file='/path/to/old2'/>
+      <target dev='vdb' bus='virtio'/>
+    </disk>
+    ...
+  </devices>
+</domain></pre>

   </body>
 </html>
diff --git a/docs/schemas/domainsnapshot.rng b/docs/schemas/domainsnapshot.rng
index 130dad9..671fbe0 100644
--- a/docs/schemas/domainsnapshot.rng
+++ b/docs/schemas/domainsnapshot.rng
@@ -31,6 +31,13 @@
           </element>
         </optional>
         <optional>
+          <element name='disks'>
+            <zeroOrMore>
+              <ref name='disksnapshot'/>
+            </zeroOrMore>
+          </element>
+        </optional>
+        <optional>
           <element name='active'>
             <choice>
               <value>0</value>
@@ -72,4 +79,49 @@
     </choice>
   </define>

+  <define name='disksnapshot'>
+    <element name='disk'>
+      <attribute name='name'>
+        <ref name='deviceName'/>
+      </attribute>
+      <choice>
+        <attribute name='snapshot'>
+          <value>no</value>
+        </attribute>
+        <attribute name='snapshot'>
+          <value>internal</value>
+        </attribute>
+        <group>
+          <optional>
+            <attribute name='snapshot'>
+              <value>external</value>
+            </attribute>
+          </optional>
+          <interleave>
+            <optional>
+              <element name='driver'>
+                <optional>
+                  <attribute name='type'>
+                    <ref name='genericName'/>
+                  </attribute>
+                </optional>
+                <empty/>
+              </element>
+            </optional>
+            <optional>
+              <element name='source'>
+                <optional>
+                  <attribute name='file'>
+                    <ref name='absFilePath'/>
+                  </attribute>
+                </optional>
+                <empty/>
+              </element>
+            </optional>
+          </interleave>
+        </group>
+      </choice>
+    </element>
+  </define>
+
 </grammar>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 0713a25..50a9bb4 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -10987,18 +10987,82 @@ cleanup:
 }

 /* Snapshot Def functions */
+static void
+virDomainSnapshotDiskDefClear(virDomainSnapshotDiskDefPtr disk)
+{
+    VIR_FREE(disk->name);
+    VIR_FREE(disk->file);
+    VIR_FREE(disk->driverType);
+}
+
 void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def)
 {
+    int i;
+
     if (!def)
         return;

     VIR_FREE(def->name);
     VIR_FREE(def->description);
     VIR_FREE(def->parent);
+    for (i = 0; i < def->ndisks; i++)
+        virDomainSnapshotDiskDefClear(&def->disks[i]);
+    VIR_FREE(def->disks);
     virDomainDefFree(def->dom);
     VIR_FREE(def);
 }

+static int
+virDomainSnapshotDiskDefParseXML(xmlNodePtr node,
+                                 virDomainSnapshotDiskDefPtr def)
+{
+    int ret = -1;
+    char *snapshot = NULL;
+    xmlNodePtr cur;
+
+    def->name = virXMLPropString(node, "name");
+    if (!def->name) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                             _("missing name from disk snapshot element"));
+        goto cleanup;
+    }
+
+    snapshot = virXMLPropString(node, "snapshot");
+    if (snapshot) {
+        def->snapshot = virDomainDiskSnapshotTypeFromString(snapshot);
+        if (def->snapshot <= 0) {
+            virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                                 _("unknown disk snapshot setting '%s'"),
+                                 snapshot);
+            goto cleanup;
+        }
+    }
+
+    cur = node->children;
+    while (cur) {
+        if (cur->type == XML_ELEMENT_NODE) {
+            if (!def->file &&
+                xmlStrEqual(cur->name, BAD_CAST "source")) {
+                def->file = virXMLPropString(cur, "file");
+            } else if (!def->driverType &&
+                       xmlStrEqual(cur->name, BAD_CAST "driver")) {
+                def->driverType = virXMLPropString(cur, "type");
+            }
+        }
+        cur = cur->next;
+    }
+
+    if (!def->snapshot && (def->file || def->driverType))
+        def->snapshot = VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL;
+
+    ret = 0;
+cleanup:
+    VIR_FREE(snapshot);
+    if (ret < 0)
+        virDomainSnapshotDiskDefClear(def);
+    return ret;
+}
+
 /* If newSnapshot is true, caps, expectedVirtTypes, and flags are ignored.  */
 virDomainSnapshotDefPtr
 virDomainSnapshotDefParseString(const char *xmlStr,
@@ -11011,6 +11075,8 @@ virDomainSnapshotDefParseString(const char *xmlStr,
     xmlDocPtr xml = NULL;
     virDomainSnapshotDefPtr def = NULL;
     virDomainSnapshotDefPtr ret = NULL;
+    xmlNodePtr *nodes = NULL;
+    int i;
     char *creation = NULL, *state = NULL;
     struct timeval tv;
     int active;
@@ -11044,6 +11110,19 @@ virDomainSnapshotDefParseString(const char *xmlStr,

     def->description = virXPathString("string(./description)", ctxt);

+    if ((i = virXPathNodeSet("./disks/*)", ctxt, &nodes)) < 0)
+        goto cleanup;
+    def->ndisks = i;
+    if (def->ndisks && VIR_ALLOC_N(def->disks, def->ndisks) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+    for (i = 0; i < def->ndisks; i++) {
+        if (virDomainSnapshotDiskDefParseXML(nodes[i], &def->disks[i]) < 0)
+            goto cleanup;
+    }
+    VIR_FREE(nodes);
+
     if (!newSnapshot) {
         if (virXPathLongLong("string(./creationTime)", ctxt,
                              &def->creationTime) < 0) {
@@ -11106,6 +11185,7 @@ virDomainSnapshotDefParseString(const char *xmlStr,
 cleanup:
     VIR_FREE(creation);
     VIR_FREE(state);
+    VIR_FREE(nodes);
     xmlXPathFreeContext(ctxt);
     if (ret == NULL)
         virDomainSnapshotDefFree(def);
@@ -11114,12 +11194,143 @@ cleanup:
     return ret;
 }

+static int
+disksorter(const void *a, const void *b)
+{
+    const virDomainSnapshotDiskDef *diska = a;
+    const virDomainSnapshotDiskDef *diskb = b;
+
+    return diskb->index - diska->index;
+}
+
+/* Align def->disks to def->domain.  Sort the list of def->disks,
+ * filling in any missing disks or snapshot state defaults given by
+ * the domain, with a fallback to a passed in default.  Issue an error
+ * and return -1 if any def->disks[n]->name appears more than once or
+ * does not map to dom->disks.  If require_match, also require that
+ * existing def->disks snapshot states do not override explicit
+ * def->dom settings.  */
+int
+virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr def,
+                            int default_snapshot,
+                            bool require_match)
+{
+    int ret = -1;
+    virBitmapPtr map = NULL;
+    int i;
+    int j;
+    bool inuse;
+
+    if (!def->dom) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                             _("missing domain in snapshot"));
+        goto cleanup;
+    }
+
+    if (def->ndisks > def->dom->ndisks) {
+        virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                             _("too many disk snapshot requests for domain"));
+        goto cleanup;
+    }
+
+    /* Unlikely to have a guest without disks but technically possible.  */
+    if (!def->dom->ndisks) {
+        ret = 0;
+        goto cleanup;
+    }
+
+    if (!(map = virBitmapAlloc(def->dom->ndisks))) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    /* Double check requested disks.  */
+    for (i = 0; i < def->ndisks; i++) {
+        virDomainSnapshotDiskDefPtr disk = &def->disks[i];
+        bool found = false;
+
+        for (j = 0; j < def->dom->ndisks; j++) {
+            if (STREQ(disk->name, def->dom->disks[j]->dst)) {
+                int disk_snapshot = def->dom->disks[j]->snapshot;
+
+                if (virBitmapGetBit(map, j, &inuse) < 0 || inuse) {
+                    virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                         _("disk '%s' specified twice"),
+                                         disk->name);
+                    goto cleanup;
+                }
+                ignore_value(virBitmapSetBit(map, j));
+                disk->index = j;
+                if (!disk_snapshot)
+                    disk_snapshot = default_snapshot;
+                if (!disk->snapshot) {
+                    disk->snapshot = disk_snapshot;
+                } else if (disk_snapshot && require_match &&
+                           disk->snapshot != disk_snapshot) {
+                    virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                         _("disk '%s' must use snapshot mode "
+                                           "'%s'"), disk->name,
+                                         virDomainDiskSnapshotTypeToString(disk_snapshot));
+                    goto cleanup;
+                }
+                if (disk->file &&
+                    disk->snapshot != VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL) {
+                    virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                         _("file '%s' for disk '%s' requires "
+                                           "use of external snapshot mode"),
+                                         disk->file, disk->name);
+                    goto cleanup;
+                }
+                break;
+            }
+        }
+        if (!found) {
+            virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                 _("no disk named '%s'"), disk->name);
+            goto cleanup;
+        }
+    }
+
+    /* Provide defaults for all remaining disks.  */
+    if (VIR_EXPAND_N(def->disks, def->ndisks,
+                     def->dom->ndisks - def->ndisks) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    for (i = 0; i < def->dom->ndisks; i++) {
+        virDomainSnapshotDiskDefPtr disk;
+
+        ignore_value(virBitmapGetBit(map, i, &inuse));
+        if (inuse)
+            continue;
+        disk = &def->disks[def->ndisks++];
+        if (!(disk->name = strdup(def->dom->disks[i]->dst))) {
+            virReportOOMError();
+            goto cleanup;
+        }
+        disk->index = i;
+        disk->snapshot = def->dom->disks[i]->snapshot;
+        if (!disk->snapshot)
+            disk->snapshot = default_snapshot;
+    }
+
+    qsort(&def->disks[0], def->ndisks, sizeof(def->disks[0]), disksorter);
+
+    ret = 0;
+
+cleanup:
+    virBitmapFree(map);
+    return ret;
+}
+
 char *virDomainSnapshotDefFormat(char *domain_uuid,
                                  virDomainSnapshotDefPtr def,
                                  unsigned int flags,
                                  int internal)
 {
     virBuffer buf = VIR_BUFFER_INITIALIZER;
+    int i;

     virCheckFlags(VIR_DOMAIN_XML_SECURE, NULL);

@@ -11139,6 +11350,34 @@ char *virDomainSnapshotDefFormat(char *domain_uuid,
     }
     virBufferAsprintf(&buf, "  <creationTime>%lld</creationTime>\n",
                       def->creationTime);
+    /* For now, only output <disks> on disk-snapshot */
+    if (def->state == VIR_DOMAIN_DISK_SNAPSHOT) {
+        virBufferAddLit(&buf, "  <disks>\n");
+        for (i = 0; i < def->ndisks; i++) {
+            virDomainSnapshotDiskDefPtr disk = &def->disks[i];
+
+            if (!disk->name)
+                continue;
+
+            virBufferEscapeString(&buf, "    <disk name='%s'", disk->name);
+            if (disk->snapshot)
+                virBufferAsprintf(&buf, " snapshot='%s'",
+                                  virDomainDiskSnapshotTypeToString(disk->snapshot));
+            if (disk->file || disk->driverType) {
+                virBufferAddLit(&buf, ">\n");
+                if (disk->file)
+                    virBufferEscapeString(&buf, "      <source file='%s'/>\n",
+                                          disk->file);
+                if (disk->driverType)
+                    virBufferEscapeString(&buf, "      <driver type='%s'/>\n",
+                                          disk->driverType);
+                virBufferAddLit(&buf, "    </disk>\n");
+            } else {
+                virBufferAddLit(&buf, "/>\n");
+            }
+        }
+        virBufferAddLit(&buf, "  </disks>\n");
+    }
     if (def->dom) {
         virDomainDefFormatInternal(def->dom, flags, &buf);
     } else {
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index ea8194a..d835f96 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1308,7 +1308,20 @@ enum virDomainTaintFlags {
     VIR_DOMAIN_TAINT_LAST
 };

-/* Snapshot state */
+/* Items related to snapshot state */
+
+/* Stores disk-snapshot information */
+typedef struct _virDomainSnapshotDiskDef virDomainSnapshotDiskDef;
+typedef virDomainSnapshotDiskDef *virDomainSnapshotDiskDefPtr;
+struct _virDomainSnapshotDiskDef {
+    char *name; /* name matching the <target dev='...' of the domain */
+    int index; /* index within snapshot->dom->disks that matches name */
+    int snapshot; /* enum virDomainDiskSnapshot */
+    char *file; /* new source file when snapshot is external */
+    char *driverType; /* file format type of new file */
+};
+
+/* Stores the complete snapshot metadata */
 typedef struct _virDomainSnapshotDef virDomainSnapshotDef;
 typedef virDomainSnapshotDef *virDomainSnapshotDefPtr;
 struct _virDomainSnapshotDef {
@@ -1318,6 +1331,10 @@ struct _virDomainSnapshotDef {
     char *parent;
     long long creationTime; /* in seconds */
     int state; /* enum virDomainSnapshotState */
+
+    size_t ndisks; /* should not exceed dom->ndisks */
+    virDomainSnapshotDiskDef *disks;
+
     virDomainDefPtr dom;

     /* Internal use.  */
@@ -1351,6 +1368,9 @@ char *virDomainSnapshotDefFormat(char *domain_uuid,
                                  virDomainSnapshotDefPtr def,
                                  unsigned int flags,
                                  int internal);
+int virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr snapshot,
+                                int default_snapshot,
+                                bool require_match);
 virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots,
                                                    const virDomainSnapshotDefPtr def);

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 034443c..a953326 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -381,6 +381,7 @@ virDomainSmartcardDefForeach;
 virDomainSmartcardDefFree;
 virDomainSmartcardTypeFromString;
 virDomainSmartcardTypeToString;
+virDomainSnapshotAlignDisks;
 virDomainSnapshotAssignDef;
 virDomainSnapshotDefFormat;
 virDomainSnapshotDefFree;
diff --git a/tests/domainsnapshotxml2xmlin/disk_snapshot.xml b/tests/domainsnapshotxml2xmlin/disk_snapshot.xml
new file mode 100644
index 0000000..1f0beb6
--- /dev/null
+++ b/tests/domainsnapshotxml2xmlin/disk_snapshot.xml
@@ -0,0 +1,16 @@
+<domainsnapshot>
+  <name>my snap name</name>
+  <description>!@#$%^</description>
+  <disks>
+    <disk name='hda'/>
+    <disk name='hdb' snapshot='no'/>
+    <disk name='hdc' snapshot='internal'/>
+    <disk name='hdd' snapshot='external'>
+      <source/>
+      <driver type='qed'/>
+    </disk>
+    <disk name='hde' snapshot='external'>
+      <source file='/path/to/new'/>
+    </disk>
+  </disks>
+</domainsnapshot>
diff --git a/tests/domainsnapshotxml2xmlout/disk_snapshot.xml b/tests/domainsnapshotxml2xmlout/disk_snapshot.xml
index 391bb57..e0414a1 100644
--- a/tests/domainsnapshotxml2xmlout/disk_snapshot.xml
+++ b/tests/domainsnapshotxml2xmlout/disk_snapshot.xml
@@ -6,6 +6,23 @@
   </parent>
   <state>disk-snapshot</state>
   <creationTime>1272917631</creationTime>
+  <disks>
+    <disk name='hda' snapshot='no'/>
+    <disk name='hdb' snapshot='no'/>
+    <disk name='hdc' snapshot='internal'/>
+    <disk name='hdd' snapshot='external'>
+      <driver type='qed'/>
+      <source file='/path/to/generated4'/>
+    </disk>
+    <disk name='hde' snapshot='external'>
+      <driver type='qcow2'/>
+      <source file='/path/to/new'/>
+    </disk>
+    <disk name='hdf' snapshot='external'>
+      <driver type='qcow2'/>
+      <source file='/path/to/generated5'/>
+    </disk>
+  </disks>
 <domain type='qemu'>
   <name>QEMUGuest1</name>
   <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
@@ -27,6 +44,31 @@
       <target dev='hda' bus='ide'/>
       <address type='drive' controller='0' bus='0' unit='0'/>
     </disk>
+    <disk type='block' device='disk'>
+      <source dev='/dev/HostVG/QEMUGuest2'/>
+      <target dev='hdb' bus='ide'/>
+      <address type='drive' controller='0' bus='1' unit='0'/>
+    </disk>
+    <disk type='block' device='disk'>
+      <source dev='/dev/HostVG/QEMUGuest3'/>
+      <target dev='hdc' bus='ide'/>
+      <address type='drive' controller='0' bus='2' unit='0'/>
+    </disk>
+    <disk type='block' device='disk'>
+      <source dev='/dev/HostVG/QEMUGuest4'/>
+      <target dev='hdd' bus='ide'/>
+      <address type='drive' controller='0' bus='3' unit='0'/>
+    </disk>
+    <disk type='block' device='disk'>
+      <source dev='/dev/HostVG/QEMUGuest5'/>
+      <target dev='hde' bus='ide'/>
+      <address type='drive' controller='0' bus='4' unit='0'/>
+    </disk>
+    <disk type='block' device='disk'>
+      <source dev='/dev/HostVG/QEMUGuest6'/>
+      <target dev='hdf' bus='ide'/>
+      <address type='drive' controller='0' bus='5' unit='0'/>
+    </disk>
     <controller type='ide' index='0'/>
     <memballoon model='virtio'/>
   </devices>
-- 
1.7.4.4




More information about the libvir-list mailing list