Skip to content

Commit aac99ac

Browse files
dgarrosclaude
andcommitted
fix lint and mypy errors after rebase onto main
- align regenerated protocols.py IpamIPPrefix annotations with SDK v1.22.0 - add targeted type ignores for stricter SDK relationship/pool typing - apply rumdl markdown formatting to overlay docs flagged by new CI lint Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent e24a4a6 commit aac99ac

11 files changed

Lines changed: 47 additions & 15 deletions

File tree

CONTEXT.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ they are used consistently in code, schema, and conversation.
1010
### Design model
1111

1212
**Design object**:
13-
An object that records *intent* — what infrastructure should look like — and persists in Infrahub
13+
An object that records _intent_ — what infrastructure should look like — and persists in Infrahub
1414
independent of whether Generators have run (e.g. a Fabric, Pod, Rack, or Tenant).
1515
_Avoid_: input, spec, template (a "device template" is a distinct supporting object).
1616

1717
**Implementation object**:
18-
An object a Generator *produces* from design intent — devices, interfaces, links, IP allocations, VNI/RT
18+
An object a Generator _produces_ from design intent — devices, interfaces, links, IP allocations, VNI/RT
1919
allocations, materialized relationships.
2020
_Avoid_: output, artifact (an "artifact" is specifically a rendered file like a startup config or cabling CSV).
2121

generators/generate_fabric.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ async def configure_overlay(self) -> None:
8787

8888
if fabric.overlay_asn.value is None:
8989
asn_pool = await self.client.get(kind=CoreNumberPool, name__value=ASN_POOL_NAME)
90-
fabric.overlay_asn.value = asn_pool # number pool -> server-side from_pool allocation
90+
fabric.overlay_asn.value = asn_pool # type: ignore[assignment] # number pool -> server-side from_pool allocation
9191
if not fabric.routing_design.value:
9292
fabric.routing_design.value = DEFAULT_ROUTING_DESIGN
9393
await fabric.save(allow_upsert=True)

