diff --git a/docs/backends/device_discovery/README.md b/docs/backends/device_discovery/README.md index 06f1c3f..1d5944c 100644 --- a/docs/backends/device_discovery/README.md +++ b/docs/backends/device_discovery/README.md @@ -16,6 +16,7 @@ The device discovery backend uses [Diode Python SDK](https://github.com/netboxla * [IP Address](https://github.com/netboxlabs/diode-sdk-python/blob/develop/docs/examples/ip_address.py) * [Prefix](https://github.com/netboxlabs/diode-sdk-python/blob/develop/docs/examples/prefix.py) * [VLAN](https://github.com/netboxlabs/diode-sdk-python/blob/develop/docs/examples/vlan.py) +* [VRF](https://github.com/netboxlabs/diode-sdk-python/blob/develop/docs/examples/vrf.py) * [VirtualChassis](https://github.com/netboxlabs/diode-sdk-python/blob/develop/docs/examples/virtual_chassis_example.py) * [Module](https://github.com/netboxlabs/diode-sdk-python/blob/develop/docs/examples/module.py) * [ModuleBay](https://github.com/netboxlabs/diode-sdk-python/blob/develop/docs/examples/module_bay.py) @@ -26,6 +27,8 @@ When a target is a switch stack / Virtual Chassis (NetBox `VirtualChassis`), dev When a target is a modular chassis and the `discover_modules` policy option is enabled, device-discovery additionally emits `Module` and `ModuleBay` entities for each chassis slot (and, in `full` mode, each transceiver sub-bay) — see [Modules / ModuleBays](#modules--modulebays) below. Defaults to `off`, so existing operators see zero behaviour change. +When the `discover_vrfs` policy option is enabled, device-discovery emits a `VRF` entity for each VRF configured on the device and attaches it to the IP addresses and prefixes of the interfaces inside that VRF — see [VRFs](#vrfs) below. Defaults to `false`, so existing operators see zero behaviour change. + ## Configuration The `device_discovery` backend does not require any special configuration, though overriding `host` and `port` values can be specified. The backend will use the `diode` settings specified in the `common` subsection to forward discovery results. @@ -71,6 +74,7 @@ Current supported options: | create_unknown_vlans | bool | When discovering interface↔VLAN associations, auto-emit a VLAN entity for any VID referenced on an interface but absent from the device's VLAN database. Stubs inherit attributes from `defaults.vlan` for stable matching. Defaults to `True`. Set `False` to drop unknown VIDs from interface associations entirely (requires every referenced VLAN to already exist in NetBox). Only drivers that implement `get_interfaces_vlans()` populate these associations — see the [supported platforms page](./supported_platforms.md). | | discover_modules | str | Controls emission of `Module` / `ModuleBay` entities on modular chassis. One of `off` (default — no modules emitted, zero behaviour change), `linecards` (one Module per chassis slot — line cards, supervisors, etc. — transceiver sub-bays skipped), or `full` (linecards plus one Module per transceiver sub-bay; interfaces carry a `module=` ref to the transceiver they're connected to). Only drivers that implement `get_modules()` populate module data — see the [supported platforms page](./supported_platforms.md#modules--modulebays). See [Modules / ModuleBays](#modules--modulebays) for the emission shape and current sub-bay rendering trade-off. | | propagate_defaults_to_prefix_scope | bool | When `True` AND no explicit `defaults.prefix.scope_*` is set, `defaults.site` cascades to `Prefix.scope_site` (the literal placeholder `"undefined"` is skipped) and `defaults.location` cascades to `Prefix.scope_location`. Defaults to `False`. Setting any explicit `defaults.prefix.scope_*` puts the operator in "explicit mode" and the cascade is skipped wholesale. | +| discover_vrfs | bool | When `True`, discovers VRFs from the device via the driver's `get_network_instances()` and attaches each VRF to the IP addresses and prefixes of its member interfaces. A discovered VRF takes precedence over the `defaults.*.vrf` / `vrf_ipv4` / `vrf_ipv6` settings for those interfaces; interfaces in the default routing table keep the configured defaults. Defaults to `False`. Only drivers that implement `get_network_instances()` populate VRF data — see the [supported platforms page](./supported_platforms.md#vrfs). See [VRFs](#vrfs) for filtering rules and route-distinguisher handling. | #### Defaults Current supported defaults: @@ -341,6 +345,24 @@ When a driver implements module discovery and the `discover_modules` policy opti **Supported drivers.** Module discovery is opt-in per driver (analogous to interface↔VLAN associations and stack discovery). See the [supported platforms page](./supported_platforms.md#modules--modulebays) for the current list; vendors land as follow-up PRs as the underlying drivers gain module-discovery support. +## VRFs + +When the `discover_vrfs` policy option is enabled (defaults to `false`), device-discovery calls the driver's standard NAPALM `get_network_instances()` getter and emits a NetBox `VRF` entity per VRF configured on the device. Each discovered VRF is attached to the `IPAddress` and `Prefix` entities of the interfaces inside that VRF; interfaces in the default routing table carry no discovered VRF and keep whatever `defaults.*.vrf` configuration is in effect. + +**Precedence.** Device state beats policy defaults: for an interface inside a discovered VRF, the discovered VRF wins over `defaults.ipaddress.vrf` / `vrf_ipv4` / `vrf_ipv6` (and the `defaults.prefix` equivalents). The defaults remain the fallback for all other interfaces, so mixed configurations work as expected. + +**Filtering.** Not every network instance a device reports is an operator-meaningful VRF. The following are skipped: + +* the default instance (the global routing table), +* L2 instance types (VPLS / EVPN / L2VPN instances — these have no NetBox VRF equivalent), +* platform-internal instances whose names start with `__` (e.g. Cisco's `__Platform_iVRF:_ID00_`). + +Management VRFs (e.g. Cisco `Mgmt-vrf`, Junos `mgmt_junos`) are real VRFs and are kept. + +**Route distinguisher.** The `rd` field is set only when the device reports a real value. Devices without MPLS routinely report an empty or placeholder RD — those stay off the wire, so the VRF matches a NetBox VRF record whose RD is empty rather than creating one with a bogus RD. Note the Diode reconciliation caveat: an ingested VRF without an RD only matches NetBox VRFs whose RD is unset. If a VRF with the same name already exists in NetBox **with** an RD configured, the first discovery cycle creates a separate RD-less VRF record alongside it (subsequent cycles converge on the RD-less record). If you pre-seed VRFs in NetBox and want discovery to match them, either leave their RD unset or make sure the device reports the same RD. + +**Supported drivers.** VRF discovery is available on drivers that implement `get_network_instances()` — see the [supported platforms page](./supported_platforms.md#vrfs) for the current list. On drivers without support, enabling the option logs a warning per cycle and discovery continues without VRF data. + ## What NAPALM Collects Automatically The tables below show which fields are populated automatically from the device versus which must be provided via `defaults` in the policy configuration. @@ -383,7 +405,7 @@ The tables below show which fields are populated automatically from the device v | Address (with prefix length) | `get_interfaces_ip()` | Auto-collected; IPv4 and IPv6 | | Assigned interface | — | Automatically linked to the interface | | Role | **Not collected** | Must be set via `defaults.ipaddress.role` | -| VRF | **Not collected** | Must be set via `defaults.ipaddress.vrf` | +| VRF | `get_network_instances()` when `options.discover_vrfs: true` | Otherwise set via `defaults.ipaddress.vrf` (a discovered VRF wins over the defaults — see [VRFs](#vrfs)) | | Tenant | **Not collected** | Must be set via `defaults.ipaddress.tenant` | ### Prefix @@ -393,7 +415,8 @@ Prefixes are derived from IP addresses discovered on interfaces. The network add | Field | Source | Notes | |-------|--------|-------| | Prefix (network address) | Derived from IP address | Auto-computed | -| VRF / Role / Tenant | **Not collected** | Must be set via `defaults.prefix.*` | +| VRF | `get_network_instances()` when `options.discover_vrfs: true` | Otherwise set via `defaults.prefix.vrf` (a discovered VRF wins over the defaults — see [VRFs](#vrfs)) | +| Role / Tenant | **Not collected** | Must be set via `defaults.prefix.*` | | Scope (site / location) | **Not collected** | Set via `defaults.prefix.scope_*` (see Nested Defaults) or opt into the cascade via `options.propagate_defaults_to_prefix_scope` | Prefix scope is a `oneof` — a Prefix carries one of `scope_site` or `scope_location`. When both `defaults.prefix.scope_*` are set, the most-specific wins on the wire: `scope_location` > `scope_site`. By default `defaults.site` does NOT auto-fill `Prefix.scope_site` — set `options.propagate_defaults_to_prefix_scope: true` to enable the cascade. Any explicit `defaults.prefix.scope_*` puts the operator in "explicit mode" and the cascade is skipped wholesale, so a cascaded more-specific scope can't override an operator's explicit less-specific choice. Clearing an existing scope requires editing NetBox directly. diff --git a/docs/backends/device_discovery/supported_platforms.md b/docs/backends/device_discovery/supported_platforms.md index 575f521..51b1c0a 100644 --- a/docs/backends/device_discovery/supported_platforms.md +++ b/docs/backends/device_discovery/supported_platforms.md @@ -235,6 +235,40 @@ Drivers that implement `get_modules()` discover the per-slot module inventory on **Extreme EXOS (`extreme_exos`).** Module discovery is scoped to the BlackDiamond X8 modular chassis. Other EXOS hardware in the field — the X670 / X870 fixed pizza-boxes, the BD-X8-X32 stackable variant — share the `X8` family token in their model strings but are not modular and must not produce module entries. Family detection scans the **full `show version` output** (not just `System Type:`, which Extreme's command-reference BD-X8 example omits in favour of `Switch :` / `Chassis :` / `Slot-*` / `FM-*` lines) using a regex with a negative-lookahead anchor (`(?:BD-|BLACKDIAMOND\s+)X8(?![-\w])`) rather than a Python `\b` word-boundary, because `\b` fires between the digit `8` and the dash in `BD-X8-32` and would accept the wrong model. For BD-X8 chassis the driver parses `show slot detail` directly — no `extreme_exos_show_slot_detail` ntc-template exists, so a block-per-slot regex walks each `Slot-N information:` / `MSM-A information:` / `FM-1 information:` block (case-insensitive) and extracts the `Hw Module Type` and `Serial number` fields. The serial capture is whitespace-tolerant: real BD-X8 prints two whitespace-separated tokens (`Serial number: 800432-00-09 1534G-01368` — part-number plus unique serial), and the parser collapses internal whitespace runs to a single space so the persisted value is stable. The classifier maps `BDX-MM` (Management Module) to `supervisor` and the I/O / Fabric Module families (`BDXA-FM` / `BDXA-` / `BDXB-`) to `linecard`; Fabric Modules emit as `linecard` because NetBox has no fabric module type today. Any hypothetical `BDXB-FM*` SKU still classifies as linecard via the generic `BDXB-` fallback. Slot header parsing tolerates both `Slot-N` and `Slot N` (space-separator) layouts that EXOS releases interchangeably emit; space variants are normalised to hyphenated bay names (`Slot 1` → `Slot-1`) so bay identifiers stay consistent across releases. Single-member envelope keyed by `None`; EXOS has no stack-of-modular dispatch in v1. +## VRFs + +Drivers that implement the standard NAPALM `get_network_instances()` getter discover the VRFs configured on the device and attach them to the IP addresses and prefixes of each VRF's member interfaces. Emission is gated by the `discover_vrfs` policy option (defaults to `false`); see the [device discovery README](./README.md#vrfs) for the filtering rules, route-distinguisher handling, and precedence over the `defaults.*.vrf` settings. + +| Driver | Status | +|--------|--------| +| `ios` | Supported (Cisco IOS / IOS-XE) — upstream NAPALM `get_network_instances()` (`show vrf detail` + `show ip interface brief`). | +| `eos` | Supported (Arista EOS) — upstream NAPALM `get_network_instances()`. | +| `junos` | Supported (Juniper Junos) — upstream NAPALM `get_network_instances()`; both `vrf` (L3VPN) and `virtual-router` (VRF-lite, incl. `mgmt_junos`) routing instances translate to VRFs. | +| `nxos` | Supported (Cisco NX-OS via NX-API) — upstream NAPALM `get_network_instances()` (`show vrf detail \| json` + `show vrf interface \| json`). | +| `nxos_ssh` | Supported (Cisco NX-OS via SSH) — same coverage as `nxos`. | +| `iosxr` | Supported (Cisco IOS-XR via pyIOSXR / XML-Agent over SSH) — `show vrf all detail` parsed driver-locally (the cisco_xr ntc-template FSM mis-parses VRF blocks without an address family or with import/export route policies attached, so a stuck-state-free block parser is used instead). An unconfigured RD (`not set`) is treated as absent. | +| `iosxr_netconf` | Supported (Cisco IOS-XR via NETCONF) — one subtree-filtered get on the `Cisco-IOS-XR-mpls-vpn-oper` `l3vpn/vrfs` model, the same data source the CLI command renders. Same coverage as `iosxr`. | +| `nokia_sros` | Supported (Nokia SR OS via NETCONF) — VPRN services map to VRFs (one get on `configure/service/vprn`); the route distinguisher comes from `bgp-ipvpn/mpls` and is absent on VPRNs without an MPLS L3VPN backbone. VPRN member interfaces are keyed `/`, matching how the driver names them for IP discovery. The Base router is the default routing table. | +| `nokia_srl` | Supported (Nokia SR Linux via SSH) — network instances are SR Linux's native routing model; three targeted `info from state network-instance` calls discover types, interface membership, and the BGP-VPN route distinguisher. `ip-vrf` instances map to VRFs; `mac-vrf` (L2) and the `default` instance are excluded. | +| `huawei_vrp` | Supported (Huawei VRP via SSH) — VPN instances map to VRFs: names and member interfaces from `display ip vpn-instance interface`, route distinguishers from the `display ip vpn-instance` summary (both the `ipv4-family` and plain `IPv4`/`IPv6` address-family column forms are handled; VRP configures the RD per address family and the first configured one wins). | +| `hp_comware` | Supported (HPE / H3C Comware via SSH) — VPN instances map to VRFs: `display ip vpn-instance` enumerates them, one `display ip vpn-instance instance-name ` per instance supplies the RD and member interfaces (wrapped multi-member lists handled). | +| `cumulus_linux` | Supported (NVIDIA Cumulus Linux via SSH) — Linux VRF devices from `ip vrf show`, member interfaces from `ip link show master ` (kernel `@parent` decorations stripped). Linux VRFs carry no kernel-level route distinguisher (it lives in FRR BGP), so VRFs are always emitted without an RD. | +| `dell_sonic` | Supported (Dell Enterprise SONiC via SSH) — VRFs and their member interfaces from `show vrf`. The management VRF (`mgmt`) is a real VRF and is kept. SONiC's `show vrf` carries no route distinguisher, so VRFs are always emitted without an RD. | +| `nokia_sros_ssh` | Supported (Nokia SR OS via SSH CLI) — VPRN services map to VRFs: `show service service-using vprn` enumerates them (parsed driver-locally because SR OS service names are optional and the ntc-template rejects the unnamed form; unnamed services fall back to the numeric service id), one `show service id interface` per service lists members, keyed `/` like the NETCONF sibling. Route distinguishers are not collected on the classic CLI in this first pass. | +| `aruba_aoscx` | Supported (Aruba AOS-CX via REST) — VRFs from the `system/vrfs` resource (EVPN route distinguisher carried when the firmware exposes it); member interfaces from each interface's VRF reference. The factory `default` VRF is the global routing table. `aruba_aoscx_ssh` is not yet supported for VRF discovery. | +| `cisco_viptela_ssh` | Supported (Cisco SD-WAN / Viptela via SSH) — VPN segments map to VRFs named by VPN number, derived from the VPN column of `show interface`. Transport VPN 0 is the underlay (default instance); the management VPN 512 is kept as a real VRF. No route distinguisher at this layer. | +| `paloalto_panos` | Supported (PaloAlto PAN-OS via XML API) — virtual routers map to VRFs (`vr:`, or `logical-router:` with Advanced Routing on PAN-OS 10.2+), derived from the forwarding field of `show interface all`. VSYS are never mapped. The factory `default` virtual router is the global routing table. Virtual routers with no interfaces assigned are not discovered. | +| `paloalto_panos_ssh` | Supported (PaloAlto PAN-OS via SSH CLI) — same mapping as `paloalto_panos`, from the FORWARDING column of `show interface logical`; both transports emit identical results for the same configuration. | +| `extreme_slx` | Supported (Extreme SLX-OS via SSH) — VRF membership from the Vrf column of `show ip interface brief` (the same rows IP discovery parses). `default-vrf` is the global routing table; `mgmt-vrf` is a real VRF and is kept. Route distinguishers are not collected, and VRFs with no L3 interfaces are not discovered (enumeration is membership-derived). | +| `dell_ftos` | Supported (Dell EMC OS9 / FTOS via SSH) — VRFs and member interfaces from `show ip vrf`; abbreviated member names (`Gi 1/2`) expand to the full forms interface discovery uses, including wrapped lists and trailing-number ranges. `default` is the global routing table; the `management` VRF is kept. RD not collected. Built from the vendor-documented output format — please report any live-device parse gaps. | +| `brocade_netiron` | Supported (Brocade/Extreme NetIron via SSH) — VRFs, default RDs, and member interfaces from `show vrf`; member shorthand (`ve2`, `e1/1`) is canonicalised to the names interface discovery emits. Built from the vendor-documented output format — please report any live-device parse gaps. | +| `extreme_vsp` | Supported (Extreme VOSS / VSP via SSH) — `show ip vrf` enumerates VRFs (incl. L3VSNs); one `show ip interface vrf ` per VRF lists members. `GlobalRouter` is the global routing table; `MgmtRouter` is kept. RD not collected. Built from the vendor-documented output format — please report any live-device parse gaps. | +| other drivers | See below — remaining drivers are either permanently N/A (no VRF concept) or demand-driven. Enabling `discover_vrfs` on them logs a warning per discovery cycle and discovery continues without VRF data. | + +**Permanently N/A** (no L3 VRF concept on the platform — no implementation planned): `aruba_os`, `aruba_osswitch`, `hp_procurve`, `avaya_ers`, `dell_powerconnect`, `cisco_s300`, `ciena_saos`, `cisco_fxos`, `cisco_wlc`, `checkpoint_gaia` (VSX virtual systems are firewall contexts, not VRFs), `huawei_smartax`, `ubiquiti_edgerouter`, `ubiquiti_edgeswitch`, `ubiquiti_unifiswitch`. + +**Demand-driven** (the platform has a VRF or VRF-like concept; implementation awaits a real-device output capture — open an issue with one if you need it): `brocade_fastiron` (ICX VRF-lite), `alcatel_aos` (OmniSwitch VRF), `aruba_aoscx_ssh` (use the REST `aruba_aoscx` driver meanwhile), `mikrotik_routeros` (RouterOS v7 `/ip/vrf` only — v6 routing marks will not be mapped), `fortinet_fortios_ssh` (only explicit interface VRF ids would be mapped — VDOMs are firewall contexts, never VRFs), `cisco_asa` / `cisco_asa_ssh` / `cisco_ftd_ssh` (multi-context/virtual-router semantics need design), `cisco_apic` (ACI tenant VRFs are fabric-level objects, out of scope for per-device discovery), `ericsson_ipos`, `mellanox_mlnxos`. + ## Querying supported drivers at runtime device-discovery exposes its effective driver list via its capabilities endpoint: