Files
ProxMenux/scripts/oci/oci_manager.sh
T
2026-04-09 22:05:23 +02:00

523 lines
14 KiB
Bash

#!/bin/bash
# ProxMenux - OCI Application Manager
# ============================================
# Author : MacRimi
# License : MIT
# Version : 1.0
# ============================================
# Manages OCI container applications through dialog menus or direct CLI.
# This script wraps the Python oci_manager.py for terminal usage.
SCRIPT_TITLE="OCI Application Manager"
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
BASE_DIR="/usr/local/share/proxmenux"
UTILS_FILE="$BASE_DIR/utils.sh"
OCI_MANAGER="$LOCAL_SCRIPTS/oci_manager.py"
# OCI paths - persistent data in proxmenux directory
OCI_DIR="$BASE_DIR/oci"
OCI_CATALOG="$OCI_DIR/catalog.json"
OCI_INSTALLED="$OCI_DIR/installed.json"
OCI_INSTANCES="$OCI_DIR/instances"
# Source catalog bundled with Scripts
SCRIPTS_CATALOG="$LOCAL_SCRIPTS/oci/catalog.json"
export BASE_DIR
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
load_language 2>/dev/null || true
initialize_cache 2>/dev/null || true
# ==========================================================
# OCI Directory Initialization
# ==========================================================
ensure_oci_directories() {
# Create OCI directories if they don't exist
mkdir -p "$OCI_DIR"
mkdir -p "$OCI_INSTANCES"
# Copy catalog from Scripts if not present
if [[ ! -f "$OCI_CATALOG" && -f "$SCRIPTS_CATALOG" ]]; then
cp "$SCRIPTS_CATALOG" "$OCI_CATALOG"
msg_ok "Initialized OCI catalog"
fi
# Create empty installed.json if not present
if [[ ! -f "$OCI_INSTALLED" ]]; then
echo '{"version": "1.0.0", "instances": {}}' > "$OCI_INSTALLED"
fi
}
# ==========================================================
# Proxmox OCI Support Detection
# ==========================================================
check_proxmox_oci_support() {
PVE_VERSION=""
OCI_SUPPORTED=false
# Get Proxmox VE version
if command -v pveversion >/dev/null 2>&1; then
PVE_VERSION=$(pveversion | grep -oP 'pve-manager/\K[0-9]+\.[0-9]+' | head -1)
# Check if version >= 9.1
local major minor
major=$(echo "$PVE_VERSION" | cut -d. -f1)
minor=$(echo "$PVE_VERSION" | cut -d. -f2)
if [[ $major -gt 9 ]] || [[ $major -eq 9 && $minor -ge 1 ]]; then
OCI_SUPPORTED=true
fi
fi
}
check_oci_support() {
check_proxmox_oci_support
if [[ "$OCI_SUPPORTED" != "true" ]]; then
msg_error "$(translate "OCI containers require Proxmox VE 9.1 or later.")"
msg_info2 "$(translate "Current version: $PVE_VERSION")"
return 1
fi
return 0
}
# ==========================================================
# Helper Functions
# ==========================================================
run_oci_manager() {
python3 "$OCI_MANAGER" "$@"
}
get_app_status() {
local app_id="$1"
run_oci_manager status --app-id "$app_id" 2>/dev/null | jq -r '.state // "not_installed"'
}
is_installed() {
local app_id="$1"
local status=$(get_app_status "$app_id")
[[ "$status" != "not_installed" ]]
}
# ==========================================================
# Secure Gateway Functions
# ==========================================================
deploy_secure_gateway() {
show_proxmenux_logo
msg_title "$(translate "Secure Gateway (Tailscale VPN)")"
if ! check_oci_support; then
return 1
fi
# Check if already installed
if is_installed "secure-gateway"; then
local status=$(get_app_status "secure-gateway")
msg_warn "$(translate "Secure Gateway is already installed.")"
msg_info2 "Status: $status"
echo ""
read -p "$(translate "Press Enter to continue...")" _
return 0
fi
msg_info2 "$(translate "This will deploy a Tailscale VPN gateway for secure remote access.")"
msg_info2 "$(translate "You will need a Tailscale auth key from: https://login.tailscale.com/admin/settings/keys")"
echo ""
# Get auth key
local auth_key
while true; do
read -p "$(translate "Enter Tailscale Auth Key"): " auth_key
if [[ -z "$auth_key" ]]; then
msg_error "$(translate "Auth key is required.")"
continue
fi
if [[ ! "$auth_key" =~ ^tskey- ]]; then
msg_warn "$(translate "Warning: Auth key should start with 'tskey-'")"
fi
break
done
# Get hostname
local default_hostname="${HOSTNAME:-proxmox}-gateway"
read -p "$(translate "Device hostname") [$default_hostname]: " hostname
hostname="${hostname:-$default_hostname}"
# Access mode
echo ""
msg_info2 "$(translate "Access Scope:")"
echo " 1) Host Only - ProxMenux Monitor & Proxmox UI only"
echo " 2) Proxmox Network - Include VMs, LXCs, and host services"
echo " 3) Custom - Select specific networks"
echo ""
local access_mode="host_only"
local routes=""
read -p "$(translate "Select access mode") [1]: " mode_choice
case "$mode_choice" in
2)
access_mode="proxmox_network"
# Auto-detect networks
routes=$(detect_networks_for_routing)
;;
3)
access_mode="custom"
msg_info2 "$(translate "Enter networks to advertise (comma-separated CIDR):")"
msg_info2 " Example: 10.0.1.0/24,192.168.1.0/24"
read -p "Networks: " routes
;;
*)
access_mode="host_only"
;;
esac
# Exit node option
local exit_node="false"
read -p "$(translate "Offer as exit node?") [y/N]: " exit_choice
[[ "$exit_choice" =~ ^[Yy] ]] && exit_node="true"
# Accept routes option
local accept_routes="false"
read -p "$(translate "Accept routes from other nodes?") [y/N]: " accept_choice
[[ "$accept_choice" =~ ^[Yy] ]] && accept_routes="true"
echo ""
msg_info2 "$(translate "Configuration Summary:")"
echo " Hostname: $hostname"
echo " Access Mode: $access_mode"
[[ -n "$routes" ]] && echo " Networks: $routes"
echo " Exit Node: $exit_node"
echo " Accept Routes: $accept_routes"
echo ""
read -p "$(translate "Deploy with this configuration?") [Y/n]: " confirm
if [[ "$confirm" =~ ^[Nn] ]]; then
msg_warn "$(translate "Deployment cancelled.")"
return 0
fi
# Build config JSON
local routes_array="[]"
if [[ -n "$routes" ]]; then
# Convert comma-separated to JSON array
routes_array=$(echo "$routes" | tr ',' '\n' | jq -R . | jq -s .)
fi
local config_json=$(cat <<EOF
{
"auth_key": "$auth_key",
"hostname": "$hostname",
"access_mode": "$access_mode",
"advertise_routes": $routes_array,
"exit_node": $exit_node,
"accept_routes": $accept_routes
}
EOF
)
msg_info "$(translate "Deploying Secure Gateway...")"
local result
result=$(run_oci_manager deploy --app-id "secure-gateway" --config "$config_json" --source "cli" 2>&1)
if echo "$result" | jq -e '.success == true' >/dev/null 2>&1; then
msg_ok "$(translate "Secure Gateway deployed successfully!")"
msg_info2 "$(translate "The gateway should appear in your Tailscale admin console shortly.")"
else
local error_msg=$(echo "$result" | jq -r '.message // "Unknown error"')
msg_error "$(translate "Deployment failed"): $error_msg"
return 1
fi
echo ""
read -p "$(translate "Press Enter to continue...")" _
}
detect_networks_for_routing() {
# Detect bridge interfaces and their subnets
local networks=""
for iface in $(ip -o link show | awk -F': ' '{print $2}' | grep -E '^vmbr|^bond' | head -5); do
local subnet=$(ip -4 addr show "$iface" 2>/dev/null | grep -oP 'inet \K[\d.]+/\d+' | head -1)
if [[ -n "$subnet" ]]; then
# Convert IP/prefix to network/prefix
local network=$(python3 -c "import ipaddress; print(ipaddress.IPv4Network('$subnet', strict=False))" 2>/dev/null)
if [[ -n "$network" ]]; then
[[ -n "$networks" ]] && networks="$networks,"
networks="$networks$network"
fi
fi
done
echo "$networks"
}
manage_secure_gateway() {
show_proxmenux_logo
msg_title "$(translate "Manage Secure Gateway")"
local status=$(get_app_status "secure-gateway")
msg_info2 "Current status: $status"
echo ""
case "$status" in
"running")
echo "1) Stop gateway"
echo "2) Restart gateway"
echo "3) View logs"
echo "4) Remove gateway"
echo "5) Back"
;;
"stopped"|"exited")
echo "1) Start gateway"
echo "2) View logs"
echo "3) Remove gateway"
echo "4) Back"
;;
*)
msg_error "$(translate "Gateway is not installed.")"
read -p "$(translate "Press Enter to continue...")" _
return
;;
esac
echo ""
read -p "$(translate "Select option"): " choice
case "$status" in
"running")
case "$choice" in
1) action_stop_gateway ;;
2) action_restart_gateway ;;
3) action_view_logs ;;
4) action_remove_gateway ;;
esac
;;
"stopped"|"exited")
case "$choice" in
1) action_start_gateway ;;
2) action_view_logs ;;
3) action_remove_gateway ;;
esac
;;
esac
}
action_start_gateway() {
msg_info "$(translate "Starting gateway...")"
local result=$(run_oci_manager start --app-id "secure-gateway" 2>&1)
if echo "$result" | jq -e '.success == true' >/dev/null 2>&1; then
msg_ok "$(translate "Gateway started.")"
else
msg_error "$(translate "Failed to start gateway.")"
fi
read -p "$(translate "Press Enter to continue...")" _
}
action_stop_gateway() {
msg_info "$(translate "Stopping gateway...")"
local result=$(run_oci_manager stop --app-id "secure-gateway" 2>&1)
if echo "$result" | jq -e '.success == true' >/dev/null 2>&1; then
msg_ok "$(translate "Gateway stopped.")"
else
msg_error "$(translate "Failed to stop gateway.")"
fi
read -p "$(translate "Press Enter to continue...")" _
}
action_restart_gateway() {
msg_info "$(translate "Restarting gateway...")"
local result=$(run_oci_manager restart --app-id "secure-gateway" 2>&1)
if echo "$result" | jq -e '.success == true' >/dev/null 2>&1; then
msg_ok "$(translate "Gateway restarted.")"
else
msg_error "$(translate "Failed to restart gateway.")"
fi
read -p "$(translate "Press Enter to continue...")" _
}
action_view_logs() {
echo ""
msg_info2 "$(translate "Recent logs:")"
echo "----------------------------------------"
$RUNTIME logs --tail 50 proxmenux-secure-gateway 2>/dev/null || echo "No logs available"
echo "----------------------------------------"
echo ""
read -p "$(translate "Press Enter to continue...")" _
}
action_remove_gateway() {
echo ""
read -p "$(translate "Remove Secure Gateway? State will be preserved.") [y/N]: " confirm
if [[ ! "$confirm" =~ ^[Yy] ]]; then
return
fi
msg_info "$(translate "Removing gateway...")"
local result=$(run_oci_manager remove --app-id "secure-gateway" 2>&1)
if echo "$result" | jq -e '.success == true' >/dev/null 2>&1; then
msg_ok "$(translate "Gateway removed.")"
else
msg_error "$(translate "Failed to remove gateway.")"
fi
read -p "$(translate "Press Enter to continue...")" _
}
# ==========================================================
# Main Menu
# ==========================================================
show_oci_menu() {
while true; do
show_proxmenux_logo
msg_title "$(translate "$SCRIPT_TITLE")"
detect_runtime
if [[ -z "$RUNTIME" ]]; then
msg_warn "$(translate "No container runtime available.")"
msg_info2 "$(translate "Install podman or docker to continue.")"
echo ""
read -p "$(translate "Press Enter to exit...")" _
return
fi
msg_info2 "Runtime: $RUNTIME $RUNTIME_VERSION"
echo ""
# Check gateway status
local gw_status=$(get_app_status "secure-gateway")
local gw_label="Secure Gateway (Tailscale VPN)"
if [[ "$gw_status" != "not_installed" ]]; then
gw_label="$gw_label [$gw_status]"
fi
echo "1) $gw_label"
echo ""
echo "0) $(translate "Exit")"
echo ""
read -p "$(translate "Select option"): " choice
case "$choice" in
1)
if [[ "$gw_status" == "not_installed" ]]; then
deploy_secure_gateway
else
manage_secure_gateway
fi
;;
0|q|Q)
break
;;
*)
msg_error "$(translate "Invalid option")"
sleep 1
;;
esac
done
}
# ==========================================================
# CLI Mode
# ==========================================================
cli_mode() {
local command="$1"
shift
case "$command" in
deploy)
local app_id=""
local config=""
while [[ $# -gt 0 ]]; do
case "$1" in
--app-id) app_id="$2"; shift 2 ;;
--config) config="$2"; shift 2 ;;
*) shift ;;
esac
done
if [[ -z "$app_id" ]]; then
echo "Error: --app-id required"
exit 1
fi
run_oci_manager deploy --app-id "$app_id" --config "${config:-{}}" --source "cli"
;;
start|stop|restart|remove|status)
local app_id=""
while [[ $# -gt 0 ]]; do
case "$1" in
--app-id) app_id="$2"; shift 2 ;;
*) shift ;;
esac
done
if [[ -z "$app_id" ]]; then
echo "Error: --app-id required"
exit 1
fi
run_oci_manager "$command" --app-id "$app_id"
;;
list)
run_oci_manager list
;;
catalog)
run_oci_manager catalog
;;
networks)
run_oci_manager networks
;;
runtime)
run_oci_manager runtime
;;
*)
echo "Usage: $0 [command] [options]"
echo ""
echo "Commands:"
echo " (no args) Interactive menu"
echo " deploy Deploy an app (--app-id, --config)"
echo " start Start an app (--app-id)"
echo " stop Stop an app (--app-id)"
echo " restart Restart an app (--app-id)"
echo " remove Remove an app (--app-id)"
echo " status Get app status (--app-id)"
echo " list List installed apps"
echo " catalog List available apps"
echo " networks Detect available networks"
echo " runtime Show container runtime info"
exit 1
;;
esac
}
# ==========================================================
# Entry Point
# ==========================================================
main() {
# Initialize OCI directories and catalog
ensure_oci_directories
if [[ $# -gt 0 ]]; then
cli_mode "$@"
else
show_oci_menu
fi
}
main "$@"