#!/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 '(?<=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"