mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-09-14 18:56:52 +00:00
ProxMenux modifies the host: it rewrites configuration files, installs packages, enables services. Until now nobody could say afterwards what had changed, and showing the script does not answer that question — a four-hundred-line function may alter two values, and the reader has no way to know which two. This adds the two halves of an answer. The change journal records what ProxMenux does as it does it. Eleven bash primitives capture the previous state, apply the change and record it in the same step, writing to a spool that the Monitor reads back. One hundred and thirteen functions across twenty-five scripts are instrumented, covering post-install, shared storage, security tooling, container conversions, disk operations and the PVE 8 to 9 upgrade path. The page shows the difference — rotate 7 becoming rotate 14 — and never the script. Restore and backup scripts are deliberately left out: a restore puts the host back to a state some other script already recorded. The Audit and reports page answers the other half: what state is this host in, regardless of who put it there. Forty-three checks across seven areas read the host and classify each result as critical, warning, observation, conformant, unverified or not applicable, with the evidence they read attached to each one. A declared policy lets the reader say what this particular host is expected to do — which guests must have a backup, which storages are essential — so the report judges the host against its own intent rather than a generic template. An inventory records the hardware, network and guest topology behind those readings, a comparison shows what moved between two runs, and six report profiles produce a printable document scoped to what the reader needs. Everything is available in the eight supported languages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
951 lines
38 KiB
Bash
951 lines
38 KiB
Bash
#!/bin/bash
|
||
# ==========================================================
|
||
# ProxMenux - Network Management and Repair Tool
|
||
# ==========================================================
|
||
# Author : MacRimi
|
||
# Copyright : (c) 2024 MacRimi
|
||
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
|
||
# Version : 2.0
|
||
# Last Updated: 07/01/2025
|
||
# ==========================================================
|
||
|
||
# Description:
|
||
# Advanced network management and troubleshooting tool for Proxmox VE.
|
||
# Features include interface detection, bridge management, connectivity testing,
|
||
# network diagnostics, configuration backup/restore, and automated repairs.
|
||
|
||
# Configuration ============================================
|
||
BASE_DIR="/usr/local/share/proxmenux"
|
||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||
BACKUP_DIR="/var/backups/proxmenux"
|
||
|
||
if [[ -f "$UTILS_FILE" ]]; then
|
||
source "$UTILS_FILE"
|
||
fi
|
||
if [[ -f "$BASE_DIR/scripts/global/pmx_journal.sh" ]]; then
|
||
source "$BASE_DIR/scripts/global/pmx_journal.sh"
|
||
fi
|
||
|
||
load_language
|
||
initialize_cache
|
||
|
||
# ==========================================================
|
||
# Utility Functions
|
||
create_backup_dir() {
|
||
[ ! -d "$BACKUP_DIR" ] && mkdir -p "$BACKUP_DIR"
|
||
}
|
||
|
||
backup_network_config() {
|
||
create_backup_dir
|
||
local timestamp=$(date +"%Y-%m-%d_%H-%M-%S")
|
||
local backup_file="$BACKUP_DIR/interfaces_backup_$timestamp"
|
||
cp /etc/network/interfaces "$backup_file"
|
||
echo "$backup_file"
|
||
}
|
||
|
||
# ==========================================================
|
||
# Network Detection Functions
|
||
|
||
detect_network_method() {
|
||
# Detect Netplan
|
||
if compgen -G "/etc/netplan/*.yaml" > /dev/null; then
|
||
echo "netplan"
|
||
return 0
|
||
fi
|
||
|
||
# Detect systemd-networkd
|
||
if systemctl is-active --quiet systemd-networkd 2>/dev/null; then
|
||
echo "systemd-networkd"
|
||
return 0
|
||
fi
|
||
|
||
# Detect NetworkManager
|
||
if systemctl is-active --quiet NetworkManager 2>/dev/null; then
|
||
echo "networkmanager"
|
||
return 0
|
||
fi
|
||
|
||
# Default: Debian/Proxmox classic
|
||
echo "classic"
|
||
}
|
||
|
||
|
||
detect_physical_interfaces() {
|
||
ip -o link show | awk -F': ' '$2 !~ /^(lo|veth|dummy|bond|tap|tun|docker|br-)/ && $2 !~ /vmbr/ {print $2}' | sort
|
||
}
|
||
|
||
detect_bridge_interfaces() {
|
||
ip -o link show | awk -F': ' '$2 ~ /^vmbr/ {print $2}' | sort
|
||
}
|
||
|
||
detect_all_interfaces() {
|
||
ip -o link show | awk -F': ' '$2 !~ /^(lo|veth|dummy|tap|tun)/ {print $2}' | sort
|
||
}
|
||
|
||
get_interface_info() {
|
||
local interface="$1"
|
||
local info=""
|
||
|
||
# Get IP address
|
||
local ip=$(ip -4 addr show "$interface" 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}/\d+' | head -1)
|
||
[ -z "$ip" ] && ip="$(translate "No IP")"
|
||
|
||
# Get status
|
||
local status=$(ip link show "$interface" 2>/dev/null | grep -o "state [A-Z]*" | cut -d' ' -f2)
|
||
[ -z "$status" ] && status="UNKNOWN"
|
||
|
||
# Get MAC address
|
||
local mac=$(ip link show "$interface" 2>/dev/null | grep -o "link/ether [a-f0-9:]*" | cut -d' ' -f2)
|
||
[ -z "$mac" ] && mac="$(translate "No MAC")"
|
||
|
||
echo "$interface|$ip|$status|$mac"
|
||
}
|
||
|
||
|
||
|
||
|
||
# ==========================================================
|
||
# Network Testing Functions
|
||
test_connectivity() {
|
||
local test_results=""
|
||
local tests=(
|
||
"8.8.8.8|Google DNS"
|
||
"1.1.1.1|Cloudflare DNS"
|
||
"$(ip route | grep default | awk '{print $3}' | head -1)|Gateway"
|
||
)
|
||
show_proxmenux_logo
|
||
msg_info "$(translate "Test Connectivity")"
|
||
test_results+="$(translate "Connectivity Test Results")\n"
|
||
test_results+="$(printf '=%.0s' {1..35})\n\n"
|
||
|
||
for test in "${tests[@]}"; do
|
||
IFS='|' read -r target name <<< "$test"
|
||
if [ -n "$target" ] && [ "$target" != "" ]; then
|
||
if ping -c 2 -W 3 "$target" >/dev/null 2>&1; then
|
||
test_results+="✓ $name ($target): $(translate "OK")\n"
|
||
else
|
||
test_results+="✗ $name ($target): $(translate "FAILED")\n"
|
||
fi
|
||
fi
|
||
done
|
||
|
||
# DNS Resolution test
|
||
if nslookup google.com >/dev/null 2>&1; then
|
||
test_results+="✓ $(translate "DNS Resolution"): $(translate "OK")\n"
|
||
else
|
||
test_results+="✗ $(translate "DNS Resolution"): $(translate "FAILED")\n"
|
||
fi
|
||
cleanup
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Connectivity Test")" \
|
||
--msgbox "$test_results" 15 60
|
||
}
|
||
|
||
advanced_network_diagnostics() {
|
||
|
||
NETWORK_METHOD=$(detect_network_method)
|
||
|
||
if [[ "$NETWORK_METHOD" != "classic" ]]; then
|
||
dialog --title "Unsupported Network Stack" \
|
||
--msgbox "WARNING: This script only supports the classic Debian/Proxmox network configuration (/etc/network/interfaces).\n\nDetected: $NETWORK_METHOD.\n\nAborting for safety.\n\nPlease configure your network using your distribution's supported tools." 14 70
|
||
exit 1
|
||
fi
|
||
|
||
|
||
show_proxmenux_logo
|
||
msg_info "$(translate "Advanced Diagnostics")"
|
||
sleep 1
|
||
|
||
local diag_info=""
|
||
|
||
diag_info+="$(translate "Advanced Network Diagnostics")\n"
|
||
diag_info+="$(printf '=%.0s' {1..40})\n\n"
|
||
|
||
# Network statistics
|
||
diag_info+="$(translate "Active Connections"): $(ss -tuln | wc -l)\n"
|
||
diag_info+="$(translate "Listening Ports"): $(ss -tln | grep LISTEN | wc -l)\n"
|
||
diag_info+="$(translate "Network Interfaces"): $(ip link show | grep -c "^[0-9]")\n\n"
|
||
|
||
# Check for common issues
|
||
diag_info+="$(translate "Common Issues Check"):\n"
|
||
|
||
# Check if NetworkManager is running (shouldn't be on Proxmox)
|
||
if systemctl is-active --quiet NetworkManager 2>/dev/null; then
|
||
diag_info+="⚠ $(translate "NetworkManager is running (may cause conflicts)")\n"
|
||
|
||
if dialog --title "$(translate "NetworkManager Detected")" \
|
||
--yesno "$(translate "NetworkManager is running, which may conflict with Proxmox.")\n\n$(translate "Do you want to disable and remove it now?")" 10 70; then
|
||
|
||
dialog --infobox "$(translate "Disabling and removing NetworkManager...")" 6 60
|
||
systemctl stop NetworkManager >/dev/null 2>&1
|
||
systemctl disable NetworkManager >/dev/null 2>&1
|
||
apt-get purge -y network-manager >/dev/null 2>&1
|
||
|
||
diag_info+="✓ $(translate "NetworkManager has been removed successfully")\n"
|
||
else
|
||
diag_info+="ℹ️ $(translate "User chose not to remove NetworkManager")\n"
|
||
fi
|
||
else
|
||
diag_info+="✓ $(translate "NetworkManager not running")\n"
|
||
fi
|
||
|
||
# Check for duplicate IPs
|
||
local ips=($(ip -4 addr show | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | sort | uniq -d))
|
||
if [ ${#ips[@]} -gt 0 ]; then
|
||
diag_info+="⚠ $(translate "Duplicate IP addresses found"): ${ips[*]}\n"
|
||
else
|
||
diag_info+="✓ $(translate "No duplicate IP addresses")\n"
|
||
fi
|
||
|
||
cleanup
|
||
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Network Diagnostics")" \
|
||
--msgbox "$diag_info" 18 70
|
||
}
|
||
|
||
|
||
# ==========================================================
|
||
# SAFE Network Analysis Functions (NO AUTO-REPAIR)
|
||
# ==========================================================
|
||
|
||
analyze_bridge_configuration() {
|
||
|
||
NETWORK_METHOD=$(detect_network_method)
|
||
|
||
if [[ "$NETWORK_METHOD" != "classic" ]]; then
|
||
dialog --title "Unsupported Network Stack" \
|
||
--msgbox "WARNING: This script only supports the classic Debian/Proxmox network configuration (/etc/network/interfaces).\n\nDetected: $NETWORK_METHOD.\n\nAborting for safety.\n\nPlease configure your network using your distribution's supported tools." 14 70
|
||
exit 1
|
||
fi
|
||
|
||
show_proxmenux_logo
|
||
msg_info "$(translate "Analyzing Bridge Configuration - READ ONLY MODE")"
|
||
sleep 1
|
||
|
||
local physical_interfaces=($(detect_physical_interfaces))
|
||
local bridges=($(detect_bridge_interfaces))
|
||
local analysis_report=""
|
||
local issues_found=0
|
||
local suggestions=""
|
||
|
||
analysis_report+="🔍 $(translate "BRIDGE CONFIGURATION ANALYSIS")\n"
|
||
analysis_report+="$(printf '=%.0s' {1..50})\n\n"
|
||
cleanup
|
||
if [ ${#bridges[@]} -eq 0 ]; then
|
||
analysis_report+="ℹ️ $(translate "No bridges found in system")\n"
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Bridge Analysis")" --msgbox "$analysis_report" 10 60
|
||
return
|
||
fi
|
||
|
||
# Analyze each bridge
|
||
for bridge in "${bridges[@]}"; do
|
||
analysis_report+="🌉 $(translate "Bridge"): $bridge\n"
|
||
|
||
# Get current configuration
|
||
local current_ports=$(grep -A5 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep "bridge-ports" | cut -d' ' -f2-)
|
||
local bridge_ip=$(ip -4 addr show "$bridge" 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}/\d+' | head -1)
|
||
local bridge_status=$(ip link show "$bridge" 2>/dev/null | grep -o "state [A-Z]*" | cut -d' ' -f2)
|
||
|
||
analysis_report+=" 📍 $(translate "Status"): ${bridge_status:-UNKNOWN}\n"
|
||
analysis_report+=" 🌐 $(translate "IP"): ${bridge_ip:-$(translate "No IP assigned")}\n"
|
||
analysis_report+=" 🔌 $(translate "Configured Ports"): ${current_ports:-$(translate "None")}\n"
|
||
|
||
if [ -n "$current_ports" ]; then
|
||
local invalid_ports=""
|
||
local valid_ports=""
|
||
|
||
# Check each configured port
|
||
for port in $current_ports; do
|
||
if ip link show "$port" >/dev/null 2>&1; then
|
||
valid_ports+="$port "
|
||
analysis_report+=" ✅ $(translate "Port") $port: $(translate "EXISTS")\n"
|
||
else
|
||
invalid_ports+="$port "
|
||
analysis_report+=" ❌ $(translate "Port") $port: $(translate "NOT FOUND")\n"
|
||
((issues_found++))
|
||
fi
|
||
done
|
||
|
||
# Generate suggestions for invalid ports
|
||
if [ -n "$invalid_ports" ]; then
|
||
suggestions+="🔧 $(translate "SUGGESTION FOR") $bridge:\n"
|
||
if [ ${#physical_interfaces[@]} -gt 0 ]; then
|
||
suggestions+=" $(translate "Replace invalid port(s)") '$invalid_ports' $(translate "with"): ${physical_interfaces[0]}\n"
|
||
suggestions+=" $(translate "Command"): sed -i 's/bridge-ports.*/bridge-ports ${physical_interfaces[0]}/' /etc/network/interfaces\n"
|
||
else
|
||
suggestions+=" $(translate "Remove invalid port(s)") '$invalid_ports'\n"
|
||
suggestions+=" $(translate "Command"): sed -i 's/bridge-ports.*/bridge-ports none/' /etc/network/interfaces\n"
|
||
fi
|
||
suggestions+="\n"
|
||
fi
|
||
else
|
||
analysis_report+=" ⚠️ $(translate "No ports configured")\n"
|
||
if [ ${#physical_interfaces[@]} -gt 0 ]; then
|
||
suggestions+="🔧 $(translate "SUGGESTION FOR") $bridge:\n"
|
||
suggestions+=" $(translate "Consider adding physical interface"): ${physical_interfaces[0]}\n"
|
||
suggestions+=" $(translate "Command"): sed -i '/iface $bridge/a\\ bridge-ports ${physical_interfaces[0]}' /etc/network/interfaces\n\n"
|
||
fi
|
||
fi
|
||
analysis_report+="\n"
|
||
done
|
||
|
||
# Summary
|
||
analysis_report+="📊 $(translate "ANALYSIS SUMMARY")\n"
|
||
analysis_report+="$(printf '=%.0s' {1..25})\n"
|
||
analysis_report+="$(translate "Bridges analyzed"): ${#bridges[@]}\n"
|
||
analysis_report+="$(translate "Issues found"): $issues_found\n"
|
||
|
||
|
||
local auto_only=$(grep "^auto" /etc/network/interfaces | awk '{print $2}' | while read i; do
|
||
grep -q "^iface $i" /etc/network/interfaces || echo "$i"
|
||
done)
|
||
|
||
if [ -n "$auto_only" ]; then
|
||
analysis_report+="⚠️ $(translate "Interfaces defined with 'auto' but no 'iface' block"): $auto_only\n"
|
||
((issues_found++))
|
||
fi
|
||
|
||
analysis_report+="$(translate "Physical interfaces available"): ${#physical_interfaces[@]}\n\n"
|
||
|
||
if [ $issues_found -gt 0 ]; then
|
||
analysis_report+="$suggestions"
|
||
analysis_report+="⚠️ $(translate "IMPORTANT"): $(translate "No changes have been made to your system")\n"
|
||
analysis_report+="$(translate "Use the Guided Repair option to fix issues safely")\n"
|
||
else
|
||
analysis_report+="✅ $(translate "No bridge configuration issues found")\n"
|
||
fi
|
||
|
||
# Show analysis in scrollable dialog
|
||
local temp_file=$(mktemp)
|
||
echo -e "$analysis_report" > "$temp_file"
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Bridge Configuration Analysis")" \
|
||
--textbox "$temp_file" 25 80
|
||
rm -f "$temp_file"
|
||
|
||
# Offer guided repair if issues found
|
||
if [ $issues_found -gt 0 ]; then
|
||
if dialog --backtitle "ProxMenux" --title "$(translate "Guided Repair Available")" \
|
||
--yesno "$(translate "Issues were found. Would you like to use the Guided Repair Assistant?")" 8 60; then
|
||
guided_bridge_repair
|
||
fi
|
||
fi
|
||
}
|
||
|
||
guided_bridge_repair() {
|
||
local FUNC_VERSION="1.0"
|
||
pmx_journal_context "guided_bridge_repair" "$FUNC_VERSION"
|
||
local step=1
|
||
local total_steps=5
|
||
|
||
|
||
local timestamp=$(date +"%Y%m%d_%H%M%S")
|
||
local preview_backup_file="$BACKUP_DIR/interfaces_backup_$timestamp"
|
||
|
||
|
||
if ! dialog --backtitle "ProxMenux" --title "$(translate "Step") $step/$total_steps: $(translate "Safety Backup")" \
|
||
--yesno "$(translate "Before making any changes, we'll create a safety backup.")\n\n$(translate "Backup location"): $preview_backup_file\n\n$(translate "Continue?")" 12 70; then
|
||
return
|
||
fi
|
||
((step++))
|
||
|
||
|
||
show_proxmenux_logo
|
||
local backup_file=$(backup_network_config)
|
||
sleep 1
|
||
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Backup Created")" \
|
||
--msgbox "$(translate "Safety backup created"): $backup_file\n\n$(translate "You can restore it anytime with"):\ncp $backup_file /etc/network/interfaces" 10 70
|
||
|
||
# Step 2: Show current configuration
|
||
if ! dialog --backtitle "ProxMenux" --title "$(translate "Step") $step/$total_steps: $(translate "Current Configuration")" \
|
||
--yesno "$(translate "Let's review your current network configuration.")\n\n$(translate "Would you like to see the current") /etc/network/interfaces $(translate "file?")" 10 70; then
|
||
return
|
||
fi
|
||
((step++))
|
||
|
||
# Show current config
|
||
local temp_config=$(mktemp)
|
||
cat /etc/network/interfaces > "$temp_config"
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Current Network Configuration")" \
|
||
--textbox "$temp_config" 20 80
|
||
rm -f "$temp_config"
|
||
|
||
# Step 3: Identify specific changes needed
|
||
local physical_interfaces=($(detect_physical_interfaces))
|
||
local bridges=($(detect_bridge_interfaces))
|
||
local changes_needed=""
|
||
|
||
for bridge in "${bridges[@]}"; do
|
||
local current_ports=$(grep -A5 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep "bridge-ports" | cut -d' ' -f2-)
|
||
|
||
if [ -n "$current_ports" ]; then
|
||
for port in $current_ports; do
|
||
if ! ip link show "$port" >/dev/null 2>&1; then
|
||
if [ ${#physical_interfaces[@]} -gt 0 ]; then
|
||
changes_needed+="$(translate "Bridge") $bridge: $(translate "Replace") '$port' $(translate "with") '${physical_interfaces[0]}'\n"
|
||
else
|
||
changes_needed+="$(translate "Bridge") $bridge: $(translate "Remove invalid port") '$port'\n"
|
||
fi
|
||
fi
|
||
done
|
||
fi
|
||
done
|
||
|
||
if [ -z "$changes_needed" ]; then
|
||
dialog --backtitle "ProxMenux" --title "$(translate "No Changes Needed")" \
|
||
--msgbox "$(translate "After detailed analysis, no changes are needed.")" 8 50
|
||
return
|
||
fi
|
||
|
||
if ! dialog --backtitle "ProxMenux" --title "$(translate "Step") $step/$total_steps: $(translate "Proposed Changes")" \
|
||
--yesno "$(translate "These are the changes that will be made"):\n\n$changes_needed\n$(translate "Do you want to proceed?")" 15 70; then
|
||
return
|
||
fi
|
||
((step++))
|
||
|
||
# Step 4: Apply changes with verification
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Step") $step/$total_steps: $(translate "Applying Changes")" \
|
||
--infobox "$(translate "Applying changes safely...")\n\n$(translate "This may take a few seconds...")" 8 50
|
||
|
||
# Apply the changes
|
||
for bridge in "${bridges[@]}"; do
|
||
local current_ports=$(grep -A5 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep "bridge-ports" | cut -d' ' -f2-)
|
||
|
||
if [ -n "$current_ports" ]; then
|
||
local new_ports=""
|
||
for port in $current_ports; do
|
||
if ip link show "$port" >/dev/null 2>&1; then
|
||
new_ports+="$port "
|
||
fi
|
||
done
|
||
|
||
# If no valid ports and we have physical interfaces, use the first one
|
||
if [ -z "$new_ports" ] && [ ${#physical_interfaces[@]} -gt 0 ]; then
|
||
new_ports="${physical_interfaces[0]}"
|
||
fi
|
||
|
||
# Apply the change
|
||
if [ "$new_ports" != "$current_ports" ]; then
|
||
pmx_edit_file /etc/network/interfaces "/iface $bridge/,/bridge-ports/ s/bridge-ports.*/bridge-ports $new_ports/"
|
||
fi
|
||
fi
|
||
done
|
||
((step++))
|
||
|
||
# Step 5: Verification
|
||
local verification_report=""
|
||
verification_report+="✅ $(translate "CHANGES APPLIED SUCCESSFULLY")\n\n"
|
||
verification_report+="$(translate "Verification"):\n"
|
||
|
||
for bridge in "${bridges[@]}"; do
|
||
local new_ports=$(grep -A5 "iface $bridge" /etc/network/interfaces 2>/dev/null | grep "bridge-ports" | cut -d' ' -f2-)
|
||
verification_report+="$(translate "Bridge") $bridge: $new_ports\n"
|
||
|
||
# Verify each port exists
|
||
for port in $new_ports; do
|
||
if ip link show "$port" >/dev/null 2>&1; then
|
||
verification_report+=" ✅ $port: $(translate "EXISTS")\n"
|
||
else
|
||
verification_report+=" ❌ $port: $(translate "NOT FOUND")\n"
|
||
fi
|
||
done
|
||
done
|
||
|
||
verification_report+="\n$(translate "Backup available at"): $backup_file\n"
|
||
verification_report+="$(translate "To restore"): cp $backup_file /etc/network/interfaces"
|
||
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Step") $step/$total_steps: $(translate "Repair Complete")" \
|
||
--msgbox "$verification_report" 18 70
|
||
|
||
# Ask about network restart
|
||
if dialog --backtitle "ProxMenux" --title "$(translate "Network Restart")" \
|
||
--yesno "$(translate "Changes have been applied to the configuration file.")\n\n$(translate "Do you want to restart the network service to apply changes?")\n\n$(translate "WARNING: This may cause a brief disconnection.")" 12 70; then
|
||
|
||
clear
|
||
msg_info "$(translate "Restarting network service...")"
|
||
|
||
if systemctl restart networking; then
|
||
msg_ok "$(translate "Network service restarted successfully")"
|
||
else
|
||
msg_error "$(translate "Failed to restart network service")"
|
||
msg_warn "$(translate "You can restore the backup with"): cp $backup_file /etc/network/interfaces"
|
||
fi
|
||
|
||
msg_success "$(translate "Press ENTER to continue...")"
|
||
read -r
|
||
fi
|
||
}
|
||
|
||
analyze_network_configuration() {
|
||
|
||
NETWORK_METHOD=$(detect_network_method)
|
||
|
||
if [[ "$NETWORK_METHOD" != "classic" ]]; then
|
||
dialog --title "Unsupported Network Stack" \
|
||
--msgbox "WARNING: This script only supports the classic Debian/Proxmox network configuration (/etc/network/interfaces).\n\nDetected: $NETWORK_METHOD.\n\nAborting for safety.\n\nPlease configure your network using your distribution's supported tools." 14 70
|
||
exit 1
|
||
fi
|
||
|
||
show_proxmenux_logo
|
||
msg_info "$(translate "Analyzing Network Configuration - READ ONLY MODE")"
|
||
sleep 1
|
||
|
||
local configured_interfaces=($(grep "^iface" /etc/network/interfaces | awk '{print $2}' | grep -v "lo"))
|
||
local analysis_report=""
|
||
local issues_found=0
|
||
local suggestions=""
|
||
|
||
analysis_report+="🔍 $(translate "NETWORK CONFIGURATION ANALYSIS")\n"
|
||
analysis_report+="$(printf '=%.0s' {1..50})\n\n"
|
||
|
||
cleanup
|
||
if [ ${#configured_interfaces[@]} -eq 0 ]; then
|
||
analysis_report+="ℹ️ $(translate "No network interfaces configured (besides loopback)")\n"
|
||
dialog --title "$(translate "Configuration Analysis")" --msgbox "$analysis_report" 10 60
|
||
return
|
||
fi
|
||
|
||
analysis_report+="📋 $(translate "CONFIGURED INTERFACES")\n"
|
||
analysis_report+="$(printf '=%.0s' {1..30})\n"
|
||
|
||
# Analyze each configured interface
|
||
for iface in "${configured_interfaces[@]}"; do
|
||
analysis_report+="🔌 $(translate "Interface"): $iface\n"
|
||
|
||
# Check if interface exists physically
|
||
if ip link show "$iface" >/dev/null 2>&1; then
|
||
local status=$(ip link show "$iface" 2>/dev/null | grep -o "state [A-Z]*" | cut -d' ' -f2)
|
||
local ip=$(ip -4 addr show "$iface" 2>/dev/null | grep -oP '(?<=inet\s)\d+(\.\d+){3}/\d+' | head -1)
|
||
|
||
analysis_report+=" ✅ $(translate "Status"): $(translate "EXISTS") ($status)\n"
|
||
analysis_report+=" 🌐 $(translate "IP"): ${ip:-$(translate "No IP assigned")}\n"
|
||
|
||
# Check if it's a bridge or bond (these are virtual, so it's normal they exist)
|
||
if [[ $iface =~ ^(vmbr|bond) ]]; then
|
||
analysis_report+=" ℹ️ $(translate "Type"): $(translate "Virtual interface (normal)")\n"
|
||
else
|
||
analysis_report+=" ℹ️ $(translate "Type"): $(translate "Physical interface")\n"
|
||
fi
|
||
else
|
||
analysis_report+=" ❌ $(translate "Status"): $(translate "NOT FOUND")\n"
|
||
analysis_report+=" ⚠️ $(translate "Issue"): $(translate "Configured but doesn't exist")\n"
|
||
((issues_found++))
|
||
|
||
# Only suggest removal for non-virtual interfaces
|
||
if [[ ! $iface =~ ^(vmbr|bond) ]]; then
|
||
suggestions+="🔧 $(translate "SUGGESTION FOR") $iface:\n"
|
||
suggestions+=" $(translate "This interface is configured but doesn't exist physically")\n"
|
||
suggestions+=" $(translate "Consider removing its configuration")\n"
|
||
suggestions+=" $(translate "Command"): sed -i '/iface $iface/,/^$/d' /etc/network/interfaces\n\n"
|
||
fi
|
||
fi
|
||
analysis_report+="\n"
|
||
done
|
||
|
||
# Summary
|
||
analysis_report+="📊 $(translate "ANALYSIS SUMMARY")\n"
|
||
analysis_report+="$(printf '=%.0s' {1..25})\n"
|
||
analysis_report+="$(translate "Interfaces configured"): ${#configured_interfaces[@]}\n"
|
||
analysis_report+="$(translate "Issues found"): $issues_found\n\n"
|
||
|
||
if [ $issues_found -gt 0 ]; then
|
||
analysis_report+="$suggestions"
|
||
analysis_report+="⚠️ $(translate "IMPORTANT"): $(translate "No changes have been made to your system")\n"
|
||
analysis_report+="$(translate "Use the Guided Cleanup option to fix issues safely")\n"
|
||
else
|
||
analysis_report+="✅ $(translate "No configuration issues found")\n"
|
||
fi
|
||
|
||
# Show analysis in scrollable dialog
|
||
local temp_file=$(mktemp)
|
||
echo -e "$analysis_report" > "$temp_file"
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Network Configuration Analysis")" \
|
||
--textbox "$temp_file" 25 80
|
||
rm -f "$temp_file"
|
||
|
||
# Offer guided cleanup if issues found
|
||
if [ $issues_found -gt 0 ]; then
|
||
if dialog --backtitle "ProxMenux" --title "$(translate "Guided Cleanup Available")" \
|
||
--yesno "$(translate "Issues were found. Would you like to use the Guided Cleanup Assistant?")" 8 60; then
|
||
guided_configuration_cleanup
|
||
fi
|
||
fi
|
||
}
|
||
|
||
guided_configuration_cleanup() {
|
||
local FUNC_VERSION="1.0"
|
||
pmx_journal_context "guided_configuration_cleanup" "$FUNC_VERSION"
|
||
local step=1
|
||
local total_steps=5
|
||
|
||
local timestamp=$(date +"%Y%m%d_%H%M%S")
|
||
local preview_backup_file="$BACKUP_DIR/interfaces_backup_$timestamp"
|
||
|
||
|
||
if ! dialog --backtitle "ProxMenux" --title "$(translate "Step") $step/$total_steps: $(translate "Safety Backup")" \
|
||
--yesno "$(translate "Before making any changes, we'll create a safety backup.")\n\n$(translate "Backup location"): $preview_backup_file\n\n$(translate "Continue?")" 12 70; then
|
||
return
|
||
fi
|
||
((step++))
|
||
|
||
|
||
show_proxmenux_logo
|
||
local backup_file=$(backup_network_config)
|
||
sleep 1
|
||
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Backup Created")" \
|
||
--msgbox "$(translate "Safety backup created"): $backup_file\n\n$(translate "You can restore it anytime with"):\ncp $backup_file /etc/network/interfaces" 10 70
|
||
|
||
# Step 2: Identify interfaces to remove
|
||
local configured_interfaces=($(grep "^iface" /etc/network/interfaces | awk '{print $2}' | grep -v "lo"))
|
||
local interfaces_to_remove=""
|
||
local removal_list=""
|
||
|
||
for iface in "${configured_interfaces[@]}"; do
|
||
if [[ ! $iface =~ ^(vmbr|bond) ]] && ! ip link show "$iface" >/dev/null 2>&1; then
|
||
interfaces_to_remove+="$iface "
|
||
removal_list+="❌ $iface: $(translate "Configured but doesn't exist")\n"
|
||
fi
|
||
done
|
||
|
||
if [ -z "$interfaces_to_remove" ]; then
|
||
dialog --backtitle "ProxMenux" --title "$(translate "No Cleanup Needed")" \
|
||
--msgbox "$(translate "After detailed analysis, no cleanup is needed.")" 8 50
|
||
return
|
||
fi
|
||
|
||
if ! dialog --backtitle "ProxMenux" --title "$(translate "Step") $step/$total_steps: $(translate "Interfaces to Remove")" \
|
||
--yesno "$(translate "These interface configurations will be removed"):\n\n$removal_list\n$(translate "Do you want to proceed?")" 15 70; then
|
||
return
|
||
fi
|
||
((step++))
|
||
|
||
# Step 3: Show what will be removed
|
||
local temp_preview=$(mktemp)
|
||
echo "$(translate "Configuration sections that will be REMOVED"):" > "$temp_preview"
|
||
echo "=================================================" >> "$temp_preview"
|
||
echo "" >> "$temp_preview"
|
||
|
||
for iface in $interfaces_to_remove; do
|
||
echo "# Interface: $iface" >> "$temp_preview"
|
||
sed -n "/^iface $iface/,/^$/p" /etc/network/interfaces >> "$temp_preview"
|
||
echo "" >> "$temp_preview"
|
||
done
|
||
|
||
if ! dialog --backtitle "ProxMenux" --title "$(translate "Step") $step/$total_steps: $(translate "Preview Changes")" \
|
||
--yesno "$(translate "Review what will be removed"):\n\n$(translate "Press OK to see the preview, then confirm")" 10 60; then
|
||
rm -f "$temp_preview"
|
||
return
|
||
fi
|
||
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Configuration to be Removed")" \
|
||
--textbox "$temp_preview" 20 80
|
||
rm -f "$temp_preview"
|
||
|
||
if ! dialog --backtitle "ProxMenux" --title "$(translate "Final Confirmation")" \
|
||
--yesno "$(translate "Are you sure you want to remove these configurations?")" 8 60; then
|
||
return
|
||
fi
|
||
((step++))
|
||
|
||
# Step 4: Apply changes
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Step") $step/$total_steps: $(translate "Applying Changes")" \
|
||
--infobox "$(translate "Removing invalid configurations...")\n\n$(translate "This may take a few seconds...")" 8 50
|
||
|
||
for iface in $interfaces_to_remove; do
|
||
pmx_edit_file /etc/network/interfaces "/^iface $iface/,/^$/d"
|
||
done
|
||
((step++))
|
||
|
||
# Step 5: Verification
|
||
local verification_report=""
|
||
verification_report+="✅ $(translate "CLEANUP COMPLETED SUCCESSFULLY")\n\n"
|
||
verification_report+="$(translate "Removed configurations for"):\n"
|
||
|
||
for iface in $interfaces_to_remove; do
|
||
verification_report+="❌ $iface\n"
|
||
done
|
||
|
||
verification_report+="\n$(translate "Verification"): $(translate "Checking remaining interfaces")\n"
|
||
local remaining_interfaces=($(grep "^iface" /etc/network/interfaces | awk '{print $2}' | grep -v "lo"))
|
||
|
||
for iface in "${remaining_interfaces[@]}"; do
|
||
if ip link show "$iface" >/dev/null 2>&1; then
|
||
verification_report+="✅ $iface: $(translate "OK")\n"
|
||
else
|
||
verification_report+="⚠️ $iface: $(translate "Still has issues")\n"
|
||
fi
|
||
done
|
||
|
||
verification_report+="\n$(translate "Backup available at"): $backup_file\n"
|
||
verification_report+="$(translate "To restore"): cp $backup_file /etc/network/interfaces"
|
||
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Step") $step/$total_steps: $(translate "Cleanup Complete")" \
|
||
--msgbox "$verification_report" 18 70
|
||
}
|
||
|
||
# ==========================================================
|
||
# Configuration Management
|
||
show_network_config() {
|
||
|
||
NETWORK_METHOD=$(detect_network_method)
|
||
|
||
if [[ "$NETWORK_METHOD" != "classic" ]]; then
|
||
dialog --title "Unsupported Network Stack" \
|
||
--msgbox "WARNING: This script only supports the classic Debian/Proxmox network configuration (/etc/network/interfaces).\n\nDetected: $NETWORK_METHOD.\n\nAborting for safety.\n\nPlease configure your network using your distribution's supported tools." 14 70
|
||
exit 1
|
||
fi
|
||
|
||
local config_content
|
||
config_content=$(cat /etc/network/interfaces)
|
||
show_proxmenux_logo
|
||
echo -e
|
||
echo -e
|
||
echo "========== $(translate "Network Configuration File") =========="
|
||
echo
|
||
cat /etc/network/interfaces
|
||
echo
|
||
msg_success "$(translate "Press Enter to continue...")"
|
||
read -r
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
restore_network_backup() {
|
||
|
||
NETWORK_METHOD=$(detect_network_method)
|
||
|
||
if [[ "$NETWORK_METHOD" != "classic" ]]; then
|
||
dialog --title "Unsupported Network Stack" \
|
||
--msgbox "WARNING: This script only supports the classic Debian/Proxmox network configuration (/etc/network/interfaces).\n\nDetected: $NETWORK_METHOD.\n\nAborting for safety.\n\nPlease configure your network using your distribution's supported tools." 14 70
|
||
exit 1
|
||
fi
|
||
|
||
local backups=($(ls -1 "$BACKUP_DIR"/interfaces_backup_* 2>/dev/null | sort -r))
|
||
|
||
if [ ${#backups[@]} -eq 0 ]; then
|
||
dialog --backtitle "ProxMenux" --title "$(translate "No Backups")" \
|
||
--msgbox "\n$(translate "No network configuration backups found.")" 14 60
|
||
return
|
||
fi
|
||
|
||
local menu_items=()
|
||
local counter=1
|
||
|
||
for backup in "${backups[@]}"; do
|
||
local filename=$(basename "$backup")
|
||
local timestamp=$(basename "$backup" | sed 's/interfaces_backup_//')
|
||
menu_items+=("$counter" "$timestamp")
|
||
((counter++))
|
||
done
|
||
|
||
local selection=$(dialog --backtitle "ProxMenux" --title "$(translate "Restore Backup")" \
|
||
--menu "$(translate "Select backup to restore:"):" 15 60 8 \
|
||
"${menu_items[@]}" 3>&1 1>&2 2>&3)
|
||
|
||
if [ -n "$selection" ] && [ "$selection" -ge 1 ] && [ "$selection" -le ${#backups[@]} ]; then
|
||
local selected_backup="${backups[$((selection-1))]}"
|
||
|
||
|
||
if dialog --backtitle "ProxMenux" --title "$(translate "Preview Backup")" \
|
||
--yesno "\n$(translate "Do you want to view the selected backup before restoring?")" 8 60; then
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Backup Preview")" \
|
||
--textbox "$selected_backup" 22 80
|
||
fi
|
||
|
||
if dialog --backtitle "ProxMenux" --title "$(translate "Confirm Restore")" \
|
||
--yesno "\n$(translate "Are you sure you want to restore this backup?\nCurrent configuration will be overwritten.")\n\n$(translate "For your safety, a backup of the current configuration will be created automatically before restoring.")" 14 70; then
|
||
|
||
local pre_restore_backup=$(backup_network_config)
|
||
cp "$selected_backup" /etc/network/interfaces
|
||
|
||
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Backup Restored")" \
|
||
--msgbox "\n$(translate "Network configuration has been restored from backup.")" 8 60
|
||
|
||
|
||
if dialog --backtitle "ProxMenux" --title "$(translate "Restart Network")" \
|
||
--yesno "\n$(translate "Do you want to restart the network service now to apply changes?")" 8 60; then
|
||
if systemctl restart networking; then
|
||
dialog --backtitle "ProxMenux" --title "$(translate "Network Restarted")" \
|
||
--msgbox "\n$(translate "Network service restarted successfully.")" 8 50
|
||
fi
|
||
fi
|
||
fi
|
||
fi
|
||
}
|
||
|
||
|
||
|
||
# ==========================================================
|
||
# Emergency System Repair Functions
|
||
# ==========================================================
|
||
|
||
|
||
emergency_proxmox_repair() {
|
||
clear
|
||
show_proxmenux_logo
|
||
echo -e
|
||
echo "=========================================="
|
||
echo " $(translate "EMERGENCY PROXMOX SYSTEM REPAIR")"
|
||
echo "=========================================="
|
||
echo
|
||
|
||
msg_warn "$(translate "This will reinstall core Proxmox packages and regenerate certificates")"
|
||
echo "$(translate "This operation may take several minutes and requires internet connectivity.")"
|
||
echo
|
||
echo -n "$(translate "Do you want to continue?") (y/N): "
|
||
read -r confirm
|
||
|
||
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
||
msg_info2 "$(translate "Operation cancelled by user.")"
|
||
return
|
||
fi
|
||
|
||
msg_info2 "$(translate "Starting Proxmox system repair...")"
|
||
echo
|
||
|
||
# Step 1: Update package lists
|
||
msg_success "$(translate "Step") 1/3: $(translate "Updating package lists...")"
|
||
if apt-get update; then
|
||
msg_ok "$(translate "Package lists updated successfully")"
|
||
else
|
||
msg_error "$(translate "Failed to update package lists")"
|
||
echo "$(translate "This might indicate network connectivity issues.")"
|
||
echo
|
||
echo "$(translate "Press ENTER to continue...")"
|
||
read -r
|
||
return 1
|
||
fi
|
||
echo
|
||
|
||
# Step 2: Reinstall core Proxmox packages
|
||
msg_success "$(translate "Step") 2/3: $(translate "Reinstalling core Proxmox packages...")"
|
||
echo "$(translate "This may take several minutes...")"
|
||
|
||
if apt-get install --reinstall proxmox-widget-toolkit pve-manager -y; then
|
||
msg_ok "$(translate "Core Proxmox packages reinstalled successfully")"
|
||
else
|
||
msg_error "$(translate "Failed to reinstall Proxmox packages")"
|
||
echo "$(translate "Check the error messages above for details.")"
|
||
echo
|
||
echo "$(translate "Press ENTER to continue...")"
|
||
read -r
|
||
return 1
|
||
fi
|
||
echo
|
||
|
||
# Step 3: Regenerate certificates and restart services
|
||
msg_success "$(translate "Step") 3/3: $(translate "Regenerating certificates and restarting services...")"
|
||
|
||
# Update certificates
|
||
if command -v pvecm >/dev/null 2>&1; then
|
||
msg_info "$(translate "Updating cluster certificates...")"
|
||
if pvecm updatecerts -f; then
|
||
msg_ok "$(translate "Cluster certificates updated")"
|
||
else
|
||
msg_warn "$(translate "Failed to update cluster certificates (might not be in a cluster)")"
|
||
fi
|
||
else
|
||
msg_warn "$(translate "pvecm command not found (might not be in a cluster)")"
|
||
fi
|
||
|
||
# Restart Proxmox services
|
||
msg_success "$(translate "Restarting Proxmox services...")"
|
||
local services_restarted=0
|
||
local services_failed=0
|
||
|
||
for service in pveproxy pvedaemon; do
|
||
if systemctl restart "$service"; then
|
||
msg_ok " $service $(translate "restarted successfully")"
|
||
((services_restarted++))
|
||
else
|
||
msg_error " $(translate "Failed to restart") $service"
|
||
((services_failed++))
|
||
fi
|
||
done
|
||
|
||
echo
|
||
echo "$(translate "REPAIR SUMMARY"):"
|
||
echo "==============="
|
||
echo " $(translate "Package lists"): $(translate "Updated")"
|
||
echo " $(translate "Core packages"): $(translate "Reinstalled")"
|
||
echo " $(translate "Services restarted"): $services_restarted"
|
||
echo " $(translate "Services failed"): $services_failed"
|
||
|
||
if [ $services_failed -eq 0 ]; then
|
||
msg_ok "$(translate "Proxmox system repair completed successfully!")"
|
||
echo
|
||
echo "$(translate "You should now be able to access the Proxmox web interface.")"
|
||
echo "$(translate "Try accessing"): https://$(hostname -I | awk '{print $1}'):8006"
|
||
else
|
||
msg_warn "$(translate "Proxmox system repair completed with some issues.")"
|
||
echo "$(translate "Check the service status manually if needed.")"
|
||
fi
|
||
|
||
echo
|
||
echo "$(translate "Press ENTER to continue...")"
|
||
read -r
|
||
}
|
||
|
||
|
||
|
||
restart_network_service() {
|
||
if dialog --title "$(translate "Restart Network")" \
|
||
--yesno "$(translate "This will restart the network service and may cause a brief disconnection. Continue?")" 10 60; then
|
||
|
||
show_proxmenux_logo
|
||
msg_info "$(translate "Restarting network service...")"
|
||
|
||
if systemctl restart networking; then
|
||
msg_ok "$(translate "Network service restarted successfully")"
|
||
else
|
||
msg_error "$(translate "Failed to restart network service")"
|
||
msg_warn "$(translate "If you lose connectivity, you can restore from backup using the console.")"
|
||
fi
|
||
|
||
msg_success "$(translate "Press ENTER to continue...")"
|
||
read -r
|
||
fi
|
||
}
|
||
|
||
|
||
|
||
# ==========================================================
|
||
# Main Menu
|
||
show_main_menu() {
|
||
while true; do
|
||
local selection=$(dialog --clear \
|
||
--backtitle "ProxMenux" \
|
||
--title "$(translate "Network Management - SAFE MODE")" \
|
||
--menu "$(translate "Select an option:"):" 20 70 12 \
|
||
"1" "$(translate "Test Connectivity")" \
|
||
"2" "$(translate "Advanced Diagnostics")" \
|
||
"3" "$(translate "Analyze Bridge Configuration")" \
|
||
"4" "$(translate "Analyze Network Configuration")" \
|
||
"5" "$(translate "Restart Network Service")" \
|
||
"6" "$(translate "Show Network Config File")" \
|
||
"7" "$(translate "Emergency Proxmox System Repair")" \
|
||
"8" "$(translate "Restore Network Backup")" \
|
||
"0" "$(translate "Exit")" \
|
||
3>&1 1>&2 2>&3)
|
||
|
||
case $selection in
|
||
|
||
1) test_connectivity ;;
|
||
2) advanced_network_diagnostics ;;
|
||
3) analyze_bridge_configuration ;;
|
||
4) analyze_network_configuration ;;
|
||
5) restart_network_service ;;
|
||
6) show_network_config ;;
|
||
7) emergency_proxmox_repair ;;
|
||
8) restore_network_backup ;;
|
||
0|"") exit ;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
# ==========================================================
|
||
show_main_menu
|