homelab/scripts/lib/proxmox.sh
nathan e16f98a183 feat(bootstrap)!: introduce unified bootstrap system with modular libraries
BREAKING CHANGE: day0bootstrap.sh deprecated in favor of bootstrap.sh

- Add scripts/bootstrap.sh (488 lines): Unified entrypoint supporting multiple hardware types (Proxmox/Docker VMs/Pi)
- Create scripts/lib/ modular library system:
  - detection.sh: OS/hardware/container detection (362 lines)
  - fingerprint.sh: System fingerprinting and inventory (494 lines)
  - network.sh: IP configuration and VLAN placement (356 lines)
  - proxmox.sh: PVE post-install automation (453 lines)
  - validation.sh: Comprehensive pre-flight checks (510 lines)
- Add validation tools: validate-node.sh, onboarding.sh, pi_init.sh
- Deprecate scripts/day0bootstrap.sh with graceful redirect wrapper
- Document architecture in scripts/README.md (495 lines) and PROXMOX-COMPARISON.md
- Update SOP-002 with new bootstrap workflow
- Add nodes/watchtower/compose.yaml (Raspberry Pi 5 stack)

Migration: Existing day0bootstrap.sh users automatically redirected to new system after 5-second warning. No manual intervention required.

Ref: Infrastructure automation modernization per active-tasks.md
2026-04-12 22:48:19 -04:00

454 lines
14 KiB
Bash

#!/bin/bash
# ==============================================================================
# PROXMOX LIBRARY: Post-Install Configuration and Management
# ==============================================================================
# Part of unified bootstrap system for homelab infrastructure
# Comprehensive Proxmox VE post-install routines inspired by community scripts
# (tteck/community-scripts ProxmoxVE post-pve-install.sh)
# ==============================================================================
# Source detection library if not already loaded
if ! type -t detect_os_family &>/dev/null; then
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=./detection.sh
source "${SCRIPT_DIR}/detection.sh"
fi
# --- PROXMOX VERSION DETECTION ---
get_pve_version() {
# Get Proxmox VE version (e.g., 8.2.4)
if ! command -v pveversion &>/dev/null; then
echo "unknown"
return 1
fi
pveversion | awk -F'/' '{print $2}' | awk -F'-' '{print $1}'
}
get_pve_major_version() {
# Get major version (8 or 9)
local ver=$(get_pve_version)
echo "$ver" | cut -d. -f1
}
is_pve_supported_version() {
# Check if PVE version is supported (8.0-8.9.x or 9.0-9.1.x)
local major=$(get_pve_major_version)
case "$major" in
8|9)
return 0
;;
*)
return 1
;;
esac
}
# --- REPOSITORY MANAGEMENT ---
disable_pve_enterprise_repo() {
# Disable enterprise repository (requires subscription)
local major=$(get_pve_major_version)
echo "[⚙] Disabling Proxmox enterprise repository..." >&2
if [ "$major" == "9" ]; then
# PVE 9.x uses .sources format
for file in /etc/apt/sources.list.d/*.sources; do
if [ -f "$file" ] && grep -q "pve-enterprise" "$file"; then
# Use Enabled: false instead of commenting
if grep -q "^Enabled:" "$file"; then
sudo sed -i 's/^Enabled:.*/Enabled: false/' "$file"
else
echo "Enabled: false" | sudo tee -a "$file" >/dev/null
fi
fi
done
else
# PVE 8.x uses .list format
if [ -f /etc/apt/sources.list.d/pve-enterprise.list ]; then
sudo sed -i 's/^deb/# deb/' /etc/apt/sources.list.d/pve-enterprise.list
fi
fi
echo " [✓] Enterprise repository disabled" >&2
}
enable_pve_no_subscription_repo() {
# Enable no-subscription repository (free updates)
local major=$(get_pve_major_version)
echo "[⚙] Enabling Proxmox no-subscription repository..." >&2
if [ "$major" == "9" ]; then
# PVE 9.x deb822 format
sudo tee /etc/apt/sources.list.d/proxmox.sources >/dev/null <<EOF
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: trixie
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF
else
# PVE 8.x traditional format
sudo tee /etc/apt/sources.list.d/pve-no-subscription.list >/dev/null <<EOF
deb http://download.proxmox.com/debian/pve bookworm pve-no-subscription
EOF
fi
echo " [✓] No-subscription repository enabled" >&2
}
configure_ceph_repos() {
# Configure Ceph storage repositories (disabled by default)
local major=$(get_pve_major_version)
echo "[⚙] Configuring Ceph package repositories..." >&2
if [ "$major" == "9" ]; then
# PVE 9.x - Squid version
sudo tee /etc/apt/sources.list.d/ceph.sources >/dev/null <<EOF
Types: deb
URIs: http://download.proxmox.com/debian/ceph-squid
Suites: trixie
Components: no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
Enabled: false
EOF
else
# PVE 8.x - Reef/Quincy versions
sudo tee /etc/apt/sources.list.d/ceph.list >/dev/null <<EOF
# Ceph Reef (disabled - enable as needed)
# deb http://download.proxmox.com/debian/ceph-reef bookworm no-subscription
# Ceph Quincy (disabled - enable as needed)
# deb http://download.proxmox.com/debian/ceph-quincy bookworm no-subscription
EOF
fi
echo " [✓] Ceph repositories configured (disabled)" >&2
}
add_pvetest_repo() {
# Add pvetest repository for beta features (disabled by default)
local major=$(get_pve_major_version)
echo "[⚙] Adding pvetest repository (disabled)..." >&2
if [ "$major" == "9" ]; then
sudo tee /etc/apt/sources.list.d/pve-test.sources >/dev/null <<EOF
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: trixie
Components: pve-test
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
Enabled: false
EOF
else
sudo tee /etc/apt/sources.list.d/pvetest-for-beta.list >/dev/null <<EOF
# PVE Test Repository (disabled - enable for beta testing)
# deb http://download.proxmox.com/debian/pve bookworm pvetest
EOF
fi
echo " [✓] pvetest repository added (disabled)" >&2
}
configure_all_pve_repos() {
# Run complete repository configuration
disable_pve_enterprise_repo
enable_pve_no_subscription_repo
configure_ceph_repos
add_pvetest_repo
echo "[✓] Proxmox repository configuration complete" >&2
}
# --- SUBSCRIPTION NAG REMOVAL ---
disable_subscription_nag() {
# Remove subscription nag from web UI and mobile UI
# Creates apt hook to persist after updates
echo "[⚙] Disabling subscription nag dialog..." >&2
# Create persistent script
sudo mkdir -p /usr/local/bin
sudo tee /usr/local/bin/pve-remove-nag.sh >/dev/null <<'SCRIPT_EOF'
#!/bin/sh
# Auto-generated by homelab bootstrap - removes Proxmox subscription nag
WEB_JS=/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js
if [ -s "$WEB_JS" ] && ! grep -q NoMoreNagging "$WEB_JS"; then
echo "Patching Web UI subscription nag..."
sed -i -e "/data\.status/ s/!//" -e "/data\.status/ s/active/NoMoreNagging/" "$WEB_JS"
fi
MOBILE_TPL=/usr/share/pve-yew-mobile-gui/index.html.tpl
MARKER="<!-- MANAGED BLOCK FOR MOBILE NAG -->"
if [ -f "$MOBILE_TPL" ] && ! grep -q "$MARKER" "$MOBILE_TPL"; then
echo "Patching Mobile UI subscription nag..."
printf "%s\n" \
"$MARKER" \
"<script>" \
" function removeSubscriptionElements() {" \
" const dialogs = document.querySelectorAll('dialog.pwt-outer-dialog');" \
" dialogs.forEach(d => { if ((d.textContent||'').toLowerCase().includes('subscription')) d.remove(); });" \
" const cards = document.querySelectorAll('.pwt-card.pwt-p-2');" \
" cards.forEach(c => { if (!c.querySelector('button') && (c.textContent||'').toLowerCase().includes('subscription')) c.remove(); });" \
" }" \
" const observer = new MutationObserver(removeSubscriptionElements);" \
" observer.observe(document.body, { childList: true, subtree: true });" \
" removeSubscriptionElements();" \
"</script>" >> "$MOBILE_TPL"
fi
SCRIPT_EOF
sudo chmod 755 /usr/local/bin/pve-remove-nag.sh
# Create apt hook to run after updates
sudo tee /etc/apt/apt.conf.d/90-pve-no-nag <<'APT_EOF'
DPkg::Post-Invoke { "/usr/local/bin/pve-remove-nag.sh"; };
APT_EOF
sudo chmod 644 /etc/apt/apt.conf.d/90-pve-no-nag
# Apply immediately
sudo /usr/local/bin/pve-remove-nag.sh 2>/dev/null || true
echo " [✓] Subscription nag disabled (clear browser cache)" >&2
}
enable_subscription_nag() {
# Remove nag removal script (restore default behavior)
echo "[⚙] Re-enabling subscription nag..." >&2
sudo rm -f /usr/local/bin/pve-remove-nag.sh
sudo rm -f /etc/apt/apt.conf.d/90-pve-no-nag
# Reinstall widget toolkit to restore original
sudo apt --reinstall install proxmox-widget-toolkit &>/dev/null || true
echo " [✓] Subscription nag re-enabled" >&2
}
# --- HIGH AVAILABILITY MANAGEMENT ---
disable_ha_services() {
# Disable HA services for single-node setups (saves resources)
echo "[⚙] Disabling high availability services..." >&2
if systemctl is-active --quiet pve-ha-lrm || systemctl is-active --quiet pve-ha-crm; then
sudo systemctl disable --now pve-ha-lrm 2>/dev/null || true
sudo systemctl disable --now pve-ha-crm 2>/dev/null || true
sudo systemctl disable --now corosync 2>/dev/null || true
echo " [✓] HA services disabled" >&2
else
echo " [✓] HA services already disabled" >&2
fi
}
enable_ha_services() {
# Enable HA services for clustered environments
echo "[⚙] Enabling high availability services..." >&2
sudo systemctl enable --now pve-ha-lrm
sudo systemctl enable --now pve-ha-crm
sudo systemctl enable --now corosync
echo " [✓] HA services enabled" >&2
}
check_ha_status() {
# Check if HA services are running
if systemctl is-active --quiet pve-ha-lrm; then
echo "enabled"
else
echo "disabled"
fi
}
# --- SYSTEM MAINTENANCE ---
update_pve_system() {
# Run full system update
echo "[⚙] Updating Proxmox VE system (this may take several minutes)..." >&2
sudo apt-get update -qq
sudo apt-get dist-upgrade -y -qq
echo " [✓] System update complete" >&2
}
check_reboot_required() {
# Check if reboot is needed after updates
if [ -f /var/run/reboot-required ]; then
return 0
fi
# Check if kernel was updated
local running_kernel=$(uname -r)
local latest_kernel=$(dpkg -l | grep linux-image | sort -V | tail -n1 | awk '{print $2}' | sed 's/linux-image-//')
if [ "$running_kernel" != "$latest_kernel" ]; then
return 0
fi
return 1
}
# --- COMPREHENSIVE POST-INSTALL ---
run_proxmox_post_install() {
# Complete post-install routine for Proxmox hosts
# Args: $1 = mode (auto|interactive) - default: auto
local mode="${1:-auto}"
echo "=======================================" >&2
echo "PROXMOX POST-INSTALL CONFIGURATION" >&2
echo "Version: $(get_pve_version)" >&2
echo "Mode: $mode" >&2
echo "=======================================" >&2
echo "" >&2
# Verify Proxmox installation
if ! is_pve_supported_version; then
echo "[✗] Unsupported Proxmox VE version" >&2
return 1
fi
# Repository configuration
configure_all_pve_repos
echo "" >&2
# Subscription nag removal (auto mode)
if [ "$mode" == "auto" ]; then
disable_subscription_nag
echo "" >&2
fi
# HA service management
local cluster_nodes=$(pvesh get /nodes --output-format=json 2>/dev/null | jq -r 'length' 2>/dev/null || echo "1")
if [ "$cluster_nodes" -eq 1 ]; then
echo "[⚙] Single-node setup detected" >&2
disable_ha_services
echo "" >&2
else
echo "[⚙] Multi-node cluster detected ($cluster_nodes nodes)" >&2
echo " [✓] HA services left enabled" >&2
echo "" >&2
fi
# System update
if [ "$mode" == "auto" ]; then
echo "[⚙] Checking for updates..." >&2
sudo apt-get update -qq
local upgrades=$(apt list --upgradable 2>/dev/null | grep -c upgradable || echo "0")
if [ "$upgrades" -gt 1 ]; then
echo " [!] $((upgrades - 1)) packages can be upgraded" >&2
echo " [!] Run manually: apt update && apt dist-upgrade" >&2
else
echo " [✓] System up to date" >&2
fi
echo "" >&2
fi
# Check reboot requirement
if check_reboot_required; then
echo "[!] REBOOT REQUIRED" >&2
echo " Kernel or critical services updated" >&2
echo " Run: reboot" >&2
echo "" >&2
fi
echo "=======================================" >&2
echo "POST-INSTALL COMPLETE" >&2
echo "=======================================" >&2
echo "" >&2
echo "Next Steps:" >&2
echo " 1. Clear browser cache (Ctrl+Shift+R)" >&2
echo " 2. Reboot if kernel was updated" >&2
echo " 3. Configure storage/networking in web UI" >&2
echo "" >&2
}
# --- VALIDATION ---
validate_proxmox_config() {
# Validate Proxmox configuration
local errors=0
echo "[⚙] Validating Proxmox configuration..." >&2
# Check PVE version
if ! is_pve_supported_version; then
echo " [✗] Unsupported PVE version: $(get_pve_version)" >&2
((errors++))
else
echo " [✓] PVE version: $(get_pve_version)" >&2
fi
# Check no-subscription repo
if grep -rq "pve-no-subscription" /etc/apt/sources.list.d/ 2>/dev/null; then
echo " [✓] No-subscription repository configured" >&2
else
echo " [✗] No-subscription repository not found" >&2
((errors++))
fi
# Check enterprise repo disabled
if grep -rq "^deb.*pve-enterprise" /etc/apt/sources.list.d/ 2>/dev/null; then
echo " [!] Enterprise repository still active (requires subscription)" >&2
else
echo " [✓] Enterprise repository disabled" >&2
fi
# Check cluster status
local cluster_status=$(pvesh get /cluster/status --output-format=json 2>/dev/null | jq -r '.[0].type' 2>/dev/null || echo "unknown")
if [ "$cluster_status" == "cluster" ]; then
echo " [✓] Part of Proxmox cluster" >&2
else
echo " [✓] Standalone node" >&2
fi
if [ $errors -eq 0 ]; then
echo "[✓] Proxmox validation passed" >&2
return 0
else
echo "[✗] Proxmox validation failed ($errors errors)" >&2
return 1
fi
}
# Export functions
export -f get_pve_version
export -f get_pve_major_version
export -f is_pve_supported_version
export -f disable_pve_enterprise_repo
export -f enable_pve_no_subscription_repo
export -f configure_ceph_repos
export -f add_pvetest_repo
export -f configure_all_pve_repos
export -f disable_subscription_nag
export -f enable_subscription_nag
export -f disable_ha_services
export -f enable_ha_services
export -f check_ha_status
export -f update_pve_system
export -f check_reboot_required
export -f run_proxmox_post_install
export -f validate_proxmox_config