#!/usr/bin/env bash # Copyright (C) 2012 Dreamhost, LLC # # This is free software; see the source for copying conditions. # There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR # A PARTICULAR PURPOSE. # # This is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as # published by the Free Software Foundation version 2. # Usage: # run_xfstests -t /dev/ -s /dev/ [-f ] -- # - test device and scratch device will both get trashed # - fstypes can be xfs, ext4, or btrfs (xfs default) # - tests can be listed individually: generic/001 xfs/008 xfs/009 # tests can also be specified by group: -g quick # # Exit status: # 0: success # 1: usage error # 2: other runtime error # 99: argument count error (programming error) # 100: getopt error (internal error) # Alex Elder # April 13, 2012 set -e PROGNAME=$(basename $0) # Default command line option values COUNT="1" EXPUNGE_FILE="" DO_RANDOMIZE="" # false FSTYP="xfs" SCRATCH_DEV="" # MUST BE SPECIFIED TEST_DEV="" # MUST BE SPECIFIED TESTS="-g auto" # The "auto" group is supposed to be "known good" # print an error message and quit with non-zero status function err() { if [ $# -gt 0 ]; then echo "" >&2 echo "${PROGNAME}: ${FUNCNAME[1]}: $@" >&2 fi exit 2 } # routine used to validate argument counts to all shell functions function arg_count() { local func local want local got if [ $# -eq 2 ]; then func="${FUNCNAME[1]}" # calling function want=$1 got=$2 else func="${FUNCNAME[0]}" # i.e., arg_count want=2 got=$# fi [ "${want}" -eq "${got}" ] && return 0 echo "${PROGNAME}: ${func}: arg count bad (want ${want} got ${got})" >&2 exit 99 } # validation function for repeat count argument function count_valid() { arg_count 1 $# test "$1" -gt 0 # 0 is pointless; negative is wrong } # validation function for filesystem type argument function fs_type_valid() { arg_count 1 $# case "$1" in xfs|ext4|btrfs) return 0 ;; *) return 1 ;; esac } # validation function for device arguments function device_valid() { arg_count 1 $# # Very simple testing--really should try to be more careful... test -b "$1" } # validation function for expunge file argument function expunge_file_valid() { arg_count 1 $# test -s "$1" } # print a usage message and quit # # if a message is supplied, print that first, and then exit # with non-zero status function usage() { if [ $# -gt 0 ]; then echo "" >&2 echo "$@" >&2 fi echo "" >&2 echo "Usage: ${PROGNAME} -- " >&2 echo "" >&2 echo " options:" >&2 echo " -h or --help" >&2 echo " show this message" >&2 echo " -c or --count" >&2 echo " iteration count (1 or more)" >&2 echo " -f or --fs-type" >&2 echo " one of: xfs, ext4, btrfs" >&2 echo " (default fs-type: xfs)" >&2 echo " -r or --randomize" >&2 echo " randomize test order" >&2 echo " -s or --scratch-dev (REQUIRED)" >&2 echo " name of device used for scratch filesystem" >&2 echo " -t or --test-dev (REQUIRED)" >&2 echo " name of device used for test filesystem" >&2 echo " -x or --expunge-file" >&2 echo " name of file with list of tests to skip" >&2 echo " tests:" >&2 echo " list of test numbers, e.g.:" >&2 echo " generic/001 xfs/008 shared/032 btrfs/009" >&2 echo " or possibly an xfstests test group, e.g.:" >&2 echo " -g quick" >&2 echo " (default tests: -g auto)" >&2 echo "" >&2 [ $# -gt 0 ] && exit 1 exit 0 # This is used for a --help } # parse command line arguments function parseargs() { # Short option flags SHORT_OPTS="" SHORT_OPTS="${SHORT_OPTS},h" SHORT_OPTS="${SHORT_OPTS},c:" SHORT_OPTS="${SHORT_OPTS},f:" SHORT_OPTS="${SHORT_OPTS},r" SHORT_OPTS="${SHORT_OPTS},s:" SHORT_OPTS="${SHORT_OPTS},t:" SHORT_OPTS="${SHORT_OPTS},x:" # Long option flags LONG_OPTS="" LONG_OPTS="${LONG_OPTS},help" LONG_OPTS="${LONG_OPTS},count:" LONG_OPTS="${LONG_OPTS},fs-type:" LONG_OPTS="${LONG_OPTS},randomize" LONG_OPTS="${LONG_OPTS},scratch-dev:" LONG_OPTS="${LONG_OPTS},test-dev:" LONG_OPTS="${LONG_OPTS},expunge-file:" TEMP=$(getopt --name "${PROGNAME}" \ --options "${SHORT_OPTS}" \ --longoptions "${LONG_OPTS}" \ -- "$@") eval set -- "$TEMP" while [ "$1" != "--" ]; do case "$1" in -h|--help) usage ;; -c|--count) count_valid "$2" || usage "invalid count '$2'" COUNT="$2" shift ;; -f|--fs-type) fs_type_valid "$2" || usage "invalid fs_type '$2'" FSTYP="$2" shift ;; -r|--randomize) DO_RANDOMIZE="t" ;; -s|--scratch-dev) device_valid "$2" || usage "invalid scratch-dev '$2'" SCRATCH_DEV="$2" shift ;; -t|--test-dev) device_valid "$2" || usage "invalid test-dev '$2'" TEST_DEV="$2" shift ;; -x|--expunge-file) expunge_file_valid "$2" || usage "invalid expunge-file '$2'" EXPUNGE_FILE="$2" shift ;; *) exit 100 # Internal error ;; esac shift done shift [ -n "${TEST_DEV}" ] || usage "test-dev must be supplied" [ -n "${SCRATCH_DEV}" ] || usage "scratch-dev must be supplied" [ $# -eq 0 ] || TESTS="$@" } ################################################################ # run mkfs on the given device using the specified filesystem type function do_mkfs() { arg_count 1 $# local dev="${1}" local options case "${FSTYP}" in xfs) options="-f" ;; ext4) options="-F" ;; btrfs) options="-f" ;; esac "mkfs.${FSTYP}" ${options} "${dev}" || err "unable to make ${FSTYP} file system on device \"${dev}\"" } # top-level setup routine function setup() { arg_count 0 $# wget -P "${TESTDIR}" http://download.ceph.com/qa/xfstests.tar.gz tar zxf "${TESTDIR}/xfstests.tar.gz" -C "$(dirname "${XFSTESTS_DIR}")" mkdir "${TEST_DIR}" mkdir "${SCRATCH_MNT}" do_mkfs "${TEST_DEV}" } # top-level (final) cleanup routine function cleanup() { arg_count 0 $# # ensure teuthology can clean up the logs chmod -R a+rw "${TESTDIR}/archive" findmnt "${TEST_DEV}" && umount "${TEST_DEV}" [ -d "${SCRATCH_MNT}" ] && rmdir "${SCRATCH_MNT}" [ -d "${TEST_DIR}" ] && rmdir "${TEST_DIR}" rm -rf "${XFSTESTS_DIR}" rm -f "${TESTDIR}/xfstests.tar.gz" } # ################################################################ start_date="$(date)" parseargs "$@" [ -n "${TESTDIR}" ] || usage "TESTDIR env variable must be set" [ -d "${TESTDIR}/archive" ] || usage "\$TESTDIR/archive directory must exist" TESTDIR="$(readlink -e "${TESTDIR}")" [ -n "${EXPUNGE_FILE}" ] && EXPUNGE_FILE="$(readlink -e "${EXPUNGE_FILE}")" XFSTESTS_DIR="/var/lib/xfstests" # hardcoded into dbench binary TEST_DIR="/mnt/test_dir" SCRATCH_MNT="/mnt/scratch_mnt" MKFS_OPTIONS="" EXT_MOUNT_OPTIONS="-o block_validity" trap cleanup EXIT ERR HUP INT QUIT setup export TEST_DEV export TEST_DIR export SCRATCH_DEV export SCRATCH_MNT export FSTYP export MKFS_OPTIONS export EXT_MOUNT_OPTIONS pushd "${XFSTESTS_DIR}" for (( i = 1 ; i <= "${COUNT}" ; i++ )); do [ "${COUNT}" -gt 1 ] && echo "=== Iteration "$i" starting at: $(date)" RESULT_BASE="${TESTDIR}/archive/results-${i}" mkdir "${RESULT_BASE}" export RESULT_BASE EXPUNGE="" [ -n "${EXPUNGE_FILE}" ] && EXPUNGE="-E ${EXPUNGE_FILE}" RANDOMIZE="" [ -n "${DO_RANDOMIZE}" ] && RANDOMIZE="-r" # -T output timestamps PATH="${PWD}/bin:${PATH}" ./check -T ${RANDOMIZE} ${EXPUNGE} ${TESTS} findmnt "${TEST_DEV}" && umount "${TEST_DEV}" [ "${COUNT}" -gt 1 ] && echo "=== Iteration "$i" complete at: $(date)" done popd # cleanup is called via the trap call, above echo "This xfstests run started at: ${start_date}" echo "xfstests run completed at: $(date)" [ "${COUNT}" -gt 1 ] && echo "xfstests run consisted of ${COUNT} iterations" echo OK