mirror of
https://github.com/systemd/systemd.git
synced 2025-03-24 14:50:17 +03:00
Merge pull request #20823 from mrc0mmand/test-storage-iscsi
test: iSCSI-related udev tests
This commit is contained in:
commit
78fac35811
@ -11,6 +11,8 @@ set -e
|
||||
TEST_DESCRIPTION="systemd-udev storage tests"
|
||||
IMAGE_NAME="default"
|
||||
TEST_NO_NSPAWN=1
|
||||
# Save only journals of failing test cases by default (to conserve space)
|
||||
TEST_SAVE_JOURNAL="${TEST_SAVE_JOURNAL:-fail}"
|
||||
QEMU_TIMEOUT="${QEMU_TIMEOUT:-600}"
|
||||
|
||||
# shellcheck source=test/test-functions
|
||||
@ -28,14 +30,20 @@ _host_has_feature() {(
|
||||
set -e
|
||||
|
||||
case "${1:?}" in
|
||||
multipath)
|
||||
command -v multipath && command -v multipathd || return $?
|
||||
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 $?
|
||||
;;
|
||||
btrfs)
|
||||
modprobe -nv btrfs && command -v mkfs.btrfs && command -v btrfs || return $?
|
||||
multipath)
|
||||
command -v multipath && command -v multipathd || return $?
|
||||
;;
|
||||
*)
|
||||
echo >&2 "ERROR: Unknown feature '$1'"
|
||||
@ -54,6 +62,7 @@ test_append_files() {(
|
||||
# checked for here
|
||||
local -A features=(
|
||||
[btrfs]=install_btrfs
|
||||
[iscsi]=install_iscsi
|
||||
[lvm]=install_lvm
|
||||
[multipath]=install_multipath
|
||||
)
|
||||
@ -360,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
|
||||
|
@ -959,6 +959,45 @@ install_btrfs() {
|
||||
inst_rules 64-btrfs-dm.rules
|
||||
}
|
||||
|
||||
install_iscsi() {
|
||||
# Install both client and server side stuff by default
|
||||
local inst="${1:-}"
|
||||
local file
|
||||
|
||||
# Install client-side stuff ("initiator" in iSCSI jargon) - Open-iSCSI in this case
|
||||
# (open-iscsi on Debian, iscsi-initiator-utils on Fedora, etc.)
|
||||
if [[ -z "$inst" || "$inst" =~ (client|initiator) ]]; then
|
||||
image_install iscsi-iname iscsiadm iscsid iscsistart
|
||||
image_install -o "${ROOTLIBDIR:?}"/system/iscsi-{init,onboot,shutdown}.service
|
||||
image_install "${ROOTLIBDIR:?}"/system/iscsid.{service,socket}
|
||||
image_install "${ROOTLIBDIR:?}"/system/iscsi.service
|
||||
mkdir -p "${initdir:?}"/var/lib/iscsi/{ifaces,isns,nodes,send_targets,slp,static}
|
||||
mkdir -p "${initdir:?}/etc/iscsi"
|
||||
echo "iscsid.startup = /bin/systemctl start iscsid.socket" >"${initdir:?}/etc/iscsi/iscsid.conf"
|
||||
inst_simple "/etc/iscsi/initiatorname.iscsi"
|
||||
fi
|
||||
|
||||
# Install server-side stuff ("target" in iSCSI jargon) - TGT in this case
|
||||
# (tgt on Debian, scsi-target-utils on Fedora, etc.)
|
||||
if [[ -z "$inst" || "$inst" =~ (server|target) ]]; then
|
||||
image_install tgt-admin tgt-setup-lun tgtadm tgtd tgtimg
|
||||
image_install -o /etc/sysconfig/tgtd
|
||||
image_install "${ROOTLIBDIR:?}"/system/tgtd.service
|
||||
mkdir -p "${initdir:?}/etc/tgt"
|
||||
touch "${initdir:?}"/etc/tgt/{tgtd,targets}.conf
|
||||
# Install perl modules required by tgt-admin
|
||||
#
|
||||
# Forgive me father for I have sinned. The monstrosity below appends
|
||||
# a perl snippet to the `tgt-admin` perl script on the fly, which
|
||||
# dumps a list of files (perl modules) required by `tgt-admin` at
|
||||
# the runtime plus any DSOs loaded via DynaLoader. This list is then
|
||||
# passed to `inst_simple` which installs the necessary files into the image
|
||||
while read -r file; do
|
||||
inst_simple "$file"
|
||||
done < <(perl -- <(cat $(command -v tgt-admin) <(echo -e 'use DynaLoader; print map { "$_\n" } values %INC; print join("\n", @DynaLoader::dl_shared_objects)')) -p | awk '/^\// { print $1 }')
|
||||
fi
|
||||
}
|
||||
|
||||
install_compiled_systemd() {
|
||||
dinfo "Install compiled systemd"
|
||||
|
||||
@ -2319,6 +2358,8 @@ inst() {
|
||||
for fun in inst_symlink inst_script inst_binary inst_simple; do
|
||||
"$fun" "$@" && return 0
|
||||
done
|
||||
|
||||
dwarn "Failed to install '$1'"
|
||||
return 1
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user