Files

472 lines
20 KiB
Bash

#!/usr/bin/env bash
set -e
export NEWT_COLORS='
root=,black
window=,black
border=color127,black
title=color178,black
button=white,color127
actbutton=black,color178
compactbutton=black,white
checkbox=color178,black
actcheckbox=black,color178
entry=color45,black
disentry=gray,black
label=white,black
listbox=white,black
actlistbox=black,color45
sellistbox=color178,black
actsellistbox=black,color178
textbox=white,black
acttextbox=black,white
scaleline=color127,black
'
BP=$(echo -e "\033[38;5;127m")
BG=$(echo -e "\033[38;5;178m")
BB=$(echo -e "\033[38;5;45m")
BR=$(echo -e "\033[38;5;196m")
BC=$(echo -e "\033[m")
BOLD=$(echo -e "\033[1m")
TAB=" "
CM="${TAB}${BG}${BC}${TAB}"
CROSS="${TAB}${BR}${BC}${TAB}"
INFO="${TAB}${BB}💡${BC}${TAB}"
# build.func
WHIPTITLE="Dynasty Revolution Provisioning"
APP="ARR Stack"
NSAPP="arr-stack"
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
trap cleanup EXIT
function error_handler() {
local exit_code="$?"
local line_number="$1"
local command="$2"
echo -e "\n${CROSS}${BR}[ERROR]${BC} Line ${line_number} | Exit: ${exit_code} | Cmd: ${command}\n"
cleanup_vmid
}
function header_info() {
clear
echo -ne "${BP}${BOLD}"
cat <<"EOF"
_ ____ ____ ____ _____ _ ____ _ __ __ ____ __
/ \ | _ \| _ \ / ___|_ _|/ \ / ___| |/ / \ \ / / \/ |
/ _ \ | |_) | |_) | \___ \ | | / _ \| | | ' / \ \ / /| |\/| |
/ ___ \| _ <| _ < ___) || |/ ___ \ |___| . \ \ V / | | | |
/_/ \_\_| \_\_| \_\ |____/ |_/_/ \_\____|_|\_\ \_/ |_| |_|
Debian 13(Trixie)
EOF
echo -e "${BG} DYNASTY PROVISIONING${BC}\n"
}
function fetch_wotd() {
local wotd_html=$(curl -s --connect-timeout 3 https://www.merriam-webster.com/word-of-the-day)
local word=$(echo "$wotd_html" | grep -oP '(?<=<title>Word of the Day: ).*?(?= \|)' | head -n1)
local def=$(echo "$wotd_html" | grep -A 1 "<h2>What It Means</h2>" | tail -n1 | sed 's/<[^>]*>//g' | xargs)
if [ -n "$word" ]; then
echo -e "${BG}Word of the Day: ${BB}${word}${BC}"
echo -e "${TAB}${def}\n"
fi
}
function cleanup_vmid() {
if qm status $VMID &>/dev/null; then
qm stop $VMID &>/dev/null
qm destroy $VMID &>/dev/null
fi
}
function cleanup() {
popd >/dev/null 2>&1 || true
if [[ -n "$TEMP_DIR" && "$TEMP_DIR" != "/" ]]; then
rm -rf "$TEMP_DIR"
fi
}
function get_valid_nextid() {
local try_id
try_id=$(pvesh get /cluster/nextid)
while [ -f "/etc/pve/qemu-server/${try_id}.conf" ] || [ -f "/etc/pve/lxc/${try_id}.conf" ]; do
try_id=$((try_id + 1))
done
echo "$try_id"
}
function remote_provisioning_ui() {
local title="$WHIPTITLE"
local prompt="$1"
local default="$2"
if command -v osascript &>/dev/null && [[ "$OSTYPE" == "darwin"* ]]; then
osascript -e "display dialog \"$prompt\" default answer \"$default\" with title \"$title\" buttons {\"Cancel\", \"Next\"} default button \"Next\"" -e "text returned of result" 2>/dev/null
elif command -v whiptail &>/dev/null; then
whiptail --backtitle "$title" --title "INPUT" --inputbox "$prompt" 10 58 "$default" 3>&1 1>&2 2>&3
else
read -p "$prompt [$default]: " val </dev/tty
echo "${val:-$default}"
fi
}
function Beginning_Remote_Provisioning() {
local target_host=$(remote_provisioning_ui "Enter Proxmox IP/FQDN:" "")
[ -z "$target_host" ] && exit
local target_user=$(remote_provisioning_ui "Enter SSH User:" "root")
[ -z "$target_user" ] && exit
header_info
echo -e "${INFO}${BB}Beginning Remote Provisioning to ${target_host}...${BC}"
}
header_info
if ! command -v pveversion >/dev/null 2>&1; then
Beginning_Remote_Provisioning
exit
fi
if ! command -v virt-customize &>/dev/null; then
echo -e "${INFO}${BB}Installing Provisioning Tools 🛠️....${BC}"
apt-get update >/dev/null 2>&1
apt-get install -y libguestfs-tools >/dev/null 2>&1
fi
if whiptail --backtitle "$WHIPTITLE" --title "ARR STACK DEPLOYMENT" --yesno "Continue Arr Stack VM Deployment?" 10 58; then
:
else
header_info && echo -e "${CROSS}${BR}User exited script.${BC}\n" && exit
fi
CHOICES=$(whiptail --backtitle "$WHIPTITLE" --title "ARR STACK SELECTION" --checklist \
"Select modules to install:" 20 65 10 \
"RADARR" "Radarr (7878)" ON \
"RADARR4K" "Radarr 4K (7879)" OFF \
"SONARR" "Sonarr (8989)" ON \
"SONARR4K" "Sonarr 4K (8990)" OFF \
"QBT" "Qbittorrent (8090)" ON \
"SABNZBD" "Sabnzbd (7777)" OFF \
"NZBGET" "NZBGet (6789)" OFF \
"GLUETUN" "VPN Subsystem" OFF \
"PORTAINER" "Portainer UI (9443)" ON 3>&1 1>&2 2>&3)
INSTALL_METHOD=$(whiptail --backtitle "$WHIPTITLE" --title "INSTALLATION METHOD" --radiolist \
"Select Installation Method:" 12 58 2 \
"DOCKER" "Docker Compose (Dynamic)" ON \
"BINARY" "Native Debian Binaries" OFF 3>&1 1>&2 2>&3)
VMID=$(get_valid_nextid)
DISK_SIZE_NUM="100"
HN="ARR-Stack"
CORE_COUNT="6"
RAM_SIZE="10240"
BRG="vmbr0"
VLAN=""
START_VM="yes"
CLOUD_INIT="no"
AUTOLOGIN="no"
ROOT_PASS=""
SSH_KEY=""
if whiptail --backtitle "$WHIPTITLE" --title "SETTINGS" --yesno "Provisioning with Defaults for ${HN}? (100GB Disk Recommended)" --yes-button "Default" --no-button "Advanced" 10 65; then
DISK_SIZE_NUM="100"
BRG=$(whiptail --backtitle "$WHIPTITLE" --inputbox "Network Bridge" 8 58 "vmbr0" --title "BRIDGE" 3>&1 1>&2 2>&3)
VLAN_ID=$(whiptail --backtitle "$WHIPTITLE" --inputbox "VLAN Tag" 8 58 "" --title "VLAN" 3>&1 1>&2 2>&3)
[ -n "$VLAN_ID" ] && VLAN=",tag=$VLAN_ID"
if whiptail --backtitle "$WHIPTITLE" --title "SSH" --yesno "Inject SSH Public Key?" 10 58; then
SSH_KEY=$(whiptail --backtitle "$WHIPTITLE" --inputbox "Paste Public Key:" 10 58 --title "SSH KEY" 3>&1 1>&2 2>&3)
fi
if whiptail --backtitle "$WHIPTITLE" --title "CLOUD-INIT" --yesno "Enable Cloud-Init?" 10 58; then
CLOUD_INIT="yes"
else
if whiptail --backtitle "$WHIPTITLE" --title "AUTO LOGIN" --yesno "Enable Passwordless Auto-login?" 10 58; then
AUTOLOGIN="yes"
else
ROOT_PASS=$(whiptail --backtitle "$WHIPTITLE" --passwordbox "Set Root Password" 8 58 --title "PASSWORD" 3>&1 1>&2 2>&3)
fi
fi
else
VMID=$(whiptail --backtitle "$WHIPTITLE" --inputbox "Set VM ID" 8 58 "$VMID" --title "VM ID" 3>&1 1>&2 2>&3)
HN=$(whiptail --backtitle "$WHIPTITLE" --inputbox "Set Hostname" 8 58 "arr-stack" --title "HOSTNAME" 3>&1 1>&2 2>&3)
CORE_COUNT=$(whiptail --backtitle "$WHIPTITLE" --inputbox "CPU Cores" 8 58 "6" --title "CORES" 3>&1 1>&2 2>&3)
RAM_SIZE=$(whiptail --backtitle "$WHIPTITLE" --inputbox "RAM MiB" 8 58 "10240" --title "RAM" 3>&1 1>&2 2>&3)
DISK_SIZE_NUM=$(whiptail --backtitle "$WHIPTITLE" --inputbox "Disk Size GiB" 8 58 "100" --title "DISK" 3>&1 1>&2 2>&3)
BRG=$(whiptail --backtitle "$WHIPTITLE" --inputbox "Network Bridge" 8 58 "vmbr0" --title "BRIDGE" 3>&1 1>&2 2>&3)
VLAN_ID=$(whiptail --backtitle "$WHIPTITLE" --inputbox "VLAN Tag" 8 58 "" --title "VLAN" 3>&1 1>&2 2>&3)
[ -n "$VLAN_ID" ] && VLAN=",tag=$VLAN_ID"
if whiptail --backtitle "$WHIPTITLE" --title "SSH" --yesno "Inject SSH Public Key?" 10 58; then
SSH_KEY=$(whiptail --backtitle "$WHIPTITLE" --inputbox "Paste Public Key:" 10 58 --title "SSH KEY" 3>&1 1>&2 2>&3)
fi
if whiptail --backtitle "$WHIPTITLE" --title "CLOUD-INIT" --yesno "Enable Cloud-Init?" 10 58; then
CLOUD_INIT="yes"
else
if whiptail --backtitle "$WHIPTITLE" --title "AUTO LOGIN" --yesno "Enable Passwordless Auto-login?" 10 58; then
AUTOLOGIN="yes"
else
ROOT_PASS=$(whiptail --backtitle "$WHIPTITLE" --passwordbox "Set Root Password" 8 58 --title "PASSWORD" 3>&1 1>&2 2>&3)
fi
fi
fi
DISK_SIZE="${DISK_SIZE_NUM}G"
while true; do
STORAGE_MENU=()
while read -r line; do
TAG=$(echo "$line" | awk '{print $1}')
TYPE=$(echo "$line" | awk '{printf "%-10s", $2}')
FREE_HUMAN=$(echo "$line" | numfmt --field 4-6 --from-unit=K --to=iec --format %.2f | awk '{printf( "%9sB", $6)}')
STORAGE_MENU+=("$TAG" "Type: $TYPE | Free: $FREE_HUMAN" "OFF")
done < <(pvesm status -content images | awk 'NR>1')
STORAGE=$(whiptail --backtitle "$WHIPTITLE" --title "STORAGE" --radiolist "Select storage (Min ${DISK_SIZE} Required):" 16 60 6 "${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
[ -z "$STORAGE" ] && exit 1
STORAGE_FREE_KB=$(pvesm status -storage "$STORAGE" | awk 'NR==2 {print $6}')
REQUIRED_KB=$((DISK_SIZE_NUM * 1024 * 1024))
if [ "$STORAGE_FREE_KB" -lt "$REQUIRED_KB" ]; then
FREE_GB=$((STORAGE_FREE_KB / 1024 / 1024))
whiptail --backtitle "$WHIPTITLE" --title "INSUFFICIENT SPACE" --msgbox "Selected storage ($STORAGE) only has ${FREE_GB}GB available.\nDeployment requires ${DISK_SIZE_NUM}GB." 12 58
else
break
fi
done
TEMP_DIR=$(mktemp -d -p /opt)
pushd "$TEMP_DIR" >/dev/null
header_info
echo -e "${INFO}${BB}Loading Provision Method...${BC}"
cat <<EOF > dynr-arrstack-setup.sh
#!/usr/bin/env bash
apt-get update
apt-get install -y curl sudo gnupg2 ca-certificates lsb-release sqlite3 jq tar p7zip-full unrar-free mono-devel golang-go openvpn wireguard-tools iptables python3-venv git qemu-guest-agent
mkdir -p /mnt/arr /opt/DYNR
touch /opt/DYNR/urls.txt
cd /opt
systemctl enable --now qemu-guest-agent
if [ "$INSTALL_METHOD" == "DOCKER" ]; then
curl -fsSL https://get.docker.com | sh
mkdir -p /opt/Docker
cat <<DYNR > /opt/Docker/docker-compose.yml
services:
DYNR
function add_docker_service() {
local name=\$1
local image=\$2
local port=\$3
local url_port=\${port%%:*}
mkdir -p /opt/Docker/\$name/config /mnt/arr/\$name/data
cat <<DYNR >> /opt/Docker/docker-compose.yml
\$name:
image: \$image
container_name: \$name
environment:
- PUID=1000
- PGID=1000
- TZ=UTC
volumes:
- /opt/Docker/\$name/config:/config
- /mnt/arr/\$name/data:/data
ports:
- \$port
restart: unless-stopped
DYNR
echo "\$name: http://localhost:\$url_port" >> /opt/DYNR/urls.txt
}
[[ "$CHOICES" == *"RADARR"* ]] && add_docker_service "radarr" "lscr.io/linuxserver/radarr:latest" "7878:7878"
[[ "$CHOICES" == *"RADARR4K"* ]] && add_docker_service "radarr4k" "lscr.io/linuxserver/radarr:latest" "7879:7878"
[[ "$CHOICES" == *"SONARR"* ]] && add_docker_service "sonarr" "lscr.io/linuxserver/sonarr:latest" "8989:8989"
[[ "$CHOICES" == *"SONARR4K"* ]] && add_docker_service "sonarr4k" "lscr.io/linuxserver/sonarr:latest" "8990:8989"
[[ "$CHOICES" == *"QBT"* ]] && add_docker_service "qbittorrent" "lscr.io/linuxserver/qbittorrent:latest" "8090:8090"
[[ "$CHOICES" == *"SABNZBD"* ]] && add_docker_service "sabnzbd" "lscr.io/linuxserver/sabnzbd:latest" "7777:7777"
[[ "$CHOICES" == *"NZBGET"* ]] && add_docker_service "nzbget" "lscr.io/linuxserver/nzbget:latest" "6789:6789"
[[ "$CHOICES" == *"GLUETUN"* ]] && add_docker_service "gluetun" "qdm12/gluetun" "8000:8000"
if [[ "$CHOICES" == *"PORTAINER"* ]]; then
docker volume create portainer_data
docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/var/lib/portainer portainer/portainer-ce:latest
echo "Portainer: https://localhost:9443" >> /opt/DYNR/urls.txt
fi
cd /opt/Docker && docker compose up -d
else
# Binary Installation Logic (FULL EXPANSION)
[[ "$CHOICES" == *"RADARR"* ]] && { mkdir -p /opt/Radarr/config; curl -L "https://radarr.servarr.com/v1/update/master/updatefile?os=linux&runtime=netcore&arch=x64" -o radarr.tar.gz; tar -xzf radarr.tar.gz -C /opt/; cat <<DYNR > /etc/systemd/system/radarr.service
[Unit]
Description=Radarr Daemon
After=network.target
[Service]
Type=simple
ExecStart=/opt/Radarr/Radarr -nobrowser -data=/opt/Radarr/config
Restart=always
[Install]
WantedBy=multi-user.target
DYNR
systemctl enable --now radarr; echo "Radarr: http://localhost:7878" >> /opt/DYNR/urls.txt; }
[[ "$CHOICES" == *"RADARR4K"* ]] && { mkdir -p /opt/Radarr4K/config; curl -L "https://radarr.servarr.com/v1/update/master/updatefile?os=linux&runtime=netcore&arch=x64" -o radarr4k.tar.gz; tar -xzf radarr4k.tar.gz -C /opt/; mv /opt/Radarr /opt/Radarr4K; cat <<DYNR > /etc/systemd/system/radarr4k.service
[Unit]
Description=Radarr 4K Daemon
After=network.target
[Service]
Type=simple
ExecStart=/opt/Radarr4K/Radarr -nobrowser -data=/opt/Radarr4K/config
Restart=always
[Install]
WantedBy=multi-user.target
DYNR
systemctl enable --now radarr4k; sleep 5; sed -i "s|<Port>7878</Port>|<Port>7879</Port>|g" /opt/Radarr4K/config/config.xml || true; systemctl restart radarr4k; echo "Radarr 4K: http://localhost:7879" >> /opt/DYNR/urls.txt; }
[[ "$CHOICES" == *"SONARR"* ]] && { mkdir -p /opt/Sonarr/config; curl -L "https://services.sonarr.tv/v1/update/main/updatefile?os=linux&runtime=netcore&arch=x64" -o sonarr.tar.gz; tar -xzf sonarr.tar.gz -C /opt/; cat <<DYNR > /etc/systemd/system/sonarr.service
[Unit]
Description=Sonarr Daemon
After=network.target
[Service]
Type=simple
ExecStart=/opt/Sonarr/Sonarr -nobrowser -data=/opt/Sonarr/config
Restart=always
[Install]
WantedBy=multi-user.target
DYNR
systemctl enable --now sonarr; echo "Sonarr: http://localhost:8989" >> /opt/DYNR/urls.txt; }
[[ "$CHOICES" == *"SONARR4K"* ]] && { mkdir -p /opt/Sonarr4K/config; curl -L "https://services.sonarr.tv/v1/update/main/updatefile?os=linux&runtime=netcore&arch=x64" -o sonarr4k.tar.gz; tar -xzf sonarr4k.tar.gz -C /opt/; mv /opt/Sonarr /opt/Sonarr4K; cat <<DYNR > /etc/systemd/system/sonarr4k.service
[Unit]
Description=Sonarr 4K Daemon
After=network.target
[Service]
Type=simple
ExecStart=/opt/Sonarr4K/Sonarr -nobrowser -data=/opt/Sonarr4K/config
Restart=always
[Install]
WantedBy=multi-user.target
DYNR
systemctl enable --now sonarr4k; sleep 5; sed -i "s|<Port>8989</Port>|<Port>8990</Port>|g" /opt/Sonarr4K/config/config.xml || true; systemctl restart sonarr4k; echo "Sonarr 4K: http://localhost:8990" >> /opt/DYNR/urls.txt; }
[[ "$CHOICES" == *"QBT"* ]] && { mkdir -p /opt/qbittorrent/config; curl -L "https://github.com/userdocs/qbittorrent-nox-static/releases/latest/download/x86_64-qbittorrent-nox" -o /opt/qbittorrent-nox; chmod +x /opt/qbittorrent-nox; mv /opt/qbittorrent-nox /opt/qbittorrent/qbittorrent-nox; cat <<DYNR > /etc/systemd/system/qbittorrent-nox.service
[Unit]
Description=qBittorrent client
After=network.target
[Service]
Type=simple
ExecStart=/opt/qbittorrent/qbittorrent-nox --profile=/opt/qbittorrent/config
Restart=always
[Install]
WantedBy=multi-user.target
DYNR
systemctl enable --now qbittorrent-nox; echo "Qbittorrent: http://localhost:8090" >> /opt/DYNR/urls.txt; }
[[ "$CHOICES" == *"SABNZBD"* ]] && { mkdir -p /opt/sabnzbd/config; cat <<DYNR >/etc/apt/sources.list.d/non-free.sources
Types: deb
URIs: http://deb.debian.org/debian/
Suites: trixie
Components: non-free
Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
DYNR
apt update && apt install -y unrar p7zip-full; python3 -m venv /opt/sabnzbd/venv; curl -L "https://github.com/sabnzbd/sabnzbd/releases/latest/download/SABnzbd-src.tar.gz" | tar xz -C /opt/sabnzbd --strip-components=1; /opt/sabnzbd/venv/bin/pip install -r /opt/sabnzbd/requirements.txt; cat <<DYNR > /etc/systemd/system/sabnzbd.service
[Unit]
Description=SABnzbd
After=network.target
[Service]
WorkingDirectory=/opt/sabnzbd
ExecStart=/opt/sabnzbd/venv/bin/python SABnzbd.py -s 0.0.0.0:7777 -f /opt/sabnzbd/config
Restart=always
[Install]
WantedBy=multi-user.target
DYNR
systemctl enable --now sabnzbd; echo "Sabnzbd: http://localhost:7777" >> /opt/DYNR/urls.txt; }
[[ "$CHOICES" == *"NZBGET"* ]] && { mkdir -p /opt/nzbget; curl -fsSL https://nzbgetcom.github.io/nzbgetcom.asc | gpg --dearmor -o /usr/share/keyrings/nzbget.gpg; echo "deb [signed-by=/usr/share/keyrings/nzbget.gpg] https://nzbgetcom.github.io/deb stable main" > /etc/apt/sources.list.d/nzbget.list; apt update && apt install -y nzbget; systemctl enable --now nzbget; echo "NZBGet: http://localhost:6789" >> /opt/DYNR/urls.txt; }
[[ "$CHOICES" == *"GLUETUN"* ]] && { mkdir -p /opt/gluetun-data; git clone https://github.com/qdm12/gluetun.git /opt/gluetun; cd /opt/gluetun && CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /usr/local/bin/gluetun ./cmd/gluetun/; cat <<DYNR > /etc/systemd/system/gluetun.service
[Unit]
Description=Gluetun VPN Client
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/gluetun-data
ExecStart=/usr/local/bin/gluetun
Restart=on-failure
[Install]
WantedBy=multi-user.target
DYNR
systemctl enable --now gluetun; echo "Gluetun (Control): http://localhost:8000" >> /opt/DYNR/urls.txt; }
fi
EOF
URL=https://cloud.debian.org/images/cloud/trixie/latest/debian-13-nocloud-amd64.qcow2
curl -f#SL -o "debian13.qcow2" "$URL"
FILE="debian13.qcow2"
echo -e "${INFO}${BP}Customizing Disk Metadata...${BC}"
virt-customize -q -a "$FILE" --hostname "${HN}"
[ -n "$ROOT_PASS" ] && virt-customize -q -a "$FILE" --root-password password:"$ROOT_PASS" >/dev/null 2>&1
[ -n "$SSH_KEY" ] && virt-customize -q -a "$FILE" --ssh-inject root:string:"$SSH_KEY" >/dev/null 2>&1
if [ "$CLOUD_INIT" == "no" ] && [ "$AUTOLOGIN" == "yes" ]; then
virt-customize -q -a "$FILE" --run-command "mkdir -p /etc/systemd/system/serial-getty@ttyS0.service.d" >/dev/null 2>&1
virt-customize -q -a "$FILE" --run-command 'cat > /etc/systemd/system/serial-getty@ttyS0.service.d/autologin.conf << EOF
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM
EOF' >/dev/null 2>&1
virt-customize -q -a "$FILE" --run-command "mkdir -p /etc/systemd/system/getty@tty1.service.d" >/dev/null 2>&1
virt-customize -q -a "$FILE" --run-command 'cat > /etc/systemd/system/getty@tty1.service.d/autologin.conf << EOF
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root --noclear %I \$TERM
EOF' >/dev/null 2>&1
fi
virt-customize -q -a "$FILE" --upload dynr-arrstack-setup.sh:/opt/dynr-arrstack-setup.sh
virt-customize -q -a "$FILE" --run-command "chmod +x /opt/dynr-arrstack-setup.sh"
virt-customize -q -a "$FILE" --firstboot /opt/dynr-arrstack-setup.sh
STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}')
case $STORAGE_TYPE in
nfs|dir) DISK_EXT=".qcow2"; DISK_IMPORT="-format qcow2"; DISK_REF="${VMID}/";;
*) DISK_EXT=""; DISK_IMPORT="-format raw"; DISK_REF="" ;;
esac
echo -e "${INFO}${BB}Welding Structure....${BC}"
qm create $VMID -agent 1 -tablet 0 -localtime 1 -bios ovmf -cores $CORE_COUNT -memory $RAM_SIZE \
-name "$HN" -tags DYNR-STACK,Debian13 -net0 virtio,bridge=$BRG$VLAN -onboot 1 -ostype l26 -scsihw virtio-scsi-pci
pvesm alloc $STORAGE $VMID vm-$VMID-disk-0${DISK_EXT} 4M >/dev/null
qm importdisk $VMID "$FILE" $STORAGE ${DISK_IMPORT} >/dev/null
qm set $VMID -efidisk0 ${STORAGE}:${DISK_REF}vm-$VMID-disk-0${DISK_EXT},efitype=4m -scsi0 ${STORAGE}:${DISK_REF}vm-$VMID-disk-1${DISK_EXT},size=${DISK_SIZE} -boot order=scsi0 -serial0 socket >/dev/null
header_info
echo -e "${CM}${BG}Provision Successful: ${HN}${BC}"
qm start $VMID
echo -e "${INFO}${BB}⌚️ Checking Network Connectivity....${BC}"
fetch_wotd
VM_IP="localhost"
ITERATION=0
while [ $ITERATION -lt 15 ]; do
if qm guest exec $VMID -- ping -c 1 -W 2 1.1.1.1 &>/dev/null; then
NEW_IP=$(qm guest network-get-interfaces $VMID --output-format json 2>/dev/null | jq -r '.[] | select(.name=="eth0") | .["ip-addresses"][] | select(.["ip-address-type"]=="ipv4") | .["ip-address"]' | cut -d/ -f1 | head -n 1)
if [ -n "$NEW_IP" ]; then
VM_IP="$NEW_IP"
echo -e "${CM}${BG}✅ Network Connection Succeeded${BC}"
echo -e "${TAB}IP Address of ${HN}: ${VM_IP} v4\n"
break
fi
fi
sleep 10
ITERATION=$((ITERATION+1))
done
[ "$VM_IP" == "localhost" ] && echo -e "${CROSS}${BR}❌ Network Connection Failed${BC}"
echo -e "\n${BB}Access Using IP Provided with App Specific Port:${BC}"
echo -e "${TAB}Portainer: ${BB}https://${VM_IP}:9443${BC}"
echo -e "${TAB} Run 'cat /opt/DYNR/urls.txt' to get ports of Installed apps.${BC}"
echo -e "\n${BG}${BOLD}COMPLETED SUCCESSFULLY! She is firing on all cylinders.${BC}\n"