diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh index a7138d3704d..23cdefa5ce9 100755 --- a/test/TEST-64-UDEV-STORAGE/test.sh +++ b/test/TEST-64-UDEV-STORAGE/test.sh @@ -33,6 +33,12 @@ _host_has_feature() {( btrfs) modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $? ;; + iscsi) + # Client/initiator (Open-iSCSI) + command -v iscsiadm && command -v iscsid || return $? + # Server/target (TGT) + command -v tgtadm && command -v tgtd || return $? + ;; lvm) command -v lvm || return $? ;; @@ -56,6 +62,7 @@ test_append_files() {( # checked for here local -A features=( [btrfs]=install_btrfs + [iscsi]=install_iscsi [lvm]=install_lvm [multipath]=install_multipath ) @@ -362,6 +369,35 @@ testcase_btrfs_basic() { rm -f "${TESTDIR:?}"/btrfsbasic*.img } +testcase_iscsi_lvm() { + if ! _host_has_feature "iscsi" || ! _host_has_feature "lvm"; then + echo "Missing iSCSI client/server tools (Open-iSCSI/TGT) or LVM utilities, 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:?}/iscsibasic${i}.img" + # Make the first disk larger for multi-partition tests + [[ $i -eq 0 ]] && size=150 || size=64 + # Make the first disk larger for multi-partition tests + + dd if=/dev/zero of="$diskpath" bs=1M count="$size" + qemu_opts+=( + "-device ide-hd,bus=ahci0.$i,drive=drive$i,model=foobar,serial=deadbeefiscsi$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:?}"/iscsibasic*.img +} # 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 4380fa0fcfa..bcc4dc9f17e 100755 --- a/test/units/testsuite-64.sh +++ b/test/units/testsuite-64.sh @@ -40,6 +40,57 @@ helper_check_device_symlinks() {( done < <(find "${paths[@]}" -type l) )} +# Wait for a specific device link to appear +# Arguments: +# $1 - device path +# $2 - number of retries (default: 10) +helper_wait_for_dev() { + local dev="${1:?}" + local ntries="${2:-10}" + local i + + for ((i = 0; i < ntries; i++)); do + test ! -e "$dev" || return 0 + sleep .2 + done + + return 1 +} + +# Wait for the lvm2-pvscan@.service of a specific device to finish +# Arguments: +# $1 - device path +# $2 - number of retries (default: 10) +helper_wait_for_pvscan() { + local dev="${1:?}" + local ntries="${2:-10}" + local MAJOR MINOR pvscan_svc real_dev + + # Sanity check we got a valid block device (or a symlink to it) + real_dev="$(readlink -f "$dev")" + if [[ ! -b "$real_dev" ]]; then + echo >&2 "ERROR: '$dev ($real_dev) is not a valid block device'" + return 1 + fi + + # Get major and minor numbers from the udev database + # (udevadm returns MAJOR= and MINOR= expressions, so let's pull them into + # the current environment via `source` for easier parsing) + source <(udevadm info -q property "$real_dev" | grep -E "(MAJOR|MINOR)=") + # Sanity check if we got correct major and minor numbers + test -e "/sys/dev/block/$MAJOR:$MINOR/" + + # Wait n_tries*0.5 seconds until the respective lvm2-pvscan service becomes + # active (i.e. it got executed and finished) + pvscan_svc="lvm2-pvscan@$MAJOR:$MINOR.service" + for ((i = 0; i < ntries; i++)); do + ! systemctl -q is-active "$pvscan_svc" || return 0 + sleep .5 + done + + return 1 +} + testcase_megasas2_basic() { lsblk -S [[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]] @@ -397,6 +448,144 @@ EOF udevadm settle } +testcase_iscsi_lvm() { + local dev i label link lun_id mpoint target_name uuid + local target_ip="127.0.0.1" + local target_port="3260" + local vgroup="iscsi_lvm$RANDOM" + local expected_symlinks=() + local devices=( + /dev/disk/by-id/ata-foobar_deadbeefiscsi{0..3} + ) + + ls -l "${devices[@]}" + + # Start the target daemon + systemctl start tgtd + systemctl status tgtd + + echo "iSCSI LUNs backed by devices" + # See RFC3721 and RFC7143 + target_name="iqn.2021-09.com.example:iscsi.test" + # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each + # backed by a device + tgtadm --lld iscsi --op new --mode target --tid=1 --targetname "$target_name" + for ((i = 0; i < ${#devices[@]}; i++)); do + # lun-0 is reserved by iSCSI + lun_id="$((i + 1))" + tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun "$lun_id" -b "${devices[$i]}" + tgtadm --lld iscsi --op update --mode logicalunit --tid 1 --lun "$lun_id" + expected_symlinks+=( + "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$lun_id" + ) + done + tgtadm --lld iscsi --op bind --mode target --tid 1 -I ALL + # Configure the iSCSI initiator + iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover + iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login + udevadm settle + # Check if all device symlinks are valid and if all expected device symlinks exist + for link in "${expected_symlinks[@]}"; do + # We need to do some active waiting anyway, as it may take kernel a bit + # to attach the newly connected SCSI devices + helper_wait_for_dev "$link" + test -e "$link" + done + udevadm settle + helper_check_device_symlinks + # Cleanup + iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout + tgtadm --lld iscsi --op delete --mode target --tid=1 + + echo "iSCSI LUNs backed by files + LVM" + # Note: we use files here to "trick" LVM the disks are indeed on a different + # host, so it doesn't automagically detect another path to the backing + # device once we disconnect the iSCSI devices + target_name="iqn.2021-09.com.example:iscsi.lvm.test" + mpoint="$(mktemp -d /iscsi_storeXXX)" + expected_symlinks=() + # Use the first device as it's configured with larger capacity + mkfs.ext4 -L iscsi_store "${devices[0]}" + udevadm settle + mount "${devices[0]}" "$mpoint" + for i in {1..4}; do + dd if=/dev/zero of="$mpoint/lun$i.img" bs=1M count=32 + done + # Initialize a new iSCSI target <$target_name> consisting of 4 LUNs, each + # backed by a file + tgtadm --lld iscsi --op new --mode target --tid=2 --targetname "$target_name" + # lun-0 is reserved by iSCSI + for i in {1..4}; do + tgtadm --lld iscsi --op new --mode logicalunit --tid 2 --lun "$i" -b "$mpoint/lun$i.img" + tgtadm --lld iscsi --op update --mode logicalunit --tid 2 --lun "$i" + expected_symlinks+=( + "/dev/disk/by-path/ip-$target_ip:$target_port-iscsi-$target_name-lun-$i" + ) + done + tgtadm --lld iscsi --op bind --mode target --tid 2 -I ALL + # Configure the iSCSI initiator + iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover + iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login + udevadm settle + # Check if all device symlinks are valid and if all expected device symlinks exist + for link in "${expected_symlinks[@]}"; do + # We need to do some active waiting anyway, as it may take kernel a bit + # to attach the newly connected SCSI devices + helper_wait_for_dev "$link" + test -e "$link" + done + udevadm settle + helper_check_device_symlinks + # Add all iSCSI devices into a LVM volume group, create two logical volumes, + # and check if necessary symlinks exist (and are valid) + lvm pvcreate -y "${expected_symlinks[@]}" + lvm pvs + lvm vgcreate "$vgroup" -y "${expected_symlinks[@]}" + lvm vgs + lvm vgchange -ay "$vgroup" + lvm lvcreate -y -L 4M "$vgroup" -n mypart1 + lvm lvcreate -y -L 8M "$vgroup" -n mypart2 + lvm lvs + udevadm settle + test -e "/dev/$vgroup/mypart1" + test -e "/dev/$vgroup/mypart2" + mkfs.ext4 -L mylvpart1 "/dev/$vgroup/mypart1" + udevadm settle + test -e "/dev/disk/by-label/mylvpart1" + helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" + # Disconnect the iSCSI devices and check all the symlinks + iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout + # "Reset" the DM state, since we yanked the backing storage from under the LVM, + # so the currently active VGs/LVs are invalid + dmsetup remove_all --deferred + udevadm settle + # The LVM and iSCSI related symlinks should be gone + test ! -e "/dev/$vgroup" + test ! -e "/dev/disk/by-label/mylvpart1" + for link in "${expected_symlinks[@]}"; do + test ! -e "$link" + done + helper_check_device_symlinks "/dev/disk" + # Reconnect the iSCSI devices and check if everything get detected correctly + iscsiadm --mode discoverydb --type sendtargets --portal "$target_ip" --discover + iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --login + udevadm settle + for link in "${expected_symlinks[@]}"; do + helper_wait_for_dev "$link" + helper_wait_for_pvscan "$link" + test -e "$link" + done + udevadm settle + test -e "/dev/$vgroup/mypart1" + test -e "/dev/$vgroup/mypart2" + test -e "/dev/disk/by-label/mylvpart1" + helper_check_device_symlinks "/dev/disk" "/dev/$vgroup" + # Cleanup + iscsiadm --mode node --targetname "$target_name" --portal "$target_ip:$target_port" --logout + tgtadm --lld iscsi --op delete --mode target --tid=2 + umount "$mpoint" + rm -rf "$mpoint" +} : >/failed udevadm settle