Tout ce qui se trouve dans ce billet a été exécuté contre un NetBox 4.7.2 posé sur mon propre bureau, avec un vrai parc chargé dedans : un site, une baie, quatorze équipements, câblés, alimentés et adressés chez trois clients. Chaque capture est cette instance, et chaque message d’erreur est un message que j’ai réellement obtenu.
L’ordre est celui dans lequel vous rencontreriez les choses. Ce qu’est la bête, pourquoi vous en voudriez une, le fork dont vous entendrez parler en une semaine de recherches, comment en monter une, l’ordre dans lequel il faut la remplir, puis ce que vous en ressortez. Le travail de personnalisation et d’extension arrive à la fin, parce que rien de tout cela n’a de sens avant d’avoir vu la forme de ce que vous étendez.
Ce qu’est NetBox
NetBox est une base de données qui a un avis très précis sur ce dont un réseau est fait, et une application web posée dessus. C’est une application Django sur PostgreSQL, elle est open source sous Apache 2.0 depuis que DigitalOcean l’a publiée en juin 2016, et le projet est aujourd’hui pris en charge par NetBox Labs aux côtés d’une équipe de mainteneurs bénévoles.12
En dessous, ce sont 149 modèles répartis sur dix applications, atteints par 146 endpoints REST et un endpoint GraphQL. Compté sur l’instance que j’ai construite pour l’occasion, et non relevé sur une page marketing :
| Application | Modèles | Application | Modèles |
|---|---|---|---|
| dcim | 56 | virtualization | 7 |
| extras | 23 | tenancy | 6 |
| ipam | 18 | wireless | 3 |
| circuits | 11 | users | 7 |
| vpn | 10 | core | 8 |
Cinquante-six d’entre eux sont du DCIM, la couche physique : sites, locations, baies, types d’équipement, équipements, et chaque sorte de port, de baie interne et de terminaison de câble qu’un équipement peut porter. Dix-huit sont de l’IPAM. Le reste couvre les liens opérateur, les tunnels et les politiques IKE, les machines virtuelles et les clusters, les liaisons sans fil, la gestion multi-client, et la machinerie qui rend l’ensemble extensible.
Le nombre n’est pas le sujet. Les jointures le sont, et le plus rapide pour le voir est l’écran pour lequel NetBox est le plus connu.
Commencez par la baie, parce que c’est l’écran qui vend la chose.

Cette élévation est dessinée à partir des données, elle n’est pas téléversée. Chaque équipement y est parce que quelque chose dit qu’il occupe ces unités, orienté de cette façon, et les couleurs viennent du rôle que vous lui avez donné. L’utilisation de l’espace affiche 28,6 % parce que NetBox l’a calculée. Personne n’entretient ce chiffre.
Deux choses en découlent, et elles valent davantage que l’image.
Vous pouvez demander quelles unités sont libres et obtenir une réponse exploitable. Vous pouvez aussi réserver des unités avant que quoi que ce soit n’y soit installé, et c’est la différence entre vendre de l’espace que vous avez et vendre de l’espace que vous croyez avoir.
Ce qu’il refuse délibérément de faire
Un produit qui sait ce qu’il n’est pas est plus rare qu’un produit qui fait tout mal, et la documentation de NetBox est franche là-dessus. Il ne fournit ni supervision réseau, ni service DNS, ni RADIUS, ni gestion de configuration, ni gestion des installations techniques.1
Plus important encore, il contient l’état souhaité de votre réseau et non son état opérationnel, et la documentation dit que l’import automatisé de l’état réel du réseau est « strongly discouraged », parce que chaque enregistrement devrait d’abord être validé par un humain.1
C’est la décision sur laquelle tout le reste repose, et c’est celle que les gens contestent. L’argument est : une source de vérité devrait bien être la vérité, donc découvrez le réseau et chargez-le. La réponse est qu’un réseau découvert vous dit ce qui est là, et ce qui est là inclut toutes les erreurs que quiconque a jamais commises. Un port de commutateur laissé dans le mauvais VLAN en 2021 est un fait. Ce n’est pas une intention.
NetBox contient l’intention. Votre supervision contient la réalité. Le chiffre intéressant est l’écart entre les deux, et on ne calcule pas un écart à partir d’une seule entrée.
L’autre principe est énoncé tout aussi clairement : entre une solution relativement simple à quatre-vingts pour cent et une solution complète bien plus complexe, prenez la simple.1 Vous le sentirez la première fois que vous voudrez modéliser quelque chose qu’il ne modélise pas, et il y a vers la fin toute une série de sections sur ce qu’il faut faire à ce moment-là.
| NetBox fait | NetBox ne fait pas |
|---|---|
| Consigner ce qui devrait être là | Interroger ce qui est là |
| Contenir le VLAN prévu pour un port | Vous dire que le port est tombé |
| Dire à quel client appartient un préfixe | Le lui facturer |
| Rendre la configuration d’un équipement depuis un gabarit | La pousser sur l’équipement |
| Suivre le lien, l’opérateur et l’engagement | Superviser le lien |
| Dire dans quelle baie est un équipement, et à quelle U | Ouvrir l’armoire |
Lisez la colonne de droite comme la liste des outils dont vous avez encore besoin. Lisez-la de travers et vous essaierez de faire de NetBox tous ces outils, et c’est ainsi qu’une source de vérité devient un système de plus auquel personne ne fait confiance.
Pourquoi il vous en faut une
Demandez à un prestataire de services managés où se trouve l’enregistrement de référence du réseau d’un client, et vous obtiendrez une réponse. Demandez séparément à deux de ses ingénieurs et vous en obtiendrez deux.
L’un montrera un tableur. L’autre montrera un schéma enregistré pour la dernière fois par quelqu’un parti en 2023. Un troisième dira que la configuration du pare-feu est la documentation, ce qui est au moins honnête, car une configuration décrit bien ce que fait une machine. Elle ne décrit simplement pas pourquoi, ni qui l’a demandé, ni lequel des quatre clients derrière cette machine paie pour la règle.
L’échec n’est jamais le jour où vous remarquez que l’enregistrement est faux. C’est le jour où quelqu’un en a besoin.
| Le moment | Ce que vous devez produire | Ce que ça coûte quand vous ne pouvez pas |
|---|---|---|
| Une discussion de renouvellement | Un détail poste par poste de ce que paie l’abonnement mensuel | Le devis du concurrent est détaillé, parce qu’il est allé compter |
| Un ingénieur démissionne | Tout ce qu’il savait, écrit | Six mois à le redécouvrir, un ticket à la fois |
| Un client part | Une description de son propre parc | Trois semaines pour l’assembler, et une référence qu’il donnera honnêtement |
| Un auditeur pose une question de périmètre | Quels systèmes détiennent des données personnelles, et où ils sont physiquement | Un régulateur à qui on a dit quelque chose qui s’avère faux ensuite |
| Une migration doit être chiffrée | Un décompte de ce qui est réellement là | Vous répondez sur une estimation et vous absorbez la différence |
Rien de tout cela n’est exotique. C’est un mardi.
Ce que vous en retirez n’est pas de la documentation. La documentation est une chose qu’on écrit puis qu’on arrête d’entretenir. Ce que vous obtenez est une base qui refuse de contenir une contradiction, et qui répond à des questions que personne n’avait pensé à lui poser. Il y a plus loin dans ce billet une baie qui se révèle remplie à 28,6 pour cent de matériel et à 90,7 pour cent d’électricité. Personne n’a cherché à trouver ça. C’est tombé d’une puissance saisie une fois sur un type d’équipement.
La réserve honnête vient avec, et la dernière section en parle. Un enregistrement ne vaut que ce qu’il vous coûte de le tenir juste. Mais l’alternative est une entreprise incapable de se décrire, et le premier à s’en apercevoir est généralement un client.
L’autre : Nautobot
Vous tomberez dessus en à peu près une semaine de recherches, donc autant savoir ce qui s’est passé.
En 2021, Network to Code a forké NetBox et a appelé le résultat Nautobot. Pas un fork léger ni une distribution : un fork dur qui divergent depuis cinq ans. Ses propres notes de version v1.0 le décrivent comme « a divergent fork of NetBox 2.10 », le dépôt a été créé le 19 février 2021 et la v1.0.0 est sortie le 26 avril 2021.3
Les raisons annoncées sont sur leur propre blog et méritent d’être lues dans leurs mots plutôt que dans les miens. Trois choses l’ont motivé. Ils voulaient vendre du support entreprise : « 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. » Ils voulaient que la source de vérité siège au centre d’une plateforme d’automatisation plutôt qu’elle serve la documentation. Et « 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
À cette première raison, il faut attacher sa date, car elle a cessé d’être vraie. En février 2021, il n’y avait aucune société derrière NetBox pour vous vendre quoi que ce soit. NetBox Labs n’a été fondée qu’en 2023, en spin-out de NS1 après son acquisition par IBM, cofondée par le mainteneur principal de NetBox lui-même.5
Et ce n’est pas un tiers qui aurait bâti une activité sur le projet d’un autre. NetBox Labs est le dépositaire de NetBox : la documentation du projet dit elle-même « the open source project is stewarded by NetBox Labs and a team of volunteer maintainers ».1 Ils vendent NetBox Enterprise pour les installations autogérées, l’hébergent pour vous sous le nom de NetBox Cloud, et proposent un support 24/7.6
Donc « you cannot buy support for NetBox » était une chose juste à dire quand Network to Code a forké, et ce n’est plus une chose juste à dire aujourd’hui. Les deux projets ont derrière eux une société commerciale qui signera quelque chose, et dans le cas de NetBox cette société est celle qui prend en charge le projet.
La phrase que la plupart des gens manquent est la suivante, et c’est la raison pour laquelle ce n’est pas une histoire sordide : « the NetBox project team suggested that we should consider forking. »
Un fork ne devient guère plus civilisé. Deux groupes voulaient des choses différentes, l’ont dit sur une longue période, et se sont séparés plutôt que de se battre autour d’une seule base de code. Les deux moitiés sont toujours en Apache 2.0. Personne n’a pris quoi que ce soit qu’il n’avait pas le droit de prendre.
À quoi le fork servait vraiment
Les notes de version de Nautobot 1.0 listent ce qu’il a ajouté par rapport à NetBox 2.10, et la liste raconte le débat mieux que n’importe quel billet de blog :3
| Ce que Nautobot a ajouté en 2021 | Où en est NetBox aujourd’hui |
|---|---|
| Prise en charge de GraphQL | NetBox l’a |
| Intégration Git comme source de données | NetBox l’a, sous forme de sources de données synchronisées |
| Authentification unique | NetBox l’a |
| Secrets | NetBox l’a via un plugin |
| Scripts et rapports regroupés en Jobs | NetBox sort les scripts vers un plugin en 4.7 |
| Custom fields sur tous les modèles | NetBox a une prise en charge large des custom fields |
| API de plugin de validation de données | NetBox a des custom validation rules |
| Statuts personnalisables, comme objets en base | Les statuts de NetBox restent un choice set Python |
| Relations définies par l’utilisateur entre modèles | NetBox n’a pas d’équivalent |
| Clés primaires UUID | NetBox utilise des clés entières |
| Améliorations de l’API de plugin | Le framework de plugins de NetBox a beaucoup grandi depuis |
La moitié supérieure a largement convergé. Plusieurs choses livrées par Nautobot en 2021 sont arrivées dans NetBox ensuite, et c’est généralement ce qui se produit quand deux projets résolvent les mêmes problèmes en public.
Les trois du bas n’ont pas convergé, et elles sont architecturales plutôt que cosmétiques. J’ai vérifié les deux bases de code aujourd’hui plutôt que de me fier aux notes de 2021.
Le Status de Nautobot est un modèle en base, décrit dans son propre code source comme un « Model for database-backend enum choice objects », donc un statut est une ligne que quelqu’un peut ajouter dans l’interface. Les statuts de NetBox viennent d’un choice set Python, ce qui explique pourquoi la section plus bas en ajoute un en modifiant configuration.py et en redémarrant. Nautobot a des modèles Relationship et RelationshipAssociation, vous pouvez donc définir une relation entre deux types d’objets existants sans écrire de code. La réponse de NetBox à ce problème est un plugin, et c’est la dernière série de sections de ce billet. Et les clés primaires de Nautobot sont des UUID, là où celles de NetBox sont des entiers.
Ils ont aussi fait ce pour quoi ils avaient forké. Aujourd’hui Nautobot publie 3.2.x et 2.4.x le même jour, ce qui est une véritable ligne de maintenance au long cours parallèle à la version courante, et c’était l’une des trois raisons annoncées.
Lequel des deux
Les chiffres honnêtes d’abord. NetBox a 21 625 étoiles et 3 133 forks ; Nautobot en a 1 617 et 422.7 Les deux ont reçu un push dans les deux derniers jours, les deux sont en Apache 2.0, et les deux ont désormais derrière eux une société commerciale qui vend du support et de l’hébergement.
Cet écart n’est pas un jugement sur la qualité. Il reflète cinq ans d’avance et le fait que la plupart des gens qui ont besoin d’une source de vérité trouvent NetBox en premier. Mais il décide de ce qui compte généralement plus que les fonctionnalités : combien de plugins, d’intégrations, de modules Ansible, de réponses de forum et de collègues vous trouverez pour celui que vous choisissez.
Donc : si vous voulez un inventaire et une source de vérité que d’autres systèmes lisent, et que vous voulez le plus grand écosystème et le recrutement le plus facile, la réponse est NetBox, et c’est de cela que parle le reste de ce billet. Si votre raison de vouloir une source de vérité est précisément d’en piloter de l’automatisation, ou si vous voulez que les statuts et les relations soient définis par votre équipe plutôt que par un fichier de configuration et un redémarrage, allez regarder Nautobot sérieusement avant de décider.
Ne laissez personne vous vendre l’un ou l’autre sur le seul support. Les deux camps couvrent ça désormais, et les différences qui seront encore là dans cinq ans sont celles du tableau ci-dessus.
Ce qu’il ne faut pas faire, c’est en choisir un parce que quelqu’un vous a dit que l’autre était mort. Aucun ne l’est, et les deux ont encore livré des versions cette semaine.
En mettre un en route
Deux chemins, et je les ai suivis tous les deux pour l’occasion. Les conteneurs si vous voulez que ça tourne en vingt minutes, les paquets sur un hôte si ça doit devenir porteur.
La pile de conteneurs
La communauté maintient netbox-docker, et c’est la réponse rapide :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
Cela vous donne l’application, PostgreSQL, Redis et un worker d’arrière-plan, câblés ensemble. J’ai construit la même chose à la main sous podman pour voir les pièces, et les pièces méritent d’être connues parce que deux d’entre elles piègent les gens :
| Conteneur | Ce qu’il fait | Si vous l’omettez |
|---|---|---|
netbox | L’application Django derrière gunicorn | rien ne marche |
postgres | La base, 15 ou plus récent | rien ne marche |
redis / valkey | Deux bases : une pour les tâches, une pour le cache | rien ne marche |
netbox-worker | rqworker, qui vide la file de tâches | les webhooks ne partent jamais, les tâches de fond ne tournent jamais, et rien ne vous avertit |
netbox-housekeeping | Le rangement périodique | les entrées du journal des changements n’expirent jamais |
Cette quatrième ligne est la bonne. Sans worker, tout a l’air en bonne santé. Les event rules s’empilent et restent là.
Deux choses m’ont mordu au premier lancement, et aucune ne figure dans un message d’erreur que vous chercheriez.
La première migration prend longtemps. Pas une minute. Sur cette machine il en a fallu plusieurs, parce que NetBox 4.7 remplace django-mptt par ltree de PostgreSQL et reconstruit au passage chaque table hiérarchique. Le conteneur reste simplement là à appliquer des migrations. Laissez-le tranquille.
Sans API_TOKEN_PEPPERS vous ne pouvez pas créer de jeton d’API v2, et le seul signe est un avertissement dans le journal :
UserWarning: API_TOKEN_PEPPERS is not defined. v2 API tokens cannot be used.
Définissez-en au moins un, d’au moins cinquante caractères, avant d’aller chercher pourquoi l’API vous rejette.
Sur un hôte, depuis les paquets
Le chemin documenté est testé sur Ubuntu 24.04. Je l’ai mené de bout en bout sur une installation propre, et voilà où ça a atterri :
| Composant | Ce que 24.04 m’a donné | Ce dont NetBox 4.7 a besoin |
|---|---|---|
| PostgreSQL | 16.15 | 15 ou plus récent |
| Redis | 7.0.15 | 6.0 ou plus récent |
| Python | 3.12.3 | 3.12, 3.13 ou 3.14 |
| Django | 6.1.1 | fourni avec NetBox |
| NetBox | v4.7.2 |
Ces minimums sont ceux de NetBox, et la 4.7 a relevé le plancher PostgreSQL comme le plancher Redis.9
Le travail entier tient en cinq étapes.
# 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
Ces cinq valeurs sont ALLOWED_HOSTS, DATABASES, REDIS, SECRET_KEY et API_TOKEN_PEPPERS. Générez les deux dernières séparément et n’utilisez pas l’une pour l’autre. Lancez donc le générateur de clé deux fois et collez les résultats sur des lignes différentes.
upgrade.sh construit l’environnement virtuel, installe chaque dépendance Python, exécute les migrations, construit la documentation pour l’usage hors ligne et collecte les fichiers statiques. Quand il termine sur une machine neuve, il affiche un avertissement qui a l’air alarmant et qui ne l’est pas :
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.)
Ensuite un superutilisateur, et ça se lance ainsi :
source /opt/netbox/venv/bin/activate
cd /opt/netbox/netbox && python3 manage.py createsuperuser
Pour quoi que ce soit de réel, mettez gunicorn devant plutôt que runserver. La configuration et les fichiers d’unité sont déjà dans le dépôt, et c’est le détail à connaître parce que les gens écrivent les leurs :
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 fait tourner gunicorn sur 127.0.0.1:8001, netbox-rq.service fait tourner le worker, et nginx ou Apache se place devant pour terminer le TLS et servir /static. Deux services, et le second est le même worker dont la pile de conteneurs a besoin.
L’ordre dans lequel il faut le remplir
Un NetBox neuf est une base vide qui a des avis, et la première heure avec lui passe généralement à découvrir lesquels. Vous allez ajouter un équipement, et il ne vous laisse pas faire.

Ces astérisques rouges sont toute la leçon. NetBox n’enregistre rien avant que les choses auxquelles il s’accroche existent, et il ne fait pas la mauvaise tête : un équipement sans type est une ligne incapable de répondre à aucune des questions pour lesquelles un équipement existe.
Plutôt que de deviner, j’ai donc demandé au modèle quelles clés étrangères sont réellement obligatoires, puis j’ai essayé de casser chaque règle par l’API pour voir ce qui revient.
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."]}
Chacun a refusé, en disant exactement ce qui manquait. Voici la même information sous forme de liste de prérequis :
| Pour créer | Requis d’abord | Optionnel, mais vous le voulez d’abord |
|---|---|---|
| Tenant | rien | un groupe de tenants |
| Region, Site group | rien | un parent de même nature, ils s’imbriquent |
| Site | rien | une Region, un Site group, un Tenant |
| Location | un Site | une Location parente, un Tenant |
| Rack type | un Manufacturer | |
| Rack | un Site | une Location, Rack group, Rack role, Rack type, Tenant |
| Device type | un Manufacturer | |
| Device role | rien | une Device role parente, elles s’imbriquent |
| Device | une Device role, un Device type, un Site | une baie et une position, une Platform, un Tenant |
| Interface, et tout autre composant | un Device | |
| Cable | deux choses sur lesquelles terminer | un Tenant |
| Aggregate | un RIR | un Tenant |
| VLAN | rien | un VLAN group, une Role, un Tenant |
| Prefix | rien | un Site, un VLAN, une Role, un VRF, un Tenant |
| IP address | rien | une Interface à laquelle l’affecter, un Tenant |
| Circuit | un Provider et un Circuit type | un Tenant |
| Circuit termination | un Circuit | un Site où atterrir |
| Cluster | un Cluster type | un Cluster group, un Site, un Tenant |
| Virtual machine | un Site, un Cluster ou un Device | une Platform, un Tenant |
| VM interface | une Virtual machine |
La deuxième colonne est celle qui vous coûte. Préfixes, adresses IP, VLAN et tenants n’exigent rien du tout, donc rien ne vous empêche de les créer au premier jour dans l’ordre qui vous plaît. Savoir s’ils servent à quelque chose est une autre question, car une adresse sans interface derrière elle est une ligne dans une liste, et un équipement sans tenant est un équipement que vous rééditerez plus tard.
L’ordre dans lequel travailler vraiment
Cela donne une séquence. Descendez-la et rien ne vous refuse jamais rien.
D’abord, les choses auxquelles tout le reste s’accroche. Rien là-dedans n’est excitant et tout y est peu coûteux à rater.
- Les groupes de tenants, puis les tenants. Faites-les avant tout le reste. Vingt-huit modèles acceptent un tenant, dont Site, Location et Rack, donc si les clients n’existent pas encore vous ne pouvez pas les tamponner au passage et vous ferez de l’édition en masse plus tard.
- Les regions et les site groups. Les deux optionnels, les deux s’imbriquent en eux-mêmes, et ils sont indépendants l’un de l’autre. Les regions servent à la géographie, les site groups à la fonction, et vous pouvez utiliser l’un, les deux ou aucun.
- Les sites. La racine de presque tout. Un site n’exige rien, ce qui en fait la première chose que vous pouvez réellement créer.
- Les locations. Elles ont besoin d’un site, et elles s’imbriquent, donc une salle contenant des rangées contenant des pods est un seul modèle sur trois niveaux.
- Les rack roles et les rack groups. Les deux optionnels. Les rack groups sont plats et se placent à côté des locations comme second axe, ce qui est pratique pour les rangées et les pods.
- Les manufacturers, puis les rack types. Un rack type a besoin d’un manufacturer. Sautez les rack types si vous ne modélisez pas les armoires elles-mêmes.
- Les baies. Elles ont besoin d’un site. Si vous leur donnez aussi une location, cette location doit appartenir à ce site, et NetBox vérifie.
Puis le catalogue matériel, pas le matériel. Avant de pouvoir ajouter un seul équipement il vous faut des manufacturers, des device types, des device roles et, en pratique, des platforms.
- Les manufacturers. Vous en avez peut-être déjà depuis l’étape 6, car les rack types en ont besoin aussi. Les module types également.
- Les device types, chacun ayant besoin d’un manufacturer.
- Les device roles, qui s’imbriquent, et les platforms.
C’est l’étape que les gens sautent, et c’est l’étape qui décide de la quantité de saisie que coûtera le reste du travail. Un device type porte ses propres interfaces, ports et baies internes sous forme de gabarits, donc chaque équipement que vous créez à partir de lui arrive avec les bons composants déjà dessus. Faites le type correctement une fois et en racker quarante, c’est quarante noms.
Platform est l’intrus de cette liste, parce qu’un équipement n’en exige pas strictement une. Faites-le maintenant quand même. C’est elle qui portera plus tard le gabarit de configuration et le pilote NAPALM, et y revenir pour la poser sur un parc entier, c’est la même soirée que celle que vous auriez passée sur les tenants.
Puis le matériel lui-même.
- Les équipements. Rôle, type et site sont tous obligatoires. Baie et position sont optionnelles, et si vous les donnez elles doivent être cohérentes avec le site.
- Les composants, si le device type ne les a pas déjà fournis.
- Les câbles, entre les composants.
Puis l’adressage, car une adresse veut une interface sur laquelle vivre et l’interface n’existe qu’après l’étape 12.
- Les RIR, puis les aggregates.
- Les roles de préfixe et de VLAN, les VRF, les VLAN groups puis les VLAN.
- Les préfixes, puis les adresses IP individuelles.
Puis la couche commerciale.
- Les providers, les provider accounts et les circuit types.
- Les circuits, puis terminez-les sur des sites.
Et le parc virtuel, qui reflète le parc physique.
- Les cluster types et les cluster groups, puis les clusters.
- Les machines virtuelles, qui ont besoin d’un site, d’un cluster ou d’un équipement, puis leurs interfaces.
Les étapes 1 à 10 sont un après-midi et elles ont l’air d’être de l’administratif. Il n’y a là rien de glorieux. C’est aussi l’après-midi qui décide si l’étape 11 prend une matinée ou quinze jours.
Les règles qui mordent plus tard
Les champs obligatoires sont la moitié facile, car ils échouent immédiatement et vous disent pourquoi. Ce qui piège les gens, ce sont les règles de cohérence, qui ne se déclenchent qu’une fois que vous avez assez de données pour vous contredire :
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)"]}
La dernière est celle que je montrerais si quelqu’un demandait pourquoi s’embêter avec tout ça. NetBox sait que l’équipement fait 1U, sait ce qu’il y a dans l’armoire, et ne vous laissera pas enregistrer deux choses au même endroit. Votre tableur vous laissera faire ça tout l’après-midi sans dire un mot, et vous le découvrirez quand quelqu’un sera debout dans la salle avec un carton dans les mains.
Rien de tout cela n’est configurable et rien ne devrait l’être. C’est la différence entre un enregistrement et un souhait.
À l’usage : ce que chaque écran vous donne
Le parc chargé, voici ce que vous en ressortez réellement. Tout ce qui suit est cette même instance : une baie, quatorze équipements, câblés et adressés chez trois clients.
Un équipement, ce sont ses composants
Entrez dans l’un de ces hyperviseurs et l’onglet intéressant n’est pas le résumé, ce sont les interfaces.

Lisez une ligne en travers. L’interface, sa vitesse, ce que vous avez écrit à son sujet, l’adresse dessus, l’étiquette du câble, et le port à l’autre bout. C’est une seule requête, et c’est la réponse à la question que tout le monde pose vraiment, à savoir « qu’est-ce qui est branché là ».
Remarquez où se trouve l’adresse. Elle est sur eno1, pas sur le serveur. Cela ressemble à du pédantisme jusqu’au jour exact où vous avez une machine avec une interface de management, deux interfaces de données et une loopback, et où quelqu’un demande quelle adresse répond pour elle. Un modèle qui accroche les adresses aux équipements ne peut pas vous le dire. Celui-ci peut, et il peut aussi contenir le cas parfaitement ordinaire de quatre adresses sur une seule interface.
Suivre le câble
Les câbles se terminent sur des composants, pas sur des équipements. Une interface à un bout, une interface à l’autre, ou un front port, un rear port, une prise électrique, une terminaison de lien. C’est le détail sur lequel repose toute la fonctionnalité, et c’est pourquoi la liste d’interfaces ci-dessus pouvait afficher l’autre bout dans sa propre colonne sans qu’on le lui dise.
Modélisez un câble d’équipement à équipement et vous avez dessiné une image. Terminez-le sur les ports et NetBox peut le parcourir.

