Todo lo que hay en este artículo se ejecutó contra un NetBox 4.7.2 en mi propio escritorio, con un parque real cargado dentro: un emplazamiento, un armario, catorce equipos, cableados, alimentados y direccionados entre tres clientes. Cada captura es esa instancia, y cada mensaje de error es uno que obtuve de verdad.

Va en el orden en que te lo encontrarías. Qué es el bicho, por qué querrías uno, el fork del que oirás hablar en una semana de búsquedas, cómo levantar uno, el orden en que tienes que llenarlo, y luego lo que sacas de vuelta. El trabajo de personalización y extensión va al final, porque nada de eso tiene sentido antes de haber visto la forma de lo que estás extendiendo.

Qué es NetBox

NetBox es una base de datos con una opinión muy concreta sobre de qué está hecha una red, y una aplicación web encima. Es una aplicación Django sobre PostgreSQL, es open source bajo Apache 2.0 desde que DigitalOcean la publicó en junio de 2016, y el proyecto está hoy a cargo de NetBox Labs junto a un equipo de mantenedores voluntarios.12

Por debajo son 149 modelos repartidos en diez aplicaciones, alcanzables mediante 146 endpoints REST y un endpoint GraphQL. Contado sobre la instancia que construí para esto, no leído de una página de características:

AplicaciónModelosAplicaciónModelos
dcim56virtualization7
extras23tenancy6
ipam18wireless3
circuits11users7
vpn10core8

Cincuenta y seis de ellos son DCIM, la capa física: emplazamientos, ubicaciones, racks, tipos de equipo, equipos, y cada clase de puerto, bahía y terminación de cable que un equipo puede tener. Dieciocho son IPAM. El resto cubre circuitos, túneles y políticas IKE, máquinas virtuales y clústeres, enlaces inalámbricos, multicliente, y la maquinaria que hace extensible todo el conjunto.

El número no es lo importante. Los joins lo son, y la forma más rápida de verlo es la pantalla por la que NetBox es más conocido.

Empieza por el armario, porque es la pantalla que vende el producto.

Página de detalle del rack MCR1-A07 en NetBox, a la izquierda región North West, emplazamiento Manchester DC1, ubicación Hall 2, estado Active y 28,6 % de utilización de espacio, y a la derecha las elevaciones frontal y trasera con paneles de parcheo en U41 y U42, dos routers en U38 y U39, dos conmutadores en U35 y U36 y seis hipervisores entre U15 y U20, cada uno coloreado según su rol

Esa elevación se dibuja a partir de los datos, no se sube. Cada equipo está ahí porque algo dice que ocupa esas unidades de rack, orientado de esa forma, y los colores vienen del rol que le diste. La utilización de espacio marca 28,6 % porque NetBox lo calculó. Nadie mantiene ese número.

De ahí se siguen dos cosas que valen más que la imagen.

Puedes preguntar qué unidades están libres y obtener una respuesta con la que actuar. También puedes reservar unidades antes de que se instale nada en ellas, y esa es la diferencia entre vender espacio que tienes y vender espacio que crees que tienes.

Lo que deliberadamente no hará

Un producto que sabe lo que no es resulta más raro que uno que lo hace todo mal, y la propia documentación de NetBox es tajante al respecto. No ofrece supervisión de red, ni servicio DNS, ni RADIUS, ni gestión de configuración, ni gestión de instalaciones.1

Más importante: contiene el estado deseado de tu red, no su estado operativo, y la documentación dice que la importación automatizada del estado vivo de la red está «strongly discouraged», porque cada registro debería ser revisado primero por una persona.1

Esa es la decisión sobre la que se apoya todo lo demás, y es la que la gente discute. El argumento va así: una fuente de verdad debería ser la verdad, así que descubre la red y cárgala. La respuesta es que una red descubierta te dice lo que hay, y lo que hay incluye cada error que alguien haya cometido jamás. Un puerto de conmutador dejado en la VLAN equivocada en 2021 es un hecho. No es una intención.

NetBox contiene la intención. Tu supervisión contiene la realidad. El número interesante es la diferencia entre ambas, y una diferencia no se calcula con una sola entrada.

El otro principio se afirma con igual claridad: entre una solución relativamente simple al ochenta por ciento y una completa mucho más compleja, quédate con la simple.1 Lo notarás la primera vez que quieras modelar algo que no modela, y hacia el final hay toda una serie de secciones sobre qué hacer cuando llegues ahí.

NetBox haceNetBox no hace
Registrar lo que debería estar ahíConsultar lo que está ahí
Contener la VLAN prevista para un puertoDecirte que el puerto está caído
Decir de qué cliente es un prefijoFacturárselo
Renderizar la configuración de un equipo desde una plantillaEmpujarla al equipo
Llevar el circuito, el proveedor y el compromisoSupervisar el circuito
Decir en qué rack está un equipo, y en qué UAbrir el armario

Lee esa columna de la derecha como una lista de las herramientas que sigues necesitando. Léela mal e intentarás convertir NetBox en todas ellas, y así es como una fuente de verdad se convierte en otro sistema en el que nadie confía.

Por qué necesitas una

Pregunta a un proveedor de servicios gestionados dónde está el registro de referencia de la red de un cliente y obtendrás una respuesta. Pregúntaselo por separado a dos de sus ingenieros y obtendrás dos.

Uno señalará una hoja de cálculo. Otro señalará un diagrama guardado por última vez por alguien que se fue en 2023. Otro más dirá que la configuración del cortafuegos es la documentación, lo cual al menos es honesto, porque una configuración sí describe lo que hace una caja. Solo que no describe por qué, ni quién lo pidió, ni cuál de los cuatro clientes detrás de esa caja paga por la regla.

El fallo nunca es el día en que te das cuenta de que el registro está mal. Es el día en que alguien lo necesita.

El momentoLo que tienes que presentarLo que cuesta cuando no puedes
Una conversación de renovaciónUn desglose partida por partida de lo que compra la cuota mensualEl presupuesto del competidor está desglosado, porque él fue y contó
Un ingeniero presenta su renunciaTodo lo que sabía, por escritoSeis meses de averiguarlo, un ticket a la vez
Un cliente se marchaUna descripción de su propio parqueTres semanas para armarla, y una referencia que dará con honestidad
Un auditor pregunta por el alcanceQué sistemas guardan datos personales y dónde están físicamenteA un regulador se le contó algo que luego resulta no ser cierto
Una migración hay que presupuestarlaUn recuento de lo que realmente hayPujas sobre una estimación y te comes la diferencia

Nada de eso es exótico. Eso es un martes.

Lo que sacas de ello no es documentación. La documentación es algo que escribes y luego dejas de mantener. Lo que obtienes es una base de datos que se niega a contener una contradicción, y que responde preguntas que nadie pensó en hacerle de antemano. Más adelante en este artículo hay un armario que resulta estar lleno al 28,6 por ciento de equipo y al 90,7 por ciento de electricidad. Nadie se puso a buscar eso. Cayó de haber introducido una potencia una vez en un tipo de equipo.

La reserva honesta viene con ello, y la última sección trata de eso. Un registro solo vale lo que te cuesta mantenerlo correcto. Pero la alternativa es un negocio incapaz de describirse, y el primero en descubrirlo suele ser un cliente.

El otro: Nautobot

Te lo encontrarás en más o menos una semana de búsquedas, así que conviene saber qué pasó.

En 2021, Network to Code forkeó NetBox y llamó al resultado Nautobot. No un fork blando ni una distribución: un fork duro que lleva cinco años divergiendo. Sus propias notas de la versión v1.0 lo describen como «a divergent fork of NetBox 2.10», el repositorio se creó el 19 de febrero de 2021 y la v1.0.0 salió el 26 de abril de 2021.3

Las razones que dieron están en su propio blog y merecen leerse en sus palabras más que en las mías. Tres cosas lo impulsaron. Querían vender soporte empresarial: «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.» Querían que la fuente de verdad se situara en el centro de una plataforma de automatización en lugar de servir a la documentación. Y «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

A esa primera razón hay que ponerle su fecha, porque ha dejado de ser cierta. En febrero de 2021 no había ninguna empresa detrás de NetBox que pudiera venderte nada. NetBox Labs no se fundó hasta 2023, como escisión de NS1 tras su adquisición por IBM, cofundada por el propio mantenedor principal de NetBox.5

Y no es un tercero que haya montado un negocio sobre el proyecto de otro. NetBox Labs es la depositaria de NetBox: la documentación del propio proyecto dice «the open source project is stewarded by NetBox Labs and a team of volunteer maintainers».1 Venden NetBox Enterprise para instalaciones autogestionadas, lo alojan por ti como NetBox Cloud, y ofrecen soporte 24/7.6

Así que «you cannot buy support for NetBox» era algo justo de decir cuando Network to Code forkeó, y hoy no es algo justo de decir. Ambos proyectos tienen detrás una empresa comercial que firmará algo, y en el caso de NetBox esa empresa es la que está a cargo del proyecto.

La frase que la mayoría se pierde es la siguiente, y es la razón por la que esta no es una historia sucia: «the NetBox project team suggested that we should consider forking.»

