]>
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_xfs_tests -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 or in ranges: 1 3-5 8 | |
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 | ||
38 | # Default command line option values | |
39 | COUNT="1" | |
40 | FS_TYPE="xfs" | |
41 | SCRATCH_DEV="" # MUST BE SPECIFIED | |
42 | TEST_DEV="" # MUST BE SPECIFIED | |
43 | TESTS="-g auto" # The "auto" group is supposed to be "known good" | |
44 | ||
45 | # rbd presents geometry information that causes mkfs.xfs to | |
46 | # issue a warning. This option avoids this class of problems. | |
47 | XFS_MKFS_OPTIONS="-l su=32k" | |
48 | ||
49 | # Override the default test list with a list of tests known to pass | |
50 | # until we can work through getting them all passing reliably. | |
51 | TESTS="1-7 9 11-15 17 19-21 26-29 31-34 41 46-48 50-54 56 61 63-67 69-70 74-76" | |
52 | TESTS="${TESTS} 78 79 84-89 91-92 100 103 105 108 110 116-121 124 126" | |
53 | TESTS="${TESTS} 129-135 137-141 164-167 182 184 187-190 192 194" | |
54 | TESTS="${TESTS} 196 199 201 203 214-216 220-227 234 236-238 241 243-249" | |
55 | TESTS="${TESTS} 253 257-259 261 262 269 273 275 277 278 280 285 286" | |
56 | # 275 was the highest available test as of 4/10/12. | |
57 | # 289 was the highest available test as of 11/15/12. | |
58 | ||
59 | ###### | |
60 | # Some explanation of why tests have been excluded above: | |
61 | # | |
62 | # Test 008 was pulled because it contained a race condition leading to | |
63 | # spurious failures. | |
64 | # | |
65 | # Test 049 was pulled because it caused a kernel fault. | |
66 | # http://tracker.newdream.net/issues/2260 | |
67 | # Test 232 was pulled because it caused an XFS error | |
68 | # http://tracker.newdream.net/issues/2302 | |
69 | # | |
70 | # This test passes but takes a LONG time (1+ hours): 127 | |
71 | # | |
72 | # These were not run for one (anticipated) reason or another: | |
73 | # 010 016 030 035 040 044 057 058-060 072 077 090 093-095 097-099 104 | |
74 | # 112 113 122 123 125 128 142 147-163 168 175-178 180 185 191 193 | |
75 | # 195 197 198 207-213 217 228 230-233 235 239 240 252 254 255 264-266 | |
76 | # 270-272 276 278-279 281-284 288 289 | |
77 | # | |
78 | # These tests all failed (produced output different from golden): | |
79 | # 042 073 083 096 109 169 170 200 202 204-206 218 229 240 242 250 | |
80 | # 263 276 277 279 287 | |
81 | # | |
82 | # The rest were not part of the "auto" group: | |
83 | # 018 022 023 024 025 036 037 038 039 043 055 071 080 081 082 101 | |
84 | # 102 106 107 111 114 115 136 171 172 173 251 267 268 | |
85 | ###### | |
86 | ||
87 | # print an error message and quit with non-zero status | |
88 | function err() { | |
89 | if [ $# -gt 0 ]; then | |
90 | echo "" >&2 | |
91 | echo "${PROGNAME}: ${FUNCNAME[1]}: $@" >&2 | |
92 | fi | |
93 | exit 2 | |
94 | } | |
95 | ||
96 | # routine used to validate argument counts to all shell functions | |
97 | function arg_count() { | |
98 | local func | |
99 | local want | |
100 | local got | |
101 | ||
102 | if [ $# -eq 2 ]; then | |
103 | func="${FUNCNAME[1]}" # calling function | |
104 | want=$1 | |
105 | got=$2 | |
106 | else | |
107 | func="${FUNCNAME[0]}" # i.e., arg_count | |
108 | want=2 | |
109 | got=$# | |
110 | fi | |
111 | [ "${want}" -eq "${got}" ] && return 0 | |
112 | echo "${PROGNAME}: ${func}: arg count bad (want ${want} got ${got})" >&2 | |
113 | exit 99 | |
114 | } | |
115 | ||
116 | # validation function for repeat count argument | |
117 | function count_valid() { | |
118 | arg_count 1 $# | |
119 | ||
120 | test "$1" -gt 0 # 0 is pointless; negative is wrong | |
121 | } | |
122 | ||
123 | # validation function for filesystem type argument | |
124 | function fs_type_valid() { | |
125 | arg_count 1 $# | |
126 | ||
127 | case "$1" in | |
128 | xfs|ext4|btrfs) return 0 ;; | |
129 | *) return 1 ;; | |
130 | esac | |
131 | } | |
132 | ||
133 | # validation function for device arguments | |
134 | function device_valid() { | |
135 | arg_count 1 $# | |
136 | ||
137 | # Very simple testing--really should try to be more careful... | |
138 | test -b "$1" | |
139 | } | |
140 | ||
141 | # print a usage message and quit | |
142 | # | |
143 | # if a message is supplied, print that first, and then exit | |
144 | # with non-zero status | |
145 | function usage() { | |
146 | if [ $# -gt 0 ]; then | |
147 | echo "" >&2 | |
148 | echo "$@" >&2 | |
149 | fi | |
150 | ||
151 | echo "" >&2 | |
152 | echo "Usage: ${PROGNAME} <options> <tests>" >&2 | |
153 | echo "" >&2 | |
154 | echo " options:" >&2 | |
155 | echo " -h or --help" >&2 | |
156 | echo " show this message" >&2 | |
157 | echo " -c or --count" >&2 | |
158 | echo " iteration count (1 or more)" >&2 | |
159 | echo " -f or --fs-type" >&2 | |
160 | echo " one of: xfs, ext4, btrfs" >&2 | |
161 | echo " (default fs-type: xfs)" >&2 | |
162 | echo " -s or --scratch-dev (REQUIRED)" >&2 | |
163 | echo " name of device used for scratch filesystem" >&2 | |
164 | echo " -t or --test-dev (REQUIRED)" >&2 | |
165 | echo " name of device used for test filesystem" >&2 | |
166 | echo " tests:" >&2 | |
167 | echo " list of test numbers or ranges, e.g.:" >&2 | |
168 | echo " 1-9 11-15 17 19-21 26-28 31-34 41" >&2 | |
169 | echo " or possibly an xfstests test group, e.g.:" >&2 | |
170 | echo " -g quick" >&2 | |
171 | echo " (default tests: -g auto)" >&2 | |
172 | echo "" >&2 | |
173 | ||
174 | [ $# -gt 0 ] && exit 1 | |
175 | ||
176 | exit 0 # This is used for a --help | |
177 | } | |
178 | ||
179 | # parse command line arguments | |
180 | function parseargs() { | |
181 | # Short option flags | |
182 | SHORT_OPTS="" | |
183 | SHORT_OPTS="${SHORT_OPTS},h" | |
184 | SHORT_OPTS="${SHORT_OPTS},c:" | |
185 | SHORT_OPTS="${SHORT_OPTS},f:" | |
186 | SHORT_OPTS="${SHORT_OPTS},s:" | |
187 | SHORT_OPTS="${SHORT_OPTS},t:" | |
188 | ||
189 | # Short option flags | |
190 | LONG_OPTS="" | |
191 | LONG_OPTS="${LONG_OPTS},help" | |
192 | LONG_OPTS="${LONG_OPTS},count:" | |
193 | LONG_OPTS="${LONG_OPTS},fs-type:" | |
194 | LONG_OPTS="${LONG_OPTS},scratch-dev:" | |
195 | LONG_OPTS="${LONG_OPTS},test-dev:" | |
196 | ||
197 | TEMP=$(getopt --name "${PROGNAME}" \ | |
198 | --options "${SHORT_OPTS}" \ | |
199 | --longoptions "${LONG_OPTS}" \ | |
200 | -- "$@") | |
201 | eval set -- "$TEMP" | |
202 | ||
203 | while [ "$1" != "--" ]; do | |
204 | case "$1" in | |
205 | -h|--help) | |
206 | usage | |
207 | ;; | |
208 | -c|--count) | |
209 | count_valid "$2" || | |
210 | usage "invalid count '$2'" | |
211 | COUNT="$2" | |
212 | shift | |
213 | ;; | |
214 | -f|--fs-type) | |
215 | fs_type_valid "$2" || | |
216 | usage "invalid fs_type '$2'" | |
217 | FS_TYPE="$2" | |
218 | shift | |
219 | ;; | |
220 | -s|--scratch-dev) | |
221 | device_valid "$2" || | |
222 | usage "invalid scratch-dev '$2'" | |
223 | SCRATCH_DEV="$2" | |
224 | shift | |
225 | ;; | |
226 | -t|--test-dev) | |
227 | device_valid "$2" || | |
228 | usage "invalid test-dev '$2'" | |
229 | TEST_DEV="$2" | |
230 | shift | |
231 | ;; | |
232 | *) | |
233 | exit 100 # Internal error | |
234 | ;; | |
235 | esac | |
236 | shift | |
237 | done | |
238 | shift | |
239 | ||
240 | [ -n "${TEST_DEV}" ] || usage "test-dev must be supplied" | |
241 | [ -n "${SCRATCH_DEV}" ] || usage "scratch-dev must be supplied" | |
242 | ||
243 | [ $# -eq 0 ] || TESTS="$@" | |
244 | } | |
245 | ||
246 | ################################################################ | |
247 | ||
248 | [ -z "$TESTDIR" ] && export TESTDIR="/tmp/cephtest" | |
249 | ||
250 | # Set up some environment for normal teuthology test setup. | |
251 | # This really should not be necessary but I found it was. | |
252 | export CEPH_ARGS="--conf ${TESTDIR}/ceph.conf" | |
253 | export CEPH_ARGS="${CEPH_ARGS} --keyring ${TESTDIR}/data/client.0.keyring" | |
254 | export CEPH_ARGS="${CEPH_ARGS} --name client.0" | |
255 | ||
256 | export LD_LIBRARY_PATH="${TESTDIR}/binary/usr/local/lib:${LD_LIBRARY_PATH}" | |
257 | export PATH="${TESTDIR}/binary/usr/local/bin:${PATH}" | |
258 | export PATH="${TESTDIR}/binary/usr/local/sbin:${PATH}" | |
259 | ||
260 | ################################################################ | |
261 | ||
262 | # Filesystem-specific mkfs options--set if not supplied | |
263 | export XFS_MKFS_OPTIONS="${XFS_MKFS_OPTIONS:--f -l su=65536}" | |
264 | export EXT4_MKFS_OPTIONS="${EXT4_MKFS_OPTIONS:--F}" | |
265 | export BTRFS_MKFS_OPTION # No defaults | |
266 | ||
267 | XFSTESTS_DIR="/var/lib/xfstests" # Where the tests live | |
268 | ||
269 | # download, build, and install xfstests | |
270 | function install_xfstests() { | |
271 | arg_count 0 $# | |
272 | ||
273 | local multiple="" | |
274 | local ncpu | |
275 | ||
276 | pushd "${TESTDIR}" | |
277 | ||
278 | git clone "${XFSTESTS_REPO}" | |
279 | ||
280 | cd xfstests | |
281 | ||
282 | # FIXME: use an older version before the tests were rearranged! | |
283 | git reset --hard e5f1a13792f20cfac097fef98007610b422f2cac | |
284 | ||
285 | ncpu=$(getconf _NPROCESSORS_ONLN 2>&1) | |
286 | [ -n "${ncpu}" -a "${ncpu}" -gt 1 ] && multiple="-j ${ncpu}" | |
287 | ||
288 | make realclean | |
289 | make ${multiple} | |
290 | make -k install | |
291 | ||
292 | popd | |
293 | } | |
294 | ||
295 | # remove previously-installed xfstests files | |
296 | function remove_xfstests() { | |
297 | arg_count 0 $# | |
298 | ||
299 | rm -rf "${TESTDIR}/xfstests" | |
300 | rm -rf "${XFSTESTS_DIR}" | |
301 | } | |
302 | ||
303 | # create a host options file that uses the specified devices | |
304 | function setup_host_options() { | |
305 | arg_count 0 $# | |
306 | ||
307 | # Create mount points for the test and scratch filesystems | |
308 | local test_dir="$(mktemp -d ${TESTDIR}/test_dir.XXXXXXXXXX)" | |
309 | local scratch_dir="$(mktemp -d ${TESTDIR}/scratch_mnt.XXXXXXXXXX)" | |
310 | ||
311 | # Write a host options file that uses these devices. | |
312 | # xfstests uses the file defined by HOST_OPTIONS as the | |
313 | # place to get configuration variables for its run, and | |
314 | # all (or most) of the variables set here are required. | |
315 | export HOST_OPTIONS="$(mktemp ${TESTDIR}/host_options.XXXXXXXXXX)" | |
316 | cat > "${HOST_OPTIONS}" <<-! | |
317 | # Created by ${PROGNAME} on $(date) | |
318 | # HOST_OPTIONS="${HOST_OPTIONS}" | |
319 | TEST_DEV="${TEST_DEV}" | |
320 | SCRATCH_DEV="${SCRATCH_DEV}" | |
321 | TEST_DIR="${test_dir}" | |
322 | SCRATCH_MNT="${scratch_dir}" | |
323 | FSTYP="${FS_TYPE}" | |
324 | export TEST_DEV SCRATCH_DEV TEST_DIR SCRATCH_MNT FSTYP | |
325 | # | |
326 | export XFS_MKFS_OPTIONS="${XFS_MKFS_OPTIONS}" | |
327 | ! | |
328 | ||
329 | # Now ensure we are using the same values | |
330 | . "${HOST_OPTIONS}" | |
331 | } | |
332 | ||
333 | # remove the host options file, plus the directories it refers to | |
334 | function cleanup_host_options() { | |
335 | arg_count 0 $# | |
336 | ||
337 | rm -rf "${TEST_DIR}" "${SCRATCH_MNT}" | |
338 | rm -f "${HOST_OPTIONS}" | |
339 | } | |
340 | ||
341 | # run mkfs on the given device using the specified filesystem type | |
342 | function do_mkfs() { | |
343 | arg_count 1 $# | |
344 | ||
345 | local dev="${1}" | |
346 | local options | |
347 | ||
348 | case "${FSTYP}" in | |
349 | xfs) options="${XFS_MKFS_OPTIONS}" ;; | |
350 | ext4) options="${EXT4_MKFS_OPTIONS}" ;; | |
351 | btrfs) options="${BTRFS_MKFS_OPTIONS}" ;; | |
352 | esac | |
353 | ||
354 | "mkfs.${FSTYP}" ${options} "${dev}" || | |
355 | err "unable to make ${FSTYP} file system on device \"${dev}\"" | |
356 | } | |
357 | ||
358 | # mount the given device on the given mount point | |
359 | function do_mount() { | |
360 | arg_count 2 $# | |
361 | ||
362 | local dev="${1}" | |
363 | local dir="${2}" | |
364 | ||
365 | mount "${dev}" "${dir}" || | |
366 | err "unable to mount file system \"${dev}\" on \"${dir}\"" | |
367 | } | |
368 | ||
369 | # unmount a previously-mounted device | |
370 | function do_umount() { | |
371 | arg_count 1 $# | |
372 | ||
373 | local dev="${1}" | |
374 | ||
375 | if mount | grep "${dev}" > /dev/null; then | |
376 | if ! umount "${dev}"; then | |
377 | err "unable to unmount device \"${dev}\"" | |
378 | fi | |
379 | else | |
380 | # Report it but don't error out | |
381 | echo "device \"${dev}\" was not mounted" >&2 | |
382 | fi | |
383 | } | |
384 | ||
385 | # do basic xfstests setup--make and mount the test and scratch filesystems | |
386 | function setup_xfstests() { | |
387 | arg_count 0 $# | |
388 | ||
389 | # TEST_DEV can persist across test runs, but for now we | |
390 | # don't bother. I believe xfstests prefers its devices to | |
391 | # have been already been formatted for the desired | |
392 | # filesystem type--it uses blkid to identify things or | |
393 | # something. So we mkfs both here for a fresh start. | |
394 | do_mkfs "${TEST_DEV}" | |
395 | do_mkfs "${SCRATCH_DEV}" | |
396 | ||
397 | # I believe the test device is expected to be mounted; the | |
398 | # scratch doesn't need to be (but it doesn't hurt). | |
399 | do_mount "${TEST_DEV}" "${TEST_DIR}" | |
400 | do_mount "${SCRATCH_DEV}" "${SCRATCH_MNT}" | |
401 | } | |
402 | ||
403 | # clean up changes made by setup_xfstests | |
404 | function cleanup_xfstests() { | |
405 | arg_count 0 $# | |
406 | ||
407 | # Unmount these in case a test left them mounted (plus | |
408 | # the corresponding setup function mounted them...) | |
409 | do_umount "${TEST_DEV}" | |
410 | do_umount "${SCRATCH_DEV}" | |
411 | } | |
412 | ||
413 | # top-level setup routine | |
414 | function setup() { | |
415 | arg_count 0 $# | |
416 | ||
417 | setup_host_options | |
418 | install_xfstests | |
419 | setup_xfstests | |
420 | } | |
421 | ||
422 | # top-level (final) cleanup routine | |
423 | function cleanup() { | |
424 | arg_count 0 $# | |
425 | ||
426 | cd / | |
427 | cleanup_xfstests | |
428 | remove_xfstests | |
429 | cleanup_host_options | |
430 | } | |
431 | trap cleanup EXIT ERR HUP INT QUIT | |
432 | ||
433 | # ################################################################ | |
434 | ||
435 | start_date="$(date)" | |
436 | ||
437 | parseargs "$@" | |
438 | ||
439 | setup | |
440 | ||
441 | pushd "${XFSTESTS_DIR}" | |
442 | for (( i = 1 ; i <= "${COUNT}" ; i++ )); do | |
443 | [ "${COUNT}" -gt 1 ] && echo "=== Iteration "$i" starting at: $(date)" | |
444 | ||
445 | ./check ${TESTS} # Here we actually run the tests | |
446 | status=$? | |
447 | ||
448 | [ "${COUNT}" -gt 1 ] && echo "=== Iteration "$i" complete at: $(date)" | |
449 | done | |
450 | popd | |
451 | ||
452 | # cleanup is called via the trap call, above | |
453 | ||
454 | echo "This xfstests run started at: ${start_date}" | |
455 | echo "xfstests run completed at: $(date)" | |
456 | [ "${COUNT}" -gt 1 ] && echo "xfstests run consisted of ${COUNT} iterations" | |
457 | ||
458 | exit "${status}" |