Handle target_disk detection strings

For preseed installs with pvcbootstrapd, implement "detect" strings,
which can be used instead of fixed block paths to determine the required
disk from a fixed set of information available to a human provisioning
the servers, or from Redfish.

The basic idea is thus:

  1. The user specifies some physical attributes of the disk, either
  manually in a detect string of the preseed configuration, or by a
  physical identifier that Redfish can identify.

  2. Redfish takes this and either passes it, or crafts a detect string
  itself based on its storage information, which is then passed to the
  installer preseed as the target_disk value.

  3. The installer uses the provided values along with the output of the
  "lsscsi" command to determine which block device to use for the system
  disk.

This supersedes and enhances the original "model-based" detection with
far greater reliability and the ability to specify specific indexes.
This commit is contained in:
Joshua Boniface 2023-09-01 15:41:58 -04:00
parent 05e567c667
commit e8aed11693
1 changed files with 57 additions and 12 deletions

View File

@ -211,18 +211,63 @@ seed_config() {
host_ipaddr=$( ip -br address show ${target_interface} | awk '{ print $3 }' | awk -F '/' '{ print $1 }' ) host_ipaddr=$( ip -br address show ${target_interface} | awk '{ print $3 }' | awk -F '/' '{ print $1 }' )
# Handle the target disk # Handle the target disk
if [[ -n ${target_disk_path} ]]; then case "${target_disk}" in
target_disk="$( realpath ${target_disk_path} )" /dev/*)
else # Get the real path of the block device (for /dev/disk/* symlink paths)
# Find the (first) disk with the given model target_disk="$( realpath ${target_disk} )"
for disk in /dev/sd?; do ;;
disk_model="$( fdisk -l ${disk} | grep 'Disk model:' | sed 's/Disk model: //g' )" detect:*)
if [[ ${disk_model} == ${target_disk_model} ]]; then # Read the detect string into separate variables
target_disk="${disk}" # A detect string is formated thusly:
# detect:<Controller-or-Vendor-Name>:<0-indexed-ID>:<Capacity-in-human-units>
# For example:
# detect:INTEL:1:800GB
# detect:DELLBOSS:0:240GB
# detect:PERC H330 Mini:0:200GB
IFS=: read detect b_name b_id b_size <<<"${target_disk}"
# Get the lsscsi output (exclude NVMe)
lsscsi_data_all="$( lsscsi -s -N )"
# Get the available sizes, and match to within +/- 2%
lsscsi_sizes=( $( awk '{ print $NF }' <<<"${lsscsi_data_all}" | sort | uniq ) )
# For each size...
for size in ${lsscsi_sizes[@]}; do
# Get whether we match +2% and -2% sizes to handle human -> real deltas
# The break below is pretty safe. I can think of no two classes of disks
# where the difference is within 2% of each other. Even the common
# 120GB -> 128GB and 240GB -> 256GB size deltas are well outside of 2%,
# so this should be safe in all cases. 1% would be narrower but has more
# chance of mis-identifying due to rounding, while 3% gets into more
# contentious differences, so 2% seems like the best option.
# We use Python for this due to BASH's problematic handling of floating-
# point numbers.
is_match="$(
python <<EOF
from re import sub
b_size = float(sub(r'\D','','${b_size}'))
t_size = float(sub(r'\D','','${size}'))
plustwopct = t_size * 1.02
minustwopct = t_size * 0.98
if b_size > minustwopct and b_size < plustwopct:
print("match")
EOF
)"
# If we do, this size is our actual block size, not what was specified
if [[ -n ${is_match} ]]; then
b_size=${size}
break break
fi fi
done done
fi # Search for the b_name first
lsscsi_data_name="$( grep --color=none -Fiw "${b_name}" <<<"${lsscsi_data_all}" )"
# Search for the b_blocks second
lsscsi_data_name_size="$( grep --color=none -Fiw "${b_size}" <<<"${lsscsi_data_name}" )"
# Read the /dev/X results into an array
lsscsi_filtered=( $( awk '{ print $(NF-1) }' <<<"${lsscsi_data_name_size}" ) )
# Get the b_id-th entry
target_disk="${lsscsi_filtered[${b_id}]}"
;;
esac
if [[ ! -b ${target_disk} ]]; then if [[ ! -b ${target_disk} ]]; then
echo "Invalid disk or disk not found!" echo "Invalid disk or disk not found!"
exit 1 exit 1