From 97c18d12719257b73f8ba1452492e53b6261d0c5 Mon Sep 17 00:00:00 2001 From: Christian Berendt Date: Sat, 18 Jul 2026 20:57:16 +0200 Subject: [PATCH] Unify SONiC breakout mode selection across name formats detect_breakout_ports mapped subport speed to breakout mode with two independent tables: the NetBox EthX/Y/Z path supported 4x10G, 4x25G, 4x50G, 4x100G and 4x200G, while the SONiC EthernetX path only knew 4x25G and 4x50G. A 4x10G breakout of a 40G/100G port (e.g. on an Edgecore AS7726-32X) was therefore silently dropped when the interfaces are named in the SONiC format. Move the speed-to-mode table into the shared constant BREAKOUT_MODE_BY_SPEED and use it at both detection sites. The EthernetX path keeps its <= 50G group membership filter, so it now covers 4x10G, 4x25G and 4x50G; 4x100G stays with the dedicated 400G branch that matches ports spaced by two. Closes #2482 Assisted-by: Claude:claude-fable-5 Signed-off-by: Christian Berendt --- osism/tasks/conductor/sonic/constants.py | 13 ++++++++ osism/tasks/conductor/sonic/interface.py | 33 +++++++++---------- .../sonic/test_breakout_detection.py | 3 ++ 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/osism/tasks/conductor/sonic/constants.py b/osism/tasks/conductor/sonic/constants.py index 4f34b8a17..6cbdd0109 100644 --- a/osism/tasks/conductor/sonic/constants.py +++ b/osism/tasks/conductor/sonic/constants.py @@ -73,6 +73,19 @@ # High speed ports that use 4x multiplier (lanes) HIGH_SPEED_PORTS = {100000, 200000, 400000, 800000} # 100G, 200G, 400G, 800G in Mbps +# Supported four-subport breakout modes keyed by subport speed in Mbps +# (get_interface_speed normalizes explicit NetBox kbps speeds to Mbps). +# The SONiC EthernetX detection path filters breakout groups to <= 50G, +# so the 100G and 200G entries only match the NetBox EthX/Y/Z path; +# SONiC-format 4x100G is handled by the dedicated 400G branch. +BREAKOUT_MODE_BY_SPEED = { + 10000: "4x10G", + 25000: "4x25G", + 50000: "4x50G", + 100000: "4x100G", + 200000: "4x200G", +} + # Path to SONiC port configuration files PORT_CONFIG_PATH = "/etc/sonic/port_config" diff --git a/osism/tasks/conductor/sonic/interface.py b/osism/tasks/conductor/sonic/interface.py index 23aee3395..abb8c02eb 100644 --- a/osism/tasks/conductor/sonic/interface.py +++ b/osism/tasks/conductor/sonic/interface.py @@ -7,7 +7,12 @@ import re from loguru import logger -from .constants import PORT_TYPE_TO_SPEED_MAP, HIGH_SPEED_PORTS, PORT_CONFIG_PATH +from .constants import ( + BREAKOUT_MODE_BY_SPEED, + HIGH_SPEED_PORTS, + PORT_CONFIG_PATH, + PORT_TYPE_TO_SPEED_MAP, +) from .cache import get_cached_device_interfaces # Global cache for port configurations to avoid repeated file reads @@ -750,17 +755,10 @@ def detect_breakout_ports(device): ) # Calculate breakout mode - if interface_speed == 10000 and num_subports == 4: - brkout_mode = "4x10G" - elif interface_speed == 25000 and num_subports == 4: - brkout_mode = "4x25G" - elif interface_speed == 50000 and num_subports == 4: - brkout_mode = "4x50G" - elif interface_speed == 100000 and num_subports == 4: - brkout_mode = "4x100G" - elif interface_speed == 200000 and num_subports == 4: - brkout_mode = "4x200G" - else: + brkout_mode = BREAKOUT_MODE_BY_SPEED.get( + interface_speed + ) + if num_subports != 4 or not brkout_mode: logger.debug( f"Unsupported breakout configuration: {num_subports} ports at {interface_speed} Mbps" ) @@ -917,14 +915,13 @@ def detect_breakout_ports(device): if len(sonic_breakout_group) == 4: master_port = f"Ethernet{base_port}" - # Determine breakout mode based on speed + # Determine breakout mode based on speed; the membership + # filter above (<= 50G) limits this path to 4x10G, 4x25G + # and 4x50G interface_speed = get_interface_speed(sonic_breakout_group[0][1]) - if interface_speed == 25000: - brkout_mode = "4x25G" - elif interface_speed == 50000: - brkout_mode = "4x50G" - else: + brkout_mode = BREAKOUT_MODE_BY_SPEED.get(interface_speed) + if not brkout_mode: continue # Skip unsupported speeds # Calculate physical port number (Ethernet0-3 -> port 1/1, Ethernet4-7 -> port 1/2, etc.) diff --git a/tests/unit/tasks/conductor/sonic/test_breakout_detection.py b/tests/unit/tasks/conductor/sonic/test_breakout_detection.py index df593947e..b68b575bc 100644 --- a/tests/unit/tasks/conductor/sonic/test_breakout_detection.py +++ b/tests/unit/tasks/conductor/sonic/test_breakout_detection.py @@ -464,6 +464,9 @@ def test_detect_breakout_ports_sonic_400g_4_lane_master_falls_through( @pytest.mark.parametrize( "speed, expected_mode", [ + # Explicit speeds are kbps as NetBox stores them; detection + # normalizes them to Mbps before the breakout mode lookup. + (10_000_000, "4x10G"), (25_000_000, "4x25G"), (50_000_000, "4x50G"), ],