From 700fb98ceef65a96261d464b73570fba4902c5b2 Mon Sep 17 00:00:00 2001 From: Wei Bai Date: Thu, 11 Feb 2021 06:39:05 +0000 Subject: [PATCH 1/4] Add PFC watchdog impact on runtime traffic test case --- .../files/pfcwd_runtime_traffic_helper.py | 166 ++++++++++++++++++ .../ixia/pfcwd/test_pfcwd_runtime_traffic.py | 35 ++++ 2 files changed, 201 insertions(+) create mode 100644 tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py create mode 100644 tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py diff --git a/tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py b/tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py new file mode 100644 index 00000000000..f2ca6302f83 --- /dev/null +++ b/tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py @@ -0,0 +1,166 @@ +import time +from math import ceil + +from tests.common.helpers.assertions import pytest_assert +from tests.common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_port,\ + ixia_api_serv_user, ixia_api_serv_passwd, ixia_api +from tests.common.ixia.ixia_helpers import get_dut_port_id +from tests.common.ixia.common_helpers import start_pfcwd, stop_pfcwd + +from abstract_open_traffic_generator.flow import DeviceTxRx, TxRx, Flow, Header,\ + Size, Rate,Duration, FixedSeconds, FixedPackets, PortTxRx, PfcPause +from abstract_open_traffic_generator.flow_ipv4 import Priority, Dscp +from abstract_open_traffic_generator.flow import Pattern as FieldPattern +from abstract_open_traffic_generator.flow import Ipv4 as Ipv4Header +from abstract_open_traffic_generator.flow import Ethernet as EthernetHeader +from abstract_open_traffic_generator.control import State, ConfigState,\ + FlowTransmitState +from abstract_open_traffic_generator.result import FlowRequest + +DATA_FLOW_NAME = "Data Flow" +DATA_PKT_SIZE = 1024 +DATA_FLOW_DURATION_SEC = 15 +PFCWD_START_DELAY_SEC = 3 +IXIA_POLL_DELAY_SEC = 2 +TOLERANCE_THRESHOLD = 0.05 + +def run_pfcwd_runtime_traffic_test(api, + testbed_config, + conn_data, + fanout_data, + duthost, + dut_port, + prio_list, + prio_dscp_map): + + pytest_assert(testbed_config is not None, 'Fail to get L2/3 testbed config') + + stop_pfcwd(duthost) + + """ Get the ID of the port to test """ + port_id = get_dut_port_id(dut_hostname=duthost.hostname, + dut_port=dut_port, + conn_data=conn_data, + fanout_data=fanout_data) + + pytest_assert(port_id is not None, + 'Fail to get ID for port {}'.format(dut_port)) + + flows = __gen_traffic(testbed_config=testbed_config, + port_id=port_id, + data_flow_name=DATA_FLOW_NAME, + data_flow_dur_sec=DATA_FLOW_DURATION_SEC, + data_pkt_size=DATA_PKT_SIZE, + prio_list=prio_list, + prio_dscp_map=prio_dscp_map) + + """ Tgen config = testbed config + flow config """ + config = testbed_config + config.flows = flows + + all_flow_names = [flow.name for flow in flows] + + flow_stats = __run_traffic(api=api, + config=config, + duthost=duthost, + all_flow_names=all_flow_names, + pfcwd_start_delay_sec=PFCWD_START_DELAY_SEC, + exp_dur_sec=DATA_FLOW_DURATION_SEC) + + speed_str = config.layer1[0].speed + speed_gbps = int(speed_str.split('_')[1]) + + __verify_results(rows=flow_stats, + speed_gbps=speed_gbps, + data_flow_dur_sec=DATA_FLOW_DURATION_SEC, + data_pkt_size=DATA_PKT_SIZE, + tolerance=TOLERANCE_THRESHOLD) + +def __gen_traffic(testbed_config, + port_id, + data_flow_name, + data_flow_dur_sec, + data_pkt_size, + prio_list, + prio_dscp_map): + + rx_port_id = port_id + tx_port_id = (port_id + 1) % len(testbed_config.devices) + + data_endpoint = DeviceTxRx( + tx_device_names=[testbed_config.devices[tx_port_id].name], + rx_device_names=[testbed_config.devices[rx_port_id].name], + ) + + result = list() + data_flow_rate_percent = int(100 / len(prio_list)) + + """ For each priority """ + for prio in prio_list: + ip_prio = Priority(Dscp(phb=FieldPattern(choice=prio_dscp_map[prio]), + ecn=FieldPattern(choice=Dscp.ECN_CAPABLE_TRANSPORT_1))) + pfc_queue = FieldPattern([prio]) + + data_flow = Flow( + name='{} Prio {}'.format(data_flow_name, prio), + tx_rx=TxRx(data_endpoint), + packet=[ + Header(choice=EthernetHeader(pfc_queue=pfc_queue)), + Header(choice=Ipv4Header(priority=ip_prio)) + ], + size=Size(data_pkt_size), + rate=Rate('line', data_flow_rate_percent), + duration=Duration(FixedSeconds(seconds=data_flow_dur_sec)) + ) + + result.append(data_flow) + + return result + +def __run_traffic(api, config, duthost, all_flow_names, pfcwd_start_delay_sec, exp_dur_sec): + api.set_state(State(ConfigState(config=config, state='set'))) + api.set_state(State(FlowTransmitState(state='start'))) + + time.sleep(pfcwd_start_delay_sec) + start_pfcwd(duthost) + time.sleep(exp_dur_sec - pfcwd_start_delay_sec) + + attempts = 0 + max_attempts = 20 + + while attempts < max_attempts: + rows = api.get_flow_results(FlowRequest(flow_names=all_flow_names)) + """ If all the flows have stopped """ + transmit_states = [row['transmit'] for row in rows] + if len(rows) == len(all_flow_names) and\ + list(set(transmit_states)) == ['stopped']: + time.sleep(IXIA_POLL_DELAY_SEC) + break + else: + time.sleep(1) + attempts += 1 + + """ Dump per-flow statistics """ + rows = api.get_flow_results(FlowRequest(flow_names=all_flow_names)) + api.set_state(State(FlowTransmitState(state='stop'))) + + return rows + +def __verify_results(rows, speed_gbps, data_flow_dur_sec, data_pkt_size, tolerance): + data_flow_rate_percent = int(100 / len(rows)) + + for row in rows: + flow_name = row['name'] + tx_frames = row['frames_tx'] + rx_frames = row['frames_rx'] + + pytest_assert(tx_frames == rx_frames, "{} packets of {} are dropped".\ + format(tx_frames-rx_frames, flow_name)) + + exp_rx_pkts = data_flow_rate_percent / 100.0 * speed_gbps \ + * 1e9 * data_flow_dur_sec / 8.0 / data_pkt_size + + deviation = (rx_frames - exp_rx_pkts) / float(exp_rx_pkts) + pytest_assert(abs(deviation) < tolerance, + "{} should receive {} packets (actual {})".\ + format(flow_name, exp_rx_pkts, rx_frames)) diff --git a/tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py b/tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py new file mode 100644 index 00000000000..780526ca356 --- /dev/null +++ b/tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py @@ -0,0 +1,35 @@ +import pytest + +from tests.common.helpers.assertions import pytest_require, pytest_assert +from tests.common.fixtures.conn_graph_facts import conn_graph_facts,\ + fanout_graph_facts +from tests.common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_port,\ + ixia_api_serv_user, ixia_api_serv_passwd, ixia_api, ixia_testbed +from tests.common.ixia.qos_fixtures import prio_dscp_map, all_prio_list + +from files.pfcwd_runtime_traffic_helper import run_pfcwd_runtime_traffic_test + +def test_pfcwd_runtime_traffic(ixia_api, + ixia_testbed, + conn_graph_facts, + fanout_graph_facts, + duthosts, + rand_one_dut_hostname, + rand_one_dut_portname_oper_up, + all_prio_list, + prio_dscp_map): + + dut_hostname, dut_port = rand_one_dut_portname_oper_up.split('|') + pytest_require(rand_one_dut_hostname == dut_hostname, + "Port is not mapped to the expected DUT") + + duthost = duthosts[rand_one_dut_hostname] + + run_pfcwd_runtime_traffic_test(api=ixia_api, + testbed_config=ixia_testbed, + conn_data=conn_graph_facts, + fanout_data=fanout_graph_facts, + duthost=duthost, + dut_port=dut_port, + prio_list=all_prio_list, + prio_dscp_map=prio_dscp_map) From 7e18fb8a6d6e0ef0e3b18be26cdc552c70f67252 Mon Sep 17 00:00:00 2001 From: Wei Bai Date: Thu, 11 Feb 2021 08:28:32 +0000 Subject: [PATCH 2/4] Add comments --- .../files/pfcwd_runtime_traffic_helper.py | 62 ++++++++++++++++++- .../ixia/pfcwd/test_pfcwd_runtime_traffic.py | 16 +++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py b/tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py index f2ca6302f83..9652e11d89b 100644 --- a/tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py +++ b/tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py @@ -32,7 +32,22 @@ def run_pfcwd_runtime_traffic_test(api, dut_port, prio_list, prio_dscp_map): - + """ + Test PFC watchdog's impact on runtime traffic + + Args: + api (obj): IXIA session + testbed_config (obj): L2/L3 config of a T0 testbed + 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 + prio_list (list): priorities of data flows + prio_dscp_map (dict): Priority vs. DSCP map (key = priority). + + Returns: + N/A + """ pytest_assert(testbed_config is not None, 'Fail to get L2/3 testbed config') stop_pfcwd(duthost) @@ -83,7 +98,22 @@ def __gen_traffic(testbed_config, data_pkt_size, prio_list, prio_dscp_map): - + """ + Generate configurations of flows + + Args: + testbed_config (obj): L2/L3 config of a T0 testbed + port_id (int): ID of DUT port to test. + data_flow_name (str): data flow name + data_flow_dur_sec (int): duration of data flows in second + data_pkt_size (int): size of data packets in byte + prio_list (list): priorities of data flows + prio_dscp_map (dict): Priority vs. DSCP map (key = priority). + + Returns: + flows configurations (list): the list should have configurations of + len(prio_list) data flows + """ rx_port_id = port_id tx_port_id = (port_id + 1) % len(testbed_config.devices) @@ -118,6 +148,21 @@ def __gen_traffic(testbed_config, return result def __run_traffic(api, config, duthost, all_flow_names, pfcwd_start_delay_sec, exp_dur_sec): + """ + Start traffic at time 0 and enable PFC watchdog at pfcwd_start_delay_sec + + Args: + api (obj): IXIA session + config (obj): experiment config (testbed config + flow config) + duthost (Ansible host instance): device under test + all_flow_names (list): list of names of all the flows + pfcwd_start_delay_sec (int): PFC watchdog start delay in second + exp_dur_sec (int): experiment duration in second + + Returns: + per-flow statistics (list) + """ + api.set_state(State(ConfigState(config=config, state='set'))) api.set_state(State(FlowTransmitState(state='start'))) @@ -147,6 +192,19 @@ def __run_traffic(api, config, duthost, all_flow_names, pfcwd_start_delay_sec, e return rows def __verify_results(rows, speed_gbps, data_flow_dur_sec, data_pkt_size, tolerance): + """ + Verify if we get expected experiment results + + Args: + rows (list): per-flow statistics + speed_gbps (int): link speed in Gbps + data_flow_dur_sec (int): duration of data flows in second + data_pkt_size (int): size of data packets in byte + tolerance (float): maximum allowable deviation + + Returns: + N/A + """ data_flow_rate_percent = int(100 / len(rows)) for row in rows: diff --git a/tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py b/tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py index 780526ca356..a76d96d036a 100644 --- a/tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py +++ b/tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py @@ -18,7 +18,23 @@ def test_pfcwd_runtime_traffic(ixia_api, rand_one_dut_portname_oper_up, all_prio_list, prio_dscp_map): + """ + Test PFC watchdog's impact on runtime traffic + Args: + ixia_api (pytest fixture): IXIA session + ixia_testbed (pytest fixture): L2/L3 config of a T0 testbed + conn_graph_facts (pytest fixture): connection graph + fanout_graph_facts (pytest fixture): fanout graph + duthosts (pytest fixture): list of DUTs + rand_one_dut_hostname (str): hostname of DUT + rand_one_dut_portname_oper_up (str): port to test, e.g., 's6100-1|Ethernet0' + all_prio_list (pytest fixture): list of all the priorities + prio_dscp_map (pytest fixture): priority vs. DSCP map (key = priority) + + Returns: + N/A + """ dut_hostname, dut_port = rand_one_dut_portname_oper_up.split('|') pytest_require(rand_one_dut_hostname == dut_hostname, "Port is not mapped to the expected DUT") From 05e96848baf194e14f310835259934f48ecd6ca2 Mon Sep 17 00:00:00 2001 From: Wei Bai Date: Wed, 17 Feb 2021 05:12:46 +0000 Subject: [PATCH 3/4] Fix alerts --- tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py b/tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py index 9652e11d89b..65302d19221 100644 --- a/tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py +++ b/tests/ixia/pfcwd/files/pfcwd_runtime_traffic_helper.py @@ -1,5 +1,4 @@ import time -from math import ceil from tests.common.helpers.assertions import pytest_assert from tests.common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_port,\ @@ -8,7 +7,7 @@ from tests.common.ixia.common_helpers import start_pfcwd, stop_pfcwd from abstract_open_traffic_generator.flow import DeviceTxRx, TxRx, Flow, Header,\ - Size, Rate,Duration, FixedSeconds, FixedPackets, PortTxRx, PfcPause + Size, Rate, Duration, FixedSeconds from abstract_open_traffic_generator.flow_ipv4 import Priority, Dscp from abstract_open_traffic_generator.flow import Pattern as FieldPattern from abstract_open_traffic_generator.flow import Ipv4 as Ipv4Header From 05e809fdbc85d99d942d671b9f5f49e04ed02d14 Mon Sep 17 00:00:00 2001 From: Wei Bai Date: Wed, 17 Feb 2021 05:14:28 +0000 Subject: [PATCH 4/4] Add Tgen topology mark --- tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py b/tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py index a76d96d036a..6c7063bb0ce 100644 --- a/tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py +++ b/tests/ixia/pfcwd/test_pfcwd_runtime_traffic.py @@ -9,6 +9,8 @@ from files.pfcwd_runtime_traffic_helper import run_pfcwd_runtime_traffic_test +@pytest.mark.topology("tgen") + def test_pfcwd_runtime_traffic(ixia_api, ixia_testbed, conn_graph_facts,