X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=scripts%2Fcommon.sh.in;h=29b85d3e10e21c990999c5e5e81c40c3aa0e377a;hb=refs%2Fheads%2Frertzinger%2Ffeature-zpool-get--p;hp=00418696c8e46cbe67c0348bdaced901ace976f8;hpb=c9c0d073da561bcbefbdf09c87fc75b227415619;p=zfs.git diff --git a/scripts/common.sh.in b/scripts/common.sh.in index 0041869..29b85d3 100644 --- a/scripts/common.sh.in +++ b/scripts/common.sh.in @@ -1,17 +1,18 @@ #!/bin/bash # -# Common support functions for testing scripts. If a .script-config +# Common support functions for testing scripts. If a script-config # files is available it will be sourced so in-tree kernel modules and -# utilities will be used. If no .script-config can be found then the +# utilities will be used. If no script-config can be found then the # installed kernel modules and utilities will be used. basedir="$(dirname $0)" -SCRIPT_CONFIG=.script-config +SCRIPT_CONFIG=zfs-script-config.sh if [ -f "${basedir}/../${SCRIPT_CONFIG}" ]; then . "${basedir}/../${SCRIPT_CONFIG}" else -MODULES=(zlib_deflate spl splat zavl znvpair zunicode zcommon zfs) +KERNEL_MODULES=(zlib_deflate zlib_inflate) +MODULES=(spl splat zavl znvpair zunicode zcommon zfs) fi PROG="" @@ -26,38 +27,69 @@ RAID0S=() RAID10S=() RAIDZS=() RAIDZ2S=() +TESTS_RUN=${TESTS_RUN:-'*'} +TESTS_SKIP=${TESTS_SKIP:-} prefix=@prefix@ exec_prefix=@exec_prefix@ -libexecdir=@libexecdir@ -pkglibexecdir=${libexecdir}/@PACKAGE@ +pkgdatadir=@datarootdir@/@PACKAGE@ bindir=@bindir@ sbindir=@sbindir@ +udevdir=@udevdir@ +udevruledir=@udevruledir@ +sysconfdir=@sysconfdir@ ETCDIR=${ETCDIR:-/etc} -DEVDIR=${DEVDIR:-/dev/disk/zpool} -ZPOOLDIR=${ZPOOLDIR:-${pkglibexecdir}/zpool-config} +DEVDIR=${DEVDIR:-/dev/disk/by-vdev} +ZPOOLDIR=${ZPOOLDIR:-${pkgdatadir}/zpool-config} +ZPIOSDIR=${ZPIOSDIR:-${pkgdatadir}/zpios-test} +ZPIOSPROFILEDIR=${ZPIOSPROFILEDIR:-${pkgdatadir}/zpios-profile} ZDB=${ZDB:-${sbindir}/zdb} ZFS=${ZFS:-${sbindir}/zfs} ZINJECT=${ZINJECT:-${sbindir}/zinject} ZPOOL=${ZPOOL:-${sbindir}/zpool} -ZPOOL_ID=${ZPOOL_ID:-${bindir}/zpool_id} ZTEST=${ZTEST:-${sbindir}/ztest} +ZPIOS=${ZPIOS:-${sbindir}/zpios} -COMMON_SH=${COMMON_SH:-${pkglibexecdir}/common.sh} -ZFS_SH=${ZFS_SH:-${pkglibexecdir}/zfs.sh} -ZPOOL_CREATE_SH=${ZPOOL_CREATE_SH:-${pkglibexecdir}/zpool-create.sh} +COMMON_SH=${COMMON_SH:-${pkgdatadir}/common.sh} +ZFS_SH=${ZFS_SH:-${pkgdatadir}/zfs.sh} +ZPOOL_CREATE_SH=${ZPOOL_CREATE_SH:-${pkgdatadir}/zpool-create.sh} +ZPIOS_SH=${ZPIOS_SH:-${pkgdatadir}/zpios.sh} +ZPIOS_SURVEY_SH=${ZPIOS_SURVEY_SH:-${pkgdatadir}/zpios-survey.sh} LDMOD=${LDMOD:-/sbin/modprobe} LSMOD=${LSMOD:-/sbin/lsmod} RMMOD=${RMMOD:-/sbin/rmmod} INFOMOD=${INFOMOD:-/sbin/modinfo} LOSETUP=${LOSETUP:-/sbin/losetup} +MDADM=${MDADM:-/sbin/mdadm} +PARTED=${PARTED:-/sbin/parted} +BLOCKDEV=${BLOCKDEV:-/sbin/blockdev} +LSSCSI=${LSSCSI:-/usr/bin/lsscsi} +SCSIRESCAN=${SCSIRESCAN:-/usr/bin/scsi-rescan} SYSCTL=${SYSCTL:-/sbin/sysctl} UDEVADM=${UDEVADM:-/sbin/udevadm} AWK=${AWK:-/usr/bin/awk} +COLOR_BLACK="\033[0;30m" +COLOR_DK_GRAY="\033[1;30m" +COLOR_BLUE="\033[0;34m" +COLOR_LT_BLUE="\033[1;34m" +COLOR_GREEN="\033[0;32m" +COLOR_LT_GREEN="\033[1;32m" +COLOR_CYAN="\033[0;36m" +COLOR_LT_CYAN="\033[1;36m" +COLOR_RED="\033[0;31m" +COLOR_LT_RED="\033[1;31m" +COLOR_PURPLE="\033[0;35m" +COLOR_LT_PURPLE="\033[1;35m" +COLOR_BROWN="\033[0;33m" +COLOR_YELLOW="\033[1;33m" +COLOR_LT_GRAY="\033[0;37m" +COLOR_WHITE="\033[1;37m" +COLOR_RESET="\033[0m" + die() { echo -e "${PROG}: $1" >&2 exit 1 @@ -70,14 +102,58 @@ msg() { } pass() { - echo "PASS" + echo -e "${COLOR_GREEN}Pass${COLOR_RESET}" } fail() { - echo "FAIL ($1)" + echo -e "${COLOR_RED}Fail${COLOR_RESET} ($1)" exit $1 } +skip() { + echo -e "${COLOR_BROWN}Skip${COLOR_RESET}" +} + +populate() { + local ROOT=$1 + local MAX_DIR_SIZE=$2 + local MAX_FILE_SIZE=$3 + + mkdir -p $ROOT/{a,b,c,d,e,f,g}/{h,i} + DIRS=`find $ROOT` + + for DIR in $DIRS; do + COUNT=$(($RANDOM % $MAX_DIR_SIZE)) + + for i in `seq $COUNT`; do + FILE=`mktemp -p ${DIR}` + SIZE=$(($RANDOM % $MAX_FILE_SIZE)) + dd if=/dev/urandom of=$FILE bs=1k count=$SIZE &>/dev/null + done + done + + return 0 +} + +init() { + # Disable the udev rule 90-zfs.rules to prevent the zfs module + # stack from being loaded due to the detection of a zfs device. + # This is important because the test scripts require full control + # over when and how the modules are loaded/unloaded. A trap is + # set to ensure the udev rule is correctly replaced on exit. + local RULE=${udevruledir}/90-zfs.rules + if test -e ${RULE}; then + trap "mv ${RULE}.disabled ${RULE}" INT TERM EXIT + mv ${RULE} ${RULE}.disabled + fi + + # Create a random directory tree of files and sub-directories to + # to act as a copy source for the various regression tests. + SRC_DIR=`mktemp -d -p /var/tmp/ zfs.src.XXXXXXXX` + trap "rm -Rf $SRC_DIR" INT TERM EXIT + populate $SRC_DIR 10 100 +} + spl_dump_log() { ${SYSCTL} -w kernel.spl.debug.dump=1 &>/dev/null local NAME=`dmesg | tail -n 1 | cut -f5 -d' '` @@ -128,7 +204,7 @@ load_module() { echo "Loading ${NAME} ($@)" fi - ${LDMOD} $* || ERROR="Failed to load $1" return 1 + ${LDMOD} $* &>/dev/null || ERROR="Failed to load $1" return 1 return 0 } @@ -136,6 +212,10 @@ load_module() { load_modules() { mkdir -p /etc/zfs + for MOD in ${KERNEL_MODULES[*]}; do + load_module ${MOD} + done + for MOD in ${MODULES[*]}; do local NAME=`basename ${MOD} .ko` local VALUE= @@ -196,8 +276,19 @@ unload_modules() { return 0 } +# +# Check that the mdadm utilities are installed. +# +check_loop_utils() { + test -f ${LOSETUP} || die "${LOSETUP} utility must be installed" +} + + +# +# Find and return an unused loopback device. +# unused_loop_device() { - for DEVICE in `ls -1 /dev/loop*`; do + for DEVICE in `ls -1 /dev/loop[0-9]* 2>/dev/null`; do ${LOSETUP} ${DEVICE} &>/dev/null if [ $? -ne 0 ]; then echo ${DEVICE} @@ -210,7 +301,7 @@ unused_loop_device() { # # This can be slightly dangerous because the loop devices we are -# cleanup up may not be ours. However, if the devices are currently +# cleaning up may not be ours. However, if the devices are currently # in use we will not be able to remove them, and we only remove # devices which include 'zpool' in the name. So any damage we might # do should be limited to other zfs related testing. @@ -227,8 +318,212 @@ cleanup_loop_devices() { } # +# Destroy the passed loopback devices, this is used when you know +# the names of the loopback devices. +# +destroy_loop_devices() { + local LODEVICES="$1" + + msg "Destroying ${LODEVICES}" + ${LOSETUP} -d ${LODEVICES} || \ + die "Error $? destroying ${FILE} -> ${DEVICE} loopback" + + rm -f ${FILES} + return 0 +} + +# +# Create a device label. +# +label() { + local DEVICE=$1 + local LABEL=$2 + + ${PARTED} ${DEVICE} --script -- mklabel ${LABEL} || return 1 + + return 0 +} + +# +# Create a primary partition on a block device. +# +partition() { + local DEVICE=$1 + local TYPE=$2 + local START=$3 + local END=$4 + + ${PARTED} --align optimal ${DEVICE} --script -- \ + mkpart ${TYPE} ${START} ${END} || return 1 + udev_trigger + + return 0 +} + +# +# Create a filesystem on the block device +# +format() { + local DEVICE=$1 + local FSTYPE=$2 + + # Force 4K blocksize, else mkfs.ext2 tries to use 8K, which + # won't mount + /sbin/mkfs.${FSTYPE} -b 4096 -F -q ${DEVICE} >/dev/null || return 1 + + return 0 +} + +# +# Check that the mdadm utilities are installed. +# +check_md_utils() { + test -f ${MDADM} || die "${MDADM} utility must be installed" + test -f ${PARTED} || die "${PARTED} utility must be installed" +} + +check_md_partitionable() { + local LOFILE=`mktemp -p /tmp zpool-lo.XXXXXXXX` + local LODEVICE=`unused_loop_device` + local MDDEVICE=`unused_md_device` + local RESULT=1 + + check_md_utils + + rm -f ${LOFILE} + dd if=/dev/zero of=${LOFILE} bs=1M count=0 seek=16 \ + &>/dev/null || return ${RESULT} + + msg "Creating ${LODEVICE} using ${LOFILE}" + ${LOSETUP} ${LODEVICE} ${LOFILE} + if [ $? -ne 0 ]; then + rm -f ${LOFILE} + return ${RESULT} + fi + + msg "Creating ${MDDEVICE} using ${LODEVICE}" + ${MDADM} --build ${MDDEVICE} --level=faulty \ + --raid-devices=1 ${LODEVICE} &>/dev/null + if [ $? -ne 0 ]; then + destroy_loop_devices ${LODEVICE} + rm -f ${LOFILE} + return ${RESULT} + fi + wait_udev ${MDDEVICE} 30 + + ${BLOCKDEV} --rereadpt ${MDDEVICE} 2>/dev/null + RESULT=$? + + destroy_md_devices ${MDDEVICE} + destroy_loop_devices ${LODEVICE} + rm -f ${LOFILE} + + return ${RESULT} +} + +# +# Find and return an unused md device. +# +unused_md_device() { + for (( i=0; i<32; i++ )); do + MDDEVICE=md${i} + + # Skip active devicesudo in /proc/mdstat. + grep -q "${MDDEVICE} " /proc/mdstat && continue + + # Device doesn't exist, use it. + if [ ! -e $/dev/{MDDEVICE} ]; then + echo /dev/${MDDEVICE} + return + fi + + # Device exists but may not be in use. + if [ -b /dev/${MDDEVICE} ]; then + ${MDADM} --detail /dev/${MDDEVICE} &>/dev/null + if [ $? -eq 1 ]; then + echo /dev/${MDDEVICE} + return + fi + fi + done + + die "Error: Unable to find unused md device" +} + +# +# This can be slightly dangerous because it is possible the md devices +# we are cleaning up may not be ours. However, if the devices are +# currently in use we will not be able to remove them, and even if +# we remove devices which were not out we do not zero the super block +# so you should be able to reconstruct them. +# +cleanup_md_devices() { + destroy_md_devices "`ls /dev/md* 2>/dev/null | grep -v p`" + udev_trigger +} + +# +# Destroy the passed md devices, this is used when you know +# the names of the md devices. +# +destroy_md_devices() { + local MDDEVICES="$1" + + msg "Destroying ${MDDEVICES}" + for MDDEVICE in ${MDDEVICES}; do + ${MDADM} --stop ${MDDEVICE} &>/dev/null + ${MDADM} --remove ${MDDEVICE} &>/dev/null + ${MDADM} --detail ${MDDEVICE} &>/dev/null + done + + return 0 +} + +# +# Check that the scsi utilities are installed. +# +check_sd_utils() { + ${INFOMOD} scsi_debug &>/dev/null || die "scsi_debug module required" + test -f ${LSSCSI} || die "${LSSCSI} utility must be installed" +} + +# +# Rescan the scsi bus for scsi_debug devices. It is preferable to use the +# scsi-rescan tool if it is installed, but if it's not we can fall back to +# removing and readding the device manually. This rescan will only effect +# the first scsi_debug device if scsi-rescan is missing. +# +scsi_rescan() { + local AWK_SCRIPT="/scsi_debug/ { print \$1; exit }" + + if [ -f ${SCSIRESCAN} ]; then + ${SCSIRESCAN} --forcerescan --remove &>/dev/null + else + local SCSIID=`${LSSCSI} | ${AWK} "${AWK_SCRIPT}" | tr -d '[]'` + local SCSIHOST=`echo ${SCSIID} | cut -f1 -d':'` + echo 1 >"/sys/class/scsi_device/${SCSIID}/device/delete" + udev_trigger + echo "- - -" >/sys/class/scsi_host/host${SCSIHOST}/scan + udev_trigger + fi +} + +# +# Trigger udev and wait for it to settle. +# +udev_trigger() { + if [ -f ${UDEVADM} ]; then + ${UDEVADM} trigger --action=change --subsystem-match=block + ${UDEVADM} settle + else + /sbin/udevtrigger + /sbin/udevsettle + fi +} + +# # The following udev helper functions assume that the provided -# udev rules file will create a /dev/disk/zpool/ +# udev rules file will create a /dev/disk/by-vdev/ # disk mapping. In this mapping each CHANNEL is represented by # the letters a-z, and the RANK is represented by the numbers # 1-n. A CHANNEL should identify a group of RANKS which are all @@ -263,14 +558,7 @@ udev_setup() { fi cp ${SRC_PATH} ${DST_PATH} - - if [ -f ${UDEVADM} ]; then - ${UDEVADM} trigger - ${UDEVADM} settle - else - /sbin/udevtrigger - /sbin/udevsettle - fi + udev_trigger fi return 0 @@ -371,3 +659,88 @@ udev_raidz2_setup() { return 0 } + +run_one_test() { + local TEST_NUM=$1 + local TEST_NAME=$2 + + printf "%-4d %-34s " ${TEST_NUM} "${TEST_NAME}" + test_${TEST_NUM} +} + +skip_one_test() { + local TEST_NUM=$1 + local TEST_NAME=$2 + + printf "%-4d %-34s " ${TEST_NUM} "${TEST_NAME}" + skip +} + +run_test() { + local TEST_NUM=$1 + local TEST_NAME=$2 + + for i in ${TESTS_SKIP[@]}; do + if [[ $i == ${TEST_NUM} ]] ; then + skip_one_test ${TEST_NUM} "${TEST_NAME}" + return 0 + fi + done + + if [ "${TESTS_RUN[0]}" = "*" ]; then + run_one_test ${TEST_NUM} "${TEST_NAME}" + else + for i in ${TESTS_RUN[@]}; do + if [[ $i == ${TEST_NUM} ]] ; then + run_one_test ${TEST_NUM} "${TEST_NAME}" + return 0 + fi + done + + skip_one_test ${TEST_NUM} "${TEST_NAME}" + fi +} + +wait_udev() { + local DEVICE=$1 + local DELAY=$2 + local COUNT=0 + + udev_trigger + while [ ! -e ${DEVICE} ]; do + if [ ${COUNT} -gt ${DELAY} ]; then + return 1 + fi + + let COUNT=${COUNT}+1 + sleep 1 + done + + return 0 +} + +stack_clear() { + local STACK_MAX_SIZE=/sys/kernel/debug/tracing/stack_max_size + local STACK_TRACER_ENABLED=/proc/sys/kernel/stack_tracer_enabled + + if [ -e $STACK_MAX_SIZE ]; then + echo 1 >$STACK_TRACER_ENABLED + echo 0 >$STACK_MAX_SIZE + fi +} + +stack_check() { + local STACK_MAX_SIZE=/sys/kernel/debug/tracing/stack_max_size + local STACK_TRACE=/sys/kernel/debug/tracing/stack_trace + local STACK_LIMIT=7000 + + if [ -e $STACK_MAX_SIZE ]; then + STACK_SIZE=`cat $STACK_MAX_SIZE` + + if [ $STACK_SIZE -ge $STACK_LIMIT ]; then + echo + echo "Warning: max stack size $STACK_SIZE bytes" + cat $STACK_TRACE + fi + fi +}