# Proxmox Cluster Network Changer Migriert ein komplettes Proxmox-Cluster (inkl. Ceph) von einem Netzwerk in ein anderes. **Problem:** Wenn man bei einem Proxmox-Cluster die IPs ändert, verliert man das Quorum und `/etc/pve` wird read-only — dann kann man weder Corosync noch Ceph über das Cluster-Dateisystem konfigurieren. Dieses Tool löst das Problem durch eine koordinierte Migration aller Nodes. ## Features - **Auto-Detect** aller Nodes, Bridges, IPs und Netzwerke - Koordinierte Migration aller Nodes in einem Durchgang - **Multi-NIC-Support** — erkennt automatisch Management-, Ceph-Public- und Ceph-Cluster-Bridges - Ceph-Support (Public Network, Cluster Network, MON-Adressen) - Funktioniert auch bei **gebrochenem Quorum** (z.B. wenn ein Node bereits manuell geändert wurde) - **Rescue-Netzwerk** — temporäres Emergency-Netz wenn sich Nodes nicht mehr erreichen können - **Passwort-Auth via `.env`** — unabhängig von `/etc/pve`, funktioniert immer - Automatische Backups aller Konfigurationen vor der Migration - Dry-Run-Modus zum gefahrlosen Testen - Verifikation nach der Migration ## Voraussetzungen - Python 3.9+ (auf Proxmox standardmäßig vorhanden) - Root-Zugriff auf dem Node, auf dem das Tool läuft - `sshpass` — wird beim ersten Start automatisch installiert falls nicht vorhanden - Root-Passwort der Proxmox-Nodes (alle Nodes müssen das gleiche Passwort haben) - Keine externen Python-Pakete nötig (nur stdlib) ## Installation ```bash # Auf einen Proxmox-Node kopieren scp -r proxmox-cluster-network-changer/ root@pve1:/root/ # Oder direkt klonen cd /root git clone proxmox-cluster-network-changer # .env erstellen cd proxmox-cluster-network-changer cp .env.example .env nano .env # SSH_PASSWORD=dein-root-passwort eintragen ``` ## SSH-Authentifizierung Das Tool nutzt `sshpass` mit dem Root-Passwort aus der `.env`-Datei. Key-basierte Auth funktioniert bei Proxmox nicht zuverlässig, weil `/etc/pve/priv/authorized_keys` verschwindet wenn `pve-cluster` gestoppt wird. ```bash # .env-Datei erstellen echo 'SSH_PASSWORD=dein-root-passwort' > .env ``` > **Hinweis:** Alle Nodes müssen das gleiche Root-Passwort haben (bei Proxmox-Clustern üblich). ## Verwendung ### Aktuelle Konfiguration anzeigen (Discovery) ```bash python3 main.py --discover ``` Zeigt an: - Alle Cluster-Nodes mit IPs - Corosync-Konfiguration - Ceph-Netzwerke und MON-Hosts - Quorum-Status - Welche Nodes erreichbar sind ### Dry-Run (nichts wird geändert) ```bash python3 main.py --dry-run ``` Durchläuft den kompletten Prozess, zeigt alle geplanten Änderungen an, schreibt aber nichts. ### Migration durchführen ```bash python3 main.py ``` Das Tool führt interaktiv durch den Prozess: ``` ============================================================ Proxmox Cluster Network Changer ============================================================ [SSH] Passwort-Authentifizierung aktiv (via sshpass) === Phase 1: Discovery === [Corosync] Cluster: mycluster Nodes gefunden: 4 - pve1 (ID: 1) -> 192.168.0.101 - pve2 (ID: 2) -> 192.168.0.102 - pve3 (ID: 3) -> 192.168.0.103 - pve4 (ID: 4) -> 192.168.0.104 [Ceph] Public Network: 192.168.0.0/24 Cluster Network: 10.0.1.0/24 === Phase 2: Migration planen === [Netzwerk-Erkennung] vmbr0: Management/Corosync (192.168.0.0/24) vmbr1: Ceph Cluster (10.0.1.0/24) [Management-Netzwerk (Corosync)] Aktuell: 192.168.0.0/24 Neues Management-Netzwerk (z.B. 172.0.2.0/16): 172.0.2.0/16 Neues Gateway [172.0.0.1]: 172.0.2.1 [Management IP-Mapping] pve1: 192.168.0.101 -> [172.0.2.101]: pve2: 192.168.0.102 -> [172.0.2.102]: [Ceph Netzwerke] Ceph Public: gleich wie Management -> wird automatisch mit umgezogen Ceph Cluster (10.0.1.0/24) auf separater NIC -> eigenes Mapping Migration durchführen? [j/N]: j ``` Das Tool erkennt automatisch welche Bridges welche Netzwerke tragen. Wenn Ceph Public/Cluster auf separaten NICs liegen, werden die IPs einzeln pro Node abgefragt. ### Optionen | Option | Beschreibung | |---|---| | `--dry-run` | Nur anzeigen, nichts ändern | | `--discover` | Nur aktuelle Config anzeigen | | `--rescue` | Rescue-Modus: Emergency-Netzwerk einrichten | | `--rescue-commands SUBNET` | Nur Rescue-Befehle ausgeben (z.B. `10.99.99.0/24`) | | `--ssh-port PORT` | SSH-Port (Standard: 22) | | `--env-file PFAD` | Pfad zur .env-Datei (Standard: `.env`) | ### `.env`-Datei | Variable | Beschreibung | |---|---| | `SSH_PASSWORD` | **Pflicht.** Root-Passwort für SSH (alle Nodes gleich) | | `SSH_USER` | SSH-Benutzer (Standard: `root`) | ## Was wird geändert? | Datei | Wo | Was | |---|---|---| | `/etc/network/interfaces` | Jeder Node | Alle Bridge-IPs (Management, Ceph), Gateway | | `/etc/hosts` | Jeder Node | Hostname-zu-IP-Zuordnung | | `/etc/corosync/corosync.conf` | Jeder Node | Corosync Ring-Adressen | | `/etc/pve/ceph.conf` | Cluster-FS | public_network, cluster_network, MON-Adressen | ## Migrationsablauf (Phase 4) 1. Neue Konfigurationen werden auf alle Nodes verteilt (Staging) 2. Corosync wird auf allen Nodes gestoppt 3. pve-cluster (pmxcfs) wird gestoppt → `/etc/pve` unmounted 4. Corosync-Config wird direkt geschrieben (`/etc/corosync/corosync.conf`) 5. `/etc/hosts` wird aktualisiert 6. `/etc/network/interfaces` wird aktualisiert + Netzwerk-Reload: - Remote-Nodes zuerst (fire-and-forget via `nohup`) - Lokaler Node zuletzt - Verifikation der neuen IPs mit Retry 7. Services starten (pve-cluster, corosync), Quorum abwarten, Ceph aktualisieren > SSH funktioniert durchgehend via `sshpass` — unabhängig von `/etc/pve`. ## Rescue-Netzwerk (Emergency Mode) **Szenario:** PVE01 hat bereits eine neue IP, PVE02-04 sind noch im alten Netz. Kein Node kann die anderen erreichen. ### Schnell: Nur Befehle anzeigen ```bash python3 main.py --rescue-commands 10.99.99.0/24 ``` Ausgabe: ``` RESCUE BEFEHLE Subnetz: 10.99.99.0/24 | Bridge: vmbr0 pve1 (192.168.0.101): ip addr add 10.99.99.1/24 dev vmbr0 pve2 (192.168.0.102): ip addr add 10.99.99.2/24 dev vmbr0 pve3 (192.168.0.103): ip addr add 10.99.99.3/24 dev vmbr0 pve4 (192.168.0.104): ip addr add 10.99.99.4/24 dev vmbr0 Zum Entfernen: ip addr del 10.99.99.1/24 dev vmbr0 # pve1 ip addr del 10.99.99.2/24 dev vmbr0 # pve2 ip addr del 10.99.99.3/24 dev vmbr0 # pve3 ip addr del 10.99.99.4/24 dev vmbr0 # pve4 ``` Diese Befehle über IPMI/iLO/iDRAC/KVM-Konsole auf jedem Node ausführen. ### Interaktiv: Rescue + Migration ```bash python3 main.py --rescue ``` oder einfach starten — wenn Nodes nicht erreichbar sind, wird automatisch gefragt: ```bash python3 main.py ``` ``` 3 Node(s) nicht erreichbar. Rescue-Netzwerk einrichten? [J/n]: j ``` Ablauf: 1. Du gibst ein freies Subnetz an (z.B. `10.99.99.0/24`) 2. Das Tool zeigt für jeden Node den `ip addr add` Befehl 3. Auf dem lokalen Node wird die IP automatisch gesetzt 4. Du führst die Befehle auf den anderen Nodes per Konsole aus 5. Das Tool testet die Verbindung und liest die Configs 6. Danach läuft die normale Migration 7. Rescue-IPs werden durch `ifreload -a` automatisch entfernt ### Wann brauche ich das? - Ein oder mehrere Nodes haben bereits manuell eine neue IP bekommen - Die Nodes liegen in verschiedenen Subnetzen - SSH zwischen den Nodes funktioniert nicht mehr - Du hast aber noch Zugriff auf die Konsolen (IPMI/iLO/iDRAC/KVM) ## Gebrochenes Quorum Wenn bereits ein Node manuell geändert wurde und das Quorum verloren ist: - Das Tool erkennt den Zustand automatisch in der Discovery-Phase - Nicht erreichbare Nodes werden per Hostname gesucht - Configs werden direkt geschrieben (nicht über `/etc/pve/`) - Nach dem Netzwerk-Reload wird `pvecm expected 1` genutzt, um Quorum zu erzwingen - Danach wird Ceph über das Cluster-Dateisystem aktualisiert ## Backups Vor der Migration werden automatisch Backups erstellt: ``` /root/network-migration-backup-20260304_143022/ ├── etc_network_interfaces ├── etc_hosts ├── etc_corosync_corosync.conf ├── etc_ceph_ceph.conf ├── etc_pve_corosync.conf └── etc_pve_ceph.conf ``` ### Restore (manuell) ```bash # Beispiel: Netzwerk-Config wiederherstellen cp /root/network-migration-backup-*/etc_network_interfaces /etc/network/interfaces ifreload -a # Corosync wiederherstellen cp /root/network-migration-backup-*/etc_corosync_corosync.conf /etc/corosync/corosync.conf systemctl restart corosync ``` ## Empfohlene Reihenfolge bei Problemen 1. `pvecm status` — Cluster-Status prüfen 2. `pvecm expected 1` — Quorum erzwingen (Notfall) 3. `ceph -s` — Ceph-Status prüfen 4. `ceph -w` — Ceph-Recovery beobachten 5. `journalctl -u corosync` — Corosync-Logs prüfen 6. `journalctl -u pve-cluster` — pmxcfs-Logs prüfen ### Workaround: Corosync zeigt noch alte IPs in der GUI Falls nach der Migration in der Proxmox-GUI unter **Datacenter → Cluster** noch die alten IPs stehen, wurde `/etc/pve/corosync.conf` nicht aktualisiert. Das passiert weil `/etc/pve` während der Migration read-only ist und die Config nur direkt nach `/etc/corosync/corosync.conf` geschrieben wird. Aktuelle Versionen des Tools aktualisieren `/etc/pve/corosync.conf` automatisch nach dem Quorum. ```bash # 1. Prüfen was wo drin steht: grep -A1 ring0_addr /etc/corosync/corosync.conf grep -A1 ring0_addr /etc/pve/corosync.conf # 2. Wenn /etc/corosync/corosync.conf die neuen IPs hat: cp /etc/corosync/corosync.conf /etc/pve/corosync.conf # 3. Falls auch /etc/corosync/corosync.conf die alten IPs hat, # manuell editieren (IPs anpassen): nano /etc/pve/corosync.conf # 4. config_version in /etc/pve/corosync.conf hochzählen # (Pflicht, damit alle Nodes die Änderung übernehmen) # Die Zeile steht ganz oben/oder fast unten in der Datei im totem {} Block: # # totem { # ... # config_version: 4 <-- diese Zahl um 1 erhöhen (z.B. 4 -> 5) # } # # Aktuelle Version anzeigen: grep config_version /etc/pve/corosync.conf # 5. Corosync neu starten: systemctl restart corosync ``` ### Workaround: Ceph MON-Map manuell aktualisieren Falls nach der Migration `ceph-mon` und `ceph-mgr` nicht starten (z.B. weil eine ältere Version des Tools die MON-Map nicht aktualisiert hat), muss die Ceph MON-Map manuell korrigiert werden. Die MON-Map ist eine interne Datenbank in der die MON-Adressen gespeichert sind — ein reines Update der `ceph.conf` reicht nicht. **Wichtig:** Die MON-Map muss auf allen Nodes identisch sein (gleiche Epoch), sonst crasht Ceph mit `notify_rank_removed`. Deshalb: auf **einem** Node erstellen und auf die anderen kopieren. **Schritt 1: MON auf allen Nodes stoppen** ```bash # Auf JEDEM Node: systemctl stop ceph-mon@$(hostname) ``` **Schritt 2: MON-Map auf dem ersten Node erstellen** ```bash # Auf pve1 (oder einem beliebigen Node): ceph-mon -i $(hostname) --extract-monmap /tmp/monmap monmaptool --print /tmp/monmap # Alte Einträge entfernen (für jeden MON-Node) monmaptool --rm pve1 /tmp/monmap monmaptool --rm pve2 /tmp/monmap monmaptool --rm pve3 /tmp/monmap # Neue Einträge mit neuen IPs hinzufügen monmaptool --addv pve1 [v2:172.0.2.101:3300/0,v1:172.0.2.101:6789/0] /tmp/monmap monmaptool --addv pve2 [v2:172.0.2.102:3300/0,v1:172.0.2.102:6789/0] /tmp/monmap monmaptool --addv pve3 [v2:172.0.2.103:3300/0,v1:172.0.2.103:6789/0] /tmp/monmap # Ergebnis prüfen monmaptool --print /tmp/monmap # Injizieren und MON starten ceph-mon -i $(hostname) --inject-monmap /tmp/monmap systemctl start ceph-mon@$(hostname) ``` **Schritt 3: Autoritative Map holen und auf die anderen Nodes kopieren** ```bash # Auf pve1 (wo der MON jetzt läuft): ceph mon getmap -o /tmp/monmap_auth # Auf die anderen Nodes kopieren: scp /tmp/monmap_auth root@172.0.2.102:/tmp/monmap scp /tmp/monmap_auth root@172.0.2.103:/tmp/monmap ``` **Schritt 4: Auf jedem weiteren Node injizieren und starten** ```bash # Auf pve2, pve3, etc.: ceph-mon -i $(hostname) --inject-monmap /tmp/monmap systemctl start ceph-mon@$(hostname) systemctl restart ceph-mgr@$(hostname) systemctl restart ceph-osd.target systemctl restart ceph-mds@$(hostname) # nur wenn CephFS vorhanden rm -f /tmp/monmap ``` > **Hinweis:** Node-Namen und IPs an das eigene Setup anpassen. Aktuelle Versionen des Tools aktualisieren die MON-Map automatisch. ## Hinweise - Das Tool muss als **root** ausgeführt werden - Alle Nodes müssen das gleiche Root-Passwort haben - VMs/CTs werden **nicht** automatisch migriert oder gestoppt — das Netzwerk wird im laufenden Betrieb geändert - Nach der Migration sollten VM-Netzwerke (Bridges in VM-Configs) geprüft werden, falls diese sich auf spezifische IPs beziehen - Die Emergency-IPs (`ip addr add`) sind temporär und werden durch `ifreload -a` automatisch entfernt - Bridges werden automatisch erkannt — keine manuelle Angabe nötig - Beim Netzwerk-Reload werden Remote-Nodes zuerst umgestellt (fire-and-forget), der lokale Node zuletzt — so schneidet sich das Tool nicht selbst die Verbindung ab - Getestet mit Proxmox VE 7.x und 8.x ## Projektstruktur ``` proxmox-cluster-network-changer/ ├── main.py # Entry-Point, CLI, .env-Loader ├── discovery.py # Phase 1: Cluster-Config lesen & parsen ├── planner.py # Phase 2: IP-Mapping, neue Configs generieren ├── backup.py # Phase 3: Backup aller Configs ├── migrator.py # Phase 4: Migration durchführen (7 Schritte) ├── verifier.py # Phase 5: Post-Migration Checks ├── rescue.py # Rescue-Netzwerk (Emergency Mode) ├── ssh_manager.py # SSH via sshpass (Passwort-Auth) ├── config_parser.py # Parser für Corosync/Ceph/Network Configs ├── models.py # Dataclasses (NodeInfo, CorosyncConfig, etc.) ├── .env.example # Vorlage für SSH-Credentials └── requirements.txt # Keine externen Dependencies ``` ## Typische Szenarien ### Szenario 1: Normaler Umzug (alles funktioniert noch) ```bash echo 'SSH_PASSWORD=dein-passwort' > .env python3 main.py --dry-run # Erst testen python3 main.py # Dann ausführen ``` ### Szenario 2: Ein Node wurde bereits manuell geändert PVE01 hat schon die neue IP, PVE02-04 noch die alte. Kein SSH möglich. ```bash # Option A: Rescue-Befehle anzeigen und manuell ausführen python3 main.py --rescue-commands 10.99.99.0/24 # -> Befehle auf allen Nodes per IPMI/KVM eingeben # -> Dann normal starten: python3 main.py --rescue # Option B: Einfach starten, das Tool fragt automatisch python3 main.py # -> "3 Node(s) nicht erreichbar. Rescue-Netzwerk einrichten? [J/n]" ``` ### Szenario 3: Nur Discovery (schauen was los ist) ```bash python3 main.py --discover ``` ### Szenario 4: Cluster komplett kaputt, kein Quorum ```bash python3 main.py --rescue # Das Tool: # 1. Richtet Emergency-Netzwerk ein # 2. Liest Configs über Emergency-IPs # 3. Migriert alles # 4. Erzwingt Quorum mit 'pvecm expected 1' # 5. Schreibt Ceph-Config direkt (nicht über /etc/pve) # 6. Räumt Emergency-IPs auf ```