#!/usr/bin/env bash # ============================================================================= # Proxmox Config Restore — run on a freshly installed Proxmox node # # Usage: # bash restore.sh pve1 — restore PVE1 (primary, hostname "pve") # bash restore.sh pve2 — restore PVE2 (secondary, hostname "pve2") # # Prerequisites on fresh Proxmox install: # apt install -y git # git clone https://@github.com/myronblair/proxmox-config.git /opt/proxmox-config # bash /opt/proxmox-config/restore.sh pve1 # ============================================================================= TARGET="${1:-}" REPO_DIR="$(cd "$(dirname "$0")" && pwd)" RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; NC='\033[0m' header() { echo -e "\n${CYAN}══════════════════════════════════════${NC}"; echo -e "${CYAN} $*${NC}"; echo -e "${CYAN}══════════════════════════════════════${NC}"; } success() { echo -e "${GREEN} ✓ $*${NC}"; } warn() { echo -e "${YELLOW} ⚠ $*${NC}"; } info() { echo -e " → $*"; } die() { echo -e "${RED} ✗ $*${NC}" >&2; exit 1; } confirm() { local msg="$1" echo -e "\n${YELLOW} $msg${NC}" read -rp " Apply this section? [Y/n] " ans [[ "${ans:-Y}" =~ ^[Yy]$ ]] } # --------------------------------------------------------------------------- # Validate arguments # --------------------------------------------------------------------------- if [[ -z "$TARGET" ]]; then echo "Usage: $0 pve1|pve2" echo "" echo " pve1 — restore primary node (hostname: pve, IP: 10.48.200.90)" echo " pve2 — restore secondary node (hostname: pve2, IP: 10.48.200.91)" exit 1 fi if [[ "$TARGET" == "pve1" ]]; then NODE_DIR="$REPO_DIR/pve" EXPECTED_HOSTNAME="pve" EXPECTED_IP="10.48.200.90" elif [[ "$TARGET" == "pve2" ]]; then NODE_DIR="$REPO_DIR/pve2" EXPECTED_HOSTNAME="pve2" EXPECTED_IP="10.48.200.91" else die "Unknown target: $TARGET. Use pve1 or pve2." fi SHARED_DIR="$REPO_DIR/shared" [[ -d "$NODE_DIR" ]] || die "Node directory not found: $NODE_DIR" [[ $(id -u) -eq 0 ]] || die "Must run as root" # --------------------------------------------------------------------------- # Welcome banner # --------------------------------------------------------------------------- clear echo -e "${CYAN}" cat << 'BANNER' ╔═══════════════════════════════════════════════════════╗ ║ PROXMOX CONFIG RESTORE — OrbiShosting ║ ╚═══════════════════════════════════════════════════════╝ BANNER echo -e "${NC}" echo " Target : $TARGET ($EXPECTED_HOSTNAME — $EXPECTED_IP)" echo " Source : $REPO_DIR" echo " Date : $(date)" echo "" warn "This script restores configs to a FRESH Proxmox install." warn "Running on an existing node may overwrite running config." echo "" read -rp " Type 'yes' to continue: " confirm_start [[ "$confirm_start" == "yes" ]] || { echo "Aborted."; exit 0; } APPLIED=() SKIPPED=() # --------------------------------------------------------------------------- # SECTION 1: Hostname # --------------------------------------------------------------------------- header "1 / 8 — Hostname" info "Will set hostname to: $EXPECTED_HOSTNAME" [[ -f "$NODE_DIR/network/hostname" ]] && info "Backed-up hostname: $(cat $NODE_DIR/network/hostname)" if confirm "Set hostname to $EXPECTED_HOSTNAME?"; then echo "$EXPECTED_HOSTNAME" > /etc/hostname hostname "$EXPECTED_HOSTNAME" success "Hostname set to $EXPECTED_HOSTNAME" APPLIED+=("hostname") else SKIPPED+=("hostname") fi # --------------------------------------------------------------------------- # SECTION 2: Network interfaces # --------------------------------------------------------------------------- header "2 / 8 — Network Configuration" if [[ -f "$NODE_DIR/network/interfaces" ]]; then info "Current /etc/network/interfaces:" cat /etc/network/interfaces | sed 's/^/ /' info "" info "Backed-up interfaces:" cat "$NODE_DIR/network/interfaces" | sed 's/^/ /' warn "Applying network config requires a reboot to take effect." if confirm "Restore network interfaces?"; then cp /etc/network/interfaces "/etc/network/interfaces.bak.$(date +%s)" cp "$NODE_DIR/network/interfaces" /etc/network/interfaces [[ -d "$NODE_DIR/network/interfaces.d" ]] && \ rsync -a "$NODE_DIR/network/interfaces.d/" /etc/network/interfaces.d/ success "Network interfaces restored (reboot required)" APPLIED+=("network") else SKIPPED+=("network") fi else warn "No network interfaces backup found — skipping" SKIPPED+=("network") fi # --------------------------------------------------------------------------- # SECTION 3: /etc/hosts # --------------------------------------------------------------------------- header "3 / 8 — /etc/hosts" if [[ -f "$NODE_DIR/network/hosts" ]]; then if confirm "Restore /etc/hosts?"; then cp /etc/hosts "/etc/hosts.bak.$(date +%s)" cp "$NODE_DIR/network/hosts" /etc/hosts success "/etc/hosts restored" APPLIED+=("hosts") else SKIPPED+=("hosts") fi fi # --------------------------------------------------------------------------- # SECTION 4: Custom scripts # --------------------------------------------------------------------------- header "4 / 8 — Custom Scripts (/usr/local/bin)" if [[ -d "$NODE_DIR/scripts" ]] && [[ -n "$(ls -A $NODE_DIR/scripts 2>/dev/null)" ]]; then info "Scripts to restore:" ls "$NODE_DIR/scripts" | sed 's/^/ /' if confirm "Restore custom scripts to /usr/local/bin/?"; then cp "$NODE_DIR/scripts/"* /usr/local/bin/ 2>/dev/null || true chmod +x /usr/local/bin/*.sh /usr/local/bin/*.py 2>/dev/null || true success "Scripts restored" APPLIED+=("scripts") if [[ "$TARGET" == "pve1" ]]; then warn "Large binaries NOT restored (ollama, filebrowser, etc.)" warn "Reinstall manually:" warn " ollama : curl -fsSL https://ollama.ai/install.sh | sh" warn " filebrowser: curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash" fi else SKIPPED+=("scripts") fi else info "No custom scripts backed up" fi # --------------------------------------------------------------------------- # SECTION 5: Systemd units # --------------------------------------------------------------------------- header "5 / 8 — Systemd Service Units" if [[ -d "$NODE_DIR/systemd" ]] && [[ -n "$(ls -A $NODE_DIR/systemd 2>/dev/null)" ]]; then info "Units to restore:" ls "$NODE_DIR/systemd" | sed 's/^/ /' if confirm "Restore and enable systemd units?"; then for unit in "$NODE_DIR/systemd/"*.service; do [[ -f "$unit" ]] || continue bname=$(basename "$unit") cp "$unit" /etc/systemd/system/ systemctl enable "$bname" 2>/dev/null || true info " Enabled: $bname" done systemctl daemon-reload success "Systemd units restored and enabled" APPLIED+=("systemd") warn "Units are ENABLED but not started — start manually after verifying prerequisites" warn " e.g.: systemctl start jarvis-agent filebrowser" else SKIPPED+=("systemd") fi fi # --------------------------------------------------------------------------- # SECTION 6: JARVIS agent # --------------------------------------------------------------------------- header "6 / 8 — JARVIS Monitoring Agent" if [[ -f "$NODE_DIR/jarvis-agent/config.json" ]]; then info "Agent config found:" cat "$NODE_DIR/jarvis-agent/config.json" | sed 's/^/ /' if confirm "Restore JARVIS agent config?"; then mkdir -p /opt/jarvis-agent cp "$NODE_DIR/jarvis-agent/config.json" /opt/jarvis-agent/config.json # Install agent if not present if [[ ! -f /opt/jarvis-agent/jarvis-agent.py ]]; then info "Downloading JARVIS agent..." curl -sk https://jarvis.orbishosting.com/install-agent.sh | bash -s "$(hostname)" proxmox || \ warn "Auto-install failed — manually install: curl -sk https://jarvis.orbishosting.com/install-agent.sh | bash -s $(hostname) proxmox" fi success "JARVIS agent restored" APPLIED+=("jarvis-agent") else SKIPPED+=("jarvis-agent") fi fi # --------------------------------------------------------------------------- # SECTION 7: Root crontab # --------------------------------------------------------------------------- header "7 / 8 — Root Crontab" if [[ -f "$NODE_DIR/cron/root" ]] && ! grep -q "^# no crontab" "$NODE_DIR/cron/root"; then info "Cron entries to restore:" cat "$NODE_DIR/cron/root" | sed 's/^/ /' if confirm "Restore root crontab?"; then crontab "$NODE_DIR/cron/root" success "Crontab restored" APPLIED+=("crontab") else SKIPPED+=("crontab") fi else info "No crontab entries backed up — skipping" fi # --------------------------------------------------------------------------- # SECTION 8: PVE cluster configs (pve1 only — pve2 joins cluster instead) # --------------------------------------------------------------------------- header "8 / 8 — PVE Cluster / VM Configs" if [[ "$TARGET" == "pve1" ]] && [[ -d "$SHARED_DIR" ]]; then echo "" warn "PVE cluster configs (storage, VM configs, users, firewall) are" warn "managed by Proxmox's cluster filesystem (/etc/pve/)." warn "" warn "IMPORTANT: Do NOT copy /etc/pve/ files directly on a fresh install." warn "Proxmox will auto-populate these when the node is online." warn "" info "Instead, after Proxmox is installed and running:" info " 1. Restore VMs from Proxmox Backup Server (PBS) at 10.48.200.85" info " → In PVE web UI: Datacenter → Storage → ProxBackups" info " → PBS fingerprint: $(grep 'fingerprint' $SHARED_DIR/storage.cfg 2>/dev/null | head -1 | tr -d ' \t' || echo 'see shared/storage.cfg')" info "" info " 2. Re-add storage manually if PBS isn't auto-discovered:" info " → Datacenter → Storage → Add → Proxmox Backup Server" info "" info " 3. VM configs for reference (not to copy directly):" [[ -d "$SHARED_DIR/nodes/pve/qemu-server" ]] && ls "$SHARED_DIR/nodes/pve/qemu-server/" | sed 's/^/ /' echo "" if confirm "Copy VM .conf files to /etc/pve/nodes/pve/qemu-server/ (only do this if cluster is NOT active)?"; then mkdir -p /etc/pve/nodes/pve/qemu-server /etc/pve/nodes/pve/lxc cp "$SHARED_DIR/nodes/pve/qemu-server/"*.conf /etc/pve/nodes/pve/qemu-server/ 2>/dev/null || true cp "$SHARED_DIR/nodes/pve/lxc/"*.conf /etc/pve/nodes/pve/lxc/ 2>/dev/null || true cp "$SHARED_DIR/storage.cfg" /etc/pve/ 2>/dev/null || true cp "$SHARED_DIR/datacenter.cfg" /etc/pve/ 2>/dev/null || true success "PVE configs copied — restart pve-cluster to apply" systemctl restart pve-cluster 2>/dev/null || true APPLIED+=("pve-configs") else SKIPPED+=("pve-configs") info "Skipped — restore VMs from PBS backup instead (recommended)" fi elif [[ "$TARGET" == "pve2" ]]; then warn "PVE2 does not hold the primary cluster config." info "To restore PVE2 as a cluster member:" info " 1. Fresh Proxmox install with hostname 'pve2' and IP 10.48.200.91" info " 2. On PVE1: pvecm add 10.48.200.91" info " 3. PVE2 will auto-sync all VM configs and cluster state from PVE1" info " 4. VM 106 and 302 will reappear automatically" SKIPPED+=("pve-configs") fi # --------------------------------------------------------------------------- # Summary # --------------------------------------------------------------------------- header "Restore Complete" echo "" if [[ ${#APPLIED[@]} -gt 0 ]]; then success "Applied sections: ${APPLIED[*]}" fi if [[ ${#SKIPPED[@]} -gt 0 ]]; then warn "Skipped sections: ${SKIPPED[*]}" fi echo "" echo -e "${YELLOW} Next steps:${NC}" if [[ " ${APPLIED[*]} " =~ " network " ]]; then echo " • REBOOT to apply network changes: reboot" fi echo " • Verify Proxmox web UI at https://$EXPECTED_IP:8006" echo " • Restore VMs from PBS backup server at https://10.48.200.85:8007" echo " • Start JARVIS agent: systemctl start jarvis-agent" if [[ "$TARGET" == "pve1" ]]; then echo " • Reinstall ollama if needed: curl -fsSL https://ollama.ai/install.sh | sh" echo " • Reinstall filebrowser: curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash" fi echo ""