Files
loc_az_hci/infrastructure/proxmox/network-config.sh
defiQUG c39465c2bd
Some checks failed
Test / test (push) Has been cancelled
Initial commit: loc_az_hci (smom-dbis-138 excluded via .gitignore)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 09:04:46 -08:00

365 lines
10 KiB
Bash
Executable File

#!/bin/bash
# Proxmox VE Network Configuration Script
# Configures two-bridge setup: vmbr0 (LAN) and vmbr1 (WAN) with DHCP
# Designed for ML110 and R630 servers with two NICs each
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Configuration
NODE_HOSTNAME="${NODE_HOSTNAME:-$(hostname)}"
DRY_RUN="${DRY_RUN:-false}"
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
check_root() {
if [ "$EUID" -ne 0 ]; then
log_error "Please run as root"
exit 1
fi
}
get_interface_speed() {
local iface=$1
# Try to get speed from ethtool (most reliable)
if command -v ethtool &>/dev/null; then
local speed=$(ethtool "$iface" 2>/dev/null | grep -i "Speed:" | awk '{print $2}' | sed 's/[^0-9]//g')
if [ -n "$speed" ]; then
echo "$speed"
return
fi
fi
# Fallback: check from /sys/class/net (if link is up)
local speed=$(cat "/sys/class/net/$iface/speed" 2>/dev/null)
if [ -n "$speed" ] && [ "$speed" != "-1" ]; then
echo "$speed"
return
fi
# Fallback: check advertised speeds
if command -v ethtool &>/dev/null; then
local adv_speeds=$(ethtool "$iface" 2>/dev/null | grep -i "Advertised link modes:" | grep -oE "[0-9]+base" | head -1 | sed 's/base//')
if [ -n "$adv_speeds" ]; then
echo "$adv_speeds"
return
fi
fi
echo "unknown"
}
detect_physical_interfaces() {
log_info "Detecting physical network interfaces..."
# Get all physical interfaces
PHYSICAL_IFACES=()
for iface in /sys/class/net/*; do
iface_name=$(basename "$iface")
# Skip loopback, virtual interfaces, bridges, bonds, and VLANs
if [[ "$iface_name" == "lo" ]] || \
[[ -L "$iface/device" ]] && [[ ! -d "$iface/device" ]] || \
[[ -d "$iface/bridge" ]] || \
[[ -d "$iface/bonding" ]] || \
[[ "$iface_name" =~ \. ]]; then
continue
fi
# Check if it's a physical interface by looking for device directory
if [ -d "$iface/device" ] || [ -L "$iface/device" ]; then
PHYSICAL_IFACES+=("$iface_name")
fi
done
# Sort interfaces for consistent selection
IFS=$'\n' PHYSICAL_IFACES=($(sort <<<"${PHYSICAL_IFACES[*]}"))
unset IFS
if [ ${#PHYSICAL_IFACES[@]} -lt 2 ]; then
log_error "Expected at least 2 physical interfaces, found: ${#PHYSICAL_IFACES[@]}"
log_error "Available interfaces: ${PHYSICAL_IFACES[*]}"
exit 1
fi
log_info "Found ${#PHYSICAL_IFACES[@]} physical interface(s): ${PHYSICAL_IFACES[*]}"
# Check which interfaces currently have IP addresses (if any)
log_info "Checking current IP assignments..."
INTERFACES_WITH_IPS=()
for iface in "${PHYSICAL_IFACES[@]}"; do
if ip addr show "$iface" 2>/dev/null | grep -q "inet "; then
IP=$(ip addr show "$iface" | grep "inet " | awk '{print $2}' | head -1)
INTERFACES_WITH_IPS+=("$iface")
log_info " $iface: Currently has IP $IP"
fi
done
# Use first two interfaces - DHCP will assign IPs to connected ones
NIC1="${PHYSICAL_IFACES[0]}"
NIC2="${PHYSICAL_IFACES[1]}"
log_info "Selected NIC 1 (LAN): $NIC1"
log_info "Selected NIC 2 (WAN): $NIC2"
log_info "Note: DHCP will assign IPs to connected interfaces"
# Allow manual override via environment variables
if [ -n "$NIC1_OVERRIDE" ]; then
NIC1="$NIC1_OVERRIDE"
log_info "NIC1 overridden to: $NIC1"
fi
if [ -n "$NIC2_OVERRIDE" ]; then
NIC2="$NIC2_OVERRIDE"
log_info "NIC2 overridden to: $NIC2"
fi
}
validate_interfaces() {
log_info "Validating interface configuration..."
# Check if interfaces exist
if ! ip link show "$NIC1" &>/dev/null; then
log_error "Interface $NIC1 not found"
exit 1
fi
if ! ip link show "$NIC2" &>/dev/null; then
log_error "Interface $NIC2 not found"
exit 1
fi
log_info "Interface validation passed"
}
backup_config() {
log_info "Backing up existing network configuration..."
BACKUP_FILE="/etc/network/interfaces.backup.$(date +%Y%m%d_%H%M%S)"
if [ "$DRY_RUN" = "true" ]; then
log_info "[DRY RUN] Would backup to: $BACKUP_FILE"
return
fi
cp /etc/network/interfaces "$BACKUP_FILE"
log_info "Backup created: $BACKUP_FILE"
}
configure_hostname() {
log_info "Configuring hostname: $NODE_HOSTNAME"
if [ "$DRY_RUN" = "true" ]; then
log_info "[DRY RUN] Would set hostname to: $NODE_HOSTNAME"
return
fi
echo "$NODE_HOSTNAME" > /etc/hostname
hostname "$NODE_HOSTNAME"
}
generate_interfaces_config() {
log_info "Generating network configuration..."
cat <<EOF
# Proxmox VE Network Configuration
# Auto-generated by network-config.sh
# Configuration for: $NODE_HOSTNAME
# NIC 1 (LAN): $NIC1 -> vmbr0 (192.168.1.0/24 via DHCP)
# NIC 2 (WAN): $NIC2 -> vmbr1 (Public IP via DHCP from Spectrum modem)
# Loopback interface
auto lo
iface lo inet loopback
# NIC 1 (LAN) - Physical interface
auto $NIC1
iface $NIC1 inet manual
# vmbr0 (LAN Bridge) - Connected to 192.168.1.0/24 network
auto vmbr0
iface vmbr0 inet dhcp
bridge-ports $NIC1
bridge-stp off
bridge-fd 0
bridge-vlan-aware no
# Higher metric - prefer WAN for default route
metric 200
# NIC 2 (WAN) - Physical interface
auto $NIC2
iface $NIC2 inet manual
# vmbr1 (WAN Bridge) - Connected to Spectrum cable modem
auto vmbr1
iface vmbr1 inet dhcp
bridge-ports $NIC2
bridge-stp off
bridge-fd 0
bridge-vlan-aware no
# Lower metric - preferred default route for internet access
metric 100
EOF
}
configure_network() {
log_info "Configuring network interfaces..."
if [ "$DRY_RUN" = "true" ]; then
log_info "[DRY RUN] Configuration that would be written:"
echo ""
generate_interfaces_config
return
fi
# Write configuration
generate_interfaces_config > /etc/network/interfaces
log_info "Network configuration written to /etc/network/interfaces"
}
apply_network_config() {
log_info "Applying network configuration..."
if [ "$DRY_RUN" = "true" ]; then
log_info "[DRY RUN] Would apply network configuration"
log_info "[DRY RUN] Would run: ifreload -a"
return
fi
# Bring down interfaces if they're up
ifdown vmbr0 2>/dev/null || true
ifdown vmbr1 2>/dev/null || true
ifdown "$NIC1" 2>/dev/null || true
ifdown "$NIC2" 2>/dev/null || true
# Reload all interfaces
ifreload -a || {
log_error "Failed to apply network configuration"
log_error "Restore backup if needed: cp /etc/network/interfaces.backup.* /etc/network/interfaces"
exit 1
}
# Wait a moment for DHCP
sleep 3
log_info "Network configuration applied successfully"
}
verify_configuration() {
log_info "Verifying network configuration..."
if [ "$DRY_RUN" = "true" ]; then
log_info "[DRY RUN] Would verify bridges are up and have IP addresses"
return
fi
# Check vmbr0
if ip link show vmbr0 &>/dev/null; then
if ip addr show vmbr0 | grep -q "inet "; then
VMBR0_IP=$(ip addr show vmbr0 | grep "inet " | awk '{print $2}' | head -1)
log_info "vmbr0 (LAN) is up with IP: $VMBR0_IP"
else
log_warn "vmbr0 is up but doesn't have an IP address yet (DHCP may be pending)"
fi
else
log_error "vmbr0 bridge is not up"
fi
# Check vmbr1
if ip link show vmbr1 &>/dev/null; then
if ip addr show vmbr1 | grep -q "inet "; then
VMBR1_IP=$(ip addr show vmbr1 | grep "inet " | awk '{print $2}' | head -1)
log_info "vmbr1 (WAN) is up with IP: $VMBR1_IP"
else
log_warn "vmbr1 is up but doesn't have an IP address yet (DHCP may be pending)"
fi
else
log_error "vmbr1 bridge is not up"
fi
# Check routing
DEFAULT_ROUTES=$(ip route | grep default)
if [ -n "$DEFAULT_ROUTES" ]; then
log_info "Default route(s):"
echo "$DEFAULT_ROUTES" | while read route; do
echo " $route"
done
# Check if WAN interface is in the default route
if echo "$DEFAULT_ROUTES" | grep -q "vmbr1"; then
log_info "✓ Default route via WAN (vmbr1) detected"
elif echo "$DEFAULT_ROUTES" | grep -q "vmbr0"; then
log_warn "⚠ Default route via LAN (vmbr0) detected - WAN (vmbr1) may need time for DHCP"
fi
else
log_warn "No default route found (may be waiting for DHCP)"
fi
# Check for specific routes
if ip route | grep -q "192.168.1.0/24"; then
LAN_ROUTE=$(ip route | grep "192.168.1.0/24" | head -1)
log_info "LAN route: $LAN_ROUTE"
fi
}
show_status() {
log_info "Current network status:"
echo ""
echo "=== Physical Interfaces ==="
ip link show "$NIC1" 2>/dev/null || echo " $NIC1: not found"
echo ""
ip link show "$NIC2" 2>/dev/null || echo " $NIC2: not found"
echo ""
echo "=== Bridges ==="
ip addr show vmbr0 2>/dev/null || echo " vmbr0: not found"
echo ""
ip addr show vmbr1 2>/dev/null || echo " vmbr1: not found"
echo ""
echo "=== Routing Table ==="
ip route show
}
main() {
log_info "Starting Proxmox network configuration..."
log_info "Hostname: $NODE_HOSTNAME"
if [ "$DRY_RUN" = "true" ]; then
log_warn "DRY RUN MODE - No changes will be made"
fi
check_root
detect_physical_interfaces
validate_interfaces
backup_config
configure_hostname
configure_network
apply_network_config
verify_configuration
log_info "Network configuration completed!"
echo ""
show_status
if [ "$DRY_RUN" = "false" ]; then
log_info "If you need to rollback, restore from: /etc/network/interfaces.backup.*"
fi
}
main "$@"