]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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}" |