diff options
author | Luca Boccassi <luca.boccassi@microsoft.com> | 2021-09-19 19:04:45 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-09-19 19:04:45 +0100 |
commit | 4ee41ed515b9d833182b8fc62112e9c0aef71313 (patch) | |
tree | 166aa84f996cfad0391dc6487f00119aad614a69 | |
parent | 66e10d45d9021c82939f0c1ccef02711f714b79d (diff) | |
parent | 26a5ae8e95c0cb0262bf6f9b462bd74322f8ece3 (diff) | |
download | systemd-4ee41ed515b9d833182b8fc62112e9c0aef71313.tar.gz |
Merge pull request #20780 from mrc0mmand/test-storage-btrfs
test: btrfs-related udev tests
-rwxr-xr-x | test/TEST-64-UDEV-STORAGE/test.sh | 162 | ||||
-rw-r--r-- | test/test-functions | 8 | ||||
-rw-r--r-- | test/units/testsuite-64.service | 2 | ||||
-rwxr-xr-x | test/units/testsuite-64.sh | 140 |
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 |