# Binary C programs
$(INSTALL) -d $(PBINDIR)
$(INSTALL) -m 750 $(O_USER) tools/captcha.sh $(PBINDIR)
- $(INSTALL) -m 750 $(O_USER) tools/joincluster $(PBINDIR)
- $(INSTALL) -m 750 $(O_USER) tools/leavecluster $(PBINDIR)
#
# Copy lite.sql
[ -d deps/sqlite3 ] && $(INSTALL) -d $(SQLDIR) || true
}
}
-# cluster setup
-join_cluster()
-{
- $EXEC_CMD "$EJABBERD_BIN_PATH/joincluster $*"
-}
-leave_cluster()
-{
- $EXEC_CMD "$EJABBERD_BIN_PATH/leavecluster $*"
-}
-
# allow sync calls
wait_for_status()
{
' etop') etop;;
' started') wait_for_status 0 30 2;; # wait 30x2s before timeout
' stopped') wait_for_status 3 15 2 && stop_epmd;; # wait 15x2s before timeout
- ' join_cluster'*) join_cluster ${ARGS# join_cluster};;
- ' leave_cluster'*) leave_cluster ${ARGS# leave_cluster};;
*) ctl $ARGS;;
esac
stop_kindly/2, send_service_message_all_mucs/2,
registered_vhosts/0,
reload_config/0,
+ %% Cluster
+ join_cluster/1, leave_cluster/1, list_cluster/0,
%% Erlang
update_list/0, update/1,
%% Accounts
args = [],
result = {res, rescode}},
+ #ejabberd_commands{name = join_cluster, tags = [cluster],
+ desc = "Join this node into the cluster handled by Node",
+ module = ?MODULE, function = join_cluster,
+ args = [{node, binary}],
+ result = {res, rescode}},
+ #ejabberd_commands{name = leave_cluster, tags = [cluster],
+ desc = "Remove node handled by Node from the cluster",
+ module = ?MODULE, function = leave_cluster,
+ args = [{node, binary}],
+ result = {res, rescode}},
+ #ejabberd_commands{name = list_cluster, tags = [cluster],
+ desc = "List nodes that are part of the cluster handled by Node",
+ module = ?MODULE, function = list_cluster,
+ args = [],
+ result = {nodes, {list, {node, atom}}}},
+
#ejabberd_commands{name = import_file, tags = [mnesia],
desc = "Import user data from jabberd14 spool file",
module = ?MODULE, function = import_file,
acl:start(),
shaper:start().
+%%%
+%%% Cluster management
+%%%
+
+join_cluster(NodeBin) ->
+ ejabberd_cluster:join(list_to_atom(binary_to_list(NodeBin))).
+
+leave_cluster(NodeBin) ->
+ ejabberd_cluster:leave(list_to_atom(binary_to_list(NodeBin))).
+
+list_cluster() ->
+ ejabberd_cluster:get_nodes().
+
%%%
%%% Migration management
%%%
%% API
-export([get_nodes/0, call/4, multicall/3, multicall/4]).
+-export([join/1, leave/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
multicall(Nodes, Module, Function, Args) ->
rpc:multicall(Nodes, Module, Function, Args, 5000).
+-spec join(node()) -> ok | {error, any()}.
+
+join(Node) ->
+ case {node(), net_adm:ping(Node)} of
+ {Node, _} ->
+ {error, {not_master, Node}};
+ {_, pong} ->
+ application:stop(ejabberd),
+ application:stop(mnesia),
+ mnesia:delete_schema([node()]),
+ application:start(mnesia),
+ mnesia:change_config(extra_db_nodes, [Node]),
+ mnesia:change_table_copy_type(schema, node(), disc_copies),
+ spawn(fun() ->
+ lists:foreach(fun(Table) ->
+ Type = call(Node, mnesia, table_info, [Table, storage_type]),
+ mnesia:add_table_copy(Table, node(), Type)
+ end, mnesia:system_info(tables)--[schema])
+ end),
+ application:start(ejabberd);
+ _ ->
+ {error, {no_ping, Node}}
+ end.
+
+-spec leave(node()) -> ok | {error, any()}.
+
+leave(Node) ->
+ case {node(), net_adm:ping(Node)} of
+ {Node, _} ->
+ Cluster = get_nodes()--[Node],
+ leave(Cluster, Node);
+ {_, pong} ->
+ rpc:call(Node, ?MODULE, leave, [Node], 10000);
+ {_, pang} ->
+ case mnesia:del_table_copy(schema, Node) of
+ {atomic, ok} -> ok;
+ {aborted, Reason} -> {error, Reason}
+ end
+ end.
+leave([], Node) ->
+ {error, {no_cluster, Node}};
+leave([Master|_], Node) ->
+ application:stop(ejabberd),
+ application:stop(mnesia),
+ call(Master, mnesia, del_table_copy, [schema, Node]),
+ spawn(fun() ->
+ mnesia:delete_schema([node()]),
+ erlang:halt(0)
+ end),
+ ok.
+++ /dev/null
-#!/bin/sh
-
-# Add the current ejabberd node in a cluster
-
-# copyright (c) 2010-2015 ProcessOne
-
-# Return Code:
-# 0 : groovy baby
-# 10 : ejabberdctl not found
-# 11 : erl not found
-# 12 : erlc not found
-# 20 : database dir can not be created
-# 21 : database dir not writable
-# 22 : temporary dir can not be created
-# 30 : network issue
-# 31 : node names incompatibility
-
-error()
-{
- echo "Error: $1" >&2
- exit $2
-}
-
-[ -z $NO_WARNINGS ] && {
- echo "--------------------------------------------------------------------"
- echo ""
- echo "ejabberd cluster configuration"
- echo ""
- echo "This ejabberd node will be configured for use in an ejabberd cluster."
- echo "IMPORTANT: all local data from the database will be lost, and"
- echo "cluster database will be initialized. All data from the master"
- echo "node will be replicated to this one."
- echo ""
- echo "--------------------------------------------------------------------"
- echo "Press any key to continue, or Ctrl+C to stop now"
- read foo
- echo ""
-}
-
-[ $# -eq 0 ] && {
- echo "Make sure you have a running remote master ejabberd node"
- echo "Before continuing, you must copy the ~/.erlang.cookie file from"
- echo "remote master node and check ejabberd.cfg compatibility."
- echo "e.g. hosts definition must match on all nodes"
- echo ""
- echo "The remote master node name is defined as ERLANG_NODE into"
- echo "ejabberdctl.cfg on that remote node."
- echo ""
- echo -n "Remote master node name: "
- read REMOTE
- echo ""
-} || {
- echo "Using passed parameter for remote master node name: $1"
- REMOTE=$1
-}
-
-PA=/tmp/clustersetup_$$
-CTL=$(which ejabberdctl)
-[ -x "$CTL" ] || {
- HERE=`which "$0"`
- BASE=`dirname $HERE`/..
- ROOTDIR=`cd $BASE; pwd`
- PATH=$ROOTDIR/bin:$PATH
- PA=$ROOTDIR/clustersetup_$$
- CTL=$(which ejabberdctl)
-}
-echo "Using commands:"
-[ -x "$CTL" ] && echo $CTL || error "can't find ejabberdctl" 10
-
-exec $CTL stop 2>/dev/null >/dev/null
-ERLC=${ERL}c
-
-[ -x $ERL ] && echo $ERL || error "can't find erl" 11
-[ -x $ERLC ] && echo $ERLC || error "can't find erlc" 12
-echo ""
-
-NAME=-name
-[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && NAME=-sname
-CLUSTERSETUP=clustersetup
-CLUSTERSETUP_ERL=$PA/$CLUSTERSETUP.erl
-
-REMOTENAME=-name
-[ "$REMOTE" = "${REMOTE%.*}" ] && REMOTENAME=-sname
-[ "$REMOTENAME" = "$NAME" ] || {
- echo "IMPORTANT!: node names are incompatible"
- echo "Remote node name is $REMOTE"
- echo "Local node name is $ERLANG_NODE"
- echo ""
- echo "Both node names must be short or fqdn names."
- echo "Using short and fqdn names is impossible."
- echo ""
- error "incompatible node names" 31
-}
-
-set -o errexit
-set -o nounset
-
-[ -d $SPOOL_DIR ] && rm -Rf $SPOOL_DIR/* || mkdir -p $SPOOL_DIR || error "$SPOOL_DIR cannot be created" 20
-[ -w $SPOOL_DIR ] || error "$SPOOL_DIR directory is not writable" 21
-mkdir -p $PA || error "$PA cannot be created" 22
-cd $PA
-cat <<EOF > $CLUSTERSETUP_ERL
--module($CLUSTERSETUP).
-
--export([start/0]).
-
-set_table_copy(Table, _Node, {badrpc, Reason}) ->
- io:format("Error: cannot get storage type for table ~p on node $REMOTE:~n ~p~n",[Table, Reason]);
-set_table_copy(Table, Node, Type) ->
- io:format("setting table ~p to mode ~p~n",[Table, Type]),
- case mnesia:add_table_copy(Table, Node, Type) of
- {aborted, _} ->
- mnesia:change_table_copy_type(Table, Node, Type);
- _ ->
- ok
- end.
-
-set_tables({badrpc, Reason}) ->
- io:format("ERROR: cannot get tables list on $REMOTE : ~p~n",[Reason]);
-set_tables([]) ->
- ok;
-set_tables([schema | Tables]) ->
- set_tables(Tables);
-set_tables([s2s | Tables]) ->
- set_tables(Tables);
-set_tables([session | Tables]) ->
- set_tables(Tables);
-set_tables([Table | Tables]) ->
- set_table_copy(Table, node(),
- rpc:call('$REMOTE', mnesia, table_info, [Table, storage_type])),
- set_tables(Tables).
-
-start() ->
- io:format("~n",[]),
- R = case net_adm:ping('$REMOTE') of
- pong ->
- set_table_copy(schema, node(), disc_copies),
- set_tables(rpc:call('$REMOTE', mnesia, system_info, [tables])),
- 0;
- pang ->
- io:format("node ~p is not reachable, please check epmd port, and FIREWALL_WINDOW ports~n", ['$REMOTE']),
- 1
- end,
- halt(R).
-EOF
-
-$ERLC -o $PA $CLUSTERSETUP_ERL
-$ERL $NAME $ERLANG_NODE -pa $PA $KERNEL_OPTS -mnesia extra_db_nodes "['$REMOTE']" dir "\"$SPOOL_DIR\"" -s mnesia -s $CLUSTERSETUP start
-cd -
-rm -Rf $PA
-
-echo "End."
-echo "Check that there is no error in the above messages."
+++ /dev/null
-#!/bin/sh
-
-# Remove the current ejabberd node in a cluster
-
-# copyright (c) 2010-2015 ProcessOne
-
-# Return Code:
-# 0 : groovy baby
-# 10 : ejabberdctl not found
-# 11 : erl not found
-# 12 : erlc not found
-# 22 : temporary dir can not be created
-
-error()
-{
- echo "Error: $1" >&2
- exit $2
-}
-
-[ -z $NO_WARNINGS ] && {
- echo "--------------------------------------------------------------------"
- echo ""
- echo "ejabberd cluster configuration"
- echo ""
- echo "This ejabberd node will be removed from the cluster."
- echo "IMPORTANT: this node will be stopped. At least one other clustered"
- echo "node must be running."
- echo ""
- echo "--------------------------------------------------------------------"
- echo "Press any key to continue, or Ctrl+C to stop now"
- read foo
- echo ""
-}
-
-PA=/tmp/clustersetup_$$
-CTL=$(which ejabberdctl)
-[ -x "$CTL" ] || {
- HERE=`which "$0"`
- BASE=`dirname $HERE`/..
- ROOTDIR=`cd $BASE; pwd`
- PATH=$ROOTDIR/bin:$PATH
- PA=$ROOTDIR/clustersetup_$$
- CTL=$(which ejabberdctl)
-}
-echo "Using commands:"
-[ -x "$CTL" ] && echo $CTL || error "can't find ejabberdctl" 10
-
-exec $CTL stop 2>/dev/null >/dev/null
-ERLC=${ERL}c
-
-[ -x $ERL ] && echo $ERL || error "can't find erl" 11
-[ -x $ERLC ] && echo $ERLC || error "can't find erlc" 12
-echo ""
-
-$CTL stopped
-
-CLUSTERSETUP=clustersetup
-CLUSTERSETUP_ERL=$PA/$CLUSTERSETUP.erl
-
-set -o errexit
-set -o nounset
-
-mkdir -p $PA || error "$PA cannot be created" 22
-cd $PA
-cat <<EOF > $CLUSTERSETUP_ERL
--module($CLUSTERSETUP).
-
--export([start/0]).
-
-del_table_copy(Table, Node) ->
- case mnesia:del_table_copy(Table, Node) of
- {aborted, Reason} -> io:format("Error: can not remove ~p table: ~p~n", [Table, Reason]);
- _ -> io:format("table ~p removed from cluster~n", [Table])
- end.
-
-del_tables([],_) ->
- ok;
-del_tables([schema | Tables], Node) ->
- del_tables(Tables, Node);
-del_tables([Table | Tables], Node) ->
- del_table_copy(Table, Node),
- del_tables(Tables, Node).
-
-start() ->
- io:format("~n",[]),
- Removed = node(),
- case mnesia:system_info(running_db_nodes)--[Removed] of
- [] -> io:format("Error: no other node running in the cluster~n");
- Nodes ->
- del_tables(mnesia:system_info(local_tables), Removed),
- mnesia:stop(),
- case rpc:call(hd(Nodes), mnesia, del_table_copy, [schema, Removed]) of
- {badrpc,Reason} -> io:format("Error: can not unregister node ~p from cluster: ~p~n", [Removed, Reason]);
- {aborted,Reason} -> io:format("Error: can not unregister node ~p from cluster: ~p~n", [Removed, Reason]);
- {atomic, ok} ->
- mnesia:delete_schema([Removed]),
- io:format("node ~p removed from cluster~n", [Removed])
- end
- end,
- halt(0).
-EOF
-
-$ERLC -o $PA $CLUSTERSETUP_ERL
-$ERL $NAME $ERLANG_NODE -pa $PA $KERNEL_OPTS -mnesia dir "\"$SPOOL_DIR\"" -s mnesia -s $CLUSTERSETUP start
-cd -
-rm -Rf $PA
-
-echo "End."
-echo "Check that there is no error in the above messages."