mirror of
https://github.com/community-scripts/ProxmoxVE.git
synced 2026-03-03 17:35:55 +01:00
* Standardize exit codes and add mappings Replace generic exit 1 usages with specific numeric exit codes and add corresponding explanations to the error lookup. This commit updates multiple misc/* scripts to return distinct codes for validation, Proxmox/LXC, networking, download and curl errors (e.g. 103-123, 64, 107-120, 206, 0 for explicit user cancels). It also updates curl error handling to propagate the original curl exit code and adds new entries in explain_exit_code and the error handler to improve diagnostics. * Set exit code 115 for update_os errors Change exit status from 6 to 115 in misc/alpine-install.func's update_os() error handlers when failing to download tools.func or when the expected functions are missing. This gives a distinct exit code for these specific failure cases. * Add tools/addon exit codes and use them Introduce exit codes 232-238 for Tools & Addon scripts in misc/api.func and misc/error_handler.func. Update addon scripts (tools/addon/adguardhome-sync.sh, tools/addon/copyparty.sh, tools/addon/cronmaster.sh) to return specific codes instead of generic exit 1: 238 for unsupported OS and 233 when the application is not installed/upgrade prerequisites are missing. This makes failures more descriptive and aligns scripts with the central error explanations. * Standardize exit codes in exporter addons Unify exit codes across exporter addon scripts: return 238 for unsupported OS detections and 233 when an update is requested but the exporter is not installed. Applied to nextcloud-exporter.sh, pihole-exporter.sh, prometheus-paperless-ngx-exporter.sh, and qbittorrent-exporter.sh to make failure modes distinguishable for callers/automation. * Use specific exit codes in addon scripts Replace generic exit 1 with distinct exit codes across multiple addon scripts to enable finer-grained error handling in automation. Exit codes introduced: 10 for Docker/Compose missing or user-declined Docker install, 233 for "nothing to update" cases, and 238 for unsupported OS cases. Affected files: tools/addon/arcane.sh, coolify.sh, dockge.sh, dokploy.sh, filebrowser-quantum.sh, filebrowser.sh, immich-public-proxy.sh, jellystat.sh, runtipi.sh. * Use specific exit codes in addon scripts Replace generic exit 1 with specific exit codes across multiple addon scripts to improve error signaling and handling. Files updated: tools/addon/add-netbird-lxc.sh (exit 238 on unsupported distro), tools/addon/add-tailscale-lxc.sh (treat user cancel as exit 0), tools/addon/glances.sh (exit 233 when not installed), tools/addon/komodo.sh (distinct exits for missing compose, legacy DB, backup/download failures, docker checks), tools/addon/netdata.sh (distinct exits for unsupported PVE versions, OS/codename detection, repo lookups), and tools/addon/phpmyadmin.sh (distinct exits for unsupported OS, network/download issues, package install/start failures, and invalid input). These changes make failures easier to identify and automate recovery or reporting. * Use specific exit codes in PVE scripts Replace generic exit 1 with distinct exit codes across tools/pve scripts to provide clearer failure signals for callers. post-pve-install.sh now returns 105 for unsupported Proxmox versions; pve-privilege-converter.sh uses 104 for non-root, 234 when no containers, and 235 for backup/conversion failures; update-apps.sh maps backup failures to 235, missing containers/selections to 234 (and UI cancellations to 0), missing backup storage to 119, and returns the actual container update exit code on failure. These changes improve diagnostics and allow external tooling to react to specific error conditions. * Standardize exit codes and behaviors Adjust exit codes and abort handling across multiple PVE helper scripts to provide clearer outcomes for automation and interactive flows. Changes include: - container-restore-from-backup.sh, core-restore-from-backup.sh: return 235 when no backups found (was 1). - fstrim.sh: treat user cancellation of non-ext4 warning as non-error (exit 0 instead of 1). - kernel-clean.sh: treat no selection or user abort as non-error (exit 0 instead of 1). - lxc-delete.sh: return 234 when no containers are present; treat no selection as non-error (exit 0). - nic-offloading-fix.sh: use specific non-zero codes for root check and tool install failures (exit 104, 237) and 236 when no matching interfaces (was 1). - pbs_microcode.sh, post-pmg-install.sh, post-pbs-install.sh: use distinct exit codes (232 and 105) for detected VM/PVE/unsupported distro conditions instead of generic 1. These modifications make scripts return distinct codes for different failure modes and ensure user-initiated aborts or benign conditions exit with 0 where appropriate. * Use exit 105 for unsupported PVE versions Standardize error handling by replacing generic exit 1 with exit 105 in pve_check() across multiple VM template scripts to indicate unsupported Proxmox VE versions. Also add API exit code 226 message for "Proxmox: VM disk import or post-creation setup failed" in misc/api.func. Affected files include misc/api.func and various vm/*-vm.sh scripts. * Use specific exit codes in VM scripts Replace generic exit 1 with distinct exit codes across vm/*.sh to make failures more actionable for callers. Changes include: use 226 for missing imported-disk references, 237 for pv installation failures, 115 for download/extract/ISO-related failures, 214 for insufficient disk space during FreeBSD decompression, and 119 for missing storage detection. Updated scripts: archlinux-vm.sh, docker-vm.sh, haos-vm.sh, openwrt-vm.sh, opnsense-vm.sh, truenas-vm.sh, umbrel-os-vm.sh.
717 lines
25 KiB
Bash
717 lines
25 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# Copyright (c) 2021-2026 community-scripts ORG
|
|
# Author: thost96 (thost96) | michelroegl-brunner | MickLesk
|
|
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
|
|
|
|
# ==============================================================================
|
|
# Docker VM - Creates a Docker-ready Virtual Machine
|
|
# ==============================================================================
|
|
|
|
source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/api.func) 2>/dev/null
|
|
source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/vm-core.func) 2>/dev/null
|
|
source <(curl -fsSL https://git.community-scripts.org/community-scripts/ProxmoxVE/raw/branch/main/misc/cloud-init.func) 2>/dev/null || true
|
|
load_functions
|
|
|
|
# ==============================================================================
|
|
# SCRIPT VARIABLES
|
|
# ==============================================================================
|
|
APP="Docker"
|
|
APP_TYPE="vm"
|
|
NSAPP="docker-vm"
|
|
var_os="debian"
|
|
var_version="13"
|
|
|
|
GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//')
|
|
RANDOM_UUID="$(cat /proc/sys/kernel/random/uuid)"
|
|
METHOD=""
|
|
DISK_SIZE="10G"
|
|
USE_CLOUD_INIT="no"
|
|
OS_TYPE=""
|
|
OS_VERSION=""
|
|
THIN="discard=on,ssd=1,"
|
|
|
|
# ==============================================================================
|
|
# ERROR HANDLING & CLEANUP
|
|
# ==============================================================================
|
|
set -e
|
|
trap 'error_handler $LINENO "$BASH_COMMAND"' ERR
|
|
trap cleanup EXIT
|
|
trap 'post_update_to_api "failed" "130"' SIGINT
|
|
trap 'post_update_to_api "failed" "143"' SIGTERM
|
|
trap 'post_update_to_api "failed" "129"; exit 129' SIGHUP
|
|
|
|
function error_handler() {
|
|
local exit_code="$?"
|
|
local line_number="$1"
|
|
local command="$2"
|
|
local error_message="${RD}[ERROR]${CL} in line ${RD}$line_number${CL}: exit code ${RD}$exit_code${CL}: while executing command ${YW}$command${CL}"
|
|
post_update_to_api "failed" "${exit_code}"
|
|
echo -e "\n$error_message\n"
|
|
cleanup_vmid
|
|
}
|
|
|
|
# ==============================================================================
|
|
# OS SELECTION FUNCTIONS
|
|
# ==============================================================================
|
|
function select_os() {
|
|
if OS_CHOICE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SELECT OS" --radiolist \
|
|
"Choose Operating System for Docker VM" 14 68 4 \
|
|
"debian13" "Debian 13 (Trixie) - Latest" ON \
|
|
"debian12" "Debian 12 (Bookworm) - Stable" OFF \
|
|
"ubuntu2404" "Ubuntu 24.04 LTS (Noble)" OFF \
|
|
"ubuntu2204" "Ubuntu 22.04 LTS (Jammy)" OFF \
|
|
3>&1 1>&2 2>&3); then
|
|
case $OS_CHOICE in
|
|
debian13)
|
|
OS_TYPE="debian"
|
|
OS_VERSION="13"
|
|
OS_CODENAME="trixie"
|
|
OS_DISPLAY="Debian 13 (Trixie)"
|
|
;;
|
|
debian12)
|
|
OS_TYPE="debian"
|
|
OS_VERSION="12"
|
|
OS_CODENAME="bookworm"
|
|
OS_DISPLAY="Debian 12 (Bookworm)"
|
|
;;
|
|
ubuntu2404)
|
|
OS_TYPE="ubuntu"
|
|
OS_VERSION="24.04"
|
|
OS_CODENAME="noble"
|
|
OS_DISPLAY="Ubuntu 24.04 LTS"
|
|
;;
|
|
ubuntu2204)
|
|
OS_TYPE="ubuntu"
|
|
OS_VERSION="22.04"
|
|
OS_CODENAME="jammy"
|
|
OS_DISPLAY="Ubuntu 22.04 LTS"
|
|
;;
|
|
esac
|
|
echo -e "${OS}${BOLD}${DGN}Operating System: ${BGN}${OS_DISPLAY}${CL}"
|
|
else
|
|
exit_script
|
|
fi
|
|
}
|
|
|
|
function select_cloud_init() {
|
|
if [ "$OS_TYPE" = "ubuntu" ]; then
|
|
USE_CLOUD_INIT="yes"
|
|
echo -e "${CLOUD:-${TAB}☁️${TAB}${CL}}${BOLD}${DGN}Cloud-Init: ${BGN}yes (Ubuntu requires Cloud-Init)${CL}"
|
|
return
|
|
fi
|
|
|
|
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "CLOUD-INIT" \
|
|
--yesno "Enable Cloud-Init for VM configuration?\n\nCloud-Init allows automatic configuration of:\n- User accounts and passwords\n- SSH keys\n- Network settings (DHCP/Static)\n- DNS configuration\n\nYou can also configure these settings later in Proxmox UI.\n\nNote: Debian without Cloud-Init will use nocloud image with console auto-login." 18 68); then
|
|
USE_CLOUD_INIT="yes"
|
|
echo -e "${CLOUD:-${TAB}☁️${TAB}${CL}}${BOLD}${DGN}Cloud-Init: ${BGN}yes${CL}"
|
|
else
|
|
USE_CLOUD_INIT="no"
|
|
echo -e "${CLOUD:-${TAB}☁️${TAB}${CL}}${BOLD}${DGN}Cloud-Init: ${BGN}no${CL}"
|
|
fi
|
|
}
|
|
|
|
function get_image_url() {
|
|
local arch=$(dpkg --print-architecture)
|
|
case $OS_TYPE in
|
|
debian)
|
|
if [ "$USE_CLOUD_INIT" = "yes" ]; then
|
|
echo "https://cloud.debian.org/images/cloud/${OS_CODENAME}/latest/debian-${OS_VERSION}-generic-${arch}.qcow2"
|
|
else
|
|
echo "https://cloud.debian.org/images/cloud/${OS_CODENAME}/latest/debian-${OS_VERSION}-nocloud-${arch}.qcow2"
|
|
fi
|
|
;;
|
|
ubuntu)
|
|
echo "https://cloud-images.ubuntu.com/${OS_CODENAME}/current/${OS_CODENAME}-server-cloudimg-${arch}.img"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# ==============================================================================
|
|
# SETTINGS FUNCTIONS
|
|
# ==============================================================================
|
|
function default_settings() {
|
|
select_os
|
|
select_cloud_init
|
|
|
|
VMID=$(get_valid_nextid)
|
|
FORMAT=""
|
|
MACHINE=" -machine q35"
|
|
DISK_CACHE=""
|
|
DISK_SIZE="10G"
|
|
HN="docker"
|
|
CPU_TYPE=" -cpu host"
|
|
CORE_COUNT="2"
|
|
RAM_SIZE="4096"
|
|
BRG="vmbr0"
|
|
MAC="$GEN_MAC"
|
|
VLAN=""
|
|
MTU=""
|
|
START_VM="yes"
|
|
METHOD="default"
|
|
|
|
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}${VMID}${CL}"
|
|
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}Q35 (Modern)${CL}"
|
|
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}${DISK_SIZE}${CL}"
|
|
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
|
|
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}${HN}${CL}"
|
|
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}"
|
|
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}${CORE_COUNT}${CL}"
|
|
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}${RAM_SIZE}${CL}"
|
|
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}${BRG}${CL}"
|
|
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}${MAC}${CL}"
|
|
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}Default${CL}"
|
|
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}Default${CL}"
|
|
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}"
|
|
echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above settings${CL}"
|
|
}
|
|
|
|
function advanced_settings() {
|
|
select_os
|
|
select_cloud_init
|
|
|
|
# SSH Key selection for Cloud-Init VMs
|
|
if [ "$USE_CLOUD_INIT" = "yes" ]; then
|
|
configure_cloudinit_ssh_keys || true
|
|
fi
|
|
|
|
METHOD="advanced"
|
|
[ -z "${VMID:-}" ] && VMID=$(get_valid_nextid)
|
|
|
|
# VM ID
|
|
while true; do
|
|
if VMID=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Virtual Machine ID" 8 58 $VMID --title "VIRTUAL MACHINE ID" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
|
if [ -z "$VMID" ]; then
|
|
VMID=$(get_valid_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 2
|
|
continue
|
|
fi
|
|
echo -e "${CONTAINERID}${BOLD}${DGN}Virtual Machine ID: ${BGN}$VMID${CL}"
|
|
break
|
|
else
|
|
exit_script
|
|
fi
|
|
done
|
|
|
|
# Machine Type
|
|
if MACH=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "MACHINE TYPE" --radiolist --cancel-button Exit-Script "Choose Type" 10 58 2 \
|
|
"q35" "Q35 (Modern, PCIe)" ON \
|
|
"i440fx" "i440fx (Legacy, PCI)" OFF \
|
|
3>&1 1>&2 2>&3); then
|
|
if [ $MACH = q35 ]; then
|
|
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}Q35 (Modern)${CL}"
|
|
FORMAT=""
|
|
MACHINE=" -machine q35"
|
|
else
|
|
echo -e "${CONTAINERTYPE}${BOLD}${DGN}Machine Type: ${BGN}i440fx (Legacy)${CL}"
|
|
FORMAT=",efitype=4m"
|
|
MACHINE=""
|
|
fi
|
|
else
|
|
exit_script
|
|
fi
|
|
|
|
# Disk Size
|
|
if DISK_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Disk Size in GiB (e.g., 10, 20)" 8 58 "$DISK_SIZE" --title "DISK SIZE" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
|
DISK_SIZE=$(echo "$DISK_SIZE" | tr -d ' ')
|
|
if [[ "$DISK_SIZE" =~ ^[0-9]+$ ]]; then
|
|
DISK_SIZE="${DISK_SIZE}G"
|
|
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
|
|
elif [[ "$DISK_SIZE" =~ ^[0-9]+G$ ]]; then
|
|
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Size: ${BGN}$DISK_SIZE${CL}"
|
|
else
|
|
echo -e "${DISKSIZE}${BOLD}${RD}Invalid Disk Size. Please use a number (e.g., 10 or 10G).${CL}"
|
|
exit_script
|
|
fi
|
|
else
|
|
exit_script
|
|
fi
|
|
|
|
# Disk Cache
|
|
if DISK_CACHE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "DISK CACHE" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
|
|
"0" "None (Default)" ON \
|
|
"1" "Write Through" OFF \
|
|
3>&1 1>&2 2>&3); then
|
|
if [ $DISK_CACHE = "1" ]; then
|
|
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}Write Through${CL}"
|
|
DISK_CACHE="cache=writethrough,"
|
|
else
|
|
echo -e "${DISKSIZE}${BOLD}${DGN}Disk Cache: ${BGN}None${CL}"
|
|
DISK_CACHE=""
|
|
fi
|
|
else
|
|
exit_script
|
|
fi
|
|
|
|
# Hostname
|
|
if VM_NAME=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set Hostname" 8 58 docker --title "HOSTNAME" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then
|
|
if [ -z $VM_NAME ]; then
|
|
HN="docker"
|
|
else
|
|
HN=$(echo ${VM_NAME,,} | tr -d ' ')
|
|
fi
|
|
echo -e "${HOSTNAME}${BOLD}${DGN}Hostname: ${BGN}$HN${CL}"
|
|
else
|
|
exit_script
|
|
fi
|
|
|
|
# CPU Model
|
|
if CPU_TYPE1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "CPU MODEL" --radiolist "Choose" --cancel-button Exit-Script 10 58 2 \
|
|
"1" "Host (Recommended)" ON \
|
|
"0" "KVM64" OFF \
|
|
3>&1 1>&2 2>&3); then
|
|
if [ $CPU_TYPE1 = "1" ]; then
|
|
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}Host${CL}"
|
|
CPU_TYPE=" -cpu host"
|
|
else
|
|
echo -e "${OS}${BOLD}${DGN}CPU Model: ${BGN}KVM64${CL}"
|
|
CPU_TYPE=""
|
|
fi
|
|
else
|
|
exit_script
|
|
fi
|
|
|
|
# CPU Cores
|
|
if CORE_COUNT=$(whiptail --backtitle "Proxmox VE Helper Scripts" --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"
|
|
fi
|
|
echo -e "${CPUCORE}${BOLD}${DGN}CPU Cores: ${BGN}$CORE_COUNT${CL}"
|
|
else
|
|
exit_script
|
|
fi
|
|
|
|
# RAM Size
|
|
if RAM_SIZE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --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"
|
|
fi
|
|
echo -e "${RAMSIZE}${BOLD}${DGN}RAM Size: ${BGN}$RAM_SIZE${CL}"
|
|
else
|
|
exit_script
|
|
fi
|
|
|
|
# Bridge
|
|
if BRG=$(whiptail --backtitle "Proxmox VE Helper Scripts" --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"
|
|
fi
|
|
echo -e "${BRIDGE}${BOLD}${DGN}Bridge: ${BGN}$BRG${CL}"
|
|
else
|
|
exit_script
|
|
fi
|
|
|
|
# MAC Address
|
|
if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --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"
|
|
else
|
|
MAC="$MAC1"
|
|
fi
|
|
echo -e "${MACADDRESS}${BOLD}${DGN}MAC Address: ${BGN}$MAC${CL}"
|
|
else
|
|
exit_script
|
|
fi
|
|
|
|
# VLAN
|
|
if VLAN1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --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=""
|
|
else
|
|
VLAN=",tag=$VLAN1"
|
|
fi
|
|
echo -e "${VLANTAG}${BOLD}${DGN}VLAN: ${BGN}$VLAN1${CL}"
|
|
else
|
|
exit_script
|
|
fi
|
|
|
|
# MTU
|
|
if MTU1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --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=""
|
|
else
|
|
MTU=",mtu=$MTU1"
|
|
fi
|
|
echo -e "${DEFAULT}${BOLD}${DGN}Interface MTU Size: ${BGN}$MTU1${CL}"
|
|
else
|
|
exit_script
|
|
fi
|
|
|
|
# Start VM
|
|
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "START VIRTUAL MACHINE" --yesno "Start VM when completed?" 10 58); then
|
|
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}yes${CL}"
|
|
START_VM="yes"
|
|
else
|
|
echo -e "${GATEWAY}${BOLD}${DGN}Start VM when completed: ${BGN}no${CL}"
|
|
START_VM="no"
|
|
fi
|
|
|
|
# Confirm
|
|
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "ADVANCED SETTINGS COMPLETE" --yesno "Ready to create a Docker VM?" --no-button Do-Over 10 58); then
|
|
echo -e "${CREATING}${BOLD}${DGN}Creating a Docker VM using the above advanced settings${CL}"
|
|
else
|
|
header_info
|
|
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
|
|
advanced_settings
|
|
fi
|
|
}
|
|
|
|
function start_script() {
|
|
if (whiptail --backtitle "Proxmox VE Helper Scripts" --title "SETTINGS" --yesno "Use Default Settings?" --no-button Advanced 10 58); then
|
|
header_info
|
|
echo -e "${DEFAULT}${BOLD}${BL}Using Default Settings${CL}"
|
|
default_settings
|
|
else
|
|
header_info
|
|
echo -e "${ADVANCED}${BOLD}${RD}Using Advanced Settings${CL}"
|
|
advanced_settings
|
|
fi
|
|
}
|
|
|
|
# ==============================================================================
|
|
# MAIN EXECUTION
|
|
# ==============================================================================
|
|
header_info
|
|
|
|
check_root
|
|
arch_check
|
|
pve_check
|
|
|
|
if whiptail --backtitle "Proxmox VE Helper Scripts" --title "Docker VM" --yesno "This will create a New Docker VM. Proceed?" 10 58; then
|
|
:
|
|
else
|
|
header_info && echo -e "${CROSS}${RD}User exited script${CL}\n" && exit
|
|
fi
|
|
|
|
start_script
|
|
post_to_api_vm
|
|
|
|
# ==============================================================================
|
|
# STORAGE SELECTION
|
|
# ==============================================================================
|
|
msg_info "Validating Storage"
|
|
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
|
|
elif [ $((${#STORAGE_MENU[@]} / 3)) -eq 1 ]; then
|
|
STORAGE=${STORAGE_MENU[0]}
|
|
else
|
|
if [ -n "$SPINNER_PID" ] && ps -p $SPINNER_PID >/dev/null; then kill $SPINNER_PID >/dev/null; fi
|
|
printf "\e[?25h"
|
|
while [ -z "${STORAGE:+x}" ]; do
|
|
STORAGE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "Storage Pools" --radiolist \
|
|
"Which storage pool would you like to use for ${HN}?\nTo make a selection, use the Spacebar.\n" \
|
|
16 $(($MSG_MAX_LENGTH + 23)) 6 \
|
|
"${STORAGE_MENU[@]}" 3>&1 1>&2 2>&3)
|
|
done
|
|
fi
|
|
msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location."
|
|
msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}."
|
|
|
|
# ==============================================================================
|
|
# PREREQUISITES
|
|
# ==============================================================================
|
|
if ! command -v virt-customize &>/dev/null; then
|
|
msg_info "Installing libguestfs-tools"
|
|
apt-get -qq update >/dev/null
|
|
apt-get -qq install libguestfs-tools lsb-release -y >/dev/null
|
|
apt-get -qq install dhcpcd-base -y >/dev/null 2>&1 || true
|
|
msg_ok "Installed libguestfs-tools"
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# IMAGE DOWNLOAD
|
|
# ==============================================================================
|
|
msg_info "Retrieving the URL for the ${OS_DISPLAY} Qcow2 Disk Image"
|
|
URL=$(get_image_url)
|
|
CACHE_DIR="/var/lib/vz/template/cache"
|
|
CACHE_FILE="$CACHE_DIR/$(basename "$URL")"
|
|
mkdir -p "$CACHE_DIR"
|
|
msg_ok "${CL}${BL}${URL}${CL}"
|
|
|
|
if [[ ! -s "$CACHE_FILE" ]]; then
|
|
curl -f#SL -o "$CACHE_FILE" "$URL"
|
|
echo -en "\e[1A\e[0K"
|
|
msg_ok "Downloaded ${CL}${BL}$(basename "$CACHE_FILE")${CL}"
|
|
else
|
|
msg_ok "Using cached image ${CL}${BL}$(basename "$CACHE_FILE")${CL}"
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# STORAGE TYPE DETECTION
|
|
# ==============================================================================
|
|
STORAGE_TYPE=$(pvesm status -storage "$STORAGE" | awk 'NR>1 {print $2}')
|
|
case $STORAGE_TYPE in
|
|
nfs | dir)
|
|
DISK_EXT=".qcow2"
|
|
DISK_REF="$VMID/"
|
|
DISK_IMPORT="--format qcow2"
|
|
THIN=""
|
|
;;
|
|
btrfs)
|
|
DISK_EXT=".raw"
|
|
DISK_REF="$VMID/"
|
|
DISK_IMPORT="--format raw"
|
|
FORMAT=",efitype=4m"
|
|
THIN=""
|
|
;;
|
|
*)
|
|
DISK_EXT=""
|
|
DISK_REF=""
|
|
DISK_IMPORT="--format raw"
|
|
;;
|
|
esac
|
|
|
|
# ==============================================================================
|
|
# IMAGE CUSTOMIZATION WITH DOCKER
|
|
# ==============================================================================
|
|
msg_info "Preparing ${OS_DISPLAY} image with Docker"
|
|
|
|
WORK_FILE=$(mktemp --suffix=.qcow2)
|
|
cp "$CACHE_FILE" "$WORK_FILE"
|
|
|
|
export LIBGUESTFS_BACKEND_SETTINGS=dns=8.8.8.8,1.1.1.1
|
|
|
|
DOCKER_PREINSTALLED="no"
|
|
|
|
# Install qemu-guest-agent and Docker during image customization
|
|
msg_info "Installing base packages in image"
|
|
if virt-customize -a "$WORK_FILE" --install qemu-guest-agent,curl,ca-certificates >/dev/null 2>&1; then
|
|
msg_ok "Installed base packages"
|
|
|
|
msg_info "Installing Docker (this may take 2-5 minutes)"
|
|
if virt-customize -q -a "$WORK_FILE" --run-command "curl -fsSL https://get.docker.com | sh" >/dev/null 2>&1 &&
|
|
virt-customize -q -a "$WORK_FILE" --run-command "systemctl enable docker" >/dev/null 2>&1; then
|
|
msg_ok "Installed Docker"
|
|
|
|
msg_info "Configuring Docker daemon"
|
|
# Optimize Docker daemon configuration
|
|
virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/docker" >/dev/null 2>&1
|
|
virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/docker/daemon.json << EOF
|
|
{
|
|
"storage-driver": "overlay2",
|
|
"log-driver": "json-file",
|
|
"log-opts": {
|
|
"max-size": "10m",
|
|
"max-file": "3"
|
|
}
|
|
}
|
|
EOF' >/dev/null 2>&1
|
|
DOCKER_PREINSTALLED="yes"
|
|
msg_ok "Configured Docker daemon"
|
|
else
|
|
msg_ok "Docker will be installed on first boot"
|
|
fi
|
|
else
|
|
msg_ok "Packages will be installed on first boot"
|
|
fi
|
|
|
|
msg_info "Finalizing image (hostname, SSH config)"
|
|
# Set hostname and prepare for unique machine-id
|
|
virt-customize -q -a "$WORK_FILE" --hostname "${HN}" >/dev/null 2>&1 || true
|
|
virt-customize -q -a "$WORK_FILE" --run-command "truncate -s 0 /etc/machine-id" >/dev/null 2>&1 || true
|
|
virt-customize -q -a "$WORK_FILE" --run-command "rm -f /var/lib/dbus/machine-id" >/dev/null 2>&1 || true
|
|
|
|
# Configure SSH for Cloud-Init
|
|
if [ "$USE_CLOUD_INIT" = "yes" ]; then
|
|
virt-customize -q -a "$WORK_FILE" --run-command "sed -i 's/^#*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config" >/dev/null 2>&1 || true
|
|
virt-customize -q -a "$WORK_FILE" --run-command "sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config" >/dev/null 2>&1 || true
|
|
else
|
|
# Configure auto-login for nocloud images (no Cloud-Init)
|
|
virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/systemd/system/serial-getty@ttyS0.service.d" >/dev/null 2>&1 || true
|
|
virt-customize -q -a "$WORK_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 || true
|
|
virt-customize -q -a "$WORK_FILE" --run-command "mkdir -p /etc/systemd/system/getty@tty1.service.d" >/dev/null 2>&1 || true
|
|
virt-customize -q -a "$WORK_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 || true
|
|
fi
|
|
msg_ok "Finalized image"
|
|
|
|
# Create first-boot Docker install script (fallback if virt-customize failed)
|
|
if [ "$DOCKER_PREINSTALLED" = "no" ]; then
|
|
if virt-customize -q -a "$WORK_FILE" --run-command 'cat > /root/install-docker.sh << "DOCKERSCRIPT"
|
|
#!/bin/bash
|
|
exec > /var/log/install-docker.log 2>&1
|
|
echo "[$(date)] Starting Docker installation"
|
|
|
|
for i in {1..30}; do
|
|
ping -c 1 8.8.8.8 >/dev/null 2>&1 && break
|
|
sleep 2
|
|
done
|
|
|
|
apt-get update
|
|
apt-get install -y qemu-guest-agent curl ca-certificates
|
|
curl -fsSL https://get.docker.com | sh
|
|
systemctl enable docker
|
|
systemctl start docker
|
|
|
|
mkdir -p /etc/docker
|
|
cat > /etc/docker/daemon.json << DAEMON
|
|
{
|
|
"storage-driver": "overlay2",
|
|
"log-driver": "json-file",
|
|
"log-opts": { "max-size": "10m", "max-file": "3" }
|
|
}
|
|
DAEMON
|
|
systemctl restart docker
|
|
|
|
touch /root/.docker-installed
|
|
echo "[$(date)] Docker installation completed"
|
|
DOCKERSCRIPT
|
|
chmod +x /root/install-docker.sh' >/dev/null 2>&1; then
|
|
|
|
virt-customize -q -a "$WORK_FILE" --run-command 'cat > /etc/systemd/system/install-docker.service << "DOCKERSERVICE"
|
|
[Unit]
|
|
Description=Install Docker on First Boot
|
|
After=network-online.target
|
|
Wants=network-online.target
|
|
ConditionPathExists=!/root/.docker-installed
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/root/install-docker.sh
|
|
RemainAfterExit=yes
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
DOCKERSERVICE
|
|
systemctl enable install-docker.service' >/dev/null 2>&1 || true
|
|
else
|
|
msg_warn "virt-customize failed for this image. Docker must be installed manually after first boot:"
|
|
msg_warn " curl -fsSL https://get.docker.com | sh"
|
|
fi
|
|
fi
|
|
|
|
# Resize disk to target size
|
|
msg_info "Resizing disk image to ${DISK_SIZE}"
|
|
qemu-img resize "$WORK_FILE" "${DISK_SIZE}" >/dev/null 2>&1
|
|
msg_ok "Resized disk image"
|
|
|
|
# ==============================================================================
|
|
# VM CREATION
|
|
# ==============================================================================
|
|
msg_info "Creating Docker VM shell"
|
|
|
|
qm create $VMID -agent 1${MACHINE} -tablet 0 -localtime 1 -bios ovmf${CPU_TYPE} -cores $CORE_COUNT -memory $RAM_SIZE \
|
|
-name $HN -tags community-script -net0 virtio,bridge=$BRG,macaddr=$MAC$VLAN$MTU -onboot 1 -ostype l26 -scsihw virtio-scsi-pci >/dev/null
|
|
|
|
msg_ok "Created VM shell"
|
|
|
|
# ==============================================================================
|
|
# DISK IMPORT
|
|
# ==============================================================================
|
|
msg_info "Importing disk into storage ($STORAGE)"
|
|
|
|
if qm disk import --help >/dev/null 2>&1; then
|
|
IMPORT_CMD=(qm disk import)
|
|
else
|
|
IMPORT_CMD=(qm importdisk)
|
|
fi
|
|
|
|
IMPORT_OUT="$("${IMPORT_CMD[@]}" "$VMID" "$WORK_FILE" "$STORAGE" ${DISK_IMPORT:-} 2>&1 || true)"
|
|
DISK_REF_IMPORTED="$(printf '%s\n' "$IMPORT_OUT" | sed -n "s/.*successfully imported disk '\([^']\+\)'.*/\1/p" | tr -d "\r\"'")"
|
|
[[ -z "$DISK_REF_IMPORTED" ]] && DISK_REF_IMPORTED="$(pvesm list "$STORAGE" | awk -v id="$VMID" '$5 ~ ("vm-"id"-disk-") {print $1":"$5}' | sort | tail -n1)"
|
|
[[ -z "$DISK_REF_IMPORTED" ]] && {
|
|
msg_error "Unable to determine imported disk reference."
|
|
echo "$IMPORT_OUT"
|
|
exit 226
|
|
}
|
|
|
|
msg_ok "Imported disk (${CL}${BL}${DISK_REF_IMPORTED}${CL})"
|
|
|
|
# Clean up work file
|
|
rm -f "$WORK_FILE"
|
|
|
|
# ==============================================================================
|
|
# VM CONFIGURATION
|
|
# ==============================================================================
|
|
msg_info "Attaching EFI and root disk"
|
|
|
|
qm set "$VMID" \
|
|
--efidisk0 "${STORAGE}:0,efitype=4m" \
|
|
--scsi0 "${DISK_REF_IMPORTED},${DISK_CACHE}${THIN%,}" \
|
|
--boot order=scsi0 \
|
|
--serial0 socket >/dev/null
|
|
|
|
qm set $VMID --agent enabled=1 >/dev/null
|
|
|
|
msg_ok "Attached EFI and root disk"
|
|
|
|
# Set VM description
|
|
set_description
|
|
|
|
# Cloud-Init configuration
|
|
if [ "$USE_CLOUD_INIT" = "yes" ]; then
|
|
msg_info "Configuring Cloud-Init"
|
|
setup_cloud_init "$VMID" "$STORAGE" "$HN" "yes"
|
|
msg_ok "Cloud-Init configured"
|
|
fi
|
|
|
|
# Start VM
|
|
if [ "$START_VM" == "yes" ]; then
|
|
msg_info "Starting Docker VM"
|
|
qm start $VMID >/dev/null 2>&1
|
|
msg_ok "Started Docker VM"
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# FINAL OUTPUT
|
|
# ==============================================================================
|
|
VM_IP=""
|
|
if [ "$START_VM" == "yes" ]; then
|
|
set +e
|
|
for i in {1..10}; do
|
|
VM_IP=$(qm guest cmd "$VMID" network-get-interfaces 2>/dev/null |
|
|
jq -r '.[] | select(.name != "lo") | ."ip-addresses"[]? | select(."ip-address-type" == "ipv4") | ."ip-address"' 2>/dev/null |
|
|
grep -v "^127\." | head -1) || true
|
|
[ -n "$VM_IP" ] && break
|
|
sleep 3
|
|
done
|
|
set -e
|
|
fi
|
|
|
|
echo -e "\n${INFO}${BOLD}${GN}Docker VM Configuration Summary:${CL}"
|
|
echo -e "${TAB}${DGN}VM ID: ${BGN}${VMID}${CL}"
|
|
echo -e "${TAB}${DGN}Hostname: ${BGN}${HN}${CL}"
|
|
echo -e "${TAB}${DGN}OS: ${BGN}${OS_DISPLAY}${CL}"
|
|
[ -n "$VM_IP" ] && echo -e "${TAB}${DGN}IP Address: ${BGN}${VM_IP}${CL}"
|
|
|
|
if [ "$DOCKER_PREINSTALLED" = "yes" ]; then
|
|
echo -e "${TAB}${DGN}Docker: ${BGN}Pre-installed (via get.docker.com)${CL}"
|
|
else
|
|
echo -e "${TAB}${DGN}Docker: ${BGN}Installing on first boot${CL}"
|
|
echo -e "${TAB}${YW}⚠️ Wait 2-3 minutes for installation to complete${CL}"
|
|
echo -e "${TAB}${YW}⚠️ Check progress: ${BL}cat /var/log/install-docker.log${CL}"
|
|
fi
|
|
|
|
if [ "$USE_CLOUD_INIT" = "yes" ]; then
|
|
display_cloud_init_info "$VMID" "$HN" 2>/dev/null || true
|
|
fi
|
|
|
|
post_update_to_api "done" "none"
|
|
msg_ok "Completed successfully!\n"
|