summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorFrantisek Sumsal <frantisek@sumsal.cz>2021-09-08 18:26:02 +0200
committerFrantisek Sumsal <frantisek@sumsal.cz>2021-09-12 18:38:42 +0200
commitd0cbad16c554043aa19ece6d94707fbffa7eb234 (patch)
tree106fffe5a3ffe7e15d5ffe109b11bc888e20dc03 /test
parent9e7c3bd48c7ee3b3ba62837cf305f2f1c4346932 (diff)
downloadsystemd-d0cbad16c554043aa19ece6d94707fbffa7eb234.tar.gz
test: add a basic multipath test + failover
Diffstat (limited to 'test')
-rwxr-xr-xtest/TEST-64-UDEV-STORAGE/test.sh52
-rwxr-xr-xtest/units/testsuite-64.sh99
2 files changed, 149 insertions, 2 deletions
diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh
index 8b2591fbac..accbe09255 100755
--- a/test/TEST-64-UDEV-STORAGE/test.sh
+++ b/test/TEST-64-UDEV-STORAGE/test.sh
@@ -23,11 +23,16 @@ test_append_files() {
instmods "=block" "=md" "=nvme" "=scsi"
install_dmevent
generate_module_dependencies
- inst_binary lsblk
- inst_binary wc
+ image_install lsblk wc
+
+ # Configure multipath
+ if command -v multipath && command -v multipathd; then
+ install_multipath
+ fi
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
)
}
@@ -182,6 +187,49 @@ EOF
QEMU_SMP=1 QEMU_TIMEOUT=60 test_run_one "${1:?}"
}
+testcase_multipath_basic_failover() {
+ if ! command -v multipath || ! command -v multipathd; then
+ echo "Missing multipath tools, skipping the test..."
+ return 77
+ fi
+
+ local qemu_opts=("-device virtio-scsi-pci,id=scsi")
+ 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
+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
+
+ # Add 64 multipath devices, each backed by 4 paths
+ for ndisk in {0..63}; do
+ wwn="0xDEADDEADBEEF$(printf "%.4d" "$ndisk")"
+ # Use a partitioned disk for the first device to test failover
+ [[ $ndisk -eq 0 ]] && image="$partdisk" || image="${TESTDIR:?}/disk$ndisk.img"
+
+ for nback in {0..3}; do
+ qemu_opts+=(
+ "-device scsi-hd,drive=drive${ndisk}x${nback},serial=MPIO$ndisk,wwn=$wwn"
+ "-drive format=raw,cache=unsafe,file=$image,file.locking=off,if=none,id=drive${ndisk}x${nback}"
+ )
+ done
+ done
+
+ KERNEL_APPEND="systemd.setenv=TEST_FUNCTION_NAME=${FUNCNAME[0]} ${USER_KERNEL_APPEND:-}"
+ QEMU_OPTIONS="${qemu_opts[*]} ${USER_QEMU_OPTIONS:-}"
+ test_run_one "${1:?}"
+}
+
# Allow overriding which tests should be run from the "outside", useful for manual
# testing (make -C test/... TESTCASES="testcase1 testcase2")
if [[ -v "TESTCASES" && -n "$TESTCASES" ]]; then
diff --git a/test/units/testsuite-64.sh b/test/units/testsuite-64.sh
index 4505306607..64859f1c89 100755
--- a/test/units/testsuite-64.sh
+++ b/test/units/testsuite-64.sh
@@ -19,6 +19,105 @@ testcase_virtio_scsi_identically_named_partitions() {
[[ "$(lsblk --noheadings -a -o NAME,PARTLABEL | grep -c "Hello world")" -eq $((16 * 8)) ]]
}
+testcase_multipath_basic_failover() {
+ local dmpath i path wwid
+
+ # Configure multipath
+ cat >/etc/multipath.conf <<\EOF
+defaults {
+ # Use /dev/mapper/$WWN paths instead of /dev/mapper/mpathX
+ user_friendly_names no
+ find_multipaths yes
+ enable_foreign "^$"
+}
+
+blacklist_exceptions {
+ property "(SCSI_IDENT_|ID_WWN)"
+}
+
+blacklist {
+}
+EOF
+ modprobe -v dm_multipath
+ systemctl start multipathd.service
+ systemctl status multipathd.service
+ multipath -ll
+ ls -l /dev/disk/by-id/
+
+ for i in {0..63}; do
+ wwid="deaddeadbeef$(printf "%.4d" "$i")"
+ path="/dev/disk/by-id/wwn-0x$wwid"
+ dmpath="$(readlink -f "$path")"
+
+ lsblk "$path"
+ multipath -C "$dmpath"
+ # We should have 4 active paths for each multipath device
+ [[ "$(multipath -l "$path" | grep -c running)" -eq 4 ]]
+ done
+
+ # Test failover (with the first multipath device that has a partitioned disk)
+ echo "${FUNCNAME[0]}: test failover"
+ local device expected link mpoint part
+ local -a devices
+ mpoint="$(mktemp -d /mnt/mpathXXX)"
+ wwid="deaddeadbeef0000"
+ path="/dev/disk/by-id/wwn-0x$wwid"
+
+ # All following symlinks should exists and should be valid
+ local -a part_links=(
+ "/dev/disk/by-id/wwn-0x$wwid-part2"
+ "/dev/disk/by-partlabel/failover_part"
+ "/dev/disk/by-partuuid/deadbeef-dead-dead-beef-000000000000"
+ "/dev/disk/by-label/failover_vol"
+ "/dev/disk/by-uuid/deadbeef-dead-dead-beef-111111111111"
+ )
+ for link in "${part_links[@]}"; do
+ test -e "$link"
+ done
+
+ # Choose a random symlink to the failover data partition each time, for
+ # a better coverage
+ part="${part_links[$RANDOM % ${#part_links[@]}]}"
+
+ # Get all devices attached to a specific multipath device (in H:C:T:L format)
+ # and sort them in a random order, so we cut off different paths each time
+ mapfile -t devices < <(multipath -l "$path" | grep -Eo '[0-9]+:[0-9]+:[0-9]+:[0-9]+' | sort -R)
+ if [[ "${#devices[@]}" -ne 4 ]]; then
+ echo "Expected 4 devices attached to WWID=$wwid, got ${#devices[@]} instead"
+ return 1
+ fi
+ # Drop the last path from the array, since we want to leave at least one path active
+ unset "devices[3]"
+ # Mount the first multipath partition, write some data we can check later,
+ # and then disconnect the remaining paths one by one while checking if we
+ # can still read/write from the mount
+ mount -t ext4 "$part" "$mpoint"
+ expected=0
+ echo -n "$expected" >"$mpoint/test"
+ # Sanity check we actually wrote what we wanted
+ [[ "$(<"$mpoint/test")" == "$expected" ]]
+
+ for device in "${devices[@]}"; do
+ echo offline >"/sys/class/scsi_device/$device/device/state"
+ [[ "$(<"$mpoint/test")" == "$expected" ]]
+ expected="$((expected + 1))"
+ echo -n "$expected" >"$mpoint/test"
+
+ # Make sure all symlinks are still valid
+ for link in "${part_links[@]}"; do
+ test -e "$link"
+ done
+ done
+
+ multipath -l "$path"
+ # Three paths should be now marked as 'offline' and one as 'running'
+ [[ "$(multipath -l "$path" | grep -c offline)" -eq 3 ]]
+ [[ "$(multipath -l "$path" | grep -c running)" -eq 1 ]]
+
+ umount "$mpoint"
+ rm -fr "$mpoint"
+}
+
: >/failed
udevadm settle