Test suite: Fix bug in node_has_status().
[sahlberg/ctdb.git] / tests / scripts / ctdb_test_functions.bash
1 # Hey Emacs, this is a -*- shell-script -*- !!!  :-)
2
3 fail ()
4 {
5     echo "$*"
6     exit 1
7 }
8
9 ######################################################################
10
11 ctdb_test_begin ()
12 {
13     local name="$1"
14
15     teststarttime=$(date '+%s')
16     testduration=0
17
18     echo "--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--"
19     echo "Running test $name ($(date '+%T'))"
20     echo "--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--"
21 }
22
23 ctdb_test_end ()
24 {
25     local name="$1" ; shift
26     local status="$1" ; shift
27     # "$@" is command-line
28
29     local interp="SKIPPED"
30     local statstr=" (reason $*)"
31     if [ -n "$status" ] ; then
32         if [ $status -eq 0 ] ; then
33             interp="PASSED"
34             statstr=""
35             echo "ALL OK: $*"
36         else
37             interp="FAILED"
38             statstr=" (status $status)"
39             testfailures=$(($testfailures+1))
40         fi
41     fi
42
43     testduration=$(($(date +%s)-$teststarttime))
44
45     echo "=========================================================================="
46     echo "TEST ${interp}: ${name}${statstr} (duration: ${testduration}s)"
47     echo "=========================================================================="
48
49 }
50
51 test_exit ()
52 {
53     exit $(($testfailures+0))
54 }
55
56 ctdb_test_exit ()
57 {
58     local status=$?
59
60     trap - 0
61
62     [ $(($testfailures+0)) -eq 0 -a $status -ne 0 ] && testfailures=$status
63     status=$(($testfailures+0))
64
65     # Avoid making a test fail from this point onwards.  The test is
66     # now complete.
67     set +e
68
69     echo "*** TEST COMPLETE (RC=$status), CLEANING UP..."
70
71     eval "$ctdb_test_exit_hook" || true
72     unset ctdb_test_exit_hook
73
74     if $ctdb_test_restart_scheduled || ! cluster_is_healthy ; then
75
76         restart_ctdb
77     else
78         # This could be made unconditional but then we might get
79         # duplication from the recovery in restart_ctdb.  We want to
80         # leave the recovery in restart_ctdb so that future tests that
81         # might do a manual restart mid-test will benefit.
82         echo "Forcing a recovery..."
83         onnode 0 ctdb recover
84     fi
85
86     exit $status
87 }
88
89 ctdb_test_exit_hook_add ()
90 {
91     ctdb_test_exit_hook="${ctdb_test_exit_hook}${ctdb_test_exit_hook:+ ; }$*"
92 }
93
94 ctdb_test_run ()
95 {
96     local name="$1" ; shift
97     
98     [ -n "$1" ] || set -- "$name"
99
100     ctdb_test_begin "$name"
101
102     local status=0
103     "$@" || status=$?
104
105     ctdb_test_end "$name" "$status" "$*"
106     
107     return $status
108 }
109
110 ctdb_test_usage()
111 {
112     local status=${1:-2}
113     
114     cat <<EOF
115 Usage: $0 [option]
116
117 Options:        
118     -h, --help          show this screen.
119     -v, --version       show test case version.
120     --category          show the test category (ACL, CTDB, Samba ...).
121     -d, --description   show test case description.
122     --summary           show short test case summary.
123     -x                  trace test using set -x
124 EOF
125
126     exit $status
127 }
128
129 ctdb_test_version ()
130 {
131     [ -n "$CTDB_DIR" ] || fail "Can not determine version."
132
133     (cd "$CTDB_DIR" && git describe)
134 }
135
136 ctdb_test_cmd_options()
137 {
138     [ -n "$1" ] || return 0
139
140     case "$1" in
141         -h|--help)        ctdb_test_usage 0   ;;
142         -v|--version)     ctdb_test_version   ;;
143         --category)       echo "CTDB"         ;; 
144         -d|--description) test_info           ;;
145         -x)               set -x ; return 0   ;;
146         *)
147             echo "Error: Unknown parameter = $1"
148             echo
149             ctdb_test_usage 2
150             ;;
151     esac
152
153     exit 0
154 }
155
156 ctdb_test_init () 
157 {
158     scriptname=$(basename "$0")
159     testfailures=0
160     ctdb_test_restart_scheduled=false
161
162     ctdb_test_cmd_options $@
163
164     trap "ctdb_test_exit" 0
165 }
166
167 ctdb_test_check_real_cluster ()
168 {
169     [ -n "$CTDB_TEST_REAL_CLUSTER" ] && return 0
170
171     echo "ERROR: This test must be run on a real/virtual cluster, not local daemons."
172     return 1
173 }
174
175 ########################################
176
177 # Sets: $out
178 try_command_on_node ()
179 {
180     local nodespec="$1" ; shift
181
182     local verbose=false
183     local onnode_opts=""
184
185     while [ "${nodespec#-}" != "$nodespec" ] ; do
186         if [ "$nodespec" = "-v" ] ; then
187             verbose=true
188         else
189             onnode_opts="$nodespec"
190         fi
191         nodespec="$1" ; shift
192     done
193
194     local cmd="$*"
195
196     out=$(onnode -q $onnode_opts "$nodespec" "$cmd" 2>&1) || {
197
198         echo "Failed to execute \"$cmd\" on node(s) \"$nodespec\""
199         echo "$out"
200         return 1
201     }
202
203     if $verbose ; then
204         echo "Output of \"$cmd\":"
205         echo "$out"
206     fi
207 }
208
209 sanity_check_output ()
210 {
211     local min_lines="$1"
212     local regexp="$2" # Should be anchored as necessary.
213     local output="$3"
214
215     local ret=0
216
217     local num_lines=$(echo "$output" | wc -l)
218     echo "There are $num_lines lines of output"
219     if [ $num_lines -lt $min_lines ] ; then
220         echo "BAD: that's less than the required number (${min_lines})"
221         ret=1
222     fi
223
224     local status=0
225     local unexpected # local doesn't pass through status of command on RHS.
226     unexpected=$(echo "$output" | egrep -v "$regexp") || status=$?
227
228     # Note that this is reversed.
229     if [ $status -eq 0 ] ; then
230         echo "BAD: unexpected lines in output:"
231         echo "$unexpected" | cat -A
232         ret=1
233     else
234         echo "Output lines look OK"
235     fi
236
237     return $ret
238 }
239
240 sanity_check_ips ()
241 {
242     local ips="$1" # Output of "ctdb ip -n all"
243
244     echo "Sanity checking IPs..."
245
246     local x ipp prev
247     prev=""
248     while read x ipp ; do
249         [ "$ipp" = "-1" ] && break
250         if [ -n "$prev" -a "$ipp" != "$prev" ] ; then
251             echo "OK"
252             return 0
253         fi
254         prev="$ipp"
255     done <<<"$ips"
256
257     echo "BAD: a node was -1 or IPs are only assigned to one node"
258     echo "Are you running an old version of CTDB?"
259     return 1
260 }
261
262 select_test_node_and_ips ()
263 {
264     try_command_on_node 0 "$CTDB ip -n all | sed -e '1d'"
265
266     # When selecting test_node we just want a node that has public
267     # IPs.  This will work and is economically semi-random.  :-)
268     local x
269     read x test_node <<<"$out"
270
271     test_node_ips=""
272     local ip pnn
273     while read ip pnn ; do
274         if [ "$pnn" = "$test_node" ] ; then
275             test_node_ips="${test_node_ips}${test_node_ips:+ }${ip}"
276         fi
277     done <<<"$out" # bashism to avoid problem setting variable in pipeline.
278
279     echo "Selected node ${test_node} with IPs: ${test_node_ips}."
280     test_ip="${test_node_ips%% *}"
281 }
282
283 #######################################
284
285 # Wait until either timeout expires or command succeeds.  The command
286 # will be tried once per second.
287 wait_until ()
288 {
289     local timeout="$1" ; shift # "$@" is the command...
290
291     echo -n "<${timeout}|"
292     local t=$timeout
293     while [ $t -gt 0 ] ; do
294         if "$@" ; then
295             echo "|$(($timeout - $t))|"
296             echo "OK"
297             return 0
298         fi
299         echo -n .
300         t=$(($t - 1))
301         sleep 1
302     done
303     
304     echo "*TIMEOUT*"
305     
306     return 1
307 }
308
309 sleep_for ()
310 {
311     echo -n "=${1}|"
312     for i in $(seq 1 $1) ; do
313         echo -n '.'
314         sleep 1
315     done
316     echo '|'
317 }
318
319 _cluster_is_healthy ()
320 {
321     local out x count line
322
323     out=$(ctdb -Y status 2>&1) || return 1
324
325     {
326         read x
327         count=0
328         while read line ; do
329             count=$(($count + 1))
330             [ "${line##:*:*:*1:}" != "$line" ] && return 1
331         done
332         [ $count -gt 0 ] && return $?
333     } <<<"$out" # Yay bash!
334 }
335
336 cluster_is_healthy ()
337 {
338     if onnode 0 $CTDB_TEST_WRAPPER _cluster_is_healthy ; then
339         echo "Cluster is HEALTHY"
340         return 0
341     else
342         echo "Cluster is UNHEALTHY"
343         if ! ${ctdb_test_restart_scheduled:-false} ; then
344             echo "DEBUG:"
345             local i
346             for i in "onnode -q 0 ctdb status" "onnode -q 0 onnode all ctdb scriptstatus" ; do
347                 echo "$i"
348                 $i || true
349             done
350         fi
351         return 1
352     fi
353 }
354
355 wait_until_healthy ()
356 {
357     local timeout="${1:-120}"
358
359     echo "Waiting for cluster to become healthy..."
360
361     wait_until 120 _cluster_is_healthy
362 }
363
364 # This function is becoming nicely overloaded.  Soon it will collapse!  :-)
365 node_has_status ()
366 {
367     local pnn="$1"
368     local status="$2"
369
370     local bits fpat mpat
371     case "$status" in
372         (unhealthy)    bits="?:?:?:1:*" ;;
373         (healthy)      bits="?:?:?:0:*" ;;
374         (disconnected) bits="1:*" ;;
375         (connected)    bits="0:*" ;;
376         (banned)       bits="?:1:*" ;;
377         (unbanned)     bits="?:0:*" ;;
378         (disabled)     bits="?:?:1:*" ;;
379         (enabled)      bits="?:?:0:*" ;;
380         (stopped)      bits="?:?:?:?:1:*" ;;
381         (notstopped)   bits="?:?:?:?:0:*" ;;
382         (frozen)       fpat='^[[:space:]]+frozen[[:space:]]+1$' ;;
383         (unfrozen)     fpat='^[[:space:]]+frozen[[:space:]]+0$' ;;
384         (monon)        mpat='^Monitoring mode:ACTIVE \(0\)$' ;;
385         (monoff)       mpat='^Monitoring mode:DISABLED \(1\)$' ;;
386         *)
387             echo "node_has_status: unknown status \"$status\""
388             return 1
389     esac
390
391     if [ -n "$bits" ] ; then
392         local out x line
393
394         out=$(ctdb -Y status 2>&1) || return 1
395
396         {
397             read x
398             while read line ; do
399                 # This needs to be done in 2 steps to avoid false matches.
400                 local line_bits="${line#:${pnn}:*:}"
401                 [ "$line_bits" = "$line" ] && continue
402                 [ "${line_bits#${bits}}" != "$line_bits" ] && return 0
403             done
404             return 1
405         } <<<"$out" # Yay bash!
406     elif [ -n "$fpat" ] ; then
407         ctdb statistics -n "$pnn" | egrep -q "$fpat"
408     elif [ -n "$mpat" ] ; then
409         ctdb getmonmode -n "$pnn" | egrep -q "$mpat"
410     else
411         echo 'node_has_status: unknown mode, neither $bits nor $fpat is set'
412         return 1
413     fi
414 }
415
416 wait_until_node_has_status ()
417 {
418     local pnn="$1"
419     local status="$2"
420     local timeout="${3:-30}"
421
422     echo "Waiting until node $pnn has status \"$status\"..."
423
424     if ! onnode any $CTDB_TEST_WRAPPER wait_until $timeout node_has_status "$pnn" "$status" ; then
425         for i in "onnode -q any ctdb status" "onnode -q any onnode all ctdb scriptstatus" ; do
426             echo "$i"
427             $i || true
428         done
429
430         return 1
431     fi
432
433 }
434
435 # Useful for superficially testing IP failover.
436 # IPs must be on nodes matching nodeglob.
437 ips_are_on_nodeglob ()
438 {
439     local nodeglob="$1" ; shift
440     local ips="$*"
441
442     local out
443
444     try_command_on_node 1 ctdb ip -n all
445
446     while read ip pnn ; do
447         for check in $ips ; do
448             if [ "$check" = "$ip" ] ; then
449                 case "$pnn" in
450                     ($nodeglob) : ;;
451                     (*) return 1  ;;
452                 esac
453                 ips="${ips/${ip}}" # Remove from list
454             fi
455         done
456     done <<<"$out" # bashism to avoid problem setting variable in pipeline.
457
458     ips="${ips// }" # Remove any spaces.
459     [ -z "$ips" ]
460 }
461
462 wait_until_ips_are_on_nodeglob ()
463 {
464     echo "Waiting for IPs to fail over..."
465
466     wait_until 60 ips_are_on_nodeglob "$@"
467 }
468
469 get_src_socket ()
470 {
471     local proto="$1"
472     local dst_socket="$2"
473     local pid="$3"
474     local prog="$4"
475
476     local pat="^${proto}[[:space:]]+[[:digit:]]+[[:space:]]+[[:digit:]]+[[:space:]]+[^[:space:]]+[[:space:]]+${dst_socket//./\\.}[[:space:]]+ESTABLISHED[[:space:]]+${pid}/${prog}[[:space:]]*\$"
477     out=$(netstat -tanp |
478         egrep "$pat" |
479         awk '{ print $4 }')
480
481     [ -n "$out" ]
482 }
483
484 wait_until_get_src_socket ()
485 {
486     local proto="$1"
487     local dst_socket="$2"
488     local pid="$3"
489     local prog="$4"
490
491     echo "Waiting for ${prog} to establish connection to ${dst_socket}..."
492
493     wait_until 5 get_src_socket "$@"
494 }
495
496 #######################################
497
498 # filename will be in $tcpdump_filename, pid in $tcpdump_pid
499 tcpdump_start ()
500 {
501     tcpdump_filter="$1" # global
502
503     echo "Running tcpdump..."
504     tcpdump_filename=$(mktemp)
505     ctdb_test_exit_hook_add "rm -f $tcpdump_filename"
506
507     # The only way of being sure that tcpdump is listening is to send
508     # some packets that it will see.  So we use dummy pings - the -U
509     # option to tcpdump ensures that packets are flushed to the file
510     # as they are captured.
511     local dummy_addr="127.3.2.1"
512     local dummy="icmp and dst host ${dummy_addr} and icmp[icmptype] == icmp-echo"
513     tcpdump -n -p -s 0 -e -U -w $tcpdump_filename -i any "($tcpdump_filter) or ($dummy)" &
514     ctdb_test_exit_hook_add "kill $! >/dev/null 2>&1"
515
516     echo "Waiting for tcpdump output file to be ready..."
517     ping -q "$dummy_addr" >/dev/null 2>&1 &
518     ctdb_test_exit_hook_add "kill $! >/dev/null 2>&1"
519
520     tcpdump_listen_for_dummy ()
521     {
522         tcpdump -n -r $tcpdump_filename -c 1 "$dummy" >/dev/null 2>&1
523     }
524
525     wait_until 10 tcpdump_listen_for_dummy
526 }
527
528 # By default, wait for 1 matching packet.
529 tcpdump_wait ()
530 {
531     local count="${1:-1}"
532     local filter="${2:-${tcpdump_filter}}"
533
534     tcpdump_check ()
535     {
536         local found=$(tcpdump -n -r $tcpdump_filename "$filter" 2>/dev/null | wc -l)
537         [ $found -ge $count ]
538     }
539
540     echo "Waiting for tcpdump to capture some packets..."
541     if ! wait_until 30 tcpdump_check ; then
542         echo "DEBUG:"
543         local i
544         for i in "onnode -q 0 ctdb status" "netstat -tanp" "tcpdump -n -e -r $tcpdump_filename" ; do
545             echo "$i"
546             $i || true
547         done
548         return 1
549     fi
550 }
551
552 tcpdump_show ()
553 {
554     local filter="${1:-${tcpdump_filter}}"
555
556     tcpdump -n -r $tcpdump_filename  "$filter" 2>/dev/null
557 }
558
559 tcptickle_sniff_start ()
560 {
561     local src="$1"
562     local dst="$2"
563
564     local in="src host ${dst%:*} and tcp src port ${dst##*:} and dst host ${src%:*} and tcp dst port ${src##*:}"
565     local out="src host ${src%:*} and tcp src port ${src##*:} and dst host ${dst%:*} and tcp dst port ${dst##*:}"
566     local tickle_ack="${in} and (tcp[tcpflags] & tcp-ack != 0) and (tcp[14] == 4) and (tcp[15] == 210)" # win == 1234
567     local ack_ack="${out} and (tcp[tcpflags] & tcp-ack != 0)"
568     tcptickle_reset="${in} and tcp[tcpflags] & tcp-rst != 0"
569     local filter="(${tickle_ack}) or (${ack_ack}) or (${tcptickle_reset})"
570
571     tcpdump_start "$filter"
572 }
573
574 tcptickle_sniff_wait_show ()
575 {
576     tcpdump_wait 1 "$tcptickle_reset"
577
578     echo "GOOD: here are some TCP tickle packets:"
579     tcpdump_show
580 }
581
582 gratarp_sniff_start ()
583 {
584     tcpdump_start "arp host ${test_ip}"
585 }
586
587 gratarp_sniff_wait_show ()
588 {
589     tcpdump_wait 2
590
591     echo "GOOD: this should be the some gratuitous ARPs:"
592     tcpdump_show
593 }
594
595
596 #######################################
597
598 daemons_stop ()
599 {
600     echo "Attempting to politely shutdown daemons..."
601     onnode 1 ctdb shutdown -n all || true
602
603     echo "Sleeping for a while..."
604     sleep_for 1
605
606     if pgrep -f $CTDB_DIR/bin/ctdbd >/dev/null ; then
607         echo "Killing remaining daemons..."
608         pkill -f $CTDB_DIR/bin/ctdbd
609
610         if pgrep -f $CTDB_DIR/bin/ctdbd >/dev/null ; then
611             echo "Once more with feeling.."
612             pkill -9 $CTDB_DIR/bin/ctdbd
613         fi
614     fi
615
616     local var_dir=$CTDB_DIR/tests/var
617     rm -rf $var_dir/test.db
618 }
619
620 daemons_setup ()
621 {
622     local num_nodes="${CTDB_TEST_NUM_DAEMONS:-2}" # default is 2 nodes
623
624     local var_dir=$CTDB_DIR/tests/var
625
626     mkdir -p $var_dir/test.db/persistent
627
628     local nodes=$var_dir/nodes.txt
629     local public_addresses=$var_dir/public_addresses.txt
630     local no_public_addresses=$var_dir/no_public_addresses.txt
631     rm -f $nodes $public_addresses $no_public_addresses
632
633     # If there are (strictly) greater than 2 nodes then we'll randomly
634     # choose a node to have no public addresses.
635     local no_public_ips=-1
636     [ $num_nodes -gt 2 ] && no_public_ips=$(($RANDOM % $num_nodes))
637     echo "$no_public_ips" >$no_public_addresses
638
639     local i
640     for i in $(seq 1 $num_nodes) ; do
641         if [ "${CTDB_USE_IPV6}x" != "x" ]; then
642             echo ::$i >> $nodes
643             ip addr add ::$i/128 dev lo
644         else
645             echo 127.0.0.$i >> $nodes
646             # 2 public addresses on most nodes, just to make things interesting.
647             if [ $(($i - 1)) -ne $no_public_ips ] ; then
648                 echo "192.0.2.$i/24 lo" >> $public_addresses
649                 echo "192.0.2.$(($i + $num_nodes))/24 lo" >> $public_addresses
650             fi
651         fi
652     done
653 }
654
655 daemons_start_1 ()
656 {
657     local pnn="$1"
658     shift # "$@" gets passed to ctdbd
659
660     local var_dir=$CTDB_DIR/tests/var
661
662     local nodes=$var_dir/nodes.txt
663     local public_addresses=$var_dir/public_addresses.txt
664     local no_public_addresses=$var_dir/no_public_addresses.txt
665
666     local no_public_ips=-1
667     [ -r $no_public_addresses ] && read no_public_ips <$no_public_addresses
668
669     if  [ "$no_public_ips" = $pnn ] ; then
670         echo "Node $no_public_ips will have no public IPs."
671     fi
672
673     local ctdb_options="--reclock=$var_dir/rec.lock --nlist $nodes --nopublicipcheck --event-script-dir=$CTDB_DIR/tests/events.d --logfile=$var_dir/daemons.log -d 0 --dbdir=$var_dir/test.db --dbdir-persistent=$var_dir/test.db/persistent"
674
675     if [ $(id -u) -eq 0 ]; then
676         ctdb_options="$ctdb_options --public-interface=lo"
677     fi
678
679     if [ $pnn -eq $no_public_ips ] ; then
680         ctdb_options="$ctdb_options --public-addresses=/dev/null"
681     else
682         ctdb_options="$ctdb_options --public-addresses=$public_addresses"
683     fi
684
685     # Need full path so we can use "pkill -f" to kill the daemons.
686     $VALGRIND $CTDB_DIR/bin/ctdbd --socket=$var_dir/sock.$pnn $ctdb_options "$@" ||return 1
687 }
688
689 daemons_start ()
690 {
691     # "$@" gets passed to ctdbd
692
693     local num_nodes="${CTDB_TEST_NUM_DAEMONS:-2}" # default is 2 nodes
694
695     echo "Starting $num_nodes ctdb daemons..."
696
697     for i in $(seq 0 $(($num_nodes - 1))) ; do
698         daemons_start_1 $i "$@"
699     done
700
701     local var_dir=$CTDB_DIR/tests/var
702
703     if [ -L /tmp/ctdb.socket -o ! -S /tmp/ctdb.socket ] ; then 
704         ln -sf $var_dir/sock.0 /tmp/ctdb.socket || return 1
705     fi
706 }
707
708 #######################################
709
710 _ctdb_hack_options ()
711 {
712     local ctdb_options="$*"
713
714     # We really just want to pass CTDB_OPTIONS but on RH
715     # /etc/sysconfig/ctdb can, and frequently does, set that variable.
716     # So instead, we hack badly.  We'll add these as we use them.
717     # Note that these may still be overridden by the above file... but
718     # we tend to use the exotic options here... so that is unlikely.
719
720     case "$ctdb_options" in
721         *--start-as-stopped*)
722             export CTDB_START_AS_STOPPED="yes"
723     esac
724 }
725
726 _restart_ctdb ()
727 {
728     _ctdb_hack_options "$@"
729
730     if [ -e /etc/redhat-release ] ; then
731         service ctdb restart
732     else
733         /etc/init.d/ctdb restart
734     fi
735 }
736
737 _ctdb_start ()
738 {
739     _ctdb_hack_options "$@"
740
741     /etc/init.d/ctdb start
742 }
743
744 setup_ctdb ()
745 {
746     if [ -n "$CTDB_NODES_SOCKETS" ] ; then
747         daemons_setup
748     fi
749 }
750
751 # Common things to do after starting one or more nodes.
752 _ctdb_start_post ()
753 {
754     onnode -q 1  $CTDB_TEST_WRAPPER wait_until_healthy || return 1
755
756     echo "Setting RerecoveryTimeout to 1"
757     onnode -pq all "ctdb setvar RerecoveryTimeout 1"
758
759     # In recent versions of CTDB, forcing a recovery like this blocks
760     # until the recovery is complete.  Hopefully this will help the
761     # cluster to stabilise before a subsequent test.
762     echo "Forcing a recovery..."
763     onnode -q 0 ctdb recover
764     sleep_for 1
765     echo "Forcing a recovery..."
766     onnode -q 0 ctdb recover
767
768     echo "ctdb is ready"
769 }
770
771 # This assumes that ctdbd is not running on the given node.
772 ctdb_start_1 ()
773 {
774     local pnn="$1"
775     shift # "$@" is passed to ctdbd start.
776
777     echo -n "Starting CTDB on node ${pnn}..."
778     
779     if [ -n "$CTDB_NODES_SOCKETS" ] ; then
780         daemons_start_1 $pnn "$@"
781     else
782         onnode $pnn $CTDB_TEST_WRAPPER _ctdb_start "$@"
783     fi
784
785     # If we're starting only 1 node then we're doing something weird.
786     ctdb_restart_when_done
787 }
788
789 restart_ctdb ()
790 {
791     # "$@" is passed to ctdbd start.
792
793     echo -n "Restarting CTDB"
794     if $ctdb_test_restart_scheduled ; then
795         echo -n " (scheduled)"
796     fi
797     echo "..."
798     
799     local i=0
800     while : ; do
801         if [ -n "$CTDB_NODES_SOCKETS" ] ; then
802             daemons_stop
803             daemons_start "$@"
804         else
805             onnode -p all $CTDB_TEST_WRAPPER _restart_ctdb "$@"
806         fi && break
807
808         i=$(($i + 1))
809         [ $i -lt 5 ] || break
810
811         echo "That didn't seem to work - sleeping for a while..."
812         sleep_for 5
813     done
814         
815     onnode -q 1  $CTDB_TEST_WRAPPER wait_until_healthy || return 1
816
817     echo "Setting RerecoveryTimeout to 1"
818     onnode -pq all "ctdb setvar RerecoveryTimeout 1"
819
820     # In recent versions of CTDB, forcing a recovery like this blocks
821     # until the recovery is complete.  Hopefully this will help the
822     # cluster to stabilise before a subsequent test.
823     echo "Forcing a recovery..."
824     onnode -q 0 ctdb recover
825     sleep_for 1
826     echo "Forcing a recovery..."
827     onnode -q 0 ctdb recover
828
829     echo "ctdb is ready"
830 }
831
832 ctdb_restart_when_done ()
833 {
834     ctdb_test_restart_scheduled=true
835 }
836
837 #######################################
838
839 install_eventscript ()
840 {
841     local script_name="$1"
842     local script_contents="$2"
843
844     if [ -n "$CTDB_TEST_REAL_CLUSTER" ] ; then
845         # The quoting here is *very* fragile.  However, we do
846         # experience the joy of installing a short script using
847         # onnode, and without needing to know the IP addresses of the
848         # nodes.
849         onnode all "f=\"\${CTDB_BASE:-/etc/ctdb}/events.d/${script_name}\" ; echo \"Installing \$f\" ; echo '${script_contents}' > \"\$f\" ; chmod 755 \"\$f\""
850     else
851         f="${CTDB_DIR}/tests/events.d/${script_name}"
852         echo "$script_contents" >"$f"
853         chmod 755 "$f"
854     fi
855 }
856
857 uninstall_eventscript ()
858 {
859     local script_name="$1"
860
861     if [ -n "$CTDB_TEST_REAL_CLUSTER" ] ; then
862         onnode all "rm -vf \"\${CTDB_BASE:-/etc/ctdb}/events.d/${script_name}\""
863     else
864         rm -vf "${CTDB_DIR}/tests/events.d/${script_name}"
865     fi
866 }