From 374967777aa277d97543e198ec1a404b3e69a6f7 Mon Sep 17 00:00:00 2001 From: Nicholas Ching Date: Mon, 15 Jun 2026 06:56:09 -0700 Subject: [PATCH 1/3] fix: drop bridge_id on create_bridge_port Signed-off-by: Nicholas Ching --- test/sai_test/config/port_configer.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/sai_test/config/port_configer.py b/test/sai_test/config/port_configer.py index a3126af4..e0126fcd 100644 --- a/test/sai_test/config/port_configer.py +++ b/test/sai_test/config/port_configer.py @@ -120,7 +120,6 @@ def create_bridge_ports(self, bridge_id, port_list: List['Port']): for index, item in enumerate(port_list): port_bp = sai_thrift_create_bridge_port( self.client, - bridge_id=bridge_id, port_id=item.oid, type=SAI_BRIDGE_PORT_TYPE_PORT, admin_state=True) @@ -498,7 +497,7 @@ def turn_up_and_get_checked_ports(self, port_list: List['Port']): ''' # For brcm devices, need to init and setup the ports at once after start the switch. - retries = 10 + retries = 2 down_port_list = [] test_port_list:List[Port] = [] @@ -527,7 +526,7 @@ def turn_up_and_get_checked_ports(self, port_list: List['Port']): if port_attr['oper_status'] == SAI_PORT_OPER_STATUS_UP: port_up = True break - time.sleep(3) + time.sleep(1) self.log_port_state(port, index) print("port {} , local index {} id {} is not up, status: {}. Retry. Reset Admin State.".format( index, port.port_index, port.oid, port_attr['oper_status'])) From 635cc30481bedc5a37b32d2316be7fb1825b0247 Mon Sep 17 00:00:00 2001 From: Nicholas Ching Date: Fri, 19 Jun 2026 11:28:04 -0700 Subject: [PATCH 2/3] feat: env-gated parallel port bring-up config option through params, default option unchanged Signed-off-by: Nicholas Ching --- test/sai_test/config/port_configer.py | 80 +++++++++++++++++++-------- 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/test/sai_test/config/port_configer.py b/test/sai_test/config/port_configer.py index e0126fcd..3f3843a2 100644 --- a/test/sai_test/config/port_configer.py +++ b/test/sai_test/config/port_configer.py @@ -18,6 +18,7 @@ # # +import os from collections import OrderedDict from ptf import config from sai_utils import * # pylint: disable=wildcard-import; lgtm[py/polluting-import] @@ -497,7 +498,22 @@ def turn_up_and_get_checked_ports(self, port_list: List['Port']): ''' # For brcm devices, need to init and setup the ports at once after start the switch. - retries = 2 + # + # Waiting strategy is configurable via environment, defaulting to the original + # behavior (retries=2, 1s poll interval). The original loop waited PER PORT + # serially (retries x interval seconds for each of N ports), which on a large + # port count where oper-status is slow to settle costs N * retries * interval + # seconds (e.g. 32 ports x 2 x 1s ~= 64s). The shared-wait path below issues + # admin-up to every port once, then polls ALL ports together for at most + # (retries x interval) seconds total, re-asserting admin-up on stragglers each + # round. Semantics are preserved: every port is set admin-up, oper-status is + # polled, and down_port_list / the returned list are identical; only the total + # wall-clock wait changes. Real-HW/ASIC consumers that do not set these env + # vars keep the exact original timing. + retries = int(os.environ.get('SAI_PORT_UP_RETRIES', '2')) + poll_interval = float(os.environ.get('SAI_PORT_UP_POLL_INTERVAL', '1')) + # Opt-in: poll all ports together in one bounded loop instead of per-port. + shared_wait = os.environ.get('SAI_PORT_UP_SHARED_WAIT', '0') == '1' down_port_list = [] test_port_list:List[Port] = [] @@ -513,29 +529,49 @@ def turn_up_and_get_checked_ports(self, port_list: List['Port']): port_oid=port.oid, admin_state=True) - for index, port in enumerate(test_port_list): - port_attr = sai_thrift_get_port_attribute( - self.client, port.oid, oper_status=True) - print("Turn up port {}".format(index)) - port_up = True - if port_attr['oper_status'] != SAI_PORT_OPER_STATUS_UP: - port_up = False - for num_of_tries in range(retries): + if shared_wait: + # Single bounded wait shared across all ports. + pending = list(enumerate(test_port_list)) + for num_of_tries in range(retries + 1): + still_down = [] + for index, port in pending: port_attr = sai_thrift_get_port_attribute( self.client, port.oid, oper_status=True) - if port_attr['oper_status'] == SAI_PORT_OPER_STATUS_UP: - port_up = True - break - time.sleep(1) - self.log_port_state(port, index) - print("port {} , local index {} id {} is not up, status: {}. Retry. Reset Admin State.".format( - index, port.port_index, port.oid, port_attr['oper_status'])) - sai_thrift_set_port_attribute( - self.client, - port_oid=port.oid, - admin_state=True) - if not port_up: - down_port_list.append(index) + if port_attr['oper_status'] != SAI_PORT_OPER_STATUS_UP: + still_down.append((index, port)) + pending = still_down + if not pending: + break + if num_of_tries < retries: + time.sleep(poll_interval) + for index, port in pending: + sai_thrift_set_port_attribute( + self.client, port_oid=port.oid, admin_state=True) + down_port_list = [index for index, _ in pending] + else: + for index, port in enumerate(test_port_list): + port_attr = sai_thrift_get_port_attribute( + self.client, port.oid, oper_status=True) + print("Turn up port {}".format(index)) + port_up = True + if port_attr['oper_status'] != SAI_PORT_OPER_STATUS_UP: + port_up = False + for num_of_tries in range(retries): + port_attr = sai_thrift_get_port_attribute( + self.client, port.oid, oper_status=True) + if port_attr['oper_status'] == SAI_PORT_OPER_STATUS_UP: + port_up = True + break + time.sleep(poll_interval) + self.log_port_state(port, index) + print("port {} , local index {} id {} is not up, status: {}. Retry. Reset Admin State.".format( + index, port.port_index, port.oid, port_attr['oper_status'])) + sai_thrift_set_port_attribute( + self.client, + port_oid=port.oid, + admin_state=True) + if not port_up: + down_port_list.append(index) if down_port_list: print("Ports {} are down after retries.".format(down_port_list)) return test_port_list From 3844dde6a54d357b6670526d12888c2d7539bf7c Mon Sep 17 00:00:00 2001 From: Nicholas Ching Date: Fri, 19 Jun 2026 11:28:22 -0700 Subject: [PATCH 3/3] fix: v4/v6 NHG member_port_indexs aliasing Signed-off-by: Nicholas Ching --- test/sai_test/config/route_configer.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/sai_test/config/route_configer.py b/test/sai_test/config/route_configer.py index a7aa2094..6019718d 100644 --- a/test/sai_test/config/route_configer.py +++ b/test/sai_test/config/route_configer.py @@ -622,9 +622,13 @@ def create_nexthop_group_by_nexthops(self, nexthopv4_list: List[Nexthop], nextho nhp_grpv4_members.append(nhp_grpv4_member) nhp_grpv6_members.append(nhp_grpv6_member) + # Give each NHG its own copy of the member port-index list. The v4 and v6 + # groups are mutated independently (remove/re-add member tests), so sharing + # one list object would let a v4 mutation corrupt the v6 group's port list + # (ValueError: x not in list) when both run in the same config group. member_port_indexs = [17, 18, 19, 20, 21, 22, 23, 24] - nhp_grpv4: NexthopGroup = NexthopGroup(nhop_groupv4_id, nhp_grpv4_members, member_port_indexs) - nhp_grpv6: NexthopGroup = NexthopGroup(nhop_groupv6_id, nhp_grpv6_members, member_port_indexs) + nhp_grpv4: NexthopGroup = NexthopGroup(nhop_groupv4_id, nhp_grpv4_members, list(member_port_indexs)) + nhp_grpv6: NexthopGroup = NexthopGroup(nhop_groupv6_id, nhp_grpv6_members, list(member_port_indexs)) self.test_obj.dut.nhp_grpv4_list.append(nhp_grpv4) self.test_obj.dut.nhp_grpv6_list.append(nhp_grpv6)