ctdb-tests: Add local_daemons.sh
authorMartin Schwenke <martin@meltin.net>
Thu, 11 Oct 2018 08:32:09 +0000 (19:32 +1100)
committerAmitay Isaacs <amitay@samba.org>
Tue, 6 Nov 2018 06:16:17 +0000 (07:16 +0100)
This provides a separate script for handling local daemons.  It can be
used for testing outside of the CTDB simple test suite.  It is
installed as ctdb_local_daemons.

The logic is copied from ctdb/tests/simple/scripts/local_daemons.bash.

Signed-off-by: Martin Schwenke <martin@meltin.net>
Reviewed-by: Amitay Isaacs <amitay@gmail.com>
ctdb/packaging/RPM/ctdb.spec.in
ctdb/tests/local_daemons.sh [new file with mode: 0755]
ctdb/wscript

index 4c9cae4..537f158 100644 (file)
@@ -270,6 +270,7 @@ test suite for ctdb
 %{_libexecdir}/%{name}/tests/*
 %{_bindir}/ctdb_run_tests
 %{_bindir}/ctdb_run_cluster_tests
+%{_bindir}/ctdb_local_daemons
 %doc tests/README
 
 %if %with_pcp_pmda
diff --git a/ctdb/tests/local_daemons.sh b/ctdb/tests/local_daemons.sh
new file mode 100755 (executable)
index 0000000..a48e166
--- /dev/null
@@ -0,0 +1,385 @@
+#!/bin/sh
+
+set -u
+
+export CTDB_TEST_MODE="yes"
+
+# Following 2 lines may be modified by installation script
+export CTDB_TESTS_ARE_INSTALLED=false
+export CTDB_TEST_DIR=$(dirname "$0")
+
+export TEST_SCRIPTS_DIR="${CTDB_TEST_DIR}/scripts"
+
+. "${TEST_SCRIPTS_DIR}/common.sh"
+
+# common.sh will set TEST_SUBDIR to a stupid value when installed
+# because common.sh is usually sourced by a test.  TEST_SUBDIR needs
+# to be correctly set so setup_ctdb_base() finds the etc-ctdb/
+# subdirectory and the test event script is correctly installed, so
+# fix it.
+TEST_SUBDIR="$CTDB_TEST_DIR"
+
+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 absoluate 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"
+       mkdir -p "$_d"
+}
+
+local_daemons_setup_usage ()
+{
+       cat >&2 <<EOF
+$0 <directory> setup [ <options>... ]
+
+Options:
+  -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)
+  -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 ()
+{
+       _disable_failover=false
+       _nodes_file=""
+       _num_nodes=3
+       _public_addresses_file=""
+       _socket_wrapper=""
+       _use_ipv6=false
+
+       set -e
+
+       while getopts "FN:n:P:S:6h?" _opt ; do
+               case "$_opt" in
+               F) _disable_failover=true ;;
+               N) _nodes_file="$OPTARG" ;;
+               n) _num_nodes="$OPTARG" ;;
+               P) _public_addresses_file="$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
+
+       if [ -n "$_socket_wrapper" ] ; then
+               setup_socket_wrapper "$_socket_wrapper"
+       fi
+
+       for _n in $(seq 0 $((_num_nodes - 1))) ; do
+               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]
+       recovery lock = ${directory}/rec.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
+       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 adress 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 "$*" <&-
+       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"
+       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 "$_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" "${VALGRIND:-} ${CTDB:-ctdb} shutdown"
+}
+
+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)
+
+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 "$@" ;;
+*) usage ;;
+esac
index b59f6d5..e8e8e4d 100644 (file)
@@ -1114,6 +1114,14 @@ def build(bld):
     bld.symlink_as(os.path.join(bld.env.BINDIR, 'ctdb_run_cluster_tests'),
                    'ctdb_run_tests')
 
+    bld.SAMBA_GENERATOR('ctdb-local-daemons',
+                        source='tests/local_daemons.sh',
+                        target='ctdb_local_daemons.sh',
+                        rule='sed -e "%s" -e "%s" ${SRC} > ${TGT}' % (
+                             sed_expr1, sed_expr2))
+    bld.INSTALL_FILES('${BINDIR}', 'ctdb_local_daemons.sh',
+                      destname='ctdb_local_daemons', chmod=0o755)
+
 
 def testonly(ctx):
     cmd = 'tests/run_tests.sh -V tests/var'