tests/eventscripts: $CTDB_ETCDIR should be in $TEST_VAR_DIR
[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 if [ -d "${TEST_SUBDIR}/etc" ] ; then    
27     cp -a "${TEST_SUBDIR}/etc" "$EVENTSCRIPTS_TESTS_VAR_DIR"
28     export CTDB_ETCDIR="${EVENTSCRIPTS_TESTS_VAR_DIR}/etc"
29 else
30     die "Unable to setup \$CTDB_ETCDIR"
31 fi
32
33 if [ -d "${TEST_SUBDIR}/etc-ctdb" ] ; then
34     CTDB_BASE="${TEST_SUBDIR}/etc-ctdb"
35 else
36     die "Unable to set \$CTDB_BASE"
37 fi
38 export CTDB_BASE
39
40 if [ ! -d "${CTDB_BASE}/events.d" ] ; then
41     cat <<EOF
42 ERROR: Directory ${CTDB_BASE}/events.d does not exist.
43
44 That means that no eventscripts can be tested.
45
46 One possible explanation:
47
48   You have CTDB installed via RPMs (or similar), so the regular
49   CTDB_BASE directory is in /etc/ctdb/
50
51   BUT
52
53   You have done a regular "configure" and "make install" so the tests
54   are installed under /usr/local/.
55
56 If so, one possible hack to fix this is to create a symlink:
57
58   ln -s /etc/ctdb /usr/local/etc/ctdb
59
60 This is nasty but it works...  :-)
61 EOF
62     exit 1
63 fi
64
65 ######################################################################
66
67 if "$TEST_VERBOSE" ; then
68     debug () { echo "$@" ; }
69 else
70     debug () { : ; }
71 fi
72
73 eventscripts_tests_cleanup_hooks=""
74
75 # This loses quoting!
76 eventscripts_test_add_cleanup ()
77 {
78     eventscripts_tests_cleanup_hooks="${eventscripts_tests_cleanup_hooks}${eventscripts_tests_cleanup_hooks:+ ; }$*"
79 }
80
81 trap 'eval $eventscripts_tests_cleanup_hooks' 0
82
83
84 ######################################################################
85
86 # General setup fakery
87
88 setup_generic ()
89 {
90     debug "Setting up shares (3 existing shares)"
91     # Create 3 fake shares/exports.
92     export FAKE_SHARES=""
93     for i in $(seq 1 3) ; do
94         _s="${EVENTSCRIPTS_TESTS_VAR_DIR}/shares/${i}_existing"
95         mkdir -p "$_s"
96         FAKE_SHARES="${FAKE_SHARES}${FAKE_SHARES:+ }${_s}"
97     done
98
99     export FAKE_PROC_NET_BONDING="$EVENTSCRIPTS_TESTS_VAR_DIR/proc-net-bonding"
100     mkdir -p "$FAKE_PROC_NET_BONDING"
101     rm -f "$FAKE_PROC_NET_BONDING"/*
102
103     export FAKE_ETHTOOL_LINK_DOWN="$EVENTSCRIPTS_TESTS_VAR_DIR/ethtool-link-down"
104     mkdir -p "$FAKE_ETHTOOL_LINK_DOWN"
105     rm -f "$FAKE_ETHTOOL_LINK_DOWN"/*
106
107     # This can only have 2 levels.  We don't want to resort to usings
108     # something dangerous like "rm -r" setup time.
109     export FAKE_IP_STATE="$EVENTSCRIPTS_TESTS_VAR_DIR/fake-ip-state"
110     mkdir -p "$FAKE_IP_STATE"
111     rm -f "$FAKE_IP_STATE"/*/*
112     rm -f "$FAKE_IP_STATE"/* 2>/dev/null || true
113     rmdir "$FAKE_IP_STATE"/* 2>/dev/null || true
114 }
115
116 tcp_port_down ()
117 {
118     for _i ; do
119         debug "Marking TCP port \"${_i}\" as not listening"
120         FAKE_TCP_LISTEN=$(echo "$FAKE_TCP_LISTEN" | sed -r -e "s@[[:space:]]*[\.0-9]+:${_i}@@g")
121     done
122 }
123
124 shares_missing ()
125 {
126     _fmt="$1" ; shift
127
128     # Replace some shares with non-existent ones.
129     _t=""
130     _n=1
131     _nl="
132 "
133     export MISSING_SHARES_TEXT=""
134     for _i in $FAKE_SHARES ; do
135         if [ $_n = "$1" ] ; then
136             shift
137             _i="${_i%_existing}_missing"
138             debug "Replacing share $_n with missing share \"$_i\""
139             rmdir "$_i" 2>/dev/null || true
140             MISSING_SHARES_TEXT="${MISSING_SHARES_TEXT}${MISSING_SHARES_TEXT:+${_nl}}"$(printf "$_fmt" "${_i}")
141         fi
142         _t="${_t}${_t:+ }${_i}"
143         _n=$(($_n + 1))
144     done
145     FAKE_SHARES="$_t"
146 }
147
148 # Setup some fake /proc/net/bonding files with just enough info for
149 # the eventscripts.
150
151 # arg1 is interface name, arg2 is currently active slave (use "None"
152 # if none), arg3 is MII status ("up" or "down").
153 setup_bond ()
154 {
155     _iface="$1"
156     _slave="${2:-${_iface}_sl_0}"
157     _mii_s="${3:-up}"
158     _mii_subs="${4:-${_mii_s:-up}}"
159     echo "Setting $_iface to be a bond with active slave $_slave and MII status $_mii_s"
160     cat >"${FAKE_PROC_NET_BONDING}/$_iface" <<EOF
161 Bonding Mode: IEEE 802.3ad Dynamic link aggregation
162 Currently Active Slave: $_slave
163 # Status of the bond
164 MII Status: $_mii_s
165 # Status of 1st pretend adapter
166 MII Status: $_mii_subs
167 # Status of 2nd pretend adapter
168 MII Status: $_mii_subs
169 EOF
170 }
171
172 ethtool_interfaces_down ()
173 {
174     for _i ; do
175         echo "Marking interface $_i DOWN for ethtool"
176         touch "${FAKE_ETHTOOL_LINK_DOWN}/${_i}"
177     done
178 }
179
180 ethtool_interfaces_up ()
181 {
182     for _i ; do
183         echo "Marking interface $_i UP for ethtool"
184         rm -f "${FAKE_ETHTOOL_LINK_DOWN}/${_i}"
185     done
186 }
187
188 setup_nmap_output_filter ()
189 {
190     OUT_FILTER="-e 's@^(DEBUG: # Nmap 5.21 scan initiated) .+ (as:)@\1 DATE \2@' -e 's@^(DEBUG: # Nmap done at) .+ (--)@\1 DATE \2@'"
191 }
192
193 dump_routes ()
194 {
195     echo "# ip rule show"
196     ip rule show
197
198     ip rule show |
199     while read _p _x _i _x _t ; do
200         # Remove trailing colon after priority/preference.
201         _p="${_p%:}"
202         # Only remove rules that match our priority/preference.
203         [ "$CTDB_PER_IP_ROUTING_RULE_PREF" = "$_p" ] || continue
204
205         echo "# ip route show table $_t"
206         ip route show table "$_t"
207     done
208 }
209
210 # Copied from 13.per_ip_routing for now... so this is lazy testing  :-(
211 ipv4_host_addr_to_net ()
212 {
213     _host="$1"
214     _maskbits="$2"
215
216     # Convert the host address to an unsigned long by splitting out
217     # the octets and doing the math.
218     _host_ul=0
219     for _o in $(export IFS="." ; echo $_host) ; do
220         _host_ul=$(( ($_host_ul << 8) + $_o)) # work around Emacs color bug
221     done
222
223     # Calculate the mask and apply it.
224     _mask_ul=$(( 0xffffffff << (32 - $_maskbits) ))
225     _net_ul=$(( $_host_ul & $_mask_ul ))
226
227     # Now convert to a network address one byte at a time.
228     _net=""
229     for _o in $(seq 1 4) ; do
230         _net="$(($_net_ul & 255))${_net:+.}${_net}"
231         _net_ul=$(($_net_ul >> 8))
232     done
233
234     echo "${_net}/${_maskbits}"
235 }
236
237 ######################################################################
238
239 # CTDB fakery
240
241 # Evaluate an expression that probably calls functions or uses
242 # variables from the CTDB functions file.  This is used for test
243 # initialisation.
244 eventscript_call ()
245 {
246     (
247         . "$CTDB_BASE/functions"
248         "$@"
249     )
250 }
251
252 # Set output for ctdb command.  Option 1st argument is return code.
253 ctdb_set_output ()
254 {
255     _out="$EVENTSCRIPTS_TESTS_VAR_DIR/ctdb.out"
256     cat >"$_out"
257
258     _rc="$EVENTSCRIPTS_TESTS_VAR_DIR/ctdb.rc"
259     echo "${1:-0}" >"$_rc"
260
261     eventscripts_test_add_cleanup "rm -f $_out $_rc"
262 }
263
264 setup_ctdb ()
265 {
266     setup_generic
267
268     export FAKE_CTDB_NUMNODES="${1:-3}"
269     echo "Setting up CTDB with ${FAKE_CTDB_NUMNODES} fake nodes"
270
271     export FAKE_CTDB_PNN="${2:-0}"
272     echo "Setting up CTDB with PNN ${FAKE_CTDB_PNN}"
273
274     if [ -n "$3" ] ; then
275         echo "Setting up CTDB_PUBLIC_ADDRESSES: $3"
276         export CTDB_PUBLIC_ADDRESSES=$(mktemp)
277         for _i in $3 ; do
278             _ip="${_i%@*}"
279             _ifaces="${_i#*@}"
280             echo "${_ip} ${_ifaces}" >>"$CTDB_PUBLIC_ADDRESSES"
281         done
282         eventscripts_test_add_cleanup "rm -f $CTDB_PUBLIC_ADDRESSES"
283     fi
284
285     export FAKE_CTDB_STATE="$EVENTSCRIPTS_TESTS_VAR_DIR/fake-ctdb"
286
287     export FAKE_CTDB_IFACES_DOWN="$FAKE_CTDB_STATE/ifaces-down"
288     mkdir -p "$FAKE_CTDB_IFACES_DOWN"
289     rm -f "$FAKE_CTDB_IFACES_DOWN"/*
290
291     export FAKE_CTDB_SCRIPTSTATUS="$FAKE_CTDB_STATE/scriptstatus"
292     mkdir -p "$FAKE_CTDB_SCRIPTSTATUS"
293     rm -f "$FAKE_CTDB_SCRIPTSTATUS"/*
294 }
295
296 ctdb_get_interfaces ()
297 {
298     # The echo/subshell forces all the output onto 1 line.
299     echo $(ctdb ifaces -Y | awk -F: 'FNR > 1 {print $2}')
300 }
301
302 ctdb_get_1_interface ()
303 {
304     _t=$(ctdb_get_interfaces)
305     echo ${_t%% *}
306 }
307
308 # Print all public addresses as: interface IP maskbits
309 # Each line is suitable for passing to takeip/releaseip
310 ctdb_get_all_public_addresses ()
311 {
312     _f="${CTDB_PUBLIC_ADDRESSES:-${CTDB_BASE}/public_addresses}"
313     while IFS="/$IFS" read _ip _maskbits _ifaces ; do
314         echo "$_ifaces $_ip $_maskbits"
315     done <"$_f"
316 }
317
318 # Print public addresses on this node as: interface IP maskbits
319 # Each line is suitable for passing to takeip/releaseip
320 ctdb_get_my_public_addresses ()
321 {
322     ctdb ip -v -Y | {
323         read _x # skip header line
324
325         while IFS=":" read _x _ip _x _iface _x ; do
326             [ -n "$_iface" ] || continue
327             while IFS="/$IFS" read _i _maskbits _x ; do
328                 if [ "$_ip" = "$_i" ] ; then
329                     echo $_iface $_ip $_maskbits
330                     break
331                 fi
332             done <"${CTDB_PUBLIC_ADDRESSES:-${CTDB_BASE}/public_addresses}"
333         done
334     }
335 }
336
337 # Prints the 1st public address as: interface IP maskbits
338 # This is suitable for passing to takeip/releaseip
339 ctdb_get_1_public_address ()
340 {
341     ctdb_get_my_public_addresses | head -n 1
342 }
343
344 ctdb_not_implemented ()
345 {
346     export CTDB_NOT_IMPLEMENTED="$1"
347     ctdb_not_implemented="\
348 DEBUG: ctdb: command \"$1\" not implemented in stub"
349 }
350
351 ctdb_fake_scriptstatus ()
352 {
353     _code="$1"
354     _status="$2"
355     _err_out="$3"
356
357     _d1=$(date '+%s.%N')
358     _d2=$(date '+%s.%N')
359
360     echo "$_code $_status $_err_out" >"$FAKE_CTDB_SCRIPTSTATUS/$script"
361 }
362
363 setup_ctdb_policy_routing ()
364 {
365     export CTDB_PER_IP_ROUTING_CONF="$CTDB_BASE/policy_routing"
366     export CTDB_PER_IP_ROUTING_RULE_PREF=100
367     export CTDB_PER_IP_ROUTING_TABLE_ID_LOW=1000
368     export CTDB_PER_IP_ROUTING_TABLE_ID_HIGH=2000
369
370     # Tests need to create and populate this file
371     rm -f "$CTDB_PER_IP_ROUTING_CONF"
372 }
373
374 ######################################################################
375
376 # Samba fakery
377
378 setup_samba ()
379 {
380     setup_ctdb
381
382     if [ "$1" != "down" ] ; then
383
384         debug "Marking Samba services as up, listening and managed by CTDB"
385         # Get into known state.
386         for i in "samba" "winbind" ; do
387             eventscript_call ctdb_service_managed "$i"
388         done
389         # All possible service names for all known distros.
390         for i in "smb" "nmb" "winbind" "samba" ; do
391             service "$i" force-started
392         done
393
394         export CTDB_SAMBA_SKIP_SHARE_CHECK="no"
395         export CTDB_MANAGED_SERVICES="foo samba winbind bar"
396
397         export FAKE_TCP_LISTEN="0.0.0.0:445 0.0.0.0:139"
398         export FAKE_WBINFO_FAIL="no"
399
400         # Some things in 50.samba are backgrounded and waited for.  If
401         # we don't sleep at all then timeouts can happen.  This avoids
402         # that...  :-)
403         export FAKE_SLEEP_FORCE=0.1
404     else
405         debug "Marking Samba services as down, not listening and not managed by CTDB"
406         # Get into known state.
407         for i in "samba" "winbind" ; do
408             eventscript_call ctdb_service_unmanaged "$i"
409         done
410         # All possible service names for all known distros.
411         for i in "smb" "nmb" "winbind" "samba" ; do
412             service "$i" force-stopped
413         done
414
415         export CTDB_SAMBA_SKIP_SHARE_CHECK="no"
416         export CTDB_MANAGED_SERVICES="foo bar"
417         unset CTDB_MANAGES_SAMBA
418         unset CTDB_MANAGES_WINBIND
419
420         export FAKE_TCP_LISTEN=""
421         export FAKE_WBINFO_FAIL="yes"
422     fi
423
424     # This is ugly but if this file isn't removed before each test
425     # then configuration changes between tests don't stick.
426     rm -f "$CTDB_VARDIR/state/samba/smb.conf.cache"
427 }
428
429 wbinfo_down ()
430 {
431     debug "Making wbinfo commands fail"
432     FAKE_WBINFO_FAIL="yes"
433 }
434
435 ######################################################################
436
437 # NFS fakery
438
439 setup_nfs ()
440 {
441     setup_ctdb
442
443     export FAKE_RPCINFO_SERVICES=""
444
445     export CTDB_NFS_SKIP_SHARE_CHECK="no"
446     export CTDB_NFS_SKIP_KNFSD_ALIVE_CHECK="no"
447
448     # Reset the failcounts for nfs services.
449     eventscript_call eval rm -f '$ctdb_fail_dir/nfs_*'
450
451     rpc_fail_limits_file="${EVENTSCRIPTS_TESTS_VAR_DIR}/rpc_fail_limits"
452
453     # Force this file to exist so tests can be individually run.
454     if [ ! -f "$rpc_fail_limits_file" ] ; then
455         # This is gross... but is needed to fake through the nfs monitor event.
456         eventscript_call ctdb_service_managed "nfs"
457         service "nfs" force-started  # might not be enough
458         CTDB_RC_LOCAL="$CTDB_BASE/rc.local.nfs.monitor.get-limits" \
459             CTDB_MANAGES_NFS="yes" \
460             "${CTDB_BASE}/events.d/60.nfs" "monitor" >"$rpc_fail_limits_file"
461     fi
462     
463     if [ "$1" != "down" ] ; then
464         debug "Setting up NFS environment: all RPC services up, NFS managed by CTDB"
465
466         eventscript_call ctdb_service_managed "nfs"
467         service "nfs" force-started  # might not be enough
468
469         export CTDB_MANAGED_SERVICES="foo nfs bar"
470
471         rpc_services_up "nfs" "mountd" "rquotad" "nlockmgr" "status"
472     else
473         debug "Setting up NFS environment: all RPC services down, NFS not managed by CTDB"
474
475         eventscript_call ctdb_service_unmanaged "nfs"
476         service "nfs" force-stopped  # might not be enough
477         eventscript_call startstop_nfs stop
478
479         export CTDB_MANAGED_SERVICES="foo bar"
480         unset CTDB_MANAGES_NFS
481     fi
482 }
483
484 rpc_services_down ()
485 {
486     for _i ; do
487         debug "Marking RPC service \"${_i}\" as unavailable"
488         FAKE_RPCINFO_SERVICES=$(echo "$FAKE_RPCINFO_SERVICES" | sed -r -e "s@[[:space:]]*${_i}:[0-9]+:[0-9]+@@g")
489     done
490 }
491
492 rpc_services_up ()
493 {
494     for _i ; do
495         debug "Marking RPC service \"${_i}\" as available"
496         case "$_i" in
497             nfs)      _t="2:3" ;;
498             mountd)   _t="1:3" ;;
499             rquotad)  _t="1:2" ;;
500             nlockmgr) _t="3:4" ;;
501             status)   _t="1:1" ;;
502             *) die "Internal error - unsupported RPC service \"${_i}\"" ;;
503         esac
504
505         FAKE_RPCINFO_SERVICES="${FAKE_RPCINFO_SERVICES}${FAKE_RPCINFO_SERVICES:+ }${_i}:${_t}"
506     done
507 }
508
509 # Set the required result for a particular RPC program having failed
510 # for a certain number of iterations.  This is probably still a work
511 # in progress.  Note that we could hook aggressively
512 # nfs_check_rpc_service() to try to implement this but we're better
513 # off testing nfs_check_rpc_service() using independent code...  even
514 # if it is incomplete and hacky.  So, if the 60.nfs eventscript
515 # changes and the tests start to fail then it may be due to this
516 # function being incomplete.
517 rpc_set_service_failure_response ()
518 {
519     _progname="$1"
520     # The number of failures defaults to the iteration number.  This
521     # will be true when we fail from the 1st iteration... but we need
522     # the flexibility to set the number of failures.
523     _numfails="${2:-${iteration}}"
524
525     _etc="$CTDB_ETCDIR" # shortcut for readability
526     for _c in "$_etc/sysconfig/nfs" "$_etc/default/nfs" "$_etc/ctdb/sysconfig/nfs" ; do
527         if [ -r "$_c" ] ; then
528             . "$_c"
529             break
530         fi
531     done
532
533     # A handy newline.  :-)
534     _nl="
535 "
536
537     # Default
538     ok_null
539
540     _ts=$(sed -n -e "s@^${_progname} @@p" "$rpc_fail_limits_file")
541
542     while [ -n "$_ts" ] ; do
543         # Get the triple: operator, fail limit and actions.
544         _op="${_ts%% *}" ; _ts="${_ts#* }"
545         _li="${_ts%% *}" ; _ts="${_ts#* }"
546         # We've lost some of the quoting but we can simulate
547         # because we know an operator is always the first in a
548         # triple.
549         _actions=""
550         while [ -n "$_ts" ] ; do
551             # If this is an operator then we've got all of the
552             # actions.
553             case "$_ts" in
554                 -*) break ;;
555             esac
556
557             _actions="${_actions}${_actions:+ }${_ts%% *}"
558             # Special case for end of list.
559             if [ "$_ts" != "${_ts#* }" ] ; then
560                 _ts="${_ts#* }"
561             else
562                 _ts=""
563             fi
564         done
565
566         if [ "$_numfails" "$_op" "$_li" ] ; then
567             _out=""
568             _rc=0
569             for _action in $_actions ; do
570                 case "$_action" in
571                     verbose)
572                         _ver=1
573                         _pn="$_progname"
574                         case "$_progname" in
575                             knfsd) _ver=3 ; _pn="nfs" ;;
576                             lockd) _ver=4 ; _pn="nlockmgr" ;;
577                             statd) _pn="status" ;;
578                         esac
579                         _out="\
580 ERROR: $_pn failed RPC check:
581 rpcinfo: RPC: Program not registered
582 program $_pn version $_ver is not available"
583                         ;;
584                     restart|restart:*)
585                         _opts=""
586                         _p="rpc.${_progname}"
587                         case "$_progname" in
588                             statd)
589                                 _opts="${STATD_HOSTNAME:+ -n }${STATD_HOSTNAME}"
590                                 ;;
591                         esac
592                         case "${_progname}${_action#restart}" in
593                             knfsd)
594                                 _t="\
595 Trying to restart NFS service
596 Starting nfslock: OK
597 Starting nfs: OK"
598                                 ;;
599                             knfsd:bs)
600                                 _t="Trying to restart NFS service"
601                                 ;;
602                             lockd)
603                                 _t="\
604 Trying to restart lock manager service
605 Stopping nfslock: OK
606 Starting nfslock: OK"
607                                 ;;
608                             lockd:*)
609                                 _t="Trying to restart lock manager service"
610                                 ;;
611                             *)
612                                 _t="Trying to restart $_progname [${_p}${_opts}]"
613                         esac
614                         _out="${_out}${_out:+${_nl}}${_t}"
615                         ;;
616                     unhealthy)
617                         _rc=1
618                 esac
619             done
620             required_result $_rc "$_out"
621             return
622         fi
623     done
624 }
625
626 ######################################################################
627
628 # VSFTPD fakery
629
630 setup_vsftpd ()
631 {
632     if [ "$1" != "down" ] ; then
633         die "setup_vsftpd up not implemented!!!"
634     else
635         debug "Setting up VSFTPD environment: service down, not managed by CTDB"
636
637         eventscript_call ctdb_service_unmanaged vsftpd
638         service vsftpd force-stopped
639
640         export CTDB_MANAGED_SERVICES="foo"
641         unset CTDB_MANAGES_VSFTPD
642     fi
643 }
644
645 ######################################################################
646
647 # HTTPD fakery
648
649 setup_httpd ()
650 {
651     if [ "$1" != "down" ] ; then
652         die "setup_httpd up not implemented!!!"
653     else
654         debug "Setting up HTTPD environment: service down, not managed by CTDB"
655
656         for i in "apache2" "httpd" ; do
657             eventscript_call ctdb_service_unmanaged "$i"
658             service "$i" force-stopped
659         done
660
661         export CTDB_MANAGED_SERVICES="foo"
662         unset CTDB_MANAGES_HTTPD
663     fi
664 }
665
666 ######################################################################
667
668 # Result and test functions
669
670 # Set some globals and print the summary.
671 define_test ()
672 {
673     desc="$1"
674
675     _f=$(basename "$0" ".sh")
676
677     # Remaining format should be NN.service.event.NNN or NN.service.NNN:
678     _num="${_f##*.}"
679     _f="${_f%.*}"
680     case "$_f" in
681         *.*.*)
682             script="${_f%.*}"
683             event="${_f##*.}"
684             ;;
685         *.*)
686             script="$_f"
687             unset event
688             ;;
689         *)
690             die "Internal error - unknown testcase filename format"
691     esac
692
693     printf "%-17s %-10s %-4s - %s\n\n" "$script" "$event" "$_num" "$desc"
694 }
695
696 _extra_header ()
697 {
698     cat <<EOF
699 CTDB_BASE="$CTDB_BASE"
700 CTDB_ETCDIR="$CTDB_ETCDIR"
701 ctdb client is "$(which ctdb)"
702 EOF
703 }
704
705 # Run an eventscript once.  The test passes if the return code and
706 # output match those required.
707
708 # Any args are passed to the eventscript.
709
710 # Eventscript tracing can be done by setting:
711 #   EVENTSCRIPTS_TESTS_TRACE="sh -x"
712
713 # or similar.  This will almost certainly make a test fail but is
714 # useful for debugging.
715 simple_test ()
716 {
717     [ -n "$event" ] || die 'simple_test: $event not set'
718
719     _extra_header=$(_extra_header)
720
721     echo "Running eventscript \"$script $event${1:+ }$*\""
722     _trace=""
723     if $TEST_COMMAND_TRACE ; then
724         _trace="sh -x"
725     fi
726     _out=$($_trace "${CTDB_BASE}/events.d/$script" "$event" "$@" 2>&1)
727
728     result_check "$_extra_header"
729 }
730
731 simple_test_event ()
732 {
733     # If something has previously failed then don't continue.
734     : ${_passed:=true}
735     $_passed || return 1
736
737     event="$1" ; shift
738     echo "##################################################"
739     simple_test "$@"
740 }
741
742 simple_test_command ()
743 {
744     # If something has previously failed then don't continue.
745     : ${_passed:=true}
746     $_passed || return 1
747
748     echo "##################################################"
749     echo "Running command \"$*\""
750     _out=$("$@" 2>&1)
751
752     result_check
753 }
754
755 # Run an eventscript iteratively.
756 # - 1st argument is the number of iterations.
757 # - 2nd argument is something to eval to do setup for every iteration.
758 #   The easiest thing to do here is to define a function and pass it
759 #   here.
760 # - Subsequent arguments come in pairs: an iteration number and
761 #   something to eval for that iteration.  Each time an iteration
762 #   number is matched the associated argument is given to eval after
763 #   the default setup is done.  The iteration numbers need to be given
764 #   in ascending order.
765 #
766 # Some optional args can be given *before* these, surrounded by extra
767 # "--" args.  These args are passed to the eventscript.  Quoting is
768 # lost.
769 #
770 # One use of the 2nd and further arguments is to call
771 # required_result() to change what is expected of a particular
772 # iteration.
773 iterate_test ()
774 {
775     [ -n "$event" ] || die 'simple_test: $event not set'
776
777     args=""
778     if [ "$1" = "--" ] ; then
779         shift
780         while [ "$1" != "--" ] ; do
781             args="${args}${args:+ }$1"
782             shift
783         done
784         shift
785     fi
786
787     _repeats="$1"
788     _setup_default="$2"
789     shift 2
790
791     echo "Running $_repeats iterations of \"$script $event\" $args"
792
793     _result=true
794
795     for iteration in $(seq 1 $_repeats) ; do
796         # This is inefficient because the iteration-specific setup
797         # might completely replace the default one.  However, running
798         # the default is good because it allows you to revert to a
799         # particular result without needing to specify it explicitly.
800         eval $_setup_default
801         if [ $iteration = "$1" ] ; then
802             eval $2
803             shift 2
804         fi
805
806         _out=$($EVENTSCRIPTS_TESTS_TRACE "${CTDB_BASE}/events.d/$script" "$event" $args 2>&1)
807         _rc=$?
808
809     if [ -n "$OUT_FILTER" ] ; then
810         _fout=$(echo "$_out" | eval sed -r $OUT_FILTER)
811     else
812         _fout="$_out"
813     fi
814
815         if [ "$_fout" = "$required_output" -a $_rc = $required_rc ] ; then
816             _passed=true
817         else
818             _passed=false
819             _result=false
820         fi
821
822         result_print "$_passed" "$_out" "$_rc" "Iteration $iteration"
823     done
824
825     result_footer "$_result" "$(_extra_header)"
826 }