Un fork no se vuelve mucho más civilizado. Dos grupos querían cosas distintas, lo dijeron durante un periodo largo, y se separaron en lugar de pelearse por una sola base de código. Ambas mitades siguen siendo Apache 2.0. Nadie se llevó nada a lo que no tuviera derecho.

Para qué servía realmente el fork

Las notas de la versión de Nautobot 1.0 enumeran lo que añadió respecto a NetBox 2.10, y esa lista cuenta el debate mejor que cualquier artículo de blog:3

Lo que Nautobot añadió en 2021Dónde está NetBox ahora
Soporte de GraphQLNetBox lo tiene
Integración con Git como fuente de datosNetBox lo tiene, como fuentes de datos sincronizadas
Inicio de sesión únicoNetBox lo tiene
SecretsNetBox lo tiene mediante un plugin
Scripts e informes unificados en JobsNetBox saca los scripts a un plugin en 4.7
Custom fields en todos los modelosNetBox tiene soporte amplio de custom fields
API de plugin de validación de datosNetBox tiene custom validation rules
Estados personalizables, como objetos en base de datosLos estados de NetBox siguen siendo un choice set de Python
Relaciones definidas por el usuario entre modelosNetBox no tiene equivalente
Claves primarias UUIDNetBox usa claves enteras
Mejoras en la API de pluginsEl framework de plugins de NetBox ha crecido mucho desde entonces

La mitad superior ha convergido en buena medida. Varias cosas que Nautobot entregó en 2021 llegaron después a NetBox, y eso es lo que suele ocurrir cuando dos proyectos resuelven los mismos problemas en público.

Las tres de abajo no han convergido, y son arquitectónicas más que cosméticas. Hoy he revisado ambas bases de código en lugar de fiarme de las notas de 2021.

El Status de Nautobot es un modelo en base de datos, descrito en su propio código fuente como un «Model for database-backend enum choice objects», así que un estado es una fila que alguien puede añadir en la interfaz. Los estados de NetBox vienen de un choice set de Python, por lo que la sección de más abajo añade uno editando configuration.py y reiniciando. Nautobot tiene modelos Relationship y RelationshipAssociation, así que puedes definir una relación entre dos tipos de objeto existentes sin escribir código. La respuesta de NetBox a ese problema es un plugin, y esa es la última serie de secciones de este artículo. Y las claves primarias de Nautobot son UUID, donde las de NetBox son enteros.

También hicieron aquello por lo que forkearon. Hoy Nautobot publica 3.2.x y 2.4.x el mismo día, lo que es una auténtica línea de mantenimiento a largo plazo en paralelo a la actual, y esa era una de las tres razones declaradas.

Cuál de los dos

Primero los números honestos. NetBox tiene 21.625 estrellas y 3.133 forks; Nautobot tiene 1.617 y 422.7 A ambos se les ha hecho push en los últimos dos días, ambos son Apache 2.0, y ambos tienen ya detrás una empresa comercial que vende soporte y alojamiento.

Esa brecha no es un juicio sobre la calidad. Refleja cinco años de ventaja y el hecho de que la mayoría de quienes necesitan una fuente de verdad encuentran NetBox primero. Pero sí decide lo que normalmente importa más que las funciones: cuántos plugins, integraciones, módulos de Ansible, respuestas de foro y colegas vas a encontrar para el que elijas.

Así que: si quieres un inventario y una fuente de verdad que otros sistemas lean, y quieres el mayor ecosistema y la contratación más fácil, la respuesta es NetBox, y de eso trata el resto de este artículo. Si tu razón para querer una fuente de verdad es concretamente pilotar automatización desde ella, o quieres que los estados y las relaciones los defina tu equipo en lugar de un fichero de configuración y un reinicio, ve a mirar Nautobot en serio antes de decidir.

No dejes que nadie te venda ninguno de los dos solo por el soporte. Ambos bandos lo tienen cubierto ya, y las diferencias que seguirán ahí en cinco años son las de la tabla de arriba.

Lo que no deberías hacer es elegir uno porque alguien te haya dicho que el otro está muerto. Ninguno lo está, y ambos han seguido sacando versiones esta semana.

Poner uno en marcha

Dos caminos, y he recorrido los dos para esto. Contenedores si quieres que funcione en veinte minutos, paquetes en un host si va a ser portante.

La pila de contenedores

La comunidad mantiene netbox-docker, y esa es la respuesta rápida: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

Eso te da la aplicación, PostgreSQL, Redis y un worker en segundo plano, conectados entre sí. Construí lo mismo a mano bajo podman para ver las piezas, y las piezas merecen conocerse porque dos de ellas pillan a la gente:

ContenedorQué haceSi lo dejas fuera
netboxLa aplicación Django detrás de gunicornnada funciona
postgresLa base de datos, 15 o posteriornada funciona
redis / valkeyDos bases de datos: una para tareas, otra para cachénada funciona
netbox-workerrqworker, que vacía la cola de tareaslos webhooks nunca se disparan, las tareas de fondo nunca corren, y nada te avisa
netbox-housekeepingLa limpieza periódicalos registros del changelog nunca caducan

Esa cuarta fila es la importante. Sin worker todo parece sano. Las event rules se acumulan y se quedan ahí.

Dos cosas me mordieron en la primera ejecución, y ninguna está en un mensaje de error que buscarías.

La primera migración tarda mucho. No un minuto. En esta máquina fueron varios, porque NetBox 4.7 sustituye django-mptt por ltree de PostgreSQL y reconstruye por el camino cada tabla jerárquica. El contenedor simplemente se queda ahí aplicando migraciones. Déjalo en paz.

Sin API_TOKEN_PEPPERS no puedes crear un token de API v2, y la única señal es un aviso en el registro:

UserWarning: API_TOKEN_PEPPERS is not defined. v2 API tokens cannot be used.

Define al menos uno, de al menos cincuenta caracteres, antes de ponerte a buscar por qué la API te rechaza.

En un host, desde los paquetes

La vía documentada está probada en Ubuntu 24.04. La recorrí de principio a fin en una instalación limpia, y quedó en esto:

ComponenteLo que me dio 24.04Lo que necesita NetBox 4.7
PostgreSQL16.1515 o posterior
Redis7.0.156.0 o posterior
Python3.12.33.12, 3.13 o 3.14
Django6.1.1viene con NetBox
NetBoxv4.7.2

Esos mínimos son los de NetBox, y la 4.7 subió tanto el suelo de PostgreSQL como el de Redis.9

Todo el trabajo son cinco pasos.

# 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

Esos cinco valores son ALLOWED_HOSTS, DATABASES, REDIS, SECRET_KEY y API_TOKEN_PEPPERS. Genera los dos últimos por separado y no reutilices uno para el otro. Así que ejecuta el generador de claves dos veces y pega los resultados en líneas distintas.

upgrade.sh construye el entorno virtual, instala todas las dependencias de Python, ejecuta las migraciones, construye la documentación para uso sin conexión y recoge los ficheros estáticos. Cuando termina en una máquina nueva imprime un aviso que parece alarmante y no lo es:

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.)

Luego un superusuario, y se ejecuta así:

source /opt/netbox/venv/bin/activate
cd /opt/netbox/netbox && python3 manage.py createsuperuser

Para cualquier cosa real, pon gunicorn delante en lugar de runserver. La configuración y los ficheros de unidad ya están en el repositorio, y ese es el detalle que conviene conocer porque la gente escribe los suyos:

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 ejecuta gunicorn en 127.0.0.1:8001, netbox-rq.service ejecuta el worker, y nginx o Apache se pone delante para terminar TLS y servir /static. Dos servicios, y el segundo es el mismo worker que necesita la pila de contenedores.

El orden en que tienes que llenarlo

Un NetBox nuevo es una base de datos vacía con opiniones, y la primera hora con él suele irse en descubrir cuáles son. Vas a añadir un equipo, y no te deja.

Formulario Add a new device de NetBox, con Name, Device role marcado con un asterisco rojo, Description y Tags bajo el encabezado Device, luego Device type también marcado con un asterisco rojo bajo Hardware, seguido de Serial number, Asset tag, Cooling method y Airflow

Esos asteriscos rojos son toda la lección. NetBox no registra nada antes de que existan las cosas de las que cuelga, y no se está poniendo difícil: un equipo sin tipo es una fila incapaz de responder a ninguna de las preguntas para las que existe un equipo.

Así que en lugar de adivinar, pregunté al modelo qué claves ajenas son realmente obligatorias, y luego intenté romper cada regla a través de la API para ver qué vuelve.

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."]}

Cada uno rechazó, y dijo exactamente qué faltaba. Aquí está la misma información como lista de requisitos previos:

Para crearRequiere antesOpcional, pero lo quieres antes
Tenantnadaun grupo de tenants
Region, Site groupnadaun padre de la misma clase, se anidan
Sitenadauna Region, un Site group, un Tenant
Locationun Siteuna Location padre, un Tenant
Rack typeun Manufacturer
Rackun Siteuna Location, Rack group, Rack role, Rack type, Tenant
Device typeun Manufacturer
Device rolenadauna Device role padre, se anidan
Deviceuna Device role, un Device type, un Siteun rack y posición, una Platform, un Tenant
Interface, y cualquier otro componenteun Device
Cabledos cosas donde terminarun Tenant
Aggregateun RIRun Tenant
VLANnadaun VLAN group, una Role, un Tenant
Prefixnadaun Site, una VLAN, una Role, un VRF, un Tenant
IP addressnadauna Interface a la que asignarla, un Tenant
Circuitun Provider y un Circuit typeun Tenant
Circuit terminationun Circuitun Site donde aterrizar
Clusterun Cluster typeun Cluster group, un Site, un Tenant
Virtual machineun Site, un Cluster o un Deviceuna Platform, un Tenant
VM interfaceuna Virtual machine

La segunda columna es la que te cuesta. Los prefijos, las direcciones IP, las VLAN y los tenants no exigen nada en absoluto, así que nada te impide crearlos el primer día en el orden que te apetezca. Si sirven de algo es otra cuestión, porque una dirección sin interfaz detrás es una fila en una lista, y un equipo sin tenant es un equipo que volverás a editar más adelante.

El orden en que trabajar de verdad

Eso da una secuencia. Bájala y nunca nada te rechaza.

Primero, las cosas de las que cuelga todo lo demás. Nada de esto es emocionante y todo es barato de equivocar.

  1. Grupos de tenants, luego tenants. Hazlos antes que nada. Veintiocho modelos aceptan un tenant, entre ellos Site, Location y Rack, así que si los clientes no existen todavía no puedes irlos sellando por el camino y acabarás editando en lote más tarde.
  2. Regiones y site groups. Ambos opcionales, ambos se anidan en sí mismos, y son independientes entre sí. Las regiones son para la geografía, los site groups para la función, y puedes usar uno, ambos o ninguno.
  3. Emplazamientos. La raíz de casi todo. Un emplazamiento no exige nada, y por eso es lo primero que realmente puedes crear.
  4. Ubicaciones. Necesitan un emplazamiento, y se anidan, así que una sala con filas con pods es un solo modelo con tres niveles.
  5. Rack roles y rack groups. Ambos opcionales. Los rack groups son planos y se sitúan junto a las ubicaciones como segundo eje, lo que resulta práctico para filas y pods.
  6. Fabricantes, luego rack types. Un rack type necesita un fabricante. Sáltate los rack types si no modelas los armarios en sí.
  7. Racks. Necesitan un emplazamiento. Si además le das una ubicación, esa ubicación tiene que pertenecer a ese emplazamiento, y NetBox lo comprueba.

Luego el catálogo de hardware, no el hardware. Antes de poder añadir un solo equipo necesitas fabricantes, device types, device roles y, en la práctica, platforms.

  1. Fabricantes. Puede que ya tengas algunos del paso 6, porque los rack types también los necesitan. Los module types igual.
  2. Device types, cada uno necesitando un fabricante.
  3. Device roles, que se anidan, y platforms.

Este es el paso que la gente se salta, y es el paso que decide cuánto teclear cuesta el resto del trabajo. Un device type lleva sus propias interfaces, puertos y bahías como plantillas, así que cada equipo que crees a partir de él llega con los componentes correctos ya puestos. Haz bien el tipo una vez y enracar cuarenta son cuarenta nombres.

Platform es el raro de esa lista, porque un equipo no exige una estrictamente. Hazlo ahora de todas formas. Es lo que llevará después la plantilla de configuración y el driver NAPALM, y volver a ponerla sobre un parque entero es la misma tarde que te habrías gastado en los tenants.

Luego el equipo en sí.

  1. Equipos. Rol, tipo y emplazamiento son todos obligatorios. Rack y posición son opcionales, y si los das tienen que ser coherentes con el emplazamiento.
  2. Componentes, si el device type no los ha suministrado ya.
  3. Cables, entre los componentes.

Luego el direccionamiento, porque una dirección quiere una interfaz donde vivir y la interfaz solo existe después del paso 12.

  1. RIR, luego aggregates.
  2. Roles de prefijo y de VLAN, VRF, VLAN groups y luego VLAN.
  3. Prefijos, luego las direcciones IP individuales.

Luego la capa comercial.

  1. Proveedores, cuentas de proveedor y circuit types.
  2. Circuitos, y luego termínalos en emplazamientos.

Y el parque virtual, que refleja el físico.

  1. Cluster types y cluster groups, luego clústeres.
  2. Máquinas virtuales, que necesitan un emplazamiento, un clúster o un equipo, y luego sus interfaces.

Los pasos 1 a 10 son una tarde y parecen administración. No hay nada glamuroso en ellos. También son la tarde que decide si el paso 11 lleva una mañana o quince días.

Las reglas que muerden más tarde

Los campos obligatorios son la mitad fácil, porque fallan de inmediato y te dicen por qué. Con lo que la gente se tropieza son las reglas de coherencia, que solo se disparan cuando ya tienes datos suficientes para contradecirte:

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)"]}

Esa última es la que señalaría si alguien preguntara por qué molestarse con todo esto. NetBox sabe que el equipo mide 1U, sabe qué hay en el armario, y no te deja registrar dos cosas en el mismo sitio. Tu hoja de cálculo te deja hacer eso toda la tarde sin decir una palabra, y te enteras cuando alguien está de pie en la sala con una caja en las manos.

Nada de esto es configurable y nada debería serlo. Esa es la diferencia entre un registro y un deseo.

En uso: lo que te da cada pantalla

Con el parque cargado, esto es lo que realmente sacas de vuelta. Todo lo que sigue es esa misma instancia: un armario, catorce equipos, cableados y direccionados entre tres clientes.

Un equipo son sus componentes

Entra en uno de esos hipervisores y la pestaña interesante no es el resumen, son las interfaces.

Pestaña de interfaces de NetBox para el equipo mcr1-hv-01 con tres interfaces: eno1, SFP28 25GE, descrita to leaf-01, con dirección IP 10.20.20.11/24, cable MCR1-DAC-0001A hacia mcr1-leaf-01 Ethernet1; eno2 SFP28 25GE to leaf-02 con cable MCR1-DAC-0001B hacia mcr1-leaf-02 Ethernet1; e ipmi 1000BASE-T descrita BMC con 10.20.10.101/24

Lee una fila de lado a lado. La interfaz, su velocidad, lo que escribiste sobre ella, la dirección que lleva, la etiqueta del cable, y el puerto del otro extremo. Eso es una sola consulta, y es la respuesta a la pregunta que todo el mundo hace de verdad, que es «¿a qué está conectado esto?».

Fíjate en dónde está la dirección. Está en eno1, no en el servidor. Eso suena a pedantería exactamente hasta que tienes una caja con una interfaz de gestión, dos de datos y un loopback, y alguien pregunta qué dirección responde por ella. Un modelo que cuelga las direcciones de los equipos no puede decírtelo. Este sí, y también puede contener el caso perfectamente ordinario de cuatro direcciones en una sola interfaz.

Seguir el cable

Los cables terminan en componentes, no en equipos. Una interfaz en un extremo, una interfaz en el otro, o un front port, un rear port, una toma eléctrica, una terminación de circuito. Ese es el detalle sobre el que se apoya toda la función, y es por lo que la lista de interfaces de arriba podía imprimir el otro extremo en su propia columna sin que se lo dijeran.

Modela un cable de equipo a equipo y habrás dibujado una imagen. Termínalo en los puertos y NetBox puede recorrerlo.

Trazado de cable de NetBox para la interfaz eno1, dibujado como diagrama vertical: mcr1-hv-01, un Supermicro SYS-1029U-TN10RT en Manchester DC1 / Hall 2 / MCR1-A07 (A07) / Front / U20.0, su interfaz eno1, el cable MCR1-DAC-0001A marcado como Connected, luego Ethernet1 en mcr1-leaf-01, un Arista DCS-7050SX3-48YC8 en Front / U36.0. Trace Completed, 1 segmento en total

Aquí un salto, porque es un cable de conexión directa. Pon paneles de parcheo en medio y los recorre, panel por panel, y te dice qué hay al otro extremo de un tendido que cruza tres armarios. Ese es el trabajo que, si no, requiere una linterna y alguien sujetando el otro extremo de una sonda de tono.

El trazado también imprime la ubicación completa de cada extremo. Emplazamiento, sala, armario, cara, unidad de rack. Si alguna vez has estado al teléfono intentando explicarle a un técnico de manos remotas qué caja mirar, esa línea es todo el valor.

Las direcciones, como árbol en lugar de como pestaña

IPAM es la mitad por la que la gente llega.

Lista de prefijos de NetBox con 10.20.0.0/16 como Container con 5 hijos al 1,6 % de utilización, descrito Manchester DC1, y debajo, con un nivel de sangría, cinco prefijos hijos activos: 10.20.10.0/24 en la VLAN mgmt (100) con rol Management, 10.20.20.0/24 asignado a Ravenscroft Legal en la VLAN ravenscroft-prod (200), 10.20.21.0/24 a Padgate Foods, 10.20.22.0/24 a Hartley Components, y 10.20.30.0/29 en la VLAN transit (110) al 16,7 % de utilización

La sangría se calcula a partir de las propias direcciones. No le dices a NetBox que 10.20.20.0/24 está dentro de 10.20.0.0/16, lo deduce, y seguirá deduciéndolo cuando alguien meta un /26 en medio el año que viene.

Cada fila lleva las cosas por las que realmente filtras: la VLAN a la que apunta, el rol, y el cliente al que pertenece. La utilización también se calcula.

Abre uno y obtienes las direcciones que contiene, y los huecos.

Pestaña IP Addresses de NetBox para el prefijo 10.20.20.0/24, con una fila verde «10 IPs available», luego 10.20.20.11/24 y 10.20.20.12/24 ambas activas y asignadas a Ravenscroft Legal, luego una fila verde «242 IPs available»

Esas filas verdes son el espacio libre, mostrado en línea con el espacio usado. Hay una llamada de API que te entrega la siguiente dirección libre de un prefijo, y es la única pieza de automatización de IPAM que se amortiza de inmediato, porque es lo que la gente hace si no entornando los ojos ante una hoja de cálculo y confiando en la suerte.

Una interfaz admite tantas direcciones como quieras, de ambas familias, y designas una de cada como la primaria del equipo.

Lista de direcciones IP de NetBox filtrada por la interfaz eno1 de mcr1-hv-01, con cuatro direcciones: 10.20.20.11/24 marcada como primaria, 10.20.20.201/24 una dirección de servicio, 2001:db8:20:20::11/64 primaria v6, y 2001:db8:20:20::201/64 una dirección de servicio, todas asignadas a Ravenscroft Legal

Dos IPv4 y dos IPv6 en un solo puerto, lo cual es un martes cualquiera y algo que un modelo centrado en el equipo no puede expresar en absoluto.

Introduce las direcciones con la máscara de la red en la que están, no como /32. NetBox aceptará 10.20.20.11/32 y lo archivará bajo el prefijo correcto, porque la contención se deduce de la dirección de host. Lo que no hará es corregirte después: la máscara se almacena exactamente como se escribió y se devuelve así a todo lo que la lea, así que un /32 en una dirección de LAN renderiza un /32 en la configuración del equipo. El sitio donde eso muerde es tres meses después en una plantilla de configuración, no hoy en el formulario.

Una instalación, tres clientes

La mayoría de los objetos de NetBox pueden asignarse a un tenant. Una empresa lo usa para unidades de negocio. Si vendes servicios gestionados, creas uno por cliente.

Página de tenant de NetBox para Ravenscroft Legal en el grupo Customers, con un panel Related Objects que lista Circuits 2, Devices 2, IP Addresses 2, Prefixes 1 y VLANs 1, y pestañas arriba para Custom Objects, Contacts, Journal y Changelog

Ese panel de la derecha es la respuesta a «¿qué tiene este cliente?», y se ha montado solo. Sin informe, sin hoja de cálculo, sin preguntarle al ingeniero que lo construyó.

Conviene ser preciso con lo que significa la asignación a tenant, porque equivocarse el primer día es un año de deshacer el enredo después. Un tenant significa que el objeto está dedicado a ese cliente. Un router que solo le sirve a él recibe su tenant. Un cortafuegos que sirve a cuatro de ellos no pertenece a ninguno, así que no recibe ninguno, y la relación va a otro sitio. Más sobre eso más abajo, porque es el punto en el que la mayoría descubre que necesita añadir algo propio.

Pon los números en el tipo de equipo

Este es el paso que separa un inventario de algo que responde preguntas, y cuesta unos diez minutos por tipo de equipo.

Un device type puede llevar su peso y, a través de sus plantillas de power port, su consumo eléctrico. Ponlos una vez en el tipo y cada equipo que crees a partir de él los hereda. Déjalos fuera y NetBox te dirá encantado que un armario está lleno al 28,6 % y nada más.

Página de device type de NetBox para el Supermicro SYS-1029U-TN10RT, con altura 1U, full depth marcado, peso 19,10 kg y cooling method Air, con una pestaña Power Ports con el recuento 2 y un panel Related Objects que muestra 6 devices

Puse peso en los cinco tipos que hay aquí, di a cada uno dos plantillas de power port con un consumo máximo y uno asignado, di al tipo PDU una entrada y doce tomas, luego creé un power panel y dos alimentaciones hacia el armario y cableé el conjunto: cada PSU1 de cada equipo a la PDU A, cada PSU2 a la PDU B, y la entrada de cada PDU a su alimentación.

Power feed de NetBox MCR1-A07-A, tipo Primary, estado Active, conectada a mcr1-pdu-a INPUT, con una utilización asignada de 5340VA de 5888VA como barra roja al 90,7 por ciento, con características eléctricas de alimentación AC, 230 voltios, 32 amperios, monofásica y 80 por ciento de utilización máxima

Entonces la página del rack cambia por completo.

Página del rack MCR1-A07 en NetBox mostrando ahora cooling capability Hybrid, cooling capacity 15,00 kW, utilización de espacio 28,6 por ciento en verde y utilización eléctrica 90,7 por ciento en rojo, junto a las elevaciones frontal y trasera

Utilización de espacio 28,6 %. Utilización eléctrica 90,7 %.

Ese armario está lleno a un tercio de equipo y casi sin electricidad, y ese es un hecho sobre tu parque que ninguna hoja de cálculo va a ofrecerte nunca por su cuenta. También es el hecho que decide si el próximo pedido se enraca ahí o en otro sitio, y cayó de datos que introdujiste una vez, en los tipos.

Dos cosas que lo dejarán marcando cero

Al principio tuve 0,0 %, dos veces, y ambas causas merecen conocerse porque ninguna produce un error.

Las tomas tienen que referenciar la entrada. Una toma eléctrica de una PDU tiene un campo power_port que apunta al puerto aguas arriba del mismo equipo. Déjalo en blanco y la cadena queda rota: NetBox no tiene forma de saber que esas doce tomas se alimentan de esa entrada, así que nada se agrega.

Deja vacíos los campos de consumo de la entrada. Esta es la contraintuitiva. NetBox solo calcula el consumo de un power port a partir de lo que está enchufado en él si sus dos propios campos de consumo están vacíos:

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, ...}

Yo había puesto servicialmente maximum_draw: 7400 en la entrada de la PDU, porque es para lo que la PDU está dimensionada. NetBox me creyó por tanto, tomó allocated_draw como no definido, e informó cero. Borra ambos y lo calcula:

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

Así que la regla es: pon números reales en las hojas, y deja vacíos los puertos intermedios para que NetBox pueda sumarlos. Un valor definido administrativamente siempre gana al calculado, lo cual es comportamiento correcto y exactamente lo que no hay que hacer en una PDU.

La refrigeración, que es nueva

La versión 4.7 añadió refrigeración a DCIM, y llegó con la mitad que se está poniendo cara. Un rack lleva una cooling capability de aire, híbrida o líquida y una capacidad en kilovatios; un device type lleva un cooling method. Por encima se sitúan las cooling sources para las enfriadoras y los equipos CRAC, las cooling feeds que representan un circuito hacia un rack, y componentes de admisión y salida en los propios equipos para placas frías y colectores.

El armario de arriba marca Hybrid, 15,00 kW, porque se lo dije al rack. Si este año recibes equipo refrigerado por líquido, ahí tienes un modelo para lo que ahora guardas en una hoja de cálculo.

Una instalación, muchos clientes

La asignación a tenants es la razón por la que un proveedor puede operar un solo NetBox en lugar de uno por cliente, y merece la pena entender qué hace y, más importante, qué no hace.

Veintiocho de los modelos de NetBox llevan un campo tenant. Emplazamientos, ubicaciones, racks y reservas de rack. Equipos, cables y virtual device contexts. Prefijos, direcciones IP, rangos, aggregates, VLAN, VLAN groups, VRF, route targets, ASN. Circuitos y circuit groups. Clústeres y máquinas virtuales. Túneles, L2VPN, redes y enlaces inalámbricos. Power feeds y cooling feeds.

Eso es todo sustantivo facturable. Ponlo de forma coherente y «¿qué tiene este cliente?» deja de ser una investigación.

Pero un tenant es una etiqueta, no un candado. Dice que el objeto está dedicado a ese cliente. No impide que nadie que pueda iniciar sesión lea el conjunto, lo cual está bien dentro de una sola empresa y no sirve de nada en el momento en que un cliente tiene una cuenta.

De ahí se siguen dos cosas, y la segunda es la que la gente hace mal.

Un tenant significa dedicado. Un router que sirve a un solo cliente recibe su tenant. Un cortafuegos que sirve a cuatro no pertenece a ninguno, así que no recibe nada, y registras la relación en lo que realmente vendiste. Forzar un tenant en equipo compartido vuelve silenciosamente incorrecto cada informe construido sobre tenants.

Y el acceso es un mecanismo completamente aparte.

En los permisos vive el aislamiento

Los object permissions de NetBox toman una restricción JSON, y la restricción es un filtro del ORM de Django. Estrecha el queryset antes de que se construya nada a partir de él, así que cada vista, exportación, llamada de API y búsqueda queda estrechada con ella.

Página de detalle de un permiso de NetBox para Ravenscroft read-only, con Enabled marcado, Actions con View marcado y Add, Change, Delete, Render configuration y Synchronize data todos tachados, Object Types listando Circuits circuit, DCIM device e IPAM prefix, un usuario asignado ravenscroft-ro, y un panel Constraints que contiene el JSON tenant__slug fijado a ravenscroft-legal