Un saut ici, parce que c’est un câble à attache directe. Mettez des panneaux de brassage au milieu et il les parcourt, panneau par panneau, et vous dit ce qu’il y a au bout d’un cheminement à travers trois armoires. C’est le travail qui demande sinon une lampe torche et quelqu’un qui tient l’autre bout d’une sonde.
Le tracé affiche aussi l’emplacement complet de chaque extrémité. Site, salle, armoire, face, unité. Si vous avez déjà été au téléphone à essayer d’expliquer à un technicien sur place quelle machine regarder, cette ligne est toute la valeur.
Les adresses, en arbre plutôt qu’en onglet
L’IPAM est la moitié pour laquelle les gens viennent.

L’indentation est calculée depuis les adresses elles-mêmes. Vous ne dites pas à NetBox que 10.20.20.0/24 est à l’intérieur de 10.20.0.0/16, il le déduit, et il continuera de le déduire quand quelqu’un ajoutera un /26 au milieu l’année prochaine.
Chaque ligne porte les choses sur lesquelles vous filtrez réellement : le VLAN vers lequel elle pointe, le rôle, et le client à qui elle appartient. L’utilisation est calculée aussi.
Ouvrez-en un et vous obtenez les adresses qu’il contient, et les trous.

Ces lignes vertes sont l’espace libre, affiché dans le fil de l’espace utilisé. Il existe un appel d’API qui vous rend la prochaine adresse libre d’un préfixe, et c’est le morceau d’automatisation IPAM qui se rentabilise immédiatement, parce que c’est ce que les gens font sinon en plissant les yeux sur un tableur et en espérant.
Une interface prend autant d’adresses que vous voulez, des deux familles, et vous en désignez une de chaque comme primaire de l’équipement.

Deux IPv4 et deux IPv6 sur un seul port, ce qui est un mardi ordinaire et quelque chose qu’un modèle centré sur l’équipement ne peut pas exprimer du tout.
Saisissez les adresses avec le masque du réseau sur lequel elles se trouvent, pas en /32. NetBox acceptera 10.20.20.11/32 et le classera sous le bon préfixe, car l’appartenance est déduite de l’adresse hôte. Ce qu’il ne fera pas, c’est vous corriger après coup : le masque est stocké exactement comme tapé et rendu tel quel à tout ce qui le lit, donc un /32 sur une adresse de LAN rend un /32 dans la configuration de l’équipement. L’endroit où ça mord, c’est trois mois plus tard dans un gabarit de configuration, pas aujourd’hui dans le formulaire.
Une installation, trois clients
La plupart des objets de NetBox peuvent être affectés à un tenant. Une entreprise s’en sert pour ses divisions. Si vous vendez des services managés, vous en créez un par client.

Ce panneau de droite est la réponse à « qu’est-ce que ce client possède », et il s’est assemblé tout seul. Pas de rapport, pas de tableur, pas besoin de demander à l’ingénieur qui l’a construit.
Il vaut la peine d’être précis sur ce que signifie la notion de tenant, car se tromper au premier jour, c’est une année à démêler plus tard. Un tenant signifie que l’objet est dédié à ce client. Un routeur qui ne sert que lui reçoit son tenant. Un pare-feu qui en sert quatre n’appartient à aucun d’eux, donc il n’en reçoit aucun, et la relation va ailleurs. Davantage là-dessus plus bas, car c’est le point où la plupart des gens découvrent qu’ils doivent ajouter quelque chose à eux.
Mettez les chiffres sur le type d’équipement
C’est l’étape qui sépare un inventaire de quelque chose qui répond aux questions, et elle coûte environ dix minutes par type d’équipement.
Un device type peut porter son poids et, via ses gabarits de power port, sa consommation électrique. Mettez-les une fois sur le type et chaque équipement que vous créerez jamais à partir de lui en hérite. Omettez-les et NetBox vous dira volontiers qu’une armoire est remplie à 28,6 % et rien d’autre.

J’ai mis un poids sur les cinq types présents, donné à chacun deux gabarits de power port avec une consommation maximale et une consommation allouée, donné au type PDU une entrée et douze prises, puis créé un power panel et deux arrivées vers l’armoire et câblé l’ensemble : chaque PSU1 d’équipement vers la PDU A, chaque PSU2 vers la PDU B, et l’entrée de chaque PDU vers son arrivée.

Alors la page de la baie change complètement.

Utilisation de l’espace 28,6 %. Utilisation électrique 90,7 %.
Cette armoire est remplie à un tiers de matériel et presque à bout d’électricité, et c’est un fait sur votre parc qu’aucun tableur ne vous proposera jamais de lui-même. C’est aussi le fait qui décide si la prochaine commande sera rackée là ou ailleurs, et il est tombé de données que vous avez saisies une fois, sur les types.
Deux choses qui le laisseront à zéro
J’ai eu 0,0 % au début, deux fois, et les deux causes méritent d’être connues parce qu’aucune ne produit d’erreur.
Les prises doivent référencer l’entrée. Une prise électrique sur une PDU a un champ power_port qui pointe vers le port amont du même équipement. Laissez-le vide et la chaîne est rompue : NetBox n’a aucun moyen de savoir que ces douze prises sont alimentées par cette entrée, donc rien ne s’agrège.
Laissez vides les champs de consommation de l’entrée. C’est le point contre-intuitif. NetBox ne calcule la consommation d’un power port à partir de ce qui y est branché que si ses deux propres champs de consommation sont vides :
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, ...}
J’avais obligeamment mis maximum_draw: 7400 sur l’entrée de la PDU, parce que c’est ce pour quoi la PDU est dimensionnée. NetBox m’a donc cru, a pris allocated_draw comme non défini, et a rapporté zéro. Effacez les deux et il le calcule :
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
La règle est donc : mettez de vrais chiffres sur les feuilles, et laissez les ports intermédiaires vides pour que NetBox puisse les additionner. Une valeur définie administrativement gagne toujours contre la valeur calculée, ce qui est un comportement correct et exactement la mauvaise chose à faire sur une PDU.
Le refroidissement, qui est nouveau
La version 4.7 a ajouté le refroidissement au DCIM, et il est arrivé avec la moitié qui devient coûteuse. Une baie porte une cooling capability air, hybride ou liquide et une capacité en kilowatts ; un device type porte une cooling method. Au-dessus siègent des cooling sources pour les groupes froids et les armoires de climatisation, des cooling feeds représentant une boucle vers une baie, et des composants d’admission et de sortie sur les équipements eux-mêmes pour les plaques froides et les collecteurs.
L’armoire ci-dessus affiche Hybrid, 15,00 kW, parce que je l’ai dit à la baie. Si vous prenez livraison de matériel refroidi au liquide cette année, voilà un modèle pour la chose que vous gardez actuellement dans un tableur.
Une installation, beaucoup de clients
La notion de tenant est la raison pour laquelle un prestataire peut faire tourner un seul NetBox plutôt qu’un par client, et il vaut la peine de comprendre ce qu’elle fait et, plus important, ce qu’elle ne fait pas.
Vingt-huit des modèles de NetBox portent un champ tenant. Sites, locations, baies et réservations de baies. Équipements, câbles et virtual device contexts. Préfixes, adresses IP, plages, aggregates, VLAN, VLAN groups, VRF, route targets, ASN. Liens opérateur et circuit groups. Clusters et machines virtuelles. Tunnels, L2VPN, réseaux et liaisons sans fil. Power feeds et cooling feeds.
C’est tout nom facturable. Renseignez-le de façon cohérente et « qu’est-ce que ce client possède » cesse d’être une enquête.
Mais un tenant est une étiquette, pas un verrou. Il dit que l’objet est dédié à ce client. Il n’empêche personne qui peut se connecter de lire l’ensemble, ce qui va très bien au sein d’une seule entreprise et ne sert plus à rien dès qu’un client a un compte.
Deux choses en découlent, et la seconde est celle que les gens ratent.
Un tenant signifie dédié. Un routeur qui ne sert qu’un client reçoit son tenant. Un pare-feu qui en sert quatre n’appartient à aucun, donc il ne reçoit rien, et vous consignez la relation sur la chose que vous avez réellement vendue. Forcer un tenant sur du matériel partagé rend discrètement faux chaque rapport bâti sur les tenants.
Et l’accès est un mécanisme entièrement distinct.
C’est dans les permissions que vit l’isolation
Les object permissions de NetBox prennent une contrainte JSON, et la contrainte est un filtre de l’ORM Django. Elle restreint le queryset avant que quoi que ce soit n’en soit construit, donc chaque vue, chaque export, chaque appel d’API et chaque recherche est restreint avec elle.

Trois champs font le travail. Les types d’objets auxquels elle s’applique, les actions qu’elle accorde, et cette contrainte en bas. Tout le reste est de la comptabilité.
Voici la liste d’équipements en tant qu’administrateur.

Et voici la même URL, la même installation, connecté en tant que client.

Deux lignes au lieu de quatorze, et regardez le menu de gauche. Il s’est réduit aux quatre choses que ce compte a le droit de toucher. Personne n’a configuré ça. La navigation est construite depuis les mêmes permissions, donc un client ne voit jamais un lien vers quelque chose qui le refuserait.
Les deux façons de dire non
C’est le détail à connaître, car les deux refus signifient des choses différentes et les deux sont délibérés.
| Le jeton du client demande | Il obtient |
|---|---|
/api/dcim/devices/ | 200, une ligne sur deux |
/api/dcim/devices/1/, le sien | 200 |
/api/dcim/devices/2/, celui d’un autre | 404 |
/api/tenancy/tenants/ | 403 |
/api/dcim/sites/, jamais accordé | 403 |
| aucun jeton du tout | 403 |
404 signifie que le type est à vous mais que cette ligne ne l’est pas. La contrainte l’a retirée du queryset, donc du point de vue de la requête elle n’existe pas. Un 403 à cet endroit confirmerait qu’elle existe, et permettrait à un client curieux de compter votre parc en parcourant les identifiants.
403 signifie que le type ne fut jamais à vous. Les tenants et les sites n’ont jamais été accordés, donc ils refusent d’emblée, et le client ne peut pas énumérer qui d’autre est sur la plateforme.
Puis laissez-les écrire
La lecture seule est le cas facile. La vraie question est de savoir si on peut confier des droits d’édition à un client, j’ai donc accordé change sous la même contrainte et je suis allé chercher la sortie.
| Tentative | Résultat |
|---|---|
| Éditer son propre équipement | 200, enregistré |
| Éditer l’équipement d’un autre client | 404 |
| Éditer son propre équipement en le déplaçant vers le tenant de l’autre client | 403 |
| Supprimer son propre équipement | 403, delete n’a jamais été accordé |
La troisième ligne est celle qui compte. Réaffecter son propre équipement au tenant de quelqu’un d’autre est l’échappatoire évidente, car l’objet est à l’intérieur de votre contrainte à l’arrivée de la requête et à l’extérieur ensuite. NetBox évalue la contrainte contre l’état dans lequel l’objet serait laissé, donc il refuse. J’ai relu l’équipement plutôt que de me fier au code de statut, et le tenant n’avait pas bougé.
C’est le trou que laisse ouvert la plupart des systèmes multi-clients faits maison, et il est normalement trouvé par un client plutôt que par un test.
Les variables qui doivent s’hériter
Voici la distinction que les gens ratent, et la réussir épargne beaucoup d’édition.
Un custom field est une valeur sur un objet. Vous la fixez objet par objet, et elle y reste. Bien pour un fait sur la chose elle-même : un numéro d’inventaire, un numéro de contrat de support, une date de mise en service.
Un config context est une valeur attachée à une caractéristique, dont tout ce qui correspond à cette caractéristique hérite. Bien pour une variable qui doit ruisseler : vos serveurs NTP, vos cibles syslog, votre communauté SNMP, votre domaine DNS, votre VLAN de management, votre fenêtre de sauvegarde.
Si vous vous surprenez à fixer le même custom field à la même valeur sur quarante équipements, c’est un config context que vous vouliez.

Un context est du JSON arbitraire, et il peut être attaché à une region, un site group, un site, une location, un device type, un rôle, une platform, un cluster, un cluster type, un cluster group, un tenant group, un tenant ou un tag. Tenant figure dans cette liste, ce qui, pour un prestataire, signifie qu’un fait vrai d’un client partout le suit sur chaque équipement que vous ajouterez jamais pour lui.
La fusion se fait par clé, et le poids décide de qui gagne chacune.

Lisez le panneau de droite contre celui de gauche. La region fournit quatre clés au poids 1000. Le site fournit une clé au poids 2000. Le context rendu garde intacts le domaine, les serveurs NTP et la communauté de la region, et prend le serveur syslog du site, parce que c’est la seule clé que quelque chose a disputée.
Vous écrivez l’exception, pas une copie neuve de tout avec l’exception dedans. C’est toute la valeur, et c’est pourquoi cela passe à l’échelle là où un fichier de variables par équipement ne le fait pas.
Le context local gagne contre tout ce qui est au-dessus
La pile d’héritage a un sommet, et c’est l’objet lui-même. Les local context data sur un équipement gagnent contre chaque source context qui s’applique à lui, quels que soient les poids.
J’ai mis ceci sur mcr1-core-01 :
{"syslog_servers": ["10.20.10.99"], "note": "this box logs somewhere else"}
et son context rendu est devenu :
{
"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"]
}

