[patch tabled 6/8] Add filesystem back-end

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds the first new back-end and makes some changes to the way
nodes are added, to make the invariants of storage_node more sensible.

The filesystem back-end itself is not intended for production use,
so it makes no attempt to run any asynchronous transfers.

We also add a test. Note that this differs from the preliminary versions
of this patch. We used to add both chunk and fs back-ends, so that tabled
replicates to both. This makes sense as a test of store path, but on
retrieval tabled selects any one of available storage nodes with the
object, randomly. It creates gaps in test coverage in any given run.
Therefore, we test two back-end types sequentially now.

Signed-off-by: Pete Zaitcev <zaitcev@xxxxxxxxxx>

---
 server/Makefile.am   |    2 
 server/stor_chunk.c  |   21 -
 server/stor_fs.c     |  498 +++++++++++++++++++++++++++++++++++++++++
 server/storage.c     |  157 ++++++++++--
 server/storparse.c   |   97 +++++++
 server/tabled.h      |   31 ++
 test/Makefile.am     |    3 
 test/be_fs-test.conf |    5 
 test/combo-redux     |   74 ++++++
 test/prep-db         |    4 
 test/start-daemon    |    1 
 test/stop-daemon     |    9 
 12 files changed, 835 insertions(+), 67 deletions(-)

commit bccedeedabbe713e4053afa185314b3f57f3d204
Author: Pete Zaitcev <zaitcev@xxxxxxxxx>
Date:   Sun Nov 28 17:58:05 2010 -0700

    Add fs back-end, with a test.

diff --git a/server/Makefile.am b/server/Makefile.am
index 52beec4..71bcb35 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -6,7 +6,7 @@ sbin_PROGRAMS	= tabled tdbadm
 tabled_SOURCES	= tabled.h		\
 		  bucket.c cldu.c config.c metarep.c object.c replica.c \
 		  server.c status.c storage.c storparse.c \
-		  stor_chunk.c util.c
+		  stor_chunk.c stor_fs.c util.c
 tabled_LDADD	= ../lib/libtdb.a		\
 		  @HAIL_LIBS@ @PCRE_LIBS@ @GLIB_LIBS@ \
 		  @CRYPTO_LIBS@ @DB4_LIBS@ @EVENT_LIBS@ @SSL_LIBS@
diff --git a/server/stor_chunk.c b/server/stor_chunk.c
index 815adcf..7462a9c 100644
--- a/server/stor_chunk.c
+++ b/server/stor_chunk.c
@@ -31,8 +31,7 @@
 #include <netdb.h>
 #include "tabled.h"
 
-static const char stor_key_fmt[] = "%016llx";
-#define STOR_KEY_SLEN  16
+static const char stor_key_fmt[] = STOR_KEY_FMT;
 
 static int stor_new_stc(struct storage_node *stn, struct st_client **stcp)
 {
@@ -66,24 +65,6 @@ static int stor_new_stc(struct storage_node *stn, struct st_client **stcp)
 	return 0;
 }
 
-static void stor_read_event(int fd, short events, void *userdata)
-{
-	struct open_chunk *cep = userdata;
-
-	cep->r_armed = false;		/* no EV_PERSIST */
-	if (cep->ocb)
-		(*cep->ocb)(cep);
-}
-
-static void stor_write_event(int fd, short events, void *userdata)
-{
-	struct open_chunk *cep = userdata;
-
-	cep->w_armed = false;		/* no EV_PERSIST */
-	if (cep->ocb)
-		(*cep->ocb)(cep);
-}
-
 /*
  * Open *cep using stn, set up chunk session if needed.
  */
