Skip to content

Commit 91e6b86

Browse files
sai_test config: drop redundant bridge_id, env-gated port bring-up, fix v4/v6 NHG port-list aliasing (#2301)
Context / motivation Part of the SAIVPP unit-test framework (see PR 1 / our docker-sai-test-vpp/devdocs/, esp. the 6-19 entry). Three independent correctness/robustness fixes in the OCP sai_test config helpers that the suite needs when run against the VPP SAI backend. What this change does test/sai_test/config/port_configer.py — drop bridge_id on create_bridge_port. Passing bridge_id to sai_thrift_create_bridge_port caused a create failure in our backend; the default 1Q bridge is used, so the argument is redundant. Removing it lets bridge-port creation succeed. test/sai_test/config/port_configer.py — env-gated, bounded port bring-up wait. turn_up_and_get_checked_ports() waited per port, serially (retries × sleep) for oper-status UP. On a 32-port topology where oper-status is slow to settle, this is ~60s+ of dead time per common-config build. The wait is now tunable via env (SAI_PORT_UP_RETRIES, SAI_PORT_UP_POLL_INTERVAL, SAI_PORT_UP_SHARED_WAIT) and can poll all ports together in one bounded window. Defaults preserve the original behavior (per-port wait), so real-HW/other OCP consumers are unchanged unless they opt in; only our harness sets the fast values. test/sai_test/config/route_configer.py — give v4/v6 NHGs independent member_port_indexs. create_nexthop_group_by_nexthops() constructed the v4 and v6 NexthopGroup objects sharing one Python list object for member_port_indexs. A mutation on one group (member remove/re-add tests) then corrupted the other's port list (ValueError: list.remove(x): x not in list). Each group now gets its own list(...) copy. (Latent aliasing bug, independent of any harness specifics.) Scope / risk 2 files, +64/−25 — test-config helpers only; no change to SAI/backend code or packet semantics. The port-up change is opt-in via env with original defaults, so it does not alter timing for existing consumers. The bridge_id removal relies on the default 1Q bridge (already how these tests are used). Dependencies None. Related to #2299 and #2300, however, their edits are isolated and each target master.
1 parent cd70272 commit 91e6b86

2 files changed

Lines changed: 64 additions & 25 deletions

File tree

test/sai_test/config/port_configer.py

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#
1919
#
2020

21+
import os
2122
from collections import OrderedDict
2223
from ptf import config
2324
from sai_utils import * # pylint: disable=wildcard-import; lgtm[py/polluting-import]
@@ -120,7 +121,6 @@ def create_bridge_ports(self, bridge_id, port_list: List['Port']):
120121
for index, item in enumerate(port_list):
121122
port_bp = sai_thrift_create_bridge_port(
122123
self.client,
123-
bridge_id=bridge_id,
124124
port_id=item.oid,
125125
type=SAI_BRIDGE_PORT_TYPE_PORT,
126126
admin_state=True)
@@ -498,7 +498,22 @@ def turn_up_and_get_checked_ports(self, port_list: List['Port']):
498498
'''
499499

500500
# For brcm devices, need to init and setup the ports at once after start the switch.
501-
retries = 10
501+
#
502+
# Waiting strategy is configurable via environment, defaulting to the original
503+
# behavior (retries=2, 1s poll interval). The original loop waited PER PORT
504+
# serially (retries x interval seconds for each of N ports), which on a large
505+
# port count where oper-status is slow to settle costs N * retries * interval
506+
# seconds (e.g. 32 ports x 2 x 1s ~= 64s). The shared-wait path below issues
507+
# admin-up to every port once, then polls ALL ports together for at most
508+
# (retries x interval) seconds total, re-asserting admin-up on stragglers each
509+
# round. Semantics are preserved: every port is set admin-up, oper-status is
510+
# polled, and down_port_list / the returned list are identical; only the total
511+
# wall-clock wait changes. Real-HW/ASIC consumers that do not set these env
512+
# vars keep the exact original timing.
513+
retries = int(os.environ.get('SAI_PORT_UP_RETRIES', '2'))
514+
poll_interval = float(os.environ.get('SAI_PORT_UP_POLL_INTERVAL', '1'))
515+
# Opt-in: poll all ports together in one bounded loop instead of per-port.
516+
shared_wait = os.environ.get('SAI_PORT_UP_SHARED_WAIT', '0') == '1'
502517
down_port_list = []
503518
test_port_list:List[Port] = []
504519

@@ -514,29 +529,49 @@ def turn_up_and_get_checked_ports(self, port_list: List['Port']):
514529
port_oid=port.oid,
515530
admin_state=True)
516531

517-
for index, port in enumerate(test_port_list):
518-
port_attr = sai_thrift_get_port_attribute(
519-
self.client, port.oid, oper_status=True)
520-
print("Turn up port {}".format(index))
521-
port_up = True
522-
if port_attr['oper_status'] != SAI_PORT_OPER_STATUS_UP:
523-
port_up = False
524-
for num_of_tries in range(retries):
532+
if shared_wait:
533+
# Single bounded wait shared across all ports.
534+
pending = list(enumerate(test_port_list))
535+
for num_of_tries in range(retries + 1):
536+
still_down = []
537+
for index, port in pending:
525538
port_attr = sai_thrift_get_port_attribute(
526539
self.client, port.oid, oper_status=True)
527-
if port_attr['oper_status'] == SAI_PORT_OPER_STATUS_UP:
528-
port_up = True
529-
break
530-
time.sleep(3)
531-
self.log_port_state(port, index)
532-
print("port {} , local index {} id {} is not up, status: {}. Retry. Reset Admin State.".format(
533-
index, port.port_index, port.oid, port_attr['oper_status']))
534-
sai_thrift_set_port_attribute(
535-
self.client,
536-
port_oid=port.oid,
537-
admin_state=True)
538-
if not port_up:
539-
down_port_list.append(index)
540+
if port_attr['oper_status'] != SAI_PORT_OPER_STATUS_UP:
541+
still_down.append((index, port))
542+
pending = still_down
543+
if not pending:
544+
break
545+
if num_of_tries < retries:
546+
time.sleep(poll_interval)
547+
for index, port in pending:
548+
sai_thrift_set_port_attribute(
549+
self.client, port_oid=port.oid, admin_state=True)
550+
down_port_list = [index for index, _ in pending]
551+
else:
552+
for index, port in enumerate(test_port_list):
553+
port_attr = sai_thrift_get_port_attribute(
554+
self.client, port.oid, oper_status=True)
555+
print("Turn up port {}".format(index))
556+
port_up = True
557+
if port_attr['oper_status'] != SAI_PORT_OPER_STATUS_UP:
558+
port_up = False
559+
for num_of_tries in range(retries):
560+
port_attr = sai_thrift_get_port_attribute(
561+
self.client, port.oid, oper_status=True)
562+
if port_attr['oper_status'] == SAI_PORT_OPER_STATUS_UP:
563+
port_up = True
564+
break
565+
time.sleep(poll_interval)
566+
self.log_port_state(port, index)
567+
print("port {} , local index {} id {} is not up, status: {}. Retry. Reset Admin State.".format(
568+
index, port.port_index, port.oid, port_attr['oper_status']))
569+
sai_thrift_set_port_attribute(
570+
self.client,
571+
port_oid=port.oid,
572+
admin_state=True)
573+
if not port_up:
574+
down_port_list.append(index)
540575
if down_port_list:
541576
print("Ports {} are down after retries.".format(down_port_list))
542577
return test_port_list

test/sai_test/config/route_configer.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -639,9 +639,13 @@ def create_nexthop_group_by_nexthops(self, nexthopv4_list: List[Nexthop], nextho
639639
nhp_grpv4_members.append(nhp_grpv4_member)
640640
nhp_grpv6_members.append(nhp_grpv6_member)
641641

642+
# Give each NHG its own copy of the member port-index list. The v4 and v6
643+
# groups are mutated independently (remove/re-add member tests), so sharing
644+
# one list object would let a v4 mutation corrupt the v6 group's port list
645+
# (ValueError: x not in list) when both run in the same config group.
642646
member_port_indexs = [17, 18, 19, 20, 21, 22, 23, 24]
643-
nhp_grpv4: NexthopGroup = NexthopGroup(nhop_groupv4_id, nhp_grpv4_members, member_port_indexs)
644-
nhp_grpv6: NexthopGroup = NexthopGroup(nhop_groupv6_id, nhp_grpv6_members, member_port_indexs)
647+
nhp_grpv4: NexthopGroup = NexthopGroup(nhop_groupv4_id, nhp_grpv4_members, list(member_port_indexs))
648+
nhp_grpv6: NexthopGroup = NexthopGroup(nhop_groupv6_id, nhp_grpv6_members, list(member_port_indexs))
645649

646650
self.test_obj.dut.nhp_grpv4_list.append(nhp_grpv4)
647651
self.test_obj.dut.nhp_grpv6_list.append(nhp_grpv6)

0 commit comments

Comments
 (0)