diff --git a/scripts/vm/syno_test.sh b/scripts/vm/syno_test.sh new file mode 100644 index 0000000..7170d1b --- /dev/null +++ b/scripts/vm/syno_test.sh @@ -0,0 +1,1080 @@ +#!/usr/bin/env bash + +# ========================================================== +# Synology VM Creator for ProxMenuX version TEST +# ========================================================== +# Author : MacRimi +# Copyright : (c) 2024 MacRimi +# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE) +# Version : 1.0 +# Last Updated: 13/03/2025 +# ========================================================== +# This script creates a Synology DSM virtual machine in Proxmox +# ========================================================== + +# Configuration +REPO_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main" +UTILS_URL="https://raw.githubusercontent.com/MacRimi/ProxMenux/main/scripts/utils.sh" + + +# Source utils.sh for common functions and styles +if ! source <(curl -sSf "$UTILS_URL"); then + echo "Error: Could not load utils.sh from $UTILS_URL" + exit 1 +fi +# ========================================================== + +GEN_MAC="02" +for i in {1..5}; do + BYTE=$(printf "%02X" $((RANDOM % 256))) + GEN_MAC="${GEN_MAC}:${BYTE}" +done + +NEXTID=$(pvesh get /cluster/nextid 2>/dev/null || echo "100") +NAME="Synology VM" +IMAGES_DIR="/var/lib/vz/template/images" +ERROR_FLAG=false + + + + + +function exit_script() { + clear + if whiptail --backtitle "ProxMenuX" --title "$NAME" --yesno "This will create a New $NAME. Proceed?" 10 58; then + start_script + else + clear + exit + fi +} + + +# Definir función header_info al inicio del script + +function header_info() { + clear + show_proxmenux_logo + echo -e "${BL}╔═══════════════════════════════════════════════╗${CL}" + echo -e "${BL}║ ║${CL}" + echo -e "${BL}║${YWB} Synology VM Creator ${BL}║${CL}" + echo -e "${BL}║ ║${CL}" + echo -e "${BL}╚═══════════════════════════════════════════════╝${CL}" + echo -e +} +# ========================================================== + + + + + + +# ========================================================== +# start Script +# ========================================================== +function start_script() { + if (whiptail --backtitle "ProxMenuX" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then + header_info + echo -e "${DEF}Using Default Settings${CL}" + default_settings + else + header_info + echo -e "${CUS}Using Advanced Settings${CL}" + advanced_settings + fi +} +# ========================================================== + + + + +# ========================================================== +# Default Settings +# ========================================================== +function default_settings() { + VMID="$NEXTID" + FORMAT="" + MACHINE=" -machine q35" + BIOS_TYPE=" -bios ovmf" + DISK_CACHE="" + HN="Synology-DSM" + CPU_TYPE=" -cpu host" + CORE_COUNT="2" + RAM_SIZE="4096" + BRG="vmbr0" + MAC="$GEN_MAC" + VLAN="" + MTU="" + SERIAL_PORT="socket" + START_VM="no" + + echo -e "${DGN}Using Virtual Machine ID: ${BGN}${VMID}${CL}" + echo -e "${DGN}Using Machine Type: ${BGN}q35${CL}" + echo -e "${DGN}Using BIOS Type: ${BGN}OVMF (UEFI)${CL}" + echo -e "${DGN}Using Hostname: ${BGN}${HN}${CL}" + echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}" + echo -e "${DGN}Allocated Cores: ${BGN}${CORE_COUNT}${CL}" + echo -e "${DGN}Allocated RAM: ${BGN}${RAM_SIZE}${CL}" + echo -e "${DGN}Using Bridge: ${BGN}${BRG}${CL}" + echo -e "${DGN}Using MAC Address: ${BGN}${MAC}${CL}" + echo -e "${DGN}Using VLAN: ${BGN}Default${CL}" + echo -e "${DGN}Using Interface MTU Size: ${BGN}Default${CL}" + echo -e "${DGN}Configuring Serial Port: ${BGN}${SERIAL_PORT}${CL}" + echo -e "${DGN}Start VM when completed: ${BGN}${START_VM}${CL}" + echo -e + echo -e "${DEF}Creating a $NAME using the above default settings${CL}" + + sleep 1 + select_disk_type +} +# ========================================================== + + + + + +# ========================================================== +# advanced Settings +# ========================================================== +function advanced_settings() { + # VM ID Selection + while true; do + if VMID=$(whiptail --backtitle "ProxMenuX" --inputbox "Set Virtual Machine ID" 8 58 $NEXTID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z "$VMID" ]; then + VMID="$NEXTID" + fi + if pct status "$VMID" &>/dev/null || qm status "$VMID" &>/dev/null; then + echo -e "${CROSS}${RD} ID $VMID is already in use${CL}" + sleep 1 + continue + fi + echo -e "${DGN}Virtual Machine ID: ${BGN}$VMID${CL}" + break + else + exit_script + fi + done + + # Machine Type Selection + if MACH=$(whiptail --backtitle "ProxMenuX" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \ + "q35" "Machine q35" ON \ + "i440fx" "Machine i440fx" OFF \ + 3>&1 1>&2 2>&3); then + if [ $MACH = q35 ]; then + echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + FORMAT="" + MACHINE=" -machine q35" + else + echo -e "${DGN}Using Machine Type: ${BGN}$MACH${CL}" + FORMAT=",efitype=4m" + MACHINE="" + fi + else + exit_script + fi + + # BIOS Type Selection + if BIOS=$(whiptail --backtitle "ProxMenuX" --title "BIOS TYPE" --radiolist --cancel-button Exit-Script "Choose BIOS Type" 10 58 2 \ + "ovmf" "UEFI (OVMF)" ON \ + "seabios" "SeaBIOS (Legacy)" OFF \ + 3>&1 1>&2 2>&3); then + if [ "$BIOS" = "seabios" ]; then + echo -e "${DGN}Using BIOS Type: ${BGN}SeaBIOS${CL}" + BIOS_TYPE=" -bios seabios" + else + echo -e "${DGN}Using BIOS Type: ${BGN}OVMF (UEFI)${CL}" + BIOS_TYPE=" -bios ovmf" + fi + else + exit_script + fi + + # Hostname Selection + if VM_NAME=$(whiptail --backtitle "ProxMenuX" --inputbox "Set Hostname" 8 58 Synology-DSM --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VM_NAME ]; then + HN="Synology-DSM" + echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + else + HN=$(echo ${VM_NAME,,} | tr -d ' ') + echo -e "${DGN}Using Hostname: ${BGN}$HN${CL}" + fi + else + exit_script + fi + + # CPU Type Selection + if CPU_TYPE1=$(whiptail --backtitle "ProxMenuX" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \ + "1" "Host" ON \ + "0" "KVM64" OFF \ + 3>&1 1>&2 2>&3); then + if [ $CPU_TYPE1 = "1" ]; then + echo -e "${DGN}Using CPU Model: ${BGN}Host${CL}" + CPU_TYPE=" -cpu host" + else + echo -e "${DGN}Using CPU Model: ${BGN}KVM64${CL}" + CPU_TYPE="" + fi + else + exit_script + fi + + # Core Count Selection + if CORE_COUNT=$(whiptail --backtitle "ProxMenuX" --inputbox "Allocate CPU Cores" 8 58 2 --title "CORE COUNT" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $CORE_COUNT ]; then + CORE_COUNT="2" + echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + else + echo -e "${DGN}Allocated Cores: ${BGN}$CORE_COUNT${CL}" + fi + else + exit_script + fi + + # RAM Size Selection + if RAM_SIZE=$(whiptail --backtitle "ProxMenuX" --inputbox "Allocate RAM in MiB" 8 58 4096 --title "RAM" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $RAM_SIZE ]; then + RAM_SIZE="4096" + echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + else + echo -e "${DGN}Allocated RAM: ${BGN}$RAM_SIZE${CL}" + fi + else + exit_script + fi + + # Bridge Selection + if BRG=$(whiptail --backtitle "ProxMenuX" --inputbox "Set a Bridge" 8 58 vmbr0 --title "BRIDGE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $BRG ]; then + BRG="vmbr0" + echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + else + echo -e "${DGN}Using Bridge: ${BGN}$BRG${CL}" + fi + else + exit_script + fi + + # MAC Address Selection + if MAC1=$(whiptail --backtitle "ProxMenuX" --inputbox "Set a MAC Address" 8 58 $GEN_MAC --title "MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MAC1 ]; then + MAC="$GEN_MAC" + echo -e "${DGN}Using MAC Address: ${BGN}$MAC${CL}" + else + MAC="$MAC1" + echo -e "${DGN}Using MAC Address: ${BGN}$MAC1${CL}" + fi + else + exit_script + fi + + # VLAN Selection + if VLAN1=$(whiptail --backtitle "ProxMenuX" --inputbox "Set a Vlan(leave blank for default)" 8 58 --title "VLAN" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $VLAN1 ]; then + VLAN1="Default" + VLAN="" + echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + else + VLAN=",tag=$VLAN1" + echo -e "${DGN}Using Vlan: ${BGN}$VLAN1${CL}" + fi + else + exit_script + fi + + # MTU Selection + if MTU1=$(whiptail --backtitle "ProxMenuX" --inputbox "Set Interface MTU Size (leave blank for default)" 8 58 --title "MTU SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if [ -z $MTU1 ]; then + MTU1="Default" + MTU="" + echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + else + MTU=",mtu=$MTU1" + echo -e "${DGN}Using Interface MTU Size: ${BGN}$MTU1${CL}" + fi + else + exit_script + fi + + + + # Confirmation + if (whiptail --backtitle "ProxMenuX" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a $NAME?" --no-button Do-Over 10 58); then + echo -e + echo -e "${CUS}Creating a $NAME using the above advanced settings${CL}" + + # Ahora preguntamos por el tipo de disco + sleep 1 + select_disk_type + else + header_info + sleep 1 + echo -e "${CUS}Using Advanced Settings${CL}" + advanced_settings + fi +} +# ========================================================== + + + + + +# ========================================================== +# Select Disk +# ========================================================== +function select_disk_type() { + + DISK_TYPE=$(whiptail --backtitle "ProxMenuX" --title "DISK TYPE" --menu "Choose disk type:" 12 58 2 \ + "virtual" "Create virtual disk" \ + "passthrough" "Use physical disk passthrough" \ + --ok-button "Select" --cancel-button "Cancel" 3>&1 1>&2 2>&3) + + EXIT_STATUS=$? + + if [[ $EXIT_STATUS -ne 0 ]]; then + clear + header_info + msg_error "Operation cancelled by user. Returning to start scrip..." + sleep 2 + if whiptail --backtitle "ProxMenuX" --title "$NAME" --yesno "This will create a New $NAME. Proceed?" 10 58; then + start_script + else + clear + exit + fi + fi + + if [[ "$DISK_TYPE" == "virtual" ]]; then + select_virtual_disk + else + select_passthrough_disk + fi +} + +# ========================================================== + + + + + +# ========================================================== +# Select Virtual Disks +# ========================================================== +function select_virtual_disk() { + + + # Inicializar variables + VIRTUAL_DISKS=() + + # Bucle para añadir múltiples discos + local add_more_disks=true + while $add_more_disks; do + + msg_info "Detecting available storage volumes..." + + # Obtener lista de almacenamiento disponible + 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') + + # Comprobar que hay almacenamiento disponible + 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 + + + # Seleccionar almacenamiento + 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 "Select Storage Volume" --radiolist \ + "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 + # Si ya hay discos añadidos, continuar con el script + add_more_disks=false + continue + fi + fi + + + fi + + # Solicitar tamaño del disco + DISK_SIZE=$(whiptail --backtitle "ProxMenuX" --inputbox "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 + # Si ya hay discos añadidos, continuar con el script + add_more_disks=false + continue + fi + fi + + if [ -z "$DISK_SIZE" ]; then + DISK_SIZE="32" + fi + + # Almacenar la configuración en la lista de discos + VIRTUAL_DISKS+=("${STORAGE}:${DISK_SIZE}") + + + # Preguntar si desea crear otro disco + if ! whiptail --backtitle "ProxMenuX" --title "Add Another Disk" \ + --yesno "Do you want to add another virtual disk?" 8 58; then + add_more_disks=false + fi + done + + # Mostrar resumen de los discos creados + 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 + fi + + + export VIRTUAL_DISKS + + + select_loader +} + +# ========================================================== + + + + + + +# ========================================================== +# Select Physical Disks +# ========================================================== +function select_passthrough_disk() { + + + FREE_DISKS=() + + # Obtener la lista de discos montados + USED_DISKS=$(lsblk -n -o PKNAME,TYPE | grep 'lvm' | awk '{print "/dev/" $1}') + MOUNTED_DISKS=$(mount | grep -o '/dev/[a-z]*' | sort | uniq) + + # Obtener todos los discos físicos + while read -r DISK; do + + if ! echo "$USED_DISKS" | grep -q "$DISK" && ! echo "$MOUNTED_DISKS" | grep -q "$DISK"; then + + MODEL=$(lsblk -dn -o MODEL "$DISK" | xargs) + SIZE=$(lsblk -dn -o SIZE "$DISK" | xargs) + DESCRIPTION=$(printf "%-40s %10s" "$MODEL" "$SIZE") + FREE_DISKS+=("$DISK" "$DESCRIPTION" "OFF") + fi + done < <(lsblk -dn -e 7,11 -o PATH) + + + # Handle no available disks + if [ "${#FREE_DISKS[@]}" -eq 0 ]; then + whiptail --title "Error" --msgbox "No disks available for this VM." 8 40 + select_disk_type + fi + + # Calculate maximum content length + MAX_WIDTH=$(printf "%s\n" "${FREE_DISKS[@]}" | awk '{print length}' | sort -nr | head -n1) + TOTAL_WIDTH=$((MAX_WIDTH + 20)) # Add additional margin + + # Set a reasonable minimum width + if [ $TOTAL_WIDTH -lt 70 ]; then + TOTAL_WIDTH=70 + fi + + # Display menu to select multiple free disks with dynamically calculated width + SELECTED_DISKS=$(whiptail --title "Select Disks" --checklist \ + "Select the disks you want to use (use spacebar to select):" 20 $TOTAL_WIDTH 10 \ + "${FREE_DISKS[@]}" 3>&1 1>&2 2>&3) + + # Check if at least one disk was selected + if [ -z "$SELECTED_DISKS" ]; then + msg_error "Disk not specified. At least one disk is required." + sleep 2 + select_disk_type + fi + + # Process selected disks + msg_ok "Disk passthrough selected:" + PASSTHROUGH_DISKS=() + 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") + done + + + select_loader +} +# ========================================================== + + + + + + +# ========================================================== +# Select Loader +# ========================================================== +function select_loader() { + # Ensure the images directory exists + if [ ! -d "$IMAGES_DIR" ]; then + msg_info "Creating images directory" + mkdir -p "$IMAGES_DIR" + chmod 755 "$IMAGES_DIR" + msg_ok "Images directory created: $IMAGES_DIR" + fi + + # Create the loader selection menu + LOADER_OPTION=$(whiptail --backtitle "ProxMenuX" --title "SELECT LOADER" --menu "Choose a loader for Synology DSM:" 15 70 4 \ + "1" "AuxXxilium Arc Loader" \ + "2" "RedPill Loader (RROrg - rr)" \ + "3" "TinyCore RedPill Loader (PeterSuh-Q3 M-shell)" \ + "4" "Custom Loader Image (from $IMAGES_DIR)" \ + 3>&1 1>&2 2>&3) + + if [ -z "$LOADER_OPTION" ]; then + exit_script + fi + + case $LOADER_OPTION in + 1) + LOADER_TYPE="arc" + LOADER_NAME="AuxXxilium Arc" + LOADER_URL="https://github.com/AuxXxilium/arc/releases/latest" + echo -e "${DGN}${TAB}Selected Loader: ${BGN}$LOADER_NAME${CL}" + download_loader + ;; + 2) + LOADER_TYPE="redpill" + LOADER_NAME="RedPill rr" + LOADER_URL="https://github.com/RROrg/rr/releases/latest" + echo -e "${DGN}${TAB}Selected Loader: ${BGN}$LOADER_NAME${CL}" + download_loader + ;; + 3) + LOADER_TYPE="tinycore" + LOADER_NAME="TinyCore RedPill M-shell" + LOADER_URL="https://github.com/PeterSuh-Q3/tinycore-redpill/releases/latest" + echo -e "${DGN}${TAB}Selected Loader: ${BGN}$LOADER_NAME${CL}" + download_loader + ;; + 4) + LOADER_TYPE="custom" + LOADER_NAME="Custom Image" + echo -e "${DGN}${TAB}Selected Loader: ${BGN}$LOADER_NAME${CL}" + select_custom_image + ;; + esac +} + +function select_custom_image() { + # Check if there are any images in the directory + IMAGES=$(find "$IMAGES_DIR" -type f -name "*.img" -o -name "*.iso" -o -name "*.qcow2" -o -name "*.vmdk" | sort) + + if [ -z "$IMAGES" ]; then + whiptail --title "No Images Found" --msgbox "No compatible images found in $IMAGES_DIR\n\nSupported formats: .img, .iso, .qcow2, .vmdk\n\nPlease add some images and try again." 15 70 + select_loader + fi + + # Create an array of image options for whiptail + IMAGE_OPTIONS=() + + while read -r img; do + filename=$(basename "$img") + filesize=$(du -h "$img" | cut -f1) + IMAGE_OPTIONS+=("$img" "$filesize") + done <<< "$IMAGES" + + # Let the user select an image + LOADER_FILE=$(whiptail --backtitle "ProxMenuX" --title "SELECT CUSTOM IMAGE" --menu "Choose a custom image:" 20 70 10 "${IMAGE_OPTIONS[@]}" 3>&1 1>&2 2>&3) + + if [ -z "$LOADER_FILE" ]; then + msg_error "No custom image selected" + exit_script + fi + + echo -e "${DGN}${TAB}Using Custom Image: ${BGN}$(basename "$LOADER_FILE")${CL}" + FILE=$(basename "$LOADER_FILE") +} +# ========================================================== + + + + + + + +# ========================================================== +# Download Loader +# ========================================================== +function download_loader() { + + echo -e "${DGN}${TAB}Retrieving the URL for the ${BGN}$LOADER_NAME loader${CL}" + + case $LOADER_TYPE in + arc) + curl -s https://api.github.com/repos/AuxXxilium/arc/releases/latest \ + | grep "browser_download_url.*evo.img.zip" \ + | cut -d '"' -f 4 \ + | xargs wget -q --show-progress -O "$IMAGES_DIR/evo.img.zip" + + if [ -f "$IMAGES_DIR/evo.img.zip" ]; then + cd "$IMAGES_DIR" + unzip -q evo.img.zip + rm evo.img.zip + FILE="arc.img" + LOADER_FILE="$IMAGES_DIR/$FILE" + cd - > /dev/null + else + msg_error "Failed to download $LOADER_NAME loader" + sleep 1 + select_loader + fi + ;; + + redpill) + curl -s https://api.github.com/repos/RROrg/rr/releases/latest \ + | grep "browser_download_url.*\.img\.zip" \ + | cut -d '"' -f 4 \ + | xargs wget -q --show-progress -O "$IMAGES_DIR/rr.img.zip" + + if [ -f "$IMAGES_DIR/rr.img.zip" ]; then + cd "$IMAGES_DIR" + msg_info "Unzipping $LOADER_NAME loader. Please wait..." + unzip -qo rr.img.zip + msg_ok "Unzipped $LOADER_NAME loader successfully." + rm -f rr.img.zip + FILE="rr.img" + LOADER_FILE="$IMAGES_DIR/$FILE" + cd - > /dev/null + fi + + ;; + + tinycore) + curl -s https://api.github.com/repos/PeterSuh-Q3/tinycore-redpill/releases/latest \ + | grep "browser_download_url.*tinycore-redpill.v.*img.gz" \ + | cut -d '"' -f 4 \ + | xargs wget -q --show-progress -O "$IMAGES_DIR/tinycore.img.gz" + + if [ -f "$IMAGES_DIR/tinycore.img.gz" ]; then + cd "$IMAGES_DIR" + + msg_info "Unzipping $LOADER_NAME loader. Please wait..." + gunzip -f tinycore.img.gz 2> /dev/null + msg_ok "Unzipped $LOADER_NAME loader successfully." + FILE="tinycore.img" + LOADER_FILE="$IMAGES_DIR/$FILE" + cd - > /dev/null + + else + msg_error "Failed to download $LOADER_NAME loader" + sleep 1 + select_loader + + fi + ;; + esac + + msg_ok "Downloaded ${CL}${BL}${FILE}${CL} to ${IMAGES_DIR}" +} +# ======================================================= + + + + + +# ========================================================== +# Select UEFI Storage +# ========================================================== +function select_efi_storage() { + local vmid=$1 + local STORAGE="" + + 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)}') + + # Crear la línea del menú + ITEM=" Type: $TYPE Free: $FREE" + OFFSET=2 + if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then + MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) + fi + + 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 for EFI disk." + + elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then + # Si hay solo una opción, usarla directamente + STORAGE=${STORAGE_MENU[0]} + + else + # Mostrar menú radiolist para seleccionar el volumen + kill $SPINNER_PID > /dev/null + while [ -z "${STORAGE:+x}" ]; do + STORAGE=$(whiptail --backtitle "ProxMenuX" --title "EFI Disk Storage" --radiolist \ + "Choose the storage volume for the EFI disk (4MB):\n\nUse Spacebar to select." \ + 16 $(($MSG_MAX_LENGTH + 23)) 6 \ + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit + + done + + fi + + echo "$STORAGE" +} +# ========================================================== + + + + + +# ========================================================== +# Select Storage Loader +# ========================================================== +function select_storage_volume() { + local vmid=$1 + local purpose=$2 + local STORAGE="" + + 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)}') + + # Crear la línea del menú + ITEM=" Type: $TYPE Free: $FREE" + OFFSET=2 + if [[ $((${#ITEM} + $OFFSET)) -gt ${MSG_MAX_LENGTH:-} ]]; then + MSG_MAX_LENGTH=$((${#ITEM} + $OFFSET)) + fi + + 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." + exit 1 + elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then + # Si hay solo una opción, usarla directamente + STORAGE=${STORAGE_MENU[0]} + else + # Mostrar menú radiolist para seleccionar el volumen + while [ -z "${STORAGE:+x}" ]; do + STORAGE=$(whiptail --backtitle "ProxMenuX" --title "Storage Pools" --radiolist \ + "Choose the storage volume for $purpose:\n\nUse Spacebar to select." \ + 16 $(($MSG_MAX_LENGTH + 23)) 6 \ + "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3) || exit + done + fi + + echo "$STORAGE" +} + + + + + + +# ========================================================== +# Create VM +# ========================================================== +function create_vm() { + + # Create the VM + 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 -onboot 1 -ostype l26 -scsihw virtio-scsi-pci \ + -serial0 socket + msg_ok "Create a $NAME" + + + +# Verificar si se está usando UEFI (OVMF) =================== + if [[ "$BIOS_TYPE" == *"ovmf"* ]]; then + + msg_info "Configuring EFI disk" + EFI_STORAGE=$(select_efi_storage $VMID) + EFI_DISK_NAME="vm-${VMID}-disk-efivars" + + # Determinar tipo de almacenamiento y extensión + 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 + + # Crear disco EFI + if pvesm alloc "$EFI_STORAGE" "$VMID" "$EFI_DISK_NAME$EFI_DISK_EXT" 4M >/dev/null 2>&1; then + # Configurar disco EFI sin preinstalar llaves + if qm set "$VMID" -efidisk0 "$EFI_STORAGE:${EFI_DISK_REF}$EFI_DISK_NAME$EFI_DISK_EXT,pre-enrolled-keys=0" >/dev/null 2>&1; then + msg_ok "EFI disk created and configured on ${CL}${BL}$EFI_STORAGE${GN}${CL}" + else + msg_error "Failed to configure EFI disk" + ERROR_FLAG=true + fi + else + msg_error "Failed to create EFI disk" + ERROR_FLAG=true + fi + + fi +# ========================================================== + + +# Select storage volume for loader ======================= + + LOADER_STORAGE=$(select_storage_volume $VMID "loader disk") + + + # Ejecutar el comando en segundo plano y capturar su PID + qm importdisk $VMID ${LOADER_FILE} $LOADER_STORAGE > /tmp/import_log_$VMID.txt 2>&1 & + import_pid=$! + + # Mostrar un indicador simple de progreso + echo -n "Importing loader disk: " + while kill -0 $import_pid 2>/dev/null; do + echo -n "." + sleep 2.5 + done + + # Esperar a que termine el proceso + wait $import_pid + # Limpiar + rm -f /tmp/import_log_$VMID.txt + + + # Obtener el disco importado + IMPORTED_DISK=$(qm config $VMID | grep -E 'unused[0-9]+' | tail -1 | cut -d: -f1) + + # Si el disco no se importó correctamente, mostrar mensaje de error, pero continuar + if [ -z "$IMPORTED_DISK" ]; then + msg_error "Loader import failed. No disk detected." + ERROR_FLAG=true + else + msg_ok "Loader imported successfully to ${CL}${BL}$LOADER_STORAGE${GN}${CL}" + fi + + # Configure the loader disk as scsi0 + DISK_NAME="vm-${VMID}-disk-0" + result=$(qm set "$VMID" -scsi0 "${LOADER_STORAGE}:${DISK_NAME}" 2>&1 > /dev/null) + if [[ $? -eq 0 ]]; then + msg_ok "Configured loader disk as scsi0" + else + ERROR_FLAG=true + fi + + # Set boot order to scsi0 + result=$(qm set "$VMID" -boot order=scsi0 2>&1) + if [[ $? -eq 0 ]]; then + msg_ok "Loader configured as boot device." + else + ERROR_FLAG=true + fi + +# ========================================================== + +if [ "$DISK_TYPE" = "virtual" ]; then + # Comprobar que hay discos virtuales configurados + if [ ${#VIRTUAL_DISKS[@]} -eq 0 ]; then + msg_error "No virtual disks configured." + exit_script + fi + + DISK_INFO="" + CONSOLE_DISK_INFO="" + + for i in "${!VIRTUAL_DISKS[@]}"; do + # Extraer almacenamiento y tamaño + IFS=':' read -r STORAGE SIZE <<< "${VIRTUAL_DISKS[$i]}" + + # Determinar tipo de almacenamiento y extensión + STORAGE_TYPE=$(pvesm status -storage $STORAGE | awk 'NR>1 {print $2}') + case $STORAGE_TYPE in + nfs | dir) + DISK_EXT=".raw" + DISK_REF="$VMID/" + ;; + *) + DISK_EXT="" + DISK_REF="" + ;; + esac + + + DISK_NUM=$((i+1)) + DISK_NAME="vm-${VMID}-disk-${DISK_NUM}${DISK_EXT}" + + + # Crear disco virtual + msg_info "Creating virtual disk..." + if ! pvesm alloc "$STORAGE" "$VMID" "$DISK_NAME" "$SIZE"G >/dev/null 2>&1; then + msg_error "Failed to allocate virtual disk $DISK_NUM" + cleanup_vmid + + fi + + # Configurar disco en la VM (sata0, sata1, etc.) + SATA_ID="sata$i" + if ! qm set "$VMID" -$SATA_ID "$STORAGE:${DISK_REF}$DISK_NAME" >/dev/null 2>&1; then + msg_error "Failed to configure virtual disk as $SATA_ID" + cleanup_vmid + + fi + msg_ok "Configured virtual disk as $SATA_ID, ${SIZE}GB on ${CL}${BL}$STORAGE${CL} ${GN}" + + # Añadir información a la descripción + DISK_INFO="${DISK_INFO}

