Zum Inhalt springen

Einen UniFi-Standort in einem Prompt gegen NetBox abgleichen

Eine Quelle der Wahrheit bleibt nur so lange wahr, bis jemand das Netz anfasst. Tausch einen Access Point, lass DHCP eine neue Management-Adresse vergeben, häng in einem Hinterzimmer ein Gerät dazu — und der Controller hat sofort recht, während NetBox still und leise falsch liegt. Das fällt niemandem auf, der nicht jede Woche zehn Geräte von Hand vergleicht, also driftet die Dokumentation, und das Vertrauen in sie driftet mit.

Die übliche Antwort ist ein Sync-Skript. Stattdessen sollte ein Satz genügen:

Keine Pipeline. Kein ausbuchstabiertes Feld-Mapping. Wenn sich das Netz nächsten Monat ändert, läuft derselbe Satz erneut.

Der Agent spricht über ein Gateway mit zwei Backends: Er liest den Live-UniFi-Controller und liest und schreibt NetBox, und jeder Aufruf läuft über die auditierte, policy-gesicherte Oberfläche des Gateways statt über eine Shell oder ein gespeichertes Admin-Token. Im Code Mode ist es ein kurzes Skript, das das Modell selbst schreibt, und seine Form ist ein Vergleich, kein Massenimport:

const site = "23by5njv"; // desc: "HOME"
const live = await toolmesh.unifi_list_devices({ site });
for (const ap of live) {
const model = MODEL_MAP[ap.model] ?? ap.model; // U7PG2 -> "UniFi AC Pro Gen2", etc.
const [nb] = await toolmesh.netbox_list_devices({ name: ap.name });
if (!nb) { // new device -> create
await toolmesh.netbox_create_device({ name: ap.name, device_type: typeId(model), role: AP, site: LII });
log("created", ap.name);
} else if (mgmtIp(nb) !== ap.ip) { // moved address -> update one field
await toolmesh.netbox_update_device(nb.id, { primary_ip4: ipId(ap.ip) });
log("updated mgmt IP", ap.name, mgmtIp(nb), "->", ap.ip);
} else {
log("ok", ap.name); // already matches -> write nothing
}
}

Zwei Dinge darin lohnen den genaueren Blick, weil Abgleich schwieriger ist als Import.

Die Modellcodes sind keine Produktnamen. Um zu entscheiden, ob ein UniFi-Gerät schon zu seinem NetBox-Eintrag passt, muss der Vergleich das normalisieren, was der Controller meldet. UniFi liefert jedes Gerät als internen Code: U7PG2 ist der UAP-AC-Pro (die 7 hat nichts mit Wi-Fi 7 zu tun), UAL6 ein U6 Lite, U7NHD ein nanoHD, U6EXT ein U6 Extender, U7PRO tatsächlich ein U7 Pro, und US8P60 der US-8-60W-Switch. Diese Übersetzungstabelle ist der Unterschied zwischen „dieser AP ist bereits korrekt” und einem Duplikat, das neben dem echten Gerät entsteht.

„Lass korrekte Einträge in Ruhe” ist die tragende Klausel. Jeder Zweig oben ist ein Get-or-Create-or-Update. Ein passendes Gerät erzeugt einen Schreibvorgang von nichts. Genau das macht den Satz sicher genug, ihn planmäßig gegen ein NetBox laufen zu lassen, das bereits Daten enthält — statt eines einmaligen Imports, den man nie zu wiederholen wagt.

Wir haben ihn gegen den Live-Controller laufen lassen. Hier das echte Ergebnis, kein konstruiertes:

reconcile HOME -> NetBox (site LII)
AP-Kueche U7NHD 192.168.100.43 ok
AP-Praxis U7NHD 192.168.100.46 ok
AP-Labor U7PG2 192.168.100.41 ok
AP-Buero U7NHD 192.168.100.44 ok
AP-Garage U7PG2 192.168.100.42 ok
AP-U7-Pro Heizungsraum U7PRO 192.168.100.154 ok
AP-U6-Extender U6EXT 192.168.100.199 ok
AP-Mobil UAL6 192.168.100.47 ok
AP-Wohnzimmer U7PG2 192.168.100.45 ok
UbSwi-01 US8P60 192.168.100.183 ok
-- 10 checked, 10 matched, 0 written.

Neun Access Points über fünf Modelle und ein PoE-Switch, jedes Gerät bereits vorhanden, jede Management-IP noch passend, das IoT-VLAN und das Management-Prefix bereits angelegt. Nettoänderung an NetBox: null.

Das ist die Antiklimax, die eigentlich der Punkt ist. Ein Agent, der nichts schreibt, wenn nichts gedriftet ist, ist genau die Eigenschaft, mit der man ihn auf ein CMDB richten kann, das einem wichtig ist, und ihn wieder und wieder laufen lässt. Man muss ihm nicht vertrauen, dass er die Daten nicht zerschießt. Man kann ihm beim Nichtstun zusehen.

Der Drift-Fall ist der, der sich auszahlt. Wäre AP-Garage auf .142 statt .42 aufgetaucht, hätte der Lauf eine Zeile ausgegeben, updated mgmt IP AP-Garage 192.168.100.42 -> 192.168.100.142, genau diesen einen Eintrag geschrieben und die anderen neun auf ok gelassen. Das Audit-Log zeigt exakt, welches Feld sich an welchem Gerät geändert hat, ausgeführt von welchem Konto — ohne dass man einen Diff durchlesen muss.

Die Geräte abzugleichen hält das Inventar ehrlich. Die Device-Type-Definitionen dahinter prüft ein Name-und-IP-Lauf nicht, und sie driften auf ihre eigene Weise. Dieser Lauf war ein guter Moment, eine davon zu erwischen: Der U7 Pro im Heizungsraum war — korrekt — mit einem Device-Type verknüpft, dessen Slug passte, aber dieser Typ war noch als „AC Pro, 802.11ac” beschriftet. Das Gerät passte, also hätte der Abgleich es nicht markiert; das Label war schlicht falsch, denn Modellcode und die Wi-Fi-7-Firmware des Geräts sagen etwas anderes. Wir haben es in derselben Sitzung korrigiert. Eine Quelle der Wahrheit ist nur so gut wie die Definitionen, auf die sie zeigt, und die verdienen einen bewussten Blick statt einer Annahme.

Stimmt, und es funktioniert. Der Unterschied ist, dass es hier kein Skript zu pflegen gibt. Die Mapping-Tabelle lebt im Kontext des Modells, die Anweisung ist Klartext, und „gleich auch die Client-Liste ab” oder „lass die Gast-SSID aus” ist eine Änderung am Satz, nicht an einer Codebasis und ihren Tests. Wenn Ubiquiti in einem Controller-Update einen Modellcode umbenennt, korrigierst du eine Zeile eines Prompts.

Die UniFi- und NetBox-Connectoren sind offen: DADL, eine YAML-Datei pro API, hinter einem Apache-2.0-Gateway. Klone es und richte es auf deinen eigenen Controller und dein eigenes NetBox. Wenn du es lieber als Managed-Instanz betreiben willst, sprich mit uns unter [email protected]. Fragen und Erfahrungsberichte sind in den GitHub Discussions willkommen.