summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuca Boccassi <luca.boccassi@microsoft.com>2021-09-19 19:04:45 +0100
committerGitHub <noreply@github.com>2021-09-19 19:04:45 +0100
commit4ee41ed515b9d833182b8fc62112e9c0aef71313 (patch)
tree166aa84f996cfad0391dc6487f00119aad614a69
parent66e10d45d9021c82939f0c1ccef02711f714b79d (diff)
parent26a5ae8e95c0cb0262bf6f9b462bd74322f8ece3 (diff)
downloadsystemd-4ee41ed515b9d833182b8fc62112e9c0aef71313.tar.gz
Merge pull request #20780 from mrc0mmand/test-storage-btrfs
test: btrfs-related udev tests
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/test.sh162
-rw-r--r--test/test-functions8
-rw-r--r--test/units/testsuite-64.service2
-rwxr-xr-xtest/units/testsuite-64.sh140
4 files changed, 258 insertions, 54 deletions
diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh
index 89de1ea692..0c6e9dfa27 100755
--- a/test/TEST-64-UDEV-STORAGE/test.sh
+++ b/test/TEST-64-UDEV-STORAGE/test.sh
@@ -5,9 +5,7 @@
# * iSCSI
# * LVM over iSCSI (?)
# * SW raid (mdadm)
-# * LUKS -> MD (mdadm) -> LVM
-# * BTRFS
-# * MD BTRFS
+# * MD (mdadm) -> DM-CRYPT -> LVM
set -e
TEST_DESCRIPTION="systemd-udev storage tests"
@@ -26,28 +24,64 @@ if ! get_bool "$QEMU_KVM"; then
exit 0
fi
-test_append_files() {
- (
- instmods "=block" "=md" "=nvme" "=scsi"
- install_dmevent
- generate_module_dependencies
- image_install lsblk wc
+_host_has_feature() {(
+ set -e
+
+ case "${1:?}" in
+ multipath)
+ command -v multipath && command -v multipathd || return $?
+ ;;
+ lvm)
+ command -v lvm || return $?
+ ;;
+ btrfs)
+ modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $?
+ ;;
+ *)
+ echo >&2 "ERROR: Unknown feature '$1'"
+ # Make this a hard error to distinguish an invalid feature from
+ # a missing feature
+ exit 1
+ esac
+)}
+
+test_append_files() {(
+ local feature
+ # An associative array of requested (but optional) features and their
+ # respective "handlers" from test/test-functions
+ #
+ # Note: we install cryptsetup unconditionally, hence it's not explicitly
+ # checked for here
+ local -A features=(
+ [btrfs]=install_btrfs
+ [lvm]=install_lvm
+ [multipath]=install_multipath
+ )
- # Configure multipath
- if command -v multipath && command -v multipathd; then
- install_multipath
- fi
+ instmods "=block" "=md" "=nvme" "=scsi"
+ install_dmevent
+ image_install lsblk wc wipefs
- # Configure LVM
- if command -v lvm; then
- install_lvm
+ # Install the optional features if the host has the respective tooling
+ for feature in "${!features[@]}"; do
+ if _host_has_feature "$feature"; then
+ "${features[$feature]}"
fi
+ done
- for i in {0..127}; do
- dd if=/dev/zero of="${TESTDIR:?}/disk$i.img" bs=1M count=1
- echo "device$i" >"${TESTDIR:?}/disk$i.img"
- done
- )
+ generate_module_dependencies
+
+ for i in {0..127}; do
+ dd if=/dev/zero of="${TESTDIR:?}/disk$i.img" bs=1M count=1
+ echo "device$i" >"${TESTDIR:?}/disk$i.img"
+ done
+)}
+
+_image_cleanup() {
+ mount_initdir
+ # Clean up certain "problematic" files which may be left over by failing tests
+ : >"${initdir:?}/etc/fstab"
+ : >"${initdir:?}/etc/crypttab"
}
test_run_one() {
@@ -76,8 +110,19 @@ test_run() {
# Execute each currently defined function starting with "testcase_"
for testcase in "${TESTCASES[@]}"; do
+ _image_cleanup
echo "------ $testcase: BEGIN ------"
- { "$testcase" "$test_id"; ec=$?; } || :
+ # Note for my future frustrated self: `fun && xxx` (as wel as ||, if, while,
+ # until, etc.) _DISABLES_ the `set -e` behavior in _ALL_ nested function
+ # calls made from `fun()`, i.e. the function _CONTINUES_ even when a called
+ # command returned non-zero EC. That may unexpectedly hide failing commands
+ # if not handled properly. See: bash(1) man page, `set -e` section.
+ #
+ # So, be careful when adding clean up snippets in the testcase_*() functions -
+ # if the `test_run_one()` function isn't the last command, you have propagate
+ # the exit code correctly (e.g. `test_run_one() || return $?`, see below).
+ ec=0
+ "$testcase" "$test_id" || ec=$?
case $ec in
0)
passed+=("$testcase")
@@ -163,11 +208,9 @@ testcase_virtio_scsi_identically_named_partitions() {
local diskpath="${TESTDIR:?}/namedpart0.img"
local lodev
- # Save some time (and storage life) during local testing
- if [[ ! -e "$diskpath" ]]; then
- dd if=/dev/zero of="$diskpath" bs=1M count=18
- lodev="$(losetup --show -f -P "$diskpath")"
- sfdisk "${lodev:?}" <<EOF
+ dd if=/dev/zero of="$diskpath" bs=1M count=18
+ lodev="$(losetup --show -f -P "$diskpath")"
+ sfdisk "${lodev:?}" <<EOF
label: gpt
name="Hello world", size=2M
@@ -179,8 +222,7 @@ name="Hello world", size=2M
name="Hello world", size=2M
name="Hello world", size=2M
EOF
- losetup -d "$lodev"
- fi
+ losetup -d "$lodev"
for i in {0..15}; do
diskpath="${TESTDIR:?}/namedpart$i.img"
@@ -197,11 +239,13 @@ EOF
KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
# Limit the number of VCPUs and set a timeout to make sure we trigger the issue
QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
- QEMU_SMP=1 QEMU_TIMEOUT=60 test_run_one "${1:?}"
+ QEMU_SMP=1 QEMU_TIMEOUT=60 test_run_one "${1:?}" || return $?
+
+ rm -f "${TESTDIR:?}"/namedpart*.img
}
testcase_multipath_basic_failover() {
- if ! command -v multipath || ! command -v multipathd; then
+ if ! _host_has_feature "multipath"; then
echo "Missing multipath tools, skipping the test..."
return 77
fi
@@ -210,19 +254,17 @@ testcase_multipath_basic_failover() {
local partdisk="${TESTDIR:?}/multipathpartitioned.img"
local image lodev nback ndisk wwn
- if [[ ! -e "$partdisk" ]]; then
- dd if=/dev/zero of="$partdisk" bs=1M count=16
- lodev="$(losetup --show -f -P "$partdisk")"
- sfdisk "${lodev:?}" <<EOF
+ dd if=/dev/zero of="$partdisk" bs=1M count=16
+ lodev="$(losetup --show -f -P "$partdisk")"
+ sfdisk "${lodev:?}" <<EOF
label: gpt
name="first_partition", size=5M
uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
EOF
- udevadm settle
- mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "${lodev}p2"
- losetup -d "$lodev"
- fi
+ udevadm settle
+ mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "${lodev}p2"
+ losetup -d "$lodev"
# Add 64 multipath devices, each backed by 4 paths
for ndisk in {0..63}; do
@@ -240,7 +282,9 @@ EOF
KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
- test_run_one "${1:?}"
+ test_run_one "${1:?}" || return $?
+
+ rm -f "$partdisk"
}
# Test case for issue https://github.com/systemd/systemd/issues/19946
@@ -256,11 +300,13 @@ testcase_simultaneous_events() {
KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
- test_run_one "${1:?}"
+ test_run_one "${1:?}" || return $?
+
+ rm -f "$partdisk"
}
testcase_lvm_basic() {
- if ! command -v lvm; then
+ if ! _host_has_feature "lvm"; then
echo "Missing lvm tools, skipping the test..."
return 77
fi
@@ -281,7 +327,37 @@ testcase_lvm_basic() {
KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
- test_run_one "${1:?}"
+ test_run_one "${1:?}" || return $?
+
+ rm -f "${TESTDIR:?}"/lvmbasic*.img
+}
+
+testcase_btrfs_basic() {
+ if ! _host_has_feature "btrfs"; then
+ echo "Missing btrfs tools/modules, skipping the test..."
+ return 77
+ fi
+
+ local qemu_opts=("-device ahci,id=ahci0")
+ local diskpath i size
+
+ for i in {0..3}; do
+ diskpath="${TESTDIR:?}/btrfsbasic${i}.img"
+ # Make the first disk larger for multi-partition tests
+ [[ $i -eq 0 ]] && size=350 || size=128
+
+ dd if=/dev/zero of="$diskpath" bs=1M count="$size"
+ qemu_opts+=(
+ "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefbtrfs$i"
+ "-drive format=raw,cache=unsafe,file=$diskpath,if=none,id=drive$i"
+ )
+ done
+
+ KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
+ QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
+ test_run_one "${1:?}" || return $?
+
+ rm -f "${TESTDIR:?}"/btrfsbasic*.img
}
# Allow overriding which tests should be run from the "outside", useful for manual
diff --git a/test/test-functions b/test/test-functions
index 8ed0dbfe06..9eb37fce58 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -950,6 +950,14 @@ install_lvm() {
mkdir -p "${initdir:?}/etc/lvm"
}
+install_btrfs() {
+ instmods btrfs
+ # Not all utilities provided by btrfs-progs are listed here; extend the list
+ # if necessary
+ image_install btrfs btrfstune mkfs.btrfs
+ inst_rules 64-btrfs-dm.rules
+}
+
install_compiled_systemd() {
dinfo "Install compiled systemd"
diff --git a/test/units/testsuite-64.service b/test/units/testsuite-64.service
index 4c8f701e73..0f8cfa3770 100644
--- a/test/units/testsuite-64.service
+++ b/test/units/testsuite-64.service
@@ -5,3 +5,5 @@ Description=TEST-64-UDEV
ExecStartPre=rm -f /failed /testok
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
Type=oneshot
+StandardOutput=journal+console
+StandardError=journal+console
diff --git a/test/units/testsuite-64.sh b/test/units/testsuite-64.sh
index 1574d51401..4380fa0fcf 100755
--- a/test/units/testsuite-64.sh
+++ b/test/units/testsuite-64.sh
@@ -6,14 +6,12 @@ set -o pipefail
# Check if all symlinks under /dev/disk/ are valid
# shellcheck disable=SC2120
-helper_check_device_symlinks() {
- # Disable verbose logging only for this function (and reset the signal handler
- # when leaving the function)
- set +x; trap "trap - RETURN; set -x" RETURN
+helper_check_device_symlinks() {(
+ set +x
local dev link path paths target
- [[ $# -gt 0 ]] && paths=("$@") || paths=("/dev/disk")
+ [[ $# -gt 0 ]] && paths=("$@") || paths=("/dev/disk" "/dev/mapper")
# Check if all given paths are valid
for path in "${paths[@]}"; do
@@ -40,7 +38,7 @@ helper_check_device_symlinks() {
return 1
fi
done < <(find "${paths[@]}" -type l)
-}
+)}
testcase_megasas2_basic() {
lsblk -S
@@ -237,11 +235,11 @@ testcase_lvm_basic() {
helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
# Same as above, but now with more "stress"
- for i in {1..100}; do
+ for i in {1..50}; do
lvm vgchange -an "$vgroup"
lvm vgchange -ay "$vgroup"
- if ((i % 10 == 0)); then
+ if ((i % 5 == 0)); then
udevadm settle
test -e "/dev/$vgroup/mypart1"
test -e "/dev/$vgroup/mypart2"
@@ -258,7 +256,7 @@ testcase_lvm_basic() {
helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
# Create & remove LVs in a loop, i.e. with more "stress"
- for i in {1..50}; do
+ for i in {1..16}; do
# 1) Create 16 logical volumes
for part in {0..15}; do
lvm lvcreate -y -L 4M "$vgroup" -n "looppart$part"
@@ -267,9 +265,9 @@ testcase_lvm_basic() {
# 2) Immediately remove them
lvm lvremove -y "$vgroup"/looppart{0..15}
- # 3) On every 10th iteration settle udev and check if all partitions are
+ # 3) On every 4th iteration settle udev and check if all partitions are
# indeed gone, and if all symlinks are still valid
- if ((i % 10 == 0)); then
+ if ((i % 4 == 0)); then
udevadm settle
for part in {0..15}; do
test ! -e "/dev/$vgroup/looppart$part"
@@ -279,6 +277,126 @@ testcase_lvm_basic() {
done
}
+testcase_btrfs_basic() {
+ local dev_stub i label mpoint uuid
+ local devices=(
+ /dev/disk/by-id/ata-foobar_deadbeefbtrfs{0..3}
+ )
+
+ ls -l "${devices[@]}"
+
+ echo "Single device: default settings"
+ uuid="deadbeef-dead-dead-beef-000000000000"
+ label="btrfs_root"
+ mkfs.btrfs -L "$label" -U "$uuid" "${devices[0]}"
+ udevadm settle
+ btrfs filesystem show
+ test -e "/dev/disk/by-uuid/$uuid"
+ test -e "/dev/disk/by-label/$label"
+ helper_check_device_symlinks
+
+ echo "Multiple devices: using partitions, data: single, metadata: raid1"
+ uuid="deadbeef-dead-dead-beef-000000000001"
+ label="btrfs_mpart"
+ sfdisk --wipe=always "${devices[0]}" <<EOF
+label: gpt
+
+name="diskpart1", size=85M
+name="diskpart2", size=85M
+name="diskpart3", size=85M
+name="diskpart4", size=85M
+EOF
+ udevadm settle
+ mkfs.btrfs -d single -m raid1 -L "$label" -U "$uuid" /dev/disk/by-partlabel/diskpart{1..4}
+ udevadm settle
+ btrfs filesystem show
+ test -e "/dev/disk/by-uuid/$uuid"
+ test -e "/dev/disk/by-label/$label"
+ helper_check_device_symlinks
+ wipefs -a -f "${devices[0]}"
+
+ echo "Multiple devices: using disks, data: raid10, metadata: raid10, mixed mode"
+ uuid="deadbeef-dead-dead-beef-000000000002"
+ label="btrfs_mdisk"
+ mkfs.btrfs -M -d raid10 -m raid10 -L "$label" -U "$uuid" "${devices[@]}"
+ udevadm settle
+ btrfs filesystem show
+ test -e "/dev/disk/by-uuid/$uuid"
+ test -e "/dev/disk/by-label/$label"
+ helper_check_device_symlinks
+
+ echo "Multiple devices: using LUKS encrypted disks, data: raid1, metadata: raid1, mixed mode"
+ uuid="deadbeef-dead-dead-beef-000000000003"
+ label="btrfs_mencdisk"
+ mpoint="/btrfs_enc$RANDOM"
+ mkdir "$mpoint"
+ # Create a key-file
+ dd if=/dev/urandom of=/etc/btrfs_keyfile bs=64 count=1 iflag=fullblock
+ chmod 0600 /etc/btrfs_keyfile
+ # Encrypt each device and add it to /etc/crypttab, so it can be mounted
+ # automagically later
+ : >/etc/crypttab
+ for ((i = 0; i < ${#devices[@]}; i++)); do
+ # Intentionally use weaker cipher-related settings, since we don't care
+ # about security here as it's a throwaway LUKS partition
+ cryptsetup luksFormat -q \
+ --use-urandom --pbkdf pbkdf2 --pbkdf-force-iterations 1000 \
+ --uuid "deadbeef-dead-dead-beef-11111111111$i" --label "encdisk$i" "${devices[$i]}" /etc/btrfs_keyfile
+ udevadm settle
+ test -e "/dev/disk/by-uuid/deadbeef-dead-dead-beef-11111111111$i"
+ test -e "/dev/disk/by-label/encdisk$i"
+ # Add the device into /etc/crypttab, reload systemd, and then activate
+ # the device so we can create a filesystem on it later
+ echo "encbtrfs$i UUID=deadbeef-dead-dead-beef-11111111111$i /etc/btrfs_keyfile luks,noearly" >>/etc/crypttab
+ systemctl daemon-reload
+ systemctl start "systemd-cryptsetup@encbtrfs$i"
+ done
+ helper_check_device_symlinks
+ # Check if we have all necessary DM devices
+ ls -l /dev/mapper/encbtrfs{0..3}
+ # Create a multi-device btrfs filesystem on the LUKS devices
+ mkfs.btrfs -M -d raid1 -m raid1 -L "$label" -U "$uuid" /dev/mapper/encbtrfs{0..3}
+ udevadm settle
+ btrfs filesystem show
+ test -e "/dev/disk/by-uuid/$uuid"
+ test -e "/dev/disk/by-label/$label"
+ helper_check_device_symlinks
+ # Mount it and write some data to it we can compare later
+ mount -t btrfs /dev/mapper/encbtrfs0 "$mpoint"
+ echo "hello there" >"$mpoint/test"
+ # "Deconstruct" the btrfs device and check if we're in a sane state (symlink-wise)
+ umount "$mpoint"
+ systemctl stop systemd-cryptsetup@encbtrfs{0..3}
+ test ! -e "/dev/disk/by-uuid/$uuid"
+ helper_check_device_symlinks
+ # Add the mount point to /etc/fstab and check if the device can be put together
+ # automagically. The source device is the DM name of the first LUKS device
+ # (from /etc/crypttab). We have to specify all LUKS devices manually, as
+ # registering the necessary devices is usually initrd's job (via btrfs device scan)
+ dev_stub="/dev/mapper/encbtrfs"
+ echo "/dev/mapper/encbtrfs0 $mpoint btrfs device=${dev_stub}0,device=${dev_stub}1,device=${dev_stub}2,device=${dev_stub}3 0 2" >>/etc/fstab
+ # Tell systemd about the new mount
+ systemctl daemon-reload
+ # Restart cryptsetup.target to trigger autounlock of partitions in /etc/crypttab
+ systemctl restart cryptsetup.target
+ # Start the corresponding mount unit and check if the btrfs device was reconstructed
+ # correctly
+ systemctl start "${mpoint##*/}.mount"
+ btrfs filesystem show
+ test -e "/dev/disk/by-uuid/$uuid"
+ test -e "/dev/disk/by-label/$label"
+ helper_check_device_symlinks
+ grep "hello there" "$mpoint/test"
+ # Cleanup
+ systemctl stop "${mpoint##*/}.mount"
+ systemctl stop systemd-cryptsetup@encbtrfs{0..3}
+ sed -i "/${mpoint##*/}/d" /etc/fstab
+ : >/etc/crypttab
+ rm -fr "$mpoint"
+ systemctl daemon-reload
+ udevadm settle
+}
+
: >/failed
udevadm settle