From 496c5a960af369f9c73e3f95b6bc917eb4465e39 Mon Sep 17 00:00:00 2001 From: Lloyd Date: Tue, 24 Feb 2026 09:29:43 +0000 Subject: [PATCH] Add Proxmox LXC installer script for pyMC Repeater with USB passthrough --- scripts/proxmox-install.sh | 203 +++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 scripts/proxmox-install.sh diff --git a/scripts/proxmox-install.sh b/scripts/proxmox-install.sh new file mode 100644 index 0000000..3c1ca41 --- /dev/null +++ b/scripts/proxmox-install.sh @@ -0,0 +1,203 @@ +#!/usr/bin/env bash +# pyMC Repeater - Proxmox LXC Installer +# Creates an LXC container with USB passthrough and installs pyMC Repeater +# +# Usage (run on the Proxmox host): +# bash -c "$(curl -fsSL https://raw.githubusercontent.com/rightup/pyMC_Repeater/main/scripts/proxmox-install.sh)" +# +# License: MIT +# Source: https://github.com/rightup/pyMC_Repeater + +set -euo pipefail + +# ── Defaults ─────────────────────────────────────────────────────────────── +REPO="https://github.com/rightup/pyMC_Repeater.git" +BRANCH="feat/E22p" +CT_TEMPLATE="debian-12-standard" +CT_RAM=1024 +CT_SWAP=512 +CT_DISK=4 +CT_CORES=2 +CT_HOSTNAME="pymc-repeater" +CT_BRIDGE="vmbr0" +CT_STORAGE="local-lvm" +CT_TEMPLATE_STORAGE="local" +CH341_VID="1a86" +CH341_PID="5512" + +# ── Colors ───────────────────────────────────────────────────────────────── +RD="\033[01;31m" GN="\033[1;92m" YW="\033[33m" BL="\033[36m" BLD="\033[1m" CL="\033[m" + +msg_info() { echo -e " ${BL}ℹ${CL} ${1}"; } +msg_ok() { echo -e " ${GN}✓${CL} ${1}"; } +msg_warn() { echo -e " ${YW}⚠${CL} ${1}"; } +msg_error() { echo -e " ${RD}✗${CL} ${1}"; } + +header() { + clear + echo -e "${BLD}" + echo "═══════════════════════════════════════════════════════════════" + echo " pyMC Repeater - Proxmox LXC Installer" + echo "═══════════════════════════════════════════════════════════════" + echo -e "${CL}" +} + +cleanup() { + local exit_code=$? + if [ $exit_code -ne 0 ] && [ -n "${CTID:-}" ] && pct status "$CTID" &>/dev/null; then + echo "" + read -p " Delete the failed container ${CTID}? [y/N]: " -r + if [[ "$REPLY" =~ ^[Yy]$ ]]; then + pct stop "$CTID" 2>/dev/null || true + pct destroy "$CTID" 2>/dev/null || true + msg_ok "Container ${CTID} removed" + fi + fi +} +trap cleanup EXIT + +# ── Preflight checks ────────────────────────────────────────────────────── +header + +if ! command -v pct &>/dev/null; then + msg_error "This script must be run on a Proxmox VE host." + exit 1 +fi + +if [ "$EUID" -ne 0 ]; then + msg_error "Please run as root" + exit 1 +fi + +msg_ok "Running on Proxmox host as root" + +# Check for CH341 +echo "" +if lsusb -d "${CH341_VID}:${CH341_PID}" &>/dev/null; then + msg_ok "CH341 USB device detected" +else + msg_warn "CH341 USB device not found — plug it in before starting the repeater" +fi + +# ── Interactive settings ────────────────────────────────────────────────── +echo "" +echo -e "${BLD}Container Settings${CL} (press Enter for defaults):" +echo "" + +read -p " Hostname [${CT_HOSTNAME}]: " -r input; CT_HOSTNAME="${input:-$CT_HOSTNAME}" +read -p " RAM in MB [${CT_RAM}]: " -r input; CT_RAM="${input:-$CT_RAM}" +read -p " Disk in GB [${CT_DISK}]: " -r input; CT_DISK="${input:-$CT_DISK}" +read -p " CPU cores [${CT_CORES}]: " -r input; CT_CORES="${input:-$CT_CORES}" +read -p " Bridge [${CT_BRIDGE}]: " -r input; CT_BRIDGE="${input:-$CT_BRIDGE}" + +AVAILABLE_STORAGES=$(pvesm status -content rootdir 2>/dev/null | awk 'NR>1 {print $1}' || echo "local-lvm") +echo " Available storages: ${AVAILABLE_STORAGES}" +read -p " Storage [${CT_STORAGE}]: " -r input; CT_STORAGE="${input:-$CT_STORAGE}" +read -p " Git branch [${BRANCH}]: " -r input; BRANCH="${input:-$BRANCH}" + +# ── Get next CTID ───────────────────────────────────────────────────────── +CTID=$(pvesh get /cluster/nextid) + +# ── Confirmation ────────────────────────────────────────────────────────── +echo "" +echo -e "${BLD}Summary:${CL}" +echo " CTID: ${CTID} Host: ${CT_HOSTNAME} RAM: ${CT_RAM}MB Disk: ${CT_DISK}GB" +echo " Cores: ${CT_CORES} Storage: ${CT_STORAGE} Bridge: ${CT_BRIDGE} Branch: ${BRANCH}" +echo " Mode: privileged (required for USB passthrough)" +echo "" +read -p " Proceed? [Y/n]: " -r +[[ "${REPLY:-Y}" =~ ^[Nn]$ ]] && { msg_warn "Aborted"; exit 0; } + +# ── Download template ───────────────────────────────────────────────────── +echo "" +msg_info "Downloading Debian 12 template..." +TEMPLATE_FILE=$(pveam available -section system 2>/dev/null | grep "${CT_TEMPLATE}" | sort -t- -k4 -V | tail -1 | awk '{print $2}') +[ -z "$TEMPLATE_FILE" ] && { msg_error "Template not found. Run: pveam update"; exit 1; } + +pveam list "$CT_TEMPLATE_STORAGE" 2>/dev/null | grep -q "$TEMPLATE_FILE" || \ + pveam download "$CT_TEMPLATE_STORAGE" "$TEMPLATE_FILE" +msg_ok "Template ready" + +# ── Create container ────────────────────────────────────────────────────── +msg_info "Creating LXC container ${CTID}..." +pct create "$CTID" "${CT_TEMPLATE_STORAGE}:vztmpl/${TEMPLATE_FILE}" \ + --hostname "$CT_HOSTNAME" \ + --memory "$CT_RAM" \ + --swap "$CT_SWAP" \ + --cores "$CT_CORES" \ + --rootfs "${CT_STORAGE}:${CT_DISK}" \ + --net0 "name=eth0,bridge=${CT_BRIDGE},ip=dhcp" \ + --unprivileged 0 \ + --features nesting=1 \ + --onboot 1 \ + --start 0 \ + --ostype debian +msg_ok "Container created" + +# ── USB passthrough ─────────────────────────────────────────────────────── +msg_info "Configuring USB passthrough..." +cat >> "/etc/pve/lxc/${CTID}.conf" <<'EOF' + +# CH341 USB passthrough for pyMC Repeater +lxc.cgroup2.devices.allow: c 189:* rwm +lxc.mount.entry: /dev/bus/usb dev/bus/usb none bind,optional,create=dir 0 0 +EOF +msg_ok "USB passthrough configured" + +# ── Host udev rule ──────────────────────────────────────────────────────── +msg_info "Installing CH341 udev rule on host..." +echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="5512", MODE="0666"' \ + > /etc/udev/rules.d/99-ch341.rules +udevadm control --reload-rules +udevadm trigger --subsystem-match=usb --action=change +msg_ok "Host udev rule installed" + +# ── Start container & wait for network ──────────────────────────────────── +msg_info "Starting container..." +pct start "$CTID" +sleep 3 +for _ in $(seq 1 30); do + pct exec "$CTID" -- ping -c1 -W1 8.8.8.8 &>/dev/null && break + sleep 1 +done +msg_ok "Container running with network" + +# ── Bootstrap: install git, clone repo ──────────────────────────────────── +msg_info "Installing git inside container..." +pct exec "$CTID" -- bash -c " + export DEBIAN_FRONTEND=noninteractive + apt-get update -qq && apt-get install -y git whiptail >/dev/null 2>&1 +" +msg_ok "Git installed" + +msg_info "Cloning pyMC_Repeater (branch: ${BRANCH})..." +pct exec "$CTID" -- bash -c "git clone --branch ${BRANCH} ${REPO} /root/pyMC_Repeater" +msg_ok "Repository cloned" + +# ── Run manage.sh install ───────────────────────────────────────────────── +msg_info "Running manage.sh install (this will take several minutes)..." +echo "" +# Use lxc-attach with a pty so manage.sh gets an interactive terminal +lxc-attach -n "$CTID" -- bash -c "cd /root/pyMC_Repeater && TERM=xterm bash manage.sh install" +echo "" +msg_ok "manage.sh install completed" + +# ── Get container IP ────────────────────────────────────────────────────── +sleep 2 +CT_IP=$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}') + +# ── Done ────────────────────────────────────────────────────────────────── +echo "" +echo -e "${BLD}" +echo "═══════════════════════════════════════════════════════════════" +echo " ✓ pyMC Repeater Installation Complete!" +echo "═══════════════════════════════════════════════════════════════" +echo -e "${CL}" +echo -e " Container: ${GN}${CTID}${CL} (${CT_HOSTNAME})" +echo -e " IP Address: ${GN}${CT_IP:-unknown}${CL}" +echo -e " Dashboard: ${GN}http://${CT_IP:-}:8000${CL}" +echo "" +echo " Next: open the dashboard and complete the setup wizard" +echo " Management: pct enter ${CTID}, then: cd /opt/pymc_repeater && bash manage.sh" +echo "" +echo "═══════════════════════════════════════════════════════════════"