ctdb-scripts: Update eventscripts to use ctdb -X instead of ctdb -Y
[samba.git] / ctdb / tests / eventscripts / scripts / local.sh
1 # Hey Emacs, this is a -*- shell-script -*- !!!  :-)
2
3 # Augment PATH with relevant stubs/ directories.  We do this by actually
4 # setting PATH, and also by setting $EVENTSCRIPTS_PATH and then
5 # prepending that to $PATH in rc.local to avoid the PATH reset in
6 # functions.
7
8 EVENTSCRIPTS_PATH=""
9
10 if [ -d "${TEST_SUBDIR}/stubs" ] ; then
11     EVENTSCRIPTS_PATH="${TEST_SUBDIR}/stubs"
12 fi
13
14 export EVENTSCRIPTS_PATH
15
16 PATH="${EVENTSCRIPTS_PATH}:${PATH}"
17
18 export EVENTSCRIPTS_TESTS_VAR_DIR="${TEST_VAR_DIR}/unit_eventscripts"
19 if [ -d "$EVENTSCRIPTS_TESTS_VAR_DIR" -a \
20     "$EVENTSCRIPTS_TESTS_VAR_DIR" != "/unit_eventscripts" ] ; then
21     rm -r "$EVENTSCRIPTS_TESTS_VAR_DIR"
22 fi
23 mkdir -p "$EVENTSCRIPTS_TESTS_VAR_DIR"
24 export CTDB_VARDIR="$EVENTSCRIPTS_TESTS_VAR_DIR/ctdb"
25
26 export CTDB_LOGGING="file:${EVENTSCRIPTS_TESTS_VAR_DIR}/log.ctdb"
27 touch "${CTDB_LOGGING#file:}" || \
28     die "Unable to setup logging for \"$CTDB_LOGGING\""
29
30 if [ -d "${TEST_SUBDIR}/etc" ] ; then    
31     cp -a "${TEST_SUBDIR}/etc" "$EVENTSCRIPTS_TESTS_VAR_DIR"
32     export CTDB_ETCDIR="${EVENTSCRIPTS_TESTS_VAR_DIR}/etc"
33 else
34     die "Unable to setup \$CTDB_ETCDIR"
35 fi
36
37 if [ -d "${TEST_SUBDIR}/etc-ctdb" ] ; then
38     cp -prL "${TEST_SUBDIR}/etc-ctdb" "$EVENTSCRIPTS_TESTS_VAR_DIR"
39     export CTDB_BASE="${EVENTSCRIPTS_TESTS_VAR_DIR}/etc-ctdb"
40 else
41     die "Unable to set \$CTDB_BASE"
42 fi
43 export CTDB_BASE
44
45 if [ ! -d "${CTDB_BASE}/events.d" ] ; then
46     cat <<EOF
47 ERROR: Directory ${CTDB_BASE}/events.d does not exist.
48
49 That means that no eventscripts can be tested.
50
51 One possible explanation:
52
53   You have CTDB installed via RPMs (or similar), so the regular
54   CTDB_BASE directory is in /etc/ctdb/
55
56   BUT
57
58   You have done a regular "configure" and "make install" so the tests
59   are installed under /usr/local/.
60
61 If so, one possible hack to fix this is to create a symlink:
62
63   ln -s /etc/ctdb /usr/local/etc/ctdb
64
65 This is nasty but it works...  :-)
66 EOF
67     exit 1
68 fi
69
70 ######################################################################
71
72 if "$TEST_VERBOSE" ; then
73     debug () { echo "$@" ; }
74 else
75     debug () { : ; }
76 fi
77
78 eventscripts_tests_cleanup_hooks=""
79
80 # This loses quoting!
81 eventscripts_test_add_cleanup ()
82 {
83     eventscripts_tests_cleanup_hooks="${eventscripts_tests_cleanup_hooks}${eventscripts_tests_cleanup_hooks:+ ; }$*"
84 }
85
86 trap 'eval $eventscripts_tests_cleanup_hooks' 0
87
88
89 ######################################################################
90
91 # General setup fakery
92
93 setup_generic ()
94 {
95     debug "Setting up shares (3 existing shares)"
96     # Create 3 fake shares/exports.
97     export FAKE_SHARES=""
98     for i in $(seq 1 3) ; do
99         _s="${EVENTSCRIPTS_TESTS_VAR_DIR}/shares/${i}_existing"
100         mkdir -p "$_s"
101         FAKE_SHARES="${FAKE_SHARES}${FAKE_SHARES:+ }${_s}"
102     done
103
104     export FAKE_PROC_NET_BONDING="$EVENTSCRIPTS_TESTS_VAR_DIR/proc-net-bonding"
105     mkdir -p "$FAKE_PROC_NET_BONDING"
106     rm -f "$FAKE_PROC_NET_BONDING"/*
107
108     export FAKE_ETHTOOL_LINK_DOWN="$EVENTSCRIPTS_TESTS_VAR_DIR/ethtool-link-down"
109     mkdir -p "$FAKE_ETHTOOL_LINK_DOWN"
110     rm -f "$FAKE_ETHTOOL_LINK_DOWN"/*
111
112     # This can only have 2 levels.  We don't want to resort to usings
113     # something dangerous like "rm -r" setup time.
114     export FAKE_IP_STATE="$EVENTSCRIPTS_TESTS_VAR_DIR/fake-ip-state"
115     mkdir -p "$FAKE_IP_STATE"
116     rm -f "$FAKE_IP_STATE"/*/*
117     rm -f "$FAKE_IP_STATE"/* 2>/dev/null || true
118     rmdir "$FAKE_IP_STATE"/* 2>/dev/null || true
119
120
121     export CTDB_DBDIR="${EVENTSCRIPTS_TESTS_VAR_DIR}/db"
122     mkdir -p "${CTDB_DBDIR}/persistent"
123
124     export FAKE_TDBTOOL_SUPPORTS_CHECK="yes"
125     export FAKE_TDB_IS_OK
126     export FAKE_DATE_OUTPUT
127
128     export FAKE_NETSTAT_TCP_ESTABLISHED FAKE_TCP_LISTEN FAKE_NETSTAT_UNIX_LISTEN
129     export FAKE_NETSTAT_TCP_ESTABLISHED_FILE=$(mktemp --tmpdir="$EVENTSCRIPTS_TESTS_VAR_DIR")
130 }
131
132 tcp_port_down ()
133 {
134     for _i ; do
135         debug "Marking TCP port \"${_i}\" as not listening"
136         FAKE_TCP_LISTEN=$(echo "$FAKE_TCP_LISTEN" | sed -r -e "s@[[:space:]]*[\.0-9]+:${_i}@@g")
137     done
138 }
139
140 shares_missing ()
141 {
142     _fmt="$1" ; shift
143
144     # Replace some shares with non-existent ones.
145     _t=""
146     _n=1
147     _nl="
148 "
149     export MISSING_SHARES_TEXT=""
150     for _i in $FAKE_SHARES ; do
151         if [ $_n = "$1" ] ; then
152             shift
153             _i="${_i%_existing}_missing"
154             debug "Replacing share $_n with missing share \"$_i\""
155             rmdir "$_i" 2>/dev/null || true
156             MISSING_SHARES_TEXT="${MISSING_SHARES_TEXT}${MISSING_SHARES_TEXT:+${_nl}}"$(printf "$_fmt" "${_i}")
157         fi
158         _t="${_t}${_t:+ }${_i}"
159         _n=$(($_n + 1))
160     done
161     FAKE_SHARES="$_t"
162 }
163
164 # Setup some fake /proc/net/bonding files with just enough info for
165 # the eventscripts.
166
167 # arg1 is interface name, arg2 is currently active slave (use "None"
168 # if none), arg3 is MII status ("up" or "down").
169 setup_bond ()
170 {
171     _iface="$1"
172     _slave="${2:-${_iface}_sl_0}"
173     _mii_s="${3:-up}"
174     _mii_subs="${4:-${_mii_s:-up}}"
175     echo "Setting $_iface to be a bond with active slave $_slave and MII status $_mii_s"
176     cat >"${FAKE_PROC_NET_BONDING}/$_iface" <<EOF
177 Bonding Mode: IEEE 802.3ad Dynamic link aggregation
178 Currently Active Slave: $_slave
179 # Status of the bond
180 MII Status: $_mii_s
181 # Status of 1st pretend adapter
182 MII Status: $_mii_subs
183 # Status of 2nd pretend adapter
184 MII Status: $_mii_subs
185 EOF
186 }
187
188 ethtool_interfaces_down ()
189 {
190     for _i ; do
191         echo "Marking interface $_i DOWN for ethtool"
192         touch "${FAKE_ETHTOOL_LINK_DOWN}/${_i}"
193     done
194 }
195
196 ethtool_interfaces_up ()
197 {
198     for _i ; do
199         echo "Marking interface $_i UP for ethtool"
200         rm -f "${FAKE_ETHTOOL_LINK_DOWN}/${_i}"
201     done
202 }
203
204 dump_routes ()
205 {
206     echo "# ip rule show"
207     ip rule show
208
209     ip rule show |
210     while read _p _x _i _x _t ; do
211         # Remove trailing colon after priority/preference.
212         _p="${_p%:}"
213         # Only remove rules that match our priority/preference.
214         [ "$CTDB_PER_IP_ROUTING_RULE_PREF" = "$_p" ] || continue
215
216         echo "# ip route show table $_t"
217         ip route show table "$_t"
218     done
219 }
220
221 # Copied from 13.per_ip_routing for now... so this is lazy testing  :-(
222 ipv4_host_addr_to_net ()
223 {
224     _host="$1"
225     _maskbits="$2"
226
227     # Convert the host address to an unsigned long by splitting out
228     # the octets and doing the math.
229     _host_ul=0
230     for _o in $(export IFS="." ; echo $_host) ; do
231         _host_ul=$(( ($_host_ul << 8) + $_o)) # work around Emacs color bug
232     done
233
234     # Calculate the mask and apply it.
235     _mask_ul=$(( 0xffffffff << (32 - $_maskbits) ))
236     _net_ul=$(( $_host_ul & $_mask_ul ))
237
238     # Now convert to a network address one byte at a time.
239     _net=""
240     for _o in $(seq 1 4) ; do
241         _net="$(($_net_ul & 255))${_net:+.}${_net}"
242         _net_ul=$(($_net_ul >> 8))
243     done
244
245     echo "${_net}/${_maskbits}"
246 }
247
248 ######################################################################
249
250 # CTDB fakery
251
252 # Evaluate an expression that probably calls functions or uses
253 # variables from the CTDB functions file.  This is used for test
254 # initialisation.
255 eventscript_call ()
256 {
257     (
258         . "$CTDB_BASE/functions"
259         "$@"
260     )
261 }
262
263 # Set output for ctdb command.  Option 1st argument is return code.
264 ctdb_set_output ()
265 {
266     _out="$EVENTSCRIPTS_TESTS_VAR_DIR/ctdb.out"
267     cat >"$_out"
268
269     _rc="$EVENTSCRIPTS_TESTS_VAR_DIR/ctdb.rc"
270     echo "${1:-0}" >"$_rc"
271
272     eventscripts_test_add_cleanup "rm -f $_out $_rc"
273 }
274
275 setup_ctdb ()
276 {
277     setup_generic
278
279     export FAKE_CTDB_NUMNODES="${1:-3}"
280     echo "Setting up CTDB with ${FAKE_CTDB_NUMNODES} fake nodes"
281
282     export FAKE_CTDB_PNN="${2:-0}"
283     echo "Setting up CTDB with PNN ${FAKE_CTDB_PNN}"
284
285     export CTDB_PUBLIC_ADDRESSES="${CTDB_BASE}/public_addresses"
286     if [ -n "$3" ] ; then
287         echo "Setting up CTDB_PUBLIC_ADDRESSES: $3"
288         CTDB_PUBLIC_ADDRESSES=$(mktemp)
289         for _i in $3 ; do
290             _ip="${_i%@*}"
291             _ifaces="${_i#*@}"
292             echo "${_ip} ${_ifaces}" >>"$CTDB_PUBLIC_ADDRESSES"
293         done
294         eventscripts_test_add_cleanup "rm -f $CTDB_PUBLIC_ADDRESSES"
295     fi
296
297     export FAKE_CTDB_STATE="$EVENTSCRIPTS_TESTS_VAR_DIR/fake-ctdb"
298
299     export FAKE_CTDB_IFACES_DOWN="$FAKE_CTDB_STATE/ifaces-down"
300     mkdir -p "$FAKE_CTDB_IFACES_DOWN"
301     rm -f "$FAKE_CTDB_IFACES_DOWN"/*
302
303     export FAKE_CTDB_SCRIPTSTATUS="$FAKE_CTDB_STATE/scriptstatus"
304     mkdir -p "$FAKE_CTDB_SCRIPTSTATUS"
305     rm -f "$FAKE_CTDB_SCRIPTSTATUS"/*
306
307     export CTDB_PARTIALLY_ONLINE_INTERFACES
308 }
309
310 setup_memcheck ()
311 {
312     setup_ctdb
313
314     _swap_total="5857276"
315
316     if [ "$1" = "bad" ] ; then
317         _swap_free="   4352"
318         _mem_cached=" 112"
319         _mem_free=" 468"
320     else
321         _swap_free="$_swap_total"
322         _mem_cached="1112"
323         _mem_free="1468"
324     fi
325
326     export FAKE_PROC_MEMINFO="\
327 MemTotal:        3940712 kB
328 MemFree:          225268 kB
329 Buffers:          146120 kB
330 Cached:          1139348 kB
331 SwapCached:        56016 kB
332 Active:          2422104 kB
333 Inactive:        1019928 kB
334 Active(anon):    1917580 kB
335 Inactive(anon):   523080 kB
336 Active(file):     504524 kB
337 Inactive(file):   496848 kB
338 Unevictable:        4844 kB
339 Mlocked:            4844 kB
340 SwapTotal:       ${_swap_total} kB
341 SwapFree:        ${_swap_free} kB
342 ..."
343
344     export FAKE_FREE_M="\
345              total       used       free     shared    buffers     cached
346 Mem:          3848       3634        213          0        142       ${_mem_cached}
347 -/+ buffers/cache:       2379       ${_mem_free}
348 Swap:         5719        246       5473"
349
350     export CTDB_MONITOR_FREE_MEMORY
351     export CTDB_MONITOR_FREE_MEMORY_WARN
352     export CTDB_CHECK_SWAP_IS_NOT_USED
353 }
354
355 ctdb_get_interfaces ()
356 {
357     # The echo/subshell forces all the output onto 1 line.
358     echo $(ctdb ifaces -X | awk -F'|' 'FNR > 1 {print $2}')
359 }
360
361 ctdb_get_1_interface ()
362 {
363     _t=$(ctdb_get_interfaces)
364     echo ${_t%% *}
365 }
366
367 # Print all public addresses as: interface IP maskbits
368 # Each line is suitable for passing to takeip/releaseip
369 ctdb_get_all_public_addresses ()
370 {
371     _f="${CTDB_PUBLIC_ADDRESSES:-${CTDB_BASE}/public_addresses}"
372     while IFS="/$IFS" read _ip _maskbits _ifaces ; do
373         echo "$_ifaces $_ip $_maskbits"
374     done <"$_f"
375 }
376
377 # Print public addresses on this node as: interface IP maskbits
378 # Each line is suitable for passing to takeip/releaseip
379 ctdb_get_my_public_addresses ()
380 {
381     ctdb ip -v -X | {
382         read _x # skip header line
383
384         while IFS="|" read _x _ip _x _iface _x ; do
385             [ -n "$_iface" ] || continue
386             while IFS="/$IFS" read _i _maskbits _x ; do
387                 if [ "$_ip" = "$_i" ] ; then
388                     echo $_iface $_ip $_maskbits
389                     break
390                 fi
391             done <"${CTDB_PUBLIC_ADDRESSES:-${CTDB_BASE}/public_addresses}"
392         done
393     }
394 }
395
396 # Prints the 1st public address as: interface IP maskbits
397 # This is suitable for passing to takeip/releaseip
398 ctdb_get_1_public_address ()
399 {
400     ctdb_get_my_public_addresses | { head -n 1 ; cat >/dev/null ; }
401 }
402
403 ctdb_not_implemented ()
404 {
405     export CTDB_NOT_IMPLEMENTED="$1"
406     ctdb_not_implemented="\
407 DEBUG: ctdb: command \"$1\" not implemented in stub"
408 }
409
410 ctdb_fake_scriptstatus ()
411 {
412     _code="$1"
413     _status="$2"
414     _err_out="$3"
415
416     _d1=$(date '+%s.%N')
417     _d2=$(date '+%s.%N')
418
419     echo "$_code $_status $_err_out" >"$FAKE_CTDB_SCRIPTSTATUS/$script"
420 }
421
422 ######################################################################
423
424 setup_ctdb_policy_routing ()
425 {
426     service_name="per_ip_routing"
427
428     export CTDB_PER_IP_ROUTING_CONF="$CTDB_BASE/policy_routing"
429     export CTDB_PER_IP_ROUTING_RULE_PREF=100
430     export CTDB_PER_IP_ROUTING_TABLE_ID_LOW=1000
431     export CTDB_PER_IP_ROUTING_TABLE_ID_HIGH=2000
432
433     # Tests need to create and populate this file
434     rm -f "$CTDB_PER_IP_ROUTING_CONF"
435 }
436
437 # Create policy routing configuration in $CTDB_PER_IP_ROUTING_CONF.
438 # $1 is the number of assigned IPs to use (<num>, all), defaulting to
439 # 1.  If $2 is "default" then a default route is also added.
440 create_policy_routing_config ()
441 {
442     _num_ips="${1:-1}"
443     _should_add_default="$2"
444
445     ctdb_get_my_public_addresses |
446     if [ "$_num_ips" = "all" ] ; then
447         cat
448     else
449         { head -n "$_num_ips" ; cat >/dev/null ; }
450     fi |
451     while read _dev _ip _bits ; do
452         _net=$(ipv4_host_addr_to_net "$_ip" "$_bits")
453         _gw="${_net%.*}.1" # a dumb, calculated default
454
455         echo "$_ip $_net"
456
457         if [ "$_should_add_default" = "default" ] ; then
458             echo "$_ip 0.0.0.0/0 $_gw"
459         fi
460     done >"$CTDB_PER_IP_ROUTING_CONF"
461 }
462
463 # Check the routes against those that are expected.  $1 is the number
464 # of assigned IPs to use (<num>, all), defaulting to 1.  If $2 is
465 # "default" then expect default routes to have been added.
466 check_routes ()
467 {
468     _num_ips="${1:-1}"
469     _should_add_default="$2"
470
471     _policy_rules=""
472     _policy_routes=""
473
474     ctdb_get_my_public_addresses |
475     if [ "$_num_ips" = "all" ] ; then
476         cat
477     else
478         { head -n "$_num_ips" ; cat >/dev/null ; }
479     fi | {
480         while read _dev _ip _bits ; do
481             _net=$(ipv4_host_addr_to_net "$_ip" "$_bits")
482             _gw="${_net%.*}.1" # a dumb, calculated default
483
484             _policy_rules="${_policy_rules}
485 ${CTDB_PER_IP_ROUTING_RULE_PREF}:       from $_ip lookup ctdb.$_ip "
486             _policy_routes="${_policy_routes}
487 # ip route show table ctdb.$_ip
488 $_net dev $_dev  scope link "
489
490             if [ "$_should_add_default" = "default" ] ; then
491                 _policy_routes="${_policy_routes}
492 default via $_gw dev $_dev "
493             fi
494         done
495
496         ok <<EOF
497 # ip rule show
498 0:      from all lookup local ${_policy_rules}
499 32766:  from all lookup main 
500 32767:  from all lookup default ${_policy_routes}
501 EOF
502
503         simple_test_command dump_routes
504     }
505 }
506
507 ######################################################################
508
509 setup_ctdb_natgw ()
510 {
511     debug "Setting up NAT gateway"
512
513     natgw_config_dir="${TEST_VAR_DIR}/natgw_config"
514     mkdir -p "$natgw_config_dir"
515
516     # These will accumulate, 1 per test... but will be cleaned up at
517     # the end.
518     export CTDB_NATGW_NODES=$(mktemp --tmpdir="$natgw_config_dir")
519
520     # Read from stdin
521     while read _ip _master _dev ; do
522         echo "$_ip"
523         if [ "$_master" = "master" ] ; then
524             export FAKE_CTDB_NATGW_MASTER="$_ip"
525         fi
526     done >"$CTDB_NATGW_NODES"
527
528     # Assume all of the nodes are on a /24 network and have IPv4
529     # addresses:
530     read _ip <"$CTDB_NATGW_NODES"
531     export CTDB_NATGW_PRIVATE_NETWORK="${_ip%.*}.0/24"
532
533     # These are fixed.  Probably don't use the same network for the
534     # private node IPs.  To unset the default gateway just set it to
535     # "".  :-)
536     export CTDB_NATGW_PUBLIC_IP="10.1.1.121/24"
537     export CTDB_NATGW_PUBLIC_IFACE="eth1"
538     export CTDB_NATGW_DEFAULT_GATEWAY="10.1.1.254"
539     export CTDB_NATGW_SLAVE_ONLY=""
540 }
541
542 ok_natgw_master_ip_addr_show ()
543 {
544     _mac=$(echo "$CTDB_NATGW_PUBLIC_IFACE" | md5sum | sed -r -e 's@(..)(..)(..)(..)(..)(..).*@\1:\2:\3:\4:\5:\6@')
545
546     # This is based on CTDB_NATGW_PUBLIC_IP
547     _brd="10.1.1.255"
548
549 ok <<EOF
550 1: ${CTDB_NATGW_PUBLIC_IFACE}: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
551     link/ether ${_mac} brd ff:ff:ff:ff:ff:ff
552     inet ${CTDB_NATGW_PUBLIC_IP} brd ${_brd} scope global ${CTDB_NATGW_PUBLIC_IFACE}
553        valid_lft forever preferred_lft forever
554 EOF
555 }
556
557 ok_natgw_slave_ip_addr_show ()
558 {
559     _mac=$(echo "$CTDB_NATGW_PUBLIC_IFACE" | md5sum | sed -r -e 's@(..)(..)(..)(..)(..)(..).*@\1:\2:\3:\4:\5:\6@')
560 ok <<EOF
561 1: ${CTDB_NATGW_PUBLIC_IFACE}: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
562     link/ether ${_mac} brd ff:ff:ff:ff:ff:ff
563 EOF
564 }
565
566 ok_natgw_master_static_routes ()
567 {
568     _nl="
569 "
570     _t=""
571     for _i in $CTDB_NATGW_STATIC_ROUTES ; do
572         # This is intentionally different to the code in 11.natgw ;-)
573         case "$_i" in
574             *@*)
575                 _net=$(echo "$_i" | sed -e 's|@.*||')
576                 _gw=$(echo "$_i" | sed -e 's|.*@||')
577                 ;;
578             *)
579                 _net="$_i"
580                 _gw="$CTDB_NATGW_DEFAULT_GATEWAY"
581         esac
582
583         [ -n "$_gw" ] || continue
584         _t="${_t}${_t:+${_nl}}"
585         _t="${_t}${_net} via ${_gw} dev ethXXX  metric 10 "
586     done
587     ok "$_t"
588 }
589
590 ok_natgw_slave_static_routes ()
591 {
592     _nl="
593 "
594     _t=""
595     for _i in $CTDB_NATGW_STATIC_ROUTES ; do
596         # This is intentionally different to the code in 11.natgw ;-)
597         _net=$(echo "$_i" | sed -e 's|@.*||')
598
599         # The interface for the private network isn't specified as
600         # part of the NATGW configuration and isn't part of the
601         # command to add the route.  It is implicitly added by "ip
602         # route" but our stub doesn't do this and adds "ethXXX".
603         _t="${_t}${_t:+${_nl}}"
604         _t="${_t}${_net} via ${FAKE_CTDB_NATGW_MASTER} dev ethXXX  metric 10 "
605     done
606     ok "$_t"
607 }
608
609 ######################################################################
610
611 # Samba/winbind fakery
612
613 setup_samba ()
614 {
615     setup_ctdb
616
617     service_name="samba"
618
619     if [ "$1" != "down" ] ; then
620
621         debug "Marking Samba services as up, listening and managed by CTDB"
622         # Get into known state.
623         eventscript_call ctdb_service_managed
624
625         # All possible service names for all known distros.
626         for i in "smb" "nmb" "samba" ; do
627             service "$i" force-started
628         done
629
630         export CTDB_SAMBA_SKIP_SHARE_CHECK="no"
631         export CTDB_MANAGED_SERVICES="foo samba bar"
632
633         export FAKE_TCP_LISTEN="0.0.0.0:445 0.0.0.0:139"
634         export FAKE_WBINFO_FAIL="no"
635
636         # Some things in 50.samba are backgrounded and waited for.  If
637         # we don't sleep at all then timeouts can happen.  This avoids
638         # that...  :-)
639         export FAKE_SLEEP_FORCE=0.1
640     else
641         debug "Marking Samba services as down, not listening and not managed by CTDB"
642         # Get into known state.
643         eventscript_call ctdb_service_unmanaged
644
645         # All possible service names for all known distros.
646         for i in "smb" "nmb" "samba" ; do
647             service "$i" force-stopped
648         done
649
650         export CTDB_SAMBA_SKIP_SHARE_CHECK="no"
651         export CTDB_MANAGED_SERVICES="foo bar"
652         unset CTDB_MANAGES_SAMBA
653
654         export FAKE_TCP_LISTEN=""
655         export FAKE_WBINFO_FAIL="yes"
656     fi
657
658     # This is ugly but if this file isn't removed before each test
659     # then configuration changes between tests don't stick.
660     rm -f "$CTDB_VARDIR/state/samba/smb.conf.cache"
661 }
662
663 setup_winbind ()
664 {
665     setup_ctdb
666
667     service_name="winbind"
668
669     if [ "$1" != "down" ] ; then
670
671         debug "Marking Winbind service as up and managed by CTDB"
672         # Get into known state.
673         eventscript_call ctdb_service_managed
674
675         service "winbind" force-started
676
677         export CTDB_MANAGED_SERVICES="foo winbind bar"
678
679         export FAKE_WBINFO_FAIL="no"
680
681     else
682         debug "Marking Winbind service as down and not managed by CTDB"
683         # Get into known state.
684         eventscript_call ctdb_service_unmanaged
685
686         service "winbind" force-stopped
687
688         export CTDB_MANAGED_SERVICES="foo bar"
689         unset CTDB_MANAGES_WINBIND
690
691         export FAKE_WBINFO_FAIL="yes"
692     fi
693 }
694
695 wbinfo_down ()
696 {
697     debug "Making wbinfo commands fail"
698     FAKE_WBINFO_FAIL="yes"
699 }
700
701 ######################################################################
702
703 # NFS fakery
704
705 setup_nfs ()
706 {
707     setup_ctdb
708
709     service_name="nfs"
710
711     export FAKE_RPCINFO_SERVICES=""
712
713     export CTDB_NFS_SKIP_SHARE_CHECK="no"
714
715     export CTDB_MONITOR_NFS_THREAD_COUNT RPCNFSDCOUNT FAKE_NFSD_THREAD_PIDS
716     export CTDB_NFS_DUMP_STUCK_THREADS FAKE_RPC_THREAD_PIDS
717
718     # Reset the failcounts for nfs services.
719     eventscript_call eval rm -f '$ctdb_fail_dir/nfs_*'
720
721     if [ "$1" != "down" ] ; then
722         debug "Setting up NFS environment: all RPC services up, NFS managed by CTDB"
723
724         eventscript_call ctdb_service_managed
725         service "nfs" force-started  # might not be enough
726
727         export CTDB_MANAGED_SERVICES="foo nfs bar"
728
729         rpc_services_up "nfs" "mountd" "rquotad" "nlockmgr" "status"
730     else
731         debug "Setting up NFS environment: all RPC services down, NFS not managed by CTDB"
732
733         eventscript_call ctdb_service_unmanaged
734         service "nfs" force-stopped  # might not be enough
735         eventscript_call startstop_nfs stop
736
737         export CTDB_MANAGED_SERVICES="foo bar"
738         unset CTDB_MANAGES_NFS
739     fi
740 }
741
742 setup_nfs_ganesha ()
743 {
744     setup_nfs "$@"
745     export CTDB_NFS_SERVER_MODE="ganesha"
746     if [ "$1" != "down" ] ; then
747         export CTDB_MANAGES_NFS="yes"
748     fi
749
750     # We do not support testing the Ganesha-nfsd-specific part of the
751     # eventscript.
752     export CTDB_SKIP_GANESHA_NFSD_CHECK="yes"
753     export CTDB_NFS_SKIP_SHARE_CHECK="yes"
754 }
755
756 rpc_services_down ()
757 {
758     for _i ; do
759         debug "Marking RPC service \"${_i}\" as unavailable"
760         FAKE_RPCINFO_SERVICES=$(echo "$FAKE_RPCINFO_SERVICES" | sed -r -e "s@[[:space:]]*${_i}:[0-9]+:[0-9]+@@g")
761     done
762 }
763
764 rpc_services_up ()
765 {
766     for _i ; do
767         debug "Marking RPC service \"${_i}\" as available"
768         case "$_i" in
769             nfs)      _t="2:3" ;;
770             mountd)   _t="1:3" ;;
771             rquotad)  _t="1:2" ;;
772             nlockmgr) _t="3:4" ;;
773             status)   _t="1:1" ;;
774             *) die "Internal error - unsupported RPC service \"${_i}\"" ;;
775         esac
776
777         FAKE_RPCINFO_SERVICES="${FAKE_RPCINFO_SERVICES}${FAKE_RPCINFO_SERVICES:+ }${_i}:${_t}"
778     done
779 }
780
781 # Set the required result for a particular RPC program having failed
782 # for a certain number of iterations.  This is probably still a work
783 # in progress.  Note that we could hook aggressively
784 # nfs_check_rpc_service() to try to implement this but we're better
785 # off testing nfs_check_rpc_service() using independent code...  even
786 # if it is incomplete and hacky.  So, if the 60.nfs eventscript
787 # changes and the tests start to fail then it may be due to this
788 # function being incomplete.
789 rpc_set_service_failure_response ()
790 {
791     _progname="$1"
792     # The number of failures defaults to the iteration number.  This
793     # will be true when we fail from the 1st iteration... but we need
794     # the flexibility to set the number of failures.
795     _numfails="${2:-${iteration}}"
796
797     _etc="$CTDB_ETCDIR" # shortcut for readability
798     for _c in "$_etc/sysconfig/nfs" "$_etc/default/nfs" "$_etc/ctdb/sysconfig/nfs" ; do
799         if [ -r "$_c" ] ; then
800             . "$_c"
801             break
802         fi
803     done
804
805     # A handy newline.  :-)
806     _nl="
807 "
808
809     # Default
810     ok_null
811
812     _file=$(ls "${CTDB_BASE}/nfs-rpc-checks.d/"[0-9][0-9]."${_progname}.check")
813     [ -r "$_file" ] || die "RPC check file \"$_file\" does not exist or is not unique"
814
815     while read _op _li _actions ; do
816         # Skip comments
817         case "$_op" in
818             \#*) continue ;;
819         esac
820
821         _hit=false
822         if [ "$_op" != "%" ] ; then
823             if [ $_numfails $_op $_li ] ; then
824                 _hit=true
825             fi
826         else
827             if [ $(($_numfails $_op $_li)) -eq 0 ] ; then
828                 _hit=true
829             fi
830         fi
831         if $_hit ; then
832             _out=""
833             _rc=0
834             for _action in $_actions ; do
835                 case "$_action" in
836                     verbose)
837                         _ver=1
838                         _pn="$_progname"
839                         case "$_progname" in
840                             nfsd) _ver=3 ; _pn="nfs" ;;
841                             lockd) _ver=4 ; _pn="nlockmgr" ;;
842                             statd) _pn="status" ;;
843                         esac
844                         _out="\
845 ERROR: $_pn failed RPC check:
846 rpcinfo: RPC: Program not registered
847 program $_pn version $_ver is not available"
848                         ;;
849                     restart*)
850                         _p="rpc.${_progname}"
851                         case "$_action" in
852                             *:b) _bg="&" ;;
853                             *)   _bg=""  ;;
854                         esac
855                         case "$_progname" in
856                             nfsd)
857                                 _t="\
858 Trying to restart NFS service"
859
860                                 if [ -n "$CTDB_NFS_DUMP_STUCK_THREADS" ] ; then
861                                     for _pid in $FAKE_NFSD_THREAD_PIDS ; do
862                                         _t="\
863 $_t
864 ${_bg}Stack trace for nfsd[${_pid}]:
865 ${_bg}[<ffffffff87654321>] fake_stack_trace_for_pid_${_pid}/stack+0x0/0xff"
866                                     done
867                                 fi
868
869                                 _t="\
870 ${_t}
871 ${_bg}Starting nfslock: OK
872 ${_bg}Starting nfs: OK"
873                                 ;;
874                             lockd)
875                                 _t="\
876 Trying to restart lock manager service
877 ${_bg}Starting nfslock: OK"
878                                 ;;
879                             *)
880                                 _t="Trying to restart $_progname [${_p}]"
881                                 if [ -n "$CTDB_NFS_DUMP_STUCK_THREADS" ] ; then
882                                     for _pid in $FAKE_RPC_THREAD_PIDS ; do
883                                         _t="\
884 $_t
885 Stack trace for ${_p}[${_pid}]:
886 [<ffffffff87654321>] fake_stack_trace_for_pid_${_pid}/stack+0x0/0xff"
887                                     done
888                                 fi
889                         esac
890                         _out="${_out}${_out:+${_nl}}${_t}"
891                         ;;
892                     unhealthy)
893                         _rc=1
894                 esac
895             done
896             required_result $_rc "$_out"
897             return
898         fi
899     done <"$_file"
900 }
901
902 ######################################################################
903
904 # VSFTPD fakery
905
906 setup_vsftpd ()
907 {
908     service_name="vsftpd"
909
910     if [ "$1" != "down" ] ; then
911         die "setup_vsftpd up not implemented!!!"
912     else
913         debug "Setting up VSFTPD environment: service down, not managed by CTDB"
914
915         eventscript_call ctdb_service_unmanaged
916         service vsftpd force-stopped
917
918         export CTDB_MANAGED_SERVICES="foo"
919         unset CTDB_MANAGES_VSFTPD
920     fi
921 }
922
923 ######################################################################
924
925 # HTTPD fakery
926
927 setup_httpd ()
928 {
929     if [ "$1" != "down" ] ; then
930         die "setup_httpd up not implemented!!!"
931     else
932         debug "Setting up HTTPD environment: service down, not managed by CTDB"
933
934         for service_name in "apache2" "httpd" ; do
935             eventscript_call ctdb_service_unmanaged
936             service "$service_name" force-stopped
937         done
938
939         export CTDB_MANAGED_SERVICES="foo"
940         unset CTDB_MANAGES_HTTPD
941     fi
942 }
943
944 ######################################################################
945
946 # multipathd fakery
947
948 setup_multipathd ()
949 {
950     for i ; do
951         case "$i" in
952             \!*)
953                 _t="${i#!}"
954                 echo "Marking ${_t} as having no active paths"
955                 FAKE_MULTIPATH_FAILURES="${FAKE_MULTIPATH_FAILURES}${FAKE_MULTIPATH+FAILURES:+ }${_t}"
956                 ;;
957             *)
958                 _t="$i"         
959         esac
960         CTDB_MONITOR_MPDEVICES="${CTDB_MONITOR_MPDEVICES}${CTDB_MONITOR_MPDEVICES:+ }${_t}"
961     done
962
963     export CTDB_MONITOR_MPDEVICES FAKE_MULTIPATH_FAILURES
964     export FAKE_SLEEP_FORCE=0.1
965 }
966
967 ######################################################################
968
969 # Result and test functions
970
971 # Set some globals and print the summary.
972 define_test ()
973 {
974     desc="$1"
975
976     _f=$(basename "$0" ".sh")
977
978     # Remaining format should be NN.service.event.NNN or NN.service.NNN:
979     _num="${_f##*.}"
980     _f="${_f%.*}"
981     case "$_f" in
982         *.*.*)
983             script="${_f%.*}"
984             event="${_f##*.}"
985             ;;
986         *.*)
987             script="$_f"
988             unset event
989             ;;
990         *)
991             die "Internal error - unknown testcase filename format"
992     esac
993
994     printf "%-17s %-10s %-4s - %s\n\n" "$script" "$event" "$_num" "$desc"
995 }
996
997 _extra_header ()
998 {
999     cat <<EOF
1000 CTDB_BASE="$CTDB_BASE"
1001 CTDB_ETCDIR="$CTDB_ETCDIR"
1002 ctdb client is "$(which ctdb)"
1003 ip command is "$(which ip)"
1004 EOF
1005 }
1006
1007 # Run an eventscript once.  The test passes if the return code and
1008 # output match those required.
1009
1010 # Any args are passed to the eventscript.
1011
1012 simple_test ()
1013 {
1014     [ -n "$event" ] || die 'simple_test: $event not set'
1015
1016     _extra_header=$(_extra_header)
1017
1018     echo "Running eventscript \"$script $event${1:+ }$*\""
1019     _shell=""
1020     if $TEST_COMMAND_TRACE ; then
1021         _shell="sh -x"
1022     else
1023         _shell="sh"
1024     fi
1025     _out=$($_shell "${CTDB_BASE}/events.d/$script" "$event" "$@" 2>&1)
1026
1027     result_check "$_extra_header"
1028 }
1029
1030 simple_test_event ()
1031 {
1032     # If something has previously failed then don't continue.
1033     : ${_passed:=true}
1034     $_passed || return 1
1035
1036     event="$1" ; shift
1037     echo "=================================================="
1038     simple_test "$@"
1039 }
1040
1041 simple_test_command ()
1042 {
1043     # If something has previously failed then don't continue.
1044     : ${_passed:=true}
1045     $_passed || return 1
1046
1047     echo "=================================================="
1048     echo "Running command \"$*\""
1049     _out=$("$@" 2>&1)
1050
1051     result_check
1052 }
1053
1054 # Run an eventscript iteratively.
1055 # - 1st argument is the number of iterations.
1056 # - 2nd argument is something to eval to do setup for every iteration.
1057 #   The easiest thing to do here is to define a function and pass it
1058 #   here.
1059 # - Subsequent arguments come in pairs: an iteration number and
1060 #   something to eval for that iteration.  Each time an iteration
1061 #   number is matched the associated argument is given to eval after
1062 #   the default setup is done.  The iteration numbers need to be given
1063 #   in ascending order.
1064 #
1065 # Some optional args can be given *before* these, surrounded by extra
1066 # "--" args.  These args are passed to the eventscript.  Quoting is
1067 # lost.
1068 #
1069 # One use of the 2nd and further arguments is to call
1070 # required_result() to change what is expected of a particular
1071 # iteration.
1072 iterate_test ()
1073 {
1074     [ -n "$event" ] || die 'simple_test: $event not set'
1075
1076     args=""
1077     if [ "$1" = "--" ] ; then
1078         shift
1079         while [ "$1" != "--" ] ; do
1080             args="${args}${args:+ }$1"
1081             shift
1082         done
1083         shift
1084     fi
1085
1086     _repeats="$1"
1087     _setup_default="$2"
1088     shift 2
1089
1090     echo "Running $_repeats iterations of \"$script $event\" $args"
1091
1092     _result=true
1093
1094     for iteration in $(seq 1 $_repeats) ; do
1095         # This is inefficient because the iteration-specific setup
1096         # might completely replace the default one.  However, running
1097         # the default is good because it allows you to revert to a
1098         # particular result without needing to specify it explicitly.
1099         eval $_setup_default
1100         if [ $iteration = "$1" ] ; then
1101             eval $2
1102             shift 2
1103         fi
1104
1105         _shell=""
1106         if $TEST_COMMAND_TRACE ; then
1107             _shell="sh -x"
1108         else
1109             _shell="sh"
1110         fi
1111         _out=$($_shell "${CTDB_BASE}/events.d/$script" "$event" $args 2>&1)
1112         _rc=$?
1113
1114         _fout=$(echo "$_out" | result_filter)
1115
1116         if [ "$_fout" = "$required_output" -a $_rc = $required_rc ] ; then
1117             _passed=true
1118         else
1119             _passed=false
1120             _result=false
1121         fi
1122
1123         result_print "$_passed" "$_out" "$_rc" "Iteration $iteration"
1124     done
1125
1126     result_footer "$_result" "$(_extra_header)"
1127 }