mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-07-11 12:25:52 +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>
|
<p>
|
||||||
Attributes of libvirt snapshots are stored as child elements of
|
Attributes of libvirt snapshots are stored as child elements of
|
||||||
the <code>domainsnapshot</code> element. At snapshot creation
|
the <code>domainsnapshot</code> element. At snapshot creation
|
||||||
time, normally only the <code>name</code>
|
time, normally only the <code>name</code>, <code>description</code>,
|
||||||
and <code>description</code> elements are settable; the rest of
|
and <code>disks</code> elements are settable; the rest of the
|
||||||
the fields are ignored on creation, and will be filled in by
|
fields are ignored on creation, and will be filled in by
|
||||||
libvirt in for informational purposes
|
libvirt in for informational purposes
|
||||||
by <code>virDomainSnapshotGetXMLDesc()</code>. However, when
|
by <code>virDomainSnapshotGetXMLDesc()</code>. However, when
|
||||||
redefining a snapshot (<span class="since">since 0.9.5</span>),
|
redefining a snapshot (<span class="since">since 0.9.5</span>),
|
||||||
@ -106,6 +106,58 @@
|
|||||||
description is omitted when initially creating the snapshot,
|
description is omitted when initially creating the snapshot,
|
||||||
then this field will be empty.
|
then this field will be empty.
|
||||||
</dd>
|
</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>
|
<dt><code>creationTime</code></dt>
|
||||||
<dd>The time this snapshot was created. The time is specified
|
<dd>The time this snapshot was created. The time is specified
|
||||||
in seconds since the Epoch, UTC (i.e. Unix time). Readonly.
|
in seconds since the Epoch, UTC (i.e. Unix time). Readonly.
|
||||||
@ -145,14 +197,21 @@
|
|||||||
|
|
||||||
<h2><a name="example">Examples</a></h2>
|
<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>
|
<pre>
|
||||||
<domainsnapshot>
|
<domainsnapshot>
|
||||||
<description>Snapshot of OS install and updates</description>
|
<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>
|
</domainsnapshot></pre>
|
||||||
|
|
||||||
<p>will result in XML similar to this from
|
<p>will result in XML similar to this from
|
||||||
virDomainSnapshotGetXMLDesc:</p>
|
<code>virDomainSnapshotGetXMLDesc()</code>:</p>
|
||||||
<pre>
|
<pre>
|
||||||
<domainsnapshot>
|
<domainsnapshot>
|
||||||
<name>1270477159</name>
|
<name>1270477159</name>
|
||||||
@ -162,13 +221,61 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<name>bare-os-install</name>
|
<name>bare-os-install</name>
|
||||||
</parent>
|
</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>
|
<domain>
|
||||||
<name>fedora</name>
|
<name>fedora</name>
|
||||||
<uuid>93a5c045-6457-2c09-e56c-927cdf34e178</uuid>
|
<uuid>93a5c045-6457-2c09-e56c-927cdf34e178</uuid>
|
||||||
<memory>1048576</memory>
|
<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>
|
</devices>
|
||||||
</domain>
|
</domain>
|
||||||
</domainsnapshot></pre>
|
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -30,6 +30,13 @@
|
|||||||
<text/>
|
<text/>
|
||||||
</element>
|
</element>
|
||||||
</optional>
|
</optional>
|
||||||
|
<optional>
|
||||||
|
<element name='disks'>
|
||||||
|
<zeroOrMore>
|
||||||
|
<ref name='disksnapshot'/>
|
||||||
|
</zeroOrMore>
|
||||||
|
</element>
|
||||||
|
</optional>
|
||||||
<optional>
|
<optional>
|
||||||
<element name='active'>
|
<element name='active'>
|
||||||
<choice>
|
<choice>
|
||||||
@ -72,4 +79,49 @@
|
|||||||
</choice>
|
</choice>
|
||||||
</define>
|
</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>
|
</grammar>
|
||||||
|
@ -11411,18 +11411,82 @@ cleanup:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Snapshot Def functions */
|
/* Snapshot Def functions */
|
||||||
|
static void
|
||||||
|
virDomainSnapshotDiskDefClear(virDomainSnapshotDiskDefPtr disk)
|
||||||
|
{
|
||||||
|
VIR_FREE(disk->name);
|
||||||
|
VIR_FREE(disk->file);
|
||||||
|
VIR_FREE(disk->driverType);
|
||||||
|
}
|
||||||
|
|
||||||
void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def)
|
void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
if (!def)
|
if (!def)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
VIR_FREE(def->name);
|
VIR_FREE(def->name);
|
||||||
VIR_FREE(def->description);
|
VIR_FREE(def->description);
|
||||||
VIR_FREE(def->parent);
|
VIR_FREE(def->parent);
|
||||||
|
for (i = 0; i < def->ndisks; i++)
|
||||||
|
virDomainSnapshotDiskDefClear(&def->disks[i]);
|
||||||
|
VIR_FREE(def->disks);
|
||||||
virDomainDefFree(def->dom);
|
virDomainDefFree(def->dom);
|
||||||
VIR_FREE(def);
|
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.
|
/* flags is bitwise-or of virDomainSnapshotParseFlags.
|
||||||
* If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then
|
* If flags does not include VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE, then
|
||||||
* caps and expectedVirtTypes are ignored.
|
* caps and expectedVirtTypes are ignored.
|
||||||
@ -11437,6 +11501,8 @@ virDomainSnapshotDefParseString(const char *xmlStr,
|
|||||||
xmlDocPtr xml = NULL;
|
xmlDocPtr xml = NULL;
|
||||||
virDomainSnapshotDefPtr def = NULL;
|
virDomainSnapshotDefPtr def = NULL;
|
||||||
virDomainSnapshotDefPtr ret = NULL;
|
virDomainSnapshotDefPtr ret = NULL;
|
||||||
|
xmlNodePtr *nodes = NULL;
|
||||||
|
int i;
|
||||||
char *creation = NULL, *state = NULL;
|
char *creation = NULL, *state = NULL;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
int active;
|
int active;
|
||||||
@ -11478,6 +11544,25 @@ virDomainSnapshotDefParseString(const char *xmlStr,
|
|||||||
|
|
||||||
def->description = virXPathString("string(./description)", ctxt);
|
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 (flags & VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE) {
|
||||||
if (virXPathLongLong("string(./creationTime)", ctxt,
|
if (virXPathLongLong("string(./creationTime)", ctxt,
|
||||||
&def->creationTime) < 0) {
|
&def->creationTime) < 0) {
|
||||||
@ -11545,6 +11630,7 @@ virDomainSnapshotDefParseString(const char *xmlStr,
|
|||||||
cleanup:
|
cleanup:
|
||||||
VIR_FREE(creation);
|
VIR_FREE(creation);
|
||||||
VIR_FREE(state);
|
VIR_FREE(state);
|
||||||
|
VIR_FREE(nodes);
|
||||||
xmlXPathFreeContext(ctxt);
|
xmlXPathFreeContext(ctxt);
|
||||||
if (ret == NULL)
|
if (ret == NULL)
|
||||||
virDomainSnapshotDefFree(def);
|
virDomainSnapshotDefFree(def);
|
||||||
@ -11553,12 +11639,178 @@ cleanup:
|
|||||||
return ret;
|
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,
|
char *virDomainSnapshotDefFormat(char *domain_uuid,
|
||||||
virDomainSnapshotDefPtr def,
|
virDomainSnapshotDefPtr def,
|
||||||
unsigned int flags,
|
unsigned int flags,
|
||||||
int internal)
|
int internal)
|
||||||
{
|
{
|
||||||
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||||
|
int i;
|
||||||
|
|
||||||
virCheckFlags(VIR_DOMAIN_XML_SECURE, NULL);
|
virCheckFlags(VIR_DOMAIN_XML_SECURE, NULL);
|
||||||
|
|
||||||
@ -11578,6 +11830,34 @@ char *virDomainSnapshotDefFormat(char *domain_uuid,
|
|||||||
}
|
}
|
||||||
virBufferAsprintf(&buf, " <creationTime>%lld</creationTime>\n",
|
virBufferAsprintf(&buf, " <creationTime>%lld</creationTime>\n",
|
||||||
def->creationTime);
|
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) {
|
if (def->dom) {
|
||||||
virDomainDefFormatInternal(def->dom, flags, &buf);
|
virDomainDefFormatInternal(def->dom, flags, &buf);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1391,7 +1391,20 @@ enum virDomainTaintFlags {
|
|||||||
VIR_DOMAIN_TAINT_LAST
|
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 struct _virDomainSnapshotDef virDomainSnapshotDef;
|
||||||
typedef virDomainSnapshotDef *virDomainSnapshotDefPtr;
|
typedef virDomainSnapshotDef *virDomainSnapshotDefPtr;
|
||||||
struct _virDomainSnapshotDef {
|
struct _virDomainSnapshotDef {
|
||||||
@ -1401,6 +1414,10 @@ struct _virDomainSnapshotDef {
|
|||||||
char *parent;
|
char *parent;
|
||||||
long long creationTime; /* in seconds */
|
long long creationTime; /* in seconds */
|
||||||
int state; /* enum virDomainSnapshotState */
|
int state; /* enum virDomainSnapshotState */
|
||||||
|
|
||||||
|
size_t ndisks; /* should not exceed dom->ndisks */
|
||||||
|
virDomainSnapshotDiskDef *disks;
|
||||||
|
|
||||||
virDomainDefPtr dom;
|
virDomainDefPtr dom;
|
||||||
|
|
||||||
/* Internal use. */
|
/* Internal use. */
|
||||||
@ -1426,7 +1443,8 @@ struct _virDomainSnapshotObjList {
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
VIR_DOMAIN_SNAPSHOT_PARSE_REDEFINE = 1 << 0,
|
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;
|
} virDomainSnapshotParseFlags;
|
||||||
|
|
||||||
virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr,
|
virDomainSnapshotDefPtr virDomainSnapshotDefParseString(const char *xmlStr,
|
||||||
@ -1438,6 +1456,9 @@ char *virDomainSnapshotDefFormat(char *domain_uuid,
|
|||||||
virDomainSnapshotDefPtr def,
|
virDomainSnapshotDefPtr def,
|
||||||
unsigned int flags,
|
unsigned int flags,
|
||||||
int internal);
|
int internal);
|
||||||
|
int virDomainSnapshotAlignDisks(virDomainSnapshotDefPtr snapshot,
|
||||||
|
int default_snapshot,
|
||||||
|
bool require_match);
|
||||||
virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots,
|
virDomainSnapshotObjPtr virDomainSnapshotAssignDef(virDomainSnapshotObjListPtr snapshots,
|
||||||
const virDomainSnapshotDefPtr def);
|
const virDomainSnapshotDefPtr def);
|
||||||
|
|
||||||
|
@ -388,6 +388,7 @@ virDomainSmartcardDefForeach;
|
|||||||
virDomainSmartcardDefFree;
|
virDomainSmartcardDefFree;
|
||||||
virDomainSmartcardTypeFromString;
|
virDomainSmartcardTypeFromString;
|
||||||
virDomainSmartcardTypeToString;
|
virDomainSmartcardTypeToString;
|
||||||
|
virDomainSnapshotAlignDisks;
|
||||||
virDomainSnapshotAssignDef;
|
virDomainSnapshotAssignDef;
|
||||||
virDomainSnapshotDefFormat;
|
virDomainSnapshotDefFormat;
|
||||||
virDomainSnapshotDefFree;
|
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>
|
</parent>
|
||||||
<state>disk-snapshot</state>
|
<state>disk-snapshot</state>
|
||||||
<creationTime>1272917631</creationTime>
|
<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'>
|
<domain type='qemu'>
|
||||||
<name>QEMUGuest1</name>
|
<name>QEMUGuest1</name>
|
||||||
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
|
<uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
|
||||||
@ -27,6 +44,31 @@
|
|||||||
<target dev='hda' bus='ide'/>
|
<target dev='hda' bus='ide'/>
|
||||||
<address type='drive' controller='0' bus='0' unit='0'/>
|
<address type='drive' controller='0' bus='0' unit='0'/>
|
||||||
</disk>
|
</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'/>
|
<controller type='ide' index='0'/>
|
||||||
<memballoon model='virtio'/>
|
<memballoon model='virtio'/>
|
||||||
</devices>
|
</devices>
|
||||||
|
Loading…
Reference in New Issue
Block a user