Skip to content

Commit 0797820

Browse files
Add q3d and portchannel support for strict priority.
Signed-off-by: Abhishek <abhishek@nexthop.ai>
1 parent 2900294 commit 0797820

File tree

5 files changed

+310
-151
lines changed

5 files changed

+310
-151
lines changed

tests/common/devices/sonic.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1838,6 +1838,7 @@ def _try_get_brcm_asic_name(self, output):
18381838
"th3": {"b98", "BCM5698"},
18391839
"th4": {"b99", "BCM5699"},
18401840
"th5": {"f90", "BCM7890"},
1841+
"q3d": {"8870", "8872"},
18411842
}
18421843
for asic in search_sets.keys():
18431844
for search_term in search_sets[asic]:

tests/common/helpers/ptf_tests_helper.py

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,168 @@
99

1010
from ipaddress import ip_address, IPv4Address
1111
from tests.common.config_reload import config_reload
12+
from tests.common.utilities import wait_until
1213

1314
logger = logging.getLogger(__name__)
1415

1516

17+
# ============================================================================
18+
# Helper Functions for PTF Port Mapping and Interface Selection
19+
# ============================================================================
20+
1621
def get_dut_to_ptf_port_mapping(duthost, tbinfo):
1722
"""
18-
Get mapping of DUT interfaces to PTF ports.
23+
Get mapping of DUT interfaces/PortChannels to PTF ports.
24+
Only includes interfaces or PortChannels that have IP addresses configured.
1925
2026
Args:
2127
duthost: DUT host object
2228
tbinfo: Testbed info
2329
2430
Returns:
25-
dict: Mapping of DUT interface names to PTF port indices
31+
dict: {interface_name: ptf_index} for interfaces with IP
32+
{portchannel_name: -1} for PortChannels with IP
2633
"""
2734
mg_facts = duthost.get_extended_minigraph_facts(tbinfo)
28-
return mg_facts.get('minigraph_ptf_indices', {})
35+
all_indices = mg_facts.get('minigraph_ptf_indices', {})
36+
mapping = {}
37+
38+
# Add interfaces with IP addresses
39+
for intf in mg_facts.get('minigraph_interfaces', []):
40+
interface_name = intf.get('attachto')
41+
if interface_name and intf.get('addr'):
42+
ptf_index = all_indices.get(interface_name)
43+
if ptf_index is not None:
44+
mapping[interface_name] = ptf_index
45+
46+
# Add PortChannels with IP addresses (PTF index = -1)
47+
for intf in mg_facts.get('minigraph_portchannel_interfaces', []):
48+
portchannel_name = intf.get('attachto')
49+
if portchannel_name and intf.get('addr'):
50+
mapping[portchannel_name] = -1
51+
52+
return mapping
53+
54+
55+
def get_interface_ip_address(interface_name, mg_facts):
56+
"""
57+
Get IP address for an interface from minigraph facts.
58+
59+
Args:
60+
interface_name: Interface or PortChannel name
61+
mg_facts: Minigraph facts dictionary
62+
63+
Returns:
64+
str: IP address or None if not found
65+
"""
66+
# Check in minigraph_interfaces
67+
for intf in mg_facts.get('minigraph_interfaces', []):
68+
if intf.get('attachto') == interface_name and intf.get('addr'):
69+
return str(intf['addr'])
70+
71+
# Check in minigraph_portchannel_interfaces
72+
for intf in mg_facts.get('minigraph_portchannel_interfaces', []):
73+
if intf.get('attachto') == interface_name and intf.get('addr'):
74+
return str(intf['addr'])
75+
76+
return None
2977

3078

