feat: Add Infrahub Jinja2 transforms for MLAG configuration (#22) (#26)

## Summary

Closes #22. Implements Infrahub Jinja2 transforms for MLAG configuration, generating Arista-native YANG-compatible JSON payloads from Infrahub intent data.

| File | Description |
|------|-------------|
| `infrahub/transforms/queries/mlag_intent.gql` | GraphQL query — fetches `InfraMlagPeerConfig` by `$device_name`, including MLAG domain attributes, peer/iBGP VLANs, local interface SVI, and peer-link LAG |
| `infrahub/transforms/templates/mlag_yang.j2` | Jinja2 template — renders a JSON object targeting `/arista-mlag-augments:mlag/config`; returns `[]` for devices with no MLAG (spines) |
| `infrahub/transforms/tests/mlag_yang/test.yml` | Test spec: smoke check + unit render tests for `leaf1` and `spine1` |
| `infrahub/transforms/tests/mlag_yang/leaf1/` | Mock input + expected output for leaf1 (full MLAG config, domain `leafs-1-2`) |
| `infrahub/transforms/tests/mlag_yang/spine1/` | Mock input (empty edges) + expected output `[]` for spine1 |
| `.infrahub.yml` | Registers `mlag_intent` query and `mlag_yang_transform` jinja2 transform |

## Output shape (leaf1)

```json
{
  "mlag": {
    "config": {
      "domain-id": "leafs-1-2",
      "peer-link": "Port-Channel999",
      "local-interface": "Vlan4090",
      "peer-address": "10.0.199.255",
      "shutdown": false
    },
    "dual_primary_detection": {
      "enabled": true,
      "delay": 10,
      "action": "errdisable",
      "heartbeat_peer_ip": "172.16.0.50",
      "heartbeat_vrf": "mgmt"
    },
    "virtual_mac": "c001.cafe.babe",
    "peer_vlan_id": 4090,
    "ibgp_vlan_id": 4091,
    "local_interface_ip": "10.0.199.254/31"
  }
}
```

## Validation

```bash
infrahubctl render mlag_yang_transform device_name=leaf1   # → MLAG config JSON
infrahubctl render mlag_yang_transform device_name=spine1  # → []
```

## Design notes

- Follows identical conventions to existing transforms (#20 VLAN/interface/VXLAN, #21 VRF/L3VNI)
- All optional relationship accesses wrapped in `is defined and is not none` guards
- Spine/non-MLAG devices return `[]` (empty array) — consistent with the "no data → empty collection" pattern used in vxlan_yang for devices without VTEP
This commit was merged in pull request #26.
This commit is contained in:
2026-03-01 11:06:55 +00:00
parent a105e44bbd
commit cf7da535ed
8 changed files with 296 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
{
"data": {
"InfraMlagPeerConfig": {
"edges": [
{
"node": {
"local_interface_ip": {
"value": "10.0.199.254/31"
},
"peer_address": {
"value": "10.0.199.255"
},
"heartbeat_peer_ip": {
"value": "172.16.0.50"
},
"device": {
"node": {
"name": {
"value": "leaf1"
}
}
},
"mlag_domain": {
"node": {
"domain_id": {
"value": "leafs-1-2"
},
"virtual_mac": {
"value": "c001.cafe.babe"
},
"heartbeat_vrf": {
"value": "mgmt"
},
"dual_primary_detection": {
"value": true
},
"dual_primary_delay": {
"value": 10
},
"dual_primary_action": {
"value": "errdisable"
},
"devices": {
"edges": [
{
"node": {
"name": {
"value": "leaf1"
}
}
},
{
"node": {
"name": {
"value": "leaf2"
}
}
}
]
},
"peer_vlan": {
"node": {
"vlan_id": {
"value": 4090
}
}
},
"ibgp_vlan": {
"node": {
"vlan_id": {
"value": 4091
}
}
}
}
},
"local_interface": {
"node": {
"name": {
"value": "Vlan4090"
}
}
},
"peer_link": {
"node": {
"name": {
"value": "Port-Channel999"
}
}
}
}
}
]
}
}
}

View File

@@ -0,0 +1,22 @@
{
"mlag": {
"config": {
"domain-id": "leafs-1-2",
"peer-link": "Port-Channel999",
"local-interface": "Vlan4090",
"peer-address": "10.0.199.255",
"shutdown": false
},
"dual_primary_detection": {
"enabled": true,
"delay": 10,
"action": "errdisable",
"heartbeat_peer_ip": "172.16.0.50",
"heartbeat_vrf": "mgmt"
},
"virtual_mac": "c001.cafe.babe",
"peer_vlan_id": 4090,
"ibgp_vlan_id": 4091,
"local_interface_ip": "10.0.199.254/31"
}
}

View File

@@ -0,0 +1,7 @@
{
"data": {
"InfraMlagPeerConfig": {
"edges": []
}
}
}

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,21 @@
---
version: "1.0"
infrahub_tests:
- resource: Jinja2Transform
resource_name: mlag_yang_transform
tests:
- name: smoke_check
spec:
kind: jinja2-transform-smoke
- name: render_leaf1
spec:
kind: jinja2-transform-unit-render
directory: infrahub/transforms/tests/mlag_yang/leaf1
input: input.json
output: output.json
- name: render_spine1
spec:
kind: jinja2-transform-unit-render
directory: infrahub/transforms/tests/mlag_yang/spine1
input: input.json
output: output.json