diff --git a/server/stor_fs.c b/server/stor_fs.c
new file mode 100644
index 0000000..b433a67
--- /dev/null
+++ b/server/stor_fs.c
@@ -0,0 +1,498 @@
+
+/*
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#define _GNU_SOURCE
+#include "tabled-config.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <string.h>
+#include <glib.h>
+#include <event.h>
+#include "tabled.h"
+
+static const char stor_key_fmt[] = STOR_KEY_FMT;
+
+static char *fs_obj_pathname(const char *base, uint64_t key)
+{
+	enum { PREFIX_LEN = 3 };
+	char prefix[PREFIX_LEN + 1];
+	char stckey[STOR_KEY_SLEN+1];
+	char *s;
+	int rc;
+
+	/* we know that stckey is going to be longer than PREFIX_LEN */
+	sprintf(stckey, stor_key_fmt, (unsigned long long) key);
+	memcpy(prefix, stckey, PREFIX_LEN);
+	prefix[PREFIX_LEN] = 0;
+
+	rc = asprintf(&s, "%s/%s/%s", base, prefix, stckey + PREFIX_LEN);
+	if (rc < 0)
+		goto err_out;
+
+	return s;
+
+err_out:
+	return NULL;
+}
+
+static char *fs_ctl_pathname(const char *base, const char *file)
+{
+	char *s;
+	int rc;
+
+	rc = asprintf(&s, "%s/%s", base, file);
+	if (rc < 0)
+		return NULL;
+	return s;
+}
+
+static int fs_obj_mkpath(const char *path)
+{
+	struct stat statb;
+	char *s;
+	int rc;
+
+	/* one dir is enough */
+	/* not using dirname because on some platforms it modifies its arg. */
+	s = strrchr(path, '/');
+	if (s == NULL)
+		return -EINVAL;
+	s = strndup(path, s-path);
+	if (!s)
+		return -ENOMEM;
+
+	/* create subdir on the fly, if not already exists */
+	if (stat(s, &statb) < 0) {
+		rc = errno;
+		if (rc != ENOENT)
+			goto err_out;
+		if (mkdir(s, 0777) < 0) {
+			rc = errno;
+			/*
+			 * Directory already exists, perhaps
+			 * because we raced with another thread.
+			 */
+			if (rc != EEXIST)
+				goto err_out;
+		}
+	} else {
+		if (!S_ISDIR(statb.st_mode)) {
+			rc = EINVAL;
+			goto err_out;
+		}
+	}
+
+	free(s);
+	return 0;
+
+err_out:
+	free(s);
+	return -rc;
+}
+
+static int fs_open(struct open_chunk *cep, struct storage_node *stn,
+		   struct event_base *ev_base)
+{
+	if (cep->node)
+		return -EBUSY;
+
+	if (!stn->basepath) {
+		applog(LOG_WARNING,
+		       "No base path for Posix chunk, nid %u", stn->id);
+		return -EINVAL;
+	}
+
+	cep->evbase = ev_base;
+	cep->node = stor_node_get(stn);
+	cep->pfd = -1;
+
+	return 0;
+}
+
+static int fs_open_read(struct open_chunk *cep,
+			void (*cb)(struct open_chunk *),
+			uint64_t key, uint64_t *psize)
+{
+	char *objpath;
+	struct stat statb;
+	uint64_t size;
+	int rc;
+
+	if (!cep->node || cep->key)
+		return -EBUSY;
+
+	objpath = fs_obj_pathname(cep->node->basepath, key);
+	if (!objpath) {
+		applog(LOG_WARNING, "No core");
+		return -ENOMEM;
+	}
+
+	rc = open(objpath, O_RDONLY);
+	if (rc == -1) {
+		rc = errno;
+		applog(LOG_WARNING, "Cannot open file %s oid %llX: %s",
+		       objpath, (long long) key, strerror(rc));
+		free(objpath);
+		return -rc;
+	}
+	cep->pfd = rc;
+
+	if (fstat(cep->pfd, &statb) < 0) {
+		rc = errno;
+		applog(LOG_WARNING, "Cannot stat file %s: %s",
+		       objpath, strerror(rc));
+		close(cep->pfd);
+		cep->pfd = -1;
+		free(objpath);
+		return -rc;
+	}
+	size = statb.st_size;
+
+	*psize = size;
+	cep->size = size;
+	cep->done = 0;
+	cep->key = key;
+	cep->ocb = cb;
+
+	/*
+	 * We cannot call cep->ocb directly. Instead, we steal the
+	 * arm-disarm mechanism from chunk. This works because in Linux
+	 * regular files can be polled and always return ready.
+	 */
+	event_set(&cep->revt, cep->pfd, EV_READ, stor_read_event, cep);
+	event_base_set(cep->evbase, &cep->revt);
+
+	free(objpath);
+	return 0;
+}
+
+static void fs_close(struct open_chunk *cep)
+{
+	if (cep->node) {
+		stor_node_put(cep->node);
+		cep->node = NULL;
+		if (cep->pfd != -1) {
+			close(cep->pfd);
+			cep->pfd = -1;
+		}
+	}
+
+	cep->done = 0;
+	cep->size = 0;
+
+	if (cep->r_armed) {
+		event_del(&cep->revt);
+		cep->r_armed = false;
+	}
+
+	if (cep->w_armed) {
+		event_del(&cep->wevt);
+		cep->w_armed = false;
+	}
+
+	cep->key = 0;
+}
+
+static void fs_abort(struct open_chunk *cep)
+{
+	if (cep->r_armed) {
+		event_del(&cep->revt);
+		cep->r_armed = false;
+	}
+	if (cep->w_armed) {
+		event_del(&cep->wevt);
+		cep->w_armed = false;
+	}
+	/* XXX delete the unfinished object under write */
+	cep->key = 0;
+}
+
+static int fs_put_start(struct open_chunk *cep,
+			void (*cb)(struct open_chunk *),
+			uint64_t key, uint64_t size)
+{
+	char *objpath;
+	int rc;
+
+	if (!cep->node || cep->key)
+		return -EBUSY;
+
+	objpath = fs_obj_pathname(cep->node->basepath, key);
+	if (!objpath) {
+		applog(LOG_WARNING, "No core");
+		return -ENOMEM;
+	}
+
+	rc = fs_obj_mkpath(objpath);
+	if (rc) {
+		applog(LOG_WARNING, "Cannot create a directory for %s: %s",
+		       objpath, strerror(-rc));
+		free(objpath);
+		return rc;
+	}
+
+	rc = open(objpath, O_WRONLY|O_TRUNC|O_CREAT, 0666);
+	if (rc == -1) {
+		rc = errno;
+		applog(LOG_WARNING, "Cannot create file %s: %s",
+		       objpath, strerror(rc));
+		free(objpath);
+		return -rc;
+	}
+	cep->pfd = rc;
+
+	cep->size = size;
+	cep->done = 0;
+	cep->key = key;
+	cep->ocb = cb;
+	event_set(&cep->wevt, cep->pfd, EV_WRITE, stor_write_event, cep);
+	event_base_set(cep->evbase, &cep->wevt);
+
+	free(objpath);
+	return 0;
+}
+
+static ssize_t fs_put_buf(struct open_chunk *cep, void *data, size_t len)
+{
+	ssize_t rcs;
+
+	if (len == 0) {
+		applog(LOG_ERR, "Put length zero remaining %ld",
+		       cep->size - cep->done);
+		return -EIO;		/* will spin otherwise, better error */
+	}
+
+	if (cep->done + len > cep->size) {
+		/* P3 */ applog(LOG_ERR, "Put length %ld remaining %ld",
+		       (long) len, (long) (cep->size - cep->done));
+		if (cep->done == cep->size)
+			return -EIO;	/* will spin otherwise, better error */
+		len = cep->size - cep->done;
+	}
+
+	if (!cep->node)
+		return -EPIPE;
+
+	rcs = write(cep->pfd, data, len);
+	if (rcs < 0)
+		return -errno;
+
+	if (!cep->w_armed) {
+		event_add(&cep->wevt, NULL);
+		cep->w_armed = true;
+	}
+
+	cep->done += rcs;
+	if (rcs < len)
+		return rcs;
+
+	return len;
+}
+
+static bool fs_put_end(struct open_chunk *cep)
+{
+	if (!cep->node)
+		return true;
+	if (cep->pfd != -1) {
+		if (fdatasync(cep->pfd) != 0)
+			return false;
+		close(cep->pfd);
+		cep->pfd = -1;
+	}
+	if (cep->w_armed) {
+		event_del(&cep->wevt);
+		cep->w_armed = false;
+	}
+	return true;
+}
+
+static ssize_t fs_get_buf(struct open_chunk *cep, void *data, size_t req_len)
+{
+	size_t xfer_len;
+	ssize_t rcs;
+
+	if (!cep->node)
+		return -EDOM;
+
+	if (cep->done + req_len < cep->done)	/* wrap */
+		return -EINVAL;
+	if (cep->done + req_len > cep->size)
+		xfer_len = cep->size - cep->done;
+	else
+		xfer_len = req_len;
+	if (xfer_len == 0)
+		return 0;
+
+	rcs = read(cep->pfd, data, xfer_len);
+	if (rcs < 0)
+		return -errno;
+
+	cep->done += rcs;
+	if (cep->done == cep->size) {
+		cep->done = 0;
+		cep->size = 0;
+		close(cep->pfd);
+		cep->pfd = -1;
+		if (cep->r_armed) {
+			event_del(&cep->revt);
+			cep->r_armed = false;
+		}
+		return rcs;
+	}
+
+	if (xfer_len != rcs && cep->size && !cep->r_armed) {
+		cep->r_armed = true;
+		if (event_add(&cep->revt, NULL))
+			cep->r_armed = false;
+	}
+
+	return rcs;
+}
+
+static int fs_obj_del(struct storage_node *stn, uint64_t key)
+{
+	char *objpath;
+	int rc;
+
+	objpath = fs_obj_pathname(stn->basepath, key);
+	if (!objpath) {
+		applog(LOG_WARNING, "No core");
+		return -ENOMEM;
+	}
+
+	if (unlink(objpath) != 0) {
+		rc = errno;
+		applog(LOG_WARNING, "Cannot unlink oid %llX file %s: %s",
+		       (long long)key, objpath, strerror(rc));
+		free(objpath);
+		return -rc;
+	}
+
+	free(objpath);
+	return 0;
+}
+
+static bool fs_obj_test(struct open_chunk *cep, uint64_t key)
+{
+	struct stat statb;
+	char *objpath;
+
+	objpath = fs_obj_pathname(cep->node->basepath, key);
+	if (!objpath) {
+		applog(LOG_WARNING, "No core");
+		return false;
+	}
+
+	if (stat(objpath, &statb) != 0) {
+		applog(LOG_WARNING, "Cannot stat oid %llX file %s: %s",
+		       (long long)key, objpath, strerror(errno));
+		free(objpath);
+		return false;
+	}
+
+	free(objpath);
+	return true;
+}
+
+static long stor_readnid(const char *fname)
+{
+	enum { LEN = 45 };
+	char buf[LEN+1];
+	long num;
+	int fd;
+	int rc;
+
+	if ((fd = open(fname, O_RDONLY)) == -1)
+		return -errno;
+	rc = read(fd, buf, LEN);
+	close(fd);
+	if (rc < 0)
+		return -errno;
+	if (rc == 0)
+		return -EPIPE;
+	buf[rc] = 0;
+
+	num = strtol(buf, NULL, 10);
+	if (num < INT_MIN || num > INT_MAX)
+		return -ERANGE;
+
+	return num;
+}
+
+static int fs_node_check(struct storage_node *stn)
+{
+	char *objpath;
+	long rcl;
+
+	if (!stn->basepath)
+		return -1;
+
+	objpath = fs_ctl_pathname(stn->basepath, "NID");
+	if (!objpath) {
+		applog(LOG_WARNING, "No core");
+		return -1;
+	}
+
+	rcl = stor_readnid(objpath);
+	if (rcl < 0) {
+		if (!stn->reported) {
+			applog(LOG_ERR, "Cannot verify nid %u path %s: %s",
+			       stn->id, objpath, strerror(-rcl));
+			stn->reported = true;
+		}
+		free(objpath);
+		return -1;
+	}
+
+	/*
+	 * This prevents a catastrophy of two entries in CLD pointing
+	 * to the same directory. Happens way easier than one expects.
+	 */
+	if (stn->id != rcl) {
+		if (!stn->reported) {
+			applog(LOG_ERR, "Mismatch nid %u fetched %u",
+			       stn->id, rcl);
+			stn->reported = true;
+		}
+		free(objpath);
+		return -1;
+	}
+
+	free(objpath);
+	return 0;
+}
+
+struct st_node_ops stor_ops_posix = {
+	.open =		fs_open,
+	.open_read =	fs_open_read,
+	.close =	fs_close,
+	.abort =	fs_abort,
+	.put_start =	fs_put_start,
+	.put_buf =	fs_put_buf,
+	.put_end =	fs_put_end,
+	.get_buf =	fs_get_buf,
+	.obj_del =	fs_obj_del,
+	.obj_test =	fs_obj_test,
+	.node_check =	fs_node_check,
+};
+
diff --git a/server/storage.c b/server/storage.c
index b0278a0..97a4ca2 100644
--- a/server/storage.c
+++ b/server/storage.c
@@ -50,6 +50,24 @@ void stor_node_put(struct storage_node *sn)
 	--sn->ref;
 }
 
