Whamcloud - gitweb
LU-10626 test: create custom udev rule
[fs/lustre-release.git] / lustre / tests / test-framework.sh
index 4b7e0db..9caa3f7 100755 (executable)
@@ -22,8 +22,10 @@ export SK_UNIQUE_NM=${SK_UNIQUE_NM:-false}
 export SK_S2S=${SK_S2S:-false}
 export SK_S2SNM=${SK_S2SNM:-TestFrameNM}
 export SK_S2SNMCLI=${SK_S2SNMCLI:-TestFrameNMCli}
+export SK_SKIPFIRST=${SK_SKIPFIRST:-true}
 export IDENTITY_UPCALL=default
 export QUOTA_AUTO=1
+export FLAKEY=${FLAKEY:-true}
 # specify environment variable containing batch job name for server statistics
 export JOBID_VAR=${JOBID_VAR:-"procname_uid"}  # or "existing" or "disable"
 
@@ -44,7 +46,7 @@ LUSTRE=${LUSTRE:-$(cd $(dirname $0)/..; echo $PWD)}
 . $LUSTRE/tests/functions.sh
 . $LUSTRE/tests/yaml.sh
 
-export LD_LIBRARY_PATH=${LUSTRE}/utils:${LD_LIBRARY_PATH}
+export LD_LIBRARY_PATH=${LUSTRE}/utils/.libs:${LUSTRE}/utils:${LD_LIBRARY_PATH}
 
 LUSTRE_TESTS_CFG_DIR=${LUSTRE_TESTS_CFG_DIR:-${LUSTRE}/tests/cfg}
 
