ctdb-scripts: Dump stack traces of smbd processes after shutdown
[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     case "$EVENTSCRIPTS_PATH" in
13         /*) : ;;
14         *) EVENTSCRIPTS_PATH="${PWD}/${EVENTSCRIPTS_PATH}" ;;
15     esac
16     export CTDB_HELPER_BINDIR="$EVENTSCRIPTS_PATH"
17 fi
18
19 export EVENTSCRIPTS_PATH
20
21 PATH="${EVENTSCRIPTS_PATH}:${PATH}"
22
23 export CTDB="ctdb"
24
25 export EVENTSCRIPTS_TESTS_VAR_DIR="${TEST_VAR_DIR}/unit_eventscripts"
26 if [ -d "$EVENTSCRIPTS_TESTS_VAR_DIR" -a \
27     "$EVENTSCRIPTS_TESTS_VAR_DIR" != "/unit_eventscripts" ] ; then
28     rm -r "$EVENTSCRIPTS_TESTS_VAR_DIR"
29 fi
30 mkdir -p "$EVENTSCRIPTS_TESTS_VAR_DIR"
31 export CTDB_SCRIPT_VARDIR="$EVENTSCRIPTS_TESTS_VAR_DIR/script-state"
32
33 export CTDB_LOGGING="file:${EVENTSCRIPTS_TESTS_VAR_DIR}/log.ctdb"
34 touch "${CTDB_LOGGING#file:}" || \
35     die "Unable to setup logging for \"$CTDB_LOGGING\""
36
37 if [ -d "${TEST_SUBDIR}/etc" ] ; then
38     cp -a "${TEST_SUBDIR}/etc" "$EVENTSCRIPTS_TESTS_VAR_DIR"
39     export CTDB_SYS_ETCDIR="${EVENTSCRIPTS_TESTS_VAR_DIR}/etc"
40 else
41     die "Unable to setup \$CTDB_SYS_ETCDIR"
42 fi
43
44 if [ -d "${TEST_SUBDIR}/etc-ctdb" ] ; then
45     cp -prL "${TEST_SUBDIR}/etc-ctdb" "$EVENTSCRIPTS_TESTS_VAR_DIR"
46     export CTDB_BASE="${EVENTSCRIPTS_TESTS_VAR_DIR}/etc-ctdb"
47 else
48     die "Unable to set \$CTDB_BASE"
49 fi
50 export CTDB_BASE
51
52 if [ ! -d "${CTDB_BASE}/events.d" ] ; then
53     cat <<EOF
54 ERROR: Directory ${CTDB_BASE}/events.d does not exist.
55
56 That means that no eventscripts can be tested.
57
58 One possible explanation:
59
60   You have CTDB installed via RPMs (or similar), so the regular
61   CTDB_BASE directory is in /etc/ctdb/
62
63   BUT
64
65   You have done a regular "configure" and "make install" so the tests
66   are installed under /usr/local/.
67
68 If so, one possible hack to fix this is to create a symlink:
69
70   ln -s /etc/ctdb /usr/local/etc/ctdb
71
72 This is nasty but it works...  :-)
73 EOF
74     exit 1
75 fi
76
77 ######################################################################
78
79 if "$TEST_VERBOSE" ; then
80     debug () { echo "$@" ; }
81 else
82     debug () { : ; }
83 fi
84
85 ######################################################################
86
87 # General setup fakery
88
89 setup_generic ()
90 {
91     debug "Setting up shares (3 existing shares)"
92     # Create 3 fake shares/exports.
93     export FAKE_SHARES=""
94     for i in $(seq 1 3) ; do
95         _s="${EVENTSCRIPTS_TESTS_VAR_DIR}/shares/${i}_existing"
96         mkdir -p "$_s"
97         FAKE_SHARES="${FAKE_SHARES}${FAKE_SHARES:+ }${_s}"
98     done
99
100     export FAKE_PROC_NET_BONDING="$EVENTSCRIPTS_TESTS_VAR_DIR/proc-net-bonding"
101     mkdir -p "$FAKE_PROC_NET_BONDING"
102     rm -f "$FAKE_PROC_NET_BONDING"/*
103
104     export FAKE_ETHTOOL_LINK_DOWN="$EVENTSCRIPTS_TESTS_VAR_DIR/ethtool-link-down"
105     mkdir -p "$FAKE_ETHTOOL_LINK_DOWN"
106     rm -f "$FAKE_ETHTOOL_LINK_DOWN"/*
107
108     # This can only have 2 levels.  We don't want to resort to usings
109     # something dangerous like "rm -r" setup time.
110     export FAKE_IP_STATE="$EVENTSCRIPTS_TESTS_VAR_DIR/fake-ip-state"
111     mkdir -p "$FAKE_IP_STATE"
112     rm -f "$FAKE_IP_STATE"/*/*
113     rm -f "$FAKE_IP_STATE"/* 2>/dev/null || true
114     rmdir "$FAKE_IP_STATE"/* 2>/dev/null || true
115
116
117     export CTDB_DBDIR="${EVENTSCRIPTS_TESTS_VAR_DIR}/db"
118     export CTDB_DBDIR_PERSISTENT="${CTDB_DBDIR}/persistent"
119     export CTDB_DBDIR_STATE="${CTDB_DBDIR}/state"
120     mkdir -p "$CTDB_DBDIR_PERSISTENT"
121     mkdir -p "$CTDB_DBDIR_STATE"
122
123     export FAKE_TDBTOOL_SUPPORTS_CHECK="yes"
124     export FAKE_TDB_IS_OK
125     export FAKE_DATE_OUTPUT
126
127     export FAKE_NETSTAT_TCP_ESTABLISHED FAKE_TCP_LISTEN FAKE_NETSTAT_UNIX_LISTEN
128     export FAKE_NETSTAT_TCP_ESTABLISHED_FILE=$(mktemp --tmpdir="$EVENTSCRIPTS_TESTS_VAR_DIR")
129 }
130
131 tcp_port_down ()
132 {
133     for _i ; do
134         debug "Marking TCP port \"${_i}\" as not listening"
135         FAKE_TCP_LISTEN=$(echo "$FAKE_TCP_LISTEN" | sed -r -e "s@[[:space:]]*[\.0-9]+:${_i}@@g")
136     done
137 }
138
139 shares_missing ()
140 {
141     _fmt="$1" ; shift
142
143     # Replace some shares with non-existent ones.
144     _t=""
145     _n=1
146     _nl="
147 "
148     export MISSING_SHARES_TEXT=""
149     for _i in $FAKE_SHARES ; do
150         if [ $_n = "$1" ] ; then
151             shift
152             _i="${_i%_existing}_missing"
153             debug "Replacing share $_n with missing share \"$_i\""
154             rmdir "$_i" 2>/dev/null || true
155             MISSING_SHARES_TEXT="${MISSING_SHARES_TEXT}${MISSING_SHARES_TEXT:+${_nl}}"$(printf "$_fmt" "${_i}")
156         fi
157         _t="${_t}${_t:+ }${_i}"
158         _n=$(($_n + 1))
159     done
160     FAKE_SHARES="$_t"
161 }
162
163 # Setup some fake /proc/net/bonding files with just enough info for
164 # the eventscripts.
165
166 # arg1 is interface name, arg2 is currently active slave (use "None"
167 # if none), arg3 is MII status ("up" or "down").
168 setup_bond ()
169 {
170     _iface="$1"
171     _slave="${2:-${_iface}_sl_0}"
172     _mii_s="${3:-up}"
173     _mii_subs="${4:-${_mii_s:-up}}"
174     echo "Setting $_iface to be a bond with active slave $_slave and MII status $_mii_s"
175     cat >"${FAKE_PROC_NET_BONDING}/$_iface" <<EOF
176 Bonding Mode: IEEE 802.3ad Dynamic link aggregation
177 Currently Active Slave: $_slave
178 # Status of the bond
179 MII Status: $_mii_s
180 # Status of 1st pretend adapter
181 MII Status: $_mii_subs
182 # Status of 2nd pretend adapter
183 MII Status: $_mii_subs
184 EOF
185 }
186
187 ethtool_interfaces_down ()
188 {
189     for _i ; do
190         echo "Marking interface $_i DOWN for ethtool"
191         touch "${FAKE_ETHTOOL_LINK_DOWN}/${_i}"
192     done
193 }
194
195 ethtool_interfaces_up ()
196 {
197     for _i ; do
198         echo "Marking interface $_i UP for ethtool"
199         rm -f "${FAKE_ETHTOOL_LINK_DOWN}/${_i}"
200     done
201 }
202
203 dump_routes ()
204 {
205     echo "# ip rule show"
206     ip rule show
207
208     ip rule show |
209     while read _p _x _i _x _t ; do
210         # Remove trailing colon after priority/preference.
211         _p="${_p%:}"
212         # Only remove rules that match our priority/preference.
213         [ "$CTDB_PER_IP_ROUTING_RULE_PREF" = "$_p" ] || continue
214
215         echo "# ip route show table $_t"
216         ip route show table "$_t"
217     done
218 }
219
220 # Copied from 13.per_ip_routing for now... so this is lazy testing  :-(
221 ipv4_host_addr_to_net ()
222 {
223     _host="$1"
224     _maskbits="$2"
225
226     # Convert the host address to an unsigned long by splitting out
227     # the octets and doing the math.
228     _host_ul=0
229     for _o in $(export IFS="." ; echo $_host) ; do
230         _host_ul=$(( ($_host_ul << 8) + $_o)) # work around Emacs color bug
231     done
232
233     # Calculate the mask and apply it.
234     _mask_ul=$(( 0xffffffff << (32 - $_maskbits) ))
235     _net_ul=$(( $_host_ul & $_mask_ul ))
236
237     # Now convert to a network address one byte at a time.
238     _net=""
239     for _o in $(seq 1 4) ; do
240         _net="$(($_net_ul & 255))${_net:+.}${_net}"
241         _net_ul=$(($_net_ul >> 8))
242     done
243
244     echo "${_net}/${_maskbits}"
245 }
246
247 ######################################################################
248
249 # CTDB fakery
250
251 # Evaluate an expression that probably calls functions or uses
252 # variables from the CTDB functions file.  This is used for test
253 # initialisation.
254 eventscript_call ()
255 {
256     (
257         . "$CTDB_BASE/functions"
258         "$@"
259     )
260 }
261
262 # For now this creates the same public addresses each time.  However,
263 # it could be made more flexible.
264 setup_public_addresses ()
265 {
266     if [ -f "$CTDB_PUBLIC_ADDRESSES" -a \
267             "${CTDB_PUBLIC_ADDRESSES%/*}" = "$EVENTSCRIPTS_TESTS_VAR_DIR" ] ; then
268         rm "$CTDB_PUBLIC_ADDRESSES"
269     fi
270
271     export CTDB_PUBLIC_ADDRESSES=$(mktemp \
272                                        --tmpdir="$EVENTSCRIPTS_TESTS_VAR_DIR" \
273                                        "public-addresses-XXXXXXXX")
274
275     echo "Setting up CTDB_PUBLIC_ADDRESSES=${CTDB_PUBLIC_ADDRESSES}"
276     cat >"$CTDB_PUBLIC_ADDRESSES" <<EOF
277 10.0.0.1/24 dev123
278 10.0.0.2/24 dev123
279 10.0.0.3/24 dev123
280 10.0.0.4/24 dev123
281 10.0.0.5/24 dev123
282 10.0.0.6/24 dev123
283 10.0.1.1/24 dev456
284 10.0.1.2/24 dev456
285 10.0.1.3/24 dev456
286 EOF
287 }
288
289 # Need to cope with ctdb_get_pnn().  If a test changes PNN then it
290 # needs to be using a different state directory, otherwise the wrong
291 # PNN can already be cached in the state directory.
292 ctdb_set_pnn ()
293 {
294     export FAKE_CTDB_PNN="$1"
295     echo "Setting up PNN ${FAKE_CTDB_PNN}"
296
297     export CTDB_SCRIPT_VARDIR="$EVENTSCRIPTS_TESTS_VAR_DIR/script-state/${FAKE_CTDB_PNN}"
298     mkdir -p "$CTDB_SCRIPT_VARDIR"
299 }
300
301 setup_ctdb ()
302 {
303     setup_generic
304
305     export FAKE_CTDB_NUMNODES="${1:-3}"
306     echo "Setting up CTDB with ${FAKE_CTDB_NUMNODES} fake nodes"
307
308     ctdb_set_pnn "${2:-0}"
309
310     setup_public_addresses
311
312     export FAKE_CTDB_STATE="$EVENTSCRIPTS_TESTS_VAR_DIR/fake-ctdb"
313
314     export FAKE_CTDB_EXTRA_CONFIG="$EVENTSCRIPTS_TESTS_VAR_DIR/fake-config.sh"
315     rm -f "$FAKE_CTDB_EXTRA_CONFIG"
316
317     export FAKE_CTDB_IFACES_DOWN="$FAKE_CTDB_STATE/ifaces-down"
318     mkdir -p "$FAKE_CTDB_IFACES_DOWN"
319     rm -f "$FAKE_CTDB_IFACES_DOWN"/*
320
321     export FAKE_CTDB_SCRIPTSTATUS="$FAKE_CTDB_STATE/scriptstatus"
322     mkdir -p "$FAKE_CTDB_SCRIPTSTATUS"
323     rm -f "$FAKE_CTDB_SCRIPTSTATUS"/*
324
325     export CTDB_PARTIALLY_ONLINE_INTERFACES
326
327     export FAKE_CTDB_TUNABLES_OK="MonitorInterval TDBMutexEnabled DatabaseHashSize"
328     export FAKE_CTDB_TUNABLES_OBSOLETE="EventScriptUnhealthyOnTimeout"
329 }
330
331 setup_config ()
332 {
333     cat >"$FAKE_CTDB_EXTRA_CONFIG"
334 }
335
336 validate_percentage ()
337 {
338     case "$1" in
339         [0-9]|[0-9][0-9]|100) return 0 ;;
340         *) echo "WARNING: ${1} is an invalid percentage${2:+\" in }${2}${2:+\"}"
341            return 1
342     esac
343 }
344
345 setup_memcheck ()
346 {
347     _mem_usage="${1:-10}" # Default is 10%
348     _swap_usage="${2:-0}" # Default is  0%
349
350     setup_ctdb
351
352     _swap_total=5857276
353     _swap_free=$(( (100 - $_swap_usage) * $_swap_total / 100 ))
354
355     _mem_total=3940712
356     _mem_free=225268
357     _mem_buffers=146120
358     _mem_cached=$(( $_mem_total * (100 - $_mem_usage) / 100 - $_mem_free - $_mem_buffers ))
359
360     export FAKE_PROC_MEMINFO="\
361 MemTotal:        ${_mem_total} kB
362 MemFree:          ${_mem_free} kB
363 Buffers:          ${_mem_buffers} kB
364 Cached:          ${_mem_cached} kB
365 SwapCached:        56016 kB
366 Active:          2422104 kB
367 Inactive:        1019928 kB
368 Active(anon):    1917580 kB
369 Inactive(anon):   523080 kB
370 Active(file):     504524 kB
371 Inactive(file):   496848 kB
372 Unevictable:        4844 kB
373 Mlocked:            4844 kB
374 SwapTotal:       ${_swap_total} kB
375 SwapFree:        ${_swap_free} kB
376 ..."
377
378     export CTDB_MONITOR_MEMORY_USAGE
379     export CTDB_MONITOR_SWAP_USAGE
380 }
381
382 setup_fscheck ()
383 {
384     export FAKE_FS_USE="${1:-10}"  # Default is 10% usage
385
386     # Causes some variables to be exported
387     setup_ctdb
388
389     export CTDB_MONITOR_FILESYSTEM_USAGE
390 }
391
392 ctdb_get_interfaces ()
393 {
394     # The echo/subshell forces all the output onto 1 line.
395     echo $(ctdb ifaces -X | awk -F'|' 'FNR > 1 {print $2}')
396 }
397
398 ctdb_get_1_interface ()
399 {
400     _t=$(ctdb_get_interfaces)
401     echo ${_t%% *}
402 }
403
404 # Print all public addresses as: interface IP maskbits
405 # Each line is suitable for passing to takeip/releaseip
406 ctdb_get_all_public_addresses ()
407 {
408     _f="${CTDB_PUBLIC_ADDRESSES:-${CTDB_BASE}/public_addresses}"
409     while IFS="/$IFS" read _ip _maskbits _ifaces ; do
410         echo "$_ifaces $_ip $_maskbits"
411     done <"$_f"
412 }
413
414 # Print public addresses on this node as: interface IP maskbits
415 # Each line is suitable for passing to takeip/releaseip
416 ctdb_get_my_public_addresses ()
417 {
418     ctdb ip -v -X | {
419         read _x # skip header line
420
421         while IFS="|" read _x _ip _x _iface _x ; do
422             [ -n "$_iface" ] || continue
423             while IFS="/$IFS" read _i _maskbits _x ; do
424                 if [ "$_ip" = "$_i" ] ; then
425                     echo $_iface $_ip $_maskbits
426                     break
427                 fi
428             done <"${CTDB_PUBLIC_ADDRESSES:-${CTDB_BASE}/public_addresses}"
429         done
430     }
431 }
432
433 # Prints the 1st public address as: interface IP maskbits
434 # This is suitable for passing to takeip/releaseip
435 ctdb_get_1_public_address ()
436 {
437     ctdb_get_my_public_addresses | { head -n 1 ; cat >/dev/null ; }
438 }
439
440 ctdb_not_implemented ()
441 {
442     export CTDB_NOT_IMPLEMENTED="$1"
443     ctdb_not_implemented="\
444 DEBUG: ctdb: command \"$1\" not implemented in stub"
445 }
446
447 ctdb_fake_scriptstatus ()
448 {
449     _code="$1"
450     _status="$2"
451     _err_out="$3"
452
453     _d1=$(date '+%s.%N')
454     _d2=$(date '+%s.%N')
455
456     echo "$_code $_status $_err_out" >"$FAKE_CTDB_SCRIPTSTATUS/$script"
457 }
458
459 ######################################################################
460
461 setup_ctdb_policy_routing ()
462 {
463     service_name="per_ip_routing"
464
465     export CTDB_PER_IP_ROUTING_CONF="$CTDB_BASE/policy_routing"
466     export CTDB_PER_IP_ROUTING_RULE_PREF=100
467     export CTDB_PER_IP_ROUTING_TABLE_ID_LOW=1000
468     export CTDB_PER_IP_ROUTING_TABLE_ID_HIGH=2000
469
470     # Tests need to create and populate this file
471     rm -f "$CTDB_PER_IP_ROUTING_CONF"
472 }
473
474 # Create policy routing configuration in $CTDB_PER_IP_ROUTING_CONF.
475 # $1 is the number of assigned IPs to use (<num>, all), defaulting to
476 # 1.  If $2 is "default" then a default route is also added.
477 create_policy_routing_config ()
478 {
479     _num_ips="${1:-1}"
480     _should_add_default="$2"
481
482     ctdb_get_my_public_addresses |
483     if [ "$_num_ips" = "all" ] ; then
484         cat
485     else
486         { head -n "$_num_ips" ; cat >/dev/null ; }
487     fi |
488     while read _dev _ip _bits ; do
489         _net=$(ipv4_host_addr_to_net "$_ip" "$_bits")
490         _gw="${_net%.*}.254" # a dumb, calculated default
491
492         echo "$_ip $_net"
493
494         if [ "$_should_add_default" = "default" ] ; then
495             echo "$_ip 0.0.0.0/0 $_gw"
496         fi
497     done >"$CTDB_PER_IP_ROUTING_CONF"
498 }
499
500 # Check the routes against those that are expected.  $1 is the number
501 # of assigned IPs to use (<num>, all), defaulting to 1.  If $2 is
502 # "default" then expect default routes to have been added.
503 check_routes ()
504 {
505     _num_ips="${1:-1}"
506     _should_add_default="$2"
507
508     _policy_rules=""
509     _policy_routes=""
510
511     ctdb_get_my_public_addresses |
512     if [ "$_num_ips" = "all" ] ; then
513         cat
514     else
515         { head -n "$_num_ips" ; cat >/dev/null ; }
516     fi | {
517         while read _dev _ip _bits ; do
518             _net=$(ipv4_host_addr_to_net "$_ip" "$_bits")
519             _gw="${_net%.*}.254" # a dumb, calculated default
520
521             _policy_rules="${_policy_rules}
522 ${CTDB_PER_IP_ROUTING_RULE_PREF}:       from $_ip lookup ctdb.$_ip "
523             _policy_routes="${_policy_routes}
524 # ip route show table ctdb.$_ip
525 $_net dev $_dev  scope link "
526
527             if [ "$_should_add_default" = "default" ] ; then
528                 _policy_routes="${_policy_routes}
529 default via $_gw dev $_dev "
530             fi
531         done
532
533         ok <<EOF
534 # ip rule show
535 0:      from all lookup local ${_policy_rules}
536 32766:  from all lookup main 
537 32767:  from all lookup default ${_policy_routes}
538 EOF
539
540         simple_test_command dump_routes
541     } || test_fail
542 }
543
544 ######################################################################
545
546 setup_ctdb_lvs ()
547 {
548         lvs_state_dir="${EVENTSCRIPTS_TESTS_VAR_DIR}/lvs"
549         mkdir -p "$lvs_state_dir"
550
551         export FAKE_LVS_STATE_DIR="${lvs_state_dir}/state"
552         mkdir "$FAKE_LVS_STATE_DIR"
553
554         lvs_header=$(ipvsadm -l -n)
555
556         export CTDB_LVS_PUBLIC_IP="$1"
557         export CTDB_LVS_PUBLIC_IFACE="$2"
558
559         [ -n "$CTDB_LVS_PUBLIC_IP" ] || return 0
560         [ -n "$CTDB_LVS_PUBLIC_IFACE" ] || return 0
561
562         export CTDB_LVS_NODES=$(mktemp --tmpdir="$lvs_state_dir")
563         export FAKE_CTDB_LVS_MASTER=""
564
565         # Read from stdin
566         _pnn=0
567         while read _ip _opts ; do
568                 case "$_opts" in
569                 master)
570                         FAKE_CTDB_LVS_MASTER="$_pnn"
571                         echo "$_ip"
572                         ;;
573                 slave-only)
574                         printf "%s\tslave-only\n" "$_ip"
575                         ;;
576                 *)
577                         echo "$_ip"
578                         ;;
579                 esac
580                 _pnn=$(($_pnn + 1))
581         done >"$CTDB_LVS_NODES"
582 }
583
584 check_ipvsadm ()
585 {
586         if [ "$1" = "NULL" ] ; then
587                 required_result 0 <<EOF
588 $lvs_header
589 EOF
590         else
591                 required_result 0 <<EOF
592 $lvs_header
593 $(cat)
594 EOF
595         fi
596
597         simple_test_command ipvsadm -l -n
598 }
599
600 check_lvs_ip ()
601 {
602         _scope="$1"
603
604         if [ "$_scope" = "NULL" ] ; then
605                 required_result 0 <<EOF
606 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
607     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
608 EOF
609         else
610                 required_result 0 <<EOF
611 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
612     link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
613     inet ${CTDB_LVS_PUBLIC_IP}/32 scope ${_scope} lo
614        valid_lft forever preferred_lft forever
615 EOF
616         fi
617
618         simple_test_command ip addr show dev lo
619 }
620
621 ######################################################################
622
623 ctdb_catdb_format_pairs ()
624 {
625     _count=0
626
627     while read _k _v ; do
628         _kn=$(echo -n "$_k" | wc -c)
629         _vn=$(echo -n "$_v" | wc -c)
630         cat <<EOF
631 key(${_kn}) = "${_k}"
632 dmaster: 0
633 rsn: 1
634 data(${_vn}) = "${_v}"
635
636 EOF
637         _count=$(($_count + 1))
638     done
639
640     echo "Dumped ${_count} records"
641 }
642
643 check_ctdb_tdb_statd_state ()
644 {
645     ctdb_get_my_public_addresses |
646     while read _x _sip _x ; do
647         for _cip ; do
648             echo "statd-state@${_sip}@${_cip}" "$FAKE_DATE_OUTPUT"
649         done
650     done |
651     ctdb_catdb_format_pairs | {
652         ok
653         simple_test_command ctdb catdb ctdb.tdb
654     } || test_fail
655 }
656
657 check_statd_callout_smnotify ()
658 {
659     _state_even=$(( $(date '+%s') / 2 * 2))
660     _state_odd=$(($_state_even + 1))
661
662     nfs_load_config
663
664     ctdb_get_my_public_addresses |
665     while read _x _sip _x ; do
666         for _cip ; do
667             cat <<EOF
668 --client=${_cip} --ip=${_sip} --server=${_sip} --stateval=${_state_even}
669 --client=${_cip} --ip=${_sip} --server=${NFS_HOSTNAME} --stateval=${_state_even}
670 --client=${_cip} --ip=${_sip} --server=${_sip} --stateval=${_state_odd}
671 --client=${_cip} --ip=${_sip} --server=${NFS_HOSTNAME} --stateval=${_state_odd}
672 EOF
673         done
674     done | {
675         ok
676         simple_test_event "notify"
677     } || test_fail
678 }
679
680 ######################################################################
681
682 setup_ctdb_natgw ()
683 {
684         debug "Setting up NAT gateway"
685
686         natgw_config_dir="${TEST_VAR_DIR}/natgw_config"
687         mkdir -p "$natgw_config_dir"
688
689         # These will accumulate, 1 per test... but will be cleaned up at
690         # the end.
691         export CTDB_NATGW_NODES=$(mktemp --tmpdir="$natgw_config_dir")
692
693         # Read from stdin
694         while read _ip _opts ; do
695                 case "$_opts" in
696                 master)
697                         export FAKE_CTDB_NATGW_MASTER="$_ip"
698                         echo "$_ip"
699                         ;;
700                 slave-only)
701                         printf "%s\tslave-only\n" "$_ip"
702                         ;;
703                 *)
704                         echo "$_ip"
705                         ;;
706                 esac
707         done >"$CTDB_NATGW_NODES"
708
709         # Assume all of the nodes are on a /24 network and have IPv4
710         # addresses:
711         read _ip <"$CTDB_NATGW_NODES"
712         export CTDB_NATGW_PRIVATE_NETWORK="${_ip%.*}.0/24"
713
714         # These are fixed.  Probably don't use the same network for the
715         # private node IPs.  To unset the default gateway just set it to
716         # "".  :-)
717         export CTDB_NATGW_PUBLIC_IP="10.1.1.121/24"
718         export CTDB_NATGW_PUBLIC_IFACE="eth1"
719         export CTDB_NATGW_DEFAULT_GATEWAY="10.1.1.254"
720         export CTDB_NATGW_SLAVE_ONLY=""
721 }
722
723 ok_natgw_master_ip_addr_show ()
724 {
725     _mac=$(echo "$CTDB_NATGW_PUBLIC_IFACE" | md5sum | sed -r -e 's@(..)(..)(..)(..)(..)(..).*@\1:\2:\3:\4:\5:\6@')
726
727     # This is based on CTDB_NATGW_PUBLIC_IP
728     _brd="10.1.1.255"
729
730 ok <<EOF
731 1: ${CTDB_NATGW_PUBLIC_IFACE}: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
732     link/ether ${_mac} brd ff:ff:ff:ff:ff:ff
733     inet ${CTDB_NATGW_PUBLIC_IP} brd ${_brd} scope global ${CTDB_NATGW_PUBLIC_IFACE}
734        valid_lft forever preferred_lft forever
735 EOF
736 }
737
738 ok_natgw_slave_ip_addr_show ()
739 {
740     _mac=$(echo "$CTDB_NATGW_PUBLIC_IFACE" | md5sum | sed -r -e 's@(..)(..)(..)(..)(..)(..).*@\1:\2:\3:\4:\5:\6@')
741 ok <<EOF
742 1: ${CTDB_NATGW_PUBLIC_IFACE}: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
743     link/ether ${_mac} brd ff:ff:ff:ff:ff:ff
744 EOF
745 }
746
747 ok_natgw_master_static_routes ()
748 {
749     _nl="
750 "
751     _t=""
752     for _i in $CTDB_NATGW_STATIC_ROUTES ; do
753         # This is intentionally different to the code in 11.natgw ;-)
754         case "$_i" in
755             *@*)
756                 _net=$(echo "$_i" | sed -e 's|@.*||')
757                 _gw=$(echo "$_i" | sed -e 's|.*@||')
758                 ;;
759             *)
760                 _net="$_i"
761                 _gw="$CTDB_NATGW_DEFAULT_GATEWAY"
762         esac
763
764         [ -n "$_gw" ] || continue
765         _t="${_t}${_t:+${_nl}}"
766         _t="${_t}${_net} via ${_gw} dev ethXXX  metric 10 "
767     done
768     _t=$(echo "$_t" | sort)
769     ok "$_t"
770 }
771
772 ok_natgw_slave_static_routes ()
773 {
774     _nl="
775 "
776     _t=""
777     for _i in $CTDB_NATGW_STATIC_ROUTES ; do
778         # This is intentionally different to the code in 11.natgw ;-)
779         _net=$(echo "$_i" | sed -e 's|@.*||')
780
781         # The interface for the private network isn't specified as
782         # part of the NATGW configuration and isn't part of the
783         # command to add the route.  It is implicitly added by "ip
784         # route" but our stub doesn't do this and adds "ethXXX".
785         _t="${_t}${_t:+${_nl}}"
786         _t="${_t}${_net} via ${FAKE_CTDB_NATGW_MASTER} dev ethXXX  metric 10 "
787     done
788     _t=$(echo "$_t" | sort)
789     ok "$_t"
790 }
791
792 ######################################################################
793
794 # Samba/winbind fakery
795
796 setup_samba ()
797 {
798     setup_ctdb
799
800     service_name="samba"
801
802     if [ "$1" != "down" ] ; then
803
804         debug "Marking Samba services as up, listening and managed by CTDB"
805         # Get into known state.
806         eventscript_call ctdb_service_managed
807
808         # All possible service names for all known distros.
809         for i in "smb" "nmb" "samba" ; do
810             service "$i" force-started
811         done
812
813         export CTDB_SAMBA_SKIP_SHARE_CHECK="no"
814         export CTDB_MANAGED_SERVICES="foo samba bar"
815
816         export FAKE_TCP_LISTEN="0.0.0.0:445 0.0.0.0:139"
817         export FAKE_WBINFO_FAIL="no"
818
819         # Some things in 50.samba are backgrounded and waited for.  If
820         # we don't sleep at all then timeouts can happen.  This avoids
821         # that...  :-)
822         export FAKE_SLEEP_FORCE=0.1
823     else
824         debug "Marking Samba services as down, not listening and not managed by CTDB"
825         # Get into known state.
826         eventscript_call ctdb_service_unmanaged
827
828         # All possible service names for all known distros.
829         for i in "smb" "nmb" "samba" ; do
830             service "$i" force-stopped
831         done
832
833         export CTDB_SAMBA_SKIP_SHARE_CHECK="no"
834         export CTDB_MANAGED_SERVICES="foo bar"
835         unset CTDB_MANAGES_SAMBA
836
837         export FAKE_TCP_LISTEN=""
838         export FAKE_WBINFO_FAIL="yes"
839     fi
840 }
841
842 samba_setup_fake_threads ()
843 {
844         export FAKE_SMBD_THREAD_PIDS="$*"
845
846         _nl="
847 "
848         _out=""
849         _count=0
850         for _pid ; do
851                 [ "$_count" -lt 5 ] || break
852                 _t=$(program_stack_trace "smbd" $_pid)
853                 _out="${_out:+${_out}${_nl}}${_t}"
854                 _count=$((_count + 1))
855         done
856         SAMBA_STACK_TRACES="$_out"
857 }
858
859 setup_winbind ()
860 {
861     setup_ctdb
862
863     service_name="winbind"
864
865     if [ "$1" != "down" ] ; then
866
867         debug "Marking Winbind service as up and managed by CTDB"
868         # Get into known state.
869         eventscript_call ctdb_service_managed
870
871         service "winbind" force-started
872
873         export CTDB_MANAGED_SERVICES="foo winbind bar"
874
875         export FAKE_WBINFO_FAIL="no"
876
877     else
878         debug "Marking Winbind service as down and not managed by CTDB"
879         # Get into known state.
880         eventscript_call ctdb_service_unmanaged
881
882         service "winbind" force-stopped
883
884         export CTDB_MANAGED_SERVICES="foo bar"
885         unset CTDB_MANAGES_WINBIND
886
887         export FAKE_WBINFO_FAIL="yes"
888     fi
889 }
890
891 wbinfo_down ()
892 {
893     debug "Making wbinfo commands fail"
894     FAKE_WBINFO_FAIL="yes"
895 }
896
897 ######################################################################
898
899 # NFS fakery
900
901 setup_nfs ()
902 {
903     setup_ctdb
904
905     service_name="nfs"
906
907     export FAKE_RPCINFO_SERVICES=""
908
909     export CTDB_NFS_SKIP_SHARE_CHECK="no"
910
911     export RPCNFSDCOUNT
912
913     # This doesn't even need to exist
914     export CTDB_NFS_EXPORTS_FILE="$EVENTSCRIPTS_TESTS_VAR_DIR/etc-exports"
915
916     # Reset the failcounts for nfs services.
917     eventscript_call eval rm -f '$ctdb_fail_dir/nfs_*'
918
919     if [ "$1" != "down" ] ; then
920         debug "Setting up NFS environment: all RPC services up, NFS managed by CTDB"
921
922         eventscript_call ctdb_service_managed
923         service "nfs" force-started
924         service "nfslock" force-started
925
926         export CTDB_MANAGED_SERVICES="foo nfs bar"
927
928         rpc_services_up \
929             "portmapper" "nfs" "mountd" "rquotad" "nlockmgr" "status"
930
931         nfs_setup_fake_threads "nfsd"
932         nfs_setup_fake_threads "rpc.foobar"  # Just set the variable to empty
933     else
934         debug "Setting up NFS environment: all RPC services down, NFS not managed by CTDB"
935
936         eventscript_call ctdb_service_unmanaged
937         service "nfs" force-stopped
938         service "nfslock" force-stopped
939
940         export CTDB_MANAGED_SERVICES="foo bar"
941         unset CTDB_MANAGES_NFS
942     fi
943
944     # This is really nasty.  However, when we test NFS we don't
945     # actually test statd-callout. If we leave it there then left
946     # over, backgrounded instances of statd-callout will do horrible
947     # things with the "ctdb ip" stub and cause the actual
948     # statd-callout tests that follow to fail.
949     rm "${CTDB_BASE}/statd-callout"
950 }
951
952 setup_nfs_ganesha ()
953 {
954     setup_nfs "$@"
955     export CTDB_NFS_CALLOUT="${CTDB_BASE}/nfs-ganesha-callout"
956     if [ "$1" != "down" ] ; then
957         export CTDB_MANAGES_NFS="yes"
958     fi
959
960     export CTDB_NFS_SKIP_SHARE_CHECK="yes"
961 }
962
963 rpc_services_down ()
964 {
965     for _i ; do
966         debug "Marking RPC service \"${_i}\" as unavailable"
967         FAKE_RPCINFO_SERVICES=$(echo "$FAKE_RPCINFO_SERVICES" | sed -r -e "s@[[:space:]]*${_i}:[0-9]+:[0-9]+@@g")
968     done
969 }
970
971 rpc_services_up ()
972 {
973     for _i ; do
974         debug "Marking RPC service \"${_i}\" as available"
975         case "$_i" in
976             portmapper) _t="2:4" ;;
977             nfs)        _t="2:3" ;;
978             mountd)     _t="1:3" ;;
979             rquotad)    _t="1:2" ;;
980             nlockmgr)   _t="3:4" ;;
981             status)     _t="1:1" ;;
982             *) die "Internal error - unsupported RPC service \"${_i}\"" ;;
983         esac
984
985         FAKE_RPCINFO_SERVICES="${FAKE_RPCINFO_SERVICES}${FAKE_RPCINFO_SERVICES:+ }${_i}:${_t}"
986     done
987 }
988
989
990 nfs_load_config ()
991 {
992     _etc="$CTDB_SYS_ETCDIR" # shortcut for readability
993     for _c in "$_etc/sysconfig/nfs" "$_etc/default/nfs" "$_etc/ctdb/sysconfig/nfs" ; do
994         if [ -r "$_c" ] ; then
995             . "$_c"
996             break
997         fi
998     done
999 }
1000
1001 nfs_setup_fake_threads ()
1002 {
1003     _prog="$1" ; shift
1004
1005     case "$_prog" in
1006         nfsd)
1007             export PROCFS_PATH=$(mktemp -d --tmpdir="$EVENTSCRIPTS_TESTS_VAR_DIR")
1008             _threads="${PROCFS_PATH}/fs/nfsd/threads"
1009             mkdir -p $(dirname "$_threads")
1010             echo $# >"$_threads"
1011             export FAKE_NFSD_THREAD_PIDS="$*"
1012             ;;
1013         *)
1014             export FAKE_RPC_THREAD_PIDS="$*"
1015             ;;
1016     esac
1017 }
1018
1019 program_stack_trace ()
1020 {
1021         _prog="$1"
1022         _pid="$2"
1023
1024         cat <<EOF
1025 Stack trace for ${_prog}[${_pid}]:
1026 [<ffffffff87654321>] fake_stack_trace_for_pid_${_pid}/stack+0x0/0xff
1027 EOF
1028 }
1029
1030 program_stack_traces ()
1031 {
1032     _prog="$1"
1033     _max="${2:-1}"
1034
1035     _count=1
1036     for _pid in ${FAKE_NFSD_THREAD_PIDS:-$FAKE_RPC_THREAD_PIDS} ; do
1037         [ $_count -le $_max ] || break
1038
1039         program_stack_trace "$_prog" "$_pid"
1040         _count=$(($_count + 1))
1041     done
1042 }
1043
1044 guess_output ()
1045 {
1046     case "$1" in
1047         $CTDB_NFS_CALLOUT\ start\ nlockmgr)
1048             echo "&Starting nfslock: OK"
1049             ;;
1050         $CTDB_NFS_CALLOUT\ start\ nfs)
1051             cat <<EOF
1052 &Starting nfslock: OK
1053 &Starting nfs: OK
1054 EOF
1055             ;;
1056         *)
1057             : # Nothing
1058     esac
1059 }
1060
1061 # Set the required result for a particular RPC program having failed
1062 # for a certain number of iterations.  This is probably still a work
1063 # in progress.  Note that we could hook aggressively
1064 # nfs_check_rpc_service() to try to implement this but we're better
1065 # off testing nfs_check_rpc_service() using independent code...  even
1066 # if it is incomplete and hacky.  So, if the 60.nfs eventscript
1067 # changes and the tests start to fail then it may be due to this
1068 # function being incomplete.
1069 rpc_set_service_failure_response ()
1070 {
1071     _rpc_service="$1"
1072     _numfails="${2:-1}" # default 1
1073
1074     # Default
1075     ok_null
1076     if [ $_numfails -eq 0 ] ; then
1077         return
1078     fi
1079
1080     nfs_load_config
1081
1082     # A handy newline.  :-)
1083     _nl="
1084 "
1085
1086     _dir="${CTDB_NFS_CHECKS_DIR:-${CTDB_BASE}/nfs-checks.d}"
1087
1088     _file=$(ls "$_dir"/[0-9][0-9]."${_rpc_service}.check")
1089     [ -r "$_file" ] || die "RPC check file \"$_file\" does not exist or is not unique"
1090
1091     _out=$(mktemp --tmpdir="$EVENTSCRIPTS_TESTS_VAR_DIR")
1092     _rc_file=$(mktemp --tmpdir="$EVENTSCRIPTS_TESTS_VAR_DIR")
1093
1094     (
1095         # Subshell to restrict scope variables...
1096
1097         # Defaults
1098         family="tcp"
1099         version=""
1100         unhealthy_after=1
1101         restart_every=0
1102         service_stop_cmd=""
1103         service_start_cmd=""
1104         service_check_cmd=""
1105         service_debug_cmd=""
1106
1107         # Don't bother syntax checking, eventscript does that...
1108         . "$_file"
1109
1110         # Just use the first version, or use default.  This is dumb but
1111         # handles all the cases that we care about now...
1112         if [ -n "$version" ] ; then
1113             _ver="${version%% *}"
1114         else
1115             case "$_rpc_service" in
1116                 portmapper) _ver="" ;;
1117                 *)          _ver=1  ;;
1118             esac
1119         fi
1120         _rpc_check_out="\
1121 $_rpc_service failed RPC check:
1122 rpcinfo: RPC: Program not registered
1123 program $_rpc_service${_ver:+ version }${_ver} is not available"
1124
1125         if [ $unhealthy_after -gt 0 -a $_numfails -ge $unhealthy_after ] ; then
1126             _unhealthy=true
1127             echo 1 >"$_rc_file"
1128             echo "ERROR: ${_rpc_check_out}" >>"$_out"
1129         else
1130             _unhealthy=false
1131             echo 0 >"$_rc_file"
1132         fi
1133
1134         if [ $restart_every -gt 0 ] && \
1135                    [ $(($_numfails % $restart_every)) -eq 0 ] ; then
1136             if ! $_unhealthy ; then
1137                 echo "WARNING: ${_rpc_check_out}" >>"$_out"
1138             fi
1139
1140             echo "Trying to restart service \"${_rpc_service}\"..." >>"$_out"
1141
1142             if [ -n "$service_debug_cmd" ] ; then
1143                 $service_debug_cmd 2>&1 >>"$_out"
1144             fi
1145
1146             guess_output "$service_start_cmd" >>"$_out"
1147         fi
1148     )
1149
1150     read _rc <"$_rc_file"
1151     required_result $_rc <"$_out"
1152
1153     rm -f "$_out" "$_rc_file"
1154 }
1155
1156 ######################################################################
1157
1158 # Recovery lock fakery
1159
1160 cleanup_reclock ()
1161 {
1162         _pattern="${script_dir}/${script}"
1163         while pgrep -f "$_pattern" >/dev/null ; do
1164                 echo "Waiting for backgrounded ${script} to exit..."
1165                 (FAKE_SLEEP_REALLY=yes sleep 1)
1166         done
1167 }
1168
1169 setup_reclock ()
1170 {
1171         CTDB_RECOVERY_LOCK=$(mktemp --tmpdir="$EVENTSCRIPTS_TESTS_VAR_DIR")
1172         export CTDB_RECOVERY_LOCK
1173
1174         test_cleanup cleanup_reclock
1175 }
1176
1177 ######################################################################
1178
1179 # VSFTPD fakery
1180
1181 setup_vsftpd ()
1182 {
1183     service_name="vsftpd"
1184
1185     if [ "$1" != "down" ] ; then
1186         die "setup_vsftpd up not implemented!!!"
1187     else
1188         debug "Setting up VSFTPD environment: service down, not managed by CTDB"
1189
1190         eventscript_call ctdb_service_unmanaged
1191         service vsftpd force-stopped
1192
1193         export CTDB_MANAGED_SERVICES="foo"
1194         unset CTDB_MANAGES_VSFTPD
1195     fi
1196 }
1197
1198 ######################################################################
1199
1200 # HTTPD fakery
1201
1202 setup_httpd ()
1203 {
1204     if [ "$1" != "down" ] ; then
1205         die "setup_httpd up not implemented!!!"
1206     else
1207         debug "Setting up HTTPD environment: service down, not managed by CTDB"
1208
1209         for service_name in "apache2" "httpd" ; do
1210             eventscript_call ctdb_service_unmanaged
1211             service "$service_name" force-stopped
1212         done
1213
1214         export CTDB_MANAGED_SERVICES="foo"
1215         unset CTDB_MANAGES_HTTPD
1216     fi
1217 }
1218
1219 ######################################################################
1220
1221 # multipathd fakery
1222
1223 setup_multipathd ()
1224 {
1225     for i ; do
1226         case "$i" in
1227             \!*)
1228                 _t="${i#!}"
1229                 echo "Marking ${_t} as having no active paths"
1230                 FAKE_MULTIPATH_FAILURES="${FAKE_MULTIPATH_FAILURES}${FAKE_MULTIPATH+FAILURES:+ }${_t}"
1231                 ;;
1232             *)
1233                 _t="$i"         
1234         esac
1235         CTDB_MONITOR_MPDEVICES="${CTDB_MONITOR_MPDEVICES}${CTDB_MONITOR_MPDEVICES:+ }${_t}"
1236     done
1237
1238     export CTDB_MONITOR_MPDEVICES FAKE_MULTIPATH_FAILURES
1239     export FAKE_SLEEP_FORCE=0.1
1240 }
1241
1242 ######################################################################
1243
1244 # Result and test functions
1245
1246 # Set some globals and print the summary.
1247 define_test ()
1248 {
1249     desc="$1"
1250
1251     _f=$(basename "$0" ".sh")
1252
1253     # Remaining format should be NN.service.event.NNN or NN.service.NNN:
1254     _num="${_f##*.}"
1255     _f="${_f%.*}"
1256
1257     case "$_f" in
1258         [0-9][0-9].*.*)
1259             script="${_f%.*}"
1260             event="${_f##*.}"
1261             script_dir="${CTDB_BASE}/events.d"
1262             ;;
1263         [0-9][0-9].*)
1264             script="$_f"
1265             unset event
1266             script_dir="${CTDB_BASE}/events.d"
1267             ;;
1268         *.*)
1269             script="${_f%.*}"
1270             event="${_f##*.}"
1271             script_dir="${CTDB_BASE}"
1272             ;;
1273         *)
1274             script="${_f%.*}"
1275             unset event
1276             script_dir="${CTDB_BASE}"
1277     esac
1278
1279     [ -r "${script_dir}/${script}" ] || \
1280         die "Internal error - unable to find script \"${script_dir}/${script}\""
1281
1282     printf "%-17s %-10s %-4s - %s\n\n" "$script" "$event" "$_num" "$desc"
1283 }
1284
1285 # Run an eventscript once.  The test passes if the return code and
1286 # output match those required.
1287
1288 # Any args are passed to the eventscript.
1289
1290 simple_test ()
1291 {
1292     [ -n "$event" ] || die 'simple_test: $event not set'
1293
1294     args="$@"
1295
1296     test_header ()
1297     {
1298         echo "Running script \"$script $event${args:+ }$args\""
1299     }
1300
1301     extra_header ()
1302     {
1303         cat <<EOF
1304
1305 ##################################################
1306 CTDB_BASE="$CTDB_BASE"
1307 CTDB_SYS_ETCDIR="$CTDB_SYS_ETCDIR"
1308 ctdb client is "$(which ctdb)"
1309 ip command is "$(which ip)"
1310 EOF
1311     }
1312
1313     script_test "${script_dir}/${script}" "$event" "$@"
1314
1315     reset_test_header
1316     reset_extra_header
1317 }
1318
1319 simple_test_event ()
1320 {
1321     # If something has previously failed then don't continue.
1322     : ${_passed:=true}
1323     $_passed || return 1
1324
1325     event="$1" ; shift
1326     echo "=================================================="
1327     simple_test "$@"
1328 }
1329
1330 simple_test_command ()
1331 {
1332     unit_test "$@"
1333 }
1334
1335 # Run an NFS eventscript iteratively.
1336 #
1337 # - 1st argument is the number of iterations.
1338 #
1339 # - 2nd argument is the NFS/RPC service being tested
1340 #
1341 #   rpcinfo (or $service_check_cmd) is used on each iteration to test
1342 #   the availability of the service
1343 #
1344 #   If this is not set or null then no RPC service is checked and the
1345 #   required output is not reset on each iteration.  This is useful in
1346 #   baseline tests to confirm that the eventscript and test
1347 #   infrastructure is working correctly.
1348 #
1349 # - Subsequent arguments come in pairs: an iteration number and
1350 #   something to eval before that iteration.  Each time an iteration
1351 #   number is matched the associated argument is given to eval after
1352 #   the default setup is done.  The iteration numbers need to be given
1353 #   in ascending order.
1354 #
1355 #   These arguments can allow a service to be started or stopped
1356 #   before a particular iteration.
1357 #
1358 nfs_iterate_test ()
1359 {
1360     _repeats="$1"
1361     _rpc_service="$2"
1362     if [ -n "$2" ] ; then
1363         shift 2
1364     else
1365         shift
1366     fi
1367
1368     echo "Running $_repeats iterations of \"$script $event\" $args"
1369
1370     _iterate_failcount=0
1371     for _iteration in $(seq 1 $_repeats) ; do
1372         # This is not a numerical comparison because $1 will often not
1373         # be set.
1374         if [ "$_iteration" = "$1" ] ; then
1375             debug "##################################################"
1376             eval "$2"
1377             debug "##################################################"
1378             shift 2
1379         fi
1380         if [ -n "$_rpc_service" ] ; then
1381             _ok=false
1382             if [ -n "$service_check_cmd" ] ; then
1383                 if eval "$service_check_cmd" ; then
1384                     _ok=true
1385                 fi
1386             else
1387                 if rpcinfo -T tcp localhost "$_rpc_service" >/dev/null 2>&1 ; then
1388                     _ok=true
1389                 fi
1390             fi
1391
1392             if $_ok ; then
1393                 _iterate_failcount=0
1394             else
1395                 _iterate_failcount=$(($_iterate_failcount + 1))
1396             fi
1397             rpc_set_service_failure_response "$_rpc_service" $_iterate_failcount
1398         fi
1399         _out=$(simple_test 2>&1)
1400         _ret=$?
1401         if "$TEST_VERBOSE" || [ $_ret -ne 0 ] ; then
1402             echo "##################################################"
1403             echo "Iteration ${_iteration}:"
1404             echo "$_out"
1405         fi
1406         if [ $_ret -ne 0 ] ; then
1407             exit $_ret
1408         fi
1409     done
1410 }