feat: Add Infrahub Jinja2 transform for BGP configuration (#23) (#27)

## Summary

Closes #23. Implements a single unified `bgp_yang_transform` covering the complete BGP router stanza for all 10 fabric devices.

**Design decision:** One transform (one query + one template) rather than 4 separate transforms, because all BGP components (process config, peer groups, neighbors, AFs) live under a single `router bgp <ASN>` stanza and must be consistent. This avoids multiple API calls per device and keeps the data model coherent.

| File | Description |
|------|-------------|
| `infrahub/transforms/queries/bgp_intent.gql` | Unified GraphQL query — `InfraBGPRouterConfig` (with peer_groups, sessions) + `InfraBGPAddressFamily` (with active_peer_groups, active_sessions, networks, optional vrf) |
| `infrahub/transforms/templates/bgp_yang.j2` | Jinja2 template — renders `bgp.global`, `bgp.peer_groups`, `bgp.neighbors`, `bgp.address_families`, `bgp.vrf_neighbors`, `bgp.vrf_address_families`; returns `[]` for devices with no BGP config |
| `infrahub/transforms/tests/bgp_yang/test.yml` | Smoke check + unit render tests for leaf1, spine1, leaf7 |
| `infrahub/transforms/tests/bgp_yang/leaf1/` | 3 peer-groups, 5 global neighbors, 2 global AFs |
| `infrahub/transforms/tests/bgp_yang/spine1/` | 1 peer-group (evpn/next-hop-unchanged), 16 neighbors (8 direct underlay + 8 EVPN), IPv4 AF activates individual sessions |
| `infrahub/transforms/tests/bgp_yang/leaf7/` | leaf1 pattern + VRF gold border session (AS 64999) + VRF-scoped IPv4 unicast AF |
| `.infrahub.yml` | Registers `bgp_intent` query and `bgp_yang_transform` |

## Validation

| Device | Expected output |
|--------|----------------|
| `leaf1` | 3 peer-groups, 5 global neighbors (underlay×2, iBGP×1, EVPN×2), 2 AFs, empty VRF sections |
| `spine1` | 1 peer-group (evpn, next-hop-unchanged), 16 neighbors (8 direct with `remote_asn`, 8 EVPN via peer-group), IPv4 AF activates individual sessions |
| `leaf7` | Same as leaf1 (AS 65004) + `vrf_neighbors: [{10.90.90.1, AS 64999, VRF gold}]` + `vrf_address_families: [{ipv4, VRF gold, active_sessions: [10.90.90.1]}]` |

```bash
infrahubctl render bgp_yang_transform device_name=leaf1
infrahubctl render bgp_yang_transform device_name=spine1
infrahubctl render bgp_yang_transform device_name=leaf7
```

## Design notes

- Follows identical conventions to existing transforms (#20–#22)
- All optional relationships (`remote_asn`, `peer_group`, `vrf`, `peer_device`, `update_source`, etc.) wrapped in `is defined and is not none` guards
- `send_community` value `"none"` (schema default) is normalised to `null` in the output — keeps the rendered JSON clean for downstream consumers
- VRF-scoped sessions and AFs are separated into `vrf_neighbors` / `vrf_address_families` arrays, each entry carrying a `"vrf"` key, so the template consumer can trivially iterate per-VRF without filtering
This commit was merged in pull request #27.
This commit is contained in:
2026-03-01 13:22:51 +00:00
parent cf7da535ed
commit 1b918a4cbc
12 changed files with 1449 additions and 0 deletions

View File

@@ -512,6 +512,7 @@ spec:
# Spine1 address families
# ============================================================
- bgp_config: ["spine1"]
device: ["spine1"]
local_identifier: "spine1__ipv4_unicast"
afi: ipv4
safi: unicast
@@ -527,6 +528,7 @@ spec:
networks:
- ["10.0.250.1/32"]
- bgp_config: ["spine1"]
device: ["spine1"]
local_identifier: "spine1__evpn_unicast"
afi: evpn
safi: unicast
@@ -536,6 +538,7 @@ spec:
# Spine2 address families
# ============================================================
- bgp_config: ["spine2"]
device: ["spine2"]
local_identifier: "spine2__ipv4_unicast"
afi: ipv4
safi: unicast
@@ -551,6 +554,7 @@ spec:
networks:
- ["10.0.250.2/32"]
- bgp_config: ["spine2"]
device: ["spine2"]
local_identifier: "spine2__evpn_unicast"
afi: evpn
safi: unicast
@@ -560,6 +564,7 @@ spec:
# Leaf1 address families
# ============================================================
- bgp_config: ["leaf1"]
device: ["leaf1"]
local_identifier: "leaf1__ipv4_unicast"
afi: ipv4
safi: unicast
@@ -570,6 +575,7 @@ spec:
- ["10.0.250.11/32"]
- ["10.0.255.11/32"]
- bgp_config: ["leaf1"]
device: ["leaf1"]
local_identifier: "leaf1__evpn_unicast"
afi: evpn
safi: unicast
@@ -579,6 +585,7 @@ spec:
# Leaf2 address families
# ============================================================
- bgp_config: ["leaf2"]
device: ["leaf2"]
local_identifier: "leaf2__ipv4_unicast"
afi: ipv4
safi: unicast
@@ -589,6 +596,7 @@ spec:
- ["10.0.250.12/32"]
- ["10.0.255.11/32"]
- bgp_config: ["leaf2"]
device: ["leaf2"]
local_identifier: "leaf2__evpn_unicast"
afi: evpn
safi: unicast
@@ -598,6 +606,7 @@ spec:
# Leaf3 address families
# ============================================================
- bgp_config: ["leaf3"]
device: ["leaf3"]
local_identifier: "leaf3__ipv4_unicast"
afi: ipv4
safi: unicast
@@ -608,6 +617,7 @@ spec:
- ["10.0.250.13/32"]
- ["10.0.255.12/32"]
- bgp_config: ["leaf3"]
device: ["leaf3"]
local_identifier: "leaf3__evpn_unicast"
afi: evpn
safi: unicast
@@ -617,6 +627,7 @@ spec:
# Leaf4 address families
# ============================================================
- bgp_config: ["leaf4"]
device: ["leaf4"]
local_identifier: "leaf4__ipv4_unicast"
afi: ipv4
safi: unicast
@@ -627,6 +638,7 @@ spec:
- ["10.0.250.14/32"]
- ["10.0.255.12/32"]
- bgp_config: ["leaf4"]
device: ["leaf4"]
local_identifier: "leaf4__evpn_unicast"
afi: evpn
safi: unicast
@@ -636,6 +648,7 @@ spec:
# Leaf5 address families
# ============================================================
- bgp_config: ["leaf5"]
device: ["leaf5"]
local_identifier: "leaf5__ipv4_unicast"
afi: ipv4
safi: unicast
@@ -646,6 +659,7 @@ spec:
- ["10.0.250.15/32"]
- ["10.0.255.13/32"]
- bgp_config: ["leaf5"]
device: ["leaf5"]
local_identifier: "leaf5__evpn_unicast"
afi: evpn
safi: unicast
@@ -655,6 +669,7 @@ spec:
# Leaf6 address families
# ============================================================
- bgp_config: ["leaf6"]
device: ["leaf6"]
local_identifier: "leaf6__ipv4_unicast"
afi: ipv4
safi: unicast
@@ -665,6 +680,7 @@ spec:
- ["10.0.250.16/32"]
- ["10.0.255.13/32"]
- bgp_config: ["leaf6"]
device: ["leaf6"]
local_identifier: "leaf6__evpn_unicast"
afi: evpn
safi: unicast
@@ -674,6 +690,7 @@ spec:
# Leaf7 address families
# ============================================================
- bgp_config: ["leaf7"]
device: ["leaf7"]
local_identifier: "leaf7__ipv4_unicast"
afi: ipv4
safi: unicast
@@ -684,6 +701,7 @@ spec:
- ["10.0.250.17/32"]
- ["10.0.255.14/32"]
- bgp_config: ["leaf7"]
device: ["leaf7"]
local_identifier: "leaf7__evpn_unicast"
afi: evpn
safi: unicast
@@ -691,6 +709,7 @@ spec:
- ["leaf7__evpn"]
# Leaf7 IPv4 unicast in VRF gold (border peering)
- bgp_config: ["leaf7"]
device: ["leaf7"]
local_identifier: "leaf7__vrf_gold__ipv4_unicast"
afi: ipv4
safi: unicast
@@ -701,6 +720,7 @@ spec:
# Leaf8 address families
# ============================================================
- bgp_config: ["leaf8"]
device: ["leaf8"]
local_identifier: "leaf8__ipv4_unicast"
afi: ipv4
safi: unicast
@@ -711,6 +731,7 @@ spec:
- ["10.0.250.18/32"]
- ["10.0.255.14/32"]
- bgp_config: ["leaf8"]
device: ["leaf8"]
local_identifier: "leaf8__evpn_unicast"
afi: evpn
safi: unicast
@@ -718,6 +739,7 @@ spec:
- ["leaf8__evpn"]
# Leaf8 IPv4 unicast in VRF gold (border peering)
- bgp_config: ["leaf8"]
device: ["leaf8"]
local_identifier: "leaf8__vrf_gold__ipv4_unicast"
afi: ipv4
safi: unicast