Tres campos hacen el trabajo. Los tipos de objeto a los que se aplica, las acciones que concede, y esa restricción de abajo. Todo lo demás es contabilidad.

Aquí está la lista de equipos como administrador.

Lista de equipos de NetBox como usuario admin, Results 14, con mcr1-core-01 y 02, mcr1-hv-01 a 06, mcr1-leaf-01 y 02, mcr1-pdu-a y b, y mcr1-pp-01 y 02, con columnas de estado, tenant, emplazamiento, ubicación, rack, rol, fabricante, tipo y dirección IP

Y aquí está la misma URL, la misma instalación, con la sesión iniciada como el cliente.

La misma lista de equipos de NetBox con la sesión iniciada como ravenscroft-ro, Results 2, mostrando solo mcr1-hv-01 y mcr1-hv-02, ambos asignados a Ravenscroft Legal, y la navegación izquierda reducida a Devices, IPAM, Circuits, Plugins y Admin

Dos filas en lugar de catorce, y mira el menú de la izquierda. Se ha plegado a las cuatro cosas que esa cuenta tiene permitido tocar. Nadie configuró eso. La navegación se construye con los mismos permisos, así que un cliente nunca ve un enlace a algo que lo rechazaría.

Las dos formas de decir no

Este es el detalle que conviene conocer, porque los dos rechazos significan cosas distintas y ambos son deliberados.

El token del cliente pideObtiene
/api/dcim/devices/200, una fila de dos
/api/dcim/devices/1/, el suyo200
/api/dcim/devices/2/, el de otro404
/api/tenancy/tenants/403
/api/dcim/sites/, nunca concedido403
ningún token en absoluto403

404 significa que el tipo es tuyo pero esa fila no. La restricción la retiró del queryset, así que para la petición no existe. Un 403 ahí confirmaría que existe, y permitiría a un cliente curioso contar tu parque recorriendo los identificadores.

403 significa que el tipo nunca fue tuyo. Los tenants y los emplazamientos nunca se concedieron, así que rechazan de entrada, y el cliente no puede enumerar quién más está en la plataforma.

Luego déjales escribir

Solo lectura es el caso fácil. La pregunta real es si se le pueden confiar derechos de edición a un cliente, así que concedí change bajo la misma restricción y me puse a buscar la salida.

IntentoResultado
Editar su propio equipo200, guardado
Editar el equipo de otro cliente404
Editar su propio equipo moviéndolo al tenant del otro cliente403
Borrar su propio equipo403, delete nunca se concedió

La tercera fila es la que importa. Reasignar tu propio equipo al tenant de otra persona es la escapatoria obvia, porque el objeto está dentro de tu restricción cuando llega la petición y fuera después. NetBox evalúa la restricción contra el estado en el que quedaría el objeto, así que rechaza. Releí el equipo en lugar de fiarme del código de estado, y el tenant no se había movido.

Ese es el agujero que la mayoría de los sistemas multicliente caseros dejan abierto, y normalmente lo encuentra un cliente y no una prueba.

Variables que tienen que heredarse

Aquí está la distinción que la gente hace mal, y hacerla bien ahorra mucha edición.

Un custom field es un valor en un objeto. Lo pones objeto por objeto, y ahí se queda. Bien para un hecho sobre la cosa en sí: una etiqueta de activo, un número de contrato de soporte, una fecha de puesta en servicio.

Un config context es un valor adjunto a una característica, que hereda todo lo que encaja con esa característica. Bien para una variable que debe caer en cascada: tus servidores NTP, tus destinos de syslog, tu comunidad SNMP, tu dominio DNS, tu VLAN de gestión, tu ventana de respaldo.

Si te pillas poniendo el mismo custom field al mismo valor en cuarenta equipos, lo que querías era un config context.

Lista de config contexts de NetBox con North West base con peso 1000 asignado a la región North West, y Manchester DC1 syslog con peso 2000 asignado al emplazamiento Manchester DC1, ambos activos

Un context es JSON arbitrario, y puede adjuntarse a una región, un site group, un emplazamiento, una ubicación, un device type, un rol, una platform, un clúster, un cluster type, un cluster group, un grupo de tenants, un tenant o una etiqueta. Tenant está en esa lista, lo que para un proveedor significa que un hecho cierto para un cliente en todas partes le sigue a cada equipo que añadas para él.

La fusión es por clave, y el peso decide quién gana cada una.

Pestaña Config Context de NetBox para mcr1-core-01, a la izquierda Rendered Context con domain, ntp_servers, snmp_community y syslog_servers, y a la derecha Source Contexts con North West base con peso 1000 con las cuatro claves y Manchester DC1 syslog con peso 2000 conteniendo solo syslog_servers

Lee el panel de la derecha contra el de la izquierda. La región aporta cuatro claves con peso 1000. El emplazamiento aporta una clave con peso 2000. El context renderizado conserva intactos el dominio, los servidores NTP y la comunidad de la región, y toma el servidor syslog del emplazamiento, porque esa es la única clave que algo disputó.

Escribes la excepción, no una copia nueva de todo con la excepción dentro. Ese es todo el valor, y es por lo que esto escala donde un fichero de variables por equipo no lo hace.

El context local gana a todo lo que esté por encima

La pila de herencia tiene una cima, y es el objeto mismo. Los local context data de un equipo ganan a cada source context que se le aplique, sean cuales sean los pesos.

Puse esto en mcr1-core-01:

{"syslog_servers": ["10.20.10.99"], "note": "this box logs somewhere else"}

y su context renderizado pasó a ser:

{
  "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"]
}

Pestaña Config Context de NetBox para mcr1-core-01 con Local Context ahora poblado con syslog_servers 10.20.10.99 y una note, el panel indicando que el config context local sobrescribe todos los source contexts, y el Rendered Context mostrando el servidor syslog local junto al dominio, los servidores NTP y la comunidad heredados

El servidor syslog local venció tanto a la sobrescritura del emplazamiento con peso 2000 como a la región con peso 1000. Todo aquello sobre lo que no dijo nada se heredó igualmente, así que el dominio, los servidores NTP y la comunidad pasaron intactos, y la clave nueva simplemente se añadió.

Esa es la salida de emergencia para la única caja que de verdad es distinta, y el panel te dice claramente que sobrescribe todos los source contexts. Si te pillas usándolo en muchos equipos en lugar de en uno, has encontrado una característica que esos equipos comparten y lo que querías era un context delimitado a ella.

Tres trampas

Un context sin delimitar es global. Crea uno y olvida asignarlo a algo, y se aplica a cada equipo y cada máquina virtual que tengas. Es comportamiento documentado y a veces lo que quieres. También es silencioso.

Las claves no pueden llevar guiones si quieres alcanzarlas de forma sencilla. Los datos de context son JSON, así que ntp-servers es perfectamente legal, pero los nombres de variable de Jinja no son claves JSON. {{ ntp-servers }} se interpreta como una resta y lanza UndefinedError: 'ntp' is undefined. Escribe {{ ntp_servers }} contra datos con guiones y obtienes una cadena vacía, un HTTP 200, y ningún aviso en ninguna parte. Usa guiones bajos en las claves y la plantilla evidente funciona.

Un perfil puede detener las erratas. Un perfil de config context agrupa contexts relacionados e impone un esquema JSON sobre sus datos al guardar. Le di a uno un esquema que exigía syslog_servers como array de cadenas IPv4, y luego cometí los dos errores que la gente comete de verdad:

{"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

Rechazados donde alguien los cometió, en lugar de cuatrocientos equipos más tarde.

Renderizar la configuración

Los datos de context más una plantilla Jinja te dan un fichero de configuración. El equipo está en ámbito como device, su context fusionado está en ámbito como variables ordinarias, y puedes recorrer sus componentes.

Pestaña Render Config de NetBox para mcr1-core-01, con la plantilla de configuración Junos base y la configuración Junos renderizada: host-name mcr1-core-01, domain-name mcr1.example.net, una línea location que indica Manchester DC1 / MCR1-A07 / U39, dos servidores NTP, el único host syslog 10.20.10.99, la comunidad SNMP n0rthwest, y cada interfaz con su descripción y familia de direcciones

Todo lo de esa página vino de un sitio distinto, y eso es lo importante:

LíneaDe dónde vino
host-name mcr1-core-01del equipo
domain-name mcr1.example.netdel config context regional
location "Manchester DC1 / MCR1-A07 / U39"del emplazamiento, el rack y la posición en él
server 172.16.10.22del context regional, peso 1000
host 10.20.10.99 any noticedel propio context local del equipo, venciendo al emplazamiento y a la región
family inet address 10.20.10.11/24de la dirección en esa interfaz, con su máscara tal como se escribió

La plantilla se resuelve por equipo, luego rol, luego platform, y la petición falla si ninguno de los tres tiene una. Así que asignas una plantilla a una platform una vez, y cada equipo que ejecute ese software renderiza desde ella salvo que su rol o el propio equipo digan otra cosa. Para eso sirve una platform: el sistema operativo o la familia de software del fabricante, no el hardware.

NetBox renderiza. No empuja. Llevar la salida a la caja es trabajo de tu automatización, y el día en que un error de plantilla habría reconfigurado cuatrocientos equipos, te alegrarás de que sean dos sistemas distintos.

Plugins

Un plugin es una aplicación Django instalada junto a NetBox. Puede añadir modelos, añadir páginas, extender ambas API, inyectar contenido en plantillas existentes, añadir navegación, añadir colas de tareas en segundo plano y cargar más aplicaciones Django. Hay muy poco que no pueda hacer, porque por debajo es simplemente Django.

Instalar uno son cuatro órdenes y un reinicio:

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

En la pila de contenedores los mismos paquetes van en plugin_requirements.txt y reconstruyes la imagen. En ambos casos, fija las versiones y comprueba primero la matriz de compatibilidad, porque un plugin que no se ha puesto al día con una versión de NetBox se negará a arrancar toda la aplicación en lugar de desactivarse él mismo.

Página installed plugins de NetBox listando Custom Objects versión 0.7.0 de NetBox Labs, Topology views versión 4.7.0 de Mattijs Vanhaverbeke, y qrcode versión 1.0.0 de Nikolay Yuzefovich

El catálogo publicado enumera 31. Estos son los que merece la pena conocer, con la licencia bajo la que cada uno se entrega realmente:

PluginQué haceLicencia
DNSZonas, registros y servidores de nombres como fuente de verdadMIT
BGPSesiones, comunidades y políticas de enrutamientoApache 2.0
Topology ViewsMapas gráficos de topología construidos desde tus cablesApache 2.0
FloorplanMapas gráficos de emplazamientos y ubicacionesLGPL 3.0
QR CodeCódigos en racks, equipos y cables, para etiquetas de activosApache 2.0
ACLsListas de acceso y reglasApache 2.0
Prometheus SDSirve a Prometheus su lista de hosts directamente desde NetBoxMIT
DocumentsDocumentos adjuntos a circuitos y equiposApache 2.0
LifecycleFin de vida del hardware, licencias y contratosApache 2.0
ContractContratos y facturasMIT
Reorder RackArrastrar y soltar unidades de rackApache 2.0
BranchingRamas aisladas y fusionables de tus datosNetBox Limited Use
Custom ObjectsNuevos tipos de objeto, definidos en la interfazNetBox Limited Use

Prometheus SD es la forma honesta de toda la idea. NetBox sabe qué existe, así que deja que NetBox se lo diga al sistema de supervisión, y deja de mantener una segunda lista de hosts que se desvía.

Una cosa que saber antes de construir sobre las dos últimas filas. NetBox en sí es Apache 2.0 y lo es desde que DigitalOcean lo publicó en 2016, y el proyecto está hoy a cargo de NetBox Labs junto a un equipo de mantenedores voluntarios.21 NetBox Branching y NetBox Custom Objects no lo son: se entregan bajo la NetBox Limited Use License 1.0, que concede el uso «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», y que no concede el derecho a usar el software «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 instalaste NetBox Community desde GitHub y lo operas en nombre de clientes, lee los términos tú mismo antes de poner un esquema detrás de ellos.10 La propia guía de instalación de NetBox recomienda ambos plugins sin mencionarlo.11

Custom fields

Un custom field añade un atributo a un modelo existente. Los valores se almacenan como JSON junto a cada objeto, así que no hay migración ni reinicio, y hay trece tipos incluidas referencias de objeto y multiobjeto a otros registros de NetBox.

Página de equipo de NetBox para mcr1-core-01 con un panel Custom Fields con un grupo Asset que contiene Warranty expires 2029-06-30 y Support contract JNPR-448120, junto al panel Device Type que muestra Juniper MX204 y un panel Dimensions que muestra un peso total de 9,5 kilogramos

Dos campos ahí, agrupados bajo un encabezado «Asset» que elegí yo. Fíjate en el panel Dimensions a su derecha: 9,5 kg, que nadie escribió en este equipo. Vinieron del tipo de equipo.

Los custom fields se validan, y merece saberse que lo hacen, porque es una diferencia real con la vía de más abajo. Le di al campo de contrato una expresión regular y la API la impuso:

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}$'"]}

Dos cosas cambiaron en 4.7 que importan a escala. Crear un campo con un valor por defecto, o borrar un campo, tiene que reescribir los datos almacenados de cada objeto al que se aplica, así que en una tabla grande ese trabajo se entrega a una tarea en segundo plano y el campo informa «provisioning» o «deleting» mientras se ejecuta. Un campo está vivo solo mientras está activo: durante cualquiera de esas operaciones no aparece en los objetos, ni en formularios, ni en filtros, ni en ninguna de las dos API. Eso necesita un worker en marcha, o se queda en ese estado indefinidamente.

Usa un custom field cuando añades un hecho sobre la cosa en sí. Usa un config context cuando el valor deba caer en cascada. Usa la sección siguiente cuando la cosa que necesitas no existe.

Menús de selección propios, y sobrescribir lo que viene de fábrica

Dos mecanismos distintos viven bajo este encabezado y resuelven problemas distintos. Uno es para tus propios campos. El otro reescribe los de NetBox.

Un choice set, para tu propio campo de selección

Un custom field de tipo «selection» saca sus opciones de un choice set, un objeto que gestionas en la interfaz como cualquier otro. Así «support tier» pasa a ser un desplegable real en lugar de texto libre que alguien escribirá de tres formas.

Choice set de custom field de NetBox llamado Support tier, descrito como what the customer pays for, listando tres opciones: bronze para next business day, silver para 8 hours, y gold para 4 hours 24x7

Se impone, incluso a través de la API:

PATCH {"custom_fields": {"support_tier": "platinum"}}
  {"__all__": ["Invalid value for custom field 'support_tier':
                Invalid choice (platinum) for choice set Support tier."]}

Los choice sets se comparten, así que un solo conjunto puede respaldar el mismo campo en varios modelos, y cambiar la lista en un sitio la cambia en todos.

FIELD_CHOICES, para los campos propios de NetBox

Este es el que la gente no sabe que existe. Varios de los campos de selección integrados de NetBox pueden extenderse o reemplazarse desde configuration.py, entre ellos el estado de equipo, el estado de emplazamiento, el estado de rack, el estado de circuito y bastantes más.12

Añade un signo más para sumar a lo que viene de fábrica. Omítelo para reemplazar la lista por completo.

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'),
    ),
}

Puse exactamente eso en esta instancia. El estado de equipo volvió con los siete valores de fábrica y mis dos:

offline, active, planned, staged, failed, inventory, decommissioning,
burn-in, awaiting-rma

y el estado de emplazamiento volvió solo con los míos, la lista de fábrica desaparecida:

surveyed, building, active, closing

Una opción puede ser una tupla simple de valor, etiqueta y color, o un diccionario que además acepta una descripción mostrada como subtítulo en el formulario. Y se comportan como valores nativos en todas partes, porque para el resto de NetBox son valores nativos:

Lista de equipos de NetBox mostrando mcr1-hv-06 con una insignia de estado Burn-in en cian junto a los demás equipos con el estado de fábrica Active, con el mismo estilo que cualquier estado integrado

Esa insignia es mi estado, en mi color, que ordena y filtra como cualquier otro.

Tres cosas que conviene saber antes de usarlo.

Reemplazar borra los valores de fábrica del menú, no de la base de datos. Cualquier objeto que ya tenga uno de ellos lo conserva, pero el valor ya no se ofrece y no será una opción válida la próxima vez que alguien edite ese objeto. Si reemplazas una lista, comprueba que no haya nada apoyado en un valor que acabas de retirar.

Vive en el fichero de configuración, así que necesita un reinicio y no es algo que un usuario de la interfaz pueda cambiar. Para un proveedor ese es el sentido correcto: el conjunto de estados que tu negocio reconoce es una decisión de gobierno, no una de un martes por la tarde.

Extiende antes de reemplazar. Los valores de fábrica son lo que cada plugin, script e integración espera ver. Añadir no cuesta nada. Reemplazar es una decisión que te pertenece para siempre.

Custom objects

Aquí hay un hueco real. NetBox modela clústeres, máquinas virtuales y discos virtuales. No modela el datastore en el que esos discos viven realmente, y para cualquiera que opere Proxmox o VMware ese es el objeto que conecta el almacenamiento que compraste con la carga que lo usa.

El plugin Custom Objects te deja definir un nuevo tipo de objeto desde la interfaz o la API, sin escribir código. Así que:

Página Custom Object Type de NetBox para Datastore, versión 1.0.0, descrito como shared storage a cluster puts VM disks on, con una pestaña Fields que indica 7 y un panel Fields que lista Name como Text, Cluster como Object apuntando a Virtualization > Cluster, Provisioned by como Multiple objects apuntando a DCIM > Device, Backing como Text, Capacity (GB) como Integer, Thin provisioned como Boolean, y Customer como Object apuntando a Tenancy > Tenant

Siete campos, dos de los cuales son todo el objetivo. cluster es una referencia de objeto único a un clúster real de NetBox, puesta en protect para que nadie pueda borrar un clúster por debajo de su almacenamiento. provisioned_by es una referencia multiobjeto a los equipos que realmente lo sirven.

