Some storage systems have internal support for snapshots. Libvirt should
be able to select a correct snapshot when starting a VM.
This patch adds a XML element to select a storage source snapshot for
the RBD protocol which supports this feature.
---
docs/formatdomain.html.in | 18 +++++++---
docs/schemas/domaincommon.rng | 8 +++++
src/conf/domain_conf.c | 38 +++++++++++++++++++---
src/conf/domain_conf.h | 1 +
src/conf/snapshot_conf.c | 6 ++--
src/qemu/qemu_command.c | 3 ++
src/util/virstoragefile.c | 8 +++++
src/util/virstoragefile.h | 1 +
.../qemuxml2argv-disk-drive-network-rbd.args | 4 +++
.../qemuxml2argv-disk-drive-network-rbd.xml | 17 ++++++++++
10 files changed, 94 insertions(+), 10 deletions(-)
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index 4f44bc0..fc35c5a 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1676,6 +1676,7 @@
<driver name="qemu" type="raw"/>
<source protocol="rbd" name="image_name2">
<host name="hostname" port="7000"/>
+ <snapshot name="snapname"/>
</source>
<target dev="hdc" bus="ide"/>
<auth username='myuser'>
@@ -1949,14 +1950,16 @@
is only valid when the specified storage volume is of 'file' or
'block' type).
<p>
- When the disk <code>type</code> is "network", the
<code>source</code>
- may have zero or more <code>host</code> sub-elements used to
- specify the hosts to connect.
+ The <code>source</code> element may contain the following sub
elements:
</p>
<dl>
<dt><code>host</code></dt>
- <dd>The <code>host</code> element supports 4 attributes, viz.
"name",
+ <dd>When the disk <code>type</code> is "network",
the <code>source</code>
+ may have zero or more <code>host</code> sub-elements used to
+ specify the hosts to connect.
+
+ The <code>host</code> element supports 4 attributes, viz.
"name",
"port", "transport" and "socket", which specify
the hostname,
the port number, transport type and path to socket, respectively.
The meaning of this element and the number of the elements depend
@@ -2005,6 +2008,13 @@
transport is "unix", the socket attribute specifies the path to an
AF_UNIX socket.
</dd>
+ <dt><code>snapshot</code></dt>
+ <dd>
+ The <code>name</code> attribute of
<code>snapshot</code> element can
+ optionally specify an internal snapshot name to be used as the
+ source for storage systems such as rbd.
+ (<span class="since">Since 1.2.11 for the qemu
driver.</span>)
+ </dd>
</dl>
<p>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 6863ec6..154d222 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -1452,6 +1452,14 @@
</choice>
</element>
</zeroOrMore>
+ <optional>
+ <element name="snapshot">
+ <attribute name="name">
+ <ref name="genericName"/>
+ </attribute>
+ <empty/>
+ </element>
+ </optional>
<empty/>
</element>
</interleave>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 2c65276..37a8042 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -3162,6 +3162,22 @@ virDomainDeviceDefPostParseInternal(virDomainDeviceDefPtr dev,
return -1;
}
+ /* verify disk source */
+ if (dev->type == VIR_DOMAIN_DEVICE_DISK) {
+ virDomainDiskDefPtr disk = dev->data.disk;
+
+ /* internal snapshots are currently supported only with rbd: */
+ if (virStorageSourceGetActualType(disk->src) != VIR_STORAGE_TYPE_NETWORK
&&
+ disk->src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD) {
+ if (disk->src->snapshot) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("<snapshot> element is currently supported
"
+ "only with 'rbd' disks"));
+ return -1;
+ }
+ }
+ }
+
return 0;
}
@@ -5316,10 +5332,14 @@ virDomainDiskSourcePoolDefParse(xmlNodePtr node,
int
virDomainDiskSourceParse(xmlNodePtr node,
+ xmlXPathContextPtr ctxt,
virStorageSourcePtr src)
{
int ret = -1;
char *protocol = NULL;
+ xmlNodePtr saveNode = ctxt->node;
+
+ ctxt->node = node;
switch ((virStorageType)src->type) {
case VIR_STORAGE_TYPE_FILE:
@@ -5372,6 +5392,9 @@ virDomainDiskSourceParse(xmlNodePtr node,
tmp[0] = '\0';
}
+ /* snapshot currently works only for remote disks */
+ src->snapshot = virXPathString("string(./snapshot/@name)", ctxt);
+
if (virDomainStorageHostParse(node, &src->hosts, &src->nhosts) <
0)
goto cleanup;
break;
@@ -5397,6 +5420,7 @@ virDomainDiskSourceParse(xmlNodePtr node,
cleanup:
VIR_FREE(protocol);
+ ctxt->node = saveNode;
return ret;
}
@@ -5452,7 +5476,7 @@ virDomainDiskBackingStoreParse(xmlXPathContextPtr ctxt,
goto cleanup;
}
- if (virDomainDiskSourceParse(source, backingStore) < 0 ||
+ if (virDomainDiskSourceParse(source, ctxt, backingStore) < 0 ||
virDomainDiskBackingStoreParse(ctxt, backingStore) < 0)
goto cleanup;
@@ -5562,7 +5586,7 @@ virDomainDiskDefParseXML(virDomainXMLOptionPtr xmlopt,
xmlStrEqual(cur->name, BAD_CAST "source")) {
sourceNode = cur;
- if (virDomainDiskSourceParse(cur, def->src) < 0)
+ if (virDomainDiskSourceParse(cur, ctxt, def->src) < 0)
goto error;
source = def->src->path;
@@ -5728,7 +5752,8 @@ virDomainDiskDefParseXML(virDomainXMLOptionPtr xmlopt,
_("mirror requires source element"));
goto error;
}
- if (virDomainDiskSourceParse(mirrorNode, def->mirror) < 0)
+ if (virDomainDiskSourceParse(mirrorNode, ctxt,
+ def->mirror) < 0)
goto error;
}
ready = virXMLPropString(cur, "ready");
@@ -16154,11 +16179,12 @@ virDomainDiskSourceFormatInternal(virBufferPtr buf,
VIR_FREE(path);
- if (src->nhosts == 0) {
+ if (src->nhosts == 0 && !src->snapshot) {
virBufferAddLit(buf, "/>\n");
} else {
virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2);
+
for (n = 0; n < src->nhosts; n++) {
virBufferAddLit(buf, "<host");
virBufferEscapeString(buf, " name='%s'",
@@ -16175,6 +16201,10 @@ virDomainDiskSourceFormatInternal(virBufferPtr buf,
virBufferAddLit(buf, "/>\n");
}
+
+ virBufferEscapeString(buf, "<snapshot
name='%s'/>\n",
+ src->snapshot);
+
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</source>\n");
}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 530a3ca..acbf13b 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -2477,6 +2477,7 @@ virDomainDiskRemove(virDomainDefPtr def, size_t i);
virDomainDiskDefPtr
virDomainDiskRemoveByName(virDomainDefPtr def, const char *name);
int virDomainDiskSourceParse(xmlNodePtr node,
+ xmlXPathContextPtr ctxt,
virStorageSourcePtr src);
bool virDomainHasDiskMirror(virDomainObjPtr vm);
diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c
index 1f83b2c..66fc2e6 100644
--- a/src/conf/snapshot_conf.c
+++ b/src/conf/snapshot_conf.c
@@ -107,6 +107,7 @@ void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def)
static int
virDomainSnapshotDiskDefParseXML(xmlNodePtr node,
+ xmlXPathContextPtr ctxt,
virDomainSnapshotDiskDefPtr def)
{
int ret = -1;
@@ -154,7 +155,7 @@ virDomainSnapshotDiskDefParseXML(xmlNodePtr node,
if (!def->src->path &&
xmlStrEqual(cur->name, BAD_CAST "source")) {
- if (virDomainDiskSourceParse(cur, def->src) < 0)
+ if (virDomainDiskSourceParse(cur, ctxt, def->src) < 0)
goto cleanup;
} else if (!def->src->format &&
@@ -352,7 +353,8 @@ virDomainSnapshotDefParse(xmlXPathContextPtr ctxt,
goto cleanup;
def->ndisks = n;
for (i = 0; i < def->ndisks; i++) {
- if (virDomainSnapshotDiskDefParseXML(nodes[i], &def->disks[i]) <
0)
+ if (virDomainSnapshotDiskDefParseXML(nodes[i], ctxt,
+ &def->disks[i]) < 0)
goto cleanup;
}
VIR_FREE(nodes);
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 021ec07..7923842 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -2988,6 +2988,9 @@ qemuBuildNetworkDriveURI(virStorageSourcePtr src,
virBufferStrcat(&buf, "rbd:", src->path, NULL);
+ if (src->snapshot)
+ virBufferEscape(&buf, '\\', ":", "@%s",
src->snapshot);
+
if (username) {
virBufferEscape(&buf, '\\', ":",
":id=%s", username);
virBufferEscape(&buf, '\\', ":",
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
index bc7c51d..efd51d2 100644
--- a/src/util/virstoragefile.c
+++ b/src/util/virstoragefile.c
@@ -1849,6 +1849,7 @@ virStorageSourceCopy(const virStorageSource *src,
VIR_STRDUP(ret->driverName, src->driverName) < 0 ||
VIR_STRDUP(ret->relPath, src->relPath) < 0 ||
VIR_STRDUP(ret->backingStoreRaw, src->backingStoreRaw) < 0 ||
+ VIR_STRDUP(ret->snapshot, src->snapshot) < 0 ||
VIR_STRDUP(ret->compat, src->compat) < 0)
goto error;
@@ -2280,6 +2281,13 @@ virStorageSourceParseRBDColonString(const char *rbdstr,
*p = '\0';
}
+ /* snapshot name */
+ if ((p = strchr(src->path, '@'))) {
+ if (VIR_STRDUP(src->snapshot, p + 1) < 0)
+ goto error;
+ *p = '\0';
+ }
+
/* options */
if (!options)
return 0; /* all done */
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
index b1ba73a..caab0b4 100644
--- a/src/util/virstoragefile.h
+++ b/src/util/virstoragefile.h
@@ -238,6 +238,7 @@ struct _virStorageSource {
char *path;
int protocol; /* virStorageNetProtocol */
char *volume; /* volume name for remote storage */
+ char *snapshot; /* for storage systems supporting internal snapshots */
size_t nhosts;
virStorageNetHostDefPtr hosts;
virStorageSourcePoolDefPtr srcpool;
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-disk-drive-network-rbd.args
b/tests/qemuxml2argvdata/qemuxml2argv-disk-drive-network-rbd.args
index 21d7b64..e4f1389 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-disk-drive-network-rbd.args
+++ b/tests/qemuxml2argvdata/qemuxml2argv-disk-drive-network-rbd.args
@@ -5,4 +5,8 @@ unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -usb \
-drive 'file=rbd:pool/image:auth_supported=none:\
mon_host=mon1.example.org\:6321\;mon2.example.org\:6322\;\
mon3.example.org\:6322,if=virtio,format=raw' \
+-drive file=rbd:pool/image@asdf:auth_supported=none,if=virtio,format=raw \
+-drive 'file=rbd:pool/image@foo:auth_supported=none:\
+mon_host=mon1.example.org\:6321\;mon2.example.org\:6322\;\
+mon3.example.org\:6322,if=virtio,format=raw' \
-net none -serial none -parallel none
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-disk-drive-network-rbd.xml
b/tests/qemuxml2argvdata/qemuxml2argv-disk-drive-network-rbd.xml
index 37e9db5..f6accd8 100644
--- a/tests/qemuxml2argvdata/qemuxml2argv-disk-drive-network-rbd.xml
+++ b/tests/qemuxml2argvdata/qemuxml2argv-disk-drive-network-rbd.xml
@@ -29,6 +29,23 @@
</source>
<target dev='vda' bus='virtio'/>
</disk>
+ <disk type='network' device='disk'>
+ <driver name='qemu' type='raw'/>
+ <source protocol='rbd' name='pool/image'>
+ <snapshot name='asdf'/>
+ </source>
+ <target dev='vdb' bus='virtio'/>
+ </disk>
+ <disk type='network' device='disk'>
+ <driver name='qemu' type='raw'/>
+ <source protocol='rbd' name='pool/image'>
+ <host name='mon1.example.org' port='6321'/>
+ <host name='mon2.example.org' port='6322'/>
+ <host name='mon3.example.org' port='6322'/>
+ <snapshot name='foo'/>
+ </source>
+ <target dev='vdc' bus='virtio'/>
+ </disk>
<controller type='usb' index='0'/>
<controller type='ide' index='0'/>
<controller type='pci' index='0' model='pci-root'/>
--
2.1.0