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.
---
v2: squash in several fixes, documented over several replies to v1
docs/formatsnapshot.html.in | 126 +++++++++-
docs/schemas/domainsnapshot.rng | 52 ++++
src/conf/domain_conf.c | 274 ++++++++++++++++++++++
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, 522 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..953272c 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,58 @@
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. If <code>source</code> is not
+ given, a file name is generated that consists of the
+ existing file name with anything after the trailing dot
+ replaced by the snapshot name. 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 +176,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 +200,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..bfa6301 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,178 @@ cleanup:
return ret;
}
+static int
+disksorter(const void *a, const void *b)
+{
+ const virDomainSnapshotDiskDef *diska = a;
+ const virDomainSnapshotDiskDef *diskb = b;
+
+ /* Integer overflow shouldn't be a problem here. */
+ return diska->index - diskb->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 ndisks;
+ 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];
+ int idx = virDomainDiskIndexByName(def->dom, disk->name);
+ int disk_snapshot;
+
+ if (idx < 0) {
+ virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("no disk named '%s'"),
disk->name);
+ goto cleanup;
+ }
+ disk_snapshot = def->dom->disks[idx]->snapshot;
+
+ if (virBitmapGetBit(map, idx, &inuse) < 0 || inuse) {
+ virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("disk '%s' specified twice"),
+ disk->name);
+ goto cleanup;
+ }
+ ignore_value(virBitmapSetBit(map, idx));
+ disk->index = idx;
+ if (!disk_snapshot)
+ disk_snapshot = default_snapshot;
+ if (!disk->snapshot) {
+ disk->snapshot = disk_snapshot;
+ } else if (disk_snapshot && require_match &&
+ disk->snapshot != disk_snapshot) {
+ const char *tmp = virDomainDiskSnapshotTypeToString(disk_snapshot);
+ virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("disk '%s' must use snapshot mode
'%s'"),
+ disk->name, tmp);
+ 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;
+ }
+ }
+
+ /* Provide defaults for all remaining disks. */
+ ndisks = def->ndisks;
+ 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[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);
+
+ /* Generate any default external file names. */
+ for (i = 0; i < def->ndisks; i++) {
+ virDomainSnapshotDiskDefPtr disk = &def->disks[i];
+
+ if (disk->snapshot == VIR_DOMAIN_DISK_SNAPSHOT_EXTERNAL &&
+ !disk->file) {
+ const char *original = def->dom->disks[i]->src;
+ const char *tmp;
+
+ if (!original) {
+ virDomainReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("cannot generate external backup name "
+ "for disk '%s' without
source"),
+ disk->name);
+ goto cleanup;
+ }
+ tmp = strrchr(original, '.');
+ if (!tmp || strchr(tmp, '/')) {
+ ignore_value(virAsprintf(&disk->file, "%s.%s",
+ original, def->name));
+ } else {
+ if ((tmp - original) > INT_MAX) {
+ virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("integer overflow"));
+ goto cleanup;
+ }
+ ignore_value(virAsprintf(&disk->file, "%.*s%s",
+ (int) (tmp - original), original,
+ def->name));
+ }
+ if (!disk->file) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ }
+ }
+
+ 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 +11385,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