Skip to content

Commit 14a9289

Browse files
committed
Add EVPN LAG Support
AI-assisted: Claude Code Signed-off-by: Freerk-Ole Zakfeld <fzakfeld@scaleuptech.com> Remove .vscode/settings.json Signed-off-by: Freerk-Ole Zakfeld <fzakfeld@scaleuptech.com> Rework Signed-off-by: Freerk-Ole Zakfeld <fzakfeld@scaleuptech.com>
1 parent 4350752 commit 14a9289

3 files changed

Lines changed: 39 additions & 5 deletions

File tree

osism/tasks/conductor/sonic/config_generator.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,17 @@ def generate_sonic_config(device, hwsku, device_as_mapping=None, config_version=
7373
# Get port channel configuration from NetBox first (needed by get_connected_interfaces)
7474
portchannel_info = detect_port_channels(device)
7575

76+
# Resolve evpn_system_mac early so it is validated once and passed explicitly later
77+
_raw_evpn_mac = device.config_context.get("_evpn_system_mac")
78+
evpn_system_mac = (
79+
_raw_evpn_mac if isinstance(_raw_evpn_mac, str) and _raw_evpn_mac else None
80+
)
81+
if _raw_evpn_mac and not evpn_system_mac:
82+
logger.warning(
83+
f"Device {device.name}: '_evpn_system_mac' in config_context is not a valid string"
84+
f" (got {type(_raw_evpn_mac).__name__!r}), ignoring"
85+
)
86+
7687
# Get connected interfaces to determine admin_status
7788
connected_interfaces, connected_portchannels = get_connected_interfaces(
7889
device, portchannel_info
@@ -274,7 +285,7 @@ def generate_sonic_config(device, hwsku, device_as_mapping=None, config_version=
274285
config["BREAKOUT_PORTS"].update(breakout_info["breakout_ports"])
275286

276287
# Add port channel configuration
277-
_add_portchannel_configuration(config, portchannel_info)
288+
_add_portchannel_configuration(config, portchannel_info, evpn_system_mac)
278289

279290
# Add VRF configuration
280291
_add_vrf_configuration(config, vrf_info, netbox_interfaces)
@@ -2108,17 +2119,20 @@ def _add_vrf_configuration(config, vrf_info, netbox_interfaces):
21082119
)
21092120

21102121

2111-
def _add_portchannel_configuration(config, portchannel_info):
2122+
def _add_portchannel_configuration(config, portchannel_info, evpn_system_mac=None):
21122123
"""Add port channel configuration from NetBox."""
21132124
if portchannel_info["portchannels"]:
21142125
for pc_name, pc_data in portchannel_info["portchannels"].items():
21152126
# Add PORTCHANNEL configuration
2116-
config["PORTCHANNEL"][pc_name] = {
2127+
pc_config = {
21172128
"admin_status": pc_data["admin_status"],
21182129
"fast_rate": pc_data["fast_rate"],
21192130
"min_links": pc_data["min_links"],
21202131
"mtu": pc_data["mtu"],
21212132
}
2133+
if pc_data.get("evpn_lag") and evpn_system_mac:
2134+
pc_config["system_mac"] = evpn_system_mac
2135+
config["PORTCHANNEL"][pc_name] = pc_config
21222136

21232137
# Add PORTCHANNEL_INTERFACE configuration to enable IPv6 link-local
21242138
config["PORTCHANNEL_INTERFACE"][pc_name] = {

osism/tasks/conductor/sonic/constants.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
# Tag to add AF L2VPN EVPN to BGP neighbor
66
BGP_AF_L2VPN_EVPN_TAG = "bgp-af-l2vpn-evpn"
77

8+
# Tag to enable EVPN Multihoming (evpn-lag mode) on a port channel
9+
EVPN_LAG_TAG = "evpn-lag"
10+
811
# Default AS prefix for local ASN calculation
912
DEFAULT_LOCAL_AS_PREFIX = 4200
1013

osism/tasks/conductor/sonic/interface.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
import re
88
from loguru import logger
99

10-
from .constants import PORT_TYPE_TO_SPEED_MAP, HIGH_SPEED_PORTS, PORT_CONFIG_PATH
10+
from .constants import (
11+
PORT_TYPE_TO_SPEED_MAP,
12+
HIGH_SPEED_PORTS,
13+
PORT_CONFIG_PATH,
14+
EVPN_LAG_TAG,
15+
)
1116
from .cache import get_cached_device_interfaces
1217

1318
# Global cache for port configurations to avoid repeated file reads
@@ -978,7 +983,7 @@ def detect_port_channels(device):
978983
# Get all interfaces for the device (using cache)
979984
interfaces = get_cached_device_interfaces(device.id)
980985

981-
# First pass: find LAG interfaces
986+
# First pass: find LAG interfaces and precompute evpn_lag lookup
982987
lag_interfaces = []
983988
for interface in interfaces:
984989
# Check if this is a LAG interface
@@ -987,6 +992,15 @@ def detect_port_channels(device):
987992
lag_interfaces.append(interface)
988993
logger.debug(f"Found LAG interface: {interface.name}")
989994

995+
evpn_lag_by_id = {
996+
iface.id: (
997+
hasattr(iface, "tags")
998+
and iface.tags
999+
and any(tag.slug == EVPN_LAG_TAG for tag in iface.tags)
1000+
)
1001+
for iface in lag_interfaces
1002+
}
1003+
9901004
# Second pass: map members to LAGs
9911005
for interface in interfaces:
9921006
# Check if this interface has a LAG parent
@@ -1044,12 +1058,15 @@ def detect_port_channels(device):
10441058

10451059
# Initialize port channel if not exists
10461060
if portchannel_name not in portchannels:
1061+
evpn_lag = evpn_lag_by_id.get(lag_parent.id, False)
1062+
10471063
portchannels[portchannel_name] = {
10481064
"members": [],
10491065
"admin_status": "up",
10501066
"fast_rate": "true",
10511067
"min_links": "1",
10521068
"mtu": "9100",
1069+
"evpn_lag": evpn_lag,
10531070
}
10541071

10551072
# Add member to port channel

0 commit comments

Comments
 (0)