mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 13:45:38 +00:00
snapshot: add <disks> to snapshot xml
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. For now, no driver accepts this element; that will come later. <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.
This commit is contained in:
parent
5b30b08d66
commit
d6f6b2d194
@ -68,9 +68,9 @@
|
||||
<p>
|
||||
Attributes of libvirt snapshots are stored as child elements of
|
||||
the <code>domainsnapshot</code> element. At snapshot creation
|
||||
time, normally only the <code>name</code>
|
||||
and <code>description</code> elements are settable; the rest of
|
||||
the fields are ignored on creation, and will be filled in by
|
||||
time, normally only the <code>name</code>, <code>description</code>,
|
||||
and <code>disks</code> elements are settable; the rest of the
|
||||
fields are ignored on creation, and will be filled in by
|
||||
libvirt in for informational purposes
|
||||
by <code>virDomainSnapshotGetXMLDesc()</code>. However, when
|
||||
redefining a snapshot (<span class="since">since 0.9.5</span>),
|
||||
@ -106,6 +106,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.
|
||||
@ -145,14 +197,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>
|
||||
<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>
|
||||
<code>virDomainSnapshotGetXMLDesc()</code>:</p>
|
||||
<pre>
|
||||
<domainsnapshot>
|
||||
<name>1270477159</name>
|
||||
@ -162,13 +221,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>
|
||||
|
@ -30,6 +30,13 @@
|
||||
<text/>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name='disks'>
|
||||
<zeroOrMore>
|
||||
<ref name='disksnapshot'/>
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
</optional>
|
||||
<optional>
|
||||
<element name='active'>
|
||||
<choice>
|
||||
@ -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>
|
||||
|
@ -11411,18 +11411,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;
|
||||
}
|
||||
|
||||
/* flags is bitwise-or of virDomainSnapshotParseFlags.
|
||||
* If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then
|
||||
* caps and expectedVirtTypes are ignored.
|
||||
@ -11437,6 +11501,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;
|
||||
@ -11478,6 +11544,25 @@ virDomainSnapshotDefParseString(const char *xmlStr,
|
||||
|
||||
def->description = virXPathString("string(./description)", ctxt);
|
||||
|
||||
if ((i = virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0)
|
||||
goto cleanup;
|
||||
if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_DISKS) {
|
||||
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);
|
||||
} else {
|
||||
virDomainReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
|
||||
_("unable to handle disk requests in snapshot"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
|
||||
if (virXPathLongLong("string(./creationTime)", ctxt,
|
||||
&def->creationTime) < 0) {
|
||||
@ -11545,6 +11630,7 @@ virDomainSnapshotDefParseString(const char *xmlStr,
|
||||
cleanup:
|
||||
VIR_FREE(creation);
|
||||
VIR_FREE(state);
|
||||
VIR_FREE(nodes);
|
||||
xmlXPathFreeContext(ctxt);
|
||||
if (ret == NULL)
|
||||
virDomainSnapshotDefFree(def);
|
||||
@ -11553,12 +11639,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);
|
||||
|
||||
@ -11578,6 +11830,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 {
|
||||
|
@ -1391,7 +1391,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 {
|
||||
@ -1401,6 +1414,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. */
|
||||
@ -1426,7 +1443,8 @@ struct _virDomainSnapshotObjList {
|
||||
|
||||
typedef enum {
|
||||
VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE = 1 << 0,
|
||||
VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL = 1 << 1,
|
||||
VIR_DOMAIN_SNAPSHOT_PARSE_DISKS = 1 << 1,
|
||||
VIR_DOMAIN_SNAPSHOT_PARSE_INTERNAL = 1 << 2,
|
||||
} virDomainSnapshotParseFlags;
|
||||
|
||||
virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr,
|
||||
@ -1438,6 +1456,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);
|
||||
|
||||
|
@ -388,6 +388,7 @@ virDomainSmartcardDefForeach;
|
||||
virDomainSmartcardDefFree;
|
||||
virDomainSmartcardTypeFromString;
|
||||
virDomainSmartcardTypeToString;
|
||||
virDomainSnapshotAlignDisks;
|
||||
virDomainSnapshotAssignDef;
|
||||
virDomainSnapshotDefFormat;
|
||||
virDomainSnapshotDefFree;
|
||||
|
16
tests/domainsnapshotxml2xmlin/disk_snapshot.xml
Normal file
16
tests/domainsnapshotxml2xmlin/disk_snapshot.xml
Normal file
@ -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>
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user