diff --git a/config-files/disks/mirror.nix b/config-files/disks/mirror.nix index dc1bbd6..6f97727 100644 --- a/config-files/disks/mirror.nix +++ b/config-files/disks/mirror.nix @@ -41,4 +41,3 @@ type = "xfs"; label = "data-storage"; mountpoint = "/mnt/data-storage"; - diff --git a/deploy.sh b/deploy.sh index 42b8174..de2a162 100755 --- a/deploy.sh +++ b/deploy.sh @@ -282,46 +282,93 @@ disk_config_generation() { echo -e "\n\n 🔎 Fetching and analyzing disks from target host... (This may take a moment)" - declare -A DISK_INFO_MAP - declare -A DISK_SIZE_MAP - declare -A DISK_BY_ID_MAP - declare -A DISK_LABEL_MAP - DISK_OPTIONS=() - DISK_NAMES=$(ssh_to_host "lsblk -d -n -o NAME,TYPE | awk '\$2==\"disk\" {print \$1}'") -# --> Get disks info - for name in $DISK_NAMES; do - details=$(echo "$REMOTE_PASS" | ssh_to_host " - set -e - devpath=/dev/$name - rota=1 # Default to rotational (HDD) - [ -f /sys/block/$name/queue/rotational ] && rota=\$(cat /sys/block/$name/queue/rotational) - tran=\$(lsblk -d -n -o TRAN \"\$devpath\" || echo 'unknown') - health=\$(sudo -S smartctl -H \"\$devpath\" 2>/dev/null | grep 'self-assessment' | awk '{print \$6}') - by_id=\$(ls -l /dev/disk/by-id | grep -m 1 \"../../$name\$\" | awk '{print \"/dev/disk/by-id/\"\$9}') - - # Determine type - if [[ \"\$name\" == nvme* ]]; then type=\"NVMe\"; # Check for NVMe first - elif [[ \"\$rota\" == \"0\" ]]; then type=\"SSD\"; - elif [[ \"\$tran\" == \"usb\" ]]; then type=\"USB\"; - else type=\"HDD\"; fi +### --> Get disk information + DISK_DETAILS=$(ssh_to_host " +# Declare arrays and variables +DISK_DEVPATH=() +DISK_NAME=() +DISK_TYPE=() +DISK_HEALTH=() +DISK_ID=() +DISK_SIZE=() +HDD=1 - # Fallback for health and by-id - [[ -z \"\$health\" || \"\$health\" == \"\" ]] && health=\"N/A\" - [ -z \"\$by_id\" ] && by_id=\"\$devpath\" +for DISK in \$(lsblk -d -n -o NAME); do + # Disk name and simple path + DISK_DEVPATH+=(\"/dev/\$DISK\") + DISK_NAME+=(\"\$DISK\") - # Get size last, after other commands that might fail - size=\$(lsblk -b -d -n -o SIZE \"\$devpath\") - echo \"\$size:::\$type:::\$health:::\$by_id\" - ") -# Get disks info <-- + # Disk type + HDD=\$(cat /sys/block/\$DISK/queue/rotational) + TRANSPORT_PROTOCOL=\$(lsblk -d -n -o TRAN /dev/\$DISK) + if [[ \"\$DISK\" == \"nvme*\" ]]; then DISK_TYPE+=(\"NVMe\"); + elif [[ \"\$TRANSPORT_PROTOCOL\" == \"usb\" ]]; then DISK_TYPE+=(\"USB\"); + elif [[ \"\$HDD\" == \"1\" ]]; then DISK_TYPE+=(\"HDD\"); + elif [[ \"\$HDD\" == \"0\" ]]; then DISK_TYPE+=(\"SSD\"); + else DISK_TYPE+=(\"Other\") + fi - mapfile -t parts < <(echo "$details" | tr ':' '\n') - size="${parts[0]}" - disk_type="${parts[3]}" - health="${parts[6]}" - by_id="${parts[9]}" + # Disk health + if [[ \$(echo "$REMOTE_PASS" | sudo -S smartctl -H /dev/\$DISK 2>/dev/null | grep 'self-assessment' | awk '{print \$6}') == \"PASSED\" ]]; then + DISK_HEALTH+=(\"PASSED\") + else + DISK_HEALTH+=(\"N/A\") + fi + + # Disk ID + DISK_ID+=(\$(ls -l /dev/disk/by-id | grep -m1 \"../../\$DISK\" | awk '{print \"/dev/disk/by-id/\" \$9}')) + + # Disk size + DISK_SIZE+=(\"\$(lsblk -d -n -o SIZE /dev/\$DISK)\") +done + +# Print elements +echo \"\${DISK_DEVPATH[@]}\" +echo \"\${DISK_NAME[@]}\" +echo \"\${DISK_TYPE[@]}\" +echo \"\${DISK_HEALTH[@]}\" +echo \"\${DISK_ID[@]}\" +echo \"\${DISK_SIZE[@]}\" +" +) + + # Get arrays back + readarray -t LINES <<<"$DISK_DETAILS" + read -r -a DISK_DEVPATH <<<"${LINES[0]}" + read -r -a DISK_NAME <<<"${LINES[1]}" + read -r -a DISK_TYPE <<<"${LINES[2]}" + read -r -a DISK_HEALTH <<<"${LINES[3]}" + read -r -a DISK_ID <<<"${LINES[4]}" + read -r -a DISK_SIZE <<<"${LINES[5]}" +### Get disk information <-- + + + +### --> Disk selection + NUMBER_OF_DISKS=${#DISK_NAME[@]} + + if [ "$NUMBER_OF_DISKS" -eq 0 ]; then + echo " ❌ No disks found on the target host. Aborting." + exit 1 + fi + + HEADER=$(printf " %-12s %-12s %-12s %-12s %s" "Device" "Type" "Size" "SMART" "Path") + + i=0 + for DISK in $(seq 1 $NUMBER_OF_DISKS); do + PRINTED_ELEMENT=$(printf "%-12s %-12s %-12s %-12s %s" "${DISK_NAME[$i]}" "${DISK_TYPE[$i]}" "${DISK_SIZE[$i]}" "${DISK_HEALTH[$i]}" "${DISK_DEVPATH[$i]}") + PRINTED_ELEMENTS+=("$PRINTED_ELEMENT") + i=$i+1 + done + + gum style --foreground 212 " ➡️ Please choose one (stripe) or two (mirror) disks for your NixOS boot installation:" + echo -e "" + mapfile -t SELECTED_BOOT_LABELS < <(gum choose --limit 2 --header "$HEADER" "${PRINTED_ELEMENTS[@]}") +### Disk selection <-- + +## Now you have to look at associative arrays to define clear variable for the following logic (i.e. BOOT_DISK_1, BOOT_DISK_2, etc) human_size=$(numfmt --to=iec-i --suffix=B "$size") label=$(printf "%-12s %-12s %-12s %-12s %s" "$name" "$disk_type" "$human_size" "$health" "$by_id") @@ -333,10 +380,6 @@ disk_config_generation() { DISK_LABEL_MAP["$name"]="$label" done - if [ ${#DISK_OPTIONS[@]} -eq 0 ]; then - echo " ❌ No disks found on the target host. Aborting." - exit 1 - fi HEADER=$(printf " %-12s %-12s %-12s %-12s %s" "Device" "Type" "Size" "SMART" "ID") gum style --foreground 212 " ➡️ Please choose one (stripe) or two (mirror) disks for your NixOS boot installation:" @@ -442,7 +485,7 @@ The mirror will be created using the size of the smaller disk, and any extra spa echo -e "\n\n ⚠️ No remaining disks to select for data." fi -# --> Final recap +### --> Final recap NUMBER_OF_BOOT_DISKS=0 [[ -n "$BOOT_DISK_1" ]] && NUMBER_OF_BOOT_DISKS=$((NUMBER_OF_BOOT_DISKS + 1)) && export BOOT_DISK_1 [[ -n "$BOOT_DISK_2" ]] && NUMBER_OF_BOOT_DISKS=$((NUMBER_OF_BOOT_DISKS + 1)) && export BOOT_DISK_2 @@ -468,35 +511,36 @@ EOF gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "$(gum format <<< "$RECAP_CONTENT")" gum confirm "Proceed with this disk configuration?" || { echo " ❌ Aborting as requested."; exit 1; } -# Final recap <-- +### Final recap <-- + + +### --> Disko file generation echo -e "\n\n ✅ Generating disko configuration from templates..." template_file="config-files/disks/boot-${NUMBER_OF_BOOT_DISKS}.nix" (envsubst < "$template_file") > ./nix-config/disks/disko.nix echo -e "\n ✅ Generated boot disk configuration." - if (( NUMBER_OF_CONTENT_DISKS == 1 && NUMBER_OF_PARITY_DISKS == 1 )); then + # Mirror configuration + if [[ "$NUMBER_OF_CONTENT_DISKS" == 1 && "$NUMBER_OF_PARITY_DISKS" == 1 ]]; then (envsubst < "config-files/disks/mirror.nix") >> ./nix-config/disks/disko.nix - fi - - if (( NUMBER_OF_CONTENT_DISKS != 2 )); then + # SnapRAID configuration + elif [[ "$NUMBER_OF_CONTENT_DISKS" -gt 0 ]]; then for i in $(seq 1 $NUMBER_OF_CONTENT_DISKS); do - disk_var="CONTENT_DISK_$i" + LOOP_DISK="CONTENT_DISK_$i" export DISK_NUMBER=$i - export DISK_PATH=${!disk_var} + export DISK_PATH=${!LOOP_DISK} (envsubst < "config-files/disks/content.nix") >> ./nix-config/disks/disko.nix done - [[ "$NUMBER_OF_CONTENT_DISKS" -gt 0 ]] && echo -e "\n ✅ Generated $NUMBER_OF_CONTENT_DISKS data disk configuration(s)." - + echo -e "\n ✅ Generated $NUMBER_OF_CONTENT_DISKS data disk configuration(s)." for i in $(seq 1 $NUMBER_OF_PARITY_DISKS); do - disk_var="PARITY_DISK_$i" + LOOP_DISK="PARITY_DISK_$i" export DISK_NUMBER=$i - export DISK_PATH=${!disk_var} + export DISK_PATH=${!LOOP_DISK} (envsubst < "config-files/disks/parity.nix") >> ./nix-config/disks/disko.nix done - [[ "$NUMBER_OF_PARITY_DISKS" -gt 0 ]] && echo -e "\n ✅ Generated $NUMBER_OF_PARITY_DISKS parity disk configuration(s)." + echo -e "\n ✅ Generated $NUMBER_OF_PARITY_DISKS parity disk configuration(s)." fi - # Close the disko.nix block cat <<'EOF' >> ./nix-config/disks/disko.nix }; @@ -504,6 +548,9 @@ EOF } EOF echo -e "\n ✅ Final disko configuration created." +### Disko file generation <-- + + # --> Generate automatic unlock configuration in ./nix-config/disks/snapraid.nix if [[ "$NUMBER_OF_CONTENT_DISKS" -gt 1 && "$NUMBER_OF_PARITY_DISKS" -gt 0 ]]; then