+void stor_read_event(int fd, short events, void *userdata)
+{
+	struct open_chunk *cep = userdata;
+
+	cep->r_armed = false;		/* no EV_PERSIST */
+	if (cep->ocb)
+		(*cep->ocb)(cep);
+}
+
+void stor_write_event(int fd, short events, void *userdata)
+{
+	struct open_chunk *cep = userdata;
+
+	cep->w_armed = false;		/* no EV_PERSIST */
+	if (cep->ocb)
+		(*cep->ocb)(cep);
+}
+
 static struct storage_node *_stor_node_by_nid(uint32_t nid)
 {
 	struct storage_node *sn;
@@ -80,6 +98,16 @@ static int stor_add_node_addr(struct storage_node *sn,
 	struct addrinfo *res, *res0;
 	int rc;
 
+	if (sn->hostname == NULL || strcmp(sn->hostname, hostname) != 0) {
+		free(sn->hostname);
+		sn->hostname = strdup(hostname);
+		if (!sn->hostname) {
+			applog(LOG_WARNING, "No core");
+			return -1;
+		}
+		sn->reported = false;
+	}
+
 	memset(&hints, 0, sizeof(struct addrinfo));
 	hints.ai_family = PF_UNSPEC;
 	hints.ai_socktype = SOCK_DGRAM;
@@ -114,7 +142,39 @@ static int stor_add_node_addr(struct storage_node *sn,
 	return -1;
 }
 
-void stor_add_node(uint32_t nid, const char *hostname, const char *portstr,
+static int stor_add_node_base(struct storage_node *sn, const char *base)
+{
+	if (sn->basepath == NULL || strcmp(sn->basepath, base) != 0) {
+		free(sn->basepath);
+		sn->basepath = strdup(base);
+		if (!sn->basepath) {
+			applog(LOG_WARNING, "No core");
+			return -1;
+		}
+		sn->reported = false;
+	}
+	return 0;
+}
+
+static int stor_add_node_this(struct storage_node *sn,
+			      enum storage_type type, const char *base,
+			      const char *hostname, const char *portstr)
+{
+	sn->type = type;
+	switch (type) {
+	case STT_POSIX:
+		sn->ops = &stor_ops_posix;
+		return stor_add_node_base(sn, base);
+	case STT_SWIFT:
+		return -1;
+	default:
+		sn->ops = &stor_ops_chunk;
+		return stor_add_node_addr(sn, hostname, portstr);
+	}
+}
+
+void stor_add_node(uint32_t nid, enum storage_type type, const char *base,
+		   const char *hostname, const char *portstr,
 		   struct geo *locp)
 {
 	struct storage_node *sn;
@@ -122,7 +182,7 @@ void stor_add_node(uint32_t nid, const char *hostname, const char *portstr,
 	g_mutex_lock(tabled_srv.bigmutex);
 	sn = _stor_node_by_nid(nid);
 	if (sn) {
-		stor_add_node_addr(sn, hostname, portstr);
+		stor_add_node_this(sn, type, base, hostname, portstr);
 	} else {
 		if ((sn = malloc(sizeof(struct storage_node))) == NULL) {
 			applog(LOG_WARNING, "No core (%ld)",
@@ -132,17 +192,10 @@ void stor_add_node(uint32_t nid, const char *hostname, const char *portstr,
 		}
 		memset(sn, 0, sizeof(struct storage_node));
 		sn->id = nid;
-		sn->ops = &stor_ops_chunk;
-
-		if ((sn->hostname = strdup(hostname)) == NULL) {
-			applog(LOG_WARNING, "No core");
-			free(sn);
-			g_mutex_unlock(tabled_srv.bigmutex);
-			return;
-		}
 
-		if (stor_add_node_addr(sn, hostname, portstr)) {
+		if (stor_add_node_this(sn, type, base, hostname, portstr)) {
 			free(sn->hostname);
+			free(sn->basepath);
 			free(sn);
 			g_mutex_unlock(tabled_srv.bigmutex);
 			return;
@@ -165,16 +218,37 @@ void stor_stats()
 	now = time(NULL);
 	list_for_each_entry(sn, &tabled_srv.all_stor, all_link) {
 		if (sn->last_up) {
-			applog(LOG_INFO,
-			       "SN: nid %u %s ref %d name %s last %lu (+ %ld)",
-			       sn->id, sn->up? "up": "down",
-			       sn->ref, sn->hostname,
-			       (long) sn->last_up, (long) (now - sn->last_up));
+			switch (sn->type) {
+			case STT_POSIX:
+				applog(LOG_INFO, "SN: nid %u %s ref %d"
+				       " path %s last %lu (+ %ld)",
+				       sn->id, sn->up? "up": "down",
+				       sn->ref, sn->basepath,
+				       (long) sn->last_up,
+				       (long) (now - sn->last_up));
+				break;
+			default:
+				applog(LOG_INFO, "SN: nid %u %s ref %d"
+				       " name %s last %lu (+ %ld)",
+				       sn->id, sn->up? "up": "down",
+				       sn->ref, sn->hostname,
+				       (long) sn->last_up,
+				       (long) (now - sn->last_up));
+			}
 		} else {
-			applog(LOG_INFO,
-			       "SN: nid %u %s ref %d name %s",
-			       sn->id, sn->up? "up": "down",
-			       sn->ref, sn->hostname);
+			switch (sn->type) {
+			case STT_POSIX:
+				applog(LOG_INFO,
+				       "SN: nid %u %s ref %d path %s",
+				       sn->id, sn->up? "up": "down",
+				       sn->ref, sn->basepath);
+				break;
+			default:
+				applog(LOG_INFO,
+				       "SN: nid %u %s ref %d name %s",
+				       sn->id, sn->up? "up": "down",
+				       sn->ref, sn->hostname);
+			}
 		}
 	}
 	g_mutex_unlock(tabled_srv.bigmutex);
@@ -193,18 +267,39 @@ bool stor_status(struct client *cli, GList *content)
 	now = time(NULL);
 	list_for_each_entry(sn, &tabled_srv.all_stor, all_link) {
 		if (sn->last_up) {
-			rc = asprintf(&str,
-				     "SN: nid %u %s ref %d name %s"
-				     " last %lu (+ %ld)<br />\r\n",
-				     sn->id, sn->up? "up": tag_down,
-				     sn->ref, sn->hostname,
-				     (long) sn->last_up,
-				     (long) (now - sn->last_up));
+			switch (sn->type) {
+			case STT_POSIX:
+				rc = asprintf(&str,
+					      "SN: nid %u %s ref %d path %s"
+					      " last %lu (+ %ld)<br />\r\n",
+					      sn->id, sn->up? "up": tag_down,
+					      sn->ref, sn->basepath,
+					      (long) sn->last_up,
+					      (long) (now - sn->last_up));
+				break;
+			default:
+				rc = asprintf(&str,
+					      "SN: nid %u %s ref %d name %s"
+					      " last %lu (+ %ld)<br />\r\n",
+					      sn->id, sn->up? "up": tag_down,
+					      sn->ref, sn->hostname,
+					      (long) sn->last_up,
+					      (long) (now - sn->last_up));
+			}
 		} else {
-			rc = asprintf(&str,
-				     "SN: nid %u %s ref %d name %s<br />\r\n",
-				     sn->id, sn->up? "up": tag_down,
-				     sn->ref, sn->hostname);
+			switch (sn->type) {
+			case STT_POSIX:
+				rc = asprintf(&str, "SN: nid %u %s ref %d"
+					      "path %s<br />\r\n",
+					      sn->id, sn->up? "up": tag_down,
+					      sn->ref, sn->basepath);
+				break;
+			default:
+				rc = asprintf(&str, "SN: nid %u %s ref %d"
+					      "name %s<br />\r\n",
+					      sn->id, sn->up? "up": tag_down,
+					      sn->ref, sn->hostname);
+			}
 		}
 		if (rc < 0)
 			break;
diff --git a/server/storparse.c b/server/storparse.c
index cbffe0f..feec74e 100644
--- a/server/storparse.c
+++ b/server/storparse.c
@@ -1,6 +1,6 @@
 
 /*
- * Copyright 2009 Red Hat, Inc.
+ * Copyright 2009,2010 Red Hat, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -28,6 +28,14 @@
 #include <ctype.h>
 #include "tabled.h"
 
+enum config_type {
+	CFG_TYPE_NONE = 0,	/* Omitted <Type>, assuming Chunk */
+	CFG_TYPE_UNKNOWN,	/* Incompatible, like "swift-ssl" or "nfs4" */
+	CFG_TYPE_CHUNK,		/* Explicit Chunk */
+	CFG_TYPE_FS,
+	CFG_TYPE_SWIFT,
+};
+
 struct config_context {
 	char		*text;
 
@@ -49,6 +57,8 @@ struct config_context {
 	struct geo	loc;
 
 	unsigned int	nid;
+	enum config_type stor_type;
+	char		*stor_base;
 };
 
 static void cfg_elm_start (GMarkupParseContext *context,
@@ -315,6 +325,37 @@ static void cfg_elm_end (GMarkupParseContext *context,
 		}
 	}
 
+	else if (!strcmp(element_name, "Base")) {
+		if (!cc->text) {
+			applog(LOG_WARNING, "%s: Base element empty",
+			       cc->fname);
+			return;
+		}
+		free(cc->stor_base);
+		cc->stor_base = cc->text;
+		cc->text = NULL;
+	}
+
+	else if (!strcmp(element_name, "Type")) {
+		if (!cc->text) {
+			applog(LOG_WARNING, "%s: Type element empty",
+			       cc->fname);
+			return;
+		}
+		if (!strcmp(cc->text, "chunk")) {
+			cc->stor_type = CFG_TYPE_CHUNK;
+		} else if (!strcmp(cc->text, "fs")) {
+			cc->stor_type = CFG_TYPE_FS;
+		} else if (!strcmp(cc->text, "swift")) {
+			/* "cf" type reserved for "old" Rackspace auth. */
+			cc->stor_type = CFG_TYPE_SWIFT;
+		} else {
+			cc->stor_type = CFG_TYPE_UNKNOWN;
+		}
+		free(cc->text);
+		cc->text = NULL;
+	}
+
 	else {
 		applog(LOG_WARNING, "%s: Unknown element \"%s\"",
 		       cc->fname, element_name);
@@ -350,6 +391,32 @@ static void cfg_elm_text (GMarkupParseContext *context,
 		cc->text = g_strndup(text, text_len);
 }
 
+static bool stor_verify_chunk(char *fname, struct config_context *cc)
+{
+	if (!cc->nid) {
+		applog(LOG_WARNING, "%s: No NID", fname);
+		return false;
+	}
+	if (!cc->stor_ok) {
+		applog(LOG_WARNING, "%s: No useable Socket clause", fname);
+		return false;
+	}
+	return true;
+}
+
+static bool stor_verify_fs(char *fname, struct config_context *cc)
+{
+	if (!cc->nid) {
+		applog(LOG_WARNING, "%s: No NID", fname);
+		return false;
+	}
+	if (!cc->stor_base) {
+		applog(LOG_WARNING, "%s: No base directory", fname);
+		return false;
+	}
+	return true;
+}
+
 static const GMarkupParser cfg_parse_ops = {
 	.start_element		= cfg_elm_start,
 	.end_element		= cfg_elm_end,
@@ -378,15 +445,25 @@ void stor_parse(char *fname, const char *text, size_t len)
 
 	g_markup_parse_context_free(parser);
 
-	if (!ctx.nid) {
-		applog(LOG_WARNING, "%s: No NID\n", fname);
-		goto out_free_all;
+	switch (ctx.stor_type) {
+	case CFG_TYPE_FS:
+		if (!stor_verify_fs(fname, &ctx))
+			goto out_free_all;
+		stor_add_node(ctx.nid, STT_POSIX, ctx.stor_base,
+			      NULL, NULL, &ctx.loc);
+		break;
+	case CFG_TYPE_SWIFT:
+	case CFG_TYPE_UNKNOWN:
+		if (debugging)
+			applog(LOG_DEBUG, "%s: Unknown storage type", fname);
+		break;
+	case CFG_TYPE_CHUNK:
+	default:
+		if (!stor_verify_chunk(fname, &ctx))
+			goto out_free_all;
+		stor_add_node(ctx.nid, STT_CHUNK, NULL,
+			      ctx.stor_ok_host, ctx.stor_ok_port, &ctx.loc);
 	}
-	if (!ctx.stor_ok) {
-		applog(LOG_WARNING, "%s: No useable Socket clause", fname);
-		goto out_free_all;
-	}
-	stor_add_node(ctx.nid, ctx.stor_ok_host, ctx.stor_ok_port, &ctx.loc);
 
 out_free_all:
 	free(ctx.text);
@@ -400,5 +477,7 @@ out_free_all:
 	free(ctx.loc.area);
 	free(ctx.loc.zone);
 	free(ctx.loc.rack);
+
+	free(ctx.stor_base);
 	return;
 }
diff --git a/server/tabled.h b/server/tabled.h
index 27bc7c1..0bbe527 100644
--- a/server/tabled.h
+++ b/server/tabled.h
@@ -24,6 +24,7 @@
 #include <time.h>
 #include <netinet/in.h>
 #include <openssl/md5.h>
+#include <curl/curl.h>
 #include <glib.h>
 #include <pcre.h>
 #include <event.h>
@@ -90,6 +91,12 @@ struct geo {
 	char			*rack;
 };
 
+enum storage_type {
+	STT_CHUNK,		/* Hail chunkserver */
+	STT_POSIX,		/* UNIX filesystem interface, may be NFS */
+	STT_SWIFT		/* OpenStack storage (evolved CloudFiles) */
+};
+
 struct storage_node;
 struct open_chunk;
 
@@ -115,15 +122,21 @@ struct st_node_ops {
 struct storage_node {
 	struct list_head	all_link;
 	uint32_t		id;
+	bool			reported;
 	bool			up;
 	time_t			last_up;
+	enum storage_type	type;
 	struct st_node_ops	*ops;
 
 	int ref;		/* number of open_chunk or other */
 
+	/* chunk */
 	unsigned		alen;
 	struct sockaddr_in6	addr;
 	char			*hostname;
+
+	/* file */
+	char			*basepath;
 };
 
 typedef bool (*cli_evt_func)(struct client *, unsigned int,
@@ -154,6 +167,13 @@ struct open_chunk {
 	int			rfd;
 	bool			r_armed;
 	struct event		revt;
+
+	/* posix */
+	/*
+	 * Don't merge fd, rfd, wfd prematurely. A future backend may need
+	 * several fds and then what? It's an implementation detail.
+	 */
+	int			pfd;
 };
 
 /* internal client socket state */
@@ -297,6 +317,9 @@ struct server {
 	struct server_stats	stats;		/* global statistics */
 };
 
+#define STOR_KEY_SLEN  16
+#define STOR_KEY_FMT	"%016llx"
+
 /*
  * Low-level channel, for both sides.
  *
@@ -429,6 +452,8 @@ extern void read_config(void);
 /* storage.c */
 extern struct storage_node *stor_node_get(struct storage_node *stn);
 extern void stor_node_put(struct storage_node *stn);
+extern void stor_read_event(int fd, short events, void *userdata);
+extern void stor_write_event(int fd, short events, void *userdata);
 static inline int stor_open(struct open_chunk *cep, struct storage_node *stn,
 			    struct event_base *ev_base)
 {
@@ -488,7 +513,8 @@ static inline int stor_node_check(struct storage_node *stn)
 	return stn->ops->node_check(stn);
 }
 extern struct storage_node *stor_node_by_nid(uint32_t nid);
-extern void stor_add_node(uint32_t nid,
+extern void stor_add_node(uint32_t nid, enum storage_type type,
+			  const char *base,
 			  const char *hostname, const char *portstr,
 			  struct geo *locp);
 extern void stor_stats(void);
@@ -500,6 +526,9 @@ extern void stor_parse(char *fname, const char *text, size_t len);
 /* stor_chunk.c */
 extern struct st_node_ops stor_ops_chunk;
 
+/* stor_fs.c */
+extern struct st_node_ops stor_ops_posix;
+
 /* replica.c */
 extern void rep_init(struct event_base *ev_base);
 extern void rep_start(void);
diff --git a/test/Makefile.am b/test/Makefile.am
index cc4e6fe..60d7b66 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -6,11 +6,13 @@ EXTRA_DIST =			\
 	test.h			\
 	users.data		\
 	chunkd-test.conf	\
+	be_fs-test.conf		\
 	tabled-test.conf	\
 	prep-db			\
 	start-daemon		\
 	pid-exists		\
 	daemon-running		\
+	combo-redux		\
 	stop-daemon		\
 	clean-db
 
@@ -27,6 +29,7 @@ TESTS =				\
 	hdr-content-type	\
 	hdr-meta		\
 	list-keys		\
+	combo-redux		\
 	stop-daemon		\
 	clean-db
 
diff --git a/test/be_fs-test.conf b/test/be_fs-test.conf
new file mode 100644
index 0000000..09b868c
--- /dev/null
+++ b/test/be_fs-test.conf
@@ -0,0 +1,5 @@
+<Chunk>
+ <NID>256</NID>
+ <Type>fs</Type>
+ <Base>data/be_fs</Base>
+</Chunk>
diff --git a/test/combo-redux b/test/combo-redux
new file mode 100755
index 0000000..2743a55
--- /dev/null
+++ b/test/combo-redux
@@ -0,0 +1,74 @@
+#!/bin/sh
+#
+# We stop tabled because it makes it easier to know what went wrong.
+# We could just swap the back-end on the fly, but then replication
+# confuses matters.
+#
+
+killpid () {
+	pidfile=$1
+	kill $(cat $pidfile)
+
+	for n in 0 1 2 3 4 5 6 7 8 9
+	do
+		if [ ! -f $pidfile ]
+		then
+			return 0
+		fi
+
+		sleep 1
+	done
+
+	echo "PID file $pidfile not removed, after signal sent." >&2
+	rm -f $pidfile
+	return 1
+}
+
+#
+# Step 1. Kill tabled and chunkd.
+#
+rm -f tabled.acc
+if [ ! -f tabled.pid ]
+then
+	# Just a warning. Previous test somehow made the daemon to die.
+	echo "No tabled PID file found." >&2
+else
+	killpid tabled.pid || exit 1
+fi
+
+if [ ! -f chunkd.pid ]
+then
+	echo "No chunkd PID file found." >&2
+else
+	killpid chunkd.pid || exit 1
+fi
+
+#
+# Step 2. Swap chunk back-end for fs.
+# We remove the chunk's <InfoPath> just to be neat.
+#
+if [ \! -s cld.port ]; then
+	echo "cld.port is not available" >&2
+	exit 1
+fi
+echo + "cldcli -h localhost:"`cat cld.port`
+cldcli -h localhost:`cat cld.port` <<EOF
+rm /chunk-default/19690720
+cpin $top_srcdir/test/be_fs-test.conf /chunk-default/256
+EOF
+
+#
+# Step 3. Restart tabled.
+#
+../server/tabled -C $top_srcdir/test/tabled-test.conf -E
+./wait-for-listen || exit 1
+
+#
+# Step 4. Run some of the tests that framework has done before.
+#
+./basic-object || exit 1
+echo "PASS redux: basic-object"
+./large-object || exit 1
+echo "PASS redux: large-object"
+
+exit 0
diff --git a/test/prep-db b/test/prep-db
index ef90e1c..b98fc63 100755
--- a/test/prep-db
+++ b/test/prep-db
@@ -4,10 +4,14 @@ DATADIR=data
 TDBDIR=$DATADIR/tdb
 CLDDIR=$DATADIR/cld
 CHUNKDIR=$DATADIR/chunk
+BEFSDIR=$DATADIR/be_fs
 
 mkdir -p $TDBDIR
 mkdir -p $CLDDIR
 mkdir -p $CHUNKDIR
+mkdir -p $BEFSDIR
+
+echo 256 > $BEFSDIR/NID
 
 cat $top_srcdir/test/users.data | ../server/tdbadm -u -C $top_srcdir/test/tabled-test.conf
 
diff --git a/test/start-daemon b/test/start-daemon
index 6985468..9eb45aa 100755
--- a/test/start-daemon
+++ b/test/start-daemon
@@ -12,6 +12,7 @@ do
 	fi
 done
 
+rm -f cld.port
 # May be different on Solaris... like /usr/libexec or such.
 cld -d data/cld -P cld.pid -p auto --port-file=cld.port -E
 
diff --git a/test/stop-daemon b/test/stop-daemon
index de5db0c..2370ec2 100755
--- a/test/stop-daemon
+++ b/test/stop-daemon
@@ -25,22 +25,21 @@ ret=0
 
 if [ ! -f tabled.pid ]
 then
-	# Just a warning. Previous test somehow made the daemon to die.
 	echo "No tabled PID file found." >&2
 else
 	killpid tabled.pid || ret=1
 fi
 
-if [ ! -f chunkd.pid ]
+# The combo-redux kills chunkd, kill quietly if anything slipped through.
+if [ -f chunkd.pid ]
 then
-	echo "No chunkd PID file found." >&2
-else
 	killpid chunkd.pid || ret=1
 fi
 
 if [ ! -f cld.pid ]
 then
-	echo "No cld PID file found." >&2
+	# Just a warning. Previous test somehow made the daemon to die.
+	echo "No cld.pid file found." >&2
 else
 	killpid cld.pid || ret=1
 fi
--
To unsubscribe from this list: send the line "unsubscribe hail-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Fedora Clound]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux