Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clab_connector/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
# Disable urllib3 warnings (optional)
urllib3.disable_warnings()

SUPPORTED_KINDS = ["nokia_srlinux", "nokia_sros"]
SUPPORTED_KINDS = ["nokia_srlinux", "nokia_sros", "nokia_srsim"]
NODE_DISPLAY_LIMIT = 5


Expand Down
1 change: 1 addition & 0 deletions clab_connector/models/node/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
KIND_MAPPING = {
"nokia_srlinux": NokiaSRLinuxNode,
"nokia_sros": NokiaSROSNode,
"nokia_srsim": NokiaSROSNode,
}


Expand Down
27 changes: 18 additions & 9 deletions clab_connector/models/node/nokia_srl.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ def get_default_node_type(self):
Returns
-------
str
The default node type (e.g., "ixrd3l").
The default node type (e.g., "ixr-d3l").
"""
return "ixrd3l"
return "ixr-d3l"

def get_platform(self):
"""
Expand All @@ -110,13 +110,22 @@ def get_platform(self):
str
The platform name (e.g. '7220 IXR-D3L').
"""
# Handle IXS-A1 with both old (ixsa1) and new (ixs-a1) syntax
if self.node_type and self.node_type.lower() in ["ixsa1", "ixs-a1"]:
return "7215 IXS-A1"

# Handle both old (ixrd2l) and new (ixr-d2l) clab type syntax
t = self.node_type.replace("ixr-", "").replace("ixr", "")
return f"7220 IXR-{t.upper()}"
m = re.match(r"(?i)(^ixr|^sxr|^ixs)-?(.*)$", self.node_type)
if m:
prefix = m.group(1) or ""
suffix = m.group(2) or ""
if prefix.lower().startswith("ixr") and suffix.lower().startswith(
("h", "d")
):
return f"7220 IXR-{suffix.upper()}"
elif prefix.lower().startswith("sxr"):
return f"7730 IXR-{suffix.upper()}"
elif prefix.lower().startswith("ixs"):
return f"7215 IXS-{suffix.upper()}"
else:
return f"7250 IXR-{suffix.upper()}"
else:
return "NoMatchOnClabType"

def is_eda_supported(self):
"""
Expand Down
92 changes: 53 additions & 39 deletions clab_connector/models/node/nokia_sros.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,45 +242,59 @@ def get_toponode(self, topology):
return helpers.render_template("toponode.j2", data)

def get_interface_name_for_kind(self, ifname):
"""Convert a containerlab interface name to the SR OS EDA format."""

eda_name = ifname

m = re.match(r"^(\d+)/(\d+)/(\d+)$", ifname)
if m:
slot, mda_num, port = m.groups()
mda_letter = chr(96 + int(mda_num))
eda_name = f"ethernet-{slot}-{mda_letter}-{port}-1"
else:
m = re.match(r"^(\d+)/(\d+)/c(\d+)/(\d+)$", ifname)
if m:
slot, mda_num, channel, port = m.groups()
if mda_num == "1":
eda_name = f"ethernet-{slot}-{channel}-{port}"
else:
mda_letter = chr(96 + int(mda_num))
eda_name = f"ethernet-{slot}-{mda_letter}-{channel}-{port}"
else:
m = re.match(r"^(\d+)/x(\d+)/(\d+)/(\d+)$", ifname)
if m:
slot, xiom_id, mda_num, port = m.groups()
mda_letter = chr(96 + int(mda_num))
eda_name = f"ethernet-{slot}-{xiom_id}-{mda_letter}-{port}"
else:
m = re.match(r"^eth(\d+)$", ifname)
if m:
eda_name = f"ethernet-1-a-{m.group(1)}-1"
else:
m = re.match(r"^e(\d+)-(\d+)$", ifname)
if m:
slot, port = m.groups()
eda_name = f"ethernet-{slot}-a-{port}-1"
else:
m = re.match(r"^lo(\d+)$", ifname)
if m:
eda_name = f"loopback-{m.group(1)}"

return eda_name
"""Convert a containerlab interface name to the SR OS EDA format.

Supported input formats:
- "1-2-3" -> "ethernet-1-b-3"
- "1-2-c3-4" -> "ethernet-1-b-3-4" (mda>1) | "1-1-c3-4" -> "ethernet-1-3-4" (mda=1)
- "1-x2-1-3" -> "ethernet-1-2-1-3"
- "1-x2-1-c3-1" -> "ethernet-1-2-1-3-1"

Args:
ifname: Interface name in containerlab format

Returns:
Interface name in SR OS EDA format
"""

def mda_to_letter(mda_num):
"""Convert MDA number to letter (1->a, 2->b, etc.)"""
return chr(96 + int(mda_num))

# Define patterns with their transformation logic
patterns = [
# Pattern: "1-2-3" -> "ethernet-1-b-3"
(
r"^e(\d+)-(\d+)-(\d+)$",
lambda m: f"ethernet-{m[0]}-{mda_to_letter(m[1])}-{m[2]}",
),
# Pattern: "1-2-c3-4" -> conditional format
(
r"^e(\d+)-(\d+)-c(\d+)-(\d+)$",
lambda m: f"ethernet-{m[0]}-{m[2]}-{m[3]}"
if m[2] == "1"
else f"ethernet-{m[0]}-{mda_to_letter(m[1])}-{m[2]}-{m[3]}",
),
# Pattern: "1-x2-1-c3-1" -> "ethernet-1-2-1-c3-1"
(
r"^e(\d+)-x(\d+)-(\d+)-c(\d+)-(\d+)$",
lambda m: f"ethernet-{m[0]}-{m[1]}-{mda_to_letter(m[2])}-{m[3]}-{m[4]}",
),
# Pattern: "1-x2-1-3" -> "ethernet-1-2-1-3"
(
r"^e(\d+)-x(\d+)-(\d+)-(\d+)$",
lambda m: f"ethernet-{m[0]}-{m[1]}-{mda_to_letter(m[2])}-{m[3]}",
),
]

