]> git.proxmox.com Git - ceph.git/blob - ceph/qa/run_xfstests.sh
4a782ba73da73b28025b39477b881a63af869315
[ceph.git] / ceph / qa / run_xfstests.sh
1 #!/bin/bash
2
3 # Copyright (C) 2012 Dreamhost, LLC
4 #
5 # This is free software; see the source for copying conditions.
6 # There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR
7 # A PARTICULAR PURPOSE.
8 #
9 # This is free software; you can redistribute it and/or modify it
10 # under the terms of the GNU General Public License as
11 # published by the Free Software Foundation version 2.
12
13 # Usage:
14 # run_xfstests -t /dev/<testdev> -s /dev/<scratchdev> [-f <fstype>] -- <tests>
15 # - test device and scratch device will both get trashed
16 # - fstypes can be xfs, ext4, or btrfs (xfs default)
17 # - tests can be listed individually: generic/001 xfs/008 xfs/009
18 # tests can also be specified by group: -g quick
19 #
20 # Exit status:
21 # 0: success
22 # 1: usage error
23 # 2: other runtime error
24 # 99: argument count error (programming error)
25 # 100: getopt error (internal error)
26
27 # Alex Elder <elder@dreamhost.com>
28 # April 13, 2012
29
30 set -e
31
32 PROGNAME=$(basename $0)
33
34 # xfstests is downloaded from this git repository and then built.
35 # XFSTESTS_REPO="git://oss.sgi.com/xfs/cmds/xfstests.git"
36 XFSTESTS_REPO="git://git.ceph.com/xfstests.git"
37 XFSTESTS_VERSION="facff609afd6a2ca557c2b679e088982026aa188"
38 XFSPROGS_REPO="git://oss.sgi.com/xfs/cmds/xfsprogs"
39 XFSPROGS_VERSION="v3.2.2"
40 XFSDUMP_REPO="git://oss.sgi.com/xfs/cmds/xfsdump"
41 XFSDUMP_VERSION="v3.1.4"
42
43 # Default command line option values
44 COUNT="1"
45 EXPUNGE_FILE=""
46 DO_RANDOMIZE="" # false
47 FS_TYPE="xfs"
48 SCRATCH_DEV="" # MUST BE SPECIFIED
49 TEST_DEV="" # MUST BE SPECIFIED
50 TESTS="-g auto" # The "auto" group is supposed to be "known good"
51
52 # We no longer need to set the stripe unit in XFS_MKFS_OPTIONS because recent
53 # versions of mkfs.xfs autodetect it.
54
55 # print an error message and quit with non-zero status
56 function err() {
57 if [ $# -gt 0 ]; then
58 echo "" >&2
59 echo "${PROGNAME}: ${FUNCNAME[1]}: $@" >&2
60 fi
61 exit 2
62 }
63
64 # routine used to validate argument counts to all shell functions
65 function arg_count() {
66 local func
67 local want
68 local got
69
70 if [ $# -eq 2 ]; then
71 func="${FUNCNAME[1]}" # calling function
72 want=$1
73 got=$2
74 else
75 func="${FUNCNAME[0]}" # i.e., arg_count
76 want=2
77 got=$#
78 fi
79 [ "${want}" -eq "${got}" ] && return 0
80 echo "${PROGNAME}: ${func}: arg count bad (want ${want} got ${got})" >&2
81 exit 99
82 }
83
84 # validation function for repeat count argument
85 function count_valid() {
86 arg_count 1 $#
87
88 test "$1" -gt 0 # 0 is pointless; negative is wrong
89 }
90
91 # validation function for filesystem type argument
92 function fs_type_valid() {
93 arg_count 1 $#
94
95 case "$1" in
96 xfs|ext4|btrfs) return 0 ;;
97 *) return 1 ;;
98 esac
99 }
100
101 # validation function for device arguments
102 function device_valid() {
103 arg_count 1 $#
104
105 # Very simple testing--really should try to be more careful...
106 test -b "$1"
107 }
108
109 # validation function for expunge file argument
110 function expunge_file_valid() {
111 arg_count 1 $#
112
113 test -s "$1"
114 }
115
116 # print a usage message and quit
117 #
118 # if a message is supplied, print that first, and then exit
119 # with non-zero status
120 function usage() {
121 if [ $# -gt 0 ]; then
122 echo "" >&2
123 echo "$@" >&2
124 fi
125
126 echo "" >&2
127 echo "Usage: ${PROGNAME} <options> -- <tests>" >&2
128 echo "" >&2
129 echo " options:" >&2
130 echo " -h or --help" >&2
131 echo " show this message" >&2
132 echo " -c or --count" >&2
133 echo " iteration count (1 or more)" >&2
134 echo " -f or --fs-type" >&2
135 echo " one of: xfs, ext4, btrfs" >&2
136 echo " (default fs-type: xfs)" >&2
137 echo " -r or --randomize" >&2
138 echo " randomize test order" >&2
139 echo " -s or --scratch-dev (REQUIRED)" >&2
140 echo " name of device used for scratch filesystem" >&2
141 echo " -t or --test-dev (REQUIRED)" >&2
142 echo " name of device used for test filesystem" >&2
143 echo " -x or --expunge-file" >&2
144 echo " name of file with list of tests to skip" >&2
145 echo " tests:" >&2
146 echo " list of test numbers, e.g.:" >&2
147 echo " generic/001 xfs/008 shared/032 btrfs/009" >&2
148 echo " or possibly an xfstests test group, e.g.:" >&2
149 echo " -g quick" >&2
150 echo " (default tests: -g auto)" >&2
151 echo "" >&2
152
153 [ $# -gt 0 ] && exit 1
154
155 exit 0 # This is used for a --help
156 }
157
158 # parse command line arguments
159 function parseargs() {
160 # Short option flags
161 SHORT_OPTS=""
162 SHORT_OPTS="${SHORT_OPTS},h"
163 SHORT_OPTS="${SHORT_OPTS},c:"
164 SHORT_OPTS="${SHORT_OPTS},f:"
165 SHORT_OPTS="${SHORT_OPTS},r"
166 SHORT_OPTS="${SHORT_OPTS},s:"
167 SHORT_OPTS="${SHORT_OPTS},t:"
168 SHORT_OPTS="${SHORT_OPTS},x:"
169
170 # Long option flags
171 LONG_OPTS=""
172 LONG_OPTS="${LONG_OPTS},help"
173 LONG_OPTS="${LONG_OPTS},count:"
174 LONG_OPTS="${LONG_OPTS},fs-type:"
175 LONG_OPTS="${LONG_OPTS},randomize"
176 LONG_OPTS="${LONG_OPTS},scratch-dev:"
177 LONG_OPTS="${LONG_OPTS},test-dev:"
178 LONG_OPTS="${LONG_OPTS},expunge-file:"
179
180 TEMP=$(getopt --name "${PROGNAME}" \
181 --options "${SHORT_OPTS}" \
182 --longoptions "${LONG_OPTS}" \
183 -- "$@")
184 eval set -- "$TEMP"
185
186 while [ "$1" != "--" ]; do
187 case "$1" in
188 -h|--help)
189 usage
190 ;;
191 -c|--count)
192 count_valid "$2" ||
193 usage "invalid count '$2'"
194 COUNT="$2"
195 shift
196 ;;
197 -f|--fs-type)
198 fs_type_valid "$2" ||
199 usage "invalid fs_type '$2'"
200 FS_TYPE="$2"
201 shift
202 ;;
203 -r|--randomize)
204 DO_RANDOMIZE="t"
205 ;;
206 -s|--scratch-dev)
207 device_valid "$2" ||
208 usage "invalid scratch-dev '$2'"
209 SCRATCH_DEV="$2"
210 shift
211 ;;
212 -t|--test-dev)
213 device_valid "$2" ||
214 usage "invalid test-dev '$2'"
215 TEST_DEV="$2"
216 shift
217 ;;
218 -x|--expunge-file)
219 expunge_file_valid "$2" ||
220 usage "invalid expunge-file '$2'"
221 EXPUNGE_FILE="$2"
222 shift
223 ;;
224 *)
225 exit 100 # Internal error
226 ;;
227 esac
228 shift
229 done
230 shift
231
232 [ -n "${TEST_DEV}" ] || usage "test-dev must be supplied"
233 [ -n "${SCRATCH_DEV}" ] || usage "scratch-dev must be supplied"
234
235 [ $# -eq 0 ] || TESTS="$@"
236 }
237
238 ################################################################
239
240 [ -n "${TESTDIR}" ] || usage "TESTDIR env variable must be set"
241
242 # Set up some environment for normal teuthology test setup.
243 # This really should not be necessary but I found it was.
244 export CEPH_ARGS="--conf ${TESTDIR}/ceph.conf"
245 export CEPH_ARGS="${CEPH_ARGS} --keyring ${TESTDIR}/data/client.0.keyring"
246 export CEPH_ARGS="${CEPH_ARGS} --name client.0"
247
248 export LD_LIBRARY_PATH="${TESTDIR}/binary/usr/local/lib:${LD_LIBRARY_PATH}"
249 export PATH="${TESTDIR}/binary/usr/local/bin:${PATH}"
250 export PATH="${TESTDIR}/binary/usr/local/sbin:${PATH}"
251
252 ################################################################
253
254 # Filesystem-specific mkfs options--set if not supplied
255 #export XFS_MKFS_OPTIONS="${XFS_MKFS_OPTIONS:--f -l su=65536}"
256 export EXT4_MKFS_OPTIONS="${EXT4_MKFS_OPTIONS:--F}"
257 export BTRFS_MKFS_OPTION # No defaults
258
259 XFSTESTS_DIR="/var/lib/xfstests" # Where the tests live
260 XFSPROGS_DIR="/tmp/cephtest/xfsprogs-install"
261 XFSDUMP_DIR="/tmp/cephtest/xfsdump-install"
262 export PATH="${XFSPROGS_DIR}/sbin:${XFSDUMP_DIR}/sbin:${PATH}"
263
264 # download, build, and install xfstests
265 function install_xfstests() {
266 arg_count 0 $#
267
268 local multiple=""
269 local ncpu
270
271 pushd "${TESTDIR}"
272
273 git clone "${XFSTESTS_REPO}"
274
275 cd xfstests
276 git checkout "${XFSTESTS_VERSION}"
277
278 ncpu=$(getconf _NPROCESSORS_ONLN 2>&1)
279 [ -n "${ncpu}" -a "${ncpu}" -gt 1 ] && multiple="-j ${ncpu}"
280
281 make realclean
282 make ${multiple}
283 make -k install
284
285 popd
286 }
287
288 # remove previously-installed xfstests files
289 function remove_xfstests() {
290 arg_count 0 $#
291
292 rm -rf "${TESTDIR}/xfstests"
293 rm -rf "${XFSTESTS_DIR}"
294 }
295
296 # create a host options file that uses the specified devices
297 function setup_host_options() {
298 arg_count 0 $#
299 export MNTDIR="/tmp/cephtest"
300
301 # Create mount points for the test and scratch filesystems
302 mkdir -p ${MNTDIR}
303 local test_dir="$(mktemp -d ${MNTDIR}/test_dir.XXXXXXXXXX)"
304 local scratch_dir="$(mktemp -d ${MNTDIR}/scratch_mnt.XXXXXXXXXX)"
305
306 # Write a host options file that uses these devices.
307 # xfstests uses the file defined by HOST_OPTIONS as the
308 # place to get configuration variables for its run, and
309 # all (or most) of the variables set here are required.
310 export HOST_OPTIONS="$(mktemp ${TESTDIR}/host_options.XXXXXXXXXX)"
311 cat > "${HOST_OPTIONS}" <<-!
312 # Created by ${PROGNAME} on $(date)
313 # HOST_OPTIONS="${HOST_OPTIONS}"
314 TEST_DEV="${TEST_DEV}"
315 SCRATCH_DEV="${SCRATCH_DEV}"
316 TEST_DIR="${test_dir}"
317 SCRATCH_MNT="${scratch_dir}"
318 FSTYP="${FS_TYPE}"
319 export TEST_DEV SCRATCH_DEV TEST_DIR SCRATCH_MNT FSTYP
320 #
321 export XFS_MKFS_OPTIONS="${XFS_MKFS_OPTIONS}"
322 !
323
324 # Now ensure we are using the same values
325 . "${HOST_OPTIONS}"
326 }
327
328 # remove the host options file, plus the directories it refers to
329 function cleanup_host_options() {
330 arg_count 0 $#
331
332 rm -rf "${TEST_DIR}" "${SCRATCH_MNT}"
333 rm -f "${HOST_OPTIONS}"
334 }
335
336 # run mkfs on the given device using the specified filesystem type
337 function do_mkfs() {
338 arg_count 1 $#
339
340 local dev="${1}"
341 local options
342
343 case "${FSTYP}" in
344 xfs) options="${XFS_MKFS_OPTIONS}" ;;
345 ext4) options="${EXT4_MKFS_OPTIONS}" ;;
346 btrfs) options="${BTRFS_MKFS_OPTIONS}" ;;
347 esac
348
349 "mkfs.${FSTYP}" ${options} "${dev}" ||
350 err "unable to make ${FSTYP} file system on device \"${dev}\""
351 }
352
353 # mount the given device on the given mount point
354 function do_mount() {
355 arg_count 2 $#
356
357 local dev="${1}"
358 local dir="${2}"
359
360 mount "${dev}" "${dir}" ||
361 err "unable to mount file system \"${dev}\" on \"${dir}\""
362 }
363
364 # unmount a previously-mounted device
365 function do_umount() {
366 arg_count 1 $#
367
368 local dev="${1}"
369
370 if mount | grep "${dev}" > /dev/null; then
371 if ! umount "${dev}"; then
372 err "unable to unmount device \"${dev}\""
373 fi
374 else
375 # Report it but don't error out
376 echo "device \"${dev}\" was not mounted" >&2
377 fi
378 }
379
380 # do basic xfstests setup--make and mount the test and scratch filesystems
381 function setup_xfstests() {
382 arg_count 0 $#
383
384 # TEST_DEV can persist across test runs, but for now we
385 # don't bother. I believe xfstests prefers its devices to
386 # have been already been formatted for the desired
387 # filesystem type--it uses blkid to identify things or
388 # something. So we mkfs both here for a fresh start.
389 do_mkfs "${TEST_DEV}"
390 do_mkfs "${SCRATCH_DEV}"
391
392 # I believe the test device is expected to be mounted; the
393 # scratch doesn't need to be (but it doesn't hurt).
394 do_mount "${TEST_DEV}" "${TEST_DIR}"
395 do_mount "${SCRATCH_DEV}" "${SCRATCH_MNT}"
396 }
397
398 # clean up changes made by setup_xfstests
399 function cleanup_xfstests() {
400 arg_count 0 $#
401
402 # Unmount these in case a test left them mounted (plus
403 # the corresponding setup function mounted them...)
404 do_umount "${TEST_DEV}"
405 do_umount "${SCRATCH_DEV}"
406 rmdir "${TEST_DIR}"
407 rmdir "${SCRATCH_MNT}"
408 rmdir "${MNTDIR}"
409 }
410
411 function install_xfsprogs() {
412 arg_count 0 $#
413
414 pushd "${TESTDIR}"
415 git clone ${XFSPROGS_REPO}
416 cd xfsprogs
417 git checkout ${XFSPROGS_VERSION}
418 libtoolize -c `libtoolize -n -i >/dev/null 2>/dev/null && echo -i` -f
419 cp include/install-sh .
420 aclocal -I m4
421 autoconf
422 ./configure --prefix=${XFSPROGS_DIR}
423 make install
424 popd
425 }
426
427 function install_xfsdump() {
428 arg_count 0 $#
429
430 pushd "${TESTDIR}"
431 git clone ${XFSDUMP_REPO}
432 cd xfsdump
433 git checkout ${XFSDUMP_VERSION}
434
435 # somebody took #define min and #define max out, which breaks the build on
436 # ubuntu. we back out this commit here, though that may cause problems with
437 # this script down the line.
438 git revert -n 5a2985233c390d59d2a9757b119cb0e001c87a96
439 libtoolize -c `libtoolize -n -i >/dev/null 2>/dev/null && echo -i` -f
440 cp include/install-sh .
441 aclocal -I m4
442 autoconf
443 ./configure --prefix=${XFSDUMP_DIR}
444 (make -k install || true) # that's right, the install process is broken too
445 popd
446 }
447
448 function remove_xfsprogs() {
449 arg_count 0 $#
450
451 rm -rf ${TESTDIR}/xfsprogs
452 rm -rf ${XFSPROGS_DIR}
453 }
454
455 function remove_xfsdump() {
456 arg_count 0 $#
457
458 rm -rf ${TESTDIR}/xfsdump
459 rm -rf ${XFSDUMP_DIR}
460 }
461
462
463 # top-level setup routine
464 function setup() {
465 arg_count 0 $#
466
467 setup_host_options
468 install_xfsprogs
469 install_xfsdump
470 install_xfstests
471 setup_xfstests
472 }
473
474 # top-level (final) cleanup routine
475 function cleanup() {
476 arg_count 0 $#
477
478 cd /
479 remove_xfsprogs
480 remove_xfsdump
481 cleanup_xfstests
482 remove_xfstests
483 cleanup_host_options
484 }
485 trap cleanup EXIT ERR HUP INT QUIT
486
487 # ################################################################
488
489 start_date="$(date)"
490
491 parseargs "$@"
492
493 setup
494
495 pushd "${XFSTESTS_DIR}"
496 for (( i = 1 ; i <= "${COUNT}" ; i++ )); do
497 [ "${COUNT}" -gt 1 ] && echo "=== Iteration "$i" starting at: $(date)"
498
499 EXPUNGE=""
500 [ -n "${EXPUNGE_FILE}" ] && EXPUNGE="-E ${EXPUNGE_FILE}"
501
502 RANDOMIZE=""
503 [ -n "${DO_RANDOMIZE}" ] && RANDOMIZE="-r"
504
505 # -T output timestamps
506 ./check -T ${RANDOMIZE} ${EXPUNGE} ${TESTS}
507 status=$?
508
509 [ "${COUNT}" -gt 1 ] && echo "=== Iteration "$i" complete at: $(date)"
510 done
511 popd
512
513 # cleanup is called via the trap call, above
514
515 echo "This xfstests run started at: ${start_date}"
516 echo "xfstests run completed at: $(date)"
517 [ "${COUNT}" -gt 1 ] && echo "xfstests run consisted of ${COUNT} iterations"
518
519 exit "${status}"