Eso te da un objeto de primera clase con su propia entrada de navegación, vista de lista, filtros, importación y exportación:

Lista de Datastores de NetBox con cuatro filas: ds-nvme-01 en el clúster MCR1-PVE provisionado por mcr1-hv-01, 02 y 03, respaldado por un pool Ceph RBD NVMe de 40960 GB y thin provisioned; ds-nvme-02; ds-archive-01 en un pool HDD con WAL NVMe de 196608 GB y no thin provisioned; y ds-ravenscroft-01 de 8192 GB asignado a Ravenscroft Legal

Y como las referencias son reales, la relación aparece también desde el otro extremo. Abre el clúster y los datastores salen listados contra él.

Página de clúster de NetBox para MCR1-PVE, un clúster Proxmox VE con ámbito Manchester DC1, con una pestaña Custom Objects que muestra los datastores que lo referencian

Un custom object type hereda casi todo lo que hace que un objeto de NetBox sea un objeto de NetBox: vistas de lista y de detalle, una entrada de navegación, endpoints REST, búsqueda de texto completo, registro de cambios, diario, etiquetas, marcadores, importación y exportación, event rules y notificaciones. Nada de eso hubo que escribirlo.

Tampoco es un blob JSON haciéndose pasar por una tabla. El plugin emite DDL real, y lo que aparece en PostgreSQL es una tabla real con restricciones reales:

                    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

El protect que pedí se convirtió en ON DELETE RESTRICT, y funciona: borrar ese clúster vuelve con un 409 nombrando lo que depende de él.

Dos cosas que saber antes de confiar en ello

La validación está en el formulario, no en la API. Esta es la que pillaría a una automatización. Declaré required, una expresión regular y límites numéricos en los campos de un custom object type, y luego escribí sobre ellos a través de la API REST:

Lo que declaréLo que enviéResultado
validation_regexun valor que no encaja con nada201 Created
required: trueel campo omitido por completo201 Created
validation_minimum: 1un número negativo201 Created
unique: trueun duplicado400, rechazado
on_delete_behavior: protectborrar el objeto referenciado409, rechazado

Las dos que aguantaron son las dos que se convirtieron en restricciones de base de datos. El resto existe solo en el formulario web, así que una persona que teclea queda restringida y una sincronización nocturna no. Compáralo con el custom field del núcleo de más arriba, donde la misma expresión regular se imponía a través de la API. Hasta que eso cambie, pon cualquier cosa de la que dependa una factura detrás de una restricción real.

Borrar un tipo elimina una tabla. Borrar un campo elimina una columna. Eso es DDL desde un formulario web, ejecutado por quien tenga el permiso, y la documentación lo dice tal cual.13 Restringe quién puede borrarlos.

Cuándo escribir un plugin de verdad en su lugar

Si el objeto importa, si la automatización escribe en él, y si te molestaría encontrar basura dentro, escribe el modelo tú mismo. Un plugin mínimo de NetBox es un PluginConfig, un modelo, un serializer, un viewset, una tabla, un formulario, unas pocas vistas y un mapa de URL, y sale por debajo de doscientas líneas mayoritariamente declarativas. Heredar de PrimaryModel te da gratis etiquetas, custom fields, registro de cambios, diario, plantillas de exportación y propiedad, y cada restricción que pongas en el modelo se impone en todas partes, porque la propia maquinaria de serializers de NetBox la ejecuta.

Hay una plantilla cookiecutter y un tutorial de plugin completo mantenidos por la comunidad. Empieza por ahí en lugar de por un directorio vacío.

Y lleva la licencia que tú elijas, lo que nadie puede cambiar después.

Validación personalizada y clases de validación

Todo lo anterior trata de registrar lo que hay. Esto trata de negarse a registrar lo que no debería estar.

NetBox valida cada objeto antes de escribirlo, y puedes añadir reglas propias encima. Hay tres mecanismos, todos viven en configuration.py, y entre ellos cubren casi todo lo que necesita un estándar de la casa.

Uno: reglas simples, sin código

Un validador puede ser una simple correspondencia entre nombres de campo y condiciones. Nada de Python, y es portátil entre instalaciones porque son solo datos.

CUSTOM_VALIDATORS = {
    'dcim.site': (
        {'description': {'required': True}},
    ),
    'dcim.device': (
        {'name': {'regex': r'^[a-z0-9]+-[a-z]+-([0-9]{2}|[a-z])$'}},
    ),
}

Las condiciones disponibles son min, max, min_length, max_length, regex, required, prohibited, eq y neq. Puedes alcanzar un objeto relacionado con una ruta por puntos, así que region.name en un emplazamiento vale, y puedes comparar con request.user.username aunque la documentación te diga, con toda razón, que para eso uses permisos.

Ambas reglas se disparan de inmediato:

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.']"]}

Esa segunda es un estándar de nomenclatura, impuesto. No escrito en una página de wiki, no en la cabeza de alguien, no algo por lo que al nuevo le llamen la atención en una revisión tres semanas después. La base de datos no lo acepta.

Dos: una clase de validador, cuando la regla es una frase

Las reglas simples comprueban un campo contra una constante. Las reglas de la casa reales suelen ser condicionales: esto solo importa cuando aquello. Para esas heredas de CustomValidator, sobrescribes validate() y llamas a fail().

Puse tres en un módulo 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',
                )

y las conecté por ruta de puntos:

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',
    ),
}

Fíjate en que un modelo toma una tupla de validadores, mezclando libremente reglas simples y clases, y todos se ejecutan. Incluso un validador único hay que pasarlo como iterable, y eso son cinco minutos fáciles de perder.

Luego las reglas hacen lo que dicen:

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

Como fail() toma un field, el mensaje aterriza en la casilla correcta del formulario en lugar de en lo alto de la página, y esa es la diferencia entre una regla que la gente aprende y una regla que la gente toma a mal.

Esa es también la respuesta al hueco de la sección de custom objects. La validación de allí existía solo en el formulario web. Un CustomValidator se ejecuta en la capa de modelo, así que se aplica a la interfaz, a la API REST, a las mutaciones GraphQL, a las importaciones en lote y a cualquier cosa que haga un script. Hay un solo sitio donde escribir la regla y ninguna forma de rodearla.

Tres: protection rules, para el borrado

CUSTOM_VALIDATORS vigila las escrituras. PROTECTION_RULES vigila los borrados, y toma exactamente las mismas dos formas.

PROTECTION_RULES = {
    'dcim.device': (
        {'status': {'eq': 'offline'}},
    ),
}

Eso dice que un equipo solo puede borrarse cuando está offline. Lo cual produce:

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

Dos pulsaciones de fricción entre alguien y un equipo en servicio, y es el seguro más barato de toda la aplicación. Haz que el proceso de retirada sea lo que desbloquea el botón de borrar.

La que te va a pillar

Los datos existentes no se comprueban hasta que vuelves a tocarlos. Añadir una regla no vuelve atrás a validar lo que ya está ahí. Se queda quieta hasta que alguien guarda un objeto que la incumple, y entonces esa persona recibe un error sobre una decisión con la que no tuvo nada que ver.

Lo vi ocurrir. mcr1-hv-06 se creó antes de que yo escribiera nada de esto, como hipervisor sin tenant. Ahí estaba, tan tranquilo. Luego edité su descripción:

