Qemu accepts gluster protocol as supported storage backend beside others.
This patch allows users to specify disks on gluster backends like this:
<disk type='network' device='disk'>
<driver name='qemu' type='raw'/>
<source protocol='gluster' name='volume/image'>
<host name='example.org' port='6000'
transport='tcp'/>
</source>
<target dev='vda' bus='virtio'/>
</disk>
Note: In the <host> element above, transport is an optional attribute.
Valid transport values are tcp, unix or rdma. If none specified, tcp is assumed.
If transport type is unix, host name specifies path to unix socket.
Signed-off-by: Harsh Prateek Bora <harsh(a)linux.vnet.ibm.com>
---
docs/schemas/domaincommon.rng | 8 ++
src/conf/domain_conf.c | 28 +++++-
src/conf/domain_conf.h | 11 +++
src/libvirt_private.syms | 2 +
src/qemu/qemu_command.c | 204 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 251 insertions(+), 2 deletions(-)
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index f47fdad..89d9b9f 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -1048,6 +1048,7 @@
<value>nbd</value>
<value>rbd</value>
<value>sheepdog</value>
+ <value>gluster</value>
</choice>
</attribute>
<optional>
@@ -1061,6 +1062,13 @@
<attribute name="port">
<ref name="unsignedInt"/>
</attribute>
+ <attribute name="transport">
+ <choice>
+ <value>tcp</value>
+ <value>unix</value>
+ <value>rdma</value>
+ </choice>
+ </attribute>
</element>
</zeroOrMore>
<empty/>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 33e1e7f..838f079 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -214,7 +214,13 @@ VIR_ENUM_IMPL(virDomainDiskErrorPolicy,
VIR_DOMAIN_DISK_ERROR_POLICY_LAST,
VIR_ENUM_IMPL(virDomainDiskProtocol, VIR_DOMAIN_DISK_PROTOCOL_LAST,
"nbd",
"rbd",
- "sheepdog")
+ "sheepdog",
+ "gluster")
+
+VIR_ENUM_IMPL(virDomainDiskProtocolTransport, VIR_DOMAIN_DISK_PROTO_TRANS_LAST,
+ "tcp",
+ "unix",
+ "rdma")
VIR_ENUM_IMPL(virDomainDiskSecretType, VIR_DOMAIN_DISK_SECRET_TYPE_LAST,
"none",
@@ -3460,6 +3466,7 @@ virDomainDiskDefParseXML(virCapsPtr caps,
char *source = NULL;
char *target = NULL;
char *protocol = NULL;
+ char *protocol_transport;
char *trans = NULL;
virDomainDiskHostDefPtr hosts = NULL;
int nhosts = 0;
@@ -3566,6 +3573,7 @@ virDomainDiskDefParseXML(virCapsPtr caps,
}
hosts[nhosts].name = NULL;
hosts[nhosts].port = NULL;
+ hosts[nhosts].transport = VIR_DOMAIN_DISK_PROTO_TRANS_TCP;
nhosts++;
hosts[nhosts - 1].name = virXMLPropString(child,
"name");
@@ -3580,6 +3588,17 @@ virDomainDiskDefParseXML(virCapsPtr caps,
"%s", _("missing port for
host"));
goto error;
}
+ /* transport can be tcp (default), unix or rdma. */
+ protocol_transport = virXMLPropString(child,
"transport");
+ if (protocol_transport != NULL) {
+ hosts[nhosts - 1].transport =
virDomainDiskProtocolTransportTypeFromString(protocol_transport);
+ if (hosts[nhosts - 1].transport < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown protocol transport
type '%s'"),
+ protocol_transport);
+ goto error;
+ }
+ }
}
child = child->next;
}
@@ -11756,8 +11775,13 @@ virDomainDiskDefFormat(virBufferPtr buf,
for (i = 0; i < def->nhosts; i++) {
virBufferEscapeString(buf, " <host
name='%s'",
def->hosts[i].name);
- virBufferEscapeString(buf, " port='%s'/>\n",
+ virBufferEscapeString(buf, " port='%s'",
def->hosts[i].port);
+ if (def->hosts[i].transport) {
+ virBufferAsprintf(buf, " transport='%s'",
+
virDomainDiskProtocolTransportTypeToString(def->hosts[i].transport));
+ }
+ virBufferAddLit(buf, "/>\n");
}
virBufferAddLit(buf, " </source>\n");
}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 14dead3..7ba7e9b 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -456,10 +456,19 @@ enum virDomainDiskProtocol {
VIR_DOMAIN_DISK_PROTOCOL_NBD,
VIR_DOMAIN_DISK_PROTOCOL_RBD,
VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG,
+ VIR_DOMAIN_DISK_PROTOCOL_GLUSTER,
VIR_DOMAIN_DISK_PROTOCOL_LAST
};
+enum virDomainDiskProtocolTransport {
+ VIR_DOMAIN_DISK_PROTO_TRANS_TCP,
+ VIR_DOMAIN_DISK_PROTO_TRANS_UNIX,
+ VIR_DOMAIN_DISK_PROTO_TRANS_RDMA,
+
+ VIR_DOMAIN_DISK_PROTO_TRANS_LAST
+};
+
enum virDomainDiskTray {
VIR_DOMAIN_DISK_TRAY_CLOSED,
VIR_DOMAIN_DISK_TRAY_OPEN,
@@ -481,6 +490,7 @@ typedef virDomainDiskHostDef *virDomainDiskHostDefPtr;
struct _virDomainDiskHostDef {
char *name;
char *port;
+ int transport; /* enum virDomainDiskProtocolTransport */
};
enum virDomainDiskIo {
@@ -2166,6 +2176,7 @@ VIR_ENUM_DECL(virDomainDiskBus)
VIR_ENUM_DECL(virDomainDiskCache)
VIR_ENUM_DECL(virDomainDiskErrorPolicy)
VIR_ENUM_DECL(virDomainDiskProtocol)
+VIR_ENUM_DECL(virDomainDiskProtocolTransport)
VIR_ENUM_DECL(virDomainDiskIo)
VIR_ENUM_DECL(virDomainDiskSecretType)
VIR_ENUM_DECL(virDomainDiskTray)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index dab607a..8e70837 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -350,6 +350,8 @@ virDomainDiskInsertPreAlloced;
virDomainDiskIoTypeFromString;
virDomainDiskIoTypeToString;
virDomainDiskPathByName;
+virDomainDiskProtocolTransportTypeFromString;
+virDomainDiskProtocolTransportTypeToString;
virDomainDiskRemove;
virDomainDiskRemoveByName;
virDomainDiskTypeFromString;
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 20730a9..0bed2bc 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -2021,6 +2021,168 @@ no_memory:
return -1;
}
+static int qemuParseGlusterString(virDomainDiskDefPtr def)
+{
+ char *host = NULL, *port = NULL, *volimg, *transp = NULL, *marker;
+
+ if (STRPREFIX(def->src, "+")) {
+ transp = def->src;
+ transp++;
+ marker = strstr(def->src, "://");
+ *marker++ = '\0';
+ marker += 2;
+ } else {
+ /* transport type not specified, use tcp as default */
+ def->hosts->transport = VIR_DOMAIN_DISK_PROTO_TRANS_TCP;
+ marker = strstr(def->src, "://");
+ marker += 3;
+ }
+
+ if (transp) {
+ def->hosts->transport =
virDomainDiskProtocolTransportTypeFromString(transp);
+ if (def->hosts->transport < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Invalid gluster transport type '%s'"),
transp);
+ return -1;
+ }
+ }
+
+ /* now marker points to string which can start with one of the following:
+ * IPv6 address in square brackets followed by port (optional)
+ * <hostname> or <IPv4 address> followed by port (optional)
+ * '/' if its a unix socket followed by path to gluster disk volume/image
+ */
+
+ /* parse server, port */
+ if (def->hosts->transport != VIR_DOMAIN_DISK_PROTO_TRANS_UNIX) {
+ if (STRPREFIX(marker, "[")) {
+ /* IPv6 addr */
+ host = ++marker;
+ marker = strchr(host, ']');
+ if (!marker) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Cannot parse IPv6 addr for gluster host %s
"), host);
+ return -1;
+ }
+ *marker++ = '\0';
+ /* parse port if specified */
+ if (STRPREFIX(marker, ":")) {
+ port = ++marker;
+ marker = strchr(port, '/');
+ if (!marker) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Cannot parse filename for gluster disk %s
"), port);
+ return -1;
+ }
+ *marker++ = '\0';
+ } else {
+ marker++; /* port not specified, skip slash */
+ }
+ } else {
+ /* IPv4 address / hostname followed by port (optional) */
+ host = marker;
+ marker = strchr(host, '/'); /* skip to path to gluster disk vol/img
*/
+ if (!marker) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Cannot parse filename for gluster disk %s "),
port);
+ return -1;
+ }
+ *marker++ = '\0';
+ port = strchr(host, ':');
+ if (port) {
+ /* port was specified with host, separate both */
+ *port++ = '\0';
+ }
+ }
+
+ /* host points to hostname / IPv4 / IPv6 addr
+ * port points to port or NULL is port was not specified
+ */
+ } else {
+ /* transport type is unix, expect one more slash
+ * followed by path to gluster disk vol/img */
+ if (STRPREFIX(marker, "/")) {
+ marker++;
+ } else {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Gluster unix transport url starts with 3 slashes i.e.
gluster+unix:///"));
+ return -1;
+ }
+ }
+
+ /* marker now points to path to gluster disk vol/img */
+ volimg = marker;
+
+ /* if transport type = unix, path to gluster disk vol/img
+ * is followed by ?socket=<path/to/socket> */
+ if (def->hosts->transport == VIR_DOMAIN_DISK_PROTO_TRANS_UNIX) {
+ if (strstr(marker, "?socket=")) {
+ /* In libvirt xml, socket path is to be provided as
+ * <host name='/path/to/socket' port='0'>
+ */
+ host = strchr(marker, '=');
+ *host++ = '\0';
+ }
+ }
+
+ if (VIR_ALLOC(def->hosts) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+ def->nhosts = 1;
+ def->hosts->name = host;
+ if (port) {
+ def->hosts->port = port;
+ } else {
+ def->hosts->port = strdup("0");
+ }
+ if (!def->hosts->port) {
+ virReportOOMError();
+ return -1;
+ }
+ def->src = strdup(volimg);
+ if (!def->src) {
+ virReportOOMError();
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+qemuBuildGlusterString(virDomainDiskDefPtr disk, virBufferPtr opt)
+{
+ int ret = 0;
+ virBufferAddLit(opt, "file=");
+ if (disk->nhosts != 1) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("gluster accepts only one host"));
+ ret = -1;
+ } else {
+ virBufferAsprintf(opt, "gluster+%s://",
+
virDomainDiskProtocolTransportTypeToString(disk->hosts->transport));
+
+ /* if transport type is not unix, specify server:port */
+ if (disk->hosts->transport != VIR_DOMAIN_DISK_PROTO_TRANS_UNIX) {
+ if (strstr(disk->hosts->name, ":")) {
+ /* if IPv6 addr, use square brackets to enclose it */
+ virBufferAsprintf(opt, "[%s]:%s", disk->hosts->name,
disk->hosts->port);
+ } else {
+ virBufferAsprintf(opt, "%s:%s", disk->hosts->name,
disk->hosts->port);
+ }
+ }
+
+ /* append source path to gluster disk image */
+ virBufferAsprintf(opt, "/%s", disk->src);
+
+ /* if transport type is unix, server name is path to unix socket, ignore port */
+ if (disk->hosts->transport == VIR_DOMAIN_DISK_PROTO_TRANS_UNIX) {
+ virBufferAsprintf(opt, "?socket=%s", disk->hosts->name);
+ }
+ }
+ return ret;
+}
+
char *
qemuBuildDriveStr(virConnectPtr conn ATTRIBUTE_UNUSED,
virDomainDiskDefPtr disk,
@@ -2162,6 +2324,12 @@ qemuBuildDriveStr(virConnectPtr conn ATTRIBUTE_UNUSED,
goto error;
virBufferAddChar(&opt, ',');
break;
+ case VIR_DOMAIN_DISK_PROTOCOL_GLUSTER:
+ if (qemuBuildGlusterString(disk, &opt) < 0)
+ goto error;
+ virBufferAddChar(&opt, ',');
+ break;
+
case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG:
if (disk->nhosts == 0) {
virBufferEscape(&opt, ',', ",",
"file=sheepdog:%s,",
@@ -5242,6 +5410,18 @@ qemuBuildCommandLine(virConnectPtr conn,
file = virBufferContentAndReset(&opt);
}
break;
+ case VIR_DOMAIN_DISK_PROTOCOL_GLUSTER:
+ {
+ virBuffer opt = VIR_BUFFER_INITIALIZER;
+ if (qemuBuildGlusterString(disk, &opt) < 0)
+ goto error;
+ if (virBufferError(&opt)) {
+ virReportOOMError();
+ goto error;
+ }
+ file = virBufferContentAndReset(&opt);
+ }
+ break;
case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG:
if (disk->nhosts == 0) {
if (virAsprintf(&file, "sheepdog:%s,",
disk->src) < 0) {
@@ -6937,6 +7117,21 @@ qemuParseCommandLineDisk(virCapsPtr caps,
goto cleanup;
VIR_FREE(p);
+ } else if (STRPREFIX(def->src, "gluster")) {
+ char *p = def->src;
+
+ def->type = VIR_DOMAIN_DISK_TYPE_NETWORK;
+ def->protocol = VIR_DOMAIN_DISK_PROTOCOL_GLUSTER;
+ def->src = strdup(p + strlen("gluster"));
+ if (!def->src) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (qemuParseGlusterString(def) < 0)
+ goto cleanup;
+
+ VIR_FREE(p);
} else if (STRPREFIX(def->src, "sheepdog:")) {
char *p = def->src;
char *port, *vdi;
@@ -8126,6 +8321,10 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps,
disk->type = VIR_DOMAIN_DISK_TYPE_NETWORK;
disk->protocol = VIR_DOMAIN_DISK_PROTOCOL_RBD;
val += strlen("rbd:");
+ } else if (STRPREFIX(val, "gluster")) {
+ disk->type = VIR_DOMAIN_DISK_TYPE_NETWORK;
+ disk->protocol = VIR_DOMAIN_DISK_PROTOCOL_GLUSTER;
+ val += strlen("gluster");
} else if (STRPREFIX(val, "sheepdog:")) {
disk->type = VIR_DOMAIN_DISK_TYPE_NETWORK;
disk->protocol = VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG;
@@ -8211,6 +8410,11 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps,
goto no_memory;
}
break;
+ case VIR_DOMAIN_DISK_PROTOCOL_GLUSTER:
+ if (qemuParseGlusterString(disk) < 0)
+ goto error;
+
+ break;
}
}
--
1.7.11.4