generators/generate_tenant.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,17 +125,17 @@ async def configure_vrf(self, vrf_node: TenantGeneratorQueryVrfNode, overlay_asn
125125
vrf = await self.client.get(kind=NetworkVrf, id=vrf_node.id)
126126
changed = False
127127
if vrf.l3vni.value is None:
128-
vrf.l3vni.value = await self.client.get(kind=CoreNumberPool, name__value=L3VNI_POOL)
128+
vrf.l3vni.value = await self.client.get(kind=CoreNumberPool, name__value=L3VNI_POOL) # type: ignore[assignment]
129129
changed = True
130130
if vrf.l3_vlan_id.value is None:
131-
vrf.l3_vlan_id.value = await self.client.get(kind=CoreNumberPool, name__value=VLAN_L3_POOL)
131+
vrf.l3_vlan_id.value = await self.client.get(kind=CoreNumberPool, name__value=VLAN_L3_POOL) # type: ignore[assignment]
132132
changed = True
133133
if changed:
134134
await vrf.save(allow_upsert=True, update_group_context=False)
135135
# FIX: pool-allocated values are not readable on the returned node; re-fetch to read them
136136
vrf = await self.client.get(kind=NetworkVrf, id=vrf_node.id)
137137

138-
rt = route_target(overlay_asn, vrf.l3vni.value)
138+
rt = route_target(overlay_asn, vrf.l3vni.value) # type: ignore[arg-type]
139139
if vrf.route_target.value != rt:
140140
vrf.route_target.value = rt
141141
await vrf.save(allow_upsert=True, update_group_context=False)
@@ -145,17 +145,17 @@ async def configure_segment(self, segment_node: TenantGeneratorQuerySegmentNode,
145145
segment = await self.client.get(kind=NetworkSegment, id=segment_node.id)
146146
changed = False
147147
if segment.vlan_id.value is None:
148-
segment.vlan_id.value = await self.client.get(kind=CoreNumberPool, name__value=VLAN_L2_POOL)
148+
segment.vlan_id.value = await self.client.get(kind=CoreNumberPool, name__value=VLAN_L2_POOL) # type: ignore[assignment]
149149
changed = True
150150
if segment.l2vni.value is None:
151-
segment.l2vni.value = await self.client.get(kind=CoreNumberPool, name__value=L2VNI_POOL)
151+
segment.l2vni.value = await self.client.get(kind=CoreNumberPool, name__value=L2VNI_POOL) # type: ignore[assignment]
152152
changed = True
153153
if changed:
154154
await segment.save(allow_upsert=True, update_group_context=False)
155155
# FIX: pool-allocated values are not readable on the returned node; re-fetch to read them
156156
segment = await self.client.get(kind=NetworkSegment, id=segment_node.id)
157157

158-
rt = route_target(overlay_asn, segment.l2vni.value)
158+
rt = route_target(overlay_asn, segment.l2vni.value) # type: ignore[arg-type]
159159
if segment.route_target.value != rt:
160160
segment.route_target.value = rt
161161
await segment.save(allow_upsert=True, update_group_context=False)
@@ -175,13 +175,13 @@ async def allocate_subnet_and_gateway(self, segment: NetworkSegment) -> None:
175175
data={"role": "tenant_subnet"},
176176
)
177177

178-
network = subnet.prefix.value
178+
network = subnet.prefix.value # type: ignore[union-attr]
179179
gateway_ip = list(network.hosts())[GATEWAY_HOST_INDEX - 1]
180180
gateway = await self.client.create(kind="IpamIPAddress", address=f"{gateway_ip}/{network.prefixlen}")
181181
await gateway.save(allow_upsert=True, update_group_context=False)
182182

183-
segment.subnet = subnet
184-
segment.gateway = gateway
183+
segment.subnet = subnet # type: ignore[assignment]
184+
segment.gateway = gateway # type: ignore[assignment]
185185
await segment.save(allow_upsert=True, update_group_context=False)
186186
self.logger.info(f"Allocated subnet {network} (gw {gateway_ip}) for segment {segment.name.value}")
187187

specs/001-evpn-overlay/contracts/config-artifact.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ router bgp <device.asn>
7272
```
7373

7474
## Invariants
75+
7576
- A device with no materialized `segments` renders **no** NVE/VLAN/VRF/SVI (guarantees spine/super-spine
7677
carry no tenant state — FR-007, SC-006).
7778
- An L2-only segment (no `gateway`) renders its `vlan`/`vn-segment`/NVE `member vni` but **no** anycast SVI

specs/001-evpn-overlay/contracts/graphql-queries.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ NetworkDevice(hostname__value: $name)
6060
```
6161

6262
**Notes**:
63+
6364
- iBGP peers = the directly-connected neighbors surfaced via `interfaces → link → endpoints → … on
6465
NetworkInterface → device` (filter out self). RR-client toward lower tiers is derived in the template.
6566
- Spine/super-spine renders have no `segments` (none materialized) → no NVE/VRF/SVI.

specs/001-evpn-overlay/contracts/infrahub-registration.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ generator/query/trigger entries already in `.infrahub.yml` and `objects/20_trigg
66
## `.infrahub.yml` additions
77

88
**queries** — add:
9+
910
- `generate_tenant``generators/generate_tenant.gql`
1011

1112
(The existing `network_device_startup_config` query entry is unchanged; its `.gql` file content is expanded
1213
per the GraphQL contract.)
1314

1415
**generator_definitions** — add:
16+
1517
```text
1618
- name: generate-tenant
1719
file_path: generators/generate_tenant.py # class OverlayGenerator
@@ -26,16 +28,20 @@ per the GraphQL contract.)
2628
config. No new artifact is introduced.
2729

2830
## Groups (`objects/01_groups.yml`)
31+
2932
- Add a `tenants` `CoreStandardGroup` (parallels the existing halls/racks/fabrics/pods/devices groups) so the
3033
generator_definition has a target group; new `NetworkTenant` objects join it.
3134

3235
## Trigger (`objects/20_triggers.yml(.save)`)
36+
3337
Add, mirroring the existing pod/rack trigger pattern:
38+
3439
- `CoreGeneratorAction`: `run-tenant-generator``generate-tenant`.
3540
- `CoreNodeTriggerRule`: fires on `NetworkTenant` mutations (checksum + relevant attributes/relationships:
3641
vrfs, segments, placement) → `run-tenant-generator`.
3742

