Skip to content

Commit ef23dd9

Browse files
authored
[202405] New test case for OQ watchdog (#433)
Double commit sonic-net/sonic-mgmt#18937
2 parents b844792 + cf2735b commit ef23dd9

File tree

5 files changed

+465
-1
lines changed

5 files changed

+465
-1
lines changed

tests/common/cisco_data.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22
import re
33
from tests.common.reboot import reboot
4+
from tests.common.utilities import wait_until
45

56

67
def is_cisco_device(dut):
@@ -97,3 +98,18 @@ def set_voq_watchdog(enable):
9798
'''.format(enable)
9899

99100
copy_dshell_script_cisco_8000(dut, asic, dshell_script, script_name="set_voq_watchdog.py")
101+
102+
103+
def check_dshell_ready(duthost):
104+
show_command = "sudo show platform npu rx cgm_global"
105+
err_msg = "debug shell server for asic 0 is not running"
106+
output = duthost.command(show_command)['stdout']
107+
if err_msg in output:
108+
return False
109+
return True
110+
111+
112+
def run_dshell_command(duthost, command):
113+
if not wait_until(300, 20, 0, check_dshell_ready, duthost):
114+
raise RuntimeError("Debug shell is not ready on {}".format(duthost.hostname))
115+
return duthost.shell(command)

tests/common/plugins/conditional_mark/tests_mark_conditions.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,12 @@ qos/test_buffer_traditional.py:
13431343
- "release not in ['201911']"
13441344
- "topo_type in ['m0', 'mx']"
13451345

1346+
qos/test_oq_watchdog.py:
1347+
skip:
1348+
reason: "OQ watchdog tests only apply to cisco 8000 platforms."
1349+
conditions:
1350+
- "asic_type not in ['cisco-8000']"
1351+
13461352
qos/test_pfc_pause.py::test_pfc_pause_lossless:
13471353
# For this test, we use the fanout connected to the DUT to send PFC pause frames.
13481354
# The fanout needs to send PFC frames fast enough so that the queue remains completely paused for the entire duration

tests/qos/qos_sai_base.py

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from tests.common.helpers.multi_thread_utils import SafeThreadPoolExecutor
1818
from tests.common.mellanox_data import is_mellanox_device as isMellanoxDevice
1919
from tests.common.cisco_data import is_cisco_device, copy_set_voq_watchdog_script_cisco_8000, \
20-
copy_dshell_script_cisco_8000
20+
copy_dshell_script_cisco_8000, run_dshell_command
2121
from tests.common.dualtor.dual_tor_utils import upper_tor_host, lower_tor_host, dualtor_ports, is_tunnel_qos_remap_enabled # noqa F401
2222
from tests.common.dualtor.mux_simulator_control \
2323
import toggle_all_simulator_ports, get_mux_status, check_mux_status, validate_check_result # noqa F401
@@ -2783,3 +2783,84 @@ def disable_voq_watchdog(self, duthosts, get_src_dst_asic_and_duts, dutConfig):
27832783
dut.shell("sudo show platform npu script {} -s set_voq_watchdog.py".format(cmd_opt))
27842784

27852785
return
2786+
2787+
def get_port_channel_members(self, dut, port_name):
2788+
"""
2789+
Get the members of portchannel on the given port.
2790+
Args:
2791+
dut (AnsibleHost): Device Under Test (DUT)
2792+
port_name (str): Name of the port to check
2793+
Returns:
2794+
list: List of port names that are members of the same portchannel.
2795+
"""
2796+
interfaces = [port_name]
2797+
mgfacts = dut.get_extended_minigraph_facts()
2798+
for portchan in mgfacts['minigraph_portchannels']:
2799+
members = mgfacts['minigraph_portchannels'][portchan]['members']
2800+
if port_name in members:
2801+
logger.info("Interface {} is a member of portchannel {}, setting interface list to members {}".format(
2802+
port_name, portchan, members))
2803+
interfaces = members
2804+
break
2805+
return interfaces
2806+
2807+
def oq_watchdog_enabled(self, get_src_dst_asic_and_duts):
2808+
dst_dut = get_src_dst_asic_and_duts['dst_dut']
2809+
if not is_cisco_device(dst_dut):
2810+
return False
2811+
namespace_option = "-n asic0" if dst_dut.facts.get("modular_chassis") else ""
2812+
show_command = "show platform npu global {}".format(namespace_option)
2813+
result = run_dshell_command(dst_dut, show_command)
2814+
pattern = r"gb_watchdog_oq_enabled +: +True"
2815+
match = re.search(pattern, result["stdout"])
2816+
return match
2817+
2818+
def copy_set_queue_pir_script_cisco_8000(self, dut, ports, queue, speed, schduler_type, asic=""):
2819+
dshell_script = '''
2820+
from common import *
2821+
def set_queue_pir(interface, queue, rate):
2822+
sai_lane = port_to_sai_lane_map[interface]
2823+
slice_id, ifg_id, serdes_id = sai_lane_to_slice_ifg_serdes(sai_lane)
2824+
sys_port = find_system_port(slice_id, ifg_id, serdes_id)
2825+
scheduler = sys_port.get_scheduler()
2826+
original_pir = scheduler.get_{}_pir(queue)
2827+
print(original_pir)
2828+
scheduler.set_{}_pir(queue, rate)
2829+
'''.format(schduler_type, schduler_type)
2830+
2831+
for intf in ports:
2832+
dshell_script += 'set_queue_pir("{}", {}, {})\n'.format(intf, queue, speed)
2833+
2834+
copy_dshell_script_cisco_8000(dut, asic, dshell_script, script_name="set_queue_pir.py")
2835+
2836+
def block_queue(self, dut, port, queue, queue_type, asic_index=""):
2837+
interfaces = self.get_port_channel_members(dut, port)
2838+
scheduler_type = "credit" if queue_type == "voq" else "transmit"
2839+
self.copy_set_queue_pir_script_cisco_8000(
2840+
dut=dut,
2841+
ports=interfaces,
2842+
queue=queue,
2843+
asic=asic_index,
2844+
speed=0,
2845+
schduler_type=scheduler_type)
2846+
cmd_opt = "-n asic{}".format(asic_index)
2847+
if not dut.sonichost.is_multi_asic:
2848+
cmd_opt = ""
2849+
result = dut.shell("sudo show platform npu script {} -s set_queue_pir.py".format(cmd_opt))
2850+
original_pir = result["stdout_lines"][0]
2851+
return int(original_pir)
2852+
2853+
def unblock_queue(self, dut, port, queue, queue_type, speed, asic_index=""):
2854+
interfaces = self.get_port_channel_members(dut, port)
2855+
scheduler_type = "credit" if queue_type == "voq" else "transmit"
2856+
self.copy_set_queue_pir_script_cisco_8000(
2857+
dut=dut,
2858+
ports=interfaces,
2859+
queue=queue,
2860+
asic=asic_index,
2861+
speed=speed,
2862+
schduler_type=scheduler_type)
2863+
cmd_opt = "-n asic{}".format(asic_index)
2864+
if not dut.sonichost.is_multi_asic:
2865+
cmd_opt = ""
2866+
dut.shell("sudo show platform npu script {} -s set_queue_pir.py".format(cmd_opt))

tests/qos/test_oq_watchdog.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
"""SAI thrift-based tests for the OQ watchdog feature in SONiC.
2+
This set of test cases verifies OQ watchdog behavior. These are dataplane
3+
tests that depend on the SAI thrift library in order to pause ports and read
4+
drop counters.
5+
Parameters:
6+
--ptf_portmap <filename> (str): file name of port index to DUT interface alias map. Default is None.
7+
In case a filename is not provided, a file containing a port indices to aliases map will be generated.
8+
--qos_swap_syncd (bool): Used to install the RPC syncd image before running the tests. Default is True.
9+
--qos_dst_ports (list) Indices of available DUT test ports to serve as destination ports. Note: This is not port
10+
index on DUT, rather an index into filtered (excludes lag member ports) DUT ports. Plan is to randomize port
11+
selection. Default is [0, 1, 3].
12+
--qos_src_ports (list) Indices of available DUT test ports to serve as source port. Similar note as in
13+
qos_dst_ports applies. Default is [2].
14+
"""
15+
16+
import logging
17+
import pytest
18+
19+
from tests.common.fixtures.duthost_utils import dut_qos_maps, \
20+
separated_dscp_to_tc_map_on_uplink # noqa F401
21+
from tests.common.fixtures.ptfhost_utils import copy_ptftests_directory # noqa F401
22+
from tests.common.fixtures.ptfhost_utils import copy_saitests_directory # noqa F401
23+
from tests.common.fixtures.ptfhost_utils import change_mac_addresses # noqa F401
24+
from .qos_sai_base import QosSaiBase
25+
26+
logger = logging.getLogger(__name__)
27+
28+
pytestmark = [
29+
pytest.mark.topology('any')
30+
]
31+
32+
PKTS_NUM = 100
33+
34+
35+
@pytest.fixture(scope="function")
36+
def ignore_log_oq_watchdog(duthosts, loganalyzer):
37+
if not loganalyzer:
38+
yield
39+
return
40+
ignore_list = [r".*HARDWARE_WATCHDOG.*", r".*soft_reset*"]
41+
for dut in duthosts:
42+
for line in ignore_list:
43+
loganalyzer[dut.hostname].ignore_regex.append(line)
44+
yield
45+
return
46+
47+
48+
class TestOqWatchdog(QosSaiBase):
49+
"""TestVoqWatchdog derives from QosSaiBase and contains collection of OQ watchdog test cases.
50+
"""
51+
@pytest.fixture(scope="class", autouse=True)
52+
def check_skip_oq_watchdog_test(self, get_src_dst_asic_and_duts):
53+
if not self.oq_watchdog_enabled(get_src_dst_asic_and_duts):
54+
pytest.skip("OQ watchdog test is skipped since OQ watchdog is not enabled.")
55+
56+
def testOqWatchdog(
57+
self, ptfhost, dutTestParams, dutConfig, dutQosConfig,
58+
get_src_dst_asic_and_duts, ignore_log_oq_watchdog,
59+
disable_voq_watchdog_function_scope
60+
):
61+
"""
62+
Test OQ watchdog functionality.
63+
Test steps:
64+
1. block voq7, sys_port scheduler set Q7 credit_pir to 0
65+
2. fill leakout of Q7 by ping, make sure no packet dequeue/enqueue in OQ7 afterwards
66+
3. block oq0, sys_port scheduler set Q0 transmit_pir to 0
67+
4. send traffic on Q0, oq watchdog should be triggered in about 5 seconds
68+
5. Unblock voq7 and oq0 to restore the system state
69+
6. Run TrafficSanityTest to verify the system state is restored
70+
Args:
71+
ptfhost (AnsibleHost): Packet Test Framework (PTF)
72+
dutTestParams (Fixture, dict): DUT host test params
73+
dutConfig (Fixture, dict): Map of DUT config containing dut interfaces, test port IDs, test port IPs,
74+
and test ports
75+
dutQosConfig (Fixture, dict): Map containing DUT host QoS configuration
76+
Returns:
77+
None
78+
Raises:
79+
RunAnsibleModuleFail if ptf test fails
80+
"""
81+
82+
# Block voq7
83+
dst_dut = get_src_dst_asic_and_duts['dst_dut']
84+
dst_asic_index = get_src_dst_asic_and_duts['dst_asic_index']
85+
dst_port = dutConfig['dutInterfaces'][dutConfig["testPorts"]["dst_port_id"]]
86+
original_pir_voq7 = self.block_queue(dst_dut, dst_port, 7, "voq", dst_asic_index)
87+
# Fill leakout of Q7 by ping
88+
cmd_opt = "sudo ip netns exec asic{}".format(dst_asic_index)
89+
if not dst_dut.sonichost.is_multi_asic:
90+
cmd_opt = ""
91+
dst_dut.shell("{} ping -I {} -c 50 1.1.1.1 -i 0 -w 0 || true".format(cmd_opt, dst_port))
92+
93+
# Block oq0
94+
original_pir_oq0 = self.block_queue(dst_dut, dst_port, 0, "oq", dst_asic_index)
95+
96+
testParams = dict()
97+
testParams.update(dutTestParams["basicParams"])
98+
testParams.update({
99+
"dscp": 8,
100+
"dst_port_id": dutConfig["testPorts"]["dst_port_id"],
101+
"dst_port_ip": dutConfig["testPorts"]["dst_port_ip"],
102+
"src_port_id": dutConfig["testPorts"]["src_port_id"],
103+
"src_port_ip": dutConfig["testPorts"]["src_port_ip"],
104+
"src_port_vlan": dutConfig["testPorts"]["src_port_vlan"],
105+
"packet_size": 1350,
106+
"pkts_num": PKTS_NUM,
107+
"oq_watchdog_enabled": True,
108+
})
109+
110+
self.runPtfTest(
111+
ptfhost, testCase="sai_qos_tests.OqWatchdogTest",
112+
testParams=testParams)
113+
114+
# Unblock voq7 and oq0 to restore the system state
115+
self.unblock_queue(dst_dut, dst_port, 7, "voq", original_pir_voq7, dst_asic_index)
116+
self.unblock_queue(dst_dut, dst_port, 0, "oq", original_pir_oq0, dst_asic_index)
117+
118+
self.runPtfTest(
119+
ptfhost, testCase="sai_qos_tests.TrafficSanityTest",
120+
testParams=testParams)

0 commit comments

Comments
 (0)