2025-03-23 17:50:33 +01:00
#!/usr/bin/env bash
# ==========================================================
# ProxMenuX - Synology DSM VM Creator Script
# ==========================================================
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : MIT (https://raw.githubusercontent.com/MacRimi/ProxMenux/main/LICENSE)
# Version : 1.0
# Last Updated: 13/03/2025
# ==========================================================
# Description:
# This script automates the creation and configuration of a Synology DSM
# (DiskStation Manager) virtual machine (VM) in Proxmox VE. It simplifies the
# setup process by allowing both default and advanced configuration options.
#
# The script automates the complete VM creation process, including loader
# download, disk configuration, and VM boot setup.
#
# **Credits**
# This script is an original idea but incorporates ideas and elements from
# a similar script by user **tim104979** from the ProxmoxVE branch:
# (https://raw.githubusercontent.com/tim104979/ProxmoxVE/refs/heads/main/vm/synology-vm.sh)
#
# Copyright (c) Proxmox VE Helper-Scripts Community
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
#
# ==========================================================
# Configuration ============================================
REPO_URL = "https://raw.githubusercontent.com/MacRimi/ProxMenux/main"
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
# ==========================================================
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/iso"
ERROR_FLAG = false
function exit_script( ) {
clear
if whiptail --backtitle "ProxMenuX" --title " $NAME " --yesno " $( translate " This will create a New $NAME . Proceed? " ) " 10 58; then
start_script
else
clear
exit
fi
}
# Define the header_info function at the beginning of the 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 " $( translate "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 " $( translate "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 " $( translate "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 " $( translate "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 " $( translate "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 " $( translate "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 " $( translate "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 " $( translate "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 " $( translate "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 " $( translate "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 " $( translate "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 " $( translate "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 " $( translate "ADVANCED SETTINGS COMPLETE" ) " --yesno " Ready to create a $NAME ? " --no-button Do-Over 10 58) ; then
echo -e
2025-03-26 20:10:23 +01:00
echo -e " ${ CUS } Creating a $NAME using the above advanced settings ${ CL } "
2025-03-23 17:50:33 +01:00
sleep 1
select_disk_type
else
header_info
sleep 1
2025-03-26 20:10:23 +01:00
echo -e " ${ CUS } Using Advanced Settings ${ CL } "
2025-03-23 17:50:33 +01:00
advanced_settings
fi
}
# ==========================================================
# ==========================================================
# Select Disk
# ==========================================================
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)
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 " $( translate " 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( ) {
VIRTUAL_DISKS = ( )
# Loop to add multiple disks
local add_more_disks = true
while $add_more_disks ; do
msg_info "Detecting available storage volumes..."
# Get list of available storage
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' )
# Check that storage is available
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
# Select storage
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
# Request disk size
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
# Store the configuration in the disk list
VIRTUAL_DISKS += ( " ${ STORAGE } : ${ DISK_SIZE } " )
# Ask if you want to create another disk
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
# Show summary of the created disks
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 = ( )
# Get the list of mounted disks
USED_DISKS = $( lsblk -n -o PKNAME,TYPE | grep 'lvm' | awk '{print "/dev/" $1}' )
MOUNTED_DISKS = $( mount | grep -o '/dev/[a-z]*' | sort | uniq)
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 " $( translate "No disks available for this VM." ) " 8 40
select_disk_type
fi
MAX_WIDTH = $( printf "%s\n" " ${ FREE_DISKS [@] } " | awk '{print length}' | sort -nr | head -n1)
TOTAL_WIDTH = $(( MAX_WIDTH + 20 ))
# 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 \
" $( translate "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 " $( translate "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/"
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/"
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/"
echo -e " ${ DGN } ${ TAB } Selected Loader: ${ BGN } $LOADER_NAME ${ CL } "
download_loader
; ;
4)
LOADER_TYPE = "custom"
LOADER_NAME = "Custom Image"
LOADER_URL = "https://xpenology.com/forum/"
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 " $( translate "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 " $( translate "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 \
2025-03-30 23:16:35 +02:00
| grep "browser_download_url.*arc-.*\.img\.zip" \
2025-03-23 17:50:33 +01:00
| cut -d '"' -f 4 \
2025-03-30 23:16:35 +02:00
| xargs wget -q --show-progress -O " $IMAGES_DIR /arc.img.zip "
2025-03-23 17:50:33 +01:00
2025-03-30 23:16:35 +02:00
if [ -f " $IMAGES_DIR /arc.img.zip " ] ; then
2025-03-23 17:50:33 +01:00
cd " $IMAGES_DIR "
2025-03-30 23:16:35 +02:00
unzip -q arc.img.zip
rm arc.img.zip
2025-03-23 17:50:33 +01:00
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)}' )
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
STORAGE = ${ STORAGE_MENU [0] }
else
kill $SPINNER_PID > /dev/null
while [ -z " ${ STORAGE : +x } " ] ; do
STORAGE = $( whiptail --backtitle "ProxMenuX" --title "EFI Disk Storage" --radiolist \
" $( translate "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)}' )
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
STORAGE = ${ STORAGE_MENU [0] }
else
while [ -z " ${ STORAGE : +x } " ] ; do
STORAGE = $( whiptail --backtitle "ProxMenuX" --title "Storage Pools" --radiolist \
" $( translate " 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 "
# Check if UEFI (OVMF) is being used ===================
if [ [ " $BIOS_TYPE " = = *"ovmf" * ] ] ; then
msg_info "Configuring EFI disk"
EFI_STORAGE = $( select_efi_storage $VMID )
EFI_DISK_NAME = " vm- ${ VMID } -disk-efivars "
# Determine storage type and extension
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
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=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" )
#Run the command in the background and capture its PID
qm importdisk $VMID ${ LOADER_FILE } $LOADER_STORAGE > /tmp/import_log_$VMID .txt 2>& 1 &
import_pid = $!
# Show a simple progress indicator
echo -n "Importing loader disk: "
while kill -0 $import_pid 2>/dev/null; do
echo -n "."
sleep 2.5
done
wait $import_pid
rm -f /tmp/import_log_$VMID .txt
IMPORTED_DISK = $( qm config $VMID | grep -E 'unused[0-9]+' | tail -1 | cut -d: -f1)
# If the disk was not imported correctly, show an error message but continue
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 " -ide0 " ${ LOADER_STORAGE } : ${ DISK_NAME } " 2>& 1 > /dev/null)
if [ [ $? -eq 0 ] ] ; then
msg_ok "Configured loader disk as ide0"
else
ERROR_FLAG = true
fi
result = $( qm set " $VMID " -boot order = ide0 2>& 1)
if [ [ $? -eq 0 ] ] ; then
msg_ok "Loader configured as boot device."
else
ERROR_FLAG = true
fi
# ==========================================================
if [ " $DISK_TYPE " = "virtual" ] ; then
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
IFS = ':' read -r STORAGE SIZE <<< " ${ VIRTUAL_DISKS [ $i ] } "
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 } "
# Create virtual disk
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 "
fi
# Configure disk in the 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 "
fi
msg_ok " Configured virtual disk as $SATA_ID , ${ SIZE } GB on ${ CL } ${ BL } $STORAGE ${ CL } ${ GN } "
# Add information to the description
DISK_INFO = " ${ DISK_INFO } <p>Virtual Disk $DISK_NUM : ${ SIZE } GB on ${ STORAGE } </p> "
CONSOLE_DISK_INFO = " ${ CONSOLE_DISK_INFO } - Virtual Disk $DISK_NUM : ${ SIZE } GB on ${ STORAGE } ( $SATA_ID )\n "
done
# HTML description
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;' >Synology DSM VM</h1>
<p style = 'margin: 0;' >Created with ProxMenuX</p>
<p style = 'margin: 0;' >Loader: $LOADER_NAME </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>
2025-03-23 19:46:51 +01:00
<a href = 'https://raw.githubusercontent.com/MacRimi/ProxMenux/refs/heads/main/scripts/vm/synology.sh' target = '_blank' ><img src = 'https://img.shields.io/badge/💻_Code-green' alt = 'Code' ></a>
2025-03-23 17:50:33 +01:00
<a href = '$LOADER_URL' target = '_blank' ><img src = 'https://img.shields.io/badge/📦_Loader-orange' alt = 'Loader' ></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 "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
# Configure multiple passthrough disks
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 "
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
# Add information to the description
DISK_INFO = " ${ DISK_INFO } <p>Passthrough Disk $(( i+1)) : $DISK ( $DISK_MODEL $DISK_SIZE )</p> "
CONSOLE_DISK_INFO = " ${ CONSOLE_DISK_INFO } - Passthrough Disk $(( i+1)) : $DISK ( $DISK_MODEL $DISK_SIZE ) ( ${ DISK_ID } )\n "
done
# HTML description
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;' >Synology DSM VM</h1>
<p style = 'margin: 0;' >Created with ProxMenuX</p>
<p style = 'margin: 0;' >Loader: $LOADER_NAME </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>
2025-03-23 19:46:51 +01:00
<a href = 'https://raw.githubusercontent.com/MacRimi/ProxMenux/refs/heads/main/scripts/vm/synology.sh' target = '_blank' ><img src = 'https://img.shields.io/badge/💻_Code-green' alt = 'Code' ></a>
2025-03-23 17:50:33 +01:00
<a href = '$LOADER_URL' target = '_blank' ><img src = 'https://img.shields.io/badge/📦_Loader-orange' alt = 'Loader' ></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>"
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 " $( translate "Completed Successfully!" ) "
echo -e " ${ TAB } ${ GN } $( translate "Next Steps:" ) ${ CL } "
echo -e " ${ TAB } 1. $( translate "Start the VM" ) "
echo -e " ${ TAB } 2. $( translate "Open the VM console and wait for the loader to boot" ) "
echo -e " ${ TAB } 3. $( translate "In the loader interface, follow the instructions to select your Synology model" ) "
echo -e " ${ TAB } 4. $( translate "Complete the DSM installation wizard" ) "
echo -e " ${ TAB } 5. $( translate "Find your device using https://finds.synology.com" ) "
echo -e
2025-03-23 17:58:12 +01:00
msg_success " $( translate "Press Enter to return to the main menu..." ) "
read -r
2025-03-23 17:50:33 +01:00
fi
}
# ==========================================================
# ==========================================================
# Main execution
# ==========================================================
header_info
echo -e "\n Loading..."
sleep 1
# Start script
if whiptail --backtitle "ProxMenuX" --title " $NAME " --yesno " $( translate " This will create a New $NAME . Proceed? " ) " 10 58; then
start_script
else
clear
exit
fi
# Create VM
create_vm
# ==========================================================