15 KiB
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
# Auf einen Proxmox-Node kopieren
scp -r proxmox-cluster-network-changer/ root@pve1:/root/
# Oder direkt klonen
cd /root
git clone <repo-url> 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.
# .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)
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)
python3 main.py --dry-run
Durchläuft den kompletten Prozess, zeigt alle geplanten Änderungen an, schreibt aber nichts.
Migration durchführen
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)
- Neue Konfigurationen werden auf alle Nodes verteilt (Staging)
- Corosync wird auf allen Nodes gestoppt
- pve-cluster (pmxcfs) wird gestoppt →
/etc/pveunmounted - Corosync-Config wird direkt geschrieben (
/etc/corosync/corosync.conf) /etc/hostswird aktualisiert/etc/network/interfaceswird aktualisiert + Netzwerk-Reload:- Remote-Nodes zuerst (fire-and-forget via
nohup) - Lokaler Node zuletzt
- Verifikation der neuen IPs mit Retry
- Remote-Nodes zuerst (fire-and-forget via
- 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
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
python3 main.py --rescue
oder einfach starten — wenn Nodes nicht erreichbar sind, wird automatisch gefragt:
python3 main.py
3 Node(s) nicht erreichbar.
Rescue-Netzwerk einrichten? [J/n]: j
Ablauf:
- Du gibst ein freies Subnetz an (z.B.
10.99.99.0/24) - Das Tool zeigt für jeden Node den
ip addr addBefehl - Auf dem lokalen Node wird die IP automatisch gesetzt
- Du führst die Befehle auf den anderen Nodes per Konsole aus
- Das Tool testet die Verbindung und liest die Configs
- Danach läuft die normale Migration
- Rescue-IPs werden durch
ifreload -aautomatisch 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 1genutzt, 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)
# 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
pvecm status— Cluster-Status prüfenpvecm expected 1— Quorum erzwingen (Notfall)ceph -s— Ceph-Status prüfenceph -w— Ceph-Recovery beobachtenjournalctl -u corosync— Corosync-Logs prüfenjournalctl -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.
# 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
# Auf JEDEM Node:
systemctl stop ceph-mon@$(hostname)
Schritt 2: MON-Map auf dem ersten Node erstellen
# 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
# 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
# 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 durchifreload -aautomatisch 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)
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.
# 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)
python3 main.py --discover
Szenario 4: Cluster komplett kaputt, kein Quorum
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