TP15: CRS326-Core als L3-Router aktiviert

- crs326-core nimmt Routing/DHCP/DNS/NAT/WAN/WireGuard von ap1 ueber.
- L2-Portplan korrigiert (ap1 #15, Arris WAN #16, branch #8, Sonos #2/3/5/6,
  IoT #14, VLAN10-Access #11, VLAN1-Hybrid #3/7/9/10/12/17-24).
- Backup-RSC gefixt (ether3-24 disabled) und via fixed-RSC eingespielt.
- Gestagede L3-Config enabled in einem Rutsch.
- Falsche statische Default-Route 0.0.0.0/0 -> 10.40.41.1 entfernt,
  WAN-DHCP via 90.146.33.177/24 aktiv.
- SFP-Ports bleiben disabled (Stromersparnis).
- ap1 ist L3-stillgelegt und wird in naechster Session reiner AP.

Backup-Dateien (*.backup) und leases-export.json bleiben lokal.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-07 22:39:29 +02:00
parent d8c8c3b828
commit d281a0f36f
25 changed files with 3214 additions and 9 deletions

86
routeros/backup-core.py Normal file
View File

@@ -0,0 +1,86 @@
#!/usr/bin/env python3
"""Erstelle ein Binary-Backup + einen Text-Export von crs326-core vor dem TP15-Cutover.
Creds aus ~/.claude.json (mikromcp-env, CRS326_CORE_*). Backup-Dateien landen unter routeros/.
"""
import json
import pathlib
import subprocess
import sys
from datetime import datetime, timezone
CORE_IP = "10.40.41.211" # VLAN1-Temp; alternativ 10.40.99.11 sobald 99T auf Uplink
CLAUDE_JSON = pathlib.Path.home() / ".claude.json"
OUT_DIR = pathlib.Path(__file__).resolve().parent
def load_core_creds():
cfg = json.loads(CLAUDE_JSON.read_text())
env = cfg.get("mcpServers", {}).get("mikromcp", {}).get("env", {})
user = env.get("CRS326_CORE_USER") or env.get("ROUTER_USER")
pw = env.get("CRS326_CORE_PASS") or env.get("ROUTER_PASS")
if not user or not pw:
raise SystemExit("CRS326_CORE_USER/PASS nicht in ~/.claude.json mikromcp.env gefunden")
return user, pw
def ssh_cmd(user, pw, remote_command):
return [
"sshpass", "-p", pw,
"ssh",
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-o", "PubkeyAuthentication=no",
f"{user}@{CORE_IP}",
remote_command,
]
def scp_cmd(user, pw, remote_file, local_file):
return [
"sshpass", "-p", pw,
"scp",
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-o", "PubkeyAuthentication=no",
f"{user}@{CORE_IP}:{remote_file}",
str(local_file),
]
def main():
user, pw = load_core_creds()
ts = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
backup_name = f"crs326-core-pre-cutover-{ts}"
print(f"crs326-core Backup via SSH {user}@{CORE_IP}")
create_cmd = ssh_cmd(
user, pw,
f'/system backup save name="{backup_name}" dont-encrypt=yes; '
f'/export file="{backup_name}"'
)
print(f" Erzeuge Backup/Export auf crs326-core ...")
subprocess.run(create_cmd, check=True)
local_backup = OUT_DIR / f"{backup_name}.backup"
local_export = OUT_DIR / f"{backup_name}.rsc"
print(f" Lade {backup_name}.backup herunter ...")
subprocess.run(scp_cmd(user, pw, f"{backup_name}.backup", local_backup), check=True)
print(f" Lade {backup_name}.rsc herunter ...")
subprocess.run(scp_cmd(user, pw, f"{backup_name}.rsc", local_export), check=True)
backup_size = local_backup.stat().st_size
export_size = local_export.stat().st_size
print(f" OK: {local_backup.name} ({backup_size} Bytes)")
print(f" OK: {local_export.name} ({export_size} Bytes)")
if backup_size < 1000:
print(" WARN: Backup-Datei auffällig klein!")
sys.exit(1)
if __name__ == "__main__":
main()