1
0
mirror of https://github.com/systemd/systemd.git synced 2024-11-01 09:21:26 +03:00

Merge pull request #20780 from mrc0mmand/test-storage-btrfs

test: btrfs-related udev tests
This commit is contained in:
Luca Boccassi 2021-09-19 19:04:45 +01:00 committed by GitHub
commit 4ee41ed515
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 259 additions and 55 deletions

View File

@ -5,9 +5,7 @@
# * iSCSI # * iSCSI
# * LVM over iSCSI (?) # * LVM over iSCSI (?)
# * SW raid (mdadm) # * SW raid (mdadm)
# * LUKS -> MD (mdadm) -> LVM # * MD (mdadm) -> DM-CRYPT -> LVM
# * BTRFS
# * MD BTRFS
set -e set -e
TEST_DESCRIPTION="systemd-udev storage tests" TEST_DESCRIPTION="systemd-udev storage tests"
@ -26,28 +24,64 @@ if ! get_bool "$QEMU_KVM"; then
exit 0 exit 0
fi fi
test_append_files() { _host_has_feature() {(
( set -e
instmods "=block" "=md" "=nvme" "=scsi"
install_dmevent
generate_module_dependencies
image_install lsblk wc
# Configure multipath case "${1:?}" in
if command -v multipath && command -v multipathd; then multipath)
install_multipath command -v multipath && command -v multipathd || return $?
fi ;;
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
)}
# Configure LVM test_append_files() {(
if command -v lvm; then local feature
install_lvm # An associative array of requested (but optional) features and their
fi # respective "handlers" from test/test-functions
#
for i in {0..127}; do # Note: we install cryptsetup unconditionally, hence it's not explicitly
dd if=/dev/zero of="${TESTDIR:?}/disk$i.img" bs=1M count=1 # checked for here
echo "device$i" >"${TESTDIR:?}/disk$i.img" local -A features=(
done [btrfs]=install_btrfs
[lvm]=install_lvm
[multipath]=install_multipath
) )
instmods "=block" "=md" "=nvme" "=scsi"
install_dmevent
image_install lsblk wc wipefs
# 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
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() { test_run_one() {
@ -76,8 +110,19 @@ test_run() {
# Execute each currently defined function starting with "testcase_" # Execute each currently defined function starting with "testcase_"
for testcase in "${TESTCASES[@]}"; do for testcase in "${TESTCASES[@]}"; do
_image_cleanup
echo "------ $testcase: BEGIN ------" 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 case $ec in
0) 0)
passed+=("$testcase") passed+=("$testcase")
@ -163,11 +208,9 @@ testcase_virtio_scsi_identically_named_partitions() {
local diskpath="${TESTDIR:?}/namedpart0.img" local diskpath="${TESTDIR:?}/namedpart0.img"
local lodev local lodev
# Save some time (and storage life) during local testing dd if=/dev/zero of="$diskpath" bs=1M count=18
if [[ ! -e "$diskpath" ]]; then lodev="$(losetup --show -f -P "$diskpath")"
dd if=/dev/zero of="$diskpath" bs=1M count=18 sfdisk "${lodev:?}" <<EOF
lodev="$(losetup --show -f -P "$diskpath")"
sfdisk "${lodev:?}" <<EOF
label: gpt label: gpt
name="Hello world", size=2M name="Hello world", size=2M
@ -179,8 +222,7 @@ name="Hello world", size=2M
name="Hello world", size=2M name="Hello world", size=2M
name="Hello world", size=2M name="Hello world", size=2M
EOF EOF
losetup -d "$lodev" losetup -d "$lodev"
fi
for i in {0..15}; do for i in {0..15}; do
diskpath="${TESTDIR:?}/namedpart$i.img" diskpath="${TESTDIR:?}/namedpart$i.img"
@ -197,11 +239,13 @@ EOF
KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}" 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 # Limit the number of VCPUs and set a timeout to make sure we trigger the issue
QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}" 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() { 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..." echo "Missing multipath tools, skipping the test..."
return 77 return 77
fi fi
@ -210,19 +254,17 @@ testcase_multipath_basic_failover() {
local partdisk="${TESTDIR:?}/multipathpartitioned.img" local partdisk="${TESTDIR:?}/multipathpartitioned.img"
local image lodev nback ndisk wwn local image lodev nback ndisk wwn
if [[ ! -e "$partdisk" ]]; then dd if=/dev/zero of="$partdisk" bs=1M count=16
dd if=/dev/zero of="$partdisk" bs=1M count=16 lodev="$(losetup --show -f -P "$partdisk")"
lodev="$(losetup --show -f -P "$partdisk")" sfdisk "${lodev:?}" <<EOF
sfdisk "${lodev:?}" <<EOF
label: gpt label: gpt
name="first_partition", size=5M name="first_partition", size=5M
uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M uuid="deadbeef-dead-dead-beef-000000000000", name="failover_part", size=5M
EOF EOF
udevadm settle udevadm settle
mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "${lodev}p2" mkfs.ext4 -U "deadbeef-dead-dead-beef-111111111111" -L "failover_vol" "${lodev}p2"
losetup -d "$lodev" losetup -d "$lodev"
fi
# Add 64 multipath devices, each backed by 4 paths # Add 64 multipath devices, each backed by 4 paths
for ndisk in {0..63}; do for ndisk in {0..63}; do
@ -240,7 +282,9 @@ EOF
KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}" KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}" 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 # 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:-}" KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}" QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
test_run_one "${1:?}" test_run_one "${1:?}" || return $?
rm -f "$partdisk"
} }
testcase_lvm_basic() { testcase_lvm_basic() {
if ! command -v lvm; then if ! _host_has_feature "lvm"; then
echo "Missing lvm tools, skipping the test..." echo "Missing lvm tools, skipping the test..."
return 77 return 77
fi fi
@ -281,7 +327,37 @@ testcase_lvm_basic() {
KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}" KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}" 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 # Allow overriding which tests should be run from the "outside", useful for manual

View File

@ -950,6 +950,14 @@ install_lvm() {
mkdir -p "${initdir:?}/etc/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() { install_compiled_systemd() {
dinfo "Install compiled systemd" dinfo "Install compiled systemd"

View File

@ -5,3 +5,5 @@ Description=TEST-64-UDEV
ExecStartPre=rm -f /failed /testok ExecStartPre=rm -f /failed /testok
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
Type=oneshot Type=oneshot
StandardOutput=journal+console
StandardError=journal+console

View File

@ -6,14 +6,12 @@ set -o pipefail
# Check if all symlinks under /dev/disk/ are valid # Check if all symlinks under /dev/disk/ are valid
# shellcheck disable=SC2120 # shellcheck disable=SC2120
helper_check_device_symlinks() { helper_check_device_symlinks() {(
# Disable verbose logging only for this function (and reset the signal handler set +x
# when leaving the function)
set +x; trap "trap - RETURN; set -x" RETURN
local dev link path paths target 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 # Check if all given paths are valid
for path in "${paths[@]}"; do for path in "${paths[@]}"; do
@ -40,7 +38,7 @@ helper_check_device_symlinks() {
return 1 return 1
fi fi
done < <(find "${paths[@]}" -type l) done < <(find "${paths[@]}" -type l)
} )}
testcase_megasas2_basic() { testcase_megasas2_basic() {
lsblk -S lsblk -S
@ -237,11 +235,11 @@ testcase_lvm_basic() {
helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
# Same as above, but now with more "stress" # 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 -an "$vgroup"
lvm vgchange -ay "$vgroup" lvm vgchange -ay "$vgroup"
if ((i % 10 == 0)); then if ((i % 5 == 0)); then
udevadm settle udevadm settle
test -e "/dev/$vgroup/mypart1" test -e "/dev/$vgroup/mypart1"
test -e "/dev/$vgroup/mypart2" test -e "/dev/$vgroup/mypart2"
@ -258,7 +256,7 @@ testcase_lvm_basic() {
helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" helper_check_device_symlinks "/dev/disk" "/dev/$vgroup"
# Create & remove LVs in a loop, i.e. with more "stress" # 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 # 1) Create 16 logical volumes
for part in {0..15}; do for part in {0..15}; do
lvm lvcreate -y -L 4M "$vgroup" -n "looppart$part" lvm lvcreate -y -L 4M "$vgroup" -n "looppart$part"
@ -267,9 +265,9 @@ testcase_lvm_basic() {
# 2) Immediately remove them # 2) Immediately remove them
lvm lvremove -y "$vgroup"/looppart{0..15} 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 # indeed gone, and if all symlinks are still valid
if ((i % 10 == 0)); then if ((i % 4 == 0)); then
udevadm settle udevadm settle
for part in {0..15}; do for part in {0..15}; do
test ! -e "/dev/$vgroup/looppart$part" test ! -e "/dev/$vgroup/looppart$part"
@ -279,6 +277,126 @@ testcase_lvm_basic() {
done 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 : >/failed
udevadm settle udevadm settle