Skip to content
Open
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
81 changes: 58 additions & 23 deletions test/sai_test/config/port_configer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -120,7 +121,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,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How will this change affect other platforms? Can this be fixed in vpp platform?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the SAI spec, SAI_BRIDGE_PORT_ATTR_BRIDGE_ID is not valid for SAI_BRIDGE_PORT_TYPE_PORT.

SAI/inc/saibridge.h

Lines 182 to 190 in 0aba236

/**
* @brief Associated bridge id
*
* @type sai_object_id_t
* @flags MANDATORY_ON_CREATE | CREATE_AND_SET
* @objects SAI_OBJECT_TYPE_BRIDGE
* @condition SAI_BRIDGE_PORT_ATTR_TYPE == SAI_BRIDGE_PORT_TYPE_SUB_PORT or SAI_BRIDGE_PORT_ATTR_TYPE == SAI_BRIDGE_PORT_TYPE_1D_ROUTER or SAI_BRIDGE_PORT_ATTR_TYPE == SAI_BRIDGE_PORT_TYPE_TUNNEL or SAI_BRIDGE_PORT_ATTR_TYPE == SAI_BRIDGE_PORT_TYPE_BRIDGE_PORT_NEXT_HOP_GROUP
*/
SAI_BRIDGE_PORT_ATTR_BRIDGE_ID,

For this type, the bridge port is implicitly associated with the switch’s default 1Q bridge and bridge_id should not be passed on create.

VPP (via saiserver) was using the sonic-sairedis meta, enforcing the @condition above and rejected the extra bridge_id for PORT type. This was causing issues during the tests where create failed at SAI API validation. Since bridge_id should not be included for PORT type according to saibridge.h, I believe VPP's enforcement is correct and the fix would be relevant to other platforms as well; they were likely ignoring the extra attribute before.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got it. I see create_bridge_ports has only one caller and the type is PORT. We can safely remove bridge id.

port_id=item.oid,
type=SAI_BRIDGE_PORT_TYPE_PORT,
admin_state=True)
Expand Down Expand Up @@ -498,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 = 10
#
# 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] = []

Expand All @@ -514,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(3)
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
Expand Down
8 changes: 6 additions & 2 deletions test/sai_test/config/route_configer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading