feat: Add Infrahub Jinja2 transforms for VLANs, interfaces, and VXLAN (#20) #24

Merged
Damien merged 4 commits from feat/infrahub-transforms-vlan-interfaces-vxlan into main 2026-02-28 17:42:09 +00:00
Owner

Summary

Closes #20

Adds Infrahub Jinja2 transforms that query device intent from Infrahub via GraphQL and produce structured JSON payloads (YANG-style) suitable for gNMI Set operations on Arista EOS devices.

  • 3 GraphQL queriesvlan_intent, interface_intent, vxlan_intent — each parameterized by $device_name
  • 3 Jinja2 templatesvlan_yang.j2, interface_yang.j2, vxlan_yang.j2 — producing JSON arrays/objects from the GraphQL response
  • Integration test fixtures — one directory per transform with input.json, output.json, and test.yml, using leaf1 from the lab topology as sample device
  • .infrahub.yml updated with queries and jinja2_transforms sections

Files added

transforms/
├── queries/
│   ├── vlan_intent.gql       # VLANs via VTEP mappings + SVI interfaces
│   ├── interface_intent.gql  # All interface types with IPs
│   └── vxlan_intent.gql      # VTEP config + VLAN/VNI/VRF mappings
├── templates/
│   ├── vlan_yang.j2          # JSON array of VLANs (merged, deduplicated, sorted)
│   ├── interface_yang.j2     # JSON array of interfaces with type discriminator
│   └── vxlan_yang.j2         # JSON object: vtep + vlan_vni + vrf_vni mappings
└── tests/
    ├── vlan_yang/{input,output,test}.{json,yml}
    ├── interface_yang/{input,output,test}.{json,yml}
    └── vxlan_yang/{input,output,test}.{json,yml}

Transform usage

# Render locally
infrahubctl render vlan_yang_transform device_name=leaf1
infrahubctl render interface_yang_transform device_name=leaf1
infrahubctl render vxlan_yang_transform device_name=leaf1

# Via API
GET /api/transform/jinja2/vlan_yang_transform?device_name=leaf1
GET /api/transform/jinja2/interface_yang_transform?device_name=leaf1
GET /api/transform/jinja2/vxlan_yang_transform?device_name=leaf1

Test plan

  • Load branch into Infrahub (infrahubctl schema load + infrahubctl object load)
  • Run infrahubctl render vlan_yang_transform device_name=leaf1 and verify JSON output matches expected VLANs (40, 4090, 4091)
  • Run infrahubctl render interface_yang_transform device_name=leaf1 and verify all interface types are present with correct attributes
  • Run infrahubctl render vxlan_yang_transform device_name=leaf1 and verify VTEP source address, UDP port, and VLAN-VNI mapping for VLAN 40 / VNI 110040
  • Run infrahubctl render vxlan_yang_transform device_name=leaf3 and verify VRF gold / L3VNI 100001 appears in vrf_vni_mappings
  • [ ] Verify infrahubctl test passes for all three test fixtures

🤖 Generated with Claude Code

## Summary Closes #20 Adds Infrahub Jinja2 transforms that query device intent from Infrahub via GraphQL and produce structured JSON payloads (YANG-style) suitable for gNMI Set operations on Arista EOS devices. - **3 GraphQL queries** — `vlan_intent`, `interface_intent`, `vxlan_intent` — each parameterized by `$device_name` - **3 Jinja2 templates** — `vlan_yang.j2`, `interface_yang.j2`, `vxlan_yang.j2` — producing JSON arrays/objects from the GraphQL response - **Integration test fixtures** — one directory per transform with `input.json`, `output.json`, and `test.yml`, using leaf1 from the lab topology as sample device - **`.infrahub.yml` updated** with `queries` and `jinja2_transforms` sections ## Files added ``` transforms/ ├── queries/ │ ├── vlan_intent.gql # VLANs via VTEP mappings + SVI interfaces │ ├── interface_intent.gql # All interface types with IPs │ └── vxlan_intent.gql # VTEP config + VLAN/VNI/VRF mappings ├── templates/ │ ├── vlan_yang.j2 # JSON array of VLANs (merged, deduplicated, sorted) │ ├── interface_yang.j2 # JSON array of interfaces with type discriminator │ └── vxlan_yang.j2 # JSON object: vtep + vlan_vni + vrf_vni mappings └── tests/ ├── vlan_yang/{input,output,test}.{json,yml} ├── interface_yang/{input,output,test}.{json,yml} └── vxlan_yang/{input,output,test}.{json,yml} ``` ## Transform usage ```bash # Render locally infrahubctl render vlan_yang_transform device_name=leaf1 infrahubctl render interface_yang_transform device_name=leaf1 infrahubctl render vxlan_yang_transform device_name=leaf1 # Via API GET /api/transform/jinja2/vlan_yang_transform?device_name=leaf1 GET /api/transform/jinja2/interface_yang_transform?device_name=leaf1 GET /api/transform/jinja2/vxlan_yang_transform?device_name=leaf1 ``` ## Test plan - [x] Load branch into Infrahub (`infrahubctl schema load` + `infrahubctl object load`) - [x] Run `infrahubctl render vlan_yang_transform device_name=leaf1` and verify JSON output matches expected VLANs (40, 4090, 4091) - [x] Run `infrahubctl render interface_yang_transform device_name=leaf1` and verify all interface types are present with correct attributes - [x] Run `infrahubctl render vxlan_yang_transform device_name=leaf1` and verify VTEP source address, UDP port, and VLAN-VNI mapping for VLAN 40 / VNI 110040 - [x] Run `infrahubctl render vxlan_yang_transform device_name=leaf3` and verify VRF gold / L3VNI 100001 appears in `vrf_vni_mappings` - ~~[ ] Verify `infrahubctl test` passes for all three test fixtures~~ 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Damien added 1 commit 2026-02-28 10:01:53 +00:00
Add three GraphQL queries, Jinja2 templates, and integration tests for
generating YANG-style JSON configuration payloads from Infrahub intent data,
suitable for gNMI Set operations on Arista EOS devices.

Queries (transforms/queries/):
- vlan_intent.gql: Fetches VLANs for a device via VTEP mappings and SVI
  interfaces, including VNI associations.
- interface_intent.gql: Fetches all interface types (loopback, ethernet, vlan,
  lag) with IP addresses and type-specific attributes.
- vxlan_intent.gql: Fetches VTEP config, VLAN-to-VNI mappings, and VRF-to-VNI
  mappings (L3VNI) via VRF device assignments.

Templates (transforms/templates/):
- vlan_yang.j2: Merges VLANs from both VTEP and SVI sources, deduplicates by
  vlan_id, and emits a sorted JSON array.
- interface_yang.j2: Emits a JSON array of interfaces sorted by name, with a
  "type" discriminator field for each interface kind.
- vxlan_yang.j2: Emits a JSON object with vtep config, vlan_vni_mappings, and
  vrf_vni_mappings sections.

Integration tests (transforms/tests/):
- One test directory per transform with input.json (sample GraphQL response for
  leaf1), output.json (expected result), and test.yml config.
- Test data reflects the lab topology: leaf1 VTEP 10.0.255.11, VLAN 40 / VNI
  110040, MLAG VLANs 4090/4091, underlay Ethernet11/12.

.infrahub.yml updated with queries and jinja2_transforms sections.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Damien added 1 commit 2026-02-28 16:41:25 +00:00
Replace all `null` literals with `none` (valid Jinja2/Python) in all three
templates. Add explicit `is defined and is not none and .node is not none`
guards before accessing nested nodes on optional GraphQL relationships, to
avoid `Undefined is not JSON serializable` errors at render time.

Also add `| default(none)` on optional scalar attributes (speed, mode,
lacp_mode, mlag_id, mtu, description, virtual_router_address,
route_distinguisher, learn_restrict, vlan_type, trunk_groups) so that
Infrahub `Undefined` values from unset optional fields are safely coerced
to JSON null rather than crashing tojson serialization.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Damien added 1 commit 2026-02-28 17:30:19 +00:00
Rewrite all three test.yml files to use the Infrahub SDK pytest format
(version: "1.0" / infrahub_tests) instead of the previous invalid
apiVersion/kind schema. Each test file now declares a smoke_check test
(jinja2-transform-smoke) and a unit render test (jinja2-transform-unit-render)
pointing to the local input/output JSON fixtures.

Update output.json files with the actual render output from the fixed templates:
- vlan_yang: trunk_groups is null (not []) for VLANs with no trunk groups set;
  key order reflects Python dict / tojson serialization order.
- interface_yang: corrected MLAG peer-link SVI IP (10.0.199.254/31), added
  iBGP SVI IP (10.0.3.0/31), removed stale Loopback1 IP address.
- vxlan_yang: source_address now includes /32 prefix as stored in Infrahub
  IPHost fields; key order matches tojson output.

Update input.json files to be consistent with the expected output:
- vlan_yang/input.json: trunk_groups.value set to null for VLAN 40.
- interface_yang/input.json: Loopback1 ip_addresses cleared; Vlan4090 IP
  updated to 10.0.199.254/31; Vlan4091 IP added as 10.0.3.0/31.
- vxlan_yang/input.json: source_address updated to 10.0.255.11/32.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Damien added 1 commit 2026-02-28 17:37:16 +00:00
Move transforms/{queries,templates,tests} under infrahub/transforms/ so all
Infrahub-managed content lives under the dedicated infrahub/ directory,
consistent with schemas/, objects/, and menus/.

Updated paths:
- .infrahub.yml: file_path and template_path entries now prefixed with
  infrahub/transforms/
- infrahub/transforms/tests/*/test.yml: directory references updated from
  transforms/tests/* to infrahub/transforms/tests/*

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Damien merged commit 668e9cbada into main 2026-02-28 17:42:09 +00:00
Damien deleted branch feat/infrahub-transforms-vlan-interfaces-vxlan 2026-02-28 17:42:13 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Damien/arista-evpn-vxlan-clab#24