Skip to content

Commit 358debe

Browse files
committed
pytest
1 parent 7b7141e commit 358debe

File tree

2 files changed

+52
-24
lines changed

2 files changed

+52
-24
lines changed

templates/clab_topology.j2

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,38 @@
1+
{#- ============================================================================
2+
Containerlab Topology Generator Template
3+
4+
Generates a Containerlab topology YAML file from Infrahub TopologyDeployment data.
5+
Supports Arista cEOS devices with management network configuration and
6+
automated link generation between devices.
7+
============================================================================ -#}
8+
{#- Define default container images for each device kind -#}
19
{% set default_images = {
210
"arista_ceos": "registry.opsmill.io/external/ceos-image:4.29.0.2F"
311
} -%}
12+
13+
{# Iterate through all topology deployments #}
414
{% for topology in data.TopologyDeployment.edges -%}
515

616

717
---
18+
{# ============================================================================
19+
Basic Topology Configuration
20+
============================================================================ #}
821
name: {{ topology.node.name.value }}
922
prefix: ""
1023

24+
{# Management network configuration #}
1125
mgmt:
1226
network: {{ topology.node.name.value | lower() }}
1327
ipv4-subnet: {{ topology.node.management_subnet.node.prefix.value }}
1428

1529
{% if topology.node.devices.edges is defined -%}
1630

1731
topology:
32+
{# ============================================================================
33+
Device Kind Definitions
34+
Configure base images and startup commands for each device type
35+
============================================================================ #}
1836
kinds:
1937
arista_ceos:
2038
image: "{{ default_images.arista_ceos }}"
@@ -23,52 +41,73 @@ topology:
2341
- FastCli -p 15 -c 'security pki key generate rsa 4096 eAPI.key'
2442
- FastCli -p 15 -c 'security pki certificate generate self-signed eAPI.crt key eAPI.key generate rsa 4096 validity 30000 parameters common-name eAPI'
2543

44+
{# ============================================================================
45+
Node Definitions
46+
Define individual device nodes with their configuration
47+
============================================================================ #}
2648
nodes:
27-
{%- for device in topology.node.devices.edges %}
49+
{% for device in topology.node.devices.edges %}
50+
{#- Only include devices that are Arista cEOS platform #}
2851
{%- if device.node.platform.node.containerlab_os.value == "arista_ceos" %}
2952
{{ device.node.name.value }}:
3053
kind: {{ device.node.platform.node.containerlab_os.value }}
3154
type: {{ device.node.device_type.node.name.value }}
55+
{#- Add custom OS version if specified #}
3256
{%- if device.node.os_version.value %}
3357
image: {{ device.node.os_version.value }}
3458
{%- endif %}
59+
{#- Extract and set management IP (strip subnet mask) #}
3560
{%- if device.node.primary_address.node.address is defined %}
3661
{%- set mgmt_ip = device.node.primary_address.node.address.value.split('/') %}
3762
mgmt-ipv4: {{ mgmt_ip[0] }}
3863
{%- endif %}
3964
startup-config: ../devices/{{ device.node.name.value }}.cfg
4065
{%- endif %}
41-
{%- endfor %}
66+
{% endfor %}
4267

4368

69+
{# ============================================================================
70+
Link Definitions
71+
Generate physical connections between devices from interface connectors.
72+
Deduplicates bidirectional links and normalizes interface naming.
73+
============================================================================ #}
4474
links:
45-
{%- set processed_endpoints = [] %}
75+
{% set processed_endpoints = [] %}
76+
{#- Build a mapping of device names to their platform kinds for interface name normalization -#}
4677
{%- set device_kinds = {} %}
4778
{%- for device in topology.node.devices.edges %}
4879
{%- set _ = device_kinds.update({device.node.name.value: device.node.platform.node.containerlab_os.value}) %}
4980
{%- endfor %}
81+
{#- Iterate through all devices and their interfaces to find connections -#}
5082
{%- for device in topology.node.devices.edges %}
5183
{%- for interface in device.node.interfaces.edges %}
84+
{#- Only process interfaces with valid connectors (skip console/management) -#}
5285
{%- if interface.node.connector is defined and interface.node.connector.node is not none and interface.node.role.value not in ["console", "management"] %}
5386
{%- set connected_endpoint = interface.node.connector.node %}
87+
{#- Build endpoint identifiers (device:interface) -#}
5488
{%- set endpoint1 = device.node.name.value + ":" + interface.node.name.value %}
5589
{%- set endpoint2 = connected_endpoint.hfid | join(":") %}
90+
{#- Create sorted tuple to ensure consistent ordering for deduplication -#}
5691
{%- set endpoint_tuple = [endpoint1, endpoint2] | sort %}
5792
{%- set endpoint_key = endpoint_tuple | join("|") %}
93+
{#- Only process each unique link once (prevents duplicates for bidirectional connections) -#}
5894
{%- if endpoint_key not in processed_endpoints %}
5995
{%- set _ = processed_endpoints.append(endpoint_key) %}
96+
{#- Normalize interface naming: "Ethernet" -> "eth" for containerlab -#}
6097
{%- set endpoint1 = endpoint1.replace("Ethernet", "eth") %}
6198
{%- set endpoint2 = endpoint2.replace("Ethernet", "eth") %}
99+
{#- Extract device names from endpoints -#}
62100
{%- set device1_name = endpoint1.split(":")[0] %}
63101
{%- set device2_name = endpoint2.split(":")[0] %}
102+
{#- Arista cEOS: Strip subinterface notation (e.g., "eth1/1/1" -> "eth1/1") -#}
64103
{%- if device_kinds.get(device1_name) == "arista_ceos" %}
65104
{%- set endpoint1 = endpoint1.split("/")[0] %}
66105
{%- endif %}
67106
{%- if device_kinds.get(device2_name) == "arista_ceos" %}
68107
{%- set endpoint2 = endpoint2.split("/")[0] %}
69108
{%- endif %}
70109
- endpoints: ["{{ endpoint1 }}", "{{ endpoint2 }}"]
71-
{%- endif %}
110+
{% endif %}
72111
{%- endif %}
73112
{%- endfor %}
74113
{%- endfor %}

tests/integration/test_workflow.py

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import logging
1919
import time
20-
from pathlib import Path
2120

2221
import pytest
2322
from infrahub_sdk import InfrahubClient, InfrahubClientSync
@@ -99,9 +98,6 @@ def test_04_load_security_data(self, client_main: InfrahubClientSync) -> None:
9998
f"Security data load failed: {load_security.stdout}\n{load_security.stderr}"
10099
)
101100

102-
@pytest.mark.skip(
103-
reason="Repository sync fails due to group resolution issue during import"
104-
)
105101
async def test_05_add_repository(
106102
self, async_client_main: InfrahubClient, remote_repos_dir: str
107103
) -> None:
@@ -113,7 +109,7 @@ async def test_05_add_repository(
113109
git_repository = GitRepo(
114110
name="demo_repo",
115111
src_directory=src_directory,
116-
dst_directory=Path(remote_repos_dir),
112+
dst_directory=remote_repos_dir,
117113
)
118114

119115
response = await git_repository.add_to_infrahub(client=client)
@@ -127,7 +123,6 @@ async def test_05_add_repository(
127123
# Wait for repository to sync
128124
synchronized = False
129125
max_attempts, attempts = 60, 0
130-
repository = None
131126

132127
while not synchronized and attempts < max_attempts:
133128
repository = await client.get(
@@ -148,7 +143,6 @@ async def test_05_add_repository(
148143
)
149144
time.sleep(10)
150145

151-
assert repository is not None, "Failed to retrieve repository"
152146
assert synchronized, (
153147
f"Repository failed to sync. Status: {repository.sync_status.value}"
154148
)
@@ -209,9 +203,6 @@ async def test_08_verify_dc3_created(
209203
assert dc3.name.value == "DC-3", f"Expected DC-3, got {dc3.name.value}"
210204
logging.info("DC-3 topology verified: %s", dc3.name.value)
211205

212-
@pytest.mark.skip(
213-
reason="Repository not loaded - generator definitions not available"
214-
)
215206
async def test_09_run_generator(
216207
self, async_client_main: InfrahubClient, default_branch: str
217208
) -> None:
@@ -251,10 +242,10 @@ async def test_09_run_generator(
251242
attempts += 1
252243
time.sleep(10)
253244

254-
if not definition:
255-
pytest.skip(
256-
"Generator definition 'create_dc' not available - skipping generator test"
257-
)
245+
assert definition, (
246+
"Generator definition 'create_dc' not available after waiting. "
247+
"Repository may not have synced properly in test_05."
248+
)
258249

259250
logging.info("Found generator: %s", definition.name.value)
260251

@@ -288,7 +279,6 @@ async def test_09_run_generator(
288279
)
289280
logging.info("Generator completed successfully")
290281

291-
@pytest.mark.skip(reason="Generator not run - no devices to verify")
292282
async def test_10_verify_devices_created(
293283
self, async_client_main: InfrahubClient, default_branch: str
294284
) -> None:
@@ -301,10 +291,10 @@ async def test_10_verify_devices_created(
301291
# Query for devices
302292
devices = await client.all(kind="DcimGenericDevice")
303293

304-
if not devices:
305-
pytest.skip(
306-
"No devices found - generator may not have run (test_09 may have been skipped)"
307-
)
294+
assert devices, (
295+
"No devices found after generator run. "
296+
"Generator in test_09 may not have completed successfully."
297+
)
308298

309299
logging.info("Found %d devices after generator run", len(devices))
310300

@@ -471,7 +461,6 @@ def test_13_merge_proposed_change(
471461
)
472462
logging.info("Proposed change merged successfully")
473463

474-
@pytest.mark.skip(reason="Generator not run - no devices to verify in main")
475464
async def test_14_verify_merge_to_main(
476465
self, async_client_main: InfrahubClient
477466
) -> None:

0 commit comments

Comments
 (0)