- nautobot/tp45-agh-firewall.py (neu): pflegt die vier `TP5-E2: LAN->AGH dns*`- Regeln im `local_config_context_data.firewall_filter` (add/remove, Dry-Run default, Unified Diff). Bewusst NICHT `ingest-core-firewall.py` -- das ist ein RE-BASELINE-Werkzeug und wuerde die Aenderung mit sich selbst absegnen. - gc-configplan.py: Jobs `tp45-agh-fw` (add + move vor die Log-Regel; `caddy` dahinter, damit der AGH-Block zusammenbleibt) und `tp45-agh-dhcp`. Fix in `create_plans`: unter derselben `change_control_id` liegen nach einem zweiten Lauf zwei Plaene. Die Auswahl folgte der REST-Reihenfolge und traf den ALTEN -> der config_set-Guard brach ab, obwohl der neue Plan korrekt erzeugt war. Jetzt gewinnt der juengste; die alten bleiben Historie. Fund: `find where dst-address=<bare-IP>` matcht in RouterOS nie (nur `~` als Regex). Die Zeile stand als "Verifikation" in docs/transaktionen/TP23-agh-firewall.commands.rsc:7 und hat dort seit dem 2026-08-25 immer `0` gemeldet, ohne je etwas zu pruefen. Gleiche Klasse wie `nc -z` (09-01): eine Pruefung, die 0 meldet, sieht wie "fehlt" aus, nicht wie "kaputt". Abnahme: Reihenfolge am Geraet (vier neue Regeln vor dem Drop), Compliance 140/140 / 0 Drifts mit `RouterOS - ip-firewall-filter` (ordered, frisch bewertet, `actual` traegt die neuen Zeilen), `dig` vom VLAN10-Client gegen .30/.31/.36/.37 alle beantwortet. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Nautobot-Tooling
ingest-baseline.py
Idempotenter Basisdaten-Ingest (Locations, Rollen, Device-Types, Devices, VLANs/Prefixes, ap1-Interfaces + IPs, WLAN-Modelle). Bildet den Ist-Zustand ab, nicht den Zielzustand.
export NAUTOBOT_TOKEN=... # aus docs/nas-vlan20/stacks/210-nautobot.yml (gitignored)
python3 nautobot/ingest-baseline.py # Dry-Run
python3 nautobot/ingest-baseline.py --apply # schreiben
API-Fallen (teuer gelernt)
tagged_vlansliest sich per REST alsnull, obwohl korrekt gespeichert. Verifikation nur über GraphQL:POST /api/graphql/mit{interfaces(device:"ap1"){name mode untagged_vlan{vid} tagged_vlans{vid}}}.- Relationen kommen ohne
?depth=1ohnedisplay→ ein voll gepflegtes Device sieht leer aus. LocationType.content_typesmussipam.vlangroup/ipam.vlan/ipam.prefix/dcim.controllerenthalten, sonst 400 beim Anlegen.parent_interfacenur für virtuelle Interfaces — WLAN-VAPs können ihren Master nicht als Parent tragen.WirelessNetworkhat kein VLAN-Feld; die VLAN-Bindung hängt anwireless/controller-managed-device-group-wireless-network-assignmentsund braucht zwingend einenController(bei uns logisch,controller_deviceleer — kein CAPsMAN).RadioProfile.channel_widthwill Integer ([20,40,80]), nicht"20mhz".- IP↔Interface läuft über
/ipam/ip-address-to-interface/.
ingest-wifi-access-list-rule.py
Legt ComplianceFeature + ComplianceRule wifi-access-list an (Plattform RouterOS,
match_config=/interface wifi access-list). Idempotent, Dry-Run ist Standard.
export NAUTOBOT_TOKEN=...
python3 nautobot/ingest-wifi-access-list-rule.py --apply
Warum es die Rule braucht: ohne sie erzeugen Eintraege in der access-list keinen Drift
und lassen sich nicht als Config-Transaktion fuehren -- gc-transaktion.py verlangt eine
ComplianceRule als --scope. docs/tp25-pin-s22.sh schreibt dort bei jedem Messrundgang, und
TP25 Fund E (signal-range-Boden) landet dauerhaft dort.
Der Soll-Zustand ist leer und steht als Kommentarblock [6c] im Template. Waehrend eines
Messlaufs ist der Drift erwartet und gehoert als offene Transaktion gefuehrt.