79+
def select_test_interface_and_ptf_port(duthost, tbinfo):
80+
"""
81+
Select a random test interface/PortChannel and its corresponding PTF port.
82+
83+
Args:
84+
duthost: DUT host object
85+
tbinfo: Testbed info
86+
87+
Returns:
88+
tuple: (interface_name, ptf_index) for interfaces
89+
(portchannel_name, -1) for PortChannels
90+
(None, None) if not found
91+
"""
92+
dut_to_ptf_mapping = get_dut_to_ptf_port_mapping(duthost, tbinfo)
93+
if not dut_to_ptf_mapping:
94+
return None, None
95+
96+
interface_name = random.choice(list(dut_to_ptf_mapping.keys()))
97+
ptf_port_index = dut_to_ptf_mapping[interface_name]
98+
99+
logger.info("Selected: {} (PTF port: {})".format(interface_name, ptf_port_index))
100+
return interface_name, ptf_port_index
101+
102+
103+
def detect_portchannel_egress_member(duthost, tbinfo, ptf_adapter, portchannel_name, test_packet):
104+
"""
105+
Detect which PortChannel member interface is actually used for egress traffic.
106+
107+
Args:
108+
duthost: DUT host object
109+
tbinfo: Testbed info
110+
ptf_adapter: PTF adapter object
111+
portchannel_name: PortChannel name
112+
test_packet: Test packet to send
113+
114+
Returns:
115+
tuple: (member_interface, ptf_port) or (None, None)
116+
"""
117+
import ptf.testutils as testutils
118+
119+
logger.info("Detecting egress member for {}".format(portchannel_name))
120+
121+
# Get PortChannel members
122+
mg_facts = duthost.get_extended_minigraph_facts(tbinfo)
123+
portchannels = mg_facts.get('minigraph_portchannels', {})
124+
125+
if portchannel_name not in portchannels:
126+
return None, None
127+
128+
members = portchannels[portchannel_name].get('members', [])
129+
if not members:
130+
return None, None
131+
132+
# Get PTF ports for members
133+
ptf_indices = mg_facts.get('minigraph_ptf_indices', {})
134+
member_ptf_ports = [(m, ptf_indices[m]) for m in members if m in ptf_indices]
135+
136+
if not member_ptf_ports:
137+
return None, None
138+
139+
# Try each member port
140+
num_test_packets = 100
141+
timeout = 10.0
142+
143+
for member_name, member_ptf_port in member_ptf_ports:
144+
logger.info("Trying {} (PTF port {})".format(member_name, member_ptf_port))
145+
ptf_adapter.dataplane.flush()
146+
147+
# Send test packets
148+
for i in range(num_test_packets):
149+
testutils.send_packet(ptf_adapter, member_ptf_port, test_packet)
150+
151+
# Check if packets received
152+
packets_received = 0
153+
154+
def check_packets_received():
155+
nonlocal packets_received
156+
while True:
157+
result = testutils.dp_poll(ptf_adapter, device_number=0, timeout=0.1)
158+
if isinstance(result, ptf_adapter.dataplane.PollSuccess):
159+
if result.port == member_ptf_port:
160+
packets_received += 1
161+
else:
162+
break
163+
return packets_received >= num_test_packets
164+
165+
if wait_until(timeout=timeout, interval=1, delay=1, condition=check_packets_received):
166+
logger.info("Found egress member: {} (PTF port {})".format(member_name, member_ptf_port))
167+
ptf_adapter.dataplane.flush()
168+
return member_name, member_ptf_port
169+
170+
# No working member found
171+
ptf_adapter.dataplane.flush()
172+
return None, None
173+
31174
@pytest.fixture(scope="module")
32175
def downstream_links(rand_selected_dut, tbinfo, nbrhosts):
33176
"""

tests/qos/files/strict_priority_params.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
"queue": 0,
44
"cir_bytes_per_sec": 500000,
55
"pir_bytes_per_sec": 750000,
6-
"traffic_duration": 100,
6+
"traffic_duration": 120,
77
"low_traffic_bytes_per_sec": 500000,
88
"high_traffic_bytes_per_sec": 1500000,
99
"drop_threshold_low": 1000,
1010
"drop_threshold_high": 10000,
1111
"bandwidth_tolerance_min": 90,
1212
"scheduler_policy": "strict_priority_rate_limit_policy",
13-
"packet_size": 1000
13+
"packet_size": 1000,
14+
"dnx_credit_worth": 10000
1415
}
1516
}

tests/qos/qos_helpers.py

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -135,34 +135,6 @@ def get_dscp_to_queue_mapping(duthost):
135135
return None
136136

137137

138-
def find_dscp_for_queue(duthost, target_queue):
139-
"""
140-
Find a DSCP value that maps to the target queue
141-
142-
Args:
143-
duthost: DUT host object
144-
target_queue: Target queue number
145-
146-
Returns:
147-
int or None: DSCP value that maps to target queue, or None if not found
148-
"""
149-
# Get DSCP to queue mapping from DUT
150-
dscp_to_queue_map = get_dscp_to_queue_mapping(duthost)
151-
if dscp_to_queue_map is None:
152-
logger.error("Could not get DSCP to queue mapping from DUT")
153-
return None
154-
155-
for dscp, queue in dscp_to_queue_map.items():
156-
if queue == target_queue:
157-
logger.info(f"Found DSCP {dscp} maps to target queue {target_queue}")
158-
return dscp
159-
160-
# If no exact match found, log available mappings and return None
161-
available_mappings = {f"DSCP {dscp}": f"Queue {queue}" for dscp, queue in dscp_to_queue_map.items()}
162-
logger.error(f"No DSCP found that maps to queue {target_queue}. Available mappings: {available_mappings}")
163-
return None
164-
165-
166138
def get_phy_intfs(host_ans):
167139
"""
168140
@Summary: Get the physical interfaces (e.g., EthernetX) of a DUT

0 commit comments

Comments
 (0)