3843
## Idempotency / loop-prevention contract
44+
3945
- OverlayGenerator writes `Device.segments` on leafs. The Rack/Pod generators' `GeneratorMixin` checksums
4046
**must exclude** overlay relationships so this write does not re-trigger the physical cascade (D12 caveat).
4147
- `overlay_asn` allocation in the FabricGenerator must be guarded ("allocate only if unset") and excluded

specs/001-evpn-overlay/data-model.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ schema changes.
88
## New nodes (`schemas/overlay.yml`)
99

1010
### NetworkTenant
11+
1112
Owner of overlay services, scoped to one fabric. `inherit_from: [GeneratorTarget]` (checksum drives the
1213
OverlayGenerator; the checksum must cover its VRFs/Segments/placement).
1314

@@ -22,6 +23,7 @@ OverlayGenerator; the checksum must cover its VRFs/Segments/placement).
2223
| `ip_namespace` | CoreIPNamespace | one | Attribute | optional; **reserved** for future overlapping-tenant space (D10) |
2324

2425
### NetworkVrf
26+
2527
A tenant's L3 routing instance (IP-VRF / L3VNI). Parent = Tenant.
2628

2729
| Attribute | Kind | Notes |
@@ -40,6 +42,7 @@ A tenant's L3 routing instance (IP-VRF / L3VNI). Parent = Tenant.
4042
| `ip_namespace` | CoreIPNamespace | one | Attribute | optional; reserved (D10) |
4143

4244
### NetworkSegment
45+
4346
A tenant L2 service (MAC-VRF / L2VNI). One VLAN ↔ one L2VNI (D7). Parent = VRF. Optional gateway ⇒ IRB vs
4447
L2-only (D5).
4548

@@ -60,18 +63,21 @@ L2-only (D5).
6063
## Edits to existing nodes
6164

6265
### NetworkFabric (`schemas/logical_design.yml`)
66+
6367
| New attribute | Kind | Notes |
6468
|---------------|------|-------|
6569
| `overlay_asn` | Number | optional; **allocated by FabricGenerator from the global ASN pool** (D8) |
6670
| `routing_design` | Dropdown | choices `ibgp_evpn_ospf_underlay` (default), `ebgp_evpn` (reserved — D13/extensibility) |
6771
| `anycast_gateway_mac` | Text | optional override; template constant default (D10) |
6872

6973
### NetworkPod (`schemas/logical_design.yml`)
74+
7075
| New relationship | Peer | Card. | Kind | Notes |
7176
|------------------|------|-------|------|-------|
7277
| `vtep_pool` | CoreIPAddressPool | one | Attribute | optional; mirrors `loopback_pool`; created by PodGenerator (D4) |
7378

7479
### NetworkDevice (`schemas/device.yml`)
80+
7581
| New attribute | Kind | Notes |
7682
|---------------|------|-------|
7783
| `asn` | Number | optional; stamped `= fabric.overlay_asn` (iBGP); per-device unique under eBGP later (D8) |
@@ -85,10 +91,12 @@ L2-only (D5).
8591
the template (D13).
8692

8793
### NetworkInterface (`schemas/device.yml`)
94+
8895
- Add roles to the `role` dropdown: `vtep` (leaf loopback1 / NVE source) and `svi` (anycast gateway + L3
8996
transit SVIs).
9097

9198
### IpamIPPrefix (`schemas/ipam.yml`)
99+
92100
- Add `role` choices: `pod_vtep_loopback` (per-pod VTEP loopback pool), `overlay_supernet` (tenant address
93101
space root), `tenant_subnet` (per-segment anycast-gateway subnet).
94102

