storage: rbd: qemu: Add support for specifying internal RBD snapshots

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.
This commit is contained in:
Peter Krempa 2014-11-11 11:35:25 +01:00
parent 930b77598b
commit 0255660658
10 changed files with 93 additions and 9 deletions

View File

@ -1679,6 +1679,7 @@
<driver name="qemu" type="raw"/> <driver name="qemu" type="raw"/>
<source protocol="rbd" name="image_name2"> <source protocol="rbd" name="image_name2">
<host name="hostname" port="7000"/> <host name="hostname" port="7000"/>
<snapshot name="snapname"/>
</source> </source>
<target dev="hdc" bus="ide"/> <target dev="hdc" bus="ide"/>
<auth username='myuser'> <auth username='myuser'>
@ -1952,15 +1953,17 @@
is only valid when the specified storage volume is of 'file' or is only valid when the specified storage volume is of 'file' or
'block' type). 'block' type).
<p> <p>
When the disk <code>type</code> is "network", the <code>source</code> The <code>source</code> element may contain the following sub elements:
may have zero or more <code>host</code> sub-elements used to
specify the hosts to connect.
</p> </p>
<dl> <dl>
<dt><code>host</code></dt> <dt><code>host</code></dt>
<dd> <dd>
<p> <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>host</code> element supports 4 attributes, viz. "name", The <code>host</code> element supports 4 attributes, viz. "name",
"port", "transport" and "socket", which specify the hostname, "port", "transport" and "socket", which specify the hostname,
the port number, transport type and path to socket, respectively. the port number, transport type and path to socket, respectively.
@ -2013,6 +2016,13 @@
AF_UNIX socket. AF_UNIX socket.
</p> </p>
</dd> </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 protocols.
Supported for 'rbd' <span class="since">since 1.2.11 (QEMU only).</span>
</dd>
</dl> </dl>
<p> <p>

View File

@ -1452,6 +1452,14 @@
</choice> </choice>
</element> </element>
</zeroOrMore> </zeroOrMore>
<optional>
<element name="snapshot">
<attribute name="name">
<ref name="genericName"/>
</attribute>
<empty/>
</element>
</optional>
<empty/> <empty/>
</element> </element>
</interleave> </interleave>

View File

