update pci_passthrough_helpers.sh

This commit is contained in:
MacRimi
2026-04-05 11:24:08 +02:00
parent 00dbd1c87e
commit 5826b0419b
13 changed files with 2322 additions and 2274 deletions

View File

@@ -1,175 +0,0 @@
#!/usr/bin/env bash
# ==========================================================
# ProxMenuX - Virtual Machine Creator Script
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
# Version : 1.0
# Last Updated: 07/05/2025
# ==========================================================
# Description:
# This script is part of the central ProxMenux VM creation module. It allows users
# to create virtual machines (VMs) in Proxmox VE using either default or advanced
# configurations, streamlining the deployment of Linux, Windows, and other systems.
#
# Key features:
# - Supports both virtual disk creation and physical disk passthrough.
# - Automates CPU, RAM, BIOS, network and storage configuration.
# - Provides a user-friendly menu to select OS type, ISO image and disk interface.
# - Automatically generates a detailed and styled HTML description for each VM.
#
# All operations are designed to simplify and accelerate VM creation in a
# consistent and maintainable way, using ProxMenux standards.
# ==========================================================
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
VM_REPO="$LOCAL_SCRIPTS/vm"
ISO_REPO="$LOCAL_SCRIPTS/vm"
MENU_REPO="$LOCAL_SCRIPTS/menus"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
[[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE"
source "$VM_REPO/vm_configurator.sh"
source "$VM_REPO/disk_selector.sh"
source "$VM_REPO/vm_creator.sh"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
function header_info() {
clear
show_proxmenux_logo
echo -e "${BL}╔═══════════════════════════════════════════════╗${CL}"
echo -e "${BL}║ ║${CL}"
echo -e "${BL}${YWB} ProxMenux VM Creator ${BL}${CL}"
echo -e "${BL}║ ║${CL}"
echo -e "${BL}╚═══════════════════════════════════════════════╝${CL}"
echo -e
}
# ==========================================================
# MAIN EXECUTION
# ==========================================================
header_info
echo -e "\n Loading..."
sleep 1
function start_vm_configuration() {
if (whiptail --title "ProxMenux" --yesno "$(translate "Use Default Settings?")" --no-button "$(translate "Advanced")" 10 60); then
header_info
load_default_vm_config "$OS_TYPE"
if [[ -z "$HN" ]]; then
HN=$(whiptail --inputbox "$(translate "Enter a name for the new virtual machine:")" 10 60 --title "VM Hostname" 3>&1 1>&2 2>&3)
[[ -z "$HN" ]] && HN="custom-vm"
fi
apply_default_vm_config
else
header_info
echo -e "${CUS}$(translate "Using advanced configuration")${CL}"
configure_vm_advanced "$OS_TYPE"
fi
}
while true; do
OS_TYPE=$(dialog --backtitle "ProxMenux" \
--title "$(translate "Select System Type")" \
--menu "\n$(translate "Choose the type of virtual system to install:")" 18 70 10 \
1 "$(translate "Create") VM System NAS" \
2 "$(translate "Create") VM System Windows" \
3 "$(translate "Create") VM System Linux" \
4 "$(translate "Create") VM System macOS (OSX-PROXMOX)" \
5 "$(translate "Create") VM System Others (based Linux)" \
6 "$(translate "Return to Main Menu")" \
3>&1 1>&2 2>&3)
[[ $? -ne 0 || "$OS_TYPE" == "5" ]] && exec bash "$MENU_REPO/main_menu.sh"
case "$OS_TYPE" in
1)
source "$ISO_REPO/select_nas_iso.sh" && select_nas_iso || continue
;;
2)
source "$ISO_REPO/select_windows_iso.sh" && select_windows_iso || continue
;;
3)
source "$ISO_REPO/select_linux_iso.sh" && select_linux_iso || continue
;;
4)
whiptail --title "OSX-PROXMOX" --yesno "$(translate "This is an external script that creates a macOS VM in Proxmox VE in just a few steps, whether you are using AMD or Intel hardware.")\n\n$(translate "The script clones the osx-proxmox.com repository and once the setup is complete, the server will automatically reboot.")\n\n$(translate "Make sure there are no critical services running as they will be interrupted. Ensure your server can be safely rebooted.")\n\n$(translate "Visit https://osx-proxmox.com for more information.")\n\n$(translate "Do you want to run the script now?")" 20 70
if [[ $? -eq 0 ]]; then
bash -c "$(curl -fsSL https://install.osx-proxmox.com)"
fi
continue
;;
5)
source "$ISO_REPO/select_linux_iso.sh" && select_linux_other_scripts || continue
;;
esac
if ! confirm_vm_creation; then
continue
fi
start_vm_configuration || continue
select_disk_type
if [[ -z "$DISK_TYPE" ]]; then
msg_error "$(translate "Disk type selection failed or cancelled")"
continue
fi
create_vm
break
done
function start_vm_configuration() {
if (whiptail --title "ProxMenux" --yesno "$(translate "Use Default Settings?")" --no-button "$(translate "Advanced")" 10 60); then
header_info
load_default_vm_config "$OS_TYPE"
if [[ -z "$HN" ]]; then
HN=$(whiptail --inputbox "$(translate "Enter a name for the new virtual machine:")" 10 60 --title "VM Hostname" 3>&1 1>&2 2>&3)
[[ -z "$HN" ]] && HN="custom-vm"
fi
apply_default_vm_config
else
header_info
echo -e "${CUS}$(translate "Using advanced configuration")${CL}"
configure_vm_advanced "$OS_TYPE"
fi
}

View File

