IPAM — NetBox¶
NetBox is the single source of truth for the network model — devices, interfaces, IPs, cables, VLANs and L2VPN. Almost everything else is derived from it by the Ansible automation; the rare non-derivable settings come from NetBox custom fields and config context.
Direction of truth¶
Git + the automation are authoritative for intent; the devices are made to match; and their live state is scraped back into NetBox as as-deployed documentation and for drift cross-check. NetBox is never in the config critical path and is never written by the converge step.
The ros_role custom field¶
The intent classifier on each interface. Values include:
loopback, core-link, uplink, venue-handoff, ebgp-peer, starlink-wan,
guest-trunk, vpls-trunk / vpls-circuit, pop-trunk / pop-handoff,
radio-mgmt, lan-trunk / access, peer-link / mlag-host / single-port.
Optional per-interface overrides: ospf_cost, ospf/ldp/bfd/mpls_enabled,
bgp_in_filter / out_filter, uplink_primary, vpls_service, circuit_id,
backup_path, guest_vpls, mlag_id, pvid.
Device-level fields: venue_id, bgp_asn (on eBGP-peer devices — 65502 Starlink,
65510 edge FW), core_as_override (blank ⇒ 65500), ros_version / ros_firmware
(written by the fact-sync playbook), oob_ip.
netbox-setup.yml creates all of these idempotently.
Derivation, not duplication¶
A unit-tested filter plugin (plugins/filter/netbox_topology.py) walks the cable
graph to derive most configuration. "Where did this config come from?" resolves
in this precedence:
For example, OSPF cost is derived from interface speed (100G→1, 40G→2, 25G→4,
10G→10, 1G→100), with wireless forced to the policy cost 500 and 4000 meaning
"drain for planned work". A /31 needs only the local IP set — the far end is
derived by arithmetic, which kills the classic "two ends disagree" bug.
Validation¶
scripts/netbox_lint.py validates the model before any apply: platform/role,
primary IP, link IP + cable presence, hand-off far-device venue_id, ebgp-peer
far-device bgp_asn (assembly fails without it), radio-mgmt completeness, and
derivable OSPF cost.
DNS from IPAM¶
Every IP carries a dns_name (<iface>.<device>.infra.pubinvest.co.uk) and is
published to PowerDNS forward + reverse by powerdns-sync.yml. OOB names derive
from the device name (<device>.oob.pubinvest.co.uk, forward-only via
oob-sync.yml). The two zones are deliberately disjoint, so forward/reverse
intentionally disagree for OOB IPs.
Terraform note¶
An earlier design used Terraform (network.yaml registry, routeros provider)
with git as the source of truth and NetBox downstream. The current implementation
uses Ansible for convergence (see Network automation); the
registry concept survives as inventory/group_vars/all/registry.yml, which
reshapes raw NetBox objects into the variables the roles consume.