From 092b548d204ea4471be927538263950b0f505026 Mon Sep 17 00:00:00 2001 From: jcastro <190036+jcastro@users.noreply.github.com> Date: Thu, 14 May 2026 14:45:45 +0200 Subject: [PATCH] Select VM ISOs from all ISO storages --- scripts/global/iso_storage_helpers.sh | 146 ++++++++++++++++++++++++++ scripts/vm/select_linux_iso.sh | 39 +++---- scripts/vm/select_windows_iso.sh | 37 ++++--- scripts/vm/vm_creator.sh | 60 +++++++---- 4 files changed, 229 insertions(+), 53 deletions(-) create mode 100644 scripts/global/iso_storage_helpers.sh diff --git a/scripts/global/iso_storage_helpers.sh b/scripts/global/iso_storage_helpers.sh new file mode 100644 index 00000000..a4d2b6b0 --- /dev/null +++ b/scripts/global/iso_storage_helpers.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env bash + +# ========================================================== +# ProxMenux - ISO Storage Helpers +# ========================================================== +# Shared helpers for VM ISO selection. Proxmox identifies ISO media by +# volume ID (for example: local:iso/debian.iso or nas:iso/win11.iso); +# using the volid lets VMs boot ISOs stored on local, NFS, CIFS or any +# other storage that advertises content=iso. +# ========================================================== + +ISO_FALLBACK_DIR="${ISO_FALLBACK_DIR:-/var/lib/vz/template/iso}" + +iso_name_from_volid() { + local volid="$1" + local rel="${volid#*:}" + basename "${rel#iso/}" +} + +iso_storage_from_volid() { + local volid="$1" + echo "${volid%%:*}" +} + +iso_volid_matches_filter() { + local volid="$1" + local filter="${2:-all}" + local name lower + + name=$(iso_name_from_volid "$volid") + lower=$(printf '%s' "$name" | tr '[:upper:]' '[:lower:]') + [[ "$lower" == *.iso ]] || return 1 + + case "$filter" in + windows) + [[ "$lower" != virtio*.iso ]] + ;; + virtio) + [[ "$lower" == virtio*.iso ]] + ;; + all|*) + return 0 + ;; + esac +} + +iso_path_to_volid() { + local path="$1" + local rest storage file + + case "$path" in + /var/lib/vz/template/iso/*) + echo "local:iso/$(basename "$path")" + return 0 + ;; + /mnt/pve/*/template/iso/*) + rest="${path#/mnt/pve/}" + storage="${rest%%/*}" + file="$(basename "$path")" + echo "${storage}:iso/${file}" + return 0 + ;; + esac + + return 1 +} + +iso_volid_to_path() { + local volid="$1" + local storage rel file path + + if command -v pvesm >/dev/null 2>&1; then + path=$(pvesm path "$volid" 2>/dev/null || true) + if [[ -n "$path" ]]; then + echo "$path" + return 0 + fi + fi + + storage=$(iso_storage_from_volid "$volid") + rel="${volid#*:}" + file="$(basename "${rel#iso/}")" + + if [[ "$storage" == "local" ]]; then + echo "/var/lib/vz/template/iso/$file" + else + echo "/mnt/pve/$storage/template/iso/$file" + fi +} + +iso_list_volids() { + local filter="${1:-all}" + local storage volid path + local -a volids=() + + if command -v pvesm >/dev/null 2>&1; then + while read -r storage; do + [[ -z "$storage" ]] && continue + while read -r volid; do + [[ -z "$volid" ]] && continue + if iso_volid_matches_filter "$volid" "$filter"; then + volids+=("$volid") + fi + done < <(pvesm list "$storage" --content iso 2>/dev/null | awk 'NR>1 {print $1}') + done < <(pvesm status -content iso 2>/dev/null | awk 'NR>1 && $3 == "active" {print $1}') + fi + + if [[ ${#volids[@]} -eq 0 && -d "$ISO_FALLBACK_DIR" ]]; then + while read -r path; do + volid=$(iso_path_to_volid "$path" 2>/dev/null || true) + [[ -z "$volid" ]] && continue + if iso_volid_matches_filter "$volid" "$filter"; then + volids+=("$volid") + fi + done < <(find "$ISO_FALLBACK_DIR" -maxdepth 1 -type f -iname "*.iso" | sort) + fi + + [[ ${#volids[@]} -gt 0 ]] && printf '%s\n' "${volids[@]}" | sort -u +} + +iso_human_size() { + local path="$1" + local bytes + + [[ -f "$path" ]] || { echo "-"; return 0; } + + if command -v du >/dev/null 2>&1; then + du -h "$path" 2>/dev/null | awk '{print $1}' + return 0 + fi + + bytes=$(wc -c < "$path" 2>/dev/null || echo "") + [[ -n "$bytes" ]] && echo "${bytes}B" || echo "-" +} + +iso_dialog_description() { + local volid="$1" + local name storage path size + + name=$(iso_name_from_volid "$volid") + storage=$(iso_storage_from_volid "$volid") + path=$(iso_volid_to_path "$volid") + size=$(iso_human_size "$path") + + printf '%-42s │ %-14s │ %s' "$name" "$storage" "$size" +} diff --git a/scripts/vm/select_linux_iso.sh b/scripts/vm/select_linux_iso.sh index 62631ac4..cb7158ca 100644 --- a/scripts/vm/select_linux_iso.sh +++ b/scripts/vm/select_linux_iso.sh @@ -20,7 +20,7 @@ # Arch, Rocky, Mint, openSUSE, Alpine, Kali, Manjaro) with # direct download URLs. # - Cloud-Init automated installers (community scripts). -# - Pick an existing ISO from /var/lib/vz/template/iso. +# - Pick an existing ISO from any Proxmox ISO storage. # - Separate "Others Prebuilt VMs" selector (HAOS, Docker, Nextcloud) # used by the main menu's Community Scripts path. # ========================================================== @@ -33,6 +33,9 @@ VENV_PATH="/opt/googletrans-env" ISO_DIR="/var/lib/vz/template/iso" [[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE" +if [[ -f "$LOCAL_SCRIPTS/global/iso_storage_helpers.sh" ]]; then + source "$LOCAL_SCRIPTS/global/iso_storage_helpers.sh" +fi load_language initialize_cache @@ -62,7 +65,7 @@ function select_linux_iso() { 20 70 10 \ 1 "$(printf '%-35s│ %s' 'Instalar con metodo tradicional' 'Desde ISO oficial')" \ 2 "$(printf '%-35s│ %s' 'Instalar con script Cloud-Init' 'Helper Scripts')" \ - 3 "$(printf '%-35s│ %s' 'Instalar con ISO personal' 'Almacenamiento local')" \ + 3 "$(printf '%-35s│ %s' 'Instalar con ISO personal' 'Almacenamiento ISO')" \ 4 "Volver al menú principal" \ 3>&1 1>&2 2>&3) else @@ -79,13 +82,13 @@ function select_linux_iso() { 18 70 10 \ 1 "$(printf '%-35s│ %s' "$desc1" "From official ISO")" \ 2 "$(printf '%-35s│ %s' "$desc2" "Helper Scripts")" \ - 3 "$(printf '%-35s│ %s' "$desc3" "Local Storage")" \ + 3 "$(printf '%-35s│ %s' "$desc3" "ISO Storage")" \ 4 "$back" \ 3>&1 1>&2 2>&3) fi if [[ $? -ne 0 || "$CHOICE" == "4" ]]; then - unset ISO_NAME ISO_TYPE ISO_URL ISO_FILE ISO_PATH HN + unset ISO_NAME ISO_TYPE ISO_URL ISO_FILE ISO_PATH ISO_VOLID HN return 1 fi @@ -151,6 +154,7 @@ function select_linux_iso_official() { IFS='|' read -r ISO_NAME ISO_TYPE SOURCE ISO_URL <<< "$SELECTED" ISO_FILE=$(basename "$ISO_URL") ISO_PATH="$ISO_DIR/$ISO_FILE" + ISO_VOLID="local:iso/$ISO_FILE" HN=$(echo "$ISO_NAME" | \ sed 's/ (.*)//' | \ @@ -159,7 +163,7 @@ function select_linux_iso_official() { sed 's/-*$//' | \ cut -c1-63) - export ISO_NAME ISO_TYPE ISO_URL ISO_FILE ISO_PATH HN + export ISO_NAME ISO_TYPE ISO_URL ISO_FILE ISO_PATH ISO_VOLID HN export OS_TYPE="3" return 0 } @@ -225,34 +229,35 @@ function select_linux_cloudinit() { function select_linux_custom_iso() { + local volid ISO_LIST=() - while read -r line; do - FILENAME=$(basename "$line") - SIZE=$(du -h "$line" | cut -f1) - ISO_LIST+=("$FILENAME" "$SIZE") - done < <(find "$ISO_DIR" -type f -iname "*.iso" | sort) + while read -r volid; do + [[ -z "$volid" ]] && continue + ISO_LIST+=("$volid" "$(iso_dialog_description "$volid")") + done < <(iso_list_volids "all") if [[ ${#ISO_LIST[@]} -eq 0 ]]; then header_info - msg_error "$(translate "No ISO images found in") $ISO_DIR." + msg_error "$(translate "No ISO images found in Proxmox ISO storages.")" sleep 2 return 1 fi - ISO_FILE=$(dialog --backtitle "ProxMenux" --title "$(translate "Available ISO Images")" \ - --menu "$(translate "Select a custom ISO to use:")" 20 70 10 \ + ISO_VOLID=$(dialog --backtitle "ProxMenux" --title "$(translate "Available ISO Images")" \ + --menu "$(translate "Select a custom ISO to use:")\n\n$(printf '%-42s │ %-14s │ %s' "$(translate "ISO")" "$(translate "Storage")" "$(translate "Size")")" 22 86 12 \ "${ISO_LIST[@]}" 3>&1 1>&2 2>&3) - if [[ -z "$ISO_FILE" ]]; then + if [[ -z "$ISO_VOLID" ]]; then header_info msg_warn "$(translate "No ISO selected.")" return 1 fi - ISO_PATH="$ISO_DIR/$ISO_FILE" + ISO_FILE=$(iso_name_from_volid "$ISO_VOLID") + ISO_PATH=$(iso_volid_to_path "$ISO_VOLID") ISO_NAME="$ISO_FILE" - export ISO_PATH ISO_FILE ISO_NAME + export ISO_PATH ISO_FILE ISO_NAME ISO_VOLID export OS_TYPE="3" return 0 } @@ -310,5 +315,3 @@ return 1 } - - diff --git a/scripts/vm/select_windows_iso.sh b/scripts/vm/select_windows_iso.sh index e4c47c80..e54a16da 100644 --- a/scripts/vm/select_windows_iso.sh +++ b/scripts/vm/select_windows_iso.sh @@ -17,7 +17,7 @@ # # Features: # - Build an up-to-date Windows ISO via the UUP Dump creator. -# - Pick a Windows ISO already present in /var/lib/vz/template/iso. +# - Pick a Windows ISO already present in any Proxmox ISO storage. # - Auto-detects the latest ISO created by UUP Dump. # - Exports ISO metadata (name, path, OS_TYPE) for the wizard. # ========================================================== @@ -30,6 +30,9 @@ VENV_PATH="/opt/googletrans-env" ISO_DIR="/var/lib/vz/template/iso" [[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE" +if [[ -f "$LOCAL_SCRIPTS/global/iso_storage_helpers.sh" ]]; then + source "$LOCAL_SCRIPTS/global/iso_storage_helpers.sh" +fi load_language initialize_cache mkdir -p "$ISO_DIR" @@ -51,7 +54,7 @@ function select_windows_iso() { --menu "\nSeleccione el tipo de instalación de Windows:\n\n$header" \ 20 70 10 \ 1 "$(printf '%-34s│ %s' 'Instalar con ISO UUP Dump' 'UUP Dump ISO creator')" \ - 2 "$(printf '%-34s│ %s' 'Instalar con ISO personal' 'Almacenamiento local')" \ + 2 "$(printf '%-34s│ %s' 'Instalar con ISO personal' 'Almacenamiento ISO')" \ 3 "Volver al menú principal" \ 3>&1 1>&2 2>&3) else @@ -65,13 +68,13 @@ function select_windows_iso() { --menu "\n$(translate "Select the type of Windows installation:")\n\n$header" \ 18 70 10 \ 1 "$(printf '%-35s│ %s' "$desc1" "UUP Dump creator")" \ - 2 "$(printf '%-35s│ %s' "$desc2" "Local Storage")" \ + 2 "$(printf '%-35s│ %s' "$desc2" "ISO Storage")" \ 3 "$back" \ 3>&1 1>&2 2>&3) fi if [[ $? -ne 0 || "$CHOICE" == "3" ]]; then - unset ISO_NAME ISO_TYPE ISO_URL ISO_FILE ISO_PATH HN + unset ISO_NAME ISO_TYPE ISO_URL ISO_FILE ISO_PATH ISO_VOLID HN return 1 fi @@ -96,30 +99,31 @@ function select_windows_iso() { function select_existing_iso() { + local volid ISO_LIST=() - while read -r line; do - FILENAME=$(basename "$line") - SIZE=$(du -h "$line" | cut -f1) - ISO_LIST+=("$FILENAME" "$SIZE") - done < <(find "$ISO_DIR" -type f -iname "*.iso" ! -iname "virtio*" | sort) + while read -r volid; do + [[ -z "$volid" ]] && continue + ISO_LIST+=("$volid" "$(iso_dialog_description "$volid")") + done < <(iso_list_volids "windows") if [[ ${#ISO_LIST[@]} -eq 0 ]]; then header_info - msg_error "$(translate "No ISO images found in") $ISO_DIR." + msg_error "$(translate "No ISO images found in Proxmox ISO storages.")" sleep 2 return 1 fi - ISO_FILE=$(dialog --backtitle "ProxMenux" --title "$(translate "Available ISO Images")" \ - --menu "$(translate "Choose a Windows ISO to use:")" 20 70 10 \ + ISO_VOLID=$(dialog --backtitle "ProxMenux" --title "$(translate "Available ISO Images")" \ + --menu "$(translate "Choose a Windows ISO to use:")\n\n$(printf '%-42s │ %-14s │ %s' "$(translate "ISO")" "$(translate "Storage")" "$(translate "Size")")" 22 86 12 \ "${ISO_LIST[@]}" 3>&1 1>&2 2>&3) - [[ -z "$ISO_FILE" ]] && msg_warn "$(translate "No ISO selected.")" && return 1 + [[ -z "$ISO_VOLID" ]] && msg_warn "$(translate "No ISO selected.")" && return 1 - ISO_PATH="$ISO_DIR/$ISO_FILE" + ISO_FILE=$(iso_name_from_volid "$ISO_VOLID") + ISO_PATH=$(iso_volid_to_path "$ISO_VOLID") ISO_NAME="$ISO_FILE" - export ISO_PATH ISO_FILE ISO_NAME + export ISO_PATH ISO_FILE ISO_NAME ISO_VOLID export OS_TYPE="2" return 0 @@ -136,8 +140,9 @@ function detect_latest_iso_created() { ISO_NAME=$(basename "$ISO_FILE") ISO_PATH="$ISO_FILE" + ISO_VOLID="local:iso/$ISO_NAME" - export ISO_PATH ISO_FILE ISO_NAME + export ISO_PATH ISO_FILE ISO_NAME ISO_VOLID export OS_TYPE="2" return 0 diff --git a/scripts/vm/vm_creator.sh b/scripts/vm/vm_creator.sh index 3218995a..052d0f94 100644 --- a/scripts/vm/vm_creator.sh +++ b/scripts/vm/vm_creator.sh @@ -47,6 +47,11 @@ if [[ -f "$LOCAL_SCRIPTS_LOCAL/global/vm_storage_helpers.sh" ]]; then elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/vm_storage_helpers.sh" ]]; then source "$LOCAL_SCRIPTS_DEFAULT/global/vm_storage_helpers.sh" fi +if [[ -f "$LOCAL_SCRIPTS_LOCAL/global/iso_storage_helpers.sh" ]]; then + source "$LOCAL_SCRIPTS_LOCAL/global/iso_storage_helpers.sh" +elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/iso_storage_helpers.sh" ]]; then + source "$LOCAL_SCRIPTS_DEFAULT/global/iso_storage_helpers.sh" +fi if [[ -f "$LOCAL_SCRIPTS_LOCAL/global/pci_passthrough_helpers.sh" ]]; then source "$LOCAL_SCRIPTS_LOCAL/global/pci_passthrough_helpers.sh" elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/pci_passthrough_helpers.sh" ]]; then @@ -69,14 +74,28 @@ function mount_iso_to_vm() { local vmid="$1" local iso_path="$2" local device="$3" + local iso_volid="${4:-}" + local iso_label - if [[ -f "$iso_path" ]]; then - local iso_basename - iso_basename=$(basename "$iso_path") - qm set "$vmid" -$device "local:iso/$iso_basename,media=cdrom" >/dev/null 2>&1 - msg_ok "$(translate "Mounted ISO on device") $device → $iso_basename" - else + if [[ -z "$iso_volid" && -n "$iso_path" ]]; then + if [[ "$iso_path" == *":iso/"* ]]; then + iso_volid="$iso_path" + elif [[ -f "$iso_path" ]] && declare -F iso_path_to_volid >/dev/null 2>&1; then + iso_volid=$(iso_path_to_volid "$iso_path" 2>/dev/null || true) + fi + fi + + if [[ -z "$iso_volid" ]]; then msg_warn "$(translate "ISO not found to mount on device") $device" + return 1 + fi + + iso_label="$(basename "${iso_volid#*:}")" + if qm set "$vmid" -$device "${iso_volid},media=cdrom" >/dev/null 2>&1; then + msg_ok "$(translate "Mounted ISO on device") $device → $iso_label" + else + msg_warn "$(translate "Could not mount ISO on device") $device → $iso_volid" + return 1 fi } @@ -256,6 +275,7 @@ function create_vm() { if [[ -f "$ISO_PATH" ]]; then + ISO_VOLID="${ISO_VOLID:-local:iso/$(basename "$ISO_PATH")}" msg_ok "$(translate "ISO image downloaded")" else msg_error "$(translate "Failed to download ISO image")" @@ -622,14 +642,15 @@ fi - if [[ -f "$ISO_PATH" ]]; then - mount_iso_to_vm "$VMID" "$ISO_PATH" "ide2" + if [[ -n "${ISO_VOLID:-}" || -f "${ISO_PATH:-}" ]]; then + mount_iso_to_vm "$VMID" "$ISO_PATH" "ide2" "${ISO_VOLID:-}" fi if [[ "$OS_TYPE" == "2" ]]; then local VIRTIO_DIR="/var/lib/vz/template/iso" local VIRTIO_SELECTED="" + local VIRTIO_VOLID="" local VIRTIO_DOWNLOAD_URL="https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" while true; do @@ -661,25 +682,26 @@ fi fi VIRTIO_SELECTED="$VIRTIO_DIR/virtio-win.iso" + VIRTIO_VOLID="local:iso/virtio-win.iso" ;; 2) + local virtio_volid VIRTIO_LIST=() - while read -r line; do - FILENAME=$(basename "$line") - SIZE=$(du -h "$line" | cut -f1) - VIRTIO_LIST+=("$FILENAME" "$SIZE") - done < <(find "$VIRTIO_DIR" -type f -iname "virtio*.iso" | sort) + while read -r virtio_volid; do + [[ -z "$virtio_volid" ]] && continue + VIRTIO_LIST+=("$virtio_volid" "$(iso_dialog_description "$virtio_volid")") + done < <(iso_list_volids "virtio") if [[ ${#VIRTIO_LIST[@]} -eq 0 ]]; then msg_warn "$(translate "No VirtIO ISO found. Please download one.")" continue fi - VIRTIO_FILE=$(whiptail --title "ProxMenux - VirtIO ISOs" --menu "$(translate "Select a VirtIO ISO to use:")" 20 70 10 "${VIRTIO_LIST[@]}" 3>&1 1>&2 2>&3) + VIRTIO_VOLID=$(whiptail --title "ProxMenux - VirtIO ISOs" --menu "$(translate "Select a VirtIO ISO to use:")\n\n$(printf '%-42s │ %-14s │ %s' "$(translate "ISO")" "$(translate "Storage")" "$(translate "Size")")" 22 86 12 "${VIRTIO_LIST[@]}" 3>&1 1>&2 2>&3) - if [[ -n "$VIRTIO_FILE" ]]; then - VIRTIO_SELECTED="$VIRTIO_DIR/$VIRTIO_FILE" + if [[ -n "$VIRTIO_VOLID" ]]; then + VIRTIO_SELECTED=$(iso_volid_to_path "$VIRTIO_VOLID") else msg_warn "$(translate "No VirtIO ISO selected. Please choose again.")" continue @@ -687,8 +709,8 @@ fi ;; esac - if [[ -n "$VIRTIO_SELECTED" && -f "$VIRTIO_SELECTED" ]]; then - mount_iso_to_vm "$VMID" "$VIRTIO_SELECTED" "ide3" + if [[ -n "$VIRTIO_VOLID" || -f "$VIRTIO_SELECTED" ]]; then + mount_iso_to_vm "$VMID" "$VIRTIO_SELECTED" "ide3" "$VIRTIO_VOLID" else msg_warn "$(translate "VirtIO ISO not found after selection.")" fi @@ -702,7 +724,7 @@ fi if [[ -n "$BOOT_ORDER" ]]; then BOOT_FINAL="$BOOT_ORDER" fi - if [[ -f "$ISO_PATH" ]]; then + if [[ -n "${ISO_VOLID:-}" || -f "${ISO_PATH:-}" ]]; then BOOT_FINAL="${BOOT_FINAL:+$BOOT_FINAL;}ide2" fi if [[ -n "$BOOT_FINAL" ]]; then