-#!/bin/ksh -p
#
# CDDL HEADER START
#
#
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
-# Copyright (c) 2012, 2016 by Delphix. All rights reserved.
-# Copyright 2016 Nexenta Systems, Inc.
+# Copyright (c) 2012, 2017 by Delphix. All rights reserved.
+# Copyright (c) 2017 by Tim Chase. All rights reserved.
+# Copyright (c) 2017 by Nexenta Systems, Inc. All rights reserved.
# Copyright (c) 2017 Lawrence Livermore National Security, LLC.
# Copyright (c) 2017 Datto Inc.
# Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
+# Use is subject to license terms.
#
. ${STF_TOOLS}/include/logapi.shlib
PATH="$STF_PATH"
fi
+#
+# Generic dot version comparison function
+#
+# Returns success when version $1 is greater than or equal to $2.
+#
+function compare_version_gte
+{
+ if [[ "$(printf "$1\n$2" | sort -V | tail -n1)" == "$1" ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
# Linux kernel version comparison function
#
# $1 Linux version ("4.10", "2.6.32") or blank for installed Linux version
log_must zfs snapshot $incr
log_must eval "zfs send -i $snap $incr | dd bs=10K count=1 > $sendfile"
log_mustnot eval "zfs recv -su $recvfs < $sendfile"
- log_must zfs destroy -r $sendfs
+ destroy_dataset "$sendfs" "-r"
log_must rm -f "$sendfile"
if [[ $(get_prop 'inconsistent' "$recvfs/%recv") -ne 1 ]]; then
typeset fs=""
for fs in $(zfs list -H -o name \
| grep "^$ZONE_POOL/$ZONE_CTR[01234]/"); do
- datasetexists $fs && \
- log_must zfs destroy -Rf $fs
+ destroy_dataset "$fs" "-Rf"
done
# Need cleanup here to avoid garbage dir left.
if is_mpath_device $disk1; then
delete_partitions
fi
+
+ rm -f $TEST_BASE_DIR/{err,out}
}
[[ $? -eq 0 ]] && \
log_must zfs unmount $TESTPOOL/$TESTCTR/$TESTFS1
- datasetexists $TESTPOOL/$TESTCTR/$TESTFS1 && \
- log_must zfs destroy -R $TESTPOOL/$TESTCTR/$TESTFS1
-
- datasetexists $TESTPOOL/$TESTCTR && \
- log_must zfs destroy -Rf $TESTPOOL/$TESTCTR
+ destroy_dataset "$TESTPOOL/$TESTCTR/$TESTFS1" "-R"
+ destroy_dataset "$TESTPOOL/$TESTCTR" "-Rf"
[[ -e $TESTDIR1 ]] && \
log_must rm -rf $TESTDIR1 > /dev/null 2>&1
typeset snap=${1:-$TESTPOOL/$TESTFS@$TESTSNAP}
if ! snapexists $snap; then
- log_fail "'$snap' does not existed."
+ log_fail "'$snap' does not exist."
fi
#
log_fail "get_prop mountpoint $snap failed."
fi
- log_must zfs destroy $snap
+ destroy_dataset "$snap"
[[ $mtpt != "" && -d $mtpt ]] && \
log_must rm -rf $mtpt
}
log_fail "get_prop mountpoint $clone failed."
fi
- log_must zfs destroy $clone
+ destroy_dataset "$clone"
[[ $mtpt != "" && -d $mtpt ]] && \
log_must rm -rf $mtpt
}
log_fail "'$bkmarkp' does not existed."
fi
- log_must zfs destroy $bkmark
+ destroy_dataset "$bkmark"
}
# Return 0 if a snapshot exists; $? otherwise
typeset i
if is_linux; then
- log_must parted $DEV_DSKDIR/$diskname -s -- mklabel gpt
+ DSK=$DEV_DSKDIR/$diskname
+ DSK=$(echo $DSK | sed -e "s|//|/|g")
+ log_must parted $DSK -s -- mklabel gpt
+ blockdev --rereadpt $DSK 2>/dev/null
+ block_device_wait
else
for i in 0 1 3 4 5 6 7
do
typeset start=$2
typeset size=$3
typeset disk=$4
- [[ -z $slicenum || -z $size || -z $disk ]] && \
- log_fail "The slice, size or disk name is unspecified."
if is_linux; then
+ if [[ -z $size || -z $disk ]]; then
+ log_fail "The size or disk name is unspecified."
+ fi
typeset size_mb=${size%%[mMgG]}
size_mb=${size_mb%%[mMgG][bB]}
blockdev --rereadpt $DEV_DSKDIR/$disk 2>/dev/null
block_device_wait
else
+ if [[ -z $slicenum || -z $size || -z $disk ]]; then
+ log_fail "The slice, size or disk name is unspecified."
+ fi
+
typeset format_file=/var/tmp/format_in.$$
echo "partition" >$format_file
((endcyl = (endcyl + 1) / ratio))
fi
-
+
echo $endcyl
}
typeset -i filenum=${3:-50}
typeset -i bytes=${4:-8192}
typeset -i num_writes=${5:-10240}
- typeset -i data=${6:-0}
+ typeset data=${6:-0}
typeset -i odirnum=1
typeset -i idirnum=0
typeset -i fn=0
typeset -i retval=0
- log_must mkdir -p $destdir/$idirnum
+ mkdir -p $destdir/$idirnum
while (($odirnum > 0)); do
if ((dirnum >= 0 && idirnum >= dirnum)); then
odirnum=0
if (($fn >= $filenum)); then
fn=0
((idirnum = idirnum + 1))
- log_must mkdir -p $destdir/$idirnum
+ mkdir -p $destdir/$idirnum
else
((fn = fn + 1))
fi
return 0
}
-#
-# Given a mountpoint, or a dataset name, determine if it is shared via NFS.
-#
-# Returns 0 if shared, 1 otherwise.
-#
-function is_shared
+function is_shared_impl
{
typeset fs=$1
typeset mtpt
- if [[ $fs != "/"* ]] ; then
- if datasetnonexists "$fs" ; then
- return 1
- else
- mtpt=$(get_prop mountpoint "$fs")
- case $mtpt in
- none|legacy|-) return 1
- ;;
- *) fs=$mtpt
- ;;
- esac
- fi
- fi
-
if is_linux; then
for mtpt in `share | awk '{print $1}'` ; do
if [[ $mtpt == $fs ]] ; then
return 1
}
+#
+# Given a mountpoint, or a dataset name, determine if it is shared via NFS.
+#
+# Returns 0 if shared, 1 otherwise.
+#
+function is_shared
+{
+ typeset fs=$1
+ typeset mtpt
+
+ if [[ $fs != "/"* ]] ; then
+ if datasetnonexists "$fs" ; then
+ return 1
+ else
+ mtpt=$(get_prop mountpoint "$fs")
+ case $mtpt in
+ none|legacy|-) return 1
+ ;;
+ *) fs=$mtpt
+ ;;
+ esac
+ fi
+ fi
+
+ is_shared_impl "$fs"
+}
+
#
# Given a dataset name determine if it is shared via SMB.
#
return 0
}
+# Return 0 if created successfully; $? otherwise
+#
+# $1 - dataset name
+# $2-n - dataset options
+
+function create_dataset #dataset dataset_options
+{
+ typeset dataset=$1
+
+ shift
+
+ if [[ -z $dataset ]]; then
+ log_note "Missing dataset name."
+ return 1
+ fi
+
+ if datasetexists $dataset ; then
+ destroy_dataset $dataset
+ fi
+
+ log_must zfs create $@ $dataset
+
+ return 0
+}
+
+# Return 0 if destroy successfully or the dataset exists; $? otherwise
+# Note: In local zones, this function should return 0 silently.
+#
+# $1 - dataset name
+# $2 - custom arguments for zfs destroy
+# Destroy dataset with the given parameters.
+
+function destroy_dataset #dataset #args
+{
+ typeset dataset=$1
+ typeset mtpt
+ typeset args=${2:-""}
+
+ if [[ -z $dataset ]]; then
+ log_note "No dataset name given."
+ return 1
+ fi
+
+ if is_global_zone ; then
+ if datasetexists "$dataset" ; then
+ mtpt=$(get_prop mountpoint "$dataset")
+ log_must_busy zfs destroy $args $dataset
+
+ [[ -d $mtpt ]] && \
+ log_must rm -rf $mtpt
+ else
+ log_note "Dataset does not exist. ($dataset)"
+ return 1
+ fi
+ fi
+
+ return 0
+}
+
#
# Firstly, create a pool with 5 datasets. Then, create a single zone and
# export the 5 datasets to it. In addition, we also add a ZFS filesystem
log_must rm -rf $zdbout
}
+#
+# Given a pool issue a scrub and verify that no checksum errors are reported.
+#
+function verify_pool
+{
+ typeset pool=${1:-$TESTPOOL}
+
+ log_must zpool scrub $pool
+ log_must wait_scrubbed $pool
+
+ cksum=$(zpool status $pool | awk 'L{print $NF;L=0} /CKSUM$/{L=1}')
+ if [[ $cksum != 0 ]]; then
+ log_must zpool status -v
+ log_fail "Unexpected CKSUM errors found on $pool ($cksum)"
+ fi
+}
+
#
# Given a pool, and this function list all disks in the pool
#
disklist=$(zpool iostat -v $1 | nawk '(NR >4) {print $1}' | \
grep -v "\-\-\-\-\-" | \
- egrep -v -e "^(mirror|raidz1|raidz2|spare|log|cache)$")
+ egrep -v -e "^(mirror|raidz[1-3]|spare|log|cache|special|dedup)$")
echo $disklist
}
function wait_hotspare_state # pool disk state timeout
{
typeset pool=$1
- typeset disk=${2#$/DEV_DSKDIR/}
+ typeset disk=${2#*$DEV_DSKDIR/}
typeset state=$3
typeset timeout=${4:-60}
typeset -i i=0
function check_vdev_state # pool disk state{online,offline,unavail}
{
typeset pool=$1
- typeset disk=${2#$/DEV_DSKDIR/}
+ typeset disk=${2#*$DEV_DSKDIR/}
typeset state=$3
cur_state=$(get_device_state $pool $disk)
function wait_vdev_state # pool disk state timeout
{
typeset pool=$1
- typeset disk=${2#$/DEV_DSKDIR/}
+ typeset disk=${2#*$DEV_DSKDIR/}
typeset state=$3
typeset timeout=${4:-60}
typeset -i i=0
# is_pool_scrubbed - to check if the pool is scrub completed
# is_pool_scrub_stopped - to check if the pool is scrub stopped
# is_pool_scrub_paused - to check if the pool has scrub paused
+# is_pool_removing - to check if the pool is removing a vdev
+# is_pool_removed - to check if the pool is remove completed
#
function is_pool_resilvering #pool <verbose>
{
return $?
}
+function is_pool_removing #pool
+{
+ check_pool_status "$1" "remove" "in progress since "
+ return $?
+}
+
+function is_pool_removed #pool
+{
+ check_pool_status "$1" "remove" "completed on"
+ return $?
+}
+
+function wait_for_degraded
+{
+ typeset pool=$1
+ typeset timeout=${2:-30}
+ typeset t0=$SECONDS
+
+ while :; do
+ [[ $(get_pool_prop health $pool) == "DEGRADED" ]] && break
+ log_note "$pool is not yet degraded."
+ sleep 1
+ if ((SECONDS - t0 > $timeout)); then
+ log_note "$pool not degraded after $timeout seconds."
+ return 1
+ fi
+ done
+
+ return 0
+}
+
#
# Use create_pool()/destroy_pool() to clean up the information in
# in the given disk to avoid slice overlapping.
"when ops is $ops."
fi
log_must datasetexists $dataset
- log_mustnot snapexists $dataset
;;
*)
log_fail "$ops is not supported."
esac
# make sure the upper level filesystem does not exist
- if datasetexists ${newdataset%/*} ; then
- log_must zfs destroy -rRf ${newdataset%/*}
- fi
+ destroy_dataset "${newdataset%/*}" "-rRf"
# without -p option, operation will fail
log_mustnot zfs $ops $dataset $newdataset
shift
log_note "user:$user $@"
- eval su - \$user -c \"$@\" > /tmp/out 2>/tmp/err
+ eval su - \$user -c \"$@\" > $TEST_BASE_DIR/out 2>$TEST_BASE_DIR/err
return $?
}
shift
+ # We could use 'zpool list' to only get the vdevs of the pool but we
+ # can't reference a mirror/raidz vdev using its ID (i.e mirror-0),
+ # therefore we use the 'zpool status' output.
typeset tmpfile=$(mktemp)
- zpool list -Hv "$pool" >$tmpfile
+ zpool status -v "$pool" | grep -A 1000 "config:" >$tmpfile
for vdev in $@; do
grep -w ${vdev##*/} $tmpfile >/dev/null 2>&1
[[ $? -ne 0 ]] && return 1
function wait_scrubbed
{
typeset pool=${1:-$TESTPOOL}
- typeset iter=${2:-10}
- for i in {1..$iter} ; do
- if is_pool_scrubbed $pool ; then
- return 0
- fi
- sleep 1
+ while true ; do
+ is_pool_scrubbed $pool && break
+ log_must sleep 1
done
- return 1
+}
+
+# Backup the zed.rc in our test directory so that we can edit it for our test.
+#
+# Returns: Backup file name. You will need to pass this to zed_rc_restore().
+function zed_rc_backup
+{
+ zedrc_backup="$(mktemp)"
+ cp $ZEDLET_DIR/zed.rc $zedrc_backup
+ echo $zedrc_backup
+}
+
+function zed_rc_restore
+{
+ mv $1 $ZEDLET_DIR/zed.rc
}
#
log_note "Stopping ZED"
if [[ -f ${ZEDLET_DIR}/zed.pid ]]; then
- zedpid=$(cat ${ZEDLET_DIR}/zed.pid)
+ zedpid=$(<${ZEDLET_DIR}/zed.pid)
kill $zedpid
while ps -p $zedpid > /dev/null; do
sleep 1
done
}
+# Set a variable in zed.rc to something, un-commenting it in the process.
+#
+# $1 variable
+# $2 value
+function zed_rc_set
+{
+ var="$1"
+ val="$2"
+ # Remove the line
+ cmd="'/$var/d'"
+ eval sed -i $cmd $ZEDLET_DIR/zed.rc
+
+ # Add it at the end
+ echo "$var=$val" >> $ZEDLET_DIR/zed.rc
+}
+
+
#
# Check is provided device is being active used as a swap device.
#
return 1
}
+
+#
+# Prints the current time in seconds since UNIX Epoch.
+#
+function current_epoch
+{
+ printf '%(%s)T'
+}
+
+#
+# Get decimal value of global uint32_t variable using mdb.
+#
+function mdb_get_uint32
+{
+ typeset variable=$1
+ typeset value
+
+ value=$(mdb -k -e "$variable/X | ::eval .=U")
+ if [[ $? -ne 0 ]]; then
+ log_fail "Failed to get value of '$variable' from mdb."
+ return 1
+ fi
+
+ echo $value
+ return 0
+}
+
+#
+# Set global uint32_t variable to a decimal value using mdb.
+#
+function mdb_set_uint32
+{
+ typeset variable=$1
+ typeset value=$2
+
+ mdb -kw -e "$variable/W 0t$value" > /dev/null
+ if [[ $? -ne 0 ]]; then
+ echo "Failed to set '$variable' to '$value' in mdb."
+ return 1
+ fi
+
+ return 0
+}
+
+#
+# Set global scalar integer variable to a hex value using mdb.
+# Note: Target should have CTF data loaded.
+#
+function mdb_ctf_set_int
+{
+ typeset variable=$1
+ typeset value=$2
+
+ mdb -kw -e "$variable/z $value" > /dev/null
+ if [[ $? -ne 0 ]]; then
+ echo "Failed to set '$variable' to '$value' in mdb."
+ return 1
+ fi
+
+ return 0
+}