2 # vi: ts=4 sw=4 tw=0 et:
6 # * MD (mdadm) -> dm-crypt -> LVM
7 # * iSCSI -> dm-crypt -> LVM
10 TEST_DESCRIPTION
="systemd-udev storage tests"
13 # Save only journals of failing test cases by default (to conserve space)
14 TEST_SAVE_JOURNAL
="${TEST_SAVE_JOURNAL:-fail}"
15 QEMU_TIMEOUT
="${QEMU_TIMEOUT:-600}"
17 # shellcheck source=test/test-functions
18 .
"${TEST_BASE_DIR:?}/test-functions"
20 USER_QEMU_OPTIONS
="${QEMU_OPTIONS:-}"
21 USER_KERNEL_APPEND
="${KERNEL_APPEND:-}"
23 if ! get_bool
"$QEMU_KVM"; then
24 echo "This test requires KVM, skipping..."
28 _host_has_feature
() {(
33 modprobe
-nv btrfs
&& command -v mkfs.btrfs
&& command -v btrfs ||
return $?
36 # Client/initiator (Open-iSCSI)
37 command -v iscsiadm
&& command -v iscsid ||
return $?
39 command -v tgtadm
&& command -v tgtd ||
return $?
42 command -v lvm ||
return $?
45 command -v multipath
&& command -v multipathd ||
return $?
48 echo >&2 "ERROR: Unknown feature '$1'"
49 # Make this a hard error to distinguish an invalid feature from
55 test_append_files
() {(
57 # An associative array of requested (but optional) features and their
58 # respective "handlers" from test/test-functions
60 # Note: we install cryptsetup unconditionally, hence it's not explicitly
66 [multipath
]=install_multipath
69 instmods
"=block" "=md" "=nvme" "=scsi"
71 image_install lsblk swapoff swapon
wc wipefs
73 # Install the optional features if the host has the respective tooling
74 for feature
in "${!features[@]}"; do
75 if _host_has_feature
"$feature"; then
76 "${features[$feature]}"
80 generate_module_dependencies
83 dd if=/dev
/zero of
="${TESTDIR:?}/disk$i.img" bs
=1M count
=1
84 echo "device$i" >"${TESTDIR:?}/disk$i.img"
90 # Clean up certain "problematic" files which may be left over by failing tests
91 : >"${initdir:?}/etc/fstab"
92 : >"${initdir:?}/etc/crypttab"
96 local test_id
="${1:?}"
98 if run_qemu
"$test_id"; then
99 check_result_qemu ||
{ echo "QEMU test failed"; return 1; }
106 local test_id
="${1:?}"
114 if get_bool
"${TEST_NO_QEMU:=}" ||
! find_qemu_bin
; then
115 dwarn
"can't run QEMU, skipping"
119 # Execute each currently defined function starting with "testcase_"
120 for testcase
in "${TESTCASES[@]}"; do
122 echo "------ $testcase: BEGIN ------"
123 # Note for my future frustrated self: `fun && xxx` (as wel as ||, if, while,
124 # until, etc.) _DISABLES_ the `set -e` behavior in _ALL_ nested function
125 # calls made from `fun()`, i.e. the function _CONTINUES_ even when a called
126 # command returned non-zero EC. That may unexpectedly hide failing commands
127 # if not handled properly. See: bash(1) man page, `set -e` section.
129 # So, be careful when adding clean up snippets in the testcase_*() functions -
130 # if the `test_run_one()` function isn't the last command, you have propagate
131 # the exit code correctly (e.g. `test_run_one() || return $?`, see below).
133 "$testcase" "$test_id" || ec
=$?
136 passed
+=("$testcase")
140 skipped
+=("$testcase")
144 failed
+=("$testcase")
147 echo "------ $testcase: END ($state) ------"
150 echo "Passed tests: ${#passed[@]}"
151 printf " * %s\n" "${passed[@]}"
152 echo "Skipped tests: ${#skipped[@]}"
153 printf " * %s\n" "${skipped[@]}"
154 echo "Failed tests: ${#failed[@]}"
155 printf " * %s\n" "${failed[@]}"
157 [[ ${#failed[@]} -eq 0 ]] ||
return 1
162 testcase_megasas2_basic
() {
163 if ! "${QEMU_BIN:?}" -device help |
grep 'name "megasas-gen2"'; then
164 echo "megasas-gen2 device driver is not available, skipping test..."
169 "-device megasas-gen2,id=scsi0"
170 "-device megasas-gen2,id=scsi1"
171 "-device megasas-gen2,id=scsi2"
172 "-device megasas-gen2,id=scsi3"
175 for i
in {0.
.127}; do
176 # Add 128 drives, 32 per bus
178 "-device scsi-hd,drive=drive$i,bus=scsi$((i / 32)).0,channel=0,scsi-id=$((i % 32)),lun=0"
179 "-drive format=raw,cache=unsafe,file=${TESTDIR:?}/disk$i.img,if=none,id=drive$i"
183 KERNEL_APPEND
="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
184 QEMU_OPTIONS
="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
185 test_run_one
"${1:?}"
188 testcase_nvme_basic
() {
189 if ! "${QEMU_BIN:?}" -device help |
grep 'name "nvme"'; then
190 echo "nvme device driver is not available, skipping test..."
196 "-device nvme,drive=nvme$i,serial=deadbeef$i,num_queues=8"
197 "-drive format=raw,cache=unsafe,file=${TESTDIR:?}/disk$i.img,if=none,id=nvme$i"
201 KERNEL_APPEND
="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
202 QEMU_OPTIONS
="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
203 test_run_one
"${1:?}"
206 # Test for issue https://github.com/systemd/systemd/issues/20212
207 testcase_virtio_scsi_identically_named_partitions
() {
208 if ! "${QEMU_BIN:?}" -device help |
grep 'name "virtio-scsi-pci"'; then
209 echo "virtio-scsi-pci device driver is not available, skipping test..."
213 # Create 16 disks, with 8 partitions per disk (all identically named)
214 # and attach them to a virtio-scsi controller
215 local qemu_opts
=("-device virtio-scsi-pci,id=scsi0,num_queues=4")
216 local diskpath
="${TESTDIR:?}/namedpart0.img"
219 dd if=/dev
/zero of
="$diskpath" bs
=1M count
=18
220 lodev
="$(losetup --show -f -P "$diskpath")"
221 sfdisk
"${lodev:?}" <<EOF
224 name="Hello world", size=2M
225 name="Hello world", size=2M
226 name="Hello world", size=2M
227 name="Hello world", size=2M
228 name="Hello world", size=2M
229 name="Hello world", size=2M
230 name="Hello world", size=2M
231 name="Hello world", size=2M
236 diskpath
="${TESTDIR:?}/namedpart$i.img"
237 if [[ $i -gt 0 ]]; then
238 cp -uv "${TESTDIR:?}/namedpart0.img" "$diskpath"
242 "-device scsi-hd,drive=drive$i,bus=scsi0.0,channel=0,scsi-id=0,lun=$i"
243 "-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i"
247 KERNEL_APPEND
="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
248 # Limit the number of VCPUs and set a timeout to make sure we trigger the issue
249 QEMU_OPTIONS
="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
250 QEMU_SMP
=1 QEMU_TIMEOUT
=60 test_run_one
"${1:?}" ||
return $?
252 rm -f "${TESTDIR:?}"/namedpart
*.img
255 testcase_multipath_basic_failover
() {
256 if ! _host_has_feature
"multipath"; then
257 echo "Missing multipath tools, skipping the test..."
261 local qemu_opts
=("-device virtio-scsi-pci,id=scsi")
262 local partdisk
="${TESTDIR:?}/multipathpartitioned.img"
263 local image lodev nback ndisk wwn
265 dd if=/dev
/zero of
="$partdisk" bs
=1M count
=16
266 lodev
="$(losetup --show -f -P "$partdisk")"
267 sfdisk
"${lodev:?}" <<EOF
270 name="first_partition", size=5M
271 uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
274 mkfs.ext4
-U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "${lodev}p2"
277 # Add 64 multipath devices, each backed by 4 paths
278 for ndisk
in {0.
.63}; do
279 wwn
="0xDEADDEADBEEF$(printf "%.4d
" "$ndisk")"
280 # Use a partitioned disk for the first device to test failover
281 [[ $ndisk -eq 0 ]] && image
="$partdisk" || image
="${TESTDIR:?}/disk$ndisk.img"
283 for nback
in {0.
.3}; do
285 "-device scsi-hd,drive=drive${ndisk}x${nback},serial=MPIO$ndisk,wwn=$wwn"
286 "-drive format=raw,cache=unsafe,file=$image,file.locking=off,if=none,id=drive${ndisk}x${nback}"
291 KERNEL_APPEND
="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
292 QEMU_OPTIONS
="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
293 test_run_one
"${1:?}" ||
return $?
298 # Test case for issue https://github.com/systemd/systemd/issues/19946
299 testcase_simultaneous_events
() {
300 local qemu_opts
=("-device virtio-scsi-pci,id=scsi")
301 local partdisk
="${TESTDIR:?}/simultaneousevents.img"
303 dd if=/dev
/zero of
="$partdisk" bs
=1M count
=110
305 "-device scsi-hd,drive=drive1,serial=deadbeeftest"
306 "-drive format=raw,cache=unsafe,file=$partdisk,if=none,id=drive1"
309 KERNEL_APPEND
="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
310 QEMU_OPTIONS
="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
311 test_run_one
"${1:?}" ||
return $?
316 testcase_lvm_basic
() {
317 if ! _host_has_feature
"lvm"; then
318 echo "Missing lvm tools, skipping the test..."
322 local qemu_opts
=("-device ahci,id=ahci0")
325 # Attach 4 SATA disks to the VM (and set their model and serial fields
326 # to something predictable, so we can refer to them later)
328 diskpath
="${TESTDIR:?}/lvmbasic${i}.img"
329 dd if=/dev
/zero of
="$diskpath" bs
=1M count
=32
331 "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeeflvm$i"
332 "-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i"
336 KERNEL_APPEND
="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
337 QEMU_OPTIONS
="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
338 test_run_one
"${1:?}" ||
return $?
340 rm -f "${TESTDIR:?}"/lvmbasic
*.img
343 testcase_btrfs_basic
() {
344 if ! _host_has_feature
"btrfs"; then
345 echo "Missing btrfs tools/modules, skipping the test..."
349 local qemu_opts
=("-device ahci,id=ahci0")
350 local diskpath i size
353 diskpath
="${TESTDIR:?}/btrfsbasic${i}.img"
354 # Make the first disk larger for multi-partition tests
355 [[ $i -eq 0 ]] && size
=350 || size
=128
357 dd if=/dev
/zero of
="$diskpath" bs
=1M count
="$size"
359 "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefbtrfs$i"
360 "-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i"
364 KERNEL_APPEND
="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
365 QEMU_OPTIONS
="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
366 test_run_one
"${1:?}" ||
return $?
368 rm -f "${TESTDIR:?}"/btrfsbasic
*.img
371 testcase_iscsi_lvm
() {
372 if ! _host_has_feature
"iscsi" ||
! _host_has_feature
"lvm"; then
373 echo "Missing iSCSI client/server tools (Open-iSCSI/TGT) or LVM utilities, skipping the test..."
377 local qemu_opts
=("-device ahci,id=ahci0")
378 local diskpath i size
381 diskpath
="${TESTDIR:?}/iscsibasic${i}.img"
382 # Make the first disk larger for multi-partition tests
383 [[ $i -eq 0 ]] && size
=150 || size
=64
384 # Make the first disk larger for multi-partition tests
386 dd if=/dev
/zero of
="$diskpath" bs
=1M count
="$size"
388 "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefiscsi$i"
389 "-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i"
393 KERNEL_APPEND
="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
394 QEMU_OPTIONS
="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
395 test_run_one
"${1:?}" ||
return $?
397 rm -f "${TESTDIR:?}"/iscsibasic
*.img
400 testcase_long_sysfs_path
() {
402 local testdisk
="${TESTDIR:?}/longsysfspath.img"
404 "-drive if=none,id=drive0,format=raw,cache=unsafe,file=$testdisk"
405 "-device pci-bridge,id=pci_bridge0,bus=pci.0,chassis_nr=64"
408 dd if=/dev
/zero of
="$testdisk" bs
=1M count
=64
409 lodev
="$(losetup --show -f -P "$testdisk")"
410 sfdisk
"${lodev:?}" <<EOF
413 name="test_swap", size=32M
414 uuid="deadbeef-dead-dead-beef-000000000000", name="test_part", size=5M
417 mkswap
-U "deadbeef-dead-dead-beef-111111111111" -L "swap_vol" "${lodev}p1"
418 mkfs.ext4
-U "deadbeef-dead-dead-beef-222222222222" -L "data_vol" "${lodev}p2"
421 # Create 25 additional PCI bridges, each one connected to the previous one
422 # (basically a really long extension cable), and attach a virtio drive to
423 # the last one. This should force udev into attempting to create a device
424 # unit with a _really_ long name.
425 for brid
in {1.
.25}; do
426 qemu_opts
+=("-device pci-bridge,id=pci_bridge$brid,bus=pci_bridge$((brid-1)),chassis_nr=$((64+brid))")
429 qemu_opts
+=("-device virtio-blk-pci,drive=drive0,scsi=off,bus=pci_bridge$brid")
431 KERNEL_APPEND
="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
432 QEMU_OPTIONS
="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
433 test_run_one
"${1:?}" ||
return $?
435 rm -f "${testdisk:?}"
438 # Allow overriding which tests should be run from the "outside", useful for manual
439 # testing (make -C test/... TESTCASES="testcase1 testcase2")
440 if [[ -v "TESTCASES" && -n "$TESTCASES" ]]; then
441 read -ra TESTCASES
<<< "$TESTCASES"
443 # This must run after all functions were defined, otherwise `declare -F` won't
445 mapfile
-t TESTCASES
< <(declare -F |
awk '$3 ~ /^testcase_/ {print $3;}')