dovecot-2.2: director: Added support for backend cluster "tags".

dovecot at dovecot.org dovecot at dovecot.org
Wed Nov 12 04:59:53 UTC 2014


details:   http://hg.dovecot.org/dovecot-2.2/rev/a7e830b9b967
changeset: 18067:a7e830b9b967
user:      Timo Sirainen <tss at iki.fi>
date:      Wed Nov 12 06:58:37 2014 +0200
description:
director: Added support for backend cluster "tags".
This allows using a single director ring for multiple backend clusters. By
default everything has an empty tag. A passdb lookup can return
"director_tag" field containing the wanted tag name. If there aren't any
backend servers with the wanted tag, it's treated the same as if there
aren't any backend servers available (= wait for 30 secs for a backend and
then return temporary failure).

Tags can be added to configuration by adding @tag suffix to IPs/hosts. For
example:

director_mail_servers = 10.0.0.100-10.0.0.110 at name1 10.0.0.120 at name2

"doveadm director add" can also add tags either with @tag suffix or with -t
parameter. "doveadm director status user at domain" requires giving the user's
correct tag with -t parameter or the results won't be correct (empty tag's
results are shown). Tags can't currently be changed for an existing host
without removing it first.

diffstat:

 src/director/director-connection.c |  22 +++++++-
 src/director/director-request.c    |  29 ++++++++--
 src/director/director-request.h    |   1 +
 src/director/director.c            |  26 ++++++++-
 src/director/director.h            |   2 +
 src/director/doveadm-connection.c  |  38 ++++++++++++---
 src/director/login-connection.c    |   6 +-
 src/director/mail-host.c           |  94 +++++++++++++++++++++++++++++--------
 src/director/mail-host.h           |   9 ++-
 src/director/main.c                |   3 +-
 src/doveadm/doveadm-director.c     |  47 +++++++++++++-----
 11 files changed, 214 insertions(+), 63 deletions(-)

diffs (truncated from 776 to 300 lines):

diff -r 26679856fbd5 -r a7e830b9b967 src/director/director-connection.c
--- a/src/director/director-connection.c	Wed Nov 12 06:46:45 2014 +0200
+++ b/src/director/director-connection.c	Wed Nov 12 06:58:37 2014 +0200
@@ -35,6 +35,7 @@
 #include "istream.h"
 #include "ostream.h"
 #include "str.h"
+#include "strescape.h"
 #include "master-service.h"
 #include "mail-host.h"
 #include "director.h"
@@ -821,15 +822,18 @@
 {
 	struct mail_host *host;
 	struct ip_addr ip;
+	const char *tag = "";
 	unsigned int vhost_count;
 	bool update;
 
-	if (str_array_length(args) != 2 ||
+	if (str_array_length(args) < 2 ||
 	    net_addr2ip(args[0], &ip) < 0 ||
 	    str_to_uint(args[1], &vhost_count) < 0) {
 		director_cmd_error(conn, "Invalid parameters");
 		return FALSE;
 	}
+	if (args[2] != NULL)
+		tag = args[2];
 	if (conn->ignore_host_events) {
 		/* remote is sending hosts in a handshake, but it doesn't have
 		   a completed ring and we do. */
@@ -839,10 +843,17 @@
 
 	host = mail_host_lookup(conn->dir->mail_hosts, &ip);
 	if (host == NULL) {
-		host = mail_host_add_ip(conn->dir->mail_hosts, &ip);
+		host = mail_host_add_ip(conn->dir->mail_hosts, &ip, tag);
 		update = TRUE;
 	} else {
 		update = host->vhost_count != vhost_count;
+		if (strcmp(tag, host->tag) != 0) {
+			i_error("director(%s): Host %s changed tag from '%s' to '%s'",
+				conn->name, net_ip2addr(&host->ip),
+				host->tag, tag);
+			mail_host_set_tag(host, tag);
+			update = TRUE;
+		}
 	}
 
 	if (update) {
@@ -1541,8 +1552,13 @@
 
 	str_printfa(str, "HOST-HAND-START\t%u\n", conn->dir->ring_handshaked);
 	array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) {
-		str_printfa(str, "HOST\t%s\t%u\n",
+		str_printfa(str, "HOST\t%s\t%u",
 			    net_ip2addr(&(*hostp)->ip), (*hostp)->vhost_count);
+		if ((*hostp)->tag[0] != '\0') {
+			str_append_c(str, '\t');
+			str_append_tabescaped(str, (*hostp)->tag);
+		}
+		str_append_c(str, '\n');
 	}
 	str_printfa(str, "HOST-HAND-END\t%u\n", conn->dir->ring_handshaked);
 }
diff -r 26679856fbd5 -r a7e830b9b967 src/director/director-request.c
--- a/src/director/director-request.c	Wed Nov 12 06:46:45 2014 +0200
+++ b/src/director/director-request.c	Wed Nov 12 06:58:37 2014 +0200
@@ -36,11 +36,18 @@
 	time_t create_time;
 	unsigned int username_hash;
 	enum director_request_delay_reason delay_reason;
+	char *username_tag;
 
 	director_request_callback *callback;
 	void *context;
 };
 
+static void director_request_free(struct director_request *request)
+{
+	i_free(request->username_tag);
+	i_free(request);
+}
+
 static const char *
 director_request_get_timeout_error(struct director_request *request,
 				   struct user *user, string_t *str)
@@ -68,7 +75,10 @@
 		str_printfa(str, ", user refreshed %u secs ago",
 			    (unsigned int)(ioloop_time - user->timestamp));
 	}
-	str_printfa(str, "hash=%u)", request->username_hash);
+	str_printfa(str, ", hash=%u", request->username_hash);
+	if (request->username_tag != NULL)
+		str_printfa(str, ", tag=%s", request->username_tag);
+	str_append_c(str, ')');
 	return str_c(str);
 }
 
