From e8aed11693ae0668be01fe30c911d97458da413b Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Fri, 1 Sep 2023 15:41:58 -0400 Subject: [PATCH] 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. --- templates/install.sh | 69 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/templates/install.sh b/templates/install.sh index abce91d..f46c0ac 100755 --- a/templates/install.sh +++ b/templates/install.sh @@ -211,18 +211,63 @@ seed_config() { host_ipaddr=$( ip -br address show ${target_interface} | awk '{ print $3 }' | awk -F '/' '{ print $1 }' ) # Handle the target disk - if [[ -n ${target_disk_path} ]]; then - target_disk="$( realpath ${target_disk_path} )" - else - # Find the (first) disk with the given model - for disk in /dev/sd?; do - disk_model="$( fdisk -l ${disk} | grep 'Disk model:' | sed 's/Disk model: //g' )" - if [[ ${disk_model} == ${target_disk_model} ]]; then - target_disk="${disk}" - break - fi - done - fi + case "${target_disk}" in + /dev/*) + # Get the real path of the block device (for /dev/disk/* symlink paths) + target_disk="$( realpath ${target_disk} )" + ;; + detect:*) + # Read the detect string into separate variables + # A detect string is formated thusly: + # detect::<0-indexed-ID>: + # 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 < 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 + fi + done + # 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 echo "Invalid disk or disk not found!" exit 1