summaryrefslogtreecommitdiffstats
path: root/ctdb/tests/UNIT/eventscripts/scripts
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /ctdb/tests/UNIT/eventscripts/scripts
parentInitial commit. (diff)
downloadsamba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz
samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ctdb/tests/UNIT/eventscripts/scripts')
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/00.ctdb.sh29
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/01.reclock.sh16
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/05.system.sh46
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/06.nfs.sh4
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/10.interface.sh72
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/11.natgw.sh119
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/13.per_ip_routing.sh42
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/20.multipathd.sh24
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/31.clamd.sh8
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/40.vsftpd.sh12
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/41.httpd.sh14
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/48.netbios.sh21
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/49.winbind.sh26
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/50.samba.sh54
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/60.nfs.sh436
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/91.lvs.sh76
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/debug_locks.sh255
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/local.sh550
-rw-r--r--ctdb/tests/UNIT/eventscripts/scripts/statd-callout.sh65
19 files changed, 1869 insertions, 0 deletions
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/00.ctdb.sh b/ctdb/tests/UNIT/eventscripts/scripts/00.ctdb.sh
new file mode 100644
index 0000000..7925679
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/00.ctdb.sh
@@ -0,0 +1,29 @@
+setup ()
+{
+ setup_dbdir
+ setup_date
+
+ export FAKE_TDBTOOL_SUPPORTS_CHECK="yes"
+ export FAKE_TDB_IS_OK="yes"
+
+ export FAKE_CTDB_TUNABLES_OK="
+ MonitorInterval
+ DatabaseHashSize
+ "
+ export FAKE_CTDB_TUNABLES_OBSOLETE="
+ EventScriptUnhealthyOnTimeout
+ "
+}
+
+setup_tunable_config ()
+{
+ cat >"${CTDB_BASE}/ctdb.tunables"
+}
+
+result_filter ()
+{
+ _date="[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]"
+ _time="[0-9][0-9][0-9][0-9][0-9][0-9]"
+ _date_time="${_date}\.${_time}"
+ sed -e "s|\.${_date_time}\.|.DATE.TIME.|"
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/01.reclock.sh b/ctdb/tests/UNIT/eventscripts/scripts/01.reclock.sh
new file mode 100644
index 0000000..cc1f086
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/01.reclock.sh
@@ -0,0 +1,16 @@
+setup ()
+{
+ if [ $# -eq 1 ] ; then
+ reclock="$1"
+ else
+ reclock="${CTDB_TEST_TMP_DIR}/reclock_subdir/rec.lock"
+ fi
+ CTDB_RECOVERY_LOCK="$reclock"
+
+ if [ -n "$CTDB_RECOVERY_LOCK" ] ; then
+ cat >>"${CTDB_BASE}/ctdb.conf" <<EOF
+[cluster]
+ recovery lock = $CTDB_RECOVERY_LOCK
+EOF
+ fi
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/05.system.sh b/ctdb/tests/UNIT/eventscripts/scripts/05.system.sh
new file mode 100644
index 0000000..eaf3e98
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/05.system.sh
@@ -0,0 +1,46 @@
+set_mem_usage ()
+{
+ _mem_usage="${1:-10}" # Default is 10%
+ _swap_usage="${2:-0}" # Default is 0%
+
+ _swap_total=5857276
+ _swap_free=$(( (100 - $_swap_usage) * $_swap_total / 100 ))
+
+ _mem_total=3940712
+ _mem_free=225268
+ _mem_buffers=146120
+ _mem_cached=$(( $_mem_total * (100 - $_mem_usage) / 100 -
+ $_mem_free - $_mem_buffers ))
+
+ export FAKE_PROC_MEMINFO="\
+MemTotal: ${_mem_total} kB
+MemFree: ${_mem_free} kB
+Buffers: ${_mem_buffers} kB
+Cached: ${_mem_cached} kB
+SwapCached: 56016 kB
+Active: 2422104 kB
+Inactive: 1019928 kB
+Active(anon): 1917580 kB
+Inactive(anon): 523080 kB
+Active(file): 504524 kB
+Inactive(file): 496848 kB
+Unevictable: 4844 kB
+Mlocked: 4844 kB
+SwapTotal: ${_swap_total} kB
+SwapFree: ${_swap_free} kB
+..."
+}
+
+set_fs_usage ()
+{
+ export FAKE_FS_USE="${1:-10}" # Default is 10% usage
+}
+
+setup ()
+{
+ setup_dbdir
+
+ # Tests use default unless explicitly set
+ set_mem_usage
+ set_fs_usage
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/06.nfs.sh b/ctdb/tests/UNIT/eventscripts/scripts/06.nfs.sh
new file mode 100644
index 0000000..48ee489
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/06.nfs.sh
@@ -0,0 +1,4 @@
+setup ()
+{
+ :
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/10.interface.sh b/ctdb/tests/UNIT/eventscripts/scripts/10.interface.sh
new file mode 100644
index 0000000..b2bc87e
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/10.interface.sh
@@ -0,0 +1,72 @@
+setup ()
+{
+ setup_public_addresses
+}
+
+_tcp_connections ()
+{
+ _count="$1"
+ _sip="$2"
+ _sport="$3"
+ _cip_base="$4"
+ _cport_base="$5"
+
+ _cip_prefix="${_cip_base%.*}"
+ _cip_suffix="${_cip_base##*.}"
+
+ for _i in $(seq 1 $_count) ; do
+ _cip_last=$((_cip_suffix + _i))
+ _cip="${_cip_prefix}.${_cip_last}"
+ _cport=$((_cport_base + _i))
+ echo "${_sip}:${_sport} ${_cip}:${_cport}"
+ done
+}
+
+setup_tcp_connections ()
+{
+ _t="${FAKE_NETWORK_STATE}/tcp-established"
+ export FAKE_NETSTAT_TCP_ESTABLISHED_FILE="$_t"
+ _tcp_connections "$@" >"$FAKE_NETSTAT_TCP_ESTABLISHED_FILE"
+}
+
+setup_tcp_connections_unkillable ()
+{
+ # These connections are listed by the "ss" stub but are not
+ # killed by the "ctdb killtcp" stub. So killing these
+ # connections will never succeed... and will look like a time
+ # out.
+ _t=$(_tcp_connections "$@" | sed -e 's/ /|/g')
+ export FAKE_NETSTAT_TCP_ESTABLISHED="$_t"
+}
+
+# Setup some fake /proc/net/bonding files with just enough info for
+# the eventscripts.
+
+# arg1 is interface name, arg2 is currently active slave (use "None"
+# if none), arg3 is MII status ("up" or "down").
+setup_bond ()
+{
+ _iface="$1"
+ _slave="${2:-${_iface}_sl_0}"
+ _mii_s="${3:-up}"
+ _mii_subs="${4:-${_mii_s:-up}}"
+
+ cat <<EOF
+Setting $_iface to be a bond with active slave $_slave and MII status $_mii_s
+EOF
+
+ _t="${FAKE_NETWORK_STATE}/proc-net-bonding"
+ export FAKE_PROC_NET_BONDING="$_t"
+ mkdir -p "$FAKE_PROC_NET_BONDING"
+
+ cat >"${FAKE_PROC_NET_BONDING}/$_iface" <<EOF
+Bonding Mode: IEEE 802.3ad Dynamic link aggregation
+Currently Active Slave: $_slave
+# Status of the bond
+MII Status: $_mii_s
+# Status of 1st pretend adapter
+MII Status: $_mii_subs
+# Status of 2nd pretend adapter
+MII Status: $_mii_subs
+EOF
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/11.natgw.sh b/ctdb/tests/UNIT/eventscripts/scripts/11.natgw.sh
new file mode 100644
index 0000000..511dc27
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/11.natgw.sh
@@ -0,0 +1,119 @@
+setup ()
+{
+ debug "Setting up NAT gateway"
+
+ natgw_nodes="${CTDB_BASE}/natgw_nodes"
+
+ ctdb_set_pnn
+}
+
+# A separate function for this makes sense because it can be done
+# multiple times per test
+setup_ctdb_natgw ()
+{
+ # Read from stdin
+ while read _ip _opts ; do
+ case "$_opts" in
+ leader)
+ export FAKE_CTDB_NATGW_LEADER="$_ip"
+ echo "$_ip"
+ ;;
+ follower-only)
+ printf "%s\tfollower-only\n" "$_ip"
+ ;;
+ *)
+ echo "$_ip"
+ ;;
+ esac
+ done >"$natgw_nodes"
+
+ # Assume all of the nodes are on a /24 network and have IPv4
+ # addresses:
+ read _ip <"$natgw_nodes"
+
+ setup_script_options <<EOF
+CTDB_NATGW_NODES="$natgw_nodes"
+CTDB_NATGW_PRIVATE_NETWORK="${_ip%.*}.0/24"
+# These are fixed. Probably don't use the same network for the
+# private node IPs. To unset the default gateway just set it to
+# "". :-)
+CTDB_NATGW_PUBLIC_IP="10.1.1.121/24"
+CTDB_NATGW_PUBLIC_IFACE="eth1"
+CTDB_NATGW_DEFAULT_GATEWAY="10.1.1.254"
+EOF
+}
+
+ok_natgw_leader_ip_addr_show ()
+{
+ _mac=$(echo "$CTDB_NATGW_PUBLIC_IFACE" |
+ cksum |
+ sed -r -e 's@(..)(..)(..).*@fe:fe:fe:\1:\2:\3@')
+
+ # This is based on CTDB_NATGW_PUBLIC_IP
+ _brd="10.1.1.255"
+
+ ok <<EOF
+1: ${CTDB_NATGW_PUBLIC_IFACE}: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
+ link/ether ${_mac} brd ff:ff:ff:ff:ff:ff
+ inet ${CTDB_NATGW_PUBLIC_IP} brd ${_brd} scope global ${CTDB_NATGW_PUBLIC_IFACE}
+ valid_lft forever preferred_lft forever
+EOF
+}
+
+ok_natgw_follower_ip_addr_show ()
+{
+ _mac=$(echo "$CTDB_NATGW_PUBLIC_IFACE" |
+ cksum |
+ sed -r -e 's@(..)(..)(..).*@fe:fe:fe:\1:\2:\3@')
+
+ ok <<EOF
+1: ${CTDB_NATGW_PUBLIC_IFACE}: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
+ link/ether ${_mac} brd ff:ff:ff:ff:ff:ff
+EOF
+}
+
+ok_natgw_leader_static_routes ()
+{
+ _nl="
+"
+ _t=""
+ for _i in $CTDB_NATGW_STATIC_ROUTES ; do
+ # This is intentionally different to the code in 11.natgw ;-)
+ case "$_i" in
+ *@*)
+ _net=$(echo "$_i" | sed -e 's|@.*||')
+ _gw=$(echo "$_i" | sed -e 's|.*@||')
+ ;;
+ *)
+ _net="$_i"
+ _gw="$CTDB_NATGW_DEFAULT_GATEWAY"
+ esac
+
+ [ -n "$_gw" ] || continue
+ _t="${_t}${_t:+${_nl}}"
+ _t="${_t}${_net} via ${_gw} dev ethXXX metric 10 "
+ done
+ _t=$(echo "$_t" | sort)
+ ok "$_t"
+}
+
+ok_natgw_follower_static_routes ()
+{
+ _nl="
+"
+ _t=""
+ for _i in $CTDB_NATGW_STATIC_ROUTES ; do
+ # This is intentionally different to the code in 11.natgw ;-)
+ _net=$(echo "$_i" | sed -e 's|@.*||')
+
+ # The interface for the private network isn't
+ # specified as part of the NATGW configuration and
+ # isn't part of the command to add the route. It is
+ # implicitly added by "ip route" but our stub doesn't
+ # do this and adds "ethXXX".
+ _t="${_t}${_t:+${_nl}}"
+ _t="${_t}${_net} via ${FAKE_CTDB_NATGW_LEADER} dev ethXXX metric 10 "
+ done
+ _t=$(echo "$_t" | sort)
+ ok "$_t"
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/13.per_ip_routing.sh b/ctdb/tests/UNIT/eventscripts/scripts/13.per_ip_routing.sh
new file mode 100644
index 0000000..e57e91a
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/13.per_ip_routing.sh
@@ -0,0 +1,42 @@
+setup ()
+{
+ setup_public_addresses
+
+ service_name="per_ip_routing"
+
+ setup_script_options <<EOF
+CTDB_PER_IP_ROUTING_CONF="${CTDB_BASE}/policy_routing"
+CTDB_PER_IP_ROUTING_RULE_PREF=100
+CTDB_PER_IP_ROUTING_TABLE_ID_LOW=1000
+CTDB_PER_IP_ROUTING_TABLE_ID_HIGH=2000
+EOF
+
+ # Tests need to create and populate this file
+ rm -f "$CTDB_PER_IP_ROUTING_CONF"
+}
+
+# Create policy routing configuration in $CTDB_PER_IP_ROUTING_CONF.
+# $1 is the number of assigned IPs to use (<num>, all), defaulting to
+# 1. If $2 is "default" then a default route is also added.
+create_policy_routing_config ()
+{
+ _num_ips="${1:-1}"
+ _should_add_default="$2"
+
+ ctdb_get_my_public_addresses |
+ if [ "$_num_ips" = "all" ] ; then
+ cat
+ else
+ { head -n "$_num_ips" ; cat >/dev/null ; }
+ fi |
+ while read _dev _ip _bits ; do
+ _net=$(ipv4_host_addr_to_net "$_ip" "$_bits")
+ _gw="${_net%.*}.254" # a dumb, calculated default
+
+ echo "$_ip $_net"
+
+ if [ "$_should_add_default" = "default" ] ; then
+ echo "$_ip 0.0.0.0/0 $_gw"
+ fi
+ done >"$CTDB_PER_IP_ROUTING_CONF"
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/20.multipathd.sh b/ctdb/tests/UNIT/eventscripts/scripts/20.multipathd.sh
new file mode 100644
index 0000000..2a69ae8
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/20.multipathd.sh
@@ -0,0 +1,24 @@
+setup ()
+{
+ _failures=""
+ _devices=""
+ for i ; do
+ case "$i" in
+ \!*)
+ _t="${i#!}"
+ echo "Marking ${_t} as having no active paths"
+ _failures="${_failures}${_failures:+ }${_t}"
+ ;;
+ *)
+ _t="$i"
+ esac
+ _devices="${_devices}${_devices:+ }${_t}"
+ done
+
+ setup_script_options <<EOF
+CTDB_MONITOR_MPDEVICES="$_devices"
+EOF
+
+ export FAKE_MULTIPATH_FAILURES="$_failures"
+ export FAKE_SLEEP_FORCE=0.1
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/31.clamd.sh b/ctdb/tests/UNIT/eventscripts/scripts/31.clamd.sh
new file mode 100644
index 0000000..8fe3bbc
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/31.clamd.sh
@@ -0,0 +1,8 @@
+setup ()
+{
+ setup_script_options <<EOF
+CTDB_CLAMD_SOCKET="/var/run/clamd.sock"
+EOF
+
+ setup_unix_listen
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/40.vsftpd.sh b/ctdb/tests/UNIT/eventscripts/scripts/40.vsftpd.sh
new file mode 100644
index 0000000..de2aa26
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/40.vsftpd.sh
@@ -0,0 +1,12 @@
+setup ()
+{
+ debug "Setting up VSFTPD environment: service $1, not managed by CTDB"
+
+ _service_name="vsftpd"
+
+ if [ "$1" != "down" ] ; then
+ service "$_service_name" start
+ else
+ service "$_service_name" force-stopped
+ fi
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/41.httpd.sh b/ctdb/tests/UNIT/eventscripts/scripts/41.httpd.sh
new file mode 100644
index 0000000..9b4a9ad
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/41.httpd.sh
@@ -0,0 +1,14 @@
+setup ()
+{
+ debug "Setting up HTTPD environment: service $1, not managed by CTDB"
+
+ if [ "$1" != "down" ] ; then
+ for _service_name in "apache2" "httpd" ; do
+ service "$_service_name" start
+ done
+ else
+ for _service_name in "apache2" "httpd" ; do
+ service "$_service_name" force-stopped
+ done
+ fi
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/48.netbios.sh b/ctdb/tests/UNIT/eventscripts/scripts/48.netbios.sh
new file mode 100644
index 0000000..f578399
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/48.netbios.sh
@@ -0,0 +1,21 @@
+setup ()
+{
+ service_name="netbios"
+
+ if [ "$1" != "down" ] ; then
+
+ debug "Marking Netbios name services as up, listening and managed by CTDB"
+
+ # All possible service names for all known distros.
+ for i in "nmb" "nmbd" ; do
+ service "$i" force-started
+ done
+ else
+ debug "Marking Netbios name services as down, not listening and not managed by CTDB"
+
+ # All possible service names for all known distros.
+ for i in "nmb" "nmbd" ; do
+ service "$i" force-stopped
+ done
+ fi
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/49.winbind.sh b/ctdb/tests/UNIT/eventscripts/scripts/49.winbind.sh
new file mode 100644
index 0000000..e9bbe31
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/49.winbind.sh
@@ -0,0 +1,26 @@
+setup ()
+{
+ service_name="winbind"
+
+ if [ "$1" != "down" ] ; then
+
+ debug "Marking Winbind service as up and managed by CTDB"
+
+ service "winbind" force-started
+
+ export FAKE_WBINFO_FAIL="no"
+
+ else
+ debug "Marking Winbind service as down and not managed by CTDB"
+
+ service "winbind" force-stopped
+
+ export FAKE_WBINFO_FAIL="yes"
+ fi
+}
+
+wbinfo_down ()
+{
+ debug "Making wbinfo commands fail"
+ FAKE_WBINFO_FAIL="yes"
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/50.samba.sh b/ctdb/tests/UNIT/eventscripts/scripts/50.samba.sh
new file mode 100644
index 0000000..51175db
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/50.samba.sh
@@ -0,0 +1,54 @@
+setup ()
+{
+ service_name="samba"
+
+ if [ "$1" != "down" ] ; then
+
+ debug "Marking Samba services as up, listening and managed by CTDB"
+
+ # All possible service names for all known distros.
+ for i in "smb" "samba" "smbd" ; do
+ service "$i" force-started
+ done
+
+ setup_tcp_listen 445 139
+
+ # Some things in 50.samba are backgrounded and waited
+ # for. If we don't sleep at all then timeouts can
+ # happen. This avoids that... :-)
+ export FAKE_SLEEP_FORCE=0.1
+ else
+ debug "Marking Samba services as down, not listening and not managed by CTDB"
+
+ # All possible service names for all known distros.
+ for i in "smb" "samba" "smbd" ; do
+ service "$i" force-stopped
+ done
+
+ setup_tcp_listen
+ fi
+
+ setup_script_options <<EOF
+CTDB_SAMBA_SKIP_SHARE_CHECK="no"
+EOF
+
+ setup_shares
+
+}
+
+samba_setup_fake_threads ()
+{
+ export FAKE_SMBD_THREAD_PIDS="$*"
+
+ _nl="
+"
+ _out=""
+ _count=0
+ for _pid ; do
+ [ "$_count" -lt 5 ] || break
+ _t=$(program_stack_trace "smbd" $_pid)
+ _out="${_out:+${_out}${_nl}}${_t}"
+ _count=$((_count + 1))
+ done
+ SAMBA_STACK_TRACES="$_out"
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/60.nfs.sh b/ctdb/tests/UNIT/eventscripts/scripts/60.nfs.sh
new file mode 100644
index 0000000..1da568e
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/60.nfs.sh
@@ -0,0 +1,436 @@
+setup ()
+{
+ setup_public_addresses
+ setup_shares
+
+ service_name="nfs"
+
+ if [ -z "$CTDB_NFS_DISTRO_STYLE" ] ; then
+ # Currently supported: sysvinit-redhat, systemd-redhat
+ CTDB_NFS_DISTRO_STYLE="systemd-redhat"
+ fi
+
+ export FAKE_RPCINFO_SERVICES=""
+
+ setup_script_options <<EOF
+CTDB_NFS_SKIP_SHARE_CHECK="no"
+# This doesn't even need to exist
+CTDB_NFS_EXPORTS_FILE="${CTDB_TEST_TMP_DIR}/etc-exports"
+EOF
+
+ export RPCNFSDCOUNT
+
+ if [ "$1" != "down" ] ; then
+ debug <<EOF
+Setting up NFS environment: all RPC services up, NFS managed by CTDB
+EOF
+
+ case "$CTDB_NFS_DISTRO_STYLE" in
+ sysvinit-*)
+ service "nfs" force-started
+ service "nfslock" force-started
+ ;;
+ systemd-*)
+ service "nfs-service" force-started
+ service "nfs-mountd" force-started
+ service "rpc-rquotad" force-started
+ service "rpc-statd" force-started
+ ;;
+ esac
+
+ rpc_services_up \
+ "portmapper" "nfs" "mountd" "rquotad" \
+ "nlockmgr" "status"
+
+ nfs_setup_fake_threads "nfsd"
+ nfs_setup_fake_threads "rpc.foobar" # Set the variable to empty
+ else
+ debug <<EOF
+Setting up NFS environment: all RPC services down, NFS not managed by CTDB
+EOF
+
+ case "$CTDB_NFS_DISTRO_STYLE" in
+ sysvinit-*)
+ service "nfs" force-stopped
+ service "nfslock" force-stopped
+ service "nfs-kernel-server" force-stopped
+ ;;
+ systemd-*)
+ service "nfs-server" force-stopped
+ service "nfs-mountd" force-stopped
+ service "rpc-quotad" force-stopped
+ service "rpc-statd" force-stopped
+ ;;
+ esac
+ fi
+
+ # This is really nasty. However, when we test NFS we don't
+ # actually test statd-callout. If we leave it there then left
+ # over, backgrounded instances of statd-callout will do
+ # horrible things with the "ctdb ip" stub and cause the actual
+ # statd-callout tests that follow to fail.
+ rm "${CTDB_BASE}/statd-callout"
+}
+
+rpc_services_down ()
+{
+ _out=""
+ for _s in $FAKE_RPCINFO_SERVICES ; do
+ for _i ; do
+ if [ "$_i" = "${_s%%:*}" ] ; then
+ debug "Marking RPC service \"${_i}\" as UNAVAILABLE"
+ continue 2
+ fi
+ done
+ _out="${_out}${_out:+ }${_s}"
+ done
+ FAKE_RPCINFO_SERVICES="$_out"
+}
+
+rpc_services_up ()
+{
+ _out="$FAKE_RPCINFO_SERVICES"
+ for _i ; do
+ debug "Marking RPC service \"${_i}\" as available"
+ case "$_i" in
+ portmapper) _t="2:4" ;;
+ nfs) _t="2:3" ;;
+ mountd) _t="1:3" ;;
+ rquotad) _t="1:2" ;;
+ nlockmgr) _t="3:4" ;;
+ status) _t="1:1" ;;
+ *) die "Internal error - unsupported RPC service \"${_i}\"" ;;
+ esac
+
+ _out="${_out}${_out:+ }${_i}:${_t}"
+ done
+ export FAKE_RPCINFO_SERVICES="$_out"
+}
+
+nfs_setup_fake_threads ()
+{
+ _prog="$1" ; shift
+
+ case "$_prog" in
+ nfsd)
+ export PROCFS_PATH="${CTDB_TEST_TMP_DIR}/proc"
+ _threads="${PROCFS_PATH}/fs/nfsd/threads"
+ mkdir -p $(dirname "$_threads")
+ echo $# >"$_threads"
+ export FAKE_NFSD_THREAD_PIDS="$*"
+ ;;
+ *)
+ export FAKE_RPC_THREAD_PIDS="$*"
+ ;;
+ esac
+}
+
+guess_output ()
+{
+ case "$1" in
+ $CTDB_NFS_CALLOUT\ start\ nlockmgr)
+ case "$CTDB_NFS_DISTRO_STYLE" in
+ sysvinit-redhat)
+ echo "&Starting nfslock: OK"
+ ;;
+ sysvinit-debian)
+ cat <<EOF
+&Starting nfs-kernel-server: OK
+EOF
+ ;;
+ systemd-*)
+ echo "&Starting rpc-statd: OK"
+ ;;
+ esac
+ ;;
+ $CTDB_NFS_CALLOUT\ start\ nfs)
+ case "$CTDB_NFS_DISTRO_STYLE" in
+ sysvinit-redhat)
+ cat <<EOF
+&Starting nfslock: OK
+&Starting nfs: OK
+EOF
+ ;;
+ sysvinit-debian)
+ cat <<EOF
+&Starting nfs-kernel-server: OK
+EOF
+ ;;
+ systemd-redhat)
+ cat <<EOF
+&Starting rpc-statd: OK
+&Starting nfs-server: OK
+&Starting rpc-rquotad: OK
+EOF
+ ;;
+ systemd-debian)
+ cat <<EOF
+&Starting rpc-statd: OK
+&Starting nfs-server: OK
+&Starting quotarpc: OK
+EOF
+ ;;
+ esac
+ ;;
+ $CTDB_NFS_CALLOUT\ stop\ mountd)
+ case "$CTDB_NFS_DISTRO_STYLE" in
+ systemd-*)
+ echo "Stopping nfs-mountd: OK"
+ ;;
+ esac
+ ;;
+ $CTDB_NFS_CALLOUT\ stop\ rquotad)
+ case "$CTDB_NFS_DISTRO_STYLE" in
+ systemd-redhat)
+ echo "Stopping rpc-rquotad: OK"
+ ;;
+ systemd-debian)
+ if service "quotarpc" status >/dev/null; then
+ echo "Stopping quotarpc: OK"
+ else
+ echo "service: can't stop quotarpc - not running"
+ fi
+ ;;
+ esac
+ ;;
+ $CTDB_NFS_CALLOUT\ stop\ status)
+ case "$CTDB_NFS_DISTRO_STYLE" in
+ systemd-*)
+ echo "Stopping rpc-statd: OK"
+ ;;
+ esac
+ ;;
+ $CTDB_NFS_CALLOUT\ start\ mountd)
+ case "$CTDB_NFS_DISTRO_STYLE" in
+ systemd-*)
+ echo "&Starting nfs-mountd: OK"
+ ;;
+ esac
+ ;;
+ $CTDB_NFS_CALLOUT\ start\ rquotad)
+ case "$CTDB_NFS_DISTRO_STYLE" in
+ systemd-redhat)
+ echo "&Starting rpc-rquotad: OK"
+ ;;
+ systemd-debian)
+ echo "&Starting quotarpc: OK"
+ ;;
+ esac
+ ;;
+ $CTDB_NFS_CALLOUT\ start\ status)
+ case "$CTDB_NFS_DISTRO_STYLE" in
+ systemd-*)
+ echo "&Starting rpc-statd: OK"
+ ;;
+ esac
+ ;;
+ *)
+ : # Nothing
+ esac
+}
+
+# Set the required result for a particular RPC program having failed
+# for a certain number of iterations. This is probably still a work
+# in progress. Note that we could hook aggressively
+# nfs_check_rpc_service() to try to implement this but we're better
+# off testing nfs_check_rpc_service() using independent code... even
+# if it is incomplete and hacky. So, if the 60.nfs eventscript
+# changes and the tests start to fail then it may be due to this
+# function being incomplete.
+rpc_set_service_failure_response ()
+{
+ _rpc_service="$1"
+ _numfails="${2:-1}" # default 1
+
+ # Default
+ ok_null
+ if [ $_numfails -eq 0 ] ; then
+ return
+ fi
+
+ nfs_load_config
+
+ # A handy newline. :-)
+ _nl="
+"
+
+ _dir="${CTDB_NFS_CHECKS_DIR:-${CTDB_BASE}/nfs-checks.d}"
+
+ _file=$(ls "$_dir"/[0-9][0-9]."${_rpc_service}.check")
+ [ -r "$_file" ] || \
+ die "RPC check file \"$_file\" does not exist or is not unique"
+
+ _out="${CTDB_TEST_TMP_DIR}/rpc_failure_output"
+ : >"$_out"
+ _rc_file="${CTDB_TEST_TMP_DIR}/rpc_result"
+
+ (
+ # Subshell to restrict scope variables...
+
+ # Defaults
+ family="tcp"
+ version=""
+ unhealthy_after=1
+ restart_every=0
+ service_stop_cmd=""
+ service_start_cmd=""
+ service_check_cmd=""
+ service_debug_cmd=""
+
+ # Don't bother syntax checking, eventscript does that...
+ . "$_file"
+
+ # Just use the first version, or use default. This is
+ # dumb but handles all the cases that we care about
+ # now...
+ if [ -n "$version" ] ; then
+ _ver="${version%% *}"
+ else
+ case "$_rpc_service" in
+ portmapper) _ver="" ;;
+ *) _ver=1 ;;
+ esac
+ fi
+ _rpc_check_out="\
+$_rpc_service failed RPC check:
+rpcinfo: RPC: Program not registered
+program $_rpc_service${_ver:+ version }${_ver} is not available"
+
+ if [ $unhealthy_after -gt 0 -a \
+ $_numfails -ge $unhealthy_after ] ; then
+ _unhealthy=true
+ echo 1 >"$_rc_file"
+ echo "ERROR: ${_rpc_check_out}" >>"$_out"
+ else
+ _unhealthy=false
+ echo 0 >"$_rc_file"
+ fi
+
+ if [ $restart_every -gt 0 ] && \
+ [ $(($_numfails % $restart_every)) -eq 0 ] ; then
+ if ! $_unhealthy ; then
+ echo "WARNING: ${_rpc_check_out}" >>"$_out"
+ fi
+
+ echo "Trying to restart service \"${_rpc_service}\"..."\
+ >>"$_out"
+
+ guess_output "$service_stop_cmd" >>"$_out"
+
+ if [ -n "$service_debug_cmd" ] ; then
+ $service_debug_cmd 2>&1 >>"$_out"
+ fi
+
+ guess_output "$service_start_cmd" >>"$_out"
+ fi
+ )
+
+ read _rc <"$_rc_file"
+ required_result $_rc <"$_out"
+
+ rm -f "$_out" "$_rc_file"
+}
+
+program_stack_traces ()
+{
+ _prog="$1"
+ _max="${2:-1}"
+
+ _count=1
+ if [ "$_prog" = "nfsd" ] ; then
+ _pids="$FAKE_NFSD_THREAD_PIDS"
+ else
+ _pids="$FAKE_RPC_THREAD_PIDS"
+ fi
+ for _pid in $_pids ; do
+ [ $_count -le $_max ] || break
+
+ program_stack_trace "$_prog" "$_pid"
+ _count=$(($_count + 1))
+ done
+}
+
+# Run an NFS eventscript iteratively.
+#
+# - 1st argument is the number of iterations.
+#
+# - 2nd argument is the NFS/RPC service being tested
+#
+# rpcinfo (or $service_check_cmd) is used on each iteration to test
+# the availability of the service
+#
+# If this is not set or null then no RPC service is checked and the
+# required output is not reset on each iteration. This is useful in
+# baseline tests to confirm that the eventscript and test
+# infrastructure is working correctly.
+#
+# - Subsequent arguments come in pairs: an iteration number and
+# something to eval before that iteration. Each time an iteration
+# number is matched the associated argument is given to eval after
+# the default setup is done. The iteration numbers need to be given
+# in ascending order.
+#
+# These arguments can allow a service to be started or stopped
+# before a particular iteration.
+#
+nfs_iterate_test ()
+{
+ _repeats="$1"
+ _rpc_service="$2"
+ if [ -n "$2" ] ; then
+ shift 2
+ else
+ shift
+ fi
+
+ echo "Running $_repeats iterations of \"$script $event\" $args"
+
+ _iterate_failcount=0
+ for _iteration in $(seq 1 $_repeats) ; do
+ # This is not a numerical comparison because $1 will
+ # often not be set.
+ if [ "$_iteration" = "$1" ] ; then
+ debug <<EOF
+##################################################
+EOF
+ eval "$2"
+ debug <<EOF
+##################################################
+EOF
+ shift 2
+ fi
+ if [ -n "$_rpc_service" ] ; then
+ _ok=false
+ if [ -n "$service_check_cmd" ] ; then
+ if eval "$service_check_cmd" ; then
+ _ok=true
+ fi
+ else
+ if rpcinfo -T tcp localhost "$_rpc_service" \
+ >/dev/null 2>&1 ; then
+ _ok=true
+ fi
+ fi
+
+ if $_ok ; then
+ _iterate_failcount=0
+ else
+ _iterate_failcount=$(($_iterate_failcount + 1))
+ fi
+ rpc_set_service_failure_response \
+ "$_rpc_service" $_iterate_failcount
+ fi
+ _out=$(simple_test 2>&1)
+ _ret=$?
+ if "$CTDB_TEST_VERBOSE" || [ $_ret -ne 0 ] ; then
+ cat <<EOF
+##################################################
+Iteration ${_iteration}:
+$_out
+EOF
+ fi
+ if [ $_ret -ne 0 ] ; then
+ exit $_ret
+ fi
+ done
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/91.lvs.sh b/ctdb/tests/UNIT/eventscripts/scripts/91.lvs.sh
new file mode 100644
index 0000000..a8104eb
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/91.lvs.sh
@@ -0,0 +1,76 @@
+setup ()
+{
+ _ip="$1"
+ _iface="$2"
+
+ export FAKE_LVS_STATE_DIR="${FAKE_NETWORK_STATE}/lvs"
+ mkdir -p "$FAKE_LVS_STATE_DIR"
+
+ lvs_header=$(ipvsadm -l -n)
+
+ [ -n "$_ip" ] || return 0
+ [ -n "$_iface" ] || return 0
+
+ setup_script_options <<EOF
+CTDB_LVS_NODES="${CTDB_BASE}/lvs_nodes"
+CTDB_LVS_PUBLIC_IP="$_ip"
+CTDB_LVS_PUBLIC_IFACE="$_iface"
+EOF
+
+ export FAKE_CTDB_LVS_LEADER=""
+
+ # Read from stdin
+ _pnn=0
+ while read _ip _opts ; do
+ case "$_opts" in
+ leader)
+ FAKE_CTDB_LVS_LEADER="$_pnn"
+ echo "$_ip"
+ ;;
+ follower-only)
+ printf "%s\tfollower-only\n" "$_ip"
+ ;;
+ *)
+ echo "$_ip"
+ ;;
+ esac
+ _pnn=$(($_pnn + 1))
+ done >"$CTDB_LVS_NODES"
+}
+
+check_ipvsadm ()
+{
+ if [ "$1" = "NULL" ] ; then
+ required_result 0 <<EOF
+$lvs_header
+EOF
+ else
+ required_result 0 <<EOF
+$lvs_header
+$(cat)
+EOF
+ fi
+
+ simple_test_command ipvsadm -l -n
+}
+
+check_lvs_ip ()
+{
+ _scope="$1"
+
+ if [ "$_scope" = "NULL" ] ; then
+ required_result 0 <<EOF
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
+ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+EOF
+ else
+ required_result 0 <<EOF
+1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
+ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+ inet ${CTDB_LVS_PUBLIC_IP}/32 scope ${_scope} lo
+ valid_lft forever preferred_lft forever
+EOF
+ fi
+
+ simple_test_command ip addr show dev lo
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/debug_locks.sh b/ctdb/tests/UNIT/eventscripts/scripts/debug_locks.sh
new file mode 100644
index 0000000..c303c60
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/debug_locks.sh
@@ -0,0 +1,255 @@
+setup ()
+{
+ setup_dbdir
+}
+
+result_filter()
+{
+ sed -e 's|\( of debug locks PID=\)[0-9]*|\1PID|'
+}
+
+tdb_path ()
+{
+ echo "${CTDB_DBDIR}/${1}.${FAKE_CTDB_PNN}"
+}
+
+fake_file_id ()
+{
+ _path="$1"
+
+ echo "$FAKE_FILE_ID_MAP" |
+ awk -v path="$_path" '$1 == path { print $2 }'
+}
+
+fake_stack_trace ()
+{
+ _pid="$1"
+ _command="${2:-smbd}"
+ _state="$3"
+
+ echo "----- Stack trace for PID=${_pid} -----"
+
+ case "$_state" in
+ D*)
+ cat <<EOF
+----- Process in D state, printing kernel stack only
+[<ffffffff87654321>] fake_stack_trace_for_pid_${_pid}/stack+0x0/0xff
+EOF
+ ;;
+ *)
+ cat <<EOF
+Thread 1 (Thread 0x7f688fbfb180 (LWP ${_pid}) "${_command}"):
+#0 0x00007f688ff7a076 in open (FAKE ARGS...) at FAKE PLACE
+....
+#3 0x000055cd368ead72 in main (argc=<optimized out>, argv=<optimized out>) at ${_command}.c
+EOF
+ ;;
+ esac
+}
+
+do_test ()
+{
+ _holder_scope="$1"
+ _holder_state="$2"
+ _helper_scope="$3"
+ _lock_type="${4:-FCNTL}"
+
+ _lock_helper_pid="4132032"
+
+ FAKE_PS_MAP=$(cat <<EOF
+1234567 ctdbd S
+2345678 smbd S
+4131931 smbd ${_holder_state}
+${_lock_helper_pid} ctdb_lock_helpe S+
+EOF
+ )
+ export FAKE_PS_MAP
+
+ FAKE_FILE_ID_MAP=""
+ _tdbs="locking.tdb brlock.tdb test.tdb foo.tdb"
+ _n=1
+ for _t in $_tdbs ; do
+ _path=$(tdb_path "$_t")
+ _inode=$((19690818 + _n))
+ FAKE_FILE_ID_MAP=$(cat <<EOF
+${FAKE_FILE_ID_MAP}
+${_path} 103:04:${_inode}
+EOF
+ )
+ rm -f "$_path"
+ touch "$_path"
+ _n=$((_n + 1))
+ done
+ export FAKE_FILE_ID_MAP
+
+ _path=$(tdb_path "locking.tdb")
+ _locking_tdb_id=$(fake_file_id "$_path")
+
+ _t=$(cat <<EOF
+POSIX ADVISORY WRITE 3769740 103:04:24380821 1073741826 1073742335
+FLOCK ADVISORY WRITE 3632524 103:02:1059266 0 EOF
+FLOCK ADVISORY WRITE 4060231 00:17:17184 0 EOF
+POSIX ADVISORY READ 1234567 ${_locking_tdb_id} 4 4
+POSIX ADVISORY WRITE 59178 103:04:24380821 1073741826 1073742335
+POSIX ADVISORY READ 4427 103:04:22152234 1073741826 1073742335
+POSIX ADVISORY WRITE 4427 103:04:22152494 0 EOF
+POSIX ADVISORY READ 4427 103:04:22152702 1073741826 1073742335
+EOF
+ )
+
+ _holder_lock=""
+ if [ "$_holder_scope" = "DB" ] ; then
+ if [ "$_lock_type" = "FCNTL" ] ; then
+ _holder_lock=$(cat <<EOF
+POSIX ADVISORY WRITE 4131931 ${_locking_tdb_id} 168 EOF
+EOF
+ )
+ elif [ "$_lock_type" = "MUTEX" ] ; then
+ _holder_lock=$(cat <<EOF
+POSIX ADVISORY WRITE 4131931 ${_locking_tdb_id} 400172 EOF
+EOF
+ )
+ fi
+ elif [ "$_holder_scope" = "RECORD" ] && \
+ [ "$_lock_type" = "FCNTL" ] ; then
+ _holder_lock=$(cat <<EOF
+POSIX ADVISORY WRITE 2345678 ${_locking_tdb_id} 112736 112736
+POSIX ADVISORY WRITE 4131931 ${_locking_tdb_id} 225472 225472
+EOF
+ )
+ fi
+
+ _t=$(cat <<EOF
+$_t
+$_holder_lock
+EOF
+ )
+
+ _helper_lock=""
+ if [ "$_helper_scope" = "DB" ] && \
+ [ "$_lock_type" = "FCNTL" ] ; then
+ _helper_lock=$(cat <<EOF
+-> POSIX ADVISORY WRITE ${_lock_helper_pid} ${_locking_tdb_id} 168 170
+EOF
+ )
+ elif [ "$_helper_scope" = "RECORD" ] && \
+ [ "$_lock_type" = "FCNTL" ] ; then
+ _helper_lock=$(cat <<EOF
+-> POSIX ADVISORY WRITE ${_lock_helper_pid} ${_locking_tdb_id} 112736 112736
+EOF
+ )
+ fi
+ _t=$(cat <<EOF
+$_t
+$_helper_lock
+EOF
+ )
+
+ if [ "$_holder_scope" = "DB" ] ; then
+ _t=$(cat <<EOF
+$_t
+POSIX ADVISORY READ 4131931 ${_locking_tdb_id} 4 4
+EOF
+ )
+ elif [ "$_holder_scope" = "RECORD" ] && \
+ [ "$_lock_type" = "FCNTL" ] ; then
+ _t=$(cat <<EOF
+$_t
+POSIX ADVISORY READ 2345678 ${_locking_tdb_id} 4 4
+POSIX ADVISORY READ 4131931 ${_locking_tdb_id} 4 4
+EOF
+ )
+ fi
+
+ _t=$(cat <<EOF
+$_t
+POSIX ADVISORY READ 3769740 103:04:24390149 1073741826 1073742335
+POSIX ADVISORY WRITE 3769740 103:04:24380839 1073741826 1073742335
+FLOCK ADVISORY WRITE 3769302 103:02:1180313 0 EOF
+FLOCK ADVISORY WRITE 3769302 103:02:1177487 0 EOF
+FLOCK ADVISORY WRITE 3769302 103:02:1180308 0 EOF
+OFDLCK ADVISORY READ -1 00:05:6 0 EOF
+EOF
+ )
+
+ FAKE_PROC_LOCKS=$(echo "$_t" | awk '{ printf "%d: %s\n", NR, $0 }')
+ export FAKE_PROC_LOCKS
+
+ _holder_mutex_lock=""
+ if [ "$_lock_type" = "MUTEX" ] ; then
+ if [ "$_holder_scope" = "RECORD" ] ; then
+ _holder_mutex_lock=$(cat <<EOF
+2345678 28142
+4131931 56284
+EOF
+ )
+ fi
+ fi
+
+ FAKE_TDB_MUTEX_CHECK="$_holder_mutex_lock"
+ export FAKE_TDB_MUTEX_CHECK
+
+ _out=''
+ _nl='
+'
+ _db="locking.tdb.${FAKE_CTDB_PNN}"
+
+ if [ -n "$_helper_lock" ] ; then
+ read -r _ _ _ _ _pid _ _start _end <<EOF
+$_helper_lock
+EOF
+ _out="Waiter:${_nl}"
+ _out="${_out}${_pid} ctdb_lock_helpe ${_db} ${_start} ${_end}"
+ fi
+
+ # fake lock info
+ _pids=''
+ _out="${_out:+${_out}${_nl}}Lock holders:"
+ if [ -n "$_holder_mutex_lock" ] ; then
+ while read -r _pid _chain ; do
+ _comm="smbd"
+ _out="${_out}${_nl}"
+ _out="${_out}${_pid} smbd ${_db} ${_chain}"
+ _pids="${_pids:+${_pids} }${_pid}"
+ done <<EOF
+$_holder_mutex_lock
+EOF
+ else
+ while read -r _ _ _ _pid _ _start _end ; do
+ _comm="smbd"
+ _out="${_out}${_nl}"
+ _out="${_out}${_pid} smbd ${_db} ${_start} ${_end}"
+ _pids="${_pids:+${_pids} }${_pid}"
+ done <<EOF
+$_holder_lock
+EOF
+ fi
+
+ # fake stack traces
+ for _pid in $_pids ; do
+ _comm="smbd"
+ if [ "$_pid" = "4131931" ] ; then
+ _state="$_holder_state"
+ else
+ _state="S"
+ fi
+ _out=$(cat <<EOF
+$_out
+$(fake_stack_trace "$_pid" "$_comm" "$_state")
+EOF
+ )
+ done
+
+ ok <<EOF
+===== Start of debug locks PID=PID =====
+$_out
+===== End of debug locks PID=PID =====
+EOF
+
+ script_test "${script_dir}/${script}" \
+ "$_lock_helper_pid" \
+ "$_helper_scope" \
+ "$_path" \
+ "$_lock_type"
+
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/local.sh b/ctdb/tests/UNIT/eventscripts/scripts/local.sh
new file mode 100644
index 0000000..0b73f7d
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/local.sh
@@ -0,0 +1,550 @@
+# Hey Emacs, this is a -*- shell-script -*- !!! :-)
+
+#
+# Augment PATH with relevant stubs/ directories.
+#
+
+stubs_dir="${CTDB_TEST_SUITE_DIR}/stubs"
+[ -d "${stubs_dir}" ] || die "Failed to locate stubs/ subdirectory"
+
+# Make the path absolute for tests that change directory
+case "$stubs_dir" in
+/*) : ;;
+*) stubs_dir="${PWD}/${stubs_dir}" ;;
+esac
+
+# Use stubs as helpers
+export CTDB_HELPER_BINDIR="$stubs_dir"
+
+PATH="${stubs_dir}:${PATH}"
+
+
+export CTDB="ctdb"
+
+# Force this to be absolute - event scripts can change directory
+CTDB_TEST_TMP_DIR=$(cd "$CTDB_TEST_TMP_DIR" && echo "$PWD")
+
+export CTDB_LOGGING="file:${CTDB_TEST_TMP_DIR}/log.ctdb"
+touch "${CTDB_LOGGING#file:}" || \
+ die "Unable to setup logging for \"$CTDB_LOGGING\""
+
+if [ -d "${CTDB_TEST_SUITE_DIR}/etc" ] ; then
+ cp -a "${CTDB_TEST_SUITE_DIR}/etc" "$CTDB_TEST_TMP_DIR"
+ export CTDB_SYS_ETCDIR="${CTDB_TEST_TMP_DIR}/etc"
+else
+ die "Unable to setup \$CTDB_SYS_ETCDIR"
+fi
+
+setup_ctdb_base "$CTDB_TEST_TMP_DIR" "etc-ctdb" \
+ debug_locks.sh \
+ functions \
+ nfs-checks.d \
+ nfs-linux-kernel-callout \
+ statd-callout
+
+export FAKE_CTDB_STATE="${CTDB_TEST_TMP_DIR}/fake-ctdb"
+mkdir -p "$FAKE_CTDB_STATE"
+
+export FAKE_NETWORK_STATE="${CTDB_TEST_TMP_DIR}/fake-network-state"
+mkdir -p "$FAKE_NETWORK_STATE"
+
+######################################################################
+
+if "$CTDB_TEST_VERBOSE" ; then
+ debug ()
+ {
+ if [ -n "$1" ] ; then
+ echo "$@" >&2
+ else
+ cat >&2
+ fi
+ }
+else
+ debug () { : ; }
+fi
+
+######################################################################
+
+# General setup fakery
+
+# Default is to use script name with ".options" appended. With
+# arguments, this can specify an alternate script name (and
+# component).
+setup_script_options ()
+{
+ if [ $# -eq 2 ] ; then
+ _script="$2"
+ elif [ $# -eq 0 ] ; then
+ _script=""
+ else
+ die "usage: setup_script_options [ component script ]"
+ fi
+
+ if [ -n "$_script" ] ; then
+ _options="${CTDB_BASE}/events/legacy/${_script}.options"
+ else
+ _options="${script_dir}/${script%.script}.options"
+ fi
+
+ cat >>"$_options"
+
+ # Source the options so that tests can use the variables
+ . "$_options"
+}
+
+setup_dbdir ()
+{
+ export CTDB_DBDIR_BASE="${CTDB_TEST_TMP_DIR}/db"
+ CTDB_DBDIR="${CTDB_DBDIR_BASE}/volatile"
+ CTDB_DBDIR_PERSISTENT="${CTDB_DBDIR_BASE}/persistent"
+ CTDB_DBDIR_STATE="${CTDB_DBDIR_BASE}/state"
+ cat >>"${CTDB_BASE}/ctdb.conf" <<EOF
+[database]
+ volatile database directory = ${CTDB_DBDIR}
+ persistent database directory = ${CTDB_DBDIR_PERSISTENT}
+ state database directory = ${CTDB_DBDIR_STATE}
+EOF
+ mkdir -p "$CTDB_DBDIR"
+ mkdir -p "$CTDB_DBDIR_PERSISTENT"
+ mkdir -p "$CTDB_DBDIR_STATE"
+}
+
+setup_date ()
+{
+ export FAKE_DATE_OUTPUT="$1"
+}
+
+setup_tcp_listen ()
+{
+ export FAKE_TCP_LISTEN="$*"
+}
+
+tcp_port_listening ()
+{
+ for _i ; do
+ FAKE_TCP_LISTEN="${FAKE_TCP_LISTEN} ${_i}"
+ done
+}
+
+tcp_port_down ()
+{
+ _port="$1"
+ debug "Marking TCP port \"${_port}\" as not listening"
+
+ _t=""
+ for _i in $FAKE_TCP_LISTEN ; do
+ if [ "$_i" = "$_port" ] ; then
+ continue
+ fi
+ _t="${_t} ${_i}"
+ done
+
+ FAKE_TCP_LISTEN="$_t"
+}
+
+setup_unix_listen ()
+{
+ export FAKE_NETSTAT_UNIX_LISTEN="$*"
+}
+
+unix_socket_listening ()
+{
+ _s="$1"
+
+ FAKE_NETSTAT_UNIX_LISTEN="${FAKE_NETSTAT_UNIX_LISTEN} ${_s}"
+}
+
+setup_shares ()
+{
+ debug "Setting up shares (3 existing shares)"
+ # Create 3 fake shares/exports.
+ export FAKE_SHARES=""
+ for i in $(seq 1 3) ; do
+ _s="${CTDB_TEST_TMP_DIR}/shares/share${i}"
+ mkdir -p "$_s"
+ FAKE_SHARES="${FAKE_SHARES}${FAKE_SHARES:+ }${_s}"
+ done
+}
+
+shares_missing ()
+{
+ # Mark some shares as non-existent
+ _fmt="$1" ; shift
+
+ _out=""
+ _nl="
+"
+
+ _n=1
+ for _i in $FAKE_SHARES ; do
+ for _j ; do
+ if [ $_n -ne "$_j" ] ; then
+ continue
+ fi
+
+ debug "Mark share $_n as missing share \"$_i\""
+ rmdir "$_i"
+ _t=$(printf "$_fmt" "${_i}")
+ _out="${_out}${_out:+${_nl}}${_t}"
+ done
+ _n=$(($_n + 1))
+ done
+
+ echo "$_out"
+}
+
+_ethtool_setup ()
+{
+ FAKE_ETHTOOL_LINK_DOWN="${FAKE_NETWORK_STATE}/ethtool-link-down"
+ export FAKE_ETHTOOL_LINK_DOWN
+ mkdir -p "$FAKE_ETHTOOL_LINK_DOWN"
+}
+
+ethtool_interfaces_down ()
+{
+ _ethtool_setup
+
+ for _i ; do
+ echo "Marking interface $_i DOWN for ethtool"
+ touch "${FAKE_ETHTOOL_LINK_DOWN}/${_i}"
+ done
+}
+
+ethtool_interfaces_up ()
+{
+ _ethtool_setup
+
+ for _i ; do
+ echo "Marking interface $_i UP for ethtool"
+ rm -f "${FAKE_ETHTOOL_LINK_DOWN}/${_i}"
+ done
+}
+
+dump_routes ()
+{
+ echo "# ip rule show"
+ ip rule show
+
+ ip rule show |
+ while read _p _x _i _x _t ; do
+ # Remove trailing colon after priority/preference.
+ _p="${_p%:}"
+ # Only remove rules that match our priority/preference.
+ [ "$CTDB_PER_IP_ROUTING_RULE_PREF" = "$_p" ] || continue
+
+ echo "# ip route show table $_t"
+ ip route show table "$_t"
+ done
+}
+
+# Copied from 13.per_ip_routing for now... so this is lazy testing :-(
+ipv4_host_addr_to_net ()
+{
+ _host="$1"
+ _maskbits="$2"
+
+ # Convert the host address to an unsigned long by splitting out
+ # the octets and doing the math.
+ _host_ul=0
+ for _o in $(export IFS="." ; echo $_host) ; do
+ _host_ul=$(( ($_host_ul << 8) + $_o)) # work around Emacs color bug
+ done
+
+ # Calculate the mask and apply it.
+ _mask_ul=$(( 0xffffffff << (32 - $_maskbits) ))
+ _net_ul=$(( $_host_ul & $_mask_ul ))
+
+ # Now convert to a network address one byte at a time.
+ _net=""
+ for _o in $(seq 1 4) ; do
+ _net="$(($_net_ul & 255))${_net:+.}${_net}"
+ _net_ul=$(($_net_ul >> 8))
+ done
+
+ echo "${_net}/${_maskbits}"
+}
+
+######################################################################
+
+# CTDB fakery
+
+setup_numnodes ()
+{
+ export FAKE_CTDB_NUMNODES="${1:-3}"
+ echo "Setting up CTDB with ${FAKE_CTDB_NUMNODES} fake nodes"
+}
+
+# For now this creates the same public addresses each time. However,
+# it could be made more flexible.
+setup_public_addresses ()
+{
+ _f="${CTDB_BASE}/public_addresses"
+
+ echo "Setting up public addresses in ${_f}"
+ cat >"$_f" <<EOF
+# This is a comment
+10.0.0.1/24 dev123
+10.0.0.2/24 dev123
+10.0.0.3/24 dev123
+10.0.0.4/24 dev123
+10.0.0.5/24 dev123
+10.0.0.6/24 dev123
+10.0.1.1/24 dev456
+10.0.1.2/24 dev456
+10.0.1.3/24 dev456
+EOF
+
+ # Needed for IP allocation
+ setup_numnodes
+}
+
+# Need to cope with ctdb_get_pnn(). If a test changes PNN then it
+# needs to be using a different state directory, otherwise the wrong
+# PNN can already be cached in the state directory.
+ctdb_set_pnn ()
+{
+ export FAKE_CTDB_PNN="$1"
+ echo "Setting up PNN ${FAKE_CTDB_PNN}"
+
+ CTDB_SCRIPT_VARDIR="${CTDB_TEST_TMP_DIR}/scripts/${FAKE_CTDB_PNN}"
+ export CTDB_SCRIPT_VARDIR
+ mkdir -p "$CTDB_SCRIPT_VARDIR"
+}
+
+ctdb_get_interfaces ()
+{
+ # The echo/subshell forces all the output onto 1 line.
+ echo $(ctdb ifaces -X | awk -F'|' 'FNR > 1 {print $2}')
+}
+
+ctdb_get_1_interface ()
+{
+ _t=$(ctdb_get_interfaces)
+ echo ${_t%% *}
+}
+
+# Print public addresses on this node as: interface IP maskbits
+# Each line is suitable for passing to takeip/releaseip
+ctdb_get_my_public_addresses ()
+{
+ ctdb ip -v -X | {
+ read _x # skip header line
+
+ while IFS="|" read _x _ip _x _iface _x ; do
+ [ -n "$_iface" ] || continue
+ while IFS="/$IFS" read _i _maskbits _x ; do
+ if [ "$_ip" = "$_i" ] ; then
+ echo $_iface $_ip $_maskbits
+ break
+ fi
+ done <"${CTDB_BASE}/public_addresses"
+ done
+ }
+}
+
+# Prints the 1st public address as: interface IP maskbits
+# This is suitable for passing to takeip/releaseip
+ctdb_get_1_public_address ()
+{
+ ctdb_get_my_public_addresses | { head -n 1 ; cat >/dev/null ; }
+}
+
+# Check the routes against those that are expected. $1 is the number
+# of assigned IPs to use (<num>, all), defaulting to 1. If $2 is
+# "default" then expect default routes to have been added.
+check_routes ()
+{
+ _num_ips="${1:-1}"
+ _should_add_default="$2"
+
+ _policy_rules=""
+ _policy_routes=""
+
+ ctdb_get_my_public_addresses |
+ if [ "$_num_ips" = "all" ] ; then
+ cat
+ else
+ { head -n "$_num_ips" ; cat >/dev/null ; }
+ fi | {
+ while read _dev _ip _bits ; do
+ _net=$(ipv4_host_addr_to_net "$_ip" "$_bits")
+ _gw="${_net%.*}.254" # a dumb, calculated default
+
+ _policy_rules="${_policy_rules}
+${CTDB_PER_IP_ROUTING_RULE_PREF}: from $_ip lookup ctdb.$_ip "
+ _policy_routes="${_policy_routes}
+# ip route show table ctdb.$_ip
+$_net dev $_dev scope link "
+
+ if [ "$_should_add_default" = "default" ] ; then
+ _policy_routes="${_policy_routes}
+default via $_gw dev $_dev "
+ fi
+ done
+
+ ok <<EOF
+# ip rule show
+0: from all lookup local ${_policy_rules}
+32766: from all lookup main
+32767: from all lookup default ${_policy_routes}
+EOF
+
+ simple_test_command dump_routes
+ } || test_fail
+}
+
+######################################################################
+
+
+nfs_load_config ()
+{
+ _etc="$CTDB_SYS_ETCDIR" # shortcut for readability
+ for _c in "$_etc/sysconfig/nfs" "$_etc/default/nfs" "$_etc/ctdb/sysconfig/nfs" ; do
+ if [ -r "$_c" ] ; then
+ . "$_c"
+ break
+ fi
+ done
+}
+
+setup_nfs_callout()
+{
+ export CTDB_NFS_CALLOUT="${CTDB_HELPER_BINDIR}/nfs-fake-callout"
+ export NFS_FAKE_CALLOUT_MAGIC="$1"
+}
+
+program_stack_trace ()
+{
+ _prog="$1"
+ _pid="$2"
+
+ cat <<EOF
+Stack trace for ${_prog}[${_pid}]:
+[<ffffffff87654321>] fake_stack_trace_for_pid_${_pid}/stack+0x0/0xff
+EOF
+}
+
+######################################################################
+
+# Result and test functions
+
+
+############################################################
+
+setup ()
+{
+ die "setup() is not defined"
+}
+
+# Set some globals and print the summary.
+define_test ()
+{
+ desc="$1"
+
+ _f=$(basename "$0" ".sh")
+
+ # Remaining format should be NN.script.event.NUM or
+ # NN.script.NUM or script.NUM:
+ _num="${_f##*.}"
+ _f="${_f%.*}"
+
+ case "$_f" in
+ [0-9][0-9].*)
+ case "$_f" in
+ [0-9][0-9].*.*)
+ script="${_f%.*}.script"
+ event="${_f##*.}"
+ ;;
+ [0-9][0-9].*)
+ script="${_f}.script"
+ unset event
+ ;;
+ esac
+ # "Enable" the script
+ _subdir="events/legacy"
+ script_dir="${CTDB_BASE}/${_subdir}"
+ # Symlink target needs to be absolute
+ case "$CTDB_SCRIPTS_DATA_DIR" in
+ /*) _data_dir="${CTDB_SCRIPTS_DATA_DIR}/${_subdir}" ;;
+ *) _data_dir="${PWD}/${CTDB_SCRIPTS_DATA_DIR}/${_subdir}"
+ esac
+ mkdir -p "$script_dir"
+ ln -s "${_data_dir}/${script}" "$script_dir"
+ ;;
+ *)
+ script="${_f%.*}"
+ script="$_f"
+ unset event
+ script_dir="${CTDB_BASE}"
+ esac
+
+ _s="${script_dir}/${script}"
+ [ -r "$_s" ] || \
+ die "Internal error - unable to find script \"${_s}\""
+
+ case "$script" in
+ *.script) script_short="${script%.script}" ;;
+ *.sh) script_short="${script%.sh}" ;;
+ *) script_short="$script" ;;
+ esac
+
+ printf "%-17s %-10s %-4s - %s\n\n" \
+ "$script_short" "$event" "$_num" "$desc"
+
+ _f="${CTDB_TEST_SUITE_DIR}/scripts/${script_short}.sh"
+ if [ -r "$_f" ] ; then
+ . "$_f"
+ fi
+
+ ctdb_set_pnn 0
+}
+
+# Run an eventscript once. The test passes if the return code and
+# output match those required.
+
+# Any args are passed to the eventscript.
+
+simple_test ()
+{
+ [ -n "$event" ] || die 'simple_test: $event not set'
+
+ args="$@"
+
+ test_header ()
+ {
+ echo "Running script \"$script $event${args:+ }$args\""
+ }
+
+ extra_header ()
+ {
+ cat <<EOF
+
+##################################################
+CTDB_BASE="$CTDB_BASE"
+CTDB_SYS_ETCDIR="$CTDB_SYS_ETCDIR"
+ctdb client is "$(which ctdb)"
+ip command is "$(which ip)"
+EOF
+ }
+
+ script_test "${script_dir}/${script}" "$event" "$@"
+
+ reset_test_header
+ reset_extra_header
+}
+
+simple_test_event ()
+{
+ # If something has previously failed then don't continue.
+ : ${_passed:=true}
+ $_passed || return 1
+
+ event="$1" ; shift
+ echo "=================================================="
+ simple_test "$@"
+}
+
+simple_test_command ()
+{
+ unit_test_notrace "$@"
+}
diff --git a/ctdb/tests/UNIT/eventscripts/scripts/statd-callout.sh b/ctdb/tests/UNIT/eventscripts/scripts/statd-callout.sh
new file mode 100644
index 0000000..88dc9ac
--- /dev/null
+++ b/ctdb/tests/UNIT/eventscripts/scripts/statd-callout.sh
@@ -0,0 +1,65 @@
+setup ()
+{
+ ctdb_set_pnn
+ setup_public_addresses
+ setup_date "123456789"
+}
+
+ctdb_catdb_format_pairs ()
+{
+ _count=0
+
+ while read _k _v ; do
+ _kn=$(echo -n "$_k" | wc -c)
+ _vn=$(echo -n "$_v" | wc -c)
+ cat <<EOF
+key(${_kn}) = "${_k}"
+dmaster: 0
+rsn: 1
+data(${_vn}) = "${_v}"
+
+EOF
+ _count=$(($_count + 1))
+ done
+
+ echo "Dumped ${_count} records"
+}
+
+check_ctdb_tdb_statd_state ()
+{
+ ctdb_get_my_public_addresses |
+ while read _x _sip _x ; do
+ for _cip ; do
+ cat <<EOF
+statd-state@${_sip}@${_cip} $(date)
+EOF
+ done
+ done |
+ ctdb_catdb_format_pairs | {
+ ok
+ simple_test_command ctdb catdb ctdb.tdb
+ } || exit $?
+}
+
+check_statd_callout_smnotify ()
+{
+ _state_even=$(( $(date '+%s') / 2 * 2))
+ _state_odd=$(($_state_even + 1))
+
+ nfs_load_config
+
+ ctdb_get_my_public_addresses |
+ while read _x _sip _x ; do
+ for _cip ; do
+ cat <<EOF
+SM_NOTIFY: ${_sip} -> ${_cip}, MON_NAME=${_sip}, STATE=${_state_even}
+SM_NOTIFY: ${_sip} -> ${_cip}, MON_NAME=${NFS_HOSTNAME}, STATE=${_state_even}
+SM_NOTIFY: ${_sip} -> ${_cip}, MON_NAME=${_sip}, STATE=${_state_odd}
+SM_NOTIFY: ${_sip} -> ${_cip}, MON_NAME=${NFS_HOSTNAME}, STATE=${_state_odd}
+EOF
+ done
+ done | {
+ ok
+ simple_test_event "notify"
+ } || exit $?
+}