update ssh keys authorization file
This commit is contained in:
parent
2ad6a8dbc4
commit
82ddb25c01
|
|
@ -91,11 +91,15 @@ def parse_ceph_conf(content: str) -> CephConfig:
|
||||||
if m:
|
if m:
|
||||||
config.cluster_network = m.group(1)
|
config.cluster_network = m.group(1)
|
||||||
|
|
||||||
# Extract mon_host
|
# Extract mon_host (can be comma-separated, space-separated, or both)
|
||||||
m = re.search(r'mon.host\s*=\s*(.+)', content)
|
m = re.search(r'mon.host\s*=\s*(.+)', content)
|
||||||
if m:
|
if m:
|
||||||
hosts_str = m.group(1).strip()
|
hosts_str = m.group(1).strip()
|
||||||
config.mon_hosts = [h.strip() for h in hosts_str.split(',') if h.strip()]
|
# Split by comma or whitespace
|
||||||
|
config.mon_hosts = [
|
||||||
|
h.strip() for h in re.split(r'[,\s]+', hosts_str)
|
||||||
|
if h.strip()
|
||||||
|
]
|
||||||
|
|
||||||
# Extract [mon.X] sections
|
# Extract [mon.X] sections
|
||||||
mon_sections = re.findall(
|
mon_sections = re.findall(
|
||||||
|
|
|
||||||
84
migrator.py
84
migrator.py
|
|
@ -36,33 +36,39 @@ class Migrator:
|
||||||
if not self._distribute_configs(plan, configs, dry_run):
|
if not self._distribute_configs(plan, configs, dry_run):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Step 2: Stop Corosync on all nodes
|
# Step 2: Preserve SSH keys before stopping pve-cluster
|
||||||
print("\n[2/7] Corosync stoppen auf allen Nodes...")
|
# /etc/pve/priv/authorized_keys gets unmounted when pve-cluster stops!
|
||||||
|
print("\n[2/8] SSH-Keys sichern (werden nach pve-cluster stop benötigt)...")
|
||||||
|
if not self._preserve_ssh_keys(reachable_nodes, dry_run):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Step 3: Stop Corosync on all nodes
|
||||||
|
print("\n[3/8] Corosync stoppen auf allen Nodes...")
|
||||||
if not self._stop_corosync(reachable_nodes, dry_run):
|
if not self._stop_corosync(reachable_nodes, dry_run):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Step 3: Stop pve-cluster (pmxcfs) to release corosync.conf
|
# Step 4: Stop pve-cluster (pmxcfs) to release corosync.conf
|
||||||
print("\n[3/7] pve-cluster stoppen...")
|
print("\n[4/8] pve-cluster stoppen...")
|
||||||
if not self._stop_pve_cluster(reachable_nodes, dry_run):
|
if not self._stop_pve_cluster(reachable_nodes, dry_run):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Step 4: Write corosync config directly
|
# Step 5: Write corosync config directly
|
||||||
print("\n[4/7] Corosync-Konfiguration aktualisieren...")
|
print("\n[5/8] Corosync-Konfiguration aktualisieren...")
|
||||||
if not self._update_corosync(reachable_nodes, configs, dry_run):
|
if not self._update_corosync(reachable_nodes, configs, dry_run):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Step 5: Update /etc/hosts on all nodes
|
# Step 6: Update /etc/hosts on all nodes
|
||||||
print("\n[5/7] /etc/hosts aktualisieren...")
|
print("\n[6/8] /etc/hosts aktualisieren...")
|
||||||
if not self._update_hosts(plan, configs, dry_run):
|
if not self._update_hosts(plan, configs, dry_run):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Step 6: Update network interfaces and restart networking
|
# Step 7: Update network interfaces and restart networking
|
||||||
print("\n[6/7] Netzwerk-Interfaces aktualisieren und Netzwerk neu starten...")
|
print("\n[7/8] Netzwerk-Interfaces aktualisieren und Netzwerk neu starten...")
|
||||||
if not self._update_network(plan, configs, dry_run):
|
if not self._update_network(plan, configs, dry_run):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Step 7: Start services back up
|
# Step 8: Start services back up
|
||||||
print("\n[7/7] Services starten...")
|
print("\n[8/8] Services starten...")
|
||||||
if not self._start_services(plan, configs, dry_run):
|
if not self._start_services(plan, configs, dry_run):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
@ -157,6 +163,56 @@ class Migrator:
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _preserve_ssh_keys(self, nodes: list, dry_run: bool) -> bool:
|
||||||
|
"""Copy /etc/pve/priv/authorized_keys to ~/.ssh/ on all nodes.
|
||||||
|
|
||||||
|
When pve-cluster (pmxcfs) is stopped, /etc/pve gets unmounted and
|
||||||
|
the cluster SSH keys disappear. This breaks SSH between nodes.
|
||||||
|
We temporarily copy them to ~/.ssh/authorized_keys so SSH keeps working.
|
||||||
|
"""
|
||||||
|
for node in nodes:
|
||||||
|
if dry_run:
|
||||||
|
print(f" [{node.name}] Würde SSH-Keys sichern")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Append pve keys to ~/.ssh/authorized_keys (avoid duplicates)
|
||||||
|
cmd = (
|
||||||
|
"if [ -f /etc/pve/priv/authorized_keys ]; then "
|
||||||
|
" mkdir -p /root/.ssh && "
|
||||||
|
" cp /root/.ssh/authorized_keys /root/.ssh/authorized_keys.pre_migration 2>/dev/null; "
|
||||||
|
" cat /etc/pve/priv/authorized_keys >> /root/.ssh/authorized_keys && "
|
||||||
|
" sort -u -o /root/.ssh/authorized_keys /root/.ssh/authorized_keys && "
|
||||||
|
" chmod 600 /root/.ssh/authorized_keys && "
|
||||||
|
" echo ok; "
|
||||||
|
"else "
|
||||||
|
" echo no_pve_keys; "
|
||||||
|
"fi"
|
||||||
|
)
|
||||||
|
rc, stdout, err = self.ssh.run_on_node(
|
||||||
|
node.ssh_host, cmd, node.is_local
|
||||||
|
)
|
||||||
|
if rc == 0 and "ok" in stdout:
|
||||||
|
print(f" [{node.name}] SSH-Keys gesichert")
|
||||||
|
elif "no_pve_keys" in stdout:
|
||||||
|
print(f" [{node.name}] Keine PVE-Keys gefunden (übersprungen)")
|
||||||
|
else:
|
||||||
|
print(f" [{node.name}] WARNUNG SSH-Keys: {err}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _restore_ssh_keys(self, nodes: list):
|
||||||
|
"""Restore original ~/.ssh/authorized_keys after migration."""
|
||||||
|
for node in nodes:
|
||||||
|
new_host = node.new_ip if not node.is_local else node.ssh_host
|
||||||
|
cmd = (
|
||||||
|
"if [ -f /root/.ssh/authorized_keys.pre_migration ]; then "
|
||||||
|
" mv /root/.ssh/authorized_keys.pre_migration /root/.ssh/authorized_keys && "
|
||||||
|
" echo restored; "
|
||||||
|
"else "
|
||||||
|
" echo no_backup; "
|
||||||
|
"fi"
|
||||||
|
)
|
||||||
|
self.ssh.run_on_node(new_host, cmd, node.is_local)
|
||||||
|
|
||||||
def _stop_corosync(self, nodes: list, dry_run: bool) -> bool:
|
def _stop_corosync(self, nodes: list, dry_run: bool) -> bool:
|
||||||
"""Stop corosync on all nodes."""
|
"""Stop corosync on all nodes."""
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
|
|
@ -337,6 +393,10 @@ class Migrator:
|
||||||
if configs.get('ceph'):
|
if configs.get('ceph'):
|
||||||
self._update_ceph(plan, configs)
|
self._update_ceph(plan, configs)
|
||||||
|
|
||||||
|
# Restore original SSH keys (pve-cluster manages them again now)
|
||||||
|
print("\n SSH-Keys wiederherstellen...")
|
||||||
|
self._restore_ssh_keys(plan.nodes)
|
||||||
|
|
||||||
# Cleanup staging directories
|
# Cleanup staging directories
|
||||||
print("\n Staging-Verzeichnisse aufräumen...")
|
print("\n Staging-Verzeichnisse aufräumen...")
|
||||||
for node in plan.nodes:
|
for node in plan.nodes:
|
||||||
|
|
|
||||||
33
planner.py
33
planner.py
|
|
@ -34,11 +34,38 @@ class Planner:
|
||||||
# Detect old network from first node
|
# Detect old network from first node
|
||||||
if nodes:
|
if nodes:
|
||||||
old_ip = ipaddress.ip_address(nodes[0].current_ip)
|
old_ip = ipaddress.ip_address(nodes[0].current_ip)
|
||||||
for iface in nodes[0].interfaces:
|
# Try to find matching interface
|
||||||
if iface.address == str(old_ip):
|
for node in nodes:
|
||||||
plan.old_network = f"{ipaddress.ip_network(f'{iface.address}/{iface.cidr}', strict=False)}"
|
for iface in node.interfaces:
|
||||||
|
if iface.address == str(old_ip) or (
|
||||||
|
iface.address and iface.cidr and
|
||||||
|
ipaddress.ip_address(iface.address) in
|
||||||
|
ipaddress.ip_network(f'{iface.address}/{iface.cidr}', strict=False) and
|
||||||
|
old_ip in ipaddress.ip_network(f'{iface.address}/{iface.cidr}', strict=False)
|
||||||
|
):
|
||||||
|
plan.old_network = str(ipaddress.ip_network(
|
||||||
|
f'{iface.address}/{iface.cidr}', strict=False
|
||||||
|
))
|
||||||
plan.bridge_name = iface.name
|
plan.bridge_name = iface.name
|
||||||
break
|
break
|
||||||
|
if plan.old_network:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Fallback: try to guess from corosync IPs
|
||||||
|
if not plan.old_network:
|
||||||
|
# Find common network from all corosync node IPs
|
||||||
|
for cidr_guess in [24, 16, 8]:
|
||||||
|
net = ipaddress.ip_network(
|
||||||
|
f'{nodes[0].current_ip}/{cidr_guess}', strict=False
|
||||||
|
)
|
||||||
|
if all(ipaddress.ip_address(n.current_ip) in net for n in nodes):
|
||||||
|
plan.old_network = str(net)
|
||||||
|
break
|
||||||
|
|
||||||
|
if plan.old_network:
|
||||||
|
print(f" Erkanntes altes Netzwerk: {plan.old_network}")
|
||||||
|
else:
|
||||||
|
print(" [!] Altes Netzwerk konnte nicht erkannt werden")
|
||||||
|
|
||||||
# Generate IP mapping suggestions
|
# Generate IP mapping suggestions
|
||||||
print("\n[IP-Mapping]")
|
print("\n[IP-Mapping]")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue