#!/usr/bin/env bash # IP Conflict Resolution Script # Resolves IP conflicts on r630-01 (Sankofa vs Order vs vault CTs). # 2026-03-25: VMID 7804 (gov-portals) owns 192.168.11.54. VMID 10070 (order-legal) must use IP_ORDER_LEGAL (default 192.168.11.87), not .54. # Original date: 2026-01-20 set -euo pipefail # Load IP configuration SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" source "${PROJECT_ROOT}/config/ip-addresses.conf" 2>/dev/null || true PROXMOX_HOST="${PROXMOX_HOST_R630_01}" LOG_FILE="/tmp/ip-conflict-resolution-$(date +%Y%m%d-%H%M%S).log" log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } log_error() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1" | tee -a "$LOG_FILE" } verify_ip_available() { local ip=$1 local vmid_if_mine=$2 log "Verifying IP $ip is available..." if ping -c 1 -W 1 "$ip" > /dev/null 2>&1; then if [ -n "${vmid_if_mine:-}" ]; then local cur cur=$(ssh root@$PROXMOX_HOST "pct config $vmid_if_mine 2>/dev/null | grep -oE 'ip=[0-9.]+' | head -1 | cut -d= -f2" | tr -d '\r') if [ "$cur" = "$ip" ]; then log "IP $ip already assigned to VMID $vmid_if_mine (ok) ✓" return 0 fi fi log_error "IP $ip is already in use!" return 1 fi log "IP $ip is available ✓" return 0 } resolve_conflict() { local vmid=$1 local old_ip=$2 local new_ip=$3 local name=$4 log "=== Resolving conflict for VMID $vmid ($name) ===" log "Current IP: $old_ip" log "New IP: $new_ip" # Verify new IP is available (or already ours on this VMID) if ! verify_ip_available "$new_ip" "$vmid"; then log_error "Cannot proceed - IP $new_ip is in use" return 1 fi # Stop container log "Stopping container $vmid..." ssh root@$PROXMOX_HOST "pct stop $vmid" || { log_error "Failed to stop container $vmid" return 1 } # Wait for stop sleep 2 # Get current network configuration log "Getting current network configuration..." local current_net=$(ssh root@$PROXMOX_HOST "pct config $vmid | grep '^net0:' | cut -d' ' -f2-") # Extract network parameters local bridge=$(echo "$current_net" | grep -oP 'bridge=\K[^,]+' || echo "vmbr0") local gw=$(echo "$current_net" | grep -oP 'gw=\K[^,]+' || echo "${PROXMOX_HOST_R630_01:-192.168.11.11}") local hwaddr=$(echo "$current_net" | grep -oP 'hwaddr=\K[^,]+' || echo "") local type=$(echo "$current_net" | grep -oP 'type=\K[^,]+' || echo "veth") # Build new network configuration local new_net="name=eth0,bridge=$bridge,gw=$gw,ip=$new_ip/24,type=$type" if [ -n "$hwaddr" ]; then new_net="$new_net,hwaddr=$hwaddr" fi # Update network configuration log "Updating network configuration..." ssh root@$PROXMOX_HOST "pct set $vmid --net0 '$new_net'" || { log_error "Failed to update network configuration" log "Attempting to start container with old configuration..." ssh root@$PROXMOX_HOST "pct start $vmid" || true return 1 } # Start container log "Starting container $vmid..." ssh root@$PROXMOX_HOST "pct start $vmid" || { log_error "Failed to start container $vmid" return 1 } # Wait for startup sleep 3 # Verify new IP log "Verifying new IP assignment..." local actual_ip=$(ssh root@$PROXMOX_HOST "pct exec $vmid -- hostname -I 2>/dev/null | awk '{print \$1}'" || echo "") if [ "$actual_ip" = "$new_ip" ]; then log "✓ IP conflict resolved successfully!" log " VMID $vmid ($name): $old_ip → $new_ip" return 0 else log_error "IP verification failed. Actual IP: $actual_ip, Expected: $new_ip" return 1 fi } main() { log "=== IP Conflict Resolution Script ===" log "Host: $PROXMOX_HOST" log "Log file: $LOG_FILE" log "" # Check if running in dry-run mode if [ "${DRY_RUN:-false}" = "true" ]; then log "DRY RUN MODE - No changes will be made" log "" fi # Verify SSH access log "Verifying SSH access..." if ! ssh -o ConnectTimeout=5 root@$PROXMOX_HOST "echo 'SSH OK'" > /dev/null 2>&1; then log_error "Cannot connect to $PROXMOX_HOST" exit 1 fi log "SSH access verified ✓" log "" # Verify target IPs are free (ping) — not .54 (in use by gov-portals 7804) log "=== Step 1: Verifying IP Availability ===" for ip in ${IP_SERVICE_55:-192.168.11.55} ${IP_SERVICE_56:-192.168.11.56}; do if ! verify_ip_available "$ip"; then log_error "Required IP $ip is not available. Cannot proceed." exit 1 fi done log "" if [ "${DRY_RUN:-false}" = "true" ]; then log "DRY RUN - Would resolve conflicts:" log " VMID 10070 (order-legal): if still on ${IP_GOV_PORTALS_DEV:-192.168.11.54} → $IP_ORDER_LEGAL" log " VMID 10230 (order-vault): ${IP_SERVICE_51:-${IP_SERVICE_51:-${IP_SERVICE_51:-${IP_SERVICE_51:-${IP_SERVICE_51:-${IP_SERVICE_51:-192.168.11.51}}}}}} → ${IP_SERVICE_55:-${IP_SERVICE_55:-192.168.11.55}}" log " VMID 10232 (CT10232): ${IP_SERVICE_52:-${IP_SERVICE_52:-192.168.11.52}} → ${IP_SERVICE_56:-${IP_SERVICE_56:-192.168.11.56}}" exit 0 fi # Resolve conflicts log "=== Step 2: Resolving IP Conflicts ===" log "" # Conflict 1: VMID 10070 (order-legal) must not share IP with VMID 7804 (gov-portals on .54) IP_GOV="${IP_GOV_PORTALS_DEV:-192.168.11.54}" IP_ORDER_LEGAL="${IP_ORDER_LEGAL:-192.168.11.87}" CURRENT_LEGAL=$(ssh root@$PROXMOX_HOST "pct config 10070 2>/dev/null | grep -oE 'ip=[0-9.]+' | head -1 | cut -d= -f2" | tr -d '\r' || echo "") result1=0 if [ "$CURRENT_LEGAL" = "$IP_GOV" ]; then log "VMID 10070 shares gov-portals IP $IP_GOV — moving to $IP_ORDER_LEGAL" resolve_conflict 10070 "$IP_GOV" "$IP_ORDER_LEGAL" "order-legal" result1=$? elif [ "$CURRENT_LEGAL" = "$IP_ORDER_LEGAL" ]; then log "VMID 10070 already on $IP_ORDER_LEGAL — skip" result1=0 else log "VMID 10070 is $CURRENT_LEGAL (expected $IP_ORDER_LEGAL or conflict with $IP_GOV); no automatic change" result1=0 fi log "" # Conflict 2: VMID 10230 (order-vault) resolve_conflict 10230 "${IP_SERVICE_51:-${IP_SERVICE_51:-${IP_SERVICE_51:-${IP_SERVICE_51:-${IP_SERVICE_51:-${IP_SERVICE_51:-192.168.11.51}}}}}}" "${IP_SERVICE_55:-${IP_SERVICE_55:-192.168.11.55}}" "order-vault" local result2=$? log "" # Conflict 3: VMID 10232 (CT10232) resolve_conflict 10232 "${IP_SERVICE_52:-${IP_SERVICE_52:-192.168.11.52}}" "${IP_SERVICE_56:-${IP_SERVICE_56:-192.168.11.56}}" "CT10232" local result3=$? log "" # Summary log "=== Resolution Summary ===" if [ $result1 -eq 0 ] && [ $result2 -eq 0 ] && [ $result3 -eq 0 ]; then log "✓ All IP conflicts resolved successfully!" log "" log "Verification:" log " ${IP_SERVICE_50:-${IP_SERVICE_50:-${IP_SERVICE_50:-${IP_SERVICE_50:-${IP_SERVICE_50:-${IP_SERVICE_50:-192.168.11.50}}}}}} → VMID 7800 (sankofa-api-1) only" log " ${IP_SERVICE_51:-${IP_SERVICE_51:-${IP_SERVICE_51:-${IP_SERVICE_51:-${IP_SERVICE_51:-${IP_SERVICE_51:-192.168.11.51}}}}}} → VMID 7801 (sankofa-portal-1) only" log " ${IP_SERVICE_52:-${IP_SERVICE_52:-192.168.11.52}} → VMID 7802 (sankofa-keycloak-1) only" log " ${IP_GOV_PORTALS_DEV:-192.168.11.54} → VMID 7804 (gov-portals-dev) only" log " ${IP_ORDER_LEGAL:-192.168.11.87} → VMID 10070 (order-legal)" log " ${IP_SERVICE_55:-${IP_SERVICE_55:-192.168.11.55}} → VMID 10230 (order-vault)" log " ${IP_SERVICE_56:-${IP_SERVICE_56:-192.168.11.56}} → VMID 10232 (CT10232)" exit 0 else log_error "Some conflicts could not be resolved. Check log file: $LOG_FILE" exit 1 fi } main "$@"