Whamcloud - gitweb
b=20057 Autovetting and test-framework enhancements
[fs/lustre-release.git] / lustre / tests / recovery-double-scale.sh
1 #!/bin/bash
2
3 # All pairwise combinations of node failures.
4 # Was cmd3-17
5 #
6 # Author: Chris Cooper <ccooper@clusterfs.com>
7 #
8 # Script fails pair of nodes:
9 # --  in parallel by default
10 # --  in series if SERIAL is set
11
12 LUSTRE=${LUSTRE:-`dirname $0`/..}
13 SETUP=${SETUP:-""}
14 CLEANUP=${CLEANUP:-""}
15 . $LUSTRE/tests/test-framework.sh
16
17 init_test_env $@
18
19 . ${CONFIG:=$LUSTRE/tests/cfg/$NAME.sh}
20 init_logging
21 TESTSUITELOG=${TESTSUITELOG:-$TMP/$(basename $0 .sh)}
22 DEBUGLOG=$TESTSUITELOG.debug
23
24 cleanup_logs
25
26 exec 2>$DEBUGLOG
27 echo "--- env ---" >&2
28 env >&2
29 echo "--- env ---" >&2
30 set -x
31
32 [ "$SHARED_DIRECTORY" ] || \
33     { skip "$0: Empty SHARED_DIRECTORY" && exit 0; }
34
35 [ -n "$CLIENTS" ] || { skip "$0 Need two or more remote clients" && exit 0; }
36 [ $CLIENTCOUNT -ge 3 ] || \
37     { skip "$0 Need two or more remote clients, have $CLIENTCOUNT" && exit 0; }
38
39 END_RUN_FILE=${END_RUN_FILE:-$SHARED_DIRECTORY/end_run_file}
40 LOAD_PID_FILE=${LOAD_PID_FILE:-$TMP/client-load.pid}
41
42 remote_mds_nodsh && skip "remote MDS with nodsh" && exit 0
43 remote_ost_nodsh && skip "remote OST with nodsh" && exit 0
44
45 check_timeout || exit 1
46
47 build_test_filter
48
49 check_and_setup_lustre
50 rm -rf $DIR/[df][0-9]*
51
52 # the test node needs to be insulated from a lustre failure as much as possible,
53 # so not even loading the lustre modules is ideal.
54 # -- umount lustre
55 # -- remove hostname from clients list
56 zconf_umount $(hostname) $MOUNT
57 NODES_TO_USE=${NODES_TO_USE:-$CLIENTS}
58 NODES_TO_USE=$(exclude_items_from_list $NODES_TO_USE $(hostname))
59
60 check_progs_installed $NODES_TO_USE ${CLIENT_LOADS[@]}
61
62 MDTS=$(get_facets MDS)
63 OSTS=$(get_facets OST)
64
65 rm -f $END_RUN_FILE
66
67 reboot_recover_node () {
68     # item var contains a pair of clients if nodetype=clients
69     # I would prefer to have a list here
70     local item=$1
71     local nodetype=$2   
72     local timeout=$($LCTL get_param  -n timeout)
73
74     # MDS, OST item contains the facet
75     case $nodetype in
76        MDS|OST )    facet_failover $item
77                 [ "$SERIAL" ] && wait_recovery_complete $item $((timeout * 4)) || true
78                 ;;
79        clients) for c in ${item//,/ }; do
80                       shutdown_client $c
81                       boot_node $c
82                       echo "Reintegrating $c"
83                       # one client fails; need dk logs from this client only 
84                       zconf_mount $c $MOUNT || NODES="$c $(mdts_nodes) $(osts_nodes)" error_exit "zconf_mount failed"
85                  done
86                  start_client_loads $item
87                  ;;
88                 # script failure:
89                 # don't use error (), the logs from all nodes not needed
90        * )      echo "reboot_recover_node: nodetype=$nodetype. Must be one of 'MDS', 'OST', or 'clients'."
91                 exit 1;;
92     esac
93 }
94
95 get_item_type () {
96     local type=$1
97     local excluded=${2:-""}
98
99     local list
100     case $type in
101        MDS )    list=$MDTS;;
102        OST )    list=$OSTS;;
103        clients) list=$NODES_TO_USE
104                 ;;
105                 # script failure:
106                 # don't use error (), the logs from all nodes not needed
107        * )      echo "Invalid type=$type. Must be one of 'MDS', 'OST', or 'clients'."
108                 exit 1;;
109     esac
110
111     [ "$excluded" ] && list=$(exclude_items_from_list $list $excluded)
112     # empty list
113     if [ ! "$(echo $list)" ]; then
114         echo
115         return
116     fi
117
118     item=$(get_random_entry $list)
119     if [ "$type" = clients ] ; then
120         item="$item $(get_random_entry $(exclude_items_from_list $list $item))"
121         item=$(comma_list $item)
122     fi
123     echo $item
124 }
125
126 # failover_pair
127 #
128 # for the two nodetypes specified, chooses a random node(s) from each
129 # class, reboots the nodes sequentially, and then restarts lustre on
130 # the nodes.
131 failover_pair() {
132     local type1=$1
133     local type2=$2
134     local title=$3
135
136     local client_nodes=""
137     local item1=
138     local item2=
139     local client1=
140     local client2=
141
142     log "
143 ==== START === $title "
144
145     item1=$(get_item_type $type1)
146     [ "$item1" ] || \
147         { echo "type1=$type1 item1 is empty" && return 0; }
148     item2=$(get_item_type $type2 $item1)
149     [ "$item2" ] || \
150         { echo "type1=$type1 item1=$item1 type2=$type2 item2=$item2 is empty" && return 0; }
151
152     # Check that our client loads are still running. If any have died,
153     # that means they have died outside of recovery, which is unacceptable.
154     log "==== Checking the clients loads BEFORE failover -- failure NOT OK"
155
156     # FIXME. need print summary on exit
157     if ! check_client_loads $NODES_TO_USE; then
158         exit 4
159     fi
160
161     log "Done checking client loads. Failing type1=$type1 item1=$item1 ... "
162
163     reboot_recover_node $item1 $type1
164
165     # Hendrix test17 description: 
166     # Introduce a failure, wait at
167     # least 5 minutes (for recovery),
168     # introduce a 2nd
169     # failure, and wait another 5
170     # minutes
171
172     # reboot_recover_node waits recovery in according to
173     # SERIAL value.
174     # We have a "double failures" if SERIAL is not set,
175     # do not need a sleep between failures for "double failures"
176
177     log "                            Failing type2=$type2 item2=$item2 ... "    
178     reboot_recover_node $item2 $type2
179
180     # Client loads are allowed to die while in recovery, so we just
181     # restart them.
182     log "==== Checking the clients loads AFTER  failovers -- ERRORS_OK=$ERRORS_OK"
183     restart_client_loads $NODES_TO_USE $ERRORS_OK || return $? 
184     log "Done checking / re-Starting client loads. PASS"
185     return 0
186 }
187
188 summary_and_cleanup () {
189     local rc=$?
190     trap 0
191
192     # Having not empty END_RUN_FILE means the failed loads only
193     if [ -s $END_RUN_FILE ]; then
194         echo "Found the END_RUN_FILE file: $END_RUN_FILE"
195         cat $END_RUN_FILE
196         local END_RUN_NODE=
197         read END_RUN_NODE < $END_RUN_FILE
198
199         # a client load will end (i.e. fail) if it finds
200         # the end run file.  that does not mean that that client load
201         # actually failed though.  the first node in the END_RUN_NODE is
202         # the one we are really interested in.
203         if [ -n "$END_RUN_NODE" ]; then
204             var=$(client_var_name $END_RUN_NODE)_load
205             echo "Client load failed on node $END_RUN_NODE"
206             echo
207             echo "client $END_RUN_NODE load debug output :"
208             local logfile=${TESTSUITELOG}_run_${!var}.sh-${END_RUN_NODE}.debug 
209             do_node ${END_RUN_NODE} "set -x; [ -e $logfile ] && cat $logfile " || true
210         fi
211         rc=1
212     fi
213
214     echo $(date +'%F %H:%M:%S') Terminating clients loads ...
215     echo "$0" >> $END_RUN_FILE
216     local result=PASS
217     [ $rc -eq 0 ] || result=FAIL
218
219     log "
220 Server failover period: $FAILOVER_PERIOD seconds
221 Exited after:           $ELAPSED seconds
222 Status: $result: rc=$rc"
223
224     # make sure the client loads die
225     do_nodes $NODES_TO_USE "set -x; test -f $TMP/client-load.pid && \
226         { kill -s TERM \$(cat $TMP/client-load.pid) || true; }"
227
228     # and free up the pdshes that started them, if any are still around
229     if [ -n "$CLIENT_LOAD_PIDS" ]; then
230         kill $CLIENT_LOAD_PIDS || true
231         sleep 5
232         kill -9 $CLIENT_LOAD_PIDS || true
233     fi
234
235     if [ $rc -ne 0 ]; then
236         # we are interested in only on failed clients and servers
237         local failedclients=$(cat $END_RUN_FILE | grep -v $0)
238         # FIXME: need ostfailover-s nodes also for FLAVOR=OST
239         local product=$(gather_logs $(comma_list $(osts_nodes) \
240                                  $(mdts_nodes) $mdsfailover_HOST $failedclients))
241         echo logs files $product
242     fi
243
244     [ $rc -eq 0 ] && zconf_mount $(hostname) $MOUNT
245     exit $rc
246 }
247
248 trap summary_and_cleanup EXIT TERM INT
249
250 #
251 # MAIN
252 #
253 log "-----============= $0 starting =============-----"
254
255 START_TS=$(date +%s)
256 CURRENT_TS=$START_TS
257 ELAPSED=0
258
259 # Set SERIAL to serialize the failure through a recovery of the first failure. 
260 SERIAL=${SERIAL:-""}
261 ERRORS_OK="yes"
262
263 [ "$SERIAL" ] && ERRORS_OK="" 
264
265 FAILOVER_PERIOD=${FAILOVER_PERIOD:-$((60*5))} # 5 minutes
266
267 # Start client loads.
268 start_client_loads $NODES_TO_USE
269 echo clients load pids:
270 if ! do_nodes $NODES_TO_USE "set -x; echo \$(hostname): && cat $TMP/client-load.pid"; then
271     if [ -e $DEBUGLOG ]; then
272         exec 2<&-
273         cat $DEBUGLOG
274         exit 3
275     fi
276 fi
277
278 # FIXME: Do we want to have an initial sleep period where the clients 
279 # just run before introducing a failure?
280 sleep $FAILOVER_PERIOD
281
282 #CMD_TEST_NUM=17.1
283 failover_pair MDS OST     "test 1: failover MDS, then OST =========="
284 sleep $FAILOVER_PERIOD
285
286 #CMD_TEST_NUM=17.2
287 failover_pair MDS clients "test 2: failover MDS, then 2 clients ===="
288 sleep $FAILOVER_PERIOD
289
290 #CMD_TEST_NUM=17.3
291 if [ $MDSCOUNT -gt 1 ]; then
292     failover_pair MDS MDS     "test 3: failover MDS, then another MDS =="
293     sleep $FAILOVER_PERIOD
294 else
295     skip "$0 : $MDSCOUNT < 2 MDTs, test 3 skipped"
296 fi 
297
298 #CMD_TEST_NUM=17.4
299 if [ $OSTCOUNT -gt 1 ]; then
300     failover_pair OST OST     "test 4: failover OST, then another OST =="
301     sleep $FAILOVER_PERIOD
302 else
303     skip "$0 : $OSTCOUNT < 2 OSTs, test 4 skipped"
304 fi 
305
306 #CMD_TEST_NUM=17.5
307 failover_pair OST clients "test 5: failover OST, then 2 clients ===="
308 sleep $FAILOVER_PERIOD
309
310 #CMD_TEST_NUM=17.6
311 failover_pair OST MDS     "test 6: failover OST, then MDS =========="
312 sleep $FAILOVER_PERIOD
313
314 #CMD_TEST_NUM=17.7
315 failover_pair clients MDS "test 7: failover 2 clients, then MDS ===="
316 sleep $FAILOVER_PERIOD
317
318 #CMD_TEST_NUM=17.8
319 #failover_pair clients OST "test 8: failover 2 clients, then OST ===="
320 sleep $FAILOVER_PERIOD
321
322 #CMD_TEST_NUM=17.9
323 if [ $CLIENTCOUNT -ge 5 ]; then
324     failover_pair clients clients "test 9: failover 2 clients, then 2 different clients =="
325     sleep $FAILOVER_PERIOD
326 fi
327 log "==== Checking the clients loads AFTER  all failovers -- failure NOT OK"
328 if ! check_client_loads $NODES_TO_USE; then
329     log "Client load failed after failover. Exiting"
330     exit 5
331 fi
332
333 CURRENT_TS=$(date +%s)
334 ELAPSED=$((CURRENT_TS - START_TS))
335
336 log "Completed successfully in $ELAPSED seconds"
337
338 exit 0