#!/bin/bash set -e # ╔════════════════════════════════════════════════════════════════════╗ # ║ VM Bench — Full Deployment Script (run on Proxmox host srv2) ║ # ║ ║ # ║ Usage: bash /mnt/converter/backend/install.sh ║ # ║ ║ # ║ Does everything: ║ # ║ 1. Creates /mnt/converter/ directory tree ║ # ║ 2. Clones (or pulls) the git repository ║ # ║ 3. Installs system packages + Python deps + systemd service ║ # ║ 4. Prompts for container ID + network config ║ # ║ 5. Creates the vm-bench LXC container with bind mount ║ # ╚════════════════════════════════════════════════════════════════════╝ # ─────────────────────────────────────────────────────────────────── # Colours & paths # ─────────────────────────────────────────────────────────────────── RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[0;33m'; CYAN='\033[0;36m'; NC='\033[0m' REPO_URL="https://git.lohmar.co.uk/cclohmar/vm-bench.git" CONVERTER_ROOT="/mnt/converter" SCRIPT_DIR="${CONVERTER_ROOT}/backend" SERVICE_NAME="vm-bench-backend" SERVICE_FILE="${SCRIPT_DIR}/${SERVICE_NAME}.service" SYSTEMD_DIR="/etc/systemd/system" # ─────────────────────────────────────────────────────────────────── # Banner # ─────────────────────────────────────────────────────────────────── echo "" echo "╔═════════════════════════════════════════════════════════╗" echo "║ VM Bench — Full Deployment Installer ║" echo "║ Target: Proxmox host (srv2) ║" echo "╚═════════════════════════════════════════════════════════╝" echo "" # ─────────────────────────────────────────────────────────────────── # Pre-flight: must run on a Proxmox host # ─────────────────────────────────────────────────────────────────── echo "[preflight] Checking environment..." if ! command -v pct &>/dev/null; then echo " ${RED}✗${NC} 'pct' not found — this script must run on a Proxmox host." exit 1 fi if ! command -v git &>/dev/null; then echo " Installing git..." apt-get update -qq && apt-get install -y -qq git fi echo " ${GREEN}✓${NC} Running on Proxmox host." # ─────────────────────────────────────────────────────────────────── # Step 1: Create directory tree # ─────────────────────────────────────────────────────────────────── echo "" echo "[1/6] Creating directory structure..." for d in \ "$CONVERTER_ROOT" \ "$CONVERTER_ROOT/in" \ "$CONVERTER_ROOT/out" \ "$CONVERTER_ROOT/logs" \ "$CONVERTER_ROOT/backend" \ "$CONVERTER_ROOT/frontend" \ "$CONVERTER_ROOT/frontend/templates" \ "$CONVERTER_ROOT/frontend/static"; do if [ ! -d "$d" ]; then mkdir -p "$d" echo " Created: $d" fi done echo " ${GREEN}✓${NC} Directories ready." # ─────────────────────────────────────────────────────────────────── # Step 2: Clone or update the git repository # ─────────────────────────────────────────────────────────────────── echo "" echo "[2/6] Cloning / updating repository..." if [ -d "$CONVERTER_ROOT/.git" ]; then echo " Existing repo found, pulling latest..." git -C "$CONVERTER_ROOT" pull --ff-only echo " ${GREEN}✓${NC} Repository updated." else # Clone into a temp location, then move if /mnt/converter is not empty if [ -z "$(ls -A "$CONVERTER_ROOT" 2>/dev/null)" ]; then git clone "$REPO_URL" "$CONVERTER_ROOT" else echo " ${YELLOW}!${NC} ${CONVERTER_ROOT} is not empty — cloning into temp and copying..." TMP_CLONE=$(mktemp -d) git clone "$REPO_URL" "$TMP_CLONE" cp -a "$TMP_CLONE"/. "$CONVERTER_ROOT"/ rm -rf "$TMP_CLONE" fi echo " ${GREEN}✓${NC} Repository cloned." fi # ─────────────────────────────────────────────────────────────────── # Step 3: System packages # ─────────────────────────────────────────────────────────────────── echo "" echo "[3/6] Checking system packages..." PACKAGES=() REQUIRED=( python3 "python3" python3-pip "pip3" qemu-utils "qemu-img" libguestfs-tools "guestfish" p7zip-full "7z" unzip "unzip" unrar-free "unrar" tar "tar" gzip "gunzip" bzip2 "bunzip2" xz-utils "xz" ) for ((i=0; i<${#REQUIRED[@]}; i+=2)); do pkg="${REQUIRED[$i]}" bin="${REQUIRED[$i+1]}" if command -v "$bin" &>/dev/null; then echo " ${GREEN}✓${NC} $bin" else echo " ${YELLOW}○${NC} $bin (will install ${pkg})" PACKAGES+=("$pkg") fi done if [ ${#PACKAGES[@]} -gt 0 ]; then echo "" echo " Installing: ${PACKAGES[*]}" apt-get update -qq apt-get install -y -qq "${PACKAGES[@]}" echo " ${GREEN}✓${NC} System packages installed." else echo " ${GREEN}✓${NC} All system packages present." fi # ─────────────────────────────────────────────────────────────────── # Step 4: Python dependencies + systemd service # ─────────────────────────────────────────────────────────────────── echo "" echo "[4/6] Installing Python dependencies..." cd "$SCRIPT_DIR" python3 -m pip install --break-system-packages -r requirements.txt -q echo " ${GREEN}✓${NC} Python dependencies installed." echo "" echo "[5/6] Installing systemd service..." if [ ! -f "$SERVICE_FILE" ]; then echo " ${RED}✗${NC} Service file not found: $SERVICE_FILE" echo " Make sure the repository was cloned correctly." exit 1 fi cp "$SERVICE_FILE" "$SYSTEMD_DIR/${SERVICE_NAME}.service" systemctl daemon-reload systemctl enable "$SERVICE_NAME" systemctl restart "$SERVICE_NAME" sleep 2 if systemctl is-active --quiet "$SERVICE_NAME"; then echo " ${GREEN}✓${NC} Backend is RUNNING on port 9000" echo " Verify: curl http://127.0.0.1:9000/api/v1/health" else echo " ${RED}✗${NC} Backend failed to start. Check logs:" echo " journalctl -u ${SERVICE_NAME} -n 30" exit 1 fi # ─────────────────────────────────────────────────────────────────── # Step 6: Create the vm-bench LXC container # ─────────────────────────────────────────────────────────────────── echo "" echo "╔═════════════════════════════════════════════════════════╗" echo "║ LXC Container Setup (vm-bench frontend) ║" echo "╚═════════════════════════════════════════════════════════╝" echo "" # ── Prompt for container ID ────────────────────────────────────── read -p " Container ID (e.g. 20020): " CTID if [ -z "$CTID" ]; then echo " ${RED}✗${NC} Container ID is required." exit 1 fi # Check if ID already exists if pct status "$CTID" &>/dev/null; then echo " ${YELLOW}!${NC} Container $CTID already exists." read -p " Overwrite? [y/N]: " OVERWRITE if [ "$OVERWRITE" != "y" ] && [ "$OVERWRITE" != "Y" ]; then echo " Aborted." exit 0 fi echo " Destroying existing container $CTID..." pct stop "$CTID" --skiplock 2>/dev/null || true pct destroy "$CTID" --purge 2>/dev/null || true fi # ── Network settings ───────────────────────────────────────────── echo "" echo " ${CYAN}Network configuration:${NC}" read -p " IP address/CIDR [10.2.0.20/16]: " CT_IP read -p " Gateway [10.2.0.2]: " CT_GW read -p " Bridge [vmbr0]: " CT_BRIDGE read -p " MAC address [BC:24:11:80:5A:B1]: " CT_MAC CT_IP="${CT_IP:-10.2.0.20/16}" CT_GW="${CT_GW:-10.2.0.2}" CT_BRIDGE="${CT_BRIDGE:-vmbr0}" CT_MAC="${CT_MAC:-BC:24:11:80:5A:B1}" NET_CFG="name=eth0,bridge=${CT_BRIDGE},firewall=1,gw=${CT_GW},hwaddr=${CT_MAC},ip=${CT_IP},type=veth" ROOTFS="local-lvm:vm-${CTID}-disk-0,size=24G" # ── Template selection ──────────────────────────────────────────── echo "" echo " ${CYAN}Available Debian templates:${NC}" pveam available 2>/dev/null | grep debian-12 || echo " (none found locally, will attempt to download)" TEMPLATE=$(pveam available 2>/dev/null | grep -m1 "debian-12.*amd64" | awk '{print $2}') if [ -z "$TEMPLATE" ]; then # Try to find any available Debian template TEMPLATE=$(pveam available 2>/dev/null | grep -m1 "debian.*amd64" | awk '{print $2}') fi if [ -z "$TEMPLATE" ]; then echo " ${YELLOW}!${NC} No Debian template found. Downloading debian-12-standard..." pveam update 2>/dev/null || true pveam download local debian-12-standard_12.7-1_amd64.tar.zst 2>/dev/null || true TEMPLATE="local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst" else # Check if template is downloaded locally TEMPLATE_NAME=$(basename "$TEMPLATE") if [ ! -f "/var/lib/vz/template/cache/${TEMPLATE_NAME}" ]; then echo " Downloading template: ${TEMPLATE_NAME}..." pveam download local "$TEMPLATE_NAME" 2>/dev/null || { echo " ${YELLOW}!${NC} Auto-download failed. Trying to continue anyway..." } fi TEMPLATE="local:vztmpl/${TEMPLATE_NAME}" fi echo " Using template: ${TEMPLATE}" # ── Create container ────────────────────────────────────────────── echo "" echo " Creating container ${CTID}..." pct create "$CTID" "$TEMPLATE" \ --arch amd64 \ --cores 3 \ --hostname vm-bench \ --memory 6144 \ --net0 "$NET_CFG" \ --ostype debian \ --rootfs "$ROOTFS" \ --swap 2048 \ --unprivileged 0 \ --features nesting=1 \ --mp0 /mnt/converter,mp=/mnt/converter \ --onboot 1 \ --start 0 echo " ${GREEN}✓${NC} Container created." # ── Write LXC config overrides ──────────────────────────────────── CONF_FILE="/etc/pve/lxc/${CTID}.conf" echo "" echo " Writing LXC config overrides..." # Add lines only if not already present add_conf_line() { local key="$1" local val="$2" if ! grep -q "^${key}:" "$CONF_FILE" 2>/dev/null; then echo "${key}: ${val}" >> "$CONF_FILE" echo " + ${key}: ${val}" fi } add_conf_line "lxc.cgroup2.devices.allow" "a" add_conf_line "lxc.mount.auto" "proc:rw sys:rw cgroup:rw" add_conf_line "lxc.apparmor.profile" "unconfined" echo " ${GREEN}✓${NC} LXC config written." # ── Start the container ─────────────────────────────────────────── echo "" read -p " Start container now? [Y/n]: " START if [ "$START" != "n" ] && [ "$START" != "N" ]; then pct start "$CTID" sleep 3 if pct status "$CTID" | grep -q running; then echo " ${GREEN}✓${NC} Container ${CTID} is running." echo "" echo " ${CYAN}Next steps:${NC}" echo " 1. Enter the container: pct enter ${CTID}" echo " 2. Run the frontend installer:" echo " bash /mnt/converter/frontend/install.sh" echo "" echo " 3. Open the web UI: http://${CT_IP%/*}:5000" echo "" else echo " ${YELLOW}!${NC} Container may not have started. Check: pct status ${CTID}" fi fi # ─────────────────────────────────────────────────────────────────── # Done # ─────────────────────────────────────────────────────────────────── echo "" echo "╔═════════════════════════════════════════════════════════╗" echo "║ ${GREEN}Deployment complete.${NC} ║" echo "║ ║" echo "║ Backend: http://$(hostname -I | awk '{print $1}'):9000 ║" echo "║ Frontend: http://${CT_IP%/*}:5000 ║" echo "║ ║" echo "║ Backend logs: journalctl -u ${SERVICE_NAME} -f ║" echo "║ Container: pct enter ${CTID} ║" echo "╚═════════════════════════════════════════════════════════╝" echo ""