@@ -103,7 +113,7 @@
 		T_BEGIN {
 			request->callback(NULL, errormsg, request->context);
 		} T_END;
-		i_free(request);
+		director_request_free(request);
 	}
 
 	if (array_count(&dir->pending_requests) == 0 && dir->to_request != NULL)
@@ -111,6 +121,7 @@
 }
 
 void director_request(struct director *dir, const char *username,
+		      const char *tag,
 		      director_request_callback *callback, void *context)
 {
 	struct director_request *request;
@@ -121,6 +132,7 @@
 	request->dir = dir;
 	request->create_time = ioloop_time;
 	request->username_hash = username_hash;
+	request->username_tag = tag[0] == '\0' ? NULL : i_strdup(tag);
 	request->callback = callback;
 	request->context = context;
 
@@ -159,7 +171,8 @@
 }
 
 static bool
-director_request_existing(struct director_request *request, struct user *user)
+director_request_existing(struct director_request *request, struct user *user,
+			  const char *tag)
 {
 	struct director *dir = request->dir;
 	struct mail_host *host;
@@ -193,7 +206,7 @@
 
 	/* user is close to being expired. another director may have
 	   already expired it. */
-	host = mail_host_get_by_hash(dir->mail_hosts, user->username_hash);
+	host = mail_host_get_by_hash(dir->mail_hosts, user->username_hash, tag);
 	if (!dir->ring_synced) {
 		/* try again later once ring is synced */
 		request->delay_reason = REQUEST_DELAY_RINGNOTSYNCED;
@@ -253,6 +266,7 @@
 	struct director *dir = request->dir;
 	struct mail_host *host;
 	struct user *user;
+	const char *tag;
 
 	if (!dir->ring_handshaked) {
 		/* delay requests until ring handshaking is complete */
@@ -264,8 +278,9 @@
 	}
 
 	user = user_directory_lookup(dir->users, request->username_hash);
+	tag = request->username_tag == NULL ? "" : request->username_tag;
 	if (user != NULL) {
-		if (!director_request_existing(request, user))
+		if (!director_request_existing(request, user, tag))
 			return FALSE;
 		user_directory_refresh(dir->users, user);
 		dir_debug("request: %u refreshed timeout to %u",
@@ -280,7 +295,7 @@
 			return FALSE;
 		}
 		host = mail_host_get_by_hash(dir->mail_hosts,
-					     request->username_hash);
+					     request->username_hash, tag);
 		if (host == NULL) {
 			/* all hosts have been removed */
 			request->delay_reason = REQUEST_DELAY_NOHOSTS;
@@ -299,6 +314,6 @@
 	T_BEGIN {
 		request->callback(&user->host->ip, NULL, request->context);
 	} T_END;
-	i_free(request);
+	director_request_free(request);
 	return TRUE;
 }
diff -r 26679856fbd5 -r a7e830b9b967 src/director/director-request.h
--- a/src/director/director-request.h	Wed Nov 12 06:46:45 2014 +0200
+++ b/src/director/director-request.h	Wed Nov 12 06:58:37 2014 +0200
@@ -9,6 +9,7 @@
 			  void *context);
 
 void director_request(struct director *dir, const char *username,
+		      const char *tag,
 		      director_request_callback *callback, void *context);
 bool director_request_continue(struct director_request *request);
 
diff -r 26679856fbd5 -r a7e830b9b967 src/director/director.c
--- a/src/director/director.c	Wed Nov 12 06:46:45 2014 +0200
+++ b/src/director/director.c	Wed Nov 12 06:58:37 2014 +0200
@@ -4,6 +4,7 @@
 #include "ioloop.h"
 #include "array.h"
 #include "str.h"
+#include "strescape.h"
 #include "ipc-client.h"
 #include "user-directory.h"
 #include "mail-host.h"
@@ -503,6 +504,8 @@
 			  struct director_host *orig_src,
 			  struct mail_host *host)
 {
+	string_t *str;
+
 	/* update state in case this is the first mail host being added */
 	director_set_state_changed(dir);
 
@@ -511,10 +514,25 @@
 		orig_src->last_seq++;
 	}
 
