Alles in diesem Beitrag lief gegen ein NetBox 4.7.2 auf meinem eigenen Schreibtisch, mit einem echten Bestand darin: ein Standort, ein Schrank, vierzehn Geräte, verkabelt, mit Strom versorgt und adressiert über drei Kunden hinweg. Jeder Screenshot ist diese Instanz, und jede Fehlermeldung ist eine, die ich wirklich bekommen habe.
Es geht in der Reihenfolge, in der du dem Ganzen begegnen würdest. Was das Ding ist, warum du eins haben willst, der Fork, von dem du innerhalb einer Woche Suche hören wirst, wie du eins aufsetzt, die Reihenfolge, in der du es füllen musst, und dann, was du wieder herausbekommst. Die Anpassungs- und Erweiterungsarbeit steht am Ende, weil davon nichts Sinn ergibt, bevor du die Form dessen gesehen hast, was du erweiterst.
Was NetBox ist
NetBox ist eine Datenbank mit einer sehr bestimmten Meinung darüber, woraus ein Netzwerk besteht, und eine Webanwendung darauf. Es ist eine Django-Anwendung auf PostgreSQL, es ist seit der Freigabe durch DigitalOcean im Juni 2016 Open Source unter Apache 2.0, und das Projekt wird heute von NetBox Labs gemeinsam mit einem Team freiwilliger Maintainer betreut.12
Darunter sind es 149 Modelle in zehn Anwendungen, erreichbar über 146 REST-Endpunkte und einen GraphQL-Endpunkt. Gezählt an der Instanz, die ich dafür gebaut habe, nicht von einer Feature-Seite abgelesen:
| Anwendung | Modelle | Anwendung | Modelle |
|---|---|---|---|
| dcim | 56 | virtualization | 7 |
| extras | 23 | tenancy | 6 |
| ipam | 18 | wireless | 3 |
| circuits | 11 | users | 7 |
| vpn | 10 | core | 8 |
Sechsundfünfzig davon sind DCIM, die physische Schicht: Standorte, Locations, Racks, Gerätetypen, Geräte und jede Art von Port, Bay und Kabelabschluss, die ein Gerät haben kann. Achtzehn sind IPAM. Der Rest deckt Leitungen, Tunnel und IKE-Policies, virtuelle Maschinen und Cluster, Funkstrecken, Mandantenfähigkeit und die Maschinerie ab, die das Ganze erweiterbar macht.
Die Zahl ist nicht der Punkt. Die Joins sind es, und der schnellste Weg, das zu sehen, ist der Bildschirm, für den NetBox am bekanntesten ist.
Fang mit dem Schrank an, denn das ist der Bildschirm, der das Ding verkauft.