Virtual Disk $DISK_NUM: ${SIZE}GB on ${STORAGE}

" + CONSOLE_DISK_INFO="${CONSOLE_DISK_INFO}- Virtual Disk $DISK_NUM: ${SIZE}GB on ${STORAGE} ($SATA_ID)\n" + done + + + + # Preparar descripción HTML + HTML_DESC="
+

Synology DSM Virtual Machine

+

Created with ProxMenuX

+

Loader: $LOADER_NAME

+ ${DISK_INFO} +
" + + # Configurar descripción + msg_info "Setting VM description" + if ! qm set "$VMID" -description "$HTML_DESC" >/dev/null 2>&1; then + msg_error "Failed to set VM description" + exit_script + fi + msg_ok "Configured VM description" + + +else + + + # Configurar múltiples discos passthrough + DISK_INFO="" + CONSOLE_DISK_INFO="" + + for i in "${!PASSTHROUGH_DISKS[@]}"; do + DISK="${PASSTHROUGH_DISKS[$i]}" + DISK_MODEL=$(lsblk -ndo MODEL "$DISK" | xargs) + DISK_SIZE=$(lsblk -ndo SIZE "$DISK" | xargs) + DISK_ID="sata$i" + + + # Configurar disco passthrough + result=$(qm set $VMID -${DISK_ID} ${DISK} 2>&1) + if [[ $? -eq 0 ]]; then + msg_ok "Configured disk ${CL}${BL}($DISK_MODEL $DISK_SIZE)${CL}${GN} as $DISK_ID" + fi + + # Añadir información del disco para la descripción HTML + DISK_INFO="${DISK_INFO}

