This patch adds a way to specify multiple backup volfile servers to the gluster block backend of QEMU with both tcp and rdma transport types.
Problem: Currenly VM Image on gluster volume is specified like this: file=gluster[+tcp]://server1[:port]/testvol/a.img Assuming we have have three servers in trustred pool with replica 3 volume in action and unfortunately server1 (mentioned in the command above) went down for some reason, since the volume is replica 3 we now have other 2 servers active from which we can boot the VM. But currently there is no mechanism to pass the other 2 gluster server addresses to qemu. Solution: New way of specifying VM Image on gluster volume with backup volfile servers: file=gluster[+transport-type]://[server[:port]]/testvol/a.img\ [?backup-volfile-servers=server1[:port]\ &backup-volfile-servers=server2[:port]] This patch gives a mechanism to provide all the server addresses which are in replica set, so in case server1 is down VM can still boot from any of the active servers. This is equivalent to the backup-volfile-servers option supported by mount.glusterfs (FUSE way of mounting gluster volume) Signed-off-by: Prasanna Kumar Kalever <prasanna.kale...@redhat.com> --- block/gluster.c | 120 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 96 insertions(+), 24 deletions(-) diff --git a/block/gluster.c b/block/gluster.c index 1eb3a8c..b864a78 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -25,17 +25,23 @@ typedef struct BDRVGlusterState { } BDRVGlusterState; typedef struct GlusterConf { - char *server; + GList *server; int port; char *volname; char *image; char *transport; } GlusterConf; +typedef struct GlusterOptions { + struct glfs *glfs; + GlusterConf *gconf; +} GlusterOptions; + + static void qemu_gluster_gconf_free(GlusterConf *gconf) { if (gconf) { - g_free(gconf->server); + g_list_foreach(gconf->server, (GFunc)g_free, NULL); g_free(gconf->volname); g_free(gconf->image); g_free(gconf->transport); @@ -43,6 +49,7 @@ static void qemu_gluster_gconf_free(GlusterConf *gconf) } } + static int parse_volume_options(GlusterConf *gconf, char *path) { char *p, *q; @@ -68,8 +75,29 @@ static int parse_volume_options(GlusterConf *gconf, char *path) return 0; } + +static void parse_volfile_server_options(gpointer data, gpointer user_data) +{ + char *addr = NULL; + int port = NULL; + char *server = (char *) data; + GlusterOptions *options = (GlusterOptions *) user_data; + + if ((server != NULL) && strchr(server, ':')) { + addr = strtok(server, ":"); + port = atoi(strtok(NULL, ":")); + glfs_set_volfile_server(options->glfs, options->gconf->transport, + addr, port); + } else { + glfs_set_volfile_server(options->glfs, options->gconf->transport, + server, options->gconf->port); + } +} + /* - * file=gluster[+transport]://[server[:port]]/volname/image[?socket=...] + * file=gluster[+transport]://[server1[:port]]/volname/image[?socket=...] \ + * [?backup-volfile-servers=server2[:port]\ + * &backup-volfile-servers=server3[:port] ...] * * 'gluster' is the protocol. * @@ -102,15 +130,41 @@ static int parse_volume_options(GlusterConf *gconf, char *path) * file=gluster+tcp://[1:2:3:4:5:6:7:8]/testvol/dir/a.img * file=gluster+tcp://[1:2:3:4:5:6:7:8]:24007/testvol/dir/a.img * file=gluster+tcp://server.domain.com:24007/testvol/dir/a.img + * file=gluster+tcp://server.domain.com/testvol/dir/a.img\ + * ?backup-volfile-servers=server1\ + * &backup-volfile-servers=server2 + * file=gluster+tcp:///testvol/dir/a.img\ + * ?backup-volfile-servers=server1\ + * &backup-volfile-servers=server2 + * file=gluster+tcp://server.domain.com:24007/testvol/dir/a.img\ + * ?backup-volfile-servers=server1:24007\ + * &backup-volfile-servers=server2:24007 + * file=gluster+tcp:///testvol/dir/a.img\ + * ?backup-volfile-servers=server1:24007\ + * &backup-volfile-servers=server2:24007 * file=gluster+unix:///testvol/dir/a.img?socket=/tmp/glusterd.socket * file=gluster+rdma://1.2.3.4:24007/testvol/a.img + * file=gluster+rdma://1.2.3.4/testvol/a.img\ + * ?backup-volfile-servers=4.5.6.7\ + * &backup-volfile-servers=8.9.10.11 + * file=gluster+rdma:///testvol/a.img\ + * ?backup-volfile-servers=1.2.3.4\ + * &backup-volfile-servers=5.6.7.8 + * file=gluster+rdma://1.2.3.4:24007/testvol/a.img\ + * ?backup-volfile-servers=4.5.6.7:24007\ + * &backup-volfile-servers=8.9.10.11:24007 + * file=gluster+rdma:///testvol/a.img\ + * ?backup-volfile-servers=1.2.3.4:24007\ + * &backup-volfile-servers=5.6.7.8:24007 */ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename) { - URI *uri; + URI *uri = NULL; QueryParams *qp = NULL; bool is_unix = false; - int ret = 0; + int i = 0; + int ret = -1; + int nservers = 0; uri = uri_parse(filename); if (!uri) { @@ -138,12 +192,12 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename) } qp = query_params_parse(uri->query); - if (qp->n > 1 || (is_unix && !qp->n) || (!is_unix && qp->n)) { - ret = -EINVAL; - goto out; - } if (is_unix) { + if (qp->n > 1) { + ret = -EINVAL; + goto out; + } if (uri->server || uri->port) { ret = -EINVAL; goto out; @@ -152,10 +206,25 @@ static int qemu_gluster_parseuri(GlusterConf *gconf, const char *filename) ret = -EINVAL; goto out; } - gconf->server = g_strdup(qp->p[0].value); + gconf->server = g_list_append(NULL, g_strdup(qp->p[0].value)); } else { - gconf->server = g_strdup(uri->server ? uri->server : "localhost"); - gconf->port = uri->port; + for (i = 0; i < qp->n; i++) { + if (strcmp(qp->p[i].name, "backup-volfile-servers")) { + ret = -EINVAL; + goto out; + } else { + ++nservers; + } + } + if (uri->port > 0) { + gconf->port = uri->port; + } + gconf->server = g_list_append(NULL, + g_strdup(uri->server ? uri->server : "localhost")); + for (i = 0; i < nservers; i++) { + gconf->server = g_list_append(gconf->server, + g_strdup(qp->p[i].value)); + } } out: @@ -170,27 +239,31 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename, Error **errp) { struct glfs *glfs = NULL; - int ret; + GlusterOptions *params = NULL; + int ret = -1; int old_errno; ret = qemu_gluster_parseuri(gconf, filename); if (ret < 0) { error_setg(errp, "Usage: file=gluster[+transport]://[server[:port]]/" - "volname/image[?socket=...]"); + "volname/image[?socket=...][?backup-volfile-servers=server1[:port]" + "&backup-volfile-servers=server2[:port] ...]"); errno = -ret; goto out; } + params = g_malloc(sizeof(GlusterOptions)); + glfs = glfs_new(gconf->volname); if (!glfs) { goto out; } - ret = glfs_set_volfile_server(glfs, gconf->transport, gconf->server, - gconf->port); - if (ret < 0) { - goto out; - } + params->glfs = glfs; + params->gconf = gconf; + + g_list_foreach(gconf->server, (GFunc) parse_volfile_server_options, + (GlusterOptions *)params); /* * TODO: Use GF_LOG_ERROR instead of hard code value of 4 here when @@ -204,17 +277,15 @@ static struct glfs *qemu_gluster_init(GlusterConf *gconf, const char *filename, ret = glfs_init(glfs); if (ret) { error_setg_errno(errp, errno, - "Gluster connection failed for server=%s port=%d " - "volume=%s image=%s transport=%s", gconf->server, - gconf->port, gconf->volname, gconf->image, - gconf->transport); - + "Gluster connection failed for " + "volume=%s image=%s", gconf->volname, gconf->image); /* glfs_init sometimes doesn't set errno although docs suggest that */ if (errno == 0) errno = EINVAL; goto out; } + g_free(params); return glfs; out: @@ -223,6 +294,7 @@ out: glfs_fini(glfs); errno = old_errno; } + g_free(params); return NULL; } -- 2.1.0