Le serveur syslog local a battu à la fois la surcharge du site au poids 2000 et la region au poids 1000. Tout ce dont il n’a rien dit a quand même été hérité, donc le domaine, les serveurs NTP et la communauté sont passés intacts, et la nouvelle clé a simplement été ajoutée.
C’est la trappe de secours pour la seule machine qui est vraiment différente, et le panneau vous dit clairement qu’elle écrase tous les source contexts. Si vous vous surprenez à l’utiliser sur beaucoup d’équipements plutôt que sur un, vous avez trouvé une caractéristique que ces équipements partagent et c’est un context cadré dessus que vous vouliez.
Trois pièges
Un context sans portée est global. Créez-en un et oubliez de l’affecter à quoi que ce soit, et il s’applique à tous les équipements et à toutes les machines virtuelles que vous avez. C’est un comportement documenté et parfois ce que vous voulez. C’est aussi silencieux.
Les clés ne peuvent pas avoir de tirets si vous voulez les atteindre simplement. Les données de context sont du JSON, donc ntp-servers est parfaitement légal, mais les noms de variables Jinja ne sont pas des clés JSON. {{ ntp-servers }} est analysé comme une soustraction et lève UndefinedError: 'ntp' is undefined. Écrivez {{ ntp_servers }} contre des données à tirets et vous obtenez une chaîne vide, un HTTP 200, et aucun avertissement nulle part. Utilisez des tirets bas dans les clés et le gabarit évident fonctionne.
Un profil peut arrêter les fautes de frappe. Un profil de config context regroupe des contexts apparentés et impose un schéma JSON sur leurs données au moment de l’enregistrement. J’en ai doté un d’un schéma exigeant syslog_servers comme tableau de chaînes IPv4, puis j’ai fait les deux erreurs que les gens font vraiment :
{"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
Rejetées là où quelqu’un les a faites, plutôt que quatre cents équipements plus tard.
Rendre la configuration
Des données de context plus un gabarit Jinja vous donnent un fichier de configuration. L’équipement est dans la portée sous device, son context fusionné est dans la portée sous forme de variables ordinaires, et vous pouvez parcourir ses composants.

Tout sur cette page vient d’un endroit différent, et c’est tout le sujet :
| Ligne | D’où elle vient |
|---|---|
host-name mcr1-core-01 | de l’équipement |
domain-name mcr1.example.net | du config context régional |
location "Manchester DC1 / MCR1-A07 / U39" | du site, de la baie et de la position dedans |
server 172.16.10.22 | du context régional, poids 1000 |
host 10.20.10.99 any notice | du propre context local de l’équipement, battant le site et la region |
family inet address 10.20.10.11/24 | de l’adresse sur cette interface, avec son masque tel que tapé |
Le gabarit est résolu par l’équipement, puis le rôle, puis la platform, et la requête échoue si aucun des trois n’en a. Vous affectez donc un gabarit à une platform une fois, et chaque équipement faisant tourner ce logiciel se rend depuis lui, sauf si son rôle ou l’équipement lui-même en dit autrement. C’est à cela que sert une platform : le système d’exploitation ou la famille logicielle du constructeur, pas le matériel.
NetBox rend. Il ne pousse pas. Amener la sortie sur la machine est le travail de votre automatisation, et le jour où un bug de gabarit aurait sinon reconfiguré quatre cents équipements, vous serez content que ce soient deux systèmes différents.
Les plugins
Un plugin est une application Django installée aux côtés de NetBox. Il peut ajouter des modèles, ajouter des pages, étendre les deux API, injecter du contenu dans des gabarits existants, ajouter de la navigation, ajouter des files de tâches de fond et charger d’autres applications Django. Il y a très peu de choses qu’il ne peut pas faire, car en dessous ce n’est que du Django.
En installer un, c’est quatre commandes et un redémarrage :
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
Sur la pile de conteneurs, les mêmes paquets vont dans plugin_requirements.txt et vous reconstruisez l’image. Dans les deux cas, épinglez les versions et vérifiez d’abord la matrice de compatibilité, car un plugin qui n’a pas suivi une version de NetBox refusera de démarrer toute l’application plutôt que de se désactiver lui-même.

Le catalogue publié en recense 31. Voici ceux qui méritent d’être connus, avec la licence sous laquelle chacun est réellement livré :
| Plugin | Ce qu’il fait | Licence |
|---|---|---|
| DNS | Zones, enregistrements et serveurs de noms comme source de vérité | MIT |
| BGP | Sessions, communautés et politiques de routage | Apache 2.0 |
| Topology Views | Cartes de topologie graphiques construites depuis vos câbles | Apache 2.0 |
| Floorplan | Plans graphiques de sites et de locations | LGPL 3.0 |
| QR Code | Des codes sur les baies, les équipements et les câbles, pour les étiquettes d’inventaire | Apache 2.0 |
| ACLs | Listes d’accès et règles | Apache 2.0 |
| Prometheus SD | Sert à Prometheus sa liste d’hôtes directement depuis NetBox | MIT |
| Documents | Des documents rattachés aux liens opérateur et aux équipements | Apache 2.0 |
| Lifecycle | Fin de vie du matériel, licences et contrats | Apache 2.0 |
| Contract | Contrats et factures | MIT |
| Reorder Rack | Glisser-déposer des unités de baie | Apache 2.0 |
| Branching | Branches isolées et fusionnables de vos données | NetBox Limited Use |
| Custom Objects | De nouveaux types d’objets, définis dans l’interface | NetBox Limited Use |
Prometheus SD est la forme honnête de toute l’idée. NetBox sait ce qui existe, donc laissez NetBox le dire au système de supervision, et arrêtez d’entretenir une seconde liste d’hôtes qui dérive.
Une chose à savoir avant de bâtir sur les deux dernières lignes. NetBox lui-même est en Apache 2.0 et l’est depuis que DigitalOcean l’a publié en 2016, et le projet est aujourd’hui pris en charge par NetBox Labs aux côtés d’une équipe de mainteneurs bénévoles.21 NetBox Branching et NetBox Custom Objects ne le sont pas : ils sont livrés sous la NetBox Limited Use License 1.0, qui accorde l’usage « 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 », et qui n’accorde pas le droit d’utiliser le logiciel « 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 ». Si vous avez installé NetBox Community depuis GitHub et que vous l’exploitez pour le compte de clients, lisez les termes vous-même avant de mettre un schéma derrière eux.10 Le guide d’installation de NetBox recommande les deux plugins sans le mentionner.11
Les custom fields
Un custom field ajoute un attribut à un modèle existant. Les valeurs sont stockées en JSON à côté de chaque objet, donc pas de migration et pas de redémarrage, et il existe treize types dont des références objet et multi-objets vers d’autres enregistrements NetBox.

Deux champs là, regroupés sous un titre « Asset » que j’ai choisi. Remarquez le panneau Dimensions à leur droite : 9,5 kg, que personne n’a tapés sur cet équipement. Ils viennent du type d’équipement.
Les custom fields sont validés, et il vaut la peine de savoir qu’ils le sont, car c’est une vraie différence avec la voie ci-dessous. J’ai donné au champ de contrat une expression régulière et l’API l’a imposée :
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}$'"]}
Deux choses ont changé en 4.7 qui comptent à grande échelle. Créer un champ avec une valeur par défaut, ou supprimer un champ, doit réécrire les données stockées de chaque objet auquel il s’applique, donc sur une grosse table ce travail est confié à une tâche de fond et le champ rapporte « provisioning » ou « deleting » pendant qu’elle tourne. Un champ n’est vivant que tant qu’il est actif : pendant l’une ou l’autre opération il n’apparaît ni sur les objets, ni dans les formulaires, ni dans les filtres, ni dans aucune des deux API. Cela exige un worker en marche, sinon il reste indéfiniment dans cet état.
Utilisez un custom field quand vous ajoutez un fait sur la chose elle-même. Utilisez un config context quand la valeur doit ruisseler. Utilisez la section suivante quand la chose dont vous avez besoin n’existe pas.
Menus déroulants personnalisés, et surcharge de ce qui est livré
Deux mécanismes différents vivent sous ce titre et ils résolvent des problèmes différents. L’un est pour vos propres champs. L’autre réécrit ceux de NetBox.
Un choice set, pour votre propre champ de sélection
Un custom field de type « selection » tire ses options d’un choice set, un objet que vous gérez dans l’interface comme n’importe quoi d’autre. Ainsi « support tier » devient une vraie liste déroulante plutôt qu’un texte libre que quelqu’un écrira de trois façons.

C’est imposé, y compris via l’API :
PATCH {"custom_fields": {"support_tier": "platinum"}}
{"__all__": ["Invalid value for custom field 'support_tier':
Invalid choice (platinum) for choice set Support tier."]}
Les choice sets sont partagés, donc un seul set peut alimenter le même champ sur plusieurs modèles, et changer la liste en un endroit la change partout.
FIELD_CHOICES, pour les champs propres à NetBox
C’est celui dont les gens ignorent l’existence. Plusieurs des champs de sélection intégrés de NetBox peuvent être étendus ou remplacés depuis configuration.py, dont le statut d’équipement, le statut de site, le statut de baie, le statut de lien opérateur et bien d’autres.12
Ajoutez un signe plus pour compléter ce qui est livré. Omettez-le pour remplacer la liste entièrement.
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'),
),
}
J’ai mis exactement cela sur cette instance. Le statut d’équipement est revenu avec les sept valeurs d’origine et mes deux :
offline, active, planned, staged, failed, inventory, decommissioning,
burn-in, awaiting-rma
et le statut de site est revenu avec seulement les miennes, la liste d’origine disparue :
surveyed, building, active, closing
Un choix peut être un simple tuple de valeur, libellé et couleur, ou un dictionnaire qui accepte aussi une description affichée en sous-titre dans le formulaire. Et ils se comportent partout comme des valeurs natives, car pour le reste de NetBox ce sont des valeurs natives :

Ce badge est mon statut, dans ma couleur, qui se trie et se filtre comme n’importe quel autre.
Trois choses à savoir avant de l’utiliser.
Remplacer supprime les valeurs d’origine du menu, pas de la base. Tout objet qui en détient déjà une la garde, mais la valeur n’est plus proposée et ne sera plus un choix valide la prochaine fois que quelqu’un éditera cet objet. Si vous remplacez une liste, vérifiez que rien ne repose sur une valeur que vous venez de retirer.
Cela vit dans le fichier de configuration, donc il faut un redémarrage et ce n’est pas quelque chose qu’un utilisateur de l’interface peut changer. Pour un prestataire, c’est le bon sens de la chose : l’ensemble des statuts que votre activité reconnaît est une décision de gouvernance, pas une décision d’un mardi après-midi.
Étendez avant de remplacer. Les valeurs d’origine sont ce que chaque plugin, script et intégration s’attend à voir. Ajouter ne coûte rien. Remplacer est une décision que vous possédez pour toujours.
Les custom objects
Voici une vraie lacune. NetBox modélise les clusters, les machines virtuelles et les disques virtuels. Il ne modélise pas le datastore sur lequel ces disques vivent réellement, et pour quiconque fait tourner Proxmox ou VMware c’est l’objet qui relie le stockage que vous avez acheté à la charge qui l’utilise.
Le plugin Custom Objects vous laisse définir un nouveau type d’objet depuis l’interface ou l’API, sans écrire de code. Donc :

Sept champs, dont deux sont tout le sujet. cluster est une référence objet unique vers un vrai cluster NetBox, réglée sur protect pour que personne ne puisse supprimer un cluster sous son stockage. provisioned_by est une référence multi-objets vers les équipements qui le servent réellement.
Cela vous donne un objet de première classe avec sa propre entrée de navigation, sa vue en liste, ses filtres, son import et son export :

Et comme les références sont réelles, la relation apparaît aussi depuis l’autre bout. Ouvrez le cluster et les datastores y sont listés.

Un custom object type hérite de l’essentiel de ce qui fait d’un objet NetBox un objet NetBox : vues en liste et en détail, une entrée de navigation, des endpoints REST, la recherche plein texte, la journalisation des changements, le journal, les tags, les favoris, l’import et l’export, les event rules et les notifications. Rien de tout cela n’a eu à être écrit.
Ce n’est pas non plus un blob JSON qui se fait passer pour une table. Le plugin émet du vrai DDL, et ce qui apparaît dans PostgreSQL est une vraie table avec de vraies contraintes :
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
Le protect que j’ai demandé est devenu ON DELETE RESTRICT, et ça marche : supprimer ce cluster revient en 409 en nommant ce qui en dépend.
Deux choses à savoir avant de vous y fier
La validation est sur le formulaire, pas sur l’API. C’est celle qui piégerait une automatisation. J’ai déclaré required, une expression régulière et des bornes numériques sur les champs d’un custom object type, puis j’ai écrit dessus par l’API REST :
| Ce que j’ai déclaré | Ce que j’ai envoyé | Résultat |
|---|---|---|
validation_regex | une valeur qui ne correspond à rien | 201 Created |
required: true | le champ entièrement omis | 201 Created |
validation_minimum: 1 | un nombre négatif | 201 Created |
unique: true | un doublon | 400, rejeté |
on_delete_behavior: protect | supprimer l’objet référencé | 409, rejeté |
Les deux qui ont tenu sont les deux qui sont devenues des contraintes en base. Le reste n’existe que sur le formulaire web, donc une personne qui tape est contrainte et une synchronisation nocturne ne l’est pas. Comparez avec le custom field du cœur plus haut, où la même expression régulière était imposée par l’API. Jusqu’à ce que cela change, mettez tout ce dont dépend une facture derrière une vraie contrainte.
Supprimer un type supprime une table. Supprimer un champ supprime une colonne. C’est du DDL depuis un formulaire web, exécuté par quiconque a la permission, et la documentation le dit telle quelle.13 Restreignez qui peut les supprimer.
Quand écrire un vrai plugin à la place
Si l’objet compte, si de l’automatisation écrit dessus, et si vous seriez contrarié d’y trouver n’importe quoi, écrivez le modèle vous-même. Un plugin NetBox minimal, c’est un PluginConfig, un modèle, un serializer, un viewset, une table, un formulaire, quelques vues et une carte d’URL, et cela tient en moins de deux cents lignes majoritairement déclaratives. Dériver PrimaryModel vous donne gratuitement les tags, les custom fields, la journalisation des changements, le journal, les gabarits d’export et la propriété, et chaque contrainte que vous posez sur le modèle est imposée partout, car la machinerie de serializer de NetBox l’exécute.
Il existe un gabarit cookiecutter et un tutoriel de plugin complet maintenus par la communauté. Partez de là plutôt que d’un répertoire vide.
Et c’est la licence que vous choisissez, ce que personne ne peut changer ensuite.
Validation personnalisée et classes de validation
Tout ce qui précède porte sur l’enregistrement de ce qui est là. Ceci porte sur le refus d’enregistrer ce qui ne devrait pas l’être.
NetBox valide chaque objet avant de l’écrire, et vous pouvez ajouter vos propres règles par-dessus. Il y a trois mécanismes, ils vivent tous dans configuration.py, et entre eux ils couvrent presque tout ce dont un standard maison a besoin.
Un : des règles simples, sans code
Un validateur peut être une simple correspondance entre des noms de champs et des conditions. Pas de Python, et c’est portable d’une installation à l’autre parce que ce ne sont que des données.
CUSTOM_VALIDATORS = {
'dcim.site': (
{'description': {'required': True}},
),
'dcim.device': (
{'name': {'regex': r'^[a-z0-9]+-[a-z]+-([0-9]{2}|[a-z])$'}},
),
}
Les conditions disponibles sont min, max, min_length, max_length, regex, required, prohibited, eq et neq. Vous pouvez atteindre un objet lié par un chemin pointé, donc region.name sur un site est permis, et vous pouvez filtrer sur request.user.username même si la documentation vous dit, à juste titre, d’utiliser plutôt les permissions pour cela.
Ces deux règles se déclenchent immédiatement :
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.']"]}
La seconde est un standard de nommage, imposé. Pas écrit sur une page de wiki, pas dans la tête de quelqu’un, pas une chose pour laquelle le nouveau se fait reprendre en revue trois semaines plus tard. La base ne l’accepte pas.
Deux : une classe de validateur, quand la règle est une phrase
Les règles simples vérifient un champ contre une constante. Les vraies règles maison sont généralement conditionnelles : ceci ne compte que quand cela. Pour celles-là vous dérivez CustomValidator, surchargez validate() et appelez fail().
J’en ai mis trois dans un module en /opt/netbox/netbox/house_rules.py :
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',
)
et je les ai câblées par chemin pointé :
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',
),
}
Notez qu’un modèle prend un tuple de validateurs, règles simples et classes librement mélangées, et qu’ils s’exécutent tous. Même un validateur unique doit être passé comme itérable, et c’est facilement cinq minutes de perdues.
Ensuite les règles font ce qu’elles disent :
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
Parce que fail() prend un field, le message atterrit sur la bonne case du formulaire plutôt qu’en haut de la page, et c’est la différence entre une règle que les gens apprennent et une règle que les gens prennent mal.
C’est aussi la réponse à la lacune de la section sur les custom objects. La validation là-bas n’existait que sur le formulaire web. Un CustomValidator s’exécute dans la couche modèle, donc il s’applique à l’interface, à l’API REST, aux mutations GraphQL, aux imports en masse et à tout ce que fait un script. Il y a un seul endroit où écrire la règle et aucun moyen de la contourner.
Trois : les protection rules, pour la suppression
CUSTOM_VALIDATORS garde les écritures. PROTECTION_RULES garde les suppressions, et il prend exactement les deux mêmes formes.
PROTECTION_RULES = {
'dcim.device': (
{'status': {'eq': 'offline'}},
),
}
Cela dit qu’un équipement ne peut être supprimé que lorsqu’il est hors ligne. Ce qui produit :
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
Deux frappes de clavier de friction entre quelqu’un et un équipement en service, et c’est l’assurance la moins chère de toute l’application. Faites du processus de décommissionnement la chose qui déverrouille le bouton de suppression.
Celle qui vous attrapera
Les données existantes ne sont pas vérifiées avant que vous les touchiez à nouveau. Ajouter une règle ne revient pas en arrière valider ce qui est déjà là. Elle reste tranquille jusqu’à ce que quelqu’un enregistre un objet qui la viole, et alors cette personne reçoit une erreur à propos d’une décision à laquelle elle n’a rien eu à voir.
Je l’ai vu arriver. mcr1-hv-06 a été créé avant que j’écrive quoi que ce soit de tout ceci, en hyperviseur sans tenant. Il était là, parfaitement tranquille. Puis j’ai édité sa description :
PATCH {"description": "touching it to trigger revalidation"}
{"tenant": ["A Hypervisor is billable kit, so it must name
the customer it belongs to."]}
L’édition n’avait rien à voir avec le tenant. La règle s’est déclenchée quand même, car la validation porte sur l’objet entier.
C’est un comportement correct et c’est aussi comment une nouvelle règle se transforme en ticket de support. Avant d’en activer une, lancez une requête sur les objets qui échoueraient et corrigez-les d’abord. L’API rend cela facile : la règle est un filtre, donc demandez les équipements ayant ce rôle et aucun tenant, et vous avez votre liste.
Activez la règle ensuite. Alors elle n’attrape plus que les nouvelles erreurs, et c’est à cela qu’elle sert.
Le piloter depuis Ansible
Une source de vérité que personne ne lit pourrit. La collection netbox.netbox est la façon dont la plupart des gens empêchent cela, et elle fonctionne dans les deux sens : NetBox dit à Ansible ce qui existe, et Ansible dit à NetBox ce qu’il a construit.
Elle est en version 3.23.0, sous licence GPL-3.0, et a été téléchargée depuis Galaxy plus de 13,4 millions de fois. Elle porte 91 modules et un plugin d’inventaire.14 Tout ce qui suit a été exécuté contre la même instance, depuis un conteneur ne contenant rien d’autre qu’ansible-core 2.21.4, pynetbox 7.8.0 et la collection.
L’inventaire est une requête, pas un fichier
C’est la moitié qui se rentabilise dès le premier après-midi. nb_inventory construit votre inventaire Ansible directement depuis 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
Cela produit ceci, sans que personne n’entretienne une liste d’hôtes :
@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
Regardez la colonne de droite. Parce que les tenants sont renseignés sur les équipements, vous obtenez gratuitement un groupe par client, donc --limit tenants_ravenscroft-legal exécute un play contre exactement le matériel d’un seul client. Ajoutez un équipement dans NetBox et il est dans le groupe à l’exécution suivante. Décommissionnez-en un et il disparaît. Personne n’édite quoi que ce soit.
Chaque hôte arrive porteur de ce que NetBox sait de lui :
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"}
Vingt-deux clés en tout, et l’une d’elles vaut la peine d’être remarquée avant qu’elle ne vous surprenne. ansible_host est l’adresse IPv6, parce que cet équipement a une primaire v6 renseignée. Je l’ai vérifié contre deux autres qui n’ont que de l’v4 et ils sont revenus en v4, donc la règle est que le plugin préfère l’v6 là où une primaire v6 existe. Ce qui est correct, et c’est aussi le genre de chose qu’on préfère découvrir maintenant plutôt qu’en se demandant pourquoi un play se connecte par un chemin auquel on n’avait pas pensé.
Écrire en retour
Les modules sont l’autre sens, et celui qu’il faut avoir est l’attribution d’adresses IP, car NetBox sait ce qui est libre et votre playbook non.
- 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
Remarquez ce qui n’y est pas. Aucune adresse IP. Vous nommez le préfixe et NetBox vous rend la prochaine libre :
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)"
Et c’est là dans l’interface une seconde plus tard, câblé à rien encore mais racké, rattaché et adressé :

Le piège dans ce playbook
Exécutez-le une seconde fois sans changer une ligne et voici ce qui arrive :
"device : mcr1-hv-07 (already correct)"
"interface: eno1 (already correct)"
"address : 10.20.22.2/24 (allocated)"
L’équipement et l’interface sont idempotents. L’adresse ne l’est pas, et ce n’est pas un bug. state: present signifie « fais que ça ressemble à ceci ». state: new signifie « donne-m’en une nouvelle », à chaque fois, et c’est exactement ce qu’il a fait : deux adresses sur une interface après deux exécutions.
C’est très bien quand vous provisionnez réellement quelque chose de neuf, et cela mangera discrètement un préfixe si vous le mettez dans une tâche qui tourne chaque nuit. Attribuez une fois et consignez le résultat, ou utilisez state: present avec l’adresse que vous détenez déjà. Le module fait ce que vous avez demandé. La question est de savoir si vous avez demandé ce que vous vouliez dire.
Ce n’est pas seulement Ansible
La collection attire l’attention parce qu’Ansible est là où la plupart des équipes réseau se trouvent déjà, mais l’API est le produit et bien des choses la parlent.
| Chose | Ce que c’est | Licence |
|---|---|---|
pynetbox | Le client Python que la collection utilise elle-même | Apache 2.0 |
terraform-provider-netbox | Gérer les objets NetBox comme des ressources Terraform, activement maintenu par e-breuninger | MPL 2.0 |
nornir_netbox | NetBox comme inventaire Nornir, pour ceux qui font du Python plutôt que du YAML | Apache 2.0 |
| Prometheus SD | Sert à Prometheus ses cibles de scrape depuis NetBox | MIT |
go-netbox | Un client Go, même s’il n’a pas été touché depuis mai 2025 | voir le dépôt |
| Diode | Le pipeline d’ingestion de NetBox Labs pour pousser des données découvertes | NetBox Limited Use |
L’entrée Terraform est l’intéressante pour quiconque gère déjà son infrastructure ainsi, car elle permet à un seul plan de créer la ressource cloud et l’enregistrement NetBox qui la documente, plutôt que de laisser la seconde moitié à la mémoire de quelqu’un.15
Diode porte la même licence que Branching et Custom Objects, donc la même lecture s’impose avant de bâtir dessus.
Ce que l’après-midi achète vraiment
Tout ce qui précède m’a pris une journée sur une seule machine, et la majeure partie de cette journée est passée à semer des données pour que les écrans aient quelque chose dedans. L’installation, c’est vingt minutes dans les deux cas. Les décisions sont la partie qui compte et elles se prennent toutes dans la première heure : les tenants avant tout, les types d’équipement avant les équipements, les chiffres sur les types plutôt que sur le matériel.
Ce que vous obtenez pour cela n’est pas de la documentation. Ce n’est pas une distinction que l’on fait avant d’avoir eu les deux : la documentation est une chose qu’on écrit puis qu’on arrête d’entretenir. Ce que vous obtenez est une base qui refuse de contenir une contradiction : elle ne vous laissera pas mettre deux choses dans une même unité de baie, ni une baie dans une location qui appartient à un autre site, ni un équipement sur un type qui n’existe pas. Chacun de ces refus est une dispute que vous n’aurez pas dans six mois.
Et elle vous dira des choses que personne ne lui a demandées. Cette armoire est remplie à 28,6 % de matériel et à 90,7 % d’électricité. Personne n’a cherché à trouver ça. C’est tombé d’une puissance mise une fois sur un type d’équipement, et c’est la différence entre racker la prochaine commande là et l’apprendre à la dure.
La réserve honnête est la même que pour toute source de vérité. Elle ne vaut que ce qu’il vous coûte de la tenir juste, et la seule version qui survit à un trimestre chargé est celle où quelque chose casse visiblement quand les données sont fausses. Câblez votre supervision, votre provisionnement ou vos règles de pare-feu pour qu’ils y lisent, et une entrée fausse cesse d’être un problème de documentation dont quelqu’un s’occupera. Elle devient une panne à neuf heures et demie un mardi, avec un nom dessus. Cela ressemble à un coût. C’est tout le mécanisme.
Personne ne vous en remerciera non plus. Un enregistrement juste se manifeste comme la migration qui a pris quinze jours au lieu d’un trimestre, l’audit qui a pris un après-midi, le client qui a eu une réponse droite au téléphone. Rien de cela n’apparaît sur un rapport à côté de votre nom.
Faites-le quand même. L’alternative est une entreprise incapable de se décrire, et une entreprise incapable de se décrire n’est pas dirigée. Elle est remémorée, par de moins en moins de gens chaque année.
NetBox, Introduction — « Today, the open source project is stewarded by NetBox Labs and a team of volunteer maintainers », et l’origine chez DigitalOcean en 2015, passé en open source en juin 2016. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎
NetBox, LICENSE.txt — Apache License 2.0, copyright DigitalOcean, LLC. Origine et prise en charge d’après l’introduction. ↩︎ ↩︎
Notes de version de Nautobot v1.0.0 — « a divergent fork of NetBox 2.10 », publiée le 26 avril 2021, et la liste de ce qu’elle a ajouté par rapport à NetBox 2.10. ↩︎ ↩︎
Network to Code, « Why Did Network to Code Fork NetBox? », 25 février 2021 — le raisonnement sur les SLA et le support au long cours, la divergence de vision, et « the NetBox project team suggested that we should consider forking ». ↩︎
NetBox Labs a été fondée en 2023 en spin-out de NS1 après son acquisition par IBM, cofondée par le mainteneur principal de NetBox Jeremy Stretch, et a annoncé une Série A de 20 M$ en avril 2023 : NetBox Labs, « Let’s Go: Announcing NetBox Labs » et l’annonce de la Série A. ↩︎
NetBox Labs, NetBox Enterprise — l’édition commerciale autogérée, avec « 24/7 expert assistance from the NetBox Labs team » ; NetBox Cloud est l’offre hébergée. Les termes de SLA précis ne sont pas publiés sur cette page. ↩︎
Nombres d’étoiles et de forks, dates de publication et licences des deux projets relevés via l’API GitHub le 30 septembre 2026 : netbox-community/netbox et nautobot/nautobot. Les versions parallèles 3.2.x et 2.4.x de Nautobot sont toutes deux datées du 28 septembre 2026. ↩︎
netbox-community/netbox-docker — la pile de conteneurs de la communauté, Apache 2.0. ↩︎
NetBox, Installation — le tableau des versions prises en charge, et les notes de version v4.7 pour les minimums PostgreSQL et Redis relevés. Version et édition relevées dans
netbox/release.yaml. ↩︎NetBox Limited Use License 1.0 — portée par netbox-custom-objects et, à l’identique, par netbox-branching. Les deux clauses citées sont littérales. ↩︎
NetBox, HTTP Server installation, « What’s Next? » — « Some of the most popular plugins include » NetBox Branching, NetBox Custom Objects, NetBox DNS et NetBox BGP, sans aucune mention des termes de licence. ↩︎
NetBox, Data Validation configuration —
FIELD_CHOICES, et le suffixe signe plus qui étend au lieu de remplacer : « To replace the available choices, specify the app, model, and field name separated by dots … To extend the available choices, append a plus sign ». ↩︎Documentation de netboxlabs/netbox-custom-objects — « Deleting a Custom Object Type drops an entire database table and should be done with caution. » ↩︎
netbox.netbox sur Ansible Galaxy et netbox-community/ansible_modules — version 3.23.0, GPL-3.0, 91 modules plus le plugin d’inventaire
nb_inventory. Nombre de téléchargements relevé sur Galaxy le 30 septembre 2026. Tout ce qui est montré a été exécuté avec ansible-core 2.21.4 et pynetbox 7.8.0. ↩︎Licences, activité et nombres d’étoiles relevés sur chaque projet le 30 septembre 2026 : pynetbox (Apache 2.0), terraform-provider-netbox (MPL 2.0, v6.0.0-rc.1 sur le registre Terraform), nornir_netbox (Apache 2.0), netbox-plugin-prometheus-sd (MIT), go-netbox (dernier push le 9 mai 2025) et Diode, qui porte la même NetBox Limited Use License 1.0 que Branching et Custom Objects. ↩︎