dovecot-2.0: director: Added support for moving user to another ...

dovecot at dovecot.org dovecot at dovecot.org
Mon May 23 14:54:20 EEST 2011


details:   http://hg.dovecot.org/dovecot-2.0/rev/79f9dce5d5fd
changeset: 12823:79f9dce5d5fd
user:      Timo Sirainen <tss at iki.fi>
date:      Mon May 23 14:54:02 2011 +0300
description:
director: Added support for moving user to another server with "doveadm director move".

diffstat:

 src/director/director-connection.c |   78 +++++++++++++-
 src/director/director-request.c    |    9 +-
 src/director/director-test.sh      |    3 +-
 src/director/director.c            |  214 +++++++++++++++++++++++++++++++++++++
 src/director/director.h            |    9 +
 src/director/doveadm-connection.c  |   37 ++++++
 src/director/user-directory.c      |    2 +
 src/director/user-directory.h      |   26 ++++
 src/doveadm/doveadm-director.c     |   38 ++++++
 src/login-common/login-proxy.c     |   45 +++++++
 10 files changed, 456 insertions(+), 5 deletions(-)

diffs (truncated from 654 to 300 lines):

diff -r f8c378e8b461 -r 79f9dce5d5fd src/director/director-connection.c
--- a/src/director/director-connection.c	Fri May 20 21:46:32 2011 +0300
+++ b/src/director/director-connection.c	Mon May 23 14:54:02 2011 +0300
@@ -348,8 +348,8 @@
 	    net_addr2ip(args[0], &ip) < 0 ||
 	    str_to_uint(args[1], &port) < 0 ||
 	    str_to_uint(args[2], &seq) < 0) {
-		i_error("director(%s): Command is missing parameters",
-			conn->name);
+		i_error("director(%s): Command is missing parameters: %s",
+			conn->name, t_strarray_join(args, " "));
 		return -1;
 	}
 	*_args = args + 3;
@@ -475,6 +475,74 @@
 	return TRUE;
 }
 
+static bool
+director_cmd_user_move(struct director_connection *conn,
+		       const char *const *args)
+{
+	struct director_host *dir_host;
+	struct mail_host *host;
+	struct ip_addr ip;
+	unsigned int username_hash;
+	int ret;
+
+	if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0)
+		return ret > 0;
+
+	if (str_array_length(args) != 2 ||
+	    str_to_uint(args[0], &username_hash) < 0 ||
+	    net_addr2ip(args[1], &ip) < 0) {
+		i_error("director(%s): Invalid USER-MOVE args", conn->name);
+		return FALSE;
+	}
+
+	host = mail_host_lookup(conn->dir->mail_hosts, &ip);
+	if (host != NULL) {
+		director_move_user(conn->dir, conn->host, dir_host,
+				   username_hash, host);
+	}
+	return TRUE;
+}
+
+static bool
+director_cmd_user_killed(struct director_connection *conn,
+			 const char *const *args)
+{
+	struct director_host *dir_host;
+	unsigned int username_hash;
+
+	if (str_array_length(args) != 1 ||
+	    str_to_uint(args[0], &username_hash) < 0) {
+		i_error("director(%s): Invalid USER-KILLED args", conn->name);
+		return FALSE;
+	}
+
+	director_user_killed(conn->dir, username_hash);
+	return TRUE;
+}
+
+static bool
+director_cmd_user_killed_everywhere(struct director_connection *conn,
+				    const char *const *args)
+{
+	struct director_host *dir_host;
+	unsigned int username_hash;
+	int ret;
+
+	if ((ret = director_cmd_is_seen(conn, &args, &dir_host)) != 0)
+		return ret > 0;
+
+	if (str_array_length(args) != 1 ||
+	    str_to_uint(args[0], &username_hash) < 0) {
+		i_error("director(%s): Invalid USER-KILLED-EVERYWHERE args",
+			conn->name);
+		return FALSE;
+	}
+
+	director_user_killed_everywhere(conn->dir, conn->host,
+					dir_host, username_hash);
+	return TRUE;
+}
+
 static void director_handshake_cmd_done(struct director_connection *conn)
 {
 	struct director *dir = conn->dir;
@@ -766,6 +834,12 @@
 		return director_cmd_host_remove(conn, args);
 	if (strcmp(cmd, "HOST-FLUSH") == 0)
 		return director_cmd_host_flush(conn, args);
+	if (strcmp(cmd, "USER-MOVE") == 0)
+		return director_cmd_user_move(conn, args);
+	if (strcmp(cmd, "USER-KILLED") == 0)
+		return director_cmd_user_killed(conn, args);
+	if (strcmp(cmd, "USER-KILLED-EVERYWHERE") == 0)
+		return director_cmd_user_killed_everywhere(conn, args);
 	if (strcmp(cmd, "DIRECTOR") == 0)
 		return director_cmd_director(conn, args);
 	if (strcmp(cmd, "SYNC") == 0)
diff -r f8c378e8b461 -r 79f9dce5d5fd src/director/director-request.c
--- a/src/director/director-request.c	Fri May 20 21:46:32 2011 +0300
+++ b/src/director/director-request.c	Mon May 23 14:54:02 2011 +0300
@@ -102,9 +102,14 @@
 	}
 
 	user = user_directory_lookup(dir->users, request->username_hash);