# Try each pattern
for pattern, transformer in patterns:
match = re.match(pattern, ifname)
if match:
return transformer(match.groups())

# Return "bollocks" if no pattern matches
return "Bollocks"

def get_topolink_interface_name(self, topology, ifname):
"""
Expand Down
2 changes: 1 addition & 1 deletion clab_connector/services/export/topology_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ def _build_node_definition(self, node_item):
# guess 'nokia_srlinux' if operating_system is 'srl*'
kind = "nokia_srlinux"
if operating_system.lower().startswith("sros"):
kind = "nokia_sros"
kind = "nokia_srsim"

node_def = {
"kind": kind,
Expand Down
10 changes: 6 additions & 4 deletions clab_connector/services/integration/sros_post_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,21 +353,24 @@ def prepare_sros_node(
namespace: str,
version: str,
mgmt_ip: str,
node_type: str,
username: str = "admin",
password: str | None = None,
quiet: bool = False,
quiet: bool = True,
) -> bool:
"""
Perform SROS-specific post-integration steps.
"""
# First check if we can login with admin:admin
# If we can't, assume the node is already bootstrapped
admin_pwd = "admin"
admin_pwd = "admin" if node_type == "nokia_sros" else "NokiaSros1!"
can_login = verify_ssh_credentials(mgmt_ip, username, [admin_pwd], quiet)

if not can_login:
if not can_login and node_type == "nokia_sros":
logger.info("Node: %s already bootstrapped", node_name)
return True
if not can_login:
logger.error("Can't login to node %s of kind %s", node_name, node_type)

# Proceed with original logic if admin:admin works
# 1. determine password list (keep provided one first if present)
Expand All @@ -382,7 +385,6 @@ def prepare_sros_node(
if not working_pw:
logger.error("No valid password found - aborting")
return False

# 2. create temp artefacts
with tempfile.TemporaryDirectory() as tdir:
tdir_path = Path(tdir)
Expand Down
10 changes: 7 additions & 3 deletions clab_connector/services/integration/topology_integrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,13 +386,17 @@ def create_topolinks(self, skip_edge_links: bool = False):

def run_sros_post_integration(self, node, namespace, normalized_version, quiet):
"""Run SROS post-integration"""
password = "NokiaSros1!"
if node.kind == "nokia_sros":
password = "admin"
return prepare_sros_node(
node_name=node.get_node_name(self.topology),
namespace=namespace,
version=normalized_version,
mgmt_ip=node.mgmt_ipv4,
username="admin",
password="admin",
password=password,
node_type=node.kind,
quiet=quiet,
)

Expand All @@ -406,9 +410,9 @@ def run_post_integration(self):

# Look for SROS nodes and run post-integration for them
for node in self.topology.nodes:
if node.kind == "nokia_sros":
if node.kind in {"nokia_sros", "nokia_srsim"}:
logger.info(
f"{SUBSTEP_INDENT}Running SROS post-integration for node {node.name}"
f"{SUBSTEP_INDENT}Running SROS post-integration for node {node.name} kind {node.kind}"
)
try:
# Get normalized version from the node
Expand Down
20 changes: 11 additions & 9 deletions example-topologies/EDA-sros.clab.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,27 @@ name: eda_sros
topology:
kinds:
nokia_srlinux:
image: ghcr.io/nokia/srlinux:24.10.1
nokia_sros:
image: registry.srlinux.dev/pub/vr-sros:25.3.R2
image: ghcr.io/nokia/srlinux:25.7.1
nokia_srsim:
image: registry.srlinux.dev/pub/nokia_srsim:25.7.R1
type: SR-1
license: license.txt
license: /opt/nokia/license_sros.txt
nodes:
dc-gw-1:
kind: nokia_sros
kind: nokia_srsim
env:
NOKIA_SROS_MDA_1: me12-100gb-qsfp28
spine1:
kind: nokia_srlinux
type: ixrd5
type: ixr-d5
leaf1:
kind: nokia_srlinux
type: ixrd3l
type: ixr-d3l
leaf2:
kind: nokia_srlinux
type: ixrd3l
type: ixr-d3l

links:
- endpoints: ["spine1:e1-1", "leaf1:e1-33"]
- endpoints: ["spine1:e1-2", "leaf2:e1-34"]
- endpoints: ["spine1:e1-3", "dc-gw-1:1/1/3"]
- endpoints: ["spine1:e1-3", "dc-gw-1:1/1/c3/1"]
27 changes: 27 additions & 0 deletions example-topologies/EDA-sros.legacy.clab.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: eda_sros

topology:
kinds:
nokia_srlinux:
image: ghcr.io/nokia/srlinux:24.10.1
nokia_sros:
image: registry.srlinux.dev/pub/vr-sros:25.3.R2
type: SR-1
license: license.txt
nodes:
dc-gw-1:
kind: nokia_sros
spine1:
kind: nokia_srlinux
type: ixrd5
leaf1:
kind: nokia_srlinux
type: ixrd3l
leaf2:
kind: nokia_srlinux
type: ixrd3l

links:
- endpoints: ["spine1:e1-1", "leaf1:e1-33"]
- endpoints: ["spine1:e1-2", "leaf2:e1-34"]
- endpoints: ["spine1:e1-3", "dc-gw-1:1/1/3"]
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "clab-connector"
version = "0.5.10"
version = "0.6.0"
description = "EDA Containerlab Connector"
readme = "README.md"
requires-python = ">=3.11"
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.