|
|
c401cc |
From fc0de008b808886ff8800527298d100adfcc14f4 Mon Sep 17 00:00:00 2001
|
|
|
c401cc |
Message-Id: <fc0de008b808886ff8800527298d100adfcc14f4@dist-git>
|
|
|
c401cc |
From: Eric Blake <eblake@redhat.com>
|
|
|
c401cc |
Date: Wed, 26 Feb 2014 14:54:23 +0100
|
|
|
c401cc |
Subject: [PATCH] storage: implement rudimentary glusterfs pool refresh
|
|
|
c401cc |
|
|
|
c401cc |
https://bugzilla.redhat.com/show_bug.cgi?id=1032370
|
|
|
c401cc |
|
|
|
c401cc |
Actually put gfapi to use, by allowing the creation of a gluster
|
|
|
c401cc |
pool. Right now, all volumes are treated as raw and directories
|
|
|
c401cc |
are skipped; further patches will allow peering into files to
|
|
|
c401cc |
allow for qcow2 files and backing chains, and reporting proper
|
|
|
c401cc |
volume allocation. This implementation was tested against Fedora
|
|
|
c401cc |
19's glusterfs 3.4.1; it might be made simpler by requiring a
|
|
|
c401cc |
higher minimum, and/or require more hacks to work with a lower
|
|
|
c401cc |
minimum.
|
|
|
c401cc |
|
|
|
c401cc |
* src/storage/storage_backend_gluster.c
|
|
|
c401cc |
(virStorageBackendGlusterRefreshPool): Initial implementation.
|
|
|
c401cc |
(virStorageBackendGlusterOpen, virStorageBackendGlusterClose)
|
|
|
c401cc |
(virStorageBackendGlusterRefreshVol): New helper functions.
|
|
|
c401cc |
|
|
|
c401cc |
Signed-off-by: Eric Blake <eblake@redhat.com>
|
|
|
c401cc |
(cherry picked from commit efee1af54ad58e7147442714b2baa560814f94be)
|
|
|
c401cc |
Signed-off-by: Jiri Denemark <jdenemar@redhat.com>
|
|
|
c401cc |
---
|
|
|
c401cc |
src/storage/storage_backend_gluster.c | 249 +++++++++++++++++++++++++++++++++-
|
|
|
c401cc |
1 file changed, 244 insertions(+), 5 deletions(-)
|
|
|
c401cc |
|
|
|
c401cc |
diff --git a/src/storage/storage_backend_gluster.c b/src/storage/storage_backend_gluster.c
|
|
|
c401cc |
index 2863c73..d57427e 100644
|
|
|
c401cc |
--- a/src/storage/storage_backend_gluster.c
|
|
|
c401cc |
+++ b/src/storage/storage_backend_gluster.c
|
|
|
c401cc |
@@ -23,20 +23,259 @@
|
|
|
c401cc |
|
|
|
c401cc |
#include <glusterfs/api/glfs.h>
|
|
|
c401cc |
|
|
|
c401cc |
-#include "virerror.h"
|
|
|
c401cc |
#include "storage_backend_gluster.h"
|
|
|
c401cc |
#include "storage_conf.h"
|
|
|
c401cc |
+#include "viralloc.h"
|
|
|
c401cc |
+#include "virerror.h"
|
|
|
c401cc |
+#include "virlog.h"
|
|
|
c401cc |
+#include "virstoragefile.h"
|
|
|
c401cc |
+#include "virstring.h"
|
|
|
c401cc |
+#include "viruri.h"
|
|
|
c401cc |
|
|
|
c401cc |
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
|
|
c401cc |
|
|
|
c401cc |
+struct _virStorageBackendGlusterState {
|
|
|
c401cc |
+ glfs_t *vol;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ /* Accept the same URIs as qemu's block/gluster.c:
|
|
|
c401cc |
+ * gluster[+transport]://[server[:port]]/vol/[dir/]image[?socket=...] */
|
|
|
c401cc |
+ virURI *uri;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ char *volname; /* vol from URI, no '/' */
|
|
|
c401cc |
+ char *dir; /* dir from URI, or "/"; always starts and ends in '/' */
|
|
|
c401cc |
+};
|
|
|
c401cc |
+
|
|
|
c401cc |
+typedef struct _virStorageBackendGlusterState virStorageBackendGlusterState;
|
|
|
c401cc |
+typedef virStorageBackendGlusterState *virStorageBackendGlusterStatePtr;
|
|
|
c401cc |
+
|
|
|
c401cc |
+static void
|
|
|
c401cc |
+virStorageBackendGlusterClose(virStorageBackendGlusterStatePtr state)
|
|
|
c401cc |
+{
|
|
|
c401cc |
+ if (!state)
|
|
|
c401cc |
+ return;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ /* Yuck - glusterfs-api-3.4.1 appears to always return -1 for
|
|
|
c401cc |
+ * glfs_fini, with errno containing random data, so there's no way
|
|
|
c401cc |
+ * to tell if it succeeded. 3.4.2 is supposed to fix this.*/
|
|
|
c401cc |
+ if (state->vol && glfs_fini(state->vol) < 0)
|
|
|
c401cc |
+ VIR_DEBUG("shutdown of gluster volume %s failed with errno %d",
|
|
|
c401cc |
+ state->volname, errno);
|
|
|
c401cc |
+
|
|
|
c401cc |
+ virURIFree(state->uri);
|
|
|
c401cc |
+ VIR_FREE(state->volname);
|
|
|
c401cc |
+ VIR_FREE(state->dir);
|
|
|
c401cc |
+ VIR_FREE(state);
|
|
|
c401cc |
+}
|
|
|
c401cc |
+
|
|
|
c401cc |
+static virStorageBackendGlusterStatePtr
|
|
|
c401cc |
+virStorageBackendGlusterOpen(virStoragePoolObjPtr pool)
|
|
|
c401cc |
+{
|
|
|
c401cc |
+ virStorageBackendGlusterStatePtr ret = NULL;
|
|
|
c401cc |
+ const char *name = pool->def->source.name;
|
|
|
c401cc |
+ const char *dir = pool->def->source.dir;
|
|
|
c401cc |
+ bool trailing_slash = true;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ /* Volume name must not contain '/'; optional path allows use of a
|
|
|
c401cc |
+ * subdirectory within the volume name. */
|
|
|
c401cc |
+ if (strchr(name, '/')) {
|
|
|
c401cc |
+ virReportError(VIR_ERR_XML_ERROR,
|
|
|
c401cc |
+ _("gluster pool name '%s' must not contain /"),
|
|
|
c401cc |
+ name);
|
|
|
c401cc |
+ return NULL;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+ if (dir) {
|
|
|
c401cc |
+ if (*dir != '/') {
|
|
|
c401cc |
+ virReportError(VIR_ERR_XML_ERROR,
|
|
|
c401cc |
+ _("gluster pool path '%s' must start with /"),
|
|
|
c401cc |
+ dir);
|
|
|
c401cc |
+ return NULL;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+ if (strchr(dir, '\0')[-1] != '/')
|
|
|
c401cc |
+ trailing_slash = false;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (VIR_ALLOC(ret) < 0)
|
|
|
c401cc |
+ return NULL;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (VIR_STRDUP(ret->volname, name) < 0)
|
|
|
c401cc |
+ goto error;
|
|
|
c401cc |
+ if (virAsprintf(&ret->dir, "%s%s", dir ? dir : "/",
|
|
|
c401cc |
+ trailing_slash ? "" : "/") < 0)
|
|
|
c401cc |
+ goto error;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ /* FIXME: Currently hard-coded to tcp transport; XML needs to be
|
|
|
c401cc |
+ * extended to allow alternate transport */
|
|
|
c401cc |
+ if (VIR_ALLOC(ret->uri) < 0)
|
|
|
c401cc |
+ goto error;
|
|
|
c401cc |
+ if (VIR_STRDUP(ret->uri->scheme, "gluster") < 0)
|
|
|
c401cc |
+ goto error;
|
|
|
c401cc |
+ if (VIR_STRDUP(ret->uri->server, pool->def->source.hosts[0].name) < 0)
|
|
|
c401cc |
+ goto error;
|
|
|
c401cc |
+ if (virAsprintf(&ret->uri->path, "/%s%s", ret->volname, ret->dir) < 0)
|
|
|
c401cc |
+ goto error;
|
|
|
c401cc |
+ ret->uri->port = pool->def->source.hosts[0].port;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ /* Actually connect to glfs */
|
|
|
c401cc |
+ if (!(ret->vol = glfs_new(ret->volname))) {
|
|
|
c401cc |
+ virReportOOMError();
|
|
|
c401cc |
+ goto error;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (glfs_set_volfile_server(ret->vol, "tcp",
|
|
|
c401cc |
+ ret->uri->server, ret->uri->port) < 0 ||
|
|
|
c401cc |
+ glfs_init(ret->vol) < 0) {
|
|
|
c401cc |
+ char *uri = virURIFormat(ret->uri);
|
|
|
c401cc |
+
|
|
|
c401cc |
+ virReportSystemError(errno, _("failed to connect to %s"),
|
|
|
c401cc |
+ NULLSTR(uri));
|
|
|
c401cc |
+ VIR_FREE(uri);
|
|
|
c401cc |
+ goto error;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (glfs_chdir(ret->vol, ret->dir) < 0) {
|
|
|
c401cc |
+ virReportSystemError(errno,
|
|
|
c401cc |
+ _("failed to change to directory '%s' in '%s'"),
|
|
|
c401cc |
+ ret->dir, ret->volname);
|
|
|
c401cc |
+ goto error;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+
|
|
|
c401cc |
+ return ret;
|
|
|
c401cc |
+
|
|
|
c401cc |
+error:
|
|
|
c401cc |
+ virStorageBackendGlusterClose(ret);
|
|
|
c401cc |
+ return NULL;
|
|
|
c401cc |
+}
|
|
|
c401cc |
+
|
|
|
c401cc |
+
|
|
|
c401cc |
+/* Populate *volptr for the given name and stat information, or leave
|
|
|
c401cc |
+ * it NULL if the entry should be skipped (such as "."). Return 0 on
|
|
|
c401cc |
+ * success, -1 on failure. */
|
|
|
c401cc |
+static int
|
|
|
c401cc |
+virStorageBackendGlusterRefreshVol(virStorageBackendGlusterStatePtr state,
|
|
|
c401cc |
+ const char *name,
|
|
|
c401cc |
+ struct stat *st,
|
|
|
c401cc |
+ virStorageVolDefPtr *volptr)
|
|
|
c401cc |
+{
|
|
|
c401cc |
+ char *tmp;
|
|
|
c401cc |
+ int ret = -1;
|
|
|
c401cc |
+ virStorageVolDefPtr vol = NULL;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ *volptr = NULL;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ /* Silently skip '.' and '..'. */
|
|
|
c401cc |
+ if (STREQ(name, ".") || STREQ(name, ".."))
|
|
|
c401cc |
+ return 0;
|
|
|
c401cc |
+ /* FIXME: support directories. For now, silently skip them. */
|
|
|
c401cc |
+ if (S_ISDIR(st->st_mode))
|
|
|
c401cc |
+ return 0;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (VIR_ALLOC(vol) < 0)
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+ if (VIR_STRDUP(vol->name, name) < 0)
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+ if (virAsprintf(&vol->key, "%s%s%s", state->volname, state->dir,
|
|
|
c401cc |
+ vol->name) < 0)
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ vol->type = VIR_STORAGE_VOL_NETWORK;
|
|
|
c401cc |
+ tmp = state->uri->path;
|
|
|
c401cc |
+ state->uri->path = vol->key;
|
|
|
c401cc |
+ if (!(vol->target.path = virURIFormat(state->uri))) {
|
|
|
c401cc |
+ state->uri->path = tmp;
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+ state->uri->path = tmp;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ /* FIXME - must open files to determine if they are non-raw */
|
|
|
c401cc |
+ vol->target.format = VIR_STORAGE_FILE_RAW;
|
|
|
c401cc |
+ vol->capacity = vol->allocation = st->st_size;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ *volptr = vol;
|
|
|
c401cc |
+ vol = NULL;
|
|
|
c401cc |
+ ret = 0;
|
|
|
c401cc |
+cleanup:
|
|
|
c401cc |
+ virStorageVolDefFree(vol);
|
|
|
c401cc |
+ return ret;
|
|
|
c401cc |
+}
|
|
|
c401cc |
|
|
|
c401cc |
static int
|
|
|
c401cc |
virStorageBackendGlusterRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
|
c401cc |
- virStoragePoolObjPtr pool ATTRIBUTE_UNUSED)
|
|
|
c401cc |
+ virStoragePoolObjPtr pool)
|
|
|
c401cc |
{
|
|
|
c401cc |
- virReportError(VIR_ERR_NO_SUPPORT, "%s",
|
|
|
c401cc |
- _("gluster pool type not fully supported yet"));
|
|
|
c401cc |
- return -1;
|
|
|
c401cc |
+ int ret = -1;
|
|
|
c401cc |
+ virStorageBackendGlusterStatePtr state = NULL;
|
|
|
c401cc |
+ struct {
|
|
|
c401cc |
+ struct dirent ent;
|
|
|
c401cc |
+ /* See comment below about readdir_r needing padding */
|
|
|
c401cc |
+ char padding[MAX(1, 256 - (int) (sizeof(struct dirent)
|
|
|
c401cc |
+ - offsetof(struct dirent, d_name)))];
|
|
|
c401cc |
+ } de;
|
|
|
c401cc |
+ struct dirent *ent;
|
|
|
c401cc |
+ glfs_fd_t *dir = NULL;
|
|
|
c401cc |
+ struct stat st;
|
|
|
c401cc |
+ struct statvfs sb;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (!(state = virStorageBackendGlusterOpen(pool)))
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ /* Why oh why did glfs 3.4 decide to expose only readdir_r rather
|
|
|
c401cc |
+ * than readdir? POSIX admits that readdir_r is inherently a
|
|
|
c401cc |
+ * flawed design, because systems are not required to define
|
|
|
c401cc |
+ * NAME_MAX: http://austingroupbugs.net/view.php?id=696
|
|
|
c401cc |
+ * http://womble.decadent.org.uk/readdir_r-advisory.html
|
|
|
c401cc |
+ *
|
|
|
c401cc |
+ * Fortunately, gluster appears to limit its underlying bricks to
|
|
|
c401cc |
+ * only use file systems such as XFS that have a NAME_MAX of 255;
|
|
|
c401cc |
+ * so we are currently guaranteed that if we provide 256 bytes of
|
|
|
c401cc |
+ * tail padding, then we should have enough space to avoid buffer
|
|
|
c401cc |
+ * overflow no matter whether the OS used d_name[], d_name[1], or
|
|
|
c401cc |
+ * d_name[256] in its 'struct dirent'.
|
|
|
c401cc |
+ * http://lists.gnu.org/archive/html/gluster-devel/2013-10/msg00083.html
|
|
|
c401cc |
+ */
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (!(dir = glfs_opendir(state->vol, state->dir))) {
|
|
|
c401cc |
+ virReportSystemError(errno, _("cannot open path '%s' in '%s'"),
|
|
|
c401cc |
+ state->dir, state->volname);
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+ while (!(errno = glfs_readdirplus_r(dir, &st, &de.ent, &ent)) && ent) {
|
|
|
c401cc |
+ virStorageVolDefPtr vol;
|
|
|
c401cc |
+ int okay = virStorageBackendGlusterRefreshVol(state,
|
|
|
c401cc |
+ ent->d_name, &st,
|
|
|
c401cc |
+ &vol;;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (okay < 0)
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+ if (vol && VIR_APPEND_ELEMENT(pool->volumes.objs, pool->volumes.count,
|
|
|
c401cc |
+ vol) < 0)
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+ if (errno) {
|
|
|
c401cc |
+ virReportSystemError(errno, _("failed to read directory '%s' in '%s'"),
|
|
|
c401cc |
+ state->dir, state->volname);
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+
|
|
|
c401cc |
+ if (glfs_statvfs(state->vol, state->dir, &sb) < 0) {
|
|
|
c401cc |
+ virReportSystemError(errno, _("cannot statvfs path '%s' in '%s'"),
|
|
|
c401cc |
+ state->dir, state->volname);
|
|
|
c401cc |
+ goto cleanup;
|
|
|
c401cc |
+ }
|
|
|
c401cc |
+
|
|
|
c401cc |
+ pool->def->capacity = ((unsigned long long)sb.f_frsize *
|
|
|
c401cc |
+ (unsigned long long)sb.f_blocks);
|
|
|
c401cc |
+ pool->def->available = ((unsigned long long)sb.f_bfree *
|
|
|
c401cc |
+ (unsigned long long)sb.f_frsize);
|
|
|
c401cc |
+ pool->def->allocation = pool->def->capacity - pool->def->available;
|
|
|
c401cc |
+
|
|
|
c401cc |
+ ret = 0;
|
|
|
c401cc |
+cleanup:
|
|
|
c401cc |
+ if (dir)
|
|
|
c401cc |
+ glfs_closedir(dir);
|
|
|
c401cc |
+ virStorageBackendGlusterClose(state);
|
|
|
c401cc |
+ if (ret < 0)
|
|
|
c401cc |
+ virStoragePoolObjClearVols(pool);
|
|
|
c401cc |
+ return ret;
|
|
|
c401cc |
}
|
|
|
c401cc |
|
|
|
c401cc |
virStorageBackend virStorageBackendGluster = {
|
|
|
c401cc |
--
|
|
|
c401cc |
1.9.0
|
|
|
c401cc |
|