diff --git a/.gitignore b/.gitignore index d6eb3b0..ed81e28 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ agents/ ai-production/ extra-files/ test.sh +test2.sh deploy.conf \ No newline at end of file diff --git a/config-files/disks/boot-1.nix b/config-files/disks/boot-1.nix index f29459f..e2e3c15 100644 --- a/config-files/disks/boot-1.nix +++ b/config-files/disks/boot-1.nix @@ -23,7 +23,7 @@ # Boot disk "system-1" = { type = "disk"; - device = "${BOOT_DISK_1}"; + device = "${BOOT_DISK_1_ID}"; content = { type = "gpt"; partitions = { diff --git a/config-files/disks/boot-2.nix b/config-files/disks/boot-2.nix index fce832d..58ef827 100644 --- a/config-files/disks/boot-2.nix +++ b/config-files/disks/boot-2.nix @@ -4,7 +4,7 @@ disk = { "system-1" = { type = "disk"; - device = "${BOOT_DISK_1}"; + device = "${BOOT_DISK_1_ID}"; content = { type = "gpt"; partitions = { @@ -34,7 +34,7 @@ }; "system-2" = { type = "disk"; - device = "${BOOT_DISK_2}"; + device = "${BOOT_DISK_2_ID}"; content = { type = "gpt"; partitions = { diff --git a/config-files/disks/content.nix b/config-files/disks/content.nix index 33dafbe..e9621d9 100644 --- a/config-files/disks/content.nix +++ b/config-files/disks/content.nix @@ -1,6 +1,6 @@ - "content-${DISK_NUMBER}" = { + "content-${i}" = { type = "disk"; - device = "${DISK_PATH}"; + device = "${CONTENT_DISK_ID}"; content = { type = "gpt"; partitions = { @@ -8,12 +8,12 @@ size = "100%"; content = { type = "luks"; - name = "crypted-content-${DISK_NUMBER}"; - settings.keyFile = "/etc/secrets/disks/content-disk-${DISK_NUMBER}"; + name = "crypted-content-${i}"; + settings.keyFile = "/etc/secrets/disks/content-disk-${i}"; content = { type = "filesystem"; format = "xfs"; - mountpoint = "/mnt/content-${DISK_NUMBER}"; + mountpoint = "/mnt/content-${i}"; }; }; }; diff --git a/config-files/disks/mirror.nix b/config-files/disks/mirror.nix index 6f97727..a69de4b 100644 --- a/config-files/disks/mirror.nix +++ b/config-files/disks/mirror.nix @@ -1,9 +1,9 @@ - + }; # Data mirror configuration generated by deploy.sh disk = { "content-1" = { type = "disk"; - device = "${CONTENT_DISK_1}"; + device = "${CONTENT_DISK_1_ID}"; content = { type = "gpt"; partitions = { @@ -20,7 +20,7 @@ }; "parity-1" = { type = "disk"; - device = "${PARITY_DISK_1}"; + device = "${PARITY_DISK_1_ID}"; content = { type = "gpt"; partitions = { diff --git a/config-files/disks/parity.nix b/config-files/disks/parity.nix index 853ef99..a8f8480 100644 --- a/config-files/disks/parity.nix +++ b/config-files/disks/parity.nix @@ -1,6 +1,6 @@ - "parity-${DISK_NUMBER}" = { + "parity-${i}" = { type = "disk"; - device = "${DISK_PATH}"; + device = "${PARITY_DISK_ID}"; content = { type = "gpt"; partitions = { @@ -8,12 +8,12 @@ size = "100%"; content = { type = "luks"; - name = "crypted-parity-${DISK_NUMBER}"; - settings.keyFile = "/etc/secrets/disks/parity-disk-${DISK_NUMBER}"; + name = "crypted-parity-${i}"; + settings.keyFile = "/etc/secrets/disks/parity-disk-${i}"; content = { type = "filesystem"; format = "xfs"; - mountpoint = "/mnt/parity-${DISK_NUMBER}"; + mountpoint = "/mnt/parity-${i}"; }; }; }; diff --git a/deploy.sh b/deploy.sh index de2a162..ca1a02d 100755 --- a/deploy.sh +++ b/deploy.sh @@ -272,37 +272,33 @@ files_generation() { } disk_config_generation() { +### --> Disk wiping warning gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 " ⚠️ $(gum style --foreground 212 'WARNING:') You will choose the disks to install NixOS on. !! PLEASE MAKE SURE YOU BACKED UP ANY IMPORTANT DATA !! - !! ALL DATA WILL BE WIPED ON THE DISKS YOU CHOOSE !! + !! ALL DATA WILL BE WIPED ON THE DISKS YOU CHOOSE !! Please press CTRL+C to abort. " - gum confirm "Do you understand and wish to proceed?" || { echo " ❌ Aborting."; exit 1; } + gum confirm "Do you understand and wish to proceed?" || { echo -e "\n\n ❌ Aborting as requested."; exit 1; } echo -e "\n\n 🔎 Fetching and analyzing disks from target host... (This may take a moment)" +### Disk wiping warning <-- ### --> Get disk information - DISK_DETAILS=$(ssh_to_host " + DISK_DETAILS=$(ssh darky " # Declare arrays and variables -DISK_DEVPATH=() -DISK_NAME=() -DISK_TYPE=() -DISK_HEALTH=() -DISK_ID=() -DISK_SIZE=() HDD=1 -for DISK in \$(lsblk -d -n -o NAME); do +for DISK in \$(lsblk -x SIZE -d -n -o NAME); do # Disk name and simple path DISK_DEVPATH+=(\"/dev/\$DISK\") DISK_NAME+=(\"\$DISK\") # Disk type HDD=\$(cat /sys/block/\$DISK/queue/rotational) - TRANSPORT_PROTOCOL=\$(lsblk -d -n -o TRAN /dev/\$DISK) + TRANSPORT_PROTOCOL=\$(lsblk -x SIZE -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\"); @@ -321,7 +317,7 @@ for DISK in \$(lsblk -d -n -o NAME); do 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)\") + DISK_SIZE+=(\"\$(lsblk -x SIZE -d -n -o SIZE /dev/\$DISK)\") done # Print elements @@ -334,7 +330,7 @@ echo \"\${DISK_SIZE[@]}\" " ) - # Get arrays back +# Get arrays back readarray -t LINES <<<"$DISK_DETAILS" read -r -a DISK_DEVPATH <<<"${LINES[0]}" read -r -a DISK_NAME <<<"${LINES[1]}" @@ -347,178 +343,136 @@ echo \"\${DISK_SIZE[@]}\" ### --> Disk selection - NUMBER_OF_DISKS=${#DISK_NAME[@]} + TOTAL_NUMBER_OF_DISKS=${#DISK_NAME[@]} - if [ "$NUMBER_OF_DISKS" -eq 0 ]; then - echo " ❌ No disks found on the target host. Aborting." + if [ "$TOTAL_NUMBER_OF_DISKS" -eq 0 ]; then + echo -e "\n\n ❌ 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 + for i in $(seq 0 $((TOTAL_NUMBER_OF_DISKS-1))); do + GUM_PRINTED_ELEMENT=$(printf "%-12s %-12s %-12s %-12s %s" \ + "${DISK_NAME[$i]}" "${DISK_TYPE[$i]}" "${DISK_SIZE[$i]}" \ + "${DISK_HEALTH[$i]}" "${DISK_DEVPATH[$i]}") + GUM_PRINTED_ELEMENTS+=("$GUM_PRINTED_ELEMENT") 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[@]}") + gum style --foreground 212 " ➡️ Please choose one (stripe) or two (mirror) disks for your NixOS boot installation :" + + SELECTED_BOOT_DISK=$(gum choose --limit 2 --header "$HEADER" "${GUM_PRINTED_ELEMENTS[@]}") + + j=0 + for i in $(seq 0 $((TOTAL_NUMBER_OF_DISKS - 1))); do + if printf '%s' "$SELECTED_BOOT_DISK" | grep -iq "${DISK_NAME[$i]}"; then + ((j++)) + export declare "BOOT_DISK_${j}_ID=${DISK_ID[$i]}" + unset "GUM_PRINTED_ELEMENTS[${i}]" + ((NUMBER_OF_BOOT_DISKS++)) + fi + done + + if [[ "$NUMBER_OF_BOOT_DISKS" == "1" ]]; then + echo -e "\n\n ⚠️ One boot disk selected, continuing with striped boot disk configuration." + echo -e " Consider using 2 boot disks instead to get data protection features on the boot disks." + elif [[ "$NUMBER_OF_BOOT_DISKS" == "2" ]]; then + echo -e "\n\n ✅ Two boot disks selected, continuing with mirrored boot disks configuration." + else + echo -e "\n\n ❌ No boot disk selected. Aborting." + exit 1 + fi + + gum style --foreground 212 " ➡️ Please choose data and parity disks (up to 9 total) :" + + SELECTED_DATA_DISK=$(gum choose --limit 9 --header "$HEADER" "${GUM_PRINTED_ELEMENTS[@]}") + + for i in $(seq 0 $((TOTAL_NUMBER_OF_DISKS - 1))); do + if printf '%s' "$SELECTED_DATA_DISK" | grep -iq "${DISK_NAME[$i]}"; then + ((NUMBER_OF_DATA_DISKS++)) + fi + done + + if [[ "$NUMBER_OF_DATA_DISKS" == "0" ]]; then + echo -e "\n\n ⚠️ No data disks selected. You should consider select at least 2 data disks." + elif [[ "$NUMBER_OF_DATA_DISKS" == "1" ]]; then + echo -e "\n\n ⚠️ One data disk selected, continuing with striped boot disk configuration." + echo -e " Consider using AT LEAST 2 data disks instead to get data protection features on the data disks." + for i in $(seq 0 $((TOTAL_NUMBER_OF_DISKS - 1))); do + if printf '%s' "$SELECTED_DATA_DISK" | grep -iq "${DISK_NAME[$i]}"; then + NUMBER_OF_CONTENT_DISKS="1" + NUMBER_OF_PARITY_DISKS="0" + export CONTENT_DISK_1_ID="${DISK_ID[$i]}" + fi + done + elif [[ "$NUMBER_OF_DATA_DISKS" == "2" ]]; then + NUMBER_OF_CONTENT_DISKS="0" + for i in $(seq 0 $((TOTAL_NUMBER_OF_DISKS - 1))); do + if printf '%s' "$SELECTED_DATA_DISK" | grep -iq "${DISK_NAME[$i]}"; then + if [[ "$NUMBER_OF_CONTENT_DISKS" == "0" ]]; then + NUMBER_OF_CONTENT_DISKS="1" + export CONTENT_DISK_1_ID="${DISK_ID[$i]}" + else + NUMBER_OF_PARITY_DISKS="1" + export PARITY_DISK_1_ID="${DISK_ID[$i]}" + fi + fi + done + else + NUMBER_OF_PARITY_DISKS=$(((NUMBER_OF_DATA_DISKS + 2) / 3)) + NUMBER_OF_CONTENT_DISKS=$((NUMBER_OF_DATA_DISKS - NUMBER_OF_PARITY_DISKS)) + j="$NUMBER_OF_PARITY_DISKS" + k="$NUMBER_OF_CONTENT_DISKS" + l="1" + m="1" + for i in $(seq 0 $((TOTAL_NUMBER_OF_DISKS - 1))); do + if printf '%s' "$SELECTED_DATA_DISK" | grep -iq "${DISK_NAME[$i]}"; then + if [[ "$k" -gt 0 ]]; then + declare "CONTENT_DISK_${l}_ID=${DISK_ID[$i]}" + k=$((k - 1)) + ((l++)) + elif [[ "$j" -gt 0 ]]; then + declare "PARITY_DISK_${m}_ID=${DISK_ID[$i]}" + j=$((j - 1)) + ((m++)) + fi + fi + done + fi ### 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") - - DISK_OPTIONS+=("$label") - DISK_INFO_MAP["$label"]="$name" - DISK_SIZE_MAP["$name"]="$size" - DISK_BY_ID_MAP["$name"]="$by_id" - DISK_LABEL_MAP["$name"]="$label" - done - 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:" - echo -e "" - mapfile -t SELECTED_BOOT_LABELS < <(gum choose --limit 2 --header "$HEADER" "${DISK_OPTIONS[@]}") - - if [ ${#SELECTED_BOOT_LABELS[@]} -eq 0 ]; then echo " ❌ No boot disk selected. Aborting."; exit 1; fi - - BOOT_DISK_1_NAME=${DISK_INFO_MAP["${SELECTED_BOOT_LABELS[0]}"]} - BOOT_DISK_1=${DISK_BY_ID_MAP[$BOOT_DISK_1_NAME]} - BOOT_DISK_2="" - if [ ${#SELECTED_BOOT_LABELS[@]} -eq 2 ]; then - BOOT_DISK_2_NAME=${DISK_INFO_MAP["${SELECTED_BOOT_LABELS[1]}"]} - BOOT_DISK_2=${DISK_BY_ID_MAP[$BOOT_DISK_2_NAME]} - fi - - REMAINING_DISKS=() - for label in "${DISK_OPTIONS[@]}"; do - is_boot_disk=false - for selected in "${SELECTED_BOOT_LABELS[@]}"; do - if [[ "$label" == "$selected" ]]; then is_boot_disk=true; break; fi - done - if ! $is_boot_disk; then REMAINING_DISKS+=("$label"); fi - done - - if [ ${#REMAINING_DISKS[@]} -gt 0 ]; then - echo -e "" - gum style --foreground 212 " ➡️ Please choose your data and parity disks (up to 9 total)." - mapfile -t SELECTED_DATA_LABELS < <(gum choose --limit 9 --header "$HEADER" "${REMAINING_DISKS[@]}") - - if [ ${#SELECTED_DATA_LABELS[@]} -gt 0 ]; then - selected_data_names=() - for label in "${SELECTED_DATA_LABELS[@]}"; do - selected_data_names+=("${DISK_INFO_MAP[$label]}") - done - - num_selected=${#selected_data_names[@]} - num_parity=0 - num_content=0 - - if (( num_selected == 2 )); then - # Special case for 2 data disks: create a mirror. - echo -e "\n ✅ Two data disks detected, creating a mirror configuration." - - disk1_name=${selected_data_names[0]} - disk2_name=${selected_data_names[1]} - disk1_size=${DISK_SIZE_MAP[$disk1_name]} - disk2_size=${DISK_SIZE_MAP[$disk2_name]} - - if [[ "$disk1_size" -ne "$disk2_size" ]]; then - gum style --border normal --margin "1" --padding "1 2" --border-foreground 212 "⚠️ The two selected disks for the mirror have different sizes. -The mirror will be created using the size of the smaller disk, and any extra space on the larger disk will be unused." - gum confirm "Do you want to proceed anyway?" || { echo " ❌ Aborting as requested."; exit 1; } - fi - - mv ./nix-config/disks/snapraid.nix ./nix-config/disks/snapraid.original - sed -i 's| ./disks/snapraid.nix|# ./disks/snapraid.nix|' ./nix-config/configuration.nix - - export CONTENT_DISK_1=${DISK_BY_ID_MAP[${selected_data_names[0]}]} - export PARITY_DISK_1=${DISK_BY_ID_MAP[${selected_data_names[1]}]} - NUMBER_OF_CONTENT_DISKS=1 # For summary and key generation - NUMBER_OF_PARITY_DISKS=1 - - elif (( num_selected == 1 )); then # If only one data disk is selected, it is a content disk - num_content=1 - num_parity=0 - NUMBER_OF_CONTENT_DISKS=$num_content - NUMBER_OF_PARITY_DISKS=$num_parity - elif (( num_selected > 2 )); then # If more than two data disks are selected - # 1 parity for up to 2 content disks. - num_parity=$(( (num_selected + 2) / 3 )) - num_content=$(( num_selected - num_parity )) - NUMBER_OF_CONTENT_DISKS=$num_content - NUMBER_OF_PARITY_DISKS=$num_parity - fi - - if (( num_selected != 2 )); then - # Sort selected disks by size (largest first) for snapraid - sorted_disks=($( - for name in "${selected_data_names[@]}"; do - echo "${DISK_SIZE_MAP[$name]} $name" - done | sort -rn | awk '{print $2}' - )) - - # Assign parity disks (the largest ones) - parity_disks_final=() - for i in $(seq 0 $((num_parity > 0 ? num_parity - 1 : -1))); do - [[ -n "${sorted_disks[$i]}" ]] && parity_disks_final+=("${DISK_BY_ID_MAP[${sorted_disks[$i]}]}") - done - - # Assign content disks (the remaining ones) - content_disks_final=() - for i in $(seq $num_parity $((num_selected - 1))); do - [[ -n "${sorted_disks[$i]}" ]] && content_disks_final+=("${DISK_BY_ID_MAP[${sorted_disks[$i]}]}") - done - - # Set exported variables (up to 6 content disks and 3 parity disks) - for i in {0..5}; do export "CONTENT_DISK_$((i+1))"="${content_disks_final[$i]:-}"; done - for i in {0..2}; do export "PARITY_DISK_$((i+1))"="${parity_disks_final[$i]:-}"; done - fi - fi - else - echo -e "\n\n ⚠️ No remaining disks to select for data." - fi - -### --> 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 - +### --> Selection recap RECAP_CONTENT=$(cat < Disko file generation +### --> Config 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 + 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." # Mirror configuration @@ -527,16 +481,16 @@ EOF # SnapRAID configuration elif [[ "$NUMBER_OF_CONTENT_DISKS" -gt 0 ]]; then for i in $(seq 1 $NUMBER_OF_CONTENT_DISKS); do - LOOP_DISK="CONTENT_DISK_$i" - export DISK_NUMBER=$i - export DISK_PATH=${!LOOP_DISK} + export i + LOOP_DISK="CONTENT_DISK_${i}_ID" + export CONTENT_DISK_ID=${!LOOP_DISK} (envsubst < "config-files/disks/content.nix") >> ./nix-config/disks/disko.nix done echo -e "\n ✅ Generated $NUMBER_OF_CONTENT_DISKS data disk configuration(s)." for i in $(seq 1 $NUMBER_OF_PARITY_DISKS); do - LOOP_DISK="PARITY_DISK_$i" - export DISK_NUMBER=$i - export DISK_PATH=${!LOOP_DISK} + export i + LOOP_DISK="PARITY_DISK_${i}_ID" + export PARITY_DISK_ID=${!LOOP_DISK} (envsubst < "config-files/disks/parity.nix") >> ./nix-config/disks/disko.nix done echo -e "\n ✅ Generated $NUMBER_OF_PARITY_DISKS parity disk configuration(s)." @@ -548,11 +502,11 @@ EOF } EOF echo -e "\n ✅ Final disko configuration created." -### Disko file generation <-- +### Config generation <-- - # --> Generate automatic unlock configuration in ./nix-config/disks/snapraid.nix +### --> Generate automatic unlock configuration if [[ "$NUMBER_OF_CONTENT_DISKS" -gt 1 && "$NUMBER_OF_PARITY_DISKS" -gt 0 ]]; then echo -e "\n ✅ Generating automatic disk unlocking configuration..." sed -i '$ d' ./nix-config/disks/snapraid.nix @@ -564,10 +518,10 @@ EOF if [[ "$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" cat <> ./nix-config/disks/snapraid.nix "crypted-content-disk-${i}" = { - device = "${!disk_var}"; + device = "${!LOOP_DISK}"; keyFile = "/etc/secrets/disks/content-disk-${i}"; }; EOF @@ -576,10 +530,10 @@ EOF if [[ "$NUMBER_OF_PARITY_DISKS" -gt 0 ]]; then for i in $(seq 1 $NUMBER_OF_PARITY_DISKS); do - disk_var="PARITY_DISK_$i" + LOOP_DISK="PARITY_DISK_$i" cat <> ./nix-config/disks/snapraid.nix "crypted-parity-disk-${i}" = { - device = "${!disk_var}"; + device = "${!LOOP_DISK}"; keyFile = "/etc/secrets/disks/parity-disk-${i}"; }; EOF @@ -592,7 +546,7 @@ EOF } EOF fi - # Generate automatic unlock configuration in ./nix-config/disks/snapraid.nix <-- +### Generate automatic unlock configuration <-- } deploy() { diff --git a/nix-config/configuration.nix b/nix-config/configuration.nix index 4212006..355975a 100644 --- a/nix-config/configuration.nix +++ b/nix-config/configuration.nix @@ -6,7 +6,7 @@ (modulesPath + "/profiles/qemu-guest.nix") inputs.sops-nix.nixosModules.sops ./disks/disko.nix - ./disks/snapraid.nix +# ./disks/snapraid.nix # ./disks/pcr-check.nix ]; diff --git a/nix-config/disks/snapraid.nix b/nix-config/disks/snapraid.original similarity index 100% rename from nix-config/disks/snapraid.nix rename to nix-config/disks/snapraid.original