diff --git a/tests/snappi_tests/pfc/files/pfc_congestion_helper.py b/tests/snappi_tests/pfc/files/pfc_congestion_helper.py new file mode 100644 index 00000000000..1e1cd190c1d --- /dev/null +++ b/tests/snappi_tests/pfc/files/pfc_congestion_helper.py @@ -0,0 +1,382 @@ +import logging +import time + +from tests.common.helpers.assertions import pytest_assert +from tests.common.fixtures.conn_graph_facts import conn_graph_facts,\ + fanout_graph_facts # noqa F401 +from tests.common.snappi_tests.common_helpers import pfc_class_enable_vector,\ + get_lossless_buffer_size, get_pg_dropped_packets,\ + stop_pfcwd, disable_packet_aging, sec_to_nanosec,\ + get_pfc_frame_count, packet_capture, config_capture_pkt,\ + start_pfcwd, enable_packet_aging, \ + traffic_flow_mode, calc_pfc_pause_flow_rate # noqa F401 +from tests.common.snappi_tests.port import select_ports, select_tx_port # noqa F401 +from tests.common.snappi_tests.snappi_helpers import wait_for_arp # noqa F401 +from tests.common.snappi_tests.traffic_generation import generate_pause_flows, verify_pause_flow, \ + verify_basic_test_flow, verify_background_flow, verify_pause_frame_count_dut, verify_egress_queue_frame_count, \ + verify_in_flight_buffer_pkts, verify_unset_cev_pause_frame_count, run_traffic_and_collect_stats, \ + multi_base_traffic_config, generate_test_flows, generate_background_flows +from tests.common.snappi_tests.snappi_test_params import SnappiTestParams +from tests.common.snappi_tests.read_pcap import validate_pfc_frame + +logger = logging.getLogger(__name__) + +dut_port_config = [] +PAUSE_FLOW_NAME = 'Pause Storm' +TEST_FLOW_NAME = 'Test Flow' +BG_FLOW_NAME = 'Background Flow' +TOLERANCE_THRESHOLD = 0.1 +CONTINUOUS_MODE = -5 +ANSIBLE_POLL_DELAY_SEC = 4 +global DATA_FLOW_DURATION_SEC +global data_flow_delay_sec + + +def run_pfc_test(api, + testbed_config, + port_config_list, + conn_data, + fanout_data, + global_pause, + pause_prio_list, + test_prio_list, + bg_prio_list, + prio_dscp_map, + test_traffic_pause, + test_def, + snappi_extra_params=None): + """ + Run a multidut PFC test + Args: + api (obj): snappi session + testbed_config (obj): testbed L1/L2/L3 configuration + port_config_list (list): list of port configuration + conn_data (dict): the dictionary returned by conn_graph_fact. + fanout_data (dict): the dictionary returned by fanout_graph_fact. + duthost (Ansible host instance): device under test + dut_port (str): DUT port to test + global_pause (bool): if pause frame is IEEE 802.3X pause + pause_prio_list (list): priorities to pause for pause frames + test_prio_list (list): priorities of test flows + bg_prio_list (list): priorities of background flows + prio_dscp_map (dict): Priority vs. DSCP map (key = priority). + test_traffic_pause (bool): if test flows are expected to be paused + test_def['enable_pause'] (bool) : if test expects no pause flow traffic. + snappi_extra_params (SnappiSysTestParams obj): additional parameters for Snappi traffic + + Returns: + N/A + """ + + TEST_FLOW_AGGR_RATE_PERCENT = test_def['TEST_FLOW_AGGR_RATE_PERCENT'] + BG_FLOW_AGGR_RATE_PERCENT = test_def['BG_FLOW_AGGR_RATE_PERCENT'] + data_flow_pkt_size = test_def['data_flow_pkt_size'] + DATA_FLOW_DURATION_SEC = test_def['DATA_FLOW_DURATION_SEC'] + data_flow_delay_sec = test_def['data_flow_delay_sec'] + SNAPPI_POLL_DELAY_SEC = test_def['SNAPPI_POLL_DELAY_SEC'] + PAUSE_FLOW_DUR_BASE_SEC = data_flow_delay_sec + DATA_FLOW_DURATION_SEC + if test_def['imix']: + fname = test_def['test_type'] + '_' + test_def['line_card_choice'] + '_' + 'IMIX' + else: + fname = test_def['test_type'] + '_' + test_def['line_card_choice'] + '_' + str(data_flow_pkt_size) + 'B' + port_map = test_def['port_map'] + + if snappi_extra_params is None: + snappi_extra_params = SnappiTestParams() + + # Traffic flow: + # tx_port (TGEN) --- ingress DUT --- egress DUT --- rx_port (TGEN) + + rx_port = snappi_extra_params.multi_dut_params.multi_dut_ports[0] + egress_duthost = rx_port['duthost'] + + tx_port = snappi_extra_params.multi_dut_params.multi_dut_ports[1] + ingress_duthost = tx_port['duthost'] + + dut_list = [] + dut_list.append(egress_duthost) + dut_list.append(ingress_duthost) + + if (test_traffic_pause): + logger.info("PFC receiving DUT is {}".format(egress_duthost.hostname)) + + pytest_assert(testbed_config is not None, 'Fail to get L2/3 testbed config') + + if (test_def['enable_pfcwd']): + start_pfcwd(egress_duthost) + start_pfcwd(ingress_duthost) + else: + stop_pfcwd(egress_duthost) + stop_pfcwd(ingress_duthost) + + if (test_def['enable_credit_wd']): + enable_packet_aging(egress_duthost, rx_port['asic_value']) + enable_packet_aging(ingress_duthost, tx_port['asic_value']) + else: + disable_packet_aging(egress_duthost, rx_port['asic_value']) + disable_packet_aging(ingress_duthost, tx_port['asic_value']) + + # Port id of Rx port for traffic config + # rx_port_id and tx_port_id belong to IXIA chassis. + rx_port_id = 0 + + # Rate percent must be an integer + bg_flow_rate_percent = int(BG_FLOW_AGGR_RATE_PERCENT / len(bg_prio_list)) + test_flow_rate_percent = int(TEST_FLOW_AGGR_RATE_PERCENT / len(test_prio_list)) + # Generate base traffic config + for i in range(port_map[2]): + tx_port_id = i+1 + snappi_extra_params.base_flow_config_list.append(multi_base_traffic_config(testbed_config=testbed_config, + port_config_list=port_config_list, + rx_port_id=rx_port_id, + tx_port_id=tx_port_id)) + + speed_str = testbed_config.layer1[0].speed + speed_gbps = int(speed_str.split('_')[1]) + + if snappi_extra_params.headroom_test_params is not None: + DATA_FLOW_DURATION_SEC += 10 + data_flow_delay_sec += 2 + + # Set up pfc delay parameter + l1_config = testbed_config.layer1[0] + pfc = l1_config.flow_control.ieee_802_1qbb + pfc.pfc_delay = snappi_extra_params.headroom_test_params[0] + + if snappi_extra_params.poll_device_runtime: + # If the switch needs to be polled as traffic is running for stats, + # then the test runtime needs to be increased for the polling delay + DATA_FLOW_DURATION_SEC += ANSIBLE_POLL_DELAY_SEC + data_flow_delay_sec = ANSIBLE_POLL_DELAY_SEC + + if snappi_extra_params.packet_capture_type != packet_capture.NO_CAPTURE: + # Setup capture config + if snappi_extra_params.is_snappi_ingress_port_cap: + # packet capture is required on the ingress snappi port + snappi_extra_params.packet_capture_ports = [snappi_extra_params.base_flow_config_list["rx_port_name"]] + else: + # packet capture will be on the egress snappi port + snappi_extra_params.packet_capture_ports = [snappi_extra_params.base_flow_config_list["tx_port_name"]] + + snappi_extra_params.packet_capture_file = snappi_extra_params.packet_capture_type.value + + config_capture_pkt(testbed_config=testbed_config, + port_names=snappi_extra_params.packet_capture_ports, + capture_type=snappi_extra_params.packet_capture_type, + capture_name=snappi_extra_params.packet_capture_file) + logger.info("Packet capture file: {}.pcapng".format(snappi_extra_params.packet_capture_file)) + + # Set default traffic flow configs if not set + if snappi_extra_params.traffic_flow_config.data_flow_config is None: + snappi_extra_params.traffic_flow_config.data_flow_config = { + "flow_name": TEST_FLOW_NAME, + "flow_dur_sec": DATA_FLOW_DURATION_SEC, + "flow_rate_percent": test_flow_rate_percent, + "flow_rate_pps": None, + "flow_rate_bps": None, + "flow_pkt_count": None, + "flow_pkt_size": data_flow_pkt_size, + "flow_delay_sec": data_flow_delay_sec, + "flow_traffic_type": traffic_flow_mode.FIXED_DURATION + } + + if snappi_extra_params.traffic_flow_config.background_flow_config is None and \ + snappi_extra_params.gen_background_traffic: + snappi_extra_params.traffic_flow_config.background_flow_config = { + "flow_name": BG_FLOW_NAME, + "flow_dur_sec": DATA_FLOW_DURATION_SEC, + "flow_rate_percent": bg_flow_rate_percent, + "flow_rate_pps": None, + "flow_rate_bps": None, + "flow_pkt_size": data_flow_pkt_size, + "flow_pkt_count": None, + "flow_delay_sec": data_flow_delay_sec, + "flow_traffic_type": traffic_flow_mode.FIXED_DURATION + } + + if (test_traffic_pause): + if snappi_extra_params.traffic_flow_config.pause_flow_config is None: + snappi_extra_params.traffic_flow_config.pause_flow_config = { + "flow_name": PAUSE_FLOW_NAME, + "flow_dur_sec": None, + "flow_rate_percent": None, + "flow_rate_pps": calc_pfc_pause_flow_rate(speed_gbps), + "flow_rate_bps": None, + "flow_pkt_size": 64, + "flow_pkt_count": None, + "flow_delay_sec": 0, + "flow_traffic_type": traffic_flow_mode.CONTINUOUS + } + + if snappi_extra_params.packet_capture_type == packet_capture.PFC_CAPTURE: + # PFC pause frame capture is requested + valid_pfc_frame_test = True + else: + # PFC pause frame capture is not requested + valid_pfc_frame_test = False + + if (test_traffic_pause): + if valid_pfc_frame_test: + snappi_extra_params.traffic_flow_config.pause_flow_config["flow_dur_sec"] = DATA_FLOW_DURATION_SEC + \ + data_flow_delay_sec + SNAPPI_POLL_DELAY_SEC + PAUSE_FLOW_DUR_BASE_SEC + snappi_extra_params.traffic_flow_config.pause_flow_config["flow_traffic_type"] = \ + traffic_flow_mode.FIXED_DURATION + + # Generate test flow config based on number of ingress ports + # Every ingress port will be used as index. Example - test flow stream 0 - for first ingress. + for m in range(port_map[2]): + generate_test_flows(testbed_config=testbed_config, + test_flow_prio_list=test_prio_list, + prio_dscp_map=prio_dscp_map, + snappi_extra_params=snappi_extra_params, + flow_index=m) + + if (test_def['background_traffic']): + for m in range(port_map[2]): + if snappi_extra_params.gen_background_traffic: + # Generate background flow config + generate_background_flows(testbed_config=testbed_config, + bg_flow_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + snappi_extra_params=snappi_extra_params, + flow_index=m) + + # Generate pause storm config + if (test_traffic_pause): + for m in range(port_map[0]): + generate_pause_flows(testbed_config=testbed_config, + pause_prio_list=pause_prio_list, + global_pause=global_pause, + snappi_extra_params=snappi_extra_params, + snap_index=m) + + flows = testbed_config.flows + + all_flow_names = [flow.name for flow in flows] + data_flow_names = [flow.name for flow in flows if PAUSE_FLOW_NAME not in flow.name] + + # Clear PFC, queue and interface counters before traffic run + for dut in dut_list: + dut.command("pfcstat -c \n") + time.sleep(1) + dut.command("sonic-clear queuecounters \n") + time.sleep(1) + dut.command("sonic-clear counters \n") + time.sleep(1) + + exp_dur_sec = DATA_FLOW_DURATION_SEC + data_flow_delay_sec + + """ Run traffic """ + tgen_flow_stats, switch_flow_stats, test_stats = \ + run_traffic_and_collect_stats(rx_duthost=ingress_duthost, + tx_duthost=egress_duthost, + api=api, + config=testbed_config, + data_flow_names=data_flow_names, + all_flow_names=all_flow_names, + exp_dur_sec=exp_dur_sec, + port_map=test_def['port_map'], + fname=fname, + stats_interval=test_def['stats_interval'], + imix=test_def['imix'], + snappi_extra_params=snappi_extra_params) + + test_check = test_def['test_check'] + if (not test_check['loss_expected']): + # Check for loss packets on IXIA and DUT. + pytest_assert(test_stats['tgen_loss_pkts'] == 0, 'Loss seen on TGEN') + pytest_assert(test_stats['dut_loss_pkts'] == 0, 'Loss seen on DUT') + + # Check for Tx and Rx packets on IXIA for lossless and lossy streams. + pytest_assert(test_stats['tgen_lossless_rx_pkts'] == test_stats['tgen_lossless_tx_pkts'], + 'Losses observed in lossless traffic streams') + pytest_assert(test_stats['tgen_lossy_rx_pkts'] == test_stats['tgen_lossy_tx_pkts'], + 'Losses observed in lossy traffic streams') + + # Check for Rx packets between IXIA and DUT for lossy and lossless streams. + pytest_assert(test_stats['tgen_lossless_rx_pkts'] == test_stats['dut_lossless_pkts'], + 'Losses observed in lossless traffic streams on DUT Tx and IXIA Rx') + pytest_assert(test_stats['tgen_lossy_rx_pkts'] == test_stats['dut_lossy_pkts'], + 'Losses observed in lossy traffic streams on DUT Tx and IXIA Rx') + else: + # Check for lossless and lossy stream percentage drop for a given tolerance limit. + lossless_drop = round((1 - float(test_stats['tgen_lossless_rx_pkts']) / test_stats['tgen_lossless_tx_pkts']), 2) + lossy_drop = round((1 - float(test_stats['tgen_lossy_rx_pkts']) / test_stats['tgen_lossy_tx_pkts']), 2) + logger.info('Lossless Drop %:{}, Lossy Drop %:{}'.format(lossless_drop, lossy_drop)) + pytest_assert((lossless_drop*100) <= test_check['lossless'], 'Lossless packet drop outside tolerance limit') + pytest_assert((lossy_drop*100) <= test_check['lossy'], 'Lossy packet drop outside tolerance limit') + + # Checking if the actual line rate on egress is within tolerable limit of egress line speed. + pytest_assert(((1 - test_stats['tgen_rx_rate'] / float(port_map[0]*port_map[1]))*100) <= test_check['speed_tol'], + 'Egress speed beyond tolerance range') + + # Checking for PFC counts on DUT + if (not test_check['pfc']): + pytest_assert(test_stats['lossless_tx_pfc'] == 0, 'Error:PFC transmitted by DUT for lossless priorities') + pytest_assert(test_stats['lossy_rx_tx_pfc'] == 0, 'Error:PFC transmitted by DUT for lossy priorities') + else: + if (test_stats['lossless_rx_pfc'] != 0): + pytest_assert(test_stats['lossless_tx_pfc'] > 0, 'Error:No Tx PFCs from DUT after receiving PFCs') + pytest_assert(test_stats['lossless_tx_pfc'] > 0, 'Error: PFC not be transmitted from DUT on congestion') + pytest_assert(test_stats['lossy_rx_tx_pfc'] == 0, 'Error:Incorrect Rx/Tx PFCs on DUT for lossy priorities') + + # Reset pfc delay parameter + pfc = testbed_config.layer1[0].flow_control.ieee_802_1qbb + pfc.pfc_delay = 0 + + # Verify PFC pause frames + if (test_traffic_pause): + if valid_pfc_frame_test: + is_valid_pfc_frame = validate_pfc_frame(snappi_extra_params.packet_capture_file + ".pcapng") + pytest_assert(is_valid_pfc_frame, "PFC frames invalid") + return + + # Verify pause flows + if (test_traffic_pause): + for metric in tgen_flow_stats: + if PAUSE_FLOW_NAME in metric.name: + pause_flow_name = metric.name + verify_pause_flow(flow_metrics=tgen_flow_stats, + pause_flow_name=pause_flow_name) + + # Check for the flows ONLY if normal packet size (non-imix) is used. + if (test_def['background_traffic'] and test_def['verify_flows'] and not test_def['imix']): + if snappi_extra_params.gen_background_traffic: + # Verify background flows + verify_background_flow(flow_metrics=tgen_flow_stats, + speed_gbps=speed_gbps, + tolerance=TOLERANCE_THRESHOLD, + snappi_extra_params=snappi_extra_params) + + # Verify basic test flows metrics from ixia + if (test_def['verify_flows'] and not test_def['imix']): + verify_basic_test_flow(flow_metrics=tgen_flow_stats, + speed_gbps=speed_gbps, + tolerance=TOLERANCE_THRESHOLD, + test_flow_pause=test_traffic_pause, + snappi_extra_params=snappi_extra_params) + + if (test_traffic_pause and test_def['verify_flows']): + verify_pause_frame_count_dut(rx_dut=ingress_duthost, + tx_dut=egress_duthost, + test_traffic_pause=test_traffic_pause, + global_pause=global_pause, + snappi_extra_params=snappi_extra_params) + + # Verify in flight TX lossless packets do not leave the DUT when traffic is expected + # to be paused, or leave the DUT when the traffic is not expected to be paused + verify_egress_queue_frame_count(duthost=egress_duthost, + switch_flow_stats=switch_flow_stats, + test_traffic_pause=test_traffic_pause, + snappi_extra_params=snappi_extra_params) + + if (test_traffic_pause and test_def['verify_flows']): + # Verify in flight TX packets count relative to switch buffer size + verify_in_flight_buffer_pkts(duthost=ingress_duthost, + asic_value=rx_port['asic_value'], + flow_metrics=tgen_flow_stats, + snappi_extra_params=snappi_extra_params) + + # Verify zero pause frames are counted when the PFC class enable vector is not set + verify_unset_cev_pause_frame_count(duthost=egress_duthost, + snappi_extra_params=snappi_extra_params) diff --git a/tests/snappi_tests/pfc/test_pfc_no_congestion_throughput.py b/tests/snappi_tests/pfc/test_pfc_no_congestion_throughput.py new file mode 100644 index 00000000000..90901ca5d03 --- /dev/null +++ b/tests/snappi_tests/pfc/test_pfc_no_congestion_throughput.py @@ -0,0 +1,445 @@ +import pytest +import random +from tests.common.helpers.assertions import pytest_require, pytest_assert # noqa: F401 +from tests.common.fixtures.conn_graph_facts import conn_graph_facts, fanout_graph_facts_multidut # noqa: F401 +from tests.common.snappi_tests.snappi_fixtures import snappi_api_serv_ip, snappi_api_serv_port, \ + snappi_api, cleanup_config, get_snappi_ports_for_rdma, snappi_multi_base_config, \ + get_snappi_ports, get_snappi_ports_multi_dut, clear_fabric_counters, check_fabric_counters # noqa: F401 +from tests.common.snappi_tests.qos_fixtures import prio_dscp_map, lossless_prio_list, \ + lossy_prio_list, all_prio_list # noqa: F401 +from tests.snappi_tests.pfc.files.pfc_congestion_helper import run_pfc_test +from tests.common.snappi_tests.snappi_test_params import SnappiTestParams +from tests.snappi_tests.variables import MULTIDUT_PORT_INFO, MULTIDUT_TESTBED + +import logging +logger = logging.getLogger(__name__) + +pytestmark = [pytest.mark.topology('multidut-tgen')] + +port_map = [[1, 100, 1, 100]] + +# Testplan: docs/testplan/PFC_Snappi_Additional_Testcases.md +# This test-script covers testcase#01-non-congestion(normal). + + +@pytest.mark.parametrize('port_map', port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_multiple_prio_diff_dist(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + + """ + Purpose of the test is to check if line-rate can be achieved. + Traffic distribution is 88% lossless priority 3 and 4 traffic. + There is additional 12% of lossy priority 0, 1 and 2 traffic. + PFCWD and Credit-watchdog is enabled. + Packet-size is 1024. IMIX can be enabled by setting imix to True. + No losses should be seen for both lossy and lossless traffic. + No PFCs should be generated during the test. + + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + Returns: + N/A + + """ + + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + # pkt_size of 1024B will be used unless imix flag is set. + # With imix flag set, the traffic_generation.py uses IMIX profile. + pkt_size = 1024 + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + test_check = {'lossless': 0, 'lossy': 0, 'speed_tol': 3, 'loss_expected': False, 'pfc': False} + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 88, + 'BG_FLOW_AGGR_RATE_PERCENT': 12, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 0, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/Single_Ingress_Egress_diff_dist_'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd': True, + 'enable_credit_wd': True, + 'stats_interval': 60, + 'background_traffic': True, + 'imix': False, + 'test_check': test_check, + 'verify_flows': True} + + test_prio_list = lossless_prio_list + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 3) + logger.info('Selected lossless :{} and lossy priorities:{} for the test'.format(test_prio_list, bg_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=False, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + for dut in duthosts: + check_fabric_counters(dut) + + finally: + cleanup_config(dut_list, snappi_ports) + + +@pytest.mark.parametrize('port_map', port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_multiple_prio_uni_dist(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + + """ + Purpose of the test is to check if line-rate can be achieved. + Traffic distribution is 40% lossless priority 3 and 4 traffic. + There is additional 60% of lossy priority 0, 1 and 2 traffic. + PFCWD and Credit-watchdog is enabled. + Packet-size is 1024. IMIX can be enabled by setting imix to True. + No losses should be seen for both lossy and lossless traffic. + No PFCs should be generated during the test. + + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + + Returns: + N/A + """ + + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + # pkt_size of 1024B will be used unless imix flag is set. + # With imix flag set, the traffic_generation.py uses IMIX profile. + pkt_size = 1024 + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + test_check = {'lossless': 0, 'lossy': 0, 'speed_tol': 3, 'loss_expected': False, 'pfc': False} + + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 40, + 'BG_FLOW_AGGR_RATE_PERCENT': 60, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 0, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/Single_Ingress_Egress_uni_dist_'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd': True, + 'enable_credit_wd': True, + 'stats_interval': 60, + 'background_traffic': True, + 'imix': False, + 'test_check': test_check, + 'verify_flows': True} + + test_prio_list = lossless_prio_list + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 3) + logger.info('Selected lossless :{} and lossy priorities:{} for the test'.format(test_prio_list, bg_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=False, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + for dut in duthosts: + check_fabric_counters(dut) + + finally: + cleanup_config(dut_list, snappi_ports) + + +@pytest.mark.parametrize('port_map', port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_single_lossless_prio(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + + """ + Purpose of the test is to check if line-rate can be achieved with single priority traffic. + Traffic distribution is 100% lossless priority 3 traffic. + PFCWD and Credit-watchdog is enabled. + Packet-size is 1024. IMIX can be enabled by setting imix to True. + No losses should be seen for both lossy and lossless traffic. + No PFCs should be generated during the test. + + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + + Returns: + N/A + """ + + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + # pkt_size of 1024B will be used unless imix flag is set. + # With imix flag set, the traffic_generation.py uses IMIX profile. + pkt_size = 1024 + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + # background_traffic is set to False. + # test_flow_aggregate_rate_percent is set to 100% to ensure all to be single lossless priority traffic. + test_check = {'lossless': 0, 'lossy': 0, 'speed_tol': 3, 'loss_expected': False, 'pfc': False} + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 100, + 'BG_FLOW_AGGR_RATE_PERCENT': 50, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 0, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/Single_Ingress_Egress_1Prio_linerate_'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd': True, + 'enable_credit_wd': True, + 'stats_interval': 60, + 'background_traffic': False, + 'imix': False, + 'test_check': test_check, + 'verify_flows': True} + + # Selecting only one lossless priority for the test. + test_prio_list = random.sample(lossless_prio_list, 1) + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 3) + logger.info('Selected lossless priority:{} for the test'.format(test_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=False, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + for dut in duthosts: + check_fabric_counters(dut) + + finally: + cleanup_config(dut_list, snappi_ports) diff --git a/tests/snappi_tests/pfc/test_pfc_port_congestion.py b/tests/snappi_tests/pfc/test_pfc_port_congestion.py new file mode 100644 index 00000000000..c397464a7f6 --- /dev/null +++ b/tests/snappi_tests/pfc/test_pfc_port_congestion.py @@ -0,0 +1,570 @@ +import pytest +import random +from tests.common.helpers.assertions import pytest_require, pytest_assert # noqa: F401 +from tests.common.fixtures.conn_graph_facts import conn_graph_facts, fanout_graph_facts_multidut # noqa: F401 +from tests.common.snappi_tests.snappi_fixtures import snappi_api_serv_ip, snappi_api_serv_port, \ + snappi_api, snappi_multi_base_config, cleanup_config, get_snappi_ports_for_rdma, \ + get_snappi_ports, get_snappi_ports_multi_dut, clear_fabric_counters, check_fabric_counters # noqa: F401 +from tests.common.snappi_tests.qos_fixtures import prio_dscp_map, lossless_prio_list, \ + lossy_prio_list, all_prio_list # noqa: F401 +from tests.snappi_tests.variables import MULTIDUT_PORT_INFO, MULTIDUT_TESTBED +from tests.snappi_tests.pfc.files.pfc_congestion_helper import run_pfc_test +from tests.common.snappi_tests.snappi_test_params import SnappiTestParams + +import logging +logger = logging.getLogger(__name__) + +pytestmark = [pytest.mark.topology('multidut-tgen')] + +port_map = [[1, 100, 2, 100], [1, 400, 2, 400]] + +# Testplan: docs/testplan/PFC_Snappi_Additional_Testcases.md +# This test-script covers following testcases: +# testcase#04: DETECT CONGESTION WITH REAL-LIFE TRAFFIC PATTERN - 90% LOSSLESS and 10% LOSSY +# testcase#05: DETECT CONGESTION WITH EQUAL DISTRIBUTION OF LOSSLESS AND LOSSY TRAFFIC + + +@pytest.mark.parametrize('port_map', port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_multiple_prio_diff_dist(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + + """ + Purpose of the test case is to test oversubscription with two ingresses and single ingress. + Traffic pattern has 90% lossless priority and 10% lossy priority traffic. + No losses for both lossless and lossy priority traffic. + + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + Returns: + N/A + + """ + + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + # pkt_size of 1024B will be used unless imix flag is set. + # With imix flag set, the traffic_generation.py uses IMIX profile. + pkt_size = 1024 + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + test_check = {'lossless': 0, 'lossy': 0, 'speed_tol': 3, 'loss_expected': False, 'pfc': True} + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 88, + 'BG_FLOW_AGGR_RATE_PERCENT': 12, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 0, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/Two_Ingress_Single_Egress_diff_dist_'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd': True, + 'enable_credit_wd': True, + 'stats_interval': 60, + 'background_traffic': True, + 'imix': False, + 'test_check': test_check, + 'verify_flows': False} + + test_prio_list = lossless_prio_list + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 3) + logger.info('Selected lossless :{} and lossy priorities:{} for the test'.format(test_prio_list, bg_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=False, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + for dut in duthosts: + check_fabric_counters(dut) + + finally: + cleanup_config(dut_list, snappi_ports) + + +@pytest.mark.parametrize('port_map', port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_multiple_prio_uni_dist(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + """ + Purpose of the test case is to test oversubscription with two ingresses and single ingress. + Traffic pattern has 24% lossless priority and 36% lossy priority traffic. + Each priority carries equal 12% of traffic. + No losses for lossless priority traffic. Some loss expected for lossy priority traffic. + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + + Returns: + N/A + """ + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + # pkt_size of 1024B will be used unless imix flag is set. + # With imix flag set, the traffic_generation.py uses IMIX profile. + pkt_size = 1024 + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + test_check = {'lossless': 0, 'lossy': 51, 'speed_tol': 3, 'loss_expected': True, 'pfc': True} + + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 40, + 'BG_FLOW_AGGR_RATE_PERCENT': 60, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 0, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/Two_Ingress_Single_Egress_uni_dist_full'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd': True, + 'enable_credit_wd': True, + 'stats_interval': 60, + 'background_traffic': True, + 'imix': False, + 'test_check': test_check, + 'verify_flows': False} + + test_prio_list = lossless_prio_list + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 3) + logger.info('Selected lossless :{} and lossy priorities:{} for the test'.format(test_prio_list, bg_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=False, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + for dut in duthosts: + check_fabric_counters(dut) + + finally: + cleanup_config(dut_list, snappi_ports) + + +@pytest.mark.parametrize('port_map', port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_multiple_prio_equal_dist(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + + """ + Purpose of the test case is to test oversubscription with two ingresses and single ingress. + Traffic pattern has 24% lossless priority and 36% lossy priority traffic. + Each priority carries equal 12% of traffic. + No losses for lossless priority traffic. Some loss expected for lossy priority traffic. + + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + + Returns: + N/A + """ + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + # pkt_size of 1024B will be used unless imix flag is set. + # With imix flag set, the traffic_generation.py uses IMIX profile. + pkt_size = 1024 + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + test_check = {'lossless': 0, 'lossy': 20, 'speed_tol': 3, 'loss_expected': True, 'pfc': True} + + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 30, + 'BG_FLOW_AGGR_RATE_PERCENT': 30, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 0, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/Two_Ingress_Single_Egress_equal_dist'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd': True, + 'enable_credit_wd': True, + 'stats_interval': 60, + 'background_traffic': True, + 'imix': False, + 'test_check': test_check, + 'verify_flows': False} + + test_prio_list = lossless_prio_list + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 2) + logger.info('Selected lossless :{} and lossy priorities:{} for the test'.format(test_prio_list, bg_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=False, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + for dut in duthosts: + check_fabric_counters(dut) + + finally: + cleanup_config(dut_list, snappi_ports) + + +@pytest.mark.parametrize('port_map', port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_multiple_prio_non_cngtn(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + + """ + Purpose of the test case is to test oversubscription with two ingresses and single ingress. + Traffic pattern has 18% lossless priority and 27% lossy priority traffic. + Total ingress link is sending only 45% link capacity and hence egress will not be congested. + No losses for both lossless and lossy priority traffic. + + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + Returns: + N/A + + """ + + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + # pkt_size of 1024B will be used unless imix flag is set. + # With imix flag set, the traffic_generation.py uses IMIX profile. + pkt_size = 1024 + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + test_check = {'lossless': 0, 'lossy': 0, 'speed_tol': 14, 'loss_expected': False, 'pfc': False} + + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 18, + 'BG_FLOW_AGGR_RATE_PERCENT': 27, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 0, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/Two_Ingress_Single_Egress_non_cngstn_'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd': True, + 'enable_credit_wd': True, + 'stats_interval': 60, + 'background_traffic': True, + 'imix': False, + 'test_check': test_check, + 'verify_flows': False} + + test_prio_list = lossless_prio_list + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 3) + logger.info('Selected lossless :{} and lossy priorities:{} for the test'.format(test_prio_list, bg_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=False, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + for dut in duthosts: + check_fabric_counters(dut) + + finally: + cleanup_config(dut_list, snappi_ports) diff --git a/tests/snappi_tests/pfcwd/files/pfcwd_actions_helper.py b/tests/snappi_tests/pfcwd/files/pfcwd_actions_helper.py new file mode 100644 index 00000000000..1c1e7a44b3e --- /dev/null +++ b/tests/snappi_tests/pfcwd/files/pfcwd_actions_helper.py @@ -0,0 +1,387 @@ +import logging +import time + +from tests.common.helpers.assertions import pytest_assert +from tests.common.fixtures.conn_graph_facts import conn_graph_facts,\ + fanout_graph_facts # noqa F401 +from tests.common.snappi_tests.common_helpers import pfc_class_enable_vector,\ + get_lossless_buffer_size, get_pg_dropped_packets,\ + stop_pfcwd, disable_packet_aging, sec_to_nanosec,\ + get_pfc_frame_count, packet_capture, config_capture_pkt,\ + start_pfcwd, enable_packet_aging, start_pfcwd_fwd, \ + traffic_flow_mode, calc_pfc_pause_flow_rate # noqa F401 +from tests.common.snappi_tests.port import select_ports, select_tx_port # noqa F401 +from tests.common.snappi_tests.snappi_helpers import wait_for_arp # noqa F401 +from tests.common.snappi_tests.traffic_generation import verify_pause_flow, \ + verify_basic_test_flow, verify_background_flow, verify_pause_frame_count_dut, \ + run_traffic_and_collect_stats, multi_base_traffic_config, verify_egress_queue_frame_count, \ + generate_test_flows, generate_background_flows, generate_pause_flows +from tests.common.snappi_tests.snappi_test_params import SnappiTestParams + + +logger = logging.getLogger(__name__) + +dut_port_config = [] +PAUSE_FLOW_NAME = 'Pause Storm' +TEST_FLOW_NAME = 'Test Flow' +BG_FLOW_NAME = 'Background Flow' +TOLERANCE_THRESHOLD = 0.1 +CONTINUOUS_MODE = -5 +ANSIBLE_POLL_DELAY_SEC = 4 +global DATA_FLOW_DURATION_SEC +global data_flow_delay_sec + + +def run_pfc_test(api, + testbed_config, + port_config_list, + conn_data, + fanout_data, + global_pause, + pause_prio_list, + test_prio_list, + bg_prio_list, + prio_dscp_map, + test_traffic_pause, + test_def, + snappi_extra_params=None): + """ + Run a multidut PFC test + Args: + api (obj): snappi session + testbed_config (obj): testbed L1/L2/L3 configuration + port_config_list (list): list of port configuration + conn_data (dict): the dictionary returned by conn_graph_fact. + fanout_data (dict): the dictionary returned by fanout_graph_fact. + duthost (Ansible host instance): device under test + dut_port (str): DUT port to test + global_pause (bool): if pause frame is IEEE 802.3X pause + pause_prio_list (list): priorities to pause for pause frames + test_prio_list (list): priorities of test flows + bg_prio_list (list): priorities of background flows + prio_dscp_map (dict): Priority vs. DSCP map (key = priority). + test_traffic_pause (bool): if test flows are expected to be paused + test_def['enable_pause'] (bool) : if test expects no pause flow traffic. + snappi_extra_params (SnappiTestParams obj): additional parameters for Snappi traffic + + Returns: + N/A + """ + + TEST_FLOW_AGGR_RATE_PERCENT = test_def['TEST_FLOW_AGGR_RATE_PERCENT'] + BG_FLOW_AGGR_RATE_PERCENT = test_def['BG_FLOW_AGGR_RATE_PERCENT'] + data_flow_pkt_size = test_def['data_flow_pkt_size'] + DATA_FLOW_DURATION_SEC = test_def['DATA_FLOW_DURATION_SEC'] + data_flow_delay_sec = test_def['data_flow_delay_sec'] + SNAPPI_POLL_DELAY_SEC = test_def['SNAPPI_POLL_DELAY_SEC'] + PAUSE_FLOW_DUR_BASE_SEC = data_flow_delay_sec + DATA_FLOW_DURATION_SEC + if test_def['imix']: + fname = test_def['test_type'] + '_' + test_def['line_card_choice'] + '_' + 'IMIX' + else: + fname = test_def['test_type'] + '_' + test_def['line_card_choice'] + '_' + str(data_flow_pkt_size) + 'B' + + port_map = test_def['port_map'] + + if snappi_extra_params is None: + snappi_extra_params = SnappiTestParams() + + # Traffic flow: + # tx_port (TGEN) --- ingress DUT --- egress DUT --- rx_port (TGEN) + + rx_port = snappi_extra_params.multi_dut_params.multi_dut_ports[0] + egress_duthost = rx_port['duthost'] + + tx_port = snappi_extra_params.multi_dut_params.multi_dut_ports[-1] + ingress_duthost = tx_port['duthost'] + dut_list = [egress_duthost, ingress_duthost] + + if (test_traffic_pause): + logger.info("PFC receiving DUT is {}".format(egress_duthost.hostname)) + + pytest_assert(testbed_config is not None, 'Fail to get L2/3 testbed config') + + if (test_def['enable_pfcwd_drop']): + start_pfcwd(egress_duthost) + start_pfcwd(ingress_duthost) + elif (test_def['enable_pfcwd_fwd']): + start_pfcwd_fwd(egress_duthost) + start_pfcwd_fwd(ingress_duthost) + else: + stop_pfcwd(egress_duthost) + stop_pfcwd(ingress_duthost) + + if (test_def['enable_credit_wd']): + enable_packet_aging(egress_duthost, rx_port['asic_value']) + enable_packet_aging(ingress_duthost, tx_port['asic_value']) + else: + disable_packet_aging(egress_duthost, rx_port['asic_value']) + disable_packet_aging(ingress_duthost, tx_port['asic_value']) + + rx_port_id = 0 + + # Rate percent must be an integer + bg_flow_rate_percent = int(BG_FLOW_AGGR_RATE_PERCENT / len(bg_prio_list)) + test_flow_rate_percent = int(TEST_FLOW_AGGR_RATE_PERCENT / len(test_prio_list)) + # Generate base traffic config + if (port_map[0] == 2): + for i in range(port_map[0]): + rx_port_id = i + tx_port_id = 2 + snappi_extra_params.base_flow_config_list.append( + multi_base_traffic_config(testbed_config=testbed_config, + port_config_list=port_config_list, + rx_port_id=rx_port_id, + tx_port_id=tx_port_id + ) + ) + else: + rx_port_id = 0 + for i in range(port_map[2]): + tx_port_id = i+1 + snappi_extra_params.base_flow_config_list.append( + multi_base_traffic_config(testbed_config=testbed_config, + port_config_list=port_config_list, + rx_port_id=rx_port_id, + tx_port_id=tx_port_id + ) + ) + + speed_str = testbed_config.layer1[0].speed + speed_gbps = int(speed_str.split('_')[1]) + + if snappi_extra_params.headroom_test_params is not None: + DATA_FLOW_DURATION_SEC += 10 + data_flow_delay_sec += 2 + + # Set up pfc delay parameter + l1_config = testbed_config.layer1[0] + pfc = l1_config.flow_control.ieee_802_1qbb + pfc.pfc_delay = snappi_extra_params.headroom_test_params[0] + + if snappi_extra_params.poll_device_runtime: + # If the switch needs to be polled as traffic is running for stats, + # then the test runtime needs to be increased for the polling delay + DATA_FLOW_DURATION_SEC += ANSIBLE_POLL_DELAY_SEC + data_flow_delay_sec = ANSIBLE_POLL_DELAY_SEC + + if snappi_extra_params.packet_capture_type != packet_capture.NO_CAPTURE: + # Setup capture config + if snappi_extra_params.is_snappi_ingress_port_cap: + # packet capture is required on the ingress snappi port + snappi_extra_params.packet_capture_ports = [snappi_extra_params.base_flow_config_list["rx_port_name"]] + else: + # packet capture will be on the egress snappi port + snappi_extra_params.packet_capture_ports = [snappi_extra_params.base_flow_config_list["tx_port_name"]] + + snappi_extra_params.packet_capture_file = snappi_extra_params.packet_capture_type.value + + config_capture_pkt(testbed_config=testbed_config, + port_names=snappi_extra_params.packet_capture_ports, + capture_type=snappi_extra_params.packet_capture_type, + capture_name=snappi_extra_params.packet_capture_file) + logger.info("Packet capture file: {}.pcapng".format(snappi_extra_params.packet_capture_file)) + + # Set default traffic flow configs if not set + if snappi_extra_params.traffic_flow_config.data_flow_config is None: + snappi_extra_params.traffic_flow_config.data_flow_config = { + "flow_name": TEST_FLOW_NAME, + "flow_dur_sec": DATA_FLOW_DURATION_SEC, + "flow_rate_percent": test_flow_rate_percent, + "flow_rate_pps": None, + "flow_rate_bps": None, + "flow_pkt_count": None, + "flow_pkt_size": data_flow_pkt_size, + "flow_delay_sec": data_flow_delay_sec, + "flow_traffic_type": traffic_flow_mode.FIXED_DURATION + } + + if snappi_extra_params.traffic_flow_config.background_flow_config is None and \ + snappi_extra_params.gen_background_traffic: + snappi_extra_params.traffic_flow_config.background_flow_config = { + "flow_name": BG_FLOW_NAME, + "flow_dur_sec": DATA_FLOW_DURATION_SEC, + "flow_rate_percent": bg_flow_rate_percent, + "flow_rate_pps": None, + "flow_rate_bps": None, + "flow_pkt_size": data_flow_pkt_size, + "flow_pkt_count": None, + "flow_delay_sec": data_flow_delay_sec, + "flow_traffic_type": traffic_flow_mode.FIXED_DURATION + } + + # PPS is high to ensure Storm is detected. + # traffic_flow_mode is changed to BURST + # Need to check how it works + if (test_traffic_pause): + if snappi_extra_params.traffic_flow_config.pause_flow_config is None: + snappi_extra_params.traffic_flow_config.pause_flow_config = { + "flow_name": PAUSE_FLOW_NAME, + "flow_dur_sec": DATA_FLOW_DURATION_SEC+60, + "flow_rate_percent": None, + "flow_rate_pps": calc_pfc_pause_flow_rate(speed_gbps), + "flow_rate_bps": None, + "flow_pkt_size": 64, + "flow_pkt_count": None, + "flow_delay_sec": 0, + "flow_traffic_type": traffic_flow_mode.FIXED_DURATION + } + + if snappi_extra_params.packet_capture_type == packet_capture.PFC_CAPTURE: + # PFC pause frame capture is requested + valid_pfc_frame_test = True + else: + # PFC pause frame capture is not requested + valid_pfc_frame_test = False + + if (test_traffic_pause): + if valid_pfc_frame_test: + snappi_extra_params.traffic_flow_config.pause_flow_config["flow_dur_sec"] = DATA_FLOW_DURATION_SEC + \ + data_flow_delay_sec + SNAPPI_POLL_DELAY_SEC + PAUSE_FLOW_DUR_BASE_SEC + snappi_extra_params.traffic_flow_config.pause_flow_config["flow_traffic_type"] = \ + traffic_flow_mode.FIXED_DURATION + + # Generate test flow config + for m in range(port_map[2]): + generate_test_flows(testbed_config=testbed_config, + test_flow_prio_list=test_prio_list, + prio_dscp_map=prio_dscp_map, + snappi_extra_params=snappi_extra_params, + flow_index=m) + + if (test_def['background_traffic']): + for m in range(port_map[2]): + if snappi_extra_params.gen_background_traffic: + # Generate background flow config + generate_background_flows(testbed_config=testbed_config, + bg_flow_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + snappi_extra_params=snappi_extra_params, + flow_index=m) + + # Generate pause storm config + if (test_traffic_pause): + for m in range(port_map[0]): + generate_pause_flows(testbed_config=testbed_config, + pause_prio_list=pause_prio_list, + global_pause=global_pause, + snappi_extra_params=snappi_extra_params, + flow_index=m) + + flows = testbed_config.flows + + all_flow_names = [flow.name for flow in flows] + data_flow_names = [flow.name for flow in flows if PAUSE_FLOW_NAME not in flow.name] + + # Clear PFC, queue and interface counters before traffic run + for dut in dut_list: + dut.command("pfcstat -c \n") + time.sleep(1) + dut.command("sonic-clear queuecounters \n") + time.sleep(1) + dut.command("sonic-clear counters \n") + time.sleep(1) + + exp_dur_sec = DATA_FLOW_DURATION_SEC + data_flow_delay_sec + + """ Run traffic """ + tgen_flow_stats, switch_flow_stats, test_stats = \ + run_traffic_and_collect_stats(rx_duthost=ingress_duthost, + tx_duthost=egress_duthost, + api=api, + config=testbed_config, + data_flow_names=data_flow_names, + all_flow_names=all_flow_names, + exp_dur_sec=exp_dur_sec, + port_map=test_def['port_map'], + fname=fname, + stats_interval=test_def['stats_interval'], + imix=test_def['imix'], + snappi_extra_params=snappi_extra_params) + + test_check = test_def['test_check'] + if (not test_check['loss_expected']): + # Check for loss packets on IXIA and DUT. + if (test_def['enable_pfcwd_drop'] or test_def['enable_credit_wd']): + pytest_assert(test_stats['tgen_loss_pkts'] == 0, 'Loss seen on TGEN') + pytest_assert(test_stats['dut_loss_pkts'] == 0, 'Loss seen on DUT') + + # Check for Tx and Rx packets on IXIA for lossless and lossy streams. + if (test_def['enable_pfcwd_drop'] or test_def['enable_credit_wd']): + pytest_assert(test_stats['tgen_lossless_rx_pkts'] == test_stats['tgen_lossless_tx_pkts'], + 'Losses observed in lossless traffic streams') + pytest_assert(test_stats['tgen_lossy_rx_pkts'] == test_stats['tgen_lossy_tx_pkts'], + 'Losses observed in lossy traffic streams') + + # Check for Rx packets between IXIA and DUT for lossy and lossless streams. + if (test_def['enable_pfcwd_drop'] or test_def['enable_credit_wd']): + pytest_assert(test_stats['tgen_lossless_rx_pkts'] == test_stats['dut_lossless_pkts'], + 'Losses observed in lossless traffic streams on DUT Tx and IXIA Rx') + pytest_assert(test_stats['tgen_lossy_rx_pkts'] == test_stats['dut_lossy_pkts'], + 'Losses observed in lossy traffic streams on DUT Tx and IXIA Rx') + else: + # Check for lossless and lossy stream percentage drop for a given tolerance limit. + lossless_drop = round((1 - float(test_stats['tgen_lossless_rx_pkts']) / test_stats['tgen_lossless_tx_pkts']), 2) + lossy_drop = round((1 - float(test_stats['tgen_lossy_rx_pkts']) / test_stats['tgen_lossy_tx_pkts']), 2) + logger.info('Lossless Drop %:{}, Lossy Drop %:{}'.format(lossless_drop, lossy_drop)) + pytest_assert((lossless_drop*100) <= test_check['lossless'], 'Lossless packet drop outside tolerance limit') + pytest_assert((lossy_drop*100) <= test_check['lossy'], 'Lossy packet drop outside tolerance limit') + + # Checking if the actual line rate on egress is within tolerable limit of egress line speed. + pytest_assert(((1 - test_stats['tgen_rx_rate'] / float(port_map[0]*port_map[1]))*100) <= test_check['speed_tol'], + 'Egress speed beyond tolerance range') + + # Checking for PFC counts on DUT + if (not test_check['pfc']): + pytest_assert(test_stats['lossless_tx_pfc'] == 0, 'Error:PFC transmitted by DUT for lossless priorities') + pytest_assert(test_stats['lossy_rx_tx_pfc'] == 0, 'Error:PFC transmitted by DUT for lossy priorities') + else: + if (test_stats['lossless_rx_pfc'] != 0 and (test_def['enable_pfcwd_drop'] or test_def['enable_pfcwd_fwd'])): + pytest_assert(test_stats['lossless_tx_pfc'] == 0, 'Error:No Tx PFCs from DUT after receiving PFCs') + if (test_stats['lossless_rx_pfc'] != 0 and + (not test_def['enable_pfcwd_drop'] and not test_def['enable_pfcwd_fwd'])): + pytest_assert(test_stats['lossless_tx_pfc'] != 0, 'Error:Tx PFCs should sent from DUT after receiving PFCs') + pytest_assert(test_stats['lossy_rx_tx_pfc'] == 0, 'Error:Incorrect Rx/Tx PFCs on DUT for lossy priorities') + + # Reset pfc delay parameter + pfc = testbed_config.layer1[0].flow_control.ieee_802_1qbb + pfc.pfc_delay = 0 + + for metric in tgen_flow_stats: + if "Pause" in metric.name: + PAUSE_FLW_NAME = metric.name + + # Verify pause flows + if (test_traffic_pause): + verify_pause_flow(flow_metrics=tgen_flow_stats, + pause_flow_name=PAUSE_FLW_NAME) + + if (test_def['background_traffic'] and test_def['verify_flows']): + if snappi_extra_params.gen_background_traffic: + # Verify background flows + verify_background_flow(flow_metrics=tgen_flow_stats, + speed_gbps=speed_gbps, + tolerance=TOLERANCE_THRESHOLD, + snappi_extra_params=snappi_extra_params) + + # Verify basic test flows metrics from ixia + if (test_def['verify_flows']): + verify_basic_test_flow(flow_metrics=tgen_flow_stats, + speed_gbps=speed_gbps, + tolerance=TOLERANCE_THRESHOLD, + test_flow_pause=test_traffic_pause, + snappi_extra_params=snappi_extra_params) + + if (test_traffic_pause and test_def['verify_flows']): + verify_pause_frame_count_dut(rx_dut=ingress_duthost, + tx_dut=egress_duthost, + test_traffic_pause=test_traffic_pause, + global_pause=global_pause, + snappi_extra_params=snappi_extra_params) + + # Verify in flight TX lossless packets do not leave the DUT when traffic is expected + # to be paused, or leave the DUT when the traffic is not expected to be paused + if (test_traffic_pause and test_def['enable_pfcwd_drop']): + verify_egress_queue_frame_count(duthost=egress_duthost, + switch_flow_stats=switch_flow_stats, + test_traffic_pause=test_traffic_pause, + snappi_extra_params=snappi_extra_params) diff --git a/tests/snappi_tests/pfcwd/test_pfcwd_actions.py b/tests/snappi_tests/pfcwd/test_pfcwd_actions.py new file mode 100644 index 00000000000..49068f75d42 --- /dev/null +++ b/tests/snappi_tests/pfcwd/test_pfcwd_actions.py @@ -0,0 +1,1017 @@ +import pytest +import logging +import random +from tests.common.helpers.assertions import pytest_require, pytest_assert # noqa: F401 +from tests.common.fixtures.conn_graph_facts import conn_graph_facts, fanout_graph_facts_multidut # noqa: F401 +from tests.common.snappi_tests.snappi_fixtures import snappi_api_serv_ip, snappi_api_serv_port, \ + snappi_api, snappi_multi_base_config, cleanup_config, get_snappi_ports_for_rdma, \ + get_snappi_ports, get_snappi_ports_multi_dut, clear_fabric_counters, check_fabric_counters # noqa: F401 +from tests.common.snappi_tests.qos_fixtures import prio_dscp_map, lossless_prio_list, \ + lossy_prio_list, all_prio_list # noqa: F401 +from tests.common.snappi_tests.common_helpers import get_pfcwd_stats +from tests.snappi_tests.pfcwd.files.pfcwd_actions_helper import run_pfc_test +from tests.common.config_reload import config_reload +from tests.common.snappi_tests.snappi_test_params import SnappiTestParams +from tests.snappi_tests.variables import MULTIDUT_PORT_INFO, MULTIDUT_TESTBED + +logger = logging.getLogger(__name__) + +pytestmark = [pytest.mark.topology('multidut-tgen')] + +port_map = [[1, 100, 1, 100], [1, 400, 1, 400]] +over_subs_port_map = [[1, 100, 2, 100], [1, 400, 2, 400]] + +# Testplan: docs/testplan/PFC_Snappi_Additional_Testcases.md +# This test-script covers testcase#10: PFCWD-enabled DROP mode test. +# This test-script also covers testcase#11: PFCWD-enabled FWD mode test. + + +@pytest.mark.parametrize('port_map', port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_pfcwd_drop_90_10(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + """ + Purpose of the test case is to enable PFCWD in drop mode and send 90% lossless traffic and 10% + lossy traffic and check the behavior. DUT is receiving pause storm on the egress port. DUT should + drop the lossless packets without generating any pause towards IXIA transmitter. No loss for lossy traffic. + + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + + Returns: + N/A + """ + + pkt_size = 1024 + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + test_check = {'lossless': 100, 'lossy': 0, 'speed_tol': 91, 'loss_expected': True, 'pfc': True} + + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 90, + 'BG_FLOW_AGGR_RATE_PERCENT': 10, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 1, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/One_Ingress_Egress_pfcwd_drop_90_10_dist'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd_drop': True, + 'enable_pfcwd_fwd': False, + 'enable_credit_wd': True, + 'stats_interval': 60, + 'background_traffic': True, + 'verify_flows': False, + 'imix': False, + 'test_check': test_check} + + test_prio_list = lossless_prio_list + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 3) + logger.info('Selected lossless :{} and lossy priorities:{} for the test'.format(test_prio_list, bg_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + logger.info('PFC-WD stats at the start of the test:') + for prio in test_prio_list: + for port in snappi_ports: + if len(dut_list) == 1: + if dut_list[0].hostname == port['peer_device']: + logger.info('PFCWD stats for dut:{}, port:{},prio:{}'. + format(dut_list[0].hostname, port['peer_port'], prio)) + pfcwd_stats = get_pfcwd_stats(dut_list[0], port['peer_port'], prio) + logger.info('PFCWD Stats:{}'.format(pfcwd_stats)) + else: + for dut in dut_list: + if dut.hostname == port['peer_device']: + logger.info('PFCWD stats for dut:{}, port:{},prio:{}'. + format(dut.hostname, port['peer_port'], prio)) + pfcwd_stats = get_pfcwd_stats(dut, port['peer_port'], prio) + logger.info('PFCWD Stats::{}'.format(pfcwd_stats)) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=True, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + logger.info('PFC-WD stats at the end of the test:') + for prio in test_prio_list: + for port in snappi_ports: + if len(dut_list) == 1: + if dut_list[0].hostname == port['peer_device']: + pfcwd_stats = get_pfcwd_stats(dut_list[0], port['peer_port'], prio) + logger.info('PFCWD Stats:for dut:{}, port:{},prio:{}, stats::{}'. + format(dut_list[0].hostname, port['peer_port'], prio, pfcwd_stats)) + else: + for dut in dut_list: + if dut.hostname == port['peer_device']: + pfcwd_stats = get_pfcwd_stats(dut, port['peer_port'], prio) + logger.info('PFCWD Stats:for dut:{}, port:{},prio:{}, stats::{}'. + format(dut.hostname, port['peer_port'], prio, pfcwd_stats)) + + for dut in duthosts: + check_fabric_counters(dut) + + finally: + for duthost in dut_list: + config_reload(sonic_host=duthost, config_source='config_db', safe_reload=True) + + +@pytest.mark.parametrize('port_map', port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_pfcwd_drop_uni(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + """ + Purpose of the test case is to enable PFCWD in drop mode and send 90% lossless traffic and 10% + lossy traffic and check the behavior. DUT is receiving pause storm on the egress port. DUT should + drop the lossless packets without generating any pause towards IXIA transmitter. No loss for lossy traffic. + + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + + Returns: + N/A + """ + + pkt_size = 1024 + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + test_check = {'lossless': 100, 'lossy': 0, 'speed_tol': 50, 'loss_expected': True, 'pfc': True} + + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 40, + 'BG_FLOW_AGGR_RATE_PERCENT': 60, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 1, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/One_Ingress_Egress_pfcwd_drop_uni_dist'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd_drop': True, + 'enable_pfcwd_fwd': False, + 'enable_credit_wd': True, + 'stats_interval': 60, + 'background_traffic': True, + 'verify_flows': False, + 'imix': False, + 'test_check': test_check} + + test_prio_list = lossless_prio_list + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 3) + logger.info('Selected lossless :{} and lossy priorities:{} for the test'.format(test_prio_list, bg_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + logger.info('PFC-WD stats at the start of the test:') + for prio in test_prio_list: + for port in snappi_ports: + if len(dut_list) == 1: + if dut_list[0].hostname == port['peer_device']: + logger.info('PFCWD stats for dut:{}, port:{},prio:{}'. + format(dut_list[0].hostname, port['peer_port'], prio)) + pfcwd_stats = get_pfcwd_stats(dut_list[0], port['peer_port'], prio) + logger.info('PFCWD Stats:{}'.format(pfcwd_stats)) + else: + for dut in dut_list: + if dut.hostname == port['peer_device']: + logger.info('PFCWD stats for dut:{}, port:{},prio:{}'. + format(dut.hostname, port['peer_port'], prio)) + pfcwd_stats = get_pfcwd_stats(dut, port['peer_port'], prio) + logger.info('PFCWD Stats::{}'.format(pfcwd_stats)) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=True, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + logger.info('PFC-WD stats at the end of the test:') + for prio in test_prio_list: + for port in snappi_ports: + if len(dut_list) == 1: + if dut_list[0].hostname == port['peer_device']: + pfcwd_stats = get_pfcwd_stats(dut_list[0], port['peer_port'], prio) + logger.info('PFCWD Stats:for dut:{}, port:{},prio:{}, stats::{}'. + format(dut_list[0].hostname, port['peer_port'], prio, pfcwd_stats)) + else: + for dut in dut_list: + if dut.hostname == port['peer_device']: + pfcwd_stats = get_pfcwd_stats(dut, port['peer_port'], prio) + logger.info('PFCWD Stats:for dut:{}, port:{},prio:{}, stats::{}'. + format(dut.hostname, port['peer_port'], prio, pfcwd_stats)) + + for dut in duthosts: + check_fabric_counters(dut) + + finally: + for duthost in dut_list: + config_reload(sonic_host=duthost, config_source='config_db', safe_reload=True) + + +@pytest.mark.parametrize('port_map', port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_pfcwd_frwd_90_10(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + + """ + Purpose of the test case is to check behavior of the DUT when PFCWD is enabled in FORWARD mode and egress port + is congested with PAUSE storm. DUT in this mode should forward the lossless packets irrespective of the pause + storm and not send any PAUSE frames towards IXIA transmitter. No effect on lossy traffic. + + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + Returns: + N/A + + """ + + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + # pkt_size of 1024B will be used unless imix flag is set. + # With imix flag set, the traffic_generation.py uses IMIX profile. + pkt_size = 1024 + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + test_check = {'lossless': 0, 'lossy': 0, 'speed_tol': 3, 'loss_expected': False, 'pfc': True} + + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 90, + 'BG_FLOW_AGGR_RATE_PERCENT': 10, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 1, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/One_Ingress_Egress_pfcwd_frwd_90_10_dist'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd_drop': False, + 'enable_pfcwd_fwd': True, + 'enable_credit_wd': True, + 'stats_interval': 60, + 'background_traffic': True, + 'verify_flows': False, + 'imix': False, + 'test_check': test_check} + + test_prio_list = lossless_prio_list + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 3) + logger.info('Selected lossless :{} and lossy priorities:{} for the test'.format(test_prio_list, bg_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + logger.info('PFC-WD stats at the start of the test:') + for prio in test_prio_list: + for port in snappi_ports: + if len(dut_list) == 1: + if dut_list[0].hostname == port['peer_device']: + logger.info('PFCWD stats for dut:{}, port:{},prio:{}'. + format(dut_list[0].hostname, port['peer_port'], prio)) + pfcwd_stats = get_pfcwd_stats(dut_list[0], port['peer_port'], prio) + logger.info('PFCWD Stats:{}'.format(pfcwd_stats)) + else: + for dut in dut_list: + if dut.hostname == port['peer_device']: + logger.info('PFCWD stats for dut:{}, port:{},prio:{}'. + format(dut.hostname, port['peer_port'], prio)) + pfcwd_stats = get_pfcwd_stats(dut, port['peer_port'], prio) + logger.info('PFCWD Stats::{}'.format(pfcwd_stats)) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=True, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + logger.info('PFC-WD stats at the end of the test:') + for prio in test_prio_list: + for port in snappi_ports: + if len(dut_list) == 1: + if dut_list[0].hostname == port['peer_device']: + pfcwd_stats = get_pfcwd_stats(dut_list[0], port['peer_port'], prio) + logger.info('PFCWD Stats:for dut:{}, port:{},prio:{}, stats::{}'. + format(dut_list[0].hostname, port['peer_port'], prio, pfcwd_stats)) + else: + for dut in dut_list: + if dut.hostname == port['peer_device']: + pfcwd_stats = get_pfcwd_stats(dut, port['peer_port'], prio) + logger.info('PFCWD Stats:for dut:{}, port:{},prio:{}, stats::{}'. + format(dut.hostname, port['peer_port'], prio, pfcwd_stats)) + + for dut in duthosts: + check_fabric_counters(dut) + + finally: + for duthost in dut_list: + config_reload(sonic_host=duthost, config_source='config_db', safe_reload=True) + + +@pytest.mark.parametrize('port_map', over_subs_port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_pfcwd_drop_over_subs_40_09(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + + """ + Purpose of the testcase is to check PFCWD behavior in DROP mode with over-subscription. + Each ingress is sending 49% of link capacity traffic and DUT is receiving PAUSE storm on egress link. + DUT should drop lossless packets. No drop for lossy traffic. + + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + + Returns: + N/A + """ + + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + # pkt_size of 1024B will be used unless imix flag is set. + # With imix flag set, the traffic_generation.py uses IMIX profile. + pkt_size = 1024 + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + test_check = {'lossless': 100, 'lossy': 0, 'speed_tol': 83, 'loss_expected': True, 'pfc': True} + + test_def = {} + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 40, + 'BG_FLOW_AGGR_RATE_PERCENT': 9, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 1, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/Two_Ingress_Single_Egress_pfcwd_drop_40_9_dist'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd_drop': True, + 'enable_pfcwd_fwd': False, + 'enable_credit_wd': True, + 'stats_interval': 60, + 'background_traffic': True, + 'verify_flows': False, + 'imix': False, + 'test_check': test_check} + + test_prio_list = lossless_prio_list + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 3) + logger.info('Selected lossless :{} and lossy priorities:{} for the test'.format(test_prio_list, bg_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + logger.info('PFC-WD stats at the start of the test:') + for prio in test_prio_list: + for port in snappi_ports: + if len(dut_list) == 1: + if dut_list[0].hostname == port['peer_device']: + logger.info('PFCWD stats for dut:{}, port:{},prio:{}'. + format(dut_list[0].hostname, port['peer_port'], prio)) + pfcwd_stats = get_pfcwd_stats(dut_list[0], port['peer_port'], prio) + logger.info('PFCWD Stats:{}'.format(pfcwd_stats)) + else: + for dut in dut_list: + if dut.hostname == port['peer_device']: + logger.info('PFCWD stats for dut:{}, port:{},prio:{}'. + format(dut.hostname, port['peer_port'], prio)) + pfcwd_stats = get_pfcwd_stats(dut, port['peer_port'], prio) + logger.info('PFCWD Stats::{}'.format(pfcwd_stats)) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=True, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + logger.info('PFC-WD stats at the end of the test:') + for prio in test_prio_list: + for port in snappi_ports: + if len(dut_list) == 1: + if dut_list[0].hostname == port['peer_device']: + pfcwd_stats = get_pfcwd_stats(dut_list[0], port['peer_port'], prio) + logger.info('PFCWD Stats:for dut:{}, port:{},prio:{}, stats::{}'. + format(dut_list[0].hostname, port['peer_port'], prio, pfcwd_stats)) + else: + for dut in dut_list: + if dut.hostname == port['peer_device']: + pfcwd_stats = get_pfcwd_stats(dut, port['peer_port'], prio) + logger.info('PFCWD Stats:for dut:{}, port:{},prio:{}, stats::{}'. + format(dut.hostname, port['peer_port'], prio, pfcwd_stats)) + + for dut in duthosts: + check_fabric_counters(dut) + + finally: + for duthost in dut_list: + config_reload(sonic_host=duthost, config_source='config_db', safe_reload=True) + + +@pytest.mark.parametrize('port_map', over_subs_port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_pfcwd_frwd_over_subs_40_09(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + + """ + Purpose of testcase is to test behavior of DUT in PFCWD-FORWARD mode in oversubscription mode. + Each ingress is sending 49% of link capacity traffic and DUT is receiving PAUSE storm on egress link. + DUT should forward for both lossy and lossless traffic without generating PAUSE frames towards IXIA + transmitter. + + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + + Returns: + N/A + """ + + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + # pkt_size of 1024B will be used unless imix flag is set. + # With imix flag set, the traffic_generation.py uses IMIX profile. + pkt_size = 1024 + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + test_check = {'lossless': 0, 'lossy': 0, 'speed_tol': 3, 'loss_expected': False, 'pfc': True} + + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 40, + 'BG_FLOW_AGGR_RATE_PERCENT': 9, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 1, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/Two_Ingress_Single_Egress_pfcwd_frwd_40_9_dist'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd_drop': False, + 'enable_pfcwd_fwd': True, + 'enable_credit_wd': True, + 'stats_interval': 60, + 'background_traffic': True, + 'verify_flows': False, + 'imix': False, + 'test_check': test_check} + + # Selecting only one lossless priority for the test. + test_prio_list = random.sample(lossless_prio_list, 1) + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 3) + logger.info('Selected lossless priority:{} for the test'.format(test_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + logger.info('PFC-WD stats at the start of the test:') + for prio in test_prio_list: + for port in snappi_ports: + if len(dut_list) == 1: + if dut_list[0].hostname == port['peer_device']: + logger.info('PFCWD stats for dut:{}, port:{},prio:{}'. + format(dut_list[0].hostname, port['peer_port'], prio)) + pfcwd_stats = get_pfcwd_stats(dut_list[0], port['peer_port'], prio) + logger.info('PFCWD Stats:{}'.format(pfcwd_stats)) + else: + for dut in dut_list: + if dut.hostname == port['peer_device']: + logger.info('PFCWD stats for dut:{}, port:{},prio:{}'. + format(dut.hostname, port['peer_port'], prio)) + pfcwd_stats = get_pfcwd_stats(dut, port['peer_port'], prio) + logger.info('PFCWD Stats::{}'.format(pfcwd_stats)) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=True, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + logger.info('PFC-WD stats at the end of the test:') + for prio in test_prio_list: + for port in snappi_ports: + if len(dut_list) == 1: + if dut_list[0].hostname == port['peer_device']: + pfcwd_stats = get_pfcwd_stats(dut_list[0], port['peer_port'], prio) + logger.info('PFCWD Stats:for dut:{}, port:{},prio:{}, stats::{}'. + format(dut_list[0].hostname, port['peer_port'], prio, pfcwd_stats)) + else: + for dut in dut_list: + if dut.hostname == port['peer_device']: + pfcwd_stats = get_pfcwd_stats(dut, port['peer_port'], prio) + logger.info('PFCWD Stats:for dut:{}, port:{},prio:{}, stats::{}'. + format(dut.hostname, port['peer_port'], prio, pfcwd_stats)) + for dut in duthosts: + check_fabric_counters(dut) + + finally: + for duthost in dut_list: + config_reload(sonic_host=duthost, config_source='config_db', safe_reload=True) + + +@pytest.mark.parametrize('port_map', port_map) +@pytest.mark.parametrize("multidut_port_info", MULTIDUT_PORT_INFO[MULTIDUT_TESTBED]) +def test_pfcwd_disable_pause_cngtn(snappi_api, # noqa: F811 + conn_graph_facts, # noqa: F811 + fanout_graph_facts_multidut, # noqa: F811 + duthosts, + prio_dscp_map, # noqa: F811 + lossless_prio_list, # noqa: F811 + lossy_prio_list, # noqa: F811 + tbinfo, + get_snappi_ports, # noqa: F811 + port_map, + multidut_port_info): # noqa: F811 + + """ + Purpose of the test case is to test oversubscription with two ingresses and single ingress. + Traffic pattern has 18% lossless priority and 27% lossy priority traffic. + Total ingress link is sending only 45% link capacity and hence egress will not be congested. + No losses for both lossless and lossy priority traffic. + + Args: + snappi_api (pytest fixture): SNAPPI session + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts_multidut (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority). + lossless_prio_list(list): list of lossless priorities + lossy_prio_list(list): list of lossy priorities. + tbinfo(key): element to identify testbed info name. + get_snappi_ports(pytest fixture): returns list of ports based on linecards selected. + port_map(list): list for port-speed combination. + multidut_port_info : Line card classification along with ports selected as Rx and Tx port. + + Returns: + N/A + """ + + # port_map is defined as port-speed combination. + # first two parameters are count of egress links and its speed. + # last two parameters are count of ingress links and its speed. + + # pkt_size of 1024B will be used unless imix flag is set. + # With imix flag set, the traffic_generation.py uses IMIX profile. + pkt_size = 1024 + + for testbed_subtype, rdma_ports in multidut_port_info.items(): + tx_port_count = port_map[0] + rx_port_count = port_map[2] + tmp_snappi_port_list = get_snappi_ports + snappi_port_list = [] + for item in tmp_snappi_port_list: + if (int(item['speed']) == (port_map[1] * 1000)): + snappi_port_list.append(item) + pytest_assert(MULTIDUT_TESTBED == tbinfo['conf-name'], + "The testbed name from testbed file doesn't match with MULTIDUT_TESTBED in variables.py ") + pytest_assert(len(snappi_port_list) >= tx_port_count + rx_port_count, + "Need Minimum of 2 ports defined in ansible/files/*links.csv file") + + pytest_assert(len(rdma_ports['tx_ports']) >= tx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Tx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + + pytest_assert(len(rdma_ports['rx_ports']) >= rx_port_count, + 'MULTIDUT_PORT_INFO doesn\'t have the required Rx ports defined for \ + testbed {}, subtype {} in variables.py'. + format(MULTIDUT_TESTBED, testbed_subtype)) + logger.info('Running test for testbed subtype: {}'.format(testbed_subtype)) + + snappi_ports = get_snappi_ports_for_rdma(snappi_port_list, rdma_ports, + tx_port_count, rx_port_count, MULTIDUT_TESTBED) + + testbed_config, port_config_list, snappi_ports = snappi_multi_base_config(duthosts, + snappi_ports, + snappi_api) + + # Percentage drop expected for lossless and lossy traffic. + # speed_tol is speed tolerance between egress link speed and actual speed. + # loss_expected to check losses on DUT and TGEN. + test_check = {'lossless': 0, 'lossy': 0, 'speed_tol': 41, 'loss_expected': False, 'pfc': True} + + test_def = {'TEST_FLOW_AGGR_RATE_PERCENT': 40, + 'BG_FLOW_AGGR_RATE_PERCENT': 60, + 'data_flow_pkt_size': pkt_size, + 'DATA_FLOW_DURATION_SEC': 300, + 'data_flow_delay_sec': 1, + 'SNAPPI_POLL_DELAY_SEC': 60, + 'test_type': '/tmp/Single_Ingress_Single_Egress_pause_cngstn_'+str(port_map[1])+'Gbps', + 'line_card_choice': testbed_subtype, + 'port_map': port_map, + 'enable_pfcwd_drop': False, + 'enable_pfcwd_fwd': False, + 'enable_credit_wd': False, + 'stats_interval': 60, + 'background_traffic': True, + 'verify_flows': False, + 'imix': False, + 'test_check': test_check} + # Selecting only one lossless priority for the test. + test_prio_list = random.sample(lossless_prio_list, 1) + pause_prio_list = test_prio_list + bg_prio_list = random.sample(lossy_prio_list, 3) + logger.info('Selected lossless priority:{} for the test'.format(test_prio_list)) + + snappi_extra_params = SnappiTestParams() + snappi_extra_params.multi_dut_params.duthost1 = snappi_ports[0]['duthost'] + snappi_extra_params.multi_dut_params.duthost2 = snappi_ports[-1]['duthost'] + + snappi_extra_params.multi_dut_params.multi_dut_ports = snappi_ports + if (snappi_ports[0]['peer_device'] == snappi_ports[-1]['peer_device']): + dut_list = [snappi_ports[0]['duthost']] + else: + dut_list = [snappi_ports[0]['duthost'], snappi_ports[-1]['duthost']] + + for dut in duthosts: + clear_fabric_counters(dut) + + try: + run_pfc_test(api=snappi_api, + testbed_config=testbed_config, + port_config_list=port_config_list, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts_multidut, + global_pause=False, + pause_prio_list=pause_prio_list, + test_prio_list=test_prio_list, + bg_prio_list=bg_prio_list, + prio_dscp_map=prio_dscp_map, + test_traffic_pause=True, + test_def=test_def, + snappi_extra_params=snappi_extra_params) + + for dut in duthosts: + check_fabric_counters(dut) + + finally: + for duthost in dut_list: + config_reload(sonic_host=duthost, config_source='config_db', safe_reload=True)