TP45 Schicht B2 Ruecknahme: VLAN30 vollstaendig auf den Stand vor dem Umzug

Zwei Christoph-Direktiven am 2026-09-21, in dieser Reihenfolge:
1. "mach die shellys sofort rueckgaengig" -- HA bindet sie per IP ein.
2. "wir sollten alle vlan30-aenderungen rueckgaengig machen" -- Sperr-Risiko
   Nuki: ein Fehlgriff haette aussperren koennen.

Neu in tp45-leases.py: die Tabellen SHELLY/REVERT und das Kommando `revert`
(nur die 26 Shellys) sowie `restore` (alles zurueck auf vor B2, inkl.
NSPanelPro-Reservierung und wiederangelegter CCU-Zeile). In gc-configplan.py
die Jobs tp45-v30-revert und tp45-v30-restore; _lease_move nimmt jetzt ein
tag, damit die :log-Zeilen den Job unterscheidbar machen.

Abnahme: Geraete-Read (alle 36 Adressen zurueck, NSPanelPro ohne Reservierung,
CCU .232 wieder da) und --refresh-compliance 145/145, 0 Drifts.

Zwei Funde, die als Merkregel bleiben: `make-dynamic` gibt es in RouterOS 7.24
nicht (der Rueckweg ist `remove`), und der GC-Plan-Status beweist nichts --
beide Laeufe meldeten Failed, waehrend der Job-:log alle 36 Statements
protokollierte.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-21 22:22:22 +02:00
parent 180c5b6c57
commit 3f774b7b17
2 changed files with 226 additions and 4 deletions

View File

@@ -618,7 +618,7 @@ def _tp45_leases_module():
return mod
def _lease_move(mac, soll):
def _lease_move(mac, soll, tag="tp45-v30"):
"""Eine Umzugs-Zeile — EINZEILIG und klammer-vollstaendig (E1015-Lehre, s. _pool_set).
Der Selektor wird GEZAEHLT: ein `find where`, das 0 trifft, sieht wie
@@ -653,8 +653,8 @@ def _lease_move(mac, soll):
arg = f'[find where mac-address={mac}]'
return (f':if ([:len [/ip dhcp-server lease {find}]] = 1) do={{ '
f'/ip dhcp-server lease set {arg} address={soll}; '
f':log info ("tp45-v30: {mac} -> {soll}") }} '
f'else={{ :log info ("tp45-v30: {mac} NICHT EINDEUTIG -- nicht angefasst") }}')
f':log info ("{tag}: {mac} -> {soll}") }} '
f'else={{ :log info ("{tag}: {mac} NICHT EINDEUTIG -- nicht angefasst") }}')
def job_tp45_v30_leases():
@@ -723,6 +723,134 @@ def job_tp45_v30_leases():
return [(["crs326-core"], "\n".join(lines), "TP45-v30-leases")]
def job_tp45_v30_revert():
"""RUECKNAHME 2026-09-21: die 26 Shelly-Reservierungen zurueck auf ihre ALTEN Adressen.
Christoph-Direktive, sofort: *„mach die shellys sofort rueckgaengig! das gibt
sonst ein riesen durcheinander mit home assistant"*. Grund: HA hat die Shellys
mit ihren alten Adressen eingebunden -- ein Umzug zerreisst die Integrationen.
🔴 **NUR die Shellys** — das war die erste, eng gefasste Ruecknahme. Die
uebrigen 10 Umzuege aus `job_tp45_v30_leases` (NukiBridge .11, P750W .13,
DTUBI .14, NukiSmartlock .177, SwitchBot .178, Twinkly .179, lichterketten
.190/.191, Withings .195/.196) und die beiden Sonderfaelle (CCU-Leiche
entfernt, NSPanelPro statisch auf .12) liess er unangetastet.
⚠️ **Noch am selben Tag ueberholt:** auf die weitergehende Direktive
(„wir sollten ALLE vlan30-aenderungen rueckgaengig machen" — Sperr-Risiko
Nuki) folgte `job_tp45_v30_restore`, der auch diese 10 plus beide
Sonderfaelle zurueckdreht. Dieser Job bleibt als der engere, zuerst
gelaufene Schritt bestehen.
Die Tabelle steht in `tp45-leases.py` als `REVERT` (abgeleitet aus `MOVES`,
gefiltert auf die 26 Shelly-MACs) -- NICHT hier, aus demselben Grund wie
`MOVES`: eine zweite Kopie laeuft still auseinander.
⚠️ **Reihenfolge:** erst `python3 nautobot/tp45-leases.py revert --apply`
(Soll zurueck), DANN diesen Job. Umgekehrt meldet GC sofort wieder Drift --
das Geraet waere richtig und das Soll falsch.
Abnahme: Geraete-Read `proplist=address,comment where server=dhcp-v30` -- die
26 Shellys muessen auf .202-.226/.233 stehen, die uebrigen 13 Eintraege
unveraendert. Danach `--refresh-compliance`, erwartet 145/145 und 0 Drifts.
"""
mod = _tp45_leases_module()
lines = [_lease_move(mac, alt, tag="tp45-v30-revert")
for mac, (_neu, alt) in sorted(mod.REVERT.items())]
# Abnahme-Zeile: die alten Baender muessen wieder voll sein, die neuen leer.
# Bewusst ein Bereichs-Paar statt aller 26 Adressen -- .202/.233 rahmen den
# alten Shelly-Block ein, .151/.176 den neuen.
lines.append(
':log info ("tp45-v30-revert: .151=" . [:tostr [:len [/ip dhcp-server lease '
'find where address=10.40.30.151]]] . " .176=" . [:tostr [:len '
'[/ip dhcp-server lease find where address=10.40.30.176]]] . " .202=" . '
'[:tostr [:len [/ip dhcp-server lease find where address=10.40.30.202]]] . '
' " .233=" . [:tostr [:len [/ip dhcp-server lease '
'find where address=10.40.30.233]]])')
return [(["crs326-core"], "\n".join(lines), "TP45-v30-shelly-revert")]
def job_tp45_v30_restore():
"""VOLLRUECKNAHME 2026-09-21: alle VLAN30-Aenderungen aus B2 zuruecknehmen.
Christoph-Direktive: *„wir sollten alle vlan30-aenderungen rueckgaengig machen.
du koenntest mich auf diesem wege aus meiner wohnung aussperren."*
Was B2 am 2026-09-21 getan hat, und was hier einzeln zurueckgedreht wird:
1. **36 Reservierungs-Adressen** verschoben -> hier ALLE 36 zurueck auf ihre
`MOVES`-Ist-Adresse (idempotent: die 26 Shellys stehen schon dort, ein
zweites `set` auf dieselbe Adresse ist ein No-Op).
2. **CCU-Leiche entfernt** (`BC:24:11:8E:0A:57`, .30.232) -> wieder angelegt,
wortgleich der Stand vor B2. Ob die Leiche spaeter geloescht werden soll,
ist eine **eigene** Entscheidung, nicht Teil dieser Ruecknahme.
3. **SONOFF-NSPanelPro auf statisch .12 gepinnt** -> Reservierung entfernt;
vor B2 hatte es keine (reiner DHCP-Client auf .249). `remove` ist der
Weg, weil RouterOS 7.24 kein `make-dynamic` kennt (s. u.).
⚠️ **Reihenfolge:** erst `python3 nautobot/tp45-leases.py restore --apply`
(Soll zurueck), DANN diesen Job.
🔴 **Warum die Eile berechtigt war:** die Reservierung eines Geraets mit
**lokaler** IP zu aendern, bewegt das Geraet nicht — aber bei einem Geraet,
das **DHCP** nutzt, haette die naechste Erneuerung es auf die neue Adresse
gezogen, und IP-gebundene Einbindungen (Home Assistant: Nuki, Shelly, …)
waeren gerissen. Dass hier nichts passiert ist, war Glueck, kein Entwurf.
Abnahme: Geraete-Read der v30-Leases + ARP-Band-Zaehlung; danach
`--refresh-compliance`, erwartet 145/145 und 0 Drifts.
"""
mod = _tp45_leases_module()
lines = [_lease_move(mac, ist, tag="tp45-v30-restore")
for mac, (ist, _soll) in sorted(mod.MOVES.items())]
# NSPanelPro: die statische Reservierung ENTFERNEN -- vor B2 hatte es keine,
# es war ein reiner DHCP-Client (dynamische Lease auf .249).
#
# 🔴 `make-dynamic` gibt es in RouterOS 7.24 NICHT: `/ip dhcp-server lease
# make-dynamic` antwortet `bad command name make-dynamic` (live gemessen
# 2026-09-21). Der erste Versuch dieses Jobs ist daran gestorben und hat den
# Plan auf `Failed` gesetzt, OBWOHL alle uebrigen Statements gelandet sind —
# genau die Klasse „Completed/Failed beweist nichts, das Geraet ist die
# Wahrheit". `make-static` existiert (B2 hat es benutzt), das Gegenstueck
# fehlt; der Rueckweg ist `remove`.
#
# ⚠️ Das Panel haelt .12 noch bis zum Lease-Ablauf (~20 h, `status=bound`,
# `active-address=10.40.30.12`) und faellt danach in den Pool zurueck.
# Das ist eine von B2 erzeugte Uebergangserscheinung, keine neue Aenderung.
for add in mod.ADDS:
mac = re.search(r"mac-address=(\S+)", add).group(1)
find = f'find where mac-address={mac}'
arg = f'[find where mac-address={mac}]'
lines.append(f':if ([:len [/ip dhcp-server lease {find}]] = 1) do={{ '
f'/ip dhcp-server lease remove {arg}; '
f':log info ("tp45-v30-restore: NSPanelPro-Reservierung entfernt") }} '
f'else={{ :log info ("tp45-v30-restore: NSPanelPro hat keine Reservierung") }}')
# CCU-Leiche wieder anlegen -- nur wenn sie fehlt.
for mac in sorted(mod.REMOVE):
find = f'find where mac-address={mac}'
lines.append(f':if ([:len [/ip dhcp-server lease {find}]] = 0) do={{ '
f'/ip dhcp-server lease {mod.RESTORE_ADD}; '
f':log info ("tp45-v30-restore: CCU-Leiche wieder angelegt") }} '
f'else={{ :log info ("tp45-v30-restore: CCU-Leiche ist schon da") }}')
# Abnahme: die alten Baender muessen voll sein, die neuen leer.
lines.append(
':log info ("tp45-v30-restore: alt .202=" . [:tostr [:len [/ip dhcp-server lease '
'find where address=10.40.30.202]]] . " .233=" . [:tostr [:len '
'[/ip dhcp-server lease find where address=10.40.30.233]]] . " .244=" . '
'[:tostr [:len [/ip dhcp-server lease find where address=10.40.30.244]]] . '
' " neu .11=" . [:tostr [:len [/ip dhcp-server lease '
'find where address=10.40.30.11]]] . " .151=" . [:tostr [:len '
'[/ip dhcp-server lease find where address=10.40.30.151]]] . " .177=" . '
'[:tostr [:len [/ip dhcp-server lease find where address=10.40.30.177]]])')
return [(["crs326-core"], "\n".join(lines), "TP45-v30-restore")]
JOBS = {
"drift-cleanup": job_drift_cleanup,
"mgmt-acl": job_mgmt_acl,
@@ -732,6 +860,8 @@ JOBS = {
"tp45-agh-dhcp": job_tp45_agh_dhcp,
"tp45-pool-v20": job_tp45_pool_v20,
"tp45-v30-leases": job_tp45_v30_leases,
"tp45-v30-revert": job_tp45_v30_revert,
"tp45-v30-restore": job_tp45_v30_restore,
}