2018-10-11 11:32:09 +03:00
#!/bin/sh
set -u
export CTDB_TEST_MODE = "yes"
# Following 2 lines may be modified by installation script
2019-08-05 03:18:08 +03:00
CTDB_TESTS_ARE_INSTALLED = false
CTDB_TEST_DIR = $( dirname " $0 " )
export CTDB_TESTS_ARE_INSTALLED CTDB_TEST_DIR
2018-10-11 11:32:09 +03:00
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.
2019-08-05 03:22:36 +03:00
# shellcheck disable=SC2034
2018-10-11 11:32:09 +03:00
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" $(( 0 x5f00 + 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 "
2019-05-12 00:52:13 +03:00
rm -rf " $_d "
2018-10-11 11:32:09 +03:00
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)
2019-01-21 04:13:29 +03:00
-R Use a command for the recovery lock ( default: use a file)
2018-10-11 11:32:09 +03:00
-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 = ""
2019-01-21 04:13:29 +03:00
_recovery_lock_use_command = false
2018-10-11 11:32:09 +03:00
_socket_wrapper = ""
_use_ipv6 = false
set -e
2019-01-21 04:13:29 +03:00
while getopts "FN:n:P:RS:6h?" _opt ; do
2018-10-11 11:32:09 +03:00
case " $_opt " in
F) _disable_failover = true ; ;
N) _nodes_file = " $OPTARG " ; ;
n) _num_nodes = " $OPTARG " ; ;
P) _public_addresses_file = " $OPTARG " ; ;
2019-01-21 04:13:29 +03:00
R) _recovery_lock_use_command = true ; ;
2018-10-11 11:32:09 +03:00
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
2019-01-21 04:13:29 +03:00
_recovery_lock = " ${ directory } /rec.lock "
if $_recovery_lock_use_command ; then
2019-03-07 07:53:31 +03:00
_helper = " ${ CTDB_SCRIPTS_HELPER_BINDIR } /ctdb_mutex_fcntl_helper "
_recovery_lock = " ! ${ _helper } ${ _recovery_lock } "
2019-01-21 04:13:29 +03:00
fi
2018-10-11 11:32:09 +03:00
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]
2019-01-21 04:13:29 +03:00
recovery lock = ${ _recovery_lock }
2018-10-11 11:32:09 +03:00
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
2018-10-18 10:02:13 +03:00
onnode " $_nodes " " ${ VALGRIND :- } ctdbd & "
2018-10-11 11:32:09 +03:00
}
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 "
}
2018-10-12 05:49:58 +03:00
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 "
}
2019-07-01 00:04:19 +03:00
local_daemons_print_log ( )
2019-05-05 05:31:41 +03:00
{
if [ $# -ne 1 ] || [ " $1 " = "-h" ] ; then
2019-07-01 00:04:19 +03:00
local_daemons_generic_usage "print-log"
2019-05-05 05:31:41 +03:00
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 " 'echo ${CTDB_BASE}/log.ctdb' |
while IFS = '' read -r _l ; do
_dir = $( dirname " $_l " )
_node = $( basename " $_dir " )
# Add fake hostname after date and time, which are the
# first 2 words on each line
sed -e " s|^\\([^ ][^ ]* [^ ][^ ]*\\)|\\1 ${ _node } | " " $_l "
done |
sort
}
2018-10-11 11:32:09 +03:00
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)
2018-10-12 05:49:58 +03:00
onnode Run a command in the environment of specified daemon( s)
print-socket Print the Unix domain socket used by specified daemon( s)
2019-07-01 00:04:19 +03:00
print-log Print logs for specified daemon( s) to stdout
2018-10-11 11:32:09 +03:00
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 " $@ " ; ;
2018-10-12 05:49:58 +03:00
onnode) local_daemons_onnode " $@ " ; ;
print-socket) local_daemons_print_socket " $@ " ; ;
2019-07-01 00:04:19 +03:00
print-log) local_daemons_print_log " $@ " ; ;
2018-10-11 11:32:09 +03:00
*) usage ; ;
esac