@@ -24,132 +24,169 @@
# consistent and maintainable way, using ProxMenux standards.
# ==========================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOCAL_SCRIPTS_LOCAL="$(cd "$SCRIPT_DIR/.." && pwd)"
LOCAL_SCRIPTS_DEFAULT="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
LOCAL_SCRIPTS="$LOCAL_SCRIPTS_DEFAULT"
UTILS_FILE="$LOCAL_SCRIPTS/utils.sh"
if [[ -f "$LOCAL_SCRIPTS_LOCAL/utils.sh" ]]; then
LOCAL_SCRIPTS="$LOCAL_SCRIPTS_LOCAL"
UTILS_FILE="$LOCAL_SCRIPTS/utils.sh"
elif [[ ! -f "$UTILS_FILE" ]]; then
UTILS_FILE="$BASE_DIR/utils.sh"
fi
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ -f "$LOCAL_SCRIPTS_LOCAL/global/vm_storage_helpers.sh" ]]; then
source "$LOCAL_SCRIPTS_LOCAL/global/vm_storage_helpers.sh"
elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/vm_storage_helpers.sh" ]]; then
source "$LOCAL_SCRIPTS_DEFAULT/global/vm_storage_helpers.sh"
fi
load_language
initialize_cache
VIRTUAL_DISKS=()
IMPORT_DISKS=()
CONTROLLER_NVME_PCIS=()
PASSTHROUGH_DISKS=()
function _build_storage_plan_summary() {
local virtual_count="${#VIRTUAL_DISKS[@]}"
local import_count="${#IMPORT_DISKS[@]}"
local controller_count="${#CONTROLLER_NVME_PCIS[@]}"
local separator
local summary
separator="$(printf '%*s' 70 '' | tr ' ' '-')"
summary="$(translate "Current selection:")\n"
summary+=" - $(translate "Virtual disks"): $virtual_count\n"
summary+=" - $(translate "Import disks"): $import_count\n"
summary+=" - $(translate "Controllers + NVMe"): $controller_count\n"
summary+="${separator}\n\n"
echo -e "$summary"
}
function select_disk_type() {
DISK_TYPE=$(whiptail --backtitle "ProxMenux" --title "DISK TYPE" --menu "$(translate "Choose disk type:")" 12 58 2 \
"virtual" "$(translate "Create virtual disk")" \
"passthrough" "$(translate "Use physical disk passthrough")" \
--ok-button "Select" --cancel-button "Cancel" 3>&1 1>&2 2>&3)
VIRTUAL_DISKS=()
IMPORT_DISKS=()
CONTROLLER_NVME_PCIS=()
[[ -z "$DISK_TYPE" ]] && return 1
while true; do
local choice
choice=$(whiptail --backtitle "ProxMenux" --title "STORAGE PLAN" --menu "$(_build_storage_plan_summary)" 18 78 5 \
"1" "$(translate "Add virtual disk")" \
"2" "$(translate "Add import disk")" \
"3" "$(translate "Add Controller or NVMe (PCI passthrough)")" \
"r" "$(translate "Reset current storage selection")" \
"d" "$(translate "[ Finish and continue ]")" \
--ok-button "Select" --cancel-button "Cancel" 3>&1 1>&2 2>&3) || return 1
if [[ "$DISK_TYPE" == "virtual" ]]; then
select_virtual_disk
else
select_passthrough_disk
fi
case "$choice" in
1)
select_virtual_disk
;;
2)
select_import_disk
;;
3)
select_controller_nvme
;;
r)
VIRTUAL_DISKS=()
IMPORT_DISKS=()
CONTROLLER_NVME_PCIS=()
;;
d|done)
if [[ ${#VIRTUAL_DISKS[@]} -eq 0 && ${#IMPORT_DISKS[@]} -eq 0 && ${#CONTROLLER_NVME_PCIS[@]} -eq 0 ]]; then
continue
fi
if [[ ${#VIRTUAL_DISKS[@]} -gt 0 ]]; then
msg_ok "$(translate "Virtual Disks Created:")"
for i in "${!VIRTUAL_DISKS[@]}"; do
echo -e "${TAB}${BL}- $(translate "Disk") $((i+1)): ${VIRTUAL_DISKS[$i]}GB${CL}"
done
fi
if [[ ${#IMPORT_DISKS[@]} -gt 0 ]]; then
msg_ok "$(translate "Import Disks Selected:")"
for i in "${!IMPORT_DISKS[@]}"; do
local disk_info
disk_info=$(lsblk -ndo MODEL,SIZE "${IMPORT_DISKS[$i]}" 2>/dev/null | xargs)
echo -e "${TAB}${BL}- $(translate "Disk") $((i+1)): ${IMPORT_DISKS[$i]}${disk_info:+ ($disk_info)}${CL}"
done
fi
if [[ ${#CONTROLLER_NVME_PCIS[@]} -gt 0 ]]; then
msg_ok "$(translate "Controllers + NVMe Selected:")"
for i in "${!CONTROLLER_NVME_PCIS[@]}"; do
local pci_info
pci_info=$(lspci -nn -s "${CONTROLLER_NVME_PCIS[$i]#0000:}" 2>/dev/null | sed 's/^[^ ]* //')
echo -e "${TAB}${BL}- $(translate "Controller") $((i+1)): ${CONTROLLER_NVME_PCIS[$i]}${pci_info:+ ($pci_info)}${CL}"
done
fi
PASSTHROUGH_DISKS=("${IMPORT_DISKS[@]}")
DISK_TYPE="mixed"
export DISK_TYPE VIRTUAL_DISKS IMPORT_DISKS CONTROLLER_NVME_PCIS PASSTHROUGH_DISKS
return 0
;;
esac
done
}
# ==========================================================
# Select Virtual Disks
# ==========================================================
function select_virtual_disk() {
VIRTUAL_DISKS=()
local add_more_disks=true
while $add_more_disks; do
msg_info "Detecting available storage volumes..."
local STORAGE_MENU=()
local TAG TYPE FREE ITEM
while read -r line; do
TAG=$(echo $line | awk '{print $1}')
TYPE=$(echo $line | awk '{print $2}')
FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format "%.2f" | awk '{printf( "%9sB", $6)}')
ITEM=$(printf "%-15s %-10s %-15s" "$TAG" "$TYPE" "$FREE")
STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
done < <(pvesm status -content images | awk 'NR>1')
STORAGE_MENU=()
while read -r line; do
TAG=$(echo $line | awk '{print $1}')
TYPE=$(echo $line | awk '{print $2}')
FREE=$(echo $line | numfmt --field 4-6 --from-unit=K --to=iec --format "%.2f" | awk '{printf( "%9sB", $6)}')
ITEM=$(printf "%-15s %-10s %-15s" "$TAG" "$TYPE" "$FREE")
STORAGE_MENU+=("$TAG" "$ITEM" "OFF")
done < <(pvesm status -content images | awk 'NR>1')
VALID=$(pvesm status -content images | awk 'NR>1')
if [ -z "$VALID" ]; then
msg_error "Unable to detect a valid storage location."
sleep 2
select_disk_type
fi
if [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
STORAGE=${STORAGE_MENU[0]}
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
else
kill $SPINNER_PID > /dev/null
STORAGE=$(whiptail --backtitle "ProxMenuX" --title "$(translate "Select Storage Volume")" --radiolist \
"$(translate "Choose the storage volume for the virtual disk:\n")" 20 78 10 \
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ] || [ -z "$STORAGE" ]; then
if [ ${#VIRTUAL_DISKS[@]} -eq 0 ]; then
msg_error "No storage selected. At least one disk is required."
select_disk_type
else
add_more_disks=false
continue
fi
fi
fi
DISK_SIZE=$(whiptail --backtitle "ProxMenuX" --inputbox "$(translate "System Disk Size (GB)")" 8 58 32 --title "VIRTUAL DISK" --cancel-button Cancel 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
if [ ${#VIRTUAL_DISKS[@]} -eq 0 ]; then
msg_error "Disk size not specified. At least one disk is required."
sleep 2
select_disk_type
else
add_more_disks=false
continue
fi
fi
if [ -z "$DISK_SIZE" ]; then
DISK_SIZE="32"
fi
VIRTUAL_DISKS+=("${STORAGE}:${DISK_SIZE}")
if ! whiptail --backtitle "ProxMenuX" --title "$(translate "Add Another Disk")" \
--yesno "$(translate "Do you want to add another virtual disk?")" 8 58; then
add_more_disks=false
fi
done
if [ ${#VIRTUAL_DISKS[@]} -gt 0 ]; then
msg_ok "Virtual Disks Created:"
for i in "${!VIRTUAL_DISKS[@]}"; do
echo -e "${TAB}${BL}- Disk $((i+1)): ${VIRTUAL_DISKS[$i]}GB${CL}"
done
local VALID
VALID=$(pvesm status -content images | awk 'NR>1')
if [ -z "$VALID" ]; then
msg_error "Unable to detect a valid storage location."
return 1
fi
local STORAGE
if [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
STORAGE=${STORAGE_MENU[0]}
else
[[ -n "${SPINNER_PID:-}" ]] && kill "$SPINNER_PID" >/dev/null 2>&1
STORAGE=$(whiptail --backtitle "ProxMenuX" --title "$(translate "Select Storage Volume")" --radiolist \
"$(translate "Choose the storage volume for the virtual disk:\n")" 20 78 10 \
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
if [ $? -ne 0 ] || [ -z "$STORAGE" ]; then
return 0
fi
fi
local DISK_SIZE
cleanup
DISK_SIZE=$(whiptail --backtitle "ProxMenuX" --inputbox "$(translate "System Disk Size (GB)")" 8 58 32 --title "VIRTUAL DISK" --cancel-button Cancel 3>&1 1>&2 2>&3)
if [ $? -ne 0 ]; then
return 0
fi
if [ -z "$DISK_SIZE" ]; then
DISK_SIZE="32"
fi
VIRTUAL_DISKS+=("${STORAGE}:${DISK_SIZE}")
export VIRTUAL_DISKS
}
# ==========================================================
@@ -160,138 +197,164 @@ function select_virtual_disk() {
# ==========================================================
# Select Physical Disks
# Select Import Disks
# ==========================================================
function select_passthrough_disk() {
function select_import_disk() {
msg_info "$(translate "Detecting available disks...")"
FREE_DISKS=()
USED_DISKS=$(lsblk -n -o PKNAME,TYPE | grep 'lvm' | awk '{print "/dev/" $1}')
MOUNTED_DISKS=$(lsblk -ln -o NAME,MOUNTPOINT | awk '$2!="" {print "/dev/" $1}')
ZFS_DISKS=""
ZFS_RAW=$(zpool list -v -H 2>/dev/null | awk '{print $1}' | grep -v '^NAME$' | grep -v '^-' | grep -v '^mirror')
for entry in $ZFS_RAW; do
path=""
if [[ "$entry" == wwn-* || "$entry" == ata-* ]]; then
if [ -e "/dev/disk/by-id/$entry" ]; then
path=$(readlink -f "/dev/disk/by-id/$entry")
fi
elif [[ "$entry" == /dev/* ]]; then
path="$entry"
fi
if [ -n "$path" ]; then
base_disk=$(lsblk -no PKNAME "$path" 2>/dev/null)
if [ -n "$base_disk" ]; then
ZFS_DISKS+="/dev/$base_disk"$'\n'
fi
fi
done
ZFS_DISKS=$(echo "$ZFS_DISKS" | sort -u)
LVM_DEVICES=$(pvs --noheadings -o pv_name 2> >(grep -v 'File descriptor .* leaked') | xargs -n1 readlink -f | sort -u)
RAID_ACTIVE=$(grep -Po 'md\d+\s*:\s*active\s+raid[0-9]+' /proc/mdstat | awk '{print $1}' | sort -u)
_refresh_host_storage_cache
local FREE_DISKS=()
local DISK INFO MODEL SIZE LABEL DESCRIPTION
while read -r DISK; do
[[ "$DISK" =~ /dev/zd ]] && continue
[[ "$DISK" =~ /dev/zd ]] && continue
if _disk_is_host_system_used "$DISK"; then
continue
fi
INFO=($(lsblk -dn -o MODEL,SIZE "$DISK"))
MODEL="${INFO[@]::${#INFO[@]}-1}"
SIZE="${INFO[-1]}"
LABEL=""
SHOW_DISK=true
INFO=($(lsblk -dn -o MODEL,SIZE "$DISK"))
MODEL="${INFO[@]::${#INFO[@]}-1}"
SIZE="${INFO[-1]}"
LABEL=""
IS_MOUNTED=false
IS_RAID=false
IS_ZFS=false
IS_LVM=false
if _disk_used_in_guest_configs "$DISK"; then
LABEL+=" [⚠ $(translate "In use by VM/LXC config")]"
fi
while read -r part fstype; do
[[ "$fstype" == "zfs_member" ]] && IS_ZFS=true
[[ "$fstype" == "linux_raid_member" ]] && IS_RAID=true
[[ "$fstype" == "LVM2_member" ]] && IS_LVM=true
if grep -q "/dev/$part" <<< "$MOUNTED_DISKS"; then
IS_MOUNTED=true
fi
done < <(lsblk -ln -o NAME,FSTYPE "$DISK" | tail -n +2)
REAL_PATH=$(readlink -f "$DISK")
if echo "$LVM_DEVICES" | grep -qFx "$REAL_PATH"; then
IS_MOUNTED=true
fi
USED_BY=""
REAL_PATH=$(readlink -f "$DISK")
CONFIG_DATA=$(cat /etc/pve/qemu-server/*.conf /etc/pve/lxc/*.conf 2>/dev/null)
if grep -Fq "$REAL_PATH" <<< "$CONFIG_DATA"; then
USED_BY="$(translate "In use")"
else
for SYMLINK in /dev/disk/by-id/*; do
if [[ "$(readlink -f "$SYMLINK")" == "$REAL_PATH" ]]; then
if grep -Fq "$SYMLINK" <<< "$CONFIG_DATA"; then
USED_BY="$(translate "In use")"
break
fi
fi
done
fi
if $IS_RAID && grep -q "$DISK" <<< "$(cat /proc/mdstat)" && grep -q "active raid" /proc/mdstat; then
SHOW_DISK=false
fi
if $IS_ZFS || $IS_MOUNTED || [[ "$ZFS_DISKS" == *"$DISK"* ]]; then
SHOW_DISK=false
fi
if $SHOW_DISK; then
[[ -n "$USED_BY" ]] && LABEL+=" [$USED_BY]"
[[ "$IS_RAID" == true ]] && LABEL+=" ⚠ RAID"
[[ "$IS_LVM" == true ]] && LABEL+=" ⚠ LVM"
[[ "$IS_ZFS" == true ]] && LABEL+=" ⚠ ZFS"
DESCRIPTION=$(printf "%-30s %10s%s" "$MODEL" "$SIZE" "$LABEL")
FREE_DISKS+=("$DISK" "$DESCRIPTION" "OFF")
fi
DESCRIPTION=$(printf "%-30s %10s%s" "$MODEL" "$SIZE" "$LABEL")
if _array_contains "$DISK" "${IMPORT_DISKS[@]}"; then
FREE_DISKS+=("$DISK" "$DESCRIPTION" "ON")
else
FREE_DISKS+=("$DISK" "$DESCRIPTION" "OFF")
fi
done < <(lsblk -dn -e 7,11 -o PATH)
if [ "${#FREE_DISKS[@]}" -eq 0 ]; then
if [[ "${#FREE_DISKS[@]}" -eq 0 ]]; then
cleanup
whiptail --title "Error" --msgbox "$(translate "No disks available for this VM.")" 8 40
select_disk_type
return
whiptail --title "Error" --msgbox "$(translate "No importable disks available. System disks and protected disks are hidden.")" 9 70
return 1
fi
local MAX_WIDTH TOTAL_WIDTH SELECTED_DISKS
MAX_WIDTH=$(printf "%s\n" "${FREE_DISKS[@]}" | awk '{print length}' | sort -nr | head -n1)
TOTAL_WIDTH=$((MAX_WIDTH + 20))
[ $TOTAL_WIDTH -lt 50 ] && TOTAL_WIDTH=50
TOTAL_WIDTH=$((MAX_WIDTH + 20))
[[ $TOTAL_WIDTH -lt 50 ]] && TOTAL_WIDTH=50
cleanup
SELECTED_DISKS=$(whiptail --title "Select Disks" --checklist \
"$(translate "Select the disks you want to use (use spacebar to select):")" 20 $TOTAL_WIDTH 10 \
SELECTED_DISKS=$(whiptail --title "Select Import Disks" --checklist \
"$(translate "Select the disks you want to import (use spacebar to toggle):")" 20 $TOTAL_WIDTH 10 \
"${FREE_DISKS[@]}" 3>&1 1>&2 2>&3)
if [ -z "$SELECTED_DISKS" ]; then
msg_error "Disk not specified. At least one disk is required."
sleep 2
select_disk_type
return
fi
[[ $? -ne 0 ]] && return 1
msg_ok "Disk passthrough selected:"
PASSTHROUGH_DISKS=()
IMPORT_DISKS=()
local DISK_INFO
for DISK in $(echo "$SELECTED_DISKS" | tr -d '"'); do
DISK_INFO=$(lsblk -ndo MODEL,SIZE "$DISK" | xargs)
echo -e "${TAB}${CL}${BL}- $DISK $DISK_INFO${GN}${CL}"
PASSTHROUGH_DISKS+=("$DISK")
_array_contains "$DISK" "${IMPORT_DISKS[@]}" || IMPORT_DISKS+=("$DISK")
done
if [[ ${#IMPORT_DISKS[@]} -eq 0 ]]; then
msg_warn "$(translate "No import disks selected for now.")"
return 0
fi
export IMPORT_DISKS
return 0
}
# ==========================================================
function select_passthrough_disk() {
select_import_disk
}
function select_controller_nvme() {
msg_info "$(translate "Detecting PCI storage controllers and NVMe devices...")"
_refresh_host_storage_cache
local menu_items=()
local blocked_report=""
local pci_path pci_full class_hex name controller_disks controller_desc disk reason safe_count blocked_count state
safe_count=0
blocked_count=0
while IFS= read -r pci_path; do
pci_full=$(basename "$pci_path")
class_hex=$(cat "$pci_path/class" 2>/dev/null | sed 's/^0x//')
[[ -z "$class_hex" ]] && continue
[[ "${class_hex:0:2}" != "01" ]] && continue
name=$(lspci -nn -s "${pci_full#0000:}" 2>/dev/null | sed 's/^[^ ]* //')
[[ -z "$name" ]] && name="$(translate "Unknown storage controller")"
controller_disks=()
while IFS= read -r disk; do
[[ -z "$disk" ]] && continue
_array_contains "$disk" "${controller_disks[@]}" || controller_disks+=("$disk")
done < <(_controller_block_devices "$pci_full")
reason=""
for disk in "${controller_disks[@]}"; do
if _disk_is_host_system_used "$disk"; then
reason+="${disk} (${DISK_USAGE_REASON}); "
elif _disk_used_in_guest_configs "$disk"; then
reason+="${disk} ($(translate "In use by VM/LXC config")); "
fi
done
if [[ -n "$reason" ]]; then
blocked_count=$((blocked_count + 1))
blocked_report+="${pci_full}${name}\n $(translate "Blocked because protected/in-use disks are attached"): ${reason}\n"
continue
fi
if [[ ${#controller_disks[@]} -gt 0 ]]; then
controller_desc="$(printf "%-50s [%s]" "$name" "$(IFS=,; echo "${controller_disks[*]}")")"
else
controller_desc="$(printf "%-50s [%s]" "$name" "$(translate "No attached disks detected")")"
fi
if _array_contains "$pci_full" "${CONTROLLER_NVME_PCIS[@]}"; then
state="ON"
else
state="OFF"
fi
menu_items+=("$pci_full" "$controller_desc" "$state")
safe_count=$((safe_count + 1))
done < <(ls -d /sys/bus/pci/devices/* 2>/dev/null | sort)
stop_spinner
if [[ $safe_count -eq 0 ]]; then
local msg
msg="$(translate "No safe controllers/NVMe devices are available for passthrough.")\n\n"
if [[ $blocked_count -gt 0 ]]; then
msg+="$(translate "Detected controllers blocked for safety:")\n\n${blocked_report}"
fi
whiptail --title "Controller + NVMe" --msgbox "$msg" 20 90
return 1
fi
if [[ $blocked_count -gt 0 ]]; then
whiptail --title "Controller + NVMe" --msgbox "$(translate "Some controllers were hidden because they have host system disks attached.")\n\n${blocked_report}" 20 90
fi
local selected
selected=$(whiptail --title "Controller + NVMe" --checklist \
"$(translate "Select controllers/NVMe to passthrough (safe devices only):")" 20 90 10 \
"${menu_items[@]}" 3>&1 1>&2 2>&3)
[[ $? -ne 0 ]] && return 1
CONTROLLER_NVME_PCIS=()
local pci
for pci in $(echo "$selected" | tr -d '"'); do
_array_contains "$pci" "${CONTROLLER_NVME_PCIS[@]}" || CONTROLLER_NVME_PCIS+=("$pci")
done
if [[ ${#CONTROLLER_NVME_PCIS[@]} -eq 0 ]]; then
msg_warn "$(translate "No Controller/NVMe selected for now.")"
return 0
fi
export CONTROLLER_NVME_PCIS
return 0
}
# ==========================================================

View File

@@ -101,27 +101,27 @@ function select_linux_iso() {
function select_linux_iso_official() {
DISTROS=(
"Ubuntu 25.04|Desktop|ProxMenux|https://releases.ubuntu.com/25.04/ubuntu-25.04-desktop-amd64.iso"
"Ubuntu 24.04|Desktop|ProxMenux|https://releases.ubuntu.com/24.04/ubuntu-24.04.2-desktop-amd64.iso"
"Ubuntu 25.10|Desktop|ProxMenux|https://releases.ubuntu.com/25.10/ubuntu-25.10-desktop-amd64.iso"
"Ubuntu 24.04|Desktop|ProxMenux|https://releases.ubuntu.com/24.04/ubuntu-24.04.4-desktop-amd64.iso"
"Ubuntu 22.04|Desktop|ProxMenux|https://releases.ubuntu.com/22.04/ubuntu-22.04.5-desktop-amd64.iso"
"Ubuntu 20.04|Desktop|ProxMenux|https://releases.ubuntu.com/20.04/ubuntu-20.04.6-desktop-amd64.iso"
"Ubuntu 25.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/25.04/ubuntu-25.04-live-server-amd64.iso"
"Ubuntu 24.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/24.04/ubuntu-24.04.2-live-server-amd64.iso"
"Ubuntu 25.10 Server|CLI|ProxMenux|https://releases.ubuntu.com/25.10/ubuntu-25.10-live-server-amd64.iso"
"Ubuntu 24.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/24.04/ubuntu-24.04.4-live-server-amd64.iso"
"Ubuntu 22.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso"
"Ubuntu 20.04 Server|CLI|ProxMenux|https://releases.ubuntu.com/20.04/ubuntu-20.04.6-live-server-amd64.iso"
"Debian 13|Desktop|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-13.0.0-amd64-DVD-1.iso"
"Debian 12|Desktop|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-12.10.0-amd64-DVD-1.iso"
"Debian 13|Desktop|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-13.4.0-amd64-DVD-1.iso"
"Debian 12|Desktop|ProxMenux|https://cdimage.debian.org/cdimage/archive/12.13.0/amd64/iso-dvd/debian-12.13.0-amd64-DVD-1.iso"
"Debian 11|Desktop|ProxMenux|https://cdimage.debian.org/cdimage/archive/11.11.0/amd64/iso-dvd/debian-11.11.0-amd64-DVD-1.iso"
"Debian 13 Netinst|CLI|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-13.0.0-amd64-netinst.iso"
"Debian 12 Netinst|CLI|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-12.10.0-amd64-netinst.iso"
"Debian 13 Netinst|CLI|ProxMenux|https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/debian-13.4.0-amd64-netinst.iso"
"Debian 12 Netinst|CLI|ProxMenux|https://cdimage.debian.org/cdimage/archive/12.13.0/amd64/iso-cd/debian-12.13.0-amd64-netinst.iso"
"Debian 11 Netinst|CLI|ProxMenux|https://cdimage.debian.org/cdimage/archive/11.11.0/amd64/iso-cd/debian-11.11.0-amd64-netinst.iso"
"Fedora Workstation 42|Desktop|ProxMenux|https://download.fedoraproject.org/pub/fedora/linux/releases/42/Workstation/x86_64/iso/Fedora-Workstation-Live-42-1.1.x86_64.iso"
"Arch Linux|CLI|ProxMenux|https://geo.mirror.pkgbuild.com/iso/2025.07.01/archlinux-2025.07.01-x86_64.iso"
"Rocky Linux 9.5|Desktop|ProxMenux|https://download.rockylinux.org/pub/rocky/9/isos/x86_64/Rocky-9.5-x86_64-dvd.iso"
"Arch Linux|CLI|ProxMenux|https://geo.mirror.pkgbuild.com/iso/latest/archlinux-x86_64.iso"
"Rocky Linux 9|Desktop|ProxMenux|https://download.rockylinux.org/pub/rocky/9/isos/x86_64/Rocky-9-latest-x86_64-dvd.iso"
"Linux Mint 22.1|Desktop|ProxMenux|https://mirrors.edge.kernel.org/linuxmint/stable/22.1/linuxmint-22.1-cinnamon-64bit.iso"
"openSUSE Leap 15.6|Desktop|ProxMenux|https://download.opensuse.org/distribution/leap/15.6/iso/openSUSE-Leap-15.6-DVD-x86_64-Media.iso"
"Alpine Linux 3.21|Desktop|ProxMenux|https://dl-cdn.alpinelinux.org/alpine/v3.21/releases/x86_64/alpine-virt-3.21.3-x86_64.iso"
"Kali Linux 2025.1|Desktop|ProxMenux|https://cdimage.kali.org/kali-2025.1c/kali-linux-2025.1c-installer-amd64.iso"
"Kali Linux 2026.1|Desktop|ProxMenux|https://cdimage.kali.org/kali-2026.1/kali-linux-2026.1-installer-amd64.iso"
"Manjaro 25.0|Desktop|ProxMenux|https://download.manjaro.org/gnome/25.0.0/manjaro-gnome-25.0.0-250414-linux612.iso"
)
@@ -219,7 +219,7 @@ function select_linux_cloudinit() {
whiptail --title "Proxmox VE Helper-Scripts" \
--msgbox "$(translate "Visit the website to discover more scripts, stay updated with the latest updates, and support the project:\n\nhttps://community-scripts.github.io/ProxmoxVE")" 15 70
exec bash "$LOCAL_SCRIPTS/vm/create_vm.sh"
exec bash "$LOCAL_SCRIPTS/menus/create_vm_menu.sh"
}
@@ -311,5 +311,3 @@ return 1

View File

@@ -7,7 +7,7 @@
# Copyright : (c) 2024 MacRimi
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
# Version : 1.0
# Last Updated: 07/05/2025
# Last Updated: 04/04/2026
# ==========================================================
# Description:
# This script is part of the central ProxMenux VM creation module. It allows users
@@ -24,11 +24,20 @@
# consistent and maintainable way, using ProxMenux standards.
# ==========================================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOCAL_SCRIPTS_LOCAL="$(cd "$SCRIPT_DIR/.." && pwd)"
LOCAL_SCRIPTS_DEFAULT="/usr/local/share/proxmenux/scripts"
LOCAL_SCRIPTS="$LOCAL_SCRIPTS_DEFAULT"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
UTILS_FILE="$LOCAL_SCRIPTS/utils.sh"
VENV_PATH="/opt/googletrans-env"
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
if [[ -f "$LOCAL_SCRIPTS_LOCAL/utils.sh" ]]; then
LOCAL_SCRIPTS="$LOCAL_SCRIPTS_LOCAL"
UTILS_FILE="$LOCAL_SCRIPTS/utils.sh"
elif [[ ! -f "$UTILS_FILE" ]]; then
UTILS_FILE="$BASE_DIR/utils.sh"
fi
[[ -f "$UTILS_FILE" ]] && source "$UTILS_FILE"
load_language
@@ -37,6 +46,138 @@ initialize_cache
ISO_DIR="/var/lib/vz/template/iso"
mkdir -p "$ISO_DIR"
function _has_curl() {
command -v curl >/dev/null 2>&1
}
function _latest_version_from_lines() {
awk 'NF' | sort -V | tail -n 1
}
function resolve_truenas_scale_iso() {
local default_ver="25.10.2.1"
local base_url="https://download.sys.truenas.net/TrueNAS-SCALE-Goldeye"
local detected_ver=""
if _has_curl; then
detected_ver=$(
curl -fsSL "${base_url}/" 2>/dev/null \
| grep -Eo '>[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?/' \
| tr -d '>/' \
| _latest_version_from_lines
)
fi
[[ -z "$detected_ver" ]] && detected_ver="$default_ver"
ISO_NAME="TrueNAS SCALE ${detected_ver} (Goldeye)"
ISO_FILE="TrueNAS-SCALE-${detected_ver}.iso"
ISO_URL="${base_url}/${detected_ver}/${ISO_FILE}"
ISO_PATH="$ISO_DIR/$ISO_FILE"
HN="TrueNAS-Scale"
}
function resolve_truenas_core_iso() {
local default_file="TrueNAS-13.3-U1.2.iso"
local detected_file=""
local base_url="https://download.freenas.org/13.3/STABLE/latest/x64"
if _has_curl; then
detected_file=$(
curl -fsSL "${base_url}/" 2>/dev/null \
| grep -Eo 'TrueNAS-13\.3-[^"]+\.iso' \
| head -n 1
)
fi
[[ -z "$detected_file" ]] && detected_file="$default_file"
ISO_NAME="TrueNAS CORE 13.3"
ISO_FILE="$detected_file"
ISO_URL="${base_url}/${ISO_FILE}"
ISO_PATH="$ISO_DIR/$ISO_FILE"
HN="TrueNAS-Core"
}
function resolve_omv_iso() {
local default_ver="8.1.1"
local detected_ver=""
if _has_curl; then
detected_ver=$(
curl -fsSL "https://sourceforge.net/projects/openmediavault/files/iso/" 2>/dev/null \
| grep -Eo '/projects/openmediavault/files/iso/[0-9]+\.[0-9]+\.[0-9]+/' \
| sed -E 's|.*/iso/([0-9]+\.[0-9]+\.[0-9]+)/$|\1|' \
| _latest_version_from_lines
)
fi
[[ -z "$detected_ver" ]] && detected_ver="$default_ver"
ISO_NAME="OpenMediaVault ${detected_ver}"
ISO_FILE="openmediavault_${detected_ver}-amd64.iso"
ISO_URL="https://sourceforge.net/projects/openmediavault/files/iso/${detected_ver}/${ISO_FILE}/download"
ISO_PATH="$ISO_DIR/$ISO_FILE"
HN="OpenMediaVault"
}
function resolve_xigmanas_iso() {
local default_train="14.3.0.5"
local default_build="14.3.0.5.10566"
local detected_train=""
local detected_build=""
if _has_curl; then
detected_train=$(
curl -fsSL "https://sourceforge.net/projects/xigmanas/files/" 2>/dev/null \
| grep -Eo '/projects/xigmanas/files/XigmaNAS-[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/' \
| sed -E 's|.*/XigmaNAS-([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/$|\1|' \
| _latest_version_from_lines
)
fi
[[ -z "$detected_train" ]] && detected_train="$default_train"
if _has_curl; then
detected_build=$(
curl -fsSL "https://sourceforge.net/projects/xigmanas/files/XigmaNAS-${detected_train}/" 2>/dev/null \
| grep -Eo "/projects/xigmanas/files/XigmaNAS-${detected_train}/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/" \
| sed -E "s|.*/XigmaNAS-${detected_train}/([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/$|\\1|" \
| _latest_version_from_lines
)
fi
[[ -z "$detected_build" ]] && detected_build="$default_build"
ISO_NAME="XigmaNAS-${detected_train}"
ISO_FILE="XigmaNAS-x64-LiveCD-${detected_build}.iso"
ISO_URL="https://sourceforge.net/projects/xigmanas/files/XigmaNAS-${detected_train}/${detected_build}/${ISO_FILE}/download"
ISO_PATH="$ISO_DIR/$ISO_FILE"
HN="XigmaNAS"
}
function resolve_rockstor_iso() {
local default_file="Rockstor-Leap15.6-generic.x86_64-5.0.15-0.install.iso"
local detected_file=""
local base_url="https://rockstor.com/downloads/installer/leap/15.6/x86_64"
if _has_curl; then
detected_file=$(
curl -fsSL "${base_url}/" 2>/dev/null \
| grep -Eo 'Rockstor-Leap15\.6-generic\.x86_64-[0-9]+\.[0-9]+\.[0-9]+-[0-9]+\.install\.iso' \
| _latest_version_from_lines
)
fi
[[ -z "$detected_file" ]] && detected_file="$default_file"
ISO_NAME="Rockstor"
ISO_FILE="$detected_file"
ISO_URL="${base_url}/${ISO_FILE}"
ISO_PATH="$ISO_DIR/$ISO_FILE"
HN="Rockstor"
}
function select_nas_iso() {
local NAS_OPTIONS=(
@@ -68,39 +209,19 @@ function select_nas_iso() {
return 1
;;
2)
ISO_NAME="TrueNAS SCALE 25 (Goldeye)"
ISO_URL="https://download.sys.truenas.net/TrueNAS-SCALE-Goldeye/25.10.0.1/TrueNAS-SCALE-25.10.0.1.iso"
ISO_FILE="TrueNAS-SCALE-25.10.0.1.iso"
ISO_PATH="$ISO_DIR/$ISO_FILE"
HN="TrueNAS-Scale"
resolve_truenas_scale_iso
;;
3)
ISO_NAME="TrueNAS CORE 13.3"
ISO_URL="https://download.freenas.org/13.3/STABLE/latest/x64/TrueNAS-13.3-U1.2.iso"
ISO_FILE="TrueNAS-13.3-U1.2.iso"
ISO_PATH="$ISO_DIR/$ISO_FILE"
HN="TrueNAS-Core"
resolve_truenas_core_iso
;;
4)
ISO_NAME="OpenMediaVault 7.4.17"
ISO_URL="https://sourceforge.net/projects/openmediavault/files/iso/7.4.17/openmediavault_7.4.17-amd64.iso/download"
ISO_FILE="openmediavault_7.4.17-amd64.iso"
ISO_PATH="$ISO_DIR/$ISO_FILE"
HN="OpenMediaVault"
resolve_omv_iso
;;
5)
ISO_NAME="XigmaNAS-14.3.0.5"
ISO_URL="https://sourceforge.net/projects/xigmanas/files/XigmaNAS-14.3.0.5/14.3.0.5.10566/XigmaNAS-x64-LiveCD-14.3.0.5.10566.iso/download"
ISO_FILE="XigmaNAS-x64-LiveCD-14.3.0.5.10566.iso"
ISO_PATH="$ISO_DIR/$ISO_FILE"
HN="XigmaNAS"
resolve_xigmanas_iso
;;
6)
ISO_NAME="Rockstor"
ISO_URL="https://rockstor.com/downloads/installer/leap/15.6/x86_64/Rockstor-Leap15.6-generic.x86_64-5.0.15-0.install.iso"
ISO_FILE="Rockstor-Leap15.6-generic.x86_64-5.0.15-0.install.iso"
ISO_PATH="$ISO_DIR/$ISO_FILE"
HN="Rockstor"
resolve_rockstor_iso
;;
7)
bash "$LOCAL_SCRIPTS/vm/zimaos.sh"

File diff suppressed because it is too large Load Diff

View File

@@ -24,15 +24,35 @@
# consistent and maintainable way, using ProxMenux standards.
# ==========================================================
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOCAL_SCRIPTS_LOCAL="$(cd "$SCRIPT_DIR/.." && pwd)"
LOCAL_SCRIPTS_DEFAULT="/usr/local/share/proxmenux/scripts"
LOCAL_SCRIPTS="$LOCAL_SCRIPTS_DEFAULT"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
UTILS_FILE="$LOCAL_SCRIPTS/utils.sh"
if [[ -f "$LOCAL_SCRIPTS_LOCAL/utils.sh" ]]; then
LOCAL_SCRIPTS="$LOCAL_SCRIPTS_LOCAL"
UTILS_FILE="$LOCAL_SCRIPTS/utils.sh"
elif [[ ! -f "$UTILS_FILE" ]]; then
UTILS_FILE="$BASE_DIR/utils.sh"
fi
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ -f "$LOCAL_SCRIPTS_LOCAL/global/vm_storage_helpers.sh" ]]; then
source "$LOCAL_SCRIPTS_LOCAL/global/vm_storage_helpers.sh"
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/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
source "$LOCAL_SCRIPTS_DEFAULT/global/pci_passthrough_helpers.sh"
fi
load_language
initialize_cache
@@ -67,7 +87,10 @@ function select_interface_type() {
"sata" "$(translate "SATA (standard - high compatibility)")" OFF \
"virtio" "$(translate "VirtIO (advanced - high performance)")" OFF \
"ide" "IDE (legacy)" OFF \
3>&1 1>&2 2>&3) || exit 1
3>&1 1>&2 2>&3) || {
msg_warn "$(translate "Disk interface selection cancelled.")" >&2
return 1
}
case "$INTERFACE_TYPE" in
"scsi"|"sata")
@@ -102,17 +125,21 @@ function select_storage_target() {
done < <(pvesm status -content images | awk 'NR>1')
if [[ ${#STORAGE_MENU[@]} -eq 0 ]]; then
msg_error "$(translate "Unable to detect a valid storage location for $PURPOSE disk.")"
exit 1
msg_error "$(translate "Unable to detect a valid storage location for $PURPOSE disk.")" >&2
return 1
elif [[ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]]; then
STORAGE="${STORAGE_MENU[0]}"
else
kill $SPINNER_PID > /dev/null
[[ -n "${SPINNER_PID:-}" ]] && kill "$SPINNER_PID" >/dev/null 2>&1
STORAGE=$(whiptail --backtitle "ProxMenux" --title "$(translate "$PURPOSE Disk Storage")" --radiolist \
"$(translate "Choose the storage volume for the $PURPOSE disk (4MB):\n\nUse Spacebar to select.")" 16 70 6 \
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit 1
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || {
msg_warn "$(translate "$PURPOSE disk storage selection cancelled.")" >&2
return 1
}
fi
[[ -z "$STORAGE" ]] && return 1
echo "$STORAGE"
}
@@ -141,6 +168,35 @@ function configure_guest_agent() {
}
function run_gpu_passthrough_wizard() {
[[ "${WIZARD_ADD_GPU:-no}" != "yes" ]] && return 0
local gpu_script="$LOCAL_SCRIPTS/gpu_tpu/add_gpu_vm.sh"
local wizard_result_file=""
if [[ ! -f "$gpu_script" ]]; then
local local_gpu_script
local_gpu_script="$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)/gpu_tpu/add_gpu_vm.sh"
[[ -f "$local_gpu_script" ]] && gpu_script="$local_gpu_script"
fi
if [[ ! -f "$gpu_script" ]]; then
msg_warn "$(translate "GPU passthrough assistant not found. You can run it later from Hardware Graphics.")"
return 0
fi
msg_info2 "$(translate "Launching GPU passthrough assistant for VM") ${VMID}..."
wizard_result_file="/tmp/proxmenux_gpu_wizard_result_${VMID}_$$.txt"
: >"$wizard_result_file"
bash "$gpu_script" --vmid "$VMID" --wizard --result-file "$wizard_result_file"
if [[ -s "$wizard_result_file" ]]; then
WIZARD_GPU_RESULT=$(head -n1 "$wizard_result_file" | tr -d '\r\n')
else
WIZARD_GPU_RESULT="cancelled"
fi
rm -f "$wizard_result_file"
}
@@ -154,7 +210,6 @@ function create_vm() {
local ISO_DIR="/var/lib/vz/template/iso"
if [[ -n "$ISO_PATH" && -n "$ISO_URL" && ! -f "$ISO_PATH" ]]; then
if [[ "$ISO_URL" == *"sourceforge.net"* ]]; then
@@ -170,7 +225,7 @@ function create_vm() {
msg_ok "$(translate "ISO image downloaded")"
else
msg_error "$(translate "Failed to download ISO image")"
return
return 1
fi
fi
@@ -180,6 +235,13 @@ function create_vm() {
GUEST_OS_TYPE="l26"
fi
local VM_TAGS="proxmenux"
case "${OS_TYPE:-}" in
1) VM_TAGS="proxmenux,nas" ;;
2) VM_TAGS="proxmenux,windows" ;;
*) VM_TAGS="proxmenux,linux" ;;
esac
# qm create "$VMID" -agent 1${MACHINE} -tablet 0 -localtime 1${BIOS_TYPE}${CPU_TYPE} \
@@ -195,7 +257,7 @@ qm create "$VMID" \
-cores "$CORE_COUNT" \
-memory "$RAM_SIZE" \
-name "$HN" \
-tags proxmenux \
-tags "$VM_TAGS" \
-net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" \
-ostype "$GUEST_OS_TYPE" \
-scsihw virtio-scsi-pci \
@@ -213,7 +275,10 @@ fi
if [[ "$BIOS_TYPE" == *"ovmf"* ]]; then
msg_info "$(translate "Configuring EFI disk")"
EFI_STORAGE=$(select_storage_target "EFI" "$VMID")
if ! EFI_STORAGE=$(select_storage_target "EFI" "$VMID"); then
msg_error "$(translate "EFI storage selection failed or was cancelled. VM creation aborted.")"
return 1
fi
STORAGE_TYPE=$(pvesm status -storage "$EFI_STORAGE" | awk 'NR>1 {print $2}')
EFI_DISK_ID="efidisk0"
EFI_KEYS="0"
@@ -249,7 +314,10 @@ fi
if [[ "$OS_TYPE" == "2" ]]; then
msg_info "$(translate "Configuring TPM device")"
TPM_STORAGE=$(select_storage_target "TPM" "$VMID")
if ! TPM_STORAGE=$(select_storage_target "TPM" "$VMID"); then
msg_error "$(translate "TPM storage selection failed or was cancelled. VM creation aborted.")"
return 1
fi
STORAGE_TYPE=$(pvesm status -storage "$TPM_STORAGE" | awk 'NR>1 {print $2}')
TPM_ID="tpmstate0"
@@ -282,18 +350,31 @@ fi
# ==========================================================
# Create Diks
# Create Disks / Import Disks / Controller + NVMe
# ==========================================================
local -a EFFECTIVE_IMPORT_DISKS=()
if [[ ${#IMPORT_DISKS[@]} -gt 0 ]]; then
EFFECTIVE_IMPORT_DISKS=("${IMPORT_DISKS[@]}")
elif [[ ${#PASSTHROUGH_DISKS[@]} -gt 0 ]]; then
EFFECTIVE_IMPORT_DISKS=("${PASSTHROUGH_DISKS[@]}")
fi
select_interface_type
if [[ ${#VIRTUAL_DISKS[@]} -gt 0 || ${#EFFECTIVE_IMPORT_DISKS[@]} -gt 0 ]]; then
if ! select_interface_type; then
msg_error "$(translate "Disk interface is required to continue VM creation.")"
return 1
fi
fi
if [[ "$DISK_TYPE" == "virtual" && ${#VIRTUAL_DISKS[@]} -gt 0 ]]; then
local NEXT_DISK_SLOT=0
if [[ ${#VIRTUAL_DISKS[@]} -gt 0 ]]; then
for i in "${!VIRTUAL_DISKS[@]}"; do
DISK_INDEX=$((i+1))
DISK_INDEX=$((NEXT_DISK_SLOT+1))
IFS=':' read -r STORAGE SIZE <<< "${VIRTUAL_DISKS[$i]}"
DISK_NAME="vm-${VMID}-disk-${DISK_INDEX}"
SLOT_NAME="${INTERFACE_TYPE}${i}"
SLOT_NAME="${INTERFACE_TYPE}${NEXT_DISK_SLOT}"
STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}')
case "$STORAGE_TYPE" in
@@ -313,6 +394,7 @@ select_interface_type
msg_ok "$(translate "Virtual disk") $DISK_INDEX ${SIZE}GB - $STORAGE ($SLOT_NAME)"
DISK_INFO+="<p>Virtual Disk $DISK_INDEX: ${SIZE}GB ($STORAGE / $SLOT_NAME)</p>"
[[ -z "$BOOT_ORDER" ]] && BOOT_ORDER="$SLOT_NAME"
NEXT_DISK_SLOT=$((NEXT_DISK_SLOT + 1))
else
msg_error "$(translate "Failed to assign virtual disk") $DISK_INDEX"
fi
@@ -325,6 +407,7 @@ select_interface_type
msg_ok "$(translate "Virtual disk") $DISK_INDEX ${SIZE}GB - $STORAGE ($SLOT_NAME)"
DISK_INFO+="<p>Virtual Disk $DISK_INDEX: ${SIZE}GB ($STORAGE / $SLOT_NAME)</p>"
[[ -z "$BOOT_ORDER" ]] && BOOT_ORDER="$SLOT_NAME"
NEXT_DISK_SLOT=$((NEXT_DISK_SLOT + 1))
else
msg_error "$(translate "Failed to create disk") $DISK_INDEX"
fi
@@ -334,19 +417,59 @@ select_interface_type
if [[ "$DISK_TYPE" == "passthrough" && ${#PASSTHROUGH_DISKS[@]} -gt 0 ]]; then
for i in "${!PASSTHROUGH_DISKS[@]}"; do
SLOT_NAME="${INTERFACE_TYPE}${i}"
DISK="${PASSTHROUGH_DISKS[$i]}"
if [[ ${#EFFECTIVE_IMPORT_DISKS[@]} -gt 0 ]]; then
for i in "${!EFFECTIVE_IMPORT_DISKS[@]}"; do
SLOT_NAME="${INTERFACE_TYPE}${NEXT_DISK_SLOT}"
DISK="${EFFECTIVE_IMPORT_DISKS[$i]}"
MODEL=$(lsblk -ndo MODEL "$DISK")
SIZE=$(lsblk -ndo SIZE "$DISK")
qm set "$VMID" -$SLOT_NAME "$DISK${DISCARD_OPTS}" >/dev/null 2>&1
msg_ok "$(translate "Passthrough disk assigned") ($DISK$SLOT_NAME)"
DISK_INFO+="<p>Passthrough Disk $((i+1)): $DISK ($MODEL $SIZE)</p>"
msg_ok "$(translate "Import disk assigned") ($DISK$SLOT_NAME)"
DISK_INFO+="<p>Import Disk $((NEXT_DISK_SLOT+1)): $DISK ($MODEL $SIZE)</p>"
[[ -z "$BOOT_ORDER" ]] && BOOT_ORDER="$SLOT_NAME"
NEXT_DISK_SLOT=$((NEXT_DISK_SLOT + 1))
done
fi
if [[ ${#CONTROLLER_NVME_PCIS[@]} -gt 0 ]]; then
if ! _vm_is_q35 "$VMID"; then
msg_error "$(translate "Controller + NVMe passthrough requires machine type q35. Skipping controller assignment.")"
else
local hostpci_idx=0
if declare -F _pci_next_hostpci_index >/dev/null 2>&1; then
hostpci_idx=$(_pci_next_hostpci_index "$VMID" 2>/dev/null || echo 0)
else
local hostpci_existing
hostpci_existing=$(qm config "$VMID" 2>/dev/null)
while grep -q "^hostpci${hostpci_idx}:" <<< "$hostpci_existing"; do
hostpci_idx=$((hostpci_idx + 1))
done
fi
local pci bdf
for pci in "${CONTROLLER_NVME_PCIS[@]}"; do
bdf="${pci#0000:}"
if declare -F _pci_function_assigned_to_vm >/dev/null 2>&1; then
if _pci_function_assigned_to_vm "$pci" "$VMID"; then
msg_warn "$(translate "Controller/NVMe already present in VM config") ($pci)"
continue
fi
elif qm config "$VMID" 2>/dev/null | grep -qE "^hostpci[0-9]+:.*(0000:)?${bdf}([,[:space:]]|$)"; then
msg_warn "$(translate "Controller/NVMe already present in VM config") ($pci)"
continue
fi
if qm set "$VMID" --hostpci${hostpci_idx} "${pci},pcie=1" >/dev/null 2>&1; then
msg_ok "$(translate "Controller/NVMe assigned") (hostpci${hostpci_idx}${pci})"
DISK_INFO+="<p>Controller/NVMe: ${pci}</p>"
hostpci_idx=$((hostpci_idx + 1))
else
msg_error "$(translate "Failed to assign Controller/NVMe") (${pci})"
fi
done
fi
fi
@@ -427,10 +550,17 @@ select_interface_type
fi
local BOOT_FINAL="$BOOT_ORDER"
[[ -f "$ISO_PATH" ]] && BOOT_FINAL="$BOOT_ORDER;ide2"
qm set "$VMID" -boot order="$BOOT_FINAL" >/dev/null
msg_ok "$(translate "Boot order set to") $BOOT_FINAL"
local BOOT_FINAL=""
if [[ -n "$BOOT_ORDER" ]]; then
BOOT_FINAL="$BOOT_ORDER"
fi
if [[ -f "$ISO_PATH" ]]; then
BOOT_FINAL="${BOOT_FINAL:+$BOOT_FINAL;}ide2"
fi
if [[ -n "$BOOT_FINAL" ]]; then
qm set "$VMID" -boot order="$BOOT_FINAL" >/dev/null
msg_ok "$(translate "Boot order set to") $BOOT_FINAL"
fi
@@ -450,7 +580,7 @@ select_interface_type
<p>
<a href='https://macrimi.github.io/ProxMenux/docs/create-vm' target='_blank'><img src='https://img.shields.io/badge/📚_Docs-blue' alt='Docs'></a>
<a href='https://github.com/MacRimi/ProxMenux/blob/main/scripts/vm/create_vm.sh' target='_blank'><img src='https://img.shields.io/badge/💻_Code-green' alt='Code'></a>
<a href='https://github.com/MacRimi/ProxMenux/blob/main/scripts/menus/create_vm_menu.sh' target='_blank'><img src='https://img.shields.io/badge/💻_Code-green' alt='Code'></a>
<a href='https://ko-fi.com/macrimi' target='_blank'><img src='https://img.shields.io/badge/☕_Ko--fi-red' alt='Ko-fi'></a>
</p>
@@ -466,16 +596,75 @@ else
msg_ok "$(translate "VM description configured")"
fi
if [[ "${WIZARD_ADD_GPU:-no}" == "yes" && "$START_VM" == "yes" ]]; then
msg_warn "$(translate "Auto-start was skipped because GPU passthrough setup was requested.")"
msg_warn "$(translate "After completing GPU setup, start the VM manually when the host is ready.")"
START_VM="no"
fi
if [[ "$START_VM" == "yes" ]]; then
qm start "$VMID"
msg_ok "$(translate "VM started")"
fi
configure_guest_agent
msg_success "$(translate "VM creation completed")"
if [[ "${WIZARD_ADD_GPU:-no}" == "yes" ]]; then
WIZARD_GPU_RESULT="cancelled"
run_gpu_passthrough_wizard
if [[ "${VM_WIZARD_CAPTURE_ACTIVE:-0}" -eq 1 ]]; then
stop_spinner
exec 1>&8
exec 8>&-
VM_WIZARD_CAPTURE_ACTIVE=0
show_proxmenux_logo
cat "$VM_WIZARD_CAPTURE_FILE"
rm -f "$VM_WIZARD_CAPTURE_FILE"
VM_WIZARD_CAPTURE_FILE=""
fi
if [[ "$WIZARD_GPU_RESULT" == "applied" ]]; then
msg_success "$(translate "VM creation completed with GPU passthrough configured.")"
elif [[ "$WIZARD_GPU_RESULT" == "no_gpu" ]]; then
msg_success "$(translate "VM creation completed. GPU passthrough was skipped (no compatible GPU detected).")"
else
msg_success "$(translate "VM creation completed. GPU passthrough was not applied.")"
fi
if [[ "$OS_TYPE" == "2" ]]; then
echo -e "${TAB}$(translate "Next Steps:")"
echo -e "${TAB}1. $(translate "Start the VM to begin Windows installation from the mounted ISO.")"
echo -e "${TAB}2. $(translate "When asked to select a disk, click Load Driver and load the VirtIO drivers.")"
echo -e "${TAB} $(translate "Required if using a VirtIO or SCSI disk.")"
echo -e "${TAB}3. $(translate "Also install the VirtIO network driver during setup to enable network access.")"
echo -e "${TAB}4. $(translate "Continue the Windows installation as usual.")"
echo -e "${TAB}5. $(translate "Once installed, open the VirtIO ISO and run the installer to complete driver setup.")"
echo -e "${TAB}6. $(translate "Reboot the VM to complete the driver installation.")"
if [[ "$WIZARD_GPU_RESULT" == "applied" ]]; then
echo -e "${TAB}- $(translate "If you want to use a physical monitor on the passthrough GPU:")"
echo -e "${TAB}$(translate "First install the GPU drivers inside the guest and verify remote access (RDP/SSH).")"
echo -e "${TAB}$(translate "Then change the VM display to none (vga: none) when the guest is stable.")"
echo -e "${TAB}$(translate "If passthrough fails on Windows: install RadeonResetBugFix.")"
fi
echo -e
elif [[ "$OS_TYPE" == "3" ]]; then
echo -e "${TAB}${GN}$(translate "Recommended: Install the QEMU Guest Agent in the VM")${CL}"
echo -e "${TAB}$(translate "Run the following inside the VM:")"
echo -e "${TAB}apt install qemu-guest-agent -y && systemctl enable --now qemu-guest-agent"
if [[ "$WIZARD_GPU_RESULT" == "applied" ]]; then
echo -e "${TAB}- $(translate "If you want to use a physical monitor on the passthrough GPU:")"
echo -e "${TAB}$(translate "First install the GPU drivers inside the guest and verify remote access (RDP/SSH).")"
echo -e "${TAB}$(translate "Then change the VM display to none (vga: none) when the guest is stable.")"
fi
echo -e
fi
msg_success "$(translate "Press Enter to return to the main menu...")"
read -r
bash "$LOCAL_SCRIPTS/menus/create_vm_menu.sh"
exit 0
fi
msg_success "$(translate "VM creation completed")"
if [[ "$OS_TYPE" == "2" ]]; then
echo -e "${TAB}${GN}$(translate "Next Steps:")${CL}"
echo -e "${TAB}$(translate "Next Steps:")"
echo -e "${TAB}1. $(translate "Start the VM to begin Windows installation from the mounted ISO.")"
echo -e "${TAB}2. $(translate "When asked to select a disk, click Load Driver and load the VirtIO drivers.")"
echo -e "${TAB} $(translate "Required if using a VirtIO or SCSI disk.")"
@@ -487,11 +676,10 @@ if [[ "$OS_TYPE" == "2" ]]; then
elif [[ "$OS_TYPE" == "3" ]]; then
echo -e "${TAB}${GN}$(translate "Recommended: Install the QEMU Guest Agent in the VM")${CL}"
echo -e "${TAB}$(translate "Run the following inside the VM:")"
echo -e "${TAB}${CY}apt install qemu-guest-agent -y && systemctl enable --now qemu-guest-agent${CL}"
echo -e "${TAB}apt install qemu-guest-agent -y && systemctl enable --now qemu-guest-agent"
echo -e
fi
msg_success "$(translate "Press Enter to return to the main menu...")"
read -r
bash "$LOCAL_SCRIPTS/menus/create_vm_menu.sh"

View File

@@ -1,466 +0,0 @@
#!/usr/bin/env bash
# ==========================================================
# ProxMenuX - Virtual Machine Creator Script
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
# Version : 1.0
# Last Updated: 07/05/2025
# ==========================================================
# Description:
# This script is part of the central ProxMenux VM creation module. It allows users
# to create virtual machines (VMs) in Proxmox VE using either default or advanced
# configurations, streamlining the deployment of Linux, Windows, and other systems.
#
# Key features:
# - Supports both virtual disk creation and physical disk passthrough.
# - Automates CPU, RAM, BIOS, network and storage configuration.
# - Provides a user-friendly menu to select OS type, ISO image and disk interface.
# - Automatically generates a detailed and styled HTML description for each VM.
#
# All operations are designed to simplify and accelerate VM creation in a
# consistent and maintainable way, using ProxMenux standards.
# ==========================================================
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
VENV_PATH="/opt/googletrans-env"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language
initialize_cache
# ==========================================================
# Mont ISOs
# ==========================================================
function mount_iso_to_vm() {
local vmid="$1"
local iso_path="$2"
local device="$3"
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
msg_warn "$(translate "ISO not found to mount on device") $device"
fi
}
# ==========================================================
# Select Interface Type
# ==========================================================
function select_interface_type() {
INTERFACE_TYPE=$(whiptail --backtitle "ProxMenux" --title "$(translate "Select Disk Interface")" --radiolist \
"$(translate "Select the bus type for the disks:")" 15 70 4 \
"scsi" "$(translate "SCSI (recommended for Linux and Windows)")" ON \
"sata" "$(translate "SATA (standard - high compatibility)")" OFF \
"virtio" "$(translate "VirtIO (advanced - high performance)")" OFF \
"ide" "IDE (legacy)" OFF \
3>&1 1>&2 2>&3) || exit 1
case "$INTERFACE_TYPE" in
"scsi"|"sata")
DISCARD_OPTS=",discard=on,ssd=on"
;;
"virtio")
DISCARD_OPTS=",discard=on"
;;
"ide")
DISCARD_OPTS=""
;;
esac
msg_ok "$(translate "Disk interface selected:") $INTERFACE_TYPE"
}
# ==========================================================
# EFI/TPM
# ==========================================================
function select_storage_target() {
local PURPOSE="$1"
local vmid="$2"
local STORAGE=""
local STORAGE_MENU=()
while read -r line; do
TAG=$(echo "$line" | awk '{print $1}')
TYPE=$(echo "$line" | awk '{printf "%-10s", $2}')
FREE=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format "%.2f" | awk '{printf("%9sB", $6)}')
STORAGE_MENU+=("$TAG" "$(translate "Type:") $TYPE $(translate "Free:") $FREE" "OFF")
done < <(pvesm status -content images | awk 'NR>1')
if [[ ${#STORAGE_MENU[@]} -eq 0 ]]; then
msg_error "$(translate "Unable to detect a valid storage location for $PURPOSE disk.")"
exit 1
elif [[ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]]; then
STORAGE="${STORAGE_MENU[0]}"
else
kill $SPINNER_PID > /dev/null
STORAGE=$(whiptail --backtitle "ProxMenux" --title "$(translate "$PURPOSE Disk Storage")" --radiolist \
"$(translate "Choose the storage volume for the $PURPOSE disk (4MB):\n\nUse Spacebar to select.")" 16 70 6 \
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit 1
fi
echo "$STORAGE"
}
# ==========================================================
# Guest Agent Configurator
# ==========================================================
function configure_guest_agent() {
if [[ -z "$VMID" ]]; then
msg_error "$(translate "No VMID defined. Cannot apply guest agent config.")"
return 1
fi
msg_info "$(translate "Adding QEMU Guest Agent support...")"
# Habilitar el agente en la VM
qm set "$VMID" -agent enabled=1 >/dev/null 2>&1
# Añadir canal de comunicación virtio
qm set "$VMID" -chardev socket,id=qga0,path=/var/run/qemu-server/$VMID.qga,server=on,wait=off >/dev/null 2>&1
qm set "$VMID" -device virtio-serial-pci -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 >/dev/null 2>&1
msg_ok "$(translate "Guest Agent configuration applied")"
}
# ==========================================================
# Create VM
# ==========================================================
function create_vm() {
local BOOT_ORDER=""
local DISK_INFO=""
local DISK_INDEX=0
local ISO_DIR="/var/lib/vz/template/iso"
if [[ -n "$ISO_PATH" && -n "$ISO_URL" && ! -f "$ISO_PATH" ]]; then
if [[ "$ISO_URL" == *"sourceforge.net"* ]]; then
wget --content-disposition --show-progress -O "$ISO_PATH" "$ISO_URL"
else
wget --no-verbose --show-progress -O "$ISO_PATH" "$ISO_URL"
fi
if [[ -f "$ISO_PATH" ]]; then
msg_ok "$(translate "ISO image downloaded")"
else
msg_error "$(translate "Failed to download ISO image")"
return
fi
fi
if [[ "$OS_TYPE" == "2" ]]; then
GUEST_OS_TYPE="win10"
else
GUEST_OS_TYPE="l26"
fi
qm create "$VMID" -agent 1${MACHINE} -tablet 0 -localtime 1${BIOS_TYPE}${CPU_TYPE} \
-cores "$CORE_COUNT" -memory "$RAM_SIZE" -name "$HN" -tags proxmenux \
-net0 "virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU" -ostype "$GUEST_OS_TYPE" \
-scsihw virtio-scsi-pci \
$( [[ -n "$SERIAL_PORT" ]] && echo "-serial0 $SERIAL_PORT" ) >/dev/null 2>&1
msg_ok "$(translate "Base VM created with ID") $VMID"
if [[ "$BIOS_TYPE" == *"ovmf"* ]]; then
msg_info "$(translate "Configuring EFI disk")"
EFI_STORAGE=$(select_storage_target "EFI" "$VMID")
EFI_DISK_NAME="vm-${VMID}-disk-efivars"
STORAGE_TYPE=$(pvesm status -storage "$EFI_STORAGE" | awk 'NR>1 {print $2}')
case "$STORAGE_TYPE" in
nfs | dir)
EFI_DISK_EXT=".raw"
EFI_DISK_REF="$VMID/"
;;
*)
EFI_DISK_EXT=""
EFI_DISK_REF=""
;;
esac
EFI_KEYS="0"
[[ "$OS_TYPE" == "2" ]] && EFI_KEYS="1"
if pvesm alloc "$EFI_STORAGE" "$VMID" "$EFI_DISK_NAME$EFI_DISK_EXT" 4M >/dev/null 2>&1; then
if qm set "$VMID" -efidisk0 "$EFI_STORAGE:${EFI_DISK_REF}$EFI_DISK_NAME$EFI_DISK_EXT,pre-enrolled-keys=$EFI_KEYS" >/dev/null 2>&1; then
msg_ok "$(translate "EFI disk created and configured on") $EFI_STORAGE"
else
msg_error "$(translate "Failed to configure EFI disk")"
fi
else
msg_error "$(translate "Failed to create EFI disk")"
fi
fi
if [[ "$OS_TYPE" == "2" ]]; then
msg_info "$(translate "Configuring TPM device")"
TPM_STORAGE=$(select_storage_target "TPM" "$VMID")
TPM_NAME="vm-${VMID}-tpmstate"
STORAGE_TYPE=$(pvesm status -storage "$TPM_STORAGE" | awk 'NR>1 {print $2}')
case "$STORAGE_TYPE" in
nfs | dir)
TPM_EXT=".raw"
TPM_REF="$VMID/"
;;
*)
TPM_EXT=""
TPM_REF=""
;;
esac
TPM_FULL_NAME="${TPM_NAME}${TPM_EXT}"
if pvesm alloc "$TPM_STORAGE" "$VMID" "$TPM_FULL_NAME" 4M >/dev/null 2>&1; then
TPM_PATH="$TPM_STORAGE:${TPM_REF}${TPM_FULL_NAME},size=4M,version=v2.0"
if qm set "$VMID" -tpmstate0 "$TPM_PATH" >/dev/null 2>&1; then
msg_ok "$(translate "TPM device added to VM")"
else
msg_error "$(translate "Failed to configure TPM device in VM")$TPM_PATH"
fi
else
msg_error "$(translate "Failed to create TPM state disk")"
fi
fi
# ==========================================================
# Create Diks
# ==========================================================
select_interface_type
if [[ "$DISK_TYPE" == "virtual" && ${#VIRTUAL_DISKS[@]} -gt 0 ]]; then
for i in "${!VIRTUAL_DISKS[@]}"; do
DISK_INDEX=$((i+1))
IFS=':' read -r STORAGE SIZE <<< "${VIRTUAL_DISKS[$i]}"
DISK_NAME="vm-${VMID}-disk-${DISK_INDEX}"
SLOT_NAME="${INTERFACE_TYPE}${i}"
STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}')
case "$STORAGE_TYPE" in
dir|nfs)
DISK_EXT=".raw"
DISK_REF="$VMID/"
;;
*)
DISK_EXT=""
DISK_REF=""
;;
esac
if pvesm alloc "$STORAGE" "$VMID" "$DISK_NAME$DISK_EXT" "$SIZE"G >/dev/null 2>&1; then
qm set "$VMID" -$SLOT_NAME "$STORAGE:${DISK_REF}${DISK_NAME}${DISK_EXT}${DISCARD_OPTS}" >/dev/null
msg_ok "$(translate "Virtual disk") $DISK_INDEX ${SIZE}GB - $STORAGE ($SLOT_NAME)"
DISK_INFO+="<p>Virtual Disk $DISK_INDEX: ${SIZE}GB ($STORAGE / $SLOT_NAME)</p>"
[[ -z "$BOOT_ORDER" ]] && BOOT_ORDER="$SLOT_NAME"
else
msg_error "$(translate "Failed to create disk") $DISK_INDEX"
fi
done
fi
if [[ "$DISK_TYPE" == "passthrough" && ${#PASSTHROUGH_DISKS[@]} -gt 0 ]]; then
for i in "${!PASSTHROUGH_DISKS[@]}"; do
SLOT_NAME="${INTERFACE_TYPE}${i}"
DISK="${PASSTHROUGH_DISKS[$i]}"
MODEL=$(lsblk -ndo MODEL "$DISK")
SIZE=$(lsblk -ndo SIZE "$DISK")
qm set "$VMID" -$SLOT_NAME "$DISK${DISCARD_OPTS}" >/dev/null 2>&1
msg_ok "$(translate "Passthrough disk assigned") ($DISK$SLOT_NAME)"
DISK_INFO+="<p>Passthrough Disk $((i+1)): $DISK ($MODEL $SIZE)</p>"
[[ -z "$BOOT_ORDER" ]] && BOOT_ORDER="$SLOT_NAME"
done
fi
if [[ -f "$ISO_PATH" ]]; then
mount_iso_to_vm "$VMID" "$ISO_PATH" "ide2"
fi
if [[ "$OS_TYPE" == "2" ]]; then
local VIRTIO_DIR="/var/lib/vz/template/iso"
local VIRTIO_SELECTED=""
local VIRTIO_DOWNLOAD_URL="https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso"
while true; do
VIRTIO_OPTION=$(whiptail --title "ProxMenux - VirtIO Drivers" --menu "$(translate "Select how to provide VirtIO drivers")" 15 70 2 \
"1" "$(translate "Download latest VirtIO ISO automatically")" \
"2" "$(translate "Use existing VirtIO ISO from storage")" 3>&1 1>&2 2>&3)
[[ $? -ne 0 ]] && msg_warn "$(translate "VirtIO ISO selection cancelled.")" && break
case "$VIRTIO_OPTION" in
1)
if [[ -f "$VIRTIO_DIR/virtio-win.iso" ]]; then
if whiptail --title "ProxMenux" --yesno "$(translate "A VirtIO ISO already exists. Do you want to overwrite it?")" 10 60; then
wget -q --show-progress -O "$VIRTIO_DIR/virtio-win.iso" "$VIRTIO_DOWNLOAD_URL"
if [[ -f "$VIRTIO_DIR/virtio-win.iso" ]]; then
msg_ok "$(translate "VirtIO driver ISO downloaded successfully.")"
else
msg_error "$(translate "Failed to download VirtIO driver ISO.")"
fi
fi
else
wget -q --show-progress -O "$VIRTIO_DIR/virtio-win.iso" "$VIRTIO_DOWNLOAD_URL"
if [[ -f "$VIRTIO_DIR/virtio-win.iso" ]]; then
msg_ok "$(translate "VirtIO driver ISO downloaded successfully.")"
else
msg_error "$(translate "Failed to download VirtIO driver ISO.")"
fi
fi
VIRTIO_SELECTED="$VIRTIO_DIR/virtio-win.iso"
;;
2)
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)
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)
if [[ -n "$VIRTIO_FILE" ]]; then
VIRTIO_SELECTED="$VIRTIO_DIR/$VIRTIO_FILE"
else
msg_warn "$(translate "No VirtIO ISO selected. Please choose again.")"
continue
fi
;;
esac
if [[ -n "$VIRTIO_SELECTED" && -f "$VIRTIO_SELECTED" ]]; then
mount_iso_to_vm "$VMID" "$VIRTIO_SELECTED" "ide3"
else
msg_warn "$(translate "VirtIO ISO not found after selection.")"
fi
break
done
fi
local BOOT_FINAL="$BOOT_ORDER"
[[ -f "$ISO_PATH" ]] && BOOT_FINAL="$BOOT_ORDER;ide2"
qm set "$VMID" -boot order="$BOOT_FINAL" >/dev/null
msg_ok "$(translate "Boot order set to") $BOOT_FINAL"
HTML_DESC="<div align='center'>
<table style='width: 100%; border-collapse: collapse;'>
<tr>
<td style='width: 100px; vertical-align: middle;'>
<img src='https://raw.githubusercontent.com/MacRimi/ProxMenux/main/images/logo_desc.png' alt='ProxMenux Logo' style='height: 100px;'>
</td>
<td style='vertical-align: middle;'>
<h1 style='margin: 0;'>$HN VM</h1>
<p style='margin: 0;'>Created with ProxMenux</p>
</td>
</tr>
</table>
<p>
<a href='https://macrimi.github.io/ProxMenux/docs/create-vm/synology' target='_blank'><img src='https://img.shields.io/badge/📚_Docs-blue' alt='Docs'></a>
<a href='https://github.com/MacRimi/ProxMenux/blob/main/scripts/vm/create_vm.sh' target='_blank'><img src='https://img.shields.io/badge/💻_Code-green' alt='Code'></a>
<a href='https://ko-fi.com/macrimi' target='_blank'><img src='https://img.shields.io/badge/☕_Ko--fi-red' alt='Ko-fi'></a>
</p>
<div>
${DISK_INFO}
</div>
</div>"
msg_info "$(translate "Setting VM description")"
if ! qm set "$VMID" -description "$HTML_DESC" >/dev/null 2>&1; then
msg_error "$(translate "Failed to set VM description")"
else
msg_ok "$(translate "VM description configured")"
fi
if [[ "$START_VM" == "yes" ]]; then
qm start "$VMID"
msg_ok "$(translate "VM started")"
fi
configure_guest_agent
msg_success "$(translate "VM creation completed")"
if [[ "$OS_TYPE" == "2" ]]; then
echo -e "${TAB}${GN}$(translate "Next Steps:")${CL}"
echo -e "${TAB}1. $(translate "Start the VM to begin Windows installation from the mounted ISO.")"
echo -e "${TAB}2. $(translate "When asked to select a disk, click Load Driver and load the VirtIO drivers.")"
echo -e "${TAB} $(translate "Required if using a VirtIO or SCSI disk.")"
echo -e "${TAB}3. $(translate "Also install the VirtIO network driver during setup to enable network access.")"
echo -e "${TAB}4. $(translate "Continue the Windows installation as usual.")"
echo -e "${TAB}5. $(translate "Once installed, open the VirtIO ISO and run the installer to complete driver setup.")"
echo -e "${TAB}6. $(translate "Reboot the VM to complete the driver installation.")"
echo -e
elif [[ "$OS_TYPE" == "3" ]]; then
echo -e "${TAB}${GN}$(translate "Recommended: Install the QEMU Guest Agent in the VM")${CL}"
echo -e "${TAB}$(translate "Run the following inside the VM:")"
echo -e "${TAB}${CY}apt install qemu-guest-agent -y && systemctl enable --now qemu-guest-agent${CL}"
echo -e
fi
msg_success "$(translate "Press Enter to return to the main menu...")"
read -r
}

File diff suppressed because it is too large Load Diff