Passthrough Disk $((i+1)): $DISK ($DISK_MODEL $DISK_SIZE)

" + + # Añadir información del disco para la consola + CONSOLE_DISK_INFO="${CONSOLE_DISK_INFO}- Passthrough Disk $((i+1)): $DISK ($DISK_MODEL $DISK_SIZE) (${DISK_ID})\n" + done + + + # Preparar descripción HTML + HTML_DESC="
+

Synology DSM Virtual Machine

+

Created with ProxMenuX

+

Loader: $LOADER_NAME

+ ${DISK_INFO} +
" + + # Configurar descripción + result=$(qm set $VMID -description "$HTML_DESC" 2>&1) + if [[ $? -eq 0 ]]; then + msg_ok "Configured VM description" + fi + + +fi + + +if [ "$ERROR_FLAG" = true ]; then + msg_error "VM created with errors. Check configuration." +else + msg_success "Completed Successfully!" + + echo -e "\n${TAB}${GN}Next Steps:${CL}" + echo -e "${TAB}1. Start the VM" + echo -e "${TAB}2. Open the VM console and wait for the loader to boot" + echo -e "${TAB}3. In the loader interface, follow the instructions to select your Synology model" + echo -e "${TAB}4. Complete the DSM installation wizard" + echo -e +fi + +} + +# ========================================================== + + + + + + +# ========================================================== +# Main execution +# ========================================================== +header_info +echo -e "\n Loading..." +sleep 1 + +# Start script +if whiptail --backtitle "ProxMenuX" --title "$NAME" --yesno "This will create a New $NAME. Proceed?" 10 58; then + start_script +else + clear + exit +fi + +# Create VM +create_vm + +# ========================================================== \ No newline at end of file