Whamcloud - gitweb
70768d642ef6e36c7524e73e57b556f7f2fb9b7c
[fs/lustre-release.git] / lustre / tests / test-framework.sh
1 #!/bin/bash
2
3 trap 'print_summary && print_stack_trace | tee $TF_FAIL && \
4     echo "$TESTSUITE: FAIL: test-framework exiting on error"' ERR
5 set -e
6
7 export LANG=en_US
8 export REFORMAT=${REFORMAT:-""}
9 export WRITECONF=${WRITECONF:-""}
10 export VERBOSE=${VERBOSE:-false}
11 export GSS=${GSS:-false}
12 export GSS_SK=${GSS_SK:-false}
13 export GSS_KRB5=false
14 export GSS_PIPEFS=false
15 export SHARED_KEY=${SHARED_KEY:-false}
16 export SK_PATH=${SK_PATH:-/tmp/test-framework-keys}
17 export SK_OM_PATH=$SK_PATH'/tmp-request-mount'
18 export SK_MOUNTED=${SK_MOUNTED:-false}
19 export SK_FLAVOR=${SK_FLAVOR:-ski}
20 export SK_NO_KEY=${SK_NO_KEY:-true}
21 export SK_UNIQUE_NM=${SK_UNIQUE_NM:-false}
22 export SK_S2S=${SK_S2S:-false}
23 export SK_S2SNM=${SK_S2SNM:-TestFrameNM}
24 export SK_S2SNMCLI=${SK_S2SNMCLI:-TestFrameNMCli}
25 export SK_SKIPFIRST=${SK_SKIPFIRST:-true}
26 export IDENTITY_UPCALL=default
27 export QUOTA_AUTO=1
28 export FLAKEY=${FLAKEY:-true}
29 # specify environment variable containing batch job name for server statistics
30 export JOBID_VAR=${JOBID_VAR:-"procname_uid"}  # or "existing" or "disable"
31
32 #export PDSH="pdsh -S -Rssh -w"
33 export MOUNT_CMD=${MOUNT_CMD:-"mount -t lustre"}
34 export UMOUNT=${UMOUNT:-"umount -d"}
35
36 export LSNAPSHOT_CONF="/etc/ldev.conf"
37 export LSNAPSHOT_LOG="/var/log/lsnapshot.log"
38
39 export DATA_SEQ_MAX_WIDTH=0x1ffffff
40
41 # sles12 umount has a issue with -d option
42 [ -e /etc/SuSE-release ] && grep -w VERSION /etc/SuSE-release | grep -wq 12 && {
43         export UMOUNT="umount"
44 }
45
46 # function used by scripts run on remote nodes
47 LUSTRE=${LUSTRE:-$(cd $(dirname $0)/..; echo $PWD)}
48 . $LUSTRE/tests/functions.sh
49 . $LUSTRE/tests/yaml.sh
50
51 export LD_LIBRARY_PATH=${LUSTRE}/utils/.libs:${LUSTRE}/utils:${LD_LIBRARY_PATH}
52
53 LUSTRE_TESTS_CFG_DIR=${LUSTRE_TESTS_CFG_DIR:-${LUSTRE}/tests/cfg}
54
55 EXCEPT_LIST_FILE=${EXCEPT_LIST_FILE:-${LUSTRE_TESTS_CFG_DIR}/tests-to-skip.sh}
56
57 if [ -f "$EXCEPT_LIST_FILE" ]; then
58         echo "Reading test skip list from $EXCEPT_LIST_FILE"
59         cat $EXCEPT_LIST_FILE
60         . $EXCEPT_LIST_FILE
61 fi
62
63 # check config files for options in decreasing order of preference
64 [ -z "$MODPROBECONF" -a -f /etc/modprobe.d/lustre.conf ] &&
65     MODPROBECONF=/etc/modprobe.d/lustre.conf
66 [ -z "$MODPROBECONF" -a -f /etc/modprobe.d/Lustre ] &&
67     MODPROBECONF=/etc/modprobe.d/Lustre
68 [ -z "$MODPROBECONF" -a -f /etc/modprobe.conf ] &&
69     MODPROBECONF=/etc/modprobe.conf
70
71 sanitize_parameters() {
72         for i in DIR DIR1 DIR2 MOUNT MOUNT1 MOUNT2
73         do
74                 local path=${!i}
75
76                 if [ -d "$path" ]; then
77                         eval export $i=$(echo $path | sed -r 's/\/+$//g')
78                 fi
79         done
80 }
81 assert_DIR () {
82         local failed=""
83         [[ $DIR/ = $MOUNT/* ]] ||
84                 { failed=1 && echo "DIR=$DIR not in $MOUNT. Aborting."; }
85         [[ $DIR1/ = $MOUNT1/* ]] ||
86                 { failed=1 && echo "DIR1=$DIR1 not in $MOUNT1. Aborting."; }
87         [[ $DIR2/ = $MOUNT2/* ]] ||
88                 { failed=1 && echo "DIR2=$DIR2 not in $MOUNT2. Aborting"; }
89
90         [ -n "$failed" ] && exit 99 || true
91 }
92
93 usage() {
94         echo "usage: $0 [-r] [-f cfgfile]"
95         echo "       -r: reformat"
96
97         exit
98 }
99
100 print_summary () {
101         trap 0
102         [ -z "$DEFAULT_SUITES" ] && return 0
103         [ -n "$ONLY" ] && echo "WARNING: ONLY is set to $(echo $ONLY)"
104         local details
105         local form="%-13s %-17s %-9s %s %s\n"
106
107         printf "$form" "status" "script" "Total(sec)" "E(xcluded) S(low)"
108         echo "---------------------------------------------------------------"
109         for O in $DEFAULT_SUITES; do
110                 O=$(echo $O  | tr "-" "_" | tr "[:lower:]" "[:upper:]")
111                 [ "${!O}" = "no" ] && continue || true
112                 local o=$(echo $O  | tr "[:upper:]_" "[:lower:]-")
113                 local log=${TMP}/${o}.log
114
115                 if is_sanity_benchmark $o; then
116                     log=${TMP}/sanity-benchmark.log
117                 fi
118                 local slow=
119                 local skipped=
120                 local total=
121                 local status=Unfinished
122
123                 if [ -f $log ]; then
124                         skipped=$(grep excluded $log |
125                                 awk '{ printf " %s", $3 }' | sed 's/test_//g')
126                         slow=$(egrep "^PASS|^FAIL" $log |
127                                 tr -d "("| sed s/s\)$//g | sort -nr -k 3 |
128                                 head -n5 |  awk '{ print $2":"$3"s" }')
129                         total=$(grep duration $log | awk '{ print $2 }')
130                         if [ "${!O}" = "done" ]; then
131                                 status=Done
132                         fi
133                         if $DDETAILS; then
134                                 local durations=$(egrep "^PASS|^FAIL" $log |
135                                         tr -d "("| sed s/s\)$//g |
136                                         awk '{ print $2":"$3"|" }')
137                                 details=$(printf "%s\n%s %s %s\n" "$details" \
138                                         "DDETAILS" "$O" "$(echo $durations)")
139                         fi
140                 fi
141                 printf "$form" $status "$O" "${total}" "E=$skipped"
142                 printf "$form" "-" "-" "-" "S=$(echo $slow)"
143         done
144
145         for O in $DEFAULT_SUITES; do
146                 O=$(echo $O  | tr "-" "_" | tr "[:lower:]" "[:upper:]")
147                         if [ "${!O}" = "no" ]; then
148                                 printf "$form" "Skipped" "$O" ""
149                         fi
150         done
151
152         # print the detailed tests durations if DDETAILS=true
153         if $DDETAILS; then
154                 echo "$details"
155         fi
156 }
157
158 # Get information about the Lustre environment. The information collected
159 # will be used in Lustre tests.
160 # usage: get_lustre_env
161 # input: No required or optional arguments
162 # output: No return values, environment variables are exported
163
164 get_lustre_env() {
165         if ! $RPC_MODE; then
166                 export mds1_FSTYPE=${mds1_FSTYPE:-$(facet_fstype mds1)}
167                 export ost1_FSTYPE=${ost1_FSTYPE:-$(facet_fstype ost1)}
168
169                 export MGS_VERSION=$(lustre_version_code mgs)
170                 export MDS1_VERSION=$(lustre_version_code mds1)
171                 export OST1_VERSION=$(lustre_version_code ost1)
172                 export CLIENT_VERSION=$(lustre_version_code client)
173         fi
174
175         # Prefer using "mds1" directly instead of SINGLEMDS.
176         # Keep this for compat until it is removed from scripts.
177         export SINGLEMDS=${SINGLEMDS:-mds1}
178 }
179
180 init_test_env() {
181         export LUSTRE=$(absolute_path $LUSTRE)
182         export TESTSUITE=$(basename $0 .sh)
183         export TEST_FAILED=false
184         export FAIL_ON_SKIP_ENV=${FAIL_ON_SKIP_ENV:-false}
185         export RPC_MODE=${RPC_MODE:-false}
186         export DO_CLEANUP=${DO_CLEANUP:-true}
187         export KEEP_ZPOOL=${KEEP_ZPOOL:-false}
188         export CLEANUP_DM_DEV=false
189         export PAGE_SIZE=$(get_page_size client)
190         export NAME=${NAME:-local}
191
192         . ${CONFIG:=$LUSTRE/tests/cfg/$NAME.sh}
193
194         export MKE2FS=$MKE2FS
195         if [ -z "$MKE2FS" ]; then
196                 if which mkfs.ldiskfs >/dev/null 2>&1; then
197                         export MKE2FS=mkfs.ldiskfs
198                 else
199                         export MKE2FS=mke2fs
200                 fi
201         fi
202
203         export DEBUGFS=$DEBUGFS
204         if [ -z "$DEBUGFS" ]; then
205                 if which debugfs.ldiskfs >/dev/null 2>&1; then
206                         export DEBUGFS=debugfs.ldiskfs
207                 else
208                         export DEBUGFS=debugfs
209                 fi
210         fi
211
212         export TUNE2FS=$TUNE2FS
213         if [ -z "$TUNE2FS" ]; then
214                 if which tunefs.ldiskfs >/dev/null 2>&1; then
215                         export TUNE2FS=tunefs.ldiskfs
216                 else
217                         export TUNE2FS=tune2fs
218                 fi
219         fi
220
221         export E2LABEL=$E2LABEL
222         if [ -z "$E2LABEL" ]; then
223                 if which label.ldiskfs >/dev/null 2>&1; then
224                         export E2LABEL=label.ldiskfs
225                 else
226                         export E2LABEL=e2label
227                 fi
228         fi
229
230         export DUMPE2FS=$DUMPE2FS
231         if [ -z "$DUMPE2FS" ]; then
232                 if which dumpfs.ldiskfs >/dev/null 2>&1; then
233                         export DUMPE2FS=dumpfs.ldiskfs
234                 else
235                         export DUMPE2FS=dumpe2fs
236                 fi
237         fi
238
239         export E2FSCK=$E2FSCK
240         if [ -z "$E2FSCK" ]; then
241                 if which fsck.ldiskfs >/dev/null 2>&1; then
242                         export E2FSCK=fsck.ldiskfs
243                 else
244                          export E2FSCK=e2fsck
245                 fi
246         fi
247
248         export RESIZE2FS=$RESIZE2FS
249         if [ -z "$RESIZE2FS" ]; then
250                 if which resizefs.ldiskfs >/dev/null 2>&1; then
251                         export RESIZE2FS=resizefs.ldiskfs
252                 else
253                         export RESIZE2FS=resize2fs
254                 fi
255         fi
256
257         export LFSCK_ALWAYS=${LFSCK_ALWAYS:-"no"} # check fs after test suite
258         export FSCK_MAX_ERR=4   # File system errors left uncorrected
259
260         export ZFS=${ZFS:-zfs}
261         export ZPOOL=${ZPOOL:-zpool}
262         export ZDB=${ZDB:-zdb}
263         export PARTPROBE=${PARTPROBE:-partprobe}
264
265         #[ -d /r ] && export ROOT=${ROOT:-/r}
266         export TMP=${TMP:-$ROOT/tmp}
267         export TESTSUITELOG=${TMP}/${TESTSUITE}.log
268         export LOGDIR=${LOGDIR:-${TMP}/test_logs/$(date +%s)}
269         export TESTLOG_PREFIX=$LOGDIR/$TESTSUITE
270
271         export HOSTNAME=${HOSTNAME:-$(hostname -s)}
272         if ! echo $PATH | grep -q $LUSTRE/utils; then
273                 export PATH=$LUSTRE/utils:$PATH
274         fi
275         if ! echo $PATH | grep -q $LUSTRE/utils/gss; then
276                 export PATH=$LUSTRE/utils/gss:$PATH
277         fi
278         if ! echo $PATH | grep -q $LUSTRE/tests; then
279                 export PATH=$LUSTRE/tests:$PATH
280         fi
281         if ! echo $PATH | grep -q $LUSTRE/../lustre-iokit/sgpdd-survey; then
282                 export PATH=$LUSTRE/../lustre-iokit/sgpdd-survey:$PATH
283         fi
284         export LST=${LST:-"$LUSTRE/../lnet/utils/lst"}
285         [ ! -f "$LST" ] && export LST=$(which lst)
286         export LSTSH=${LSTSH:-"$LUSTRE/../lustre-iokit/lst-survey/lst.sh"}
287         [ ! -f "$LSTSH" ] && export LSTSH=$(which lst.sh)
288         export SGPDDSURVEY=${SGPDDSURVEY:-"$LUSTRE/../lustre-iokit/sgpdd-survey/sgpdd-survey")}
289         [ ! -f "$SGPDDSURVEY" ] && export SGPDDSURVEY=$(which sgpdd-survey)
290         export MCREATE=${MCREATE:-mcreate}
291         export MULTIOP=${MULTIOP:-multiop}
292         export MMAP_CAT=${MMAP_CAT:-mmap_cat}
293         export STATX=${STATX:-statx}
294         # Ubuntu, at least, has a truncate command in /usr/bin
295         # so fully path our truncate command.
296         export TRUNCATE=${TRUNCATE:-$LUSTRE/tests/truncate}
297         export FSX=${FSX:-$LUSTRE/tests/fsx}
298         export MDSRATE=${MDSRATE:-"$LUSTRE/tests/mpi/mdsrate"}
299         [ ! -f "$MDSRATE" ] && export MDSRATE=$(which mdsrate 2> /dev/null)
300         if ! echo $PATH | grep -q $LUSTRE/tests/racer; then
301                 export PATH=$LUSTRE/tests/racer:$PATH:
302         fi
303         if ! echo $PATH | grep -q $LUSTRE/tests/mpi; then
304                 export PATH=$LUSTRE/tests/mpi:$PATH
305         fi
306
307         export LNETCTL=${LNETCTL:-"$LUSTRE/../lnet/utils/lnetctl"}
308         [ ! -f "$LNETCTL" ] && export LNETCTL=$(which lnetctl 2> /dev/null)
309         export LCTL=${LCTL:-"$LUSTRE/utils/lctl"}
310         [ ! -f "$LCTL" ] && export LCTL=$(which lctl)
311         export LFS=${LFS:-"$LUSTRE/utils/lfs"}
312         [ ! -f "$LFS" ] && export LFS=$(which lfs)
313         export KSOCKLND_CONFIG=${KSOCKLND_CONFIG:-"$LUSTRE/scripts/ksocklnd-config"}
314         [ ! -f "$KSOCKLND_CONFIG" ] &&
315                 export KSOCKLND_CONFIG=$(which ksocklnd-config 2> /dev/null)
316
317         export PERM_CMD=$(echo ${PERM_CMD:-"$LCTL conf_param"})
318
319         export L_GETIDENTITY=${L_GETIDENTITY:-"$LUSTRE/utils/l_getidentity"}
320         if [ ! -f "$L_GETIDENTITY" ]; then
321                 if `which l_getidentity > /dev/null 2>&1`; then
322                         export L_GETIDENTITY=$(which l_getidentity)
323                 else
324                         export L_GETIDENTITY=NONE
325                 fi
326         fi
327         export LL_DECODE_FILTER_FID=${LL_DECODE_FILTER_FID:-"$LUSTRE/utils/ll_decode_filter_fid"}
328         [ ! -f "$LL_DECODE_FILTER_FID" ] &&
329                 export LL_DECODE_FILTER_FID="ll_decode_filter_fid"
330         export LL_DECODE_LINKEA=${LL_DECODE_LINKEA:-"$LUSTRE/utils/ll_decode_linkea"}
331         [ ! -f "$LL_DECODE_LINKEA" ] &&
332                 export LL_DECODE_LINKEA="ll_decode_linkea"
333         export MKFS=${MKFS:-"$LUSTRE/utils/mkfs.lustre"}
334         [ ! -f "$MKFS" ] && export MKFS="mkfs.lustre"
335         export TUNEFS=${TUNEFS:-"$LUSTRE/utils/tunefs.lustre"}
336         [ ! -f "$TUNEFS" ] && export TUNEFS="tunefs.lustre"
337         export CHECKSTAT="${CHECKSTAT:-"checkstat -v"} "
338         export LUSTRE_RMMOD=${LUSTRE_RMMOD:-$LUSTRE/scripts/lustre_rmmod}
339         [ ! -f "$LUSTRE_RMMOD" ] &&
340                 export LUSTRE_RMMOD=$(which lustre_rmmod 2> /dev/null)
341         export LUSTRE_ROUTES_CONVERSION=${LUSTRE_ROUTES_CONVERSION:-$LUSTRE/scripts/lustre_routes_conversion}
342         [ ! -f "$LUSTRE_ROUTES_CONVERSION" ] &&
343                 export LUSTRE_ROUTES_CONVERSION=$(which lustre_routes_conversion 2> /dev/null)
344         export LFS_MIGRATE=${LFS_MIGRATE:-$LUSTRE/scripts/lfs_migrate}
345         [ ! -f "$LFS_MIGRATE" ] &&
346                 export LFS_MIGRATE=$(which lfs_migrate 2> /dev/null)
347         export LR_READER=${LR_READER:-"$LUSTRE/utils/lr_reader"}
348         [ ! -f "$LR_READER" ] &&
349                 export LR_READER=$(which lr_reader 2> /dev/null)
350         [ -z "$LR_READER" ] && export LR_READER="/usr/sbin/lr_reader"
351         export LSOM_SYNC=${LSOM_SYNC:-"$LUSTRE/utils/llsom_sync"}
352         [ ! -f "$LSOM_SYNC" ] &&
353                 export LSOM_SYNC=$(which llsom_sync 2> /dev/null)
354         [ -z "$LSOM_SYNC" ] && export LSOM_SYNC="/usr/sbin/llsom_sync"
355         export LGSSD=${LGSSD:-"$LUSTRE/utils/gss/lgssd"}
356         [ "$GSS_PIPEFS" = "true" ] && [ ! -f "$LGSSD" ] &&
357                 export LGSSD=$(which lgssd)
358         export LSVCGSSD=${LSVCGSSD:-"$LUSTRE/utils/gss/lsvcgssd"}
359         [ ! -f "$LSVCGSSD" ] && export LSVCGSSD=$(which lsvcgssd 2> /dev/null)
360         export KRB5DIR=${KRB5DIR:-"/usr/kerberos"}
361         export DIR2
362         export SAVE_PWD=${SAVE_PWD:-$LUSTRE/tests}
363         export AT_MAX_PATH
364         export LDEV=${LDEV:-"$LUSTRE/scripts/ldev"}
365         [ ! -f "$LDEV" ] && export LDEV=$(which ldev 2> /dev/null)
366
367         export DMSETUP=${DMSETUP:-dmsetup}
368         export DM_DEV_PATH=${DM_DEV_PATH:-/dev/mapper}
369         export LOSETUP=${LOSETUP:-losetup}
370
371         if [ "$ACCEPTOR_PORT" ]; then
372                 export PORT_OPT="--port $ACCEPTOR_PORT"
373         fi
374
375         if $SHARED_KEY; then
376                 $RPC_MODE || echo "Using GSS shared-key feature"
377                 [ -n "$LGSS_SK" ] ||
378                         export LGSS_SK=$(which lgss_sk 2> /dev/null)
379                 [ -n "$LGSS_SK" ] ||
380                         export LGSS_SK="$LUSTRE/utils/gss/lgss_sk"
381                 [ -n "$LGSS_SK" ] ||
382                         error_exit "built with lgss_sk disabled! SEC=$SEC"
383                 GSS=true
384                 GSS_SK=true
385                 SEC=$SK_FLAVOR
386         fi
387
388         case "x$SEC" in
389                 xkrb5*)
390                 $RPC_MODE || echo "Using GSS/krb5 ptlrpc security flavor"
391                 which lgss_keyring > /dev/null 2>&1 ||
392                         error_exit "built with gss disabled! SEC=$SEC"
393                 GSS=true
394                 GSS_KRB5=true
395                 ;;
396         esac
397
398         case "x$IDUP" in
399                 xtrue)
400                         IDENTITY_UPCALL=true
401                         ;;
402                 xfalse)
403                         IDENTITY_UPCALL=false
404                         ;;
405         esac
406
407         export LOAD_MODULES_REMOTE=${LOAD_MODULES_REMOTE:-false}
408
409         # Paths on remote nodes, if different
410         export RLUSTRE=${RLUSTRE:-$LUSTRE}
411         export RPWD=${RPWD:-$PWD}
412         export I_MOUNTED=${I_MOUNTED:-"no"}
413         export AUSTER_CLEANUP=${AUSTER_CLEANUP:-false}
414         if [ ! -f /lib/modules/$(uname -r)/kernel/fs/lustre/mdt.ko -a \
415              ! -f /lib/modules/$(uname -r)/updates/kernel/fs/lustre/mdt.ko -a \
416              ! -f /lib/modules/$(uname -r)/extra/kernel/fs/lustre/mdt.ko -a \
417              ! -f $LUSTRE/mdt/mdt.ko ]; then
418             export CLIENTMODSONLY=yes
419         fi
420
421         export SHUTDOWN_ATTEMPTS=${SHUTDOWN_ATTEMPTS:-3}
422         export OSD_TRACK_DECLARES_LBUG=${OSD_TRACK_DECLARES_LBUG:-"yes"}
423
424         # command line
425
426         while getopts "rvwf:" opt $*; do
427                 case $opt in
428                         f) CONFIG=$OPTARG;;
429                         r) REFORMAT=yes;;
430                         v) VERBOSE=true;;
431                         w) WRITECONF=writeconf;;
432                         \?) usage;;
433                 esac
434         done
435
436         shift $((OPTIND - 1))
437         ONLY=${ONLY:-$*}
438
439         # print the durations of each test if "true"
440         DDETAILS=${DDETAILS:-false}
441         [ "$TESTSUITELOG" ] && rm -f $TESTSUITELOG || true
442         if ! $RPC_MODE; then
443                 rm -f $TMP/*active
444         fi
445
446         export TF_FAIL=${TF_FAIL:-$TMP/tf.fail}
447
448         # Constants used in more than one test script
449         export LOV_MAX_STRIPE_COUNT=2000
450         export DELETE_OLD_POOLS=${DELETE_OLD_POOLS:-false}
451         export KEEP_POOLS=${KEEP_POOLS:-false}
452         export PARALLEL=${PARALLEL:-"no"}
453
454         export BLCKSIZE=${BLCKSIZE:-4096}
455         export MACHINEFILE=${MACHINEFILE:-$TMP/$(basename $0 .sh).machines}
456         get_lustre_env
457
458         # use localrecov to enable recovery for local clients, LU-12722
459         [[ $MDS1_VERSION -lt $(version_code 2.13.52) ]] || {
460                 export MDS_MOUNT_OPTS=${MDS_MOUNT_OPTS:-"-o localrecov"}
461                 export MGS_MOUNT_OPTS=${MGS_MOUNT_OPTS:-"-o localrecov"}
462         }
463
464         [[ $OST1_VERSION -lt $(version_code 2.13.52) ]] ||
465                 export OST_MOUNT_OPTS=${OST_MOUNT_OPTS:-"-o localrecov"}
466 }
467
468 check_cpt_number() {
469         local facet=$1
470         local ncpts
471
472         ncpts=$(do_facet $facet "lctl get_param -n " \
473                 "cpu_partition_table 2>/dev/null| wc -l" || echo 1)
474
475         if [ $ncpts -eq 0 ]; then
476                 echo "1"
477         else
478                 echo $ncpts
479         fi
480 }
481
482 # Return a numeric version code based on a version string.  The version
483 # code is useful for comparison two version strings to see which is newer.
484 version_code() {
485         # split arguments like "1.8.6-wc3" into "1", "8", "6", "3"
486         eval set -- $(tr "[:punct:][a-zA-Z]" " " <<< $*)
487
488         echo -n $(((${1:-0}<<24) | (${2:-0}<<16) | (${3:-0}<<8) | (${4:-0})))
489 }
490
491 export LINUX_VERSION=$(uname -r | sed -e "s/\([0-9]*\.[0-9]*\.[0-9]*\).*/\1/")
492 export LINUX_VERSION_CODE=$(version_code ${LINUX_VERSION//\./ })
493
494 # Report the Lustre build version string (e.g. 1.8.7.3 or 2.4.1).
495 #
496 # usage: lustre_build_version
497 #
498 # All Lustre versions support "lctl get_param" to report the version of the
499 # code running in the kernel (what our tests are interested in), but it
500 # doesn't work without modules loaded.  After 2.9.53 and in upstream kernels
501 # the "version" parameter doesn't include "lustre: " at the beginning.
502 # If that fails, call "lctl lustre_build_version" which prints either (or both)
503 # the userspace and kernel build versions, but until 2.8.55 required root
504 # access to get the Lustre kernel version.  If that also fails, fall back to
505 # using "lctl --version", which is easy to parse and works without the kernel
506 # modules, but was only added in 2.6.50 and only prints the lctl tool version,
507 # not the module version, though they are usually the same.
508 #
509 # Various commands and their output format for different Lustre versions:
510 # lctl get_param version:       2.9.55
511 # lctl get_param version:       lustre: 2.8.53
512 # lctl get_param version:       lustre: 2.6.52
513 #                               kernel: patchless_client
514 #                               build: v2_6_92_0-2.6.32-431.el6_lustre.x86_64
515 # lctl lustre_build_version:    Lustre version: 2.8.53_27_gae67fc01
516 # lctl lustre_build_version:    error: lustre_build_version: Permission denied
517 #       (as non-root user)      lctl   version: v2_6_92_0-2.6.32-431.el6.x86_64
518 # lctl lustre_build_version:    Lustre version: 2.5.3-2.6.32.26-175.fc12.x86_64
519 #                               lctl   version: 2.5.3-2.6.32..26-175fc12.x86_64
520 # lctl --version:               lctl 2.6.50
521 #
522 # output: prints version string to stdout in (up to 4) dotted-decimal values
523 lustre_build_version() {
524         local facet=${1:-client}
525         local facet_version=${facet}_VERSION
526
527         # if the global variable is already set, then use that
528         [ -n "${!facet_version}" ] && echo ${!facet_version} && return
529
530         # this is the currently-running version of the kernel modules
531         local ver=$(do_facet $facet "$LCTL get_param -n version 2>/dev/null")
532         # we mostly test 2.10+ systems, only try others if the above fails
533         if [ -z "$ver" ]; then
534                 ver=$(do_facet $facet "$LCTL lustre_build_version 2>/dev/null")
535         fi
536         if [ -z "$ver" ]; then
537                 ver=$(do_facet $facet "$LCTL --version 2>/dev/null" |
538                       cut -d' ' -f2)
539         fi
540         local lver=$(egrep -i "lustre: |version: " <<<"$ver" | head -n 1)
541         [ -n "$lver" ] && ver="$lver"
542
543         lver=$(sed -e 's/[^:]*: //' -e 's/^v//' -e 's/[ -].*//' <<<$ver |
544                tr _ . | cut -d. -f1-4)
545
546         # save in global variable for the future
547         export $facet_version=$lver
548
549         echo $lver
550 }
551
552 # Report the Lustre numeric build version code for the supplied facet.
553 lustre_version_code() {
554         version_code $(lustre_build_version $1)
555 }
556
557 module_loaded () {
558         /sbin/lsmod | grep -q "^\<$1\>"
559 }
560
561 check_lfs_df_ret_val() {
562         # Ignore only EOPNOTSUPP (which is 95; Operation not supported) error
563         # returned by 'lfs df' for valid dentry but not a lustrefs.
564         #
565         # 'lfs df' historically always returned success(0) instead of
566         # EOPNOTSUPP. This function for compatibility reason, ignores and
567         # masquerades EOPNOTSUPP as success.
568         [[ $1 -eq 95 ]] && return 0
569         return $1
570 }
571
572 PRLFS=false
573 lustre_insmod() {
574         local module=$1
575         shift
576         local args="$@"
577         local msg
578         local rc=0
579
580         if ! $PRLFS; then
581                 msg="$(insmod $module $args 2>&1)" && return 0 || rc=$?
582         fi
583
584         # parallels can't load modules directly from prlfs, use /tmp instead
585         if $PRLFS || [[ "$(stat -f -c%t $module)" == "7c7c6673" ]]; then
586                 local target="$(mktemp)"
587
588                 cp "$module" "$target"
589                 insmod $target $args
590                 rc=$?
591                 [[ $rc == 0 ]] && PRLFS=true
592                 rm -f $target
593         else
594                 echo "$msg"
595         fi
596         return $rc
597 }
598
599 # Load a module on the system where this is running.
600 #
601 # usage: load_module module_name [module arguments for insmod/modprobe]
602 #
603 # If module arguments are not given but MODOPTS_<MODULE> is set, then its value
604 # will be used as the arguments.  Otherwise arguments will be obtained from
605 # /etc/modprobe.conf, from /etc/modprobe.d/Lustre, or else none will be used.
606 #
607 load_module() {
608         local module=$1 # '../libcfs/libcfs/libcfs', 'obdclass/obdclass', ...
609         shift
610         local ext=".ko"
611         local base=$(basename $module $ext)
612         local path
613         local -A module_is_loaded_aa
614         local optvar
615         local mod
616
617         for mod in $(lsmod | awk '{ print $1; }'); do
618                 module_is_loaded_aa[${mod//-/_}]=true
619         done
620
621         module_is_loaded() {
622                 ${module_is_loaded_aa[${1//-/_}]:-false}
623         }
624
625         if module_is_loaded $base; then
626                 return
627         fi
628
629         if [[ -f $LUSTRE/$module$ext ]]; then
630                 path=$LUSTRE/$module$ext
631         elif [[ "$base" == lnet_selftest ]] &&
632              [[ -f $LUSTRE/../lnet/selftest/$base$ext ]]; then
633                 path=$LUSTRE/../lnet/selftest/$base$ext
634         else
635                 path=''
636         fi
637
638         if [[ -n "$path" ]]; then
639                 # Try to load any non-Lustre modules that $module depends on.
640                 for mod in $(modinfo --field=depends $path | tr ',' ' '); do
641                         if ! module_is_loaded $mod; then
642                                 modprobe $mod
643                         fi
644                 done
645         fi
646
647         # If no module arguments were passed then get them from
648         # $MODOPTS_<MODULE>, otherwise from modprobe.conf.
649         if [ $# -eq 0 ]; then
650                 # $MODOPTS_<MODULE>; we could use associative arrays, but that's
651                 # not in Bash until 4.x, so we resort to eval.
652                 optvar="MODOPTS_$(basename $module | tr a-z A-Z)"
653                 eval set -- \$$optvar
654                 if [ $# -eq 0 -a -n "$MODPROBECONF" ]; then
655                         # Nothing in $MODOPTS_<MODULE>; try modprobe.conf
656                         local opt
657                         opt=$(awk -v var="^options $base" '$0 ~ var \
658                               {gsub("'"options $base"'",""); print}' \
659                                 $MODPROBECONF)
660                         set -- $(echo -n $opt)
661
662                         # Ensure we have accept=all for lnet
663                         if [[ "$base" == lnet ]]; then
664                                 # OK, this is a bit wordy...
665                                 local arg accept_all_present=false
666
667                                 for arg in "$@"; do
668                                         [[ "$arg" == accept=all ]] &&
669                                                 accept_all_present=true
670                                 done
671
672                                 $accept_all_present || set -- "$@" accept=all
673                         fi
674
675                         export $optvar="$*"
676                 fi
677         fi
678
679         [ $# -gt 0 ] && echo "${module} options: '$*'"
680
681         # Note that insmod will ignore anything in modprobe.conf, which is why
682         # we're passing options on the command-line. If $path does not exist
683         # then we must be testing a "make install" or"rpm" installation. Also
684         # note that failing to load ptlrpc_gss is not considered fatal.
685         if [[ -n "$path" ]]; then
686                 lustre_insmod $path "$@"
687         elif [[ "$base" == ptlrpc_gss ]]; then
688                 if ! modprobe $base "$@" 2>/dev/null; then
689                         echo "gss/krb5 is not supported"
690                 fi
691         else
692                 modprobe $base "$@"
693         fi
694 }
695
696 do_lnetctl() {
697         $LCTL mark "$LNETCTL $*"
698         echo "$LNETCTL $*"
699         $LNETCTL "$@"
700 }
701
702 load_lnet() {
703         # For kmemleak-enabled kernels we need clear all past state
704         # that obviously has nothing to do with this Lustre run
705         # Disable automatic memory scanning to avoid perf hit.
706         if [ -f /sys/kernel/debug/kmemleak ] ; then
707                 echo scan=off > /sys/kernel/debug/kmemleak || true
708                 echo scan > /sys/kernel/debug/kmemleak || true
709                 echo clear > /sys/kernel/debug/kmemleak || true
710         fi
711
712         echo Loading modules from $LUSTRE
713
714         local ncpus
715
716         if [ -f /sys/devices/system/cpu/online ]; then
717                 ncpus=$(($(cut -d "-" -f 2 /sys/devices/system/cpu/online) + 1))
718                 echo "detected $ncpus online CPUs by sysfs"
719         else
720                 ncpus=$(getconf _NPROCESSORS_CONF 2>/dev/null)
721                 local rc=$?
722
723                 if [ $rc -eq 0 ]; then
724                         echo "detected $ncpus online CPUs by getconf"
725                 else
726                         echo "Can't detect number of CPUs"
727                         ncpus=1
728                 fi
729         fi
730
731         # if there is only one CPU core, libcfs can only create one partition
732         # if there is more than 4 CPU cores, libcfs should create multiple CPU
733         # partitions. So we just force libcfs to create 2 partitions for
734         # system with 2 or 4 cores
735         local saved_opts="$MODOPTS_LIBCFS"
736
737         if [ $ncpus -le 4 ] && [ $ncpus -gt 1 ]; then
738                 # force to enable multiple CPU partitions
739                 echo "Force libcfs to create 2 CPU partitions"
740                 MODOPTS_LIBCFS="cpu_npartitions=2 $MODOPTS_LIBCFS"
741         else
742                 echo "libcfs will create CPU partition based on online CPUs"
743         fi
744
745         load_module ../libcfs/libcfs/libcfs
746         # Prevent local MODOPTS_LIBCFS being passed as part of environment
747         # variable to remote nodes
748         unset MODOPTS_LIBCFS
749
750         set_default_debug "neterror net nettrace malloc"
751         if [ "$1" = "config_on_load=1" ]; then
752                 load_module ../lnet/lnet/lnet
753         else
754                 load_module ../lnet/lnet/lnet "$@"
755         fi
756
757         LNDPATH=${LNDPATH:-"../lnet/klnds"}
758         if [ -z "$LNETLND" ]; then
759                 case $NETTYPE in
760                 o2ib*)  LNETLND="o2iblnd/ko2iblnd" ;;
761                 tcp*)   LNETLND="socklnd/ksocklnd" ;;
762                 kfi*)   LNETLND="kfilnd/kkfilnd" ;;
763                 gni*)   LNETLND="gnilnd/kgnilnd" ;;
764                 *)      local lnd="${NETTYPE%%[0-9]}lnd"
765                         [ -f "$LNDPATH/$lnd/k$lnd.ko" ] &&
766                                 LNETLND="$lnd/k$lnd" ||
767                                 LNETLND="socklnd/ksocklnd"
768                 esac
769         fi
770         load_module ../lnet/klnds/$LNETLND
771
772         if [ "$1" = "config_on_load=1" ]; then
773                 do_lnetctl lnet configure --all ||
774                         return $?
775         fi
776 }
777
778 load_modules_local() {
779         if [ -n "$MODPROBE" ]; then
780                 # use modprobe
781                 echo "Using modprobe to load modules"
782                 return 0
783         fi
784
785         # Create special udev test rules on every node
786         if [ -f $LUSTRE/lustre/conf/99-lustre.rules ]; then {
787                 sed -e 's|/usr/sbin/lctl|$LCTL|g' $LUSTRE/lustre/conf/99-lustre.rules > /etc/udev/rules.d/99-lustre-test.rules
788         } else {
789                 echo "SUBSYSTEM==\"lustre\", ACTION==\"change\", ENV{PARAM}==\"?*\", RUN+=\"$LCTL set_param '\$env{PARAM}=\$env{SETTING}'\"" > /etc/udev/rules.d/99-lustre-test.rules
790         } fi
791         udevadm control --reload-rules
792         udevadm trigger
793
794         load_lnet
795
796         load_module obdclass/obdclass
797         if ! client_only; then
798                 MODOPTS_PTLRPC=${MODOPTS_PTLRPC:-"lbug_on_grant_miscount=1"}
799         fi
800         load_module ptlrpc/ptlrpc
801         load_module ptlrpc/gss/ptlrpc_gss
802         load_module fld/fld
803         load_module fid/fid
804         load_module lmv/lmv
805         load_module osc/osc
806         load_module lov/lov
807         load_module mdc/mdc
808         load_module mgc/mgc
809         load_module obdecho/obdecho
810         if ! client_only; then
811                 load_module lfsck/lfsck
812                 [ "$LQUOTA" != "no" ] &&
813                         load_module quota/lquota $LQUOTAOPTS
814                 if [[ $(node_fstypes $HOSTNAME) == *zfs* ]]; then
815                         load_module osd-zfs/osd_zfs
816                 elif [[ $(node_fstypes $HOSTNAME) == *ldiskfs* ]]; then
817                         load_module ../ldiskfs/ldiskfs
818                         load_module osd-ldiskfs/osd_ldiskfs
819                 fi
820                 load_module mgs/mgs
821                 load_module mdd/mdd
822                 load_module mdt/mdt
823                 load_module ost/ost
824                 load_module lod/lod
825                 load_module osp/osp
826                 load_module ofd/ofd
827                 load_module osp/osp
828         fi
829
830         load_module llite/lustre
831         [ -d /r ] && OGDB=${OGDB:-"/r/tmp"}
832         OGDB=${OGDB:-$TMP}
833         rm -f $OGDB/ogdb-$HOSTNAME
834         $LCTL modules > $OGDB/ogdb-$HOSTNAME
835
836         # 'mount' doesn't look in $PATH, just sbin
837         local mount_lustre=$LUSTRE/utils/mount.lustre
838         if [ -f $mount_lustre ]; then
839                 local sbin_mount=$(readlink -f /sbin)/mount.lustre
840                 if grep -qw "$sbin_mount" /proc/mounts; then
841                         cmp -s $mount_lustre $sbin_mount || umount $sbin_mount
842                 fi
843                 if ! grep -qw "$sbin_mount" /proc/mounts; then
844                         [ ! -f "$sbin_mount" ] && touch "$sbin_mount"
845                         if [ ! -s "$sbin_mount" -a -w "$sbin_mount" ]; then
846                                 cat <<- EOF > "$sbin_mount"
847                                 #!/bin/bash
848                                 #STUB MARK
849                                 echo "This $sbin_mount just a mountpoint." 1>&2
850                                 echo "It is never supposed to be run." 1>&2
851                                 logger -p emerg -- "using stub $sbin_mount $@"
852                                 exit 1
853                                 EOF
854                                 chmod a+x $sbin_mount
855                         fi
856                         mount --bind $mount_lustre $sbin_mount ||
857                                 error "can't bind $mount_lustre to $sbin_mount"
858                 fi
859         fi
860 }
861
862 load_modules () {
863         local facets
864         local facet
865         local failover
866         load_modules_local
867         # bug 19124
868         # load modules on remote nodes optionally
869         # lustre-tests have to be installed on these nodes
870         if $LOAD_MODULES_REMOTE; then
871                 local list=$(comma_list $(remote_nodes_list))
872
873                 # include failover nodes in case they are not in the list yet
874                 facets=$(get_facets)
875                 for facet in ${facets//,/ }; do
876                         failover=$(facet_failover_host $facet)
877                         [ -n "$list" ] && [[ ! "$list" =~ "$failover" ]] &&
878                                 list="$list,$failover"
879                 done
880
881                 if [ -n "$list" ]; then
882                         echo "loading modules on: '$list'"
883                         do_rpc_nodes "$list" load_modules_local
884                 fi
885         fi
886 }
887
888 check_mem_leak () {
889         LEAK_LUSTRE=$(dmesg | tail -n 30 | grep "obd_memory.*leaked" || true)
890         LEAK_PORTALS=$(dmesg | tail -n 20 | egrep -i "libcfs.*memory leaked" ||
891                 true)
892         if [ "$LEAK_LUSTRE" -o "$LEAK_PORTALS" ]; then
893                 echo "$LEAK_LUSTRE" 1>&2
894                 echo "$LEAK_PORTALS" 1>&2
895                 mv $TMP/debug $TMP/debug-leak.`date +%s` || true
896                 echo "Memory leaks detected"
897                 [ -n "$IGNORE_LEAK" ] &&
898                         { echo "ignoring leaks" && return 0; } || true
899                 return 1
900         fi
901 }
902
903 unload_modules_local() {
904         $LUSTRE_RMMOD ldiskfs || return 2
905
906         [ -f /etc/udev/rules.d/99-lustre-test.rules ] &&
907                 rm /etc/udev/rules.d/99-lustre-test.rules
908         udevadm control --reload-rules
909         udevadm trigger
910
911         check_mem_leak || return 254
912
913         return 0
914 }
915
916 unload_modules() {
917         local rc=0
918
919         wait_exit_ST client # bug 12845
920
921         unload_modules_local || rc=$?
922
923         if $LOAD_MODULES_REMOTE; then
924                 local list=$(comma_list $(remote_nodes_list))
925                 if [ -n "$list" ]; then
926                         echo "unloading modules on: '$list'"
927                         do_rpc_nodes "$list" unload_modules_local
928                 fi
929         fi
930
931         local sbin_mount=$(readlink -f /sbin)/mount.lustre
932         if grep -qe "$sbin_mount " /proc/mounts; then
933                 umount $sbin_mount || true
934                 [ -s $sbin_mount ] && ! grep -q "STUB MARK" $sbin_mount ||
935                         rm -f $sbin_mount
936         fi
937
938         [[ $rc -eq 0 ]] && echo "modules unloaded."
939
940         return $rc
941 }
942
943 fs_log_size() {
944         local facet=${1:-ost1}
945         local size=0
946         local mult=$OSTCOUNT
947
948         case $(facet_fstype $facet) in
949                 ldiskfs) size=32;; # largest seen is 64 with multiple OSTs
950                 # grant_block_size is in bytes, allow at least 2x max blocksize
951                 zfs)     size=$(lctl get_param osc.$FSNAME*.import |
952                                 awk '/grant_block_size:/ {print $2/512; exit;}')
953                           ;;
954         esac
955
956         [[ $facet =~ mds ]] && mult=$MDTCOUNT
957         echo -n $((size * mult))
958 }
959
960 fs_inode_ksize() {
961         local facet=${1:-$SINGLEMDS}
962         local fstype=$(facet_fstype $facet)
963         local size=0
964         case $fstype in
965                 ldiskfs) size=4;;  # ~4KB per inode
966                 zfs)     size=11;; # 10 to 11KB per inode
967         esac
968
969         echo -n $size
970 }
971
972 check_gss_daemon_nodes() {
973         local list=$1
974         local dname=$2
975
976         do_nodesv $list "num=\\\$(ps -o cmd -C $dname | grep $dname | wc -l);
977 if [ \\\"\\\$num\\\" -ne 1 ]; then
978     echo \\\$num instance of $dname;
979     exit 1;
980 fi; "
981 }
982
983 check_gss_daemon_facet() {
984         local facet=$1
985         local dname=$2
986
987         num=`do_facet $facet ps -o cmd -C $dname | grep $dname | wc -l`
988         if [ $num -ne 1 ]; then
989                 echo "$num instance of $dname on $facet"
990                 return 1
991         fi
992         return 0
993 }
994
995 send_sigint() {
996         local list=$1
997
998         shift
999         echo "Stopping "$@" on $list"
1000         do_nodes $list "killall -2 $* 2>/dev/null || true"
1001 }
1002
1003 # start gss daemons on all nodes, or "daemon" on "nodes" if set
1004 start_gss_daemons() {
1005         local nodes=$1
1006         local daemon=$2
1007
1008         if [ "$nodes" ] && [ "$daemon" ] ; then
1009                 echo "Starting gss daemon on nodes: $nodes"
1010                 do_nodes $nodes "$daemon" || return 8
1011                 return 0
1012         fi
1013
1014         nodes=$(comma_list $(mdts_nodes))
1015         echo "Starting gss daemon on mds: $nodes"
1016         if $GSS_SK; then
1017                 # Start all versions, in case of switching
1018                 do_nodes $nodes "$LSVCGSSD -vvv -s -m -o -z" || return 1
1019         else
1020                 do_nodes $nodes "$LSVCGSSD -vvv" || return 1
1021         fi
1022         if $GSS_PIPEFS; then
1023                 do_nodes $nodes "$LGSSD -v" || return 2
1024         fi
1025
1026         nodes=$(comma_list $(osts_nodes))
1027         echo "Starting gss daemon on ost: $nodes"
1028         if $GSS_SK; then
1029                 # Start all versions, in case of switching
1030                 do_nodes $nodes "$LSVCGSSD -vvv -s -m -o -z" || return 3
1031         else
1032                 do_nodes $nodes "$LSVCGSSD -vvv" || return 3
1033         fi
1034         # starting on clients
1035
1036         local clients=${CLIENTS:-$HOSTNAME}
1037
1038         if $GSS_PIPEFS; then
1039                 echo "Starting $LGSSD on clients $clients "
1040                 do_nodes $clients  "$LGSSD -v" || return 4
1041         fi
1042
1043         # wait daemons entering "stable" status
1044         sleep 5
1045
1046         #
1047         # check daemons are running
1048         #
1049         nodes=$(comma_list $(mdts_nodes) $(osts_nodes))
1050         check_gss_daemon_nodes $nodes lsvcgssd || return 5
1051         if $GSS_PIPEFS; then
1052                 nodes=$(comma_list $(mdts_nodes))
1053                 check_gss_daemon_nodes $nodes lgssd || return 6
1054         fi
1055         if $GSS_PIPEFS; then
1056                 check_gss_daemon_nodes $clients lgssd || return 7
1057         fi
1058 }
1059
1060 stop_gss_daemons() {
1061         local nodes=$(comma_list $(mdts_nodes))
1062
1063         send_sigint $nodes lsvcgssd lgssd
1064
1065         nodes=$(comma_list $(osts_nodes))
1066         send_sigint $nodes lsvcgssd
1067
1068         nodes=${CLIENTS:-$HOSTNAME}
1069         send_sigint $nodes lgssd
1070 }
1071
1072 add_sk_mntflag() {
1073         # Add mount flags for shared key
1074         local mt_opts=$@
1075
1076         if grep -q skpath <<< "$mt_opts" ; then
1077                 mt_opts=$(echo $mt_opts |
1078                         sed -e "s#skpath=[^ ,]*#skpath=$SK_PATH#")
1079         else
1080                 if [ -z "$mt_opts" ]; then
1081                         mt_opts="-o skpath=$SK_PATH"
1082                 else
1083                         mt_opts="$mt_opts,skpath=$SK_PATH"
1084                 fi
1085         fi
1086         echo -n $mt_opts
1087 }
1088
1089 from_build_tree() {
1090         local from_tree
1091
1092         case $LUSTRE in
1093         /usr/lib/lustre/* | /usr/lib64/lustre/* | /usr/lib/lustre | \
1094         /usr/lib64/lustre )
1095                 from_tree=false
1096                 ;;
1097         *)
1098                 from_tree=true
1099                 ;;
1100         esac
1101
1102         [ $from_tree = true ]
1103 }
1104
1105 init_gss() {
1106         if $SHARED_KEY; then
1107                 GSS=true
1108                 GSS_SK=true
1109         fi
1110
1111         if ! $GSS; then
1112                 return
1113         fi
1114
1115         if ! module_loaded ptlrpc_gss; then
1116                 load_module ptlrpc/gss/ptlrpc_gss
1117                 module_loaded ptlrpc_gss ||
1118                         error_exit "init_gss: GSS=$GSS, but gss/krb5 missing"
1119         fi
1120
1121         if $GSS_KRB5 || $GSS_SK; then
1122                 start_gss_daemons || error_exit "start gss daemon failed! rc=$?"
1123         fi
1124
1125         if $GSS_SK && ! $SK_NO_KEY; then
1126                 echo "Loading basic SSK keys on all servers"
1127                 do_nodes $(comma_list $(all_server_nodes)) \
1128                         "$LGSS_SK -t server -l $SK_PATH/$FSNAME.key || true"
1129                 do_nodes $(comma_list $(all_server_nodes)) \
1130                                 "keyctl show | grep lustre | cut -c1-11 |
1131                                 sed -e 's/ //g;' |
1132                                 xargs -IX keyctl setperm X 0x3f3f3f3f"
1133         fi
1134
1135         if $GSS_SK && $SK_NO_KEY; then
1136                 local numclients=${1:-$CLIENTCOUNT}
1137                 local clients=${CLIENTS:-$HOSTNAME}
1138
1139                 # security ctx config for keyring
1140                 SK_NO_KEY=false
1141                 local lgssc_conf_file="/etc/request-key.d/lgssc.conf"
1142
1143                 if from_build_tree; then
1144                         mkdir -p $SK_OM_PATH
1145                         if grep -q request-key /proc/mounts > /dev/null; then
1146                                 echo "SSK: Request key already mounted."
1147                         else
1148                                 mount -o bind $SK_OM_PATH /etc/request-key.d/
1149                         fi
1150                         local lgssc_conf_line='create lgssc * * '
1151                         lgssc_conf_line+=$(which lgss_keyring)
1152                         lgssc_conf_line+=' %o %k %t %d %c %u %g %T %P %S'
1153                         echo "$lgssc_conf_line" > $lgssc_conf_file
1154                 fi
1155
1156                 [ -e $lgssc_conf_file ] ||
1157                         error_exit "Could not find key options in $lgssc_conf_file"
1158                 echo "$lgssc_conf_file content is:"
1159                 cat $lgssc_conf_file
1160
1161                 if ! local_mode; then
1162                         if from_build_tree; then
1163                                 do_nodes $(comma_list $(all_nodes)) "mkdir -p \
1164                                         $SK_OM_PATH"
1165                                 do_nodes $(comma_list $(all_nodes)) "mount \
1166                                         -o bind $SK_OM_PATH \
1167                                         /etc/request-key.d/"
1168                                 do_nodes $(comma_list $(all_nodes)) "rsync \
1169                                         -aqv $HOSTNAME:$lgssc_conf_file \
1170                                         $lgssc_conf_file >/dev/null 2>&1"
1171                         else
1172                                 do_nodes $(comma_list $(all_nodes)) \
1173                                         "echo $lgssc_conf_file: ; \
1174                                         cat $lgssc_conf_file"
1175                         fi
1176                 fi
1177
1178                 # create shared key on all nodes
1179                 mkdir -p $SK_PATH/nodemap
1180                 rm -f $SK_PATH/$FSNAME.key $SK_PATH/nodemap/c*.key \
1181                         $SK_PATH/$FSNAME-*.key
1182                 # for nodemap testing each client may need own key,
1183                 # and S2S now requires keys as well, both for "client"
1184                 # and for "server"
1185                 if $SK_S2S; then
1186                         $LGSS_SK -t server -f$FSNAME -n $SK_S2SNMCLI \
1187                                 -w $SK_PATH/$FSNAME-nmclient.key \
1188                                 -d /dev/urandom >/dev/null 2>&1
1189                         $LGSS_SK -t mgs,server -f$FSNAME -n $SK_S2SNM \
1190                                 -w $SK_PATH/$FSNAME-s2s-server.key \
1191                                 -d /dev/urandom >/dev/null 2>&1
1192                 fi
1193                 # basic key create
1194                 $LGSS_SK -t server -f$FSNAME -w $SK_PATH/$FSNAME.key \
1195                         -d /dev/urandom >/dev/null 2>&1
1196                 # per-nodemap keys
1197                 for i in $(seq 0 $((numclients - 1))); do
1198                         $LGSS_SK -t server -f$FSNAME -n c$i \
1199                                 -w $SK_PATH/nodemap/c$i.key -d /dev/urandom \
1200                                 >/dev/null 2>&1
1201                 done
1202                 # Distribute keys
1203                 if ! local_mode; then
1204                         for lnode in $(all_nodes); do
1205                                 scp -r $SK_PATH ${lnode}:$(dirname $SK_PATH)/
1206                         done
1207                 fi
1208                 # Set client keys to client type to generate prime P
1209                 if local_mode; then
1210                         do_nodes $(all_nodes) "$LGSS_SK -t client,server -m \
1211                                 $SK_PATH/$FSNAME.key >/dev/null 2>&1"
1212                 else
1213                         do_nodes $clients "$LGSS_SK -t client -m \
1214                                 $SK_PATH/$FSNAME.key >/dev/null 2>&1"
1215                         do_nodes $clients "find $SK_PATH/nodemap \
1216                                 -name \*.key | xargs -IX $LGSS_SK -t client \
1217                                 -m X >/dev/null 2>&1"
1218                 fi
1219                 # This is required for servers as well, if S2S in use
1220                 if $SK_S2S; then
1221                         do_nodes $(comma_list $(mdts_nodes)) \
1222                                 "cp $SK_PATH/$FSNAME-s2s-server.key \
1223                                 $SK_PATH/$FSNAME-s2s-client.key; $LGSS_SK \
1224                                 -t client -m $SK_PATH/$FSNAME-s2s-client.key \
1225                                 >/dev/null 2>&1"
1226                         do_nodes $(comma_list $(osts_nodes)) \
1227                                 "cp $SK_PATH/$FSNAME-s2s-server.key \
1228                                 $SK_PATH/$FSNAME-s2s-client.key; $LGSS_SK \
1229                                 -t client -m $SK_PATH/$FSNAME-s2s-client.key \
1230                                 >/dev/null 2>&1"
1231                         do_nodes $clients "$LGSS_SK -t client \
1232                                 -m $SK_PATH/$FSNAME-nmclient.key \
1233                                  >/dev/null 2>&1"
1234                 fi
1235         fi
1236         if $GSS_SK; then
1237                 # mount options for servers and clients
1238                 MGS_MOUNT_OPTS=$(add_sk_mntflag $MGS_MOUNT_OPTS)
1239                 MDS_MOUNT_OPTS=$(add_sk_mntflag $MDS_MOUNT_OPTS)
1240                 OST_MOUNT_OPTS=$(add_sk_mntflag $OST_MOUNT_OPTS)
1241                 MOUNT_OPTS=$(add_sk_mntflag $MOUNT_OPTS)
1242                 SEC=$SK_FLAVOR
1243                 if [ -z "$LGSS_KEYRING_DEBUG" ]; then
1244                         LGSS_KEYRING_DEBUG=4
1245                 fi
1246         fi
1247
1248         if [ -n "$LGSS_KEYRING_DEBUG" ] && \
1249                ( local_mode || from_build_tree ); then
1250                 lctl set_param -n \
1251                      sptlrpc.gss.lgss_keyring.debug_level=$LGSS_KEYRING_DEBUG
1252         elif [ -n "$LGSS_KEYRING_DEBUG" ]; then
1253                 do_nodes $(comma_list $(all_nodes)) "modprobe ptlrpc_gss && \
1254                 lctl set_param -n \
1255                    sptlrpc.gss.lgss_keyring.debug_level=$LGSS_KEYRING_DEBUG"
1256         fi
1257 }
1258
1259 cleanup_gss() {
1260         if $GSS; then
1261                 stop_gss_daemons
1262                 # maybe cleanup credential cache?
1263         fi
1264 }
1265
1266 cleanup_sk() {
1267         if $GSS_SK; then
1268                 if $SK_S2S; then
1269                         do_node $(mgs_node) "$LCTL nodemap_del $SK_S2SNM"
1270                         do_node $(mgs_node) "$LCTL nodemap_del $SK_S2SNMCLI"
1271                         $RPC_MODE || echo "Sleeping for 10 sec for Nodemap.."
1272                         sleep 10
1273                 fi
1274                 stop_gss_daemons
1275                 $RPC_MODE || echo "Cleaning up Shared Key.."
1276                 do_nodes $(comma_list $(all_nodes)) "rm -f \
1277                         $SK_PATH/$FSNAME*.key $SK_PATH/nodemap/$FSNAME*.key"
1278                 do_nodes $(comma_list $(all_nodes)) "keyctl show | \
1279                   awk '/lustre/ { print \\\$1 }' | xargs -IX keyctl unlink X"
1280                 if from_build_tree; then
1281                         # Remove the mount and clean up the files we added to
1282                         # SK_PATH
1283                         do_nodes $(comma_list $(all_nodes)) "while grep -q \
1284                                 request-key.d /proc/mounts; do umount \
1285                                 /etc/request-key.d/; done"
1286                         do_nodes $(comma_list $(all_nodes)) "rm -f \
1287                                 $SK_OM_PATH/lgssc.conf"
1288                         do_nodes $(comma_list $(all_nodes)) "rmdir $SK_OM_PATH"
1289                 fi
1290                 SK_NO_KEY=true
1291         fi
1292 }
1293
1294 facet_svc() {
1295         local facet=$1
1296         local var=${facet}_svc
1297
1298         echo -n ${!var}
1299 }
1300
1301 facet_type() {
1302         local facet=$1
1303
1304         echo -n $facet | sed -e 's/^fs[0-9]\+//' -e 's/[0-9_]\+//' |
1305                 tr '[:lower:]' '[:upper:]'
1306 }
1307
1308 facet_number() {
1309         local facet=$1
1310
1311         if [ $facet == mgs ] || [ $facet == client ]; then
1312                 return 1
1313         fi
1314
1315         echo -n $facet | sed -e 's/^fs[0-9]\+//' | sed -e 's/^[a-z]\+//'
1316 }
1317
1318 facet_fstype() {
1319         local facet=$1
1320         local var
1321
1322         var=${facet}_FSTYPE
1323         if [ -n "${!var}" ]; then
1324                 echo -n ${!var}
1325                 return
1326         fi
1327
1328         var=$(facet_type $facet)FSTYPE
1329         if [ -n "${!var}" ]; then
1330                 echo -n ${!var}
1331                 return
1332         fi
1333
1334         if [ -n "$FSTYPE" ]; then
1335                 echo -n $FSTYPE
1336                 return
1337         fi
1338
1339         if [[ $facet == mgs ]] && combined_mgs_mds; then
1340                 facet_fstype mds1
1341                 return
1342         fi
1343
1344         return 1
1345 }
1346
1347 node_fstypes() {
1348         local node=$1
1349         local fstypes
1350         local fstype
1351         local facets=$(get_facets)
1352         local facet
1353
1354         for facet in ${facets//,/ }; do
1355                 if [ $node == $(facet_host $facet) ] ||
1356                    [ $node == "$(facet_failover_host $facet)" ]; then
1357                         fstype=$(facet_fstype $facet)
1358                         if [[ $fstypes != *$fstype* ]]; then
1359                                 fstypes+="${fstypes:+,}$fstype"
1360                         fi
1361                 fi
1362         done
1363         echo -n $fstypes
1364 }
1365
1366 facet_index() {
1367         local facet=$1
1368         local num=$(facet_number $facet)
1369         local index
1370
1371         if [[ $(facet_type $facet) = OST ]]; then
1372                 index=OSTINDEX${num}
1373                 if [[ -n "${!index}" ]]; then
1374                         echo -n ${!index}
1375                         return
1376                 fi
1377
1378                 index=${OST_INDICES[num - 1]}
1379         fi
1380
1381         [[ -n "$index" ]] || index=$((num - 1))
1382         echo -n $index
1383 }
1384
1385 devicelabel() {
1386         local facet=$1
1387         local dev=$2
1388         local label
1389         local fstype=$(facet_fstype $facet)
1390
1391         case $fstype in
1392         ldiskfs)
1393                 label=$(do_facet ${facet} "$E2LABEL ${dev} 2>/dev/null");;
1394         zfs)
1395                 label=$(do_facet ${facet} "$ZFS get -H -o value lustre:svname \
1396                                            ${dev} 2>/dev/null");;
1397         *)
1398                 error "unknown fstype!";;
1399         esac
1400
1401         echo -n $label
1402 }
1403
1404 #
1405 # Get the device of a facet.
1406 #
1407 facet_device() {
1408         local facet=$1
1409         local device
1410
1411         case $facet in
1412                 mgs) device=$(mgsdevname) ;;
1413                 mds*) device=$(mdsdevname $(facet_number $facet)) ;;
1414                 ost*) device=$(ostdevname $(facet_number $facet)) ;;
1415                 fs2mds) device=$(mdsdevname 1_2) ;;
1416                 fs2ost) device=$(ostdevname 1_2) ;;
1417                 fs3ost) device=$(ostdevname 2_2) ;;
1418                 *) ;;
1419         esac
1420
1421         echo -n $device
1422 }
1423
1424 #
1425 # Get the virtual device of a facet.
1426 #
1427 facet_vdevice() {
1428         local facet=$1
1429         local device
1430
1431         case $facet in
1432                 mgs) device=$(mgsvdevname) ;;
1433                 mds*) device=$(mdsvdevname $(facet_number $facet)) ;;
1434                 ost*) device=$(ostvdevname $(facet_number $facet)) ;;
1435                 fs2mds) device=$(mdsvdevname 1_2) ;;
1436                 fs2ost) device=$(ostvdevname 1_2) ;;
1437                 fs3ost) device=$(ostvdevname 2_2) ;;
1438                 *) ;;
1439         esac
1440
1441         echo -n $device
1442 }
1443
1444 running_in_vm() {
1445         local virt=$(virt-what 2> /dev/null)
1446
1447         [ $? -eq 0 ] && [ -n "$virt" ] && { echo $virt; return; }
1448
1449         virt=$(dmidecode -s system-product-name | awk '{print $1}')
1450
1451         case $virt in
1452                 VMware|KVM|VirtualBox|Parallels|Bochs)
1453                         echo $virt | tr '[A-Z]' '[a-z]' ;;
1454                 *) ;;
1455         esac
1456 }
1457
1458 #
1459 # Re-read the partition table on failover partner host.
1460 # After a ZFS storage pool is created on a shared device, the partition table
1461 # on the device may change. However, the operating system on the failover
1462 # host may not notice the change automatically. Without the up-to-date partition
1463 # block devices, 'zpool import ..' cannot find the labels, whose positions are
1464 # relative to partition rather than disk beginnings.
1465 #
1466 # This function performs partprobe on the failover host to make it re-read the
1467 # partition table.
1468 #
1469 refresh_partition_table() {
1470         local facet=$1
1471         local device=$2
1472         local host
1473
1474         host=$(facet_passive_host $facet)
1475         if [[ -n "$host" ]]; then
1476                 do_node $host "$PARTPROBE $device"
1477         fi
1478 }
1479
1480 #
1481 # Get ZFS storage pool name.
1482 #
1483 zpool_name() {
1484         local facet=$1
1485         local device
1486         local poolname
1487
1488         device=$(facet_device $facet)
1489         # poolname is string before "/"
1490         poolname="${device%%/*}"
1491
1492         echo -n $poolname
1493 }
1494
1495 #
1496 #
1497 # Get ZFS local fsname.
1498 #
1499 zfs_local_fsname() {
1500         local facet=$1
1501         local lfsname=$(basename $(facet_device $facet))
1502
1503         echo -n $lfsname
1504 }
1505
1506 #
1507 # Create ZFS storage pool.
1508 #
1509 create_zpool() {
1510         local facet=$1
1511         local poolname=$2
1512         local vdev=$3
1513         shift 3
1514         local opts=${@:-"-o cachefile=none"}
1515
1516         do_facet $facet "lsmod | grep zfs >&/dev/null || modprobe zfs;
1517                 $ZPOOL list -H $poolname >/dev/null 2>&1 ||
1518                 $ZPOOL create -f $opts $poolname $vdev"
1519 }
1520
1521 #
1522 # Create ZFS file system.
1523 #
1524 create_zfs() {
1525         local facet=$1
1526         local dataset=$2
1527         shift 2
1528         local opts=${@:-"-o mountpoint=legacy"}
1529
1530         do_facet $facet "$ZFS list -H $dataset >/dev/null 2>&1 ||
1531                 $ZFS create $opts $dataset"
1532 }
1533
1534 #
1535 # Export ZFS storage pool.
1536 # Before exporting the pool, all datasets within the pool should be unmounted.
1537 #
1538 export_zpool() {
1539         local facet=$1
1540         shift
1541         local opts="$@"
1542         local poolname
1543
1544         poolname=$(zpool_name $facet)
1545
1546         if [[ -n "$poolname" ]]; then
1547                 do_facet $facet "! $ZPOOL list -H $poolname >/dev/null 2>&1 ||
1548                         grep -q ^$poolname/ /proc/mounts ||
1549                         $ZPOOL export $opts $poolname"
1550         fi
1551 }
1552
1553 #
1554 # Destroy ZFS storage pool.
1555 # Destroy the given pool and free up any devices for other use. This command
1556 # tries to unmount any active datasets before destroying the pool.
1557 # -f    Force any active datasets contained within the pool to be unmounted.
1558 #
1559 destroy_zpool() {
1560         local facet=$1
1561         local poolname=${2:-$(zpool_name $facet)}
1562
1563         if [[ -n "$poolname" ]]; then
1564                 do_facet $facet "! $ZPOOL list -H $poolname >/dev/null 2>&1 ||
1565                         $ZPOOL destroy -f $poolname"
1566         fi
1567 }
1568
1569 #
1570 # Import ZFS storage pool.
1571 # Force importing, even if the pool appears to be potentially active.
1572 #
1573 import_zpool() {
1574         local facet=$1
1575         shift
1576         local opts=${@:-"-o cachefile=none -o failmode=panic"}
1577         local poolname
1578
1579         poolname=$(zpool_name $facet)
1580
1581         if [[ -n "$poolname" ]]; then
1582                 opts+=" -d $(dirname $(facet_vdevice $facet))"
1583                 do_facet $facet "lsmod | grep zfs >&/dev/null || modprobe zfs;
1584                         $ZPOOL list -H $poolname >/dev/null 2>&1 ||
1585                         $ZPOOL import -f $opts $poolname"
1586         fi
1587 }
1588
1589 #
1590 # Reimport ZFS storage pool with new name
1591 #
1592 reimport_zpool() {
1593         local facet=$1
1594         local newpool=$2
1595         local opts="-o cachefile=none"
1596         local poolname=$(zpool_name $facet)
1597
1598         opts+=" -d $(dirname $(facet_vdevice $facet))"
1599         do_facet $facet "$ZPOOL export $poolname;
1600                          $ZPOOL import $opts $poolname $newpool"
1601 }
1602
1603 #
1604 # Set the "cachefile=none" property on ZFS storage pool so that the pool
1605 # is not automatically imported on system startup.
1606 #
1607 # In a failover environment, this will provide resource level fencing which
1608 # will ensure that the same ZFS storage pool will not be imported concurrently
1609 # on different nodes.
1610 #
1611 disable_zpool_cache() {
1612         local facet=$1
1613         local poolname
1614
1615         poolname=$(zpool_name $facet)
1616
1617         if [[ -n "$poolname" ]]; then
1618                 do_facet $facet "$ZPOOL set cachefile=none $poolname"
1619         fi
1620 }
1621
1622 #
1623 # This and set_osd_param() shall be used to access OSD parameters
1624 # once existed under "obdfilter":
1625 #
1626 #   mntdev
1627 #   stats
1628 #   read_cache_enable
1629 #   writethrough_cache_enable
1630 #
1631 get_osd_param() {
1632         local nodes=$1
1633         local device=${2:-$FSNAME-OST*}
1634         local name=$3
1635
1636         do_nodes $nodes "$LCTL get_param -n osd-*.$device.$name"
1637 }
1638
1639 set_osd_param() {
1640         local nodes=$1
1641         local device=${2:-$FSNAME-OST*}
1642         local name=$3
1643         local value=$4
1644
1645         do_nodes $nodes "$LCTL set_param -n osd-*.$device.$name=$value"
1646 }
1647
1648 set_debug_size () {
1649         local dz=${1:-$DEBUG_SIZE}
1650
1651         if [ -f /sys/devices/system/cpu/possible ]; then
1652                 local cpus=$(($(cut -d "-" -f 2 /sys/devices/system/cpu/possible)+1))
1653         else
1654                 local cpus=$(getconf _NPROCESSORS_CONF 2>/dev/null)
1655         fi
1656
1657         # bug 19944, adjust size to be -gt num_possible_cpus()
1658         # promise 2MB for every cpu at least
1659         if [ -n "$cpus" ] && [ $((cpus * 2)) -gt $dz ]; then
1660                 dz=$((cpus * 2))
1661         fi
1662         lctl set_param debug_mb=$dz
1663 }
1664
1665 set_default_debug () {
1666         local debug=${1:-"$PTLDEBUG"}
1667         local subsys=${2:-"$SUBSYSTEM"}
1668         local debug_size=${3:-$DEBUG_SIZE}
1669
1670         [ -n "$debug" ] && lctl set_param debug="$debug" >/dev/null
1671         [ -n "$subsys" ] &&
1672                 lctl set_param subsystem_debug="${subsys# }" >/dev/null
1673
1674         [ -n "$debug_size" ] && set_debug_size $debug_size > /dev/null
1675 }
1676
1677 set_default_debug_nodes () {
1678         local nodes="$1"
1679         local debug="${2:-"$PTLDEBUG"}"
1680         local subsys="${3:-"$SUBSYSTEM"}"
1681         local debug_size="${4:-$DEBUG_SIZE}"
1682
1683         if [[ ,$nodes, = *,$HOSTNAME,* ]]; then
1684                 nodes=$(exclude_items_from_list "$nodes" "$HOSTNAME")
1685                 set_default_debug
1686         fi
1687
1688         [[ -z "$nodes" ]] ||
1689                 do_rpc_nodes "$nodes" set_default_debug \
1690                         \\\"$debug\\\" \\\"$subsys\\\" $debug_size || true
1691 }
1692
1693 set_default_debug_facet () {
1694         local facet=$1
1695         local debug="${2:-"$PTLDEBUG"}"
1696         local subsys="${3:-"$SUBSYSTEM"}"
1697         local debug_size="${4:-$DEBUG_SIZE}"
1698         local node=$(facet_active_host $facet)
1699
1700         [ -n "$node" ] || error "No host defined for facet $facet"
1701
1702         set_default_debug_nodes $node "$debug" "$subsys" $debug_size
1703 }
1704
1705 set_params_nodes () {
1706         [[ $# -ge 2 ]] || return 0
1707
1708         local nodes=$1
1709         shift
1710         do_nodes $nodes $LCTL set_param "$@"
1711 }
1712
1713 set_params_clients () {
1714         local clients=${1:-$CLIENTS}
1715         local params=${2:-$CLIENT_LCTL_SETPARAM_PARAM}
1716
1717         [[ -n $params ]] || return 0
1718         set_params_nodes $clients $params
1719 }
1720
1721 set_hostid () {
1722         local hostid=${1:-$(hostid)}
1723
1724         if [ ! -s /etc/hostid ]; then
1725                 printf $(echo -n $hostid |
1726             sed 's/\(..\)\(..\)\(..\)\(..\)/\\x\4\\x\3\\x\2\\x\1/') >/etc/hostid
1727         fi
1728 }
1729
1730 # Facet functions
1731 mount_facets () {
1732         local facets=${1:-$(get_facets)}
1733         local facet
1734         local -a mountpids
1735         local total=0
1736         local ret=0
1737
1738         for facet in ${facets//,/ }; do
1739                 mount_facet $facet &
1740                 mountpids[total]=$!
1741                 total=$((total+1))
1742         done
1743         for ((index=0; index<$total; index++)); do
1744                 wait ${mountpids[index]}
1745                 local RC=$?
1746                 [ $RC -eq 0 ] && continue
1747
1748                 if [ "$TESTSUITE.$TESTNAME" = "replay-dual.test_0a" ]; then
1749                         skip_noexit "Restart of $facet failed!." &&
1750                                 touch $LU482_FAILED
1751                 else
1752                         error "Restart of $facet failed!"
1753                 fi
1754                 ret=$RC
1755         done
1756         return $ret
1757 }
1758
1759 #
1760 # Add argument "arg" (e.g., "loop") to the comma-separated list
1761 # of arguments for option "opt" (e.g., "-o") on command
1762 # line "opts" (e.g., "-o flock").
1763 #
1764 csa_add() {
1765         local opts=$1
1766         local opt=$2
1767         local arg=$3
1768         local opt_pattern="\([[:space:]]\+\|^\)$opt"
1769
1770         if echo "$opts" | grep -q $opt_pattern; then
1771                 opts=$(echo "$opts" | sed -e \
1772                         "s/$opt_pattern[[:space:]]*[^[:space:]]\+/&,$arg/")
1773         else
1774                 opts+="${opts:+ }$opt $arg"
1775         fi
1776         echo -n "$opts"
1777 }
1778
1779 #
1780 # Associate loop device with a given regular file.
1781 # Return the loop device.
1782 #
1783 setup_loop_device() {
1784         local facet=$1
1785         local file=$2
1786
1787         do_facet $facet "loop_dev=\\\$($LOSETUP -j $file | cut -d : -f 1);
1788                          if [[ -z \\\$loop_dev ]]; then
1789                                 loop_dev=\\\$($LOSETUP -f);
1790                                 $LOSETUP \\\$loop_dev $file || loop_dev=;
1791                          fi;
1792                          echo -n \\\$loop_dev"
1793 }
1794
1795 #
1796 # Detach a loop device.
1797 #
1798 cleanup_loop_device() {
1799         local facet=$1
1800         local loop_dev=$2
1801
1802         do_facet $facet "! $LOSETUP $loop_dev >/dev/null 2>&1 ||
1803                          $LOSETUP -d $loop_dev"
1804 }
1805
1806 #
1807 # Check if a given device is a block device.
1808 #
1809 is_blkdev() {
1810         local facet=$1
1811         local dev=$2
1812         local size=${3:-""}
1813
1814         [[ -n "$dev" ]] || return 1
1815         do_facet $facet "test -b $dev" || return 1
1816         if [[ -n "$size" ]]; then
1817                 local in=$(do_facet $facet "dd if=$dev of=/dev/null bs=1k \
1818                                             count=1 skip=$size 2>&1" |
1819                                             awk '($3 == "in") { print $1 }')
1820                 [[ "$in" = "1+0" ]] || return 1
1821         fi
1822 }
1823
1824 #
1825 # Check if a given device is a device-mapper device.
1826 #
1827 is_dm_dev() {
1828         local facet=$1
1829         local dev=$2
1830
1831         [[ -n "$dev" ]] || return 1
1832         do_facet $facet "$DMSETUP status $dev >/dev/null 2>&1"
1833 }
1834
1835 #
1836 # Check if a given device is a device-mapper flakey device.
1837 #
1838 is_dm_flakey_dev() {
1839         local facet=$1
1840         local dev=$2
1841         local type
1842
1843         [[ -n "$dev" ]] || return 1
1844
1845         type=$(do_facet $facet "$DMSETUP status $dev 2>&1" |
1846                awk '{print $3}')
1847         [[ $type = flakey ]] && return 0 || return 1
1848 }
1849
1850 #
1851 # Check if device-mapper flakey device is supported by the kernel
1852 # of $facet node or not.
1853 #
1854 dm_flakey_supported() {
1855         local facet=$1
1856
1857         $FLAKEY || return 1
1858         do_facet $facet "modprobe dm-flakey;
1859                          $DMSETUP targets | grep -q flakey" &> /dev/null
1860 }
1861
1862 #
1863 # Get the device-mapper flakey device name of a given facet.
1864 #
1865 dm_facet_devname() {
1866         local facet=$1
1867         [[ $facet = mgs ]] && combined_mgs_mds && facet=mds1
1868
1869         echo -n ${facet}_flakey
1870 }
1871
1872 #
1873 # Get the device-mapper flakey device of a given facet.
1874 # A device created by dmsetup will appear as /dev/mapper/<device-name>.
1875 #
1876 dm_facet_devpath() {
1877         local facet=$1
1878
1879         echo -n $DM_DEV_PATH/$(dm_facet_devname $facet)
1880 }
1881
1882 #
1883 # Set a device-mapper device with a new table.
1884 #
1885 # The table has the following format:
1886 # <logical_start_sector> <num_sectors> <target_type> <target_args>
1887 #
1888 # flakey <target_args> includes:
1889 # <destination_device> <offset> <up_interval> <down_interval> \
1890 # [<num_features> [<feature_arguments>]]
1891 #
1892 # linear <target_args> includes:
1893 # <destination_device> <start_sector>
1894 #
1895 dm_set_dev_table() {
1896         local facet=$1
1897         local dm_dev=$2
1898         local target_type=$3
1899         local num_sectors
1900         local real_dev
1901         local tmp
1902         local table
1903
1904         read tmp num_sectors tmp real_dev tmp \
1905                 <<< $(do_facet $facet "$DMSETUP table $dm_dev")
1906
1907         case $target_type in
1908         flakey)
1909                 table="0 $num_sectors flakey $real_dev 0 0 1800 1 drop_writes"
1910                 ;;
1911         linear)
1912                 table="0 $num_sectors linear $real_dev 0"
1913                 ;;
1914         *) error "invalid target type $target_type" ;;
1915         esac
1916
1917         do_facet $facet "$DMSETUP suspend --nolockfs --noflush $dm_dev" ||
1918                 error "failed to suspend $dm_dev"
1919         do_facet $facet "$DMSETUP load $dm_dev --table \\\"$table\\\"" ||
1920                 error "failed to load $target_type table into $dm_dev"
1921         do_facet $facet "$DMSETUP resume $dm_dev" ||
1922                 error "failed to resume $dm_dev"
1923 }
1924
1925 #
1926 # Set a device-mapper flakey device as "read-only" by using the "drop_writes"
1927 # feature parameter.
1928 #
1929 # drop_writes:
1930 #       All write I/O is silently ignored.
1931 #       Read I/O is handled correctly.
1932 #
1933 dm_set_dev_readonly() {
1934         local facet=$1
1935         local dm_dev=${2:-$(dm_facet_devpath $facet)}
1936
1937         dm_set_dev_table $facet $dm_dev flakey
1938 }
1939
1940 #
1941 # Set a device-mapper device to traditional linear mapping mode.
1942 #
1943 dm_clear_dev_readonly() {
1944         local facet=$1
1945         local dm_dev=${2:-$(dm_facet_devpath $facet)}
1946
1947         dm_set_dev_table $facet $dm_dev linear
1948 }
1949
1950 #
1951 # Set the device of a given facet as "read-only".
1952 #
1953 set_dev_readonly() {
1954         local facet=$1
1955         local svc=${facet}_svc
1956
1957         if [[ $(facet_fstype $facet) = zfs ]] ||
1958            ! dm_flakey_supported $facet; then
1959                 do_facet $facet $LCTL --device ${!svc} readonly
1960         else
1961                 dm_set_dev_readonly $facet
1962         fi
1963 }
1964
1965 #
1966 # Get size in 512-byte sectors (BLKGETSIZE64 / 512) of a given device.
1967 #
1968 get_num_sectors() {
1969         local facet=$1
1970         local dev=$2
1971         local num_sectors
1972
1973         num_sectors=$(do_facet $facet "blockdev --getsz $dev 2>/dev/null")
1974         [[ ${PIPESTATUS[0]} = 0 && -n "$num_sectors" ]] || num_sectors=0
1975         echo -n $num_sectors
1976 }
1977
1978 #
1979 # Create a device-mapper device with a given block device or regular file (will
1980 # be associated with loop device).
1981 # Return the full path of the device-mapper device.
1982 #
1983 dm_create_dev() {
1984         local facet=$1
1985         local real_dev=$2                                  # destination device
1986         local dm_dev_name=${3:-$(dm_facet_devname $facet)} # device name
1987         local dm_dev=$DM_DEV_PATH/$dm_dev_name            # device-mapper device
1988
1989         # check if the device-mapper device to be created already exists
1990         if is_dm_dev $facet $dm_dev; then
1991                 # if the existing device was set to "read-only", then clear it
1992                 ! is_dm_flakey_dev $facet $dm_dev ||
1993                         dm_clear_dev_readonly $facet $dm_dev
1994
1995                 echo -n $dm_dev
1996                 return 0
1997         fi
1998
1999         # check if the destination device is a block device, and if not,
2000         # associate it with a loop device
2001         is_blkdev $facet $real_dev ||
2002                 real_dev=$(setup_loop_device $facet $real_dev)
2003         [[ -n "$real_dev" ]] || { echo -n $real_dev; return 2; }
2004
2005         # now create the device-mapper device
2006         local num_sectors=$(get_num_sectors $facet $real_dev)
2007         local table="0 $num_sectors linear $real_dev 0"
2008         local rc=0
2009
2010         do_facet $facet "$DMSETUP create $dm_dev_name --table \\\"$table\\\"" ||
2011                 { rc=${PIPESTATUS[0]}; dm_dev=; }
2012         do_facet $facet "$DMSETUP mknodes >/dev/null 2>&1"
2013
2014         echo -n $dm_dev
2015         return $rc
2016 }
2017
2018 #
2019 # Map the facet name to its device variable name.
2020 #
2021 facet_device_alias() {
2022         local facet=$1
2023         local dev_alias=$facet
2024
2025         case $facet in
2026                 fs2mds) dev_alias=mds1_2 ;;
2027                 fs2ost) dev_alias=ost1_2 ;;
2028                 fs3ost) dev_alias=ost2_2 ;;
2029                 *) ;;
2030         esac
2031
2032         echo -n $dev_alias
2033 }
2034
2035 #
2036 # Save the original value of the facet device and export the new value.
2037 #
2038 export_dm_dev() {
2039         local facet=$1
2040         local dm_dev=$2
2041
2042         local active_facet=$(facet_active $facet)
2043         local dev_alias=$(facet_device_alias $active_facet)
2044         local dev_name=${dev_alias}_dev
2045         local dev=${!dev_name}
2046
2047         if [[ $active_facet = $facet ]]; then
2048                 local failover_dev=${dev_alias}failover_dev
2049                 if [[ ${!failover_dev} = $dev ]]; then
2050                         eval export ${failover_dev}_saved=$dev
2051                         eval export ${failover_dev}=$dm_dev
2052                 fi
2053         else
2054                 dev_alias=$(facet_device_alias $facet)
2055                 local facet_dev=${dev_alias}_dev
2056                 if [[ ${!facet_dev} = $dev ]]; then
2057                         eval export ${facet_dev}_saved=$dev
2058                         eval export ${facet_dev}=$dm_dev
2059                 fi
2060         fi
2061
2062         eval export ${dev_name}_saved=$dev
2063         eval export ${dev_name}=$dm_dev
2064 }
2065
2066 #
2067 # Restore the saved value of the facet device.
2068 #
2069 unexport_dm_dev() {
2070         local facet=$1
2071
2072         [[ $facet = mgs ]] && combined_mgs_mds && facet=mds1
2073         local dev_alias=$(facet_device_alias $facet)
2074
2075         local saved_dev=${dev_alias}_dev_saved
2076         [[ -z ${!saved_dev} ]] ||
2077                 eval export ${dev_alias}_dev=${!saved_dev}
2078
2079         saved_dev=${dev_alias}failover_dev_saved
2080         [[ -z ${!saved_dev} ]] ||
2081                 eval export ${dev_alias}failover_dev=${!saved_dev}
2082 }
2083
2084 #
2085 # Remove a device-mapper device.
2086 # If the destination device is a loop device, then also detach it.
2087 #
2088 dm_cleanup_dev() {
2089         local facet=$1
2090         local dm_dev=${2:-$(dm_facet_devpath $facet)}
2091         local major
2092         local minor
2093
2094         is_dm_dev $facet $dm_dev || return 0
2095
2096         read major minor <<< $(do_facet $facet "$DMSETUP table $dm_dev" |
2097                 awk '{ print $4 }' | awk -F: '{ print $1" "$2 }')
2098
2099         do_facet $facet "$DMSETUP remove $dm_dev"
2100         do_facet $facet "$DMSETUP mknodes >/dev/null 2>&1"
2101
2102         unexport_dm_dev $facet
2103
2104         # detach a loop device
2105         [[ $major -ne 7 ]] || cleanup_loop_device $facet /dev/loop$minor
2106
2107         # unload dm-flakey module
2108         do_facet $facet "modprobe -r dm-flakey" || true
2109 }
2110
2111 mount_facet() {
2112         local facet=$1
2113         shift
2114         local active_facet=$(facet_active $facet)
2115         local dev_alias=$(facet_device_alias $active_facet)
2116         local dev=${dev_alias}_dev
2117         local opt=${facet}_opt
2118         local mntpt=$(facet_mntpt $facet)
2119         local opts="${!opt} $@"
2120         local fstype=$(facet_fstype $facet)
2121         local devicelabel
2122         local dm_dev=${!dev}
2123
2124         [[ $dev == "mgsfailover_dev" ]] && combined_mgs_mds &&
2125                 dev=mds1failover_dev
2126
2127         module_loaded lustre || load_modules
2128
2129         case $fstype in
2130         ldiskfs)
2131                 if dm_flakey_supported $facet; then
2132                         dm_dev=$(dm_create_dev $facet ${!dev})
2133                         [[ -n "$dm_dev" ]] || dm_dev=${!dev}
2134                 fi
2135
2136                 is_blkdev $facet $dm_dev || opts=$(csa_add "$opts" -o loop)
2137
2138                 devicelabel=$(do_facet ${facet} "$E2LABEL $dm_dev");;
2139         zfs)
2140                 # import ZFS storage pool
2141                 import_zpool $facet || return ${PIPESTATUS[0]}
2142
2143                 devicelabel=$(do_facet ${facet} "$ZFS get -H -o value \
2144                                                 lustre:svname $dm_dev");;
2145         *)
2146                 error "unknown fstype!";;
2147         esac
2148
2149         echo "Starting ${facet}: $opts $dm_dev $mntpt"
2150         # for testing LU-482 error handling in mount_facets() and test_0a()
2151         if [ -f $TMP/test-lu482-trigger ]; then
2152                 RC=2
2153         else
2154                 local seq_width=$(($OSTSEQWIDTH / $OSTCOUNT))
2155                 (( $seq_width >= 16384 )) || seq_width=16384
2156                 do_facet ${facet} \
2157                         "mkdir -p $mntpt; $MOUNT_CMD $opts $dm_dev $mntpt"
2158                 RC=${PIPESTATUS[0]}
2159                 if [[ ${facet} =~ ost ]]; then
2160                         do_facet ${facet} "$LCTL set_param \
2161                                 seq.cli-$(devicelabel $facet $dm_dev)-super.width=$seq_width"
2162                 fi
2163         fi
2164
2165         if [ $RC -ne 0 ]; then
2166                 echo "Start of $dm_dev on ${facet} failed ${RC}"
2167                 return $RC
2168         fi
2169
2170         health=$(do_facet ${facet} "$LCTL get_param -n health_check")
2171         if [[ "$health" != "healthy" ]]; then
2172                 error "$facet is in a unhealthy state"
2173         fi
2174
2175         set_default_debug_facet $facet
2176
2177         if [[ $opts =~ .*nosvc.* ]]; then
2178                 echo "Start $dm_dev without service"
2179         else
2180
2181                 case $fstype in
2182                 ldiskfs)
2183                         wait_update_facet ${facet} "$E2LABEL $dm_dev \
2184                                 2>/dev/null | grep -E ':[a-zA-Z]{3}[0-9]{4}'" \
2185                                 "" || error "$dm_dev failed to initialize!";;
2186                 zfs)
2187                         wait_update_facet ${facet} "$ZFS get -H -o value \
2188                                 lustre:svname $dm_dev 2>/dev/null | \
2189                                 grep -E ':[a-zA-Z]{3}[0-9]{4}'" "" ||
2190                                 error "$dm_dev failed to initialize!";;
2191
2192                 *)
2193                         error "unknown fstype!";;
2194                 esac
2195         fi
2196
2197         # commit the device label change to disk
2198         if [[ $devicelabel =~ (:[a-zA-Z]{3}[0-9]{4}) ]]; then
2199                 echo "Commit the device label on ${!dev}"
2200                 do_facet $facet "sync; sleep 1; sync"
2201         fi
2202
2203
2204         label=$(devicelabel ${facet} $dm_dev)
2205         [ -z "$label" ] && echo no label for $dm_dev && exit 1
2206         eval export ${facet}_svc=${label}
2207         echo Started ${label}
2208
2209         export_dm_dev $facet $dm_dev
2210
2211         return $RC
2212 }
2213
2214 # start facet device options
2215 start() {
2216         local facet=$1
2217         shift
2218         local device=$1
2219         shift
2220         local dev_alias=$(facet_device_alias $facet)
2221
2222         eval export ${dev_alias}_dev=${device}
2223         eval export ${facet}_opt=\"$*\"
2224
2225         combined_mgs_mds && [[ ${dev_alias} == mds1 ]] &&
2226                 eval export mgs_dev=${device}
2227
2228         local varname=${dev_alias}failover_dev
2229         if [ -n "${!varname}" ] ; then
2230                 eval export ${dev_alias}failover_dev=${!varname}
2231         else
2232                 eval export ${dev_alias}failover_dev=$device
2233                 combined_mgs_mds && [[ ${dev_alias} == mds1 ]] &&
2234                         eval export mgsfailover_dev=${device}
2235
2236         fi
2237
2238         local mntpt=$(facet_mntpt $facet)
2239         do_facet ${facet} mkdir -p $mntpt
2240         eval export ${facet}_MOUNT=$mntpt
2241         mount_facet ${facet}
2242         RC=$?
2243
2244         return $RC
2245 }
2246
2247 stop() {
2248         local running
2249         local facet=$1
2250         shift
2251         local HOST=$(facet_active_host $facet)
2252         [[ -z $HOST ]] && echo stop: no host for $facet && return 0
2253
2254         local mntpt=$(facet_mntpt $facet)
2255         running=$(do_facet ${facet} "grep -c $mntpt' ' /proc/mounts || true")
2256         if [ ${running} -ne 0 ]; then
2257                 echo "Stopping $mntpt (opts:$*) on $HOST"
2258                 do_facet ${facet} $UMOUNT "$@" $mntpt
2259         fi
2260
2261         # umount should block, but we should wait for unrelated obd's
2262         # like the MGS or MGC to also stop.
2263         wait_exit_ST ${facet} || return ${PIPESTATUS[0]}
2264
2265         if [[ $(facet_fstype $facet) == zfs ]]; then
2266                 # export ZFS storage pool
2267                 [ "$KEEP_ZPOOL" = "true" ] || export_zpool $facet
2268         elif dm_flakey_supported $facet; then
2269                 local host=${facet}_HOST
2270                 local failover_host=${facet}failover_HOST
2271                 if [[ -n ${!failover_host} && ${!failover_host} != ${!host} ]]||
2272                         $CLEANUP_DM_DEV || [[ $facet = fs* ]]; then
2273                         dm_cleanup_dev $facet
2274                 fi
2275         fi
2276 }
2277
2278 # get mdt quota type
2279 mdt_quota_type() {
2280         local varsvc=${SINGLEMDS}_svc
2281         do_facet $SINGLEMDS $LCTL get_param -n \
2282                 osd-$(facet_fstype $SINGLEMDS).${!varsvc}.quota_slave.enabled
2283 }
2284
2285 # get ost quota type
2286 ost_quota_type() {
2287         # All OSTs should have same quota type
2288         local varsvc=ost1_svc
2289         do_facet ost1 $LCTL get_param -n \
2290                 osd-$(facet_fstype ost1).${!varsvc}.quota_slave.enabled
2291 }
2292
2293 # restore old quota type settings
2294 restore_quota() {
2295         for usr in $QUOTA_USERS; do
2296                 echo "Setting up quota on $HOSTNAME:$MOUNT for $usr..."
2297                 for type in u g; do
2298                         cmd="$LFS setquota -$type $usr -b 0"
2299                         cmd="$cmd -B 0 -i 0 -I 0 $MOUNT"
2300                         echo "+ $cmd"
2301                         eval $cmd || error "$cmd FAILED!"
2302                 done
2303                 # display the quota status
2304                 echo "Quota settings for $usr : "
2305                 $LFS quota -v -u $usr $MOUNT || true
2306         done
2307         if [ "$old_MDT_QUOTA_TYPE" ]; then
2308                 if [[ $PERM_CMD == *"set_param -P"* ]]; then
2309                         do_facet mgs $PERM_CMD \
2310                                 osd-*.$FSNAME-MDT*.quota_slave.enabled = \
2311                                 $old_MDT_QUOTA_TYPE
2312                 else
2313                         do_facet mgs $PERM_CMD \
2314                                 $FSNAME.quota.mdt=$old_MDT_QUOTA_TYPE
2315                 fi
2316         fi
2317         if [ "$old_OST_QUOTA_TYPE" ]; then
2318                 if [[ $PERM_CMD == *"set_param -P"* ]]; then
2319                         do_facet mgs $PERM_CMD \
2320                                 osd-*.$FSNAME-OST*.quota_slave.enabled = \
2321                                 $old_OST_QUOTA_TYPE
2322                 else
2323                         do_facet mgs $LCTL conf_param \
2324                                 $FSNAME.quota.ost=$old_OST_QUOTA_TYPE
2325                 fi
2326         fi
2327 }
2328
2329 # Handle the case when there is a space in the lfs df
2330 # "filesystem summary" line the same as when there is no space.
2331 # This will allow fixing the "lfs df" summary line in the future.
2332 lfs_df() {
2333         $LFS df $* | sed -e 's/filesystem /filesystem_/'
2334         check_lfs_df_ret_val $?
2335 }
2336
2337 # Get free inodes on the MDT specified by mdt index, free indoes on
2338 # the whole filesystem will be returned when index == -1.
2339 mdt_free_inodes() {
2340         local index=$1
2341         local free_inodes
2342         local mdt_uuid
2343
2344         if [ $index -eq -1 ]; then
2345                 mdt_uuid="summary"
2346         else
2347                 mdt_uuid=$(mdtuuid_from_index $index)
2348         fi
2349
2350         free_inodes=$(lfs_df -i $MOUNT | grep $mdt_uuid | awk '{print $4}')
2351         echo $free_inodes
2352 }
2353
2354 #
2355 # Get the OST device status from 'lfs df' with a given OST index.
2356 #
2357 ost_dev_status() {
2358         local ost_idx=$1
2359         local mnt_pnt=${2:-$MOUNT}
2360         local opts=$3
2361         local ost_uuid
2362
2363         ost_uuid=$(ostuuid_from_index $ost_idx $mnt_pnt)
2364         lfs_df $opts $mnt_pnt | awk '/'$ost_uuid'/ { print $7 }'
2365 }
2366
2367 setup_quota(){
2368         local mntpt=$1
2369
2370         # save old quota type & set new quota type
2371         local mdt_qtype=$(mdt_quota_type)
2372         local ost_qtype=$(ost_quota_type)
2373
2374         echo "[HOST:$HOSTNAME] [old_mdt_qtype:$mdt_qtype]" \
2375                 "[old_ost_qtype:$ost_qtype] [new_qtype:$QUOTA_TYPE]"
2376
2377         export old_MDT_QUOTA_TYPE=$mdt_qtype
2378         export old_OST_QUOTA_TYPE=$ost_qtype
2379
2380         if [[ $PERM_CMD == *"set_param -P"* ]]; then
2381                 do_facet mgs $PERM_CMD \
2382                         osd-*.$FSNAME-MDT*.quota_slave.enabled=$QUOTA_TYPE
2383                 do_facet mgs $PERM_CMD \
2384                         osd-*.$FSNAME-OST*.quota_slave.enabled=$QUOTA_TYPE
2385         else
2386                 do_facet mgs $PERM_CMD $FSNAME.quota.mdt=$QUOTA_TYPE ||
2387                         error "set mdt quota type failed"
2388                 do_facet mgs $PERM_CMD $FSNAME.quota.ost=$QUOTA_TYPE ||
2389                         error "set ost quota type failed"
2390         fi
2391
2392         local quota_usrs=$QUOTA_USERS
2393
2394         # get_filesystem_size
2395         local disksz=$(lfs_df $mntpt | grep "summary" | awk '{print $2}')
2396         local blk_soft=$((disksz + 1024))
2397         local blk_hard=$((blk_soft + blk_soft / 20)) # Go 5% over
2398
2399         local inodes=$(lfs_df -i $mntpt | grep "summary" | awk '{print $2}')
2400         local i_soft=$inodes
2401         local i_hard=$((i_soft + i_soft / 20))
2402
2403         echo "Total disk size: $disksz  block-softlimit: $blk_soft" \
2404                 "block-hardlimit: $blk_hard inode-softlimit: $i_soft" \
2405                 "inode-hardlimit: $i_hard"
2406
2407         local cmd
2408         for usr in $quota_usrs; do
2409                 echo "Setting up quota on $HOSTNAME:$mntpt for $usr..."
2410                 for type in u g; do
2411                         cmd="$LFS setquota -$type $usr -b $blk_soft"
2412                         cmd="$cmd -B $blk_hard -i $i_soft -I $i_hard $mntpt"
2413                         echo "+ $cmd"
2414                         eval $cmd || error "$cmd FAILED!"
2415                 done
2416                 # display the quota status
2417                 echo "Quota settings for $usr : "
2418                 $LFS quota -v -u $usr $mntpt || true
2419         done
2420 }
2421
2422 zconf_mount() {
2423         local client=$1
2424         local mnt=$2
2425         local opts=${3:-$MOUNT_OPTS}
2426         opts=${opts:+-o $opts}
2427         local flags=${4:-$MOUNT_FLAGS}
2428
2429         local device=$MGSNID:/$FSNAME$FILESET
2430         if [ -z "$mnt" -o -z "$FSNAME" ]; then
2431                 echo "Bad mount command: opt=$flags $opts dev=$device " \
2432                      "mnt=$mnt"
2433                 exit 1
2434         fi
2435
2436         if $GSS_SK; then
2437                 # update mount option with skpath
2438                 opts=$(add_sk_mntflag $opts)
2439         fi
2440
2441         echo "Starting client: $client: $flags $opts $device $mnt"
2442         do_node $client mkdir -p $mnt
2443         if [ -n "$FILESET" -a -z "$SKIP_FILESET" ];then
2444                 do_node $client $MOUNT_CMD $flags $opts $MGSNID:/$FSNAME \
2445                         $mnt || return 1
2446                 #disable FILESET if not supported
2447                 do_nodes $client lctl get_param -n \
2448                         mdc.$FSNAME-MDT0000*.import | grep -q subtree ||
2449                                 device=$MGSNID:/$FSNAME
2450                 do_node $client mkdir -p $mnt/$FILESET
2451                 do_node $client "! grep -q $mnt' ' /proc/mounts ||
2452                         umount $mnt"
2453         fi
2454         if $GSS_SK && ($SK_UNIQUE_NM || $SK_S2S); then
2455                 # Mount using nodemap key
2456                 local mountkey=$SK_PATH/$FSNAME-nmclient.key
2457                 if $SK_UNIQUE_NM; then
2458                         mountkey=$SK_PATH/nodemap/c0.key
2459                 fi
2460                 local prunedopts=$(echo $opts |
2461                                 sed -e "s#skpath=[^,^ ]*#skpath=$mountkey#g")
2462                 do_node $client $MOUNT_CMD $flags $prunedopts $device $mnt ||
2463                                 return 1
2464         else
2465                 do_node $client $MOUNT_CMD $flags $opts $device $mnt ||
2466                                 return 1
2467         fi
2468
2469         set_default_debug_nodes $client
2470         set_params_clients $client
2471
2472         return 0
2473 }
2474
2475 zconf_umount() {
2476         local client=$1
2477         local mnt=$2
2478         local force
2479         local busy
2480         local need_kill
2481         local running=$(do_node $client "grep -c $mnt' ' /proc/mounts") || true
2482
2483         [ "$3" ] && force=-f
2484         [ $running -eq 0 ] && return 0
2485
2486         echo "Stopping client $client $mnt (opts:$force)"
2487         do_node $client lsof -t $mnt || need_kill=no
2488         if [ "x$force" != "x" ] && [ "x$need_kill" != "xno" ]; then
2489                 pids=$(do_node $client lsof -t $mnt | sort -u);
2490                 if [ -n "$pids" ]; then
2491                         do_node $client kill -9 $pids || true
2492                 fi
2493         fi
2494
2495         busy=$(do_node $client "umount $force $mnt 2>&1" | grep -c "busy") ||
2496                 true
2497         if [ $busy -ne 0 ] ; then
2498                 echo "$mnt is still busy, wait one second" && sleep 1
2499                 do_node $client umount $force $mnt
2500         fi
2501 }
2502
2503 # Mount the file system on the MDS
2504 mount_mds_client() {
2505         local host=$(facet_active_host $SINGLEMDS)
2506         echo $host
2507         zconf_mount $host $MOUNT2 $MOUNT_OPTS ||
2508                 error "unable to mount $MOUNT2 on $host"
2509 }
2510
2511 # Unmount the file system on the MDS
2512 umount_mds_client() {
2513         local host=$(facet_active_host $SINGLEMDS)
2514         zconf_umount $host $MOUNT2
2515         do_facet $SINGLEMDS "rmdir $MOUNT2"
2516 }
2517
2518 # nodes is comma list
2519 sanity_mount_check_nodes () {
2520         local nodes=$1
2521         shift
2522         local mnts="$@"
2523         local mnt
2524
2525         # FIXME: assume that all cluster nodes run the same os
2526         [ "$(uname)" = Linux ] || return 0
2527
2528         local rc=0
2529         for mnt in $mnts ; do
2530                 do_nodes $nodes "running=\\\$(grep -c $mnt' ' /proc/mounts);
2531 mpts=\\\$(mount | grep -c $mnt' ');
2532 if [ \\\$running -ne \\\$mpts ]; then
2533     echo \\\$(hostname) env are INSANE!;
2534     exit 1;
2535 fi"
2536                 [ $? -eq 0 ] || rc=1
2537         done
2538         return $rc
2539 }
2540
2541 sanity_mount_check_servers () {
2542         [ -n "$CLIENTONLY" ] &&
2543                 { echo "CLIENTONLY mode, skip mount_check_servers"; return 0; } || true
2544         echo Checking servers environments
2545
2546         # FIXME: modify get_facets to display all facets wo params
2547         local facets="$(get_facets OST),$(get_facets MDS),mgs"
2548         local node
2549         local mntpt
2550         local facet
2551         for facet in ${facets//,/ }; do
2552         node=$(facet_host ${facet})
2553         mntpt=$(facet_mntpt $facet)
2554         sanity_mount_check_nodes $node $mntpt ||
2555                 { error "server $node environments are insane!"; return 1; }
2556         done
2557 }
2558
2559 sanity_mount_check_clients () {
2560         local clients=${1:-$CLIENTS}
2561         local mntpt=${2:-$MOUNT}
2562         local mntpt2=${3:-$MOUNT2}
2563
2564         [ -z $clients ] && clients=$(hostname)
2565         echo Checking clients $clients environments
2566
2567         sanity_mount_check_nodes $clients $mntpt $mntpt2 ||
2568                 error "clients environments are insane!"
2569 }
2570
2571 sanity_mount_check () {
2572         sanity_mount_check_servers || return 1
2573         sanity_mount_check_clients || return 2
2574 }
2575
2576 # mount clients if not mouted
2577 zconf_mount_clients() {
2578         local clients=$1
2579         local mnt=$2
2580         local opts=${3:-$MOUNT_OPTS}
2581         opts=${opts:+-o $opts}
2582         local flags=${4:-$MOUNT_FLAGS}
2583         local device=$MGSNID:/$FSNAME$FILESET
2584         if [ -z "$mnt" -o -z "$FSNAME" ]; then
2585                 echo "Bad conf mount command: opt=$flags $opts dev=$device " \
2586                      "mnt=$mnt"
2587                 exit 1
2588         fi
2589
2590         echo "Starting client $clients: $flags $opts $device $mnt"
2591         do_nodes $clients mkdir -p $mnt
2592         if [ -n "$FILESET" -a -z "$SKIP_FILESET" ]; then
2593                 if $GSS_SK && ($SK_UNIQUE_NM || $SK_S2S); then
2594                         # Mount with own nodemap key
2595                         local i=0
2596                         # Mount all server nodes first with per-NM keys
2597                         for nmclient in ${clients//,/ }; do
2598                                 do_nodes $(comma_list $(all_server_nodes)) \
2599                                 "$LGSS_SK -t server -l $SK_PATH/nodemap/c$i.key"
2600                                 i=$((i + 1))
2601                         done
2602                         # set perms for per-nodemap keys else permission denied
2603                         do_nodes $(comma_list $(all_nodes)) \
2604                                 "keyctl show | grep lustre | cut -c1-11 |
2605                                 sed -e 's/ //g;' |
2606                                 xargs -IX keyctl setperm X 0x3f3f3f3f"
2607                         local mountkey=$SK_PATH/$FSNAME-nmclient.key
2608                         i=0
2609                         for nmclient in ${clients//,/ }; do
2610                                 if $SK_UNIQUE_NM; then
2611                                         mountkey=$SK_PATH/nodemap/c$i.key
2612                                 fi
2613                                 do_node $nmclient "! grep -q $mnt' ' \
2614                                         /proc/mounts || umount $mnt"
2615                                 local prunedopts=$(add_sk_mntflag $opts);
2616                                 prunedopts=$(echo $prunedopts | sed -e \
2617                                         "s#skpath=[^ ^,]*#skpath=$mountkey#g")
2618                                 set -x
2619                                 do_nodes $(comma_list $(all_server_nodes)) \
2620                                         "keyctl show"
2621                                 set +x
2622                                 do_node $nmclient $MOUNT_CMD $flags \
2623                                         $prunedopts $MGSNID:/$FSNAME $mnt ||
2624                                         return 1
2625                                 i=$((i + 1))
2626                         done
2627                 else
2628                         do_nodes $clients "! grep -q $mnt' ' /proc/mounts ||
2629                                         umount $mnt"
2630                         do_nodes $clients $MOUNT_CMD $flags $opts \
2631                                         $MGSNID:/$FSNAME $mnt || return 1
2632                 fi
2633                 #disable FILESET if not supported
2634                 do_nodes $clients lctl get_param -n \
2635                         mdc.$FSNAME-MDT0000*.import | grep -q subtree ||
2636                                 device=$MGSNID:/$FSNAME
2637                 do_nodes $clients mkdir -p $mnt/$FILESET
2638                 do_nodes $clients "! grep -q $mnt' ' /proc/mounts ||
2639                         umount $mnt"
2640         fi
2641
2642         if $GSS_SK && ($SK_UNIQUE_NM || $SK_S2S); then
2643                 # Mount with nodemap key
2644                 local i=0
2645                 local mountkey=$SK_PATH/$FSNAME-nmclient.key
2646                 for nmclient in ${clients//,/ }; do
2647                         if $SK_UNIQUE_NM; then
2648                                 mountkey=$SK_PATH/nodemap/c$i.key
2649                         fi
2650                         local prunedopts=$(echo $opts | sed -e \
2651                                 "s#skpath=[^ ^,]*#skpath=$mountkey#g");
2652                         do_node $nmclient "! grep -q $mnt' ' /proc/mounts ||
2653                                 umount $mnt"
2654                         do_node $nmclient "
2655                 running=\\\$(mount | grep -c $mnt' ');
2656                 rc=0;
2657                 if [ \\\$running -eq 0 ] ; then
2658                         mkdir -p $mnt;
2659                         $MOUNT_CMD $flags $prunedopts $device $mnt;
2660                         rc=\\\$?;
2661                 else
2662                         lustre_mnt_count=\\\$(mount | grep $mnt' ' | \
2663                                 grep 'type lustre' | wc -l);
2664                         if [ \\\$running -ne \\\$lustre_mnt_count ] ; then
2665                                 echo zconf_mount_clients FAILED: \
2666                                         mount count \\\$running, not matching \
2667                                         with mount count of 'type lustre' \
2668                                         \\\$lustre_mnt_count;
2669                                 rc=1;
2670                         fi;
2671                 fi;
2672         exit \\\$rc" || return ${PIPESTATUS[0]}
2673
2674                         i=$((i + 1))
2675                 done
2676         else
2677
2678                 local tmpopts=$opts
2679                 if $SHARED_KEY; then
2680                         tmpopts=$(add_sk_mntflag $opts)
2681                 fi
2682                 do_nodes $clients "
2683 running=\\\$(mount | grep -c $mnt' ');
2684 rc=0;
2685 if [ \\\$running -eq 0 ] ; then
2686         mkdir -p $mnt;
2687         $MOUNT_CMD $flags $tmpopts $device $mnt;
2688         rc=\\\$?;
2689 fi;
2690 exit \\\$rc" || return ${PIPESTATUS[0]}
2691         fi
2692
2693         echo "Started clients $clients: "
2694         do_nodes $clients "mount | grep $mnt' '"
2695
2696         set_default_debug_nodes $clients
2697         set_params_clients $clients
2698
2699         return 0
2700 }
2701
2702 zconf_umount_clients() {
2703         local clients=$1
2704         local mnt=$2
2705         local force
2706
2707         [ "$3" ] && force=-f
2708
2709         echo "Stopping clients: $clients $mnt (opts:$force)"
2710         do_nodes $clients "running=\\\$(grep -c $mnt' ' /proc/mounts);
2711 if [ \\\$running -ne 0 ] ; then
2712 echo Stopping client \\\$(hostname) $mnt opts:$force;
2713 lsof $mnt || need_kill=no;
2714 if [ "x$force" != "x" -a "x\\\$need_kill" != "xno" ]; then
2715     pids=\\\$(lsof -t $mnt | sort -u);
2716     if [ -n \\\"\\\$pids\\\" ]; then
2717              kill -9 \\\$pids;
2718     fi
2719 fi;
2720 while umount $force $mnt 2>&1 | grep -q "busy"; do
2721     echo "$mnt is still busy, wait one second" && sleep 1;
2722 done;
2723 fi"
2724 }
2725
2726 shutdown_node () {
2727         local node=$1
2728
2729         echo + $POWER_DOWN $node
2730         $POWER_DOWN $node
2731 }
2732
2733 shutdown_node_hard () {
2734         local host=$1
2735         local attempts=$SHUTDOWN_ATTEMPTS
2736
2737         for i in $(seq $attempts) ; do
2738                 shutdown_node $host
2739                 sleep 1
2740                 wait_for_function --quiet "! ping -w 3 -c 1 $host" 5 1 &&
2741                         return 0
2742                 echo "waiting for $host to fail attempts=$attempts"
2743                 [ $i -lt $attempts ] ||
2744                         { echo "$host still pingable after power down! attempts=$attempts" && return 1; }
2745         done
2746 }
2747
2748 shutdown_client() {
2749         local client=$1
2750         local mnt=${2:-$MOUNT}
2751         local attempts=3
2752
2753         if [ "$FAILURE_MODE" = HARD ]; then
2754                 shutdown_node_hard $client
2755         else
2756                 zconf_umount_clients $client $mnt -f
2757         fi
2758 }
2759
2760 facets_on_host () {
2761         local affected
2762         local host=$1
2763         local facets="$(get_facets OST),$(get_facets MDS)"
2764
2765         combined_mgs_mds || facets="$facets,mgs"
2766
2767         for facet in ${facets//,/ }; do
2768                 if [ $(facet_active_host $facet) == $host ]; then
2769                         affected="$affected $facet"
2770                 fi
2771         done
2772
2773         echo $(comma_list $affected)
2774 }
2775
2776 facet_up() {
2777         local facet=$1
2778         local host=${2:-$(facet_host $facet)}
2779
2780         local label=$(convert_facet2label $facet)
2781         do_node $host $LCTL dl | awk '{ print $4 }' | grep -q "^$label\$"
2782 }
2783
2784 facets_up_on_host () {
2785         local affected_up
2786         local host=$1
2787         local facets=$(facets_on_host $host)
2788
2789         for facet in ${facets//,/ }; do
2790                 if $(facet_up $facet $host); then
2791                         affected_up="$affected_up $facet"
2792                 fi
2793         done
2794
2795         echo $(comma_list $affected_up)
2796 }
2797
2798 shutdown_facet() {
2799         local facet=$1
2800         local affected_facet
2801         local affected_facets
2802
2803         if [[ "$FAILURE_MODE" = HARD ]]; then
2804                 if [[ $(facet_fstype $facet) = ldiskfs ]] &&
2805                         dm_flakey_supported $facet; then
2806                         affected_facets=$(affected_facets $facet)
2807                         for affected_facet in ${affected_facets//,/ }; do
2808                                 unexport_dm_dev $affected_facet
2809                         done
2810                 fi
2811
2812                 shutdown_node_hard $(facet_active_host $facet)
2813         else
2814                 stop $facet
2815         fi
2816 }
2817
2818 reboot_node() {
2819         local node=$1
2820
2821         echo + $POWER_UP $node
2822         $POWER_UP $node
2823 }
2824
2825 remount_facet() {
2826         local facet=$1
2827
2828         stop $facet
2829         mount_facet $facet
2830 }
2831
2832 reboot_facet() {
2833         local facet=$1
2834         local node=$(facet_active_host $facet)
2835         local sleep_time=${2:-10}
2836
2837         if [ "$FAILURE_MODE" = HARD ]; then
2838                 boot_node $node
2839         else
2840                 sleep $sleep_time
2841         fi
2842 }
2843
2844 boot_node() {
2845         local node=$1
2846
2847         if [ "$FAILURE_MODE" = HARD ]; then
2848                 reboot_node $node
2849                 wait_for_host $node
2850                 if $LOAD_MODULES_REMOTE; then
2851                         echo "loading modules on $node: $facet"
2852                         do_rpc_nodes $node load_modules_local
2853                 fi
2854         fi
2855 }
2856
2857 facets_hosts () {
2858         local hosts
2859         local facets=$1
2860
2861         for facet in ${facets//,/ }; do
2862                 hosts=$(expand_list $hosts $(facet_host $facet) )
2863         done
2864
2865         echo $hosts
2866 }
2867
2868 _check_progs_installed () {
2869         local progs=$@
2870         local rc=0
2871
2872         for prog in $progs; do
2873                 if ! [ "$(which $prog)"  -o  "${!prog}" ]; then
2874                         echo $prog missing on $(hostname)
2875                         rc=1
2876                 fi
2877         done
2878         return $rc
2879 }
2880
2881 check_progs_installed () {
2882         local nodes=$1
2883         shift
2884
2885         do_rpc_nodes "$nodes" _check_progs_installed "$@"
2886 }
2887
2888 # recovery-scale functions
2889 node_var_name() {
2890     echo __$(echo $1 | tr '-' '_' | tr '.' '_')
2891 }
2892
2893 start_client_load() {
2894         local client=$1
2895         local load=$2
2896         local var=$(node_var_name $client)_load
2897         eval export ${var}=$load
2898
2899         do_node $client "PATH=$PATH MOUNT=$MOUNT ERRORS_OK=$ERRORS_OK \
2900                         BREAK_ON_ERROR=$BREAK_ON_ERROR \
2901                         END_RUN_FILE=$END_RUN_FILE \
2902                         LOAD_PID_FILE=$LOAD_PID_FILE \
2903                         TESTLOG_PREFIX=$TESTLOG_PREFIX \
2904                         TESTNAME=$TESTNAME \
2905                         DBENCH_LIB=$DBENCH_LIB \
2906                         DBENCH_SRC=$DBENCH_SRC \
2907                         CLIENT_COUNT=$((CLIENTCOUNT - 1)) \
2908                         LFS=$LFS \
2909                         LCTL=$LCTL \
2910                         FSNAME=$FSNAME \
2911                         MPIRUN=$MPIRUN \
2912                         MPIRUN_OPTIONS=\\\"$MPIRUN_OPTIONS\\\" \
2913                         MACHINEFILE_OPTION=\\\"$MACHINEFILE_OPTION\\\" \
2914                         num_clients=$(get_node_count ${CLIENTS//,/ }) \
2915                         ior_THREADS=$ior_THREADS ior_iteration=$ior_iteration \
2916                         ior_blockSize=$ior_blockSize \
2917                         ior_blockUnit=$ior_blockUnit \
2918                         ior_xferSize=$ior_xferSize ior_type=$ior_type \
2919                         ior_DURATION=$ior_DURATION \
2920                         ior_stripe_params=\\\"$ior_stripe_params\\\" \
2921                         ior_custom_params=\\\"$ior_custom_param\\\" \
2922                         mpi_ior_custom_threads=$mpi_ior_custom_threads \
2923                         run_${load}.sh" &
2924         local ppid=$!
2925         log "Started client load: ${load} on $client"
2926
2927         # get the children process IDs
2928         local pids=$(ps --ppid $ppid -o pid= | xargs)
2929         CLIENT_LOAD_PIDS="$CLIENT_LOAD_PIDS $ppid $pids"
2930         return 0
2931 }
2932
2933 start_client_loads () {
2934         local -a clients=(${1//,/ })
2935         local numloads=${#CLIENT_LOADS[@]}
2936
2937         for ((nodenum=0; nodenum < ${#clients[@]}; nodenum++ )); do
2938                 local load=$((nodenum % numloads))
2939                 start_client_load ${clients[nodenum]} ${CLIENT_LOADS[load]}
2940         done
2941         # bug 22169: wait the background threads to start
2942         sleep 2
2943 }
2944
2945 # only for remote client
2946 check_client_load () {
2947         local client=$1
2948         local var=$(node_var_name $client)_load
2949         local testload=run_${!var}.sh
2950
2951         ps auxww | grep -v grep | grep $client | grep -q $testload || return 1
2952
2953         # bug 18914: try to connect several times not only when
2954         # check ps, but  while check_node_health also
2955
2956         local tries=3
2957         local RC=254
2958         while [ $RC = 254 -a $tries -gt 0 ]; do
2959                 let tries=$tries-1
2960                 # assume success
2961                 RC=0
2962                 if ! check_node_health $client; then
2963                         RC=${PIPESTATUS[0]}
2964                         if [ $RC -eq 254 ]; then
2965                                 # FIXME: not sure how long we shuold sleep here
2966                                 sleep 10
2967                                 continue
2968                         fi
2969                         echo "check node health failed: RC=$RC "
2970                         return $RC
2971                 fi
2972         done
2973         # We can continue try to connect if RC=254
2974         # Just print the warning about this
2975         if [ $RC = 254 ]; then
2976                 echo "got a return status of $RC from do_node while checking " \
2977                 "node health on $client"
2978         fi
2979
2980         # see if the load is still on the client
2981         tries=3
2982         RC=254
2983         while [ $RC = 254 -a $tries -gt 0 ]; do
2984                 let tries=$tries-1
2985                 # assume success
2986                 RC=0
2987                 if ! do_node $client \
2988                         "ps auxwww | grep -v grep | grep -q $testload"; then
2989                         RC=${PIPESTATUS[0]}
2990                         sleep 30
2991                 fi
2992         done
2993         if [ $RC = 254 ]; then
2994                 echo "got a return status of $RC from do_node while checking " \
2995                 "(node health and 'ps') the client load on $client"
2996                 # see if we can diagnose a bit why this is
2997         fi
2998
2999         return $RC
3000 }
3001 check_client_loads () {
3002         local clients=${1//,/ }
3003         local client=
3004         local rc=0
3005
3006         for client in $clients; do
3007                 check_client_load $client
3008                 rc=${PIPESTATUS[0]}
3009                 if [ "$rc" != 0 ]; then
3010                         log "Client load failed on node $client, rc=$rc"
3011                         return $rc
3012                 fi
3013         done
3014 }
3015
3016 restart_client_loads () {
3017         local clients=${1//,/ }
3018         local expectedfail=${2:-""}
3019         local client=
3020         local rc=0
3021
3022         for client in $clients; do
3023                 check_client_load $client
3024                 rc=${PIPESTATUS[0]}
3025                 if [ "$rc" != 0 -a "$expectedfail" ]; then
3026                         local var=$(node_var_name $client)_load
3027
3028                         start_client_load $client ${!var}
3029                         echo "Restarted client load ${!var}: on $client. Checking ..."
3030                         check_client_load $client
3031                         rc=${PIPESTATUS[0]}
3032                         if [ "$rc" != 0 ]; then
3033                                 log "Client load failed to restart on node $client, rc=$rc"
3034                                 # failure one client load means test fail
3035                                 # we do not need to check other
3036                                 return $rc
3037                         fi
3038                 else
3039                         return $rc
3040                 fi
3041         done
3042 }
3043
3044 # Start vmstat and save its process ID in a file.
3045 start_vmstat() {
3046         local nodes=$1
3047         local pid_file=$2
3048
3049         [ -z "$nodes" -o -z "$pid_file" ] && return 0
3050
3051         do_nodes $nodes \
3052         "vmstat 1 > $TESTLOG_PREFIX.$TESTNAME.vmstat.\\\$(hostname -s).log \
3053         2>/dev/null </dev/null & echo \\\$! > $pid_file"
3054 }
3055
3056 # Display the nodes on which client loads failed.
3057 print_end_run_file() {
3058         local file=$1
3059         local node
3060
3061         [ -s $file ] || return 0
3062
3063         echo "Found the END_RUN_FILE file: $file"
3064         cat $file
3065
3066         # A client load will stop if it finds the END_RUN_FILE file.
3067         # That does not mean the client load actually failed though.
3068         # The first node in END_RUN_FILE is the one we are interested in.
3069         read node < $file
3070
3071         if [ -n "$node" ]; then
3072                 local var=$(node_var_name $node)_load
3073
3074                 local prefix=$TESTLOG_PREFIX
3075                 [ -n "$TESTNAME" ] && prefix=$prefix.$TESTNAME
3076                 local stdout_log=$prefix.run_${!var}_stdout.$node.log
3077                 local debug_log=$(echo $stdout_log |
3078                         sed 's/\(.*\)stdout/\1debug/')
3079
3080                 echo "Client load ${!var} failed on node $node:"
3081                 echo "$stdout_log"
3082                 echo "$debug_log"
3083         fi
3084 }
3085
3086 # Stop the process which had its PID saved in a file.
3087 stop_process() {
3088         local nodes=$1
3089         local pid_file=$2
3090
3091         [ -z "$nodes" -o -z "$pid_file" ] && return 0
3092
3093         do_nodes $nodes "test -f $pid_file &&
3094                 { kill -s TERM \\\$(cat $pid_file); rm -f $pid_file; }" || true
3095 }
3096
3097 # Stop all client loads.
3098 stop_client_loads() {
3099         local nodes=${1:-$CLIENTS}
3100         local pid_file=$2
3101
3102         # stop the client loads
3103         stop_process $nodes $pid_file
3104
3105         # clean up the processes that started them
3106         [ -n "$CLIENT_LOAD_PIDS" ] &&
3107                 kill -9 $CLIENT_LOAD_PIDS 2>/dev/null || true
3108 }
3109 # End recovery-scale functions
3110
3111 ##
3112 # wait for a command to return the expected result
3113 #
3114 # This will run @check on @node repeatedly until the output matches @expect
3115 # based on the supplied condition, or until @max_wait seconds have elapsed,
3116 # whichever comes first.  @cond may be one of the normal bash operators,
3117 # "-gt", "-ge", "-eq", "-le", "-lt", "==", "!=", or "=~", and must be quoted
3118 # in the caller to avoid unintentional evaluation by the shell in the caller.
3119 #
3120 # If @max_wait is not specified, the condition will be checked for up to 90s.
3121 #
3122 # If --verbose is passed as the first argument, the result is printed on each
3123 # value change, otherwise it is only printed after every 10s interval.
3124 #
3125 # If --quiet is passed as the first/second argument, the do_node() command
3126 # will not print the remote command before executing it each time.
3127 #
3128 # Using wait_update_cond() or related helper function is preferable to adding
3129 # a "long enough" wait for some state to change in the background, since
3130 # "long enough" may be too short due to tunables, system config, or running in
3131 # a VM, and must by necessity wait too long for most cases or risk failure.
3132 #
3133 # usage: wait_update_cond [--verbose] [--quiet] node check cond expect [max_wait]
3134 wait_update_cond() {
3135         local verbose
3136         local quiet
3137
3138         [[ "$1" == "--verbose" ]] && verbose="$1" && shift
3139         [[ "$1" == "--quiet" || "$1" == "-q" ]] && quiet="$1" && shift
3140
3141         local node=$1
3142         local check="$2"
3143         local cond="$3"
3144         local expect="$4"
3145         local max_wait=${5:-90}
3146         local result
3147         local prev_result
3148         local waited=0
3149         local begin=$SECONDS
3150         local sleep=1
3151         local print=10
3152
3153         while (( $waited <= $max_wait )); do
3154                 result=$(do_node $quiet $node "$check")
3155
3156                 eval [[ "'$result'" $cond "'$expect'" ]]
3157                 if [[ $? == 0 ]]; then
3158                         [[ -z "$result" || $waited -le $sleep ]] ||
3159                                 echo "Updated after ${waited}s: want '$expect' got '$result'"
3160                         return 0
3161                 fi
3162                 if [[ -n "$verbose" && "$result" != "$prev_result" ]]; then
3163                         [[ -n "$prev_result" ]] &&
3164                                 echo "Changed after ${waited}s: from '$prev_result' to '$result'"
3165                         prev_result="$result"
3166                 fi
3167                 (( $waited % $print == 0 )) &&
3168                         echo "Waiting $((max_wait - waited))s for '$expect'"
3169                 sleep $sleep
3170                 waited=$((SECONDS - begin))
3171         done
3172         echo "Update not seen after ${max_wait}s: want '$expect' got '$result'"
3173         return 3
3174 }
3175
3176 # usage: wait_update [--verbose] [--quiet] node check expect [max_wait]
3177 wait_update() {
3178         local verbose
3179         local quiet
3180
3181         [[ "$1" == "--verbose" ]] && verbose="$1" && shift
3182         [[ "$1" == "--quiet" || "$1" == "-q" ]] && quiet="$1" && shift
3183
3184         local node="$1"
3185         local check="$2"
3186         local expect="$3"
3187         local max_wait=$4
3188
3189         wait_update_cond $verbose $quiet $node "$check" "==" "$expect" $max_wait
3190 }
3191
3192 # usage: wait_update_facet_cond [--verbose] facet check cond expect [max_wait]
3193 wait_update_facet_cond() {
3194         local verbose
3195         local quiet
3196
3197         [[ "$1" == "--verbose" ]] && verbose="$1" && shift
3198         [[ "$1" == "--quiet" || "$1" == "-q" ]] && quiet="$1" && shift
3199
3200         local node=$(facet_active_host $1)
3201         local check="$2"
3202         local cond="$3"
3203         local expect="$4"
3204         local max_wait=$5
3205
3206         wait_update_cond $verbose $quiet $node "$check" "$cond" "$expect" $max_wait
3207 }
3208
3209 # usage: wait_update_facet [--verbose] facet check expect [max_wait]
3210 wait_update_facet() {
3211         local verbose
3212         local quiet
3213
3214         [[ "$1" == "--verbose" ]] && verbose="$1" && shift
3215         [[ "$1" == "--quiet" || "$1" == "-q" ]] && quiet="$1" && shift
3216
3217         local node=$(facet_active_host $1)
3218         local check="$2"
3219         local expect="$3"
3220         local max_wait=$4
3221
3222         wait_update_cond $verbose $quiet $node "$check" "==" "$expect" $max_wait
3223 }
3224
3225 sync_all_data() {
3226         do_nodes $(comma_list $(mdts_nodes)) \
3227             "lctl set_param -n os[cd]*.*MDT*.force_sync=1"
3228         do_nodes $(comma_list $(osts_nodes)) \
3229             "lctl set_param -n osd*.*OS*.force_sync=1" 2>&1 |
3230                 grep -v 'Found no match'
3231 }
3232
3233 wait_zfs_commit() {
3234         local zfs_wait=${2:-5}
3235
3236         # the occupied disk space will be released
3237         # only after TXGs are committed
3238         if [[ $(facet_fstype $1) == zfs ]]; then
3239                 echo "sleep $zfs_wait for ZFS $(facet_fstype $1)"
3240                 sleep $zfs_wait
3241         fi
3242 }
3243
3244 fill_ost() {
3245         local filename=$1
3246         local ost_idx=$2
3247         local lwm=$3  #low watermark
3248         local size_mb #how many MB should we write to pass watermark
3249         local ost_name=$(ostname_from_index $ost_idx)
3250
3251         free_kb=$($LFS df $MOUNT | awk "/$ost_name/ { print \$4 }")
3252         size_mb=0
3253         if (( $free_kb / 1024 > lwm )); then
3254                 size_mb=$((free_kb / 1024 - lwm))
3255         fi
3256         #If 10% of free space cross low watermark use it
3257         if (( $free_kb / 10240 > size_mb )); then
3258                 size_mb=$((free_kb / 10240))
3259         else
3260                 #At least we need to store 1.1 of difference between
3261                 #free space and low watermark
3262                 size_mb=$((size_mb + size_mb / 10))
3263         fi
3264         if (( lwm <= $free_kb / 1024 )) ||
3265            [ ! -f $DIR/${filename}.fill_ost$ost_idx ]; then
3266                 $LFS setstripe -i $ost_idx -c1 $DIR/${filename}.fill_ost$ost_idx
3267                 dd if=/dev/zero of=$DIR/${filename}.fill_ost$ost_idx bs=1M \
3268                         count=$size_mb oflag=append conv=notrunc
3269         fi
3270
3271         sleep_maxage
3272
3273         free_kb=$($LFS df $MOUNT | awk "/$ost_name/ { print \$4 }")
3274         echo "OST still has $((free_kb / 1024)) MB free"
3275 }
3276
3277 # This checks only the primary MDS
3278 ost_watermarks_get() {
3279         local ost_idx=$1
3280         local ost_name=$(ostname_from_index $ost_idx)
3281         local mdtosc_proc=$(get_mdtosc_proc_path $SINGLEMDS $ost_name)
3282
3283         local hwm=$(do_facet $SINGLEMDS $LCTL get_param -n \
3284                         osp.$mdtosc_proc.reserved_mb_high)
3285         local lwm=$(do_facet $SINGLEMDS $LCTL get_param -n \
3286                         osp.$mdtosc_proc.reserved_mb_low)
3287
3288         echo "$lwm $hwm"
3289 }
3290
3291 # Note that we set watermarks on all MDSes (necessary for striped dirs)
3292 ost_watermarks_set() {
3293         local ost_idx=$1
3294         local lwm=$2
3295         local hwm=$3
3296         local ost_name=$(ostname_from_index $ost_idx)
3297         local facets=$(get_facets MDS)
3298
3299         do_nodes $(comma_list $(mdts_nodes)) $LCTL set_param -n \
3300                 osp.*$ost_name*.reserved_mb_low=$lwm \
3301                 osp.*$ost_name*.reserved_mb_high=$hwm > /dev/null
3302
3303         # sleep to ensure we see the change
3304         sleep_maxage
3305 }
3306
3307 ost_watermarks_set_low_space() {
3308         local ost_idx=$1
3309         local wms=$(ost_watermarks_get $ost_idx)
3310         local ost_name=$(ostname_from_index $ost_idx)
3311
3312         local old_lwm=$(echo $wms | awk '{ print $1 }')
3313         local old_hwm=$(echo $wms | awk '{ print $2 }')
3314
3315         local blocks=$($LFS df $MOUNT | awk "/$ost_name/ { print \$4 }")
3316         # minimal extension size is 64M
3317         local new_lwm=50
3318         if (( $blocks / 1024 > 50 )); then
3319                 new_lwm=$((blocks / 1024 - 50))
3320         fi
3321         local new_hwm=$((new_lwm + 5))
3322
3323         ost_watermarks_set $ost_idx $new_lwm $new_hwm
3324         echo "watermarks: $old_lwm $old_hwm $new_lwm $new_hwm"
3325 }
3326
3327 # Set watermarks to ~current available space & then write data to fill it
3328 # Note OST is not *actually* full after this, it just reports ENOSPC in the
3329 # internal statfs used by the stripe allocator
3330 #
3331 # first parameter is the filename-prefix, which must get under t-f cleanup
3332 # requirements (rm -rf $DIR/[Rdfs][0-9]*), i.e. $tfile work fine
3333 ost_watermarks_set_enospc() {
3334         local filename=$1
3335         local ost_idx=$2
3336         # on the mdt's osc
3337         local ost_name=$(ostname_from_index $ost_idx)
3338         local facets=$(get_facets MDS)
3339         local wms
3340         local MDS
3341
3342         for MDS in ${facets//,/ }; do
3343                 local mdtosc_proc=$(get_mdtosc_proc_path $MDS $ost_name)
3344
3345                 do_facet $MDS $LCTL get_param -n \
3346                         osp.$mdtosc_proc.reserved_mb_high ||
3347                         skip  "remote MDS does not support reserved_mb_high"
3348         done
3349
3350         wms=$(ost_watermarks_set_low_space $ost_idx)
3351         local new_lwm=$(echo $wms | awk '{ print $4 }')
3352         fill_ost $filename $ost_idx $new_lwm
3353         #First enospc could execute orphan deletion so repeat
3354         fill_ost $filename $ost_idx $new_lwm
3355         echo $wms
3356 }
3357
3358 ost_watermarks_enospc_delete_files() {
3359         local filename=$1
3360         local ost_idx=$2
3361
3362         rm -f $DIR/${filename}.fill_ost$ost_idx
3363
3364         wait_delete_completed
3365         wait_mds_ost_sync
3366 }
3367
3368 # clean up from "ost_watermarks_set_enospc"
3369 ost_watermarks_clear_enospc() {
3370         local filename=$1
3371         local ost_idx=$2
3372         local old_lwm=$4
3373         local old_hwm=$5
3374
3375         ost_watermarks_enospc_delete_files $filename $ost_idx
3376         ost_watermarks_set $ost_idx $old_lwm $old_hwm
3377         echo "set OST$ost_idx lwm back to $old_lwm, hwm back to $old_hwm"
3378 }
3379
3380 wait_delete_completed_mds() {
3381         local max_wait=${1:-20}
3382         local mds2sync=""
3383         local stime=$(date +%s)
3384         local etime
3385         local node
3386         local changes
3387
3388         # find MDS with pending deletions
3389         for node in $(mdts_nodes); do
3390                 changes=$(do_node $node "$LCTL get_param -n osc.*MDT*.sync_*" \
3391                         2>/dev/null | calc_sum)
3392                 if [[ $changes -eq 0 ]]; then
3393                         continue
3394                 fi
3395                 mds2sync="$mds2sync $node"
3396         done
3397         if [ -z "$mds2sync" ]; then
3398                 wait_zfs_commit $SINGLEMDS
3399                 return 0
3400         fi
3401         mds2sync=$(comma_list $mds2sync)
3402
3403         # sync MDS transactions
3404         do_nodes $mds2sync "$LCTL set_param -n os[cd]*.*MD*.force_sync 1"
3405
3406         # wait till all changes are sent and commmitted by OSTs
3407         # for ldiskfs space is released upon execution, but DMU
3408         # do this upon commit
3409
3410         local WAIT=0
3411         while [[ $WAIT -ne $max_wait ]]; do
3412                 changes=$(do_nodes $mds2sync \
3413                         "$LCTL get_param -n osc.*MDT*.sync_*" | calc_sum)
3414                 #echo "$node: $changes changes on all"
3415                 if [[ $changes -eq 0 ]]; then
3416                         wait_zfs_commit $SINGLEMDS
3417
3418                         # the occupied disk space will be released
3419                         # only after TXGs are committed
3420                         wait_zfs_commit ost1
3421                         return 0
3422                 fi
3423                 sleep 1
3424                 WAIT=$((WAIT + 1))
3425         done
3426
3427         etime=$(date +%s)
3428         echo "Delete is not completed in $((etime - stime)) seconds"
3429         do_nodes $mds2sync "$LCTL get_param osc.*MDT*.sync_*"
3430         return 1
3431 }
3432
3433 wait_for_host() {
3434         local hostlist=$1
3435
3436         # we can use "for" here because we are waiting the slowest
3437         for host in ${hostlist//,/ }; do
3438                 check_network "$host" 900
3439         done
3440         while ! do_nodes $hostlist hostname  > /dev/null; do sleep 5; done
3441 }
3442
3443 wait_for_facet() {
3444         local facetlist=$1
3445         local hostlist
3446
3447         for facet in ${facetlist//,/ }; do
3448                 hostlist=$(expand_list $hostlist $(facet_active_host $facet))
3449         done
3450         wait_for_host $hostlist
3451 }
3452
3453 _wait_recovery_complete () {
3454         local param=$1
3455
3456         # Use default policy if $2 is not passed by caller.
3457         local MAX=${2:-$(max_recovery_time)}
3458
3459         local WAIT=0
3460         local STATUS=
3461
3462         while [ $WAIT -lt $MAX ]; do
3463                 STATUS=$(lctl get_param -n $param | grep status)
3464                 echo $param $STATUS
3465                 [[ $STATUS == "status: COMPLETE" ||
3466                         $STATUS == "status: INACTIVE" ]] && return 0
3467                 sleep 5
3468                 WAIT=$((WAIT + 5))
3469                 echo "Waiting $((MAX - WAIT)) secs for $param recovery done. $STATUS"
3470         done
3471         echo "$param recovery not done in $MAX sec. $STATUS"
3472         return 1
3473 }
3474
3475 wait_recovery_complete () {
3476         local facet=$1
3477
3478         # with an assumption that at_max is the same on all nodes
3479         local MAX=${2:-$(max_recovery_time)}
3480
3481         local facets=$facet
3482         if [ "$FAILURE_MODE" = HARD ]; then
3483                 facets=$(facets_on_host $(facet_active_host $facet))
3484         fi
3485         echo affected facets: $facets
3486
3487         facets=${facets//,/ }
3488         # We can use "for" here because we are waiting the slowest.
3489         # The mgs not having the recovery_status proc entry, exclude it
3490         # from the facet list.
3491         for facet in ${facets//mgs/ }; do
3492                 local var_svc=${facet}_svc
3493                 local param="*.${!var_svc}.recovery_status"
3494
3495                 local host=$(facet_active_host $facet)
3496                 do_rpc_nodes "$host" _wait_recovery_complete $param $MAX
3497         done
3498 }
3499
3500 wait_mds_ost_sync () {
3501         # just because recovery is done doesn't mean we've finished
3502         # orphan cleanup. Wait for llogs to get synchronized.
3503         echo "Waiting for orphan cleanup..."
3504         # MAX value includes time needed for MDS-OST reconnection
3505         local MAX=$(( TIMEOUT * 2 ))
3506         local WAIT_TIMEOUT=${1:-$MAX}
3507         local WAIT=0
3508         local new_wait=true
3509         local list=$(comma_list $(mdts_nodes))
3510         local cmd="$LCTL get_param -n osp.*osc*.old_sync_processed"
3511         if ! do_facet $SINGLEMDS \
3512                 "$LCTL list_param osp.*osc*.old_sync_processed 2> /dev/null"
3513         then
3514                 # old way, use mds_sync
3515                 new_wait=false
3516                 list=$(comma_list $(osts_nodes))
3517                 cmd="$LCTL get_param -n obdfilter.*.mds_sync"
3518         fi
3519
3520         echo "wait $WAIT_TIMEOUT secs maximumly for $list mds-ost sync done."
3521         while [ $WAIT -lt $WAIT_TIMEOUT ]; do
3522                 local -a sync=($(do_nodes $list "$cmd"))
3523                 local con=1
3524                 local i
3525                 for ((i=0; i<${#sync[@]}; i++)); do
3526                         if $new_wait; then
3527                                 [ ${sync[$i]} -eq 1 ] && continue
3528                         else
3529                                 [ ${sync[$i]} -eq 0 ] && continue
3530                         fi
3531                         # there is a not finished MDS-OST synchronization
3532                         con=0
3533                         break;
3534                 done
3535                 sleep 2 # increase waiting time and cover statfs cache
3536                 [ ${con} -eq 1 ] && return 0
3537                 echo "Waiting $WAIT secs for $list $i mds-ost sync done."
3538                 WAIT=$((WAIT + 2))
3539         done
3540
3541         # show which nodes are not finished.
3542         cmd=$(echo $cmd | sed 's/-n//')
3543         do_nodes $list "$cmd"
3544         echo "$facet recovery node $i not done in $WAIT_TIMEOUT sec. $STATUS"
3545         return 1
3546 }
3547
3548 # Wait OSTs to be active on both client and MDT side.
3549 wait_osts_up() {
3550         local cmd="$LCTL get_param -n lov.$FSNAME-clilov-*.target_obd |
3551                 awk 'BEGIN {c = 0} /ACTIVE/{c += 1} END {printf \\\"%d\\\", c}'"
3552         wait_update $HOSTNAME "eval $cmd" $OSTCOUNT ||
3553                 error "wait_update OSTs up on client failed"
3554
3555         cmd="$LCTL get_param osp.$FSNAME-OST*-MDT0000.prealloc_last_id |
3556              awk '/=[1-9][0-9]/ { c += 1 } END { printf \\\"%d\\\", c }'"
3557         wait_update_facet $SINGLEMDS "eval $cmd" $OSTCOUNT ||
3558                 error "wait_update OSTs up on MDT0000 failed"
3559 }
3560
3561 wait_destroy_complete () {
3562         echo "Waiting for MDT destroys to complete"
3563         # MAX value shouldn't be big as this mean server responsiveness
3564         # never increase this just to make test pass but investigate
3565         # why it takes so long time
3566         local MAX=${1:-5}
3567         local WAIT=0
3568         local list=$(comma_list $(mdts_nodes))
3569         while [ $WAIT -lt $MAX ]; do
3570                 local -a RPCs=($(do_nodes $list $LCTL get_param -n osp.*.destroys_in_flight))
3571                 local con=1
3572                 local i
3573
3574                 for ((i=0; i<${#RPCs[@]}; i++)); do
3575                         [ ${RPCs[$i]} -eq 0 ] && continue
3576                         # there are still some destroy RPCs in flight
3577                         con=0
3578                         break;
3579                 done
3580                 [ ${con} -eq 1 ] && return 0 # done waiting
3581                 sleep 1
3582                 echo "Waiting ${WAIT}s for local destroys to complete"
3583                 WAIT=$((WAIT + 1))
3584         done
3585         echo "MDT destroys weren't done in $MAX sec."
3586         return 1
3587 }
3588
3589 wait_delete_completed() {
3590         wait_delete_completed_mds $1 || return $?
3591         wait_destroy_complete || return $?
3592 }
3593
3594 wait_exit_ST () {
3595         local facet=$1
3596
3597         local WAIT=0
3598         local INTERVAL=1
3599         local running
3600         # conf-sanity 31 takes a long time cleanup
3601         while [ $WAIT -lt 300 ]; do
3602                 running=$(do_facet ${facet} "lsmod | grep lnet > /dev/null &&
3603 lctl dl | grep ' ST ' || true")
3604                 [ -z "${running}" ] && return 0
3605                 echo "waited $WAIT for${running}"
3606                 [ $INTERVAL -lt 64 ] && INTERVAL=$((INTERVAL + INTERVAL))
3607                 sleep $INTERVAL
3608                 WAIT=$((WAIT + INTERVAL))
3609         done
3610         echo "service didn't stop after $WAIT seconds.  Still running:"
3611         echo ${running}
3612         return 1
3613 }
3614
3615 wait_remote_prog () {
3616         local prog=$1
3617         local WAIT=0
3618         local INTERVAL=5
3619         local rc=0
3620
3621         [ "$PDSH" = "no_dsh" ] && return 0
3622
3623         while [ $WAIT -lt $2 ]; do
3624                 running=$(ps uax | grep "$PDSH.*$prog.*$MOUNT" |
3625                         grep -v grep) || true
3626                 [ -z "${running}" ] && return 0 || true
3627                 echo "waited $WAIT for: "
3628                 echo "$running"
3629                 [ $INTERVAL -lt 60 ] && INTERVAL=$((INTERVAL + INTERVAL))
3630                 sleep $INTERVAL
3631                 WAIT=$((WAIT + INTERVAL))
3632         done
3633         local pids=$(ps  uax | grep "$PDSH.*$prog.*$MOUNT" |
3634                         grep -v grep | awk '{print $2}')
3635         [ -z "$pids" ] && return 0
3636         echo "$PDSH processes still exists after $WAIT seconds.  Still running: $pids"
3637         # FIXME: not portable
3638         for pid in $pids; do
3639                 cat /proc/${pid}/status || true
3640                 cat /proc/${pid}/wchan || true
3641                 echo "Killing $pid"
3642                 kill -9 $pid || true
3643                 sleep 1
3644                 ps -P $pid && rc=1
3645         done
3646
3647         return $rc
3648 }
3649
3650 lfs_df_check() {
3651         local clients=${1:-$CLIENTS}
3652         local rc=0
3653
3654         if [ -z "$clients" ]; then
3655                 $LFS df $MOUNT > /dev/null || rc=$?
3656         else
3657                 $PDSH $clients "$LFS df $MOUNT" > /dev/null || rc=$?
3658         fi
3659
3660         check_lfs_df_ret_val $rc
3661 }
3662
3663 clients_up() {
3664         # not every config has many clients
3665         sleep 1
3666         lfs_df_check
3667 }
3668
3669 all_mds_up() {
3670         (( MDSCOUNT == 1 )) && return
3671
3672         # wait so that statfs data on MDT expire
3673         local delay=$(do_facet mds1 $LCTL \
3674                 get_param -n osp.*MDT*MDT0000.maxage | sort -n | tail -1)
3675
3676         [ -n "$delay" ] || error "fail to get maxage"
3677         sleep $delay
3678         local nodes=$(comma_list $(mdts_nodes))
3679         # initiate statfs RPC, all to all MDTs
3680         do_nodes $nodes $LCTL get_param -N osp.*MDT*MDT*.filesfree >&/dev/null
3681         do_nodes $nodes $LCTL get_param -N osp.*MDT*MDT*.filesfree >&/dev/null
3682 }
3683
3684 client_up() {
3685         # usually checked on particular client or locally
3686         sleep 1
3687         lfs_df_check $1
3688 }
3689
3690 client_evicted() {
3691     ! client_up $1
3692 }
3693
3694 client_reconnect_try() {
3695         local f=$MOUNT/recon
3696
3697         uname -n >> $f
3698         if [ -z "$CLIENTS" ]; then
3699                 $LFS df $MOUNT; uname -n >> $f
3700         else
3701                 do_nodes $CLIENTS "$LFS df $MOUNT; uname -n >> $f" > /dev/null
3702         fi
3703         echo "Connected clients: $(cat $f)"
3704         ls -l $f > /dev/null
3705         rm $f
3706 }
3707
3708 client_reconnect() {
3709         # one client_reconnect_try call does not always do the job...
3710         while true ; do
3711                 client_reconnect_try && break
3712                 sleep 1
3713         done
3714 }
3715
3716 affected_facets () {
3717         local facet=$1
3718
3719         local host=$(facet_active_host $facet)
3720         local affected=$facet
3721
3722         if [ "$FAILURE_MODE" = HARD ]; then
3723                 affected=$(facets_up_on_host $host)
3724         fi
3725         echo $affected
3726 }
3727
3728 facet_failover() {
3729         local E2FSCK_ON_MDT0=false
3730         if [ "$1" == "--fsck" ]; then
3731                 shift
3732                 [ $(facet_fstype $SINGLEMDS) == ldiskfs ] &&
3733                         E2FSCK_ON_MDT0=true
3734         fi
3735
3736         local facets=$1
3737         local sleep_time=$2
3738         local -a affecteds
3739         local facet
3740         local total=0
3741         local index=0
3742         local skip
3743
3744         #Because it will only get up facets, we need get affected
3745         #facets before shutdown
3746         #For HARD Failure mode, it needs make sure facets on the same
3747         #HOST will only be shutdown and reboot once
3748         for facet in ${facets//,/ }; do
3749                 local affected_facet
3750                 skip=0
3751                 #check whether facet has been included in other affected facets
3752                 for ((index=0; index<$total; index++)); do
3753                         [[ ,${affecteds[index]}, == *,$facet,* ]] && skip=1
3754                 done
3755
3756                 if [ $skip -eq 0 ]; then
3757                         affecteds[$total]=$(affected_facets $facet)
3758                         total=$((total+1))
3759                 fi
3760         done
3761
3762         for ((index=0; index<$total; index++)); do
3763                 facet=$(echo ${affecteds[index]} | tr -s " " | cut -d"," -f 1)
3764                 local host=$(facet_active_host $facet)
3765                 echo "Failing ${affecteds[index]} on $host"
3766                 shutdown_facet $facet
3767         done
3768
3769         echo "$(date +'%H:%M:%S (%s)') shut down"
3770
3771         local hostlist
3772         local waithostlist
3773
3774         for facet in ${facets//,/ }; do
3775                 local host=$(facet_active_host $facet)
3776
3777                 hostlist=$(expand_list $hostlist $host)
3778                 if [ $(facet_host $facet) = \
3779                         $(facet_failover_host $facet) ]; then
3780                         waithostlist=$(expand_list $waithostlist $host)
3781                 fi
3782         done
3783
3784         if [ "$FAILURE_MODE" = HARD ]; then
3785                 for host in ${hostlist//,/ }; do
3786                         reboot_node $host
3787                 done
3788                 echo "$(date +'%H:%M:%S (%s)') $hostlist rebooted"
3789                 # We need to wait the rebooted hosts in case if
3790                 # facet_HOST == facetfailover_HOST
3791                 if ! [ -z "$waithostlist" ]; then
3792                         wait_for_host $waithostlist
3793                         if $LOAD_MODULES_REMOTE; then
3794                                 echo "loading modules on $waithostlist"
3795                                 do_rpc_nodes $waithostlist load_modules_local
3796                         fi
3797                 fi
3798         else
3799                 sleep 10
3800         fi
3801
3802         if [[ " ${affecteds[@]} " =~ " $SINGLEMDS " ]]; then
3803                 change_active $SINGLEMDS
3804         fi
3805
3806         $E2FSCK_ON_MDT0 && (run_e2fsck $(facet_active_host $SINGLEMDS) \
3807                 $(facet_device $SINGLEMDS) "-n" || error "Running e2fsck")
3808
3809         local -a mountpids
3810
3811         for ((index=0; index<$total; index++)); do
3812                 if [[ ${affecteds[index]} != $SINGLEMDS ]]; then
3813                         change_active ${affecteds[index]}
3814                 fi
3815                 if $GSS_SK; then
3816                         init_gss
3817                         init_facets_vars_simple
3818                 fi
3819                 # start mgs first if it is affected
3820                 if ! combined_mgs_mds &&
3821                         list_member ${affecteds[index]} mgs; then
3822                         mount_facet mgs || error "Restart of mgs failed"
3823                         affecteds[index]=$(exclude_items_from_list \
3824                                 ${affecteds[index]} mgs)
3825                 fi
3826                 if [ -n "${affecteds[index]}" ]; then
3827                         echo mount facets: ${affecteds[index]}
3828                         mount_facets ${affecteds[index]} &
3829                         mountpids[index]=$!
3830                 fi
3831         done
3832         for ((index=0; index<$total; index++)); do
3833                 if [ -n "${affecteds[index]}" ]; then
3834                         wait ${mountpids[index]}
3835                 fi
3836
3837                 if $GSS_SK; then
3838                         do_nodes $(comma_list $(all_nodes)) \
3839                                 "keyctl show | grep lustre | cut -c1-11 |
3840                                 sed -e 's/ //g;' |
3841                                 xargs -IX keyctl setperm X 0x3f3f3f3f"
3842                 fi
3843         done
3844         echo "$(date +'%H:%M:%S (%s)') targets are mounted"
3845
3846         if [ "$FAILURE_MODE" = HARD ]; then
3847                 hostlist=$(exclude_items_from_list $hostlist $waithostlist)
3848                 if ! [ -z "$hostlist" ]; then
3849                         wait_for_host $hostlist
3850                         if $LOAD_MODULES_REMOTE; then
3851                                 echo "loading modules on $hostlist"
3852                                 do_rpc_nodes $hostlist load_modules_local
3853                         fi
3854                 fi
3855         fi
3856
3857         echo "$(date +'%H:%M:%S (%s)') facet_failover done"
3858 }
3859
3860 replay_barrier() {
3861         local facet=$1
3862         do_facet $facet "sync; sync; sync"
3863         $LFS df $MOUNT
3864
3865         # make sure there will be no seq change
3866         local clients=${CLIENTS:-$HOSTNAME}
3867         local f=fsa-\\\$\(hostname\)
3868         do_nodes $clients "mcreate $MOUNT/$f; rm $MOUNT/$f"
3869         do_nodes $clients "if [ -d $MOUNT2 ]; then mcreate $MOUNT2/$f; rm $MOUNT2/$f; fi"
3870
3871         local svc=${facet}_svc
3872         do_facet $facet $LCTL --device ${!svc} notransno
3873         #
3874         # If a ZFS OSD is made read-only here, its pool is "freezed". This
3875         # in-memory state has to be cleared by either rebooting the host or
3876         # exporting and reimporting the pool.
3877         #
3878         # Although the uberblocks are not updated when a pool is freezed,
3879         # transactions are still written to the disks. Modified blocks may be
3880         # cached in memory when tests try reading them back. The
3881         # export-and-reimport process also evicts any cached pool data from
3882         # memory to provide the correct "data loss" semantics.
3883         #
3884         # In the test framework, the exporting and importing operations are
3885         # handled by stop() and mount_facet() separately, which are used
3886         # inside fail() and fail_abort().
3887         #
3888         set_dev_readonly $facet
3889         do_facet $facet $LCTL mark "$facet REPLAY BARRIER on ${!svc}"
3890         $LCTL mark "local REPLAY BARRIER on ${!svc}"
3891 }
3892
3893 replay_barrier_nodf() {
3894         local facet=$1    echo running=${running}
3895         do_facet $facet "sync; sync; sync"
3896         local svc=${facet}_svc
3897         echo Replay barrier on ${!svc}
3898         do_facet $facet $LCTL --device ${!svc} notransno
3899         set_dev_readonly $facet
3900         do_facet $facet $LCTL mark "$facet REPLAY BARRIER on ${!svc}"
3901         $LCTL mark "local REPLAY BARRIER on ${!svc}"
3902 }
3903
3904 replay_barrier_nosync() {
3905         local facet=$1    echo running=${running}
3906         local svc=${facet}_svc
3907         echo Replay barrier on ${!svc}
3908         do_facet $facet $LCTL --device ${!svc} notransno
3909         set_dev_readonly $facet
3910         do_facet $facet $LCTL mark "$facet REPLAY BARRIER on ${!svc}"
3911         $LCTL mark "local REPLAY BARRIER on ${!svc}"
3912 }
3913
3914 #
3915 # Get Lustre client uuid for a given Lustre mount point.
3916 #
3917 get_client_uuid() {
3918         local mntpnt=${1:-$MOUNT}
3919
3920         local name=$($LFS getname $mntpnt | cut -d' ' -f1)
3921         local uuid=$($LCTL get_param -n llite.$name.uuid)
3922
3923         echo -n $uuid
3924 }
3925
3926 mds_evict_client() {
3927         local mntpnt=${1:-$MOUNT}
3928         local uuid=$(get_client_uuid $mntpnt)
3929
3930         do_facet $SINGLEMDS \
3931                 "$LCTL set_param -n mdt.${mds1_svc}.evict_client $uuid"
3932 }
3933
3934 ost_evict_client() {
3935         local mntpnt=${1:-$MOUNT}
3936         local uuid=$(get_client_uuid $mntpnt)
3937
3938         do_facet ost1 \
3939                 "$LCTL set_param -n obdfilter.${ost1_svc}.evict_client $uuid"
3940 }
3941
3942 fail() {
3943         local facets=$1
3944         local clients=${CLIENTS:-$HOSTNAME}
3945
3946         SK_NO_KEY_save=$SK_NO_KEY
3947         if $GSS_SK; then
3948                 export SK_NO_KEY=false
3949         fi
3950         facet_failover $* || error "failover: $?"
3951         export SK_NO_KEY=$SK_NO_KEY_save
3952         # to initiate all OSC idling connections
3953         clients_up
3954         wait_clients_import_ready "$clients" "$facets"
3955         clients_up || error "post-failover stat: $?"
3956 }
3957
3958 fail_nodf() {
3959         local facet=$1
3960
3961         facet_failover $facet
3962 }
3963
3964 fail_abort() {
3965         local facet=$1
3966         local abort_type=${2:-"abort_recovery"}
3967
3968         stop $facet
3969         change_active $facet
3970         wait_for_facet $facet
3971         mount_facet $facet -o $abort_type
3972         clients_up || echo "first stat failed: $?"
3973         clients_up || error "post-failover stat: $?"
3974         all_mds_up
3975 }
3976
3977 # LU-16159: abort recovery will cancel update logs, which may leave broken
3978 # directories in the system, remove name entry if necessary
3979 fail_abort_cleanup() {
3980         rm -rf $DIR/$tdir/*
3981         find $DIR/$tdir -depth | while read D; do
3982                 rmdir "$D" || $LFS rm_entry "$D" || error "rm $D failed"
3983         done
3984 }
3985
3986 host_nids_address() {
3987         local nodes=$1
3988         local net=${2:-"."}
3989
3990         do_nodes $nodes "$LCTL list_nids | grep -w $net | cut -f 1 -d @"
3991 }
3992
3993 h2name_or_ip() {
3994         if [ "$1" = "'*'" ]; then echo \'*\'; else
3995                 echo $1"@$2"
3996         fi
3997 }
3998
3999 h2nettype() {
4000         if [[ -n "$NETTYPE" ]]; then
4001                 h2name_or_ip "$1" "$NETTYPE"
4002         else
4003                 h2name_or_ip "$1" "$2"
4004         fi
4005 }
4006 declare -fx h2nettype
4007
4008 # This enables variables in cfg/"setup".sh files to support the pdsh HOSTLIST
4009 # expressions format. As a bonus we can then just pass in those variables
4010 # to pdsh. What this function does is take a HOSTLIST type string and
4011 # expand it into a space deliminated list for us.
4012 hostlist_expand() {
4013         local hostlist=$1
4014         local offset=$2
4015         local myList
4016         local item
4017         local list
4018
4019         [ -z "$hostlist" ] && return
4020
4021         # Translate the case of [..],..,[..] to [..] .. [..]
4022         list="${hostlist/],/] }"
4023         front=${list%%[*}
4024         [[ "$front" == *,* ]] && {
4025                 new="${list%,*} "
4026                 old="${list%,*},"
4027                 list=${list/${old}/${new}}
4028         }
4029
4030         for item in $list; do
4031         # Test if we have any []'s at all
4032                 if [ "$item" != "${item/\[/}" ]; then {
4033                 # Expand the [*] into list
4034                 name=${item%%[*}
4035                 back=${item#*]}
4036
4037                         if [ "$name" != "$item" ]; then
4038                                 group=${item#$name[*}
4039                                 group=${group%%]*}
4040
4041                                 for range in ${group//,/ }; do
4042                                         local order
4043
4044                                         begin=${range%-*}
4045                                         end=${range#*-}
4046
4047                                         # Number of leading zeros
4048                                         padlen=${#begin}
4049                                         padlen2=${#end}
4050                                         end=$(echo $end | sed 's/0*//')
4051                                         [[ -z "$end" ]] && end=0
4052                                         [[ $padlen2 -gt $padlen ]] && {
4053                                                 [[ $padlen2 -eq ${#end} ]] &&
4054                                                         padlen2=0
4055                                                 padlen=$padlen2
4056                                         }
4057                                         begin=$(echo $begin | sed 's/0*//')
4058                                         [ -z $begin ] && begin=0
4059
4060                                         if [ ! -z "${begin##[!0-9]*}" ]; then
4061                                                 order=$(seq -f "%0${padlen}g" $begin $end)
4062                                         else
4063                                                 order=$(eval echo {$begin..$end});
4064                                         fi
4065
4066                                         for num in $order; do
4067                                                 value="${name#*,}${num}${back}"
4068
4069                                                 [ "$value" != "${value/\[/}" ] && {
4070                                                     value=$(hostlist_expand "$value")
4071                                                 }
4072                                                 myList="$myList $value"
4073                                         done
4074                                 done
4075                         fi
4076                 } else {
4077                         myList="$myList $item"
4078                 } fi
4079         done
4080         myList=${myList//,/ }
4081         myList=${myList:1} # Remove first character which is a space
4082
4083         # Filter any duplicates without sorting
4084         list="$myList "
4085         myList="${list%% *}"
4086
4087         while [[ "$list" != ${myList##* } ]]; do
4088                 local tlist=" $list"
4089
4090                 list=${tlist// ${list%% *} / }
4091                 list=${list:1}
4092                 myList="$myList ${list%% *}"
4093         done
4094         myList="${myList%* }";
4095
4096         # We can select an object at an offset in the list
4097         [ $# -eq 2 ] && {
4098         cnt=0
4099         for item in $myList; do
4100                 let cnt=cnt+1
4101                 [ $cnt -eq $offset ] && {
4102                         myList=$item
4103                 }
4104         done
4105         [ $(get_node_count $myList) -ne 1 ] && myList=""
4106         }
4107         echo $myList
4108 }
4109
4110 facet_host() {
4111         local facet=$1
4112         local varname
4113
4114         [ "$facet" == client ] && echo -n $HOSTNAME && return
4115         varname=${facet}_HOST
4116         if [ -z "${!varname}" ]; then
4117                 if [ "${facet:0:3}" == "ost" ]; then
4118                         local fh=${facet%failover}_HOST
4119                         eval export ${facet}_HOST=${!fh}
4120                         if [ -z "${!varname}" ]; then
4121                                 eval export ${facet}_HOST=${ost_HOST}
4122                         fi
4123                 elif [ "${facet:0:3}" == "mdt" -o \
4124                         "${facet:0:3}" == "mds" -o \
4125                         "${facet:0:3}" == "mgs" ]; then
4126                         local temp
4127                         if [ "${facet}" == "mgsfailover" ] &&
4128                            [ -n "$mds1failover_HOST" ]; then
4129                                 temp=$mds1failover_HOST
4130                         else
4131                                 temp=${mds_HOST}
4132                         fi
4133                         eval export ${facet}_HOST=$temp
4134                 fi
4135         fi
4136         echo -n ${!varname}
4137 }
4138
4139 facet_failover_host() {
4140         local facet=$1
4141         local varname
4142
4143         var=${facet}failover_HOST
4144         if [ -n "${!var}" ]; then
4145                 echo ${!var}
4146                 return
4147         fi
4148
4149         if combined_mgs_mds && [ $facet == "mgs" ] &&
4150                 [ -n "$mds1failover_HOST" ]; then
4151                 echo $mds1failover_HOST
4152                 return
4153         fi
4154
4155         if [ "${facet:0:3}" == "mdt" -o "${facet:0:3}" == "mds" -o \
4156              "${facet:0:3}" == "mgs" ]; then
4157
4158                 eval export ${facet}failover_host=${mds_HOST}
4159                 echo ${mds_HOST}
4160                 return
4161         fi
4162
4163         if [[ $facet == ost* ]]; then
4164                 eval export ${facet}failover_host=${ost_HOST}
4165                 echo ${ost_HOST}
4166                 return
4167         fi
4168 }
4169
4170 facet_active() {
4171         local facet=$1
4172         local activevar=${facet}active
4173
4174         if [ -f $TMP/${facet}active ] ; then
4175                 source $TMP/${facet}active
4176         fi
4177
4178         active=${!activevar}
4179         if [ -z "$active" ] ; then
4180                 echo -n ${facet}
4181         else
4182                 echo -n ${active}
4183         fi
4184 }
4185
4186 facet_active_host() {
4187         facet_host $(facet_active $1)
4188 }
4189
4190 # Get the passive failover partner host of facet.
4191 facet_passive_host() {
4192         local facet=$1
4193         [[ $facet = client ]] && return
4194
4195         local host=${facet}_HOST
4196         local failover_host=${facet}failover_HOST
4197         local active_host=$(facet_active_host $facet)
4198
4199         [[ -z ${!failover_host} || ${!failover_host} = ${!host} ]] && return
4200
4201         if [[ $active_host = ${!host} ]]; then
4202                 echo -n ${!failover_host}
4203         else
4204                 echo -n ${!host}
4205         fi
4206 }
4207
4208 change_active() {
4209         local facetlist=$1
4210         local facet
4211
4212         for facet in ${facetlist//,/ }; do
4213                 local failover=${facet}failover
4214                 local host=`facet_host $failover`
4215
4216                 [ -z "$host" ] && return
4217
4218                 local curactive=`facet_active $facet`
4219
4220                 if [ -z "${curactive}" -o "$curactive" == "$failover" ] ; then
4221                         eval export ${facet}active=$facet
4222                 else
4223                         eval export ${facet}active=$failover
4224                 fi
4225                 # save the active host for this facet
4226                 local activevar=${facet}active
4227
4228                 echo "$activevar=${!activevar}" > $TMP/$activevar
4229                 [[ $facet = mds1 ]] && combined_mgs_mds && \
4230                 echo "mgsactive=${!activevar}" > $TMP/mgsactive
4231                 local TO=`facet_active_host $facet`
4232                 echo "Failover $facet to $TO"
4233         done
4234 }
4235
4236 do_node() {
4237         local verbose
4238         local quiet
4239
4240         # do not strip off hostname if verbose, b=19215
4241         [[ "$1" == "--verbose" ]] && verbose="$1" && shift
4242         [[ "$1" == "--quiet" || "$1" == "-q" ]] && quiet="$1" && shift
4243
4244         local HOST=$1
4245         shift
4246         local myPDSH=$PDSH
4247
4248         if [ "$HOST" = "$HOSTNAME" ]; then
4249                 myPDSH="no_dsh"
4250         elif [ -z "$myPDSH" -o "$myPDSH" = "no_dsh" ]; then
4251                 echo "cannot run remote command on $HOST with $myPDSH"
4252                 return 128
4253         fi
4254         if $VERBOSE && [[ -z "$quiet" ]]; then
4255                 echo "CMD: $HOST $*" >&2
4256                 $myPDSH $HOST "$LCTL mark \"$*\"" > /dev/null 2>&1 || :
4257         fi
4258
4259         if [[ "$myPDSH" == "rsh" ]] ||
4260            [[ "$myPDSH" == *pdsh* && "$myPDSH" != *-S* ]]; then
4261                 # we need this because rsh and pdsh do not return
4262                 # exit code of an executed command
4263                 local command_status="$TMP/cs"
4264                 eval $myPDSH $HOST ":> $command_status"
4265                 eval $myPDSH $HOST "(PATH=\$PATH:$RLUSTRE/utils:$RLUSTRE/tests;
4266                                      PATH=\$PATH:/sbin:/usr/sbin;
4267                                      cd $RPWD;
4268                                      LUSTRE=\"$RLUSTRE\" bash -c \"$*\") ||
4269                                      echo command failed >$command_status"
4270                 [[ -n "$($myPDSH $HOST cat $command_status)" ]] && return 1 ||
4271                         return 0
4272         fi
4273
4274         if [[ -n "$verbose" ]]; then
4275                 # print HOSTNAME for myPDSH="no_dsh"
4276                 if [[ $myPDSH = no_dsh ]]; then
4277                         $myPDSH $HOST \
4278                         "(PATH=\$PATH:$RLUSTRE/utils:$RLUSTRE/tests:/sbin:/usr/sbin;\
4279                         cd $RPWD; LUSTRE=\"$RLUSTRE\" bash -c \"$*\")" |
4280                         sed -e "s/^/${HOSTNAME}: /"
4281                 else
4282                         $myPDSH $HOST \
4283                         "(PATH=\$PATH:$RLUSTRE/utils:$RLUSTRE/tests:/sbin:/usr/sbin;\
4284                         cd $RPWD; LUSTRE=\"$RLUSTRE\" bash -c \"$*\")"
4285                 fi
4286         else
4287                 $myPDSH $HOST \
4288                 "(PATH=\$PATH:$RLUSTRE/utils:$RLUSTRE/tests:/sbin:/usr/sbin;\
4289                 cd $RPWD; LUSTRE=\"$RLUSTRE\" bash -c \"$*\")" |
4290                 sed "s/^${HOST}: //"
4291         fi
4292         return ${PIPESTATUS[0]}
4293 }
4294
4295 ##
4296 # Execute exact command line on host
4297 #
4298 # The \a host may be on a local or remote node, which is determined at
4299 # the time the command is run. Does careful argument quotation to
4300 # ensure that the exact command line is executed without any globbing,
4301 # substitution, or shell interpretation on the remote side. Does not
4302 # support --verbose or --quiet. Does not include "$host: " prefixes on
4303 # output. See also do_facet_vp().
4304 #
4305 # usage: do_node_vp "$host" "$command" "$arg"...
4306 do_node_vp() {
4307         local host="$1"
4308         shift
4309
4310         if [[ "$host" == "$HOSTNAME" ]]; then
4311                 sh -c "$(printf -- ' %q' "$@")"
4312                 return $?
4313         fi
4314
4315         if [[ "${PDSH}" != *pdsh* || "${PDSH}" != *-S* ]]; then
4316                 echo "cannot run '$*' on host '${host}' with PDSH='${PDSH}'" >&2
4317                 return 128
4318         fi
4319
4320         # -N Disable hostname: prefix on lines of output.
4321
4322         $PDSH "${host}" -N "cd $RPWD; PATH=\$PATH:$RLUSTRE/utils:$RLUSTRE/tests:/sbin:/usr/sbin; export LUSTRE=$RLUSTRE; $(printf -- ' %q' "$@")"
4323 }
4324
4325 single_local_node () {
4326         [ "$1" = "$HOSTNAME" ]
4327 }
4328
4329 # Outputs environment variable assignments that should be passed to remote nodes
4330 get_env_vars() {
4331         local var
4332         local value
4333         local facets=$(get_facets)
4334         local facet
4335
4336         for var in ${!MODOPTS_*}; do
4337                 value=${!var//\"/\\\"}
4338                 echo -n " ${var}=\"$value\""
4339         done
4340
4341         for facet in ${facets//,/ }; do
4342                 var=${facet}_FSTYPE
4343                 if [ -n "${!var}" ]; then
4344                         echo -n " $var=${!var}"
4345                 fi
4346         done
4347
4348         for var in MGSFSTYPE MDSFSTYPE OSTFSTYPE; do
4349                 if [ -n "${!var}" ]; then
4350                         echo -n " $var=${!var}"
4351                 fi
4352         done
4353
4354         for var in VERBOSE; do
4355                 if [ -n "${!var}" ]; then
4356                         echo -n " $var=${!var}"
4357                 fi
4358         done
4359
4360         if [ -n "$FSTYPE" ]; then
4361                 echo -n " FSTYPE=$FSTYPE"
4362         fi
4363
4364         for var in LNETLND NETTYPE; do
4365                 if [ -n "${!var}" ]; then
4366                         echo -n " $var=${!var}"
4367                 fi
4368         done
4369 }
4370
4371 do_nodes() {
4372         local verbose
4373         local quiet
4374
4375         # do not strip off hostname if verbose, b=19215
4376         [[ "$1" == "--verbose" ]] && verbose="$1" && shift
4377         [[ "$1" == "--quiet" || "$1" == "-q" ]] && quiet="$1" && shift
4378
4379         local rnodes=$1
4380         shift
4381
4382         if single_local_node $rnodes; then
4383                 do_node $verbose $quiet $rnodes "$@"
4384                 return $?
4385         fi
4386
4387         # This is part from do_node
4388         local myPDSH=$PDSH
4389
4390         [ -z "$myPDSH" -o "$myPDSH" = "no_dsh" -o "$myPDSH" = "rsh" ] &&
4391                 echo "cannot run remote command on $rnodes with $myPDSH" &&
4392                 return 128
4393
4394         export FANOUT=$(get_node_count "${rnodes//,/ }")
4395         if $VERBOSE && [[ -z "$quiet" ]]; then
4396                 echo "CMD: $rnodes $*" >&2
4397                 $myPDSH $rnodes "$LCTL mark \"$*\"" > /dev/null 2>&1 || :
4398         fi
4399
4400         # do not replace anything from pdsh output if -N is used
4401         # -N     Disable hostname: prefix on lines of output.
4402         if [[ -n "$verbose" || $myPDSH = *-N* ]]; then
4403                 $myPDSH $rnodes "(PATH=\$PATH:$RLUSTRE/utils:$RLUSTRE/tests:/sbin:/usr/sbin; cd $RPWD; LUSTRE=\"$RLUSTRE\" $(get_env_vars) bash -c \"$*\")"
4404         else
4405                 $myPDSH $rnodes "(PATH=\$PATH:$RLUSTRE/utils:$RLUSTRE/tests:/sbin:/usr/sbin; cd $RPWD; LUSTRE=\"$RLUSTRE\" $(get_env_vars) bash -c \"$*\")" | sed -re "s/^[^:]*: //g"
4406         fi
4407         return ${PIPESTATUS[0]}
4408 }
4409
4410 ##
4411 # Execute commands on a single service's host
4412 #
4413 # The \a facet (service) may be on a local or remote node, which is
4414 # determined at the time the command is run.
4415 #
4416 # usage: do_facet $facet command [arg ...]
4417 do_facet() {
4418         local verbose
4419         local quiet
4420
4421         [[ "$1" == "--verbose" ]] && verbose="$1" && shift
4422         [[ "$1" == "--quiet" || "$1" == "-q" ]] && quiet="$1" && shift
4423
4424         local facet=$1
4425         shift
4426         local host=$(facet_active_host $facet)
4427
4428         [ -z "$host" ] && echo "No host defined for facet ${facet}" && exit 1
4429         do_node $verbose $quiet $host "$@"
4430 }
4431
4432 ##
4433 # Execute exact command line on the host of a facet
4434 #
4435 # The \a facet (service) may be on a local or remote node, which is
4436 # determined at the time the command is run. Does careful argument
4437 # quotation to ensure that the exact command line is executed without
4438 # any globbing, substitution, or shell interpretation on the remote
4439 # side. Does not support --verbose or --quiet. Does not include
4440 # "$host: " prefixes on output.
4441 #
4442 # usage: do_facet_vp "$facet" "$command" "$arg"...
4443 do_facet_vp() {
4444         local facet="$1"
4445         local host=$(facet_active_host "$facet")
4446         shift
4447
4448         if [[ -z "$host" ]]; then
4449                 echo "no host defined for facet ${facet}" >&2
4450                 exit 1
4451         fi
4452
4453         do_node_vp "$host" "$@"
4454 }
4455
4456 # Function: do_facet_random_file $FACET $FILE $SIZE
4457 # Creates FILE with random content on the given FACET of given SIZE
4458
4459 do_facet_random_file() {
4460         local facet="$1"
4461         local fpath="$2"
4462         local fsize="$3"
4463         local cmd="dd if=/dev/urandom of='$fpath' bs=$fsize count=1"
4464         do_facet $facet "$cmd 2>/dev/null"
4465 }
4466
4467 do_facet_create_file() {
4468         local facet="$1"
4469         local fpath="$2"
4470         local fsize="$3"
4471         local cmd="dd if=/dev/zero of='$fpath' bs=$fsize count=1"
4472         do_facet $facet "$cmd 2>/dev/null"
4473 }
4474
4475 do_nodesv() {
4476         do_nodes --verbose "$@"
4477 }
4478
4479 add() {
4480         local facet=$1
4481         shift
4482         # make sure its not already running
4483         stop ${facet} -f
4484         rm -f $TMP/${facet}active
4485         [[ $facet = mds1 ]] && combined_mgs_mds && rm -f $TMP/mgsactive
4486
4487         # make sure in-tree ldiskfs is loaded before mkfs
4488         if local_mode && [[ $(node_fstypes $HOSTNAME) == *ldiskfs* ]]; then
4489                 load_module ../ldiskfs/ldiskfs
4490         fi
4491
4492         do_facet ${facet} $MKFS $* || return ${PIPESTATUS[0]}
4493
4494         if [[ $(facet_fstype $facet) == zfs ]]; then
4495                 #
4496                 # After formatting a ZFS target, "cachefile=none" property will
4497                 # be set on the ZFS storage pool so that the pool is not
4498                 # automatically imported on system startup. And then the pool
4499                 # will be exported so as to leave the importing and exporting
4500                 # operations handled by mount_facet() and stop() separately.
4501                 #
4502                 refresh_partition_table $facet $(facet_vdevice $facet)
4503                 disable_zpool_cache $facet
4504                 export_zpool $facet
4505         fi
4506 }
4507
4508 # Device formatted as ost
4509 ostdevname() {
4510         local num=$1
4511         local DEVNAME=OSTDEV$num
4512
4513         local fstype=$(facet_fstype ost$num)
4514
4515         case $fstype in
4516                 ldiskfs )
4517                         local dev=ost${num}_dev
4518                         [[ -n ${!dev} ]] && eval DEVPTR=${!dev} ||
4519                         #if $OSTDEVn isn't defined, default is $OSTDEVBASE + num
4520                         eval DEVPTR=${!DEVNAME:=${OSTDEVBASE}${num}};;
4521                 zfs )
4522                         #try $OSTZFSDEVn - independent of vdev
4523                         DEVNAME=OSTZFSDEV$num
4524                         eval DEVPTR=${!DEVNAME:=${FSNAME}-ost${num}/ost${num}};;
4525                 * )
4526                         error "unknown fstype!";;
4527         esac
4528
4529         echo -n $DEVPTR
4530 }
4531
4532 # Physical device location of data
4533 ostvdevname() {
4534         local num=$1
4535         local DEVNAME
4536         local VDEVPTR
4537
4538         local fstype=$(facet_fstype ost$num)
4539
4540         case $fstype in
4541                 ldiskfs )
4542                         # vdevs are not supported by ldiskfs
4543                         eval VDEVPTR="";;
4544                 zfs )
4545                         #if $OSTDEVn isn't defined, default is $OSTDEVBASE{n}
4546                         # Device formatted by zfs
4547                         DEVNAME=OSTDEV$num
4548                         eval VDEVPTR=${!DEVNAME:=${OSTDEVBASE}${num}};;
4549                 * )
4550                         error "unknown fstype!";;
4551         esac
4552
4553         echo -n $VDEVPTR
4554 }
4555
4556 # Logical device formatted for lustre
4557 mdsdevname() {
4558         local num=$1
4559         local DEVNAME=MDSDEV$num
4560
4561         local fstype=$(facet_fstype mds$num)
4562
4563         case $fstype in
4564                 ldiskfs )
4565                         local dev=mds${num}_dev
4566                         [[ -n ${!dev} ]] && eval DEVPTR=${!dev} ||
4567                         #if $MDSDEVn isn't defined, default is $MDSDEVBASE{n}
4568                         eval DEVPTR=${!DEVNAME:=${MDSDEVBASE}${num}};;
4569                 zfs )
4570                         # try $MDSZFSDEVn - independent of vdev
4571                         DEVNAME=MDSZFSDEV$num
4572                         eval DEVPTR=${!DEVNAME:=${FSNAME}-mdt${num}/mdt${num}};;
4573                 * )
4574                         error "unknown fstype!";;
4575         esac
4576
4577         echo -n $DEVPTR
4578 }
4579
4580 # Physical location of data
4581 mdsvdevname() {
4582         local VDEVPTR=""
4583         local num=$1
4584         local fstype=$(facet_fstype mds$num)
4585
4586         case $fstype in
4587                 ldiskfs )
4588                         # vdevs are not supported by ldiskfs
4589                         eval VDEVPTR="";;
4590                 zfs )
4591                         # if $MDSDEVn isn't defined, default is $MDSDEVBASE{n}
4592                         # Device formatted by ZFS
4593                         local DEVNAME=MDSDEV$num
4594                         eval VDEVPTR=${!DEVNAME:=${MDSDEVBASE}${num}};;
4595                 * )
4596                         error "unknown fstype!";;
4597         esac
4598
4599         echo -n $VDEVPTR
4600 }
4601
4602 mgsdevname() {
4603         local DEVPTR
4604         local fstype=$(facet_fstype mgs)
4605
4606         case $fstype in
4607         ldiskfs )
4608                 if [ $(facet_host mgs) = $(facet_host mds1) ] &&
4609                    ( [ -z "$MGSDEV" ] || [ $MGSDEV = $MDSDEV1 ] ); then
4610                         DEVPTR=$(mdsdevname 1)
4611                 else
4612                         [[ -n $mgs_dev ]] && DEVPTR=$mgs_dev ||
4613                         DEVPTR=$MGSDEV
4614                 fi;;
4615         zfs )
4616                 if [ $(facet_host mgs) = $(facet_host mds1) ] &&
4617                     ( [ -z "$MGSZFSDEV" ] &&
4618                         [ -z "$MGSDEV" -o "$MGSDEV" = $(mdsvdevname 1) ] ); then
4619                         DEVPTR=$(mdsdevname 1)
4620                 else
4621                         DEVPTR=${MGSZFSDEV:-${FSNAME}-mgs/mgs}
4622                 fi;;
4623         * )
4624                 error "unknown fstype!";;
4625         esac
4626
4627         echo -n $DEVPTR
4628 }
4629
4630 mgsvdevname() {
4631         local VDEVPTR=""
4632
4633         local fstype=$(facet_fstype mgs)
4634
4635         case $fstype in
4636         ldiskfs )
4637                 # vdevs are not supported by ldiskfs
4638                 ;;
4639         zfs )
4640                 if [ $(facet_host mgs) = $(facet_host mds1) ] &&
4641                    ( [ -z "$MGSDEV" ] &&
4642                        [ -z "$MGSZFSDEV" -o "$MGSZFSDEV" = $(mdsdevname 1) ]); then
4643                         VDEVPTR=$(mdsvdevname 1)
4644                 elif [ -n "$MGSDEV" ]; then
4645                         VDEVPTR=$MGSDEV
4646                 fi;;
4647         * )
4648                 error "unknown fstype!";;
4649         esac
4650
4651         echo -n $VDEVPTR
4652 }
4653
4654 facet_mntpt () {
4655         local facet=$1
4656         [[ $facet = mgs ]] && combined_mgs_mds && facet="mds1"
4657
4658         local var=${facet}_MOUNT
4659         eval mntpt=${!var:-${MOUNT}-$facet}
4660
4661         echo -n $mntpt
4662 }
4663
4664 mount_ldiskfs() {
4665         local facet=$1
4666         local dev=$(facet_device $facet)
4667         local mnt=${2:-$(facet_mntpt $facet)}
4668         local opts
4669         local dm_dev=$dev
4670
4671         if dm_flakey_supported $facet; then
4672                 dm_dev=$(dm_create_dev $facet $dev)
4673                 [[ -n "$dm_dev" ]] || dm_dev=$dev
4674         fi
4675         is_blkdev $facet $dm_dev || opts=$(csa_add "$opts" -o loop)
4676         export_dm_dev $facet $dm_dev
4677
4678         do_facet $facet mount -t ldiskfs $opts $dm_dev $mnt
4679 }
4680
4681 unmount_ldiskfs() {
4682         local facet=$1
4683         local dev=$(facet_device $facet)
4684         local mnt=${2:-$(facet_mntpt $facet)}
4685
4686         do_facet $facet $UMOUNT $mnt
4687 }
4688
4689 var_name() {
4690         echo -n "$1" | tr -c '[:alnum:]\n' '_'
4691 }
4692
4693 mount_zfs() {
4694         local facet=$1
4695         local ds=$(facet_device $facet)
4696         local mnt=${2:-$(facet_mntpt $facet)}
4697         local canmnt
4698         local mntpt
4699
4700         import_zpool $facet
4701         canmnt=$(do_facet $facet $ZFS get -H -o value canmount $ds)
4702         mntpt=$(do_facet $facet $ZFS get -H -o value mountpoint $ds)
4703         do_facet $facet $ZFS set canmount=noauto $ds
4704         #
4705         # The "legacy" mount method is used here because "zfs unmount $mnt"
4706         # calls stat(2) on $mnt/../*, which may include $MOUNT.  If certain
4707         # targets are not available at the time, the stat(2) on $MOUNT will
4708         # hang.
4709         #
4710         do_facet $facet $ZFS set mountpoint=legacy $ds
4711         do_facet $facet mount -t zfs $ds $mnt
4712         eval export mz_$(var_name ${facet}_$ds)_canmount=$canmnt
4713         eval export mz_$(var_name ${facet}_$ds)_mountpoint=$mntpt
4714 }
4715
4716 unmount_zfs() {
4717         local facet=$1
4718         local ds=$(facet_device $facet)
4719         local mnt=${2:-$(facet_mntpt $facet)}
4720         local var_mntpt=mz_$(var_name ${facet}_$ds)_mountpoint
4721         local var_canmnt=mz_$(var_name ${facet}_$ds)_canmount
4722         local mntpt=${!var_mntpt}
4723         local canmnt=${!var_canmnt}
4724
4725         unset $var_mntpt
4726         unset $var_canmnt
4727         do_facet $facet umount $mnt
4728         do_facet $facet $ZFS set mountpoint=$mntpt $ds
4729         do_facet $facet $ZFS set canmount=$canmnt $ds
4730         export_zpool $facet
4731 }
4732
4733 mount_fstype() {
4734         local facet=$1
4735         local mnt=$2
4736         local fstype=$(facet_fstype $facet)
4737
4738         mount_$fstype $facet $mnt
4739 }
4740
4741 unmount_fstype() {
4742         local facet=$1
4743         local mnt=$2
4744         local fstype=$(facet_fstype $facet)
4745
4746         unmount_$fstype $facet $mnt
4747 }
4748
4749 ########
4750 ## MountConf setup
4751
4752 stopall() {
4753         # make sure we are using the primary server, so test-framework will
4754         # be able to clean up properly.
4755         activemds=`facet_active mds1`
4756         if [ $activemds != "mds1" ]; then
4757                 fail mds1
4758         fi
4759
4760         local clients=$CLIENTS
4761         [ -z $clients ] && clients=$(hostname)
4762
4763         zconf_umount_clients $clients $MOUNT "$*" || true
4764         [ -n "$MOUNT2" ] && zconf_umount_clients $clients $MOUNT2 "$*" || true
4765
4766         [ -n "$CLIENTONLY" ] && return
4767
4768         # The add fn does rm ${facet}active file, this would be enough
4769         # if we use do_facet <facet> only after the facet added, but
4770         # currently we use do_facet mds in local.sh
4771         local num
4772         for num in `seq $MDSCOUNT`; do
4773                 stop mds$num -f
4774                 rm -f ${TMP}/mds${num}active
4775         done
4776         combined_mgs_mds && rm -f $TMP/mgsactive
4777
4778         for num in `seq $OSTCOUNT`; do
4779                 stop ost$num -f
4780                 rm -f $TMP/ost${num}active
4781         done
4782
4783         if ! combined_mgs_mds ; then
4784                 stop mgs
4785         fi
4786
4787         if $SHARED_KEY; then
4788                 export SK_MOUNTED=false
4789         fi
4790
4791         return 0
4792 }
4793
4794 cleanup_echo_devs () {
4795         trap 0
4796         local dev
4797         local devs=$($LCTL dl | grep echo | awk '{print $4}')
4798
4799         for dev in $devs; do
4800                 $LCTL --device $dev cleanup
4801                 $LCTL --device $dev detach
4802         done
4803 }
4804
4805 cleanupall() {
4806         nfs_client_mode && return
4807         cifs_client_mode && return
4808
4809         cleanup_echo_devs
4810         CLEANUP_DM_DEV=true stopall $*
4811
4812         unload_modules
4813         cleanup_sk
4814         cleanup_gss
4815 }
4816
4817 combined_mgs_mds () {
4818         [[ "$(mdsdevname 1)" = "$(mgsdevname)" ]] &&
4819                 [[ "$(facet_host mds1)" = "$(facet_host mgs)" ]]
4820 }
4821
4822 lower() {
4823         echo -n "$1" | tr '[:upper:]' '[:lower:]'
4824 }
4825
4826 upper() {
4827         echo -n "$1" | tr '[:lower:]' '[:upper:]'
4828 }
4829
4830 squash_opt() {
4831         local var="$*"
4832         local other=""
4833         local opt_o=""
4834         local opt_e=""
4835         local first_e=0
4836         local first_o=0
4837         local take=""
4838
4839         var=$(echo "$var" | sed -e 's/,\( \)*/,/g')
4840         for i in $(echo "$var"); do
4841                 if [ "$i" == "-O" ]; then
4842                         take="o";
4843                         first_o=$(($first_o + 1))
4844                         continue;
4845                 fi
4846                 if [ "$i" == "-E" ]; then
4847                         take="e";
4848                         first_e=$(($first_e + 1 ))
4849                         continue;
4850                 fi
4851                 case $take in
4852                         "o")
4853                                 [ $first_o -gt 1 ] && opt_o+=",";
4854                                 opt_o+="$i";
4855                                 ;;
4856                         "e")
4857                                 [ $first_e -gt 1 ] && opt_e+=",";
4858                                 opt_e+="$i";
4859                                 ;;
4860                         *)
4861                                 other+=" $i";
4862                                 ;;
4863                 esac
4864                 take=""
4865         done
4866
4867         echo -n "$other"
4868         [ -n "$opt_o" ] && echo " -O $opt_o"
4869         [ -n "$opt_e" ] && echo " -E $opt_e"
4870 }
4871
4872 mkfs_opts() {
4873         local facet=$1
4874         local dev=$2
4875         local fsname=${3:-"$FSNAME"}
4876         local type=$(facet_type $facet)
4877         local index=$(facet_index $facet)
4878         local fstype=$(facet_fstype $facet)
4879         local host=$(facet_host $facet)
4880         local opts
4881         local fs_mkfs_opts
4882         local var
4883         local varbs=${facet}_BLOCKSIZE
4884
4885         if [ $type == MGS ] || ( [ $type == MDS ] &&
4886                                  [ "$dev" == $(mgsdevname) ] &&
4887                                  [ "$host" == "$(facet_host mgs)" ] ); then
4888                 opts="--mgs"
4889         else
4890                 opts="--mgsnode=$MGSNID"
4891         fi
4892
4893         if [ $type != MGS ]; then
4894                 opts+=" --fsname=$fsname --$(lower ${type/MDS/MDT}) \
4895                         --index=$index"
4896         fi
4897
4898         var=${facet}failover_HOST
4899         if [ -n "${!var}" ] && [ ${!var} != $(facet_host $facet) ]; then
4900                 opts+=" --failnode=$(h2nettype ${!var})"
4901         fi
4902
4903         opts+=${TIMEOUT:+" --param=sys.timeout=$TIMEOUT"}
4904         opts+=${LDLM_TIMEOUT:+" --param=sys.ldlm_timeout=$LDLM_TIMEOUT"}
4905
4906         if [ $type == MDS ]; then
4907                 opts+=${DEF_STRIPE_SIZE:+" --param=lov.stripesize=$DEF_STRIPE_SIZE"}
4908                 opts+=${DEF_STRIPE_COUNT:+" --param=lov.stripecount=$DEF_STRIPE_COUNT"}
4909                 opts+=${L_GETIDENTITY:+" --param=mdt.identity_upcall=$L_GETIDENTITY"}
4910
4911                 if [ $fstype == ldiskfs ]; then
4912                         var=${facet}_JRN
4913                         if [ -n "${!var}" ]; then
4914                                 fs_mkfs_opts+=" -J device=${!var}"
4915                         else
4916                                 fs_mkfs_opts+=${MDSJOURNALSIZE:+" -J size=$MDSJOURNALSIZE"}
4917                         fi
4918                         fs_mkfs_opts+=${MDSISIZE:+" -i $MDSISIZE"}
4919                 fi
4920         fi
4921
4922         if [ $type == OST ]; then
4923                 if [ $fstype == ldiskfs ]; then
4924                         var=${facet}_JRN
4925                         if [ -n "${!var}" ]; then
4926                                 fs_mkfs_opts+=" -J device=${!var}"
4927                         else
4928                                 fs_mkfs_opts+=${OSTJOURNALSIZE:+" -J size=$OSTJOURNALSIZE"}
4929                         fi
4930                 fi
4931         fi
4932
4933         opts+=" --backfstype=$fstype"
4934
4935         var=${type}SIZE
4936         if [ -n "${!var}" ]; then
4937                 opts+=" --device-size=${!var}"
4938         fi
4939
4940         var=$(upper $fstype)_MKFS_OPTS
4941         fs_mkfs_opts+=${!var:+" ${!var}"}
4942
4943         var=${type}_FS_MKFS_OPTS
4944         fs_mkfs_opts+=${!var:+" ${!var}"}
4945
4946         [[ "$QUOTA_TYPE" =~ "p" ]] && fs_mkfs_opts+=" -O project"
4947
4948         [ $fstype == ldiskfs ] && fs_mkfs_opts+=" -b ${!varbs:-$BLCKSIZE}"
4949         [ $fstype == ldiskfs ] && fs_mkfs_opts=$(squash_opt $fs_mkfs_opts)
4950
4951         if [ -n "${fs_mkfs_opts## }" ]; then
4952                 opts+=" --mkfsoptions=\\\"${fs_mkfs_opts## }\\\""
4953         fi
4954
4955         var=${type}OPT
4956         opts+=${!var:+" ${!var}"}
4957
4958         echo -n "$opts"
4959 }
4960
4961 mountfs_opts() {
4962         local facet=$1
4963         local type=$(facet_type $facet)
4964         local var=${type}_MOUNT_FS_OPTS
4965         local opts=""
4966         if [ -n "${!var}" ]; then
4967                 opts+=" --mountfsoptions=${!var}"
4968         fi
4969         echo -n "$opts"
4970 }
4971
4972 check_ost_indices() {
4973         local index_count=${#OST_INDICES[@]}
4974         [[ $index_count -eq 0 || $OSTCOUNT -le $index_count ]] && return 0
4975
4976         # OST count is greater than the index count in $OST_INDEX_LIST.
4977         # We need check whether there are duplicate indices.
4978         local i
4979         local j
4980         local index
4981         for i in $(seq $((index_count + 1)) $OSTCOUNT); do
4982                 index=$(facet_index ost$i)
4983                 for j in $(seq 0 $((index_count - 1))); do
4984                         [[ $index -ne ${OST_INDICES[j]} ]] ||
4985                         error "ost$i has the same index $index as ost$((j+1))"
4986                 done
4987         done
4988 }
4989
4990 __touch_device()
4991 {
4992         local facet_type=$1 # mgs || mds || ost
4993         local facet_num=$2
4994         local facet=${1}${2}
4995         local device
4996
4997         case "$(facet_fstype $facet)" in
4998         ldiskfs)
4999                 device=$(${facet_type}devname $facet_num)
5000                 ;;
5001         zfs)
5002                 device=$(${facet_type}vdevname $facet_num)
5003                 ;;
5004         *)
5005                 error "Unhandled filesystem type"
5006                 ;;
5007         esac
5008
5009         do_facet $facet "[ -e \"$device\" ]" && return
5010
5011         # Note: the following check only works with absolute paths
5012         [[ ! "$device" =~ ^/dev/ ]] || [[ "$device" =~ ^/dev/shm/ ]] ||
5013                 error "$facet: device '$device' does not exist"
5014
5015         # zpool create doesn't like empty files
5016         [[ $(facet_fstype $facet) == zfs ]] && return 0
5017
5018         do_facet $facet "touch \"${device}\""
5019 }
5020
5021 format_mgs() {
5022         local quiet
5023
5024         if ! $VERBOSE; then
5025                 quiet=yes
5026         fi
5027         echo "Format mgs: $(mgsdevname)"
5028         reformat_external_journal mgs
5029
5030         # touch "device" in case it is a loopback file for testing and needs to
5031         # be created. mkfs.lustre doesn't do this to avoid accidentally writing
5032         # to non-existent files in /dev if the admin made a typo during setup
5033         __touch_device mgs
5034
5035         add mgs $(mkfs_opts mgs $(mgsdevname)) $(mountfs_opts mgs) --reformat \
5036                 $(mgsdevname) $(mgsvdevname) ${quiet:+>/dev/null} || exit 10
5037 }
5038
5039 format_mdt() {
5040         local num=$1
5041         local quiet
5042
5043         if ! $VERBOSE; then
5044                 quiet=yes
5045         fi
5046         echo "Format mds$num: $(mdsdevname $num)"
5047         reformat_external_journal mds$num
5048
5049         __touch_device mds $num
5050
5051         add mds$num $(mkfs_opts mds$num $(mdsdevname ${num})) \
5052                 $(mountfs_opts mds$num) --reformat $(mdsdevname $num) \
5053                 $(mdsvdevname $num) ${quiet:+>/dev/null} || exit 10
5054 }
5055
5056 format_ost() {
5057         local num=$1
5058
5059         if ! $VERBOSE; then
5060                 quiet=yes
5061         fi
5062         echo "Format ost$num: $(ostdevname $num)"
5063         reformat_external_journal ost$num
5064
5065         __touch_device ost $num
5066
5067         add ost$num $(mkfs_opts ost$num $(ostdevname ${num})) \
5068                 $(mountfs_opts ost$num) --reformat $(ostdevname $num) \
5069                 $(ostvdevname ${num}) ${quiet:+>/dev/null} || exit 10
5070 }
5071
5072 formatall() {
5073         stopall -f
5074         # Set hostid for ZFS/SPL zpool import protection
5075         # (Assumes MDS version is also OSS version)
5076         if [ $(lustre_version_code $SINGLEMDS) -ge $(version_code 2.8.54) ];
5077         then
5078                 do_rpc_nodes "$(comma_list $(all_server_nodes))" set_hostid
5079         fi
5080
5081         # We need ldiskfs here, may as well load them all
5082         load_modules
5083         [ -n "$CLIENTONLY" ] && return
5084         echo Formatting mgs, mds, osts
5085         if ! combined_mgs_mds ; then
5086                 format_mgs
5087         fi
5088
5089         for num in $(seq $MDSCOUNT); do
5090                 format_mdt $num
5091         done
5092
5093         export OST_INDICES=($(hostlist_expand "$OST_INDEX_LIST"))
5094         check_ost_indices
5095         for num in $(seq $OSTCOUNT); do
5096                 format_ost $num
5097         done
5098 }
5099
5100 mount_client() {
5101         grep " $1 " /proc/mounts || zconf_mount $HOSTNAME $*
5102 }
5103
5104 umount_client() {
5105         grep " $1 " /proc/mounts && zconf_umount $HOSTNAME $*
5106 }
5107
5108 # return value:
5109 # 0: success, the old identity set already.
5110 # 1: success, the old identity does not set.
5111 # 2: fail.
5112 switch_identity() {
5113     local num=$1
5114     local switch=$2
5115     local j=`expr $num - 1`
5116     local MDT="`(do_facet mds$num lctl get_param -N mdt.*MDT*$j 2>/dev/null | cut -d"." -f2 2>/dev/null) || true`"
5117
5118     if [ -z "$MDT" ]; then
5119         return 2
5120     fi
5121
5122     local old="`do_facet mds$num "lctl get_param -n mdt.$MDT.identity_upcall"`"
5123
5124     if $switch; then
5125         do_facet mds$num "lctl set_param -n mdt.$MDT.identity_upcall \"$L_GETIDENTITY\""
5126     else
5127         do_facet mds$num "lctl set_param -n mdt.$MDT.identity_upcall \"NONE\""
5128     fi
5129
5130     do_facet mds$num "lctl set_param -n mdt/$MDT/identity_flush=-1"
5131
5132     if [ $old = "NONE" ]; then
5133         return 1
5134     else
5135         return 0
5136     fi
5137 }
5138
5139 remount_client()
5140 {
5141         zconf_umount $HOSTNAME $1 || error "umount failed"
5142         zconf_mount $HOSTNAME $1 || error "mount failed"
5143 }
5144
5145 writeconf_facet() {
5146         local facet=$1
5147         local dev=$2
5148
5149         stop ${facet} -f
5150         rm -f $TMP/${facet}active
5151         do_facet ${facet} "$TUNEFS --quiet --writeconf $dev" || return 1
5152         return 0
5153 }
5154
5155 writeconf_all () {
5156         local mdt_count=${1:-$MDSCOUNT}
5157         local ost_count=${2:-$OSTCOUNT}
5158         local rc=0
5159
5160         for num in $(seq $mdt_count); do
5161                 DEVNAME=$(mdsdevname $num)
5162                 writeconf_facet mds$num $DEVNAME || rc=$?
5163         done
5164
5165         for num in $(seq $ost_count); do
5166                 DEVNAME=$(ostdevname $num)
5167                 writeconf_facet ost$num $DEVNAME || rc=$?
5168         done
5169         return $rc
5170 }
5171
5172 mountmgs() {
5173         if ! combined_mgs_mds ; then
5174                 start mgs $(mgsdevname) $MGS_MOUNT_OPTS
5175         fi
5176 }
5177
5178 mountmds() {
5179         local num
5180         local devname
5181         local host
5182         local varname
5183         for num in $(seq $MDSCOUNT); do
5184                 devname=$(mdsdevname $num)
5185                 start mds$num $devname $MDS_MOUNT_OPTS
5186
5187                 # We started mds$num, now we should set mds${num}_HOST
5188                 # and mds${num}failover_HOST variables properly if they
5189                 # are not set.
5190                 host=$(facet_host mds$num)
5191                 for varname in mds${num}_HOST mds${num}failover_HOST; do
5192                         if [[ -z "${!varname}" ]]; then
5193                                 eval $varname=$host
5194                         fi
5195                 done
5196                 if [ $IDENTITY_UPCALL != "default" ]; then
5197                         switch_identity $num $IDENTITY_UPCALL
5198                 fi
5199         done
5200 }
5201
5202 mountoss() {
5203         local num
5204         local devname
5205         local host
5206         local varname
5207         for num in $(seq $OSTCOUNT); do
5208                 devname=$(ostdevname $num)
5209                 start ost$num $devname $OST_MOUNT_OPTS
5210
5211                 # We started ost$num, now we should set ost${num}_HOST
5212                 # and ost${num}failover_HOST variables properly if they
5213                 # are not set.
5214                 host=$(facet_host ost$num)
5215                 for varname in ost${num}_HOST ost${num}failover_HOST; do
5216                         if [[ -z "${!varname}" ]]; then
5217                                 eval $varname=$host
5218                         fi
5219                 done
5220         done
5221 }
5222
5223 mountcli() {
5224         [ "$DAEMONFILE" ] && $LCTL debug_daemon start $DAEMONFILE $DAEMONSIZE
5225         if [ ! -z $arg1 ]; then
5226                 [ "$arg1" = "server_only" ] && return
5227         fi
5228         mount_client $MOUNT
5229         if [ -n "$CLIENTS" ]; then
5230                 zconf_mount_clients $CLIENTS $MOUNT
5231         fi
5232         clients_up
5233
5234         if [ "$MOUNT_2" ]; then
5235                 mount_client $MOUNT2
5236                 if [ -n "$CLIENTS" ]; then
5237                         zconf_mount_clients $CLIENTS $MOUNT2
5238                 fi
5239         fi
5240 }
5241
5242 sk_nodemap_setup() {
5243         local sk_map_name=${1:-$SK_S2SNM}
5244         local sk_map_nodes=${2:-$HOSTNAME}
5245         do_node $(mgs_node) "$LCTL nodemap_add $sk_map_name"
5246         for servernode in $sk_map_nodes; do
5247                 local nids=$(do_nodes $servernode "$LCTL list_nids")
5248                 for nid in $nids; do
5249                         do_node $(mgs_node) "$LCTL nodemap_add_range --name \
5250                                 $sk_map_name --range $nid"
5251                 done
5252         done
5253 }
5254
5255 setupall() {
5256         local arg1=$1
5257
5258         nfs_client_mode && return
5259         cifs_client_mode && return
5260
5261         sanity_mount_check || error "environments are insane!"
5262
5263         load_modules
5264
5265         init_gss
5266
5267         if [ -z "$CLIENTONLY" ]; then
5268                 echo Setup mgs, mdt, osts
5269                 echo $WRITECONF | grep -q "writeconf" && writeconf_all
5270
5271                 if $SK_MOUNTED; then
5272                         echo "Shared Key file system already mounted"
5273                 else
5274                         mountmgs
5275                         mountmds
5276                         mountoss
5277                         if $SHARED_KEY; then
5278                                 export SK_MOUNTED=true
5279                         fi
5280                 fi
5281                 if $GSS_SK; then
5282                         echo "GSS_SK: setting kernel keyring perms"
5283                         do_nodes $(comma_list $(all_nodes)) \
5284                                 "keyctl show | grep lustre | cut -c1-11 |
5285                                 sed -e 's/ //g;' |
5286                                 xargs -IX keyctl setperm X 0x3f3f3f3f"
5287
5288                         if $SK_S2S; then
5289                                 # Need to start one nodemap for servers,
5290                                 # and one for clients.
5291                                 sk_nodemap_setup $SK_S2SNM \
5292                                         $(comma_list $(all_server_nodes))
5293                                 mountcli
5294                                 sk_nodemap_setup $SK_S2SNMCLI \
5295                                         ${CLIENTS:-$HOSTNAME}
5296                                 echo "Nodemap set up for SK S2S, remounting."
5297                                 stopall
5298                                 mountmgs
5299                                 mountmds
5300                                 mountoss
5301                         fi
5302                 fi
5303         fi
5304
5305         # wait a while to allow sptlrpc configuration be propogated to targets,
5306         # only needed when mounting new target devices.
5307         if $GSS; then
5308                 sleep 10
5309         fi
5310
5311         mountcli
5312         init_param_vars
5313
5314         # by remounting mdt before ost, initial connect from mdt to ost might
5315         # timeout because ost is not ready yet. wait some time to its fully
5316         # recovery. initial obd_connect timeout is 5s; in GSS case it's
5317         # preceeded by a context negotiation rpc with $TIMEOUT.
5318         # FIXME better by monitoring import status.
5319         if $GSS; then
5320                 if $GSS_SK; then
5321                         set_rule $FSNAME any cli2mdt $SK_FLAVOR
5322                         set_rule $FSNAME any cli2ost $SK_FLAVOR
5323                         if $SK_SKIPFIRST; then
5324                                 export SK_SKIPFIRST=false
5325
5326                                 sleep 30
5327                                 do_nodes $CLIENTS \
5328                                          "lctl set_param osc.*.idle_connect=1"
5329                                 return
5330                         else
5331                                 wait_flavor cli2mdt $SK_FLAVOR
5332                                 wait_flavor cli2ost $SK_FLAVOR
5333                         fi
5334                 else
5335                         set_flavor_all $SEC
5336                 fi
5337                 sleep $((TIMEOUT + 5))
5338         else
5339                 sleep 5
5340         fi
5341 }
5342
5343 mounted_lustre_filesystems() {
5344         awk '($3 ~ "lustre" && $1 ~ ":") { print $2 }' /proc/mounts
5345 }
5346
5347 init_facet_vars () {
5348         [ -n "$CLIENTONLY" ] && return 0
5349         local facet=$1
5350         shift
5351         local device=$1
5352
5353         shift
5354
5355         eval export ${facet}_dev=${device}
5356         eval export ${facet}_opt=\"$*\"
5357
5358         local dev=${facet}_dev
5359
5360         # We need to loop for the label
5361         # in case its not initialized yet.
5362         for wait_time in {0,1,3,5,10}; do
5363
5364                 if [ $wait_time -gt 0 ]; then
5365                         echo "${!dev} not yet initialized,"\
5366                                 "waiting ${wait_time} seconds."
5367                         sleep $wait_time
5368                 fi
5369
5370                 local label=$(devicelabel ${facet} ${!dev})
5371
5372                 # Check to make sure the label does
5373                 # not include ffff at the end of the label.
5374                 # This indicates it has not been initialized yet.
5375
5376                 if [[ $label =~ [f|F]{4}$ ]]; then
5377                         # label is not initialized, unset the result
5378                         # and either try again or fail
5379                         unset label
5380                 else
5381                         break
5382                 fi
5383         done
5384
5385         [ -z "$label" ] && echo no label for ${!dev} && exit 1
5386
5387         eval export ${facet}_svc=${label}
5388
5389         local varname=${facet}failover_HOST
5390         if [ -z "${!varname}" ]; then
5391                 local temp
5392                 if combined_mgs_mds && [ $facet == "mgs" ] &&
5393                    [ -n "$mds1failover_HOST" ]; then
5394                         temp=$mds1failover_HOST
5395                 else
5396                         temp=$(facet_host $facet)
5397                 fi
5398                 eval export $varname=$temp
5399         fi
5400
5401         varname=${facet}_HOST
5402         if [ -z "${!varname}" ]; then
5403                 eval export $varname=$(facet_host $facet)
5404         fi
5405
5406         # ${facet}failover_dev is set in cfg file
5407         varname=${facet}failover_dev
5408         if [ -n "${!varname}" ] ; then
5409                 eval export ${facet}failover_dev=${!varname}
5410         else
5411                 eval export ${facet}failover_dev=$device
5412         fi
5413
5414         # get mount point of already mounted device
5415         # is facet_dev is already mounted then use the real
5416         #  mount point of this facet; otherwise use $(facet_mntpt $facet)
5417         # i.e. ${facet}_MOUNT if specified by user or default
5418         local mntpt=$(do_facet ${facet} cat /proc/mounts | \
5419                         awk '"'${!dev}'" == $1 && $3 == "lustre" { print $2 }')
5420         if [ -z $mntpt ]; then
5421                 mntpt=$(facet_mntpt $facet)
5422         fi
5423         eval export ${facet}_MOUNT=$mntpt
5424 }
5425
5426 init_facets_vars () {
5427         local DEVNAME
5428
5429         if ! remote_mds_nodsh; then
5430                 for num in $(seq $MDSCOUNT); do
5431                         DEVNAME=$(mdsdevname $num)
5432                         init_facet_vars mds$num $DEVNAME $MDS_MOUNT_OPTS
5433                 done
5434         fi
5435
5436         init_facet_vars mgs $(mgsdevname) $MGS_MOUNT_OPTS
5437
5438         if ! remote_ost_nodsh; then
5439                 for num in $(seq $OSTCOUNT); do
5440                         DEVNAME=$(ostdevname $num)
5441                         init_facet_vars ost$num $DEVNAME $OST_MOUNT_OPTS
5442                 done
5443         fi
5444 }
5445
5446 init_facets_vars_simple () {
5447         local devname
5448
5449         if ! remote_mds_nodsh; then
5450                 for num in $(seq $MDSCOUNT); do
5451                         devname=$(mdsdevname $num)
5452                         eval export mds${num}_dev=${devname}
5453                         eval export mds${num}_opt=\"${MDS_MOUNT_OPTS}\"
5454                 done
5455         fi
5456
5457         if ! combined_mgs_mds ; then
5458                 eval export mgs_dev=$(mgsdevname)
5459                 eval export mgs_opt=\"${MGS_MOUNT_OPTS}\"
5460         fi
5461
5462         if ! remote_ost_nodsh; then
5463                 for num in $(seq $OSTCOUNT); do
5464                         devname=$(ostdevname $num)
5465                         eval export ost${num}_dev=${devname}
5466                         eval export ost${num}_opt=\"${OST_MOUNT_OPTS}\"
5467                 done
5468         fi
5469 }
5470
5471 osc_ensure_active () {
5472         local facet=$1
5473         local timeout=$2
5474         local period=0
5475
5476         while [ $period -lt $timeout ]; do
5477                 count=$(do_facet $facet "lctl dl | grep ' IN osc ' 2>/dev/null | wc -l")
5478                 if [ $count -eq 0 ]; then
5479                         break
5480                 fi
5481
5482                 echo "$count OST inactive, wait $period seconds, and try again"
5483                 sleep 3
5484                 period=$((period+3))
5485         done
5486
5487         [ $period -lt $timeout ] ||
5488                 log "$count OST are inactive after $timeout seconds, give up"
5489 }
5490
5491 set_conf_param_and_check() {
5492         local myfacet=$1
5493         local TEST=$2
5494         local PARAM=$3
5495         local ORIG=$(do_facet $myfacet "$TEST")
5496         if [ $# -gt 3 ]; then
5497                 local FINAL=$4
5498         else
5499                 local -i FINAL
5500                 FINAL=$((ORIG + 5))
5501         fi
5502         echo "Setting $PARAM from $ORIG to $FINAL"
5503         do_facet mgs "$LCTL conf_param $PARAM='$FINAL'" ||
5504                 error "conf_param $PARAM failed"
5505
5506         wait_update_facet $myfacet "$TEST" "$FINAL" ||
5507                 error "check $PARAM failed!"
5508 }
5509
5510 set_persistent_param() {
5511         local myfacet=$1
5512         local test_param=$2
5513         local param=$3
5514         local orig=$(do_facet $myfacet "$LCTL get_param -n $test_param")
5515
5516         if [ $# -gt 3 ]; then
5517                 local final=$4
5518         else
5519                 local -i final
5520                 final=$((orig + 5))
5521         fi
5522
5523         if [[ $PERM_CMD == *"set_param -P"* ]]; then
5524                 echo "Setting $test_param from $orig to $final"
5525                 do_facet mgs "$PERM_CMD $test_param='$final'" ||
5526                         error "$PERM_CMD $test_param failed"
5527         else
5528                 echo "Setting $param from $orig to $final"
5529                 do_facet mgs "$PERM_CMD $param='$final'" ||
5530                         error "$PERM_CMD $param failed"
5531         fi
5532 }
5533
5534 set_persistent_param_and_check() {
5535         local myfacet=$1
5536         local test_param=$2
5537         local param=$3
5538         local orig=$(do_facet $myfacet "$LCTL get_param -n $test_param")
5539
5540         if [ $# -gt 3 ]; then
5541                 local final=$4
5542         else
5543                 local -i final
5544                 final=$((orig + 5))
5545         fi
5546
5547         set_persistent_param $myfacet $test_param $param "$final"
5548
5549         wait_update_facet $myfacet "$LCTL get_param -n $test_param" "$final" ||
5550                 error "check $param failed!"
5551 }
5552
5553 init_param_vars () {
5554         TIMEOUT=$(lctl get_param -n timeout)
5555         TIMEOUT=${TIMEOUT:-20}
5556
5557         if [ -n "$arg1" ]; then
5558                 [ "$arg1" = "server_only" ] && return
5559         fi
5560
5561         remote_mds_nodsh && log "Using TIMEOUT=$TIMEOUT" && return 0
5562
5563         TIMEOUT=$(do_facet $SINGLEMDS "lctl get_param -n timeout")
5564         log "Using TIMEOUT=$TIMEOUT"
5565
5566         # tune down to speed up testing on (usually) small setups
5567         local mgc_timeout=/sys/module/mgc/parameters/mgc_requeue_timeout_min
5568         do_nodes $(comma_list $(nodes_list)) \
5569                 "[ -f $mgc_timeout ] && echo 1 > $mgc_timeout; exit 0"
5570
5571         osc_ensure_active $SINGLEMDS $TIMEOUT
5572         osc_ensure_active client $TIMEOUT
5573         $LCTL set_param osc.*.idle_timeout=debug
5574
5575         if [ -n "$(lctl get_param -n mdc.*.connect_flags|grep jobstats)" ]; then
5576                 local current_jobid_var=$($LCTL get_param -n jobid_var)
5577
5578                 if [ $JOBID_VAR = "existing" ]; then
5579                         echo "keeping jobstats as $current_jobid_var"
5580                 elif [ $current_jobid_var != $JOBID_VAR ]; then
5581                         echo "setting jobstats to $JOBID_VAR"
5582
5583                         set_persistent_param_and_check client \
5584                                 "jobid_var" "$FSNAME.sys.jobid_var" $JOBID_VAR
5585                 fi
5586         else
5587                 echo "jobstats not supported by server"
5588         fi
5589
5590         if [ $QUOTA_AUTO -ne 0 ]; then
5591                 if [ "$ENABLE_QUOTA" ]; then
5592                         echo "enable quota as required"
5593                         setup_quota $MOUNT || return 2
5594                 else
5595                         echo "disable quota as required"
5596                         # $LFS quotaoff -ug $MOUNT > /dev/null 2>&1
5597                 fi
5598         fi
5599
5600         (( MDS1_VERSION <= $(version_code 2.13.52) )) ||
5601                 do_facet mgs "$LCTL set_param -P lod.*.mdt_hash=crush"
5602         return 0
5603 }
5604
5605 nfs_client_mode () {
5606         if [ "$NFSCLIENT" ]; then
5607                 echo "NFSCLIENT mode: setup, cleanup, check config skipped"
5608                 local clients=$CLIENTS
5609
5610                 [ -z $clients ] && clients=$(hostname)
5611
5612                 # FIXME: remove hostname when 19215 fixed
5613                 do_nodes $clients "echo \\\$(hostname); grep ' '$MOUNT' ' /proc/mounts"
5614                 declare -a nfsexport=(`grep ' '$MOUNT' ' /proc/mounts |
5615                         awk '{print $1}' | awk -F: '{print $1 " "  $2}'`)
5616                 if [[ ${#nfsexport[@]} -eq 0 ]]; then
5617                         error_exit NFSCLIENT=$NFSCLIENT mode, but no NFS export found!
5618                 fi
5619                 do_nodes ${nfsexport[0]} "echo \\\$(hostname); df -T  ${nfsexport[1]}"
5620                 return
5621         fi
5622         return 1
5623 }
5624
5625 cifs_client_mode () {
5626         [ x$CIFSCLIENT = xyes ] &&
5627                 echo "CIFSCLIENT=$CIFSCLIENT mode: setup, cleanup, check config skipped"
5628 }
5629
5630 check_config_client () {
5631         local mntpt=$1
5632         local mounted=$(mount | grep " $mntpt ")
5633
5634         if [ -n "$CLIENTONLY" ]; then
5635                 # bug 18021
5636                 # CLIENTONLY should not depend on *_HOST settings
5637                 local mgc=$($LCTL device_list | awk '/MGC/ {print $4}')
5638                 # in theory someone could create a new,
5639                 # client-only config file that assumed lustre was already
5640                 # configured and didn't set the MGSNID. If MGSNID is not set,
5641                 # then we should use the mgs nid currently being used
5642                 # as the default value. bug 18021
5643                 [[ x$MGSNID = x ]] &&
5644                 MGSNID=${mgc//MGC/}
5645
5646                 if [[ x$mgc != xMGC$MGSNID ]]; then
5647                         if [ "$mgs_HOST" ]; then
5648                                 local mgc_ip=$(ping -q -c1 -w1 $mgs_HOST |
5649                                         grep PING | awk '{print $3}' |
5650                                         sed -e "s/(//g" -e "s/)//g")
5651
5652                                 # [[ x$mgc = xMGC$mgc_ip@$NETTYPE ]] ||
5653                                 # error_exit "MGSNID=$MGSNID, mounted: $mounted, MGC : $mgc"
5654                         fi
5655                 fi
5656                 return 0
5657         fi
5658
5659         echo Checking config lustre mounted on $mntpt
5660         local mgshost=$(mount | grep " $mntpt " | awk -F@ '{print $1}')
5661         mgshost=$(echo $mgshost | awk -F: '{print $1}')
5662
5663 }
5664
5665 check_config_clients () {
5666         local clients=${CLIENTS:-$HOSTNAME}
5667         local mntpt=$1
5668
5669         nfs_client_mode && return
5670         cifs_client_mode && return
5671
5672         do_rpc_nodes "$clients" check_config_client $mntpt
5673
5674         sanity_mount_check || error "environments are insane!"
5675 }
5676
5677 check_timeout () {
5678     local mdstimeout=$(do_facet $SINGLEMDS "lctl get_param -n timeout")
5679     local cltimeout=$(lctl get_param -n timeout)
5680     if [ $mdstimeout -ne $TIMEOUT ] || [ $mdstimeout -ne $cltimeout ]; then
5681         error "timeouts are wrong! mds: $mdstimeout, client: $cltimeout, TIMEOUT=$TIMEOUT"
5682         return 1
5683     fi
5684 }
5685
5686 is_mounted () {
5687         local mntpt=$1
5688         [ -z $mntpt ] && return 1
5689         local mounted=$(mounted_lustre_filesystems)
5690
5691         echo $mounted' ' | grep -w -q $mntpt' '
5692 }
5693
5694 create_pools () {
5695         local pool=$1
5696         local ostsn=${2:-$OSTCOUNT}
5697         local npools=${FS_NPOOLS:-$((OSTCOUNT / ostsn))}
5698         local n
5699
5700         echo ostsn=$ostsn npools=$npools
5701         if [[ $ostsn -gt $OSTCOUNT ]];  then
5702                 echo "request to use $ostsn OSTs in the pool, \
5703                         using max available OSTCOUNT=$OSTCOUNT"
5704                 ostsn=$OSTCOUNT
5705         fi
5706         for (( n=0; n < $npools; n++ )); do
5707                 p=${pool}$n
5708                 if ! $DELETE_OLD_POOLS; then
5709                         log "request to not delete old pools: $FSNAME.$p exist?"
5710                         if ! check_pool_not_exist $FSNAME.$p; then
5711                                 echo "Using existing $FSNAME.$p"
5712                                 $LCTL pool_list $FSNAME.$p
5713                                 continue
5714                         fi
5715                 fi
5716                 create_pool $FSNAME.$p $KEEP_POOLS ||
5717                         error "create_pool $FSNAME.$p failed"
5718
5719                 local first=$(( (n * ostsn) % OSTCOUNT ))
5720                 local last=$(( (first + ostsn - 1) % OSTCOUNT ))
5721                 if [[ $first -le $last ]]; then
5722                         pool_add_targets $p $first $last ||
5723                                 error "pool_add_targets $p $first $last failed"
5724                 else
5725                         pool_add_targets $p $first $(( OSTCOUNT - 1 )) ||
5726                                 error "pool_add_targets $p $first \
5727                                         $(( OSTCOUNT - 1 )) failed"
5728                         pool_add_targets $p 0 $last ||
5729                                 error "pool_add_targets $p 0 $last failed"
5730                 fi
5731         done
5732 }
5733
5734 set_pools_quota () {
5735         local u
5736         local o
5737         local p
5738         local i
5739         local j
5740
5741         [[ $ENABLE_QUOTA ]] || error "Required Pool Quotas: \
5742                 $POOLS_QUOTA_USERS_SET, but ENABLE_QUOTA not set!"
5743
5744         # POOLS_QUOTA_USERS_SET=
5745         #              "quota15_1:20M          -- for all of the found pools
5746         #               quota15_2:1G:gpool0
5747         #               quota15_3              -- for global limit only
5748         #               quota15_4:200M:gpool0
5749         #               quota15_4:200M:gpool1"
5750
5751         declare -a pq_userset=(${POOLS_QUOTA_USERS_SET="mpiuser"})
5752         declare -a pq_users
5753         declare -A pq_limits
5754
5755         for ((i=0; i<${#pq_userset[@]}; i++)); do
5756                 u=${pq_userset[i]%%:*}
5757                 o=""
5758                 # user gets no pool limits if
5759                 # POOLS_QUOTA_USERS_SET does not specify it
5760                 [[ ${pq_userset[i]} =~ : ]] && o=${pq_userset[i]##$u:}
5761                 pq_limits[$u]+=" $o"
5762         done
5763         pq_users=(${!pq_limits[@]})
5764
5765         declare -a opts
5766         local pool
5767
5768         for ((i=0; i<${#pq_users[@]}; i++)); do
5769                 u=${pq_users[i]}
5770                 # set to max limit (_u64)
5771                 $LFS setquota -u $u -B $((2**24 - 1))T $DIR
5772                 opts=(${pq_limits[$u]})
5773                 for ((j=0; j<${#opts[@]}; j++)); do
5774                         p=${opts[j]##*:}
5775                         o=${opts[j]%%:*}
5776                         # Set limit for all existing pools if
5777                         # no pool specified
5778                         if [ $p == $o ];  then
5779                                 p=$(list_pool $FSNAME | sed "s/$FSNAME.//")
5780                                 echo "No pool specified for $u,
5781                                         set limit $o for all existing pools"
5782                         fi
5783                         for pool in $p; do
5784                                 $LFS setquota -u $u -B $o --pool $pool $DIR ||
5785                                         error "setquota -u $u -B $o \
5786                                                 --pool $pool failed"
5787                         done
5788                 done
5789                 $LFS quota -uv $u --pool  $DIR
5790         done
5791 }
5792
5793 check_and_setup_lustre() {
5794         sanitize_parameters
5795         nfs_client_mode && return
5796         cifs_client_mode && return
5797
5798         local MOUNTED=$(mounted_lustre_filesystems)
5799
5800         local do_check=true
5801         # 1.
5802         # both MOUNT and MOUNT2 are not mounted
5803         if ! is_mounted $MOUNT && ! is_mounted $MOUNT2; then
5804                 [ "$REFORMAT" = "yes" ] && CLEANUP_DM_DEV=true formatall
5805                 # setupall mounts both MOUNT and MOUNT2 (if MOUNT_2 is set)
5806                 setupall
5807                 is_mounted $MOUNT || error "NAME=$NAME not mounted"
5808                 export I_MOUNTED=yes
5809                 do_check=false
5810         # 2.
5811         # MOUNT2 is mounted
5812         elif is_mounted $MOUNT2; then
5813                 # 3.
5814                 # MOUNT2 is mounted, while MOUNT_2 is not set
5815                 if ! [ "$MOUNT_2" ]; then
5816                         cleanup_mount $MOUNT2
5817                         export I_UMOUNTED2=yes
5818
5819                 # 4.
5820                 # MOUNT2 is mounted, MOUNT_2 is set
5821                 else
5822                         # FIXME: what to do if check_config failed?
5823                         # i.e. if:
5824                         # 1) remote client has mounted other Lustre fs ?
5825                         # 2) it has insane env ?
5826                         # let's try umount MOUNT2 on all clients and mount it
5827                         # again:
5828                         if ! check_config_clients $MOUNT2; then
5829                                 cleanup_mount $MOUNT2
5830                                 restore_mount $MOUNT2
5831                                 export I_MOUNTED2=yes
5832                         fi
5833                 fi
5834         # 5.
5835         # MOUNT is mounted MOUNT2 is not mounted
5836         elif [ "$MOUNT_2" ]; then
5837                 restore_mount $MOUNT2
5838                 export I_MOUNTED2=yes
5839         fi
5840
5841         if $do_check; then
5842                 # FIXME: what to do if check_config failed?
5843                 # i.e. if:
5844                 # 1) remote client has mounted other Lustre fs?
5845                 # 2) lustre is mounted on remote_clients atall ?
5846                 check_config_clients $MOUNT
5847                 init_facets_vars
5848                 init_param_vars
5849
5850                 set_default_debug_nodes $(comma_list $(nodes_list))
5851                 set_params_clients
5852         fi
5853
5854         if [ -z "$CLIENTONLY" -a $(lower $OSD_TRACK_DECLARES_LBUG) == 'yes' ]; then
5855                 local facets=""
5856                 [ "$(facet_fstype ost1)" = "ldiskfs" ] &&
5857                         facets="$(get_facets OST)"
5858                 [ "$(facet_fstype mds1)" = "ldiskfs" ] &&
5859                         facets="$facets,$(get_facets MDS)"
5860                 [ "$(facet_fstype mgs)" = "ldiskfs" ] &&
5861                         facets="$facets,mgs"
5862                 local nodes="$(facets_hosts ${facets})"
5863                 if [ -n "$nodes" ] ; then
5864                         do_nodes $nodes "$LCTL set_param \
5865                                  osd-ldiskfs.track_declares_assert=1 || true"
5866                 fi
5867         fi
5868
5869         if [ -n "$fs_STRIPEPARAMS" ]; then
5870                 setstripe_getstripe $MOUNT $fs_STRIPEPARAMS
5871         fi
5872         if $GSS_SK; then
5873                 set_flavor_all null
5874         elif $GSS; then
5875                 set_flavor_all $SEC
5876         fi
5877
5878         if $DELETE_OLD_POOLS; then
5879                 destroy_all_pools
5880         fi
5881         if [[ -n "$FS_POOL" ]]; then
5882                 create_pools $FS_POOL $FS_POOL_NOSTS
5883         fi
5884
5885         if [[ -n "$POOLS_QUOTA_USERS_SET" ]]; then
5886                 set_pools_quota
5887         fi
5888         if [ "$ONLY" == "setup" ]; then
5889                 exit 0
5890         fi
5891 }
5892
5893 restore_mount () {
5894         local clients=${CLIENTS:-$HOSTNAME}
5895         local mntpt=$1
5896
5897         zconf_mount_clients $clients $mntpt
5898 }
5899
5900 cleanup_mount () {
5901         local clients=${CLIENTS:-$HOSTNAME}
5902         local mntpt=$1
5903
5904         zconf_umount_clients $clients $mntpt
5905 }
5906
5907 cleanup_and_setup_lustre() {
5908         if [ "$ONLY" == "cleanup" -o "`mount | grep $MOUNT`" ]; then
5909                 lctl set_param debug=0 || true
5910                 cleanupall
5911                 if [ "$ONLY" == "cleanup" ]; then
5912                         exit 0
5913                 fi
5914         fi
5915         check_and_setup_lustre
5916 }
5917
5918 # Run e2fsck on MDT or OST device.
5919 run_e2fsck() {
5920         local node=$1
5921         local target_dev=$2
5922         local extra_opts=$3
5923         local cmd="$E2FSCK -d -v -t -t -f $extra_opts $target_dev"
5924         local log=$TMP/e2fsck.log
5925         local rc=0
5926
5927         # turn on pfsck if it is supported
5928         do_node $node $E2FSCK -h 2>&1 | grep -qw -- -m && cmd+=" -m8"
5929         echo $cmd
5930         do_node $node $cmd 2>&1 | tee $log
5931         rc=${PIPESTATUS[0]}
5932         if [ -n "$(grep "DNE mode isn't supported" $log)" ]; then
5933                 rm -f $log
5934                 if [ $MDSCOUNT -gt 1 ]; then
5935                         skip_noexit "DNE mode isn't supported!"
5936                         cleanupall
5937                         exit_status
5938                 else
5939                         error "It's not DNE mode."
5940                 fi
5941         fi
5942         rm -f $log
5943
5944         [ $rc -le $FSCK_MAX_ERR ] ||
5945                 error "$cmd returned $rc, should be <= $FSCK_MAX_ERR"
5946
5947         return 0
5948 }
5949
5950 #
5951 # Run resize2fs on MDT or OST device.
5952 #
5953 run_resize2fs() {
5954         local facet=$1
5955         local device=$2
5956         local size=$3
5957         shift 3
5958         local opts="$@"
5959
5960         do_facet $facet "$RESIZE2FS $opts $device $size"
5961 }
5962
5963 # verify a directory is shared among nodes.
5964 check_shared_dir() {
5965         local dir=$1
5966         local list=${2:-$(comma_list $(nodes_list))}
5967
5968         [ -z "$dir" ] && return 1
5969         do_rpc_nodes "$list" check_logdir $dir
5970         check_write_access $dir "$list" || return 1
5971         return 0
5972 }
5973
5974 run_lfsck() {
5975         do_nodes $(comma_list $(mdts_nodes) $(osts_nodes)) \
5976                 $LCTL set_param printk=+lfsck
5977         do_facet $SINGLEMDS "$LCTL lfsck_start -M $FSNAME-MDT0000 -r -A -t all"
5978
5979         for k in $(seq $MDSCOUNT); do
5980                 # wait up to 10+1 minutes for LFSCK to complete
5981                 wait_update_facet --verbose mds${k} "$LCTL get_param -n \
5982                         mdd.$(facet_svc mds${k}).lfsck_layout |
5983                         awk '/^status/ { print \\\$2 }'" "completed" 600 ||
5984                         error "MDS${k} layout isn't the expected 'completed'"
5985                 wait_update_facet --verbose mds${k} "$LCTL get_param -n \
5986                         mdd.$(facet_svc mds${k}).lfsck_namespace |
5987                         awk '/^status/ { print \\\$2 }'" "completed" 60 ||
5988                         error "MDS${k} namespace isn't the expected 'completed'"
5989         done
5990         local rep_mdt=$(do_nodes $(comma_list $(mdts_nodes)) \
5991                         $LCTL get_param -n mdd.$FSNAME-*.lfsck_* |
5992                         awk '/repaired/ { print $2 }' | calc_sum)
5993         local rep_ost=$(do_nodes $(comma_list $(osts_nodes)) \
5994                         $LCTL get_param -n obdfilter.$FSNAME-*.lfsck_* |
5995                         awk '/repaired/ { print $2 }' | calc_sum)
5996         local repaired=$((rep_mdt + rep_ost))
5997         [ $repaired -eq 0 ] ||
5998                 error "lfsck repaired $rep_mdt MDT and $rep_ost OST errors"
5999 }
6000
6001 dump_file_contents() {
6002         local nodes=$1
6003         local dir=$2
6004         local logname=$3
6005         local node
6006
6007         if [ -z "$nodes" -o -z "$dir" -o -z "$logname" ]; then
6008                 error_noexit false \
6009                         "Invalid parameters for dump_file_contents()"
6010                 return 1
6011         fi
6012         for node in ${nodes}; do
6013                 do_node $node "for i in \\\$(find $dir -type f); do
6014                                 echo ====\\\${i}=======================;
6015                                 cat \\\${i};
6016                                 done" >> ${logname}.${node}.log
6017         done
6018 }
6019
6020 dump_command_output() {
6021         local nodes=$1
6022         local cmd=$2
6023         local logname=$3
6024         local node
6025
6026         if [ -z "$nodes" -o -z "$cmd" -o -z "$logname" ]; then
6027                 error_noexit false \
6028                         "Invalid parameters for dump_command_output()"
6029                 return 1
6030         fi
6031
6032         for node in ${nodes}; do
6033                 do_node $node "echo ====${cmd}=======================;
6034                                 $cmd" >> ${logname}.${node}.log
6035         done
6036 }
6037
6038 log_zfs_info() {
6039         local logname=$1
6040
6041         # dump file contents from /proc/spl in case of zfs test
6042         if [ "$(facet_fstype ost1)" = "zfs" ]; then
6043                 dump_file_contents "$(osts_nodes)" "/proc/spl" "${logname}"
6044                 dump_command_output \
6045                         "$(osts_nodes)" "zpool events -v" "${logname}"
6046         fi
6047
6048         if [ "$(facet_fstype $SINGLEMDS)" = "zfs" ]; then
6049                 dump_file_contents "$(mdts_nodes)" "/proc/spl" "${logname}"
6050                 dump_command_output \
6051                         "$(mdts_nodes)" "zpool events -v" "${logname}"
6052         fi
6053 }
6054
6055 check_and_cleanup_lustre() {
6056         if [ "$LFSCK_ALWAYS" = "yes" -a "$TESTSUITE" != "sanity-lfsck" -a \
6057              "$TESTSUITE" != "sanity-scrub" ]; then
6058                 run_lfsck
6059         fi
6060
6061         if is_mounted $MOUNT; then
6062                 if $DO_CLEANUP; then
6063                         [ -n "$DIR" ] && rm -rf $DIR/[Rdfs][0-9]* ||
6064                                 error "remove sub-test dirs failed"
6065                 else
6066                         echo "skip cleanup"
6067                 fi
6068                 [ "$ENABLE_QUOTA" ] && restore_quota || true
6069         fi
6070
6071         if [ "$I_UMOUNTED2" = "yes" ]; then
6072                 restore_mount $MOUNT2 || error "restore $MOUNT2 failed"
6073         fi
6074
6075         if [ "$I_MOUNTED2" = "yes" ]; then
6076                 cleanup_mount $MOUNT2
6077         fi
6078
6079         if [[ "$I_MOUNTED" = "yes" ]] && ! $AUSTER_CLEANUP; then
6080                 cleanupall -f || error "cleanup failed"
6081                 unset I_MOUNTED
6082         fi
6083 }
6084
6085 #######
6086 # General functions
6087
6088 wait_for_function () {
6089         local quiet=""
6090
6091         # suppress fn both stderr and stdout
6092         if [ "$1" = "--quiet" ]; then
6093                 shift
6094                 quiet=" > /dev/null 2>&1"
6095         fi
6096
6097         local fn=$1
6098         local max=${2:-900}
6099         local sleep=${3:-5}
6100
6101         local wait=0
6102
6103         while true; do
6104
6105                 eval $fn $quiet && return 0
6106
6107                 [ $wait -lt $max ] || return 1
6108                 echo waiting $fn, $((max - wait)) secs left ...
6109                 wait=$((wait + sleep))
6110                 [ $wait -gt $max ] && ((sleep -= wait - max))
6111                 sleep $sleep
6112         done
6113 }
6114
6115 check_network() {
6116         local host=$1
6117         local max=$2
6118         local sleep=${3:-5}
6119
6120         [ "$host" = "$HOSTNAME" ] && return 0
6121
6122         if ! wait_for_function --quiet "ping -c 1 -w 3 $host" $max $sleep; then
6123                 echo "$(date +'%H:%M:%S (%s)') waited for $host network ${max}s"
6124                 exit 1
6125         fi
6126 }
6127
6128 no_dsh() {
6129         shift
6130         eval "$@"
6131 }
6132
6133 # Convert a space-delimited list to a comma-delimited list.  If the input is
6134 # only whitespace, ensure the output is empty (i.e. "") so [ -n $list ] works
6135 comma_list() {
6136         # echo is used to convert newlines to spaces, since it doesn't
6137         # introduce a trailing space as using "tr '\n' ' '" does
6138         echo $(tr -s " " "\n" <<< $* | sort -b -u) | tr ' ' ','
6139 }
6140
6141 list_member () {
6142         local list=$1
6143         local item=$2
6144         echo $list | grep -qw $item
6145 }
6146
6147 # list, excluded are the comma separated lists
6148 exclude_items_from_list () {
6149         local list=$1
6150         local excluded=$2
6151         local item
6152
6153         list=${list//,/ }
6154         for item in ${excluded//,/ }; do
6155                 list=$(echo " $list " | sed -re "s/\s+$item\s+/ /g")
6156         done
6157         echo $(comma_list $list)
6158 }
6159
6160 # list, expand  are the comma separated lists
6161 expand_list () {
6162         local list=${1//,/ }
6163         local expand=${2//,/ }
6164         local expanded=
6165
6166         expanded=$(for i in $list $expand; do echo $i; done | sort -u)
6167         echo $(comma_list $expanded)
6168 }
6169
6170 testslist_filter () {
6171         local script=$LUSTRE/tests/${TESTSUITE}.sh
6172
6173         [ -f $script ] || return 0
6174
6175         local start_at=$START_AT
6176         local stop_at=$STOP_AT
6177
6178         local var=${TESTSUITE//-/_}_START_AT
6179         [ x"${!var}" != x ] && start_at=${!var}
6180         var=${TESTSUITE//-/_}_STOP_AT
6181         [ x"${!var}" != x ] && stop_at=${!var}
6182
6183         sed -n 's/^test_\([^ (]*\).*/\1/p' $script |
6184         awk ' BEGIN { if ("'${start_at:-0}'" != 0) flag = 1 }
6185             /^'${start_at}'$/ {flag = 0}
6186             {if (flag == 1) print $0}
6187             /^'${stop_at}'$/ { flag = 1 }'
6188 }
6189
6190 absolute_path() {
6191         (cd `dirname $1`; echo $PWD/`basename $1`)
6192 }
6193
6194 get_facets () {
6195         local types=${1:-"OST MDS MGS"}
6196
6197         local list=""
6198
6199         for entry in $types; do
6200                 local name=$(echo $entry | tr "[:upper:]" "[:lower:]")
6201                 local type=$(echo $entry | tr "[:lower:]" "[:upper:]")
6202
6203                 case $type in
6204                         MGS ) list="$list $name";;
6205                         MDS|OST|AGT ) local count=${type}COUNT
6206                                 for ((i=1; i<=${!count}; i++)) do
6207                                         list="$list ${name}$i"
6208                                 done;;
6209                         * ) error "Invalid facet type"
6210                                 exit 1;;
6211                 esac
6212         done
6213         echo $(comma_list $list)
6214 }
6215
6216 ##################################
6217 # Adaptive Timeouts funcs
6218
6219 at_is_enabled() {
6220         # only check mds, we assume at_max is the same on all nodes
6221         local at_max=$(do_facet $SINGLEMDS "lctl get_param -n at_max")
6222
6223         if [ $at_max -eq 0 ]; then
6224                 return 1
6225         else
6226                 return 0
6227         fi
6228 }
6229
6230 at_get() {
6231         local facet=$1
6232         local at=$2
6233
6234         # suppose that all ost-s have the same $at value set
6235         [ $facet != "ost" ] || facet=ost1
6236
6237         do_facet $facet "lctl get_param -n $at"
6238 }
6239
6240 at_max_get() {
6241         at_get $1 at_max
6242 }
6243
6244 at_max_set() {
6245         local at_max=$1
6246         shift
6247
6248         local facet
6249         local hosts
6250
6251         for facet in "$@"; do
6252                 if [ $facet == "ost" ]; then
6253                         facet=$(get_facets OST)
6254                 elif [ $facet == "mds" ]; then
6255                         facet=$(get_facets MDS)
6256                 fi
6257                 hosts=$(expand_list $hosts $(facets_hosts $facet))
6258         done
6259
6260         do_nodes $hosts lctl set_param at_max=$at_max
6261 }
6262
6263 ##################################
6264 # OBD_FAIL funcs
6265
6266 drop_request() {
6267 # OBD_FAIL_MDS_ALL_REQUEST_NET
6268         RC=0
6269         do_facet $SINGLEMDS lctl set_param fail_val=0 fail_loc=0x123
6270         do_facet client "$1" || RC=$?
6271         do_facet $SINGLEMDS lctl set_param fail_loc=0
6272         return $RC
6273 }
6274
6275 drop_reply() {
6276 # OBD_FAIL_MDS_ALL_REPLY_NET
6277         RC=0
6278         do_facet $SINGLEMDS $LCTL set_param fail_loc=0x122
6279         eval "$@" || RC=$?
6280         do_facet $SINGLEMDS $LCTL set_param fail_loc=0
6281         return $RC
6282 }
6283
6284 drop_reint_reply() {
6285 # OBD_FAIL_MDS_REINT_NET_REP
6286         RC=0
6287         do_facet $SINGLEMDS $LCTL set_param fail_loc=0x119
6288         eval "$@" || RC=$?
6289         do_facet $SINGLEMDS $LCTL set_param fail_loc=0
6290         return $RC
6291 }
6292
6293 drop_update_reply() {
6294 # OBD_FAIL_OUT_UPDATE_NET_REP
6295         local index=$1
6296         shift 1
6297         RC=0
6298         do_facet mds${index} lctl set_param fail_loc=0x1701
6299         do_facet client "$@" || RC=$?
6300         do_facet mds${index} lctl set_param fail_loc=0
6301         return $RC
6302 }
6303
6304 pause_bulk() {
6305 #define OBD_FAIL_OST_BRW_PAUSE_BULK      0x214
6306         RC=0
6307
6308         local timeout=${2:-0}
6309         # default is (obd_timeout / 4) if unspecified
6310         echo "timeout is $timeout/$2"
6311         do_facet ost1 lctl set_param fail_val=$timeout fail_loc=0x80000214
6312         do_facet client "$1" || RC=$?
6313         do_facet client "sync"
6314         do_facet ost1 lctl set_param fail_loc=0
6315         return $RC
6316 }
6317
6318 drop_ldlm_cancel() {
6319 #define OBD_FAIL_LDLM_CANCEL_NET                        0x304
6320         local RC=0
6321         local list=$(comma_list $(mdts_nodes) $(osts_nodes))
6322         do_nodes $list lctl set_param fail_loc=0x304
6323
6324         do_facet client "$@" || RC=$?
6325
6326         do_nodes $list lctl set_param fail_loc=0
6327         return $RC
6328 }
6329
6330 drop_bl_callback_once() {
6331         local rc=0
6332         do_facet client lctl set_param ldlm.namespaces.*.early_lock_cancel=0
6333 #define OBD_FAIL_LDLM_BL_CALLBACK_NET                   0x305
6334         do_facet client lctl set_param fail_loc=0x80000305
6335         do_facet client "$@" || rc=$?
6336         do_facet client lctl set_param fail_loc=0
6337         do_facet client lctl set_param fail_val=0
6338         do_facet client lctl set_param ldlm.namespaces.*.early_lock_cancel=1
6339         return $rc
6340 }
6341
6342 drop_bl_callback() {
6343         rc=0
6344         do_facet client lctl set_param ldlm.namespaces.*.early_lock_cancel=0
6345 #define OBD_FAIL_LDLM_BL_CALLBACK_NET                   0x305
6346         do_facet client lctl set_param fail_loc=0x305
6347         do_facet client "$@" || rc=$?
6348         do_facet client lctl set_param fail_loc=0
6349         do_facet client lctl set_param fail_val=0
6350         do_facet client lctl set_param ldlm.namespaces.*.early_lock_cancel=1
6351         return $rc
6352 }
6353
6354 drop_mdt_ldlm_reply() {
6355 #define OBD_FAIL_MDS_LDLM_REPLY_NET     0x157
6356         RC=0
6357         local list=$(comma_list $(mdts_nodes))
6358
6359         do_nodes $list lctl set_param fail_loc=0x157
6360
6361         do_facet client "$@" || RC=$?
6362
6363         do_nodes $list lctl set_param fail_loc=0
6364         return $RC
6365 }
6366
6367 drop_mdt_ldlm_reply_once() {
6368 #define OBD_FAIL_MDS_LDLM_REPLY_NET     0x157
6369         RC=0
6370         local list=$(comma_list $(mdts_nodes))
6371
6372         do_nodes $list lctl set_param fail_loc=0x80000157
6373
6374         do_facet client "$@" || RC=$?
6375
6376         do_nodes $list lctl set_param fail_loc=0
6377         return $RC
6378 }
6379
6380 clear_failloc() {
6381         facet=$1
6382         pause=$2
6383         sleep $pause
6384         echo "clearing fail_loc on $facet"
6385         do_facet $facet "lctl set_param fail_loc=0 2>/dev/null || true"
6386 }
6387
6388 set_nodes_failloc () {
6389         local fv=${3:-0}
6390         do_nodes $(comma_list $1)  lctl set_param fail_val=$fv fail_loc=$2
6391 }
6392
6393 cancel_lru_locks() {
6394         #$LCTL mark "cancel_lru_locks $1 start"
6395         $LCTL set_param -n ldlm.namespaces.*$1*.lru_size=clear
6396         $LCTL get_param ldlm.namespaces.*$1*.lock_unused_count | grep -v '=0'
6397         #$LCTL mark "cancel_lru_locks $1 stop"
6398 }
6399
6400 default_lru_size()
6401 {
6402         local nr_cpu=$(grep -c "processor" /proc/cpuinfo)
6403
6404         echo $((100 * nr_cpu))
6405 }
6406
6407 lru_resize_enable()
6408 {
6409         lctl set_param ldlm.namespaces.*$1*.lru_size=0
6410 }
6411
6412 lru_resize_disable()
6413 {
6414         local dev=${1}
6415         local lru_size=${2:-$(default_lru_size)}
6416
6417         $LCTL set_param ldlm.namespaces.*$dev*.lru_size=$lru_size
6418 }
6419
6420 flock_is_enabled()
6421 {
6422         local mountpath=${1:-$MOUNT}
6423         local RC=0
6424
6425         [ -z "$(mount | grep "$mountpath .*flock" | grep -v noflock)" ] && RC=1
6426         return $RC
6427 }
6428
6429 pgcache_empty() {
6430         local FILE
6431
6432         for FILE in `lctl get_param -N "llite.*.dump_page_cache"`; do
6433                 if [ `lctl get_param -n $FILE | wc -l` -gt 1 ]; then
6434                         echo there is still data in page cache $FILE ?
6435                         lctl get_param -n $FILE
6436                         return 1
6437                 fi
6438         done
6439         return 0
6440 }
6441
6442 debugsave() {
6443         DEBUGSAVE="$(lctl get_param -n debug)"
6444         DEBUGSAVE_SERVER=$(do_facet $SINGLEMDS "$LCTL get_param -n debug")
6445 }
6446
6447 debugrestore() {
6448         [ -n "$DEBUGSAVE" ] &&
6449                 do_nodes $CLIENTS "$LCTL set_param debug=\\\"${DEBUGSAVE}\\\""||
6450                 true
6451         DEBUGSAVE=""
6452
6453         [ -n "$DEBUGSAVE_SERVER" ] &&
6454                 do_nodes $(comma_list $(all_server_nodes)) \
6455                          "$LCTL set_param debug=\\\"${DEBUGSAVE_SERVER}\\\"" ||
6456                          true
6457         DEBUGSAVE_SERVER=""
6458 }
6459
6460 debug_size_save() {
6461         DEBUG_SIZE_SAVED="$(lctl get_param -n debug_mb)"
6462 }
6463
6464 debug_size_restore() {
6465         [ -n "$DEBUG_SIZE_SAVED" ] &&
6466                 do_nodes $(comma_list $(nodes_list)) "$LCTL set_param debug_mb=$DEBUG_SIZE_SAVED"
6467         DEBUG_SIZE_SAVED=""
6468 }
6469
6470 start_full_debug_logging() {
6471         debugsave
6472         debug_size_save
6473
6474         local fulldebug=-1
6475         local debug_size=150
6476         local nodes=$(comma_list $(nodes_list))
6477
6478         do_nodes $nodes "$LCTL set_param debug=$fulldebug debug_mb=$debug_size"
6479 }
6480
6481 stop_full_debug_logging() {
6482         debug_size_restore
6483         debugrestore
6484 }
6485
6486 # prints bash call stack
6487 print_stack_trace() {
6488         local skip=${1:-1}
6489         echo "  Trace dump:"
6490         for (( i=$skip; i < ${#BASH_LINENO[*]} ; i++ )) ; do
6491                 local src=${BASH_SOURCE[$i]}
6492                 local lineno=${BASH_LINENO[$i-1]}
6493                 local funcname=${FUNCNAME[$i]}
6494                 echo "  = $src:$lineno:$funcname()"
6495         done
6496 }
6497
6498 report_error() {
6499         local TYPE=${TYPE:-"FAIL"}
6500
6501         local dump=true
6502         # do not dump logs if $1=false
6503         if [ "x$1" = "xfalse" ]; then
6504                 shift
6505                 dump=false
6506         fi
6507
6508         log " ${TESTSUITE} ${TESTNAME}: @@@@@@ ${TYPE}: $* "
6509         (print_stack_trace 2) >&2
6510         mkdir -p $LOGDIR
6511         # We need to dump the logs on all nodes
6512         if $dump; then
6513                 gather_logs $(comma_list $(nodes_list))
6514         fi
6515
6516         debugrestore
6517         [ "$TESTSUITELOG" ] &&
6518                 echo "$TESTSUITE: $TYPE: $TESTNAME $*" >> $TESTSUITELOG
6519         if [ -z "$*" ]; then
6520                 echo "error() without useful message, please fix" > $LOGDIR/err
6521         else
6522                 if [[ `echo $TYPE | grep ^IGNORE` ]]; then
6523                         echo "$@" > $LOGDIR/ignore
6524                 else
6525                         echo "$@" > $LOGDIR/err
6526                 fi
6527         fi
6528
6529         # cleanup the env for failed tests
6530         reset_fail_loc
6531 }
6532
6533 ##################################
6534 # Test interface
6535 ##################################
6536
6537 # usage: stack_trap arg sigspec
6538 #
6539 # stack_trap() behaves like bash's built-in trap, except that it "stacks" the
6540 # command "arg" on top of previously defined commands for "sigspec" instead
6541 # of overwriting them.
6542 # stacked traps are executed in reverse order of their registration
6543 #
6544 # arg and sigspec have the same meaning as in man (1) trap
6545 stack_trap()
6546 {
6547         local arg="$1"
6548         local sigspec="${2:-EXIT}"
6549
6550         # Use "trap -p" to get the quoting right
6551         local old_trap="$(trap -p "$sigspec")"
6552         # Append ";" and remove the leading "trap -- '" added by "trap -p"
6553         old_trap="${old_trap:+"; ${old_trap#trap -- \'}"}"
6554
6555         # Once again, use "trap -p" to get the quoting right
6556         local new_trap="$(trap -- "$arg" "$sigspec"
6557                           trap -p "$sigspec"
6558                           trap -- '' "$sigspec")"
6559
6560         # Remove the trailing "' $sigspec" part added by "trap -p" and merge
6561         #
6562         # The resulting string should be safe to "eval" as it is (supposedly
6563         # correctly) quoted by "trap -p"
6564         eval "${new_trap%\' $sigspec}${old_trap:-"' $sigspec"}"
6565 }
6566
6567 error_noexit() {
6568         report_error "$@"
6569 }
6570
6571 exit_status () {
6572         local status=0
6573         local logs="$TESTSUITELOG $1"
6574
6575         for log in $logs; do
6576                 if [ -f "$log" ]; then
6577                         grep -qw FAIL $log && status=1
6578                 fi
6579         done
6580
6581         exit $status
6582 }
6583
6584 error() {
6585         report_error "$@"
6586         exit 1
6587 }
6588
6589 error_exit() {
6590         report_error "$@"
6591         exit 1
6592 }
6593
6594 # use only if we are ignoring failures for this test, bugno required.
6595 # (like ALWAYS_EXCEPT, but run the test and ignore the results.)
6596 # e.g. error_ignore bz5494 "your message" or
6597 # error_ignore LU-5494 "your message"
6598 error_ignore() {
6599         local TYPE="IGNORE ($1)"
6600         shift
6601         report_error "$@"
6602 }
6603
6604 error_and_remount() {
6605         report_error "$@"
6606         remount_client $MOUNT
6607         exit 1
6608 }
6609
6610 # Throw an error if it's not running in vm - usually for performance
6611 # verification
6612 error_not_in_vm() {
6613         local virt=$(running_in_vm)
6614         if [[ -n "$virt" ]]; then
6615                 echo "running in VM '$virt', ignore error"
6616                 error_ignore env=$virt "$@"
6617         else
6618                 error "$@"
6619         fi
6620 }
6621
6622 #
6623 # Function: skip_env()
6624 # Purpose:  to skip a test during developer testing because some tool
6625 #           is missing, but fail the test in release testing because the test
6626 #           environment is not configured properly".
6627 #
6628 skip_env () {
6629         $FAIL_ON_SKIP_ENV && error false "$@" || skip "$@"
6630 }
6631
6632 skip_noexit() {
6633         echo
6634         log " SKIP: $TESTSUITE $TESTNAME $*"
6635
6636         if [[ -n "$ALWAYS_SKIPPED" ]]; then
6637                 skip_logged $TESTNAME "$@"
6638         else
6639                 mkdir -p $LOGDIR
6640                 echo "$@" > $LOGDIR/skip
6641         fi
6642
6643         [[ -n "$TESTSUITELOG" ]] &&
6644                 echo "$TESTSUITE: SKIP: $TESTNAME $*" >> $TESTSUITELOG || true
6645         unset TESTNAME
6646 }
6647
6648 skip() {
6649         skip_noexit "$@"
6650         exit 0
6651 }
6652
6653 #
6654 # For interop testing treate EOPNOTSUPP as success
6655 # and skip
6656 #
6657 skip_eopnotsupp() {
6658         local retstr=$@
6659
6660         echo $retstr | awk -F'|' '{print $1}' |
6661                 grep -E unsupported\|"(Operation not supported)"
6662         (( $? == 0 )) || error "$retstr"
6663         skip $retstr
6664 }
6665
6666 # Add a list of tests to ALWAYS_EXCEPT due to an issue.
6667 # Usage: always_except LU-4815 23 42q ...
6668 #
6669 function always_except() {
6670         local issue="${1:-}" # single jira style issue ("LU-4815")
6671         local test_num
6672
6673         shift
6674
6675         if ! [[ "$issue" =~ ^[[:upper:]]+-[[:digit:]]+$ ]]; then
6676                 error "always_except: invalid issue '$issue' for tests '$*'"
6677         fi
6678
6679         for test_num in "$@"; do
6680                 ALWAYS_EXCEPT+=" $test_num"
6681         done
6682 }
6683
6684 build_test_filter() {
6685         EXCEPT="$EXCEPT $(testslist_filter)"
6686
6687         # allow test numbers separated by '+', or ',', in addition to ' '
6688         # to avoid issues with multiple arguments handling by shell/autotest
6689         for O in ${ONLY//[+,]/ }; do
6690                 if [[ $O =~ [0-9]*-[0-9]* ]]; then
6691                         for ((num=${O%-[0-9]*}; num <= ${O#[0-9]*-}; num++)); do
6692                                 eval ONLY_$num=true
6693                         done
6694                 else
6695                         eval ONLY_${O}=true
6696                 fi
6697         done
6698
6699         [[ -z "$EXCEPT$ALWAYS_EXCEPT" ]] ||
6700                 log "excepting tests: $(echo $EXCEPT $ALWAYS_EXCEPT)"
6701         [[ -z "$EXCEPT_SLOW" ]] ||
6702                 log "skipping tests SLOW=no: $(echo $EXCEPT_SLOW)"
6703         for E in ${EXCEPT//[+,]/ }; do
6704                 eval EXCEPT_${E}=true
6705         done
6706         for E in ${ALWAYS_EXCEPT//[+,]/ }; do
6707                 eval EXCEPT_ALWAYS_${E}=true
6708         done
6709         for E in ${EXCEPT_SLOW//[+,]/ }; do
6710                 eval EXCEPT_SLOW_${E}=true
6711         done
6712         for G in ${GRANT_CHECK_LIST//[+,]/ }; do
6713                 eval GCHECK_ONLY_${G}=true
6714         done
6715 }
6716
6717 basetest() {
6718         if [[ $1 = [a-z]* ]]; then
6719                 echo $1
6720         else
6721                 echo ${1%%[a-zA-Z]*}
6722         fi
6723 }
6724
6725 # print a newline if the last test was skipped
6726 export LAST_SKIPPED=
6727 export ALWAYS_SKIPPED=
6728 #
6729 # Main entry into test-framework. This is called with the number and
6730 # description of a test. The number is used to find the function to run
6731 # the test using "test_$name".
6732 #
6733 # This supports a variety of methods of specifying specific test to
6734 # run or not run:
6735 # - ONLY= env variable with space-separated list of test numbers to run
6736 # - EXCEPT= env variable with space-separated list of test numbers to exclude
6737 #
6738 run_test() {
6739         assert_DIR
6740         local testnum=$1
6741         local testmsg=$2
6742         export base=$(basetest $testnum)
6743         export TESTNAME=test_$testnum
6744         LAST_SKIPPED=
6745         ALWAYS_SKIPPED=
6746
6747         # Check the EXCEPT, ALWAYS_EXCEPT and SLOW lists to see if we
6748         # need to skip the current test. If so, set the ALWAYS_SKIPPED flag.
6749         local isexcept=EXCEPT_$testnum
6750         local isexcept_base=EXCEPT_$base
6751         if [ ${!isexcept}x != x ]; then
6752                 ALWAYS_SKIPPED="y"
6753                 skip_message="skipping excluded test $testnum"
6754         elif [ ${!isexcept_base}x != x ]; then
6755                 ALWAYS_SKIPPED="y"
6756                 skip_message="skipping excluded test $testnum (base $base)"
6757         fi
6758
6759         isexcept=EXCEPT_ALWAYS_$testnum
6760         isexcept_base=EXCEPT_ALWAYS_$base
6761         if [ ${!isexcept}x != x ]; then
6762                 ALWAYS_SKIPPED="y"
6763                 skip_message="skipping ALWAYS excluded test $testnum"
6764         elif [ ${!isexcept_base}x != x ]; then
6765                 ALWAYS_SKIPPED="y"
6766                 skip_message="skipping ALWAYS excluded test $testnum (base $base)"
6767         fi
6768
6769         isexcept=EXCEPT_SLOW_$testnum
6770         isexcept_base=EXCEPT_SLOW_$base
6771         if [ ${!isexcept}x != x ]; then
6772                 ALWAYS_SKIPPED="y"
6773                 skip_message="skipping SLOW test $testnum"
6774         elif [ ${!isexcept_base}x != x ]; then
6775                 ALWAYS_SKIPPED="y"
6776                 skip_message="skipping SLOW test $testnum (base $base)"
6777         fi
6778
6779         # If there are tests on the ONLY list, check if the current test
6780         # is on that list and, if so, check if the test is to be skipped
6781         # and if we are supposed to honor the skip lists.
6782         if [ -n "$ONLY" ]; then
6783                 local isonly=ONLY_$testnum
6784                 local isonly_base=ONLY_$base
6785                 if [[ ${!isonly}x != x || ${!isonly_base}x != x ]]; then
6786
6787                         if [[ -n "$ALWAYS_SKIPPED" &&
6788                                         -n "$HONOR_EXCEPT" ]]; then
6789                                 LAST_SKIPPED="y"
6790                                 skip_noexit "$skip_message"
6791                                 return 0
6792                         else
6793                                 [ -n "$LAST_SKIPPED" ] &&
6794                                         echo "" && LAST_SKIPPED=
6795                                 ALWAYS_SKIPPED=
6796                                 run_one_logged $testnum "$testmsg"
6797                                 return $?
6798                         fi
6799
6800                 else
6801                         LAST_SKIPPED="y"
6802                         return 0
6803                 fi
6804         fi
6805
6806         if [ -n "$ALWAYS_SKIPPED" ]; then
6807                 LAST_SKIPPED="y"
6808                 skip_noexit "$skip_message"
6809                 return 0
6810         else
6811                 run_one_logged $testnum "$testmsg"
6812                 return $?
6813         fi
6814 }
6815
6816 log() {
6817         echo "$*" >&2
6818         load_module ../libcfs/libcfs/libcfs
6819
6820         local MSG="$*"
6821         # Get rid of '
6822         MSG=${MSG//\'/\\\'}
6823         MSG=${MSG//\*/\\\*}
6824         MSG=${MSG//\(/\\\(}
6825         MSG=${MSG//\)/\\\)}
6826         MSG=${MSG//\;/\\\;}
6827         MSG=${MSG//\|/\\\|}
6828         MSG=${MSG//\>/\\\>}
6829         MSG=${MSG//\</\\\<}
6830         MSG=${MSG//\//\\\/}
6831         do_nodes $(comma_list $(nodes_list)) $LCTL mark "$MSG" 2> /dev/null || true
6832 }
6833
6834 trace() {
6835         log "STARTING: $*"
6836         strace -o $TMP/$1.strace -ttt $*
6837         RC=$?
6838         log "FINISHED: $*: rc $RC"
6839         return 1
6840 }
6841
6842 complete () {
6843         local duration=$1
6844
6845         banner test complete, duration $duration sec
6846         [ -f "$TESTSUITELOG" ] && egrep .FAIL $TESTSUITELOG || true
6847         echo duration $duration >>$TESTSUITELOG
6848 }
6849
6850 pass() {
6851         # Set TEST_STATUS here. It will be used for logging the result.
6852         TEST_STATUS="PASS"
6853
6854         if [[ -f $LOGDIR/err ]]; then
6855                 TEST_STATUS="FAIL"
6856         elif [[ -f $LOGDIR/skip ]]; then
6857                 TEST_STATUS="SKIP"
6858         fi
6859         echo "$TEST_STATUS $*" 2>&1 | tee -a $TESTSUITELOG
6860 }
6861
6862 check_mds() {
6863         local FFREE=$(do_node $SINGLEMDS \
6864         lctl get_param -n osd*.*MDT*.filesfree | calc_sum)
6865         local FTOTAL=$(do_node $SINGLEMDS \
6866         lctl get_param -n osd*.*MDT*.filestotal | calc_sum)
6867
6868         [ $FFREE -ge $FTOTAL ] && error "files free $FFREE > total $FTOTAL" ||
6869                 true
6870 }
6871
6872 reset_fail_loc () {
6873         #echo -n "Resetting fail_loc on all nodes..."
6874         do_nodes --quiet $(comma_list $(nodes_list)) \
6875                 "lctl set_param -n fail_loc=0 fail_val=0 2>/dev/null" || true
6876         #echo done.
6877 }
6878
6879
6880 #
6881 # Log a message (on all nodes) padded with "=" before and after.
6882 # Also appends a timestamp and prepends the testsuite name.
6883 #
6884
6885 # ======================================================== 15:06:12 (1624050372)
6886 EQUALS="========================================================"
6887 banner() {
6888         msg="== ${TESTSUITE} $*"
6889         last=${msg: -1:1}
6890         [[ $last != "=" && $last != " " ]] && msg="$msg "
6891         msg=$(printf '%s%.*s'  "$msg"  $((${#EQUALS} - ${#msg})) $EQUALS )
6892         # always include at least == after the message
6893         log "$msg== $(date +"%H:%M:%S (%s)")"
6894 }
6895
6896 check_dmesg_for_errors() {
6897         local res
6898         local errors="VFS: Busy inodes after unmount of\|\
6899 ldiskfs_check_descriptors: Checksum for group 0 failed\|\
6900 group descriptors corrupted"
6901
6902         res=$(do_nodes -q $(comma_list $(nodes_list)) "dmesg" | grep "$errors")
6903         [ -z "$res" ] && return 0
6904         echo "Kernel error detected: $res"
6905         return 1
6906 }
6907
6908 #
6909 # Run a single test function and cleanup after it.
6910 #
6911 # This function should be run in a subshell so the test func can
6912 # exit() without stopping the whole script.
6913 #
6914 run_one() {
6915         local testnum=$1
6916         local testmsg="$2"
6917         local SAVE_UMASK=`umask`
6918         umask 0022
6919
6920         if ! grep -q $DIR /proc/mounts; then
6921                 $SETUP
6922         fi
6923
6924         banner "test $testnum: $testmsg"
6925         test_${testnum} || error "test_$testnum failed with $?"
6926         cd $SAVE_PWD
6927         reset_fail_loc
6928         check_grant ${testnum} || error "check_grant $testnum failed with $?"
6929         check_node_health
6930         check_dmesg_for_errors || error "Error in dmesg detected"
6931         if [ "$PARALLEL" != "yes" ]; then
6932                 ps auxww | grep -v grep | grep -q "multiop " &&
6933                                         error "multiop still running"
6934         fi
6935         umask $SAVE_UMASK
6936         $CLEANUP
6937         return 0
6938 }
6939
6940 #
6941 # Wrapper around run_one to ensure:
6942 #  - test runs in subshell
6943 #  - output of test is saved to separate log file for error reporting
6944 #  - test result is saved to data file
6945 #
6946 run_one_logged() {
6947         local before=$SECONDS
6948         local testnum=$1
6949         local testmsg=$2
6950         export tfile=f${testnum}.${TESTSUITE}
6951         export tdir=d${testnum}.${TESTSUITE}
6952         local test_log=$TESTLOG_PREFIX.$TESTNAME.test_log.$(hostname -s).log
6953         local zfs_debug_log=$TESTLOG_PREFIX.$TESTNAME.zfs_log
6954         local SAVE_UMASK=$(umask)
6955         local rc=0
6956         umask 0022
6957
6958         rm -f $LOGDIR/err $LOGDIR/ignore $LOGDIR/skip
6959         echo
6960         # if $ONLY is set, repeat subtest $ONLY_REPEAT times, otherwise once
6961         local repeat=${ONLY:+$ONLY_REPEAT}
6962
6963         for ((testiter=0; testiter < ${repeat:-1}; testiter++)); do
6964                 local before_sub=$SECONDS
6965
6966                 log_sub_test_begin $TESTNAME
6967                 # remove temp files between repetitions to avoid test failures
6968                 if [[ -n "$append" ]]; then
6969                         [[ -n "$tdir" ]] && rm -rvf $DIR/$tdir*
6970                         [[ -n "$tfile" ]] && rm -vf $DIR/$tfile*
6971                         echo "subtest iteration $testiter/$repeat"
6972                 fi
6973                 # loop around subshell so stack_trap EXIT triggers each time
6974                 (run_one $testnum "$testmsg") 2>&1 | tee -i $append $test_log
6975                 rc=${PIPESTATUS[0]}
6976                 local append=-a
6977                 local duration_sub=$((SECONDS - before_sub))
6978                 local test_error
6979
6980                 [[ $rc != 0 && ! -f $LOGDIR/err ]] &&
6981                         echo "$TESTNAME returned $rc" | tee $LOGDIR/err
6982
6983                 if [[ -f $LOGDIR/err ]]; then
6984                         test_error=$(cat $LOGDIR/err)
6985                         TEST_STATUS="FAIL"
6986                 elif [[ -f $LOGDIR/ignore ]]; then
6987                         test_error=$(cat $LOGDIR/ignore)
6988                 elif [[ -f $LOGDIR/skip ]]; then
6989                         test_error=$(cat $LOGDIR/skip)
6990                         TEST_STATUS="SKIP"
6991                 else
6992                         TEST_STATUS="PASS"
6993                 fi
6994
6995                 pass "$testnum" "(${duration_sub}s)"
6996                 if [ -n "${DUMP_OK}" ]; then
6997                         gather_logs $(comma_list $(nodes_list))
6998                 fi
6999
7000                 log_sub_test_end $TEST_STATUS $duration_sub "$rc" "$test_error"
7001                 [[ $rc != 0 || "$TEST_STATUS" != "PASS" ]] && break
7002         done
7003
7004         if [[ "$TEST_STATUS" != "SKIP" && -f $TF_SKIP ]]; then
7005                 rm -f $TF_SKIP
7006         fi
7007
7008         if [ -f $LOGDIR/err ]; then
7009                 log_zfs_info "$zfs_debug_log"
7010                 $FAIL_ON_ERROR && exit $rc
7011         fi
7012
7013         umask $SAVE_UMASK
7014
7015         unset TESTNAME
7016         unset tdir
7017         unset tfile
7018
7019         return 0
7020 }
7021
7022 #
7023 # Print information of skipped tests to result.yml
7024 #
7025 skip_logged(){
7026         log_sub_test_begin $1
7027         shift
7028         log_sub_test_end "SKIP" "0" "0" "$@"
7029 }
7030
7031 grant_from_clients() {
7032         local nodes="$1"
7033
7034         # get client grant
7035         do_nodes $nodes "$LCTL get_param -n osc.${FSNAME}-*.cur_*grant_bytes" |
7036                 calc_sum
7037 }
7038
7039 grant_from_servers() {
7040         local nodes="$1"
7041
7042         # get server grant
7043         # which is tot_granted less grant_precreate
7044         do_nodes $nodes "$LCTL get_param obdfilter.${FSNAME}-OST*.tot_granted" \
7045                 " obdfilter.${FSNAME}-OST*.tot_pending" \
7046                 " obdfilter.${FSNAME}-OST*.grant_precreate" |
7047                 tr '=' ' ' | awk '/tot_granted/{ total += $2 };
7048                                   /tot_pending/{ total -= $2 };
7049                                   /grant_precreate/{ total -= $2 };
7050                                   END { printf("%0.0f", total) }'
7051 }
7052
7053 check_grant() {
7054         export base=$(basetest $1)
7055         [ "$CHECK_GRANT" == "no" ] && return 0
7056
7057         local isonly_base=GCHECK_ONLY_${base}
7058         local isonly=GCHECK_ONLY_$1
7059         [ ${!isonly_base}x == x -a ${!isonly}x == x ] && return 0
7060
7061         echo -n "checking grant......"
7062
7063         local osts=$(comma_list $(osts_nodes))
7064         local clients=$CLIENTS
7065         [ -z "$clients" ] && clients=$(hostname)
7066
7067         # sync all the data and make sure no pending data on server
7068         do_nodes $clients sync
7069         do_nodes $clients $LFS df # initiate all idling connections
7070
7071         # get client grant
7072         cli_grant=$(grant_from_clients $clients)
7073
7074         # get server grant
7075         # which is tot_granted less grant_precreate
7076         srv_grant=$(grant_from_servers $osts)
7077
7078         count=0
7079         # check whether client grant == server grant
7080         while [[ $cli_grant != $srv_grant && count++ -lt 30 ]]; do
7081                 echo "wait for client:$cli_grant == server:$srv_grant"
7082                 sleep 1
7083                 cli_grant=$(grant_from_clients $clients)
7084                 srv_grant=$(grant_from_servers $osts)
7085         done
7086         if [[ $cli_grant -ne $srv_grant ]]; then
7087                 do_nodes $(comma_list $(osts_nodes)) \
7088                         "$LCTL get_param obdfilter.${FSNAME}-OST*.tot*" \
7089                         "obdfilter.${FSNAME}-OST*.grant_*"
7090                 do_nodes $clients "$LCTL get_param osc.${FSNAME}-*.cur_*_bytes"
7091                 error "failed grant check: client:$cli_grant server:$srv_grant"
7092         else
7093                 echo "pass grant check: client:$cli_grant server:$srv_grant"
7094         fi
7095 }
7096
7097 ########################
7098 # helper functions
7099
7100 osc_to_ost()
7101 {
7102         osc=$1
7103         ost=`echo $1 | awk -F_ '{print $3}'`
7104         if [ -z $ost ]; then
7105                 ost=`echo $1 | sed 's/-osc.*//'`
7106         fi
7107         echo $ost
7108 }
7109
7110 ostuuid_from_index()
7111 {
7112         $LFS osts $2 | sed -ne "/^$1: /s/.* \(.*\) .*$/\1/p"
7113 }
7114
7115 ostname_from_index() {
7116         local uuid=$(ostuuid_from_index $1 $2)
7117
7118         echo ${uuid/_UUID/}
7119 }
7120
7121 mdtname_from_index() {
7122         local uuid=$(mdtuuid_from_index $1 $2)
7123         echo ${uuid/_UUID/}
7124 }
7125
7126 mdssize_from_index () {
7127         local mdt=$(mdtname_from_index $2)
7128         $LFS df $1 | grep $mdt | awk '{ print $2 }'
7129 }
7130
7131 index_from_ostuuid()
7132 {
7133         $LFS osts $2 | sed -ne "/${1}/s/\(.*\): .* .*$/\1/p"
7134 }
7135
7136 mdtuuid_from_index()
7137 {
7138         $LFS mdts $2 | sed -ne "/^$1: /s/.* \(.*\) .*$/\1/p"
7139 }
7140
7141 # Description:
7142 #   Return unique identifier for given hostname
7143 host_id() {
7144         local host_name=$1
7145         echo $host_name | md5sum | cut -d' ' -f1
7146 }
7147
7148 # Description:
7149 #   Returns list of ip addresses for each interface
7150 local_addr_list() {
7151         ip addr | awk '/inet / {print $2}' | awk -F/ '{print $1}'
7152 }
7153
7154 # Description:
7155 #   Returns list of interfaces configured for LNet
7156 lnet_if_list() {
7157         local nids=( $($LCTL list_nids | xargs echo) )
7158
7159         [[ -z ${nids[@]} ]] &&
7160                 return 0
7161
7162         if [[ ${NETTYPE} =~ kfi* ]]; then
7163                 $LNETCTL net show 2>/dev/null | awk '/ cxi[0-9]+$/{print $NF}' |
7164                         sort -u | xargs echo
7165                 return 0
7166         fi
7167
7168         declare -a INTERFACES
7169
7170         for ((i = 0; i < ${#nids[@]}; i++)); do
7171                 ip=$(sed 's/^\(.*\)@.*$/\1/'<<<${nids[i]})
7172                 INTERFACES[i]=$(ip -o a s |
7173                                 awk '$4 ~ /^'$ip'\//{print $2}')
7174                 INTERFACES=($(echo "${INTERFACES[@]}" | tr ' ' '\n' | uniq | tr '\n' ' '))
7175                 if [[ -z ${INTERFACES[i]} ]]; then
7176                         error "Can't determine interface name for NID ${nids[i]}"
7177                 elif [[ 1 -ne $(wc -w <<<${INTERFACES[i]}) ]]; then
7178                         error "Found $(wc -w <<<${INTERFACES[i]}) interfaces for NID ${nids[i]}. Expect 1"
7179                 fi
7180         done
7181
7182         echo "${INTERFACES[@]}"
7183
7184         return 0
7185 }
7186
7187 is_local_addr() {
7188         local addr=$1
7189         # Cache address list to avoid mutiple execution of local_addr_list
7190         LOCAL_ADDR_LIST=${LOCAL_ADDR_LIST:-$(local_addr_list)}
7191         local i
7192         for i in $LOCAL_ADDR_LIST ; do
7193                 [[ "$i" == "$addr" ]] && return 0
7194         done
7195         return 1
7196 }
7197
7198 local_node() {
7199         local host_name=$1
7200         local is_local="IS_LOCAL_$(host_id $host_name)"
7201         if [ -z "${!is_local-}" ] ; then
7202                 eval $is_local=0
7203                 local host_ip=$(getent ahostsv4 $host_name |
7204                                         awk 'NR == 1 { print $1 }')
7205                 is_local_addr "$host_ip" && eval $is_local=1
7206         fi
7207         [[ "${!is_local}" == "1" ]]
7208 }
7209
7210 remote_node () {
7211         local node=$1
7212         local_node $node && return 1
7213         return 0
7214 }
7215
7216 remote_mds ()
7217 {
7218         local node
7219         for node in $(mdts_nodes); do
7220                 remote_node $node && return 0
7221         done
7222         return 1
7223 }
7224
7225 remote_mds_nodsh()
7226 {
7227         [ -n "$CLIENTONLY" ] && return 0 || true
7228         remote_mds && [ "$PDSH" = "no_dsh" -o -z "$PDSH" -o -z "$mds_HOST" ]
7229 }
7230
7231 require_dsh_mds()
7232 {
7233         remote_mds_nodsh && echo "SKIP: $TESTSUITE: remote MDS with nodsh" &&
7234                 MSKIPPED=1 && return 1
7235         return 0
7236 }
7237
7238 remote_ost ()
7239 {
7240         local node
7241         for node in $(osts_nodes) ; do
7242                 remote_node $node && return 0
7243         done
7244         return 1
7245 }
7246
7247 remote_ost_nodsh()
7248 {
7249         [ -n "$CLIENTONLY" ] && return 0 || true
7250         remote_ost && [ "$PDSH" = "no_dsh" -o -z "$PDSH" -o -z "$ost_HOST" ]
7251 }
7252
7253 require_dsh_ost()
7254 {
7255         remote_ost_nodsh && echo "SKIP: $TESTSUITE: remote OST with nodsh" &&
7256                 OSKIPPED=1 && return 1
7257         return 0
7258 }
7259
7260 remote_mgs_nodsh()
7261 {
7262         [ -n "$CLIENTONLY" ] && return 0 || true
7263         local MGS
7264         MGS=$(facet_host mgs)
7265         remote_node $MGS && [ "$PDSH" = "no_dsh" -o -z "$PDSH" -o -z "$ost_HOST" ]
7266 }
7267
7268 local_mode ()
7269 {
7270         remote_mds_nodsh || remote_ost_nodsh ||
7271                 $(single_local_node $(comma_list $(nodes_list)))
7272 }
7273
7274 remote_servers () {
7275         remote_ost && remote_mds
7276 }
7277
7278 # Get the active nodes for facets.
7279 facets_nodes () {
7280         local facets=$1
7281         local facet
7282         local nodes
7283         local nodes_sort
7284         local i
7285
7286         for facet in ${facets//,/ }; do
7287                 nodes="$nodes $(facet_active_host $facet)"
7288         done
7289
7290         nodes_sort=$(for i in $nodes; do echo $i; done | sort -u)
7291         echo -n $nodes_sort
7292 }
7293
7294 # Get name of the active MGS node.
7295 mgs_node () {
7296                 echo -n $(facets_nodes $(get_facets MGS))
7297         }
7298
7299 # Get all of the active MDS nodes.
7300 mdts_nodes () {
7301         echo -n $(facets_nodes $(get_facets MDS))
7302 }
7303
7304 # Get all of the active OSS nodes.
7305 osts_nodes () {
7306         echo -n $(facets_nodes $(get_facets OST))
7307 }
7308
7309 # Get all of the client nodes and active server nodes.
7310 nodes_list () {
7311         local nodes=$HOSTNAME
7312         local nodes_sort
7313         local i
7314
7315         # CLIENTS (if specified) contains the local client
7316         [ -n "$CLIENTS" ] && nodes=${CLIENTS//,/ }
7317
7318         if [ "$PDSH" -a "$PDSH" != "no_dsh" ]; then
7319                 nodes="$nodes $(facets_nodes $(get_facets))"
7320         fi
7321
7322         nodes_sort=$(for i in $nodes; do echo $i; done | sort -u)
7323         echo -n $nodes_sort
7324 }
7325
7326 # Get all of the remote client nodes and remote active server nodes.
7327 remote_nodes_list () {
7328         echo -n $(nodes_list) | sed -re "s/\<$HOSTNAME\>//g"
7329 }
7330
7331 # Get all of the MDS nodes, including active and passive nodes.
7332 all_mdts_nodes () {
7333         local host
7334         local failover_host
7335         local nodes
7336         local nodes_sort
7337         local i
7338
7339         for i in $(seq $MDSCOUNT); do
7340                 host=mds${i}_HOST
7341                 failover_host=mds${i}failover_HOST
7342                 nodes="$nodes ${!host} ${!failover_host}"
7343         done
7344
7345         [ -n "$nodes" ] || nodes="${mds_HOST} ${mdsfailover_HOST}"
7346         nodes_sort=$(for i in $nodes; do echo $i; done | sort -u)
7347         echo -n $nodes_sort
7348 }
7349
7350 # Get all of the OSS nodes, including active and passive nodes.
7351 all_osts_nodes () {
7352         local host
7353         local failover_host
7354         local nodes=
7355         local nodes_sort
7356         local i
7357
7358         for i in $(seq $OSTCOUNT); do
7359                 host=ost${i}_HOST
7360                 failover_host=ost${i}failover_HOST
7361                 nodes="$nodes ${!host} ${!failover_host}"
7362         done
7363
7364         [ -n "$nodes" ] || nodes="${ost_HOST} ${ostfailover_HOST}"
7365         nodes_sort=$(for i in $nodes; do echo $i; done | sort -u)
7366         echo -n $nodes_sort
7367 }
7368
7369 # Get all of the server nodes, including active and passive nodes.
7370 all_server_nodes () {
7371         local nodes
7372         local nodes_sort
7373         local i
7374
7375         nodes="$mgs_HOST $mgsfailover_HOST $(all_mdts_nodes) $(all_osts_nodes)"
7376
7377         nodes_sort=$(for i in $nodes; do echo $i; done | sort -u)
7378         echo -n $nodes_sort
7379 }
7380
7381 # Get all of the client and server nodes, including active and passive nodes.
7382 all_nodes () {
7383         local nodes=$HOSTNAME
7384         local nodes_sort
7385         local i
7386
7387         # CLIENTS (if specified) contains the local client
7388         [ -n "$CLIENTS" ] && nodes=${CLIENTS//,/ }
7389
7390         if [ "$PDSH" -a "$PDSH" != "no_dsh" ]; then
7391                 nodes="$nodes $(all_server_nodes)"
7392         fi
7393
7394         nodes_sort=$(for i in $nodes; do echo $i; done | sort -u)
7395         echo -n $nodes_sort
7396 }
7397
7398 init_clients_lists () {
7399         # Sanity check: exclude the local client from RCLIENTS
7400         local clients=$(hostlist_expand "$RCLIENTS")
7401         local rclients=$(exclude_items_from_list "$clients" $HOSTNAME)
7402
7403         # Sanity check: exclude the dup entries
7404         RCLIENTS=$(for i in ${rclients//,/ }; do echo $i; done | sort -u)
7405
7406         export CLIENT1=${CLIENT1:-$HOSTNAME}
7407         export SINGLECLIENT=$CLIENT1
7408
7409         clients="$SINGLECLIENT $HOSTNAME $RCLIENTS"
7410
7411         # Sanity check: exclude the dup entries from CLIENTS
7412         # for those configs which has SINGLCLIENT set to local client
7413         clients=$(for i in $clients; do echo $i; done | sort -u)
7414
7415         export CLIENTS=$(comma_list $clients)
7416         local -a remoteclients=($RCLIENTS)
7417         for ((i=0; $i<${#remoteclients[@]}; i++)); do
7418                 varname=CLIENT$((i + 2))
7419
7420                 eval export $varname=${remoteclients[i]}
7421         done
7422
7423         export CLIENTCOUNT=$((${#remoteclients[@]} + 1))
7424 }
7425
7426 get_random_entry () {
7427         local rnodes=$1
7428
7429         rnodes=${rnodes//,/ }
7430
7431         local -a nodes=($rnodes)
7432         local num=${#nodes[@]}
7433         local i=$((RANDOM * num * 2 / 65536))
7434
7435         echo ${nodes[i]}
7436 }
7437
7438 client_only () {
7439         [ -n "$CLIENTONLY" ] || [ "x$CLIENTMODSONLY" = "xyes" ]
7440 }
7441
7442 check_versions () {
7443     [ "$(lustre_version_code client)" = "$(lustre_version_code $SINGLEMDS)" -a \
7444       "$(lustre_version_code client)" = "$(lustre_version_code ost1)" ]
7445 }
7446
7447 get_node_count() {
7448         local nodes="$@"
7449
7450         echo $nodes | wc -w || true
7451 }
7452
7453 mixed_mdt_devs () {
7454         local nodes=$(mdts_nodes)
7455         local mdtcount=$(get_node_count "$nodes")
7456
7457         [ ! "$MDSCOUNT" = "$mdtcount" ]
7458 }
7459
7460 generate_machine_file() {
7461         local nodes=${1//,/ }
7462         local machinefile=$2
7463
7464         rm -f $machinefile
7465         for node in $nodes; do
7466                 echo $node >>$machinefile ||
7467                         { echo "can not generate machinefile $machinefile" &&
7468                                 return 1; }
7469         done
7470 }
7471
7472 get_stripe () {
7473         local file=$1/stripe
7474
7475         touch $file
7476         $LFS getstripe -v $file || error "getstripe $file failed"
7477         rm -f $file
7478 }
7479
7480 # Check and add a test group.
7481 add_group() {
7482         local group_id=$1
7483         local group_name=$2
7484         local rc=0
7485
7486         local gid=$(getent group $group_name | cut -d: -f3)
7487         if [[ -n "$gid" ]]; then
7488                 [[ "$gid" -eq "$group_id" ]] || {
7489                         error_noexit "inconsistent group ID:" \
7490                                      "new: $group_id, old: $gid"
7491                         rc=1
7492                 }
7493         else
7494                 echo "adding group $group_name:$group_id"
7495                 getent group $group_name || true
7496                 getent group $group_id || true
7497                 groupadd -g $group_id $group_name
7498                 rc=${PIPESTATUS[0]}
7499         fi
7500
7501         return $rc
7502 }
7503
7504 # Check and add a test user.
7505 add_user() {
7506         local user_id=$1
7507         shift
7508         local user_name=$1
7509         shift
7510         local group_name=$1
7511         shift
7512         local home=$1
7513         shift
7514         local opts="$@"
7515         local rc=0
7516
7517         local uid=$(getent passwd $user_name | cut -d: -f3)
7518         if [[ -n "$uid" ]]; then
7519                 if [[ "$uid" -eq "$user_id" ]]; then
7520                         local dir=$(getent passwd $user_name | cut -d: -f6)
7521                         if [[ "$dir" != "$home" ]]; then
7522                                 mkdir -p $home
7523                                 usermod -d $home $user_name
7524                                 rc=${PIPESTATUS[0]}
7525                         fi
7526                 else
7527                         error_noexit "inconsistent user ID:" \
7528                                      "new: $user_id, old: $uid"
7529                         rc=1
7530                 fi
7531         else
7532                 mkdir -p $home
7533                 useradd -M -u $user_id -d $home -g $group_name $opts $user_name
7534                 rc=${PIPESTATUS[0]}
7535         fi
7536
7537         return $rc
7538 }
7539
7540 check_runas_id_ret() {
7541         local myRC=0
7542         local myRUNAS_UID=$1
7543         local myRUNAS_GID=$2
7544         shift 2
7545         local myRUNAS=$@
7546
7547         if [ -z "$myRUNAS" ]; then
7548                 error_exit "check_runas_id_ret requires myRUNAS argument"
7549         fi
7550
7551         $myRUNAS true ||
7552                 error "Unable to execute $myRUNAS"
7553
7554         id $myRUNAS_UID > /dev/null ||
7555                 error "Invalid RUNAS_ID $myRUNAS_UID. Please set RUNAS_ID to " \
7556                       "some UID which exists on MDS and client or add user " \
7557                       "$myRUNAS_UID:$myRUNAS_GID on these nodes."
7558
7559         if $GSS_KRB5; then
7560                 $myRUNAS krb5_login.sh ||
7561                         error "Failed to refresh krb5 TGT for UID $myRUNAS_ID."
7562         fi
7563         mkdir $DIR/d0_runas_test
7564         chmod 0755 $DIR
7565         chown $myRUNAS_UID:$myRUNAS_GID $DIR/d0_runas_test
7566         $myRUNAS -u $myRUNAS_UID -g $myRUNAS_GID touch $DIR/d0_runas_test/f$$ ||
7567                 myRC=$?
7568         rm -rf $DIR/d0_runas_test
7569         return $myRC
7570 }
7571
7572 check_runas_id() {
7573         local myRUNAS_UID=$1
7574         local myRUNAS_GID=$2
7575         shift 2
7576         local myRUNAS=$@
7577
7578         check_runas_id_ret $myRUNAS_UID $myRUNAS_GID $myRUNAS || \
7579                 error "unable to write to $DIR/d0_runas_test as " \
7580                       "UID $myRUNAS_UID."
7581 }
7582
7583 # obtain the UID/GID for MPI_USER
7584 get_mpiuser_id() {
7585         local mpi_user=$1
7586
7587         MPI_USER_UID=$(do_facet client "getent passwd $mpi_user | cut -d: -f3;
7588 exit \\\${PIPESTATUS[0]}") || error_exit "failed to get the UID for $mpi_user"
7589
7590         MPI_USER_GID=$(do_facet client "getent passwd $mpi_user | cut -d: -f4;
7591 exit \\\${PIPESTATUS[0]}") || error_exit "failed to get the GID for $mpi_user"
7592 }
7593
7594 # obtain and cache Kerberos ticket-granting ticket
7595 refresh_krb5_tgt() {
7596         local myRUNAS_UID=$1
7597         local myRUNAS_GID=$2
7598         shift 2
7599         local myRUNAS=$@
7600         if [ -z "$myRUNAS" ]; then
7601                 error_exit "myRUNAS command must be specified for refresh_krb5_tgt"
7602         fi
7603
7604         CLIENTS=${CLIENTS:-$HOSTNAME}
7605         do_nodes $CLIENTS "set -x
7606 if ! $myRUNAS krb5_login.sh; then
7607     echo "Failed to refresh Krb5 TGT for UID/GID $myRUNAS_UID/$myRUNAS_GID."
7608     exit 1
7609 fi"
7610 }
7611
7612 # Run multiop in the background, but wait for it to print
7613 # "PAUSING" to its stdout before returning from this function.
7614 multiop_bg_pause() {
7615         MULTIOP_PROG=${MULTIOP_PROG:-$MULTIOP}
7616         FILE=$1
7617         ARGS=$2
7618
7619         TMPPIPE=/tmp/multiop_open_wait_pipe.$$
7620         mkfifo $TMPPIPE
7621
7622         echo "$MULTIOP_PROG $FILE v$ARGS"
7623         $MULTIOP_PROG $FILE v$ARGS > $TMPPIPE &
7624
7625         echo "TMPPIPE=${TMPPIPE}"
7626         read -t 60 multiop_output < $TMPPIPE
7627         if [ $? -ne 0 ]; then
7628                 rm -f $TMPPIPE
7629                 return 1
7630         fi
7631         rm -f $TMPPIPE
7632         if [ "$multiop_output" != "PAUSING" ]; then
7633                 echo "Incorrect multiop output: $multiop_output"
7634                 kill -9 $PID
7635                 return 1
7636         fi
7637
7638         return 0
7639 }
7640
7641 do_and_time () {
7642         local cmd="$1"
7643         local start
7644         local rc
7645
7646         start=$SECONDS
7647         eval '$cmd'
7648         [ ${PIPESTATUS[0]} -eq 0 ] || rc=1
7649
7650         echo $((SECONDS - start))
7651         return $rc
7652 }
7653
7654 inodes_available () {
7655         local IFree=$($LFS df -i $MOUNT | grep ^$FSNAME | awk '{ print $4 }' |
7656                 sort -un | head -n1) || return 1
7657
7658         echo $((IFree))
7659 }
7660
7661 mdsrate_inodes_available () {
7662         local min_inodes=$(inodes_available)
7663
7664         echo $((min_inodes * 99 / 100))
7665 }
7666
7667 # reset stat counters
7668 clear_stats() {
7669         local paramfile="$1"
7670
7671         lctl set_param -n $paramfile=0
7672 }
7673
7674 # sum stat items
7675 calc_stats() {
7676         local paramfile="$1"
7677         local stat="$2"
7678
7679         lctl get_param -n $paramfile |
7680                 awk '/^'$stat'/ { sum += $2 } END { printf("%0.0f", sum) }'
7681 }
7682
7683 calc_sum () {
7684         awk '{sum += $1} END { printf("%0.0f", sum) }'
7685 }
7686
7687 calc_osc_kbytes () {
7688         $LFS df $MOUNT > /dev/null
7689         $LCTL get_param -n osc.*[oO][sS][cC][-_][0-9a-f]*.$1 | calc_sum
7690 }
7691
7692 free_min_max () {
7693         wait_delete_completed
7694         AVAIL=($(lctl get_param -n osc.*[oO][sS][cC]-[^M]*.kbytesavail))
7695         echo "OST kbytes available: ${AVAIL[*]}"
7696         MAXV=${AVAIL[0]}
7697         MAXI=0
7698         MINV=${AVAIL[0]}
7699         MINI=0
7700         for ((i = 0; i < ${#AVAIL[@]}; i++)); do
7701                 #echo OST $i: ${AVAIL[i]}kb
7702                 if [[ ${AVAIL[i]} -gt $MAXV ]]; then
7703                         MAXV=${AVAIL[i]}
7704                         MAXI=$i
7705                 fi
7706                 if [[ ${AVAIL[i]} -lt $MINV ]]; then
7707                         MINV=${AVAIL[i]}
7708                         MINI=$i
7709                 fi
7710         done
7711         echo "Min free space: OST $MINI: $MINV"
7712         echo "Max free space: OST $MAXI: $MAXV"
7713 }
7714
7715 # save_lustre_params(comma separated facet list, parameter_mask)
7716 # generate a stream of formatted strings (<facet> <param name>=<param value>)
7717 save_lustre_params() {
7718         local facets=$1
7719         local facet
7720         local facet_svc
7721
7722         for facet in ${facets//,/ }; do
7723                 facet_svc=$(facet_svc $facet)
7724                 do_facet $facet \
7725                         "params=\\\$($LCTL get_param $2);
7726                          [[ -z \\\"$facet_svc\\\" ]] && param= ||
7727                          param=\\\$(grep $facet_svc <<< \\\"\\\$params\\\");
7728                          [[ -z \\\$param ]] && param=\\\"\\\$params\\\";
7729                          while read s; do echo $facet \\\$s;
7730                          done <<< \\\"\\\$param\\\""
7731         done
7732 }
7733
7734 # restore lustre parameters from input stream, produces by save_lustre_params
7735 restore_lustre_params() {
7736         local facet
7737         local name
7738         local val
7739
7740         while IFS=" =" read facet name val; do
7741                 do_facet $facet "$LCTL set_param -n $name=$val"
7742         done
7743 }
7744
7745 check_node_health() {
7746         local nodes=${1:-$(comma_list $(nodes_list))}
7747         local health=$TMP/node_health.$$
7748
7749         do_nodes -q $nodes "$LCTL get_param catastrophe 2>&1" | tee $health |
7750                 grep "catastrophe=1" && error "LBUG/LASSERT detected"
7751         # Only check/report network health if get_param isn't reported, since
7752         # *clearly* the network is working if get_param returned something.
7753         if (( $(grep -c catastro $health) != $(wc -w <<< ${nodes//,/ }) )); then
7754                 for node in ${nodes//,/ }; do
7755                         check_network $node 5
7756                 done
7757         fi
7758         rm -f $health
7759 }
7760
7761 mdsrate_cleanup () {
7762         if [ -d $4 ]; then
7763                 mpi_run ${MACHINEFILE_OPTION} $2 -np $1 ${MDSRATE} --unlink \
7764                         --nfiles $3 --dir $4 --filefmt $5 $6
7765                 rmdir $4
7766         fi
7767 }
7768
7769 ########################
7770
7771 convert_facet2label() {
7772         local facet=$1
7773
7774         if [ x$facet = xost ]; then
7775                 facet=ost1
7776         elif [ x$facet = xmgs ] && combined_mgs_mds ; then
7777                 facet=mds1
7778         fi
7779
7780         local varsvc=${facet}_svc
7781
7782         if [ -n "${!varsvc}" ]; then
7783                 echo ${!varsvc}
7784         else
7785                 error "No label for $facet!"
7786         fi
7787 }
7788
7789 get_clientosc_proc_path() {
7790         echo "${1}-osc-[-0-9a-f]*"
7791 }
7792
7793 get_mdtosc_proc_path() {
7794         local mds_facet=$1
7795         local ost_label=${2:-"*OST*"}
7796
7797         [ "$mds_facet" = "mds" ] && mds_facet=$SINGLEMDS
7798         local mdt_label=$(convert_facet2label $mds_facet)
7799         local mdt_index=$(echo $mdt_label | sed -e 's/^.*-//')
7800
7801         if [[ $ost_label = *OST* ]]; then
7802                 echo "${ost_label}-osc-${mdt_index}"
7803         else
7804                 echo "${ost_label}-osp-${mdt_index}"
7805         fi
7806 }
7807
7808 get_osc_import_name() {
7809         local facet=$1
7810         local ost=$2
7811         local label=$(convert_facet2label $ost)
7812
7813         if [ "${facet:0:3}" = "mds" ]; then
7814                 get_mdtosc_proc_path $facet $label
7815                 return 0
7816         fi
7817
7818         get_clientosc_proc_path $label
7819         return 0
7820 }
7821
7822 _wait_import_state () {
7823         local expected="$1"
7824         local CONN_PROC="$2"
7825         local maxtime=${3:-$(max_recovery_time)}
7826         local err_on_fail=${4:-1}
7827         local CONN_STATE
7828         local i=0
7829
7830         CONN_STATE=$($LCTL get_param -n $CONN_PROC 2>/dev/null | cut -f2 | uniq)
7831         while ! echo "${CONN_STATE}" | egrep -q "^${expected}\$" ; do
7832                 if [[ "${expected}" == "DISCONN" ]]; then
7833                         # for disconn we can check after proc entry is removed
7834                         [[ -z "${CONN_STATE}" ]] && return 0
7835                         # with AT, we can have connect request timeout near
7836                         # reconnect timeout and test can't see real disconnect
7837                         [[ "${CONN_STATE}" == "CONNECTING" ]] && return 0
7838                 fi
7839                 if (( $i >= $maxtime )); then
7840                         (( $err_on_fail != 0 )) &&
7841                                 error "can't put import for $CONN_PROC into ${expected} state after $i sec, have ${CONN_STATE}"
7842                         return 1
7843                 fi
7844                 sleep 1
7845                 # Add uniq for multi-mount case
7846                 CONN_STATE=$($LCTL get_param -n $CONN_PROC 2>/dev/null |
7847                              cut -f2 | uniq)
7848                 i=$((i + 1))
7849         done
7850
7851         log "$CONN_PROC in ${CONN_STATE} state after $i sec"
7852         return 0
7853 }
7854
7855 wait_import_state() {
7856         local expected="$1"
7857         local params="$2"
7858         local maxtime=${3:-$(max_recovery_time)}
7859         local err_on_fail=${4:-1}
7860         local param
7861
7862         for param in ${params//,/ }; do
7863                 _wait_import_state "$expected" "$param" $maxtime $err_on_fail ||
7864                 return
7865         done
7866 }
7867
7868 wait_import_state_mount() {
7869         if ! is_mounted $MOUNT && ! is_mounted $MOUNT2; then
7870                 return 0
7871         fi
7872
7873         wait_import_state "$@"
7874 }
7875
7876 # One client request could be timed out because server was not ready
7877 # when request was sent by client.
7878 # The request timeout calculation details :
7879 # ptl_send_rpc ()
7880 #      /* We give the server rq_timeout secs to process the req, and
7881 #      add the network latency for our local timeout. */
7882 #      request->rq_deadline = request->rq_sent + request->rq_timeout +
7883 #           ptlrpc_at_get_net_latency(request) ;
7884 #
7885 # ptlrpc_connect_import ()
7886 #      request->rq_timeout = INITIAL_CONNECT_TIMEOUT
7887 #
7888 # init_imp_at () ->
7889 #   -> at_init(&at->iat_net_latency, 0, 0) -> iat_net_latency=0
7890 # ptlrpc_at_get_net_latency(request) ->
7891 #       at_get (max (iat_net_latency=0, at_min)) = at_min
7892 #
7893 # i.e.:
7894 # request->rq_timeout + ptlrpc_at_get_net_latency(request) =
7895 # INITIAL_CONNECT_TIMEOUT + at_min
7896 #
7897 # We will use obd_timeout instead of INITIAL_CONNECT_TIMEOUT
7898 # because we can not get this value in runtime,
7899 # the value depends on configure options, and it is not stored in /proc.
7900 # obd_support.h:
7901 # #define CONNECTION_SWITCH_MIN 5U
7902 # #define INITIAL_CONNECT_TIMEOUT max(CONNECTION_SWITCH_MIN,obd_timeout/20)
7903
7904 request_timeout () {
7905         local facet=$1
7906
7907         # request->rq_timeout = INITIAL_CONNECT_TIMEOUT
7908         local init_connect_timeout=$TIMEOUT
7909         [[ $init_connect_timeout -ge 5 ]] || init_connect_timeout=5
7910
7911         local at_min=$(at_get $facet at_min)
7912
7913         echo $(( init_connect_timeout + at_min ))
7914 }
7915
7916 _wait_osc_import_state() {
7917         local facet=$1
7918         local ost_facet=$2
7919         local expected=$3
7920         local target=$(get_osc_import_name $facet $ost_facet)
7921         local param="os[cp].${target}.ost_server_uuid"
7922         local params=$param
7923         local i=0
7924
7925         # 1. wait the deadline of client 1st request (it could be skipped)
7926         # 2. wait the deadline of client 2nd request
7927         local maxtime=$(( 2 * $(request_timeout $facet)))
7928
7929         if [[ $facet == client* ]]; then
7930                 # During setup time, the osc might not be setup, it need wait
7931                 # until list_param can return valid value.
7932                 params=$($LCTL list_param $param 2>/dev/null || true)
7933                 while [ -z "$params" ]; do
7934                         if [ $i -ge $maxtime ]; then
7935                                 echo "can't get $param in $maxtime secs"
7936                                 return 1
7937                         fi
7938                         sleep 1
7939                         i=$((i + 1))
7940                         params=$($LCTL list_param $param 2>/dev/null || true)
7941                 done
7942         fi
7943
7944         if [[ $ost_facet = mds* ]]; then
7945                 # no OSP connection to itself
7946                 if [[ $facet = $ost_facet ]]; then
7947                         return 0
7948                 fi
7949                 param="osp.${target}.mdt_server_uuid"
7950                 params=$param
7951         fi
7952
7953         local plist=$(comma_list $params)
7954         if ! do_rpc_nodes "$(facet_active_host $facet)" \
7955                         wait_import_state $expected $plist $maxtime; then
7956                 error "$facet: import is not in $expected state after $maxtime"
7957                 return 1
7958         fi
7959
7960         return 0
7961 }
7962
7963 wait_osc_import_state() {
7964         local facet=$1
7965         local ost_facet=$2
7966         local expected=$3
7967         local num
7968
7969         if [[ $facet = mds ]]; then
7970                 for num in $(seq $MDSCOUNT); do
7971                         _wait_osc_import_state mds$num "$ost_facet" "$expected"
7972                 done
7973         else
7974                 _wait_osc_import_state "$facet" "$ost_facet" "$expected"
7975         fi
7976 }
7977
7978 wait_osc_import_ready() {
7979         wait_osc_import_state $1 $2 "\(FULL\|IDLE\)"
7980 }
7981
7982 _wait_mgc_import_state() {
7983         local facet=$1
7984         local expected=$2
7985         local error_on_failure=${3:-1}
7986         local param="mgc.*.mgs_server_uuid"
7987         local params=$param
7988         local i=0
7989
7990         # 1. wait the deadline of client 1st request (it could be skipped)
7991         # 2. wait the deadline of client 2nd request
7992         local maxtime=$(( 2 * $(request_timeout $facet)))
7993
7994         if [[ $facet == client* ]]; then
7995                 # During setup time, the osc might not be setup, it need wait
7996                 # until list_param can return valid value. And also if there
7997                 # are mulitple osc entries we should list all of them before
7998                 # go to wait.
7999                 params=$($LCTL list_param $param 2>/dev/null || true)
8000                 while [ -z "$params" ]; do
8001                         if [ $i -ge $maxtime ]; then
8002                                 echo "can't get $param in $maxtime secs"
8003                                 return 1
8004                         fi
8005                         sleep 1
8006                         i=$((i + 1))
8007                         params=$($LCTL list_param $param 2>/dev/null || true)
8008                 done
8009         fi
8010         local plist=$(comma_list $params)
8011         if ! do_rpc_nodes "$(facet_active_host $facet)" \
8012                         wait_import_state $expected $plist $maxtime \
8013                                           $error_on_failure; then
8014                 if [ $error_on_failure -ne 0 ]; then
8015                     error "import is not in ${expected} state"
8016                 fi
8017                 return 1
8018         fi
8019
8020         return 0
8021 }
8022
8023 wait_mgc_import_state() {
8024         local facet=$1
8025         local expected=$2
8026         local error_on_failure=${3:-1}
8027         local num
8028
8029         if [[ $facet = mds ]]; then
8030                 for num in $(seq $MDSCOUNT); do
8031                         _wait_mgc_import_state mds$num "$expected" \
8032                                                $error_on_failure || return
8033                 done
8034         else
8035                 _wait_mgc_import_state "$facet" "$expected" \
8036                                        $error_on_failure || return
8037         fi
8038 }
8039
8040 wait_dne_interconnect() {
8041         local num
8042
8043         if [ $MDSCOUNT -gt 1 ]; then
8044                 for num in $(seq $MDSCOUNT); do
8045                         wait_osc_import_ready mds mds$num
8046                 done
8047         fi
8048 }
8049
8050 get_clientmdc_proc_path() {
8051     echo "${1}-mdc-*"
8052 }
8053
8054 get_clientmgc_proc_path() {
8055     echo "*"
8056 }
8057
8058 do_rpc_nodes () {
8059         local quiet
8060
8061         [[ "$1" == "--quiet" || "$1" == "-q" ]] && quiet="$1" && shift
8062
8063         local list=$1
8064         shift
8065
8066         [ -z "$list" ] && return 0
8067
8068         # Add paths to lustre tests for 32 and 64 bit systems.
8069         local LIBPATH="/usr/lib/lustre/tests:/usr/lib64/lustre/tests:"
8070         local TESTPATH="$RLUSTRE/tests:"
8071         local RPATH="PATH=${TESTPATH}${LIBPATH}${PATH}:/sbin:/bin:/usr/sbin:"
8072         do_nodes ${quiet:-"--verbose"} $list "${RPATH} NAME=${NAME} \
8073                 TESTLOG_PREFIX=$TESTLOG_PREFIX TESTNAME=$TESTNAME \
8074                 bash rpc.sh $* "
8075 }
8076
8077 wait_clients_import_state () {
8078         local list="$1"
8079         local facet="$2"
8080         local expected="$3"
8081         local facets="$facet"
8082
8083         if [ "$FAILURE_MODE" = HARD ]; then
8084                 facets=$(for f in ${facet//,/ }; do
8085                         facets_on_host $(facet_active_host $f) | tr "," "\n"
8086                 done | sort -u | paste -sd , )
8087         fi
8088
8089         for facet in ${facets//,/ }; do
8090                 local label=$(convert_facet2label $facet)
8091                 local proc_path
8092                 case $facet in
8093                 ost* ) proc_path="osc.$(get_clientosc_proc_path \
8094                                         $label).ost_server_uuid" ;;
8095                 mds* ) proc_path="mdc.$(get_clientmdc_proc_path \
8096                                         $label).mds_server_uuid" ;;
8097                 mgs* ) proc_path="mgc.$(get_clientmgc_proc_path \
8098                                         $label).mgs_server_uuid" ;;
8099                 *) error "unknown facet!" ;;
8100                 esac
8101
8102                 local params=$(expand_list $params $proc_path)
8103         done
8104
8105         if ! do_rpc_nodes "$list" wait_import_state_mount "$expected" $params;
8106         then
8107                 error "import is not in ${expected} state"
8108                 return 1
8109         fi
8110 }
8111
8112 wait_clients_import_ready() {
8113         wait_clients_import_state "$1" "$2" "\(FULL\|IDLE\)"
8114 }
8115
8116 wait_osp_active() {
8117         local facet=$1
8118         local tgt_name=$2
8119         local tgt_idx=$3
8120         local expected=$4
8121         local num
8122
8123         # wait until all MDTs are in the expected state
8124         for ((num = 1; num <= $MDSCOUNT; num++)); do
8125                 local mdtosp=$(get_mdtosc_proc_path mds${num} ${tgt_name})
8126                 local wait=0
8127                 local mproc
8128
8129                 if [ $facet = "mds" ]; then
8130                         mproc="osp.$mdtosp.active"
8131                         [ $num -eq $((tgt_idx + 1)) ] && continue
8132                 else
8133                         mproc="osc.$mdtosp.active"
8134                 fi
8135
8136                 echo "check $mproc"
8137                 while true; do
8138                         sleep 5
8139                         local result=$(do_facet mds${num} "$LCTL get_param -n $mproc")
8140                         local max=30
8141
8142                         [ ${PIPESTATUS[0]} = 0 ] || error "Can't read $mproc"
8143                         if [ $result -eq $expected ]; then
8144                                 echo -n "target updated after "
8145                                 echo "$wait sec (got $result)"
8146                                 break
8147                         fi
8148                         wait=$((wait + 5))
8149                         if [ $wait -eq $max ]; then
8150                                 error "$tgt_name: wanted $expected got $result"
8151                         fi
8152                         echo "Waiting $((max - wait)) secs for $tgt_name"
8153                 done
8154         done
8155 }
8156
8157 oos_full() {
8158         local -a AVAILA
8159         local -a GRANTA
8160         local -a TOTALA
8161         local OSCFULL=1
8162         AVAILA=($(do_nodes $(comma_list $(osts_nodes)) \
8163                   $LCTL get_param obdfilter.*.kbytesavail))
8164         GRANTA=($(do_nodes $(comma_list $(osts_nodes)) \
8165                   $LCTL get_param -n obdfilter.*.tot_granted))
8166         TOTALA=($(do_nodes $(comma_list $(osts_nodes)) \
8167                   $LCTL get_param -n obdfilter.*.kbytestotal))
8168         for ((i=0; i<${#AVAILA[@]}; i++)); do
8169                 local -a AVAIL1=(${AVAILA[$i]//=/ })
8170                 local -a TOTAL=(${TOTALA[$i]//=/ })
8171                 GRANT=$((${GRANTA[$i]}/1024))
8172                 # allow 1% of total space in bavail because of delayed
8173                 # allocation with ZFS which might release some free space after
8174                 # txg commit.  For small devices, we set a mininum of 8MB
8175                 local LIMIT=$((${TOTAL} / 100 + 8000))
8176                 echo -n $(echo ${AVAIL1[0]} | cut -d"." -f2) avl=${AVAIL1[1]} \
8177                         grnt=$GRANT diff=$((AVAIL1[1] - GRANT)) limit=${LIMIT}
8178                 [ $((AVAIL1[1] - GRANT)) -lt $LIMIT ] && OSCFULL=0 && \
8179                         echo " FULL" || echo
8180         done
8181         return $OSCFULL
8182 }
8183
8184 list_pool() {
8185         echo -e "$(do_facet $SINGLEMDS $LCTL pool_list $1 | sed '1d')"
8186 }
8187
8188 check_pool_not_exist() {
8189         local fsname=${1%%.*}
8190         local poolname=${1##$fsname.}
8191         [[ $# -ne 1 ]] && return 0
8192         [[ x$poolname = x ]] &&  return 0
8193         list_pool $fsname | grep -w $1 && return 1
8194         return 0
8195 }
8196
8197 create_pool() {
8198         local fsname=${1%%.*}
8199         local poolname=${1##$fsname.}
8200         local keep_pools=${2:-false}
8201
8202         stack_trap "destroy_test_pools $fsname" EXIT
8203         do_facet mgs lctl pool_new $1
8204         local RC=$?
8205         # get param should return err unless pool is created
8206         [[ $RC -ne 0 ]] && return $RC
8207
8208         for mds_id in $(seq $MDSCOUNT); do
8209                 local mdt_id=$((mds_id-1))
8210                 local lodname=$fsname-MDT$(printf "%04x" $mdt_id)-mdtlov
8211                 wait_update_facet mds$mds_id \
8212                         "lctl get_param -n lod.$lodname.pools.$poolname \
8213                                 2>/dev/null || echo foo" "" ||
8214                         error "mds$mds_id: pool_new failed $1"
8215         done
8216         wait_update $HOSTNAME "lctl get_param -n lov.$fsname-*.pools.$poolname \
8217                 2>/dev/null || echo foo" "" || error "pool_new failed $1"
8218
8219         $keep_pools || add_pool_to_list $1
8220         return $RC
8221 }
8222
8223 add_pool_to_list () {
8224         local fsname=${1%%.*}
8225         local poolname=${1##$fsname.}
8226
8227         local listvar=${fsname}_CREATED_POOLS
8228         local temp=${listvar}=$(expand_list ${!listvar} $poolname)
8229         eval export $temp
8230 }
8231
8232 remove_pool_from_list () {
8233         local fsname=${1%%.*}
8234         local poolname=${1##$fsname.}
8235
8236         local listvar=${fsname}_CREATED_POOLS
8237         local temp=${listvar}=$(exclude_items_from_list "${!listvar}" $poolname)
8238         eval export $temp
8239 }
8240
8241 # cleanup all pools exist on $FSNAME
8242 destroy_all_pools () {
8243         local i
8244         for i in $(list_pool $FSNAME); do
8245                 destroy_pool $i
8246         done
8247 }
8248
8249 destroy_pool_int() {
8250         local ost
8251         local OSTS=$(list_pool $1)
8252         for ost in $OSTS; do
8253                 do_facet mgs lctl pool_remove $1 $ost
8254         done
8255         wait_update_facet $SINGLEMDS "lctl pool_list $1 | wc -l" "1" ||
8256                 error "MDS: pool_list $1 failed"
8257         do_facet mgs lctl pool_destroy $1
8258 }
8259
8260 # <fsname>.<poolname> or <poolname>
8261 destroy_pool() {
8262         local fsname=${1%%.*}
8263         local poolname=${1##$fsname.}
8264
8265         [[ x$fsname = x$poolname ]] && fsname=$FSNAME
8266
8267         local RC
8268
8269         check_pool_not_exist $fsname.$poolname && return 0 || true
8270
8271         destroy_pool_int $fsname.$poolname
8272         RC=$?
8273         [[ $RC -ne 0 ]] && return $RC
8274         for mds_id in $(seq $MDSCOUNT); do
8275                 local mdt_id=$((mds_id-1))
8276                 local lodname=$fsname-MDT$(printf "%04x" $mdt_id)-mdtlov
8277                 wait_update_facet mds$mds_id \
8278                         "lctl get_param -n lod.$lodname.pools.$poolname \
8279                                 2>/dev/null || echo foo" "foo" ||
8280                         error "mds$mds_id: destroy pool failed $1"
8281         done
8282         wait_update $HOSTNAME "lctl get_param -n lov.$fsname-*.pools.$poolname \
8283                 2>/dev/null || echo foo" "foo" || error "destroy pool failed $1"
8284
8285         remove_pool_from_list $fsname.$poolname
8286
8287         return $RC
8288 }
8289
8290 destroy_pools () {
8291         local fsname=${1:-$FSNAME}
8292         local poolname
8293         local listvar=${fsname}_CREATED_POOLS
8294
8295         [ x${!listvar} = x ] && return 0
8296
8297         echo "Destroy the created pools: ${!listvar}"
8298         for poolname in ${!listvar//,/ }; do
8299                 destroy_pool $fsname.$poolname
8300         done
8301 }
8302
8303 destroy_test_pools () {
8304         trap 0
8305         local fsname=${1:-$FSNAME}
8306         destroy_pools $fsname || true
8307 }
8308
8309 gather_logs () {
8310         local list=$1
8311
8312         local ts=$(date +%s)
8313         local docp=true
8314
8315         if [[ ! -f "$YAML_LOG" ]]; then
8316                 # init_logging is not performed before gather_logs,
8317                 # so the $LOGDIR needs to be checked here
8318                 check_shared_dir $LOGDIR && touch $LOGDIR/shared
8319         fi
8320
8321         [ -f $LOGDIR/shared ] && docp=false
8322
8323         # dump lustre logs, dmesg, and journal if GSS_SK=true
8324
8325         prefix="$TESTLOG_PREFIX.$TESTNAME"
8326         suffix="$ts.log"
8327         echo "Dumping lctl log to ${prefix}.*.${suffix}"
8328
8329         if [ -n "$CLIENTONLY" -o "$PDSH" == "no_dsh" ]; then
8330                 echo "Dumping logs only on local client."
8331                 $LCTL dk > ${prefix}.debug_log.$(hostname -s).${suffix}
8332                 dmesg > ${prefix}.dmesg.$(hostname -s).${suffix}
8333                 [ "$SHARED_KEY" = true ] && find $SK_PATH -name '*.key' -exec \
8334                         $LGSS_SK -r {} \; &> \
8335                         ${prefix}.ssk_keys.$(hostname -s).${suffix}
8336                 [ "$SHARED_KEY" = true ] && lctl get_param 'nodemap.*.*' > \
8337                         ${prefix}.nodemaps.$(hostname -s).${suffix}
8338                 [ "$GSS" = true ] && keyctl show > \
8339                         ${prefix}.keyring.$(hostname -s).${suffix}
8340                 [ "$GSS" = true ] && journalctl -a > \
8341                         ${prefix}.journal.$(hostname -s).${suffix}
8342                 return
8343         fi
8344
8345         do_nodesv $list \
8346                 "$LCTL dk > ${prefix}.debug_log.\\\$(hostname -s).${suffix};
8347                 dmesg > ${prefix}.dmesg.\\\$(hostname -s).${suffix}"
8348         if [ "$SHARED_KEY" = true ]; then
8349                 do_nodesv $list "find $SK_PATH -name '*.key' -exec \
8350                         $LGSS_SK -r {} \; &> \
8351                         ${prefix}.ssk_keys.\\\$(hostname -s).${suffix}"
8352                 do_facet mds1 "lctl get_param 'nodemap.*.*' > \
8353                         ${prefix}.nodemaps.\\\$(hostname -s).${suffix}"
8354         fi
8355         if [ "$GSS" = true ]; then
8356                 do_nodesv $list "keyctl show > \
8357                         ${prefix}.keyring.\\\$(hostname -s).${suffix}"
8358                 do_nodesv $list "journalctl -a > \
8359                         ${prefix}.journal.\\\$(hostname -s).${suffix}"
8360         fi
8361
8362         if [ ! -f $LOGDIR/shared ]; then
8363                 local remote_nodes=$(exclude_items_from_list $list $HOSTNAME)
8364
8365                 for node in ${remote_nodes//,/ }; do
8366                         rsync -az -e ssh $node:${prefix}.'*'.${suffix} $LOGDIR &
8367                 done
8368         fi
8369 }
8370
8371 do_ls () {
8372         local mntpt_root=$1
8373         local num_mntpts=$2
8374         local dir=$3
8375         local i
8376         local cmd
8377         local pids
8378         local rc=0
8379
8380         for i in $(seq 0 $num_mntpts); do
8381                 cmd="ls -laf ${mntpt_root}$i/$dir"
8382                 echo + $cmd;
8383                 $cmd > /dev/null &
8384                 pids="$pids $!"
8385         done
8386         echo pids=$pids
8387         for pid in $pids; do
8388                 wait $pid || rc=$?
8389         done
8390
8391         return $rc
8392 }
8393
8394 # check_and_start_recovery_timer()
8395 #       service_time = at_est2timeout(service_time);
8396 #       service_time += 2 * INITIAL_CONNECT_TIMEOUT;
8397 #       service_time += 2 * (CONNECTION_SWITCH_MAX + CONNECTION_SWITCH_INC);
8398
8399 #define INITIAL_CONNECT_TIMEOUT max(CONNECTION_SWITCH_MIN, obd_timeout/20)
8400 #define CONNECTION_SWITCH_MAX min(50, max(CONNECTION_SWITCH_MIN, obd_timeout))
8401 #define CONNECTION_SWITCH_MIN 5
8402 #define CONNECTION_SWITCH_INC 5
8403 max_recovery_time() {
8404         local init_connect_timeout=$((TIMEOUT / 20))
8405         ((init_connect_timeout >= 5)) || init_connect_timeout=5
8406
8407         local service_time=$(($(at_max_get client) * 9 / 4 + 5))
8408         service_time=$((service_time + 2 * (init_connect_timeout + 50 + 5)))
8409
8410         echo -n $service_time
8411 }
8412
8413 recovery_time_min() {
8414         local connection_switch_min=5
8415         local connection_switch_inc=5
8416         local connection_switch_max
8417         local reconnect_delay_max
8418         local initial_connect_timeout
8419         local max
8420         local timout_20
8421
8422         #connection_switch_max=min(50, max($connection_switch_min,$TIMEOUT)
8423         (($connection_switch_min > $TIMEOUT)) &&
8424                 max=$connection_switch_min || max=$TIMEOUT
8425         (($max < 50)) && connection_switch_max=$max || connection_switch_max=50
8426
8427         #initial_connect_timeout = max(connection_switch_min, obd_timeout/20)
8428         timeout_20=$((TIMEOUT/20))
8429         (($connection_switch_min > $timeout_20)) &&
8430                 initial_connect_timeout=$connection_switch_min ||
8431                 initial_connect_timeout=$timeout_20
8432
8433         reconnect_delay_max=$((connection_switch_max + connection_switch_inc +
8434                                initial_connect_timeout))
8435         echo $((2 * reconnect_delay_max))
8436 }
8437
8438 get_clients_mount_count () {
8439         local clients=${CLIENTS:-$HOSTNAME}
8440
8441         # we need to take into account the clients mounts and
8442         # exclude mds/ost mounts if any;
8443         do_nodes $clients cat /proc/mounts | grep lustre |
8444                 grep -w $MOUNT | wc -l
8445 }
8446
8447 # gss functions
8448 PROC_CLI="srpc_info"
8449 PROC_CON="srpc_contexts"
8450
8451 combination()
8452 {
8453         local M=$1
8454         local N=$2
8455         local R=1
8456
8457         if [ $M -lt $N ]; then
8458                 R=0
8459         else
8460                 N=$((N + 1))
8461                 while [ $N -lt $M ]; do
8462                         R=$((R * N))
8463                         N=$((N + 1))
8464                 done
8465         fi
8466
8467         echo $R
8468         return 0
8469 }
8470
8471 calc_connection_cnt() {
8472         local dir=$1
8473
8474         # MDT->MDT = 2 * C(M, 2)
8475         # MDT->OST = M * O
8476         # CLI->OST = C * O
8477         # CLI->MDT = C * M
8478         comb_m2=$(combination $MDSCOUNT 2)
8479
8480         local num_clients=$(get_clients_mount_count)
8481
8482         local cnt_mdt2mdt=$((comb_m2 * 2))
8483         local cnt_mdt2ost=$((MDSCOUNT * OSTCOUNT))
8484         local cnt_cli2ost=$((num_clients * OSTCOUNT))
8485         local cnt_cli2mdt=$((num_clients * MDSCOUNT))
8486         if is_mounted $MOUNT2; then
8487                 cnt_cli2mdt=$((cnt_cli2mdt * 2))
8488                 cnt_cli2ost=$((cnt_cli2ost * 2))
8489         fi
8490         if local_mode; then
8491                 cnt_mdt2mdt=0
8492                 cnt_mdt2ost=0
8493                 cnt_cli2ost=2
8494                 cnt_cli2mdt=1
8495         fi
8496         local cnt_all2ost=$((cnt_mdt2ost + cnt_cli2ost))
8497         local cnt_all2mdt=$((cnt_mdt2mdt + cnt_cli2mdt))
8498         local cnt_all2all=$((cnt_mdt2ost + cnt_mdt2mdt \
8499                 + cnt_cli2ost + cnt_cli2mdt))
8500
8501         local var=cnt_$dir
8502         local res=${!var}
8503
8504         echo $res
8505 }
8506
8507 set_rule()
8508 {
8509         local tgt=$1
8510         local net=$2
8511         local dir=$3
8512         local flavor=$4
8513         local cmd="$tgt.srpc.flavor"
8514
8515         if [ $net == "any" ]; then
8516                 net="default"
8517         fi
8518         cmd="$cmd.$net"
8519
8520         if [ $dir != "any" ]; then
8521                 cmd="$cmd.$dir"
8522         fi
8523
8524         cmd="$cmd=$flavor"
8525         log "Setting sptlrpc rule: $cmd"
8526         do_facet mgs "$LCTL conf_param $cmd"
8527 }
8528
8529 count_contexts()
8530 {
8531         local output=$1
8532         local total_ctx=$(echo "$output" | grep -c "expire.*key.*hdl")
8533         echo $total_ctx
8534 }
8535
8536 count_flvr()
8537 {
8538         local output=$1
8539         local flavor=$2
8540         local count=0
8541
8542         rpc_flvr=`echo $flavor | awk -F - '{ print $1 }'`
8543         bulkspec=`echo $flavor | awk -F - '{ print $2 }'`
8544
8545         count=`echo "$output" | grep "rpc flavor" | grep $rpc_flvr | wc -l`
8546
8547         if [ "x$bulkspec" != "x" ]; then
8548                 algs=`echo $bulkspec | awk -F : '{ print $2 }'`
8549
8550                 if [ "x$algs" != "x" ]; then
8551                         bulk_count=`echo "$output" | grep "bulk flavor" |
8552                                 grep $algs | wc -l`
8553                 else
8554                         bulk=`echo $bulkspec | awk -F : '{ print $1 }'`
8555
8556                         if [ $bulk == "bulkn" ]; then
8557                                 bulk_count=`echo "$output" |
8558                                         grep "bulk flavor" | grep "null/null" |
8559                                         wc -l`
8560                         elif [ $bulk == "bulki" ]; then
8561                                 bulk_count=`echo "$output" |
8562                                         grep "bulk flavor" | grep "/null" |
8563                                         grep -v "null/" | wc -l`
8564                         else
8565                                 bulk_count=`echo "$output" |
8566                                         grep "bulk flavor" | grep -v "/null" |
8567                                         grep -v "null/" | wc -l`
8568                         fi
8569                 fi
8570                 [ $bulk_count -lt $count ] && count=$bulk_count
8571         fi
8572
8573         echo $count
8574 }
8575
8576 flvr_cnt_cli2mdt()
8577 {
8578         local flavor=$1
8579         local cnt
8580
8581         local clients=${CLIENTS:-$HOSTNAME}
8582
8583         for c in ${clients//,/ }; do
8584                 local output=$(do_node $c lctl get_param -n \
8585                          mdc.*-*-mdc-*.$PROC_CLI 2>/dev/null)
8586                 local tmpcnt=$(count_flvr "$output" $flavor)
8587
8588                 if $GSS_SK && [ $flavor != "null" ]; then
8589                         # tmpcnt=min(contexts,flavors) to ensure SK context is
8590                         # on
8591                         output=$(do_node $c lctl get_param -n \
8592                                  mdc.*-MDT*-mdc-*.$PROC_CON 2>/dev/null)
8593                         local outcon=$(count_contexts "$output")
8594
8595                         if [ "$outcon" -lt "$tmpcnt" ]; then
8596                                 tmpcnt=$outcon
8597                         fi
8598                 fi
8599                 cnt=$((cnt + tmpcnt))
8600         done
8601         echo $cnt
8602 }
8603
8604 flvr_cnt_cli2ost()
8605 {
8606         local flavor=$1
8607         local cnt
8608
8609         local clients=${CLIENTS:-$HOSTNAME}
8610
8611         for c in ${clients//,/ }; do
8612                 # reconnect if idle
8613                 do_node $c lctl set_param osc.*.idle_connect=1 >/dev/null 2>&1
8614                 local output=$(do_node $c lctl get_param -n \
8615                          osc.*OST*-osc-[^M][^D][^T]*.$PROC_CLI 2>/dev/null)
8616                 local tmpcnt=$(count_flvr "$output" $flavor)
8617
8618                 if $GSS_SK && [ $flavor != "null" ]; then
8619                         # tmpcnt=min(contexts,flavors) to ensure SK context is on
8620                         output=$(do_node $c lctl get_param -n \
8621                                  osc.*OST*-osc-[^M][^D][^T]*.$PROC_CON 2>/dev/null)
8622                         local outcon=$(count_contexts "$output")
8623
8624                         if [ "$outcon" -lt "$tmpcnt" ]; then
8625                                 tmpcnt=$outcon
8626                         fi
8627                 fi
8628                 cnt=$((cnt + tmpcnt))
8629         done
8630         echo $cnt
8631 }
8632
8633 flvr_cnt_mdt2mdt()
8634 {
8635         local flavor=$1
8636         local cnt=0
8637
8638         if [ $MDSCOUNT -le 1 ]; then
8639                 echo 0
8640                 return
8641         fi
8642
8643         for num in `seq $MDSCOUNT`; do
8644                 local output=$(do_facet mds$num lctl get_param -n \
8645                         osp.*-MDT*osp-MDT*.$PROC_CLI 2>/dev/null)
8646                 local tmpcnt=$(count_flvr "$output" $flavor)
8647
8648                 if $GSS_SK && [ $flavor != "null" ]; then
8649                         # tmpcnt=min(contexts,flavors) to ensure SK context is on
8650                         output=$(do_facet mds$num lctl get_param -n \
8651                                 osp.*-MDT*osp-MDT*.$PROC_CON 2>/dev/null)
8652                         local outcon=$(count_contexts "$output")
8653
8654                         if [ "$outcon" -lt "$tmpcnt" ]; then
8655                                 tmpcnt=$outcon
8656                         fi
8657                 fi
8658                 cnt=$((cnt + tmpcnt))
8659         done
8660         echo $cnt;
8661 }
8662
8663 flvr_cnt_mdt2ost()
8664 {
8665         local flavor=$1
8666         local cnt=0
8667         local mdtosc
8668
8669         for num in `seq $MDSCOUNT`; do
8670                 mdtosc=$(get_mdtosc_proc_path mds$num)
8671                 mdtosc=${mdtosc/-MDT*/-MDT\*}
8672                 local output=$(do_facet mds$num lctl get_param -n \
8673                                 os[cp].$mdtosc.$PROC_CLI 2>/dev/null)
8674                 # Ensure SK context is on
8675                 local tmpcnt=$(count_flvr "$output" $flavor)
8676
8677                 if $GSS_SK && [ $flavor != "null" ]; then
8678                         output=$(do_facet mds$num lctl get_param -n \
8679                                  os[cp].$mdtosc.$PROC_CON 2>/dev/null)
8680                         local outcon=$(count_contexts "$output")
8681
8682                         if [ "$outcon" -lt "$tmpcnt" ]; then
8683                                 tmpcnt=$outcon
8684                         fi
8685                 fi
8686                 cnt=$((cnt + tmpcnt))
8687         done
8688         echo $cnt;
8689 }
8690
8691 flvr_cnt_mgc2mgs()
8692 {
8693         local flavor=$1
8694
8695         local output=$(do_facet client lctl get_param -n mgc.*.$PROC_CLI \
8696                         2>/dev/null)
8697         count_flvr "$output" $flavor
8698 }
8699
8700 do_check_flavor()
8701 {
8702         local dir=$1        # from to
8703         local flavor=$2     # flavor expected
8704         local res=0
8705
8706         if [ $dir == "cli2mdt" ]; then
8707                 res=`flvr_cnt_cli2mdt $flavor`
8708         elif [ $dir == "cli2ost" ]; then
8709                 res=`flvr_cnt_cli2ost $flavor`
8710         elif [ $dir == "mdt2mdt" ]; then
8711                 res=`flvr_cnt_mdt2mdt $flavor`
8712         elif [ $dir == "mdt2ost" ]; then
8713                 res=`flvr_cnt_mdt2ost $flavor`
8714         elif [ $dir == "all2ost" ]; then
8715                 res1=`flvr_cnt_mdt2ost $flavor`
8716                 res2=`flvr_cnt_cli2ost $flavor`
8717                 res=$((res1 + res2))
8718         elif [ $dir == "all2mdt" ]; then
8719                 res1=`flvr_cnt_mdt2mdt $flavor`
8720                 res2=`flvr_cnt_cli2mdt $flavor`
8721                 res=$((res1 + res2))
8722         elif [ $dir == "all2all" ]; then
8723                 res1=`flvr_cnt_mdt2ost $flavor`
8724                 res2=`flvr_cnt_cli2ost $flavor`
8725                 res3=`flvr_cnt_mdt2mdt $flavor`
8726                 res4=`flvr_cnt_cli2mdt $flavor`
8727                 res=$((res1 + res2 + res3 + res4))
8728         fi
8729
8730         echo $res
8731 }
8732
8733 wait_flavor()
8734 {
8735         local dir=$1        # from to
8736         local flavor=$2     # flavor expected
8737         local expect=${3:-$(calc_connection_cnt $dir)} # number expected
8738         local WAITFLAVOR_MAX=20 # how many retries before abort?
8739
8740         local res=0
8741         for ((i = 0; i < $WAITFLAVOR_MAX; i++)); do
8742                 echo -n "checking $dir..."
8743                 res=$(do_check_flavor $dir $flavor)
8744                 echo "found $res/$expect $flavor connections"
8745                 [ $res -ge $expect ] && return 0
8746                 sleep 4
8747         done
8748
8749         echo "Error checking $flavor of $dir: expect $expect, actual $res"
8750 #       echo "Dumping additional logs for SK debug.."
8751         do_nodes $(comma_list $(all_server_nodes)) "keyctl show"
8752         if $dump; then
8753                 gather_logs $(comma_list $(nodes_list))
8754         fi
8755         return 1
8756 }
8757
8758 restore_to_default_flavor()
8759 {
8760         local proc="mgs.MGS.live.$FSNAME"
8761
8762         echo "restoring to default flavor..."
8763
8764         local nrule=$(do_facet mgs lctl get_param -n $proc 2>/dev/null |
8765                 grep ".srpc.flavor" | wc -l)
8766
8767         # remove all existing rules if any
8768         if [ $nrule -ne 0 ]; then
8769                 echo "$nrule existing rules"
8770                 for rule in $(do_facet mgs lctl get_param -n $proc 2>/dev/null |
8771                     grep ".srpc.flavor."); do
8772                         echo "remove rule: $rule"
8773                         spec=`echo $rule | awk -F = '{print $1}'`
8774                         do_facet mgs "$LCTL conf_param -d $spec"
8775                 done
8776         fi
8777
8778         # verify no rules left
8779         nrule=$(do_facet mgs lctl get_param -n $proc 2>/dev/null |
8780                 grep ".srpc.flavor." | wc -l)
8781         [ $nrule -ne 0 ] && error "still $nrule rules left"
8782
8783         # wait for default flavor to be applied
8784         if $GSS_SK; then
8785                 if $SK_S2S; then
8786                         set_rule $FSNAME any any $SK_FLAVOR
8787                         wait_flavor all2all $SK_FLAVOR
8788                 else
8789                         set_rule $FSNAME any cli2mdt $SK_FLAVOR
8790                         set_rule $FSNAME any cli2ost $SK_FLAVOR
8791                         wait_flavor cli2mdt $SK_FLAVOR
8792                         wait_flavor cli2ost $SK_FLAVOR
8793                 fi
8794                 echo "GSS_SK now at default flavor: $SK_FLAVOR"
8795         else
8796                 wait_flavor all2all null
8797         fi
8798 }
8799
8800 set_flavor_all()
8801 {
8802         local flavor=${1:-null}
8803
8804         echo "setting all flavor to $flavor"
8805
8806         # FIXME need parameter to this fn
8807         # and remove global vars
8808         local cnt_all2all=$(calc_connection_cnt all2all)
8809
8810         local res=$(do_check_flavor all2all $flavor)
8811         if [ $res -eq $cnt_all2all ]; then
8812                 echo "already have total $res $flavor connections"
8813                 return
8814         fi
8815
8816         echo "found $res $flavor out of total $cnt_all2all connections"
8817         restore_to_default_flavor
8818
8819         [[ $flavor = null ]] && return 0
8820
8821         if $GSS_SK && [ $flavor != "null" ]; then
8822                 if $SK_S2S; then
8823                         set_rule $FSNAME any any $flavor
8824                         wait_flavor all2all $flavor
8825                 else
8826                         set_rule $FSNAME any cli2mdt $flavor
8827                         set_rule $FSNAME any cli2ost $flavor
8828                         set_rule $FSNAME any mdt2ost null
8829                         set_rule $FSNAME any mdt2mdt null
8830                         wait_flavor cli2mdt $flavor
8831                         wait_flavor cli2ost $flavor
8832                 fi
8833                 echo "GSS_SK now at flavor: $flavor"
8834         else
8835                 set_rule $FSNAME any cli2mdt $flavor
8836                 set_rule $FSNAME any cli2ost $flavor
8837                 set_rule $FSNAME any mdt2ost null
8838                 set_rule $FSNAME any mdt2mdt null
8839                 wait_flavor cli2mdt $flavor
8840                 wait_flavor cli2ost $flavor
8841         fi
8842 }
8843
8844
8845 check_logdir() {
8846         local dir=$1
8847         # Checking for shared logdir
8848         if [ ! -d $dir ]; then
8849                 # Not found. Create local logdir
8850                 mkdir -p $dir
8851         else
8852                 touch $dir/check_file.$(hostname -s)
8853         fi
8854         return 0
8855 }
8856
8857 check_write_access() {
8858         local dir=$1
8859         local list=${2:-$(comma_list $(nodes_list))}
8860         local node
8861         local file
8862
8863         for node in ${list//,/ }; do
8864                 file=$dir/check_file.$(short_nodename $node)
8865                 if [[ ! -f "$file" ]]; then
8866                         # Logdir not accessible/writable from this node.
8867                         return 1
8868                 fi
8869                 rm -f $file || return 1
8870         done
8871         return 0
8872 }
8873
8874 init_logging() {
8875         [[ -n $YAML_LOG ]] && return
8876         local save_umask=$(umask)
8877         umask 0000
8878
8879         export YAML_LOG=${LOGDIR}/results.yml
8880         mkdir -p $LOGDIR
8881         init_clients_lists
8882
8883         # If the yaml log already exists then we will just append to it
8884         if [ ! -f $YAML_LOG ]; then
8885                 if check_shared_dir $LOGDIR; then
8886                         touch $LOGDIR/shared
8887                         echo "Logging to shared log directory: $LOGDIR"
8888                 else
8889                         echo "Logging to local directory: $LOGDIR"
8890                 fi
8891
8892                 yml_nodes_file $LOGDIR >> $YAML_LOG
8893                 yml_results_file >> $YAML_LOG
8894         fi
8895
8896         umask $save_umask
8897
8898         # log actual client and server versions if needed for debugging
8899         log "Client: $(lustre_build_version client)"
8900         log "MDS: $(lustre_build_version mds1)"
8901         log "OSS: $(lustre_build_version ost1)"
8902 }
8903
8904 log_test() {
8905         yml_log_test $1 >> $YAML_LOG
8906 }
8907
8908 log_test_status() {
8909         yml_log_test_status "$@" >> $YAML_LOG
8910 }
8911
8912 log_sub_test_begin() {
8913         yml_log_sub_test_begin "$@" >> $YAML_LOG
8914 }
8915
8916 log_sub_test_end() {
8917         yml_log_sub_test_end "$@" >> $YAML_LOG
8918 }
8919
8920 run_llverdev()
8921 {
8922         local dev=$1; shift
8923         local llverdev_opts="$*"
8924         local devname=$(basename $dev)
8925         local size=$(awk "/$devname$/ {print \$3}" /proc/partitions)
8926         # loop devices aren't in /proc/partitions
8927         [[ -z "$size" ]] && size=$(stat -c %s $dev)
8928
8929         local size_gb=$((size / 1024 / 1024)) # Gb
8930
8931         local partial_arg=""
8932         # Run in partial (fast) mode if the size of a partition > 1 GB
8933         (( $size == 0 || $size_gb > 1 )) && partial_arg="-p"
8934
8935         llverdev --force $partial_arg $llverdev_opts $dev
8936 }
8937
8938 run_llverfs()
8939 {
8940         local dir=$1
8941         local llverfs_opts=$2
8942         local use_partial_arg=$3
8943         local partial_arg=""
8944         local size=$(df -B G $dir |tail -n 1 |awk '{print $2}' |sed 's/G//') #GB
8945
8946         # Run in partial (fast) mode if the size of a partition > 1 GB
8947         [ "x$use_partial_arg" != "xno" ] && [ $size -gt 1 ] && partial_arg="-p"
8948
8949         llverfs $partial_arg $llverfs_opts $dir
8950 }
8951
8952 run_sgpdd () {
8953         local devs=${1//,/ }
8954         shift
8955         local params=$@
8956         local rslt=$TMP/sgpdd_survey
8957
8958         # sgpdd-survey cleanups ${rslt}.* files
8959
8960         local cmd="rslt=$rslt $params scsidevs=\"$devs\" $SGPDDSURVEY"
8961         echo + $cmd
8962         eval $cmd
8963         cat ${rslt}.detail
8964 }
8965
8966 # returns the canonical name for an ldiskfs device
8967 ldiskfs_canon() {
8968         local dev="$1"
8969         local facet="$2"
8970
8971         do_facet $facet "dv=\\\$($LCTL get_param -n $dev);
8972                          if foo=\\\$(lvdisplay -c \\\$dv 2>/dev/null); then
8973                                 echo dm-\\\${foo##*:};
8974                          else
8975                                 name=\\\$(basename \\\$dv);
8976                                 if [[ \\\$name = *flakey* ]]; then
8977                                         name=\\\$(lsblk -o NAME,KNAME |
8978                                                 awk /\\\$name/'{print \\\$NF}');
8979                                 fi;
8980                                 echo \\\$name;
8981                          fi;"
8982 }
8983
8984 is_sanity_benchmark() {
8985         local benchmarks="dbench bonnie iozone fsx"
8986         local suite=$1
8987
8988         for b in $benchmarks; do
8989                 if [ "$b" == "$suite" ]; then
8990                         return 0
8991                 fi
8992         done
8993         return 1
8994 }
8995
8996 min_ost_size () {
8997         $LFS df | grep OST | awk '{print $4}' | sort -un | head -1
8998 }
8999
9000 #
9001 # Get the available size (KB) of a given obd target.
9002 #
9003 get_obd_size() {
9004         local facet=$1
9005         local obd=$2
9006         local size
9007
9008         [[ $facet != client ]] || return 0
9009
9010         size=$(do_facet $facet $LCTL get_param -n *.$obd.kbytesavail | head -n1)
9011         echo -n $size
9012 }
9013
9014 #
9015 # Get the page size (bytes) on a given facet node.
9016 # The local client page_size is directly available in PAGE_SIZE.
9017 #
9018 get_page_size() {
9019         local facet=$1
9020         local page_size=$(getconf PAGE_SIZE 2>/dev/null)
9021
9022         [ -z "$CLIENTONLY" -a "$facet" != "client" ] &&
9023                 page_size=$(do_facet $facet getconf PAGE_SIZE)
9024         echo -n ${page_size:-4096}
9025 }
9026
9027 #
9028 # Get the block count of the filesystem.
9029 #
9030 get_block_count() {
9031         local facet=$1
9032         local device=$2
9033         local count
9034
9035         [ -z "$CLIENTONLY" ] &&
9036                 count=$(do_facet $facet "$DUMPE2FS -h $device 2>&1" |
9037                         awk '/^Block count:/ {print $3}')
9038         echo -n ${count:-0}
9039 }
9040
9041 # Check whether the "ea_inode" feature is enabled or not, to allow
9042 # ldiskfs xattrs over one block in size.  Allow both the historical
9043 # Lustre feature name (large_xattr) and the upstream name (ea_inode).
9044 large_xattr_enabled() {
9045         [[ $(facet_fstype $SINGLEMDS) == zfs ]] && return 0
9046
9047         local mds_dev=$(mdsdevname ${SINGLEMDS//mds/})
9048
9049         do_facet $SINGLEMDS "$DUMPE2FS -h $mds_dev 2>&1 |
9050                 grep -E -q '(ea_inode|large_xattr)'"
9051         return ${PIPESTATUS[0]}
9052 }
9053
9054 # Get the maximum xattr size supported by the filesystem.
9055 max_xattr_size() {
9056         $LCTL get_param -n llite.*.max_easize
9057 }
9058
9059 # Dump the value of the named xattr from a file.
9060 get_xattr_value() {
9061         local xattr_name=$1
9062         local file=$2
9063
9064         echo "$(getfattr -n $xattr_name --absolute-names --only-values $file)"
9065 }
9066
9067 # Generate a string with size of $size bytes.
9068 generate_string() {
9069         local size=${1:-1024} # in bytes
9070
9071         echo "$(head -c $size < /dev/zero | tr '\0' y)"
9072 }
9073
9074 reformat_external_journal() {
9075         local facet=$1
9076         local var
9077
9078         var=${facet}_JRN
9079         local varbs=${facet}_BLOCKSIZE
9080         if [ -n "${!var}" ]; then
9081                 local rcmd="do_facet $facet"
9082                 local bs=${!varbs:-$BLCKSIZE}
9083
9084                 bs="-b $bs"
9085                 echo "reformat external journal on $facet:${!var}"
9086                 ${rcmd} mke2fs -O journal_dev $bs ${!var} || return 1
9087         fi
9088 }
9089
9090 # MDT file-level backup/restore
9091 mds_backup_restore() {
9092         local facet=$1
9093         local igif=$2
9094         local devname=$(mdsdevname $(facet_number $facet))
9095         local mntpt=$(facet_mntpt brpt)
9096         local rcmd="do_facet $facet"
9097         local metadata=${TMP}/backup_restore.tgz
9098         local opts=${MDS_MOUNT_FS_OPTS}
9099         local svc=${facet}_svc
9100
9101         if ! ${rcmd} test -b ${devname}; then
9102                 opts=$(csa_add "$opts" -o loop)
9103         fi
9104
9105         echo "file-level backup/restore on $facet:${devname}"
9106
9107         # step 1: build mount point
9108         ${rcmd} mkdir -p $mntpt
9109         # step 2: cleanup old backup
9110         ${rcmd} rm -f $metadata
9111         # step 3: mount dev
9112         ${rcmd} mount -t ldiskfs $opts $devname $mntpt || return 3
9113         if [ ! -z $igif ]; then
9114                 # step 3.5: rm .lustre
9115                 ${rcmd} rm -rf $mntpt/ROOT/.lustre || return 3
9116         fi
9117         # step 4: backup metadata
9118         echo "backup data"
9119         ${rcmd} tar zcf $metadata --xattrs --xattrs-include="trusted.*" \
9120                 --sparse -C $mntpt/ . > /dev/null 2>&1 || return 4
9121         # step 5: umount
9122         ${rcmd} $UMOUNT $mntpt || return 5
9123         # step 6: reformat dev
9124         echo "reformat new device"
9125         format_mdt $(facet_number $facet)
9126         # step 7: mount dev
9127         ${rcmd} mount -t ldiskfs $opts $devname $mntpt || return 7
9128         # step 8: restore metadata
9129         echo "restore data"
9130         ${rcmd} tar zxfp $metadata --xattrs --xattrs-include="trusted.*" \
9131                 --sparse -C $mntpt > /dev/null 2>&1 || return 8
9132         # step 9: remove recovery logs
9133         echo "remove recovery logs"
9134         ${rcmd} rm -fv $mntpt/OBJECTS/* $mntpt/CATALOGS
9135         # step 10: umount dev
9136         ${rcmd} $UMOUNT $mntpt || return 10
9137         # step 11: cleanup tmp backup
9138         ${rcmd} rm -f $metaea $metadata
9139         # step 12: reset device label - it's not virgin on
9140         ${rcmd} e2label $devname ${!svc}
9141 }
9142
9143 # remove OI files
9144 mds_remove_ois() {
9145         local facet=$1
9146         local idx=$2
9147         local devname=$(mdsdevname $(facet_number $facet))
9148         local mntpt=$(facet_mntpt brpt)
9149         local rcmd="do_facet $facet"
9150         local opts=${MDS_MOUNT_FS_OPTS}
9151
9152         if ! ${rcmd} test -b ${devname}; then
9153                 opts=$(csa_add "$opts" -o loop)
9154         fi
9155
9156         echo "removing OI files on $facet: idx=${idx}"
9157
9158         # step 1: build mount point
9159         ${rcmd} mkdir -p $mntpt
9160         # step 2: mount dev
9161         ${rcmd} mount -t ldiskfs $opts $devname $mntpt || return 1
9162         if [ -z $idx ]; then
9163                 # step 3: remove all OI files
9164                 ${rcmd} rm -fv $mntpt/oi.16*
9165         elif [ $idx -lt 2 ]; then
9166                 ${rcmd} rm -fv $mntpt/oi.16.${idx}
9167         else
9168                 local i
9169
9170                 # others, rm oi.16.[idx, idx * idx, idx ** ...]
9171                 for ((i=${idx}; i<64; i=$((i * idx)))); do
9172                         ${rcmd} rm -fv $mntpt/oi.16.${i}
9173                 done
9174         fi
9175         # step 4: umount
9176         ${rcmd} $UMOUNT $mntpt || return 2
9177         # OI files will be recreated when mounted as lustre next time.
9178 }
9179
9180 # generate maloo upload-able log file name
9181 # \param logname specify unique part of file name
9182 generate_logname() {
9183         local logname=${1:-"default_logname"}
9184
9185         echo "$TESTLOG_PREFIX.$TESTNAME.$logname.$(hostname -s).log"
9186 }
9187
9188 # make directory on different MDTs
9189 test_mkdir() {
9190         local path
9191         local p_option
9192         local hash_type
9193         local hash_name=("all_char" "fnv_1a_64" "crush")
9194         local dirstripe_count=${DIRSTRIPE_COUNT:-"2"}
9195         local dirstripe_index=${DIRSTRIPE_INDEX:-$((base % $MDSCOUNT))}
9196         local OPTIND=1
9197
9198         (( $MDS1_VERSION > $(version_code 2.15.0) )) &&
9199                 hash_name+=("crush2")
9200
9201         while getopts "c:H:i:p" opt; do
9202                 case $opt in
9203                         c) dirstripe_count=$OPTARG;;
9204                         H) hash_type=$OPTARG;;
9205                         i) dirstripe_index=$OPTARG;;
9206                         p) p_option="-p";;
9207                         \?) error "only support -c -H -i -p";;
9208                 esac
9209         done
9210
9211         shift $((OPTIND - 1))
9212         [ $# -eq 1 ] || error "Only creating single directory is supported"
9213         path="$*"
9214
9215         local parent=$(dirname $path)
9216         if [ "$p_option" == "-p" ]; then
9217                 [ -d $path ] && return 0
9218                 if [ ! -d ${parent} ]; then
9219                         mkdir -p ${parent} ||
9220                                 error "mkdir parent '$parent' failed"
9221                 fi
9222         fi
9223
9224         if [ $MDSCOUNT -le 1 ] || ! is_lustre ${parent}; then
9225                 mkdir $path || error "mkdir '$path' failed"
9226         else
9227                 local mdt_index
9228
9229                 if [ $dirstripe_index -eq -1 ]; then
9230                         mdt_index=$((base % MDSCOUNT))
9231                 else
9232                         mdt_index=$dirstripe_index
9233                 fi
9234
9235                 # randomly choose hash type
9236                 [ -z "$hash_type" ] &&
9237                         hash_type=${hash_name[$((RANDOM % ${#hash_name[@]}))]}
9238
9239                 if (($MDS1_VERSION >= $(version_code 2.8.0))); then
9240                         if [ $dirstripe_count -eq -1 ]; then
9241                                 dirstripe_count=$((RANDOM % MDSCOUNT + 1))
9242                         fi
9243                 else
9244                         dirstripe_count=1
9245                 fi
9246
9247                 echo "striped dir -i$mdt_index -c$dirstripe_count -H $hash_type $path"
9248                 $LFS mkdir -i$mdt_index -c$dirstripe_count -H $hash_type $path ||
9249                         error "mkdir -i $mdt_index -c$dirstripe_count -H $hash_type $path failed"
9250         fi
9251 }
9252
9253 # free_fd: find the smallest and not in use file descriptor [above @last_fd]
9254 #
9255 # If called many times, passing @last_fd will avoid repeated searching
9256 # already-open FDs repeatedly if we know they are still in use.
9257 #
9258 # usage: free_fd [last_fd]
9259 free_fd()
9260 {
9261         local max_fd=$(ulimit -n)
9262         local fd=$((${1:-2} + 1))
9263
9264         while [[ $fd -le $max_fd && -e /proc/self/fd/$fd ]]; do
9265                 ((++fd))
9266         done
9267         [ $fd -lt $max_fd ] || error "finding free file descriptor failed"
9268         echo $fd
9269 }
9270
9271 check_mount_and_prep()
9272 {
9273         is_mounted $MOUNT || setupall
9274
9275         rm -rf $DIR/[df][0-9]* || error "Fail to cleanup the env!"
9276         mkdir_on_mdt0 $DIR/$tdir || error "Fail to mkdir $DIR/$tdir."
9277         for idx in $(seq $MDSCOUNT); do
9278                 local name="MDT$(printf '%04x' $((idx - 1)))"
9279                 rm -rf $MOUNT/.lustre/lost+found/$name/*
9280         done
9281 }
9282
9283 # calcule how many ost-objects to be created.
9284 precreated_ost_obj_count()
9285 {
9286         local mdt_idx=$1
9287         local ost_idx=$2
9288         local mdt_name="MDT$(printf '%04x' $mdt_idx)"
9289         local ost_name="OST$(printf '%04x' $ost_idx)"
9290         local proc_path="${FSNAME}-${ost_name}-osc-${mdt_name}"
9291         local last_id=$(do_facet mds$((mdt_idx + 1)) lctl get_param -n \
9292                         osp.$proc_path.prealloc_last_id)
9293         local next_id=$(do_facet mds$((mdt_idx + 1)) lctl get_param -n \
9294                         osp.$proc_path.prealloc_next_id)
9295         echo $((last_id - next_id + 1))
9296 }
9297
9298 check_file_in_pool()
9299 {
9300         local file=$1
9301         local pool=$2
9302         local tlist="$3"
9303         local res=$($LFS getstripe $file | grep 0x | cut -f2)
9304         for i in $res
9305         do
9306                 for t in $tlist ; do
9307                         [ "$i" -eq "$t" ] && continue 2
9308                 done
9309
9310                 echo "pool list: $tlist"
9311                 echo "striping: $res"
9312                 error_noexit "$file not allocated in $pool"
9313                 return 1
9314         done
9315         return 0
9316 }
9317
9318 pool_add() {
9319         echo "Creating new pool"
9320         local pool=$1
9321
9322         create_pool $FSNAME.$pool ||
9323                 { error_noexit "No pool created, result code $?"; return 1; }
9324         [ $($LFS pool_list $FSNAME | grep -c "$FSNAME.${pool}\$") -eq 1 ] ||
9325                 { error_noexit "$pool not in lfs pool_list"; return 2; }
9326 }
9327
9328 pool_add_targets() {
9329         echo "Adding targets to pool"
9330         local pool=$1
9331         local first=$2
9332         local last=$3
9333         local step=${4:-1}
9334
9335         if [ -z $last ]; then
9336                 local list=$first
9337         else
9338                 local list=$(seq $first $step $last)
9339         fi
9340
9341         local t=$(for i in $list; do printf "$FSNAME-OST%04x_UUID " $i; done)
9342         local tg=$(for i in $list;
9343                 do printf -- "-e $FSNAME-OST%04x_UUID " $i; done)
9344         local firstx=$(printf "%04x" $first)
9345         local lastx=$(printf "%04x" $last)
9346
9347         do_facet mgs $LCTL pool_add \
9348                 $FSNAME.$pool $FSNAME-OST[$firstx-$lastx/$step]
9349         # ignore EEXIST(17)
9350         if (( $? != 0 && $? != 17 )); then
9351                 error_noexit "pool_add $FSNAME-OST[$firstx-$lastx/$step] failed"
9352                 return 3
9353         fi
9354
9355         # wait for OSTs to be added to the pool
9356         for mds_id in $(seq $MDSCOUNT); do
9357                 local mdt_id=$((mds_id-1))
9358                 local lodname=$FSNAME-MDT$(printf "%04x" $mdt_id)-mdtlov
9359                 wait_update_facet mds$mds_id \
9360                         "lctl get_param -n lod.$lodname.pools.$pool |
9361                                 grep $tg | sort -u | tr '\n' ' '" "$t" || {
9362                         error_noexit "mds$mds_id: Add to pool failed"
9363                         return 2
9364                 }
9365         done
9366         wait_update $HOSTNAME "lctl get_param -n lov.$FSNAME-*.pools.$pool |
9367                         grep $tg | sort -u | tr '\n' ' ' " "$t" || {
9368                 error_noexit "Add to pool failed"
9369                 return 1
9370         }
9371 }
9372
9373 pool_set_dir() {
9374         local pool=$1
9375         local tdir=$2
9376         echo "Setting pool on directory $tdir"
9377
9378         $LFS setstripe -c 2 -p $pool $tdir && return 0
9379
9380         error_noexit "Cannot set pool $pool to $tdir"
9381         return 1
9382 }
9383
9384 pool_check_dir() {
9385         local pool=$1
9386         local tdir=$2
9387         echo "Checking pool on directory $tdir"
9388
9389         local res=$($LFS getstripe --pool $tdir | sed "s/\s*$//")
9390         [ "$res" = "$pool" ] && return 0
9391
9392         error_noexit "Pool on '$tdir' is '$res', not '$pool'"
9393         return 1
9394 }
9395
9396 pool_dir_rel_path() {
9397         echo "Testing relative path works well"
9398         local pool=$1
9399         local tdir=$2
9400         local root=$3
9401
9402         mkdir -p $root/$tdir/$tdir
9403         cd $root/$tdir
9404         pool_set_dir $pool $tdir          || return 1
9405         pool_set_dir $pool ./$tdir        || return 2
9406         pool_set_dir $pool ../$tdir       || return 3
9407         pool_set_dir $pool ../$tdir/$tdir || return 4
9408         rm -rf $tdir; cd - > /dev/null
9409 }
9410
9411 pool_alloc_files() {
9412         echo "Checking files allocation from directory pool"
9413         local pool=$1
9414         local tdir=$2
9415         local count=$3
9416         local tlist="$4"
9417
9418         local failed=0
9419         for i in $(seq -w 1 $count)
9420         do
9421                 local file=$tdir/file-$i
9422                 touch $file
9423                 check_file_in_pool $file $pool "$tlist" || \
9424                         failed=$((failed + 1))
9425         done
9426         [ "$failed" = 0 ] && return 0
9427
9428         error_noexit "$failed files not allocated in $pool"
9429         return 1
9430 }
9431
9432 pool_create_files() {
9433         echo "Creating files in pool"
9434         local pool=$1
9435         local tdir=$2
9436         local count=$3
9437         local tlist="$4"
9438
9439         mkdir -p $tdir
9440         local failed=0
9441         for i in $(seq -w 1 $count)
9442         do
9443                 local file=$tdir/spoo-$i
9444                 $LFS setstripe -p $pool $file
9445                 check_file_in_pool $file $pool "$tlist" || \
9446                         failed=$((failed + 1))
9447         done
9448         [ "$failed" = 0 ] && return 0
9449
9450         error_noexit "$failed files not allocated in $pool"
9451         return 1
9452 }
9453
9454 pool_lfs_df() {
9455         echo "Checking 'lfs df' output"
9456         local pool=$1
9457
9458         local t=$($LCTL get_param -n lov.$FSNAME-clilov-*.pools.$pool |
9459                         tr '\n' ' ')
9460         local res=$($LFS df --pool $FSNAME.$pool |
9461                         awk '{print $1}' |
9462                         grep "$FSNAME-OST" |
9463                         tr '\n' ' ')
9464         [ "$res" = "$t" ] && return 0
9465
9466         error_noexit "Pools OSTs '$t' is not '$res' that lfs df reports"
9467         return 1
9468 }
9469
9470 pool_file_rel_path() {
9471         echo "Creating files in a pool with relative pathname"
9472         local pool=$1
9473         local tdir=$2
9474
9475         mkdir -p $tdir ||
9476                 { error_noexit "unable to create $tdir"; return 1 ; }
9477         local file="/..$tdir/$tfile-1"
9478         $LFS setstripe -p $pool $file ||
9479                 { error_noexit "unable to create $file" ; return 2 ; }
9480
9481         cd $tdir
9482         $LFS setstripe -p $pool $tfile-2 || {
9483                 error_noexit "unable to create $tfile-2 in $tdir"
9484                 return 3
9485         }
9486 }
9487
9488 pool_remove_first_target() {
9489         echo "Removing first target from a pool"
9490         pool_remove_target $1 -1
9491 }
9492
9493 pool_remove_target() {
9494         local pool=$1
9495         local index=$2
9496
9497         local pname="lov.$FSNAME-*.pools.$pool"
9498         if [ $index -eq -1 ]; then
9499                 local t=$($LCTL get_param -n $pname | head -1)
9500         else
9501                 local t=$(printf "$FSNAME-OST%04x_UUID" $index)
9502         fi
9503
9504         echo "Removing $t from $pool"
9505         do_facet mgs $LCTL pool_remove $FSNAME.$pool $t
9506         for mds_id in $(seq $MDSCOUNT); do
9507                 local mdt_id=$((mds_id-1))
9508                 local lodname=$FSNAME-MDT$(printf "%04x" $mdt_id)-mdtlov
9509                 wait_update_facet mds$mds_id \
9510                         "lctl get_param -n lod.$lodname.pools.$pool |
9511                                 grep $t" "" || {
9512                         error_noexit "mds$mds_id: $t not removed from" \
9513                         "$FSNAME.$pool"
9514                         return 2
9515                 }
9516         done
9517         wait_update $HOSTNAME "lctl get_param -n $pname | grep $t" "" || {
9518                 error_noexit "$t not removed from $FSNAME.$pool"
9519                 return 1
9520         }
9521 }
9522
9523 pool_remove_all_targets() {
9524         echo "Removing all targets from pool"
9525         local pool=$1
9526         local file=$2
9527         local pname="lov.$FSNAME-*.pools.$pool"
9528         for t in $($LCTL get_param -n $pname | sort -u)
9529         do
9530                 do_facet mgs $LCTL pool_remove $FSNAME.$pool $t
9531         done
9532         for mds_id in $(seq $MDSCOUNT); do
9533                 local mdt_id=$((mds_id-1))
9534                 local lodname=$FSNAME-MDT$(printf "%04x" $mdt_id)-mdtlov
9535                 wait_update_facet mds$mds_id "lctl get_param -n \
9536                         lod.$lodname.pools.$pool" "" || {
9537                         error_noexit "mds$mds_id: Pool $pool not drained"
9538                         return 4
9539                 }
9540         done
9541         wait_update $HOSTNAME "lctl get_param -n $pname" "" || {
9542                 error_noexit "Pool $FSNAME.$pool cannot be drained"
9543                 return 1
9544         }
9545         # striping on an empty/nonexistant pool should fall back
9546         # to "pool of everything"
9547         touch $file || {
9548                 error_noexit "failed to use fallback striping for empty pool"
9549                 return 2
9550         }
9551         # setstripe on an empty pool should fail
9552         $LFS setstripe -p $pool $file 2>/dev/null && {
9553                 error_noexit "expected failure when creating file" \
9554                                                         "with empty pool"
9555                 return 3
9556         }
9557         return 0
9558 }
9559
9560 pool_remove() {
9561         echo "Destroying pool"
9562         local pool=$1
9563         local file=$2
9564
9565         do_facet mgs $LCTL pool_destroy $FSNAME.$pool
9566
9567         sleep 2
9568         # striping on an empty/nonexistant pool should fall back
9569         # to "pool of everything"
9570         touch $file || {
9571                 error_noexit "failed to use fallback striping for missing pool"
9572                 return 1
9573         }
9574         # setstripe on an empty pool should fail
9575         $LFS setstripe -p $pool $file 2>/dev/null && {
9576                 error_noexit "expected failure when creating file" \
9577                                                         "with missing pool"
9578                 return 2
9579         }
9580
9581         # get param should return err once pool is gone
9582         if wait_update $HOSTNAME "lctl get_param -n \
9583                 lov.$FSNAME-*.pools.$pool 2>/dev/null || echo foo" "foo"
9584         then
9585                 remove_pool_from_list $FSNAME.$pool
9586                 return 0
9587         fi
9588         error_noexit "Pool $FSNAME.$pool is not destroyed"
9589         return 3
9590 }
9591
9592 # Get and check the actual stripe count of one file.
9593 # Usage: check_stripe_count <file> <expected_stripe_count>
9594 check_stripe_count() {
9595         local file=$1
9596         local expected=$2
9597         local actual
9598
9599         [[ -z "$file" || -z "$expected" ]] &&
9600                 error "check_stripe_count: invalid argument"
9601
9602         local cmd="$LFS getstripe -c $file"
9603         actual=$($cmd) || error "$cmd failed"
9604         actual=${actual%% *}
9605
9606         if [[ $actual -ne $expected ]]; then
9607                 [[ $expected -eq -1 ]] || { $LFS getstripe $file;
9608                         error "$cmd not expected ($expected): found $actual"; }
9609                 [[ $actual -eq $OSTCOUNT ]] || { $LFS getstripe $file;
9610                         error "$cmd not OST count ($OSTCOUNT): found $actual"; }
9611         fi
9612 }
9613
9614 # Get and check the actual list of OST indices on one file.
9615 # Usage: check_obdidx <file> <expected_comma_separated_list_of_ost_indices>
9616 check_obdidx() {
9617         local file=$1
9618         local expected=$2
9619         local obdidx
9620
9621         [[ -z "$file" || -z "$expected" ]] &&
9622                 error "check_obdidx: invalid argument!"
9623
9624         obdidx=$(comma_list $($LFS getstripe $file | grep -A $OSTCOUNT obdidx |
9625                               grep -v obdidx | awk '{print $1}' | xargs))
9626
9627         [[ $obdidx = $expected ]] ||
9628                 error "list of OST indices on $file is $obdidx," \
9629                       "should be $expected"
9630 }
9631
9632 # Get and check the actual OST index of the first stripe on one file.
9633 # Usage: check_start_ost_idx <file> <expected_start_ost_idx>
9634 check_start_ost_idx() {
9635         local file=$1
9636         local expected=$2
9637         local start_ost_idx
9638
9639         [[ -z "$file" || -z "$expected" ]] &&
9640                 error "check_start_ost_idx: invalid argument!"
9641
9642         start_ost_idx=$($LFS getstripe $file | grep -A 1 obdidx |
9643                          grep -v obdidx | awk '{print $1}')
9644
9645         [[ $start_ost_idx = $expected ]] ||
9646                 error "OST index of the first stripe on $file is" \
9647                       "$start_ost_idx, should be $expected"
9648 }
9649
9650 killall_process () {
9651         local clients=${1:-$(hostname)}
9652         local name=$2
9653         local signal=$3
9654         local rc=0
9655
9656         do_nodes $clients "killall $signal $name"
9657 }
9658
9659 lsnapshot_create()
9660 {
9661         do_facet mgs "$LCTL snapshot_create -F $FSNAME $*"
9662 }
9663
9664 lsnapshot_destroy()
9665 {
9666         do_facet mgs "$LCTL snapshot_destroy -F $FSNAME $*"
9667 }
9668
9669 lsnapshot_modify()
9670 {
9671         do_facet mgs "$LCTL snapshot_modify -F $FSNAME $*"
9672 }
9673
9674 lsnapshot_list()
9675 {
9676         do_facet mgs "$LCTL snapshot_list -F $FSNAME $*"
9677 }
9678
9679 lsnapshot_mount()
9680 {
9681         do_facet mgs "$LCTL snapshot_mount -F $FSNAME $*"
9682 }
9683
9684 lsnapshot_umount()
9685 {
9686         do_facet mgs "$LCTL snapshot_umount -F $FSNAME $*"
9687 }
9688
9689 lss_err()
9690 {
9691         local msg=$1
9692
9693         do_facet mgs "cat $LSNAPSHOT_LOG"
9694         error $msg
9695 }
9696
9697 lss_cleanup()
9698 {
9699         echo "Cleaning test environment ..."
9700
9701         # Every lsnapshot command takes exclusive lock with others,
9702         # so can NOT destroy the snapshot during list with 'xargs'.
9703         while true; do
9704                 local ssname=$(lsnapshot_list | grep snapshot_name |
9705                         grep lss_ | awk '{ print $2 }' | head -n 1)
9706                 [ -z "$ssname" ] && break
9707
9708                 lsnapshot_destroy -n $ssname -f ||
9709                         lss_err "Fail to destroy $ssname by force"
9710         done
9711 }
9712
9713 lss_gen_conf_one()
9714 {
9715         local facet=$1
9716         local role=$2
9717         local idx=$3
9718
9719         local host=$(facet_active_host $facet)
9720         local dir=$(dirname $(facet_vdevice $facet))
9721         local pool=$(zpool_name $facet)
9722         local lfsname=$(zfs_local_fsname $facet)
9723         local label=${FSNAME}-${role}$(printf '%04x' $idx)
9724
9725         do_facet mgs \
9726                 "echo '$host - $label zfs:${dir}/${pool}/${lfsname} - -' >> \
9727                 $LSNAPSHOT_CONF"
9728 }
9729
9730 lss_gen_conf()
9731 {
9732         do_facet mgs "rm -f $LSNAPSHOT_CONF"
9733         echo "Generating $LSNAPSHOT_CONF on MGS ..."
9734
9735         if ! combined_mgs_mds ; then
9736                 [ $(facet_fstype mgs) != zfs ] &&
9737                         skip "Lustre snapshot 1 only works for ZFS backend"
9738
9739                 local host=$(facet_active_host mgs)
9740                 local dir=$(dirname $(facet_vdevice mgs))
9741                 local pool=$(zpool_name mgs)
9742                 local lfsname=$(zfs_local_fsname mgs)
9743
9744                 do_facet mgs \
9745                         "echo '$host - MGS zfs:${dir}/${pool}/${lfsname} - -' \
9746                         >> $LSNAPSHOT_CONF" || lss_err "generate lss conf (mgs)"
9747         fi
9748
9749         for num in `seq $MDSCOUNT`; do
9750                 [ $(facet_fstype mds$num) != zfs ] &&
9751                         skip "Lustre snapshot 1 only works for ZFS backend"
9752
9753                 lss_gen_conf_one mds$num MDT $((num - 1)) ||
9754                         lss_err "generate lss conf (mds$num)"
9755         done
9756
9757         for num in `seq $OSTCOUNT`; do
9758                 [ $(facet_fstype ost$num) != zfs ] &&
9759                         skip "Lustre snapshot 1 only works for ZFS backend"
9760
9761                 lss_gen_conf_one ost$num OST $((num - 1)) ||
9762                         lss_err "generate lss conf (ost$num)"
9763         done
9764
9765         do_facet mgs "cat $LSNAPSHOT_CONF"
9766 }
9767
9768 # Parse 'lfs getstripe -d <path_with_dir_name>' for non-composite dir
9769 parse_plain_dir_param()
9770 {
9771         local invalues=($1)
9772         local param=""
9773
9774         if [[ ${invalues[0]} =~ "stripe_count:" ]]; then
9775                 param="-c ${invalues[1]}"
9776         fi
9777         if [[ ${invalues[2]} =~ "stripe_size:" ]]; then
9778                 param="$param -S ${invalues[3]}"
9779         fi
9780         if [[ ${invalues[4]} =~ "pattern:" ]]; then
9781                 if [[ ${invalues[5]} =~ "stripe_offset:" ]]; then
9782                         param="$param -i ${invalues[6]}"
9783                 else
9784                         param="$param -L ${invalues[5]} -i ${invalues[7]}"
9785                 fi
9786         elif [[ ${invalues[4]} =~ "stripe_offset:" ]]; then
9787                 param="$param -i ${invalues[5]}"
9788         fi
9789         echo "$param"
9790 }
9791
9792 parse_plain_param()
9793 {
9794         local line=$1
9795         local val=$(awk '{print $2}' <<< $line)
9796
9797         if [[ $line =~ ^"lmm_stripe_count:" ]]; then
9798                 echo "-c $val"
9799         elif [[ $line =~ ^"lmm_stripe_size:" ]]; then
9800                 echo "-S $val"
9801         elif [[ $line =~ ^"lmm_stripe_offset:" && $SKIP_INDEX != yes ]]; then
9802                 echo "-i $val"
9803         elif [[ $line =~ ^"lmm_pattern:" ]]; then
9804                 echo "-L $val"
9805         fi
9806 }
9807
9808 parse_layout_param()
9809 {
9810         local mode=""
9811         local val=""
9812         local param=""
9813
9814         while read line; do
9815                 if [[ ! -z $line ]]; then
9816                         if [[ -z $mode ]]; then
9817                                 if [[ $line =~ ^"stripe_count:" ]]; then
9818                                         mode="plain_dir"
9819                                 elif [[ $line =~ ^"lmm_stripe_count:" ]]; then
9820                                         mode="plain_file"
9821                                 elif [[ $line =~ ^"lcm_layout_gen:" ]]; then
9822                                         mode="pfl"
9823                                 fi
9824                         fi
9825
9826                         if [[ $mode = "plain_dir" ]]; then
9827                                 param=$(parse_plain_dir_param "$line")
9828                         elif [[ $mode = "plain_file" ]]; then
9829                                 val=$(parse_plain_param "$line")
9830                                 [[ ! -z $val ]] && param="$param $val"
9831                         elif [[ $mode = "pfl" ]]; then
9832                                 val=$(echo $line | awk '{print $2}')
9833                                 if [[ $line =~ ^"lcme_extent.e_end:" ]]; then
9834                                         if [[ $val = "EOF" ]]; then
9835                                                 param="$param -E -1"
9836                                         else
9837                                                 param="$param -E $val"
9838                                         fi
9839                                 elif [[ $line =~ ^"stripe_count:" ]]; then
9840                                         # pfl dir
9841                                         val=$(parse_plain_dir_param "$line")
9842                                         param="$param $val"
9843                                 else
9844                                         #pfl file
9845                                         val=$(parse_plain_param "$line")
9846                                         [[ ! -z $val ]] && param="$param $val"
9847                                 fi
9848                         fi
9849                 fi
9850         done
9851         echo "$param"
9852 }
9853
9854 get_layout_param()
9855 {
9856         local param=$($LFS getstripe -d $1 | parse_layout_param)
9857         echo "$param"
9858 }
9859
9860 lfsck_verify_pfid()
9861 {
9862         local f
9863         local rc=0
9864
9865         # Cancel locks before setting lfsck_verify_pfid so that errors are more
9866         # controllable
9867         cancel_lru_locks mdc
9868         cancel_lru_locks osc
9869
9870         # make sure PFID is set correctly for files
9871         do_nodes $(comma_list $(osts_nodes)) \
9872                "$LCTL set_param -n obdfilter.${FSNAME}-OST*.lfsck_verify_pfid=1"
9873
9874         for f in "$@"; do
9875                 cat $f &> /dev/nullA ||
9876                         { rc=$?; echo "verify $f failed"; break; }
9877         done
9878
9879         do_nodes $(comma_list $(osts_nodes)) \
9880                "$LCTL set_param -n obdfilter.${FSNAME}-OST*.lfsck_verify_pfid=0"
9881         return $rc
9882 }
9883
9884 # check that clients "oscs" was evicted after "before"
9885 check_clients_evicted() {
9886         local before=$1
9887         shift
9888         local oscs=${@}
9889         local osc
9890         local rc=0
9891
9892         for osc in $oscs; do
9893                 echo "Check state for $osc"
9894                 local evicted=$(do_facet client $LCTL get_param osc.$osc.state |
9895                         tail -n 5 | awk -F"[ ,]" \
9896                         '/EVICTED/ { if (mx<$4) { mx=$4; } } END { print mx }')
9897                 if (($? == 0)) && (($evicted > $before)); then
9898                         echo "$osc is evicted at $evicted"
9899                 else
9900                         ((rc++))
9901                         echo "$osc was not evicted after $before:"
9902                         do_facet client $LCTL get_param osc.$osc.state |
9903                                 tail -n 8
9904                 fi
9905         done
9906
9907         [ $rc -eq 0 ] || error "client not evicted from OST"
9908 }
9909
9910 # check that clients OSCS current_state is FULL
9911 check_clients_full() {
9912         local timeout=$1
9913         shift
9914         local oscs=${@}
9915
9916         for osc in $oscs; do
9917                 wait_update_facet client \
9918                         "lctl get_param -n osc.$osc.state |
9919                         grep 'current_state: FULL'" \
9920                         "current_state: FULL" $timeout
9921                 [ $? -eq 0 ] || error "$osc state is not FULL"
9922         done
9923 }
9924
9925 #Changelogs
9926 __changelog_deregister() {
9927         local facet=$1
9928         local mdt="$(facet_svc $facet)"
9929         local cl_user=$2
9930         local rc=0
9931
9932         # skip cleanup if no user registered for this MDT
9933         [ -z "$cl_user" ] && echo "$mdt: no changelog user" && return 0
9934         # user is no longer registered, skip cleanup
9935         changelog_users "$facet" | grep -q "$cl_user" ||
9936                 { echo "$mdt: changelog user '$cl_user' not found"; return 0; }
9937
9938         # From this point, if any operation fails, it is an error
9939         __changelog_clear $facet $cl_user 0 ||
9940                 error_noexit "$mdt: changelog_clear $cl_user 0 fail: $rc"
9941         do_facet $facet $LCTL --device $mdt changelog_deregister $cl_user ||
9942                 error_noexit "$mdt: changelog_deregister '$cl_user' fail: $rc"
9943 }
9944
9945 declare -Ax CL_USERS
9946 changelog_register() {
9947         for M in $(seq $MDSCOUNT); do
9948                 local facet=mds$M
9949                 local mdt="$(facet_svc $facet)"
9950                 local cl_mask
9951
9952                 cl_mask=$(do_facet $facet $LCTL get_param \
9953                              mdd.${mdt}.changelog_mask -n)
9954                 stack_trap "do_facet $facet $LCTL \
9955                         set_param mdd.$mdt.changelog_mask=\'$cl_mask\' -n" EXIT
9956                 do_facet $facet $LCTL set_param mdd.$mdt.changelog_mask=+hsm ||
9957                         error "$mdt: changelog_mask=+hsm failed: $?"
9958
9959                 local cl_user
9960                 cl_user=$(do_facet $facet $LCTL --device $mdt \
9961                         changelog_register -n "$@") ||
9962                         error "$mdt: register changelog user failed: $?"
9963                 stack_trap "__changelog_deregister $facet $cl_user" EXIT
9964
9965                 stack_trap "CL_USERS[$facet]='${CL_USERS[$facet]}'" EXIT
9966                 # Bash does not support nested arrays, but the format of a
9967                 # cl_user is constrained enough to use whitespaces as separators
9968                 CL_USERS[$facet]+="$cl_user "
9969         done
9970         echo "Registered $MDSCOUNT changelog users: '${CL_USERS[*]% }'"
9971 }
9972
9973 changelog_deregister() {
9974         local cl_user
9975         # bash assoc arrays do not guarantee to list keys in created order
9976         # so reorder to get same order than in changelog_register()
9977         local cl_facets=$(echo "${!CL_USERS[@]}" | tr " " "\n" | sort |
9978                           tr "\n" " ")
9979
9980         for facet in $cl_facets; do
9981                 for cl_user in ${CL_USERS[$facet]}; do
9982                         __changelog_deregister $facet $cl_user || return $?
9983                 done
9984                 unset CL_USERS[$facet]
9985         done
9986 }
9987
9988 changelog_users() {
9989         local facet=$1
9990         local service=$(facet_svc $facet)
9991
9992         do_facet $facet $LCTL get_param -n mdd.$service.changelog_users
9993 }
9994
9995 changelog_user_rec() {
9996         local facet=$1
9997         local cl_user=$2
9998         local service=$(facet_svc $facet)
9999
10000         changelog_users $facet | awk '$1 == "'$cl_user'" { print $2 }'
10001 }
10002
10003 changelog_chmask() {
10004         local mask=$1
10005
10006         do_nodes $(comma_list $(mdts_nodes)) \
10007                 $LCTL set_param mdd.*.changelog_mask="$mask"
10008 }
10009
10010 # usage: __changelog_clear FACET CL_USER [+]INDEX
10011 __changelog_clear()
10012 {
10013         local facet=$1
10014         local mdt="$(facet_svc $facet)"
10015         local cl_user=$2
10016         local -i rec
10017
10018         case "$3" in
10019         +*)
10020                 # Remove the leading '+'
10021                 rec=${3:1}
10022                 rec+=$(changelog_user_rec $facet $cl_user)
10023                 ;;
10024         *)
10025                 rec=$3
10026                 ;;
10027         esac
10028
10029         if [ $rec -eq 0 ]; then
10030                 echo "$mdt: clear the changelog for $cl_user of all records"
10031         else
10032                 echo "$mdt: clear the changelog for $cl_user to record #$rec"
10033         fi
10034         $LFS changelog_clear $mdt $cl_user $rec
10035 }
10036
10037 # usage: changelog_clear [+]INDEX [facet]...
10038 #
10039 # If INDEX is prefixed with '+', increment every changelog user's record index
10040 # by INDEX. Otherwise, clear the changelog up to INDEX for every changelog
10041 # users.
10042 changelog_clear() {
10043         local rc
10044         local idx=$1
10045         shift
10046         local cl_facets="$@"
10047         # bash assoc arrays do not guarantee to list keys in created order
10048         # so reorder to get same order than in changelog_register()
10049         [[ -n "$cl_facets" ]] ||
10050                 cl_facets=$(echo "${!CL_USERS[@]}" | tr " " "\n" | sort |
10051                         tr "\n" " ")
10052         local cl_user
10053
10054         for facet in $cl_facets; do
10055                 for cl_user in ${CL_USERS[$facet]}; do
10056                         __changelog_clear $facet $cl_user $idx || rc=${rc:-$?}
10057                 done
10058         done
10059
10060         return ${rc:-0}
10061 }
10062
10063 changelog_dump() {
10064         local rc
10065
10066         for M in $(seq $MDSCOUNT); do
10067                 local facet=mds$M
10068                 local mdt="$(facet_svc $facet)"
10069                 local output
10070                 local ret
10071
10072                 output=$($LFS changelog $mdt)
10073                 ret=$?
10074                 if [ $ret -ne 0 ]; then
10075                         rc=${rc:-$ret}
10076                 elif [ -n "$output" ]; then
10077                         echo "$output" | sed -e 's/^/'$mdt'./'
10078                 fi
10079         done
10080
10081         return ${rc:-0}
10082 }
10083
10084 changelog_extract_field() {
10085         local cltype=$1
10086         local file=$2
10087         local identifier=$3
10088
10089         changelog_dump | gawk "/$cltype.*$file$/ {
10090                 print gensub(/^.* "$identifier'(\[[^\]]*\]).*$/,"\\1",1)}' |
10091                 tail -1
10092 }
10093
10094 # Prints a changelog record produced by "lfs changelog" as an associative array
10095 #
10096 # Example:
10097 # $> changelog2array 16 01CREAT 10:28:46.968438800 2018.03.09 0x0 \
10098 #                    t=[0x200000401:0x10:0x0] j=touch.501 ef=0xf u=501:501 \
10099 #                    nid=0@lo p=[0x200000007:0x1:0x0] blob
10100 # ([index]='16' [type]='CREAT' [time]='10:28:46.968438800'
10101 #  [date]='2018.03.09' [flags]=0x0 ['target-fid']='[0x200000401:0x10:0x0]'
10102 #  ['jobid']='touch.501' ['extra-flags']='0x0f' [uid]='0' ['gid']='0'
10103 #  ['nid']='0@lo' ['parent-fid']='[0x200000007:0x1:0x0]')
10104 #
10105 # Note that the changelog record is not quoted
10106 # Also note that the line breaks in the output were only added for readability
10107 #
10108 # Typically, you want to eval the output of the command to fill an actual
10109 # associative array, like this:
10110 # $> eval declare -A changelog=$(changelog2array $entry)
10111 #
10112 # It can then be accessed like any bash associative array:
10113 # $> echo "${changelog[index]}" "${changelog[type]}" "${changelog[flags]}"
10114 # 16 CREAT 0x0
10115 # $> echo "${changelog[uid]}":"${changelog[gid]}"
10116 # 501:501
10117 #
10118 changelog2array()
10119 {
10120         # Start the array
10121         printf '('
10122
10123         # A changelog, as printed by "lfs changelog" typically looks like this:
10124         # <index> <type> <time> <date> <flags> <key1=value1> <key2=value2> ...
10125
10126         # Parse the positional part of the changelog
10127
10128         # changelog_dump() prefixes records with their mdt's name
10129         local index="${1##*.}"
10130
10131         printf "[index]='%s' [type]='%s' [time]='%s' [date]='%s' [flags]='%s'" \
10132                "$index" "${2:2}" "$3" "$4" "$5"
10133
10134         # Parse the key/value part of the changelog
10135         for arg in "${@:5}"; do
10136                 # Check it matches a key=value syntax
10137                 [[ "$arg" =~ ^[[:alpha:]]+= ]] || continue
10138
10139                 local key="${arg%%=*}"
10140                 local value="${arg#*=}"
10141
10142                 case "$key" in
10143                 u)
10144                         # u is actually for uid AND gid: u=UID:GID
10145                         printf " [uid]='%s'" "${value%:*}"
10146                         key=gid
10147                         value="${value#*:}"
10148                         ;;
10149                 t)
10150                         key=target-fid
10151                         value="${value#[}"
10152                         value="${value%]}"
10153                         ;;
10154                 j)
10155                         key=jobid
10156                         ;;
10157                 p)
10158                         key=parent-fid
10159                         value="${value#[}"
10160                         value="${value%]}"
10161                         ;;
10162                 ef)
10163                         key=extra-flags
10164                         ;;
10165                 m)
10166                         key=mode
10167                         ;;
10168                 x)
10169                         key=xattr
10170                         ;;
10171                 *)
10172                         ;;
10173                 esac
10174
10175                 printf " ['%s']='%s'" "$key" "$value"
10176         done
10177
10178         # end the array
10179         printf ')'
10180 }
10181
10182 # Format and print a changelog record
10183 #
10184 # Interpreted sequences are:
10185 #       %%      a single %
10186 #       %f      the "flags" attribute of a changelog record
10187 __changelog_printf()
10188 {
10189         local format="$1"
10190
10191         local -i i
10192         for ((i = 0; i < ${#format}; i++)); do
10193                 local char="${format:$i:1}"
10194                 if [ "$char" != % ]; then
10195                         printf '%c' "$char"
10196                         continue
10197                 fi
10198
10199                 i+=1
10200                 char="${format:$i:1}"
10201                 case "$char" in
10202                 f)
10203                         printf '%s' "${changelog[flags]}"
10204                         ;;
10205                 %)
10206                         printf '%'
10207                         ;;
10208                 esac
10209         done
10210         printf '\n'
10211 }
10212
10213 # Filter changelog records
10214 changelog_find()
10215 {
10216         local -A filter
10217         local action='print'
10218         local format
10219
10220         while [ $# -gt 0 ]; do
10221                 case "$1" in
10222                 -print)
10223                         action='print'
10224                         ;;
10225                 -printf)
10226                         action='printf'
10227                         format="$2"
10228                         shift
10229                         ;;
10230                 -*)
10231                         filter[${1#-}]="$2"
10232                         shift
10233                         ;;
10234                 esac
10235                 shift
10236         done
10237
10238         local found=false
10239         local record
10240         changelog_dump | { while read -r record; do
10241                 eval local -A changelog=$(changelog2array $record)
10242                 for key in "${!filter[@]}"; do
10243                         case "$key" in
10244                         *)
10245                                 [ "${changelog[$key]}" == "${filter[$key]}" ]
10246                                 ;;
10247                         esac || continue 2
10248                 done
10249
10250                 found=true
10251
10252                 case "${action:-print}" in
10253                 print)
10254                         printf '%s\n' "$record"
10255                         ;;
10256                 printf)
10257                         __changelog_printf "$format"
10258                         ;;
10259                 esac
10260         done; $found; }
10261 }
10262
10263 restore_layout() {
10264         local dir=$1
10265         local layout=$2
10266
10267         [ ! -d "$dir" ] && return
10268
10269         [ -z "$layout" ] && {
10270                 $LFS setstripe -d $dir || error "error deleting stripe '$dir'"
10271                 return
10272         }
10273
10274         setfattr -n trusted.lov -v $layout $dir ||
10275                 error "error restoring layout '$layout' to '$dir'"
10276 }
10277
10278 # save the layout of a directory, the returned string will be used by
10279 # restore_layout() to restore the layout
10280 save_layout() {
10281         local dir=$1
10282         local str=$(getfattr -n trusted.lov --absolute-names -e hex $dir \
10283                     2> /dev/null | awk -F'=' '/trusted.lov/{ print $2 }')
10284         echo "$str"
10285 }
10286
10287 # save layout of a directory and restore it at exit
10288 save_layout_restore_at_exit() {
10289         local dir=$1
10290         local layout=$(save_layout $dir)
10291
10292         stack_trap "restore_layout $dir $layout" EXIT
10293 }
10294
10295 verify_yaml_layout() {
10296         local src=$1
10297         local dst=$2
10298         local temp=$3
10299         local msg_prefix=$4
10300
10301         echo "getstripe --yaml $src"
10302         $LFS getstripe --yaml $src > $temp || error "getstripe $src failed"
10303         echo "setstripe --yaml=$temp $dst"
10304         $LFS setstripe --yaml=$temp $dst|| error "setstripe $dst failed"
10305
10306         echo "compare"
10307         local layout1=$(get_layout_param $src)
10308         local layout2=$(get_layout_param $dst)
10309         # compare their layout info
10310         [ "$layout1" == "$layout2" ] ||
10311                 error "$msg_prefix $src/$dst layouts are not equal"
10312 }
10313
10314 is_project_quota_supported() {
10315         $ENABLE_PROJECT_QUOTAS || return 1
10316         [[ -z "$SAVE_PROJECT_SUPPORTED" ]] || return $SAVE_PROJECT_SUPPORTED
10317         local save_project_supported=1
10318
10319         [[ "$(facet_fstype $SINGLEMDS)" == "ldiskfs" &&
10320            $(lustre_version_code $SINGLEMDS) -gt $(version_code 2.9.55) ]] &&
10321                 do_facet mds1 lfs --list-commands |& grep -q project &&
10322                         save_project_supported=0
10323
10324         [[ "$(facet_fstype $SINGLEMDS)" == "zfs" &&
10325            $(lustre_version_code $SINGLEMDS) -gt $(version_code 2.10.53) ]] &&
10326                 do_facet mds1 $ZPOOL get all | grep -q project_quota &&
10327                         save_project_supported=0
10328
10329         # cache state of project quotas once instead of re-checking each time
10330         export SAVE_PROJECT_SUPPORTED=$save_project_supported
10331         echo "using SAVE_PROJECT_SUPPORTED=$SAVE_PROJECT_SUPPORTED"
10332
10333         return $save_project_supported
10334 }
10335
10336 # ZFS project quota enable/disable:
10337 #   This  feature  will  become  active as soon as it is enabled and will never
10338 #   return to being disabled. Each filesystem will be upgraded automatically
10339 #   when remounted or when [a] new file is created under that filesystem. The
10340 #   upgrade can also be triggered on filesystems via `zfs set version=current
10341 #   <pool/fs>`. The upgrade process runs in the background and may take a
10342 #   while to complete for the filesystems containing a large number of files.
10343 enable_project_quota() {
10344         is_project_quota_supported || return 0
10345         local zkeeper=${KEEP_ZPOOL}
10346         stack_trap "KEEP_ZPOOL=$zkeeper" EXIT
10347         KEEP_ZPOOL="true"
10348         stopall || error "failed to stopall (1)"
10349
10350         local zfeat_en="feature@project_quota=enabled"
10351         for facet in $(seq -f mds%g $MDSCOUNT) $(seq -f ost%g $OSTCOUNT); do
10352                 local facet_fstype=${facet:0:3}1_FSTYPE
10353                 local devname
10354
10355                 if [ "${!facet_fstype}" = "zfs" ]; then
10356                         devname=$(zpool_name ${facet})
10357                         do_facet ${facet} $ZPOOL set "$zfeat_en" $devname ||
10358                                 error "$ZPOOL set $zfeat_en $devname"
10359                 else
10360                         [ ${facet:0:3} == "mds" ] &&
10361                                 devname=$(mdsdevname ${facet:3}) ||
10362                                 devname=$(ostdevname ${facet:3})
10363                         do_facet ${facet} $TUNE2FS -O project $devname ||
10364                                 error "tune2fs $devname failed"
10365                 fi
10366         done
10367
10368         KEEP_ZPOOL="${zkeeper}"
10369         mount
10370         setupall
10371 }
10372
10373 disable_project_quota() {
10374         is_project_quota_supported || return 0
10375         [ "$mds1_FSTYPE" != "ldiskfs" ] && return 0
10376         stopall || error "failed to stopall (1)"
10377
10378         for num in $(seq $MDSCOUNT); do
10379                 do_facet mds$num $TUNE2FS -Q ^prj $(mdsdevname $num) ||
10380                         error "tune2fs $(mdsdevname $num) failed"
10381         done
10382
10383         for num in $(seq $OSTCOUNT); do
10384                 do_facet ost$num $TUNE2FS -Q ^prj $(ostdevname $num) ||
10385                         error "tune2fs $(ostdevname $num) failed"
10386         done
10387
10388         mount
10389         setupall
10390 }
10391
10392 #
10393 # In order to test multiple remote HSM agents, a new facet type named "AGT" and
10394 # the following associated variables are added:
10395 #
10396 # AGTCOUNT: number of agents
10397 # AGTDEV{N}: target HSM mount point (root path of the backend)
10398 # agt{N}_HOST: hostname of the agent agt{N}
10399 # SINGLEAGT: facet of the single agent
10400 #
10401 # The number of agents is initialized as the number of remote client nodes.
10402 # By default, only single copytool is started on a remote client/agent. If there
10403 # was no remote client, then the copytool will be started on the local client.
10404 #
10405 init_agt_vars() {
10406         local n
10407         local agent
10408
10409         export AGTCOUNT=${AGTCOUNT:-$((CLIENTCOUNT - 1))}
10410         [[ $AGTCOUNT -gt 0 ]] || AGTCOUNT=1
10411
10412         export SHARED_DIRECTORY=${SHARED_DIRECTORY:-$TMP}
10413         if [[ $CLIENTCOUNT -gt 1 ]] &&
10414                 ! check_shared_dir $SHARED_DIRECTORY $CLIENTS; then
10415                 skip_env "SHARED_DIRECTORY should be accessible"\
10416                          "on all client nodes"
10417                 exit 0
10418         fi
10419
10420         # We used to put the HSM archive in $SHARED_DIRECTORY but that
10421         # meant NFS issues could hose sanity-hsm sessions. So now we
10422         # use $TMP instead.
10423         for n in $(seq $AGTCOUNT); do
10424                 eval export AGTDEV$n=\$\{AGTDEV$n:-"$TMP/arc$n"\}
10425                 agent=CLIENT$((n + 1))
10426                 if [[ -z "${!agent}" ]]; then
10427                         [[ $CLIENTCOUNT -eq 1 ]] && agent=CLIENT1 ||
10428                                 agent=CLIENT2
10429                 fi
10430                 eval export agt${n}_HOST=\$\{agt${n}_HOST:-${!agent}\}
10431                 local var=agt${n}_HOST
10432                 [[ ! -z "${!var}" ]] || error "agt${n}_HOST is empty!"
10433         done
10434
10435         export SINGLEAGT=${SINGLEAGT:-agt1}
10436
10437         export HSMTOOL=${HSMTOOL:-"lhsmtool_posix"}
10438         export HSMTOOL_PID_FILE=${HSMTOOL_PID_FILE:-"/var/run/lhsmtool_posix.pid"}
10439         export HSMTOOL_VERBOSE=${HSMTOOL_VERBOSE:-""}
10440         export HSMTOOL_UPDATE_INTERVAL=${HSMTOOL_UPDATE_INTERVAL:=""}
10441         export HSMTOOL_EVENT_FIFO=${HSMTOOL_EVENT_FIFO:=""}
10442         export HSMTOOL_TESTDIR
10443         export HSMTOOL_ARCHIVE_FORMAT=${HSMTOOL_ARCHIVE_FORMAT:-v2}
10444
10445         if ! [[ $HSMTOOL =~ hsmtool ]]; then
10446                 echo "HSMTOOL = '$HSMTOOL' does not contain 'hsmtool', GLWT" >&2
10447         fi
10448
10449         HSM_ARCHIVE_NUMBER=2
10450
10451         # The test only support up to 10 MDTs
10452         MDT_PREFIX="mdt.$FSNAME-MDT000"
10453         HSM_PARAM="${MDT_PREFIX}0.hsm"
10454
10455         # archive is purged at copytool setup
10456         HSM_ARCHIVE_PURGE=true
10457
10458         # Don't allow copytool error upon start/setup
10459         HSMTOOL_NOERROR=false
10460 }
10461
10462 # Get the backend root path for the given agent facet.
10463 copytool_device() {
10464         local facet=$1
10465         local dev=AGTDEV$(facet_number $facet)
10466
10467         echo -n ${!dev}
10468 }
10469
10470 get_mdt_devices() {
10471         local mdtno
10472         # get MDT device for each mdc
10473         for mdtno in $(seq 1 $MDSCOUNT); do
10474                 local idx=$(($mdtno - 1))
10475                 MDT[$idx]=$($LCTL get_param -n \
10476                         mdc.$FSNAME-MDT000${idx}-mdc-*.mds_server_uuid |
10477                         awk '{gsub(/_UUID/,""); print $1}' | head -n1)
10478         done
10479 }
10480
10481 pkill_copytools() {
10482         local hosts="$1"
10483         local signal="$2"
10484
10485         do_nodes "$hosts" \
10486                 "pkill --pidfile=$HSMTOOL_PID_FILE --signal=$signal hsmtool"
10487 }
10488
10489 copytool_continue() {
10490         local agents=${1:-$(facet_active_host $SINGLEAGT)}
10491
10492         pkill_copytools "$agents" CONT || return 0
10493         echo "Copytool is continued on $agents"
10494 }
10495
10496 kill_copytools() {
10497         local hosts=${1:-$(facet_active_host $SINGLEAGT)}
10498
10499         echo "Killing existing copytools on $hosts"
10500         pkill_copytools "$hosts" TERM || return 0
10501         copytool_continue "$hosts"
10502 }
10503
10504 copytool_monitor_cleanup() {
10505         local facet=${1:-$SINGLEAGT}
10506         local agent=$(facet_active_host $facet)
10507
10508         if [ -n "$HSMTOOL_MONITOR_DIR" ]; then
10509                 # Should die when the copytool dies, but just in case.
10510                 local cmd="kill \\\$(cat $HSMTOOL_MONITOR_DIR/monitor_pid)"
10511                 cmd+=" 2>/dev/null || true"
10512                 do_node $agent "$cmd"
10513                 do_node $agent "rm -fr $HSMTOOL_MONITOR_DIR"
10514                 export HSMTOOL_MONITOR_DIR=
10515         fi
10516
10517         # The pdsh should die on its own when the monitor dies. Just
10518         # in case, though, try to clean up to avoid any cruft.
10519         if [ -n "$HSMTOOL_MONITOR_PDSH" ]; then
10520                 kill $HSMTOOL_MONITOR_PDSH 2>/dev/null || true
10521                 export HSMTOOL_MONITOR_PDSH=
10522         fi
10523 }
10524
10525 copytool_logfile()
10526 {
10527         local host="$(facet_host "$1")"
10528         local prefix=$TESTLOG_PREFIX
10529         [ -n "$TESTNAME" ] && prefix+=.$TESTNAME
10530
10531         printf "${prefix}.copytool${archive_id}_log.${host}.log"
10532 }
10533
10534 __lhsmtool_rebind()
10535 {
10536         do_facet $facet $HSMTOOL \
10537                 "${hsmtool_options[@]}" --rebind "$@" "$mountpoint"
10538 }
10539
10540 __lhsmtool_import()
10541 {
10542         mkdir -p "$(dirname "$2")" ||
10543                 error "cannot create directory '$(dirname "$2")'"
10544         do_facet $facet $HSMTOOL \
10545                 "${hsmtool_options[@]}" --import "$@" "$mountpoint"
10546 }
10547
10548 __lhsmtool_setup()
10549 {
10550         local host="$(facet_host "$facet")"
10551         local cmd="$HSMTOOL ${hsmtool_options[@]} --daemon --pid-file=$HSMTOOL_PID_FILE"
10552
10553         [ -n "$bandwidth" ] && cmd+=" --bandwidth $bandwidth"
10554         [ -n "$archive_id" ] && cmd+=" --archive $archive_id"
10555         #       [ ${#misc_options[@]} -gt 0 ] &&
10556 #               cmd+=" $(IFS=" " echo "$@")"
10557         cmd+=" $@ \"$mountpoint\""
10558
10559         echo "Starting copytool '$facet' on '$host' with cmdline '$cmd'"
10560         stack_trap "pkill_copytools $host TERM || true" EXIT
10561         do_node "$host" "$cmd < /dev/null > \"$(copytool_logfile $facet)\" 2>&1"
10562 }
10563
10564 hsm_root() {
10565         local facet="${1:-$SINGLEAGT}"
10566
10567         printf "$(copytool_device "$facet")/${TESTSUITE}.${TESTNAME}/"
10568 }
10569
10570 # Main entry point to perform copytool related operations
10571 #
10572 # Sub-commands:
10573 #
10574 #       setup   setup a copytool to run in the background, that copytool will be
10575 #               killed on EXIT
10576 #       import  import a file from an HSM backend
10577 #       rebind  rebind an archived file to a new fid
10578 #
10579 # Although the semantics might suggest otherwise, one does not need to 'setup'
10580 # a copytool before a call to 'copytool import' or 'copytool rebind'.
10581 #
10582 copytool()
10583 {
10584         local action=$1
10585         shift
10586
10587         # Use default values
10588         local facet=$SINGLEAGT
10589         local mountpoint="${MOUNT2:-$MOUNT}"
10590
10591         # Parse arguments
10592         local fail_on_error=true
10593         local -a hsmtool_options=()
10594         local -a action_options=()
10595
10596         if [[ -n "$HSMTOOL_ARCHIVE_FORMAT" ]]; then
10597                 hsmtool_options+=("--archive-format=$HSMTOOL_ARCHIVE_FORMAT")
10598         fi
10599
10600         if [[ -n "$HSMTOOL_VERBOSE" ]]; then
10601                 hsmtool_options+=("$HSMTOOL_VERBOSE")
10602         fi
10603
10604         while [ $# -gt 0 ]; do
10605                 case "$1" in
10606                 -f|--facet)
10607                         shift
10608                         facet="$1"
10609                         ;;
10610                 -m|--mountpoint)
10611                         shift
10612                         mountpoint="$1"
10613                         ;;
10614                 -a|--archive-id)
10615                         shift
10616                         local archive_id="$1"
10617                         ;;
10618                 -h|--hsm-root)
10619                         shift
10620                         local hsm_root="$1"
10621                         ;;
10622                 -b|--bwlimit)
10623                         shift
10624                         local bandwidth="$1" # in MB/s
10625                         ;;
10626                 -n|--no-fail)
10627                         local fail_on_error=false
10628                         ;;
10629                 *)
10630                         # Uncommon(/copytool dependent) option
10631                         action_options+=("$1")
10632                         ;;
10633                 esac
10634                 shift
10635         done
10636
10637         local hsm_root="${hsm_root:-$(hsm_root "$facet")}"
10638         hsmtool_options+=("--hsm-root=$hsm_root")
10639
10640         stack_trap "do_facet $facet rm -rf '$hsm_root'" EXIT
10641         do_facet $facet mkdir -p "$hsm_root" ||
10642                 error "mkdir '$hsm_root' failed"
10643
10644         case "$HSMTOOL" in
10645         lhsmtool_posix)
10646                 local copytool=lhsmtool
10647                 ;;
10648         esac
10649
10650         __${copytool}_${action} "${action_options[@]}"
10651         if [ $? -ne 0 ]; then
10652                 local error_msg
10653
10654                 case $action in
10655                 setup)
10656                         local host="$(facet_host $facet)"
10657                         error_msg="Failed to start copytool $facet on '$host'"
10658                         ;;
10659                 import)
10660                         local src="${action_options[0]}"
10661                         local dest="${action_options[1]}"
10662                         error_msg="Failed to import '$src' to '$dest'"
10663                         ;;
10664                 rebind)
10665                         error_msg="could not rebind file"
10666                         ;;
10667                 esac
10668
10669                 $fail_on_error && error "$error_msg" || echo "$error_msg"
10670         fi
10671 }
10672
10673 needclients() {
10674         local client_count=$1
10675         if [[ $CLIENTCOUNT -lt $client_count ]]; then
10676                 skip "Need $client_count or more clients, have $CLIENTCOUNT"
10677                 return 1
10678         fi
10679         return 0
10680 }
10681
10682 path2fid() {
10683         $LFS path2fid $1 | tr -d '[]'
10684         return ${PIPESTATUS[0]}
10685 }
10686
10687 get_hsm_flags() {
10688         local f=$1
10689         local u=$2
10690         local st
10691
10692         if [[ $u == "user" ]]; then
10693                 st=$($RUNAS $LFS hsm_state $f)
10694         else
10695                 u=root
10696                 st=$($LFS hsm_state $f)
10697         fi
10698
10699         [[ $? == 0 ]] || error "$LFS hsm_state $f failed (run as $u)"
10700
10701         st=$(echo $st | cut -f 2 -d" " | tr -d "()," )
10702         echo $st
10703 }
10704
10705 check_hsm_flags() {
10706         local f=$1
10707         local fl=$2
10708
10709         local st=$(get_hsm_flags $f)
10710         [[ $st == $fl ]] || error "hsm flags on $f are $st != $fl"
10711 }
10712
10713 mdts_set_param() {
10714         local arg=$1
10715         local key=$2
10716         local value=$3
10717         local mdtno
10718         local rc=0
10719         if [[ "$value" != "" ]]; then
10720                 value="='$value'"
10721         fi
10722         for mdtno in $(seq 1 $MDSCOUNT); do
10723                 local idx=$(($mdtno - 1))
10724                 local facet=mds${mdtno}
10725                 # if $arg include -P option, run 1 set_param per MDT on the MGS
10726                 # else, run set_param on each MDT
10727                 [[ $arg = *"-P"* ]] && facet=mgs
10728                 do_facet $facet $LCTL set_param $arg mdt.${MDT[$idx]}.$key$value
10729                 [[ $? != 0 ]] && rc=1
10730         done
10731         return $rc
10732 }
10733
10734 mdts_check_param() {
10735         local key="$1"
10736         local target="$2"
10737         local timeout="$3"
10738         local mdtno
10739
10740         for mdtno in $(seq 1 $MDSCOUNT); do
10741                 local idx=$(($mdtno - 1))
10742                 wait_update_facet --verbose mds${mdtno} \
10743                         "$LCTL get_param -n $MDT_PREFIX${idx}.$key" "$target" \
10744                         $timeout ||
10745                         error "$key state is not '$target' on mds${mdtno}"
10746         done
10747 }
10748
10749 cdt_set_mount_state() {
10750         mdts_set_param "-P" hsm_control "$1"
10751         # set_param -P is asynchronous operation and could race with set_param.
10752         # In such case configs could be retrieved and applied at mgc after
10753         # set_param -P completion. Sleep here to avoid race with set_param.
10754         # We need at least 20 seconds. 10 for mgc_requeue_thread to wake up
10755         # MGC_TIMEOUT_MIN_SECONDS + MGC_TIMEOUT_RAND_CENTISEC(5 + 5)
10756         # and 10 seconds to retrieve config from server.
10757         sleep 20
10758 }
10759
10760 cdt_check_state() {
10761         mdts_check_param hsm_control "$1" 20
10762 }
10763
10764 cdt_set_sanity_policy() {
10765         if [[ "$CDT_POLICY_HAD_CHANGED" ]]
10766         then
10767                 # clear all
10768                 mdts_set_param "" hsm.policy "+NRA"
10769                 mdts_set_param "" hsm.policy "-NBR"
10770                 CDT_POLICY_HAD_CHANGED=
10771         fi
10772 }
10773
10774 set_hsm_param() {
10775         local param=$1
10776         local value=$2
10777         local opt=$3
10778         mdts_set_param "$opt -n" "hsm.$param" "$value"
10779         return $?
10780 }
10781
10782 wait_request_state() {
10783         local fid=$1
10784         local request=$2
10785         local state=$3
10786         # 4th arg (mdt index) is optional
10787         local mdtidx=${4:-0}
10788         local mds=mds$(($mdtidx + 1))
10789
10790         local cmd="$LCTL get_param -n ${MDT_PREFIX}${mdtidx}.hsm.actions"
10791         cmd+=" | awk '/'$fid'.*action='$request'/ {print \\\$13}' | cut -f2 -d="
10792
10793         wait_update_facet --verbose $mds "$cmd" "$state" 200 ||
10794                 error "request on $fid is not $state on $mds"
10795 }
10796
10797
10798 rmultiop_start() {
10799         local client=$1
10800         local file=$2
10801         local cmds=$3
10802         local WAIT_MAX=${4:-60}
10803         local wait_time=0
10804
10805         # We need to run do_node in bg, because pdsh does not exit
10806         # if child process of run script exists.
10807         # I.e. pdsh does not exit when runmultiop_bg_pause exited,
10808         # because of multiop_bg_pause -> $MULTIOP_PROG &
10809         # By the same reason we need sleep a bit after do_nodes starts
10810         # to let runmultiop_bg_pause start muliop and
10811         # update /tmp/multiop_bg.pid ;
10812         # The rm /tmp/multiop_bg.pid guarantees here that
10813         # we have the updated by runmultiop_bg_pause
10814         # /tmp/multiop_bg.pid file
10815
10816         local pid_file=$TMP/multiop_bg.pid.$$
10817
10818         do_node $client "MULTIOP_PID_FILE=$pid_file LUSTRE= \
10819                         runmultiop_bg_pause $file $cmds" &
10820         local pid=$!
10821         local multiop_pid
10822
10823         while [[ $wait_time -lt $WAIT_MAX ]]; do
10824                 sleep 3
10825                 wait_time=$((wait_time + 3))
10826                 multiop_pid=$(do_node $client cat $pid_file)
10827                 if [ -n "$multiop_pid" ]; then
10828                         break
10829                 fi
10830         done
10831
10832         [ -n "$multiop_pid" ] ||
10833                 error "$client : Can not get multiop_pid from $pid_file "
10834
10835         eval export $(node_var_name $client)_multiop_pid=$multiop_pid
10836         eval export $(node_var_name $client)_do_node_pid=$pid
10837         local var=$(node_var_name $client)_multiop_pid
10838         echo client $client multiop_bg started multiop_pid=${!var}
10839         return $?
10840 }
10841
10842 rmultiop_stop() {
10843         local client=$1
10844         local multiop_pid=$(node_var_name $client)_multiop_pid
10845         local do_node_pid=$(node_var_name $client)_do_node_pid
10846
10847         echo "Stopping multiop_pid=${!multiop_pid} (kill ${!multiop_pid} on $client)"
10848         do_node $client kill -USR1 ${!multiop_pid}
10849
10850         wait ${!do_node_pid}
10851 }
10852
10853 sleep_maxage() {
10854         local delay=$(do_facet $SINGLEMDS lctl get_param -n lo[vd].*.qos_maxage |
10855                       awk '{ print $1 * 2; exit; }')
10856         sleep $delay
10857 }
10858
10859 check_component_count() {
10860         local comp_cnt=$($LFS getstripe --component-count $1)
10861         [ $comp_cnt -eq $2 ] || error "$1, component count $comp_cnt != $2"
10862 }
10863
10864 # Verify there are no init components with "extension" flag
10865 verify_no_init_extension() {
10866         local flg_opts="--component-flags init,extension"
10867         local found=$($LFS find $flg_opts $1 | wc -l)
10868         [ $found -eq 0 ] || error "$1 has component with initialized extension"
10869 }
10870
10871 # Verify there is at least one component starting at 0
10872 verify_comp_at_zero() {
10873         flg_opts="--component-flags init"
10874         found=$($LFS find --component-start 0M $flg_opts $1 | wc -l)
10875         [ $found -eq 1 ] ||
10876                 error "No component starting at zero(!)"
10877 }
10878
10879 # version after which Self-Extending Layouts are available
10880 SEL_VER="2.12.55"
10881
10882 sel_layout_sanity() {
10883         local file=$1
10884         local comp_cnt=$2
10885
10886         verify_no_init_extension $file
10887         verify_comp_at_zero $file
10888         check_component_count $file $comp_cnt
10889 }
10890
10891 statx_supported() {
10892         $STATX --quiet --version
10893         return $?
10894 }
10895
10896 # lfs rm_entry is disabled on native client
10897 is_rmentry_supported() {
10898         $LFS rm_entry $DIR/dir/not/exists > /dev/null
10899         # is return code ENOENT?
10900         (( $? == 2 ))
10901 }
10902
10903 #
10904 # wrappers for createmany and unlinkmany
10905 # to set debug=0 if number of creates is high enough
10906 # this is to speedup testing
10907 #
10908 function createmany() {
10909         local count=${!#}
10910         local rc
10911
10912         if (( count > 100 )); then
10913                 debugsave
10914                 do_nodes $(comma_list $(all_nodes)) $LCTL set_param -n debug=0
10915         fi
10916         $LUSTRE/tests/createmany $*
10917         rc=$?
10918         debugrestore > /dev/null
10919
10920         return $rc
10921 }
10922
10923 function unlinkmany() {
10924         local count=${!#}
10925         local rc
10926
10927         if (( count > 100 )); then
10928                 debugsave
10929                 do_nodes $(comma_list $(all_nodes)) $LCTL set_param -n debug=0
10930         fi
10931         $LUSTRE/tests/unlinkmany $*
10932         rc=$?
10933         debugrestore > /dev/null
10934
10935         return $rc
10936 }
10937
10938 # Check if fallocate on facet is working. Returns fallocate mode if enabled.
10939 # Takes optional facet name as argument, to allow separate MDS/OSS checks.
10940 function check_fallocate_supported()
10941 {
10942         local facet=${1:-ost1}
10943         local supported="FALLOCATE_SUPPORTED_$facet"
10944         local fstype="${facet}_FSTYPE"
10945
10946         if [[ -n "${!supported}" ]]; then
10947                 echo "${!supported}"
10948                 return 0
10949         fi
10950         if [[ -z "${!fstype}" ]]; then
10951                 eval export $fstype=$(facet_fstype $facet)
10952         fi
10953         if [[ "${!fstype}" != "ldiskfs" ]]; then
10954                 echo "fallocate on ${!fstype} doesn't consume space" 1>&2
10955                 return 1
10956         fi
10957
10958         local fa_mode="osd-ldiskfs.$(facet_svc $facet).fallocate_zero_blocks"
10959         local mode=$(do_facet $facet $LCTL get_param -n $fa_mode 2>/dev/null |
10960                      head -n 1)
10961
10962         if [[ -z "$mode" ]]; then
10963                 echo "fallocate not supported on $facet" 1>&2
10964                 return 1
10965         fi
10966         eval export $supported="$mode"
10967
10968         echo ${!supported}
10969         return 0
10970 }
10971
10972 # Check if fallocate supported on OSTs, enable if unset, skip if unavailable.
10973 # Takes optional facet name as argument.
10974 function check_fallocate_or_skip()
10975 {
10976         local facet=$1
10977
10978         check_fallocate_supported $1 || skip "fallocate not supported"
10979 }
10980
10981 # Check if fallocate supported on OSTs, enable if unset, default mode=0
10982 # Optionally pass the OST fallocate mode (0=unwritten extents, 1=zero extents)
10983 function check_set_fallocate()
10984 {
10985         local new_mode="$1"
10986         local fa_mode="osd-ldiskfs.*.fallocate_zero_blocks"
10987         local old_mode="$(check_fallocate_supported)"
10988
10989         [[ -n "$old_mode" ]] || { echo "fallocate not supported"; return 1; }
10990         [[ -z "$new_mode" && "$old_mode" != "-1" ]] &&
10991                 { echo "keep default fallocate mode: $old_mode"; return 0; }
10992         [[ "$new_mode" && "$old_mode" == "$new_mode" ]] &&
10993                 { echo "keep current fallocate mode: $old_mode"; return 0; }
10994         local osts=$(comma_list $(osts_nodes))
10995
10996         stack_trap "do_nodes $osts $LCTL set_param $fa_mode=$old_mode"
10997         do_nodes $osts $LCTL set_param $fa_mode=${new_mode:-0} ||
10998                 error "set $fa_mode=$new_mode"
10999 }
11000
11001 # Check if fallocate supported on OSTs, enable if unset, skip if unavailable
11002 function check_set_fallocate_or_skip()
11003 {
11004         check_set_fallocate || skip "need >= 2.13.57 and ldiskfs for fallocate"
11005 }
11006
11007 function disable_opencache()
11008 {
11009         local state=$($LCTL get_param -n "llite.*.opencache_threshold_count" |
11010                         head -1)
11011
11012         test -z "${saved_OPENCACHE_value}" &&
11013                                         export saved_OPENCACHE_value="$state"
11014
11015         [[ "$state" = "off" ]] && return
11016
11017         $LCTL set_param -n "llite.*.opencache_threshold_count"=off
11018 }
11019
11020 function set_opencache()
11021 {
11022         local newvalue="$1"
11023         local state=$($LCTL get_param -n "llite.*.opencache_threshold_count")
11024
11025         [[ -n "$newvalue" ]] || return
11026
11027         [[ -n "${saved_OPENCACHE_value}" ]] ||
11028                                         export saved_OPENCACHE_value="$state"
11029
11030         $LCTL set_param -n "llite.*.opencache_threshold_count"=$newvalue
11031 }
11032
11033
11034
11035 function restore_opencache()
11036 {
11037         [[ -z "${saved_OPENCACHE_value}" ]] ||
11038                 $LCTL set_param -n "llite.*.opencache_threshold_count"=${saved_OPENCACHE_value}
11039 }
11040
11041 # LU-13417: XXX lots of tests assume the directory to be created under MDT0,
11042 # created on MDT0, use this function to create directory on specific MDT
11043 # explicitly, and set default LMV to create subdirs on the same MDT too.
11044 mkdir_on_mdt() {
11045         local mdt
11046         local OPTIND=1
11047
11048         while getopts "i:" opt $*; do
11049                 case $opt in
11050                         i) mdt=$OPTARG;;
11051                 esac
11052         done
11053
11054         shift $((OPTIND - 1))
11055
11056         $LFS mkdir -i $mdt -c 1 $*
11057 }
11058
11059 mkdir_on_mdt0() {
11060         mkdir_on_mdt -i0 $*
11061 }
11062
11063 # Wait for nodemap synchronization
11064 wait_nm_sync() {
11065         local nodemap_name=$1
11066         local key=$2
11067         local value=$3
11068         local opt=$4
11069         local proc_param
11070         local is_active=$(do_facet mgs $LCTL get_param -n nodemap.active)
11071         local max_retries=20
11072         local is_sync
11073         local out1=""
11074         local out2
11075         local mgs_ip=$(host_nids_address $mgs_HOST $NETTYPE | cut -d' ' -f1)
11076         local i
11077
11078         if [ "$nodemap_name" == "active" ]; then
11079                 proc_param="active"
11080         elif [ -z "$key" ]; then
11081                 proc_param=${nodemap_name}
11082         else
11083                 proc_param="${nodemap_name}.${key}"
11084         fi
11085         if [ "$opt" == "inactive" ]; then
11086                 # check nm sync even if nodemap is not activated
11087                 is_active=1
11088                 opt=""
11089         fi
11090         (( is_active == 0 )) && [ "$proc_param" != "active" ] && return
11091
11092         if [ -z "$value" ]; then
11093                 out1=$(do_facet mgs $LCTL get_param $opt \
11094                         nodemap.${proc_param} 2>/dev/null)
11095                 echo "On MGS ${mgs_ip}, ${proc_param} = $out1"
11096         else
11097                 out1=$value;
11098         fi
11099
11100         # if servers run on the same node, it is impossible to tell if they get
11101         # synced with the mgs, so just wait an arbitrary 10 seconds
11102         if [ $(facet_active_host mgs) == $(facet_active_host mds) ] &&
11103            [ $(facet_active_host mgs) == $(facet_active_host ost1) ]; then
11104                 echo "waiting 10 secs for sync"
11105                 sleep 10
11106                 return
11107         fi
11108
11109         # wait up to 10 seconds for other servers to sync with mgs
11110         for i in $(seq 1 10); do
11111                 for node in $(all_server_nodes); do
11112                         local node_ip=$(host_nids_address $node $NETTYPE |
11113                                         cut -d' ' -f1)
11114
11115                         is_sync=true
11116                         if [ -z "$value" ]; then
11117                                 [ $node_ip == $mgs_ip ] && continue
11118                         fi
11119
11120                         out2=$(do_node $node_ip $LCTL get_param $opt \
11121                                nodemap.$proc_param 2>/dev/null)
11122                         echo "On $node ${node_ip}, ${proc_param} = $out2"
11123                         [ "$out1" != "$out2" ] && is_sync=false && break
11124                 done
11125                 $is_sync && break
11126                 sleep 1
11127         done
11128         if ! $is_sync; then
11129                 echo MGS
11130                 echo $out1
11131                 echo OTHER - IP: $node_ip
11132                 echo $out2
11133                 error "mgs and $nodemap_name ${key} mismatch, $i attempts"
11134         fi
11135         echo "waited $((i - 1)) seconds for sync"
11136 }
11137
11138 consume_precreations() {
11139         local dir=$1
11140         local mfacet=$2
11141         local OSTIDX=$3
11142         local extra=${4:-2}
11143         local OST=$(ostname_from_index $OSTIDX $dir)
11144
11145         mkdir_on_mdt -i $(facet_index $mfacet) $dir/${OST}
11146         $LFS setstripe -i $OSTIDX -c 1 ${dir}/${OST}
11147
11148         # on the mdt's osc
11149         local mdtosc_proc=$(get_mdtosc_proc_path $mfacet $OST)
11150         local last_id=$(do_facet $mfacet $LCTL get_param -n \
11151                         osp.$mdtosc_proc.prealloc_last_id)
11152         local next_id=$(do_facet $mfacet $LCTL get_param -n \
11153                         osp.$mdtosc_proc.prealloc_next_id)
11154         echo "Creating to objid $last_id on ost $OST..."
11155         createmany -o $dir/${OST}/f $next_id $((last_id - next_id + extra))
11156 }
11157
11158 __exhaust_precreations() {
11159         local OSTIDX=$1
11160         local FAILLOC=$2
11161         local FAILIDX=${3:-$OSTIDX}
11162         local ofacet=ost$((OSTIDX + 1))
11163
11164         mkdir_on_mdt0 $DIR/$tdir
11165         local mdtidx=$($LFS getstripe -m $DIR/$tdir)
11166         local mfacet=mds$((mdtidx + 1))
11167         echo OSTIDX=$OSTIDX MDTIDX=$mdtidx
11168
11169         local mdtosc_proc=$(get_mdtosc_proc_path $mfacet)
11170         do_facet $mfacet $LCTL get_param osp.$mdtosc_proc.prealloc*
11171
11172 #define OBD_FAIL_OST_ENOSPC              0x215
11173         do_facet $ofacet $LCTL set_param fail_val=$FAILIDX fail_loc=0x215
11174
11175         consume_precreations $DIR/$tdir $mfacet $OSTIDX
11176
11177         do_facet $mfacet $LCTL get_param osp.$mdtosc_proc.prealloc*
11178         do_facet $ofacet $LCTL set_param fail_loc=$FAILLOC
11179 }
11180
11181 exhaust_precreations() {
11182         __exhaust_precreations $1 $2 $3
11183         sleep_maxage
11184 }
11185
11186 exhaust_all_precreations() {
11187         local i
11188         for (( i=0; i < OSTCOUNT; i++ )) ; do
11189                 __exhaust_precreations $i $1 -1
11190         done
11191         sleep_maxage
11192 }
11193
11194 force_new_seq() {
11195         local mfacet=$1
11196         local MDTIDX=$(facet_index $mfacet)
11197         local MDT=$(mdtname_from_index $MDTIDX $DIR)
11198         local i
11199
11200 #define OBD_FAIL_OSP_FORCE_NEW_SEQ              0x210a
11201         do_facet $mfacet $LCTL set_param fail_loc=0x210a
11202         mkdir_on_mdt -i $MDTIDX $DIR/${MDT}
11203         for (( i=0; i < OSTCOUNT; i++ )) ; do
11204                 # consume preallocated objects, to wake up precreate thread
11205                 consume_precreations $DIR/${MDT} $mfacet $i
11206         done
11207         do_facet $mfacet $LCTL set_param fail_loc=0
11208         rm -rf $DIR/${MDT}
11209 }
11210
11211 force_new_seq_all() {
11212         local i
11213         for (( i=0; i < MDSCOUNT; i++ )) ; do
11214                 force_new_seq mds$((i + 1))
11215         done
11216         sleep_maxage
11217 }
11218
11219 ost_set_temp_seq_width_all() {
11220         local osts=$(comma_list $(osts_nodes))
11221         local width=$(do_facet ost1 $LCTL get_param -n seq.*OST0000-super.width)
11222
11223         do_nodes $osts $LCTL set_param seq.*OST*-super.width=$1
11224         stack_trap "do_nodes $osts $LCTL set_param seq.*OST*-super.width=$width"
11225 }
11226
11227 verify_yaml_available() {
11228         python3 -c "import yaml; yaml.safe_load('''a: b''')"
11229 }
11230
11231 verify_yaml() {
11232         python3 -c "import sys, yaml; obj = yaml.safe_load(sys.stdin)"
11233 }