summaryrefslogtreecommitdiffstats
path: root/ctdb/tests/local_daemons.sh
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xctdb/tests/local_daemons.sh506
1 files changed, 506 insertions, 0 deletions
diff --git a/ctdb/tests/local_daemons.sh b/ctdb/tests/local_daemons.sh
new file mode 100755
index 0000000..b474668
--- /dev/null
+++ b/ctdb/tests/local_daemons.sh
@@ -0,0 +1,506 @@
+#!/bin/sh
+
+set -u
+
+export CTDB_TEST_MODE="yes"
+
+# Following 2 lines may be modified by installation script
+CTDB_TESTS_ARE_INSTALLED=false
+CTDB_TEST_DIR=$(dirname "$0")
+export CTDB_TESTS_ARE_INSTALLED CTDB_TEST_DIR
+
+export TEST_SCRIPTS_DIR="${CTDB_TEST_DIR}/scripts"
+
+. "${TEST_SCRIPTS_DIR}/common.sh"
+
+if ! $CTDB_TESTS_ARE_INSTALLED ; then
+ hdir="$CTDB_SCRIPTS_HELPER_BINDIR"
+ export CTDB_EVENTD="${hdir}/ctdb-eventd"
+ export CTDB_EVENT_HELPER="${hdir}/ctdb-event"
+ export CTDB_LOCK_HELPER="${hdir}/ctdb_lock_helper"
+ export CTDB_RECOVERY_HELPER="${hdir}/ctdb_recovery_helper"
+ export CTDB_TAKEOVER_HELPER="${hdir}/ctdb_takeover_helper"
+ export CTDB_CLUSTER_MUTEX_HELPER="${hdir}/ctdb_mutex_fcntl_helper"
+fi
+
+########################################
+
+# If the given IP is hosted then print 2 items: maskbits and iface
+have_ip ()
+{
+ _addr="$1"
+
+ case "$_addr" in
+ *:*) _bits=128 ;;
+ *) _bits=32 ;;
+ esac
+
+ _t=$(ip addr show to "${_addr}/${_bits}")
+ [ -n "$_t" ]
+}
+
+setup_nodes ()
+{
+ _num_nodes="$1"
+ _use_ipv6="$2"
+
+ _have_all_ips=true
+ for _i in $(seq 0 $((_num_nodes - 1)) ) ; do
+ if $_use_ipv6 ; then
+ _j=$(printf "%04x" $((0x5f00 + 1 + _i)) )
+ _node_ip="fd00::5357:${_j}"
+ if have_ip "$_node_ip" ; then
+ echo "$_node_ip"
+ else
+ cat >&2 <<EOF
+ERROR: ${_node_ip} not on an interface, please add it
+EOF
+ _have_all_ips=false
+ fi
+ else
+ _c=$(( _i / 100 ))
+ _d=$(( 1 + (_i % 100) ))
+ echo "127.0.${_c}.${_d}"
+ fi
+ done
+
+ # Fail if we don't have all of the IPv6 addresses assigned
+ $_have_all_ips
+}
+
+setup_public_addresses ()
+{
+ _num_nodes="$1"
+ _node_no_ips="$2"
+ _use_ipv6="$3"
+
+ for _i in $(seq 0 $((_num_nodes - 1)) ) ; do
+ if [ "$_i" -eq "$_node_no_ips" ] ; then
+ continue
+ fi
+
+ # 2 public addresses on most nodes, just to make
+ # things interesting
+ if $_use_ipv6 ; then
+ printf 'fc00:10::1:%x/64 lo\n' $((1 + _i))
+ printf 'fc00:10::2:%x/64 lo\n' $((1 + _i))
+ else
+ _c1=$(( 100 + (_i / 100) ))
+ _c2=$(( 200 + (_i / 100) ))
+ _d=$(( 1 + (_i % 100) ))
+ printf '192.168.%d.%d/24 lo\n' "$_c1" "$_d"
+ printf '192.168.%d.%d/24 lo\n' "$_c2" "$_d"
+ fi
+ done
+}
+
+setup_socket_wrapper ()
+{
+ _socket_wrapper_so="$1"
+
+ _so="${directory}/libsocket-wrapper.so"
+ if [ ! -f "$_socket_wrapper_so" ] ; then
+ die "$0 setup: Unable to find ${_socket_wrapper_so}"
+ fi
+
+ # Find absolute path if only relative is given
+ case "$_socket_wrapper_so" in
+ /*) : ;;
+ *) _socket_wrapper_so="${PWD}/${_socket_wrapper_so}" ;;
+ esac
+
+ rm -f "$_so"
+ ln -s "$_socket_wrapper_so" "$_so"
+
+ _d="${directory}/sw"
+ rm -rf "$_d"
+ mkdir -p "$_d"
+}
+
+local_daemons_setup_usage ()
+{
+ cat >&2 <<EOF
+$0 <directory> setup [ <options>... ]
+
+Options:
+ -C Comment out given config item (default: item uncommented)
+ -F Disable failover (default: failover enabled)
+ -N <file> Nodes file (default: automatically generated)
+ -n <num> Number of nodes (default: 3)
+ -P <file> Public addresses file (default: automatically generated)
+ -R Use a command for the cluster lock (default: use a file)
+ -r <time> Like -R and set recheck interval to <time> (default: use a file)
+ -S <library> Socket wrapper shared library to preload (default: none)
+ -6 Generate IPv6 IPs for nodes, public addresses (default: IPv4)
+EOF
+
+ exit 1
+}
+
+local_daemons_setup ()
+{
+ _commented_config=""
+ _disable_failover=false
+ _nodes_file=""
+ _num_nodes=3
+ _public_addresses_file=""
+ _cluster_lock_use_command=false
+ _cluster_lock_recheck_interval=""
+ _socket_wrapper=""
+ _use_ipv6=false
+
+ set -e
+
+ while getopts "C:FN:n:P:Rr:S:6h?" _opt ; do
+ case "$_opt" in
+ C) _t="${_commented_config}${_commented_config:+|}"
+ _commented_config="${_t}${OPTARG}"
+ ;;
+ F) _disable_failover=true ;;
+ N) _nodes_file="$OPTARG" ;;
+ n) _num_nodes="$OPTARG" ;;
+ P) _public_addresses_file="$OPTARG" ;;
+ R) _cluster_lock_use_command=true ;;
+ r) _cluster_lock_use_command=true
+ _cluster_lock_recheck_interval="$OPTARG"
+ ;;
+ S) _socket_wrapper="$OPTARG" ;;
+ 6) _use_ipv6=true ;;
+ \?|h) local_daemons_setup_usage ;;
+ esac
+ done
+ shift $((OPTIND - 1))
+
+ mkdir -p "$directory"
+
+ _nodes_all="${directory}/nodes"
+ if [ -n "$_nodes_file" ] ; then
+ cp "$_nodes_file" "$_nodes_all"
+ else
+ setup_nodes "$_num_nodes" $_use_ipv6 >"$_nodes_all"
+ fi
+
+ # If there are (strictly) greater than 2 nodes then we'll
+ # "randomly" choose a node to have no public addresses
+ _node_no_ips=-1
+ if [ "$_num_nodes" -gt 2 ] ; then
+ _node_no_ips=$(($$ % _num_nodes))
+ fi
+
+ _public_addresses_all="${directory}/public_addresses"
+ if [ -n "$_public_addresses_file" ] ; then
+ cp "$_public_addresses_file" "$_public_addresses_all"
+ else
+ setup_public_addresses "$_num_nodes" \
+ $_node_no_ips \
+ "$_use_ipv6" >"$_public_addresses_all"
+ fi
+
+ _cluster_lock_dir="${directory}/shared/.ctdb"
+ mkdir -p "$_cluster_lock_dir"
+ _cluster_lock="${_cluster_lock_dir}/cluster.lock"
+ if $_cluster_lock_use_command ; then
+ _helper="${CTDB_SCRIPTS_HELPER_BINDIR}/ctdb_mutex_fcntl_helper"
+ _t="! ${_helper} ${_cluster_lock}"
+ if [ -n "$_cluster_lock_recheck_interval" ] ; then
+ _t="${_t} ${_cluster_lock_recheck_interval}"
+ fi
+ _cluster_lock="$_t"
+ fi
+
+ if [ -n "$_socket_wrapper" ] ; then
+ setup_socket_wrapper "$_socket_wrapper"
+ fi
+
+ for _n in $(seq 0 $((_num_nodes - 1))) ; do
+ # CTDB_TEST_SUITE_DIR needs to be correctly set so
+ # setup_ctdb_base() finds the etc-ctdb/ subdirectory
+ # and the test event script is correctly installed
+ # shellcheck disable=SC2034
+ CTDB_TEST_SUITE_DIR="$CTDB_TEST_DIR" \
+ setup_ctdb_base "$directory" "node.${_n}" \
+ functions notify.sh debug-hung-script.sh
+
+ cp "$_nodes_all" "${CTDB_BASE}/nodes"
+
+ _public_addresses="${CTDB_BASE}/public_addresses"
+
+ if [ -z "$_public_addresses_file" ] && \
+ [ "$_node_no_ips" -eq "$_n" ] ; then
+ echo "Node ${_n} will have no public IPs."
+ : >"$_public_addresses"
+ else
+ cp "$_public_addresses_all" "$_public_addresses"
+ fi
+
+ _node_ip=$(sed -n -e "$((_n + 1))p" "$_nodes_all")
+
+ _db_dir="${CTDB_BASE}/db"
+ for _d in "volatile" "persistent" "state" ; do
+ mkdir -p "${_db_dir}/${_d}"
+ done
+
+ cat >"${CTDB_BASE}/ctdb.conf" <<EOF
+[logging]
+ location = file:${CTDB_BASE}/log.ctdb
+ log level = INFO
+
+[cluster]
+ cluster lock = ${_cluster_lock}
+ node address = ${_node_ip}
+
+[database]
+ volatile database directory = ${_db_dir}/volatile
+ persistent database directory = ${_db_dir}/persistent
+ state database directory = ${_db_dir}/state
+
+[failover]
+ disabled = ${_disable_failover}
+
+[event]
+ debug script = debug-hung-script.sh
+EOF
+
+ (
+ IFS='|'
+ for _c in $_commented_config ; do
+ # Quote all backslashes due to double-quotes
+ sed -i -e "s|^\\t\\(${_c}\\) = |\\t# \\1 = |" \
+ "${CTDB_BASE}/ctdb.conf"
+ done
+ )
+ done
+}
+
+local_daemons_ssh_usage ()
+{
+ cat >&2 <<EOF
+usage: $0 <directory> ssh [ -n ] <ip> <command>
+EOF
+
+ exit 1
+}
+
+local_daemons_ssh ()
+{
+ if [ $# -lt 2 ] ; then
+ local_daemons_ssh_usage
+ fi
+
+ # Only try to respect ssh -n option, others can't be used so discard them
+ _close_stdin=false
+ while getopts "nh?" _opt ; do
+ case "$_opt" in
+ n) _close_stdin=true ;;
+ \?|h) local_daemons_ssh_usage ;;
+ *) : ;;
+ esac
+ done
+ shift $((OPTIND - 1))
+
+ if [ $# -lt 2 ] ; then
+ local_daemons_ssh_usage
+ fi
+
+ _nodes="${directory}/nodes"
+
+ # IP address of node. onnode can pass hostnames but not in these tests
+ _ip="$1" ; shift
+ # "$*" is command
+
+
+ # Determine the correct CTDB base directory
+ _num=$(awk -v ip="$_ip" '$1 == ip { print NR }' "$_nodes")
+ _node=$((_num - 1))
+ export CTDB_BASE="${directory}/node.${_node}"
+
+ if [ ! -d "$CTDB_BASE" ] ; then
+ die "$0 ssh: Unable to find base for node ${_ip}"
+ fi
+
+ if $_close_stdin ; then
+ exec sh -c "$*" </dev/null
+ else
+ exec sh -c "$*"
+ fi
+}
+
+onnode_common ()
+{
+ # onnode will execute this, which fakes ssh against local daemons
+ export ONNODE_SSH="${0} ${directory} ssh"
+
+ # onnode just needs the nodes file, so use the common one
+ export CTDB_BASE="$directory"
+}
+
+local_daemons_generic_usage ()
+{
+ cat >&2 <<EOF
+usage: $0 <directory> ${1} <nodes>
+
+<nodes> can be "all", a node number or any specification supported by onnode
+EOF
+
+ exit 1
+}
+
+local_daemons_start_socket_wrapper ()
+{
+ _so="${directory}/libsocket-wrapper.so"
+ _d="${directory}/sw"
+
+ if [ -d "$_d" ] && [ -f "$_so" ] ; then
+ export SOCKET_WRAPPER_DIR="$_d"
+ export LD_PRELOAD="$_so"
+ export SOCKET_WRAPPER_DIR_ALLOW_ORIG="1"
+ fi
+}
+
+local_daemons_start ()
+{
+ if [ $# -ne 1 ] || [ "$1" = "-h" ] ; then
+ local_daemons_generic_usage "start"
+ fi
+
+ local_daemons_start_socket_wrapper
+
+ _nodes="$1"
+
+ onnode_common
+
+ onnode -i "$_nodes" "${VALGRIND:-} ctdbd"
+}
+
+local_daemons_stop ()
+{
+ if [ $# -ne 1 ] || [ "$1" = "-h" ] ; then
+ local_daemons_generic_usage "stop"
+ fi
+
+ _nodes="$1"
+
+ onnode_common
+
+ onnode -p "$_nodes" \
+ "if [ -e \"\${CTDB_BASE}/run/ctdbd.pid\" ] ; then \
+ ${CTDB:-${VALGRIND:-} ctdb} shutdown ; \
+ fi"
+}
+
+local_daemons_onnode_usage ()
+{
+ cat >&2 <<EOF
+usage: $0 <directory> onnode <nodes> <command>...
+
+<nodes> can be "all", a node number or any specification supported by onnode
+EOF
+
+ exit 1
+}
+
+local_daemons_onnode ()
+{
+ if [ $# -lt 2 ] || [ "$1" = "-h" ] ; then
+ local_daemons_onnode_usage
+ fi
+
+ _nodes="$1"
+ shift
+
+ onnode_common
+
+ onnode "$_nodes" "$@"
+}
+
+local_daemons_print_socket ()
+{
+ if [ $# -ne 1 ] || [ "$1" = "-h" ] ; then
+ local_daemons_generic_usage "print-socket"
+ fi
+
+ _nodes="$1"
+ shift
+
+ onnode_common
+
+ _path="${CTDB_SCRIPTS_HELPER_BINDIR}/ctdb-path"
+ onnode -q "$_nodes" "${VALGRIND:-} ${_path} socket ctdbd"
+}
+
+local_daemons_print_log ()
+{
+ if [ $# -ne 1 ] || [ "$1" = "-h" ] ; then
+ local_daemons_generic_usage "print-log"
+ fi
+
+ _nodes="$1"
+ shift
+
+ onnode_common
+
+ # shellcheck disable=SC2016
+ # $CTDB_BASE must only be expanded under onnode, not in top-level shell
+ onnode -q "$_nodes" 'cat ${CTDB_BASE}/log.ctdb' |
+ sort
+
+}
+
+local_daemons_tail_log ()
+{
+ if [ $# -ne 1 ] || [ "$1" = "-h" ] ; then
+ local_daemons_generic_usage "tail-log"
+ fi
+
+ _nodes="$1"
+ shift
+
+ onnode_common
+
+ # shellcheck disable=SC2016,SC2046
+ # $CTDB_BASE must only be expanded under onnode, not in top-level shell
+ # Intentional word splitting to separate log filenames
+ tail -f $(onnode -q "$_nodes" 'echo ${CTDB_BASE}/log.ctdb')
+}
+
+usage ()
+{
+ cat <<EOF
+usage: $0 <directory> <command> [ <options>... ]
+
+Commands:
+ setup Set up daemon configuration according to given options
+ start Start specified daemon(s)
+ stop Stop specified daemon(s)
+ onnode Run a command in the environment of specified daemon(s)
+ print-socket Print the Unix domain socket used by specified daemon(s)
+ print-log Print logs for specified daemon(s) to stdout
+ tail-log Follow logs for specified daemon(s) to stdout
+
+All commands use <directory> for daemon configuration
+
+Run command with -h option to see per-command usage
+EOF
+
+ exit 1
+}
+
+if [ $# -lt 2 ] ; then
+ usage
+fi
+
+directory="$1"
+command="$2"
+shift 2
+
+case "$command" in
+setup) local_daemons_setup "$@" ;;
+ssh) local_daemons_ssh "$@" ;; # Internal, not shown by usage()
+start) local_daemons_start "$@" ;;
+stop) local_daemons_stop "$@" ;;
+onnode) local_daemons_onnode "$@" ;;
+print-socket) local_daemons_print_socket "$@" ;;
+print-log) local_daemons_print_log "$@" ;;
+tail-log) local_daemons_tail_log "$@" ;;
+*) usage ;;
+esac