@ -3165,6 +3165,22 @@ virDomainDeviceDefPostParseInternal(virDomainDeviceDefPtr dev,
return -1; 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; return 0;
} }
@ -5316,10 +5332,14 @@ virDomainDiskSourcePoolDefParse(xmlNodePtr node,
int int
virDomainDiskSourceParse(xmlNodePtr node, virDomainDiskSourceParse(xmlNodePtr node,
xmlXPathContextPtr ctxt,
virStorageSourcePtr src) virStorageSourcePtr src)
{ {
int ret = -1; int ret = -1;
char *protocol = NULL; char *protocol = NULL;
xmlNodePtr saveNode = ctxt->node;
ctxt->node = node;
switch ((virStorageType)src->type) { switch ((virStorageType)src->type) {
case VIR_STORAGE_TYPE_FILE: case VIR_STORAGE_TYPE_FILE:
@ -5372,6 +5392,9 @@ virDomainDiskSourceParse(xmlNodePtr node,
tmp[0] = '\0'; 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) if (virDomainStorageHostParse(node, &src->hosts, &src->nhosts) < 0)
goto cleanup; goto cleanup;
break; break;
@ -5397,6 +5420,7 @@ virDomainDiskSourceParse(xmlNodePtr node,
cleanup: cleanup:
VIR_FREE(protocol); VIR_FREE(protocol);
ctxt->node = saveNode;
return ret; return ret;
} }
@ -5452,7 +5476,7 @@ virDomainDiskBackingStoreParse(xmlXPathContextPtr ctxt,
goto cleanup; goto cleanup;
} }
if (virDomainDiskSourceParse(source, backingStore) < 0 || if (virDomainDiskSourceParse(source, ctxt, backingStore) < 0 ||
virDomainDiskBackingStoreParse(ctxt, backingStore) < 0) virDomainDiskBackingStoreParse(ctxt, backingStore) < 0)
goto cleanup; goto cleanup;
@ -5562,7 +5586,7 @@ virDomainDiskDefParseXML(virDomainXMLOptionPtr xmlopt,
xmlStrEqual(cur->name, BAD_CAST "source")) { xmlStrEqual(cur->name, BAD_CAST "source")) {
sourceNode = cur; sourceNode = cur;
if (virDomainDiskSourceParse(cur, def->src) < 0) if (virDomainDiskSourceParse(cur, ctxt, def->src) < 0)
goto error; goto error;
source = def->src->path; source = def->src->path;
@ -5728,7 +5752,8 @@ virDomainDiskDefParseXML(virDomainXMLOptionPtr xmlopt,
_("mirror requires source element")); _("mirror requires source element"));
goto error; goto error;
} }
if (virDomainDiskSourceParse(mirrorNode, def->mirror) < 0) if (virDomainDiskSourceParse(mirrorNode, ctxt,
def->mirror) < 0)
goto error; goto error;
} }
ready = virXMLPropString(cur, "ready"); ready = virXMLPropString(cur, "ready");
@ -16142,11 +16167,12 @@ virDomainDiskSourceFormatInternal(virBufferPtr buf,
VIR_FREE(path); VIR_FREE(path);
if (src->nhosts == 0) { if (src->nhosts == 0 && !src->snapshot) {
virBufferAddLit(buf, "/>\n"); virBufferAddLit(buf, "/>\n");
} else { } else {
virBufferAddLit(buf, ">\n"); virBufferAddLit(buf, ">\n");
virBufferAdjustIndent(buf, 2); virBufferAdjustIndent(buf, 2);
for (n = 0; n < src->nhosts; n++) { for (n = 0; n < src->nhosts; n++) {
virBufferAddLit(buf, "<host"); virBufferAddLit(buf, "<host");
virBufferEscapeString(buf, " name='%s'", virBufferEscapeString(buf, " name='%s'",
@ -16163,6 +16189,10 @@ virDomainDiskSourceFormatInternal(virBufferPtr buf,
virBufferAddLit(buf, "/>\n"); virBufferAddLit(buf, "/>\n");
} }
virBufferEscapeString(buf, "<snapshot name='%s'/>\n",
src->snapshot);
virBufferAdjustIndent(buf, -2); virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</source>\n"); virBufferAddLit(buf, "</source>\n");
} }

View File

@ -2492,6 +2492,7 @@ virDomainDiskRemove(virDomainDefPtr def, size_t i);
virDomainDiskDefPtr virDomainDiskDefPtr
virDomainDiskRemoveByName(virDomainDefPtr def, const char *name); virDomainDiskRemoveByName(virDomainDefPtr def, const char *name);
int virDomainDiskSourceParse(xmlNodePtr node, int virDomainDiskSourceParse(xmlNodePtr node,
xmlXPathContextPtr ctxt,
virStorageSourcePtr src); virStorageSourcePtr src);
bool virDomainHasDiskMirror(virDomainObjPtr vm); bool virDomainHasDiskMirror(virDomainObjPtr vm);

View File

@ -107,6 +107,7 @@ void virDomainSnapshotDefFree(virDomainSnapshotDefPtr def)
static int static int
virDomainSnapshotDiskDefParseXML(xmlNodePtr node, virDomainSnapshotDiskDefParseXML(xmlNodePtr node,
xmlXPathContextPtr ctxt,
virDomainSnapshotDiskDefPtr def) virDomainSnapshotDiskDefPtr def)
{ {
int ret = -1; int ret = -1;
@ -154,7 +155,7 @@ virDomainSnapshotDiskDefParseXML(xmlNodePtr node,
if (!def->src->path && if (!def->src->path &&
xmlStrEqual(cur->name, BAD_CAST "source")) { xmlStrEqual(cur->name, BAD_CAST "source")) {
if (virDomainDiskSourceParse(cur, def->src) < 0) if (virDomainDiskSourceParse(cur, ctxt, def->src) < 0)
goto cleanup; goto cleanup;
} else if (!def->src->format && } else if (!def->src->format &&
@ -352,7 +353,8 @@ virDomainSnapshotDefParse(xmlXPathContextPtr ctxt,
goto cleanup; goto cleanup;
def->ndisks = n; def->ndisks = n;
for (i = 0; i < def->ndisks; i++) { 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; goto cleanup;
} }
VIR_FREE(nodes); VIR_FREE(nodes);

View File

@ -2983,6 +2983,9 @@ qemuBuildNetworkDriveURI(virStorageSourcePtr src,
virBufferStrcat(&buf, "rbd:", src->path, NULL); virBufferStrcat(&buf, "rbd:", src->path, NULL);
if (src->snapshot)
virBufferEscape(&buf, '\\', ":", "@%s", src->snapshot);
if (username) { if (username) {
virBufferEscape(&buf, '\\', ":", ":id=%s", username); virBufferEscape(&buf, '\\', ":", ":id=%s", username);
virBufferEscape(&buf, '\\', ":", virBufferEscape(&buf, '\\', ":",

View File

@ -1848,6 +1848,7 @@ virStorageSourceCopy(const virStorageSource *src,
VIR_STRDUP(ret->driverName, src->driverName) < 0 || VIR_STRDUP(ret->driverName, src->driverName) < 0 ||
VIR_STRDUP(ret->relPath, src->relPath) < 0 || VIR_STRDUP(ret->relPath, src->relPath) < 0 ||
VIR_STRDUP(ret->backingStoreRaw, src->backingStoreRaw) < 0 || VIR_STRDUP(ret->backingStoreRaw, src->backingStoreRaw) < 0 ||
VIR_STRDUP(ret->snapshot, src->snapshot) < 0 ||
VIR_STRDUP(ret->compat, src->compat) < 0) VIR_STRDUP(ret->compat, src->compat) < 0)
goto error; goto error;
@ -2280,6 +2281,13 @@ virStorageSourceParseRBDColonString(const char *rbdstr,
*p = '\0'; *p = '\0';
} }
/* snapshot name */
if ((p = strchr(src->path, '@'))) {
if (VIR_STRDUP(src->snapshot, p + 1) < 0)
goto error;
*p = '\0';
}
/* options */ /* options */
if (!options) if (!options)
return 0; /* all done */ return 0; /* all done */

View File

@ -238,6 +238,7 @@ struct _virStorageSource {
char *path; char *path;
int protocol; /* virStorageNetProtocol */ int protocol; /* virStorageNetProtocol */
char *volume; /* volume name for remote storage */ char *volume; /* volume name for remote storage */
char *snapshot; /* for storage systems supporting internal snapshots */
size_t nhosts; size_t nhosts;
virStorageNetHostDefPtr hosts; virStorageNetHostDefPtr hosts;
virStorageSourcePoolDefPtr srcpool; virStorageSourcePoolDefPtr srcpool;

View File

@ -5,4 +5,8 @@ unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -usb \
-drive 'file=rbd:pool/image:auth_supported=none:\ -drive 'file=rbd:pool/image:auth_supported=none:\
mon_host=mon1.example.org\:6321\;mon2.example.org\:6322\;\ mon_host=mon1.example.org\:6321\;mon2.example.org\:6322\;\
mon3.example.org\:6322,if=virtio,format=raw' \ 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 -net none -serial none -parallel none

View File

@ -29,6 +29,23 @@
</source> </source>
<target dev='vda' bus='virtio'/> <target dev='vda' bus='virtio'/>
</disk> </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='usb' index='0'/>
<controller type='ide' index='0'/> <controller type='ide' index='0'/>
<controller type='pci' index='0' model='pci-root'/> <controller type='pci' index='0' model='pci-root'/>