Diese Elevation wird aus den Daten gezeichnet, nicht hochgeladen. Jedes Gerät ist dort, weil irgendetwas sagt, dass es diese Höheneinheiten belegt, in diese Richtung zeigend, und die Farben kommen von der Rolle, die du ihm gegeben hast. Die Platzauslastung liest 28,6 %, weil NetBox das ausgerechnet hat. Niemand pflegt diese Zahl.
Zwei Dinge folgen daraus, die mehr wert sind als das Bild.
Du kannst fragen, welche Einheiten frei sind, und bekommst eine Antwort, mit der du arbeiten kannst. Du kannst Einheiten auch reservieren, bevor irgendetwas darin eingebaut ist, und das ist der Unterschied zwischen dem Verkauf von Platz, den du hast, und dem Verkauf von Platz, von dem du glaubst, du hättest ihn.
Was es bewusst nicht tun wird
Ein Produkt, das weiß, was es nicht ist, ist seltener als eines, das alles schlecht macht, und NetBox’ eigene Dokumentation ist da unverblümt. Es bietet kein Netzwerk-Monitoring, keinen DNS-Dienst, kein RADIUS, kein Konfigurationsmanagement und kein Facility-Management.1
Wichtiger noch: Es hält den gewünschten Zustand deines Netzwerks, nicht seinen Betriebszustand, und die Dokumentation sagt, der automatisierte Import des lebenden Netzwerkzustands sei „strongly discouraged“, weil jeder Datensatz zuerst von einem Menschen geprüft werden sollte.1
Das ist die Entscheidung, auf der alles andere aufbaut, und es ist die, über die Leute streiten. Das Argument geht so: Eine Source of Truth sollte doch die Wahrheit sein, also entdecke das Netzwerk und lade es hinein. Die Antwort ist, dass ein entdecktes Netzwerk dir sagt, was da ist, und was da ist, schließt jeden Fehler ein, den irgendwer jemals gemacht hat. Ein Switch-Port, der 2021 im falschen VLAN gelassen wurde, ist eine Tatsache. Er ist keine Absicht.
NetBox hält die Absicht. Dein Monitoring hält die Wirklichkeit. Die interessante Zahl ist die Differenz zwischen beiden, und eine Differenz kannst du nicht aus einer einzigen Eingabe berechnen.
Der andere Grundsatz steht genauso klar da: Wenn du zwischen einer relativ einfachen Achtzig-Prozent-Lösung und einer viel komplexeren vollständigen wählen kannst, nimm die einfache.1 Das wirst du beim ersten Mal spüren, wenn du etwas modellieren willst, was es nicht modelliert, und es gibt gegen Ende einen ganzen Block von Abschnitten darüber, was du dann tust.
| NetBox tut | NetBox tut nicht |
|---|---|
| Festhalten, was da sein soll | Abfragen, was da ist |
| Das vorgesehene VLAN für einen Port halten | Dir sagen, dass der Port unten ist |
| Sagen, welchem Kunden ein Prefix gehört | Es ihm in Rechnung stellen |
| Die Konfiguration eines Geräts aus einem Template rendern | Sie auf das Gerät schieben |
| Leitung, Provider und Commit nachhalten | Die Leitung überwachen |
| Sagen, in welchem Rack ein Gerät steht, und in welcher U | Den Schrank aufmachen |
Lies die rechte Spalte als Liste der Werkzeuge, die du weiterhin brauchst. Lies sie falsch, und du wirst versuchen, NetBox zu allen davon zu machen, und genau so wird aus einer Source of Truth noch ein System, dem niemand traut.
Warum du eins brauchst
Frag einen Managed-Service-Provider, wo die verbindliche Aufzeichnung des Netzwerks eines Kunden liegt, und du bekommst eine Antwort. Frag zwei seiner Techniker getrennt, und du bekommst zwei.
Einer zeigt auf eine Tabelle. Einer zeigt auf ein Diagramm, das zuletzt von jemandem gespeichert wurde, der 2023 gegangen ist. Noch jemand sagt, die Firewall-Konfiguration sei die Dokumentation, was wenigstens ehrlich ist, denn eine Konfiguration beschreibt tatsächlich, was eine Kiste tut. Sie beschreibt nur nicht, warum, oder wer das wollte, oder welcher der vier Kunden hinter dieser Kiste für die Regel bezahlt.
Das Versagen ist nie der Tag, an dem du merkst, dass die Aufzeichnung falsch ist. Es ist der Tag, an dem jemand sie braucht.
| Der Moment | Was du vorlegen musst | Was es kostet, wenn du es nicht kannst |
|---|---|---|
| Ein Verlängerungsgespräch | Eine Einzelaufstellung, was die monatliche Gebühr kauft | Das Angebot des Mitbewerbers ist aufgeschlüsselt, weil er nachgezählt hat |
| Ein Techniker kündigt | Alles, was er wusste, aufgeschrieben | Sechs Monate Herausfinden, ein Ticket auf einmal |
| Ein Kunde geht | Eine Beschreibung seines eigenen Bestands | Drei Wochen, um sie zusammenzutragen, und eine Referenz, die er ehrlich geben wird |
| Ein Prüfer stellt eine Scope-Frage | Welche Systeme personenbezogene Daten halten und wo sie physisch stehen | Einer Aufsichtsbehörde wurde etwas erzählt, was sich später als unwahr erweist |
| Eine Migration braucht einen Preis | Eine Zählung dessen, was wirklich da ist | Du bietest auf eine Schätzung und frisst die Differenz |
Nichts davon ist exotisch. Das ist Dienstag.
Was du dafür bekommst, ist keine Dokumentation. Dokumentation ist etwas, das du schreibst und dann nicht mehr pflegst. Was du bekommst, ist eine Datenbank, die sich weigert, einen Widerspruch zu halten, und die Fragen beantwortet, die ihr vorher niemand zu stellen gedacht hat. Weiter unten in diesem Beitrag gibt es einen Schrank, der sich als 28,6 Prozent voll mit Technik und 90,7 Prozent voll mit Strom entpuppt. Niemand hat sich vorgenommen, das zu finden. Es fiel daraus heraus, einmal eine Wattzahl an einem Gerätetyp eingetragen zu haben.
Der ehrliche Vorbehalt kommt mit dazu, und der letzte Abschnitt handelt davon. Eine Aufzeichnung ist nur so viel wert, wie es dich kostet, sie richtig zu halten. Aber die Alternative ist ein Unternehmen, das sich selbst nicht beschreiben kann, und der Erste, der das herausfindet, ist üblicherweise ein Kunde.
Das andere: Nautobot
Darauf stößt du innerhalb von etwa einer Woche Suche, also lohnt es sich zu wissen, was passiert ist.
2021 hat Network to Code NetBox geforkt und das Ergebnis Nautobot genannt. Kein Soft Fork und keine Distribution: ein harter Fork, der seit fünf Jahren auseinanderläuft. Seine eigenen v1.0-Release-Notes beschreiben ihn als „a divergent fork of NetBox 2.10“, das Repository wurde am 19. Februar 2021 erstellt und v1.0.0 kam am 26. April 2021.3
Die genannten Gründe stehen im eigenen Blog und sind in ihren Worten lesenswerter als in meinen. Drei Dinge trieben es an. Sie wollten Enterprise-Support verkaufen: „We need to offer high-touch support models with Service Level Agreements (SLAs) we can guarantee. We need flexibility to offer Long-term Support (LTS) for customers who can’t upgrade at the pace of a fast moving open source project.“ Sie wollten, dass die Source of Truth im Zentrum einer Automatisierungsplattform sitzt, statt Dokumentation zu bedienen. Und „there became a growing divergence in our vision about what a Source of Truth for networking should look like and how to get there“.4
Dem ersten Grund muss man das Datum anhängen, denn er ist nicht mehr wahr. Im Februar 2021 gab es kein Unternehmen hinter NetBox, das dir irgendetwas verkaufen konnte. NetBox Labs wurde erst 2023 gegründet, als Spin-out aus NS1, nachdem IBM es übernommen hatte, mitgegründet von NetBox’ eigenem Lead Maintainer.5
Und es ist kein Dritter, der ein Geschäft auf dem Projekt eines anderen aufgebaut hat. NetBox Labs ist der Sachwalter von NetBox: Die Dokumentation des Projekts selbst sagt „the open source project is stewarded by NetBox Labs and a team of volunteer maintainers“.1 Sie verkaufen NetBox Enterprise für selbst verwaltete Installationen, hosten es als NetBox Cloud für dich und bieten Support rund um die Uhr.6
„you cannot buy support for NetBox“ war also eine faire Aussage, als Network to Code forkte, und ist heute keine faire Aussage mehr. Hinter beiden Projekten steht ein kommerzielles Unternehmen, das etwas unterschreibt, und im Fall von NetBox ist dieses Unternehmen dasjenige, das das Projekt betreut.
Die Zeile, die die meisten übersehen, ist die nächste, und sie ist der Grund, warum das keine schmutzige Geschichte ist: „the NetBox project team suggested that we should consider forking.“
Zivilisierter wird ein Fork kaum. Zwei Gruppen wollten Unterschiedliches, sagten das über einen längeren Zeitraum und trennten sich, statt sich um eine Codebasis zu streiten. Beide Hälften sind weiterhin Apache 2.0. Niemand hat etwas genommen, worauf er kein Recht hatte.
Wozu der Fork wirklich da war
Die Release-Notes von Nautobot 1.0 listen auf, was es gegenüber NetBox 2.10 hinzugefügt hat, und die Liste erzählt das Argument besser als jeder Blogbeitrag:3
| Was Nautobot 2021 hinzufügte | Wo NetBox heute steht |
|---|---|
| GraphQL-Unterstützung | NetBox hat es |
| Git-Integration als Datenquelle | NetBox hat es, als synchronisierte Datenquellen |
| Single Sign-on | NetBox hat es |
| Secrets | NetBox hat es über ein Plugin |
| Scripts und Reports zu Jobs zusammengefasst | NetBox zieht Scripts in 4.7 in ein Plugin aus |
| Custom Fields auf allen Modellen | NetBox hat breite Custom-Field-Unterstützung |
| Data-Validation-Plugin-API | NetBox hat Custom Validation Rules |
| Anpassbare Status als Datenbankobjekte | NetBox’ Status sind weiterhin ein Python-Choice-Set |
| Benutzerdefinierte Beziehungen zwischen Modellen | NetBox hat kein Äquivalent |
| UUID-Primärschlüssel | NetBox nutzt Integer-Schlüssel |
| Verbesserungen der Plugin-API | NetBox’ Plugin-Framework ist seither stark gewachsen |
Die obere Hälfte ist größtenteils zusammengewachsen. Mehreres, was Nautobot 2021 ausgeliefert hat, kam danach in NetBox an, und das ist üblicherweise, was passiert, wenn zwei Projekte dieselben Probleme öffentlich lösen.
Die unteren drei sind nicht zusammengewachsen, und sie sind architektonisch, nicht kosmetisch. Ich habe heute beide Codebasen geprüft, statt den Notizen von 2021 zu trauen.
Nautobots Status ist ein Datenbankmodell, in seinem eigenen Quellcode beschrieben als „Model for database-backend enum choice objects“, also ist ein Status eine Zeile, die jemand in der UI hinzufügen kann. NetBox’ Status kommen aus einem Python-Choice-Set, weshalb der Abschnitt weiter unten einen hinzufügt, indem er configuration.py bearbeitet und neu startet. Nautobot hat Relationship- und RelationshipAssociation-Modelle, du kannst also eine Beziehung zwischen zwei bestehenden Objekttypen definieren, ohne Code zu schreiben. NetBox’ Antwort auf dieses Problem ist ein Plugin, und das ist der letzte Block von Abschnitten in diesem Beitrag. Und Nautobots Primärschlüssel sind UUIDs, wo NetBox’ Integer sind.
Sie haben auch das getan, wofür sie geforkt haben. Heute veröffentlicht Nautobot 3.2.x und 2.4.x am selben Tag, was eine echte Long-Term-Maintenance-Linie neben der aktuellen ist, und das war einer der drei genannten Gründe.
Welches von beiden
Zuerst die ehrlichen Zahlen. NetBox hat 21.625 Sterne und 3.133 Forks; Nautobot hat 1.617 und 422.7 Bei beiden wurde innerhalb der letzten zwei Tage gepusht, beide sind Apache 2.0, und hinter beiden steht inzwischen ein kommerzielles Unternehmen, das Support und Hosting verkauft.
Dieser Abstand ist kein Urteil über Qualität. Er spiegelt fünf Jahre Vorsprung und die Tatsache, dass die meisten, die eine Source of Truth brauchen, zuerst NetBox finden. Aber er entscheidet das, was meist mehr zählt als Features: wie viele Plugins, Integrationen, Ansible-Module, Forenantworten und Kollegen du für das findest, was du wählst.
Also: Wenn du ein Inventar und eine Source of Truth willst, die andere Systeme lesen, und du das größte Ökosystem und die leichteste Einstellung willst, ist die Antwort NetBox, und darum geht es im Rest dieses Beitrags. Wenn dein Grund für eine Source of Truth speziell ist, Automatisierung daraus zu betreiben, oder wenn du Status und Beziehungen von deinem Team definiert haben willst statt von einer Konfigurationsdatei und einem Neustart, dann sieh dir Nautobot richtig an, bevor du entscheidest.
Lass dir keines von beiden allein über Support verkaufen. Beide Seiten haben das inzwischen abgedeckt, und die Unterschiede, die in fünf Jahren noch da sind, sind die in der Tabelle oben.
Was du nicht tun solltest, ist eines zu wählen, weil dir jemand gesagt hat, das andere sei tot. Keines ist es, und beide haben diese Woche noch Releases ausgeliefert.
Eins zum Laufen bringen
Zwei Wege, und ich habe beide dafür durchgespielt. Container, wenn du es in zwanzig Minuten laufen haben willst, Pakete auf einem Host, wenn es tragend wird.
Der Container-Stack
Die Community pflegt netbox-docker, und das ist die schnelle Antwort:8
git clone -b release https://github.com/netbox-community/netbox-docker.git
cd netbox-docker
tee docker-compose.override.yml <<'EOF'
services:
netbox:
ports:
- 8000:8080
EOF
docker compose pull
docker compose up
Das gibt dir die Anwendung, PostgreSQL, Redis und einen Background-Worker, miteinander verdrahtet. Ich habe dasselbe von Hand unter podman gebaut, um die Teile zu sehen, und die Teile sind es wert, sie zu kennen, denn zwei von ihnen erwischen Leute:
| Container | Macht was | Wenn du ihn weglässt |
|---|---|---|
netbox | Die Django-Anwendung hinter gunicorn | nichts funktioniert |
postgres | Die Datenbank, 15 oder neuer | nichts funktioniert |
redis / valkey | Zwei Datenbanken: eine für Tasks, eine fürs Caching | nichts funktioniert |
netbox-worker | rqworker, der die Task-Queue abarbeitet | Webhooks feuern nie, Background-Jobs laufen nie, und nichts warnt dich |
netbox-housekeeping | Das periodische Aufräumen | Changelog-Einträge verfallen nie |
Diese vierte Zeile ist die entscheidende. Ohne Worker sieht alles gesund aus. Event Rules stapeln sich und bleiben liegen.
Zwei Dinge haben mich beim ersten Lauf gebissen, und keines steht in einer Fehlermeldung, nach der du suchen würdest.
Die erste Migration dauert lange. Keine Minute. Auf dieser Maschine waren es mehrere, weil NetBox 4.7 django-mptt durch PostgreSQL ltree ersetzt und dabei jede hierarchische Tabelle neu aufbaut. Der Container sitzt einfach da und wendet Migrationen an. Lass ihn in Ruhe.
Ohne API_TOKEN_PEPPERS kannst du kein v2-API-Token erstellen, und das einzige Anzeichen ist eine Warnung im Log:
UserWarning: API_TOKEN_PEPPERS is not defined. v2 API tokens cannot be used.
Setz mindestens eines, mindestens fünfzig Zeichen lang, bevor du anfängst zu suchen, warum die API dich abweist.
Auf einem Host, aus den Paketen
Der dokumentierte Weg ist auf Ubuntu 24.04 getestet. Ich habe ihn auf einem frischen von Anfang bis Ende durchgespielt, und es lief darauf hinaus:
| Komponente | Was 24.04 mir gab | Was NetBox 4.7 braucht |
|---|---|---|
| PostgreSQL | 16.15 | 15 oder neuer |
| Redis | 7.0.15 | 6.0 oder neuer |
| Python | 3.12.3 | 3.12, 3.13 oder 3.14 |
| Django | 6.1.1 | kommt mit NetBox |
| NetBox | v4.7.2 |
Diese Mindestversionen sind NetBox’ eigene, und 4.7 hat sowohl die PostgreSQL- als auch die Redis-Untergrenze angehoben.9
Der ganze Job sind fünf Schritte.
# 1. the services
sudo apt install -y postgresql redis-server
sudo -u postgres psql -c "CREATE DATABASE netbox;"
sudo -u postgres psql -c "CREATE USER netbox WITH PASSWORD 'something-you-generated';"
sudo -u postgres psql -c "ALTER DATABASE netbox OWNER TO netbox;"
sudo -u postgres psql -d netbox -c "GRANT CREATE ON SCHEMA public TO netbox;"
# 2. the build dependencies
sudo apt install -y python3 python3-pip python3-venv python3-dev build-essential libxml2-dev libxslt1-dev libffi-dev libpq-dev libssl-dev zlib1g-dev git
# 3. the application, at a release tag rather than at main
sudo mkdir -p /opt/netbox && cd /opt/netbox
sudo git clone https://github.com/netbox-community/netbox.git .
sudo git checkout v4.7.2
sudo adduser --system --group netbox
sudo chown -R netbox /opt/netbox/netbox/media/ /opt/netbox/netbox/scripts/ /opt/netbox/netbox/reports/
# 4. the configuration: five values, no more
cd /opt/netbox/netbox/netbox/
sudo cp configuration_example.py configuration.py
python3 /opt/netbox/netbox/generate_secret_key.py # run it twice
sudo $EDITOR configuration.py
# 5. let the upgrade script do the rest
sudo /opt/netbox/upgrade.sh
Diese fünf Werte sind ALLOWED_HOSTS, DATABASES, REDIS, SECRET_KEY und API_TOKEN_PEPPERS. Erzeuge die letzten zwei getrennt und verwende nicht eines für das andere. Lass den Key-Generator also zweimal laufen und füge die Ergebnisse in verschiedene Zeilen ein.
upgrade.sh baut die virtuelle Umgebung, installiert jede Python-Abhängigkeit, führt die Migrationen aus, baut die Dokumentation für die Offline-Nutzung und sammelt die statischen Dateien ein. Wenn es auf einer frischen Kiste fertig ist, gibt es eine Warnung aus, die alarmierend aussieht und es nicht ist:
WARNING: No existing virtual environment was detected. A new one has
been created. Update your systemd service files to reflect the new
Python and gunicorn executables. (If this is a new installation,
this warning can be ignored.)
Dann ein Superuser, und der läuft so:
source /opt/netbox/venv/bin/activate
cd /opt/netbox/netbox && python3 manage.py createsuperuser
Für alles Echte setz gunicorn davor statt runserver. Die Konfiguration und die Unit-Dateien liegen schon im Repository, und das ist das Detail, das man kennen sollte, weil Leute ihre eigenen schreiben:
sudo cp /opt/netbox/contrib/gunicorn.py /opt/netbox/gunicorn.py
sudo cp -v /opt/netbox/contrib/*.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now netbox netbox-rq
netbox.service fährt gunicorn auf 127.0.0.1:8001, netbox-rq.service fährt den Worker, und nginx oder Apache steht davor, um TLS zu terminieren und /static auszuliefern. Zwei Dienste, und der zweite ist derselbe Worker, den der Container-Stack braucht.
Die Reihenfolge, in der du es füllen musst
Ein frisches NetBox ist eine leere Datenbank mit Meinungen, und die erste Stunde damit geht meist dafür hin, herauszufinden, welche das sind. Du gehst hin, um ein Gerät anzulegen, und es lässt dich nicht.

Diese roten Sternchen sind die ganze Lektion. NetBox hält nichts fest, bevor die Dinge existieren, an denen es hängt, und es ist nicht bockig: Ein Gerät ohne Typ ist eine Zeile, die keine der Fragen beantworten kann, für die ein Gerät da ist.
Statt zu raten, habe ich also das Modell gefragt, welche Fremdschlüssel wirklich zwingend sind, und dann versucht, jede Regel über die API zu brechen, um zu sehen, was zurückkommt.
POST /api/dcim/device-types/ no manufacturer
{"manufacturer": ["This field is required."]}
POST /api/dcim/devices/ no device type
{"device_type": ["This field is required."]}
POST /api/dcim/interfaces/ no device
{"device": ["This field is required."]}
POST /api/ipam/aggregates/ no RIR
{"rir": ["This field is required."]}
POST /api/circuits/circuits/ no provider, no type
{"provider": ["This field is required."], "type": ["This field is required."]}
POST /api/virtualization/virtual-machines/ nothing at all
{"__all__": ["A virtual machine must be assigned to a site, cluster, or device."]}
Jedes einzelne hat verweigert und genau gesagt, was fehlte. Hier dieselbe Information als Liste von Voraussetzungen:
| Zum Anlegen von | Vorher nötig | Optional, aber du willst es vorher |
|---|---|---|
| Tenant | nichts | eine Tenant-Gruppe |
| Region, Site group | nichts | ein übergeordnetes gleicher Art, sie verschachteln |
| Site | nichts | eine Region, eine Site group, ein Tenant |
| Location | eine Site | eine übergeordnete Location, ein Tenant |
| Rack type | ein Manufacturer | |
| Rack | eine Site | eine Location, Rack group, Rack role, Rack type, Tenant |
| Device type | ein Manufacturer | |
| Device role | nichts | eine übergeordnete Device role, sie verschachteln |
| Device | eine Device role, ein Device type, eine Site | ein Rack und eine Position, eine Platform, ein Tenant |
| Interface und jede andere Komponente | ein Device | |
| Cable | zwei Dinge zum Abschließen | ein Tenant |
| Aggregate | ein RIR | ein Tenant |
| VLAN | nichts | eine VLAN group, eine Role, ein Tenant |
| Prefix | nichts | eine Site, ein VLAN, eine Role, ein VRF, ein Tenant |
| IP address | nichts | ein Interface, dem es zugewiesen wird, ein Tenant |
| Circuit | ein Provider und ein Circuit type | ein Tenant |
| Circuit termination | ein Circuit | eine Site zum Landen |
| Cluster | ein Cluster type | eine Cluster group, eine Site, ein Tenant |
| Virtual machine | eine Site, ein Cluster oder ein Device | eine Platform, ein Tenant |
| VM interface | eine Virtual machine |
Die zweite Spalte ist die, die dich etwas kostet. Prefixes, IP-Adressen, VLANs und Tenants brauchen gar nichts, es hält dich also nichts davon ab, sie an Tag eins in beliebiger Reihenfolge anzulegen. Ob sie dann nützlich sind, ist eine andere Frage, denn eine Adresse ohne Interface dahinter ist eine Zeile in einer Liste, und ein Gerät ohne Tenant ist ein Gerät, das du später noch einmal bearbeitest.
Die Reihenfolge, in der man wirklich arbeitet
Daraus ergibt sich eine Abfolge. Arbeite sie ab, und nichts verweigert dir je etwas.
Zuerst die Dinge, an denen alles andere hängt. Nichts davon ist aufregend, und alles davon ist billig falsch zu machen.
- Tenant-Gruppen, dann Tenants. Mach die vor allem anderen. Achtundzwanzig Modelle nehmen einen Tenant, darunter Site, Location und Rack, wenn die Kunden also noch nicht existieren, kannst du sie nicht unterwegs mitstempeln und wirst später massenhaft nachbearbeiten.
- Regionen und Site groups. Beide optional, beide in sich verschachtelbar, und sie sind unabhängig voneinander. Regionen sind für Geografie, Site groups für Funktion, und du kannst eines, beides oder keines nutzen.
- Sites. Die Wurzel von fast allem. Eine Site braucht nichts, weshalb sie das Erste ist, was du wirklich anlegen kannst.
- Locations. Die brauchen eine Site, und sie verschachteln, eine Halle mit Reihen mit Pods ist also ein Modell in drei Ebenen.
- Rack roles und Rack groups. Beide optional. Rack groups sind flach und stehen neben Locations als zweite Achse, was für Reihen und Pods praktisch ist.
- Manufacturers, dann Rack types. Ein Rack type braucht einen Manufacturer. Lass Rack types weg, wenn du die Schränke selbst nicht modellierst.
- Racks. Die brauchen eine Site. Wenn du einem auch eine Location gibst, muss diese Location zu dieser Site gehören, und NetBox prüft das.
Dann der Hardware-Katalog, nicht die Hardware. Bevor du ein einziges Gerät anlegen kannst, brauchst du Manufacturers, Device types, Device roles und, in der Praxis, Platforms.
- Manufacturers. Einige hast du vielleicht schon aus Schritt 6, denn Rack types brauchen sie auch. Module types ebenso.
- Device types, jeder mit einem Manufacturer.
- Device roles, die verschachteln, und Platforms.
Das ist der Schritt, den Leute überspringen, und es ist der Schritt, der entscheidet, wie viel Tipparbeit der Rest des Jobs kostet. Ein Device type trägt seine eigenen Interfaces, Ports und Bays als Templates, jedes Gerät, das du daraus anlegst, kommt also mit den richtigen Komponenten schon daran. Mach den Typ einmal richtig, und vierzig davon einzuracken sind vierzig Namen.
Platform ist der Sonderfall in dieser Liste, denn ein Gerät braucht streng genommen keine. Mach es trotzdem jetzt. Sie trägt später das Config-Template und den NAPALM-Treiber, und sie auf einem bestehenden Bestand nachzutragen ist derselbe Abend, den du für Tenants aufgewendet hättest.
Dann die Technik selbst.
- Devices. Rolle, Typ und Site sind alle zwingend. Rack und Position sind optional, und wenn du sie angibst, müssen sie zur Site passen.
- Komponenten, falls der Device type sie nicht schon geliefert hat.
- Kabel, zwischen den Komponenten.
Dann die Adressierung, denn eine Adresse will ein Interface, auf dem sie lebt, und das Interface existiert erst nach Schritt 12.
- RIRs, dann Aggregates.
- Prefix- und VLAN-Rollen, VRFs, VLAN groups, dann VLANs.
- Prefixes, dann die einzelnen IP-Adressen.
Dann die kommerzielle Schicht.
- Providers, Provider accounts und Circuit types.
- Circuits, dann terminiere sie auf Sites.
Und der virtuelle Bestand, der den physischen spiegelt.
- Cluster types und Cluster groups, dann Cluster.
- Virtuelle Maschinen, die eine Site, ein Cluster oder ein Device brauchen, dann deren Interfaces.
Die Schritte 1 bis 10 sind ein Nachmittag und fühlen sich wie Verwaltung an. Daran ist nichts glamourös. Sie sind auch der Nachmittag, der entscheidet, ob Schritt 11 einen Morgen oder zwei Wochen dauert.
Die Regeln, die später beißen
Pflichtfelder sind die leichte Hälfte, denn sie scheitern sofort und sagen dir, warum. Die Leute erwischt es bei den Konsistenzregeln, und die feuern erst, wenn du genug Daten hast, um dir selbst zu widersprechen:
device at Leeds, put in a Manchester rack
{"rack": ["Rack MCR1-A07 (A07) does not belong to site Leeds Edge."]}
device at Leeds, in a Manchester location
{"location": ["Location Hall 2 does not belong to site Leeds Edge."]}
rack at Leeds, in a Manchester location
{"__all__": ["Assigned location must belong to parent site (Leeds Edge)."]}
a second device in a unit that is already taken
{"position": ["U39.0 is already occupied or does not have sufficient
space to accommodate this device type: MX204 (1.0U)"]}
Die letzte ist die, auf die ich zeigen würde, wenn jemand fragt, warum man sich das alles antut. NetBox weiß, dass das Gerät 1U hoch ist, weiß, was im Schrank steht, und lässt dich nicht zwei Dinge im selben Raum festhalten. Deine Tabelle lässt dich das den ganzen Nachmittag machen und sagt kein Wort, und du findest es heraus, wenn jemand mit einem Karton in den Händen in der Halle steht.
Nichts davon ist konfigurierbar, und nichts davon sollte es sein. Das ist der Unterschied zwischen einer Aufzeichnung und einem Wunsch.
Im Einsatz: Was jeder Bildschirm dir gibt
Mit eingepflegtem Bestand ist hier, was du wirklich wieder herausbekommst. Alles Folgende ist dieselbe Instanz: ein Schrank, vierzehn Geräte, verkabelt und adressiert über drei Kunden hinweg.
Ein Gerät sind seine Komponenten
Klick in einen dieser Hypervisoren hinein, und der interessante Tab ist nicht die Übersicht, sondern die Interfaces.

Lies eine Zeile quer. Das Interface, seine Geschwindigkeit, was du darüber geschrieben hast, die Adresse darauf, das Label am Kabel und der Port am anderen Ende. Das ist eine Abfrage, und es ist die Antwort auf die Frage, die alle wirklich stellen, nämlich „was hängt hier dran“.
Beachte, wo die Adresse sitzt. Sie ist auf eno1, nicht auf dem Server. Das klingt nach Pedanterie, genau bis du eine Kiste mit einem Management-Interface, zwei Daten-Interfaces und einem Loopback hast und jemand fragt, welche Adresse für sie antwortet. Ein Modell, das Adressen an Geräte hängt, kann es dir nicht sagen. Dieses kann es, und es kann auch den völlig gewöhnlichen Fall von vier Adressen auf einem Interface halten.
Dem Kabel folgen
Kabel enden an Komponenten, nicht an Geräten. Ein Interface an einem Ende, ein Interface am anderen, oder ein Front Port, ein Rear Port, eine Power Outlet, ein Circuit Termination. Das ist das Detail, auf dem das ganze Feature ruht, und es ist der Grund, warum die Interface-Liste oben das andere Ende in einer eigenen Spalte ausgeben konnte, ohne dass man es ihr gesagt hat.
Modelliere ein Kabel von Gerät zu Gerät, und du hast ein Bild gemalt. Schließ es an den Ports ab, und NetBox kann es ablaufen.

Hier ein Hop, weil es ein Direct-Attach-Kabel ist. Setz Patchpanels in die Mitte, und es läuft sie ab, Panel für Panel, und sagt dir, was am anderen Ende einer Strecke durch drei Schränke hängt. Das ist der Job, der sonst eine Taschenlampe und jemanden braucht, der das andere Ende einer Tonsonde hält.
Die Verfolgung gibt außerdem den vollständigen Ort jedes Endes aus. Standort, Halle, Schrank, Seite, Höheneinheit. Wenn du je in einem Telefonat versucht hast, einem Remote-Hands-Techniker zu erklären, welche Kiste er ansehen soll, ist diese Zeile der ganze Wert.
Adressen, als Baum statt als Tab
IPAM ist die Hälfte, für die Leute kommen.

Die Einrückung wird aus den Adressen selbst berechnet. Du sagst NetBox nicht, dass 10.20.20.0/24 in 10.20.0.0/16 sitzt, es rechnet das aus, und es wird es weiter ausrechnen, wenn nächstes Jahr jemand ein /26 mitten hinein setzt.
Jede Zeile trägt die Dinge, nach denen du wirklich filterst: das VLAN, auf das sie zeigt, die Rolle und den Kunden, dem sie gehört. Die Auslastung wird auch berechnet.
Öffne eines, und du bekommst die Adressen darin, und die Lücken.

Diese grünen Zeilen sind der freie Raum, in einer Linie mit dem belegten gezeigt. Es gibt einen API-Aufruf, der dir die nächste freie Adresse aus einem Prefix gibt, und das ist das eine Stück IPAM-Automatisierung, das sich sofort bezahlt, denn es ist das, was Leute sonst machen, indem sie auf eine Tabelle schielen und hoffen.
Ein Interface nimmt so viele Adressen, wie du willst, aus beiden Familien, und du benennst je eine als primäre des Geräts.

Zwei IPv4 und zwei IPv6 auf einem Port, was ein gewöhnlicher Dienstag ist und etwas, das ein gerätezentriertes Modell überhaupt nicht ausdrücken kann.
Trag Adressen mit der Maske des Netzes ein, auf dem sie liegen, nicht als /32. NetBox nimmt 10.20.20.11/32 an und sortiert es unter das richtige Prefix, denn die Zugehörigkeit wird aus der Host-Adresse ermittelt. Was es nicht tut, ist dich hinterher zu überstimmen: Die Maske wird genau so gespeichert, wie sie getippt wurde, und so an alles weitergegeben, was sie liest, ein /32 auf einer LAN-Adresse rendert also ein /32 in die Gerätekonfiguration. Die Stelle, an der das beißt, ist drei Monate später in einem Config-Template, nicht heute im Formular.
Eine Installation, drei Kunden
Die meisten Objekte in NetBox können einem Tenant zugewiesen werden. Ein Unternehmen nutzt das für Geschäftsbereiche. Wenn du Managed Services verkaufst, legst du einen pro Kunde an.

Dieses Panel rechts ist die Antwort auf „was hat dieser Kunde“, und es hat sich selbst zusammengestellt. Kein Report, keine Tabelle, kein Fragen des Technikers, der es gebaut hat.
Es lohnt sich, bei der Bedeutung von Tenancy genau zu sein, denn es an Tag eins falsch zu machen bedeutet ein Jahr Entwirren später. Ein Tenant heißt, das Objekt ist diesem Kunden zugeordnet. Ein Router, der nur ihn bedient, bekommt seinen Tenant. Eine Firewall, die vier von ihnen bedient, gehört keinem von ihnen, bekommt also keinen, und die Beziehung geht woandershin. Dazu weiter unten mehr, denn das ist der Punkt, an dem die meisten merken, dass sie etwas Eigenes hinzufügen müssen.
Trag die Zahlen am Gerätetyp ein
Das ist der Schritt, der ein Inventar von etwas trennt, das Fragen beantwortet, und er kostet etwa zehn Minuten pro Gerätetyp.
Ein Device type kann sein Gewicht tragen und, über seine Power-Port-Templates, seine Leistungsaufnahme. Trag die einmal am Typ ein, und jedes Gerät, das du je daraus anlegst, erbt sie. Lass sie weg, und NetBox sagt dir bereitwillig, dass ein Schrank zu 28,6 % voll ist, und nichts weiter.

Ich habe allen fünf Typen hier ein Gewicht gegeben, jedem zwei Power-Port-Templates mit einer maximalen und einer zugewiesenen Aufnahme, dem PDU-Typ einen Inlet und zwölf Outlets, dann ein Power Panel und zwei Feeds in den Schrank angelegt und das Ganze verkabelt: jedes PSU1 jedes Geräts an PDU A, jedes PSU2 an PDU B und den Inlet jeder PDU an ihren Feed.

Dann verändert sich die Rack-Seite vollständig.

Platzauslastung 28,6 %. Stromauslastung 90,7 %.
Dieser Schrank ist zu einem Drittel voll mit Technik und nahezu ohne Strom, und das ist eine Tatsache über deinen Bestand, die keine Tabelle jemals von sich aus beisteuern wird. Es ist auch die Tatsache, die entscheidet, ob die nächste Bestellung dort oder woanders eingeracked wird, und sie fiel aus Daten heraus, die du einmal eingetragen hast, an den Typen.
Zwei Dinge, die es auf null stehen lassen
Ich hatte zuerst 0,0 %, zweimal, und beide Ursachen sind es wert, sie zu kennen, denn keine erzeugt einen Fehler.
Die Outlets müssen auf den Inlet verweisen. Eine Power Outlet an einer PDU hat ein Feld power_port, das auf den vorgelagerten Port am selben Gerät zeigt. Lass es leer, und die Kette ist gebrochen: NetBox hat keine Möglichkeit zu wissen, dass diese zwölf Outlets von jenem Inlet versorgt werden, also aggregiert nichts.
Lass die Aufnahmefelder des Inlets leer. Das ist das Gegenintuitive. NetBox berechnet die Aufnahme eines Power Ports aus dem, was daran hängt, nur wenn seine beiden eigenen Aufnahmefelder leer sind:
if self.allocated_draw is None and self.maximum_draw is None:
...aggregate the downstream power ports...
# otherwise
return {'allocated': self.allocated_draw or 0, ...}
Ich hatte hilfsbereit maximum_draw: 7400 am PDU-Inlet gesetzt, denn darauf ist die PDU ausgelegt. NetBox hat mir daher geglaubt, allocated_draw als nicht gesetzt genommen und null gemeldet. Lösch beides, und es rechnet es aus:
mcr1-pdu-a INPUT -> allocated 5340 VA, maximum 8480 VA, across 12 outlets
mcr1-pdu-b INPUT -> allocated 5340 VA, maximum 8480 VA, across 12 outlets
feed MCR1-A07-A: available 5888 VA (230 V x 32 A x 80% max utilisation)
RACK power utilisation: 90.7 %
RACK weight: 166.6 kg of 900 kg
Die Regel lautet also: Trag echte Zahlen an den Blättern ein und lass die Zwischenports leer, damit NetBox sie zusammenzählen kann. Ein administrativ gesetzter Wert gewinnt immer gegen den berechneten, was korrektes Verhalten und an einer PDU genau das Falsche ist.
Kühlung, die neu ist
Version 4.7 hat Kühlung zu DCIM hinzugefügt, und sie kam mit der Hälfte an, die teuer wird. Ein Rack trägt eine Cooling capability von Luft, hybrid oder flüssig und eine Kapazität in Kilowatt; ein Device type trägt eine Cooling method. Darüber sitzen Cooling Sources für die Kältemaschinen und CRAC-Einheiten, Cooling Feeds, die einen Kreislauf zu einem Rack darstellen, und Intake- und Outflow-Komponenten an den Geräten selbst für Kühlplatten und Verteiler.
Der Schrank oben liest Hybrid, 15,00 kW, weil ich das dem Rack so gesagt habe. Wenn du dieses Jahr flüssiggekühlte Technik annimmst, ist das ein Modell für das, was du gerade in einer Tabelle hältst.
Eine Installation, viele Kunden
Tenancy ist der Grund, warum ein Provider ein NetBox betreiben kann statt eines pro Kunde, und es lohnt sich zu verstehen, was es tut und, wichtiger, was es nicht tut.
Achtundzwanzig von NetBox’ Modellen tragen ein Tenant-Feld. Sites, Locations, Racks und Rack-Reservierungen. Devices, Kabel und Virtual Device Contexts. Prefixes, IP-Adressen, Ranges, Aggregates, VLANs, VLAN groups, VRFs, Route Targets, ASNs. Circuits und Circuit groups. Cluster und virtuelle Maschinen. Tunnel, L2VPNs, Wireless LANs und Links. Power Feeds und Cooling Feeds.
Das ist jedes abrechenbare Substantiv. Setz es konsequent, und „was hat dieser Kunde“ hört auf, eine Ermittlung zu sein.
Aber ein Tenant ist ein Label, kein Schloss. Er sagt, das Objekt ist diesem Kunden zugeordnet. Er hält niemanden, der sich anmelden kann, davon ab, das Ganze zu lesen, was innerhalb einer Firma in Ordnung ist und überhaupt nicht mehr, sobald ein Kunde ein Konto hat.
Zwei Dinge folgen daraus, und das zweite ist das, was Leute falsch machen.
Ein Tenant heißt zugeordnet. Ein Router, der nur einen Kunden bedient, bekommt dessen Tenant. Eine Firewall, die vier bedient, gehört keinem davon, bekommt also nichts, und du hältst die Beziehung stattdessen an dem fest, was du tatsächlich verkauft hast. Einen Tenant auf geteilte Technik zu zwingen macht jeden Report, der auf Tenancy aufbaut, still und leise falsch.
Und Zugriff ist ein völlig eigener Mechanismus.
In den Permissions sitzt die Isolation
NetBox’ Object Permissions nehmen eine JSON-Einschränkung, und die Einschränkung ist ein Django-ORM-Filter. Sie verengt das Queryset, bevor irgendetwas daraus gebaut wird, jede Ansicht, jeder Export, jeder API-Aufruf und jede Suche wird also damit verengt.

Drei Felder machen die Arbeit. Die Objekttypen, für die sie gilt, die Aktionen, die sie gewährt, und diese Einschränkung unten. Alles andere ist Buchhaltung.
Hier die Geräteliste als Administrator.

Und hier dieselbe URL, dieselbe Installation, angemeldet als der Kunde.

Zwei Zeilen statt vierzehn, und schau auf das linke Menü. Es ist auf die vier Dinge zusammengefallen, die dieses Konto anfassen darf. Das hat niemand konfiguriert. Die Navigation wird aus denselben Permissions gebaut, ein Kunde sieht also nie einen Link auf etwas, das ihn abweisen würde.
Die zwei Arten, nein zu sagen
Das ist das Detail, das man kennen sollte, denn die zwei Abweisungen bedeuten Unterschiedliches, und beide sind gewollt.
| Das Token des Kunden fragt nach | Es bekommt |
|---|---|
/api/dcim/devices/ | 200, eine Zeile von zwei |
/api/dcim/devices/1/, sein eigenes | 200 |
/api/dcim/devices/2/, das von jemand anderem | 404 |
/api/tenancy/tenants/ | 403 |
/api/dcim/sites/, nie gewährt | 403 |
| gar kein Token | 403 |
404 heißt, der Typ ist deiner, aber diese Zeile nicht. Die Einschränkung hat sie aus dem Queryset entfernt, für die Anfrage existiert sie also nicht. Ein 403 dort würde bestätigen, dass sie existiert, und einem neugierigen Kunden erlauben, deinen Bestand durch Ablaufen der IDs zu zählen.
403 heißt, der Typ war nie deiner. Tenants und Sites wurden nie gewährt, sie verweigern also direkt, und der Kunde kann nicht aufzählen, wer sonst auf der Plattform ist.
Dann lass sie schreiben
Nur lesen ist der leichte Fall. Die echte Frage ist, ob man einem Kunden mit Schreibrechten diese zutrauen kann, ich habe also change unter derselben Einschränkung gewährt und nach dem Weg nach draußen gesucht.
| Versuch | Ergebnis |
|---|---|
| Sein eigenes Gerät bearbeiten | 200, gespeichert |
| Das Gerät eines anderen Kunden bearbeiten | 404 |
| Sein eigenes Gerät bearbeiten und auf den Tenant des anderen Kunden verschieben | 403 |
| Sein eigenes Gerät löschen | 403, delete wurde nie gewährt |
Die dritte Zeile ist die entscheidende. Das eigene Gerät dem Tenant eines anderen zuzuweisen ist der naheliegende Ausbruch, denn das Objekt ist bei Ankunft der Anfrage innerhalb deiner Einschränkung und danach außerhalb. NetBox prüft die Einschränkung gegen den Zustand, in dem das Objekt zurückbleiben würde, also verweigert es. Ich habe das Gerät zurückgelesen, statt dem Statuscode zu trauen, und der Tenant hatte sich nicht bewegt.
Das ist das Loch, das selbstgebaute Mandantenfähigkeit meist offen lässt, und gefunden wird es normalerweise von einem Kunden und nicht von einem Test.
Variablen, die vererben müssen
Hier die Unterscheidung, die Leute falsch machen, und sie richtig zu machen erspart viel Bearbeitung.
Ein Custom Field ist ein Wert an einem Objekt. Du setzt ihn pro Objekt, und dort bleibt er. Gut für eine Tatsache über das Ding selbst: ein Asset Tag, eine Supportvertragsnummer, ein Inbetriebnahmedatum.
Ein Config Context ist ein Wert, der an einer Eigenschaft hängt und den alles erbt, was auf diese Eigenschaft passt. Gut für eine Variable, die durchrieseln soll: deine NTP-Server, deine Syslog-Ziele, deine SNMP-Community, deine DNS-Domain, dein Management-VLAN, dein Backup-Fenster.
Wenn du dich dabei erwischst, dasselbe Custom Field an vierzig Geräten auf denselben Wert zu setzen, wolltest du einen Config Context.

Ein Context ist beliebiges JSON, und er kann an eine Region, Site group, Site, Location, einen Device type, eine Rolle, Platform, ein Cluster, einen Cluster type, eine Cluster group, Tenant group, einen Tenant oder ein Tag gehängt werden. Tenant steht in dieser Liste, und für einen Provider heißt das: Eine Tatsache, die für einen Kunden überall gilt, folgt ihm auf jedes Gerät, das du je für ihn anlegst.
Die Zusammenführung erfolgt pro Schlüssel, und das Gewicht entscheidet, wer jeden einzelnen gewinnt.

Lies das rechte Panel gegen das linke. Die Region liefert vier Schlüssel mit Gewicht 1000. Die Site liefert einen Schlüssel mit Gewicht 2000. Der gerenderte Context behält Domain, NTP-Server und Community der Region unangetastet und nimmt den Syslog-Server der Site, denn das ist der einzige Schlüssel, um den irgendetwas gestritten hat.
Du schreibst die Ausnahme, nicht eine frische Kopie von allem mit der Ausnahme darin. Das ist der ganze Wert, und deshalb skaliert das, wo eine Variablendatei pro Gerät es nicht tut.
Lokaler Context gewinnt gegen alles darüber
Der Vererbungsstapel hat eine Spitze, und das ist das Objekt selbst. Local Context Data an einem Gerät gewinnt gegen jeden Source Context, der darauf zutrifft, egal welche Gewichte.
Ich habe das an mcr1-core-01 gesetzt:
{"syslog_servers": ["10.20.10.99"], "note": "this box logs somewhere else"}
und sein gerenderter Context wurde:
{
"note": "this box logs somewhere else",
"domain": "mcr1.example.net",
"ntp_servers": ["172.16.10.22", "172.16.10.33"],
"snmp_community": "n0rthwest",
"syslog_servers": ["10.20.10.99"]
}

Der lokale Syslog-Server hat sowohl das Site-Override mit Gewicht 2000 als auch die Region mit Gewicht 1000 geschlagen. Alles, worüber er nichts gesagt hat, wurde weiter vererbt, die Domain, die NTP-Server und die Community kamen also unangetastet durch, und der neue Schlüssel wurde einfach hinzugefügt.
Das ist die Notluke für die eine Kiste, die wirklich anders ist, und das Panel sagt dir klar, dass es alle Source Contexts überschreibt. Wenn du dich dabei erwischst, das an vielen Geräten statt an einem zu nutzen, hast du eine Eigenschaft gefunden, die diese Geräte teilen, und wolltest einen Context, der darauf zugeschnitten ist.
Drei Fallen
Ein Context ohne Zuweisung ist global. Leg einen an und vergiss, ihn irgendetwas zuzuweisen, und er gilt für jedes Gerät und jede virtuelle Maschine, die du hast. Das ist dokumentiertes Verhalten und gelegentlich das, was du willst. Es ist auch lautlos.
Schlüssel dürfen keine Bindestriche haben, wenn du sie einfach erreichen willst. Context-Daten sind JSON, ntp-servers ist also völlig legal, aber Jinja-Variablennamen sind keine JSON-Schlüssel. {{ ntp-servers }} wird als Subtraktion geparst und löst UndefinedError: 'ntp' is undefined aus. Schreib {{ ntp_servers }} gegen Daten mit Bindestrich, und du bekommst einen leeren String, HTTP 200 und nirgends eine Warnung. Nimm Unterstriche in den Schlüsseln, und das naheliegende Template funktioniert.
Ein Profil kann die Tippfehler stoppen. Ein Config-Context-Profil gruppiert verwandte Contexts und erzwingt beim Speichern ein JSON-Schema auf deren Daten. Ich habe einem ein Schema gegeben, das syslog_servers als Array von IPv4-Strings verlangt, und dann die zwei Fehler gemacht, die Leute wirklich machen:
{"syslog-server": ["10.1.1.1"]} singular, by accident
400 Data does not conform to profile schema:
'syslog-servers' is a required property
{"syslog_servers": [...], "syslog_port": 99999}
400 Data does not conform to profile schema:
99999 is greater than the maximum of 65535
Abgewiesen dort, wo jemand sie gemacht hat, und nicht vierhundert Geräte später.
Die Konfiguration rendern
Context-Daten plus ein Jinja-Template geben dir eine Konfigurationsdatei. Das Gerät ist als device im Scope, sein zusammengeführter Context ist als gewöhnliche Variablen im Scope, und du kannst seine Komponenten ablaufen.

Alles auf dieser Seite kam von woanders her, und das ist der Punkt:
| Zeile | Woher sie kam |
|---|---|
host-name mcr1-core-01 | vom Gerät |
domain-name mcr1.example.net | vom regionalen Config Context |
location "Manchester DC1 / MCR1-A07 / U39" | von der Site, dem Rack und der Position darin |
server 172.16.10.22 | vom regionalen Context, Gewicht 1000 |
host 10.20.10.99 any notice | vom eigenen lokalen Context des Geräts, das sowohl Site als auch Region schlägt |
family inet address 10.20.10.11/24 | von der Adresse auf diesem Interface, mit der Maske wie getippt |
Das Template wird über das Gerät, dann die Rolle, dann die Platform aufgelöst, und die Anfrage scheitert, wenn keines der drei eines hat. Du weist also einer Platform einmal ein Template zu, und jedes Gerät, das diese Software fährt, rendert daraus, sofern nicht seine Rolle oder das Gerät selbst etwas anderes sagt. Dafür ist eine Platform da: das Betriebssystem oder die Softwarefamilie des Herstellers, nicht die Hardware.
NetBox rendert. Es schiebt nicht. Die Ausgabe auf die Kiste zu bekommen ist die Aufgabe deiner Automatisierung, und an dem Tag, an dem ein Template-Fehler sonst vierhundert Geräte umkonfiguriert hätte, wirst du froh sein, dass das zwei verschiedene Systeme sind.
Plugins
Ein Plugin ist eine Django-Anwendung, die neben NetBox installiert wird. Es kann Modelle hinzufügen, Seiten hinzufügen, beide APIs erweitern, Inhalte in bestehende Templates einspeisen, Navigation hinzufügen, Background-Job-Queues hinzufügen und weitere Django-Apps laden. Es gibt sehr wenig, was es nicht kann, denn darunter ist es einfach Django.
Eines zu installieren sind vier Befehle und ein Neustart:
source /opt/netbox/venv/bin/activate
pip install netbox-topology-views netbox-qrcode
# add the package names to PLUGINS in configuration.py, then
python3 manage.py migrate
python3 manage.py collectstatic --no-input
sudo systemctl restart netbox netbox-rq
Im Container-Stack kommen dieselben Pakete in plugin_requirements.txt und du baust das Image neu. In beiden Fällen gilt: pinne die Versionen und prüf vorher die Kompatibilitätsmatrix, denn ein Plugin, das einem NetBox-Release nicht nachgekommen ist, verweigert den Start der ganzen Anwendung, statt sich selbst zu deaktivieren.

Der veröffentlichte Katalog listet 31. Diese sind die, die man kennen sollte, mit der Lizenz, unter der jedes wirklich ausgeliefert wird:
| Plugin | Was es tut | Lizenz |
|---|---|---|
| DNS | Zonen, Records und Nameserver als Source of Truth | MIT |
| BGP | Sessions, Communities und Routing-Policies | Apache 2.0 |
| Topology Views | Grafische Topologiekarten, aus deinen Kabeln gebaut | Apache 2.0 |
| Floorplan | Grafische Standort- und Location-Karten | LGPL 3.0 |
| QR Code | Codes auf Racks, Geräten und Kabeln, für Asset-Labels | Apache 2.0 |
| ACLs | Access-Lists und Regeln | Apache 2.0 |
| Prometheus SD | Liefert Prometheus seine Hostliste direkt aus NetBox | MIT |
| Documents | Dokumente, an Circuits und Geräte angehängt | Apache 2.0 |
| Lifecycle | Hardware-End-of-Life, Lizenzen und Verträge | Apache 2.0 |
| Contract | Verträge und Rechnungen | MIT |
| Reorder Rack | Höheneinheiten per Drag and Drop | Apache 2.0 |
| Branching | Isolierte, zusammenführbare Branches deiner Daten | NetBox Limited Use |
| Custom Objects | Neue Objekttypen, in der UI definiert | NetBox Limited Use |
Prometheus SD ist die ehrliche Form der ganzen Idee. NetBox weiß, was existiert, also lass NetBox es dem Monitoring sagen und hör auf, eine zweite Hostliste zu pflegen, die auseinanderdriftet.
Eines solltest du wissen, bevor du auf den letzten zwei Zeilen aufbaust. NetBox selbst ist Apache 2.0 und ist das seit der Freigabe durch DigitalOcean 2016, und das Projekt wird heute von NetBox Labs gemeinsam mit einem Team freiwilliger Maintainer betreut.21 NetBox Branching und NetBox Custom Objects sind es nicht: Sie werden unter der NetBox Limited Use License 1.0 ausgeliefert, die die Nutzung „only as part of a NetBox installation obtained from NetBox Labs or a NetBox distributor authorized by NetBox Labs, and only for your own internal use“ gewährt und die nicht das Recht gewährt, die Software zu nutzen, „to provide a managed service or software products that includes, integrates with, or extends NetBox in a way that competes with any product or service of NetBox Labs“. Wenn du NetBox Community von GitHub installiert hast und es im Auftrag von Kunden betreibst, lies die Bedingungen selbst, bevor du ein Schema dahinter stellst.10 NetBox’ eigene Installationsanleitung empfiehlt beide Plugins, ohne das zu erwähnen.11
Custom Fields
Ein Custom Field fügt einem bestehenden Modell ein Attribut hinzu. Werte werden als JSON neben jedem Objekt gespeichert, es gibt also keine Migration und keinen Neustart, und es gibt dreizehn Typen, darunter Objekt- und Multi-Objekt-Referenzen auf andere NetBox-Datensätze.

Zwei Felder dort, gruppiert unter einer Überschrift „Asset“, die ich gewählt habe. Beachte das Panel Dimensions rechts davon: 9,5 kg, die niemand an diesem Gerät eingetippt hat. Sie kamen vom Gerätetyp.
Custom Fields werden validiert, und es lohnt sich zu wissen, dass sie es werden, denn es ist ein echter Unterschied zum Weg weiter unten. Ich habe dem Vertragsfeld einen regulären Ausdruck gegeben, und die API hat ihn erzwungen:
PATCH {"custom_fields": {"support_contract": "nonsense"}}
{"__all__": ["Invalid value for custom field 'support_contract':
Value must match regex '^[A-Z]{2,4}-[0-9]{6}$'"]}
Zwei Dinge haben sich in 4.7 geändert, die in der Breite zählen. Ein Feld mit einem Standardwert anzulegen oder ein Feld zu löschen muss die gespeicherten Daten jedes Objekts umschreiben, für das es gilt, auf einer großen Tabelle wird diese Arbeit also an einen Background-Job übergeben, und das Feld meldet „provisioning“ oder „deleting“, während das läuft. Ein Feld ist nur live, solange es aktiv ist: Während einer dieser Operationen erscheint es nicht an Objekten, nicht in Formularen, nicht in Filtern und in keiner der beiden APIs. Dafür muss ein Worker laufen, sonst bleibt es unbegrenzt in diesem Zustand.
Nimm ein Custom Field, wenn du eine Tatsache über das Ding selbst hinzufügst. Nimm einen Config Context, wenn der Wert durchrieseln soll. Nimm den nächsten Abschnitt, wenn das, was du brauchst, nicht existiert.
Eigene Auswahlmenüs, und das Überschreiben dessen, was ausgeliefert wird
Zwei verschiedene Mechanismen liegen unter dieser Überschrift, und sie lösen verschiedene Probleme. Einer ist für deine eigenen Felder. Der andere schreibt NetBox’ um.
Ein Choice Set, für dein eigenes Auswahlfeld
Ein Custom Field vom Typ „selection“ zieht seine Optionen aus einem Choice Set, einem Objekt, das du in der UI verwaltest wie alles andere. So wird „support tier“ ein echtes Dropdown statt Freitext, den jemand auf drei Arten schreibt.

Es wird erzwungen, auch über die API:
PATCH {"custom_fields": {"support_tier": "platinum"}}
{"__all__": ["Invalid value for custom field 'support_tier':
Invalid choice (platinum) for choice set Support tier."]}
Choice Sets werden geteilt, ein Set kann also dasselbe Feld auf mehreren Modellen tragen, und die Liste an einer Stelle zu ändern ändert sie überall.
FIELD_CHOICES, für NetBox’ eigene Felder
Das ist das, von dem Leute nicht wissen, dass es existiert. Mehrere der eingebauten Auswahlfelder von NetBox können aus configuration.py erweitert oder ersetzt werden, darunter Gerätestatus, Standortstatus, Rack-Status, Circuit-Status und viele weitere.12
Häng ein Plus an, um zu dem hinzuzufügen, was ausgeliefert wird. Lass es weg, um die Liste komplett zu ersetzen.
FIELD_CHOICES = {
# add to what NetBox ships with
'dcim.Device.status+': (
('burn-in', 'Burn-in', 'cyan'),
{'value': 'awaiting-rma', 'label': 'Awaiting RMA', 'color': 'orange',
'description': 'Faulty, with the vendor'},
),
# replace the stock list outright
'dcim.Site.status': (
('surveyed', 'Surveyed', 'purple'),
('building', 'Building out', 'orange'),
('active', 'Active', 'green'),
('closing', 'Closing', 'red'),
),
}
Genau das habe ich auf diese Instanz gelegt. Der Gerätestatus kam mit den sieben Standardwerten und meinen zwei zurück:
offline, active, planned, staged, failed, inventory, decommissioning,
burn-in, awaiting-rma
und der Standortstatus kam nur mit meinen zurück, die Standardliste war weg:
surveyed, building, active, closing
Eine Auswahl kann ein einfaches Tupel aus Wert, Label und Farbe sein oder ein Dictionary, das zusätzlich eine Beschreibung nimmt, die im Formular als Untertitel erscheint. Und sie verhalten sich überall wie native Werte, denn für den Rest von NetBox sind sie native Werte:

Dieses Badge ist mein Status, in meiner Farbe, sortiert und filtert wie jeder andere.
Drei Dinge solltest du wissen, bevor du es nutzt.
Ersetzen löscht die Standardwerte aus dem Menü, nicht aus der Datenbank. Jedes Objekt, das schon einen davon hält, behält ihn, aber der Wert wird nicht mehr angeboten und wird beim nächsten Bearbeiten dieses Objekts keine gültige Auswahl sein. Wenn du eine Liste ersetzt, prüf, dass nichts auf einem Wert sitzt, den du gerade entfernt hast.
Es lebt in der Konfigurationsdatei, braucht also einen Neustart und ist nichts, was ein UI-Nutzer ändern kann. Für einen Provider ist das die richtige Richtung: Welche Status dein Geschäft anerkennt, ist eine Governance-Entscheidung, keine an einem Dienstagnachmittag.
Erweitere, bevor du ersetzt. Die Standardwerte sind das, was jedes Plugin, Script und jede Integration zu sehen erwartet. Anhängen kostet nichts. Ersetzen ist eine Entscheidung, die du für immer besitzt.
Custom Objects
Hier ist eine echte Lücke. NetBox modelliert Cluster, virtuelle Maschinen und virtuelle Disks. Es modelliert nicht den Datastore, auf dem diese Disks wirklich liegen, und für jeden, der Proxmox oder VMware betreibt, ist das das Objekt, das den gekauften Speicher mit der Last verbindet, die ihn nutzt.
Das Custom-Objects-Plugin lässt dich einen neuen Objekttyp aus der UI oder der API definieren, ohne Code zu schreiben. Also:

Sieben Felder, von denen zwei der ganze Punkt sind. cluster ist eine Einzelobjekt-Referenz auf ein echtes NetBox-Cluster, auf protect gesetzt, damit niemand ein Cluster unter seinem Speicher weglöschen kann. provisioned_by ist eine Multi-Objekt-Referenz auf die Geräte, die ihn wirklich bereitstellen.
Das gibt dir ein erstklassiges Objekt mit eigenem Navigationseintrag, Listenansicht, Filtern, Import und Export:

Und weil die Referenzen echt sind, zeigt sich die Beziehung auch vom anderen Ende. Öffne das Cluster, und die Datastores sind dagegen aufgeführt.

Ein Custom Object Type erbt das meiste von dem, was ein NetBox-Objekt zu einem NetBox-Objekt macht: Listen- und Detailansichten, einen Navigationseintrag, REST-Endpunkte, Volltextsuche, Change Logging, Journaling, Tags, Bookmarks, Import und Export, Event Rules und Benachrichtigungen. Nichts davon musste geschrieben werden.
Es ist auch kein JSON-Blob, der eine Tabelle vorgibt zu sein. Das Plugin setzt echtes DDL ab, und was in PostgreSQL erscheint, ist eine echte Tabelle mit echten Constraints:
Table "public.custom_objects_2"
Column | Type | Nullable | Default
-----------------+----------+----------+------------------
id | bigint | not null | identity
name | varchar | |
cluster_id | bigint | |
backing | varchar | |
capacity_gb | bigint | |
thin_provisioned| boolean | |
Indexes:
"custom_objects_2_name_key" UNIQUE CONSTRAINT, btree (name)
Foreign-key constraints:
... FOREIGN KEY (cluster_id) REFERENCES virtualization_cluster(id) ON DELETE RESTRICT
Das protect, um das ich gebeten habe, wurde zu ON DELETE RESTRICT, und es funktioniert: Dieses Cluster zu löschen kommt als 409 zurück und nennt, was davon abhängt.
Zwei Dinge, die man wissen muss, bevor man sich darauf verlässt
Die Validierung sitzt am Formular, nicht an der API. Das ist die, die eine Automatisierung erwischen würde. Ich habe required, einen regulären Ausdruck und numerische Grenzen an den Feldern eines Custom Object Type deklariert und dann über die REST-API darauf geschrieben:
| Was ich deklariert habe | Was ich gesendet habe | Ergebnis |
|---|---|---|
validation_regex | einen Wert, der auf nichts passt | 201 Created |
required: true | das Feld ganz weggelassen | 201 Created |
validation_minimum: 1 | eine negative Zahl | 201 Created |
unique: true | ein Duplikat | 400, abgewiesen |
on_delete_behavior: protect | das referenzierte Objekt löschen | 409, abgewiesen |
Die zwei, die gehalten haben, sind die zwei, die zu Datenbank-Constraints wurden. Der Rest existiert nur am Webformular, ein tippender Mensch ist also eingeschränkt und ein nächtlicher Sync nicht. Vergleich das mit dem Core-Custom-Field weiter oben, wo derselbe reguläre Ausdruck über die API erzwungen wurde. Bis sich das ändert, stell alles, wovon eine Rechnung abhängt, hinter ein echtes Constraint.
Einen Typ zu löschen wirft eine Tabelle weg. Ein Feld zu löschen wirft eine Spalte weg. Das ist DDL aus einem Webformular, ausgeführt von wem auch immer die Berechtigung hat, und die Dokumentation sagt genau das.13 Schränk ein, wer die löschen darf.
Wann du stattdessen ein echtes Plugin schreibst
Wenn das Objekt wichtig ist, wenn Automatisierung darauf schreibt und wenn es dich ärgern würde, Müll darin zu finden, schreib das Modell selbst. Ein minimales NetBox-Plugin ist ein PluginConfig, ein Modell, ein Serializer, ein Viewset, eine Tabelle, ein Formular, ein paar Views und eine URL-Map, und es kommt auf unter zweihundert Zeilen überwiegend Deklarationen. PrimaryModel zu subclassen gibt dir Tags, Custom Fields, Change Logging, Journaling, Export-Templates und Ownership gratis, und jedes Constraint, das du an das Modell hängst, wird überall erzwungen, denn NetBox’ eigene Serializer-Maschinerie führt es aus.
Es gibt ein Cookiecutter-Template und ein vollständiges Plugin-Tutorial, von der Community gepflegt. Fang damit an und nicht mit einem leeren Verzeichnis.
Und es hat die Lizenz, die du wählst, was später niemand ändern kann.
Custom Validation und Validierungsklassen
Alles oben dreht sich darum, festzuhalten, was da ist. Hier geht es darum, zu verweigern, was nicht festgehalten werden sollte.
NetBox validiert jedes Objekt, bevor es es schreibt, und du kannst eigene Regeln obendrauf legen. Es gibt drei Mechanismen, sie leben alle in configuration.py, und zusammen decken sie fast alles ab, was ein Hausstandard braucht.
Eins: einfache Regeln, kein Code
Ein Validator kann eine einfache Zuordnung von Feldnamen zu Bedingungen sein. Kein Python, und es ist zwischen Installationen portabel, weil es nur Daten sind.
CUSTOM_VALIDATORS = {
'dcim.site': (
{'description': {'required': True}},
),
'dcim.device': (
{'name': {'regex': r'^[a-z0-9]+-[a-z]+-([0-9]{2}|[a-z])$'}},
),
}
Die verfügbaren Bedingungen sind min, max, min_length, max_length, regex, required, prohibited, eq und neq. Du kannst mit einem Punktpfad in ein verbundenes Objekt hineingreifen, region.name an einer Site ist also erlaubt, und du kannst auf request.user.username prüfen, auch wenn die Dokumentation dir völlig zu Recht sagt, dafür lieber Permissions zu nutzen.
Beide Regeln feuern sofort:
POST a site with no description
{"__all__": ["Custom validation failed for description:
['This field must not be empty.']"]}
POST a device named "Server1"
{"__all__": ["Custom validation failed for name: ['Enter a valid value.']"]}
Die zweite ist ein Namensstandard, erzwungen. Nicht auf eine Wiki-Seite geschrieben, nicht in jemandes Kopf, nicht etwas, wofür der Neue drei Wochen später in einem Review gerügt wird. Die Datenbank nimmt es nicht an.
Zwei: eine Validator-Klasse, wenn die Regel ein Satz ist
Einfache Regeln prüfen ein Feld gegen eine Konstante. Echte Hausregeln sind meist bedingt: das zählt nur, wenn jenes. Dafür subclasst du CustomValidator, überschreibst validate() und rufst fail().
Ich habe drei davon in ein Modul unter /opt/netbox/netbox/house_rules.py gelegt:
from extras.validators import CustomValidator
class BillableKitNamesItsCustomer(CustomValidator):
"""Anything in a role we sell has to say whose it is."""
BILLABLE_ROLES = {'hypervisor'}
def validate(self, instance, request):
role = getattr(instance, 'role', None)
if role and role.slug in self.BILLABLE_ROLES and not instance.tenant:
self.fail(
f"A {role} is billable kit, so it must name the customer it belongs to.",
field='tenant',
)
class RackedDeviceNeedsAPosition(CustomValidator):
"""A device in a rack with no rack unit is a device nobody can find."""
def validate(self, instance, request):
if instance.rack and instance.position is None:
if instance.device_type and instance.device_type.u_height:
self.fail(
"A device in a rack needs a rack unit. Somebody has to find it.",
field='position',
)
und sie per Punktpfad verdrahtet:
CUSTOM_VALIDATORS = {
'dcim.device': (
{'name': {'regex': r'^[a-z0-9]+-[a-z]+-([0-9]{2}|[a-z])$'}},
'house_rules.BillableKitNamesItsCustomer',
'house_rules.RackedDeviceNeedsAPosition',
),
'ipam.prefix': (
'house_rules.CustomerPrefixNeedsATenant',
),
}
Beachte, dass ein Modell ein Tupel von Validatoren nimmt, einfache Regeln und Klassen frei gemischt, und sie laufen alle. Auch ein einzelner Validator muss als Iterable übergeben werden, und das sind leicht fünf verlorene Minuten.
Dann tun die Regeln, was sie sagen:
POST a hypervisor with no tenant
{"tenant": ["A Hypervisor is billable kit, so it must name
the customer it belongs to."]}
POST a device into a rack with no position
{"position": ["A device in a rack needs a rack unit.
Somebody has to find it."]}
POST a prefix with role "customer" and no tenant
{"tenant": ["A customer prefix must be assigned to a tenant."]}
POST a leaf switch with no tenant
accepted, because a leaf switch is not in BILLABLE_ROLES
Weil fail() ein field nimmt, landet die Meldung am richtigen Kasten im Formular statt oben auf der Seite, und das ist der Unterschied zwischen einer Regel, die Leute lernen, und einer Regel, die Leute übelnehmen.
Das ist auch die Antwort auf die Lücke im Custom-Objects-Abschnitt. Die Validierung dort existierte nur am Webformular. Ein CustomValidator läuft in der Modellschicht, gilt also für die UI, die REST-API, GraphQL-Mutationen, Bulk-Importe und alles, was ein Script tut. Es gibt eine Stelle, an der die Regel geschrieben wird, und keinen Weg daran vorbei.
Drei: Protection Rules, fürs Löschen
CUSTOM_VALIDATORS wacht über Schreibvorgänge. PROTECTION_RULES wacht über Löschvorgänge, und es nimmt genau dieselben zwei Formen.
PROTECTION_RULES = {
'dcim.device': (
{'status': {'eq': 'offline'}},
),
}
Das sagt, ein Gerät kann nur gelöscht werden, wenn es offline ist. Was dies ergibt:
DELETE an active device
{"detail": "Deletion is prevented by a protection rule:
[\"Custom validation failed for status:
['Ensure this value is equal to offline.']\"]"}
set it to offline, then DELETE
204 No Content
Zwei Tastenanschläge Reibung zwischen jemandem und einem laufenden Gerät, und es ist die billigste Versicherung in der ganzen Anwendung. Mach den Außerbetriebnahmeprozess zu dem, was den Löschknopf freischaltet.
Die eine, die dich erwischt
Bestehende Daten werden erst geprüft, wenn du sie das nächste Mal anfasst. Eine Regel hinzuzufügen geht nicht zurück und validiert, was schon da ist. Sie liegt still, bis jemand ein Objekt speichert, das sie bricht, und dann bekommt der einen Fehler über eine Entscheidung, mit der er nichts zu tun hatte.
Ich habe zugesehen, wie es passiert ist. mcr1-hv-06 wurde angelegt, bevor ich irgendetwas davon geschrieben habe, als Hypervisor ohne Tenant. Es lag vollkommen zufrieden da. Dann habe ich seine Beschreibung bearbeitet:
PATCH {"description": "touching it to trigger revalidation"}
{"tenant": ["A Hypervisor is billable kit, so it must name
the customer it belongs to."]}
Die Änderung hatte nichts mit dem Tenant zu tun. Die Regel hat trotzdem gefeuert, denn die Validierung läuft über das ganze Objekt.
Das ist korrektes Verhalten, und es ist auch, wie aus einer neuen Regel ein Support-Ticket wird. Bevor du eine einschaltest, frag die Objekte ab, die daran scheitern würden, und reparier sie zuerst. Die API macht das leicht: Die Regel ist ein Filter, frag also nach den Geräten mit dieser Rolle und ohne Tenant, und du hast deine Liste.
Schalt die Regel danach ein. Dann erwischt sie nur noch neue Fehler, und dafür ist sie da.
Steuerung aus Ansible
Eine Source of Truth, die niemand liest, verrottet. Die Collection netbox.netbox ist, wie die meisten das verhindern, und sie funktioniert in beide Richtungen: NetBox sagt Ansible, was existiert, und Ansible sagt NetBox, was es gebaut hat.
Sie steht bei Version 3.23.0, ist unter GPL-3.0 lizenziert und wurde über 13,4 Millionen Mal aus Galaxy gezogen. Sie trägt 91 Module und ein Inventory-Plugin.14 Alles Folgende lief gegen dieselbe Instanz, aus einem Container, in dem nichts war außer ansible-core 2.21.4, pynetbox 7.8.0 und der Collection.
Das Inventar ist eine Abfrage, keine Datei
Das ist die Hälfte, die sich am ersten Nachmittag bezahlt. nb_inventory baut dein Ansible-Inventar direkt aus NetBox:
plugin: netbox.netbox.nb_inventory
api_endpoint: http://netbox:8080
token: "{{ lookup('env', 'NETBOX_TOKEN') }}"
config_context: false
group_by:
- sites
- device_roles
- tenants
- racks
device_query_filters:
- has_primary_ip: true
Das erzeugt dies, ohne dass jemand eine Hostliste pflegt:
@sites_manchester-dc1: @device_roles_hypervisor:
|--mcr1-core-01 |--mcr1-hv-01
|--mcr1-core-02 |--mcr1-hv-02
|--mcr1-hv-01 |--mcr1-hv-03
|--mcr1-hv-02 |--mcr1-hv-04
... |--mcr1-hv-05
@racks_MCR1-A07: @tenants_ravenscroft-legal:
|--mcr1-core-01 |--mcr1-hv-01
|--mcr1-core-02 |--mcr1-hv-02
... @tenants_padgate-foods:
@device_roles_core-router: |--mcr1-hv-03
|--mcr1-core-01 |--mcr1-hv-04
|--mcr1-core-02 @tenants_hartley-components:
|--mcr1-hv-05
Schau auf die rechte Spalte. Weil Tenancy an den Geräten gesetzt ist, bekommst du eine Gruppe pro Kunde gratis, --limit tenants_ravenscroft-legal fährt ein Play also gegen genau die Technik eines Kunden. Füge ein Gerät in NetBox hinzu, und es ist beim nächsten Lauf in der Gruppe. Nimm eines außer Betrieb, und es ist weg. Niemand bearbeitet irgendetwas.
Jeder Host kommt mit dem an, was NetBox über ihn weiß:
ansible_host "2001:db8:20:20::11"
primary_ip4 "10.20.20.11"
primary_ip6 "2001:db8:20:20::11"
device_roles ["hypervisor"]
sites ["manchester-dc1"]
racks ["MCR1-A07"]
tenants ["ravenscroft-legal"]
device_types ["sys-1029u-tn10rt"]
manufacturers ["supermicro"]
status {"label": "Active", "value": "active"}
Zweiundzwanzig Schlüssel insgesamt, und einer davon ist es wert, ihn zu bemerken, bevor er dich überrascht. ansible_host ist die IPv6-Adresse, weil dieses Gerät eine primäre v6 gesetzt hat. Ich habe es gegen zwei andere geprüft, die nur v4 haben, und die kamen mit v4 zurück, die Regel ist also, dass das Plugin v6 bevorzugt, wo eine primäre v6 existiert. Was korrekt ist und auch die Art Sache, die du lieber jetzt entdeckst als beim Grübeln, warum ein Play über einen Pfad verbindet, an den du nicht gedacht hattest.
Zurückschreiben
Die Module sind die andere Richtung, und das, was man haben will, ist die IP-Vergabe, denn NetBox weiß, was frei ist, und dein Playbook nicht.
- name: Rack the device
netbox.netbox.netbox_device:
netbox_url: "{{ nb_url }}"
netbox_token: "{{ nb_token }}"
data:
name: mcr1-hv-07
device_type: SYS-1029U-TN10RT
device_role: Hypervisor
site: Manchester DC1
rack: MCR1-A07
position: 14
face: Front
tenant: Hartley Components
state: present
- name: Let NetBox pick the next free address out of the customer prefix
netbox.netbox.netbox_ip_address:
netbox_url: "{{ nb_url }}"
netbox_token: "{{ nb_token }}"
data:
prefix: 10.20.22.0/24
tenant: Hartley Components
assigned_object:
device: mcr1-hv-07
name: eno1
state: new
Beachte, was nicht darin steht. Keine IP-Adresse. Du benennst das Prefix, und NetBox gibt die nächste freie zurück:
TASK [Let NetBox pick the next free address out of the customer prefix] ****
changed: [localhost]
"device : mcr1-hv-07 (created)"
"interface: eno1 (created)"
"address : 10.20.22.1/24 (allocated)"
Und eine Sekunde später ist es in der UI, noch an nichts verkabelt, aber eingeracked, mit Tenant und adressiert:

Die Falle in diesem Playbook
Lass es ein zweites Mal laufen, ohne eine Zeile zu ändern, und das passiert:
"device : mcr1-hv-07 (already correct)"
"interface: eno1 (already correct)"
"address : 10.20.22.2/24 (allocated)"
Das Gerät und das Interface sind idempotent. Die Adresse ist es nicht, und das ist kein Bug. state: present heißt „mach, dass es so aussieht“. state: new heißt „gib mir eine neue“, jedes einzelne Mal, und genau das hat es getan: zwei Adressen auf einem Interface nach zwei Läufen.
Das ist in Ordnung, wenn du wirklich etwas Neues bereitstellst, und es frisst still ein Prefix auf, wenn du es in einen nächtlichen Job steckst. Vergib einmal und halte das Ergebnis fest, oder nimm state: present mit der Adresse, die du schon hast. Das Modul tut, was du verlangt hast. Die Frage ist, ob du verlangt hast, was du meintest.
Es ist nicht nur Ansible
Die Collection bekommt die Aufmerksamkeit, weil Ansible dort ist, wo die meisten Netzwerkteams schon sind, aber die API ist das Produkt, und eine Menge Dinge sprechen sie.
| Ding | Was es ist | Lizenz |
|---|---|---|
pynetbox | Der Python-Client, den die Collection selbst nutzt | Apache 2.0 |
terraform-provider-netbox | NetBox-Objekte als Terraform-Ressourcen verwalten, aktiv gepflegt von e-breuninger | MPL 2.0 |
nornir_netbox | NetBox als Nornir-Inventar, für Leute, die Python statt YAML machen | Apache 2.0 |
| Prometheus SD | Liefert Prometheus seine Scrape-Targets aus NetBox | MIT |
go-netbox | Ein Go-Client, allerdings seit Mai 2025 nicht angefasst | siehe Repository |
| Diode | NetBox Labs’ eigene Ingestion-Pipeline, um entdeckte Daten hineinzuschieben | NetBox Limited Use |
Der Terraform-Eintrag ist der interessante für jeden, der Infrastruktur schon so verwaltet, denn er lässt einen einzigen Plan die Cloud-Ressource und den NetBox-Datensatz anlegen, der sie dokumentiert, statt die zweite Hälfte jemandes Gedächtnis zu überlassen.15
Diode trägt dieselbe Lizenz wie Branching und Custom Objects, es gilt also dieselbe Lektüre, bevor du darauf aufbaust.
Was der Nachmittag wirklich kauft
Alles oben hat mich einen Tag auf einer Maschine gekostet, und der größte Teil dieses Tages ging dafür hin, Daten zu säen, damit die Bildschirme etwas enthielten. Die Installation sind zwanzig Minuten, so oder so. Die Entscheidungen sind der Teil, der zählt, und sie werden alle in der ersten Stunde getroffen: Tenants vor allem anderen, Gerätetypen vor Geräten, die Zahlen an den Typen statt an der Technik.
Was du dafür bekommst, ist keine Dokumentation. Diese Unterscheidung macht niemand, bevor er beides hatte: Dokumentation ist etwas, das du schreibst und dann nicht mehr pflegst. Was du bekommst, ist eine Datenbank, die sich weigert, einen Widerspruch zu halten: Sie lässt dich nicht zwei Dinge in eine Höheneinheit setzen, oder ein Rack in eine Location, die zu einer anderen Site gehört, oder ein Gerät auf einen Typ, der nicht existiert. Jede dieser Verweigerungen ist eine Diskussion, die du in sechs Monaten nicht führst.
Und es wird dir Dinge sagen, nach denen niemand gefragt hat. Dieser Schrank ist 28,6 % voll mit Technik und 90,7 % voll mit Strom. Niemand hat sich vorgenommen, das zu finden. Es fiel daraus heraus, einmal eine Wattzahl an einem Gerätetyp eingetragen zu haben, und es ist der Unterschied zwischen dem Einracken der nächsten Bestellung dort und dem Herausfinden auf die harte Art.
Der ehrliche Vorbehalt ist derselbe, den jede Source of Truth hat. Sie ist nur so viel wert, wie es dich kostet, sie richtig zu halten, und die einzige Version davon, die ein geschäftiges Quartal übersteht, ist die, in der sichtbar etwas kaputtgeht, wenn die Daten falsch sind. Verdrahte dein Monitoring, dein Provisioning oder deine Firewall-Regeln so, dass sie daraus lesen, und ein falscher Eintrag hört auf, ein Dokumentationsproblem zu sein, um das sich irgendwann jemand kümmert. Er wird zu einem Ausfall um halb zehn an einem Dienstag, mit einem Namen daran. Das klingt wie ein Kostenpunkt. Es ist der ganze Mechanismus.
Danken wird dir dafür auch niemand. Eine korrekte Aufzeichnung zeigt sich als die Migration, die zwei Wochen statt eines Quartals gedauert hat, als die Prüfung, die einen Nachmittag gedauert hat, als der Kunde, der am Telefon eine gerade Antwort bekommen hat. Nichts davon erscheint in einem Report neben deinem Namen.
Mach es trotzdem. Die Alternative ist ein Unternehmen, das sich selbst nicht beschreiben kann, und ein Unternehmen, das sich selbst nicht beschreiben kann, wird nicht geführt. Es wird erinnert, von jedes Jahr weniger Leuten.
NetBox, Introduction — „Today, the open source project is stewarded by NetBox Labs and a team of volunteer maintainers“, und die Herkunft bei DigitalOcean 2015, Open Source seit Juni 2016. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
NetBox, LICENSE.txt — Apache License 2.0, Copyright DigitalOcean, LLC. Herkunft und Betreuung aus der Einführung. ↩︎ ↩︎
Release-Notes zu Nautobot v1.0.0 — „a divergent fork of NetBox 2.10“, veröffentlicht am 26. April 2021, und die Liste dessen, was es gegenüber NetBox 2.10 hinzufügte. ↩︎ ↩︎
Network to Code, „Why Did Network to Code Fork NetBox?“, 25. Februar 2021 — die Begründung mit SLA und Long-term Support, die auseinandergehende Vision und „the NetBox project team suggested that we should consider forking“. ↩︎
NetBox Labs wurde 2023 als Spin-out aus NS1 nach der Übernahme durch IBM gegründet, mitgegründet von NetBox’ Lead Maintainer Jeremy Stretch, und gab im April 2023 eine Series A über 20 Mio. $ bekannt: NetBox Labs, „Let’s Go: Announcing NetBox Labs“ und die Series-A-Ankündigung. ↩︎
NetBox Labs, NetBox Enterprise — die selbst verwaltete kommerzielle Edition, mit „24/7 expert assistance from the NetBox Labs team“; NetBox Cloud ist das gehostete Angebot. Konkrete SLA-Bedingungen sind auf dieser Seite nicht veröffentlicht. ↩︎
Stern- und Fork-Zahlen, Release-Daten und Lizenzen beider Projekte am 30. September 2026 über die GitHub-API abgelesen: netbox-community/netbox und nautobot/nautobot. Nautobots parallele Releases 3.2.x und 2.4.x sind beide auf den 28. September 2026 datiert. ↩︎
netbox-community/netbox-docker — der Container-Stack der Community, Apache 2.0. ↩︎
NetBox, Installation — die Tabelle der unterstützten Versionen, und die Release-Notes zu v4.7 für die angehobenen PostgreSQL- und Redis-Mindestversionen. Version und Edition abgelesen aus
netbox/release.yaml. ↩︎NetBox Limited Use License 1.0 — getragen von netbox-custom-objects und, identisch, von netbox-branching. Beide zitierten Klauseln sind wörtlich. ↩︎
NetBox, HTTP Server installation, „What’s Next?“ — „Some of the most popular plugins include“ NetBox Branching, NetBox Custom Objects, NetBox DNS und NetBox BGP, ohne Erwähnung der Lizenzbedingungen. ↩︎
NetBox, Data Validation configuration —
FIELD_CHOICESund das Plus-Suffix, das erweitert statt ersetzt: „To replace the available choices, specify the app, model, and field name separated by dots … To extend the available choices, append a plus sign“. ↩︎Dokumentation zu netboxlabs/netbox-custom-objects — „Deleting a Custom Object Type drops an entire database table and should be done with caution.“ ↩︎
netbox.netbox auf Ansible Galaxy und netbox-community/ansible_modules — Version 3.23.0, GPL-3.0, 91 Module plus das Inventory-Plugin
nb_inventory. Download-Zahl am 30. September 2026 bei Galaxy abgelesen. Alles Gezeigte lief mit ansible-core 2.21.4 und pynetbox 7.8.0. ↩︎Lizenzen, Aktivität und Stern-Zahlen am 30. September 2026 bei jedem Projekt abgelesen: pynetbox (Apache 2.0), terraform-provider-netbox (MPL 2.0, v6.0.0-rc.1 in der Terraform Registry), nornir_netbox (Apache 2.0), netbox-plugin-prometheus-sd (MIT), go-netbox (letzter Push am 9. Mai 2025) und Diode, das dieselbe NetBox Limited Use License 1.0 trägt wie Branching und Custom Objects. ↩︎