-	if (user != NULL)
+	if (user != NULL) {
+		if (user->kill_state != USER_KILL_STATE_NONE) {
+			/* delay processing this user's connections until
+			   its existing connections have been killed */
+			return FALSE;
+		}
 		user_directory_refresh(dir->users, user);
-	else {
+	} else {
 		if (!dir->ring_synced) {
 			/* delay adding new users until ring is again synced */
 			ring_log_delayed_warning(dir);
diff -r f8c378e8b461 -r 79f9dce5d5fd src/director/director-test.sh
--- a/src/director/director-test.sh	Fri May 20 21:46:32 2011 +0300
+++ b/src/director/director-test.sh	Mon May 23 14:54:02 2011 +0300
@@ -10,7 +10,7 @@
 while [ $i != $director_count ]; do
   i=`expr $i + 1`
   dirs="$dirs 127.0.1.$i"
-  echo "director	127.0.1.$i"
+  echo "127.0.1.$i	director"
   cat > dovecot-director$i.conf <<EOF
 listen = 127.0.1.$i
 base_dir = /var/run/dovecot$i
@@ -24,6 +24,7 @@
 info_log_path = /var/log/dovecot-access.log
 director_servers =$dirs
 director_mail_servers = 127.0.0.1-127.0.0.255
+disable_plaintext_auth = no
 
 ssl = no
 service director {
diff -r f8c378e8b461 -r 79f9dce5d5fd src/director/director.c
--- a/src/director/director.c	Fri May 20 21:46:32 2011 +0300
+++ b/src/director/director.c	Mon May 23 14:54:02 2011 +0300
@@ -4,14 +4,19 @@
 #include "ioloop.h"
 #include "array.h"
 #include "str.h"
+#include "ipc-client.h"
 #include "user-directory.h"
 #include "mail-host.h"
 #include "director-host.h"
 #include "director-connection.h"
 #include "director.h"
 
+#define DIRECTOR_IPC_PROXY_PATH "ipc"
+
 #define DIRECTOR_RECONNECT_RETRY_SECS 60
 #define DIRECTOR_RECONNECT_TIMEOUT_MSECS (30*1000)
+#define DIRECTOR_USER_MOVE_TIMEOUT_MSECS (30*1000)
+#define DIRECTOR_USER_MOVE_FINISH_DELAY_MSECS (12*1000)
 
 static bool director_is_self_ip_set(struct director *dir)
 {
@@ -346,6 +351,209 @@
 		net_ip2addr(&user->host->ip)));
 }
 
+struct director_user_kill_finish_ctx {
+	struct director *dir;
+	struct user *user;
+};
+
+static void
+director_user_kill_finish_delayed_to(struct director_user_kill_finish_ctx *ctx)
+{
+	i_assert(ctx->user->kill_state == USER_KILL_STATE_DELAY);
+
+	ctx->user->kill_state = USER_KILL_STATE_NONE;
+	timeout_remove(&ctx->user->to_move);
+
+	ctx->dir->state_change_callback(ctx->dir);
+	i_free(ctx);
+}
+
+static void
+director_user_kill_finish_delayed(struct director *dir, struct user *user)
+{
+	struct director_user_kill_finish_ctx *ctx;
+
+	ctx = i_new(struct director_user_kill_finish_ctx, 1);
+	ctx->dir = dir;
+	ctx->user = user;
+
+	user->kill_state = USER_KILL_STATE_DELAY;
+	timeout_remove(&user->to_move);
+
+	user->to_move = timeout_add(DIRECTOR_USER_MOVE_FINISH_DELAY_MSECS,
+				    director_user_kill_finish_delayed_to, ctx);
+}
+
+struct director_kill_context {
+	struct director *dir;
+	unsigned int username_hash;
+	bool self;
+};
+
+static void
+director_finish_user_kill(struct director *dir, struct user *user, bool self)
+{
+	if (dir->right == NULL || dir->right == dir->left) {
+		/* we're alone */
+		director_user_kill_finish_delayed(dir, user);
+	} else if (self ||
+		   user->kill_state == USER_KILL_STATE_KILLING_NOTIFY_RECEIVED) {
+		director_connection_send(dir->right, t_strdup_printf(
+			"USER-KILLED\t%u\n", user->username_hash));
+		user->kill_state = USER_KILL_STATE_KILLED_WAITING_FOR_EVERYONE;
+	} else {
+		i_assert(user->kill_state == USER_KILL_STATE_KILLING);
+		user->kill_state = USER_KILL_STATE_KILLED_WAITING_FOR_NOTIFY;
+	}
+}
+
+static void director_kill_user_callback(enum ipc_client_cmd_state state,
+					const char *data, void *context)
+{
+	struct director_kill_context *ctx = context;
+	struct user *user;
+
+	switch (state) {
+	case IPC_CLIENT_CMD_STATE_REPLY:
+		return;
+	case IPC_CLIENT_CMD_STATE_OK:
+		break;
+	case IPC_CLIENT_CMD_STATE_ERROR:
+		i_error("Failed to kill user %u connections: %s",
+			ctx->username_hash, data);
+		/* we can't really do anything but continue anyway */
+		break;
+	}
+
+	user = user_directory_lookup(ctx->dir->users, ctx->username_hash);
+	if (user == NULL || user->kill_state == USER_KILL_STATE_NONE)
+		return;
+
+	director_finish_user_kill(ctx->dir, user, ctx->self);
+}
+
+static void director_user_move_timeout(struct user *user)
+{
+	i_error("Finishing user %u move timed out, "
+		"its state may now be inconsistent", user->username_hash);
+
+	user->kill_state = USER_KILL_STATE_NONE;
+	timeout_remove(&user->to_move);
+}
+
+void director_move_user(struct director *dir, struct director_host *src,
+			struct director_host *orig_src,
+			unsigned int username_hash, struct mail_host *host)
+{
+	struct user *user;
+	const char *cmd;
+	struct director_kill_context *ctx;
+
+	/* 1. move this user's host, and set its "killing" flag to delay all of
+	   its future connections until all directors have killed the
+	   connections and notified us about it.
+
+	   2. tell the other directors about the move
+
+	   3. once user kill callback is called, tell the other directors
+	   with USER-KILLED that we're done killing the user.
+
+	   4. when some director gets a duplicate USER-KILLED, it's
+	   responsible for notifying all directors that user is completely
+	   killed.
+
+	   5. after receiving USER-KILLED-EVERYWHERE notification,
+	   new connections are again allowed for the user.
+	*/
+	user = user_directory_lookup(dir->users, username_hash);
+	if (user == NULL) {
+		user = user_directory_add(dir->users, username_hash,
+					  host, ioloop_time);
+	} else {
+		if (user->host == host) {
+			/* user is already in this host */
+			return;
+		}
+		user->host->user_count--;
+		user->host = host;
+		user->host->user_count++;
+		user->timestamp = ioloop_time;
+	}
+	if (user->kill_state == USER_KILL_STATE_NONE) {
+		ctx = i_new(struct director_kill_context, 1);
+		ctx->dir = dir;


More information about the dovecot-cvs mailing list