specs/001-evpn-overlay/quickstart.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ inv start # bring up the stack; generators run via triggers
3636
```
3737

3838
**Expected after generators settle**:
39+
3940
- Every device has `asn == its fabric.overlay_asn` (allocated once from the global ASN pool).
4041
- Each **leaf** has a `vtep_ip` and a `vtep`-role loopback interface; **spines/super-spines** do not.
4142
- The seed tenant (e.g. "Blue") has a VRF with an allocated `l3vni` + `l3_vlan_id` + `route_target`, and its
@@ -50,6 +51,7 @@ allocated ids + `route_target`; query a leaf `NetworkDevice` and confirm `segmen
5051
## 4. Inspect the rendered config artifact (the core proof — User Story 1)
5152

5253
Open the `startup_configuration` artifact for a **leaf** and confirm (per `contracts/config-artifact.md`):
54+
5355
- OSPF underlay intact, now advertising the vtep loopback.
5456
- `router bgp <asn>` with iBGP L2VPN-EVPN neighbors to its cabled spines.
5557
- `interface nve1` (source = loopback1), per-segment `member vni <l2vni>`, per-VRF `member vni <l3vni>

specs/001-evpn-overlay/research.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,29 @@ remain.**
88
## Architecture & control plane
99

1010
### D1 — Overlay scope is per-fabric
11+
1112
- **Decision**: A Tenant and its VRFs/Segments belong to exactly one Fabric; each Fabric is an independent
1213
EVPN domain with its own ASN and VNI/RT space.
1314
- **Rationale**: Two independent Clos fabrics; fabric-local numbering is simplest and collision-free.
1415
- **Alternatives**: Cross-fabric tenants (rejected — requires DCI: inter-fabric gateways, RT stitching).
1516

1617
### D2 — Standalone OverlayGenerator beside the physical cascade
18+
1719
- **Decision**: Tenant is a first-class design object with a dedicated OverlayGenerator (triggered by
1820
`NetworkTenant` checksum); physical generators get only small device-attribute extensions.
1921
- **Rationale**: Tenancy is orthogonal to the physical hierarchy and has its own lifecycle; preserves
2022
"one generator owns one concern" and a scoped day-two workflow (symmetric with add-rack).
2123
- **Alternatives**: Fold overlay into the Rack generator (rejected — conflates lifecycles, forces rebuilds).
2224

2325
### D3 — Hierarchical route reflection, leaf-only VTEPs
26+
2427
- **Decision**: leafs = RR clients of their spines; spines = RR for leafs **and** clients of super-spines;
2528
super-spines = top RR. Only leafs are VTEPs.
2629
- **Rationale**: Canonical 5-stage design; sessions follow topology; low per-leaf session count.
2730
- **Alternatives**: Super-spines-only RR (rejected — more sessions/leaf, spine tier carries no EVPN).
2831

2932
### D8 — ASN from a global NumberPool, stamped to devices
33+
3034
- **Decision**: A global `CoreNumberPool``NetworkFabric.overlay_asn`; FabricGenerator stamps
3135
`device.asn = overlay_asn`. Template renders `router bgp {{ device.asn }}`.
3236
- **Rationale**: ASN-as-allocated-resource fits the Resource Manager showcase; control-plane-agnostic value
@@ -37,24 +41,28 @@ remain.**
3741
## Services & data model
3842

3943
### D5 — Anycast gateway optional on Segment
44+
4045
- **Decision**: Segment is always an L2VNI bridge; subnet + anycast gateway are optional (present ⇒ IRB,
4146
absent ⇒ L2-only). Segment always nests under a VRF.
4247
- **Rationale**: Near-free flexibility; reflects real tenants with mixed routed/bridge-only segments.
4348
- **Alternatives**: Always-IRB (rejected — no L2-only support).
4449

4550
### D7 — VLAN-based service model, fabric-global VLANs
51+
4652
- **Decision**: 1 VLAN ↔ 1 L2VNI ↔ 1 Segment; NX-OS `feature vn-segment-vlan-based`; VLAN ID is a
4753
fabric-consistent handle, L2VNI is the global id.
4854
- **Alternatives**: VLAN-aware bundle / multi-VLAN EVI (rejected — unnecessary complexity for the demo).
4955

5056
### D13 — Schema shape & simplifications
57+
5158
- **Decision**: Tenant/VRF/Segment are plain `Network`-namespace nodes with `kind: Parent` relationships
5259
(not `NetworkBuildingBlock`). **No `route_reflector` boolean** (derived from tier ordering in the template).
5360
Materialize **only `Device↔Segment`** (VRF presence derived via `segment.vrf`).
5461
- **Rationale**: Different kinds → plain parent rels; tier ordering is the cleaner RR source and also right
5562
for eBGP-future; one materialized relationship is enough.
5663

5764
### D14 — L3VNI transit VLAN + two VLAN pools
65+
5866
- **Decision**: Add `NetworkVrf.l3_vlan_id` for the L3VNI transit/core SVI. Two VLAN pools over disjoint
5967
ranges: L2 `100–3899``Segment.vlan_id`, L3 `3900–4094``Vrf.l3_vlan_id`.
6068
- **Rationale**: Symmetric IRB needs a transit VLAN per VRF on each carrying leaf; a NumberPool binds to one
@@ -64,31 +72,36 @@ remain.**
6472
## Allocation, addressing, placement, triggering
6573

6674
### D6 — VLAN/L2VNI/L3VNI from per-fabric NumberPools
75+
6776
- **Decision**: NumberPools (Resource Manager), not deterministic compute. Ranges: VLAN-L2 100–3899,
6877
VLAN-L3 3900–4094, L2VNI 10000–19999, L3VNI 50000–59999.
6978
- **Rationale**: On-brand with the solution's RM teaching point; queryable, collision-free.
7079
- **Alternatives**: `VNI = base + VLAN` (noted as the simpler documented alternative).
7180

7281
### D9 — RT stored (generator-set, queryable); RD template-rendered
82+
7383
- **Decision**: OverlayGenerator stores `route_target = "<asn>:<vni>"` on VRF/Segment; RD is per-device →
7484
rendered in template `<loopback0>:<id>`. Optional `import_rt`/`export_rt` overrides reserved.
7585
- **Rationale**: Queryable source of truth without computed-attr relationship-traversal risk; RD has no
7686
single home object.
7787
- **Alternatives**: Computed-attribute RT (traversal uncertain); NX-OS `auto` (hides RD/RT from Infrahub).
7888

7989
### D10 — Separate overlay supernet; default IP namespace first
90+
8091
- **Decision**: New IPPrefix roles `overlay_supernet` + `tenant_subnet`; segment subnets from an overlay
8192
supernet distinct from underlay `10.0.0.0/8`. Anycast GW = `.1`, distributed; fabric-wide
8293
`anycast-gateway-mac` (template constant + optional Fabric override). Default namespace initially;
8394
optional `ip_namespace` on VRF/Tenant reserved for overlapping tenant space.
8495
- **Alternatives**: Per-VRF namespaces now (deferred — heavier generator bookkeeping for a phase-2 toggle).
8596

8697
### D4 — Dedicated VTEP loopback1 on leafs
98+
8799
- **Decision**: Leaf loopback1 (role `vtep`) as NVE source; advertised in OSPF; allocated from a per-pod
88100
VTEP pool (role `pod_vtep_loopback`). loopback0 stays router-id/iBGP source. Spines/super-spines: none.
89101
- **Rationale**: Standard best practice; keeps anycast-VTEP/MLAG open; role-based IPAM consistency.
90102

91103
### D11/D12 — Placement (advertise-all default) via materialized relationship (Design Y)
104+
92105
- **Decision**: Optional `Segment↔Rack` placement intent (empty ⇒ every leaf in fabric). OverlayGenerator
93106
materializes `Device↔Segment` onto carrying leafs; leaf-device change rides the existing
94107
device→artifact regeneration path.

src/infrahub_solution_ai_dc/cabling.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def build_rack_cabling_plan(
5050
dst_device_count = len(dst_devices)
5151

5252
for src_device, src_interfaces in src_interface_map.items():
53-
src_device_index: int = src_device.index.value # type: ignore[attr-defined]
53+
src_device_index: int = src_device.index.value # type: ignore[assignment]
5454

5555
for dst_index, src_interface in enumerate(src_interfaces[:dst_device_count]):
5656
start = (rack_index * 2) - 2

0 commit comments

Comments
 (0)