PATCH {"description": "touching it to trigger revalidation"}
  {"tenant": ["A Hypervisor is billable kit, so it must name
              the customer it belongs to."]}

La edición no tenía nada que ver con el tenant. La regla se disparó igualmente, porque la validación corre sobre el objeto entero.

Ese es el comportamiento correcto y es también cómo una regla nueva se convierte en un ticket de soporte. Antes de activar una, consulta los objetos que fallarían y arréglalos primero. La API lo pone fácil: la regla es un filtro, así que pide los equipos con ese rol y sin tenant, y ya tienes tu lista.

Activa la regla después. Entonces solo pilla errores nuevos, que es para lo que está.

Pilotarlo desde Ansible

Una fuente de verdad que nadie lee se pudre. La colección netbox.netbox es como la mayoría evita que eso pase, y funciona en ambos sentidos: NetBox le dice a Ansible qué existe, y Ansible le dice a NetBox qué ha construido.

Va por la versión 3.23.0, con licencia GPL-3.0, y se ha descargado de Galaxy más de 13,4 millones de veces. Lleva 91 módulos y un plugin de inventario.14 Todo lo que sigue se ejecutó contra esa misma instancia, desde un contenedor sin nada más dentro que ansible-core 2.21.4, pynetbox 7.8.0 y la colección.

El inventario es una consulta, no un fichero

Esta es la mitad que se amortiza la primera tarde. nb_inventory construye tu inventario de Ansible directamente desde 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

Eso produce esto, sin que nadie mantenga una lista de hosts:

@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

Mira la columna de la derecha. Como los tenants están puestos en los equipos, obtienes un grupo por cliente gratis, así que --limit tenants_ravenscroft-legal ejecuta una play contra exactamente el equipo de un solo cliente. Añade un equipo en NetBox y está en el grupo en la siguiente ejecución. Retira uno y desaparece. Nadie edita nada.

Cada host llega cargando lo que NetBox sabe de él:

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"}

Veintidós claves en total, y una de ellas merece notarse antes de que te sorprenda. ansible_host es la dirección IPv6, porque ese equipo tiene una primaria v6 puesta. Lo comprobé contra otros dos que solo tienen v4 y volvieron en v4, así que la regla es que el plugin prefiere v6 donde existe una primaria v6. Lo cual es correcto, y es también de esas cosas que prefieres descubrir ahora y no mientras te preguntas por qué una play conecta por un camino en el que no habías pensado.

Escribir de vuelta

Los módulos son el otro sentido, y el que merece tener es la asignación de IP, porque NetBox sabe qué está libre y tu playbook no.

- 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

Fíjate en lo que no hay ahí. Ninguna dirección IP. Nombras el prefijo y NetBox te devuelve la siguiente 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)"

Y está en la interfaz un segundo después, todavía sin cablear a nada pero enracado, con tenant y direccionado:

Pestaña de interfaces de NetBox para mcr1-hv-07, un equipo creado por el playbook, con una interfaz eno1 de tipo SFP28 25GE descrita to leaf-01 y con 10.20.22.1/24

La trampa de ese playbook

Ejecútalo una segunda vez sin cambiar una línea y pasa esto:

"device   : mcr1-hv-07  (already correct)"
"interface: eno1  (already correct)"
"address  : 10.20.22.2/24  (allocated)"

El equipo y la interfaz son idempotentes. La dirección no, y no es un error. state: present significa «haz que quede así». state: new significa «dame una nueva», todas y cada una de las veces, y eso es exactamente lo que hizo: dos direcciones en una interfaz tras dos ejecuciones.

Eso está bien cuando de verdad estás aprovisionando algo nuevo, y se comerá un prefijo en silencio si lo metes en una tarea que corre cada noche. Asigna una vez y registra el resultado, o usa state: present con la dirección que ya tienes. El módulo hace lo que le pediste. La cuestión es si pediste lo que querías decir.

No es solo Ansible

La colección se lleva la atención porque Ansible es donde ya está la mayoría de los equipos de red, pero la API es el producto y bastantes cosas la hablan.

CosaQué esLicencia
pynetboxEl cliente Python que usa la propia colecciónApache 2.0
terraform-provider-netboxGestionar objetos de NetBox como recursos de Terraform, mantenido activamente por e-breuningerMPL 2.0
nornir_netboxNetBox como inventario de Nornir, para quien hace Python en vez de YAMLApache 2.0
Prometheus SDSirve a Prometheus sus objetivos de scrape desde NetBoxMIT
go-netboxUn cliente Go, aunque no se ha tocado desde mayo de 2025ver repositorio
DiodeLa propia tubería de ingesta de NetBox Labs para empujar datos descubiertosNetBox Limited Use

La entrada de Terraform es la interesante para quien ya gestione infraestructura así, porque permite que un solo plan cree el recurso en la nube y el registro de NetBox que lo documenta, en lugar de dejar la segunda mitad a la memoria de alguien.15

Diode lleva la misma licencia que Branching y Custom Objects, así que aplica la misma lectura antes de construir sobre él.

Lo que esa tarde compra de verdad

Todo lo anterior me llevó un día en una sola máquina, y la mayor parte de ese día se fue en sembrar datos para que las pantallas tuvieran algo dentro. La instalación son veinte minutos por cualquiera de las dos vías. Las decisiones son la parte que importa y todas se toman en la primera hora: los tenants antes que nada, los tipos de equipo antes que los equipos, los números en los tipos y no en el equipo.

Lo que obtienes a cambio no es documentación. Esa distinción no la hace nadie hasta que ha tenido ambas: la documentación es algo que escribes y luego dejas de mantener. Lo que obtienes es una base de datos que se niega a contener una contradicción: no te dejará poner dos cosas en una misma unidad de rack, ni un rack en una ubicación que pertenece a otro emplazamiento, ni un equipo sobre un tipo que no existe. Cada uno de esos rechazos es una discusión que no vas a tener dentro de seis meses.

Y te dirá cosas que nadie le preguntó. Ese armario está lleno al 28,6 % de equipo y al 90,7 % de electricidad. Nadie se puso a buscar eso. Cayó de poner una potencia una vez en un tipo de equipo, y es la diferencia entre enracar el próximo pedido ahí y descubrirlo por las malas.

La reserva honesta es la misma que tiene toda fuente de verdad. Solo vale lo que te cuesta mantenerla correcta, y la única versión de esto que sobrevive a un trimestre ajetreado es aquella en la que algo se rompe de forma visible cuando los datos están mal. Conecta tu supervisión, tu aprovisionamiento o tus reglas de cortafuegos para que lean de ella, y una entrada equivocada deja de ser un problema de documentación que alguien atenderá algún día. Pasa a ser una caída a las nueve y media de un martes, con un nombre puesto. Eso suena a coste. Es todo el mecanismo.

Tampoco nadie te lo va a agradecer. Un registro correcto se manifiesta como la migración que llevó quince días en lugar de un trimestre, la auditoría que llevó una tarde, el cliente que obtuvo una respuesta directa por teléfono. Nada de eso aparece en un informe junto a tu nombre.

Hazlo igualmente. La alternativa es un negocio incapaz de describirse, y un negocio incapaz de describirse no se está dirigiendo. Se está recordando, por cada año menos gente.


  1. NetBox, Introduction — «Today, the open source project is stewarded by NetBox Labs and a team of volunteer maintainers», y el origen en DigitalOcean en 2015, liberado como open source en junio de 2016. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  2. NetBox, LICENSE.txt — Apache License 2.0, copyright DigitalOcean, LLC. Origen y custodia desde la introducción. ↩︎ ↩︎

  3. Notas de la versión v1.0.0 de Nautobot — «a divergent fork of NetBox 2.10», publicada el 26 de abril de 2021, y la lista de lo que añadió respecto a NetBox 2.10. ↩︎ ↩︎

  4. Network to Code, «Why Did Network to Code Fork NetBox?», 25 de febrero de 2021 — el razonamiento sobre los SLA y el soporte a largo plazo, la divergencia de visión, y «the NetBox project team suggested that we should consider forking». ↩︎

  5. NetBox Labs se fundó en 2023 como escisión de NS1 tras su adquisición por IBM, cofundada por el mantenedor principal de NetBox, Jeremy Stretch, y anunció una Serie A de 20 millones de dólares en abril de 2023: NetBox Labs, «Let’s Go: Announcing NetBox Labs» y el anuncio de la Serie A. ↩︎

  6. NetBox Labs, NetBox Enterprise — la edición comercial autogestionada, con «24/7 expert assistance from the NetBox Labs team»; NetBox Cloud es la oferta alojada. Los términos concretos de SLA no están publicados en esa página. ↩︎

  7. Recuentos de estrellas y forks, fechas de publicación y licencias de ambos proyectos leídos de la API de GitHub el 30 de septiembre de 2026: netbox-community/netbox y nautobot/nautobot. Las versiones paralelas 3.2.x y 2.4.x de Nautobot están ambas fechadas el 28 de septiembre de 2026. ↩︎

  8. netbox-community/netbox-docker — la pila de contenedores de la comunidad, Apache 2.0. ↩︎

  9. NetBox, Installation — la tabla de versiones admitidas, y las notas de la versión v4.7 para los mínimos elevados de PostgreSQL y Redis. Versión y edición leídas de netbox/release.yaml. ↩︎

  10. NetBox Limited Use License 1.0 — la llevan netbox-custom-objects y, de forma idéntica, netbox-branching. Ambas cláusulas citadas son literales. ↩︎

  11. NetBox, HTTP Server installation, «What’s Next?» — «Some of the most popular plugins include» NetBox Branching, NetBox Custom Objects, NetBox DNS y NetBox BGP, sin mención alguna de los términos de licencia. ↩︎

  12. NetBox, Data Validation configuration — FIELD_CHOICES, y el sufijo de signo más que extiende en lugar de reemplazar: «To replace the available choices, specify the app, model, and field name separated by dots … To extend the available choices, append a plus sign». ↩︎

  13. Documentación de netboxlabs/netbox-custom-objects — «Deleting a Custom Object Type drops an entire database table and should be done with caution.» ↩︎

  14. netbox.netbox en Ansible Galaxy y netbox-community/ansible_modules — versión 3.23.0, GPL-3.0, 91 módulos más el plugin de inventario nb_inventory. Recuento de descargas leído en Galaxy el 30 de septiembre de 2026. Todo lo mostrado se ejecutó con ansible-core 2.21.4 y pynetbox 7.8.0. ↩︎

  15. Licencias, actividad y recuentos de estrellas leídos de cada proyecto el 30 de septiembre de 2026: pynetbox (Apache 2.0), terraform-provider-netbox (MPL 2.0, v6.0.0-rc.1 en el registro de Terraform), nornir_netbox (Apache 2.0), netbox-plugin-prometheus-sd (MIT), go-netbox (último push el 9 de mayo de 2025) y Diode, que lleva la misma NetBox Limited Use License 1.0 que Branching y Custom Objects. ↩︎