@@ -155,6 +157,7 @@ init_test_env() {
        export DO_CLEANUP=${DO_CLEANUP:-true}
        export KEEP_ZPOOL=${KEEP_ZPOOL:-false}
        export CLEANUP_DM_DEV=false
+       export PAGE_SIZE=$(get_page_size client)
 
        export MKE2FS=$MKE2FS
        if [ -z "$MKE2FS" ]; then
@@ -273,6 +276,8 @@ init_test_env() {
        SETSTRIPE=${SETSTRIPE:-"$LFS setstripe"}
        GETSTRIPE=${GETSTRIPE:-"$LFS getstripe"}
 
+       export PERM_CMD=${PERM_CMD:-"$LCTL conf_param"}
+
        export L_GETIDENTITY=${L_GETIDENTITY:-"$LUSTRE/utils/l_getidentity"}
        if [ ! -f "$L_GETIDENTITY" ]; then
                if `which l_getidentity > /dev/null 2>&1`; then
@@ -293,7 +298,7 @@ init_test_env() {
        export LUSTRE_RMMOD=${LUSTRE_RMMOD:-$LUSTRE/scripts/lustre_rmmod}
        [ ! -f "$LUSTRE_RMMOD" ] &&
                export LUSTRE_RMMOD=$(which lustre_rmmod 2> /dev/null)
-       export LUSTRE_ROUTES_COVERSION=${LUSTRE_ROUTES_CONVERSION:-$LUSTRE/scripts/lustre_routes_conversion}
+       export LUSTRE_ROUTES_CONVERSION=${LUSTRE_ROUTES_CONVERSION:-$LUSTRE/scripts/lustre_routes_conversion}
        [ ! -f "$LUSTRE_ROUTES_CONVERSION" ] &&
                export LUSTRE_ROUTES_CONVERSION=$(which lustre_routes_conversion 2> /dev/null)
        export LFS_MIGRATE=${LFS_MIGRATE:-$LUSTRE/scripts/lfs_migrate}
@@ -303,6 +308,10 @@ init_test_env() {
        [ ! -f "$LR_READER" ] &&
                export LR_READER=$(which lr_reader 2> /dev/null)
        [ -z "$LR_READER" ] && export LR_READER="/usr/sbin/lr_reader"
+       export LSOM_SYNC=${LSOM_SYNC:-"$LUSTRE/utils/llsom_sync"}
+       [ ! -f "$LSOM_SYNC" ] &&
+               export LSOM_SYNC=$(which llsom_sync 2> /dev/null)
+       [ -z "$LSOM_SYNC" ] && export LSOM_SYNC="/usr/sbin/llsom_sync"
        export NAME=${NAME:-local}
        export LGSSD=${LGSSD:-"$LUSTRE/utils/gss/lgssd"}
        [ "$GSS_PIPEFS" = "true" ] && [ ! -f "$LGSSD" ] &&
@@ -506,62 +515,91 @@ lustre_insmod() {
 # /etc/modprobe.conf, from /etc/modprobe.d/Lustre, or else none will be used.
 #
 load_module() {
-    local optvar
-    EXT=".ko"
-    module=$1
-    shift
-    BASE=$(basename $module $EXT)
-
-    module_loaded ${BASE} && return
-
-    # If no module arguments were passed, get them from $MODOPTS_<MODULE>,
-    # else from modprobe.conf
-    if [ $# -eq 0 ]; then
-        # $MODOPTS_<MODULE>; we could use associative arrays, but that's not in
-        # Bash until 4.x, so we resort to eval.
-        optvar="MODOPTS_$(basename $module | tr a-z A-Z)"
-        eval set -- \$$optvar
-        if [ $# -eq 0 -a -n "$MODPROBECONF" ]; then
-               # Nothing in $MODOPTS_<MODULE>; try modprobe.conf
-               local opt
-               opt=$(awk -v var="^options $BASE" '$0 ~ var \
-                       {gsub("'"options $BASE"'",""); print}' $MODPROBECONF)
-               set -- $(echo -n $opt)
-
-               # Ensure we have accept=all for lnet
-               if [ $(basename $module) = lnet ]; then
-                       # OK, this is a bit wordy...
-                       local arg accept_all_present=false
-
-                       for arg in "$@"; do
-                               [ "$arg" = accept=all ] && \
-                                       accept_all_present=true
-                       done
-                       $accept_all_present || set -- "$@" accept=all
+       local module=$1 # '../libcfs/libcfs/libcfs', 'obdclass/obdclass', ...
+       shift
+       local ext=".ko"
+       local base=$(basename $module $ext)
+       local path
+       local -A module_is_loaded_aa
+       local optvar
+       local mod
+
+       for mod in $(lsmod | awk '{ print $1; }'); do
+               module_is_loaded_aa[${mod//-/_}]=true
+       done
+
+       module_is_loaded() {
+               ${module_is_loaded_aa[${1//-/_}]:-false}
+       }
+
+       if module_is_loaded $base; then
+               return
+       fi
+
+       if [[ -f $LUSTRE/$module$ext ]]; then
+               path=$LUSTRE/$module$ext
+       elif [[ "$base" == lnet_selftest ]] &&
+            [[ -f $LUSTRE/../lnet/selftest/$base$ext ]]; then
+               path=$LUSTRE/../lnet/selftest/$base$ext
+       else
+               path=''
+       fi
+
+       if [[ -n "$path" ]]; then
+               # Try to load any non-Lustre modules that $module depends on.
+               for mod in $(modinfo --field=depends $path | tr ',' ' '); do
+                       if ! module_is_loaded $mod; then
+                               modprobe $mod
+                       fi
+               done
+       fi
+
+       # If no module arguments were passed then get them from
+       # $MODOPTS_<MODULE>, otherwise from modprobe.conf.
+       if [ $# -eq 0 ]; then
+               # $MODOPTS_<MODULE>; we could use associative arrays, but that's
+               # not in Bash until 4.x, so we resort to eval.
+               optvar="MODOPTS_$(basename $module | tr a-z A-Z)"
+               eval set -- \$$optvar
+               if [ $# -eq 0 -a -n "$MODPROBECONF" ]; then
+                       # Nothing in $MODOPTS_<MODULE>; try modprobe.conf
+                       local opt
+                       opt=$(awk -v var="^options $base" '$0 ~ var \
+                             {gsub("'"options $base"'",""); print}' \
+                               $MODPROBECONF)
+                       set -- $(echo -n $opt)
+
+                       # Ensure we have accept=all for lnet
+                       if [[ "$base" == lnet ]]; then
+                               # OK, this is a bit wordy...
+                               local arg accept_all_present=false
+
+                               for arg in "$@"; do
+                                       [[ "$arg" == accept=all ]] &&
+                                               accept_all_present=true
+                               done
+
+                               $accept_all_present || set -- "$@" accept=all
+                       fi
+
+                       export $optvar="$*"
                fi
-               export $optvar="$*"
-        fi
-    fi
+       fi
 
-    [ $# -gt 0 ] && echo "${module} options: '$*'"
+       [ $# -gt 0 ] && echo "${module} options: '$*'"
 
        # Note that insmod will ignore anything in modprobe.conf, which is why
-       # we're passing options on the command-line.
-       if [[ "$BASE" == "lnet_selftest" ]] &&
-               [[ -f ${LUSTRE}/../lnet/selftest/${module}${EXT} ]]; then
-               lustre_insmod ${LUSTRE}/../lnet/selftest/${module}${EXT}
-       elif [[ -f ${LUSTRE}/${module}${EXT} ]]; then
-               [[ "$BASE" != "ptlrpc_gss" ]] || modprobe sunrpc
-               lustre_insmod ${LUSTRE}/${module}${EXT} "$@"
-       else
-               # must be testing a "make install" or "rpm" installation
-               # note failed to load ptlrpc_gss is considered not fatal
-               if [[ "$BASE" == "ptlrpc_gss" ]]; then
-                       modprobe $BASE "$@" 2>/dev/null ||
-                               echo "gss/krb5 is not supported"
-               else
-                       modprobe $BASE "$@"
+       # we're passing options on the command-line. If $path does not exist
+       # then we must be testing a "make install" or"rpm" installation. Also
+       # note that failing to load ptlrpc_gss is not considered fatal.
+       if [[ -n "$path" ]]; then
+               lustre_insmod $path "$@"
+       elif [[ "$base" == ptlrpc_gss ]]; then
+               if ! modprobe $base "$@" 2>/dev/null; then
+                       echo "gss/krb5 is not supported"
                fi
+       else
+               modprobe $base "$@"
        fi
 }
 
@@ -572,6 +610,15 @@ load_modules_local() {
                return 0
        fi
 
+       # Create special udev test rules on every node
+       if [ -f $LUSTRE/lustre/conf/99-lustre.rules ]; then {
+               sed -e 's|/usr/sbin/lctl|$LCTL|g' $LUSTRE/lustre/conf/99-lustre.rules > /etc/udev/rules.d/99-lustre-test.rules
+       } else {
+               echo "SUBSYSTEM==\"lustre\", ACTION==\"change\", ENV{PARAM}==\"?*\", RUN+=\"$LCTL set_param '\$env{PARAM}=\$env{SETTING}'\"" > /etc/udev/rules.d/99-lustre-test.rules
+       } fi
+       udevadm control --reload-rules
+       udevadm trigger
+
        echo Loading modules from $LUSTRE
 
        local ncpus
@@ -634,23 +681,12 @@ load_modules_local() {
        load_module mgc/mgc
        load_module obdecho/obdecho
        if ! client_only; then
-               SYMLIST=/proc/kallsyms
-               grep -q crc16 $SYMLIST ||
-                       { modprobe crc16 2>/dev/null || true; }
-               grep -q -w jbd2 $SYMLIST ||
-                       { modprobe jbd2 2>/dev/null || true; }
                load_module lfsck/lfsck
                [ "$LQUOTA" != "no" ] &&
                        load_module quota/lquota $LQUOTAOPTS
                if [[ $(node_fstypes $HOSTNAME) == *zfs* ]]; then
-                       lsmod | grep zfs >&/dev/null || modprobe zfs
                        load_module osd-zfs/osd_zfs
-               fi
-               if [[ $(node_fstypes $HOSTNAME) == *ldiskfs* ]]; then
-                       grep -q exportfs_decode_fh $SYMLIST ||
-                               { modprobe exportfs 2> /dev/null || true; }
-                       grep -q -w mbcache $SYMLIST ||
-                               { modprobe mbcache 2>/dev/null || true; }
+               elif [[ $(node_fstypes $HOSTNAME) == *ldiskfs* ]]; then
                        load_module ../ldiskfs/ldiskfs
                        load_module osd-ldiskfs/osd_ldiskfs
                fi
@@ -728,12 +764,20 @@ unload_modules() {
 
        $LUSTRE_RMMOD ldiskfs || return 2
 
+       [ -f /etc/udev/rules.d/99-lustre-test.rules ] &&
+               rm /etc/udev/rules.d/99-lustre-test.rules
+       udevadm control --reload-rules
+       udevadm trigger
+
        if $LOAD_MODULES_REMOTE; then
                local list=$(comma_list $(remote_nodes_list))
                if [ -n "$list" ]; then
                        echo "unloading modules on: '$list'"
                        do_rpc_nodes "$list" $LUSTRE_RMMOD ldiskfs
                        do_rpc_nodes "$list" check_mem_leak
+                       do_rpc_nodes "$list" "rm /etc/udev/rules.d/99-lustre-test.rules"
+                       do_rpc_nodes "$list" "udevadm control --reload-rules"
+                       do_rpc_nodes "$list" "udevadm trigger"
                fi
        fi
 
@@ -756,11 +800,13 @@ fs_log_size() {
 
        case $(facet_fstype $facet) in
                ldiskfs) size=50;; # largest seen is 44, leave some headroom
-               zfs)     size=$(lctl get_param osc.$FSNAME-OST*.import |
-                       awk '/grant_block_size:/ { print $2 * 2; exit; }');;
+               # grant_block_size is in bytes, allow at least 2x max blocksize
+               zfs)     size=$(lctl get_param osc.$FSNAME*.import |
+                               awk '/grant_block_size:/ {print $2/512; exit;}')
+                         ;;
        esac
 
-       echo -n $size
+       echo -n $((size * MDSCOUNT))
 }
 
 fs_inode_ksize() {
@@ -916,7 +962,11 @@ init_gss() {
                # security ctx config for keyring
                SK_NO_KEY=false
                mkdir -p $SK_OM_PATH
-               mount -o bind $SK_OM_PATH /etc/request-key.d/
+               if grep -q request-key /proc/mounts > /dev/null; then
+                       echo "SSK: Request key already mounted."
+               else
+                       mount -o bind $SK_OM_PATH /etc/request-key.d/
+               fi
                local lgssc_conf_line='create lgssc * * '
                lgssc_conf_line+=$(which lgss_keyring)
                lgssc_conf_line+=' %o %k %t %d %c %u %g %T %P %S'
@@ -992,6 +1042,8 @@ init_gss() {
                                -m $SK_PATH/$FSNAME-nmclient.key \
                                 >/dev/null 2>&1"
                fi
+       fi
+       if $GSS_SK; then
                # mount options for servers and clients
                MGS_MOUNT_OPTS=$(add_sk_mntflag $MGS_MOUNT_OPTS)
                MDS_MOUNT_OPTS=$(add_sk_mntflag $MDS_MOUNT_OPTS)
@@ -1025,9 +1077,12 @@ cleanup_sk() {
                $RPC_MODE || echo "Cleaning up Shared Key.."
                do_nodes $(comma_list $(all_nodes)) "rm -f \
                        $SK_PATH/$FSNAME*.key $SK_PATH/nodemap/$FSNAME*.key"
+               do_nodes $(comma_list $(all_nodes)) "keyctl show | \
+                 awk '/lustre/ { print \\\$1 }' | xargs -IX keyctl unlink X"
                # Remove the mount and clean up the files we added to SK_PATH
-               do_nodes $(comma_list $(all_nodes)) "umount \
-                       /etc/request-key.d/"
+               do_nodes $(comma_list $(all_nodes)) "while grep -q \
+                       request-key.d /proc/mounts; do umount \
+                       /etc/request-key.d/; done"
                do_nodes $(comma_list $(all_nodes)) "rm -f \
                        $SK_OM_PATH/lgssc.conf"
                do_nodes $(comma_list $(all_nodes)) "rmdir $SK_OM_PATH"
@@ -1473,7 +1528,8 @@ mount_facets () {
                [ $RC -eq 0 ] && continue
 
                if [ "$TESTSUITE.$TESTNAME" = "replay-dual.test_0a" ]; then
-                       skip "Restart of $facet failed!." && touch $LU482_FAILED
+                       skip_noexit "Restart of $facet failed!." &&
+                               touch $LU482_FAILED
                else
                        error "Restart of $facet failed!"
                fi
@@ -1579,6 +1635,7 @@ is_dm_flakey_dev() {
 dm_flakey_supported() {
        local facet=$1
 
+       $FLAKEY || return 1
        do_facet $facet "modprobe dm-flakey;
                         $DMSETUP targets | grep -q flakey" &> /dev/null
 }
@@ -1827,6 +1884,9 @@ dm_cleanup_dev() {
 
        # detach a loop device
        [[ $major -ne 7 ]] || cleanup_loop_device $facet /dev/loop$minor
+
+       # unload dm-flakey module
+       do_facet $facet "modprobe -r dm-flakey" || true
 }
 
 mount_facet() {
@@ -1992,46 +2052,6 @@ stop() {
        fi
 }
 
-# save quota version (both administrative and operational quotas)
-# add an additional parameter if mountpoint is ever different from $MOUNT
-#
-# XXX This function is kept for interoperability with old server (< 2.3.50),
-#     it should be removed whenever we drop the interoperability for such
-#     server.
-quota_save_version() {
-    local fsname=${2:-$FSNAME}
-    local spec=$1
-    local ver=$(tr -c -d "123" <<< $spec)
-    local type=$(tr -c -d "ug" <<< $spec)
-
-    [ -n "$ver" -a "$ver" != "3" ] && error "wrong quota version specifier"
-
-    [ -n "$type" ] && { $LFS quotacheck -$type $MOUNT || error "quotacheck has failed"; }
-
-    do_facet mgs "lctl conf_param ${fsname}-MDT*.mdd.quota_type=$spec"
-    local varsvc
-    local osts=$(get_facets OST)
-    for ost in ${osts//,/ }; do
-        varsvc=${ost}_svc
-        do_facet mgs "lctl conf_param ${!varsvc}.ost.quota_type=$spec"
-    done
-}
-
-# client could mount several lustre
-#
-# XXX This function is kept for interoperability with old server (< 2.3.50),
-#     it should be removed whenever we drop the interoperability for such
-#     server.
-quota_type() {
-       local fsname=${1:-$FSNAME}
-       local rc=0
-       do_facet $SINGLEMDS lctl get_param mdd.${fsname}-MDT*.quota_type ||
-               rc=$?
-       do_nodes $(comma_list $(osts_nodes)) \
-               lctl get_param obdfilter.${fsname}-OST*.quota_type || rc=$?
-       return $rc
-}
-
 # get mdt quota type
 mdt_quota_type() {
        local varsvc=${SINGLEMDS}_svc
@@ -2050,12 +2070,24 @@ ost_quota_type() {
 # restore old quota type settings
 restore_quota() {
        if [ "$old_MDT_QUOTA_TYPE" ]; then
-               do_facet mgs $LCTL conf_param \
-                       $FSNAME.quota.mdt=$old_MDT_QUOTA_TYPE
+               if [[ $PERM_CMD = *"set_param -P"* ]]; then
+                       do_facet mgs $PERM_CMD \
+                               osd-*.$FSNAME-MDT*.quota_slave.enable = \
+                               $old_MDT_QUOTA_TYPE
+               else
+                       do_facet mgs $PERM_CMD \
+                               $FSNAME.quota.mdt=$old_MDT_QUOTA_TYPE
+               fi
        fi
        if [ "$old_OST_QUOTA_TYPE" ]; then
-               do_facet mgs $LCTL conf_param \
-                       $FSNAME.quota.ost=$old_OST_QUOTA_TYPE
+               if [[ $PERM_CMD = *"set_param -P"* ]]; then
+                       do_facet mgs $PERM_CMD \
+                               osd-*.$FSNAME-OST*.quota_slave.enable = \
+                               $old_OST_QUOTA_TYPE
+               else
+                       do_facet mgs $LCTL conf_param \
+                               $FSNAME.quota.ost=$old_OST_QUOTA_TYPE
+               fi
        fi
 }
 
@@ -2108,10 +2140,17 @@ setup_quota(){
        export old_MDT_QUOTA_TYPE=$mdt_qtype
        export old_OST_QUOTA_TYPE=$ost_qtype
 
-       do_facet mgs $LCTL conf_param $FSNAME.quota.mdt=$QUOTA_TYPE ||
-               error "set mdt quota type failed"
-       do_facet mgs $LCTL conf_param $FSNAME.quota.ost=$QUOTA_TYPE ||
-               error "set ost quota type failed"
+       if [[ $PERM_CMD = *"set_param -P"* ]]; then
+               do_facet mgs $PERM_CMD \
+                       osd-*.$FSNAME-MDT*.quota_slave.enable=$QUOTA_TYPE
+               do_facet mgs $PERM_CMD \
+                       osd-*.$FSNAME-OST*.quota_slave.enable=$QUOTA_TYPE
+       else
+               do_facet mgs $PERM_CMD $FSNAME.quota.mdt=$QUOTA_TYPE ||
+                       error "set mdt quota type failed"
+               do_facet mgs $PERM_CMD $FSNAME.quota.ost=$QUOTA_TYPE ||
+                       error "set ost quota type failed"
+       fi
 
        local quota_usrs=$QUOTA_USERS
 
@@ -3052,7 +3091,7 @@ wait_mds_ost_sync () {
        done
 
        # show which nodes are not finished.
-       cmd=$(echo $cmd | sed '/-n//')
+       cmd=$(echo $cmd | sed 's/-n//')
        do_nodes $list "$cmd"
        echo "$facet recovery node $i not done in $WAIT_TIMEOUT sec. $STATUS"
        return 1
@@ -3373,7 +3412,9 @@ fail() {
        local clients=${CLIENTS:-$HOSTNAME}
 
        facet_failover $* || error "failover: $?"
-       wait_clients_import_state "$clients" "$facets" FULL
+       # to initiate all OSC idling connections
+       clients_up
+       wait_clients_import_state "$clients" "$facets" "\(FULL\|IDLE\)"
        clients_up || error "post-failover stat: $?"
 }
 
@@ -3580,59 +3621,20 @@ facet_failover_host() {
        fi
 }
 
-detect_active() {
-       local facet=$1
-       [ "$CLIENTONLY" ] && echo $facet && return
-
-       local failover=$(facet_failover_host $facet)
-
-       # failover is not associated with all facet types:
-       # "AGT" facet type (remote HSM agents) does not
-       # have a failover.
-       [[ -z "$failover" ]] && echo $facet && return
-
-       local host=$(facet_host $facet)
-       local dev=$(facet_device $facet)
-
-       # ${facet}_svc can not be used here because of
-       # facet_active() is called before this var initialized
-       local svc=$(do_node $host $E2LABEL ${dev})
-
-       # active facet is ${facet}failover if device is mounted on failover
-       # on other cases active facet is $facet
-       [[ $dev = $(do_node $failover \
-                       lctl get_param -n *.$svc.mntdev 2>/dev/null) ]] &&
-               echo ${facet}failover && return
-
-       echo $facet
-}
-
-init_active() {
-       local facet=$1
-
-       local active=$(detect_active $facet)
-       echo "${facet}active=$active" > $TMP/${facet}active
-}
-
 facet_active() {
-       local facet=$1
-       local activevar=${facet}active
+    local facet=$1
+    local activevar=${facet}active
 
-       # file is missing (nothing to store) if fail() is not
-       # executed during this test session yet;
-       # file content:
-       #      ost1active=ost1failover
-       #      ost1active=ost1
-       # let's detect active facet based on current lustre state
-       if [ ! -f $TMP/${facet}active ] ; then
-               init_active $facet
-       fi
-       source $TMP/${facet}active
+    if [ -f $TMP/${facet}active ] ; then
+        source $TMP/${facet}active
+    fi
 
-       # is ${facet}active set somewhere else?
-       active=${!activevar}
-       [[ -z "$active" ]] && exit 1
-       echo -n ${active}
+    active=${!activevar}
+    if [ -z "$active" ] ; then
+        echo -n ${facet}
+    else
+        echo -n ${active}
+    fi
 }
 
 facet_active_host() {
@@ -4132,6 +4134,13 @@ unmount_fstype() {
 ## MountConf setup
 
 stopall() {
+    # make sure we are using the primary server, so test-framework will
+    # be able to clean up properly.
+    activemds=`facet_active mds1`
+    if [ $activemds != "mds1" ]; then
+        fail mds1
+    fi
+
     local clients=$CLIENTS
     [ -z $clients ] && clients=$(hostname)
 
@@ -4176,8 +4185,8 @@ cleanupall() {
        nfs_client_mode && return
        cifs_client_mode && return
 
-       CLEANUP_DM_DEV=true stopall $*
        cleanup_echo_devs
+       CLEANUP_DM_DEV=true stopall $*
 
        unload_modules
        cleanup_sk
@@ -4240,7 +4249,7 @@ mkfs_opts() {
                        # Check for wide striping
                        if [ $OSTCOUNT -gt 160 ]; then
                                MDSJOURNALSIZE=${MDSJOURNALSIZE:-4096}
-                               fs_mkfs_opts+="-O large_xattr"
+                               fs_mkfs_opts+="-O ea_inode"
                        fi
 
                        var=${facet}_JRN
@@ -4648,8 +4657,17 @@ setupall() {
                if $GSS_SK; then
                        set_rule $FSNAME any cli2mdt $SK_FLAVOR
                        set_rule $FSNAME any cli2ost $SK_FLAVOR
-                       wait_flavor cli2mdt $SK_FLAVOR
-                       wait_flavor cli2ost $SK_FLAVOR
+                       if $SK_SKIPFIRST; then
+                               export SK_SKIPFIRST=false
+
+                               sleep 30
+                               do_nodes $CLIENTS \
+                                        "lctl set_param osc.*.idle_connect=1"
+                               return
+                       else
+                               wait_flavor cli2mdt $SK_FLAVOR
+                               wait_flavor cli2ost $SK_FLAVOR
+                       fi
                else
                        set_flavor_all $SEC
                fi
@@ -4793,10 +4811,57 @@ set_conf_param_and_check() {
                error "check $PARAM failed!"
 }
 
+set_persistent_param() {
+       local myfacet=$1
+       local test_param=$2
+       local param=$3
+       local orig=$(do_facet $myfacet "$LCTL get_param -n $test_param")
+
+       if [ $# -gt 3 ]; then
+               local final=$4
+       else
+               local -i final
+               final=$((orig + 5))
+       fi
+
+       if [[ $PERM_CMD = *"set_param -P"* ]]; then
+               echo "Setting $test_param from $orig to $final"
+               do_facet mgs "$PERM_CMD $test_param='$final'" ||
+                       error "$PERM_CMD $test_param failed"
+       else
+               echo "Setting $param from $orig to $final"
+               do_facet mgs "$PERM_CMD $param='$final'" ||
+                       error "$PERM_CMD $param failed"
+       fi
+}
+
+set_persistent_param_and_check() {
+       local myfacet=$1
+       local test_param=$2
+       local param=$3
+       local orig=$(do_facet $myfacet "$LCTL get_param -n $test_param")
+
+       if [ $# -gt 3 ]; then
+               local final=$4
+       else
+               local -i final
+               final=$((orig + 5))
+       fi
+
+       set_persistent_param $myfacet $test_param $param "$final"
+
+       wait_update_facet $myfacet "$LCTL get_param -n $test_param" "$final" ||
+               error "check $param failed!"
+}
+
 init_param_vars () {
        TIMEOUT=$(lctl get_param -n timeout)
        TIMEOUT=${TIMEOUT:-20}
 
+       if [ -n $arg1 ]; then
+               [ "$arg1" = "server_only" ] && return
+       fi
+
        remote_mds_nodsh && log "Using TIMEOUT=$TIMEOUT" && return 0
 
        TIMEOUT=$(do_facet $SINGLEMDS "lctl get_param -n timeout")
@@ -4804,6 +4869,7 @@ init_param_vars () {
 
        osc_ensure_active $SINGLEMDS $TIMEOUT
        osc_ensure_active client $TIMEOUT
+       $LCTL set_param osc.*.idle_timeout=debug
 
        if [ -n "$(lctl get_param -n mdc.*.connect_flags|grep jobstats)" ]; then
                local current_jobid_var=$($LCTL get_param -n jobid_var)
@@ -4811,11 +4877,10 @@ init_param_vars () {
                if [ $JOBID_VAR = "existing" ]; then
                        echo "keeping jobstats as $current_jobid_var"
                elif [ $current_jobid_var != $JOBID_VAR ]; then
-                       echo "seting jobstats to $JOBID_VAR"
+                       echo "setting jobstats to $JOBID_VAR"
 
-                       set_conf_param_and_check client                 \
-                               "$LCTL get_param -n jobid_var"          \
-                               "$FSNAME.sys.jobid_var" $JOBID_VAR
+                       set_persistent_param_and_check client \
+                               "jobid_var" "$FSNAME.sys.jobid_var" $JOBID_VAR
                fi
        else
                echo "jobstats not supported by server"
@@ -5013,7 +5078,6 @@ check_and_setup_lustre() {
                fi
        fi
 
-       init_gss
        if $GSS_SK; then
                set_flavor_all null
        elif $GSS; then
@@ -5119,7 +5183,7 @@ run_e2fsck() {
        if [ -n "$(grep "DNE mode isn't supported" $log)" ]; then
                rm -f $log
                if [ $MDSCOUNT -gt 1 ]; then
-                       skip "DNE mode isn't supported!"
+                       skip_noexit "DNE mode isn't supported!"
                        cleanupall
                        exit_status
                else
@@ -5802,7 +5866,7 @@ skip_env () {
        $FAIL_ON_SKIP_ENV && error false $@ || skip $@
 }
 
-skip() {
+skip_noexit() {
        echo
        log " SKIP: $TESTSUITE $TESTNAME $@"
 
@@ -5817,8 +5881,13 @@ skip() {
                echo "$TESTSUITE: SKIP: $TESTNAME $@" >> $TESTSUITELOG || true
 }
 
+skip() {
+       skip_noexit $@
+       exit 0
+}
+
 build_test_filter() {
-    EXCEPT="$EXCEPT $(testslist_filter)"
+       EXCEPT="$EXCEPT $(testslist_filter)"
 
        for O in $ONLY; do
                if [[ $O = [0-9]*-[0-9]* ]]; then
@@ -5892,32 +5961,32 @@ run_test() {
        ALWAYS_SKIPPED="y"
        testname=EXCEPT_$1
        if [ ${!testname}x != x ]; then
-               TESTNAME=test_$1 skip "skipping excluded test $1"
+               TESTNAME=test_$1 skip_noexit "skipping excluded test $1"
                return 0
        fi
        testname=EXCEPT_$base
        if [ ${!testname}x != x ]; then
-               TESTNAME=test_$1 skip "skipping excluded test $1 (base $base)"
+               TESTNAME=test_$1 skip_noexit "skipping excluded test $1 (base $base)"
                return 0
        fi
        testname=EXCEPT_ALWAYS_$1
        if [ ${!testname}x != x ]; then
-               TESTNAME=test_$1 skip "skipping ALWAYS excluded test $1"
+               TESTNAME=test_$1 skip_noexit "skipping ALWAYS excluded test $1"
                return 0
        fi
        testname=EXCEPT_ALWAYS_$base
        if [ ${!testname}x != x ]; then
-               TESTNAME=test_$1 skip "skipping ALWAYS excluded test $1 (base $base)"
+               TESTNAME=test_$1 skip_noexit "skipping ALWAYS excluded test $1 (base $base)"
                return 0
        fi
        testname=EXCEPT_SLOW_$1
        if [ ${!testname}x != x ]; then
-               TESTNAME=test_$1 skip "skipping SLOW test $1"
+               TESTNAME=test_$1 skip_noexit "skipping SLOW test $1"
                return 0
        fi
        testname=EXCEPT_SLOW_$base
        if [ ${!testname}x != x ]; then
-               TESTNAME=test_$1 skip "skipping SLOW test $1 (base $base)"
+               TESTNAME=test_$1 skip_noexit "skipping SLOW test $1 (base $base)"
                return 0
        fi
 
@@ -6137,6 +6206,7 @@ check_grant() {
 
        # sync all the data and make sure no pending data on server
        do_nodes $clients sync
+       clients_up # initiate all idling connections
 
        # get client grant
        client_grant=$(do_nodes $clients \
@@ -6711,7 +6781,7 @@ calc_sum () {
 }
 
 calc_osc_kbytes () {
-       df $MOUNT > /dev/null
+       $LFS df $MOUNT > /dev/null
        $LCTL get_param -n osc.*[oO][sS][cC][-_][0-9a-f]*.$1 | calc_sum
 }
 
@@ -6849,7 +6919,7 @@ _wait_import_state () {
     local i=0
 
        CONN_STATE=$($LCTL get_param -n $CONN_PROC 2>/dev/null | cut -f2 | uniq)
-    while [ "${CONN_STATE}" != "${expected}" ]; do
+    while ! echo "${CONN_STATE}" | egrep -q "^${expected}\$" ; do
         if [ "${expected}" == "DISCONN" ]; then
             # for disconn we can check after proc entry is removed
             [ "x${CONN_STATE}" == "x" ] && return 0
@@ -6994,6 +7064,10 @@ wait_osc_import_state() {
        fi
 }
 
+wait_osc_import_ready() {
+       wait_osc_import_state $1 $2 "\(FULL\|IDLE\)"
+}
+
 _wait_mgc_import_state() {
        local facet=$1
        local expected=$2
@@ -7056,7 +7130,7 @@ wait_dne_interconnect() {
 
        if [ $MDSCOUNT -gt 1 ]; then
                for num in $(seq $MDSCOUNT); do
-                       wait_osc_import_state mds mds$num FULL
+                       wait_osc_import_ready mds mds$num
                done
        fi
 }
@@ -7079,7 +7153,7 @@ do_rpc_nodes () {
        local LIBPATH="/usr/lib/lustre/tests:/usr/lib64/lustre/tests:"
        local TESTPATH="$RLUSTRE/tests:"
        local RPATH="PATH=${TESTPATH}${LIBPATH}${PATH}:/sbin:/bin:/usr/sbin:"
-       do_nodesv $list "${RPATH} NAME=${NAME} sh rpc.sh $@ "
+       do_nodesv $list "${RPATH} NAME=${NAME} bash rpc.sh $@ "
 }
 
 wait_clients_import_state () {
@@ -7109,7 +7183,7 @@ wait_clients_import_state () {
                local params=$(expand_list $params $proc_path)
        done
 
-       if ! do_rpc_nodes "$list" wait_import_state_mount $expected $params;
+       if ! do_rpc_nodes "$list" wait_import_state_mount "$expected" $params;
        then
                error "import is not in ${expected} state"
                return 1
@@ -7126,6 +7200,7 @@ wait_osp_active() {
        # wait until all MDTs are in the expected state
        for ((num = 1; num <= $MDSCOUNT; num++)); do
                local mdtosp=$(get_mdtosc_proc_path mds${num} ${tgt_name})
+               local wait=0
                local mproc
 
                if [ $facet = "mds" ]; then
@@ -7140,7 +7215,6 @@ wait_osp_active() {
                        sleep 5
                        local result=$(do_facet mds${num} "$LCTL get_param -n $mproc")
                        local max=30
-                       local wait=0
 
                        [ ${PIPESTATUS[0]} = 0 ] || error "Can't read $mproc"
                        if [ $result -eq $expected ]; then
@@ -7201,7 +7275,7 @@ create_pool() {
        local fsname=${1%%.*}
        local poolname=${1##$fsname.}
 
-       trap "destroy_test_pools $fsname" EXIT
+       stack_trap "destroy_test_pools $fsname" EXIT
        do_facet mgs lctl pool_new $1
        local RC=$?
        # get param should return err unless pool is created
@@ -7569,6 +7643,8 @@ flvr_cnt_cli2ost()
     local clients=${CLIENTS:-$HOSTNAME}
 
     for c in ${clients//,/ }; do
+       # reconnect if idle
+       do_node $c lctl set_param osc.*.idle_connect=1 >/dev/null 2>&1
        local output=$(do_node $c lctl get_param -n \
                 osc.*OST*-osc-[^M][^D][^T]*.$PROC_CLI 2>/dev/null)
        local tmpcnt=$(count_flvr "$output" $flavor)
@@ -8053,13 +8129,15 @@ get_obd_size() {
 
 #
 # Get the page size (bytes) on a given facet node.
+# The local client page_size is directly available in PAGE_SIZE.
 #
 get_page_size() {
        local facet=$1
-       local size=$(getconf PAGE_SIZE 2>/dev/null)
+       local page_size=$(getconf PAGE_SIZE 2>/dev/null)
 
-       [ -z "$CLIENTONLY" ] && size=$(do_facet $facet getconf PAGE_SIZE)
-       echo -n ${size:-4096}
+       [ -z "$CLIENTONLY" -a "$facet" != "client" ] &&
+               page_size=$(do_facet $facet getconf PAGE_SIZE)
+       echo -n ${page_size:-4096}
 }
 
 #
@@ -8086,7 +8164,9 @@ get_block_size() {
        echo -n ${size:-0}
 }
 
-# Check whether the "large_xattr" feature is enabled or not.
+# Check whether the "ea_inode" feature is enabled or not, to allow
+# ldiskfs xattrs over one block in size.  Allow both the historical
+# Lustre feature name (large_xattr) and the upstream name (ea_inode).
 large_xattr_enabled() {
        [[ $(facet_fstype $SINGLEMDS) == zfs ]] && return 0
 
@@ -8762,8 +8842,7 @@ lss_gen_conf()
 
        if ! combined_mgs_mds ; then
                [ $(facet_fstype mgs) != zfs ] &&
-                       skip "Lustre snapshot 1 only works for ZFS backend" &&
-                       exit 0
+                       skip "Lustre snapshot 1 only works for ZFS backend"
 
                local host=$(facet_active_host mgs)
                local dir=$(dirname $(facet_vdevice mgs))
@@ -8777,8 +8856,7 @@ lss_gen_conf()
 
        for num in `seq $MDSCOUNT`; do
                [ $(facet_fstype mds$num) != zfs ] &&
-                       skip "Lustre snapshot 1 only works for ZFS backend" &&
-                       exit 0
+                       skip "Lustre snapshot 1 only works for ZFS backend"
 
                lss_gen_conf_one mds$num MDT $((num - 1)) ||
                        lss_err "generate lss conf (mds$num)"
@@ -8786,8 +8864,7 @@ lss_gen_conf()
 
        for num in `seq $OSTCOUNT`; do
                [ $(facet_fstype ost$num) != zfs ] &&
-                       skip "Lustre snapshot 1 only works for ZFS backend" &&
-                       exit 0
+                       skip "Lustre snapshot 1 only works for ZFS backend"
 
                lss_gen_conf_one ost$num OST $((num - 1)) ||
                        lss_err "generate lss conf (ost$num)"
@@ -8975,8 +9052,12 @@ changelog_register() {
        for M in $(seq $MDSCOUNT); do
                local facet=mds$M
                local mdt="$(facet_svc $facet)"
+               local cl_mask
+
+               cl_mask=$(do_facet $facet $LCTL get_param \
+                            mdd.${mdt}.changelog_mask -n)
                stack_trap "do_facet $facet $LCTL \
-                       set_param mdd.$mdt.changelog_mask=-hsm" EXIT
+                       set_param mdd.$mdt.changelog_mask=\'$cl_mask\' -n" EXIT
                do_facet $facet $LCTL set_param mdd.$mdt.changelog_mask=+hsm ||
                        error "$mdt: changelog_mask=+hsm failed: $?"
 
@@ -8996,8 +9077,12 @@ changelog_register() {
 
 changelog_deregister() {
        local cl_user
+       # bash assoc arrays do not guarantee to list keys in created order
+       # so reorder to get same order than in changelog_register()
+       local cl_facets=$(echo "${!CL_USERS[@]}" | tr " " "\n" | sort |
+                         tr "\n" " ")
 
-       for facet in "${!CL_USERS[@]}"; do
+       for facet in $cl_facets; do
                for cl_user in ${CL_USERS[$facet]}; do
                        __changelog_deregister $facet $cl_user || return $?
                done
@@ -9061,7 +9146,12 @@ __changelog_clear()
 # users.
 changelog_clear() {
        local rc
-       for facet in ${!CL_USERS[@]}; do
+       # bash assoc arrays do not guarantee to list keys in created order
+       # so reorder to get same order than in changelog_register()
+       local cl_facets=$(echo "${!CL_USERS[@]}" | tr " " "\n" | sort |
+                         tr "\n" " ")
+
+       for facet in $cl_facets; do
                for cl_user in ${CL_USERS[$facet]}; do
                        __changelog_clear $facet $cl_user $1 || rc=${rc:-$?}
                done
@@ -9089,6 +9179,175 @@ changelog_extract_field() {
                tail -1
 }
 
+# Prints a changelog record produced by "lfs changelog" as an associative array
+#
+# Example:
+# $> changelog2array 16 01CREAT 10:28:46.968438800 2018.03.09 0x0 \
+#                    t=[0x200000401:0x10:0x0] j=touch.501 ef=0xf u=501:501 \
+#                    nid=0@lo p=[0x200000007:0x1:0x0] blob
+# ([index]='16' [type]='CREAT' [time]='10:28:46.968438800'
+#  [date]='2018.03.09' [flags]=0x0 ['target-fid']='[0x200000401:0x10:0x0]'
+#  ['jobid']='touch.501' ['extra-flags']='0x0f' [uid]='0' ['gid']='0'
+#  ['nid']='0@lo' ['parent-fid']='[0x200000007:0x1:0x0]')
+#
+# Note that the changelog record is not quoted
+# Also note that the line breaks in the output were only added for readability
+#
+# Typically, you want to eval the output of the command to fill an actual
+# associative array, like this:
+# $> eval declare -A changelog=$(changelog2array $entry)
+#
+# It can then be accessed like any bash associative array:
+# $> echo "${changelog[index]}" "${changelog[type]}" "${changelog[flags]}"
+# 16 CREAT 0x0
+# $> echo "${changelog[uid]}":"${changelog[gid]}"
+# 501:501
+#
+changelog2array()
+{
+       # Start the array
+       printf '('
+
+       # A changelog, as printed by "lfs changelog" typically looks like this:
+       # <index> <type> <time> <date> <flags> <key1=value1> <key2=value2> ...
+
+       # Parse the positional part of the changelog
+
+       # changelog_dump() prefixes records with their mdt's name
+       local index="${1##*.}"
+
+       printf "[index]='%s' [type]='%s' [time]='%s' [date]='%s' [flags]='%s'" \
+              "$index" "${2:2}" "$3" "$4" "$5"
+
+       # Parse the key/value part of the changelog
+       for arg in "${@:5}"; do
+               # Check it matches a key=value syntax
+               [[ "$arg" =~ ^[[:alpha:]]+= ]] || continue
+
+               local key="${arg%%=*}"
+               local value="${arg#*=}"
+
+               case "$key" in
+               u)
+                       # u is actually for uid AND gid: u=UID:GID
+                       printf " [uid]='%s'" "${value%:*}"
+                       key=gid
+                       value="${value#*:}"
+                       ;;
+               t)
+                       key=target-fid
+                       value="${value#[}"
+                       value="${value%]}"
+                       ;;
+               j)
+                       key=jobid
+                       ;;
+               p)
+                       key=parent-fid
+                       value="${value#[}"
+                       value="${value%]}"
+                       ;;
+               ef)
+                       key=extra-flags
+                       ;;
+               m)
+                       key=mode
+                       ;;
+               x)
+                       key=xattr
+                       ;;
+               *)
+                       ;;
+               esac
+
+               printf " ['%s']='%s'" "$key" "$value"
+       done
+
+       # end the array
+       printf ')'
+}
+
+# Format and print a changelog record
+#
+# Interpreted sequences are:
+#      %%      a single %
+#      %f      the "flags" attribute of a changelog record
+__changelog_printf()
+{
+       local format="$1"
+
+       local -i i
+       for ((i = 0; i < ${#format}; i++)); do
+               local char="${format:$i:1}"
+               if [ "$char" != % ]; then
+                       printf '%c' "$char"
+                       continue
+               fi
+
+               i+=1
+               char="${format:$i:1}"
+               case "$char" in
+               f)
+                       printf '%s' "${changelog[flags]}"
+                       ;;
+               %)
+                       printf '%'
+                       ;;
+               esac
+       done
+       printf '\n'
+}
+
+# Filter changelog records
+changelog_find()
+{
+       local -A filter
+       local action='print'
+       local format
+
+       while [ $# -gt 0 ]; do
+               case "$1" in
+               -print)
+                       action='print'
+                       ;;
+               -printf)
+                       action='printf'
+                       format="$2"
+                       shift
+                       ;;
+               -*)
+                       filter[${1#-}]="$2"
+                       shift
+                       ;;
+               esac
+               shift
+       done
+
+       local found=false
+       local record
+       changelog_dump | { while read -r record; do
+               eval local -A changelog=$(changelog2array $record)
+               for key in "${!filter[@]}"; do
+                       case "$key" in
+                       *)
+                               [ "${changelog[$key]}" == "${filter[$key]}" ]
+                               ;;
+                       esac || continue 2
+               done
+
+               found=true
+
+               case "${action:-print}" in
+               print)
+                       printf '%s\n' "$record"
+                       ;;
+               printf)
+                       __changelog_printf "$format"
+                       ;;
+               esac
+       done; $found; }
+}
+
 restore_layout() {
        local dir=$1
        local layout=$2