-	director_update_send(dir, src, t_strdup_printf(
-		"HOST\t%s\t%u\t%u\t%s\t%u\n",
-		net_ip2addr(&orig_src->ip), orig_src->port, orig_src->last_seq,
-		net_ip2addr(&host->ip), host->vhost_count));
+	str = t_str_new(128);
+	str_printfa(str, "HOST\t%s\t%u\t%u\t%s\t%u",
+		    net_ip2addr(&orig_src->ip), orig_src->port,
+		    orig_src->last_seq,
+		    net_ip2addr(&host->ip), host->vhost_count);
+	if (host->tag[0] == '\0')
+		;
+	else if (dir->ring_handshaked &&
+		 dir->ring_min_version < DIRECTOR_VERSION_TAGS) {
+		i_error("Ring has directors that don't support tags - removing host %s with tag '%s'",
+			net_ip2addr(&host->ip), host->tag);
+		director_remove_host(dir, NULL, NULL, host);
+		return;
+	} else {
+		str_append_c(str, '\t');
+		str_append_tabescaped(str, host->tag);
+	}
+	str_append_c(str, '\n');
+	director_update_send(dir, src, str_c(str));
 	director_sync(dir);
 }
 
diff -r 26679856fbd5 -r a7e830b9b967 src/director/director.h
--- a/src/director/director.h	Wed Nov 12 06:46:45 2014 +0200
+++ b/src/director/director.h	Wed Nov 12 06:58:37 2014 +0200
@@ -18,6 +18,8 @@
 #define DIRECTOR_VERSION_USER_KICK 4
 /* options supported in handshake */
 #define DIRECTOR_VERSION_OPTIONS 5
+/* user tags supported */
+#define DIRECTOR_VERSION_TAGS 5
 
 /* Minimum time between even attempting to communicate with a director that
    failed due to a protocol error. */
diff -r 26679856fbd5 -r a7e830b9b967 src/director/doveadm-connection.c
--- a/src/director/doveadm-connection.c	Wed Nov 12 06:46:45 2014 +0200
+++ b/src/director/doveadm-connection.c	Wed Nov 12 06:58:37 2014 +0200
@@ -7,6 +7,7 @@
 #include "ostream.h"
 #include "array.h"
 #include "str.h"
+#include "strescape.h"
 #include "llist.h"
 #include "master-service.h"
 #include "user-directory.h"
@@ -46,9 +47,11 @@
 	string_t *str = t_str_new(1024);
 
 	array_foreach(mail_hosts_get(conn->dir->mail_hosts), hostp) {
-		str_printfa(str, "%s\t%u\t%u\n",
+		str_printfa(str, "%s\t%u\t%u\t",
 			    net_ip2addr(&(*hostp)->ip), (*hostp)->vhost_count,
 			    (*hostp)->user_count);
+		str_append_tabescaped(str, (*hostp)->tag);
+		str_append_c(str, '\n');
 	}
 	str_append_c(str, '\n');
 	o_stream_nsend(conn->output, str_data(str), str_len(str));
@@ -244,14 +247,21 @@
 doveadm_cmd_host_set(struct doveadm_connection *conn, const char *line)
 {
 	struct director *dir = conn->dir;
-	const char *const *args;
+	const char *const *args, *ip_str, *tag = "";
 	struct mail_host *host;
 	struct ip_addr ip;
 	unsigned int vhost_count = UINT_MAX;
 
 	args = t_strsplit_tab(line);
-	if (args[0] == NULL ||
-	    net_addr2ip(args[0], &ip) < 0 ||
+	ip_str = args[0];
+	if (ip_str != NULL) {
+		tag = strchr(ip_str, '@');
+		if (tag == NULL)
+			tag = "";
+		else
+			ip_str = t_strdup_until(ip_str, tag++);
+	}
+	if (ip_str == NULL || net_addr2ip(ip_str, &ip) < 0 ||
 	    (args[1] != NULL && str_to_uint(args[1], &vhost_count) < 0)) {
 		i_error("doveadm sent invalid HOST-SET parameters: %s", line);


More information about the dovecot-cvs mailing list