ctdb-tests: Stop cross-talk between reclock tests
[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 setup_winbind ()
843 {
844     setup_ctdb
845
846     service_name="winbind"
847
848     if [ "$1" != "down" ] ; then
849
850         debug "Marking Winbind service as up and managed by CTDB"
851         # Get into known state.
852         eventscript_call ctdb_service_managed
853
854         service "winbind" force-started
855
856         export CTDB_MANAGED_SERVICES="foo winbind bar"
857
858         export FAKE_WBINFO_FAIL="no"
859
860     else
861         debug "Marking Winbind service as down and not managed by CTDB"
862         # Get into known state.
863         eventscript_call ctdb_service_unmanaged
864
865         service "winbind" force-stopped
866
867         export CTDB_MANAGED_SERVICES="foo bar"
868         unset CTDB_MANAGES_WINBIND
869
870         export FAKE_WBINFO_FAIL="yes"
871     fi
872 }
873
874 wbinfo_down ()
875 {
876     debug "Making wbinfo commands fail"
877     FAKE_WBINFO_FAIL="yes"
878 }
879
880 ######################################################################
881
882 # NFS fakery
883
884 setup_nfs ()
885 {
886     setup_ctdb
887
888     service_name="nfs"
889
890     export FAKE_RPCINFO_SERVICES=""
891
892     export CTDB_NFS_SKIP_SHARE_CHECK="no"
893
894     export RPCNFSDCOUNT
895
896     # This doesn't even need to exist
897     export CTDB_NFS_EXPORTS_FILE="$EVENTSCRIPTS_TESTS_VAR_DIR/etc-exports"
898
899     # Reset the failcounts for nfs services.
900     eventscript_call eval rm -f '$ctdb_fail_dir/nfs_*'
901
902     if [ "$1" != "down" ] ; then
903         debug "Setting up NFS environment: all RPC services up, NFS managed by CTDB"
904
905         eventscript_call ctdb_service_managed
906         service "nfs" force-started
907         service "nfslock" force-started
908
909         export CTDB_MANAGED_SERVICES="foo nfs bar"
910
911         rpc_services_up \
912             "portmapper" "nfs" "mountd" "rquotad" "nlockmgr" "status"
913
914         nfs_setup_fake_threads "nfsd"
915         nfs_setup_fake_threads "rpc.foobar"  # Just set the variable to empty
916     else
917         debug "Setting up NFS environment: all RPC services down, NFS not managed by CTDB"
918
919         eventscript_call ctdb_service_unmanaged
920         service "nfs" force-stopped
921         service "nfslock" force-stopped
922
923         export CTDB_MANAGED_SERVICES="foo bar"
924         unset CTDB_MANAGES_NFS
925     fi
926
927     # This is really nasty.  However, when we test NFS we don't
928     # actually test statd-callout. If we leave it there then left
929     # over, backgrounded instances of statd-callout will do horrible
930     # things with the "ctdb ip" stub and cause the actual
931     # statd-callout tests that follow to fail.
932     rm "${CTDB_BASE}/statd-callout"
933 }
934
935 setup_nfs_ganesha ()
936 {
937     setup_nfs "$@"
938     export CTDB_NFS_CALLOUT="${CTDB_BASE}/nfs-ganesha-callout"
939     if [ "$1" != "down" ] ; then
940         export CTDB_MANAGES_NFS="yes"
941     fi
942
943     export CTDB_NFS_SKIP_SHARE_CHECK="yes"
944 }
945
946 rpc_services_down ()
947 {
948     for _i ; do
949         debug "Marking RPC service \"${_i}\" as unavailable"
950         FAKE_RPCINFO_SERVICES=$(echo "$FAKE_RPCINFO_SERVICES" | sed -r -e "s@[[:space:]]*${_i}:[0-9]+:[0-9]+@@g")
951     done
952 }
953
954 rpc_services_up ()
955 {
956     for _i ; do
957         debug "Marking RPC service \"${_i}\" as available"
958         case "$_i" in
959             portmapper) _t="2:4" ;;
960             nfs)        _t="2:3" ;;
961             mountd)     _t="1:3" ;;
962             rquotad)    _t="1:2" ;;
963             nlockmgr)   _t="3:4" ;;
964             status)     _t="1:1" ;;
965             *) die "Internal error - unsupported RPC service \"${_i}\"" ;;
966         esac
967
968         FAKE_RPCINFO_SERVICES="${FAKE_RPCINFO_SERVICES}${FAKE_RPCINFO_SERVICES:+ }${_i}:${_t}"
969     done
970 }
971
972
973 nfs_load_config ()
974 {
975     _etc="$CTDB_SYS_ETCDIR" # shortcut for readability
976     for _c in "$_etc/sysconfig/nfs" "$_etc/default/nfs" "$_etc/ctdb/sysconfig/nfs" ; do
977         if [ -r "$_c" ] ; then
978             . "$_c"
979             break
980         fi
981     done
982 }
983
984 nfs_setup_fake_threads ()
985 {
986     _prog="$1" ; shift
987
988     case "$_prog" in
989         nfsd)
990             export PROCFS_PATH=$(mktemp -d --tmpdir="$EVENTSCRIPTS_TESTS_VAR_DIR")
991             _threads="${PROCFS_PATH}/fs/nfsd/threads"
992             mkdir -p $(dirname "$_threads")
993             echo $# >"$_threads"
994             export FAKE_NFSD_THREAD_PIDS="$*"
995             ;;
996         *)
997             export FAKE_RPC_THREAD_PIDS="$*"
998             ;;
999     esac
1000 }
1001
1002 program_stack_traces ()
1003 {
1004     _prog="$1"
1005     _max="${2:-1}"
1006
1007     _count=1
1008     for _pid in ${FAKE_NFSD_THREAD_PIDS:-$FAKE_RPC_THREAD_PIDS} ; do
1009         [ $_count -le $_max ] || break
1010
1011         cat <<EOF
1012 Stack trace for ${_prog}[${_pid}]:
1013 [<ffffffff87654321>] fake_stack_trace_for_pid_${_pid}/stack+0x0/0xff
1014 EOF
1015         _count=$(($_count + 1))
1016     done
1017 }
1018
1019 guess_output ()
1020 {
1021     case "$1" in
1022         $CTDB_NFS_CALLOUT\ start\ nlockmgr)
1023             echo "&Starting nfslock: OK"
1024             ;;
1025         $CTDB_NFS_CALLOUT\ start\ nfs)
1026             cat <<EOF
1027 &Starting nfslock: OK
1028 &Starting nfs: OK
1029 EOF
1030             ;;
1031         *)
1032             : # Nothing
1033     esac
1034 }
1035
1036 # Set the required result for a particular RPC program having failed
1037 # for a certain number of iterations.  This is probably still a work
1038 # in progress.  Note that we could hook aggressively
1039 # nfs_check_rpc_service() to try to implement this but we're better
1040 # off testing nfs_check_rpc_service() using independent code...  even
1041 # if it is incomplete and hacky.  So, if the 60.nfs eventscript
1042 # changes and the tests start to fail then it may be due to this
1043 # function being incomplete.
1044 rpc_set_service_failure_response ()
1045 {
1046     _rpc_service="$1"
1047     _numfails="${2:-1}" # default 1
1048
1049     # Default
1050     ok_null
1051     if [ $_numfails -eq 0 ] ; then
1052         return
1053     fi
1054
1055     nfs_load_config
1056
1057     # A handy newline.  :-)
1058     _nl="
1059 "
1060
1061     _dir="${CTDB_NFS_CHECKS_DIR:-${CTDB_BASE}/nfs-checks.d}"
1062
1063     _file=$(ls "$_dir"/[0-9][0-9]."${_rpc_service}.check")
1064     [ -r "$_file" ] || die "RPC check file \"$_file\" does not exist or is not unique"
1065
1066     _out=$(mktemp --tmpdir="$EVENTSCRIPTS_TESTS_VAR_DIR")
1067     _rc_file=$(mktemp --tmpdir="$EVENTSCRIPTS_TESTS_VAR_DIR")
1068
1069     (
1070         # Subshell to restrict scope variables...
1071
1072         # Defaults
1073         family="tcp"
1074         version=""
1075         unhealthy_after=1
1076         restart_every=0
1077         service_stop_cmd=""
1078         service_start_cmd=""
1079         service_check_cmd=""
1080         service_debug_cmd=""
1081
1082         # Don't bother syntax checking, eventscript does that...
1083         . "$_file"
1084
1085         # Just use the first version, or use default.  This is dumb but
1086         # handles all the cases that we care about now...
1087         if [ -n "$version" ] ; then
1088             _ver="${version%% *}"
1089         else
1090             case "$_rpc_service" in
1091                 portmapper) _ver="" ;;
1092                 *)          _ver=1  ;;
1093             esac
1094         fi
1095         _rpc_check_out="\
1096 $_rpc_service failed RPC check:
1097 rpcinfo: RPC: Program not registered
1098 program $_rpc_service${_ver:+ version }${_ver} is not available"
1099
1100         if [ $unhealthy_after -gt 0 -a $_numfails -ge $unhealthy_after ] ; then
1101             _unhealthy=true
1102             echo 1 >"$_rc_file"
1103             echo "ERROR: ${_rpc_check_out}" >>"$_out"
1104         else
1105             _unhealthy=false
1106             echo 0 >"$_rc_file"
1107         fi
1108
1109         if [ $restart_every -gt 0 ] && \
1110                    [ $(($_numfails % $restart_every)) -eq 0 ] ; then
1111             if ! $_unhealthy ; then
1112                 echo "WARNING: ${_rpc_check_out}" >>"$_out"
1113             fi
1114
1115             echo "Trying to restart service \"${_rpc_service}\"..." >>"$_out"
1116
1117             if [ -n "$service_debug_cmd" ] ; then
1118                 $service_debug_cmd 2>&1 >>"$_out"
1119             fi
1120
1121             guess_output "$service_start_cmd" >>"$_out"
1122         fi
1123     )
1124
1125     read _rc <"$_rc_file"
1126     required_result $_rc <"$_out"
1127
1128     rm -f "$_out" "$_rc_file"
1129 }
1130
1131 ######################################################################
1132
1133 # Recovery lock fakery
1134
1135 cleanup_reclock ()
1136 {
1137         _pattern="${script_dir}/${script}"
1138         while pgrep -f "$_pattern" >/dev/null ; do
1139                 echo "Waiting for backgrounded ${script} to exit..."
1140                 (FAKE_SLEEP_REALLY=yes sleep 1)
1141         done
1142 }
1143
1144 setup_reclock ()
1145 {
1146         CTDB_RECOVERY_LOCK=$(mktemp --tmpdir="$EVENTSCRIPTS_TESTS_VAR_DIR")
1147         export CTDB_RECOVERY_LOCK
1148
1149         test_cleanup cleanup_reclock
1150 }
1151
1152 ######################################################################
1153
1154 # VSFTPD fakery
1155
1156 setup_vsftpd ()
1157 {
1158     service_name="vsftpd"
1159
1160     if [ "$1" != "down" ] ; then
1161         die "setup_vsftpd up not implemented!!!"
1162     else
1163         debug "Setting up VSFTPD environment: service down, not managed by CTDB"
1164
1165         eventscript_call ctdb_service_unmanaged
1166         service vsftpd force-stopped
1167
1168         export CTDB_MANAGED_SERVICES="foo"
1169         unset CTDB_MANAGES_VSFTPD
1170     fi
1171 }
1172
1173 ######################################################################
1174
1175 # HTTPD fakery
1176
1177 setup_httpd ()
1178 {
1179     if [ "$1" != "down" ] ; then
1180         die "setup_httpd up not implemented!!!"
1181     else
1182         debug "Setting up HTTPD environment: service down, not managed by CTDB"
1183
1184         for service_name in "apache2" "httpd" ; do
1185             eventscript_call ctdb_service_unmanaged
1186             service "$service_name" force-stopped
1187         done
1188
1189         export CTDB_MANAGED_SERVICES="foo"
1190         unset CTDB_MANAGES_HTTPD
1191     fi
1192 }
1193
1194 ######################################################################
1195
1196 # multipathd fakery
1197
1198 setup_multipathd ()
1199 {
1200     for i ; do
1201         case "$i" in
1202             \!*)
1203                 _t="${i#!}"
1204                 echo "Marking ${_t} as having no active paths"
1205                 FAKE_MULTIPATH_FAILURES="${FAKE_MULTIPATH_FAILURES}${FAKE_MULTIPATH+FAILURES:+ }${_t}"
1206                 ;;
1207             *)
1208                 _t="$i"         
1209         esac
1210         CTDB_MONITOR_MPDEVICES="${CTDB_MONITOR_MPDEVICES}${CTDB_MONITOR_MPDEVICES:+ }${_t}"
1211     done
1212
1213     export CTDB_MONITOR_MPDEVICES FAKE_MULTIPATH_FAILURES
1214     export FAKE_SLEEP_FORCE=0.1
1215 }
1216
1217 ######################################################################
1218
1219 # Result and test functions
1220
1221 # Set some globals and print the summary.
1222 define_test ()
1223 {
1224     desc="$1"
1225
1226     _f=$(basename "$0" ".sh")
1227
1228     # Remaining format should be NN.service.event.NNN or NN.service.NNN:
1229     _num="${_f##*.}"
1230     _f="${_f%.*}"
1231
1232     case "$_f" in
1233         [0-9][0-9].*.*)
1234             script="${_f%.*}"
1235             event="${_f##*.}"
1236             script_dir="${CTDB_BASE}/events.d"
1237             ;;
1238         [0-9][0-9].*)
1239             script="$_f"
1240             unset event
1241             script_dir="${CTDB_BASE}/events.d"
1242             ;;
1243         *.*)
1244             script="${_f%.*}"
1245             event="${_f##*.}"
1246             script_dir="${CTDB_BASE}"
1247             ;;
1248         *)
1249             script="${_f%.*}"
1250             unset event
1251             script_dir="${CTDB_BASE}"
1252     esac
1253
1254     [ -r "${script_dir}/${script}" ] || \
1255         die "Internal error - unable to find script \"${script_dir}/${script}\""
1256
1257     printf "%-17s %-10s %-4s - %s\n\n" "$script" "$event" "$_num" "$desc"
1258 }
1259
1260 # Run an eventscript once.  The test passes if the return code and
1261 # output match those required.
1262
1263 # Any args are passed to the eventscript.
1264
1265 simple_test ()
1266 {
1267     [ -n "$event" ] || die 'simple_test: $event not set'
1268
1269     args="$@"
1270
1271     test_header ()
1272     {
1273         echo "Running script \"$script $event${args:+ }$args\""
1274     }
1275
1276     extra_header ()
1277     {
1278         cat <<EOF
1279
1280 ##################################################
1281 CTDB_BASE="$CTDB_BASE"
1282 CTDB_SYS_ETCDIR="$CTDB_SYS_ETCDIR"
1283 ctdb client is "$(which ctdb)"
1284 ip command is "$(which ip)"
1285 EOF
1286     }
1287
1288     script_test "${script_dir}/${script}" "$event" "$@"
1289
1290     reset_test_header
1291     reset_extra_header
1292 }
1293
1294 simple_test_event ()
1295 {
1296     # If something has previously failed then don't continue.
1297     : ${_passed:=true}
1298     $_passed || return 1
1299
1300     event="$1" ; shift
1301     echo "=================================================="
1302     simple_test "$@"
1303 }
1304
1305 simple_test_command ()
1306 {
1307     unit_test "$@"
1308 }
1309
1310 # Run an NFS eventscript iteratively.
1311 #
1312 # - 1st argument is the number of iterations.
1313 #
1314 # - 2nd argument is the NFS/RPC service being tested
1315 #
1316 #   rpcinfo (or $service_check_cmd) is used on each iteration to test
1317 #   the availability of the service
1318 #
1319 #   If this is not set or null then no RPC service is checked and the
1320 #   required output is not reset on each iteration.  This is useful in
1321 #   baseline tests to confirm that the eventscript and test
1322 #   infrastructure is working correctly.
1323 #
1324 # - Subsequent arguments come in pairs: an iteration number and
1325 #   something to eval before that iteration.  Each time an iteration
1326 #   number is matched the associated argument is given to eval after
1327 #   the default setup is done.  The iteration numbers need to be given
1328 #   in ascending order.
1329 #
1330 #   These arguments can allow a service to be started or stopped
1331 #   before a particular iteration.
1332 #
1333 nfs_iterate_test ()
1334 {
1335     _repeats="$1"
1336     _rpc_service="$2"
1337     if [ -n "$2" ] ; then
1338         shift 2
1339     else
1340         shift
1341     fi
1342
1343     echo "Running $_repeats iterations of \"$script $event\" $args"
1344
1345     _iterate_failcount=0
1346     for _iteration in $(seq 1 $_repeats) ; do
1347         # This is not a numerical comparison because $1 will often not
1348         # be set.
1349         if [ "$_iteration" = "$1" ] ; then
1350             debug "##################################################"
1351             eval "$2"
1352             debug "##################################################"
1353             shift 2
1354         fi
1355         if [ -n "$_rpc_service" ] ; then
1356             _ok=false
1357             if [ -n "$service_check_cmd" ] ; then
1358                 if eval "$service_check_cmd" ; then
1359                     _ok=true
1360                 fi
1361             else
1362                 if rpcinfo -T tcp localhost "$_rpc_service" >/dev/null 2>&1 ; then
1363                     _ok=true
1364                 fi
1365             fi
1366
1367             if $_ok ; then
1368                 _iterate_failcount=0
1369             else
1370                 _iterate_failcount=$(($_iterate_failcount + 1))
1371             fi
1372             rpc_set_service_failure_response "$_rpc_service" $_iterate_failcount
1373         fi
1374         _out=$(simple_test 2>&1)
1375         _ret=$?
1376         if "$TEST_VERBOSE" || [ $_ret -ne 0 ] ; then
1377             echo "##################################################"
1378             echo "Iteration ${_iteration}:"
1379             echo "$_out"
1380         fi
1381         if [ $_ret -ne 0 ] ; then
1382             exit $_ret
1383         fi
1384     done
1385 }