Bugs found and fixed: - converter.py: STAGING_IN.iterdir() → extract_base.iterdir() (checked wrong dir when source was in session subdir) - vm-bench.service: TMPDIR=/mnt/converter/in → /mnt/converter/tmp (old path poisoned all temp file writes) - backend/app.py: removed unused STAGING_IN constant - converter.py: removed unused STAGING_OUT constant - install.sh: removed creation of old /in and /out dirs
324 lines
15 KiB
Bash
Executable file
324 lines
15 KiB
Bash
Executable file
#!/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/logs" \
|
|
"$CONVERTER_ROOT/tmp" \
|
|
"$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 ""
|