diff --git a/README.md b/README.md index b01cc64..85dc1b6 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ Browser vm-bench LXC Proxmox Host (srv2) ├── frontend/ # Web UI (runs on vm-bench LXC) │ ├── app.py # FastAPI app, routes, session handling │ ├── api_client.py # Typed REST client → backend +│ ├── install.sh # Frontend installer (system deps + service) +│ ├── vm-bench.service # Systemd unit for the frontend │ ├── requirements.txt │ ├── templates/ # Jinja2 HTML templates │ │ ├── base.html @@ -36,14 +38,14 @@ Browser vm-bench LXC Proxmox Host (srv2) │ │ └── polling.html # Progress bar + job status │ └── static/ │ └── proxmox.css -├── backend/ # REST API (deploy to Proxmox host srv2) +├── backend/ # REST API (deploys to Proxmox host srv2) │ ├── app.py # FastAPI app, routes │ ├── models.py # Pydantic request/response models │ ├── converter.py # Archive extraction, disk probing, OS/EFI detection │ ├── provisioner.py # VM provisioning (qm create/importdisk) -│ ├── requirements.txt -│ ├── install.sh # Systemd installation script -│ └── vm-bench-backend.service +│ ├── install.sh # Full deployment script (backend + LXC creation) +│ ├── vm-bench-backend.service +│ └── requirements.txt ├── open-api.yaml # API spec (single source of truth) ├── in/ # Shared staging directory (gitignored) └── out/ # Shared output directory (gitignored) @@ -60,53 +62,121 @@ Browser vm-bench LXC Proxmox Host (srv2) ## Prerequisites -### Backend (Proxmox host `srv2`) +### Proxmox host (`srv2`) -- Python 3.13+ -- `qemu-img` (from `qemu-utils`) -- `guestfish` (from `libguestfs-tools`) -- `7z`, `unzip`, `tar` (for archive extraction) -- `systemd` (for service management) +The host must be a Proxmox VE node with: +- `pct` — LXC container management (built-in) +- `git` — to clone the repository (install script handles this) +- Internet access to download container templates -### Frontend (`vm-bench` LXC) +### LXC container (`vm-bench`) -- Python 3.13+ -- `wget` (for URL downloads) -- `systemd` (for service management) -- Network access to backend at `10.2.0.2:9000` +Created automatically by the install script. Requires: +- 3 CPU cores, 6 GB RAM, 24 GB disk, 2 GB swap +- Debian 12 template (downloaded by script) +- Network bridge `vmbr0` -## Installation +## Deployment -### 1. Backend (on Proxmox host `srv2`) +All deployment is driven by a single script on the Proxmox host. It creates the +directory tree, clones the repo, installs the backend, and provisions the +`vm-bench` LXC container ready for the frontend. + +### Step 1: Clone & deploy (on `srv2`) ```bash -# The backend code is shared via /mnt/converter/backend/ -cd /mnt/converter/backend -bash install.sh +# Create the shared directory and clone the repo +mkdir -p /mnt/converter +cd /mnt/converter +git clone https://git.lohmar.co.uk/cclohmar/vm-bench.git . + +# Run the installer — handles everything interactively +bash backend/install.sh ``` -This installs Python dependencies, copies the systemd service, and starts -`vm-bench-backend` on port 9000. Verify: +The script will: +1. Create the `/mnt/converter/` directory tree +2. Clone or update the git repository +3. Install system packages (qemu-img, guestfish, archive tools, etc.) +4. Install Python dependencies and start the backend systemd service +5. **Prompt for container ID + network settings** +6. **Create and configure the `vm-bench` LXC container** with: + - 3 cores, 6 GB RAM, 24 GB rootfs, 2 GB swap + - Bind mount: `/mnt/converter` shared with host + - Unprivileged mode off, nesting enabled + - AppArmor unconfined, cgroup device access allowed +7. Optionally start the container +Example prompts: +``` +Container ID (e.g. 20020): 20020 +IP address/CIDR [10.2.0.20/16]: +Gateway [10.2.0.2]: +Bridge [vmbr0]: +MAC address [BC:24:11:80:5A:B1]: +``` + +Verify the backend is running: ```bash curl http://127.0.0.1:9000/api/v1/health # → {"status":"ok"} ``` -### 2. Frontend (on `vm-bench` LXC) +### Step 2: Install frontend (inside the LXC) + +After the container is created and started, enter it: ```bash -# Install dependencies -cd /mnt/converter/frontend -pip3 install --break-system-packages -r requirements.txt - -# Copy service file and start -cp /etc/systemd/system/vm-bench.service /etc/systemd/system/ -systemctl daemon-reload -systemctl enable --now vm-bench +pct enter 20020 # use your container ID ``` -The frontend serves on port 5000. Open `http://:5000` in a browser. +Inside the container, run the frontend installer: + +```bash +bash /mnt/converter/frontend/install.sh +``` + +This installs Python, pip, `wget`, `curl`, Python dependencies, and starts the +`vm-bench` systemd service on port 5000. + +Verify: +```bash +curl http://127.0.0.1:5000 +# → HTML response (the web UI) +``` + +Open `http://:5000` in a browser. + +### Container LXC Config (created by install script) + +| Setting | Value | +|---------|-------| +| Arch | `amd64` | +| Cores | `3` | +| Memory | `6144` MB | +| Swap | `2048` MB | +| Rootfs | `local-lvm:24G` | +| OS type | `debian` | +| Unprivileged | `0` (disabled) | +| Features | `nesting=1` | +| Network | `vmbr0`, firewall enabled, veth | +| Bind mount | `mp0: /mnt/converter,mp=/mnt/converter` | +| AppArmor | `unconfined` | +| Cgroup | `devices.allow: a`, `mount.auto: proc:rw sys:rw cgroup:rw` | + +### Re-deploying / updating + +The install scripts are idempotent — safe to re-run: + +```bash +# On srv2: update backend +cd /mnt/converter && git pull +bash backend/install.sh # skips LXC creation if container exists + +# Inside LXC: update frontend +cd /mnt/converter && git pull +bash frontend/install.sh +``` ## API Reference diff --git a/backend/install.sh b/backend/install.sh index d3ea400..d8b7bdc 100755 --- a/backend/install.sh +++ b/backend/install.sh @@ -1,56 +1,323 @@ #!/bin/bash set -e -# VM Bench Backend — Installer for Proxmox host (srv2) -# Run: bash /mnt/converter/backend/install.sh +# ╔════════════════════════════════════════════════════════════════════╗ +# ║ 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 ║ +# ╚════════════════════════════════════════════════════════════════════╝ -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# ─────────────────────────────────────────────────────────────────── +# 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" -echo "=== VM Bench Backend Installer ===" +# ─────────────────────────────────────────────────────────────────── +# Banner +# ─────────────────────────────────────────────────────────────────── +echo "" +echo "╔═════════════════════════════════════════════════════════╗" +echo "║ VM Bench — Full Deployment Installer ║" +echo "║ Target: Proxmox host (srv2) ║" +echo "╚═════════════════════════════════════════════════════════╝" echo "" -# 1. Install Python dependencies -echo "[1/4] Installing Python dependencies..." -cd "$SCRIPT_DIR" -python3 -m pip install --break-system-packages -r requirements.txt -q -echo " Dependencies installed." - -# 2. Copy systemd service file -echo "[2/4] Installing systemd service..." -if [ ! -f "$SERVICE_FILE" ]; then - echo " ERROR: ${SERVICE_FILE} not found." +# ─────────────────────────────────────────────────────────────────── +# 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 -cp "$SERVICE_FILE" "$SYSTEMD_DIR/${SERVICE_NAME}.service" -echo " Service file copied to ${SYSTEMD_DIR}/" +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." -# 3. Reload systemd and enable -echo "[3/4] Enabling service..." +# ─────────────────────────────────────────────────────────────────── +# 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/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" + 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" -echo " Service enabled (auto-start on boot)." - -# 4. Start the service -echo "[4/4] Starting service..." systemctl restart "$SERVICE_NAME" sleep 2 -# Verify if systemctl is-active --quiet "$SERVICE_NAME"; then - echo "" - echo " Backend is RUNNING on port 9000" - echo "" - echo " Check: curl http://127.0.0.1:9000/health" - echo " Logs: journalctl -u ${SERVICE_NAME} -f" + echo " ${GREEN}✓${NC} Backend is RUNNING on port 9000" + echo " Verify: curl http://127.0.0.1:9000/api/v1/health" else - echo "" - echo " WARNING: Service did not start. Check logs:" - echo " journalctl -u ${SERVICE_NAME} -n 30" + 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 "" -echo "=== Done ===" diff --git a/frontend/install.sh b/frontend/install.sh new file mode 100755 index 0000000..0f66c98 --- /dev/null +++ b/frontend/install.sh @@ -0,0 +1,131 @@ +#!/bin/bash +set -e + +# ─────────────────────────────────────────────────────────────────── +# VM Bench Frontend — Installer for vm-bench LXC +# +# Run: bash /mnt/converter/frontend/install.sh +# +# Installs system tools, Python dependencies, and the systemd +# service that serves the web UI on port 5000. +# ─────────────────────────────────────────────────────────────────── + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NC='\033[0m' + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SERVICE_NAME="vm-bench" +SERVICE_FILE="${SCRIPT_DIR}/${SERVICE_NAME}.service" +SYSTEMD_DIR="/etc/systemd/system" + +echo "" +echo "╔════════════════════════════════════════════╗" +echo "║ VM Bench Frontend Installer (LXC) ║" +echo "╚════════════════════════════════════════════╝" +echo "" + +# ─────────────────────────────────────────────────────────────────── +# Step 1: System packages +# ─────────────────────────────────────────────────────────────────── +echo "[1/5] Checking system packages..." + +PACKAGES=() +REQUIRED=( + python3 "python3" + python3-pip "pip3" + wget "wget" + curl "curl" +) + +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 missing packages: ${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 2: Python dependencies +# ─────────────────────────────────────────────────────────────────── +echo "" +echo "[2/5] Installing Python dependencies..." + +cd "$SCRIPT_DIR" +python3 -m pip install --break-system-packages -r requirements.txt -q +echo " ${GREEN}✓${NC} Python dependencies installed." + +# ─────────────────────────────────────────────────────────────────── +# Step 3: Verify backend connectivity +# ─────────────────────────────────────────────────────────────────── +echo "" +echo "[3/5] Checking backend connectivity..." + +BACKEND_URL="${BACKEND_URL:-http://10.2.0.2:9000}" +if curl -sf --max-time 5 "${BACKEND_URL}/api/v1/health" > /dev/null 2>&1; then + echo " ${GREEN}✓${NC} Backend reachable at $BACKEND_URL" +else + echo " ${YELLOW}!${NC} Backend not reachable at $BACKEND_URL" + echo " The frontend will start, but conversion jobs will fail until the" + echo " backend is running on srv2. You can change BACKEND_URL in" + echo " ${SYSTEMD_DIR}/${SERVICE_NAME}.service" +fi + +# ─────────────────────────────────────────────────────────────────── +# Step 4: Systemd service +# ─────────────────────────────────────────────────────────────────── +echo "" +echo "[4/5] Installing systemd service..." + +if [ ! -f "$SERVICE_FILE" ]; then + echo " ${RED}✗${NC} Service file not found: $SERVICE_FILE" + exit 1 +fi + +cp "$SERVICE_FILE" "$SYSTEMD_DIR/${SERVICE_NAME}.service" +systemctl daemon-reload +systemctl enable "$SERVICE_NAME" +echo " ${GREEN}✓${NC} Service installed and enabled." + +# ─────────────────────────────────────────────────────────────────── +# Step 5: Start & verify +# ─────────────────────────────────────────────────────────────────── +echo "" +echo "[5/5] Starting service..." + +systemctl restart "$SERVICE_NAME" +sleep 2 + +if systemctl is-active --quiet "$SERVICE_NAME"; then + echo " ${GREEN}✓${NC} Frontend is RUNNING on port 5000" + echo "" + echo " Open: http://$(hostname -I 2>/dev/null | awk '{print $1}'):5000" + echo " Verify: curl http://127.0.0.1:5000" + echo " Logs: journalctl -u ${SERVICE_NAME} -f" + echo " Status: systemctl status ${SERVICE_NAME}" +else + echo " ${RED}✗${NC} Service failed to start. Check logs:" + echo " journalctl -u ${SERVICE_NAME} -n 30" + exit 1 +fi + +echo "" +echo "╔════════════════════════════════════════════╗" +echo "║ ${GREEN}Frontend installation complete.${NC} ║" +echo "╚════════════════════════════════════════════╝" +echo "" diff --git a/frontend/vm-bench.service b/frontend/vm-bench.service new file mode 100644 index 0000000..a0d69af --- /dev/null +++ b/frontend/vm-bench.service @@ -0,0 +1,16 @@ +[Unit] +Description=VM Bench — Proxmox Image Conversion Frontend +After=network.target + +[Service] +Type=simple +User=root +WorkingDirectory=/mnt/converter/frontend +Environment=PATH=/usr/local/bin:/usr/bin:/bin +Environment=BACKEND_URL=http://10.2.0.2:9000 +ExecStart=/usr/bin/python3 -m uvicorn app:app --host 0.0.0.0 --port 5000 +Restart=always +RestartSec=3 + +[Install] +WantedBy=multi-user.target