402 lines
13 KiB
Markdown
402 lines
13 KiB
Markdown
# 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 <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.
|
|
|
|
```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: 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.
|
|
|
|
**Auf jedem Node ausführen:**
|
|
|
|
```bash
|
|
# 1. MON stoppen
|
|
systemctl stop ceph-mon@$(hostname)
|
|
|
|
# 2. Aktuelle MON-Map extrahieren und prüfen
|
|
ceph-mon -i $(hostname) --extract-monmap /tmp/monmap
|
|
monmaptool --print /tmp/monmap
|
|
|
|
# 3. Alte Einträge entfernen (für jeden MON-Node)
|
|
monmaptool --rm pve1 /tmp/monmap
|
|
monmaptool --rm pve2 /tmp/monmap
|
|
monmaptool --rm pve3 /tmp/monmap
|
|
|
|
# 4. 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
|
|
|
|
# 5. Ergebnis prüfen
|
|
monmaptool --print /tmp/monmap
|
|
|
|
# 6. Aktualisierte MON-Map zurückschreiben
|
|
ceph-mon -i $(hostname) --inject-monmap /tmp/monmap
|
|
|
|
# 7. Services starten
|
|
systemctl start ceph-mon@$(hostname)
|
|
systemctl restart ceph-mgr@$(hostname)
|
|
systemctl restart ceph-osd.target
|
|
|
|
# 8. Aufräumen
|
|
rm -f /tmp/monmap
|
|
```
|
|
|
|
> **Hinweis:** Node-Namen und IPs an das eigene Setup anpassen. Schritte 3+4 müssen alle MON-Nodes des Clusters enthalten, nicht nur den lokalen. 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
|
|
```
|