Skip to content

Commit d487336

Browse files
authored
Merge pull request #71 from srl-labs/interface-selector-regex
Add interface digit selection regex for flow panel drawio
2 parents 919901c + 218330d commit d487336

File tree

6 files changed

+105
-36
lines changed

6 files changed

+105
-36
lines changed

docs/clab2drawio.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,22 @@ The easiest way to create perfect layouts is using the VS Code Containerlab exte
141141
clab2drawio -i <path_to_your_yaml_file> -g --theme grafana --grafana-config <path_to_your_cfg_file>
142142
```
143143

144+
- `--grafana-interface-format`: Maps interface names for Grafana export using regex patterns. This allows you to transform interface names to match your monitoring system's naming conventions.
145+
146+
```bash
147+
clab2drawio -i <path_to_your_yaml_file> -g --grafana-interface-format "e1-{x}:ethernet1/{x}"
148+
```
149+
150+
This would convert interface names like `e1-1` to `ethernet1/1` in the Grafana dashboard.
151+
152+
- `--grafana-interface-selector`: Selects which part of the interface name to display as the port label using regex patterns with capture groups.
153+
154+
```bash
155+
clab2drawio -i <path_to_your_yaml_file> -g --grafana-interface-selector "e1-1-c{x}-1"
156+
```
157+
158+
This would extract and display only the captured digit (e.g., from `e1-1-c3-1` it would display `3` as the port label).
159+
144160
For more detailed information about this feature, including compatibility, usage guidelines, and future enhancements, please see the [Grafana Dashboard Documentation](./grafana.md).
145161
146162
- `--include-unlinked-nodes`: Include nodes without any links in the topology diagram. By default, only nodes with at least one connection are included.

docs/grafana.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,32 @@ clab2drawio -i <path_to_yaml> -g --theme grafana --grafana-config <path_to_confi
5858
> [!TIP]
5959
> Customize targets, thresholds, or labels in your YAML to align with your network's metrics and visualization needs.
6060
61+
#### Interface Name Mapping
62+
63+
The `--grafana-interface-format` option allows you to transform interface names to match your monitoring system's naming conventions. This is useful when your containerlab topology uses different interface naming than your telemetry system.
64+
65+
Example:
66+
```bash
67+
clab2drawio -i topo.yml -g --grafana-interface-format "e1-{x}:ethernet1/{x}"
68+
```
69+
70+
This would map interface names like:
71+
- `e1-1``ethernet1/1`
72+
- `e1-48``ethernet1/48`
73+
74+
#### Interface Label Selection
75+
76+
The `--grafana-interface-selector` option lets you control which part of complex interface names appears as port labels in the diagram. This is useful for cleaner visualization when interface names contain multiple segments.
77+
78+
Example:
79+
```bash
80+
clab2drawio -i topo.yml -g --grafana-interface-selector "e1-1-c{x}-1"
81+
```
82+
83+
This would extract and display only the captured digit from interface names like:
84+
- `e1-1-c3-1` → displays `3` as the port label
85+
- `e1-1-c10-1` → displays `10` as the port label
86+
6187
#### To export the diagram as an SVG:
6288
To get a full guide: [https://github.com/andymchugh/andrewbmchugh-flow-panel/blob/main/src/README.md#using-drawio-to-create-your-svg](https://github.com/andymchugh/andrewbmchugh-flow-panel/blob/main/src/README.md#using-drawio-to-create-your-svg)
6389

pyproject.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "clab-io-draw"
3-
version = "0.6.2"
3+
version = "0.6.3"
44
description = "Convert between draw.io and containerlab topology files"
55
readme = "README.md"
66
requires-python = ">=3.11"
@@ -10,12 +10,12 @@ dependencies = [
1010
"PyYAML==6.0.2",
1111
"six==1.17.0",
1212
"wcwidth==0.2.13",
13-
"ruamel-yaml==0.18.14",
13+
"ruamel-yaml==0.18.15",
1414
"textual-dev==1.7.0",
1515
"textual==5.3.0",
1616
"networkx>=3.5.0",
1717
"defusedxml>=0.7.1",
18-
"typer>=0.16.0",
18+
"typer>=0.16.1",
1919
"pytest>=8.4.1",
2020
]
2121

@@ -75,5 +75,5 @@ docstring-code-line-length = "dynamic"
7575
[dependency-groups]
7676
dev = [
7777
"pre-commit>=4.2.0",
78-
"ruff>=0.11.13",
78+
"ruff>=0.12.10",
7979
]

src/clab_io_draw/clab2drawio.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def main(
5454
interactive: bool = False,
5555
grafana_config_path: str = None,
5656
grafana_interface_format: str | None = None,
57+
grafana_interface_selector: str | None = None,
5758
) -> None:
5859
"""
5960
Main function to generate a topology diagram from a containerlab YAML or draw.io XML file.
@@ -68,6 +69,7 @@ def main(
6869
:param log_level: Logging level for output.
6970
:param interactive: Run in interactive mode to define graph-levels and icons.
7071
:param grafana_interface_format: Regex pattern for mapping interface names (e.g., "e1-{x}:ethernet1/{x}").
72+
:param grafana_interface_selector: Regex pattern for selecting which part of interface name to display (e.g., "e1-1-c{x}-1" to select 'x').
7173
"""
7274
logger.debug("Starting clab2drawio main function.")
7375
script_dir = os.path.dirname(__file__)
@@ -269,6 +271,9 @@ def main(
269271
if grafana:
270272
styles["ports"] = True
271273

274+
if grafana_interface_selector:
275+
styles["grafana_interface_selector"] = grafana_interface_selector
276+
272277
if styles["ports"]:
273278
logger.debug("Adding ports and generating Grafana dashboard...")
274279
diagram_builder.add_ports(diagram, styles)
@@ -340,6 +345,11 @@ def cli( # noqa: B008
340345
"--grafana-interface-format",
341346
help="Regex pattern for mapping interface names (e.g., 'e1-{x}:ethernet1/{x}')",
342347
), # noqa: B008
348+
grafana_interface_selector: str | None = typer.Option(
349+
None,
350+
"--grafana-interface-selector",
351+
help="Regex pattern for selecting which part of interface name to display (e.g., 'e1-1-c{x}-1' to select 'x')",
352+
), # noqa: B008
343353
include_unlinked_nodes: bool = typer.Option(
344354
False, "--include-unlinked-nodes", help="Include nodes without links"
345355
), # noqa: B008
@@ -369,6 +379,7 @@ def cli( # noqa: B008
369379
interactive=interactive,
370380
grafana_config_path=str(grafana_config) if grafana_config else None,
371381
grafana_interface_format=grafana_interface_format,
382+
grafana_interface_selector=grafana_interface_selector,
372383
)
373384

374385

src/clab_io_draw/core/diagram/diagram_builder.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,22 @@ class DiagramBuilder:
1111
Builds diagram elements such as nodes, ports, and links into the Draw.io diagram.
1212
"""
1313

14+
def get_intf_digit(self, intf_name, styles):
15+
interface_selector = styles.get("grafana_interface_selector")
16+
if interface_selector:
17+
regex_pattern = interface_selector.replace("{x}", r"(\d+)")
18+
19+
try:
20+
match = re.match(f"^{regex_pattern}$", intf_name)
21+
if match:
22+
return match.group(1)
23+
except re.error as e:
24+
logger.warning(f"Invalid pattern for grafana_interface_selector: {e}")
25+
26+
# fallback to default
27+
digits = re.findall(r"\d+", intf_name)
28+
return digits[-1] if digits else "0"
29+
1430
def add_ports(self, diagram, styles, _verbose=True):
1531
"""
1632
Add ports and their connections to the diagram.
@@ -207,7 +223,7 @@ def add_ports(self, diagram, styles, _verbose=True):
207223
if connection_id not in processed_connections:
208224
processed_connections.add(connection_id)
209225
source_cID = f"{link.source.name}:{link.source_intf}:{link.target.name}:{link.target_intf}"
210-
source_label = re.findall(r"\d+", link.source_intf)[-1]
226+
source_label = self.get_intf_digit(link.source_intf, styles)
211227
source_connector_pos = link.port_pos
212228
port_width = styles["port_width"]
213229
port_height = styles["port_height"]
@@ -219,7 +235,7 @@ def add_ports(self, diagram, styles, _verbose=True):
219235
target_cID = f"{link.target.name}:{link.target_intf}:{link.source.name}:{link.source_intf}"
220236
target_link = diagram.get_target_link(link)
221237
target_connector_pos = target_link.port_pos
222-
target_label = re.findall(r"\d+", target_link.source_intf)[-1]
238+
target_label = self.get_intf_digit(target_link.source_intf, styles)
223239

224240
if link.target.name not in connector_dict:
225241
connector_dict[link.target.name] = []

0 commit comments

Comments
 (0)