#!/bin/bash # LXS - Proxmox VE Management Script # Description: Tools for managing and troubleshooting Proxmox VE # Author: LXS # Date: 2025 # Load LXS common library (colors, separator, run_spinner, loggers, helpers) LXS_RAW_BASE="${LXS_RAW_BASE:-https://git.hyko.cx/hykocx/lxs/raw/branch/main}" _lib=$(curl -fsSL "${LXS_RAW_BASE}/lib/common.sh") || { echo "Failed to fetch lib/common.sh" >&2; exit 1; } eval "$_lib" unset _lib export LXS_LOG_FILE="/tmp/lxs_proxmox.log" require_root "$0" "$@" # ═══════════════════════════════════════════════════════════════════════════ # Configuration # ═══════════════════════════════════════════════════════════════════════════ LOCKFILE="/var/lib/pve-cluster/.pmxcfs.lockfile" SERVICE_NAME="pve-cluster" PANEL_PORT=8006 # ═══════════════════════════════════════════════════════════════════════════ # Helper Functions # ═══════════════════════════════════════════════════════════════════════════ is_proxmox() { [ -f /etc/pve/.version ] || command -v pveversion &> /dev/null } get_service_status() { if systemctl is-active --quiet $SERVICE_NAME; then echo -e "${GREEN}running${NC}" else echo -e "${RED}stopped${NC}" fi } check_proxmox() { if ! is_proxmox; then echo -e "${RED}[!] This is not a Proxmox VE system!${NC}" echo -e "${YELLOW} This script is designed for Proxmox VE only.${NC}" return 1 fi return 0 } # ═══════════════════════════════════════════════════════════════════════════ # Login Fix Functions # ═══════════════════════════════════════════════════════════════════════════ fix_login_issue() { local start_time=$(date +%s) echo -e "${WHITE}${BOLD}PROXMOX LOGIN FIX${NC}\n" check_proxmox || return 1 echo -e "${CYAN}This will reset the pve-cluster lockfile to fix login issues.${NC}" echo "" echo "[1/3] Stopping pve-cluster service..." run_spinner "Stopping $SERVICE_NAME..." "systemctl stop $SERVICE_NAME" if [ $? -ne 0 ]; then echo -e "${RED}[!] Failed to stop pve-cluster service${NC}" return 1 fi echo "" echo "[2/3] Removing lockfile..." if [ -f "$LOCKFILE" ]; then run_spinner "Removing lockfile..." "rm -f '$LOCKFILE'" echo -e "${GRAY} Lockfile was present and has been removed${NC}" else echo -e "${YELLOW}[!] Lockfile not found (may already be removed)${NC}" fi echo "" echo "[3/3] Starting pve-cluster service..." run_spinner "Starting $SERVICE_NAME..." "systemctl start $SERVICE_NAME" if [ $? -ne 0 ]; then echo -e "${RED}[!] Failed to start pve-cluster service${NC}" echo -e "${YELLOW} Check logs with: journalctl -xe${NC}" return 1 fi sleep 2 local duration=$(( $(date +%s) - start_time )) local server_ip=$(hostname -I | awk '{print $1}') echo "" show_separator echo -e "${GREEN}${BOLD}Fix Completed!${NC}" echo -e "${GRAY}Time: ${duration}s${NC}" echo "" echo -e "${WHITE}Service Status:${NC} $(get_service_status)" echo "" echo -e "${CYAN}You should now be able to login to the Proxmox web interface.${NC}" echo -e "${WHITE}Panel URL: ${GREEN}${BOLD}https://$server_ip:$PANEL_PORT${NC}" show_separator } # ═══════════════════════════════════════════════════════════════════════════ # Service Management Functions # ═══════════════════════════════════════════════════════════════════════════ restart_pve_cluster() { echo -e "${WHITE}${BOLD}RESTART PVE-CLUSTER${NC}\n" check_proxmox || return 1 run_spinner "Restarting $SERVICE_NAME..." "systemctl restart $SERVICE_NAME" sleep 2 echo "" echo -e "${WHITE}Service Status:${NC} $(get_service_status)" } restart_all_services() { echo -e "${WHITE}${BOLD}RESTART ALL PROXMOX SERVICES${NC}\n" check_proxmox || return 1 local services=("pve-cluster" "pvedaemon" "pveproxy" "pvestatd") for service in "${services[@]}"; do if systemctl list-unit-files | grep -q "^$service.service"; then run_spinner "Restarting $service..." "systemctl restart $service" fi done sleep 2 echo "" echo -e "${GREEN}[✓] All services restarted${NC}" echo "" echo -e "${WHITE}Panel URL: ${GREEN}${BOLD}https://$(hostname -I | awk '{print $1}'):$PANEL_PORT${NC}" } # ═══════════════════════════════════════════════════════════════════════════ # Status Functions # ═══════════════════════════════════════════════════════════════════════════ show_status() { echo -e "${WHITE}${BOLD}PROXMOX VE STATUS${NC}\n" check_proxmox || return 1 # Proxmox version echo -e "${WHITE}Proxmox Version:${NC}" pveversion 2>/dev/null || echo -e "${GRAY}Unable to determine version${NC}" echo "" # Service statuses echo -e "${WHITE}Service Status:${NC}" local services=("pve-cluster" "pvedaemon" "pveproxy" "pvestatd") for service in "${services[@]}"; do if systemctl list-unit-files | grep -q "^$service.service"; then if systemctl is-active --quiet $service; then echo -e " ${GREEN}[✓]${NC} $service" else echo -e " ${RED}[✗]${NC} $service" fi fi done echo "" # Lockfile status if [ -f "$LOCKFILE" ]; then echo -e "${WHITE}Lockfile:${NC} ${YELLOW}Present${NC} ($LOCKFILE)" else echo -e "${WHITE}Lockfile:${NC} ${GREEN}Not present${NC}" fi echo "" # Access URL local server_ip=$(hostname -I | awk '{print $1}') echo -e "${WHITE}Panel URL: ${GREEN}${BOLD}https://$server_ip:$PANEL_PORT${NC}" echo "" # Cluster info if command -v pvecm &> /dev/null; then echo -e "${WHITE}Cluster Status:${NC}" pvecm status 2>/dev/null | head -5 || echo -e "${GRAY}Not in a cluster or unable to get status${NC}" fi return 0 } show_storage_status() { echo -e "${WHITE}${BOLD}PROXMOX STORAGE STATUS${NC}\n" check_proxmox || return 1 # Storage list echo -e "${WHITE}Storage Configuration:${NC}" if command -v pvesm &> /dev/null; then pvesm status 2>/dev/null || echo -e "${GRAY}Unable to get storage status${NC}" else echo -e "${GRAY}pvesm command not available${NC}" fi echo "" # Disk usage echo -e "${WHITE}Disk Usage:${NC}" df -h / /var /tmp 2>/dev/null | head -5 return 0 } # ═══════════════════════════════════════════════════════════════════════════ # Maintenance Functions # ═══════════════════════════════════════════════════════════════════════════ clear_cache() { echo -e "${WHITE}${BOLD}CLEAR PROXMOX CACHE${NC}\n" check_proxmox || return 1 echo -e "${CYAN}This will clear various Proxmox caches and temporary files.${NC}" echo "" run_spinner "Clearing apt cache..." "apt clean" run_spinner "Clearing journal logs (older than 7 days)..." "journalctl --vacuum-time=7d" # Clear task logs older than 30 days if [ -d "/var/log/pve/tasks" ]; then run_spinner "Clearing old task logs..." "find /var/log/pve/tasks -type f -mtime +30 -delete" fi echo "" echo -e "${GREEN}[✓] Cache cleared${NC}" } update_proxmox() { echo -e "${WHITE}${BOLD}UPDATE PROXMOX VE${NC}\n" check_proxmox || return 1 require_disk_space 1024 || return 1 # Configure non-interactive mode export DEBIAN_FRONTEND=noninteractive export NEEDRESTART_MODE=a export NEEDRESTART_SUSPEND=1 local start_time=$(date +%s) run_spinner "Updating package lists..." "apt update" echo "" echo -e "${PURPLE}[*] Upgrading packages...${NC}" show_separator apt dist-upgrade -y local exit_code=$? show_separator if [ $exit_code -eq 0 ]; then local duration=$(( $(date +%s) - start_time )) echo "" echo -e "${GREEN}${BOLD}Update Completed!${NC}" echo -e "${GRAY}Time: $((duration / 60))m $((duration % 60))s${NC}" echo "" echo -e "${YELLOW}[!] A reboot may be required if the kernel was updated.${NC}" else echo "" echo -e "${RED}[✗] Update failed${NC}" return 1 fi } # ═══════════════════════════════════════════════════════════════════════════ # Network Functions # ═══════════════════════════════════════════════════════════════════════════ show_network_info() { echo -e "${WHITE}${BOLD}PROXMOX NETWORK INFO${NC}\n" check_proxmox || return 1 local server_ip=$(hostname -I | awk '{print $1}') local public_ip=$(get_public_ip) echo -e "${WHITE}Hostname:${NC} $(hostname)" echo -e "${WHITE}Local IP:${NC} $server_ip" echo -e "${WHITE}Public IP:${NC} $public_ip" echo "" echo -e "${WHITE}Network Interfaces:${NC}" ip -br addr show 2>/dev/null || ifconfig 2>/dev/null | grep -E "^[a-z]|inet " echo "" echo -e "${WHITE}Bridges:${NC}" if command -v brctl &> /dev/null; then brctl show 2>/dev/null || echo -e "${GRAY}No bridges found${NC}" else ip link show type bridge 2>/dev/null || echo -e "${GRAY}No bridges found${NC}" fi return 0 } # ═══════════════════════════════════════════════════════════════════════════ # Firewall # ═══════════════════════════════════════════════════════════════════════════ open_firewall_ports() { echo -e "${WHITE}${BOLD}OPEN PROXMOX FIREWALL PORTS${NC}\n" check_proxmox || return 1 if ! command -v ufw >/dev/null 2>&1; then echo -e "${YELLOW}[!] UFW is not installed on this host. Nothing to do.${NC}" echo -e "${GRAY} Proxmox uses its own pve-firewall; UFW is optional.${NC}" return 0 fi if ! ufw status 2>/dev/null | grep -q "Status: active"; then echo -e "${YELLOW}[!] UFW is installed but inactive. Enable it first.${NC}" return 0 fi ufw_allow "${PANEL_PORT}/tcp" "Proxmox web UI" ufw_allow 5900:5999/tcp "Proxmox VNC console" ufw_allow 3128/tcp "Proxmox SPICE proxy" echo "" echo -e "${GRAY}[i] For clustered nodes, also open: 5404-5405/udp (corosync), 60000-60050/tcp (live migration).${NC}" echo -e "${GRAY}[i] If using NFS storage: 111/tcp+udp and 2049/tcp.${NC}" } # ═══════════════════════════════════════════════════════════════════════════ # Main Menu # ═══════════════════════════════════════════════════════════════════════════ show_menu() { clear echo -e "${WHITE}${BOLD}PROXMOX VE MANAGEMENT${NC}\n" echo -e " ${GREEN}[1]${NC} Fix Login Issue (reset lockfile)" echo -e " ${YELLOW}[2]${NC} Restart pve-cluster service" echo -e " ${YELLOW}[3]${NC} Restart all Proxmox services" echo -e " ${CYAN}[4]${NC} View System Status" echo -e " ${CYAN}[5]${NC} View Storage Status" echo -e " ${CYAN}[6]${NC} View Network Info" echo -e " ${PURPLE}[7]${NC} Update Proxmox VE" echo -e " ${PURPLE}[8]${NC} Clear Cache" echo -e " ${PURPLE}[9]${NC} Open Firewall Ports (UFW)" echo -e " ${RED}[0]${NC} Back to main menu" echo "" echo -n "Choice [0-9]: " } main() { while true; do show_menu read -r choice echo "" case $choice in 1) fix_login_issue ;; 2) restart_pve_cluster ;; 3) restart_all_services ;; 4) show_status ;; 5) show_storage_status ;; 6) show_network_info ;; 7) update_proxmox ;; 8) clear_cache ;; 9) open_firewall_ports ;; 0) return 75 ;; *) echo -e "${RED}Invalid option${NC}" ;; esac echo "" read -p "Press Enter to continue..." done } main