From 06cd811b47b37b262903e387982b5ca82889f25c Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Thu, 25 Jun 2020 21:38:31 +0530 Subject: [PATCH 01/52] [ixia/Keysight] sample test cases and fixtures This directory should contain the Ixia ECN and RDMA test cases. The lib folder contains * fixtures required to run ixia test cases * helper function required to write ixia test cases --- tests/ixia/lib/__init__.py | 1 + tests/ixia/lib/ixia_fixtures.py | 75 ++++++++++ tests/ixia/lib/ixia_helpers.py | 59 ++++++++ tests/ixia/test_ixia_traffic_restpy.py | 198 +++++++++++++++++++++++++ 4 files changed, 333 insertions(+) create mode 100644 tests/ixia/lib/__init__.py create mode 100644 tests/ixia/lib/ixia_fixtures.py create mode 100644 tests/ixia/lib/ixia_helpers.py create mode 100644 tests/ixia/test_ixia_traffic_restpy.py diff --git a/tests/ixia/lib/__init__.py b/tests/ixia/lib/__init__.py new file mode 100644 index 00000000000..8867a92a02f --- /dev/null +++ b/tests/ixia/lib/__init__.py @@ -0,0 +1 @@ +# package for Ixia Lib \ No newline at end of file diff --git a/tests/ixia/lib/ixia_fixtures.py b/tests/ixia/lib/ixia_fixtures.py new file mode 100644 index 00000000000..cb48e3b2480 --- /dev/null +++ b/tests/ixia/lib/ixia_fixtures.py @@ -0,0 +1,75 @@ +import pytest +import pprint +# from common.devices import SonicHost, Localhost, PTFHost, EosHost, FanoutHost +from common.devices import FanoutHost +""" +In an IXIA testbed, there is no PTF docker. +Hence, we use ptf_ip field to store IXIA API server. +This fixture returns the IP address of the IXIA API server. +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_ip(testbed): + return testbed['ptf_ip'] + +""" +Return the username of IXIA API server +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_user(duthost): + return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['user'] + +""" +Return the password of IXIA API server +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_passwd(duthost): + return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['password'] + +""" +Return REST port. +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_port(duthost): + return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['rest_port'] + +""" +IXIA PTF can spawn multiple session on the same REST port. Optional for LINUX, Rewuired for windows +Return the session ID. +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_session_id(duthost): + return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['session_id'] + +""" +IXIA chassis are leaf fanout switches in the testbed. +This fixture returns the hostnames and IP addresses of the IXIA chassis in the dictionary format. +""" +@pytest.fixture(scope = "module") +def ixia_dev(duthost, ansible_adhoc, conn_graph_facts, creds): + dev_conn = conn_graph_facts['device_conn'] if 'device_conn' in conn_graph_facts else {} + fanout_hosts = {} + result = dict() + # WA for virtual testbed which has no fanout + try: + for dut_port in dev_conn.keys(): + fanout_rec = dev_conn[dut_port] + fanout_host = fanout_rec['peerdevice'] + fanout_port = fanout_rec['peerport'] + if fanout_host in fanout_hosts.keys(): + fanout = fanout_hosts[fanout_host] + else: + user = pswd = None + host_vars = ansible_adhoc().options['inventory_manager'].get_host(fanout_host).vars + os_type = 'eos' if 'os' not in host_vars else host_vars['os'] + if os_type == "ixia": + fanout = FanoutHost(ansible_adhoc, os_type, fanout_host, 'FanoutLeaf', user, pswd) + fanout_hosts[fanout_host] = fanout + fanout.add_port_map(dut_port, fanout_port) + except: + pass + + ixia_dev_hostnames = fanout_hosts.keys() + for hostname in ixia_dev_hostnames: + result[hostname] = duthost.host.options['inventory_manager'].get_host(hostname).get_vars()['ansible_host'] + + return result diff --git a/tests/ixia/lib/ixia_helpers.py b/tests/ixia/lib/ixia_helpers.py new file mode 100644 index 00000000000..4f24e4f32a1 --- /dev/null +++ b/tests/ixia/lib/ixia_helpers.py @@ -0,0 +1,59 @@ +import re + +""" +@summary: given a DUT interface, return the management IP address of its neighbor IXIA device +@param intf: DUT interface +@param conn_graph_facts: testbed connectivity graph +@param ixia_dev: the mapping of hostname to IP address of IXIA devices +@return the management IP address of its neighbor IXIA device or None if we cannot find it +""" +def get_neigh_ixia_mgmt_ip(intf, conn_graph_facts, ixia_dev): + device_conn = conn_graph_facts['device_conn'] + if intf not in device_conn: + return None + + ixia_dev_hostname = device_conn[intf]['peerdevice'] + if ixia_dev_hostname not in ixia_dev: + return None + + return ixia_dev[ixia_dev_hostname] + +""" +@summary: given a DUT interface, return the card of its neighbor IXIA device +@param intf: DUT interface +@param conn_graph_facts: testbed connectivity graph +@return the card of its neighbor IXIA device or None if we cannot find it +""" +def get_neigh_ixia_card(intf, conn_graph_facts): + device_conn = conn_graph_facts['device_conn'] + if intf not in device_conn: + return None + + ixia_intf = device_conn[intf]['peerport'] + pattern = r'Card(\d+)/Port(\d+)' + m = re.match(pattern, ixia_intf) + + if m is None: + return None + else: + return m.group(1) + +""" +@summary: given a DUT interface, return the port of its neighbor IXIA device +@param intf: DUT interface +@param conn_graph_facts: testbed connectivity graph +@return the port of its neighbor IXIA device or None if we cannot find it +""" +def get_neigh_ixia_port(intf, conn_graph_facts): + device_conn = conn_graph_facts['device_conn'] + if intf not in device_conn: + return None + + ixia_intf = device_conn[intf]['peerport'] + pattern = r'Card(\d+)/Port(\d+)' + m = re.match(pattern, ixia_intf) + + if m is None: + return None + else: + return m.group(2) diff --git a/tests/ixia/test_ixia_traffic_restpy.py b/tests/ixia/test_ixia_traffic_restpy.py new file mode 100644 index 00000000000..963c818f6fe --- /dev/null +++ b/tests/ixia/test_ixia_traffic_restpy.py @@ -0,0 +1,198 @@ +############################################################################### +# This test cases demonstrates: +# * All the fixtures required for running ixia script (please see the +# arguments of the test function) +# * How Ixia chassis card/ports are addressed +# * How you can configure/control ixia devices, start traffic and collect +# statistics using REST API +# * This simple sanity test cases can be used to check if testbed setup +# is correct or not - since it prints a lot of testbed data +############################################################################### + +import logging +import time +import pytest +import pprint +from common.utilities import wait_until +from common.fixtures.conn_graph_facts import conn_graph_facts +from common.platform.interface_utils import check_interface_information +from common.platform.daemon_utils import check_pmon_daemon_status +from common.reboot import * +from common.platform.device_utils import fanout_switch_port_lookup +from common.helpers import assertions +from ixnetwork_restpy import SessionAssistant, Files + +from lib.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ + ixia_api_serv_passwd, ixia_dev, ixia_api_serv_port, ixia_api_serv_session_id + +from lib.ixia_helpers import get_neigh_ixia_mgmt_ip, get_neigh_ixia_card,\ + get_neigh_ixia_port + + +import time + +#pytestmark = [pytest.mark.disable_loganalyzer] + +def returnIxiaChassisIp (chassisDict, no) : + chList = [] + for key in chassisDict.keys(): + chList.append(chassisDict[key]) + + return (chList[no - 1]) + +def parseDeviceConn (device_conn) : + retval = [] + dive_con_dict = device_conn['device_conn'] + for key in dive_con_dict.keys() : + pp = dive_con_dict[key]['peerport'] + string = pp + '/' + key + retval.append(string) + retval.sort() + return(retval) + +def getCard(ixiaCardPortList, num) : + card = ixiaCardPortList[num].split('/')[0] + cardNo = int(card.replace('Card', '')) + return(cardNo) + +def getPort(ixiaCardPortList, num) : + port = ixiaCardPortList[num].split('/')[1] + portNo = int(port.replace('Port', '')) + return(portNo) + +def test_testbed(testbed, conn_graph_facts, duthost, ixia_dev, ixia_api_serv_ip, + ixia_api_serv_user, ixia_api_serv_passwd, ixia_api_serv_port, + ixia_api_serv_session_id): + print("conn_graph_fact ==============") + pprint.pprint(conn_graph_facts) + print("DUT hostname ==============") + print(duthost.hostname) + print(dir(duthost)) + print("ixia ports ==============") + ixiaports = parseDeviceConn(conn_graph_facts) + print(ixiaports) + print("IXIA CHASSIS IP ==============") + print(ixia_dev) + print("IXIA API SERVER IP ==============") + print(ixia_api_serv_ip) + print("IXIA API SERVER USER ==============") + print(ixia_api_serv_user) + print("IXIA API SERVER PASSWORD ==============") + print(ixia_api_serv_passwd) + print("IXIA API REST PORT ==============") + print(ixia_api_serv_port) + print("IXIA API SESSION ID ==============") + print(ixia_api_serv_session_id) + print("=======================================") + + clientIp = ixia_api_serv_ip + UserName = ixia_api_serv_user + Password = ixia_api_serv_passwd + RestPort = ixia_api_serv_passwd + SessionId = ixia_api_serv_session_id + chassisIp = returnIxiaChassisIp(ixia_dev, 1) + + cardId = getCard(ixiaports, 0) + PortId1 = getPort(ixiaports, 0) + PortId2 = getPort(ixiaports, 1) + PortId3 = getPort(ixiaports, 2) + PortId4 = getPort(ixiaports, 3) + PortId5 = getPort(ixiaports, 4) + PortId6 = getPort(ixiaports, 5) + + if (SessionId != "None") : + session = SessionAssistant(IpAddress = clientIp, + UserName = UserName, + Password = Password, + RestPort = RestPort, + SessionId = SessionId) + else : + session = SessionAssistant(IpAddress = clientIp, + UserName = UserName, + Password = Password, + RestPort = RestPort) + + sessionData = session.Session + ixNetwork = session.Ixnetwork + ixNetwork.NewConfig() + portMap = session.PortMapAssistant() + + vPort1 = portMap.Map(chassisIp, cardId, PortId1) + vPort2 = portMap.Map(chassisIp, cardId, PortId2) + vPort3 = portMap.Map(chassisIp, cardId, PortId3) + vPort4 = portMap.Map(chassisIp, cardId, PortId4) + vPort5 = portMap.Map(chassisIp, cardId, PortId5) + vPort6 = portMap.Map(chassisIp, cardId, PortId6) + #print ('connecting to chassis %s' %(chassisIp)) + + t1 = time.time() + portMap.Connect(ChassisTimeout=1200, ForceOwnership=True) + t2 = time.time() + + time_taken = t2 - t1 + print("time-taken to connect == %s" %(time_taken)) + + vPort1.L1Config.NovusHundredGigLan.IeeeL1Defaults = False + vPort1.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False + vPort1.L1Config.NovusHundredGigLan.EnableRsFec = True + vPort1.L1Config.NovusHundredGigLan.EnableRsFecStats = True + + vPort2.L1Config.NovusHundredGigLan.IeeeL1Defaults = False + vPort2.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False + vPort2.L1Config.NovusHundredGigLan.EnableRsFec = True + vPort2.L1Config.NovusHundredGigLan.EnableRsFecStats = True + + vPort3.L1Config.NovusHundredGigLan.IeeeL1Defaults = False + vPort3.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False + vPort3.L1Config.NovusHundredGigLan.EnableRsFec = True + vPort3.L1Config.NovusHundredGigLan.EnableRsFecStats = True + + vPort4.L1Config.NovusHundredGigLan.IeeeL1Defaults = False + vPort4.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False + vPort4.L1Config.NovusHundredGigLan.EnableRsFec = True + vPort4.L1Config.NovusHundredGigLan.EnableRsFecStats = True + + vPort5.L1Config.NovusHundredGigLan.IeeeL1Defaults = False + vPort5.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False + vPort5.L1Config.NovusHundredGigLan.EnableRsFec = True + vPort5.L1Config.NovusHundredGigLan.EnableRsFecStats = True + + vPort6.L1Config.NovusHundredGigLan.IeeeL1Defaults = False + vPort6.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False + vPort6.L1Config.NovusHundredGigLan.EnableRsFec = True + vPort6.L1Config.NovusHundredGigLan.EnableRsFecStats = True + + vPort1.Name = 'Tx1' + vPort4.Name = 'Rx1' + state = vPort1.State + print ('creating topology') + + topology1 = ixNetwork.Topology.add(Name='Topo1', Ports=[vPort1, vPort2, vPort3, vPort4, vPort5, vPort6]) + deviceGroup1 = topology1.DeviceGroup.add(Name='DG1', Multiplier='1') + ethernet1 = deviceGroup1.Ethernet.add(Name='Eth1') + ipv4 = ethernet1.Ipv4.add(Name='Ipv4') + + ipv4.GatewayIp.ValueList(['192.168.1.1','192.168.1.1','192.168.1.1','192.168.1.1', '192.168.1.1', '192.168.1.1']) + ipv4.Address.ValueList(['192.168.1.2','192.168.1.3','192.168.1.4','192.168.1.5', '192.168.1.6', '192.168.1.7']) + + ixNetwork.StartAllProtocols() + time.sleep(60) + + # Traffic + traffic_item = ixNetwork.Traffic.TrafficItem.add(Name='Traffic Test', TrafficType='ipv4') + dest = [{'arg1': '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv4/1', 'arg2': 1, 'arg3': 4, 'arg4': 1 ,'arg5': 1}] + src = [{'arg1': '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv4/1', 'arg2': 1, 'arg3': 1, 'arg4': 1, 'arg5': 1}] + endPoint = traffic_item.EndpointSet.add() + traffic_item.Tracking.find().TrackBy = ['trackingenabled0'] + endPoint.ScalableSources = src + endPoint.ScalableDestinations = dest + traffic_item.Generate() + ixNetwork.Traffic.Apply() + ixNetwork.Traffic.Start() + time.sleep(10) + print(session.StatViewAssistant('Traffic Item Statistics')) + + print('passed') + + assert 1 + From b6a096321aad6fae53625530c22d38be1945dce5 Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Fri, 26 Jun 2020 17:24:05 +0530 Subject: [PATCH 02/52] [ixia/Keysight] adding ixia libraries As per review comments given against the 1819 we have to keep the ixia libraries under sonic-mgmt/tests/common/ixia/ --- tests/common/ixia/ixia_fixtures.py | 75 ++++++++++++++++++++++++++++++ tests/common/ixia/ixia_helpers.py | 59 +++++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 tests/common/ixia/ixia_fixtures.py create mode 100644 tests/common/ixia/ixia_helpers.py diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py new file mode 100644 index 00000000000..cb48e3b2480 --- /dev/null +++ b/tests/common/ixia/ixia_fixtures.py @@ -0,0 +1,75 @@ +import pytest +import pprint +# from common.devices import SonicHost, Localhost, PTFHost, EosHost, FanoutHost +from common.devices import FanoutHost +""" +In an IXIA testbed, there is no PTF docker. +Hence, we use ptf_ip field to store IXIA API server. +This fixture returns the IP address of the IXIA API server. +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_ip(testbed): + return testbed['ptf_ip'] + +""" +Return the username of IXIA API server +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_user(duthost): + return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['user'] + +""" +Return the password of IXIA API server +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_passwd(duthost): + return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['password'] + +""" +Return REST port. +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_port(duthost): + return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['rest_port'] + +""" +IXIA PTF can spawn multiple session on the same REST port. Optional for LINUX, Rewuired for windows +Return the session ID. +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_session_id(duthost): + return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['session_id'] + +""" +IXIA chassis are leaf fanout switches in the testbed. +This fixture returns the hostnames and IP addresses of the IXIA chassis in the dictionary format. +""" +@pytest.fixture(scope = "module") +def ixia_dev(duthost, ansible_adhoc, conn_graph_facts, creds): + dev_conn = conn_graph_facts['device_conn'] if 'device_conn' in conn_graph_facts else {} + fanout_hosts = {} + result = dict() + # WA for virtual testbed which has no fanout + try: + for dut_port in dev_conn.keys(): + fanout_rec = dev_conn[dut_port] + fanout_host = fanout_rec['peerdevice'] + fanout_port = fanout_rec['peerport'] + if fanout_host in fanout_hosts.keys(): + fanout = fanout_hosts[fanout_host] + else: + user = pswd = None + host_vars = ansible_adhoc().options['inventory_manager'].get_host(fanout_host).vars + os_type = 'eos' if 'os' not in host_vars else host_vars['os'] + if os_type == "ixia": + fanout = FanoutHost(ansible_adhoc, os_type, fanout_host, 'FanoutLeaf', user, pswd) + fanout_hosts[fanout_host] = fanout + fanout.add_port_map(dut_port, fanout_port) + except: + pass + + ixia_dev_hostnames = fanout_hosts.keys() + for hostname in ixia_dev_hostnames: + result[hostname] = duthost.host.options['inventory_manager'].get_host(hostname).get_vars()['ansible_host'] + + return result diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py new file mode 100644 index 00000000000..4f24e4f32a1 --- /dev/null +++ b/tests/common/ixia/ixia_helpers.py @@ -0,0 +1,59 @@ +import re + +""" +@summary: given a DUT interface, return the management IP address of its neighbor IXIA device +@param intf: DUT interface +@param conn_graph_facts: testbed connectivity graph +@param ixia_dev: the mapping of hostname to IP address of IXIA devices +@return the management IP address of its neighbor IXIA device or None if we cannot find it +""" +def get_neigh_ixia_mgmt_ip(intf, conn_graph_facts, ixia_dev): + device_conn = conn_graph_facts['device_conn'] + if intf not in device_conn: + return None + + ixia_dev_hostname = device_conn[intf]['peerdevice'] + if ixia_dev_hostname not in ixia_dev: + return None + + return ixia_dev[ixia_dev_hostname] + +""" +@summary: given a DUT interface, return the card of its neighbor IXIA device +@param intf: DUT interface +@param conn_graph_facts: testbed connectivity graph +@return the card of its neighbor IXIA device or None if we cannot find it +""" +def get_neigh_ixia_card(intf, conn_graph_facts): + device_conn = conn_graph_facts['device_conn'] + if intf not in device_conn: + return None + + ixia_intf = device_conn[intf]['peerport'] + pattern = r'Card(\d+)/Port(\d+)' + m = re.match(pattern, ixia_intf) + + if m is None: + return None + else: + return m.group(1) + +""" +@summary: given a DUT interface, return the port of its neighbor IXIA device +@param intf: DUT interface +@param conn_graph_facts: testbed connectivity graph +@return the port of its neighbor IXIA device or None if we cannot find it +""" +def get_neigh_ixia_port(intf, conn_graph_facts): + device_conn = conn_graph_facts['device_conn'] + if intf not in device_conn: + return None + + ixia_intf = device_conn[intf]['peerport'] + pattern = r'Card(\d+)/Port(\d+)' + m = re.match(pattern, ixia_intf) + + if m is None: + return None + else: + return m.group(2) From 2a8ad2b4946df1128d7e1e2127e0cbe3a7954405 Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Fri, 26 Jun 2020 17:45:13 +0530 Subject: [PATCH 03/52] [ixia/Keysight] This file should be in common/ixia This file should be in tests/common/ixia folder. So deleting it from tests/ixia/lib/ folder --- tests/ixia/lib/__init__.py | 1 - 1 file changed, 1 deletion(-) delete mode 100644 tests/ixia/lib/__init__.py diff --git a/tests/ixia/lib/__init__.py b/tests/ixia/lib/__init__.py deleted file mode 100644 index 8867a92a02f..00000000000 --- a/tests/ixia/lib/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# package for Ixia Lib \ No newline at end of file From 694ae89290f7435b92ca020fdef28380f56ac69f Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Fri, 26 Jun 2020 17:47:56 +0530 Subject: [PATCH 04/52] [ixia/Keysight] lib/ixia_fixtures.py This file should be under the folder tests/common/ixia Hence deleting it form tests/ixia/lib --- tests/ixia/lib/ixia_fixtures.py | 75 --------------------------------- 1 file changed, 75 deletions(-) delete mode 100644 tests/ixia/lib/ixia_fixtures.py diff --git a/tests/ixia/lib/ixia_fixtures.py b/tests/ixia/lib/ixia_fixtures.py deleted file mode 100644 index cb48e3b2480..00000000000 --- a/tests/ixia/lib/ixia_fixtures.py +++ /dev/null @@ -1,75 +0,0 @@ -import pytest -import pprint -# from common.devices import SonicHost, Localhost, PTFHost, EosHost, FanoutHost -from common.devices import FanoutHost -""" -In an IXIA testbed, there is no PTF docker. -Hence, we use ptf_ip field to store IXIA API server. -This fixture returns the IP address of the IXIA API server. -""" -@pytest.fixture(scope = "module") -def ixia_api_serv_ip(testbed): - return testbed['ptf_ip'] - -""" -Return the username of IXIA API server -""" -@pytest.fixture(scope = "module") -def ixia_api_serv_user(duthost): - return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['user'] - -""" -Return the password of IXIA API server -""" -@pytest.fixture(scope = "module") -def ixia_api_serv_passwd(duthost): - return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['password'] - -""" -Return REST port. -""" -@pytest.fixture(scope = "module") -def ixia_api_serv_port(duthost): - return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['rest_port'] - -""" -IXIA PTF can spawn multiple session on the same REST port. Optional for LINUX, Rewuired for windows -Return the session ID. -""" -@pytest.fixture(scope = "module") -def ixia_api_serv_session_id(duthost): - return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['session_id'] - -""" -IXIA chassis are leaf fanout switches in the testbed. -This fixture returns the hostnames and IP addresses of the IXIA chassis in the dictionary format. -""" -@pytest.fixture(scope = "module") -def ixia_dev(duthost, ansible_adhoc, conn_graph_facts, creds): - dev_conn = conn_graph_facts['device_conn'] if 'device_conn' in conn_graph_facts else {} - fanout_hosts = {} - result = dict() - # WA for virtual testbed which has no fanout - try: - for dut_port in dev_conn.keys(): - fanout_rec = dev_conn[dut_port] - fanout_host = fanout_rec['peerdevice'] - fanout_port = fanout_rec['peerport'] - if fanout_host in fanout_hosts.keys(): - fanout = fanout_hosts[fanout_host] - else: - user = pswd = None - host_vars = ansible_adhoc().options['inventory_manager'].get_host(fanout_host).vars - os_type = 'eos' if 'os' not in host_vars else host_vars['os'] - if os_type == "ixia": - fanout = FanoutHost(ansible_adhoc, os_type, fanout_host, 'FanoutLeaf', user, pswd) - fanout_hosts[fanout_host] = fanout - fanout.add_port_map(dut_port, fanout_port) - except: - pass - - ixia_dev_hostnames = fanout_hosts.keys() - for hostname in ixia_dev_hostnames: - result[hostname] = duthost.host.options['inventory_manager'].get_host(hostname).get_vars()['ansible_host'] - - return result From b5316e8e595f5ef96c9cc00e3d004fac190c7db9 Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Fri, 26 Jun 2020 17:50:04 +0530 Subject: [PATCH 05/52] [ixia/Keysight] deleting ixia_helpers.py This file should be under tests/common/ixia So deleting it from tests/ixia/lib --- tests/ixia/lib/ixia_helpers.py | 59 ---------------------------------- 1 file changed, 59 deletions(-) delete mode 100644 tests/ixia/lib/ixia_helpers.py diff --git a/tests/ixia/lib/ixia_helpers.py b/tests/ixia/lib/ixia_helpers.py deleted file mode 100644 index 4f24e4f32a1..00000000000 --- a/tests/ixia/lib/ixia_helpers.py +++ /dev/null @@ -1,59 +0,0 @@ -import re - -""" -@summary: given a DUT interface, return the management IP address of its neighbor IXIA device -@param intf: DUT interface -@param conn_graph_facts: testbed connectivity graph -@param ixia_dev: the mapping of hostname to IP address of IXIA devices -@return the management IP address of its neighbor IXIA device or None if we cannot find it -""" -def get_neigh_ixia_mgmt_ip(intf, conn_graph_facts, ixia_dev): - device_conn = conn_graph_facts['device_conn'] - if intf not in device_conn: - return None - - ixia_dev_hostname = device_conn[intf]['peerdevice'] - if ixia_dev_hostname not in ixia_dev: - return None - - return ixia_dev[ixia_dev_hostname] - -""" -@summary: given a DUT interface, return the card of its neighbor IXIA device -@param intf: DUT interface -@param conn_graph_facts: testbed connectivity graph -@return the card of its neighbor IXIA device or None if we cannot find it -""" -def get_neigh_ixia_card(intf, conn_graph_facts): - device_conn = conn_graph_facts['device_conn'] - if intf not in device_conn: - return None - - ixia_intf = device_conn[intf]['peerport'] - pattern = r'Card(\d+)/Port(\d+)' - m = re.match(pattern, ixia_intf) - - if m is None: - return None - else: - return m.group(1) - -""" -@summary: given a DUT interface, return the port of its neighbor IXIA device -@param intf: DUT interface -@param conn_graph_facts: testbed connectivity graph -@return the port of its neighbor IXIA device or None if we cannot find it -""" -def get_neigh_ixia_port(intf, conn_graph_facts): - device_conn = conn_graph_facts['device_conn'] - if intf not in device_conn: - return None - - ixia_intf = device_conn[intf]['peerport'] - pattern = r'Card(\d+)/Port(\d+)' - m = re.match(pattern, ixia_intf) - - if m is None: - return None - else: - return m.group(2) From 663e92031c8900948f94d5dc87bd60572e64ffaa Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Fri, 26 Jun 2020 18:01:44 +0530 Subject: [PATCH 06/52] [ixia/Keysight] updated test_ixia_traffic_restpy.py There were many review comment against this file. Some of the comments I have implemented. But some of the comments may be implemented in future. Like "(Wei Bai) Can you move all the low level IXIA API functions to ixia_helpers.py?" or "I have a high level comment. can we design some IXIA wrapper functions (e.g., configure ports, create a PFC traffic item) and only call these high level wrapper functions in py.test scripts without exposing too many IXIA low level details? (Wei Bai)" This comment may be addressed in future PRs once we come up with the high level wrapper APIs. --- tests/ixia/test_ixia_traffic_restpy.py | 65 ++++++++++---------------- 1 file changed, 25 insertions(+), 40 deletions(-) diff --git a/tests/ixia/test_ixia_traffic_restpy.py b/tests/ixia/test_ixia_traffic_restpy.py index 963c818f6fe..3d112a361ce 100644 --- a/tests/ixia/test_ixia_traffic_restpy.py +++ b/tests/ixia/test_ixia_traffic_restpy.py @@ -1,9 +1,9 @@ ############################################################################### -# This test cases demonstrates: -# * All the fixtures required for running ixia script (please see the +# This test cases demonstrates: +# * All the fixtures required for running ixia script (please see the # arguments of the test function) # * How Ixia chassis card/ports are addressed -# * How you can configure/control ixia devices, start traffic and collect +# * How you can configure/control ixia devices, start traffic and collect # statistics using REST API # * This simple sanity test cases can be used to check if testbed setup # is correct or not - since it prints a lot of testbed data @@ -22,12 +22,11 @@ from common.helpers import assertions from ixnetwork_restpy import SessionAssistant, Files -from lib.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ +from common.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ ixia_api_serv_passwd, ixia_dev, ixia_api_serv_port, ixia_api_serv_session_id -from lib.ixia_helpers import get_neigh_ixia_mgmt_ip, get_neigh_ixia_card,\ - get_neigh_ixia_port - +from common.ixia_helpers import get_neigh_ixia_mgmt_ip, get_neigh_ixia_card,\ + get_neigh_ixia_port import time @@ -63,27 +62,16 @@ def getPort(ixiaCardPortList, num) : def test_testbed(testbed, conn_graph_facts, duthost, ixia_dev, ixia_api_serv_ip, ixia_api_serv_user, ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id): - print("conn_graph_fact ==============") - pprint.pprint(conn_graph_facts) - print("DUT hostname ==============") - print(duthost.hostname) - print(dir(duthost)) - print("ixia ports ==============") + logger.info("Connection Graph Facts = %s " %(conn_graph_facts)) + logger.info("DUT hostname = %s" %(duthost.hostname)) ixiaports = parseDeviceConn(conn_graph_facts) - print(ixiaports) - print("IXIA CHASSIS IP ==============") - print(ixia_dev) - print("IXIA API SERVER IP ==============") - print(ixia_api_serv_ip) - print("IXIA API SERVER USER ==============") - print(ixia_api_serv_user) - print("IXIA API SERVER PASSWORD ==============") - print(ixia_api_serv_passwd) - print("IXIA API REST PORT ==============") - print(ixia_api_serv_port) - print("IXIA API SESSION ID ==============") - print(ixia_api_serv_session_id) - print("=======================================") + logger.info("Ixia ports = %s" %(ixiaports)) + logger.info("Ixia chassis IP = %s" %(ixia_dev)) + logger.info("Ixia API server IP = %s" %(ixia_api_serv_ip)) + logger.info("Ixia API server user = %s" %(ixia_api_serv_user)) + logger.info("Ixia API server password = %s" %(ixia_api_serv_passwd)) + logger.info("Ixia API server port = %s" %(ixia_api_serv_port)) + logger.info("Ixia API server sessionId = %s" %(ixia_api_serv_session_id)) clientIp = ixia_api_serv_ip UserName = ixia_api_serv_user @@ -111,7 +99,7 @@ def test_testbed(testbed, conn_graph_facts, duthost, ixia_dev, ixia_api_serv_ip, UserName = UserName, Password = Password, RestPort = RestPort) - + sessionData = session.Session ixNetwork = session.Ixnetwork ixNetwork.NewConfig() @@ -130,7 +118,7 @@ def test_testbed(testbed, conn_graph_facts, duthost, ixia_dev, ixia_api_serv_ip, t2 = time.time() time_taken = t2 - t1 - print("time-taken to connect == %s" %(time_taken)) + logger.info("time-taken to connect = %s" %(time_taken)) vPort1.L1Config.NovusHundredGigLan.IeeeL1Defaults = False vPort1.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False @@ -140,32 +128,31 @@ def test_testbed(testbed, conn_graph_facts, duthost, ixia_dev, ixia_api_serv_ip, vPort2.L1Config.NovusHundredGigLan.IeeeL1Defaults = False vPort2.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False vPort2.L1Config.NovusHundredGigLan.EnableRsFec = True - vPort2.L1Config.NovusHundredGigLan.EnableRsFecStats = True + vPort2.L1Config.NovusHundredGigLan.EnableRsFecStats = True vPort3.L1Config.NovusHundredGigLan.IeeeL1Defaults = False vPort3.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False vPort3.L1Config.NovusHundredGigLan.EnableRsFec = True - vPort3.L1Config.NovusHundredGigLan.EnableRsFecStats = True + vPort3.L1Config.NovusHundredGigLan.EnableRsFecStats = True vPort4.L1Config.NovusHundredGigLan.IeeeL1Defaults = False vPort4.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False vPort4.L1Config.NovusHundredGigLan.EnableRsFec = True - vPort4.L1Config.NovusHundredGigLan.EnableRsFecStats = True + vPort4.L1Config.NovusHundredGigLan.EnableRsFecStats = True vPort5.L1Config.NovusHundredGigLan.IeeeL1Defaults = False vPort5.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False vPort5.L1Config.NovusHundredGigLan.EnableRsFec = True - vPort5.L1Config.NovusHundredGigLan.EnableRsFecStats = True + vPort5.L1Config.NovusHundredGigLan.EnableRsFecStats = True vPort6.L1Config.NovusHundredGigLan.IeeeL1Defaults = False vPort6.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False vPort6.L1Config.NovusHundredGigLan.EnableRsFec = True - vPort6.L1Config.NovusHundredGigLan.EnableRsFecStats = True + vPort6.L1Config.NovusHundredGigLan.EnableRsFecStats = True vPort1.Name = 'Tx1' vPort4.Name = 'Rx1' state = vPort1.State - print ('creating topology') topology1 = ixNetwork.Topology.add(Name='Topo1', Ports=[vPort1, vPort2, vPort3, vPort4, vPort5, vPort6]) deviceGroup1 = topology1.DeviceGroup.add(Name='DG1', Multiplier='1') @@ -176,7 +163,7 @@ def test_testbed(testbed, conn_graph_facts, duthost, ixia_dev, ixia_api_serv_ip, ipv4.Address.ValueList(['192.168.1.2','192.168.1.3','192.168.1.4','192.168.1.5', '192.168.1.6', '192.168.1.7']) ixNetwork.StartAllProtocols() - time.sleep(60) + time.sleep(60) # Traffic traffic_item = ixNetwork.Traffic.TrafficItem.add(Name='Traffic Test', TrafficType='ipv4') @@ -190,9 +177,7 @@ def test_testbed(testbed, conn_graph_facts, duthost, ixia_dev, ixia_api_serv_ip, ixNetwork.Traffic.Apply() ixNetwork.Traffic.Start() time.sleep(10) - print(session.StatViewAssistant('Traffic Item Statistics')) - - print('passed') + logger.info(session.StatViewAssistant('Traffic Item Statistics')) + ixNetwork.Traffic.Stop() assert 1 - From 61ff293c3598c54d16e60f146f306049171b82bc Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Wed, 1 Jul 2020 16:07:56 +0530 Subject: [PATCH 07/52] Update test_ixia_traffic_restpy.py --- tests/ixia/test_ixia_traffic_restpy.py | 254 +++++++++++-------------- 1 file changed, 112 insertions(+), 142 deletions(-) diff --git a/tests/ixia/test_ixia_traffic_restpy.py b/tests/ixia/test_ixia_traffic_restpy.py index 3d112a361ce..d46006b8ab6 100644 --- a/tests/ixia/test_ixia_traffic_restpy.py +++ b/tests/ixia/test_ixia_traffic_restpy.py @@ -1,9 +1,9 @@ ############################################################################### -# This test cases demonstrates: -# * All the fixtures required for running ixia script (please see the +# This test cases demonstrates: +# * All the fixtures required for running ixia script (please see the # arguments of the test function) # * How Ixia chassis card/ports are addressed -# * How you can configure/control ixia devices, start traffic and collect +# * How you can configure/control ixia devices, start traffic and collect # statistics using REST API # * This simple sanity test cases can be used to check if testbed setup # is correct or not - since it prints a lot of testbed data @@ -12,107 +12,82 @@ import logging import time import pytest -import pprint +import ipaddr from common.utilities import wait_until -from common.fixtures.conn_graph_facts import conn_graph_facts -from common.platform.interface_utils import check_interface_information -from common.platform.daemon_utils import check_pmon_daemon_status +from common.fixtures.conn_graph_facts import conn_graph_facts, fanout_graph_facts from common.reboot import * -from common.platform.device_utils import fanout_switch_port_lookup -from common.helpers import assertions from ixnetwork_restpy import SessionAssistant, Files -from common.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ - ixia_api_serv_passwd, ixia_dev, ixia_api_serv_port, ixia_api_serv_session_id +from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ + ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id, \ + ixia_api_server_session -from common.ixia_helpers import get_neigh_ixia_mgmt_ip, get_neigh_ixia_card,\ - get_neigh_ixia_port +from common.ixia.ixia_helpers import get_neigh_ixia_mgmt_ip, get_neigh_ixia_card,\ + get_neigh_ixia_port, IxiaFanoutManager import time -#pytestmark = [pytest.mark.disable_loganalyzer] - -def returnIxiaChassisIp (chassisDict, no) : - chList = [] - for key in chassisDict.keys(): - chList.append(chassisDict[key]) - - return (chList[no - 1]) - -def parseDeviceConn (device_conn) : - retval = [] - dive_con_dict = device_conn['device_conn'] - for key in dive_con_dict.keys() : - pp = dive_con_dict[key]['peerport'] - string = pp + '/' + key - retval.append(string) - retval.sort() - return(retval) - -def getCard(ixiaCardPortList, num) : - card = ixiaCardPortList[num].split('/')[0] - cardNo = int(card.replace('Card', '')) - return(cardNo) - -def getPort(ixiaCardPortList, num) : - port = ixiaCardPortList[num].split('/')[1] - portNo = int(port.replace('Port', '')) - return(portNo) - -def test_testbed(testbed, conn_graph_facts, duthost, ixia_dev, ixia_api_serv_ip, - ixia_api_serv_user, ixia_api_serv_passwd, ixia_api_serv_port, - ixia_api_serv_session_id): +def create_ipv4_traffic_end_points ( + src_start_port, + src_port_count, + src_first_route_index, + src_route_count, + dst_start_port, + dst_port_count, + dst_first_route_index, + dst_route_count) : + + src = [{'arg1': '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv4/1', + 'arg2': src_start_port, + 'arg3': src_port_count, + 'arg4': src_first_route_index, + 'arg5': dst_route_count} + ] + + dst = [{'arg1': '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv4/1', + 'arg2': dst_start_port, + 'arg3': dst_port_count, + 'arg4': dst_first_route_index, + 'arg5': dst_route_count} + ] + + return (src, dst) + + +def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, + ixia_api_server_session, fanouthosts): + logger.info("Connection Graph Facts = %s " %(conn_graph_facts)) + logger.info("Fanout Graph facts = %s" %(fanout_graph_facts)) logger.info("DUT hostname = %s" %(duthost.hostname)) - ixiaports = parseDeviceConn(conn_graph_facts) - logger.info("Ixia ports = %s" %(ixiaports)) - logger.info("Ixia chassis IP = %s" %(ixia_dev)) - logger.info("Ixia API server IP = %s" %(ixia_api_serv_ip)) - logger.info("Ixia API server user = %s" %(ixia_api_serv_user)) - logger.info("Ixia API server password = %s" %(ixia_api_serv_passwd)) - logger.info("Ixia API server port = %s" %(ixia_api_serv_port)) - logger.info("Ixia API server sessionId = %s" %(ixia_api_serv_session_id)) - - clientIp = ixia_api_serv_ip - UserName = ixia_api_serv_user - Password = ixia_api_serv_passwd - RestPort = ixia_api_serv_passwd - SessionId = ixia_api_serv_session_id - chassisIp = returnIxiaChassisIp(ixia_dev, 1) - - cardId = getCard(ixiaports, 0) - PortId1 = getPort(ixiaports, 0) - PortId2 = getPort(ixiaports, 1) - PortId3 = getPort(ixiaports, 2) - PortId4 = getPort(ixiaports, 3) - PortId5 = getPort(ixiaports, 4) - PortId6 = getPort(ixiaports, 5) - - if (SessionId != "None") : - session = SessionAssistant(IpAddress = clientIp, - UserName = UserName, - Password = Password, - RestPort = RestPort, - SessionId = SessionId) - else : - session = SessionAssistant(IpAddress = clientIp, - UserName = UserName, - Password = Password, - RestPort = RestPort) - - sessionData = session.Session - ixNetwork = session.Ixnetwork - ixNetwork.NewConfig() - portMap = session.PortMapAssistant() - - vPort1 = portMap.Map(chassisIp, cardId, PortId1) - vPort2 = portMap.Map(chassisIp, cardId, PortId2) - vPort3 = portMap.Map(chassisIp, cardId, PortId3) - vPort4 = portMap.Map(chassisIp, cardId, PortId4) - vPort5 = portMap.Map(chassisIp, cardId, PortId5) - vPort6 = portMap.Map(chassisIp, cardId, PortId6) - #print ('connecting to chassis %s' %(chassisIp)) - + + mg_facts = duthost.minigraph_facts(host=duthost.hostname) + gatewayIp = mg_facts['ansible_facts']['minigraph_vlan_interfaces'][0]['addr'] + + ixiaFanoutHostList = IxiaFanoutManager(fanout_graph_facts) + ixiaFanoutHostList.get_fanout_device_details(device_number = 0) + + # Build gateway valuelist. Same gateway IP for all interface + gateway_value_list = [] + for i in ixiaFanoutHostList.ports() : + gateway_value_list.append(gatewayIp) + + # Create ixNetwork interface IP address list + interface_ip_list = [] + ipaddress = ipaddr.IPv4Address(gatewayIp) + for i in ixiaFanoutHostList.ports() : + ipaddress = ipaddress + 1 + interface_ip_list.append(ipaddress._string_from_ip_int(ipaddress._ip)) + + session = ixia_api_server_session + ixNetwork = session.Ixnetwork + portMap = session.PortMapAssistant() + + vport_list = [] + for i in ixiaFanoutHostList.ports() : + (chassisIp, cardId, portId) = ixiaFanoutHostList.getCardPort(i) + vport_list.append(portMap.Map(chassisIp, cardId, portId)) + t1 = time.time() portMap.Connect(ChassisTimeout=1200, ForceOwnership=True) t2 = time.time() @@ -120,64 +95,59 @@ def test_testbed(testbed, conn_graph_facts, duthost, ixia_dev, ixia_api_serv_ip, time_taken = t2 - t1 logger.info("time-taken to connect = %s" %(time_taken)) - vPort1.L1Config.NovusHundredGigLan.IeeeL1Defaults = False - vPort1.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False - vPort1.L1Config.NovusHundredGigLan.EnableRsFec = True - vPort1.L1Config.NovusHundredGigLan.EnableRsFecStats = True - - vPort2.L1Config.NovusHundredGigLan.IeeeL1Defaults = False - vPort2.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False - vPort2.L1Config.NovusHundredGigLan.EnableRsFec = True - vPort2.L1Config.NovusHundredGigLan.EnableRsFecStats = True - - vPort3.L1Config.NovusHundredGigLan.IeeeL1Defaults = False - vPort3.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False - vPort3.L1Config.NovusHundredGigLan.EnableRsFec = True - vPort3.L1Config.NovusHundredGigLan.EnableRsFecStats = True - - vPort4.L1Config.NovusHundredGigLan.IeeeL1Defaults = False - vPort4.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False - vPort4.L1Config.NovusHundredGigLan.EnableRsFec = True - vPort4.L1Config.NovusHundredGigLan.EnableRsFecStats = True - - vPort5.L1Config.NovusHundredGigLan.IeeeL1Defaults = False - vPort5.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False - vPort5.L1Config.NovusHundredGigLan.EnableRsFec = True - vPort5.L1Config.NovusHundredGigLan.EnableRsFecStats = True - - vPort6.L1Config.NovusHundredGigLan.IeeeL1Defaults = False - vPort6.L1Config.NovusHundredGigLan.EnableAutoNegotiation =False - vPort6.L1Config.NovusHundredGigLan.EnableRsFec = True - vPort6.L1Config.NovusHundredGigLan.EnableRsFecStats = True - - vPort1.Name = 'Tx1' - vPort4.Name = 'Rx1' - state = vPort1.State - - topology1 = ixNetwork.Topology.add(Name='Topo1', Ports=[vPort1, vPort2, vPort3, vPort4, vPort5, vPort6]) + for vport in vport_list : + vport.L1Config.NovusHundredGigLan.IeeeL1Defaults = False + vport.L1Config.NovusHundredGigLan.EnableAutoNegotiation = False + vport.L1Config.NovusHundredGigLan.EnableRsFec = True + vport.L1Config.NovusHundredGigLan.EnableRsFecStats = True + + topology1 = ixNetwork.Topology.add(Name='Topo1', Ports=vport_list) deviceGroup1 = topology1.DeviceGroup.add(Name='DG1', Multiplier='1') ethernet1 = deviceGroup1.Ethernet.add(Name='Eth1') ipv4 = ethernet1.Ipv4.add(Name='Ipv4') - ipv4.GatewayIp.ValueList(['192.168.1.1','192.168.1.1','192.168.1.1','192.168.1.1', '192.168.1.1', '192.168.1.1']) - ipv4.Address.ValueList(['192.168.1.2','192.168.1.3','192.168.1.4','192.168.1.5', '192.168.1.6', '192.168.1.7']) + ipv4.GatewayIp.ValueList(gateway_value_list) + ipv4.Address.ValueList(interface_ip_list) ixNetwork.StartAllProtocols() - time.sleep(60) - - # Traffic - traffic_item = ixNetwork.Traffic.TrafficItem.add(Name='Traffic Test', TrafficType='ipv4') - dest = [{'arg1': '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv4/1', 'arg2': 1, 'arg3': 4, 'arg4': 1 ,'arg5': 1}] - src = [{'arg1': '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv4/1', 'arg2': 1, 'arg3': 1, 'arg4': 1, 'arg5': 1}] - endPoint = traffic_item.EndpointSet.add() + logger.info("Wait for 5 seconds for iv4 sessions to up") + time.sleep(5) + + # Create a traffic item + traffic_item = ixNetwork.Traffic.TrafficItem.add( + Name = 'Traffic Test', + TrafficType = 'ipv4') + + # Create a ipv4 source and destination for the endpoint of traffic item. + src_dst_ep = create_ipv4_traffic_end_points ( + src_start_port = 1, + src_port_count = 1, + src_first_route_index = 1, + src_route_count = 1, + dst_start_port = 2, + dst_port_count = 3, + dst_first_route_index = 1, + dst_route_count = 1 + ) + + # Create endpoint set and set source and destination. + endPoint = traffic_item.EndpointSet.add() + endPoint.ScalableSources = src_dst_ep[0] + endPoint.ScalableDestinations = src_dst_ep[1] + + # Enable tracking. traffic_item.Tracking.find().TrackBy = ['trackingenabled0'] - endPoint.ScalableSources = src - endPoint.ScalableDestinations = dest + + # Generate, apply and start traffic. traffic_item.Generate() ixNetwork.Traffic.Apply() ixNetwork.Traffic.Start() - time.sleep(10) + + logger.info("run traffic for 5 seconds") + time.sleep(5) + + # Fetch statistics. logger.info(session.StatViewAssistant('Traffic Item Statistics')) ixNetwork.Traffic.Stop() - assert 1 + From 721b6b1d24398d87adf79076fe346b404bd32304 Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Wed, 1 Jul 2020 16:12:55 +0530 Subject: [PATCH 08/52] [ixia/Keysight] Changed as per review comments Added a new fixture ixia_api_server_session --- tests/common/ixia/ixia_fixtures.py | 60 +++++++++++++----------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py index cb48e3b2480..fe74509bb3c 100644 --- a/tests/common/ixia/ixia_fixtures.py +++ b/tests/common/ixia/ixia_fixtures.py @@ -1,7 +1,8 @@ -import pytest +mport pytest import pprint -# from common.devices import SonicHost, Localhost, PTFHost, EosHost, FanoutHost from common.devices import FanoutHost +from ixnetwork_restpy import SessionAssistant, Files + """ In an IXIA testbed, there is no PTF docker. Hence, we use ptf_ip field to store IXIA API server. @@ -39,37 +40,28 @@ def ixia_api_serv_port(duthost): @pytest.fixture(scope = "module") def ixia_api_serv_session_id(duthost): return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['session_id'] - + """ -IXIA chassis are leaf fanout switches in the testbed. -This fixture returns the hostnames and IP addresses of the IXIA chassis in the dictionary format. -""" -@pytest.fixture(scope = "module") -def ixia_dev(duthost, ansible_adhoc, conn_graph_facts, creds): - dev_conn = conn_graph_facts['device_conn'] if 'device_conn' in conn_graph_facts else {} - fanout_hosts = {} - result = dict() - # WA for virtual testbed which has no fanout - try: - for dut_port in dev_conn.keys(): - fanout_rec = dev_conn[dut_port] - fanout_host = fanout_rec['peerdevice'] - fanout_port = fanout_rec['peerport'] - if fanout_host in fanout_hosts.keys(): - fanout = fanout_hosts[fanout_host] - else: - user = pswd = None - host_vars = ansible_adhoc().options['inventory_manager'].get_host(fanout_host).vars - os_type = 'eos' if 'os' not in host_vars else host_vars['os'] - if os_type == "ixia": - fanout = FanoutHost(ansible_adhoc, os_type, fanout_host, 'FanoutLeaf', user, pswd) - fanout_hosts[fanout_host] = fanout - fanout.add_port_map(dut_port, fanout_port) - except: - pass +IXIA session manager with PTF server +""" +@pytest.fixture(scope = "function") +def ixia_api_server_session(ixia_api_serv_ip, + ixia_api_serv_user, ixia_api_serv_passwd, ixia_api_serv_port, + ixia_api_serv_session_id) : + + if (ixia_api_serv_session_id != "None") : + session = SessionAssistant(IpAddress = ixia_api_serv_ip, + UserName = ixia_api_serv_user, + Password = ixia_api_serv_passwd, + RestPort = ixia_api_serv_port, + SessionId = ixia_api_serv_session_id) + else : + session = SessionAssistant(IpAddress = ixia_api_serv_ip, + UserName = ixia_api_serv_user, + Password = ixia_api_serv_passwd, + RestPort = ixia_api_serv_port) + sessionData = session.Session + ixNetwork = session.Ixnetwork + ixNetwork.NewConfig() + return session - ixia_dev_hostnames = fanout_hosts.keys() - for hostname in ixia_dev_hostnames: - result[hostname] = duthost.host.options['inventory_manager'].get_host(hostname).get_vars()['ansible_host'] - - return result From 48335130b2c1938e814d00240b83cc386d4bc50d Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Wed, 1 Jul 2020 16:16:53 +0530 Subject: [PATCH 09/52] [ixia/Keysight] added new IxiaFanoutManager class This class is added to efficiently manage ixia fanout devices --- tests/common/ixia/ixia_helpers.py | 70 ++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index 4f24e4f32a1..782521d7360 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -1,5 +1,5 @@ import re - +from common.reboot import * """ @summary: given a DUT interface, return the management IP address of its neighbor IXIA device @param intf: DUT interface @@ -56,4 +56,70 @@ def get_neigh_ixia_port(intf, conn_graph_facts): if m is None: return None else: - return m.group(2) + return m.group(2) + +def parseFanoutConnections (device_conn) : + retval = [] + for key in device_conn.keys() : + pp = device_conn[key]['peerport'] + string = key + '/' + pp + retval.append(string) + retval.sort() + return(retval) + +def getCardPort(i) : + crd = (i.split('/')[0]).replace('Card', '') + prt = (i.split('/')[1]).replace('Port', '') + return (crd, prt) + +class IxiaFanoutManager () : + def __init__(self,fanout_data) : + self.last_fanout_assessed = None + self.fanout_list = [] + self.last_device_connection_details = None + self.current_ixia_port_list = None + self.ip_address = '0.0.0.0' + for i in fanout_data.keys() : + self.fanout_list.append(fanout_data[i]) + + def get_fanout_device_details (self, device_number) : + + # Pointer to chassis info + self.last_fanout_assessed = device_number + + # Chassis connection details + self.last_device_connection_details = \ + self.fanout_list[self.last_fanout_assessed]['device_conn'] + + # Chassis ip details + self.ip_address = \ + self.fanout_list[self.last_fanout_assessed]['device_info']['mgmtip'] + + # List of chassis cards and ports + self.current_ixia_port_list = \ + self.__parseFanoutConnections__() + + #return self.fanout_list[self.last_fanout_assessed] + + def __parseFanoutConnections__ (self) : + device_conn = self.last_device_connection_details + retval = [] + for key in device_conn.keys() : + pp = device_conn[key]['peerport'] + string = key + '/' + pp + retval.append(string) + retval.sort() + return(retval) + + def getCardPort (self, crd_prt) : + ip = self.ip_address + crd = (crd_prt.split('/')[0]).replace('Card', '') + prt = (crd_prt.split('/')[1]).replace('Port', '') + return (ip, crd, prt) + + def get_chassis_ip (self) : + return self.ip_address + + def ports(self) : + return self.current_ixia_port_list + From b0f1bf615366acc7acb381342365e24e22862e58 Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Wed, 1 Jul 2020 16:20:22 +0530 Subject: [PATCH 10/52] [ixia/Keysight] Adding __init__().py Making ixia directory a package, to access ixia_fixtures.py and ixia_helpers.py --- tests/common/ixia/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 tests/common/ixia/__init__.py diff --git a/tests/common/ixia/__init__.py b/tests/common/ixia/__init__.py new file mode 100644 index 00000000000..fd92c343462 --- /dev/null +++ b/tests/common/ixia/__init__.py @@ -0,0 +1 @@ +# place fro ixia fixtures From 7bd3dbdc14901178642c2a035ba75d2b3e4795c2 Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Wed, 1 Jul 2020 16:29:15 +0530 Subject: [PATCH 11/52] [ixia/Keysight] Adding IxiaHost class This class is a place holder right now. Ixia fanout devices (chassis) do not require this abstraction layer. Ixia PTF (ixia API server) takes care of all the operation that we can do on the ixia fanout devices (chassis) and hence we execute them via API server only. But it is still nice to have all abstractions in the same location. So future users knows where to find them. --- tests/common/devices.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/common/devices.py b/tests/common/devices.py index 549f8108ddf..24959896d2d 100644 --- a/tests/common/devices.py +++ b/tests/common/devices.py @@ -851,6 +851,26 @@ def exec_template(self, ansible_root, ansible_playbook, inventory, **kwargs): if res["localhost"]["rc"] != 0: raise Exception("Unable to execute template\n{}".format(res["localhost"]["stdout"])) +class IxiaHost (AnsibleHostBase): + """ + @summary: For running ansible module on Ixia Fanout switch in future (TBD). + Now it is just a place holder + """ + def __init__ (self, ansible_adhoc, os, hostname, device_type) : + self.ansible_adhoc = ansible_adhoc + self.os = os + self.hostname = hostname + self.device_type = device_type + + def get_host_name (self) : + return self.hostname + + def get_os (self) : + return self.os + + def exacute (self, cmd) : + if (self.os == 'ixia') : + eval(cmd) class FanoutHost(): """ @@ -873,7 +893,7 @@ def __init__(self, ansible_adhoc, os, hostname, device_type, user, passwd, shell elif os == 'ixia': # TODO: add ixia chassis abstraction self.os = os - self.host = None + self.host = IxiaHost(ansible_adhoc, os, hostname, device_type) else: # Use eos host if the os type is unknown self.os = 'eos' From cbf0235aa78f2215b64ba3d642634ce1e6db8334 Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Wed, 1 Jul 2020 16:42:14 +0530 Subject: [PATCH 12/52] [ixia/Keysight] corrected typo added a new class Added a new class that should make chassis port management easier --- tests/common/ixia/ixia_fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py index fe74509bb3c..1c9a0a1482c 100644 --- a/tests/common/ixia/ixia_fixtures.py +++ b/tests/common/ixia/ixia_fixtures.py @@ -1,4 +1,4 @@ -mport pytest +import pytest import pprint from common.devices import FanoutHost from ixnetwork_restpy import SessionAssistant, Files From 84f32018e5b84608a752932eb6565c3b9e1f671c Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Wed, 1 Jul 2020 16:50:45 +0530 Subject: [PATCH 13/52] [ixia/Keysight] Deleted from the review There was an typo --- tests/common/ixia/ixia_fixtures.py | 67 ------------------------------ 1 file changed, 67 deletions(-) delete mode 100644 tests/common/ixia/ixia_fixtures.py diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py deleted file mode 100644 index 1c9a0a1482c..00000000000 --- a/tests/common/ixia/ixia_fixtures.py +++ /dev/null @@ -1,67 +0,0 @@ -import pytest -import pprint -from common.devices import FanoutHost -from ixnetwork_restpy import SessionAssistant, Files - -""" -In an IXIA testbed, there is no PTF docker. -Hence, we use ptf_ip field to store IXIA API server. -This fixture returns the IP address of the IXIA API server. -""" -@pytest.fixture(scope = "module") -def ixia_api_serv_ip(testbed): - return testbed['ptf_ip'] - -""" -Return the username of IXIA API server -""" -@pytest.fixture(scope = "module") -def ixia_api_serv_user(duthost): - return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['user'] - -""" -Return the password of IXIA API server -""" -@pytest.fixture(scope = "module") -def ixia_api_serv_passwd(duthost): - return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['password'] - -""" -Return REST port. -""" -@pytest.fixture(scope = "module") -def ixia_api_serv_port(duthost): - return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['rest_port'] - -""" -IXIA PTF can spawn multiple session on the same REST port. Optional for LINUX, Rewuired for windows -Return the session ID. -""" -@pytest.fixture(scope = "module") -def ixia_api_serv_session_id(duthost): - return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['session_id'] - -""" -IXIA session manager with PTF server -""" -@pytest.fixture(scope = "function") -def ixia_api_server_session(ixia_api_serv_ip, - ixia_api_serv_user, ixia_api_serv_passwd, ixia_api_serv_port, - ixia_api_serv_session_id) : - - if (ixia_api_serv_session_id != "None") : - session = SessionAssistant(IpAddress = ixia_api_serv_ip, - UserName = ixia_api_serv_user, - Password = ixia_api_serv_passwd, - RestPort = ixia_api_serv_port, - SessionId = ixia_api_serv_session_id) - else : - session = SessionAssistant(IpAddress = ixia_api_serv_ip, - UserName = ixia_api_serv_user, - Password = ixia_api_serv_passwd, - RestPort = ixia_api_serv_port) - sessionData = session.Session - ixNetwork = session.Ixnetwork - ixNetwork.NewConfig() - return session - From f0098d1c26c2d2076292fee177597bda32c057c9 Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Wed, 1 Jul 2020 17:08:33 +0530 Subject: [PATCH 14/52] [ixia/Keysight] adding ixia_fixtures Deletes accidentally --- tests/common/ixia/ixia_fixtures.py | 75 ++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/common/ixia/ixia_fixtures.py diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py new file mode 100644 index 00000000000..cb48e3b2480 --- /dev/null +++ b/tests/common/ixia/ixia_fixtures.py @@ -0,0 +1,75 @@ +import pytest +import pprint +# from common.devices import SonicHost, Localhost, PTFHost, EosHost, FanoutHost +from common.devices import FanoutHost +""" +In an IXIA testbed, there is no PTF docker. +Hence, we use ptf_ip field to store IXIA API server. +This fixture returns the IP address of the IXIA API server. +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_ip(testbed): + return testbed['ptf_ip'] + +""" +Return the username of IXIA API server +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_user(duthost): + return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['user'] + +""" +Return the password of IXIA API server +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_passwd(duthost): + return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['password'] + +""" +Return REST port. +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_port(duthost): + return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['rest_port'] + +""" +IXIA PTF can spawn multiple session on the same REST port. Optional for LINUX, Rewuired for windows +Return the session ID. +""" +@pytest.fixture(scope = "module") +def ixia_api_serv_session_id(duthost): + return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['session_id'] + +""" +IXIA chassis are leaf fanout switches in the testbed. +This fixture returns the hostnames and IP addresses of the IXIA chassis in the dictionary format. +""" +@pytest.fixture(scope = "module") +def ixia_dev(duthost, ansible_adhoc, conn_graph_facts, creds): + dev_conn = conn_graph_facts['device_conn'] if 'device_conn' in conn_graph_facts else {} + fanout_hosts = {} + result = dict() + # WA for virtual testbed which has no fanout + try: + for dut_port in dev_conn.keys(): + fanout_rec = dev_conn[dut_port] + fanout_host = fanout_rec['peerdevice'] + fanout_port = fanout_rec['peerport'] + if fanout_host in fanout_hosts.keys(): + fanout = fanout_hosts[fanout_host] + else: + user = pswd = None + host_vars = ansible_adhoc().options['inventory_manager'].get_host(fanout_host).vars + os_type = 'eos' if 'os' not in host_vars else host_vars['os'] + if os_type == "ixia": + fanout = FanoutHost(ansible_adhoc, os_type, fanout_host, 'FanoutLeaf', user, pswd) + fanout_hosts[fanout_host] = fanout + fanout.add_port_map(dut_port, fanout_port) + except: + pass + + ixia_dev_hostnames = fanout_hosts.keys() + for hostname in ixia_dev_hostnames: + result[hostname] = duthost.host.options['inventory_manager'].get_host(hostname).get_vars()['ansible_host'] + + return result From b91dc5e5cda608e3aac735afc7d158cf49c14fe9 Mon Sep 17 00:00:00 2001 From: abhijit-dhar <67135817+abhijit-dhar@users.noreply.github.com> Date: Wed, 1 Jul 2020 17:16:07 +0530 Subject: [PATCH 15/52] [ixia/Keysight] updated ixia_fixtures.py --- tests/common/ixia/ixia_fixtures.py | 58 +++++++++++++----------------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py index cb48e3b2480..1c9a0a1482c 100644 --- a/tests/common/ixia/ixia_fixtures.py +++ b/tests/common/ixia/ixia_fixtures.py @@ -1,7 +1,8 @@ import pytest import pprint -# from common.devices import SonicHost, Localhost, PTFHost, EosHost, FanoutHost from common.devices import FanoutHost +from ixnetwork_restpy import SessionAssistant, Files + """ In an IXIA testbed, there is no PTF docker. Hence, we use ptf_ip field to store IXIA API server. @@ -39,37 +40,28 @@ def ixia_api_serv_port(duthost): @pytest.fixture(scope = "module") def ixia_api_serv_session_id(duthost): return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['session_id'] - + """ -IXIA chassis are leaf fanout switches in the testbed. -This fixture returns the hostnames and IP addresses of the IXIA chassis in the dictionary format. -""" -@pytest.fixture(scope = "module") -def ixia_dev(duthost, ansible_adhoc, conn_graph_facts, creds): - dev_conn = conn_graph_facts['device_conn'] if 'device_conn' in conn_graph_facts else {} - fanout_hosts = {} - result = dict() - # WA for virtual testbed which has no fanout - try: - for dut_port in dev_conn.keys(): - fanout_rec = dev_conn[dut_port] - fanout_host = fanout_rec['peerdevice'] - fanout_port = fanout_rec['peerport'] - if fanout_host in fanout_hosts.keys(): - fanout = fanout_hosts[fanout_host] - else: - user = pswd = None - host_vars = ansible_adhoc().options['inventory_manager'].get_host(fanout_host).vars - os_type = 'eos' if 'os' not in host_vars else host_vars['os'] - if os_type == "ixia": - fanout = FanoutHost(ansible_adhoc, os_type, fanout_host, 'FanoutLeaf', user, pswd) - fanout_hosts[fanout_host] = fanout - fanout.add_port_map(dut_port, fanout_port) - except: - pass +IXIA session manager with PTF server +""" +@pytest.fixture(scope = "function") +def ixia_api_server_session(ixia_api_serv_ip, + ixia_api_serv_user, ixia_api_serv_passwd, ixia_api_serv_port, + ixia_api_serv_session_id) : + + if (ixia_api_serv_session_id != "None") : + session = SessionAssistant(IpAddress = ixia_api_serv_ip, + UserName = ixia_api_serv_user, + Password = ixia_api_serv_passwd, + RestPort = ixia_api_serv_port, + SessionId = ixia_api_serv_session_id) + else : + session = SessionAssistant(IpAddress = ixia_api_serv_ip, + UserName = ixia_api_serv_user, + Password = ixia_api_serv_passwd, + RestPort = ixia_api_serv_port) + sessionData = session.Session + ixNetwork = session.Ixnetwork + ixNetwork.NewConfig() + return session - ixia_dev_hostnames = fanout_hosts.keys() - for hostname in ixia_dev_hostnames: - result[hostname] = duthost.host.options['inventory_manager'].get_host(hostname).get_vars()['ansible_host'] - - return result From b2881236aac909b51a86c2a3cd14ca0608b5522d Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Mon, 6 Jul 2020 10:15:25 +0000 Subject: [PATCH 16/52] modifying and rdma test case according to ixia proposal --- tests/common/ixia/common_helpers.py | 48 +++++++ tests/common/ixia/ixia_fixtures.py | 12 +- tests/common/ixia/qos_fixtures.py | 31 ++++ tests/ixia/test_pfc_pause_lossless.py | 195 ++++++++++++++++++++++++++ 4 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 tests/common/ixia/common_helpers.py create mode 100644 tests/common/ixia/qos_fixtures.py create mode 100644 tests/ixia/test_pfc_pause_lossless.py diff --git a/tests/common/ixia/common_helpers.py b/tests/common/ixia/common_helpers.py new file mode 100644 index 00000000000..288457defe6 --- /dev/null +++ b/tests/common/ixia/common_helpers.py @@ -0,0 +1,48 @@ +from netaddr import IPNetwork + +def ansible_stdout_to_str(ansible_stdout): + """ + @Summary: The stdout of Ansible host is essentially a list of unicode characters. This function converts it to a string. + @param ansible_stdout: stdout of Ansible + @return: Return a string + """ + result = "" + for x in ansible_stdout: + result += x.encode('UTF8') + return result + +""" +@Summary: Get Vlan subnet of a T0 device +@param host_ans: Ansible host instance of the device +@return: Vlan subnet, e.g., "192.168.1.1/24" where 192.168.1.1 is gateway and 24 is prefix length +""" +def get_vlan_subnet(host_ans): + mg_facts = host_ans.minigraph_facts(host=host_ans.hostname)['ansible_facts'] + mg_vlans = mg_facts['minigraph_vlans'] + + if len(mg_vlans) != 1: + print 'There should be only one Vlan at the DUT' + return None + + mg_vlan_intfs = mg_facts['minigraph_vlan_interfaces'] + prefix_len = mg_vlan_intfs[0]['prefixlen'] + gw_addr = ansible_stdout_to_str(mg_vlan_intfs[0]['addr']) + return gw_addr + '/' + str(prefix_len) + +""" +@Summary: Get N IP addresses in a subnet +@param subnet: IPv4 subnet, e.g., '192.168.1.1/24' +@param n: # of IP addresses to get +@return: Retuen n IPv4 addresses in this subnet in a list +""" +def get_addrs_in_subnet(subnet, n): + ip_addr = subnet.split('/')[0] + ip_addrs = [str(x) for x in list(IPNetwork(subnet))] + ip_addrs.remove(ip_addr) + + """ Try to avoid network and broadcast addresses """ + if len(ip_addrs) >= n + 2: + del ip_addrs[0] + del ip_addrs[-1] + + return ip_addrs[:n] diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py index 1c9a0a1482c..768d124f3ec 100644 --- a/tests/common/ixia/ixia_fixtures.py +++ b/tests/common/ixia/ixia_fixtures.py @@ -41,6 +41,15 @@ def ixia_api_serv_port(duthost): def ixia_api_serv_session_id(duthost): return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['session_id'] + +@pytest.fixture(scope = "module") +def ixia_dev(duthost, fanouthosts): + result = dict() + ixia_dev_hostnames = fanouthosts.keys() + for hostname in ixia_dev_hostnames: + result[hostname] = duthost.host.options['inventory_manager'].get_host(hostname).get_vars()['ansible_host'] + return result + """ IXIA session manager with PTF server """ @@ -54,7 +63,8 @@ def ixia_api_server_session(ixia_api_serv_ip, UserName = ixia_api_serv_user, Password = ixia_api_serv_passwd, RestPort = ixia_api_serv_port, - SessionId = ixia_api_serv_session_id) + SessionId = ixia_api_serv_session_id, + LogLevel='all') else : session = SessionAssistant(IpAddress = ixia_api_serv_ip, UserName = ixia_api_serv_user, diff --git a/tests/common/ixia/qos_fixtures.py b/tests/common/ixia/qos_fixtures.py new file mode 100644 index 00000000000..76c71db75c4 --- /dev/null +++ b/tests/common/ixia/qos_fixtures.py @@ -0,0 +1,31 @@ +import pytest + +@pytest.fixture(scope = "module") +def lossless_prio_dscp_map(duthost): + config_facts = duthost.config_facts(host=duthost.hostname, source="persistent")['ansible_facts'] + + if "PORT_QOS_MAP" not in config_facts.keys(): + return None + + port_qos_map = config_facts["PORT_QOS_MAP"] + lossless_priorities = list() + intf = port_qos_map.keys()[0] + if 'pfc_enable' not in port_qos_map[intf]: + return None + + lossless_priorities = [int(x) for x in port_qos_map[intf]['pfc_enable'].split(',')] + dscp_to_tc_map = config_facts["DSCP_TO_TC_MAP"] + + result = dict() + for prio in lossless_priorities: + result[prio] = list() + + profile = dscp_to_tc_map.keys()[0] + + for dscp in dscp_to_tc_map[profile]: + tc = dscp_to_tc_map[profile][dscp] + + if int(tc) in lossless_priorities: + result[int(tc)].append(int(dscp)) + + return result diff --git a/tests/ixia/test_pfc_pause_lossless.py b/tests/ixia/test_pfc_pause_lossless.py new file mode 100644 index 00000000000..ea12d27f36d --- /dev/null +++ b/tests/ixia/test_pfc_pause_lossless.py @@ -0,0 +1,195 @@ +from common.reboot import logger +import logging +import time +import pytest +from common.fixtures.conn_graph_facts import conn_graph_facts +from common.helpers.assertions import pytest_assert +from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user, ixia_api_serv_passwd, ixia_dev,\ + ixia_api_serv_port, ixia_api_serv_session_id, ixia_api_server_session +from common.ixia.ixia_helpers import get_neigh_ixia_mgmt_ip, get_neigh_ixia_card, get_neigh_ixia_port, \ + create_session, remove_session, config_ports, create_topology, start_protocols, create_ipv4_traffic, \ + create_pause_traffic, start_traffc, stop_traffic, get_statistics, IxiaFanoutManager + +from common.ixia.common_helpers import get_vlan_subnet, get_addrs_in_subnet + +from common.ixia.qos_fixtures import lossless_prio_dscp_map + +pytestmark = [pytest.mark.disable_loganalyzer] + +""" Data packet size in bytes """ +DATA_PKT_SIZE = 1024 + +""" +Run a PFC experiment + _________ + | | +IXIA tx_port ------ | DUT |------ IXIA rx_port + |_________| +IXIA sends test traffic and background traffic from tx_port +IXIA sends PFC pause frames from rx_port to pause priorities + +@param session: IXIA session +@param dut: Ansible instance of SONiC device under test (DUT) +@param tx_port: IXIA port to transmit traffic +@param rx_port: IXIA port to receive traffic +@param port_bw: bandwidth (in Mbps) of tx_port and rx_port +@param test_prio_list: PFC priorities of test traffic and PFC pause frames +@param test_dscp_list: DSCP values of test traffic +@param bg_dscp_list: DSCP values of background traffic +@param exp_dur: experiment duration in second +@param paused: if test traffic should be paused +""" +def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, test_dscp_list, bg_dscp_list,\ + exp_dur, paused): + + """ Disable DUT's PFC watchdog """ + dut.shell('sudo pfcwd stop') + + vlan_subnet = get_vlan_subnet(dut) + pytest_assert(vlan_subnet is not None, "Fail to get Vlan subnet information") + + gw_addr = vlan_subnet.split('/')[0] + """ One for sender and the other one for receiver """ + vlan_ip_addrs = get_addrs_in_subnet(vlan_subnet, 2) + + topo_receiver = create_topology(session=session, + name="Receiver", + ports=list(rx_port), + ip_start=vlan_ip_addrs[0], + ip_incr_step='0.0.0.1', + gw_start=gw_addr, + gw_incr_step='0.0.0.0') + + topo_sender = create_topology(session=session, + name="Sender", + ports=list(tx_port), + ip_start=vlan_ip_addrs[1], + ip_incr_step='0.0.0.1', + gw_start=gw_addr, + gw_incr_step='0.0.0.0') + + start_protocols(session) + + test_traffic = create_ipv4_traffic(session=session, + name='Test Data Traffic', + source=topo_sender, + destination=topo_receiver, + pkt_size=DATA_PKT_SIZE, + duration=exp_dur, + rate_percent=50, + start_delay=1, + dscp_list=test_dscp_list, + lossless_prio_list=test_prio_list) + + background_traffic = create_ipv4_traffic(session=session, + name='Background Data Traffic', + source=topo_sender, + destination=topo_receiver, + pkt_size=DATA_PKT_SIZE, + duration=exp_dur, + rate_percent=50, + start_delay=1, + dscp_list=bg_dscp_list, + lossless_prio_list=None) + + """ Pause time duration (in second) for each PFC pause frame """ + pause_dur_per_pkt = 65535 * 64 * 8.0 / (port_bw * 1000000) + + """ Do not specify duration here as we want it keep running """ + pfc_traffic = create_pause_traffic(session=session, + name='PFC Pause Storm', + source=rx_port, + pkt_per_sec=1.1/pause_dur_per_pkt, + start_delay=0, + global_pause=False, + pause_prio_list=test_prio_list) + + start_traffc(session) + + """ Wait for test and background traffic to finish """ + time.sleep(exp_dur+1.5) + + """ Capture traffic statistics """ + + flow_statistics = get_statistics(session) + logger.info(flow_statistics) + + for row_number, flow_stat in enumerate(flow_statistics.Rows): + tx_frames = int(flow_stat['Tx Frames']) + rx_frames = int(flow_stat['Rx Frames']) + + if 'Test' in flow_stat['Traffic Item']: + if paused: + pytest_assert(tx_frames>0 and rx_frames==0, "Test traffic should be fully paused") + else: + pytest_assert(tx_frames>0 and tx_frames==rx_frames, "Test traffic should not be impacted") + + elif 'PFC' in flow_stat['Traffic Item']: + pytest_assert(tx_frames>0 and rx_frames==0, "PFC packets should be dropped") + else: + pytest_assert(tx_frames>0 and tx_frames==rx_frames, "Background traffic should not be impacted") + + stop_traffic(session) + +def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map, duthost, ixia_dev, \ + ixia_api_server_session, fanout_graph_facts): + + port_list = list() + fanout_devices = IxiaFanoutManager(fanout_graph_facts) + fanout_devices.get_fanout_device_details(device_number = 0) + + device_conn = conn_graph_facts['device_conn'] + for intf in fanout_devices.ports(): + ixia_mgmt_ip = fanout_devices.get_chassis_ip() + (ixia_mgmt_ip, ixia_card, ixia_port) = \ + fanout_devices.getCardPort(intf) + + dutIntf = (intf.split('/'))[2] + port_list.append({'ip': ixia_mgmt_ip, + 'card_id': ixia_card, + 'port_id': ixia_port, + 'speed': int(device_conn[dutIntf]['speed'])}) + + """ The topology should have at least two interfaces """ + pytest_assert(len(device_conn)>=2, "The topology should have at least two interfaces") + + """ Test pausing each lossless priority individually """ + + session = ixia_api_server_session + for prio in lossless_prio_dscp_map: + for i in range(len(port_list)): + vports = config_ports(session, port_list) + + rx_id = i + tx_id = (i+1) % len(port_list) + + rx_port = vports[rx_id] + tx_port = vports[tx_id] + rx_port_bw = port_list[rx_id]['speed'] + tx_port_bw = port_list[tx_id]['speed'] + + pytest_assert(rx_port_bw == tx_port_bw) + + """ All the DSCP values mapped to this priority """ + test_dscp_list = lossless_prio_dscp_map[prio] + """ The other DSCP values """ + bg_dscp_list = [x for x in range(64) if x not in test_dscp_list] + + exp_dur = 2 + + run_pfc_exp(session=session, + dut=duthost, + tx_port=tx_port, + rx_port=rx_port, + port_bw=tx_port_bw, + test_prio_list=[prio], + test_dscp_list=test_dscp_list, + bg_dscp_list=bg_dscp_list, + exp_dur=exp_dur, + paused=True) + + ixNetwork = session.Ixnetwork + ixNetwork.NewConfig() + + remove_session(session) + From 318c287d289e8a1545638146395e7159de4e09fc Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Mon, 6 Jul 2020 10:37:05 +0000 Subject: [PATCH 17/52] modifying and rdma test case according to ixia proposal --- tests/common/ixia/ixia_helpers.py | 585 +++++++++++++++++++++++++++++- 1 file changed, 575 insertions(+), 10 deletions(-) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index 782521d7360..b4d57bfd92b 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -1,5 +1,8 @@ import re from common.reboot import * +from ixnetwork_restpy import SessionAssistant, Files + + """ @summary: given a DUT interface, return the management IP address of its neighbor IXIA device @param intf: DUT interface @@ -82,6 +85,16 @@ def __init__(self,fanout_data) : for i in fanout_data.keys() : self.fanout_list.append(fanout_data[i]) + def __parseFanoutConnections__ (self) : + device_conn = self.last_device_connection_details + retval = [] + for key in device_conn.keys() : + pp = device_conn[key]['peerport'] + string = key + '/' + pp + retval.append(string) + retval.sort() + return(retval) + def get_fanout_device_details (self, device_number) : # Pointer to chassis info @@ -101,21 +114,14 @@ def get_fanout_device_details (self, device_number) : #return self.fanout_list[self.last_fanout_assessed] - def __parseFanoutConnections__ (self) : - device_conn = self.last_device_connection_details - retval = [] - for key in device_conn.keys() : - pp = device_conn[key]['peerport'] - string = key + '/' + pp - retval.append(string) - retval.sort() - return(retval) + def get_connection_details (self) : + return(self.last_device_connection_details) def getCardPort (self, crd_prt) : ip = self.ip_address crd = (crd_prt.split('/')[0]).replace('Card', '') prt = (crd_prt.split('/')[1]).replace('Port', '') - return (ip, crd, prt) + return(ip, crd, prt) def get_chassis_ip (self) : return self.ip_address @@ -123,3 +129,562 @@ def get_chassis_ip (self) : def ports(self) : return self.current_ixia_port_list +#------------------------------------------------------------------------------ +# Newely added +#------------------------------------------------------------------------------ +def create_session(server_ip, username, password, log_file="ixia.log"): + + return SessionAssistant(IpAddress=server_ip, RestPort=None, UserName=username, Password=password, + SessionName=None, SessionId=None, ApiKey=None, ClearConfig=True, + LogLevel='all', LogFilename=log_file) + + +def remove_session(session): + session.Session.remove() + + +""" +Configure ports of the IXIA chassis +@param session: IXIA session +@param port_list: List of port locations. Each entry has four keys: 'ip', 'card_id', 'port_id', 'speed' +@return the list of ports if the configuration succeeds. Otherwise return None +""" +def config_ports(session, port_list): + port_map = session.PortMapAssistant() + vports = list() + + index = 1 + for port in port_list: + port_name = 'Port_{}'.format(index) + index += 1 + """ Map a test port location (ip, card, port) to a virtual port (name) """ + vports.append(port_map.Map(IpAddress=port['ip'], CardId=port['card_id'], + PortId=port['port_id'], Name=port_name)) + + """ Connect all mapped virtual ports to test port locations """ + port_map.Connect() + + ixnetwork = session.Ixnetwork + i = 0 + for vport in ixnetwork.Vport.find(): + vport.L1Config.CurrentType = 'novusHundredGigLanFcoe' + vport.L1Config.NovusHundredGigLan.Fcoe.PfcPriorityGroups = [0,1,2,3,4,5,6,7] + vport.L1Config.NovusHundredGigLan.IeeeL1Defaults = False + vport.L1Config.NovusHundredGigLan.EnableAutoNegotiation = False + vport.L1Config.NovusHundredGigLan.Speed = 'speed{}g'.format(port_list[i]['speed']/1000) + i += 1 + + return vports + + +""" +Configure capturing packets on a IXIA port +@param session: IXIA session +@param port: port to configure +@param capture_control_pkt: if enable capturing control plane packets +@param capture_data_pkt: if enable capturing data plane packets +""" +def config_port_capture_pkt(session, port, capture_control_pkt, capture_data_pkt): + if capture_control_pkt or capture_data_pkt: + port.RxMode = 'captureAndMeasure' + port.Capture.SoftwareEnabled = capture_control_pkt + port.Capture.HardwareEnabled = capture_data_pkt + +""" +Create a topology +@param session: IXIA session +@param name: Topology name +@param ports: List of ports +@param ip_start: Start value of IPv4 addresses, e.g., 192.168.1.1 +@param ip_incr_step: Increment step of IPv4 addresses, e.g., 0.0.0.1 +@param gw_start: Start value of gateway IPv4 addresses, e.g., 192.168.1.1 +@param gw_incr_step: Increment step of gateway IPv4 addresses, e.g., 0.0.0.0 (no increment) +@return the topology +""" +def create_topology(session, name, ports, ip_start, ip_incr_step, gw_start, gw_incr_step): + ixnetwork = session.Ixnetwork + + topology = ixnetwork.Topology.add(Name=name, Ports=ports) + ixnetwork.info('Creating Topology Group {}'.format(name)) + + device_group = topology.DeviceGroup.add(Name=name+' DG', Multiplier='1') + ethernet = device_group.Ethernet.add(Name='Ethernet') + + ipv4 = ethernet.Ipv4.add(Name='Ipv4') + ipv4.Address.Increment(start_value=ip_start, step_value=ip_incr_step) + ipv4.Address.Steps.Step = ip_incr_step + + ipv4.GatewayIp.Increment(start_value=gw_start, step_value=gw_incr_step) + ipv4.GatewayIp.Steps.Step = gw_incr_step + + ixnetwork.info('Configure IPv4') + + return topology + + +""" +Start protocols (e.g., IP and Ethernet) +@param session: IXIA session +""" +def start_protocols(session): + ixnetwork = session.Ixnetwork + ixnetwork.StartAllProtocols(Arg1='sync') + protocolSummary = session.StatViewAssistant('Protocols Summary') + protocolSummary.CheckCondition('Sessions Not Started', protocolSummary.EQUAL, 0) + protocolSummary.CheckCondition('Sessions Down', protocolSummary.EQUAL, 0) + logger.info(protocolSummary) + + +""" +Create a raw Ethernet/IP traffic item +@param session: IXIA session +@param name: name of traffic item +@param source: source endpoint +@param destination: destination endpoint +@param src_mac: source MAC address +@param dst_mac: destination MAC address +@param src_ip: source IP address +@param dst_ip: destination IP address +@param dscp_list: list of DSCPs +@param pkt_size: packet size +@param pkt_count: packet count (can be None if you want to keep running the traffic) +@param rate_percent: percentage of line rate +@param start_delay: start delay in second +@return the created traffic item +""" +def create_raw_traffic(session, name, source, destination, src_mac=None, dst_mac=None, src_ip=None, + dst_ip=None, dscp_list=None, pkt_size=64, pkt_count=None, rate_percent=100, + start_delay=0): + + ixnetwork = session.Ixnetwork + traffic_item = ixnetwork.Traffic.TrafficItem.add(Name=name, BiDirectional=False, TrafficType='raw') + + traffic_item.EndpointSet.add(Sources=source.Protocols.find(), Destinations=destination.Protocols.find()) + + traffic_config = traffic_item.ConfigElement.find()[0] + traffic_config.FrameRate.update(Type='percentLineRate', Rate=rate_percent) + traffic_config.FrameRateDistribution.PortDistribution = 'splitRateEvenly' + traffic_config.FrameSize.FixedSize = pkt_size + + if pkt_count is not None and pkt_count > 0: + traffic_config.TransmissionControl.update(Type='fixedFrameCount', FrameCount=pkt_count) + else: + traffic_config.TransmissionControl.update(Type='continuous') + + if start_delay > 0: + traffic_config.TransmissionControl.update(StartDelayUnits='nanoseconds', + StartDelay=start_delay*(10**6)) + + """ Add IP header """ + ip_stack_obj = create_pkt_hdr(ixnetwork=ixnetwork, traffic_item=traffic_item, + pkt_hdr_to_add='^IPv4', append_to_stack='Ethernet II') + + eth_stack_obj = traffic_item.ConfigElement.find().Stack.find('Ethernet II').Field.find() + set_eth_fields(eth_stack_obj=eth_stack_obj, src_mac=src_mac, dst_mac=dst_mac) + set_ip_fields(ip_stack_obj=ip_stack_obj, src_ip=src_ip, dst_ip=dst_ip, dscp_list=dscp_list) + + traffic_item.Tracking.find()[0].TrackBy = ['flowGroup0'] + + """ Push ConfigElement settings down to HighLevelStream resources """ + traffic_item.Generate() + + return traffic_item + + +""" +Create an IPv4 traffic item +@param session: IXIA session +@param name: name of traffic item +@param source: source endpoints +@param destination: destination endpoints +@param pkt_size: packet size +@param pkt_count: packet count +@param duration: traffic duration in second (positive integer only!) +@param rate_percent: percentage of line rate +@param start_delay: start delay in second +@param dscp_list: list of DSCPs +@param lossless_prio_list: list of lossless priorities +@param ecn_capable: if packets can get ECN marked +@return the created traffic item +""" +def create_ipv4_traffic(session, name, source, destination, pkt_size=64, pkt_count=None, duration=None, + rate_percent=100, start_delay=0, dscp_list=None, lossless_prio_list=None, + ecn_capable=False): + + ixnetwork = session.Ixnetwork + + traffic_item = ixnetwork.Traffic.TrafficItem.add(Name=name, BiDirectional=False, TrafficType='ipv4') + traffic_item.EndpointSet.add(Sources=source, Destinations=destination) + + traffic_config = traffic_item.ConfigElement.find()[0] + """ Todo: add sending rate support """ + traffic_config.FrameRate.update(Type='percentLineRate', Rate=rate_percent) + traffic_config.FrameRateDistribution.PortDistribution = 'splitRateEvenly' + traffic_config.FrameSize.FixedSize = pkt_size + + if pkt_count is not None and duration is not None: + print 'You can only specify either pkt_count or duration' + return None + + if pkt_count is not None: + traffic_config.TransmissionControl.update(Type='fixedFrameCount', FrameCount=pkt_count) + + elif duration is not None: + if type(duration) != int or duration <= 0: + print 'Invalid duration value {} (positive integer only)'.format(duration) + return None + else: + traffic_config.TransmissionControl.update(Type='fixedDuration', Duration=duration) + + else: + traffic_config.TransmissionControl.update(Type='continuous') + + if start_delay > 0: + traffic_config.TransmissionControl.update(StartDelayUnits='nanoseconds', + StartDelay=start_delay*(10**6)) + + if dscp_list is not None and len(dscp_list) > 0: + phb_field = traffic_item.ConfigElement.find().Stack.find('IPv4').Field.\ + find(DisplayName='Default PHB') + + phb_field.ActiveFieldChoice = True + phb_field.ValueType = 'valueList' + phb_field.ValueList = dscp_list + + """ Set ECN bits to 10 (ECN capable) """ + if ecn_capable: + phb_field = traffic_item.ConfigElement.find().Stack.find('IPv4').\ + Field.find(FieldTypeId='ipv4.header.priority.ds.phb.defaultPHB.unused') + phb_field.ActiveFieldChoice = True + phb_field.ValueType = 'singleValue' + phb_field.SingleValue = 2 + + if lossless_prio_list is not None and len(lossless_prio_list) > 0: + eth_stack = traffic_item.ConfigElement.find()[0].Stack.find(DisplayName='Ethernet II') + pfc_queue = eth_stack.Field.find(DisplayName='PFC Queue') + pfc_queue.ValueType = 'valueList' + pfc_queue.ValueList = lossless_prio_list + + traffic_item.Tracking.find()[0].TrackBy = ['flowGroup0'] + + """ Push ConfigElement settings down to HighLevelStream resources """ + traffic_item.Generate() + + return traffic_item + +""" +Create a pause traffic item +@param session: IXIA session +@param name: Name of traffic item +@param source: source endpoint +@param pkt_per_sec: packets per second +@param pkt_count: packet count +@param duration: traffic duration in second (positive integer only!) +@param start_delay: start delay in second +@param global_pause: if the generated packets are global pause (IEEE 802.3X PAUSE) +@param pause_prio_list: list of priorities to pause. Only valid when global_pause is False +@return the created traffic item or None if any errors happen +""" +def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, duration=None, + start_delay=0, global_pause=False, pause_prio_list=[]): + + if pause_prio_list is not None: + for prio in pause_prio_list: + if prio < 0 or prio > 7: + print 'Invalid pause priorities {}'.format(pause_prio_list) + return None + + ixnetwork = session.Ixnetwork + traffic_item = ixnetwork.Traffic.TrafficItem.add(Name=name, BiDirectional=False, TrafficType='raw') + + """ Since PFC packets will not be forwarded by the switch, so destinations are actually not used """ + traffic_item.EndpointSet.add(Sources=source.Protocols.find(), Destinations=source.Protocols.find()) + + traffic_config = traffic_item.ConfigElement.find()[0] + traffic_config.FrameRate.update(Type='framesPerSecond', Rate=pkt_per_sec) + traffic_config.FrameRateDistribution.PortDistribution = 'splitRateEvenly' + traffic_config.FrameSize.FixedSize = 64 + + if pkt_count is not None and duration is not None: + print 'You can only specify either pkt_count or duration' + return None + + if pkt_count is not None: + traffic_config.TransmissionControl.update(Type='fixedFrameCount', FrameCount=pkt_count) + + elif duration is not None: + if type(duration) != int or duration <= 0: + print 'Invalid duration value {} (positive integer only)'.format(duration) + return None + else: + traffic_config.TransmissionControl.update(Type='fixedDuration', Duration=duration) + + else: + traffic_config.TransmissionControl.update(Type='continuous') + + if start_delay > 0: + traffic_config.TransmissionControl.update(StartDelayUnits='nanoseconds', + StartDelay=start_delay*(10**6)) + + """ Add PFC header """ + pfc_stack_obj = create_pkt_hdr(ixnetwork=ixnetwork, + traffic_item=traffic_item, + pkt_hdr_to_add='^PFC PAUSE \(802.1Qbb\)', + append_to_stack='Ethernet II') + + """ Construct global pause and PFC packets """ + if global_pause: + set_global_pause_fields(pfc_stack_obj) + else: + set_pfc_fields(pfc_stack_obj, pause_prio_list) + + """ Remove Ethernet header """ + traffic_item.ConfigElement.find()[0].Stack.find(DisplayName="Ethernet II").Remove() + + traffic_item.Tracking.find()[0].TrackBy = ['flowGroup0'] + + """ Push ConfigElement settings down to HighLevelStream resources """ + traffic_item.Generate() + + return traffic_item + + +def set_global_pause_fields(pfc_stack_obj): + code = pfc_stack_obj.find(DisplayName='Control opcode') + code.ValueType = 'singleValue' + code.SingleValue = '1' + + """ This field is pause duration in global pause packet """ + prio_enable_vector = pfc_stack_obj.find(DisplayName='priority_enable_vector') + prio_enable_vector.ValueType = 'singleValue' + prio_enable_vector.SingleValue = 'ffff' + + """ pad bytes """ + for i in range(8): + pause_duration = pfc_stack_obj.find(DisplayName='PFC Queue {}'.format(i)) + pause_duration.ValueType = 'singleValue' + pause_duration.SingleValue = '0' + + +def set_eth_fields(eth_stack_obj, src_mac, dst_mac): + if src_mac is not None: + src_mac_field = eth_stack_obj.find(DisplayName='Source MAC Address') + src_mac_field.ValueType = 'singleValue' + src_mac_field.SingleValue = src_mac + + if dst_mac is not None: + dst_mac_field = eth_stack_obj.find(DisplayName='Destination MAC Address') + dst_mac_field.ValueType = 'singleValue' + dst_mac_field.SingleValue = dst_mac + + +def set_ip_fields(ip_stack_obj, src_ip, dst_ip, dscp_list): + if src_ip is not None: + src_ip_field = ip_stack_obj.find(DisplayName='Source Address') + src_ip_field.ValueType = 'singleValue' + src_ip_field.SingleValue = src_ip + + if dst_ip is not None: + dst_ip_field = ip_stack_obj.find(DisplayName='Destination Address') + dst_ip_field.ValueType = 'singleValue' + dst_ip_field.SingleValue = dst_ip + + if dscp_list is not None and len(dscp_list) > 0: + phb_field = ip_stack_obj.find(DisplayName='Default PHB') + phb_field.ActiveFieldChoice = True + phb_field.ValueType = 'valueList' + phb_field.ValueList = dscp_list + + +def set_pfc_fields(pfc_stack_obj, pause_prio_list): + code = pfc_stack_obj.find(DisplayName='Control opcode') + code.ValueType = 'singleValue' + code.SingleValue = '101' + + prio_enable_vector = pfc_stack_obj.find(DisplayName='priority_enable_vector') + prio_enable_vector.ValueType = 'singleValue' + val = 0 + for prio in pause_prio_list: + val += (1 << prio) + prio_enable_vector.SingleValue = hex(val) + + for i in range(8): + pause_duration = pfc_stack_obj.find(DisplayName='PFC Queue {}'.format(i)) + pause_duration.ValueType = 'singleValue' + + if i in pause_prio_list: + pause_duration.SingleValue = 'ffff' + else: + pause_duration.SingleValue = '0' + + +def create_pkt_hdr(ixnetwork, traffic_item, pkt_hdr_to_add, append_to_stack): + #Add new packet header in traffic item + config_element = traffic_item.ConfigElement.find()[0] + + # Do the followings to add packet headers on the new traffic item + + # Uncomment this to show a list of all the available protocol templates to create (packet headers) + #for protocolHeader in ixNetwork.Traffic.ProtocolTemplate.find(): + # ixNetwork.info('Protocol header: -- {} --'.format(protocolHeader.DisplayName)) + + # 1> Get the protocol template from the ProtocolTemplate list. + pkt_hdr_proto_template = ixnetwork.Traffic.ProtocolTemplate.find(DisplayName=pkt_hdr_to_add) + #ixNetwork.info('protocolTemplate: {}'.format(packetHeaderProtocolTemplate)) + + # 2> Append the object after the specified packet header stack. + append_to_stack_obj = config_element.Stack.find(DisplayName=append_to_stack) + #ixNetwork.info('appendToStackObj: {}'.format(appendToStackObj)) + append_to_stack_obj.Append(Arg2=pkt_hdr_proto_template) + + # 3> Get the new packet header stack to use it for appending an IPv4 stack after it. + # Look for the packet header object and stack ID. + pkt_hdr_stack_obj = config_element.Stack.find(DisplayName=pkt_hdr_to_add) + + # 4> In order to modify the fields, get the field object + pkt_hdr_field_obj = pkt_hdr_stack_obj.Field.find() + #ixNetwork.info('packetHeaderFieldObj: {}'.format(packetHeaderFieldObj)) + + # 5> Save the above configuration to the base config file. + #ixNetwork.SaveConfig(Files('baseConfig.ixncfg', local_file=True)) + + return pkt_hdr_field_obj + + +""" +Get statistics +@param session: IXIA session +@return statistics information +""" +def get_statistics(session): + ixnetwork = session.Ixnetwork + flow_statistics = session.StatViewAssistant('Flow Statistics') + ixnetwork.info('{}\n'.format(flow_statistics)) + return flow_statistics + + +""" +Start all the traffic items +@param session: IXIA session +""" +def start_traffc(session): + ixnetwork = session.Ixnetwork + """ Apply traffic to hardware """ + ixnetwork.Traffic.Apply() + """ Run traffic """ + ixnetwork.Traffic.StartStatelessTrafficBlocking() + + +""" +Stop all the traffic items +@param session: IXIA session +""" +def stop_traffic(session): + ixnetwork = session.Ixnetwork + ixnetwork.Traffic.StopStatelessTrafficBlocking() + +""" +Start capturing traffic +@param session: IXIA session +""" +def start_capture(session): + ixnetwork = session.Ixnetwork + ixnetwork.StartCapture() + +""" +Stop capturing traffic +@param session: IXIA session +""" +def stop_capture(session): + ixnetwork = session.Ixnetwork + ixnetwork.StopCapture() + +""" +Save the captured packets to a new user specifided location on the API server +@param session: IXIA session +@param dir: directory +@return the list of relative paths of all the captures saves +""" +def save_capture_pkts(session, dir=''): + ixnetwork = session.Ixnetwork + capture_files = ixnetwork.SaveCaptureFiles(Arg1=dir) + ixnetwork.info('Capture files: {}'.format(capture_files)) + return capture_files + +""" +Download a file from the API server +@param session: IXIA session +@param remote_filename: the name of the remote file +@param local_filename: the name that the remote contents will be saved to +@return the local file name +""" +def download_file(session, remote_filename, local_filename): + return session.Session.DownloadFile(remote_filename, local_filename) + +""" +Get all data packets captured in this port. +For each captured data packets, this function parses its every packet header +(e.g., Ethernet and IP) and every packet field (e.g., destination IP and ECN) +@param session: IXIA session +@param port: port where we capture pdata ackets +@return the list of parsed data packets. +""" +def get_captured_data_pkts(session, port): + result = list() + pkt_count = port.Capture.DataPacketCounter + ixnetwork = session.Ixnetwork + + ixnetwork.info('Total data packets captured: {}'.format(pkt_count)) + + for i in range(pkt_count): + pkt = dict() + + port.Capture.CurrentPacket.GetPacketFromDataCapture(Arg2=i) + pkt_hdr_stacks = port.Capture.CurrentPacket.Stack.find() + + for pkt_hdr in pkt_hdr_stacks.find(): + ixnetwork.info('\nPacketHeaderName: {}'.format(pkt_hdr.DisplayName)) + pkt[pkt_hdr.DisplayName] = dict() + + for field in pkt_hdr.Field.find(): + ixnetwork.info('\t{}: {}'.format(field.DisplayName, field.FieldValue)) + pkt[pkt_hdr.DisplayName][field.DisplayName] = field.FieldValue + + result.append(pkt) + + return result + +""" +Get the value of a specific field of the packet +@param pkt: packet +@param header: header name, e.g., "Internet Protocol" +@param field: field name, e.g., 'Explicit Congestion Notification' +@return the field value of None if the packet does not have this field +""" +def get_pkt_field(pkt, header, field): + if header not in pkt: + return None + + pkt_hdr = pkt[header] + if field not in pkt_hdr: + return None + + return pkt[header][field] + +""" +Get the ECN field value of the packet +@param pkt: packet +@return the ECN value +""" +def get_ecn(pkt): + return get_pkt_field(pkt=pkt, header='Internet Protocol', field='Explicit Congestion Notification') + +""" +Get the DSCP field value of the packet +@param pkt: packet +@return the DSCP value (or None the packets does not have this field) +""" +def get_dscp(pkt): + return get_pkt_field(pkt=pkt, header='Internet Protocol', field='Differentiated Services Codepoint') + + From c33e7c8fd43138043ee3d307c974922331723886 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Tue, 14 Jul 2020 04:35:54 +0000 Subject: [PATCH 18/52] removing the low level api from test_ixia_traffic_restpy.py --- tests/common/ixia/common_helpers.py | 7 + tests/common/ixia/ixia_helpers.py | 171 ++++++++++++++++++++----- tests/ixia/test_ixia_traffic_restpy.py | 133 ++++++------------- 3 files changed, 179 insertions(+), 132 deletions(-) diff --git a/tests/common/ixia/common_helpers.py b/tests/common/ixia/common_helpers.py index 288457defe6..bb4578da599 100644 --- a/tests/common/ixia/common_helpers.py +++ b/tests/common/ixia/common_helpers.py @@ -1,5 +1,12 @@ +import ipaddr from netaddr import IPNetwork +def incriment_ip_address (ip, incrment=1) : + ipaddress = ipaddr.IPv4Address(ip) + ipaddress = ipaddress + incrment + return_value = ipaddress._string_from_ip_int(ipaddress._ip) + return(return_value) + def ansible_stdout_to_str(ansible_stdout): """ @Summary: The stdout of Ansible host is essentially a list of unicode characters. This function converts it to a string. diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index b4d57bfd92b..c7f0868f0fc 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -70,10 +70,11 @@ def parseFanoutConnections (device_conn) : retval.sort() return(retval) -def getCardPort(i) : - crd = (i.split('/')[0]).replace('Card', '') - prt = (i.split('/')[1]).replace('Port', '') - return (crd, prt) +def get_card_port(i) : + chassis = i.split('/')[0] + crd = (i.split('/')[1]).replace('Card', '') + prt = (i.split('/')[2]).replace('Port', '') + return (chassis, crd, prt) class IxiaFanoutManager () : def __init__(self,fanout_data) : @@ -85,12 +86,12 @@ def __init__(self,fanout_data) : for i in fanout_data.keys() : self.fanout_list.append(fanout_data[i]) - def __parseFanoutConnections__ (self) : + def __parse_fanout_connections__ (self) : device_conn = self.last_device_connection_details retval = [] for key in device_conn.keys() : - pp = device_conn[key]['peerport'] - string = key + '/' + pp + #pp = device_conn[key]['peerport'] + string = self.ip_address + '/' + key retval.append(string) retval.sort() return(retval) @@ -110,18 +111,22 @@ def get_fanout_device_details (self, device_number) : # List of chassis cards and ports self.current_ixia_port_list = \ - self.__parseFanoutConnections__() + self.__parse_fanout_connections__() #return self.fanout_list[self.last_fanout_assessed] def get_connection_details (self) : return(self.last_device_connection_details) - def getCardPort (self, crd_prt) : - ip = self.ip_address - crd = (crd_prt.split('/')[0]).replace('Card', '') - prt = (crd_prt.split('/')[1]).replace('Port', '') - return(ip, crd, prt) + def get_card_port (self, crd_prt) : + chassis = i.split('/')[0] + if (self.ip_address == chassis) : + crd = (i.split('/')[1]).replace('Card', '') + prt = (i.split('/')[2]).replace('Port', '') + else : + pytest_assert(0) + + return(chassis, crd, prt) def get_chassis_ip (self) : return self.ip_address @@ -133,7 +138,6 @@ def ports(self) : # Newely added #------------------------------------------------------------------------------ def create_session(server_ip, username, password, log_file="ixia.log"): - return SessionAssistant(IpAddress=server_ip, RestPort=None, UserName=username, Password=password, SessionName=None, SessionId=None, ApiKey=None, ClearConfig=True, LogLevel='all', LogFilename=log_file) @@ -149,17 +153,30 @@ def remove_session(session): @param port_list: List of port locations. Each entry has four keys: 'ip', 'card_id', 'port_id', 'speed' @return the list of ports if the configuration succeeds. Otherwise return None """ -def config_ports(session, port_list): +def configure_ports(session, + port_list, + start_name='port', + pfc_priotity_groups=[0,1,2,3,4,5,6,7], + ieee_l1_defaults=False, + enable_auto_negotiation=False, + port_speed = 10000000): + port_map = session.PortMapAssistant() vports = list() index = 1 for port in port_list: - port_name = 'Port_{}'.format(index) + port_name = start_name + '-' + str(index) index += 1 """ Map a test port location (ip, card, port) to a virtual port (name) """ - vports.append(port_map.Map(IpAddress=port['ip'], CardId=port['card_id'], - PortId=port['port_id'], Name=port_name)) + chassis_card_port = port.split('/') + + vports.append(port_map.Map( + IpAddress=chassis_card_port[0], + CardId=chassis_card_port[1].replace('Card', ''), + PortId=chassis_card_port[2].replace('Port', ''), + Name=port_name) + ) """ Connect all mapped virtual ports to test port locations """ port_map.Connect() @@ -168,10 +185,18 @@ def config_ports(session, port_list): i = 0 for vport in ixnetwork.Vport.find(): vport.L1Config.CurrentType = 'novusHundredGigLanFcoe' - vport.L1Config.NovusHundredGigLan.Fcoe.PfcPriorityGroups = [0,1,2,3,4,5,6,7] - vport.L1Config.NovusHundredGigLan.IeeeL1Defaults = False - vport.L1Config.NovusHundredGigLan.EnableAutoNegotiation = False - vport.L1Config.NovusHundredGigLan.Speed = 'speed{}g'.format(port_list[i]['speed']/1000) + + vport.L1Config.NovusHundredGigLan.Fcoe.PfcPriorityGroups =\ + pfc_priotity_groups + + vport.L1Config.NovusHundredGigLan.IeeeL1Defaults =\ + ieee_l1_defaults + + vport.L1Config.NovusHundredGigLan.EnableAutoNegotiation =\ + enable_auto_negotiation + + vport.L1Config.NovusHundredGigLan.Speed = port_speed + i += 1 return vports @@ -201,7 +226,16 @@ def config_port_capture_pkt(session, port, capture_control_pkt, capture_data_pkt @param gw_incr_step: Increment step of gateway IPv4 addresses, e.g., 0.0.0.0 (no increment) @return the topology """ -def create_topology(session, name, ports, ip_start, ip_incr_step, gw_start, gw_incr_step): +def create_topology( + session, + ports, + name='Topology 1', + ip_type='ipv4', + ip_start='10.0.0.1', + ip_incr_step='0.0.1.0', + gw_start='10.0.0.2', + gw_incr_step='0.0.1.0'): + ixnetwork = session.Ixnetwork topology = ixnetwork.Topology.add(Name=name, Ports=ports) @@ -209,19 +243,22 @@ def create_topology(session, name, ports, ip_start, ip_incr_step, gw_start, gw_i device_group = topology.DeviceGroup.add(Name=name+' DG', Multiplier='1') ethernet = device_group.Ethernet.add(Name='Ethernet') - - ipv4 = ethernet.Ipv4.add(Name='Ipv4') - ipv4.Address.Increment(start_value=ip_start, step_value=ip_incr_step) - ipv4.Address.Steps.Step = ip_incr_step - - ipv4.GatewayIp.Increment(start_value=gw_start, step_value=gw_incr_step) - ipv4.GatewayIp.Steps.Step = gw_incr_step - - ixnetwork.info('Configure IPv4') + if (ip_type == 'ipv4') : + ixnetwork.info('Configure IPv4') + ipv4 = ethernet.Ipv4.add(Name='Ipv4') + ipv4.Address.Increment(start_value=ip_start, step_value=ip_incr_step) + ipv4.Address.Steps.Step = ip_incr_step + + ipv4.GatewayIp.Increment(start_value=gw_start, step_value=gw_incr_step) + ipv4.GatewayIp.Steps.Step = gw_incr_step + elif (ip_type == 'ipv6') : + # TBD + pass + else : + pytest_assert(0) return topology - """ Start protocols (e.g., IP and Ethernet) @param session: IXIA session @@ -307,8 +344,17 @@ def create_raw_traffic(session, name, source, destination, src_mac=None, dst_mac @param ecn_capable: if packets can get ECN marked @return the created traffic item """ -def create_ipv4_traffic(session, name, source, destination, pkt_size=64, pkt_count=None, duration=None, - rate_percent=100, start_delay=0, dscp_list=None, lossless_prio_list=None, +def create_ipv4_traffic(session, + name, + source, + destination, + pkt_size=64, + pkt_count=None, + duration=None, + rate_percent=100, + start_delay=0, + dscp_list=None, + lossless_prio_list=None, ecn_capable=False): ixnetwork = session.Ixnetwork @@ -687,4 +733,59 @@ def get_ecn(pkt): def get_dscp(pkt): return get_pkt_field(pkt=pkt, header='Internet Protocol', field='Differentiated Services Codepoint') +def start_traffic(session): + ixnetwork = session.Ixnetwork + """ Apply traffic to hardware """ + ixnetwork.Traffic.Apply() + """ Run traffic """ + ixnetwork.Traffic.StartStatelessTrafficBlocking() + +def stop_traffic(session): + ixnetwork = session.Ixnetwork + ixnetwork.Traffic.StopStatelessTrafficBlocking() +def create_ip_traffic_item_using_wizard_arguments ( + session, + src_start_port, + src_port_count, + src_first_route_index, + src_route_count, + dst_start_port, + dst_port_count, + dst_first_route_index, + dst_route_count, + name='example_traffic', + traffic_type='ipv4') : + + traffic_item = session.Ixnetwork.Traffic.TrafficItem.add( + Name = name, + TrafficType = traffic_type) + + if (traffic_type == 'ipv4') : + obj = '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv4/1' + elif (traffic_type == 'ipv6'): + obj = '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv6/1' + else : + pytest_assert(0) + + src = [{'arg1': obj, + 'arg2': src_start_port, + 'arg3': src_port_count, + 'arg4': src_first_route_index, + 'arg5': dst_route_count} + ] + + dst = [{'arg1': obj, + 'arg2': dst_start_port, + 'arg3': dst_port_count, + 'arg4': dst_first_route_index, + 'arg5': dst_route_count} + ] + + endPoint = traffic_item.EndpointSet.add() + endPoint.ScalableSources = src + endPoint.ScalableDestinations = dst + + # Enable tracking. + traffic_item.Tracking.find().TrackBy = ['trackingenabled0'] + return traffic_item diff --git a/tests/ixia/test_ixia_traffic_restpy.py b/tests/ixia/test_ixia_traffic_restpy.py index d46006b8ab6..3d4a870571b 100644 --- a/tests/ixia/test_ixia_traffic_restpy.py +++ b/tests/ixia/test_ixia_traffic_restpy.py @@ -14,45 +14,21 @@ import pytest import ipaddr from common.utilities import wait_until -from common.fixtures.conn_graph_facts import conn_graph_facts, fanout_graph_facts -from common.reboot import * +from common.fixtures.conn_graph_facts import conn_graph_facts, \ + fanout_graph_facts + +from common.reboot import logger from ixnetwork_restpy import SessionAssistant, Files from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id, \ ixia_api_server_session -from common.ixia.ixia_helpers import get_neigh_ixia_mgmt_ip, get_neigh_ixia_card,\ - get_neigh_ixia_port, IxiaFanoutManager - -import time - -def create_ipv4_traffic_end_points ( - src_start_port, - src_port_count, - src_first_route_index, - src_route_count, - dst_start_port, - dst_port_count, - dst_first_route_index, - dst_route_count) : - - src = [{'arg1': '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv4/1', - 'arg2': src_start_port, - 'arg3': src_port_count, - 'arg4': src_first_route_index, - 'arg5': dst_route_count} - ] - - dst = [{'arg1': '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv4/1', - 'arg2': dst_start_port, - 'arg3': dst_port_count, - 'arg4': dst_first_route_index, - 'arg5': dst_route_count} - ] - - return (src, dst) +from common.ixia.ixia_helpers import IxiaFanoutManager, configure_ports,\ + create_topology, create_ip_traffic_item_using_wizard_arguments,\ + start_protocols, start_traffic, stop_traffic +from common.ixia.common_helpers import incriment_ip_address def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, ixia_api_server_session, fanouthosts): @@ -62,92 +38,55 @@ def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, logger.info("DUT hostname = %s" %(duthost.hostname)) mg_facts = duthost.minigraph_facts(host=duthost.hostname) - gatewayIp = mg_facts['ansible_facts']['minigraph_vlan_interfaces'][0]['addr'] + gateway_ip = mg_facts['ansible_facts']['minigraph_vlan_interfaces'][0]['addr'] + start_interface_ip = incriment_ip_address(gateway_ip) ixiaFanoutHostList = IxiaFanoutManager(fanout_graph_facts) ixiaFanoutHostList.get_fanout_device_details(device_number = 0) - # Build gateway valuelist. Same gateway IP for all interface - gateway_value_list = [] - for i in ixiaFanoutHostList.ports() : - gateway_value_list.append(gatewayIp) - - # Create ixNetwork interface IP address list - interface_ip_list = [] - ipaddress = ipaddr.IPv4Address(gatewayIp) - for i in ixiaFanoutHostList.ports() : - ipaddress = ipaddress + 1 - interface_ip_list.append(ipaddress._string_from_ip_int(ipaddress._ip)) - - session = ixia_api_server_session + session = ixia_api_server_session ixNetwork = session.Ixnetwork - portMap = session.PortMapAssistant() + portMap = session.PortMapAssistant() - vport_list = [] - for i in ixiaFanoutHostList.ports() : - (chassisIp, cardId, portId) = ixiaFanoutHostList.getCardPort(i) - vport_list.append(portMap.Map(chassisIp, cardId, portId)) - t1 = time.time() - portMap.Connect(ChassisTimeout=1200, ForceOwnership=True) + port_list = configure_ports(session=session, + port_list=ixiaFanoutHostList.ports()) t2 = time.time() time_taken = t2 - t1 logger.info("time-taken to connect = %s" %(time_taken)) - - for vport in vport_list : - vport.L1Config.NovusHundredGigLan.IeeeL1Defaults = False - vport.L1Config.NovusHundredGigLan.EnableAutoNegotiation = False - vport.L1Config.NovusHundredGigLan.EnableRsFec = True - vport.L1Config.NovusHundredGigLan.EnableRsFecStats = True - - topology1 = ixNetwork.Topology.add(Name='Topo1', Ports=vport_list) - deviceGroup1 = topology1.DeviceGroup.add(Name='DG1', Multiplier='1') - ethernet1 = deviceGroup1.Ethernet.add(Name='Eth1') - ipv4 = ethernet1.Ipv4.add(Name='Ipv4') - - ipv4.GatewayIp.ValueList(gateway_value_list) - ipv4.Address.ValueList(interface_ip_list) - - ixNetwork.StartAllProtocols() + + topology = create_topology(session=session, + ports=port_list, + name="Sender", + ip_start=start_interface_ip, + ip_incr_step='0.0.0.1', + gw_start=gateway_ip, + gw_incr_step='0.0.0.0') + + start_protocols(session) logger.info("Wait for 5 seconds for iv4 sessions to up") time.sleep(5) # Create a traffic item - traffic_item = ixNetwork.Traffic.TrafficItem.add( - Name = 'Traffic Test', - TrafficType = 'ipv4') - - # Create a ipv4 source and destination for the endpoint of traffic item. - src_dst_ep = create_ipv4_traffic_end_points ( - src_start_port = 1, - src_port_count = 1, - src_first_route_index = 1, - src_route_count = 1, - dst_start_port = 2, - dst_port_count = 3, - dst_first_route_index = 1, - dst_route_count = 1 - ) - - # Create endpoint set and set source and destination. - endPoint = traffic_item.EndpointSet.add() - endPoint.ScalableSources = src_dst_ep[0] - endPoint.ScalableDestinations = src_dst_ep[1] - - # Enable tracking. - traffic_item.Tracking.find().TrackBy = ['trackingenabled0'] + traffic_item = create_ip_traffic_item_using_wizard_arguments( + session=session, + src_start_port=1, + src_port_count=1, + src_first_route_index=1, + src_route_count=1, + dst_start_port=2, + dst_port_count=3, + dst_first_route_index=1, + dst_route_count=1) # Generate, apply and start traffic. - traffic_item.Generate() - ixNetwork.Traffic.Apply() - ixNetwork.Traffic.Start() + start_traffic(session) logger.info("run traffic for 5 seconds") time.sleep(5) # Fetch statistics. logger.info(session.StatViewAssistant('Traffic Item Statistics')) - ixNetwork.Traffic.Stop() - assert 1 + stop_traffic(session) From b789490a80c022ce2e00a14fc42d8a4596a4c70b Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Wed, 15 Jul 2020 06:42:42 +0000 Subject: [PATCH 19/52] changed test scripts and lib functions --- tests/common/ixia/ixia_helpers.py | 153 ++++++++++++++++--------- tests/ixia/test_ixia_traffic_restpy.py | 8 +- tests/ixia/test_pfc_pause_lossless.py | 17 +-- 3 files changed, 110 insertions(+), 68 deletions(-) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index c7f0868f0fc..9d8c7571441 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -1,7 +1,7 @@ import re -from common.reboot import * +from common.reboot import logger from ixnetwork_restpy import SessionAssistant, Files - +import pytest """ @summary: given a DUT interface, return the management IP address of its neighbor IXIA device @@ -61,20 +61,20 @@ def get_neigh_ixia_port(intf, conn_graph_facts): else: return m.group(2) -def parseFanoutConnections (device_conn) : - retval = [] - for key in device_conn.keys() : - pp = device_conn[key]['peerport'] - string = key + '/' + pp - retval.append(string) - retval.sort() - return(retval) - -def get_card_port(i) : - chassis = i.split('/')[0] - crd = (i.split('/')[1]).replace('Card', '') - prt = (i.split('/')[2]).replace('Port', '') - return (chassis, crd, prt) +#def parseFanoutConnections (device_conn) : +# retval = [] +# for key in device_conn.keys() : +# pp = device_conn[key]['peerport'] +# string = key + '/' + pp +# retval.append(string) +# retval.sort() +# return(retval) + +#def get_card_port(i) : +# chassis = i.split('/')[0] +# crd = (i.split('/')[1]).replace('Card', '') +# prt = (i.split('/')[2]).replace('Port', '') +# return (chassis, crd, prt) class IxiaFanoutManager () : def __init__(self,fanout_data) : @@ -90,8 +90,8 @@ def __parse_fanout_connections__ (self) : device_conn = self.last_device_connection_details retval = [] for key in device_conn.keys() : - #pp = device_conn[key]['peerport'] - string = self.ip_address + '/' + key + pp = device_conn[key]['peerport'] + string = self.ip_address + '/' + key + '/' + pp retval.append(string) retval.sort() return(retval) @@ -118,21 +118,33 @@ def get_fanout_device_details (self, device_number) : def get_connection_details (self) : return(self.last_device_connection_details) - def get_card_port (self, crd_prt) : - chassis = i.split('/')[0] - if (self.ip_address == chassis) : - crd = (i.split('/')[1]).replace('Card', '') - prt = (i.split('/')[2]).replace('Port', '') - else : - pytest_assert(0) - - return(chassis, crd, prt) + #def get_card_port (self, i) : + # chs = i.split('/')[0] + # if (self.ip_address == chs) : + # crd = (i.split('/')[1]).replace('Card', '') + # prt = (i.split('/')[2]).replace('Port', '') + # peer_port = i.split('/')[3] + # else : + # pytest_assert(0, 'Invalid portlist %s for chassis %s' %(chs, prt)) + # + # return(chs, crd, prt, peer_port) def get_chassis_ip (self) : return self.ip_address def ports(self) : - return self.current_ixia_port_list + retval = [] + for ports in self.current_ixia_port_list: + info_list = ports.split('/') + dict_element = { + 'ip': info_list[0], + 'card_id': info_list[1].replace('Card', ''), + 'port_id': info_list[2].replace('Port', ''), + 'peer_port': info_list[3], + } + retval.append(dict_element) + + return retval #------------------------------------------------------------------------------ # Newely added @@ -153,54 +165,91 @@ def remove_session(session): @param port_list: List of port locations. Each entry has four keys: 'ip', 'card_id', 'port_id', 'speed' @return the list of ports if the configuration succeeds. Otherwise return None """ -def configure_ports(session, - port_list, - start_name='port', - pfc_priotity_groups=[0,1,2,3,4,5,6,7], - ieee_l1_defaults=False, - enable_auto_negotiation=False, - port_speed = 10000000): +def configure_ports(session, port_list, start_name='port') : port_map = session.PortMapAssistant() + ixnetwork = session.Ixnetwork vports = list() + + # Add default vport properties here. If vport property is not available in + # port_list dictionary get it from here + port_property = { + 'speed': 10000000, + 'ieee_l1_defaults': False, + 'pfc_priotity_groups': [0,1,2,3,4,5,6,7], + 'card_type': 'novusHundredGigLanFcoe', + 'enable_auto_negotiation': False + } index = 1 - for port in port_list: - port_name = start_name + '-' + str(index) + for port in port_list: + port_name = start_name + '-' + str(index) index += 1 """ Map a test port location (ip, card, port) to a virtual port (name) """ - chassis_card_port = port.split('/') - vports.append(port_map.Map( - IpAddress=chassis_card_port[0], - CardId=chassis_card_port[1].replace('Card', ''), - PortId=chassis_card_port[2].replace('Port', ''), + IpAddress=port['ip'], + CardId=port['card_id'], + PortId=port['port_id'], Name=port_name) ) """ Connect all mapped virtual ports to test port locations """ port_map.Connect() - - ixnetwork = session.Ixnetwork + + # Set L1 config i = 0 for vport in ixnetwork.Vport.find(): - vport.L1Config.CurrentType = 'novusHundredGigLanFcoe' + vport.L1Config.CurrentType = \ + port_list[i].get('card_type', port_property['card_type']) + + vport.L1Config.NovusHundredGigLan.Fcoe.PfcPriorityGroups = \ + port_list[i].get('pfc_priotity_groups', + port_property['pfc_priotity_groups']) - vport.L1Config.NovusHundredGigLan.Fcoe.PfcPriorityGroups =\ - pfc_priotity_groups - vport.L1Config.NovusHundredGigLan.IeeeL1Defaults =\ - ieee_l1_defaults + vport.L1Config.NovusHundredGigLan.IeeeL1Defaults = \ + port_list[i].get('ieee_l1_defaults', + port_property['ieee_l1_defaults']) - vport.L1Config.NovusHundredGigLan.EnableAutoNegotiation =\ - enable_auto_negotiation + vport.L1Config.NovusHundredGigLan.EnableAutoNegotiation = \ + port_list[i].get('enable_auto_negotiation', + port_property['enable_auto_negotiation']) - vport.L1Config.NovusHundredGigLan.Speed = port_speed + port_speed = port_list[i].get('speed', port_property['speed']) + vport.L1Config.NovusHundredGigLan.Speed = \ + 'speed{}g'.format(port_speed/1000) i += 1 return vports +#def config_ports(session, port_list): +# port_map = session.PortMapAssistant() +# vports = list() +# +# index = 1 +# for port in port_list: +# port_name = 'Port_{}'.format(index) +# index += 1 +# """ Map a test port location (ip, card, port) to a virtual port (name) """ +# vports.append(port_map.Map(IpAddress=port['ip'], CardId=port['card_id'], +# PortId=port['port_id'], Name=port_name)) +# +# """ Connect all mapped virtual ports to test port locations """ +# port_map.Connect() +# +# ixnetwork = session.Ixnetwork +# i = 0 +# for vport in ixnetwork.Vport.find(): +# vport.L1Config.CurrentType = 'novusHundredGigLanFcoe' +# vport.L1Config.NovusHundredGigLan.Fcoe.PfcPriorityGroups = [0,1,2,3,4,5,6,7] +# vport.L1Config.NovusHundredGigLan.IeeeL1Defaults = False +# vport.L1Config.NovusHundredGigLan.EnableAutoNegotiation = False +# vport.L1Config.NovusHundredGigLan.Speed = 'speed{}g'.format(port_list[i]['speed']/1000) +# logger.info('speed{}g'.format(port_list[i]['speed']/1000)) +# i += 1 +# +# return vports """ Configure capturing packets on a IXIA port diff --git a/tests/ixia/test_ixia_traffic_restpy.py b/tests/ixia/test_ixia_traffic_restpy.py index 3d4a870571b..8f68f014777 100644 --- a/tests/ixia/test_ixia_traffic_restpy.py +++ b/tests/ixia/test_ixia_traffic_restpy.py @@ -4,9 +4,9 @@ # arguments of the test function) # * How Ixia chassis card/ports are addressed # * How you can configure/control ixia devices, start traffic and collect -# statistics using REST API +# statistics. # * This simple sanity test cases can be used to check if testbed setup -# is correct or not - since it prints a lot of testbed data +# is correct or not. ############################################################################### import logging @@ -37,7 +37,7 @@ def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, logger.info("Fanout Graph facts = %s" %(fanout_graph_facts)) logger.info("DUT hostname = %s" %(duthost.hostname)) - mg_facts = duthost.minigraph_facts(host=duthost.hostname) + mg_facts = duthost.minigraph_facts(host=duthost.hostname) gateway_ip = mg_facts['ansible_facts']['minigraph_vlan_interfaces'][0]['addr'] start_interface_ip = incriment_ip_address(gateway_ip) @@ -45,8 +45,6 @@ def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, ixiaFanoutHostList.get_fanout_device_details(device_number = 0) session = ixia_api_server_session - ixNetwork = session.Ixnetwork - portMap = session.PortMapAssistant() t1 = time.time() port_list = configure_ports(session=session, diff --git a/tests/ixia/test_pfc_pause_lossless.py b/tests/ixia/test_pfc_pause_lossless.py index ea12d27f36d..6a700faf284 100644 --- a/tests/ixia/test_pfc_pause_lossless.py +++ b/tests/ixia/test_pfc_pause_lossless.py @@ -7,7 +7,7 @@ from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user, ixia_api_serv_passwd, ixia_dev,\ ixia_api_serv_port, ixia_api_serv_session_id, ixia_api_server_session from common.ixia.ixia_helpers import get_neigh_ixia_mgmt_ip, get_neigh_ixia_card, get_neigh_ixia_port, \ - create_session, remove_session, config_ports, create_topology, start_protocols, create_ipv4_traffic, \ + create_session, remove_session, configure_ports, create_topology, start_protocols, create_ipv4_traffic, \ create_pause_traffic, start_traffc, stop_traffic, get_statistics, IxiaFanoutManager from common.ixia.common_helpers import get_vlan_subnet, get_addrs_in_subnet @@ -140,15 +140,10 @@ def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map, d device_conn = conn_graph_facts['device_conn'] for intf in fanout_devices.ports(): - ixia_mgmt_ip = fanout_devices.get_chassis_ip() - (ixia_mgmt_ip, ixia_card, ixia_port) = \ - fanout_devices.getCardPort(intf) + peer_port = intf['peer_port'] + intf['speed'] = int(device_conn[peer_port]['speed']) * 100 + port_list.append(intf) - dutIntf = (intf.split('/'))[2] - port_list.append({'ip': ixia_mgmt_ip, - 'card_id': ixia_card, - 'port_id': ixia_port, - 'speed': int(device_conn[dutIntf]['speed'])}) """ The topology should have at least two interfaces """ pytest_assert(len(device_conn)>=2, "The topology should have at least two interfaces") @@ -157,8 +152,8 @@ def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map, d session = ixia_api_server_session for prio in lossless_prio_dscp_map: - for i in range(len(port_list)): - vports = config_ports(session, port_list) + for i in range(len(port_list)): + vports = configure_ports(session, port_list) rx_id = i tx_id = (i+1) % len(port_list) From 0678c570c341c7f2da599b19ea8e3a70adce72b4 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Wed, 15 Jul 2020 14:10:42 +0000 Subject: [PATCH 20/52] added google style comments and changed function names --- tests/common/ixia/ixia_fixtures.py | 5 +- tests/common/ixia/ixia_helpers.py | 227 +++++++++++-------------- tests/ixia/test_ixia_traffic_restpy.py | 12 +- tests/ixia/test_pfc_pause_lossless.py | 18 +- 4 files changed, 117 insertions(+), 145 deletions(-) diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py index 768d124f3ec..8627cba6bb9 100644 --- a/tests/common/ixia/ixia_fixtures.py +++ b/tests/common/ixia/ixia_fixtures.py @@ -73,5 +73,8 @@ def ixia_api_server_session(ixia_api_serv_ip, sessionData = session.Session ixNetwork = session.Ixnetwork ixNetwork.NewConfig() - return session + + yield session + ixNetwork.NewConfig() + session.Session.remove() diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index 9d8c7571441..c27865cb762 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -1,83 +1,35 @@ +# -*- coding: utf-8 -*- +"""This module contains the high-level wrapper function using the APIs defined +by Ixia/Keysights ixnetwork_restpy library functions. Intention of providing +these to SONiC group is to avoid writing multiple low-level rest API calls for +doing the top-level tasks like configure ports, create topology, +start protocols, start traffic etc. + +This module also contains a definition of a simple helper class +"IxiaFanoutManager" which can be used to manage cards and ports of ixia +chassis instead of reading it from fanout_graph_facts fixture. +""" + import re from common.reboot import logger from ixnetwork_restpy import SessionAssistant, Files import pytest -""" -@summary: given a DUT interface, return the management IP address of its neighbor IXIA device -@param intf: DUT interface -@param conn_graph_facts: testbed connectivity graph -@param ixia_dev: the mapping of hostname to IP address of IXIA devices -@return the management IP address of its neighbor IXIA device or None if we cannot find it -""" -def get_neigh_ixia_mgmt_ip(intf, conn_graph_facts, ixia_dev): - device_conn = conn_graph_facts['device_conn'] - if intf not in device_conn: - return None - - ixia_dev_hostname = device_conn[intf]['peerdevice'] - if ixia_dev_hostname not in ixia_dev: - return None - - return ixia_dev[ixia_dev_hostname] - -""" -@summary: given a DUT interface, return the card of its neighbor IXIA device -@param intf: DUT interface -@param conn_graph_facts: testbed connectivity graph -@return the card of its neighbor IXIA device or None if we cannot find it -""" -def get_neigh_ixia_card(intf, conn_graph_facts): - device_conn = conn_graph_facts['device_conn'] - if intf not in device_conn: - return None - - ixia_intf = device_conn[intf]['peerport'] - pattern = r'Card(\d+)/Port(\d+)' - m = re.match(pattern, ixia_intf) - - if m is None: - return None - else: - return m.group(1) +class IxiaFanoutManager () : + """Class for managing multiple chassis and extracting the information + like chassis IP, card, port etc. from fanout_graph_fact.""" -""" -@summary: given a DUT interface, return the port of its neighbor IXIA device -@param intf: DUT interface -@param conn_graph_facts: testbed connectivity graph -@return the port of its neighbor IXIA device or None if we cannot find it -""" -def get_neigh_ixia_port(intf, conn_graph_facts): - device_conn = conn_graph_facts['device_conn'] - if intf not in device_conn: - return None - - ixia_intf = device_conn[intf]['peerport'] - pattern = r'Card(\d+)/Port(\d+)' - m = re.match(pattern, ixia_intf) + def __init__(self,fanout_data) : + """ When multiple chassis are available inside fanout_graph_facts + this method makes a list of chassis connection-details out of it. + So each chassis and details associated with it can be accessed by + a integer index (starting from 0) - if m is None: - return None - else: - return m.group(2) - -#def parseFanoutConnections (device_conn) : -# retval = [] -# for key in device_conn.keys() : -# pp = device_conn[key]['peerport'] -# string = key + '/' + pp -# retval.append(string) -# retval.sort() -# return(retval) - -#def get_card_port(i) : -# chassis = i.split('/')[0] -# crd = (i.split('/')[1]).replace('Card', '') -# prt = (i.split('/')[2]).replace('Port', '') -# return (chassis, crd, prt) + Args: + fanout_data (dict): the dictionary returned by fanout_graph_fact -class IxiaFanoutManager () : - def __init__(self,fanout_data) : + """ + self.last_fanout_assessed = None self.fanout_list = [] self.last_device_connection_details = None @@ -97,6 +49,23 @@ def __parse_fanout_connections__ (self) : return(retval) def get_fanout_device_details (self, device_number) : + """With the help of this function you can select the chassis you want + to access. For example get_fanout_device_details(0) selects the + first chassis. It just select the chassis but does not return + anything. The rest of the function then used to extract chassis + information like "get_chassis_ip()" will the return the ip address + of chassis 0 - the first chassis in the list. + + Note: + Counting or indexing starts from 0. That is 0 = 1st cassis, + 1 = 2nd chassis ... + + Args: + device_number (int): the chassis index (0 is the first) + + Returns: + None + """ # Pointer to chassis info self.last_fanout_assessed = device_number @@ -116,23 +85,51 @@ def get_fanout_device_details (self, device_number) : #return self.fanout_list[self.last_fanout_assessed] def get_connection_details (self) : + """This function returns all the details associated with a particular + chassis (selected earlier using get_fanout_device_details() function). + Details of the chassis will be available like chassis IP, card, ports, + peer port etc. in a dictionary format. + + Args: + This function takes no argument. + + Note: If you have not used get_fanout_device_details(), by default 0th + (first) chassis remains selected. + + Returns: + Details of the chassis connection as dictionary format. + """ return(self.last_device_connection_details) - #def get_card_port (self, i) : - # chs = i.split('/')[0] - # if (self.ip_address == chs) : - # crd = (i.split('/')[1]).replace('Card', '') - # prt = (i.split('/')[2]).replace('Port', '') - # peer_port = i.split('/')[3] - # else : - # pytest_assert(0, 'Invalid portlist %s for chassis %s' %(chs, prt)) - # - # return(chs, crd, prt, peer_port) - def get_chassis_ip (self) : + """This function returns IP address of a particular chassis + (selected earlier using get_fanout_device_details() function). + + Args: + This function takes no argument. + + Note: If you have not used get_fanout_device_details(), by default 0th + (first) chassis remains selected. + + Returns: + The IP address + """ return self.ip_address - def ports(self) : + def get_ports(self) : + """This function returns list of ports associated with a chassis + (selected earlier using get_fanout_device_details() function) + as a list of dictionary. + + Args: + This function takes no argument. + + Note: If you have not used get_fanout_device_details(), by default 0th + (first) chassis remains selected. + + Returns: + Dictionary of chassis card port information. + """ retval = [] for ports in self.current_ixia_port_list: info_list = ports.split('/') @@ -146,26 +143,26 @@ def ports(self) : return retval -#------------------------------------------------------------------------------ -# Newely added -#------------------------------------------------------------------------------ -def create_session(server_ip, username, password, log_file="ixia.log"): - return SessionAssistant(IpAddress=server_ip, RestPort=None, UserName=username, Password=password, - SessionName=None, SessionId=None, ApiKey=None, ClearConfig=True, - LogLevel='all', LogFilename=log_file) +def configure_ports(session, port_list, start_name='port') : + """Configures ports of the IXIA chassis and returns the list + of configured Ixia ports -def remove_session(session): - session.Session.remove() + Args: + session (obj): IXIA session object + port_list (list): List of dictionaries. like below - + [{'ip': 10.0.0.1, card_id: '1', 'port_id': '1'}, + {'ip': 10.0.0.1, card_id: '1', 'port_id': '2'}, ...]. 'ip', + 'card_id' and 'port_id' are the mandatory keys. + start_name (str): (optional) The port name to start with, port + names will be incremented automatically like port1, port2 ... - -""" -Configure ports of the IXIA chassis -@param session: IXIA session -@param port_list: List of port locations. Each entry has four keys: 'ip', 'card_id', 'port_id', 'speed' -@return the list of ports if the configuration succeeds. Otherwise return None -""" -def configure_ports(session, port_list, start_name='port') : + Note: This is like the return value of the method, + IxiaFanoutManager.get_ports() + + Returns: The list of Ixia port objects if the configuration + succeeds. Otherwise return None + """ port_map = session.PortMapAssistant() ixnetwork = session.Ixnetwork @@ -223,34 +220,6 @@ def configure_ports(session, port_list, start_name='port') : return vports -#def config_ports(session, port_list): -# port_map = session.PortMapAssistant() -# vports = list() -# -# index = 1 -# for port in port_list: -# port_name = 'Port_{}'.format(index) -# index += 1 -# """ Map a test port location (ip, card, port) to a virtual port (name) """ -# vports.append(port_map.Map(IpAddress=port['ip'], CardId=port['card_id'], -# PortId=port['port_id'], Name=port_name)) -# -# """ Connect all mapped virtual ports to test port locations """ -# port_map.Connect() -# -# ixnetwork = session.Ixnetwork -# i = 0 -# for vport in ixnetwork.Vport.find(): -# vport.L1Config.CurrentType = 'novusHundredGigLanFcoe' -# vport.L1Config.NovusHundredGigLan.Fcoe.PfcPriorityGroups = [0,1,2,3,4,5,6,7] -# vport.L1Config.NovusHundredGigLan.IeeeL1Defaults = False -# vport.L1Config.NovusHundredGigLan.EnableAutoNegotiation = False -# vport.L1Config.NovusHundredGigLan.Speed = 'speed{}g'.format(port_list[i]['speed']/1000) -# logger.info('speed{}g'.format(port_list[i]['speed']/1000)) -# i += 1 -# -# return vports - """ Configure capturing packets on a IXIA port @param session: IXIA session diff --git a/tests/ixia/test_ixia_traffic_restpy.py b/tests/ixia/test_ixia_traffic_restpy.py index 8f68f014777..25cc1691ea4 100644 --- a/tests/ixia/test_ixia_traffic_restpy.py +++ b/tests/ixia/test_ixia_traffic_restpy.py @@ -46,14 +46,11 @@ def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, session = ixia_api_server_session - t1 = time.time() + logger.info("Configuring ports.") port_list = configure_ports(session=session, - port_list=ixiaFanoutHostList.ports()) - t2 = time.time() + port_list=ixiaFanoutHostList.get_ports()) - time_taken = t2 - t1 - logger.info("time-taken to connect = %s" %(time_taken)) - + logger.info("Creating topology.") topology = create_topology(session=session, ports=port_list, name="Sender", @@ -63,10 +60,11 @@ def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, gw_incr_step='0.0.0.0') start_protocols(session) - logger.info("Wait for 5 seconds for iv4 sessions to up") + logger.info("Wait for 5 seconds for iv4 sessions to up.") time.sleep(5) # Create a traffic item + logger.info("Configuring traffic.") traffic_item = create_ip_traffic_item_using_wizard_arguments( session=session, src_start_port=1, diff --git a/tests/ixia/test_pfc_pause_lossless.py b/tests/ixia/test_pfc_pause_lossless.py index 6a700faf284..d9fd501e682 100644 --- a/tests/ixia/test_pfc_pause_lossless.py +++ b/tests/ixia/test_pfc_pause_lossless.py @@ -3,12 +3,16 @@ import time import pytest from common.fixtures.conn_graph_facts import conn_graph_facts + from common.helpers.assertions import pytest_assert -from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user, ixia_api_serv_passwd, ixia_dev,\ - ixia_api_serv_port, ixia_api_serv_session_id, ixia_api_server_session -from common.ixia.ixia_helpers import get_neigh_ixia_mgmt_ip, get_neigh_ixia_card, get_neigh_ixia_port, \ - create_session, remove_session, configure_ports, create_topology, start_protocols, create_ipv4_traffic, \ - create_pause_traffic, start_traffc, stop_traffic, get_statistics, IxiaFanoutManager + +from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ + ixia_api_serv_passwd, ixia_dev, ixia_api_serv_port,\ + ixia_api_serv_session_id, ixia_api_server_session + +from common.ixia.ixia_helpers import configure_ports,\ + create_topology, start_protocols, create_ipv4_traffic, create_pause_traffic, \ + start_traffc, stop_traffic, get_statistics, IxiaFanoutManager from common.ixia.common_helpers import get_vlan_subnet, get_addrs_in_subnet @@ -139,7 +143,7 @@ def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map, d fanout_devices.get_fanout_device_details(device_number = 0) device_conn = conn_graph_facts['device_conn'] - for intf in fanout_devices.ports(): + for intf in fanout_devices.get_ports(): peer_port = intf['peer_port'] intf['speed'] = int(device_conn[peer_port]['speed']) * 100 port_list.append(intf) @@ -186,5 +190,3 @@ def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map, d ixNetwork = session.Ixnetwork ixNetwork.NewConfig() - remove_session(session) - From a0182c84367a852051bea20e0c31f8204ec24846 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Thu, 16 Jul 2020 07:38:47 +0000 Subject: [PATCH 21/52] Added the google doc string for the methods in ixia_fixtures.py and ixia_helpers.py --- tests/common/ixia/ixia_fixtures.py | 97 ++++- tests/common/ixia/ixia_helpers.py | 576 ++++--------------------- tests/ixia/test_ixia_traffic_restpy.py | 3 +- 3 files changed, 168 insertions(+), 508 deletions(-) diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py index 8627cba6bb9..ceeb150ef67 100644 --- a/tests/common/ixia/ixia_fixtures.py +++ b/tests/common/ixia/ixia_fixtures.py @@ -3,60 +3,115 @@ from common.devices import FanoutHost from ixnetwork_restpy import SessionAssistant, Files -""" -In an IXIA testbed, there is no PTF docker. -Hence, we use ptf_ip field to store IXIA API server. -This fixture returns the IP address of the IXIA API server. -""" @pytest.fixture(scope = "module") def ixia_api_serv_ip(testbed): + """ + In an Ixia testbed, there is no PTF docker. + Hence, we use ptf_ip field to store Ixia API server. + This fixture returns the IP address of the Ixia API server. + + Args: + testbed (pytest fixture): The testbed fixture. + + Returns: + Ixia PTF server (API server) IP + """ return testbed['ptf_ip'] -""" -Return the username of IXIA API server -""" + @pytest.fixture(scope = "module") def ixia_api_serv_user(duthost): + """ + Return the username of Ixia API server. + + Args: + duthost (pytest fixture): The duthost fixture. + + Returns: + Ixia PTF server (API server) username. + """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['user'] -""" -Return the password of IXIA API server -""" + @pytest.fixture(scope = "module") def ixia_api_serv_passwd(duthost): + """ + Return the password of Ixia API server. + + Args: + duthost (pytest fixture): The duthost fixture. + + Returns: + Ixia PTF server (API server) password. + """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['password'] -""" -Return REST port. -""" + @pytest.fixture(scope = "module") def ixia_api_serv_port(duthost): + """ + Return REST port of the ixia API server. + + Args: + duthost (pytest fixture): The duthost fixture. + + Returns: + Ixia PTF server (API server) REST port. + """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['rest_port'] -""" -IXIA PTF can spawn multiple session on the same REST port. Optional for LINUX, Rewuired for windows -Return the session ID. -""" + @pytest.fixture(scope = "module") def ixia_api_serv_session_id(duthost): + """ + Ixia PTF can spawn multiple session on the same REST port. + Optional for LINUX, required for windows return the session ID. + + Args: + duthost (pytest fixture): The duthost fixture. + + Returns: + Ixia PTF server (API server) session id. + """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['session_id'] @pytest.fixture(scope = "module") def ixia_dev(duthost, fanouthosts): + """ + Returns the Ixia chassis IP + + Args: + duthost (pytest fixture): The duthost fixture. + fanouthosts (pytest fixture): The fanouthosts fixture. + + Returns: + Ixia Chassis IP. + """ result = dict() ixia_dev_hostnames = fanouthosts.keys() for hostname in ixia_dev_hostnames: result[hostname] = duthost.host.options['inventory_manager'].get_host(hostname).get_vars()['ansible_host'] return result -""" -IXIA session manager with PTF server -""" @pytest.fixture(scope = "function") def ixia_api_server_session(ixia_api_serv_ip, ixia_api_serv_user, ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id) : + """ + Ixia session manager fixture. + + Args: + ixia_api_serv_ip (pytest fixture): ixia_api_serv_ip fixture + ixia_api_serv_user (pytest fixture): ixia_api_serv_user fixture. + ixia_api_serv_passwd (pytest fixture): ixia_api_serv_passwd fixture. + ixia_api_serv_port (pytest fixture): ixia_api_serv_port fixture. + ixia_api_serv_session_id (pytest fixture): ixia_api_serv_session_id + fixture. + + Returns: + IxNetwork Session Id + """ if (ixia_api_serv_session_id != "None") : session = SessionAssistant(IpAddress = ixia_api_serv_ip, diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index c27865cb762..20365993e7b 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -157,11 +157,11 @@ def configure_ports(session, port_list, start_name='port') : start_name (str): (optional) The port name to start with, port names will be incremented automatically like port1, port2 ... - Note: This is like the return value of the method, - IxiaFanoutManager.get_ports() + Note: This is like the return value of the method, + IxiaFanoutManager.get_ports() - Returns: The list of Ixia port objects if the configuration - succeeds. Otherwise return None + Returns: The list of Ixia port objects if the configuration + succeeds. Otherwise return None """ port_map = session.PortMapAssistant() @@ -220,30 +220,7 @@ def configure_ports(session, port_list, start_name='port') : return vports -""" -Configure capturing packets on a IXIA port -@param session: IXIA session -@param port: port to configure -@param capture_control_pkt: if enable capturing control plane packets -@param capture_data_pkt: if enable capturing data plane packets -""" -def config_port_capture_pkt(session, port, capture_control_pkt, capture_data_pkt): - if capture_control_pkt or capture_data_pkt: - port.RxMode = 'captureAndMeasure' - port.Capture.SoftwareEnabled = capture_control_pkt - port.Capture.HardwareEnabled = capture_data_pkt -""" -Create a topology -@param session: IXIA session -@param name: Topology name -@param ports: List of ports -@param ip_start: Start value of IPv4 addresses, e.g., 192.168.1.1 -@param ip_incr_step: Increment step of IPv4 addresses, e.g., 0.0.0.1 -@param gw_start: Start value of gateway IPv4 addresses, e.g., 192.168.1.1 -@param gw_incr_step: Increment step of gateway IPv4 addresses, e.g., 0.0.0.0 (no increment) -@return the topology -""" def create_topology( session, ports, @@ -254,6 +231,27 @@ def create_topology( gw_start='10.0.0.2', gw_incr_step='0.0.1.0'): + """ This function creates a topology with ethernet and IP stack on + IxNetwork + + Args: + session (obj): Ixia session object. + ports (list): List of IxNetwork port objects, returned by the + function 'configure_ports' + name (str): The name of the topology. + ip_type (str): IP stack type - ipv4 or ipv6. + ip_start (str): Starting interface IP address. + ip_incr_step (str): IP address increment step in IP format like + "0.0.0.1" + gw_start (str): Starting gateway IP address. + gw_incr_step (str): IP address increment step in IP format like + "0.0.0.1" + + Note: ipv6 stack option is left for future extension. + + Return: IxNetwork topology obect. + """ + ixnetwork = session.Ixnetwork topology = ixnetwork.Topology.add(Name=name, Ports=ports) @@ -270,18 +268,23 @@ def create_topology( ipv4.GatewayIp.Increment(start_value=gw_start, step_value=gw_incr_step) ipv4.GatewayIp.Steps.Step = gw_incr_step elif (ip_type == 'ipv6') : - # TBD + # ipv6 stack option is left for future extension. pass else : pytest_assert(0) return topology -""" -Start protocols (e.g., IP and Ethernet) -@param session: IXIA session -""" def start_protocols(session): + """This function starts all the protocols configured on the IxNetwork + protocol stack (e.g., IP and Ethernet). + + Args: + session (obj) : IxNetwork session object. + + Returns: + None + """ ixnetwork = session.Ixnetwork ixnetwork.StartAllProtocols(Arg1='sync') protocolSummary = session.StatViewAssistant('Protocols Summary') @@ -289,478 +292,49 @@ def start_protocols(session): protocolSummary.CheckCondition('Sessions Down', protocolSummary.EQUAL, 0) logger.info(protocolSummary) - -""" -Create a raw Ethernet/IP traffic item -@param session: IXIA session -@param name: name of traffic item -@param source: source endpoint -@param destination: destination endpoint -@param src_mac: source MAC address -@param dst_mac: destination MAC address -@param src_ip: source IP address -@param dst_ip: destination IP address -@param dscp_list: list of DSCPs -@param pkt_size: packet size -@param pkt_count: packet count (can be None if you want to keep running the traffic) -@param rate_percent: percentage of line rate -@param start_delay: start delay in second -@return the created traffic item -""" -def create_raw_traffic(session, name, source, destination, src_mac=None, dst_mac=None, src_ip=None, - dst_ip=None, dscp_list=None, pkt_size=64, pkt_count=None, rate_percent=100, - start_delay=0): - - ixnetwork = session.Ixnetwork - traffic_item = ixnetwork.Traffic.TrafficItem.add(Name=name, BiDirectional=False, TrafficType='raw') - - traffic_item.EndpointSet.add(Sources=source.Protocols.find(), Destinations=destination.Protocols.find()) - - traffic_config = traffic_item.ConfigElement.find()[0] - traffic_config.FrameRate.update(Type='percentLineRate', Rate=rate_percent) - traffic_config.FrameRateDistribution.PortDistribution = 'splitRateEvenly' - traffic_config.FrameSize.FixedSize = pkt_size - - if pkt_count is not None and pkt_count > 0: - traffic_config.TransmissionControl.update(Type='fixedFrameCount', FrameCount=pkt_count) - else: - traffic_config.TransmissionControl.update(Type='continuous') - - if start_delay > 0: - traffic_config.TransmissionControl.update(StartDelayUnits='nanoseconds', - StartDelay=start_delay*(10**6)) - - """ Add IP header """ - ip_stack_obj = create_pkt_hdr(ixnetwork=ixnetwork, traffic_item=traffic_item, - pkt_hdr_to_add='^IPv4', append_to_stack='Ethernet II') - - eth_stack_obj = traffic_item.ConfigElement.find().Stack.find('Ethernet II').Field.find() - set_eth_fields(eth_stack_obj=eth_stack_obj, src_mac=src_mac, dst_mac=dst_mac) - set_ip_fields(ip_stack_obj=ip_stack_obj, src_ip=src_ip, dst_ip=dst_ip, dscp_list=dscp_list) - - traffic_item.Tracking.find()[0].TrackBy = ['flowGroup0'] - - """ Push ConfigElement settings down to HighLevelStream resources """ - traffic_item.Generate() - - return traffic_item - - -""" -Create an IPv4 traffic item -@param session: IXIA session -@param name: name of traffic item -@param source: source endpoints -@param destination: destination endpoints -@param pkt_size: packet size -@param pkt_count: packet count -@param duration: traffic duration in second (positive integer only!) -@param rate_percent: percentage of line rate -@param start_delay: start delay in second -@param dscp_list: list of DSCPs -@param lossless_prio_list: list of lossless priorities -@param ecn_capable: if packets can get ECN marked -@return the created traffic item -""" -def create_ipv4_traffic(session, - name, - source, - destination, - pkt_size=64, - pkt_count=None, - duration=None, - rate_percent=100, - start_delay=0, - dscp_list=None, - lossless_prio_list=None, - ecn_capable=False): - - ixnetwork = session.Ixnetwork - - traffic_item = ixnetwork.Traffic.TrafficItem.add(Name=name, BiDirectional=False, TrafficType='ipv4') - traffic_item.EndpointSet.add(Sources=source, Destinations=destination) - - traffic_config = traffic_item.ConfigElement.find()[0] - """ Todo: add sending rate support """ - traffic_config.FrameRate.update(Type='percentLineRate', Rate=rate_percent) - traffic_config.FrameRateDistribution.PortDistribution = 'splitRateEvenly' - traffic_config.FrameSize.FixedSize = pkt_size - - if pkt_count is not None and duration is not None: - print 'You can only specify either pkt_count or duration' - return None - - if pkt_count is not None: - traffic_config.TransmissionControl.update(Type='fixedFrameCount', FrameCount=pkt_count) - - elif duration is not None: - if type(duration) != int or duration <= 0: - print 'Invalid duration value {} (positive integer only)'.format(duration) - return None - else: - traffic_config.TransmissionControl.update(Type='fixedDuration', Duration=duration) - - else: - traffic_config.TransmissionControl.update(Type='continuous') - - if start_delay > 0: - traffic_config.TransmissionControl.update(StartDelayUnits='nanoseconds', - StartDelay=start_delay*(10**6)) - - if dscp_list is not None and len(dscp_list) > 0: - phb_field = traffic_item.ConfigElement.find().Stack.find('IPv4').Field.\ - find(DisplayName='Default PHB') - - phb_field.ActiveFieldChoice = True - phb_field.ValueType = 'valueList' - phb_field.ValueList = dscp_list - - """ Set ECN bits to 10 (ECN capable) """ - if ecn_capable: - phb_field = traffic_item.ConfigElement.find().Stack.find('IPv4').\ - Field.find(FieldTypeId='ipv4.header.priority.ds.phb.defaultPHB.unused') - phb_field.ActiveFieldChoice = True - phb_field.ValueType = 'singleValue' - phb_field.SingleValue = 2 - - if lossless_prio_list is not None and len(lossless_prio_list) > 0: - eth_stack = traffic_item.ConfigElement.find()[0].Stack.find(DisplayName='Ethernet II') - pfc_queue = eth_stack.Field.find(DisplayName='PFC Queue') - pfc_queue.ValueType = 'valueList' - pfc_queue.ValueList = lossless_prio_list - - traffic_item.Tracking.find()[0].TrackBy = ['flowGroup0'] - - """ Push ConfigElement settings down to HighLevelStream resources """ - traffic_item.Generate() - - return traffic_item - -""" -Create a pause traffic item -@param session: IXIA session -@param name: Name of traffic item -@param source: source endpoint -@param pkt_per_sec: packets per second -@param pkt_count: packet count -@param duration: traffic duration in second (positive integer only!) -@param start_delay: start delay in second -@param global_pause: if the generated packets are global pause (IEEE 802.3X PAUSE) -@param pause_prio_list: list of priorities to pause. Only valid when global_pause is False -@return the created traffic item or None if any errors happen -""" -def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, duration=None, - start_delay=0, global_pause=False, pause_prio_list=[]): - - if pause_prio_list is not None: - for prio in pause_prio_list: - if prio < 0 or prio > 7: - print 'Invalid pause priorities {}'.format(pause_prio_list) - return None - - ixnetwork = session.Ixnetwork - traffic_item = ixnetwork.Traffic.TrafficItem.add(Name=name, BiDirectional=False, TrafficType='raw') - - """ Since PFC packets will not be forwarded by the switch, so destinations are actually not used """ - traffic_item.EndpointSet.add(Sources=source.Protocols.find(), Destinations=source.Protocols.find()) - - traffic_config = traffic_item.ConfigElement.find()[0] - traffic_config.FrameRate.update(Type='framesPerSecond', Rate=pkt_per_sec) - traffic_config.FrameRateDistribution.PortDistribution = 'splitRateEvenly' - traffic_config.FrameSize.FixedSize = 64 - - if pkt_count is not None and duration is not None: - print 'You can only specify either pkt_count or duration' - return None - - if pkt_count is not None: - traffic_config.TransmissionControl.update(Type='fixedFrameCount', FrameCount=pkt_count) - - elif duration is not None: - if type(duration) != int or duration <= 0: - print 'Invalid duration value {} (positive integer only)'.format(duration) - return None - else: - traffic_config.TransmissionControl.update(Type='fixedDuration', Duration=duration) - - else: - traffic_config.TransmissionControl.update(Type='continuous') - - if start_delay > 0: - traffic_config.TransmissionControl.update(StartDelayUnits='nanoseconds', - StartDelay=start_delay*(10**6)) - - """ Add PFC header """ - pfc_stack_obj = create_pkt_hdr(ixnetwork=ixnetwork, - traffic_item=traffic_item, - pkt_hdr_to_add='^PFC PAUSE \(802.1Qbb\)', - append_to_stack='Ethernet II') - - """ Construct global pause and PFC packets """ - if global_pause: - set_global_pause_fields(pfc_stack_obj) - else: - set_pfc_fields(pfc_stack_obj, pause_prio_list) - - """ Remove Ethernet header """ - traffic_item.ConfigElement.find()[0].Stack.find(DisplayName="Ethernet II").Remove() - - traffic_item.Tracking.find()[0].TrackBy = ['flowGroup0'] - - """ Push ConfigElement settings down to HighLevelStream resources """ - traffic_item.Generate() - - return traffic_item - - -def set_global_pause_fields(pfc_stack_obj): - code = pfc_stack_obj.find(DisplayName='Control opcode') - code.ValueType = 'singleValue' - code.SingleValue = '1' - - """ This field is pause duration in global pause packet """ - prio_enable_vector = pfc_stack_obj.find(DisplayName='priority_enable_vector') - prio_enable_vector.ValueType = 'singleValue' - prio_enable_vector.SingleValue = 'ffff' - - """ pad bytes """ - for i in range(8): - pause_duration = pfc_stack_obj.find(DisplayName='PFC Queue {}'.format(i)) - pause_duration.ValueType = 'singleValue' - pause_duration.SingleValue = '0' - - -def set_eth_fields(eth_stack_obj, src_mac, dst_mac): - if src_mac is not None: - src_mac_field = eth_stack_obj.find(DisplayName='Source MAC Address') - src_mac_field.ValueType = 'singleValue' - src_mac_field.SingleValue = src_mac - - if dst_mac is not None: - dst_mac_field = eth_stack_obj.find(DisplayName='Destination MAC Address') - dst_mac_field.ValueType = 'singleValue' - dst_mac_field.SingleValue = dst_mac - - -def set_ip_fields(ip_stack_obj, src_ip, dst_ip, dscp_list): - if src_ip is not None: - src_ip_field = ip_stack_obj.find(DisplayName='Source Address') - src_ip_field.ValueType = 'singleValue' - src_ip_field.SingleValue = src_ip - - if dst_ip is not None: - dst_ip_field = ip_stack_obj.find(DisplayName='Destination Address') - dst_ip_field.ValueType = 'singleValue' - dst_ip_field.SingleValue = dst_ip - - if dscp_list is not None and len(dscp_list) > 0: - phb_field = ip_stack_obj.find(DisplayName='Default PHB') - phb_field.ActiveFieldChoice = True - phb_field.ValueType = 'valueList' - phb_field.ValueList = dscp_list - - -def set_pfc_fields(pfc_stack_obj, pause_prio_list): - code = pfc_stack_obj.find(DisplayName='Control opcode') - code.ValueType = 'singleValue' - code.SingleValue = '101' - - prio_enable_vector = pfc_stack_obj.find(DisplayName='priority_enable_vector') - prio_enable_vector.ValueType = 'singleValue' - val = 0 - for prio in pause_prio_list: - val += (1 << prio) - prio_enable_vector.SingleValue = hex(val) - - for i in range(8): - pause_duration = pfc_stack_obj.find(DisplayName='PFC Queue {}'.format(i)) - pause_duration.ValueType = 'singleValue' - - if i in pause_prio_list: - pause_duration.SingleValue = 'ffff' - else: - pause_duration.SingleValue = '0' - - -def create_pkt_hdr(ixnetwork, traffic_item, pkt_hdr_to_add, append_to_stack): - #Add new packet header in traffic item - config_element = traffic_item.ConfigElement.find()[0] - - # Do the followings to add packet headers on the new traffic item - - # Uncomment this to show a list of all the available protocol templates to create (packet headers) - #for protocolHeader in ixNetwork.Traffic.ProtocolTemplate.find(): - # ixNetwork.info('Protocol header: -- {} --'.format(protocolHeader.DisplayName)) - - # 1> Get the protocol template from the ProtocolTemplate list. - pkt_hdr_proto_template = ixnetwork.Traffic.ProtocolTemplate.find(DisplayName=pkt_hdr_to_add) - #ixNetwork.info('protocolTemplate: {}'.format(packetHeaderProtocolTemplate)) - - # 2> Append the object after the specified packet header stack. - append_to_stack_obj = config_element.Stack.find(DisplayName=append_to_stack) - #ixNetwork.info('appendToStackObj: {}'.format(appendToStackObj)) - append_to_stack_obj.Append(Arg2=pkt_hdr_proto_template) - - # 3> Get the new packet header stack to use it for appending an IPv4 stack after it. - # Look for the packet header object and stack ID. - pkt_hdr_stack_obj = config_element.Stack.find(DisplayName=pkt_hdr_to_add) - - # 4> In order to modify the fields, get the field object - pkt_hdr_field_obj = pkt_hdr_stack_obj.Field.find() - #ixNetwork.info('packetHeaderFieldObj: {}'.format(packetHeaderFieldObj)) - - # 5> Save the above configuration to the base config file. - #ixNetwork.SaveConfig(Files('baseConfig.ixncfg', local_file=True)) - - return pkt_hdr_field_obj - - -""" -Get statistics -@param session: IXIA session -@return statistics information -""" def get_statistics(session): + """This function fetches the traffic statistics information. + + Args: + session (obj) : IxNetwork session object. + + Returns: + None + """ ixnetwork = session.Ixnetwork flow_statistics = session.StatViewAssistant('Flow Statistics') ixnetwork.info('{}\n'.format(flow_statistics)) return flow_statistics -""" -Start all the traffic items -@param session: IXIA session -""" -def start_traffc(session): - ixnetwork = session.Ixnetwork - """ Apply traffic to hardware """ - ixnetwork.Traffic.Apply() - """ Run traffic """ - ixnetwork.Traffic.StartStatelessTrafficBlocking() - - -""" -Stop all the traffic items -@param session: IXIA session -""" def stop_traffic(session): - ixnetwork = session.Ixnetwork - ixnetwork.Traffic.StopStatelessTrafficBlocking() - -""" -Start capturing traffic -@param session: IXIA session -""" -def start_capture(session): - ixnetwork = session.Ixnetwork - ixnetwork.StartCapture() - -""" -Stop capturing traffic -@param session: IXIA session -""" -def stop_capture(session): - ixnetwork = session.Ixnetwork - ixnetwork.StopCapture() - -""" -Save the captured packets to a new user specifided location on the API server -@param session: IXIA session -@param dir: directory -@return the list of relative paths of all the captures saves -""" -def save_capture_pkts(session, dir=''): - ixnetwork = session.Ixnetwork - capture_files = ixnetwork.SaveCaptureFiles(Arg1=dir) - ixnetwork.info('Capture files: {}'.format(capture_files)) - return capture_files + """ This function stops all the IxNetwork traffic items configured + on all the ports. + Args: + session (obj): IxNetwork session object. -""" -Download a file from the API server -@param session: IXIA session -@param remote_filename: the name of the remote file -@param local_filename: the name that the remote contents will be saved to -@return the local file name -""" -def download_file(session, remote_filename, local_filename): - return session.Session.DownloadFile(remote_filename, local_filename) - -""" -Get all data packets captured in this port. -For each captured data packets, this function parses its every packet header -(e.g., Ethernet and IP) and every packet field (e.g., destination IP and ECN) -@param session: IXIA session -@param port: port where we capture pdata ackets -@return the list of parsed data packets. -""" -def get_captured_data_pkts(session, port): - result = list() - pkt_count = port.Capture.DataPacketCounter + Returns: + None. + """ ixnetwork = session.Ixnetwork - - ixnetwork.info('Total data packets captured: {}'.format(pkt_count)) - - for i in range(pkt_count): - pkt = dict() - - port.Capture.CurrentPacket.GetPacketFromDataCapture(Arg2=i) - pkt_hdr_stacks = port.Capture.CurrentPacket.Stack.find() - - for pkt_hdr in pkt_hdr_stacks.find(): - ixnetwork.info('\nPacketHeaderName: {}'.format(pkt_hdr.DisplayName)) - pkt[pkt_hdr.DisplayName] = dict() - - for field in pkt_hdr.Field.find(): - ixnetwork.info('\t{}: {}'.format(field.DisplayName, field.FieldValue)) - pkt[pkt_hdr.DisplayName][field.DisplayName] = field.FieldValue - - result.append(pkt) - - return result - -""" -Get the value of a specific field of the packet -@param pkt: packet -@param header: header name, e.g., "Internet Protocol" -@param field: field name, e.g., 'Explicit Congestion Notification' -@return the field value of None if the packet does not have this field -""" -def get_pkt_field(pkt, header, field): - if header not in pkt: - return None - - pkt_hdr = pkt[header] - if field not in pkt_hdr: - return None - - return pkt[header][field] - -""" -Get the ECN field value of the packet -@param pkt: packet -@return the ECN value -""" -def get_ecn(pkt): - return get_pkt_field(pkt=pkt, header='Internet Protocol', field='Explicit Congestion Notification') + ixnetwork.Traffic.StopStatelessTrafficBlocking() -""" -Get the DSCP field value of the packet -@param pkt: packet -@return the DSCP value (or None the packets does not have this field) -""" -def get_dscp(pkt): - return get_pkt_field(pkt=pkt, header='Internet Protocol', field='Differentiated Services Codepoint') def start_traffic(session): + """ This function starts all the IxNetwork traffic items configured + on all the ports. + Args: + session (obj): IxNetwork session object. + + Returns: + None. + """ ixnetwork = session.Ixnetwork """ Apply traffic to hardware """ ixnetwork.Traffic.Apply() """ Run traffic """ ixnetwork.Traffic.StartStatelessTrafficBlocking() -def stop_traffic(session): - ixnetwork = session.Ixnetwork - ixnetwork.Traffic.StopStatelessTrafficBlocking() def create_ip_traffic_item_using_wizard_arguments ( session, @@ -775,6 +349,38 @@ def create_ip_traffic_item_using_wizard_arguments ( name='example_traffic', traffic_type='ipv4') : + """ + This function creates a traffic item where source and destination ports + belong to same IxNetwork topology-object. Since source and destination + belong to same topology, source and destination endpoints may be + selected by selecting starting source port, source port count, first + route address index on the source port, source route count, destination + start port, destination port count, destination first-route index, + and destination route count. + + Args: + session (obj): IxNetwork session object. + src_start_port (int): The start port number. + src_port_count (int): The number of ports involved in sending traffic + starting from src_start_port number. Example, if the start port is + port2 and port2 to port5 is sending traffic then src_start_port = 2 + and src_port_count = 3. + src_first_route_index (int): The first route address index. + src_route_count (int): Number of routes starting from the + src_first_route_index. + dst_start_port (int): The first destination port number. + dst_port_count (int): Number of ports involved in receiving the traffic + starting from dst_start_port number. Example, if rhe rx port is + port6 and port7 then dst_start_port = 6 and dst_port_count = 2 + dst_first_route_index (int): The first destination IP index. + dst_route_count (int): Number of destination IPs starting from + dst_first_route_index. + name (str, optional): Name of the traffic item. Default name is + 'example_traffic'. + traffic_type (str, optional): Type of the ip source and destination + (ipv4/ipv6). Default traffic_type is 'ipv4'. + """ + traffic_item = session.Ixnetwork.Traffic.TrafficItem.add( Name = name, TrafficType = traffic_type) diff --git a/tests/ixia/test_ixia_traffic_restpy.py b/tests/ixia/test_ixia_traffic_restpy.py index 25cc1691ea4..609256b394f 100644 --- a/tests/ixia/test_ixia_traffic_restpy.py +++ b/tests/ixia/test_ixia_traffic_restpy.py @@ -12,7 +12,6 @@ import logging import time import pytest -import ipaddr from common.utilities import wait_until from common.fixtures.conn_graph_facts import conn_graph_facts, \ fanout_graph_facts @@ -60,7 +59,7 @@ def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, gw_incr_step='0.0.0.0') start_protocols(session) - logger.info("Wait for 5 seconds for iv4 sessions to up.") + logger.info("Wait for 5 seconds for IPv4 sessions to up.") time.sleep(5) # Create a traffic item From cfe3cbc3fe737bd414fa9e14b2303feae25b4c9a Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Thu, 16 Jul 2020 10:33:42 +0000 Subject: [PATCH 22/52] Implementing PR1819 review comments --- tests/common/devices.py | 34 ++++++++++-- tests/common/ixia/__init__.py | 2 +- tests/common/ixia/common_helpers.py | 72 ++++++++++++++++++++------ tests/common/ixia/ixia_helpers.py | 39 ++++++++------ tests/ixia/test_ixia_traffic_restpy.py | 7 ++- 5 files changed, 115 insertions(+), 39 deletions(-) diff --git a/tests/common/devices.py b/tests/common/devices.py index 24959896d2d..259cd2dfb8b 100644 --- a/tests/common/devices.py +++ b/tests/common/devices.py @@ -852,23 +852,47 @@ def exec_template(self, ansible_root, ansible_playbook, inventory, **kwargs): raise Exception("Unable to execute template\n{}".format(res["localhost"]["stdout"])) class IxiaHost (AnsibleHostBase): - """ - @summary: For running ansible module on Ixia Fanout switch in future (TBD). - Now it is just a place holder + """ This class is a place-holder for running ansible module on Ixia + fanout devices in future (TBD). """ def __init__ (self, ansible_adhoc, os, hostname, device_type) : + """ Initializing Ixia fanout host for using ansible modules. + + Note: Right now, it is just a place holder. + + Args: + ansible_adhoc :The pytest-ansible fixture + os (str): The os type of Ixia Fanout. + hostname (str): The Ixia fanout host-name + device_type (str): The Ixia fanout device type. + """ self.ansible_adhoc = ansible_adhoc self.os = os self.hostname = hostname self.device_type = device_type - def get_host_name (self) : + def get_host_name (self): + """Returns the Ixia hostname + + Args: + This function takes no argument. + """ return self.hostname def get_os (self) : + """Returns the os type of the ixia device. + + Args: + This function takes no argument. + """ return self.os - def exacute (self, cmd) : + def execute (self, cmd) : + """Execute a given command on ixia fanout host. + + Args: + cmd (str): Command to be executed. + """ if (self.os == 'ixia') : eval(cmd) diff --git a/tests/common/ixia/__init__.py b/tests/common/ixia/__init__.py index fd92c343462..77f343fca0b 100644 --- a/tests/common/ixia/__init__.py +++ b/tests/common/ixia/__init__.py @@ -1 +1 @@ -# place fro ixia fixtures +# Place for Ixia fixtures. diff --git a/tests/common/ixia/common_helpers.py b/tests/common/ixia/common_helpers.py index bb4578da599..824e1514c67 100644 --- a/tests/common/ixia/common_helpers.py +++ b/tests/common/ixia/common_helpers.py @@ -1,29 +1,64 @@ +"""This module contains some auxiliary functions that are required +to support automation activities. These functions are used for various +secondary activities like: convert the ansible Unicode STDOUT output +to string, get IP address in a subnet, increment a IP address, get +VLAN subnet etc. + +This file is also a placeholder for auxiliary function that are +required for supporting automation with Ixia devices in future: +like collecting diagnostics, uploading and downloading files +to/from API server, processing the statistics after obtaining them +in .csv format etc. +""" + import ipaddr from netaddr import IPNetwork -def incriment_ip_address (ip, incrment=1) : +def incriment_ip_address (ip, incr=1) : + """ + Increment IP address by a integer number. + + Args: + ip (str): IP address in string format. + incr (int): Increment by. + + Return: + IP address in the argument incremented by the given integer. + """ ipaddress = ipaddr.IPv4Address(ip) - ipaddress = ipaddress + incrment + ipaddress = ipaddress + incr return_value = ipaddress._string_from_ip_int(ipaddress._ip) return(return_value) + def ansible_stdout_to_str(ansible_stdout): """ - @Summary: The stdout of Ansible host is essentially a list of unicode characters. This function converts it to a string. - @param ansible_stdout: stdout of Ansible - @return: Return a string + The stdout of Ansible host is essentially a list of unicode characters. + This function converts it to a string. + + Args: + ansible_stdout: stdout of Ansible + + Returns: + Return a string """ result = "" for x in ansible_stdout: result += x.encode('UTF8') return result -""" -@Summary: Get Vlan subnet of a T0 device -@param host_ans: Ansible host instance of the device -@return: Vlan subnet, e.g., "192.168.1.1/24" where 192.168.1.1 is gateway and 24 is prefix length -""" + def get_vlan_subnet(host_ans): + """ + Get Vlan subnet of a T0 device + + Args: + host_ans: Ansible host instance of the device + + Returns : + Vlan subnet, e.g., "192.168.1.1/24" where 192.168.1.1 is gateway + and 24 is prefix length + """ mg_facts = host_ans.minigraph_facts(host=host_ans.hostname)['ansible_facts'] mg_vlans = mg_facts['minigraph_vlans'] @@ -36,13 +71,18 @@ def get_vlan_subnet(host_ans): gw_addr = ansible_stdout_to_str(mg_vlan_intfs[0]['addr']) return gw_addr + '/' + str(prefix_len) -""" -@Summary: Get N IP addresses in a subnet -@param subnet: IPv4 subnet, e.g., '192.168.1.1/24' -@param n: # of IP addresses to get -@return: Retuen n IPv4 addresses in this subnet in a list -""" + def get_addrs_in_subnet(subnet, n): + """ + Get N IP addresses in a subnet. + + Args: + subnet (str): IPv4 subnet, e.g., '192.168.1.1/24' + n (int): Number of IP addresses to get + + Return: + Retuen n IPv4 addresses in this subnet in a list. + """ ip_addr = subnet.split('/')[0] ip_addrs = [str(x) for x in list(IPNetwork(subnet))] ip_addrs.remove(ip_addr) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index 20365993e7b..b62ffb263c8 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -90,12 +90,12 @@ def get_connection_details (self) : Details of the chassis will be available like chassis IP, card, ports, peer port etc. in a dictionary format. - Args: - This function takes no argument. - Note: If you have not used get_fanout_device_details(), by default 0th (first) chassis remains selected. + Args: + This function takes no argument. + Returns: Details of the chassis connection as dictionary format. """ @@ -121,12 +121,12 @@ def get_ports(self) : (selected earlier using get_fanout_device_details() function) as a list of dictionary. - Args: - This function takes no argument. - Note: If you have not used get_fanout_device_details(), by default 0th (first) chassis remains selected. + Args: + This function takes no argument. + Returns: Dictionary of chassis card port information. """ @@ -148,6 +148,9 @@ def configure_ports(session, port_list, start_name='port') : """Configures ports of the IXIA chassis and returns the list of configured Ixia ports + Note: This is like the return value of the method, + IxiaFanoutManager.get_ports() + Args: session (obj): IXIA session object port_list (list): List of dictionaries. like below - @@ -157,8 +160,6 @@ def configure_ports(session, port_list, start_name='port') : start_name (str): (optional) The port name to start with, port names will be incremented automatically like port1, port2 ... - Note: This is like the return value of the method, - IxiaFanoutManager.get_ports() Returns: The list of Ixia port objects if the configuration succeeds. Otherwise return None @@ -234,6 +235,8 @@ def create_topology( """ This function creates a topology with ethernet and IP stack on IxNetwork + Note: ipv6 stack option is left for future extension. + Args: session (obj): Ixia session object. ports (list): List of IxNetwork port objects, returned by the @@ -247,8 +250,6 @@ def create_topology( gw_incr_step (str): IP address increment step in IP format like "0.0.0.1" - Note: ipv6 stack option is left for future extension. - Return: IxNetwork topology obect. """ @@ -275,6 +276,7 @@ def create_topology( return topology + def start_protocols(session): """This function starts all the protocols configured on the IxNetwork protocol stack (e.g., IP and Ethernet). @@ -292,19 +294,22 @@ def start_protocols(session): protocolSummary.CheckCondition('Sessions Down', protocolSummary.EQUAL, 0) logger.info(protocolSummary) -def get_statistics(session): + +def get_traffic_statistics(session, stat_view_name='Flow Statistics'): """This function fetches the traffic statistics information. Args: session (obj) : IxNetwork session object. + stat_view_name (str, optional): Statistics view name. Default + value is 'Flow Statistics' Returns: - None + traffic statistics dictionary. """ ixnetwork = session.Ixnetwork - flow_statistics = session.StatViewAssistant('Flow Statistics') - ixnetwork.info('{}\n'.format(flow_statistics)) - return flow_statistics + traffic_statistics = session.StatViewAssistant(stat_view_name) + ixnetwork.info('{}\n'.format(traffic_statistics)) + return traffic_statistics def stop_traffic(session): @@ -379,6 +384,10 @@ def create_ip_traffic_item_using_wizard_arguments ( 'example_traffic'. traffic_type (str, optional): Type of the ip source and destination (ipv4/ipv6). Default traffic_type is 'ipv4'. + + Returns: + IxNetwork traffic item object. + """ traffic_item = session.Ixnetwork.Traffic.TrafficItem.add( diff --git a/tests/ixia/test_ixia_traffic_restpy.py b/tests/ixia/test_ixia_traffic_restpy.py index 609256b394f..3cf9aba592e 100644 --- a/tests/ixia/test_ixia_traffic_restpy.py +++ b/tests/ixia/test_ixia_traffic_restpy.py @@ -25,7 +25,7 @@ from common.ixia.ixia_helpers import IxiaFanoutManager, configure_ports,\ create_topology, create_ip_traffic_item_using_wizard_arguments,\ - start_protocols, start_traffic, stop_traffic + start_protocols, start_traffic, stop_traffic, get_traffic_statistics from common.ixia.common_helpers import incriment_ip_address @@ -82,6 +82,9 @@ def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, time.sleep(5) # Fetch statistics. - logger.info(session.StatViewAssistant('Traffic Item Statistics')) + stats = get_traffic_statistics(session=session, + stat_view_name='Traffic Item Statistics') + logger.info(stats) + stop_traffic(session) From ed09027afd3a112b6d75108665bf96175ac93ec1 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Thu, 16 Jul 2020 10:59:31 +0000 Subject: [PATCH 23/52] Removing test_ixia_traffic_restpy.py adding test_ixia_traffic.py --- tests/ixia/{test_ixia_traffic_restpy.py => test_ixia_traffic.py} | 1 - 1 file changed, 1 deletion(-) rename tests/ixia/{test_ixia_traffic_restpy.py => test_ixia_traffic.py} (98%) diff --git a/tests/ixia/test_ixia_traffic_restpy.py b/tests/ixia/test_ixia_traffic.py similarity index 98% rename from tests/ixia/test_ixia_traffic_restpy.py rename to tests/ixia/test_ixia_traffic.py index 3cf9aba592e..f0220e32f27 100644 --- a/tests/ixia/test_ixia_traffic_restpy.py +++ b/tests/ixia/test_ixia_traffic.py @@ -17,7 +17,6 @@ fanout_graph_facts from common.reboot import logger -from ixnetwork_restpy import SessionAssistant, Files from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id, \ From b54f209bad8e5cb6b944db1c4e4732837d8a19a0 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Thu, 16 Jul 2020 14:34:10 +0000 Subject: [PATCH 24/52] Changing some comment string --- tests/common/devices.py | 2 ++ tests/common/ixia/common_helpers.py | 12 ++++++------ tests/common/ixia/ixia_fixtures.py | 6 ++++++ tests/common/ixia/ixia_helpers.py | 6 +++--- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/common/devices.py b/tests/common/devices.py index 259cd2dfb8b..2a375270742 100644 --- a/tests/common/devices.py +++ b/tests/common/devices.py @@ -851,6 +851,7 @@ def exec_template(self, ansible_root, ansible_playbook, inventory, **kwargs): if res["localhost"]["rc"] != 0: raise Exception("Unable to execute template\n{}".format(res["localhost"]["stdout"])) + class IxiaHost (AnsibleHostBase): """ This class is a place-holder for running ansible module on Ixia fanout devices in future (TBD). @@ -896,6 +897,7 @@ def execute (self, cmd) : if (self.os == 'ixia') : eval(cmd) + class FanoutHost(): """ @summary: Class for Fanout switch diff --git a/tests/common/ixia/common_helpers.py b/tests/common/ixia/common_helpers.py index 824e1514c67..0ff4971a13c 100644 --- a/tests/common/ixia/common_helpers.py +++ b/tests/common/ixia/common_helpers.py @@ -1,7 +1,7 @@ """This module contains some auxiliary functions that are required to support automation activities. These functions are used for various -secondary activities like: convert the ansible Unicode STDOUT output -to string, get IP address in a subnet, increment a IP address, get +secondary activities like convert the ansible Unicode STDOUT output +to string, get IP address in a subnet, increment an IP address, get VLAN subnet etc. This file is also a placeholder for auxiliary function that are @@ -16,7 +16,7 @@ def incriment_ip_address (ip, incr=1) : """ - Increment IP address by a integer number. + Increment IP address by an integer number. Args: ip (str): IP address in string format. @@ -50,13 +50,13 @@ def ansible_stdout_to_str(ansible_stdout): def get_vlan_subnet(host_ans): """ - Get Vlan subnet of a T0 device + Get VLAN subnet of a T0 device Args: host_ans: Ansible host instance of the device - Returns : - Vlan subnet, e.g., "192.168.1.1/24" where 192.168.1.1 is gateway + Returns: + VLAN subnet, e.g., "192.168.1.1/24" where 192.168.1.1 is gateway and 24 is prefix length """ mg_facts = host_ans.minigraph_facts(host=host_ans.hostname)['ansible_facts'] diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py index ceeb150ef67..ed24ae67818 100644 --- a/tests/common/ixia/ixia_fixtures.py +++ b/tests/common/ixia/ixia_fixtures.py @@ -1,3 +1,8 @@ +"""This module contains the necessary fixtures for running test cases with + Ixia devices and IxNetwork. If more fixtures are required, they should be +included in this file. +""" + import pytest import pprint from common.devices import FanoutHost @@ -94,6 +99,7 @@ def ixia_dev(duthost, fanouthosts): result[hostname] = duthost.host.options['inventory_manager'].get_host(hostname).get_vars()['ansible_host'] return result + @pytest.fixture(scope = "function") def ixia_api_server_session(ixia_api_serv_ip, ixia_api_serv_user, ixia_api_serv_passwd, ixia_api_serv_port, diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index b62ffb263c8..ff1c37a5bd0 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -105,12 +105,12 @@ def get_chassis_ip (self) : """This function returns IP address of a particular chassis (selected earlier using get_fanout_device_details() function). - Args: - This function takes no argument. - Note: If you have not used get_fanout_device_details(), by default 0th (first) chassis remains selected. + Args: + This function takes no argument. + Returns: The IP address """ From c4c531065d2824a8faff994409029c09bca955b5 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Fri, 17 Jul 2020 04:44:58 +0000 Subject: [PATCH 25/52] Added stop_protocols function in ixia_helpers.py --- tests/common/ixia/ixia_helpers.py | 14 ++++++++++++++ tests/ixia/test_ixia_traffic.py | 10 ++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index ff1c37a5bd0..73c98c7ff03 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -295,6 +295,20 @@ def start_protocols(session): logger.info(protocolSummary) +def stop_protocols(session) : + """This function stops all the protocols configured on the IxNetwork + protocol stack (e.g., IP and Ethernet). + + Args: + session (obj) : IxNetwork session object. + + Returns: + None + """ + ixnetwork = session.Ixnetwork + ixnetwork.StopAllProtocols(Arg1='sync') + + def get_traffic_statistics(session, stat_view_name='Flow Statistics'): """This function fetches the traffic statistics information. diff --git a/tests/ixia/test_ixia_traffic.py b/tests/ixia/test_ixia_traffic.py index f0220e32f27..1cb695139e4 100644 --- a/tests/ixia/test_ixia_traffic.py +++ b/tests/ixia/test_ixia_traffic.py @@ -1,11 +1,11 @@ ############################################################################### -# This test cases demonstrates: +# This test case demonstrates: # * All the fixtures required for running ixia script (please see the # arguments of the test function) # * How Ixia chassis card/ports are addressed # * How you can configure/control ixia devices, start traffic and collect # statistics. -# * This simple sanity test cases can be used to check if testbed setup +# * This simple sanity test case can be used to check if testbed setup # is correct or not. ############################################################################### @@ -24,7 +24,8 @@ from common.ixia.ixia_helpers import IxiaFanoutManager, configure_ports,\ create_topology, create_ip_traffic_item_using_wizard_arguments,\ - start_protocols, start_traffic, stop_traffic, get_traffic_statistics + start_protocols, start_traffic, stop_traffic, get_traffic_statistics,\ + stop_protocols from common.ixia.common_helpers import incriment_ip_address @@ -85,5 +86,6 @@ def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, stat_view_name='Traffic Item Statistics') logger.info(stats) - stop_traffic(session) + stop_traffic(session) + stop_protocols(session) From c5ca281d95104c2baac7ff7d1b6fb33ad7a9c091 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Fri, 17 Jul 2020 07:10:28 +0000 Subject: [PATCH 26/52] Removed a sleep and added a debugging statement --- tests/common/ixia/ixia_helpers.py | 1 + tests/ixia/test_ixia_traffic.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index 73c98c7ff03..da2794a4977 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -272,6 +272,7 @@ def create_topology( # ipv6 stack option is left for future extension. pass else : + logger.info("Unsupported address-type") pytest_assert(0) return topology diff --git a/tests/ixia/test_ixia_traffic.py b/tests/ixia/test_ixia_traffic.py index 1cb695139e4..19336def99a 100644 --- a/tests/ixia/test_ixia_traffic.py +++ b/tests/ixia/test_ixia_traffic.py @@ -58,9 +58,8 @@ def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, gw_start=gateway_ip, gw_incr_step='0.0.0.0') + logger.info("Starting all protocols") start_protocols(session) - logger.info("Wait for 5 seconds for IPv4 sessions to up.") - time.sleep(5) # Create a traffic item logger.info("Configuring traffic.") From 9ab2d655a69309b027cd0ac9744db53dcacc92e6 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Fri, 17 Jul 2020 08:08:31 +0000 Subject: [PATCH 27/52] [ixia/Keysight] updating the review comments PR1819 --- tests/common/devices.py | 36 +- tests/common/ixia/__init__.py | 2 +- tests/common/ixia/common_helpers.py | 95 +++++ tests/common/ixia/ixia_fixtures.py | 120 ++++-- tests/common/ixia/ixia_helpers.py | 490 ++++++++++++++++++++----- tests/ixia/test_ixia_traffic.py | 90 +++++ tests/ixia/test_ixia_traffic_restpy.py | 153 -------- 7 files changed, 716 insertions(+), 270 deletions(-) create mode 100644 tests/common/ixia/common_helpers.py create mode 100644 tests/ixia/test_ixia_traffic.py delete mode 100644 tests/ixia/test_ixia_traffic_restpy.py diff --git a/tests/common/devices.py b/tests/common/devices.py index 24959896d2d..2a375270742 100644 --- a/tests/common/devices.py +++ b/tests/common/devices.py @@ -851,27 +851,53 @@ def exec_template(self, ansible_root, ansible_playbook, inventory, **kwargs): if res["localhost"]["rc"] != 0: raise Exception("Unable to execute template\n{}".format(res["localhost"]["stdout"])) + class IxiaHost (AnsibleHostBase): - """ - @summary: For running ansible module on Ixia Fanout switch in future (TBD). - Now it is just a place holder + """ This class is a place-holder for running ansible module on Ixia + fanout devices in future (TBD). """ def __init__ (self, ansible_adhoc, os, hostname, device_type) : + """ Initializing Ixia fanout host for using ansible modules. + + Note: Right now, it is just a place holder. + + Args: + ansible_adhoc :The pytest-ansible fixture + os (str): The os type of Ixia Fanout. + hostname (str): The Ixia fanout host-name + device_type (str): The Ixia fanout device type. + """ self.ansible_adhoc = ansible_adhoc self.os = os self.hostname = hostname self.device_type = device_type - def get_host_name (self) : + def get_host_name (self): + """Returns the Ixia hostname + + Args: + This function takes no argument. + """ return self.hostname def get_os (self) : + """Returns the os type of the ixia device. + + Args: + This function takes no argument. + """ return self.os - def exacute (self, cmd) : + def execute (self, cmd) : + """Execute a given command on ixia fanout host. + + Args: + cmd (str): Command to be executed. + """ if (self.os == 'ixia') : eval(cmd) + class FanoutHost(): """ @summary: Class for Fanout switch diff --git a/tests/common/ixia/__init__.py b/tests/common/ixia/__init__.py index fd92c343462..77f343fca0b 100644 --- a/tests/common/ixia/__init__.py +++ b/tests/common/ixia/__init__.py @@ -1 +1 @@ -# place fro ixia fixtures +# Place for Ixia fixtures. diff --git a/tests/common/ixia/common_helpers.py b/tests/common/ixia/common_helpers.py new file mode 100644 index 00000000000..0ff4971a13c --- /dev/null +++ b/tests/common/ixia/common_helpers.py @@ -0,0 +1,95 @@ +"""This module contains some auxiliary functions that are required +to support automation activities. These functions are used for various +secondary activities like convert the ansible Unicode STDOUT output +to string, get IP address in a subnet, increment an IP address, get +VLAN subnet etc. + +This file is also a placeholder for auxiliary function that are +required for supporting automation with Ixia devices in future: +like collecting diagnostics, uploading and downloading files +to/from API server, processing the statistics after obtaining them +in .csv format etc. +""" + +import ipaddr +from netaddr import IPNetwork + +def incriment_ip_address (ip, incr=1) : + """ + Increment IP address by an integer number. + + Args: + ip (str): IP address in string format. + incr (int): Increment by. + + Return: + IP address in the argument incremented by the given integer. + """ + ipaddress = ipaddr.IPv4Address(ip) + ipaddress = ipaddress + incr + return_value = ipaddress._string_from_ip_int(ipaddress._ip) + return(return_value) + + +def ansible_stdout_to_str(ansible_stdout): + """ + The stdout of Ansible host is essentially a list of unicode characters. + This function converts it to a string. + + Args: + ansible_stdout: stdout of Ansible + + Returns: + Return a string + """ + result = "" + for x in ansible_stdout: + result += x.encode('UTF8') + return result + + +def get_vlan_subnet(host_ans): + """ + Get VLAN subnet of a T0 device + + Args: + host_ans: Ansible host instance of the device + + Returns: + VLAN subnet, e.g., "192.168.1.1/24" where 192.168.1.1 is gateway + and 24 is prefix length + """ + mg_facts = host_ans.minigraph_facts(host=host_ans.hostname)['ansible_facts'] + mg_vlans = mg_facts['minigraph_vlans'] + + if len(mg_vlans) != 1: + print 'There should be only one Vlan at the DUT' + return None + + mg_vlan_intfs = mg_facts['minigraph_vlan_interfaces'] + prefix_len = mg_vlan_intfs[0]['prefixlen'] + gw_addr = ansible_stdout_to_str(mg_vlan_intfs[0]['addr']) + return gw_addr + '/' + str(prefix_len) + + +def get_addrs_in_subnet(subnet, n): + """ + Get N IP addresses in a subnet. + + Args: + subnet (str): IPv4 subnet, e.g., '192.168.1.1/24' + n (int): Number of IP addresses to get + + Return: + Retuen n IPv4 addresses in this subnet in a list. + """ + ip_addr = subnet.split('/')[0] + ip_addrs = [str(x) for x in list(IPNetwork(subnet))] + ip_addrs.remove(ip_addr) + + """ Try to avoid network and broadcast addresses """ + if len(ip_addrs) >= n + 2: + del ip_addrs[0] + del ip_addrs[-1] + + return ip_addrs[:n] diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py index 1c9a0a1482c..ed24ae67818 100644 --- a/tests/common/ixia/ixia_fixtures.py +++ b/tests/common/ixia/ixia_fixtures.py @@ -1,60 +1,131 @@ +"""This module contains the necessary fixtures for running test cases with + Ixia devices and IxNetwork. If more fixtures are required, they should be +included in this file. +""" + import pytest import pprint from common.devices import FanoutHost from ixnetwork_restpy import SessionAssistant, Files -""" -In an IXIA testbed, there is no PTF docker. -Hence, we use ptf_ip field to store IXIA API server. -This fixture returns the IP address of the IXIA API server. -""" @pytest.fixture(scope = "module") def ixia_api_serv_ip(testbed): + """ + In an Ixia testbed, there is no PTF docker. + Hence, we use ptf_ip field to store Ixia API server. + This fixture returns the IP address of the Ixia API server. + + Args: + testbed (pytest fixture): The testbed fixture. + + Returns: + Ixia PTF server (API server) IP + """ return testbed['ptf_ip'] -""" -Return the username of IXIA API server -""" + @pytest.fixture(scope = "module") def ixia_api_serv_user(duthost): + """ + Return the username of Ixia API server. + + Args: + duthost (pytest fixture): The duthost fixture. + + Returns: + Ixia PTF server (API server) username. + """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['user'] -""" -Return the password of IXIA API server -""" + @pytest.fixture(scope = "module") def ixia_api_serv_passwd(duthost): + """ + Return the password of Ixia API server. + + Args: + duthost (pytest fixture): The duthost fixture. + + Returns: + Ixia PTF server (API server) password. + """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['password'] -""" -Return REST port. -""" + @pytest.fixture(scope = "module") def ixia_api_serv_port(duthost): + """ + Return REST port of the ixia API server. + + Args: + duthost (pytest fixture): The duthost fixture. + + Returns: + Ixia PTF server (API server) REST port. + """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['rest_port'] -""" -IXIA PTF can spawn multiple session on the same REST port. Optional for LINUX, Rewuired for windows -Return the session ID. -""" + @pytest.fixture(scope = "module") def ixia_api_serv_session_id(duthost): + """ + Ixia PTF can spawn multiple session on the same REST port. + Optional for LINUX, required for windows return the session ID. + + Args: + duthost (pytest fixture): The duthost fixture. + + Returns: + Ixia PTF server (API server) session id. + """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['session_id'] -""" -IXIA session manager with PTF server -""" + +@pytest.fixture(scope = "module") +def ixia_dev(duthost, fanouthosts): + """ + Returns the Ixia chassis IP + + Args: + duthost (pytest fixture): The duthost fixture. + fanouthosts (pytest fixture): The fanouthosts fixture. + + Returns: + Ixia Chassis IP. + """ + result = dict() + ixia_dev_hostnames = fanouthosts.keys() + for hostname in ixia_dev_hostnames: + result[hostname] = duthost.host.options['inventory_manager'].get_host(hostname).get_vars()['ansible_host'] + return result + + @pytest.fixture(scope = "function") def ixia_api_server_session(ixia_api_serv_ip, ixia_api_serv_user, ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id) : + """ + Ixia session manager fixture. + + Args: + ixia_api_serv_ip (pytest fixture): ixia_api_serv_ip fixture + ixia_api_serv_user (pytest fixture): ixia_api_serv_user fixture. + ixia_api_serv_passwd (pytest fixture): ixia_api_serv_passwd fixture. + ixia_api_serv_port (pytest fixture): ixia_api_serv_port fixture. + ixia_api_serv_session_id (pytest fixture): ixia_api_serv_session_id + fixture. + + Returns: + IxNetwork Session Id + """ if (ixia_api_serv_session_id != "None") : session = SessionAssistant(IpAddress = ixia_api_serv_ip, UserName = ixia_api_serv_user, Password = ixia_api_serv_passwd, RestPort = ixia_api_serv_port, - SessionId = ixia_api_serv_session_id) + SessionId = ixia_api_serv_session_id, + LogLevel='all') else : session = SessionAssistant(IpAddress = ixia_api_serv_ip, UserName = ixia_api_serv_user, @@ -63,5 +134,8 @@ def ixia_api_server_session(ixia_api_serv_ip, sessionData = session.Session ixNetwork = session.Ixnetwork ixNetwork.NewConfig() - return session + + yield session + ixNetwork.NewConfig() + session.Session.remove() diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index 782521d7360..da2794a4977 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -1,79 +1,35 @@ -import re -from common.reboot import * -""" -@summary: given a DUT interface, return the management IP address of its neighbor IXIA device -@param intf: DUT interface -@param conn_graph_facts: testbed connectivity graph -@param ixia_dev: the mapping of hostname to IP address of IXIA devices -@return the management IP address of its neighbor IXIA device or None if we cannot find it -""" -def get_neigh_ixia_mgmt_ip(intf, conn_graph_facts, ixia_dev): - device_conn = conn_graph_facts['device_conn'] - if intf not in device_conn: - return None - - ixia_dev_hostname = device_conn[intf]['peerdevice'] - if ixia_dev_hostname not in ixia_dev: - return None - - return ixia_dev[ixia_dev_hostname] +# -*- coding: utf-8 -*- +"""This module contains the high-level wrapper function using the APIs defined +by Ixia/Keysights ixnetwork_restpy library functions. Intention of providing +these to SONiC group is to avoid writing multiple low-level rest API calls for +doing the top-level tasks like configure ports, create topology, +start protocols, start traffic etc. +This module also contains a definition of a simple helper class +"IxiaFanoutManager" which can be used to manage cards and ports of ixia +chassis instead of reading it from fanout_graph_facts fixture. """ -@summary: given a DUT interface, return the card of its neighbor IXIA device -@param intf: DUT interface -@param conn_graph_facts: testbed connectivity graph -@return the card of its neighbor IXIA device or None if we cannot find it -""" -def get_neigh_ixia_card(intf, conn_graph_facts): - device_conn = conn_graph_facts['device_conn'] - if intf not in device_conn: - return None - - ixia_intf = device_conn[intf]['peerport'] - pattern = r'Card(\d+)/Port(\d+)' - m = re.match(pattern, ixia_intf) - if m is None: - return None - else: - return m.group(1) - -""" -@summary: given a DUT interface, return the port of its neighbor IXIA device -@param intf: DUT interface -@param conn_graph_facts: testbed connectivity graph -@return the port of its neighbor IXIA device or None if we cannot find it -""" -def get_neigh_ixia_port(intf, conn_graph_facts): - device_conn = conn_graph_facts['device_conn'] - if intf not in device_conn: - return None - - ixia_intf = device_conn[intf]['peerport'] - pattern = r'Card(\d+)/Port(\d+)' - m = re.match(pattern, ixia_intf) - - if m is None: - return None - else: - return m.group(2) - -def parseFanoutConnections (device_conn) : - retval = [] - for key in device_conn.keys() : - pp = device_conn[key]['peerport'] - string = key + '/' + pp - retval.append(string) - retval.sort() - return(retval) - -def getCardPort(i) : - crd = (i.split('/')[0]).replace('Card', '') - prt = (i.split('/')[1]).replace('Port', '') - return (crd, prt) +import re +from common.reboot import logger +from ixnetwork_restpy import SessionAssistant, Files +import pytest class IxiaFanoutManager () : + """Class for managing multiple chassis and extracting the information + like chassis IP, card, port etc. from fanout_graph_fact.""" + def __init__(self,fanout_data) : + """ When multiple chassis are available inside fanout_graph_facts + this method makes a list of chassis connection-details out of it. + So each chassis and details associated with it can be accessed by + a integer index (starting from 0) + + Args: + fanout_data (dict): the dictionary returned by fanout_graph_fact + + """ + self.last_fanout_assessed = None self.fanout_list = [] self.last_device_connection_details = None @@ -82,7 +38,34 @@ def __init__(self,fanout_data) : for i in fanout_data.keys() : self.fanout_list.append(fanout_data[i]) + def __parse_fanout_connections__ (self) : + device_conn = self.last_device_connection_details + retval = [] + for key in device_conn.keys() : + pp = device_conn[key]['peerport'] + string = self.ip_address + '/' + key + '/' + pp + retval.append(string) + retval.sort() + return(retval) + def get_fanout_device_details (self, device_number) : + """With the help of this function you can select the chassis you want + to access. For example get_fanout_device_details(0) selects the + first chassis. It just select the chassis but does not return + anything. The rest of the function then used to extract chassis + information like "get_chassis_ip()" will the return the ip address + of chassis 0 - the first chassis in the list. + + Note: + Counting or indexing starts from 0. That is 0 = 1st cassis, + 1 = 2nd chassis ... + + Args: + device_number (int): the chassis index (0 is the first) + + Returns: + None + """ # Pointer to chassis info self.last_fanout_assessed = device_number @@ -97,29 +80,360 @@ def get_fanout_device_details (self, device_number) : # List of chassis cards and ports self.current_ixia_port_list = \ - self.__parseFanoutConnections__() + self.__parse_fanout_connections__() #return self.fanout_list[self.last_fanout_assessed] - def __parseFanoutConnections__ (self) : - device_conn = self.last_device_connection_details - retval = [] - for key in device_conn.keys() : - pp = device_conn[key]['peerport'] - string = key + '/' + pp - retval.append(string) - retval.sort() - return(retval) - - def getCardPort (self, crd_prt) : - ip = self.ip_address - crd = (crd_prt.split('/')[0]).replace('Card', '') - prt = (crd_prt.split('/')[1]).replace('Port', '') - return (ip, crd, prt) + def get_connection_details (self) : + """This function returns all the details associated with a particular + chassis (selected earlier using get_fanout_device_details() function). + Details of the chassis will be available like chassis IP, card, ports, + peer port etc. in a dictionary format. + Note: If you have not used get_fanout_device_details(), by default 0th + (first) chassis remains selected. + + Args: + This function takes no argument. + + Returns: + Details of the chassis connection as dictionary format. + """ + return(self.last_device_connection_details) + def get_chassis_ip (self) : + """This function returns IP address of a particular chassis + (selected earlier using get_fanout_device_details() function). + + Note: If you have not used get_fanout_device_details(), by default 0th + (first) chassis remains selected. + + Args: + This function takes no argument. + + Returns: + The IP address + """ return self.ip_address - def ports(self) : - return self.current_ixia_port_list + def get_ports(self) : + """This function returns list of ports associated with a chassis + (selected earlier using get_fanout_device_details() function) + as a list of dictionary. + + Note: If you have not used get_fanout_device_details(), by default 0th + (first) chassis remains selected. + + Args: + This function takes no argument. + + Returns: + Dictionary of chassis card port information. + """ + retval = [] + for ports in self.current_ixia_port_list: + info_list = ports.split('/') + dict_element = { + 'ip': info_list[0], + 'card_id': info_list[1].replace('Card', ''), + 'port_id': info_list[2].replace('Port', ''), + 'peer_port': info_list[3], + } + retval.append(dict_element) + + return retval + + +def configure_ports(session, port_list, start_name='port') : + """Configures ports of the IXIA chassis and returns the list + of configured Ixia ports + + Note: This is like the return value of the method, + IxiaFanoutManager.get_ports() + + Args: + session (obj): IXIA session object + port_list (list): List of dictionaries. like below - + [{'ip': 10.0.0.1, card_id: '1', 'port_id': '1'}, + {'ip': 10.0.0.1, card_id: '1', 'port_id': '2'}, ...]. 'ip', + 'card_id' and 'port_id' are the mandatory keys. + start_name (str): (optional) The port name to start with, port + names will be incremented automatically like port1, port2 ... + + + Returns: The list of Ixia port objects if the configuration + succeeds. Otherwise return None + """ + + port_map = session.PortMapAssistant() + ixnetwork = session.Ixnetwork + vports = list() + + # Add default vport properties here. If vport property is not available in + # port_list dictionary get it from here + port_property = { + 'speed': 10000000, + 'ieee_l1_defaults': False, + 'pfc_priotity_groups': [0,1,2,3,4,5,6,7], + 'card_type': 'novusHundredGigLanFcoe', + 'enable_auto_negotiation': False + } + + index = 1 + for port in port_list: + port_name = start_name + '-' + str(index) + index += 1 + """ Map a test port location (ip, card, port) to a virtual port (name) """ + vports.append(port_map.Map( + IpAddress=port['ip'], + CardId=port['card_id'], + PortId=port['port_id'], + Name=port_name) + ) + + """ Connect all mapped virtual ports to test port locations """ + port_map.Connect() + + # Set L1 config + i = 0 + for vport in ixnetwork.Vport.find(): + vport.L1Config.CurrentType = \ + port_list[i].get('card_type', port_property['card_type']) + + vport.L1Config.NovusHundredGigLan.Fcoe.PfcPriorityGroups = \ + port_list[i].get('pfc_priotity_groups', + port_property['pfc_priotity_groups']) + + + vport.L1Config.NovusHundredGigLan.IeeeL1Defaults = \ + port_list[i].get('ieee_l1_defaults', + port_property['ieee_l1_defaults']) + + vport.L1Config.NovusHundredGigLan.EnableAutoNegotiation = \ + port_list[i].get('enable_auto_negotiation', + port_property['enable_auto_negotiation']) + + port_speed = port_list[i].get('speed', port_property['speed']) + vport.L1Config.NovusHundredGigLan.Speed = \ + 'speed{}g'.format(port_speed/1000) + + i += 1 + + return vports + + +def create_topology( + session, + ports, + name='Topology 1', + ip_type='ipv4', + ip_start='10.0.0.1', + ip_incr_step='0.0.1.0', + gw_start='10.0.0.2', + gw_incr_step='0.0.1.0'): + + """ This function creates a topology with ethernet and IP stack on + IxNetwork + + Note: ipv6 stack option is left for future extension. + + Args: + session (obj): Ixia session object. + ports (list): List of IxNetwork port objects, returned by the + function 'configure_ports' + name (str): The name of the topology. + ip_type (str): IP stack type - ipv4 or ipv6. + ip_start (str): Starting interface IP address. + ip_incr_step (str): IP address increment step in IP format like + "0.0.0.1" + gw_start (str): Starting gateway IP address. + gw_incr_step (str): IP address increment step in IP format like + "0.0.0.1" + + Return: IxNetwork topology obect. + """ + + ixnetwork = session.Ixnetwork + + topology = ixnetwork.Topology.add(Name=name, Ports=ports) + ixnetwork.info('Creating Topology Group {}'.format(name)) + + device_group = topology.DeviceGroup.add(Name=name+' DG', Multiplier='1') + ethernet = device_group.Ethernet.add(Name='Ethernet') + if (ip_type == 'ipv4') : + ixnetwork.info('Configure IPv4') + ipv4 = ethernet.Ipv4.add(Name='Ipv4') + ipv4.Address.Increment(start_value=ip_start, step_value=ip_incr_step) + ipv4.Address.Steps.Step = ip_incr_step + + ipv4.GatewayIp.Increment(start_value=gw_start, step_value=gw_incr_step) + ipv4.GatewayIp.Steps.Step = gw_incr_step + elif (ip_type == 'ipv6') : + # ipv6 stack option is left for future extension. + pass + else : + logger.info("Unsupported address-type") + pytest_assert(0) + + return topology + + +def start_protocols(session): + """This function starts all the protocols configured on the IxNetwork + protocol stack (e.g., IP and Ethernet). + + Args: + session (obj) : IxNetwork session object. + + Returns: + None + """ + ixnetwork = session.Ixnetwork + ixnetwork.StartAllProtocols(Arg1='sync') + protocolSummary = session.StatViewAssistant('Protocols Summary') + protocolSummary.CheckCondition('Sessions Not Started', protocolSummary.EQUAL, 0) + protocolSummary.CheckCondition('Sessions Down', protocolSummary.EQUAL, 0) + logger.info(protocolSummary) + + +def stop_protocols(session) : + """This function stops all the protocols configured on the IxNetwork + protocol stack (e.g., IP and Ethernet). + + Args: + session (obj) : IxNetwork session object. + + Returns: + None + """ + ixnetwork = session.Ixnetwork + ixnetwork.StopAllProtocols(Arg1='sync') + + +def get_traffic_statistics(session, stat_view_name='Flow Statistics'): + """This function fetches the traffic statistics information. + + Args: + session (obj) : IxNetwork session object. + stat_view_name (str, optional): Statistics view name. Default + value is 'Flow Statistics' + + Returns: + traffic statistics dictionary. + """ + ixnetwork = session.Ixnetwork + traffic_statistics = session.StatViewAssistant(stat_view_name) + ixnetwork.info('{}\n'.format(traffic_statistics)) + return traffic_statistics + + +def stop_traffic(session): + """ This function stops all the IxNetwork traffic items configured + on all the ports. + Args: + session (obj): IxNetwork session object. + + Returns: + None. + """ + ixnetwork = session.Ixnetwork + ixnetwork.Traffic.StopStatelessTrafficBlocking() + + +def start_traffic(session): + """ This function starts all the IxNetwork traffic items configured + on all the ports. + Args: + session (obj): IxNetwork session object. + + Returns: + None. + """ + ixnetwork = session.Ixnetwork + """ Apply traffic to hardware """ + ixnetwork.Traffic.Apply() + """ Run traffic """ + ixnetwork.Traffic.StartStatelessTrafficBlocking() + + +def create_ip_traffic_item_using_wizard_arguments ( + session, + src_start_port, + src_port_count, + src_first_route_index, + src_route_count, + dst_start_port, + dst_port_count, + dst_first_route_index, + dst_route_count, + name='example_traffic', + traffic_type='ipv4') : + + """ + This function creates a traffic item where source and destination ports + belong to same IxNetwork topology-object. Since source and destination + belong to same topology, source and destination endpoints may be + selected by selecting starting source port, source port count, first + route address index on the source port, source route count, destination + start port, destination port count, destination first-route index, + and destination route count. + + Args: + session (obj): IxNetwork session object. + src_start_port (int): The start port number. + src_port_count (int): The number of ports involved in sending traffic + starting from src_start_port number. Example, if the start port is + port2 and port2 to port5 is sending traffic then src_start_port = 2 + and src_port_count = 3. + src_first_route_index (int): The first route address index. + src_route_count (int): Number of routes starting from the + src_first_route_index. + dst_start_port (int): The first destination port number. + dst_port_count (int): Number of ports involved in receiving the traffic + starting from dst_start_port number. Example, if rhe rx port is + port6 and port7 then dst_start_port = 6 and dst_port_count = 2 + dst_first_route_index (int): The first destination IP index. + dst_route_count (int): Number of destination IPs starting from + dst_first_route_index. + name (str, optional): Name of the traffic item. Default name is + 'example_traffic'. + traffic_type (str, optional): Type of the ip source and destination + (ipv4/ipv6). Default traffic_type is 'ipv4'. + + Returns: + IxNetwork traffic item object. + + """ + + traffic_item = session.Ixnetwork.Traffic.TrafficItem.add( + Name = name, + TrafficType = traffic_type) + + if (traffic_type == 'ipv4') : + obj = '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv4/1' + elif (traffic_type == 'ipv6'): + obj = '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv6/1' + else : + pytest_assert(0) + + src = [{'arg1': obj, + 'arg2': src_start_port, + 'arg3': src_port_count, + 'arg4': src_first_route_index, + 'arg5': dst_route_count} + ] + + dst = [{'arg1': obj, + 'arg2': dst_start_port, + 'arg3': dst_port_count, + 'arg4': dst_first_route_index, + 'arg5': dst_route_count} + ] + + endPoint = traffic_item.EndpointSet.add() + endPoint.ScalableSources = src + endPoint.ScalableDestinations = dst + # Enable tracking. + traffic_item.Tracking.find().TrackBy = ['trackingenabled0'] + return traffic_item diff --git a/tests/ixia/test_ixia_traffic.py b/tests/ixia/test_ixia_traffic.py new file mode 100644 index 00000000000..19336def99a --- /dev/null +++ b/tests/ixia/test_ixia_traffic.py @@ -0,0 +1,90 @@ +############################################################################### +# This test case demonstrates: +# * All the fixtures required for running ixia script (please see the +# arguments of the test function) +# * How Ixia chassis card/ports are addressed +# * How you can configure/control ixia devices, start traffic and collect +# statistics. +# * This simple sanity test case can be used to check if testbed setup +# is correct or not. +############################################################################### + +import logging +import time +import pytest +from common.utilities import wait_until +from common.fixtures.conn_graph_facts import conn_graph_facts, \ + fanout_graph_facts + +from common.reboot import logger + +from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ + ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id, \ + ixia_api_server_session + +from common.ixia.ixia_helpers import IxiaFanoutManager, configure_ports,\ + create_topology, create_ip_traffic_item_using_wizard_arguments,\ + start_protocols, start_traffic, stop_traffic, get_traffic_statistics,\ + stop_protocols + +from common.ixia.common_helpers import incriment_ip_address + +def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, + ixia_api_server_session, fanouthosts): + + logger.info("Connection Graph Facts = %s " %(conn_graph_facts)) + logger.info("Fanout Graph facts = %s" %(fanout_graph_facts)) + logger.info("DUT hostname = %s" %(duthost.hostname)) + + mg_facts = duthost.minigraph_facts(host=duthost.hostname) + gateway_ip = mg_facts['ansible_facts']['minigraph_vlan_interfaces'][0]['addr'] + start_interface_ip = incriment_ip_address(gateway_ip) + + ixiaFanoutHostList = IxiaFanoutManager(fanout_graph_facts) + ixiaFanoutHostList.get_fanout_device_details(device_number = 0) + + session = ixia_api_server_session + + logger.info("Configuring ports.") + port_list = configure_ports(session=session, + port_list=ixiaFanoutHostList.get_ports()) + + logger.info("Creating topology.") + topology = create_topology(session=session, + ports=port_list, + name="Sender", + ip_start=start_interface_ip, + ip_incr_step='0.0.0.1', + gw_start=gateway_ip, + gw_incr_step='0.0.0.0') + + logger.info("Starting all protocols") + start_protocols(session) + + # Create a traffic item + logger.info("Configuring traffic.") + traffic_item = create_ip_traffic_item_using_wizard_arguments( + session=session, + src_start_port=1, + src_port_count=1, + src_first_route_index=1, + src_route_count=1, + dst_start_port=2, + dst_port_count=3, + dst_first_route_index=1, + dst_route_count=1) + + # Generate, apply and start traffic. + start_traffic(session) + + logger.info("run traffic for 5 seconds") + time.sleep(5) + + # Fetch statistics. + stats = get_traffic_statistics(session=session, + stat_view_name='Traffic Item Statistics') + logger.info(stats) + + stop_traffic(session) + stop_protocols(session) + diff --git a/tests/ixia/test_ixia_traffic_restpy.py b/tests/ixia/test_ixia_traffic_restpy.py deleted file mode 100644 index d46006b8ab6..00000000000 --- a/tests/ixia/test_ixia_traffic_restpy.py +++ /dev/null @@ -1,153 +0,0 @@ -############################################################################### -# This test cases demonstrates: -# * All the fixtures required for running ixia script (please see the -# arguments of the test function) -# * How Ixia chassis card/ports are addressed -# * How you can configure/control ixia devices, start traffic and collect -# statistics using REST API -# * This simple sanity test cases can be used to check if testbed setup -# is correct or not - since it prints a lot of testbed data -############################################################################### - -import logging -import time -import pytest -import ipaddr -from common.utilities import wait_until -from common.fixtures.conn_graph_facts import conn_graph_facts, fanout_graph_facts -from common.reboot import * -from ixnetwork_restpy import SessionAssistant, Files - -from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ - ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id, \ - ixia_api_server_session - -from common.ixia.ixia_helpers import get_neigh_ixia_mgmt_ip, get_neigh_ixia_card,\ - get_neigh_ixia_port, IxiaFanoutManager - -import time - -def create_ipv4_traffic_end_points ( - src_start_port, - src_port_count, - src_first_route_index, - src_route_count, - dst_start_port, - dst_port_count, - dst_first_route_index, - dst_route_count) : - - src = [{'arg1': '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv4/1', - 'arg2': src_start_port, - 'arg3': src_port_count, - 'arg4': src_first_route_index, - 'arg5': dst_route_count} - ] - - dst = [{'arg1': '/api/v1/sessions/1/ixnetwork/topology/1/deviceGroup/1/ethernet/1/ipv4/1', - 'arg2': dst_start_port, - 'arg3': dst_port_count, - 'arg4': dst_first_route_index, - 'arg5': dst_route_count} - ] - - return (src, dst) - - -def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, - ixia_api_server_session, fanouthosts): - - logger.info("Connection Graph Facts = %s " %(conn_graph_facts)) - logger.info("Fanout Graph facts = %s" %(fanout_graph_facts)) - logger.info("DUT hostname = %s" %(duthost.hostname)) - - mg_facts = duthost.minigraph_facts(host=duthost.hostname) - gatewayIp = mg_facts['ansible_facts']['minigraph_vlan_interfaces'][0]['addr'] - - ixiaFanoutHostList = IxiaFanoutManager(fanout_graph_facts) - ixiaFanoutHostList.get_fanout_device_details(device_number = 0) - - # Build gateway valuelist. Same gateway IP for all interface - gateway_value_list = [] - for i in ixiaFanoutHostList.ports() : - gateway_value_list.append(gatewayIp) - - # Create ixNetwork interface IP address list - interface_ip_list = [] - ipaddress = ipaddr.IPv4Address(gatewayIp) - for i in ixiaFanoutHostList.ports() : - ipaddress = ipaddress + 1 - interface_ip_list.append(ipaddress._string_from_ip_int(ipaddress._ip)) - - session = ixia_api_server_session - ixNetwork = session.Ixnetwork - portMap = session.PortMapAssistant() - - vport_list = [] - for i in ixiaFanoutHostList.ports() : - (chassisIp, cardId, portId) = ixiaFanoutHostList.getCardPort(i) - vport_list.append(portMap.Map(chassisIp, cardId, portId)) - - t1 = time.time() - portMap.Connect(ChassisTimeout=1200, ForceOwnership=True) - t2 = time.time() - - time_taken = t2 - t1 - logger.info("time-taken to connect = %s" %(time_taken)) - - for vport in vport_list : - vport.L1Config.NovusHundredGigLan.IeeeL1Defaults = False - vport.L1Config.NovusHundredGigLan.EnableAutoNegotiation = False - vport.L1Config.NovusHundredGigLan.EnableRsFec = True - vport.L1Config.NovusHundredGigLan.EnableRsFecStats = True - - topology1 = ixNetwork.Topology.add(Name='Topo1', Ports=vport_list) - deviceGroup1 = topology1.DeviceGroup.add(Name='DG1', Multiplier='1') - ethernet1 = deviceGroup1.Ethernet.add(Name='Eth1') - ipv4 = ethernet1.Ipv4.add(Name='Ipv4') - - ipv4.GatewayIp.ValueList(gateway_value_list) - ipv4.Address.ValueList(interface_ip_list) - - ixNetwork.StartAllProtocols() - logger.info("Wait for 5 seconds for iv4 sessions to up") - time.sleep(5) - - # Create a traffic item - traffic_item = ixNetwork.Traffic.TrafficItem.add( - Name = 'Traffic Test', - TrafficType = 'ipv4') - - # Create a ipv4 source and destination for the endpoint of traffic item. - src_dst_ep = create_ipv4_traffic_end_points ( - src_start_port = 1, - src_port_count = 1, - src_first_route_index = 1, - src_route_count = 1, - dst_start_port = 2, - dst_port_count = 3, - dst_first_route_index = 1, - dst_route_count = 1 - ) - - # Create endpoint set and set source and destination. - endPoint = traffic_item.EndpointSet.add() - endPoint.ScalableSources = src_dst_ep[0] - endPoint.ScalableDestinations = src_dst_ep[1] - - # Enable tracking. - traffic_item.Tracking.find().TrackBy = ['trackingenabled0'] - - # Generate, apply and start traffic. - traffic_item.Generate() - ixNetwork.Traffic.Apply() - ixNetwork.Traffic.Start() - - logger.info("run traffic for 5 seconds") - time.sleep(5) - - # Fetch statistics. - logger.info(session.StatViewAssistant('Traffic Item Statistics')) - ixNetwork.Traffic.Stop() - assert 1 - From aba759a227f0e4f97ee16cca24a99596c94c6481 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Mon, 20 Jul 2020 15:23:21 +0000 Subject: [PATCH 28/52] adding a simple poc --- tests/common/tgen_api/.gitignore | 3 + tests/common/tgen_api/__init__.py | 1 + tests/common/tgen_api/keystgenapi.py | 80 ++++++++++ tests/common/tgen_api/testcases.py | 28 ++++ tests/common/tgen_api/tgenapi.py | 65 ++++++++ tests/common/tgen_api/tgenfixtures.py | 40 +++++ tests/common/tgen_api/tgenmodels.py | 218 ++++++++++++++++++++++++++ tests/ixia/test_ixia_poc.py | 85 ++++++++++ 8 files changed, 520 insertions(+) create mode 100644 tests/common/tgen_api/.gitignore create mode 100644 tests/common/tgen_api/__init__.py create mode 100644 tests/common/tgen_api/keystgenapi.py create mode 100644 tests/common/tgen_api/testcases.py create mode 100644 tests/common/tgen_api/tgenapi.py create mode 100644 tests/common/tgen_api/tgenfixtures.py create mode 100644 tests/common/tgen_api/tgenmodels.py create mode 100644 tests/ixia/test_ixia_poc.py diff --git a/tests/common/tgen_api/.gitignore b/tests/common/tgen_api/.gitignore new file mode 100644 index 00000000000..b943e2c69ca --- /dev/null +++ b/tests/common/tgen_api/.gitignore @@ -0,0 +1,3 @@ +__pycache__ +*.pyc +.pytest_cache diff --git a/tests/common/tgen_api/__init__.py b/tests/common/tgen_api/__init__.py new file mode 100644 index 00000000000..19dba67729a --- /dev/null +++ b/tests/common/tgen_api/__init__.py @@ -0,0 +1 @@ +"""This file is a place holder for Tgen API""" diff --git a/tests/common/tgen_api/keystgenapi.py b/tests/common/tgen_api/keystgenapi.py new file mode 100644 index 00000000000..fa1db2bca66 --- /dev/null +++ b/tests/common/tgen_api/keystgenapi.py @@ -0,0 +1,80 @@ +""" The IxNetwork Test Generator API implementation + + Note: Please note that some of these functions is implemented + with the help of common/ixia/ixia_helpers.py for the demo + purpose. +""" +import json +from typing import Union, List, Dict +from common.reboot import logger + +from tgenapi import TgenApi +from tgenmodels import Config +from ixnetwork_restpy import SessionAssistant +import common.ixia.ixia_helpers as helpers + +class KeysTgenApi(TgenApi): + def __init__(self, **kwargs): + self._vports = None + self.config = Config() + for key in kwargs.keys() : + if (key == 'session') : + self._assistant = kwargs['session'] + self._ixnetwork = self._assistant.Ixnetwork + else : + logger.info('invalid key %s in init' %(key)) + pytest_assert(0) + + def init_tgen(self, config = None) : + """Staging repositary Initial configuration data + """ + logger.info("Staging the initial configuration ....") + if config is not None: + self.config = config + + def connect(self, host): + if ']:' in host: + address, port = host.split(']:') + elif ':' in host: + address, port = host.split(':') + else: + address = host + port = None + self._assistant = SessionAssistant(IpAddress=address, RestPort=port) + self._ixnetwork = self._assistant.Ixnetwork + self._port_map = self._assistant.PortMapAssistant() + + def configure(self): + """ + restpy code goes here + take the configuration objects and use restpy to configure the + test tool + + Note: We expect port list to be a list of dictionaries. + """ + + self._vports = helpers.configure_ports(self._assistant, + self.config.ports._port_list) + + helpers.create_topology( + self._assistant, + self._vports, + name=self.config.topo._ip_address['topo_name'], + ip_type='ipv4', + ip_start=self.config.topo._ip_address['if_ip'], + ip_incr_step=self.config.topo._ip_address['if_ip_step'], + gw_start=self.config.topo._ip_address['gw_ip'], + gw_incr_step=self.config.topo._ip_address['gw_ip_step']) + + def deconfigure(self): + self._ixnetwork.NewConfig() + + def start(self): + helpers.start_protocols(self._assistant) + + def stop(self): + helpers.stop_protocols(self._assistant) + pass + + def json_config(self): + return json.dumps(self.config, default=lambda o: o.__dict__, indent=4) diff --git a/tests/common/tgen_api/testcases.py b/tests/common/tgen_api/testcases.py new file mode 100644 index 00000000000..b3e35437e82 --- /dev/null +++ b/tests/common/tgen_api/testcases.py @@ -0,0 +1,28 @@ +""" A SONIC test case using the injected tgen_api fixture +""" +import pytest +from tgenapi import TgenApi +from tgenmodels import * +from tgenfixtures import * + + +def test_pfc_pause_lossless(tgen_api): + # configure dut + + # create a configuration + tgen_api.config.flows = [ + Flow('Test Traffic', tgen_api.config.ports[0], packet=[Ethernet(), Ipv4()]), + Flow('Background Traffic', tgen_api.config.ports[0], packet=[Ethernet()]), + Flow('Pause Traffic', tgen_api.config.ports[1], packet=[PfcPause()]) + ] + + # configure and control the test tool + tgen_api.configure() + tgen_api.start() + tgen_api.stop() + + # asserts for pass/fail + + # teardown the test tool + tgen_api.deconfigure() + diff --git a/tests/common/tgen_api/tgenapi.py b/tests/common/tgen_api/tgenapi.py new file mode 100644 index 00000000000..4605af5e679 --- /dev/null +++ b/tests/common/tgen_api/tgenapi.py @@ -0,0 +1,65 @@ +""" The abstract Test Generator API +""" +#from abc import ABC, abstractmethod +from abc import ABCMeta, abstractmethod +from typing import Union, List, Dict +from tgenmodels import Config + + +class TgenApi(): + # __slots__ = ['config'] + __metaclass__ = ABCMeta + + @abstractmethod + def __init__(self, **kwargs): + super().__init__() + pass + + @abstractmethod + def init_tgen(self, config): + """Initialize your traffic configuration + """ + pass + + @abstractmethod + def connect(self, host): + """Information for establishing a connection to the test tool + """ + pass + + @abstractmethod + def configure(self, config = None): + """Configure the test tool using the data from the Config + """ + pass + + @abstractmethod + def deconfigure(self): + """Deconfigure the test tool + """ + pass + + @abstractmethod + def start(self): + """Start traffic on the test tool + """ + pass + + @abstractmethod + def stop(self): + """Stop traffic on the test tool + """ + pass + + @abstractmethod + def json_config(self): + """Get the json representation of the Config object + + Returns: + str: A json string representation of the Config object + """ + pass + + def __str__(self): + raise NotImplementedError + diff --git a/tests/common/tgen_api/tgenfixtures.py b/tests/common/tgen_api/tgenfixtures.py new file mode 100644 index 00000000000..59cebc3410e --- /dev/null +++ b/tests/common/tgen_api/tgenfixtures.py @@ -0,0 +1,40 @@ +""" +A SONIC pytest fixture returning a TgenApi implementation +""" + +import pytest +from tgenmodels import Config, Port +from tgenapi import TgenApi +from keystgenapi import KeysTgenApi + +from common.fixtures.conn_graph_facts import conn_graph_facts, \ + fanout_graph_facts + +from common.reboot import logger +from common.ixia.ixia_helpers import IxiaFanoutManager + +import common.tgen_api + + +@pytest.fixture +def TgenApi(ixia_api_server_session): + """ + Fixture: TgenApi -> Creates a IxNetwork session. + + Note: ixia_api_server_session is a function level fixture and + automatically removes session when test case execution is + complete. So we need not take care of session tear down + here. + + Args: + ixia_api_server_session (pytest fisture): ixia_api_server_session + fixture + + Returns: + Ixia KeysTgenApi object instance, which is a derived class from + abstract class "TgenApi". + """ + + tgen = KeysTgenApi(session=ixia_api_server_session) + return tgen + diff --git a/tests/common/tgen_api/tgenmodels.py b/tests/common/tgen_api/tgenmodels.py new file mode 100644 index 00000000000..b14b6dd9577 --- /dev/null +++ b/tests/common/tgen_api/tgenmodels.py @@ -0,0 +1,218 @@ +""" The abstract Test Generator Data Model +""" +from typing import Union, List, Dict +from common.reboot import logger + + +class Port(object): + """Port model + Port is a list of dict + """ + __slots__ = ['_port_list'] + + def __init__(self, port_list): + self._port_list = [] + if (isinstance(port_list, list)): + for port in port_list: + if (isinstance(port, dict)): + temp = {} + for key in port.keys(): + if key in ['ip', 'card_id', 'port_id']: + temp[key] = port[key] + self._port_list.append(temp) + logger.info(self._port_list) + else : + logger.info('Port must be a list of dict') + pytest_assert(0) + + +Ports = Union[Port, List[Port], Dict] +class Layer1(object): + #def __init__(ports: Union[Port, List[Port]]): + def __init__(ports): + self.ports = ports + + def novus_hundred_gig_lan(auto_instrumentation='floating', bad_blocks_number=4): + """Novus 100 Gb Lan card settings + """ + pass + + def uhd(auto_instrumentation='floating'): + """Uhd appliance settings + """ + pass + + def ethernet_vm(): + """Ethernet virtual machine settings + """ + pass + +class Topology (object): + def __init__(self, ip_address) : + self._ip_address = {} + if (isinstance(ip_address, dict)) : + for key in ip_address.keys() : + if key in ['if_ip', 'if_ip_step', 'gw_ip', 'gw_ip_step', 'topo_name']: + self._ip_address[key] = ip_address[key] + logger.info(self._ip_address) + else: + logger.info('Port must be a list of dict') + + +class Ethernet(object): + """Ethernet II traffic protocol header + + Properties + ---------- + dst_address (str=01:80:C2:00:00:01): Destination address + src_address (str=00:00:AA:00:00:01): Source address + ether_type (str=0x8808): Ethernet type + pfc_queue (str=0): PFC Queue + """ + __STACK_TYPE_ID = 'ethernet' + __FIELD_MAP = { + 'dst_address': { + 'fieldTypeId': 'ethernet.header.destinationAddress', + 'default': '00:00:00:00:00:00' + }, + 'src_address': { + 'fieldTypeId': 'ethernet.header.sourceAddress', + 'default': '00:00:00:00:00:00' + }, + 'ether_type': { + 'fieldTypeId': 'ethernet.header.etherType', + 'default': '0xFFFF' + }, + 'pfc_queue': { + 'fieldTypeId': 'ethernet.header.pfcQueue', + 'default': '0' + } + } + __slots__ = ['dst_address', 'src_address', 'ether_type', 'pfc_queue'] + + def __init__(self): + self.dst_address = Ethernet.__FIELD_MAP['dst_address']['default'] + self.src_address = Ethernet.__FIELD_MAP['src_address']['default'] + self.ether_type = Ethernet.__FIELD_MAP['ether_type']['default'] + self.pfc_queue = Ethernet.__FIELD_MAP['pfc_queue']['default'] + + +class Ipv4(object): + """Ethernet II traffic protocol header + + Properties + ---------- + dst_address (str=01:80:C2:00:00:01): Destination address + src_address (str=00:00:AA:00:00:01): Source address + """ + __STACK_TYPE_ID = 'ipv4' + __FIELD_MAP = { + 'dst_address': { + 'fieldTypeId': 'ipv4.header.dstIp', + 'default': '0.0.0.0' + }, + 'src_address': { + 'fieldTypeId': 'ipv4.header.srcIp', + 'default': '0.0.0.0' + } + } + __slots__ = ['dst_address', 'src_address'] + + def __init__(self): + self.dst_address = Ipv4.__FIELD_MAP['dst_address']['default'] + self.src_address = Ipv4.__FIELD_MAP['src_address']['default'] + + +class PfcPause(object): + """PFC PAUSE (802.1Qbb) traffic protocol header + + Properties + ---------- + dst_address (str=01:80:C2:00:00:01): Destination address + src_address (str=00:00:AA:00:00:01): Source address + ether_type (str=0x8808): Ethernet type + control_op_code (str=0x0101): Control operation code + """ + __FIELD_MAP = { + 'dst_address': { + 'fieldTypeId': 'pfcPause.header.header.dstAddress', + 'default': '01:80:C2:00:00:01' + }, + 'src_address': { + 'fieldTypeId': 'pfcPause.header.header.srcAddress', + 'default': '00:00:AA:00:00:01' + }, + 'ether_type': { + 'fieldTypeId': 'pfcPause.header.header.etherType', + 'default': '0x8808' + }, + 'control_op_code': { + 'fieldTypeId': 'pfcPause.header.macControl.controlOpCode', + 'default': '0x0101' + } + } + __slots__ = ['dst_address', 'src_address', 'ether_type', 'control_op_code'] + + def __init__(self): + self.dst_address = PfcPause.__FIELD_MAP['dst_address']['default'] + self.src_address = PfcPause.__FIELD_MAP['src_address']['default'] + self.ether_type = PfcPause.__FIELD_MAP['ether_type']['default'] + self.control_op_code = PfcPause.__FIELD_MAP['control_op_code']['default'] + + +class Flow(object): + """Traffic flow container + + Properties + ---------- + - name (str): Unique name of the traffic flow + - tx_port (Union[str, Port]): The name of a Port object that will transmit + traffic + - rx_ports (list(str)): Intended receive ports + - packet (list(Union[Ethernet, Ipv4, PfcPause])): The traffic protocols + that define the packet for the flow + """ + __slots__ = ['name', 'tx_port', 'rx_ports', 'packet'] + + def __init__(self, name, tx_port, rx_ports=None, packet=None): + self.name = name + self.tx_port = tx_port + self.rx_ports = rx_ports + self.packet = packet + + +Flows = Union[Flow, List[Flow]] +class Config(object): + """Test tool confguration container + + Properties + ---------- + - ports (list(Port)): A list of Port objects + - layer1 (list(Layer1)): A list of Layer1 objects + - flows (list(Flow)): A list of Flow objects + """ + __slots__ = ['ports', 'layer1', 'topo', 'flows'] + + def __init__(self, ports = None, layer1 = None, topo = None, flows = None): + + # Add port + if ports is not None: + if isinstance(ports, Port): + self.ports = ports + # Add layer 1 + if layer1 is not None: + if isinstance(layer1, Layer1): + self.layer1 = layer1 + # Add IP + if topo is not None: + if isinstance(topo, Topology): + self.topo = topo + + # Add flows + self.flows = [] + if flows is not None: + if isinstance(flows, list): + self.flows = flows + else: + self.flows.append(flows) + diff --git a/tests/ixia/test_ixia_poc.py b/tests/ixia/test_ixia_poc.py new file mode 100644 index 00000000000..e583df1c5c9 --- /dev/null +++ b/tests/ixia/test_ixia_poc.py @@ -0,0 +1,85 @@ +import logging +import time +import pytest +from common.utilities import wait_until + +from common.reboot import logger + +from common.fixtures.conn_graph_facts import conn_graph_facts, \ + fanout_graph_facts + +from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ + ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id, \ + ixia_api_server_session + +from common.ixia.ixia_helpers import IxiaFanoutManager +from common.ixia.common_helpers import incriment_ip_address + +from common.tgen_api.tgenfixtures import TgenApi + +""" +Import Tgen Data Models + Related to protocol configuration: + Port -> Port configuration (name and location) + Layer1 -> Layer1 configuration + Topology -> Topology configuration. + Related to traffic configuration: + Ethernet-> Ethernet layer configuration of data packet + IP -> IP layer configuration of data packet. + PfcPause -> PFC pause configuration + Flow -> Flow configuration + Repository all the above config: + Config -> Repository all the above config +""" +from common.tgen_api.tgenmodels import Port, Layer1, Topology, Ethernet,\ + Ipv4, PfcPause, Flow, Config + +def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, + fanouthosts, TgenApi): + + """ + This test module demonstrates capability of the tgen fixture. + + Note: All the below fixture in the argument must be available in the + test case. They cannot me hidden inside other fixture. + + Args: + testbed (pytest fixture): Pytest fixture to get the testbed + information. + + conn_graph_facts (pytest fixture): Pytest fixture to get the connection + graph. + + duthost (pytest fixture): Pytest fixture to get the DUT details. + + fanout_graph_facts (pytest fixture): Details of the fanout devices. + + fanouthosts (pytest fixture): Details of the fanout hosts. + + TgenApi (pytest fixture) : Ixia defined pytest fixture. + """ + + # Extract the Gateway IP from dut host fixture. + mg_facts = duthost.minigraph_facts(host=duthost.hostname) + gateway_ip = mg_facts['ansible_facts']['minigraph_vlan_interfaces'][0]['addr'] + start_interface_ip = incriment_ip_address(gateway_ip) + + # extract the ports from fanout_graph_facts fixture. + ixiaFanoutHostList = IxiaFanoutManager(fanout_graph_facts) + ixiaFanoutHostList.get_fanout_device_details(device_number = 0) + + logger.info("Configuring ports.") + config = Config(ports=Port(ixiaFanoutHostList.get_ports()), + topo=Topology({'topo_name':'T1', 'if_ip': start_interface_ip, 'if_ip_step': '0.0.0.1', 'gw_ip': gateway_ip, 'gw_ip_step': '0.0.0.0'})) + + TgenApi.init_tgen(config) + TgenApi.configure() + TgenApi.start() + + logger.info("wait for two seconds") + time.sleep(2) + + + + logger.info("I am blank!!") + From 23a687fa29d9621a8625596dc47e2361b16f8aca Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Mon, 20 Jul 2020 15:47:01 +0000 Subject: [PATCH 29/52] Changed ixia/test_ixia_poc.py a bit --- tests/ixia/test_ixia_poc.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/ixia/test_ixia_poc.py b/tests/ixia/test_ixia_poc.py index e583df1c5c9..aa3977bfab8 100644 --- a/tests/ixia/test_ixia_poc.py +++ b/tests/ixia/test_ixia_poc.py @@ -79,7 +79,5 @@ def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, logger.info("wait for two seconds") time.sleep(2) + TgenApi.stop() - - logger.info("I am blank!!") - From bc275950a846143ff9ffc5ea1d8d863c3aada832 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Tue, 21 Jul 2020 11:40:56 +0000 Subject: [PATCH 30/52] Implementing the comments of PR1819 --- tests/common/devices.py | 2 + tests/common/ixia/ixia_fixtures.py | 63 ++++++------ tests/common/ixia/ixia_helpers.py | 155 +++++++++++++++++++++++------ 3 files changed, 158 insertions(+), 62 deletions(-) diff --git a/tests/common/devices.py b/tests/common/devices.py index 2a375270742..d42a6666ae4 100644 --- a/tests/common/devices.py +++ b/tests/common/devices.py @@ -867,6 +867,8 @@ def __init__ (self, ansible_adhoc, os, hostname, device_type) : hostname (str): The Ixia fanout host-name device_type (str): The Ixia fanout device type. """ + super().__init__() + self.ansible_adhoc = ansible_adhoc self.os = os self.hostname = hostname diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py index ed24ae67818..ad6f09653ab 100644 --- a/tests/common/ixia/ixia_fixtures.py +++ b/tests/common/ixia/ixia_fixtures.py @@ -1,12 +1,11 @@ -"""This module contains the necessary fixtures for running test cases with - Ixia devices and IxNetwork. If more fixtures are required, they should be +""" +This module contains the necessary fixtures for running test cases with +Ixia devices and IxNetwork. If more fixtures are required, they should be included in this file. """ import pytest -import pprint -from common.devices import FanoutHost -from ixnetwork_restpy import SessionAssistant, Files +from ixnetwork_restpy import SessionAssistant @pytest.fixture(scope = "module") def ixia_api_serv_ip(testbed): @@ -19,7 +18,7 @@ def ixia_api_serv_ip(testbed): testbed (pytest fixture): The testbed fixture. Returns: - Ixia PTF server (API server) IP + Ixia API server IP """ return testbed['ptf_ip'] @@ -33,7 +32,7 @@ def ixia_api_serv_user(duthost): duthost (pytest fixture): The duthost fixture. Returns: - Ixia PTF server (API server) username. + Ixia API server username. """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['user'] @@ -47,7 +46,7 @@ def ixia_api_serv_passwd(duthost): duthost (pytest fixture): The duthost fixture. Returns: - Ixia PTF server (API server) password. + Ixia API server password. """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['password'] @@ -55,13 +54,13 @@ def ixia_api_serv_passwd(duthost): @pytest.fixture(scope = "module") def ixia_api_serv_port(duthost): """ - Return REST port of the ixia API server. + This fixture returns the TCP port for REST API of the ixia API server. Args: duthost (pytest fixture): The duthost fixture. Returns: - Ixia PTF server (API server) REST port. + Ixia API server REST port. """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['rest_port'] @@ -69,14 +68,14 @@ def ixia_api_serv_port(duthost): @pytest.fixture(scope = "module") def ixia_api_serv_session_id(duthost): """ - Ixia PTF can spawn multiple session on the same REST port. + Ixia API server can spawn multiple session on the same REST port. Optional for LINUX, required for windows return the session ID. Args: duthost (pytest fixture): The duthost fixture. Returns: - Ixia PTF server (API server) session id. + Ixia API server session id. """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['session_id'] @@ -84,14 +83,15 @@ def ixia_api_serv_session_id(duthost): @pytest.fixture(scope = "module") def ixia_dev(duthost, fanouthosts): """ - Returns the Ixia chassis IP + Returns the Ixia chassis IP. This fixture can return multiple IPs if + multiple Ixia chassis are present in the test topology. Args: duthost (pytest fixture): The duthost fixture. fanouthosts (pytest fixture): The fanouthosts fixture. Returns: - Ixia Chassis IP. + Dictionary of Ixia Chassis IP/IPs. """ result = dict() ixia_dev_hostnames = fanouthosts.keys() @@ -101,9 +101,12 @@ def ixia_dev(duthost, fanouthosts): @pytest.fixture(scope = "function") -def ixia_api_server_session(ixia_api_serv_ip, - ixia_api_serv_user, ixia_api_serv_passwd, ixia_api_serv_port, - ixia_api_serv_session_id) : +def ixia_api_server_session( + ixia_api_serv_ip, + ixia_api_serv_user, + ixia_api_serv_passwd, + ixia_api_serv_port, + ixia_api_serv_session_id) : """ Ixia session manager fixture. @@ -116,23 +119,21 @@ def ixia_api_server_session(ixia_api_serv_ip, fixture. Returns: - IxNetwork Session Id + IxNetwork Session """ - if (ixia_api_serv_session_id != "None") : - session = SessionAssistant(IpAddress = ixia_api_serv_ip, - UserName = ixia_api_serv_user, - Password = ixia_api_serv_passwd, - RestPort = ixia_api_serv_port, - SessionId = ixia_api_serv_session_id, - LogLevel='all') + if (ixia_api_serv_session_id.lower() != 'none') : + session = SessionAssistant(IpAddress=ixia_api_serv_ip, + UserName=ixia_api_serv_user, + Password=ixia_api_serv_passwd, + RestPort=ixia_api_serv_port, + SessionId=ixia_api_serv_session_id) else : - session = SessionAssistant(IpAddress = ixia_api_serv_ip, - UserName = ixia_api_serv_user, - Password = ixia_api_serv_passwd, - RestPort = ixia_api_serv_port) - sessionData = session.Session - ixNetwork = session.Ixnetwork + session = SessionAssistant(IpAddress=ixia_api_serv_ip, + UserName=ixia_api_serv_user, + Password=ixia_api_serv_passwd, + RestPort=ixia_api_serv_port) + ixNetwork = session.Ixnetwork ixNetwork.NewConfig() yield session diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index da2794a4977..ba33a6753ee 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -10,10 +10,8 @@ chassis instead of reading it from fanout_graph_facts fixture. """ -import re from common.reboot import logger from ixnetwork_restpy import SessionAssistant, Files -import pytest class IxiaFanoutManager () : """Class for managing multiple chassis and extracting the information @@ -26,10 +24,86 @@ def __init__(self,fanout_data) : a integer index (starting from 0) Args: - fanout_data (dict): the dictionary returned by fanout_graph_fact - + fanout_data (dict): the dictionary returned by fanout_graph_fact. + Example format of the fanout_data is given below + + {u'ixia-sonic': { + u'device_conn': { + u'Card9/Port1': { + u'peerdevice': u'sonic-s6100-dut', + u'peerport': u'Ethernet0', + u'speed': u'100000' + }, + u'Card9/Port2': { + u'peerdevice': u'sonic-s6100-dut', + u'peerport': u'Ethernet4', + u'speed': u'100000' + }, + u'Card9/Port3': { + u'peerdevice': u'sonic-s6100-dut', + u'peerport': u'Ethernet8', + u'speed': u'100000' + }, + 'Card9/Port4': { + u'peerdevice': u'sonic-s6100-dut', + u'peerport': u'Ethernet12', + u'speed': u'100000' + }, + u'Card9/Port5': { + u'peerdevice': u'sonic-s6100-dut', + u'peerport': u'Ethernet16', + u'speed': u'100000' + }, + u'Card9/Port6': { + u'peerdevice': u'sonic-s6100-dut', + u'peerport': u'Ethernet20', + u'speed': u'100000' + } + }, + u'device_info': { + u'HwSku': u'IXIA-tester', + u'ManagementGw': u'10.36.78.54', + u'ManagementIp': u'10.36.78.53/32', + u'Type': u'DevIxiaChassis', + u'mgmtip': u'10.36.78.53' + }, + u'device_port_vlans': { + u'Card9/Port1': { + u'mode': u'Access', + u'vlanids': u'300', + u'vlanlist': [300] + }, + u'Card9/Port2': { + u'mode': u'Access', + u'vlanids': u'301', + u'vlanlist': [301] + }, + u'Card9/Port3': { + u'mode': u'Access', + u'vlanids': u'302', + u'vlanlist': [302] + }, + u'Card9/Port4': { + u'mode': u'Access', + u'vlanids': u'300', + u'vlanlist': [300] + }, + u'Card9/Port5': { + u'mode': u'Access', + u'vlanids': u'301', + u'vlanlist': [301] + }, + u'Card9/Port6': { + u'mode': u'Access', + u'vlanids': u'302', + u'vlanlist': [302] + } + }, + u'device_vlan_list': [301, 302, 300, 302, 300, 301], + u'device_vlan_range': [u'300-302'] + } + } """ - self.last_fanout_assessed = None self.fanout_list = [] self.last_device_connection_details = None @@ -131,8 +205,8 @@ def get_ports(self) : Dictionary of chassis card port information. """ retval = [] - for ports in self.current_ixia_port_list: - info_list = ports.split('/') + for port in self.current_ixia_port_list: + info_list = port.split('/') dict_element = { 'ip': info_list[0], 'card_id': info_list[1].replace('Card', ''), @@ -144,6 +218,19 @@ def get_ports(self) : return retval +def clean_configuration(session) : + """Clean up the configurations cteated in IxNetwork API server. + + Args: + session (IxNetwork Session object): IxNetwork session. + + Returns: + None + """ + ixNetwork = session.Ixnetwork + ixNetwork.NewConfig() + + def configure_ports(session, port_list, start_name='port') : """Configures ports of the IXIA chassis and returns the list of configured Ixia ports @@ -223,14 +310,14 @@ def configure_ports(session, port_list, start_name='port') : def create_topology( - session, - ports, - name='Topology 1', - ip_type='ipv4', - ip_start='10.0.0.1', - ip_incr_step='0.0.1.0', - gw_start='10.0.0.2', - gw_incr_step='0.0.1.0'): + session, + ports, + name='Topology 1', + ip_type='ipv4', + ip_start='10.0.0.1', + ip_incr_step='0.0.0.1', + gw_start='10.0.0.2', + gw_incr_step='0.0.0.0'): """ This function creates a topology with ethernet and IP stack on IxNetwork @@ -357,17 +444,17 @@ def start_traffic(session): def create_ip_traffic_item_using_wizard_arguments ( - session, - src_start_port, - src_port_count, - src_first_route_index, - src_route_count, - dst_start_port, - dst_port_count, - dst_first_route_index, - dst_route_count, - name='example_traffic', - traffic_type='ipv4') : + session, + src_start_port, + src_port_count, + src_first_route_index, + src_route_count, + dst_start_port, + dst_port_count, + dst_first_route_index, + dst_route_count, + name='example_traffic', + traffic_type='ipv4') : """ This function creates a traffic item where source and destination ports @@ -385,16 +472,22 @@ def create_ip_traffic_item_using_wizard_arguments ( starting from src_start_port number. Example, if the start port is port2 and port2 to port5 is sending traffic then src_start_port = 2 and src_port_count = 3. - src_first_route_index (int): The first route address index. + src_first_route_index (int): The first route address index. Conceptually + assume the routes (source IP address) are organized as list. Choose + the starting route index. src_route_count (int): Number of routes starting from the - src_first_route_index. + src_first_route_index. So this together src_first_route_index will + determine total number of sources. dst_start_port (int): The first destination port number. dst_port_count (int): Number of ports involved in receiving the traffic - starting from dst_start_port number. Example, if rhe rx port is + starting from dst_start_port number. Example, if the rx port is port6 and port7 then dst_start_port = 6 and dst_port_count = 2 - dst_first_route_index (int): The first destination IP index. + dst_first_route_index (int): The first destination IP index. Conceptually + assume the routes (destination IP address) organized as list. Choose + the starting destination route index. dst_route_count (int): Number of destination IPs starting from - dst_first_route_index. + dst_first_route_index. So this together with dst_first_route_index + will determine the total number of destinations. name (str, optional): Name of the traffic item. Default name is 'example_traffic'. traffic_type (str, optional): Type of the ip source and destination From b3970a971941586a2685f02c157e399bfd8a4448 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Tue, 21 Jul 2020 11:51:46 +0000 Subject: [PATCH 31/52] Implementing the comments of PR1819 --- tests/common/devices.py | 2 + tests/common/ixia/ixia_fixtures.py | 63 ++++++------ tests/common/ixia/ixia_helpers.py | 157 +++++++++++++++++++++++------ 3 files changed, 159 insertions(+), 63 deletions(-) diff --git a/tests/common/devices.py b/tests/common/devices.py index 2a375270742..d42a6666ae4 100644 --- a/tests/common/devices.py +++ b/tests/common/devices.py @@ -867,6 +867,8 @@ def __init__ (self, ansible_adhoc, os, hostname, device_type) : hostname (str): The Ixia fanout host-name device_type (str): The Ixia fanout device type. """ + super().__init__() + self.ansible_adhoc = ansible_adhoc self.os = os self.hostname = hostname diff --git a/tests/common/ixia/ixia_fixtures.py b/tests/common/ixia/ixia_fixtures.py index ed24ae67818..ad6f09653ab 100644 --- a/tests/common/ixia/ixia_fixtures.py +++ b/tests/common/ixia/ixia_fixtures.py @@ -1,12 +1,11 @@ -"""This module contains the necessary fixtures for running test cases with - Ixia devices and IxNetwork. If more fixtures are required, they should be +""" +This module contains the necessary fixtures for running test cases with +Ixia devices and IxNetwork. If more fixtures are required, they should be included in this file. """ import pytest -import pprint -from common.devices import FanoutHost -from ixnetwork_restpy import SessionAssistant, Files +from ixnetwork_restpy import SessionAssistant @pytest.fixture(scope = "module") def ixia_api_serv_ip(testbed): @@ -19,7 +18,7 @@ def ixia_api_serv_ip(testbed): testbed (pytest fixture): The testbed fixture. Returns: - Ixia PTF server (API server) IP + Ixia API server IP """ return testbed['ptf_ip'] @@ -33,7 +32,7 @@ def ixia_api_serv_user(duthost): duthost (pytest fixture): The duthost fixture. Returns: - Ixia PTF server (API server) username. + Ixia API server username. """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['user'] @@ -47,7 +46,7 @@ def ixia_api_serv_passwd(duthost): duthost (pytest fixture): The duthost fixture. Returns: - Ixia PTF server (API server) password. + Ixia API server password. """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['password'] @@ -55,13 +54,13 @@ def ixia_api_serv_passwd(duthost): @pytest.fixture(scope = "module") def ixia_api_serv_port(duthost): """ - Return REST port of the ixia API server. + This fixture returns the TCP port for REST API of the ixia API server. Args: duthost (pytest fixture): The duthost fixture. Returns: - Ixia PTF server (API server) REST port. + Ixia API server REST port. """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['rest_port'] @@ -69,14 +68,14 @@ def ixia_api_serv_port(duthost): @pytest.fixture(scope = "module") def ixia_api_serv_session_id(duthost): """ - Ixia PTF can spawn multiple session on the same REST port. + Ixia API server can spawn multiple session on the same REST port. Optional for LINUX, required for windows return the session ID. Args: duthost (pytest fixture): The duthost fixture. Returns: - Ixia PTF server (API server) session id. + Ixia API server session id. """ return duthost.host.options['variable_manager']._hostvars[duthost.hostname]['secret_group_vars']['ixia_api_server']['session_id'] @@ -84,14 +83,15 @@ def ixia_api_serv_session_id(duthost): @pytest.fixture(scope = "module") def ixia_dev(duthost, fanouthosts): """ - Returns the Ixia chassis IP + Returns the Ixia chassis IP. This fixture can return multiple IPs if + multiple Ixia chassis are present in the test topology. Args: duthost (pytest fixture): The duthost fixture. fanouthosts (pytest fixture): The fanouthosts fixture. Returns: - Ixia Chassis IP. + Dictionary of Ixia Chassis IP/IPs. """ result = dict() ixia_dev_hostnames = fanouthosts.keys() @@ -101,9 +101,12 @@ def ixia_dev(duthost, fanouthosts): @pytest.fixture(scope = "function") -def ixia_api_server_session(ixia_api_serv_ip, - ixia_api_serv_user, ixia_api_serv_passwd, ixia_api_serv_port, - ixia_api_serv_session_id) : +def ixia_api_server_session( + ixia_api_serv_ip, + ixia_api_serv_user, + ixia_api_serv_passwd, + ixia_api_serv_port, + ixia_api_serv_session_id) : """ Ixia session manager fixture. @@ -116,23 +119,21 @@ def ixia_api_server_session(ixia_api_serv_ip, fixture. Returns: - IxNetwork Session Id + IxNetwork Session """ - if (ixia_api_serv_session_id != "None") : - session = SessionAssistant(IpAddress = ixia_api_serv_ip, - UserName = ixia_api_serv_user, - Password = ixia_api_serv_passwd, - RestPort = ixia_api_serv_port, - SessionId = ixia_api_serv_session_id, - LogLevel='all') + if (ixia_api_serv_session_id.lower() != 'none') : + session = SessionAssistant(IpAddress=ixia_api_serv_ip, + UserName=ixia_api_serv_user, + Password=ixia_api_serv_passwd, + RestPort=ixia_api_serv_port, + SessionId=ixia_api_serv_session_id) else : - session = SessionAssistant(IpAddress = ixia_api_serv_ip, - UserName = ixia_api_serv_user, - Password = ixia_api_serv_passwd, - RestPort = ixia_api_serv_port) - sessionData = session.Session - ixNetwork = session.Ixnetwork + session = SessionAssistant(IpAddress=ixia_api_serv_ip, + UserName=ixia_api_serv_user, + Password=ixia_api_serv_passwd, + RestPort=ixia_api_serv_port) + ixNetwork = session.Ixnetwork ixNetwork.NewConfig() yield session diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index da2794a4977..d1534bf1cfe 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -10,10 +10,8 @@ chassis instead of reading it from fanout_graph_facts fixture. """ -import re from common.reboot import logger from ixnetwork_restpy import SessionAssistant, Files -import pytest class IxiaFanoutManager () : """Class for managing multiple chassis and extracting the information @@ -26,10 +24,86 @@ def __init__(self,fanout_data) : a integer index (starting from 0) Args: - fanout_data (dict): the dictionary returned by fanout_graph_fact - + fanout_data (dict): the dictionary returned by fanout_graph_fact. + Example format of the fanout_data is given below + + {u'ixia-sonic': { + u'device_conn': { + u'Card9/Port1': { + u'peerdevice': u'sonic-s6100-dut', + u'peerport': u'Ethernet0', + u'speed': u'100000' + }, + u'Card9/Port2': { + u'peerdevice': u'sonic-s6100-dut', + u'peerport': u'Ethernet4', + u'speed': u'100000' + }, + u'Card9/Port3': { + u'peerdevice': u'sonic-s6100-dut', + u'peerport': u'Ethernet8', + u'speed': u'100000' + }, + 'Card9/Port4': { + u'peerdevice': u'sonic-s6100-dut', + u'peerport': u'Ethernet12', + u'speed': u'100000' + }, + u'Card9/Port5': { + u'peerdevice': u'sonic-s6100-dut', + u'peerport': u'Ethernet16', + u'speed': u'100000' + }, + u'Card9/Port6': { + u'peerdevice': u'sonic-s6100-dut', + u'peerport': u'Ethernet20', + u'speed': u'100000' + } + }, + u'device_info': { + u'HwSku': u'IXIA-tester', + u'ManagementGw': u'10.36.78.54', + u'ManagementIp': u'10.36.78.53/32', + u'Type': u'DevIxiaChassis', + u'mgmtip': u'10.36.78.53' + }, + u'device_port_vlans': { + u'Card9/Port1': { + u'mode': u'Access', + u'vlanids': u'300', + u'vlanlist': [300] + }, + u'Card9/Port2': { + u'mode': u'Access', + u'vlanids': u'301', + u'vlanlist': [301] + }, + u'Card9/Port3': { + u'mode': u'Access', + u'vlanids': u'302', + u'vlanlist': [302] + }, + u'Card9/Port4': { + u'mode': u'Access', + u'vlanids': u'300', + u'vlanlist': [300] + }, + u'Card9/Port5': { + u'mode': u'Access', + u'vlanids': u'301', + u'vlanlist': [301] + }, + u'Card9/Port6': { + u'mode': u'Access', + u'vlanids': u'302', + u'vlanlist': [302] + } + }, + u'device_vlan_list': [301, 302, 300, 302, 300, 301], + u'device_vlan_range': [u'300-302'] + } + } """ - self.last_fanout_assessed = None self.fanout_list = [] self.last_device_connection_details = None @@ -131,8 +205,8 @@ def get_ports(self) : Dictionary of chassis card port information. """ retval = [] - for ports in self.current_ixia_port_list: - info_list = ports.split('/') + for port in self.current_ixia_port_list: + info_list = port.split('/') dict_element = { 'ip': info_list[0], 'card_id': info_list[1].replace('Card', ''), @@ -144,6 +218,19 @@ def get_ports(self) : return retval +def clean_configuration(session) : + """Clean up the configurations cteated in IxNetwork API server. + + Args: + session (IxNetwork Session object): IxNetwork session. + + Returns: + None + """ + ixNetwork = session.Ixnetwork + ixNetwork.NewConfig() + + def configure_ports(session, port_list, start_name='port') : """Configures ports of the IXIA chassis and returns the list of configured Ixia ports @@ -223,14 +310,14 @@ def configure_ports(session, port_list, start_name='port') : def create_topology( - session, - ports, - name='Topology 1', - ip_type='ipv4', - ip_start='10.0.0.1', - ip_incr_step='0.0.1.0', - gw_start='10.0.0.2', - gw_incr_step='0.0.1.0'): + session, + ports, + name='Topology 1', + ip_type='ipv4', + ip_start='10.0.0.1', + ip_incr_step='0.0.0.1', + gw_start='10.0.0.2', + gw_incr_step='0.0.0.0'): """ This function creates a topology with ethernet and IP stack on IxNetwork @@ -357,17 +444,17 @@ def start_traffic(session): def create_ip_traffic_item_using_wizard_arguments ( - session, - src_start_port, - src_port_count, - src_first_route_index, - src_route_count, - dst_start_port, - dst_port_count, - dst_first_route_index, - dst_route_count, - name='example_traffic', - traffic_type='ipv4') : + session, + src_start_port, + src_port_count, + src_first_route_index, + src_route_count, + dst_start_port, + dst_port_count, + dst_first_route_index, + dst_route_count, + name='example_traffic', + traffic_type='ipv4') : """ This function creates a traffic item where source and destination ports @@ -385,19 +472,25 @@ def create_ip_traffic_item_using_wizard_arguments ( starting from src_start_port number. Example, if the start port is port2 and port2 to port5 is sending traffic then src_start_port = 2 and src_port_count = 3. - src_first_route_index (int): The first route address index. + src_first_route_index (int): The first route address index. Conceptually + assume the routes (source IP address) are organized as list. Choose + the starting route index. src_route_count (int): Number of routes starting from the - src_first_route_index. + src_first_route_index. So this together src_first_route_index will + determine total number of sources. dst_start_port (int): The first destination port number. dst_port_count (int): Number of ports involved in receiving the traffic - starting from dst_start_port number. Example, if rhe rx port is + starting from dst_start_port number. Example, if the rx port is port6 and port7 then dst_start_port = 6 and dst_port_count = 2 - dst_first_route_index (int): The first destination IP index. + dst_first_route_index (int): The first destination IP index. Conceptually + assume the routes (destination IP address) organized as list. Choose + the starting destination route index. dst_route_count (int): Number of destination IPs starting from - dst_first_route_index. + dst_first_route_index. So this together with dst_first_route_index + will determine the total number of destinations. name (str, optional): Name of the traffic item. Default name is 'example_traffic'. - traffic_type (str, optional): Type of the ip source and destination + traffic_type (str, optional): Type of the IP source and destination (ipv4/ipv6). Default traffic_type is 'ipv4'. Returns: From fc1278583f34461acc05f55dbf8d1b9df87b8d6e Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Wed, 22 Jul 2020 05:38:52 +0000 Subject: [PATCH 32/52] Updating the files as per the comment made on PR1819 --- tests/common/devices.py | 2 +- tests/common/ixia/common_helpers.py | 15 ++++++++------- tests/common/ixia/ixia_helpers.py | 2 +- tests/ixia/test_ixia_traffic.py | 11 +++++------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/common/devices.py b/tests/common/devices.py index d42a6666ae4..0249c559231 100644 --- a/tests/common/devices.py +++ b/tests/common/devices.py @@ -867,12 +867,12 @@ def __init__ (self, ansible_adhoc, os, hostname, device_type) : hostname (str): The Ixia fanout host-name device_type (str): The Ixia fanout device type. """ - super().__init__() self.ansible_adhoc = ansible_adhoc self.os = os self.hostname = hostname self.device_type = device_type + super().__init__(IxiaHost, self) def get_host_name (self): """Returns the Ixia hostname diff --git a/tests/common/ixia/common_helpers.py b/tests/common/ixia/common_helpers.py index 0ff4971a13c..431198c515f 100644 --- a/tests/common/ixia/common_helpers.py +++ b/tests/common/ixia/common_helpers.py @@ -14,13 +14,13 @@ import ipaddr from netaddr import IPNetwork -def incriment_ip_address (ip, incr=1) : +def increment_ip_address (ip, incr=1) : """ Increment IP address by an integer number. Args: ip (str): IP address in string format. - incr (int): Increment by. + incr (int): Increment by the specified number. Return: IP address in the argument incremented by the given integer. @@ -72,24 +72,25 @@ def get_vlan_subnet(host_ans): return gw_addr + '/' + str(prefix_len) -def get_addrs_in_subnet(subnet, n): +def get_addrs_in_subnet(subnet, number_of_ip): """ Get N IP addresses in a subnet. Args: subnet (str): IPv4 subnet, e.g., '192.168.1.1/24' - n (int): Number of IP addresses to get + number_of_ip (int): Number of IP addresses to get Return: - Retuen n IPv4 addresses in this subnet in a list. + Return n IPv4 addresses in this subnet in a list. """ ip_addr = subnet.split('/')[0] ip_addrs = [str(x) for x in list(IPNetwork(subnet))] ip_addrs.remove(ip_addr) """ Try to avoid network and broadcast addresses """ - if len(ip_addrs) >= n + 2: + if len(ip_addrs) >= number_of_ip + 2: del ip_addrs[0] del ip_addrs[-1] - return ip_addrs[:n] + return ip_addrs[:number_of_ip] + diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index d1534bf1cfe..b621726763b 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -443,7 +443,7 @@ def start_traffic(session): ixnetwork.Traffic.StartStatelessTrafficBlocking() -def create_ip_traffic_item_using_wizard_arguments ( +def create_ip_traffic_item ( session, src_start_port, src_port_count, diff --git a/tests/ixia/test_ixia_traffic.py b/tests/ixia/test_ixia_traffic.py index 19336def99a..86fbd32e1ed 100644 --- a/tests/ixia/test_ixia_traffic.py +++ b/tests/ixia/test_ixia_traffic.py @@ -23,11 +23,10 @@ ixia_api_server_session from common.ixia.ixia_helpers import IxiaFanoutManager, configure_ports,\ - create_topology, create_ip_traffic_item_using_wizard_arguments,\ - start_protocols, start_traffic, stop_traffic, get_traffic_statistics,\ - stop_protocols + create_topology, create_ip_traffic_item, start_protocols, \ + start_traffic, stop_traffic, get_traffic_statistics, stop_protocols -from common.ixia.common_helpers import incriment_ip_address +from common.ixia.common_helpers import increment_ip_address def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, ixia_api_server_session, fanouthosts): @@ -38,7 +37,7 @@ def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, mg_facts = duthost.minigraph_facts(host=duthost.hostname) gateway_ip = mg_facts['ansible_facts']['minigraph_vlan_interfaces'][0]['addr'] - start_interface_ip = incriment_ip_address(gateway_ip) + start_interface_ip = increment_ip_address(gateway_ip) ixiaFanoutHostList = IxiaFanoutManager(fanout_graph_facts) ixiaFanoutHostList.get_fanout_device_details(device_number = 0) @@ -63,7 +62,7 @@ def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, # Create a traffic item logger.info("Configuring traffic.") - traffic_item = create_ip_traffic_item_using_wizard_arguments( + traffic_item = create_ip_traffic_item( session=session, src_start_port=1, src_port_count=1, From b7af710ae744326949488cb4c55a1cf3fe2ced19 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Thu, 23 Jul 2020 10:09:02 +0000 Subject: [PATCH 33/52] Refactoring the code for Tgen POC --- tests/common/tgen_api/keystgenapi.py | 55 +++++++++---------- tests/common/tgen_api/tgenapi.py | 8 +-- tests/common/tgen_api/tgenfixtures.py | 76 ++++++++++++++++++--------- tests/ixia/test_ixia_poc.py | 61 ++------------------- 4 files changed, 83 insertions(+), 117 deletions(-) diff --git a/tests/common/tgen_api/keystgenapi.py b/tests/common/tgen_api/keystgenapi.py index fa1db2bca66..0d7fff1a105 100644 --- a/tests/common/tgen_api/keystgenapi.py +++ b/tests/common/tgen_api/keystgenapi.py @@ -14,35 +14,36 @@ import common.ixia.ixia_helpers as helpers class KeysTgenApi(TgenApi): - def __init__(self, **kwargs): - self._vports = None - self.config = Config() - for key in kwargs.keys() : - if (key == 'session') : - self._assistant = kwargs['session'] - self._ixnetwork = self._assistant.Ixnetwork - else : - logger.info('invalid key %s in init' %(key)) - pytest_assert(0) + def __init__(self, config): + if config is None: + self.config = Config() + elif isinstance(config, dict): + self.config = Config(**json.loads(config)) + else: + self.config = config - def init_tgen(self, config = None) : - """Staging repositary Initial configuration data - """ - logger.info("Staging the initial configuration ....") - if config is not None: - self.config = config + #def init_tgen(self, config = None) : + # """Staging repositary Initial configuration data + # """ + # logger.info("Staging the initial configuration ....") + # if config is not None: + # self.config = config - def connect(self, host): - if ']:' in host: - address, port = host.split(']:') - elif ':' in host: - address, port = host.split(':') - else: - address = host - port = None - self._assistant = SessionAssistant(IpAddress=address, RestPort=port) - self._ixnetwork = self._assistant.Ixnetwork - self._port_map = self._assistant.PortMapAssistant() + def connect(self, host, port, username, password): + """ + Connect to Ixia API server. + """ + try : + self._assistant = SessionAssistant(IpAddress=host, + RestPort=port, + UserName=username, + Password=password) + ixNetwork = self._assistant.Ixnetwork + ixNetwork.NewConfig() + except: + logger.info('unable to connect to the API server') + #pytest_assert(0) + return self._assistant def configure(self): """ diff --git a/tests/common/tgen_api/tgenapi.py b/tests/common/tgen_api/tgenapi.py index 4605af5e679..819999410c0 100644 --- a/tests/common/tgen_api/tgenapi.py +++ b/tests/common/tgen_api/tgenapi.py @@ -16,13 +16,7 @@ def __init__(self, **kwargs): pass @abstractmethod - def init_tgen(self, config): - """Initialize your traffic configuration - """ - pass - - @abstractmethod - def connect(self, host): + def connect(self, host, port, username, password): """Information for establishing a connection to the test tool """ pass diff --git a/tests/common/tgen_api/tgenfixtures.py b/tests/common/tgen_api/tgenfixtures.py index 59cebc3410e..f2411bff912 100644 --- a/tests/common/tgen_api/tgenfixtures.py +++ b/tests/common/tgen_api/tgenfixtures.py @@ -3,38 +3,64 @@ """ import pytest -from tgenmodels import Config, Port -from tgenapi import TgenApi -from keystgenapi import KeysTgenApi +from common.reboot import logger from common.fixtures.conn_graph_facts import conn_graph_facts, \ fanout_graph_facts -from common.reboot import logger -from common.ixia.ixia_helpers import IxiaFanoutManager +from common.ixia.ixia_helpers import IxiaFanoutManager +from common.ixia.common_helpers import incriment_ip_address + +from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ + ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id + +from tgenmodels import Config, Port +from tgenapi import TgenApi +from keystgenapi import KeysTgenApi import common.tgen_api +from common.tgen_api.tgenmodels import Port, Layer1, Topology, Ethernet,\ + Ipv4, PfcPause, Flow, Config @pytest.fixture -def TgenApi(ixia_api_server_session): - """ - Fixture: TgenApi -> Creates a IxNetwork session. - - Note: ixia_api_server_session is a function level fixture and - automatically removes session when test case execution is - complete. So we need not take care of session tear down - here. - - Args: - ixia_api_server_session (pytest fisture): ixia_api_server_session - fixture - - Returns: - Ixia KeysTgenApi object instance, which is a derived class from - abstract class "TgenApi". - """ - - tgen = KeysTgenApi(session=ixia_api_server_session) - return tgen +def TgenApi(testbed, + conn_graph_facts, + duthost, + fanout_graph_facts, + fanouthosts, + ixia_api_serv_ip, + ixia_api_serv_user, + ixia_api_serv_passwd, + ixia_api_serv_port, + ixia_api_serv_session_id): + + mg_facts = duthost.minigraph_facts(host=duthost.hostname) + + gateway_ip = mg_facts['ansible_facts']['minigraph_vlan_interfaces'][0]['addr'] + start_interface_ip = incriment_ip_address(gateway_ip) + + # extract the ports from fanout_graph_facts fixture. + ixiaFanoutHostList = IxiaFanoutManager(fanout_graph_facts) + ixiaFanoutHostList.get_fanout_device_details(device_number = 0) + + logger.info("Configuring ports.") + config = Config(ports=Port(ixiaFanoutHostList.get_ports()), + topo=Topology({'topo_name':'T1', 'if_ip': start_interface_ip, 'if_ip_step': '0.0.0.1', 'gw_ip': gateway_ip, 'gw_ip_step': '0.0.0.0'})) + + tgen = KeysTgenApi(config) + session = tgen.connect( + host=ixia_api_serv_ip, + port=ixia_api_serv_port, + username=ixia_api_serv_user, + password=ixia_api_serv_passwd + ) + + ixNetwork = session.Ixnetwork + ixNetwork.NewConfig() + + yield tgen + + ixNetwork.NewConfig() + session.Session.remove() diff --git a/tests/ixia/test_ixia_poc.py b/tests/ixia/test_ixia_poc.py index aa3977bfab8..854c5653a88 100644 --- a/tests/ixia/test_ixia_poc.py +++ b/tests/ixia/test_ixia_poc.py @@ -5,6 +5,7 @@ from common.reboot import logger + from common.fixtures.conn_graph_facts import conn_graph_facts, \ fanout_graph_facts @@ -12,67 +13,11 @@ ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id, \ ixia_api_server_session -from common.ixia.ixia_helpers import IxiaFanoutManager -from common.ixia.common_helpers import incriment_ip_address - from common.tgen_api.tgenfixtures import TgenApi -""" -Import Tgen Data Models - Related to protocol configuration: - Port -> Port configuration (name and location) - Layer1 -> Layer1 configuration - Topology -> Topology configuration. - Related to traffic configuration: - Ethernet-> Ethernet layer configuration of data packet - IP -> IP layer configuration of data packet. - PfcPause -> PFC pause configuration - Flow -> Flow configuration - Repository all the above config: - Config -> Repository all the above config -""" -from common.tgen_api.tgenmodels import Port, Layer1, Topology, Ethernet,\ - Ipv4, PfcPause, Flow, Config - -def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, - fanouthosts, TgenApi): - - """ - This test module demonstrates capability of the tgen fixture. - - Note: All the below fixture in the argument must be available in the - test case. They cannot me hidden inside other fixture. - - Args: - testbed (pytest fixture): Pytest fixture to get the testbed - information. - - conn_graph_facts (pytest fixture): Pytest fixture to get the connection - graph. - - duthost (pytest fixture): Pytest fixture to get the DUT details. - fanout_graph_facts (pytest fixture): Details of the fanout devices. - - fanouthosts (pytest fixture): Details of the fanout hosts. - - TgenApi (pytest fixture) : Ixia defined pytest fixture. - """ - - # Extract the Gateway IP from dut host fixture. - mg_facts = duthost.minigraph_facts(host=duthost.hostname) - gateway_ip = mg_facts['ansible_facts']['minigraph_vlan_interfaces'][0]['addr'] - start_interface_ip = incriment_ip_address(gateway_ip) - - # extract the ports from fanout_graph_facts fixture. - ixiaFanoutHostList = IxiaFanoutManager(fanout_graph_facts) - ixiaFanoutHostList.get_fanout_device_details(device_number = 0) - - logger.info("Configuring ports.") - config = Config(ports=Port(ixiaFanoutHostList.get_ports()), - topo=Topology({'topo_name':'T1', 'if_ip': start_interface_ip, 'if_ip_step': '0.0.0.1', 'gw_ip': gateway_ip, 'gw_ip_step': '0.0.0.0'})) - - TgenApi.init_tgen(config) +def test_testbed(TgenApi): + TgenApi.configure() TgenApi.start() From 482282f1cde0f6864cca193a2aa634c23e288505 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Thu, 23 Jul 2020 14:20:28 +0000 Subject: [PATCH 34/52] Updating common/tgen_api/keystgenapi.py --- tests/common/tgen_api/keystgenapi.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/common/tgen_api/keystgenapi.py b/tests/common/tgen_api/keystgenapi.py index 0d7fff1a105..029816e2ee2 100644 --- a/tests/common/tgen_api/keystgenapi.py +++ b/tests/common/tgen_api/keystgenapi.py @@ -22,13 +22,6 @@ def __init__(self, config): else: self.config = config - #def init_tgen(self, config = None) : - # """Staging repositary Initial configuration data - # """ - # logger.info("Staging the initial configuration ....") - # if config is not None: - # self.config = config - def connect(self, host, port, username, password): """ Connect to Ixia API server. From 6b96a73ec24b32ff8d4f652184a0ff933f991988 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Sat, 25 Jul 2020 14:14:49 +0000 Subject: [PATCH 35/52] Updating the pfc pause test cases --- tests/common/ixia/ixia_helpers.py | 316 ++++++++++++++++++++++++++ tests/common/ixia/qos_fixtures.py | 3 +- tests/ixia/test_pfc_pause_lossless.py | 96 ++++---- 3 files changed, 372 insertions(+), 43 deletions(-) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index b621726763b..da9ebaa0517 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -530,3 +530,319 @@ def create_ip_traffic_item ( # Enable tracking. traffic_item.Tracking.find().TrackBy = ['trackingenabled0'] return traffic_item + + +def create_pkt_hdr(ixnetwork, + traffic_item, + pkt_hdr_to_add, + append_to_stack): + """ + + """ + #Add new packet header in traffic item + config_element = traffic_item.ConfigElement.find()[0] + + # Do the followings to add packet headers on the new traffic item + + # Uncomment this to show a list of all the available protocol templates + # to create (packet headers) + #for protocolHeader in ixNetwork.Traffic.ProtocolTemplate.find(): + # ixNetwork.info('Protocol header: -- {} --'. + # format(protocolHeader.DisplayName)) + + # 1> Get the protocol template from the ProtocolTemplate + # list. + pkt_hdr_proto_template = \ + ixnetwork.Traffic.ProtocolTemplate.find(DisplayName=pkt_hdr_to_add) + #ixNetwork.info('protocolTemplate: {}'.format(packetHeaderProtocolTemplate)) + + # 2> Append the object after the specified packet + # header stack. + append_to_stack_obj = config_element.Stack.find( + DisplayName=append_to_stack + ) + #ixNetwork.info('appendToStackObj: {}'.format(appendToStackObj)) + append_to_stack_obj.Append(Arg2=pkt_hdr_proto_template) + + # 3> Get the new packet header stack to use it for appending an + # IPv4 stack after it. Look for the packet header object and stack ID. + pkt_hdr_stack_obj = config_element.Stack.find(DisplayName=pkt_hdr_to_add) + + # 4> In order to modify the fields, get the field object + pkt_hdr_field_obj = pkt_hdr_stack_obj.Field.find() + #ixNetwork.info('packetHeaderFieldObj: {}'.format(packetHeaderFieldObj)) + + # 5> Save the above configuration to the base config file. + # ixNetwork.SaveConfig(Files('baseConfig.ixncfg', local_file=True)) + return pkt_hdr_field_obj + + +def create_ipv4_traffic(session, + name, + source, + destination, + pkt_size=64, + pkt_count=None, + duration=None, + rate_percent=100, + start_delay=0, + dscp_list=None, + lossless_prio_list=None, + ecn_capable=False): + """ + Create an IPv4 traffic item on IxNetwork. + + Args: + session (obj): IxNetwork session object. + name (str): Name of traffic item + source (obj list): Source endpoints - list of IxNetwork vport objects. + destination (obj list): Destination endpoints - list of IxNetwork + vport objects. + pkt_size (int): Packet size. + pkt_count (int): Packet count. + duration (int): Traffic duration in second (positive integer only!) + rate_percent (int): Percentage of line rate. + start_delay (int): Start delay in second. + dscp_list(int list): List of DSCPs. + lossless_prio_list (int list): List of lossless priorities. + ecn_capable (bool): If packets can get ECN marked. + + Returns: + The created traffic item or None in case of error. + """ + ixnetwork = session.Ixnetwork + + traffic_item = ixnetwork.Traffic.TrafficItem.add(Name=name, + BiDirectional=False, + TrafficType='ipv4') + + traffic_item.EndpointSet.add(Sources=source, Destinations=destination) + + traffic_config = traffic_item.ConfigElement.find()[0] + + """ Todo: add sending rate support """ + traffic_config.FrameRate.update(Type='percentLineRate', Rate=rate_percent) + traffic_config.FrameRateDistribution.PortDistribution = 'splitRateEvenly' + traffic_config.FrameSize.FixedSize = pkt_size + + if pkt_count is not None and duration is not None: + logger.info('You can only specify either pkt_count or duration') + return None + + if pkt_count is not None: + traffic_config.TransmissionControl.update(Type='fixedFrameCount', + FrameCount=pkt_count) + elif duration is not None: + if type(duration) != int or duration <= 0: + logger.info('Invalid duration value {} (positive integer only)'. + format(duration)) + + return None + else: + traffic_config.TransmissionControl.update( + Type='fixedDuration', + Duration=duration) + else: + traffic_config.TransmissionControl.update(Type='continuous') + + if start_delay > 0: + traffic_config.TransmissionControl.update( + StartDelayUnits='nanoseconds', + StartDelay=start_delay*(10**6)) + + if dscp_list is not None and len(dscp_list) > 0: + phb_field = traffic_item.ConfigElement.find().Stack.find('IPv4').Field.\ + find(DisplayName='Default PHB') + + phb_field.ActiveFieldChoice = True + phb_field.ValueType = 'valueList' + phb_field.ValueList = dscp_list + + """ Set ECN bits to 10 (ECN capable) """ + if ecn_capable: + phb_field = traffic_item.ConfigElement.find().Stack.find('IPv4').\ + Field.find(FieldTypeId='ipv4.header.priority.ds.phb.defaultPHB.unused') + + phb_field.ActiveFieldChoice = True + phb_field.ValueType = 'singleValue' + phb_field.SingleValue = 2 + + if lossless_prio_list is not None and len(lossless_prio_list) > 0: + eth_stack = traffic_item.ConfigElement.find()[0].Stack.find( + DisplayName='Ethernet II') + + pfc_queue = eth_stack.Field.find(DisplayName='PFC Queue') + pfc_queue.ValueType = 'valueList' + pfc_queue.ValueList = lossless_prio_list + + traffic_item.Tracking.find()[0].TrackBy = ['flowGroup0'] + + """ Push ConfigElement settings down to HighLevelStream resources """ + traffic_item.Generate() + + return traffic_item + + +def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, + duration=None, start_delay=0, global_pause=False, + pause_prio_list=[]): + """ + Create a pause traffic item. + + Args: + session (obj): IxNetwork session object. + name (str): Name of traffic item. + source (obj list): Source endpoints - list of IxNetwork vport objects. + pkt_per_sec (int): Packets per second. + pkt_count (int): Packet count. + duration (int): Traffic duration in second (positive integer only!). + start_delay (int): Start delay in second. + global_pause (bool): If the generated packets are global pause + (IEEE 802.3X PAUSE). + pause_prio_list: list of priorities to pause. Only valid when + global_pause is False. + + Returns: + The created traffic item or None if any errors happen. + """ + if pause_prio_list is not None: + for prio in pause_prio_list: + if prio < 0 or prio > 7: + logger.info('Invalid pause priorities {}'. + format(pause_prio_list)) + return None + + ixnetwork = session.Ixnetwork + traffic_item = ixnetwork.Traffic.TrafficItem.add(Name=name, + BiDirectional=False, + TrafficType='raw') + + """ Since PFC packets will not be forwarded by the switch, so + destinations are actually not used """ + traffic_item.EndpointSet.add(Sources=source.Protocols.find(), + Destinations=source.Protocols.find()) + + traffic_config = traffic_item.ConfigElement.find()[0] + traffic_config.FrameRate.update(Type='framesPerSecond', Rate=pkt_per_sec) + traffic_config.FrameRateDistribution.PortDistribution = 'splitRateEvenly' + traffic_config.FrameSize.FixedSize = 64 + + if pkt_count is not None and duration is not None: + logger.info('You can only specify either pkt_count or duration') + return None + + if pkt_count is not None: + traffic_config.TransmissionControl.update( + Type='fixedFrameCount', + FrameCount=pkt_count) + + elif duration is not None: + if type(duration) != int or duration <= 0: + logger.info('Invalid duration value {} (positive integer only)'. + format(duration)) + + return None + else: + traffic_config.TransmissionControl.update( + Type='fixedDuration', + Duration=duration) + + else: + traffic_config.TransmissionControl.update(Type='continuous') + + if start_delay > 0: + traffic_config.TransmissionControl.update( + StartDelayUnits='nanoseconds', + StartDelay=start_delay*(10**6)) + + """ Add PFC header """ + pfc_stack_obj = create_pkt_hdr(ixnetwork=ixnetwork, + traffic_item=traffic_item, + pkt_hdr_to_add='^PFC PAUSE \(802.1Qbb\)', + append_to_stack='Ethernet II') + + """ Construct global pause and PFC packets """ + if global_pause: + set_global_pause_fields(pfc_stack_obj) + else: + set_pfc_fields(pfc_stack_obj, pause_prio_list) + + """ Remove Ethernet header """ + traffic_item.ConfigElement.find()[0].Stack.\ + find(DisplayName="Ethernet II").Remove() + + traffic_item.Tracking.find()[0].TrackBy = ['flowGroup0'] + + """ Push ConfigElement settings down to HighLevelStream resources """ + traffic_item.Generate() + + return traffic_item + + +def set_global_pause_fields(pfc_stack_obj): + code = pfc_stack_obj.find(DisplayName='Control opcode') + code.ValueType = 'singleValue' + code.SingleValue = '1' + + """ This field is pause duration in global pause packet """ + prio_enable_vector = pfc_stack_obj.find(DisplayName='priority_enable_vector') + prio_enable_vector.ValueType = 'singleValue' + prio_enable_vector.SingleValue = 'ffff' + + """ pad bytes """ + for i in range(8): + pause_duration = pfc_stack_obj.find(DisplayName='PFC Queue {}'.format(i)) + pause_duration.ValueType = 'singleValue' + pause_duration.SingleValue = '0' + +""" +def set_eth_fields(eth_stack_obj, src_mac, dst_mac): + if src_mac is not None: + src_mac_field = eth_stack_obj.find(DisplayName='Source MAC Address') + src_mac_field.ValueType = 'singleValue' + src_mac_field.SingleValue = src_mac + + if dst_mac is not None: + dst_mac_field = eth_stack_obj.find(DisplayName='Destination MAC Address') + dst_mac_field.ValueType = 'singleValue' + dst_mac_field.SingleValue = dst_mac + +def set_ip_fields(ip_stack_obj, src_ip, dst_ip, dscp_list): + if src_ip is not None: + src_ip_field = ip_stack_obj.find(DisplayName='Source Address') + src_ip_field.ValueType = 'singleValue' + src_ip_field.SingleValue = src_ip + + if dst_ip is not None: + dst_ip_field = ip_stack_obj.find(DisplayName='Destination Address') + dst_ip_field.ValueType = 'singleValue' + dst_ip_field.SingleValue = dst_ip + + if dscp_list is not None and len(dscp_list) > 0: + phb_field = ip_stack_obj.find(DisplayName='Default PHB') + phb_field.ActiveFieldChoice = True + phb_field.ValueType = 'valueList' + phb_field.ValueList = dscp_list +""" +def set_pfc_fields(pfc_stack_obj, pause_prio_list): + code = pfc_stack_obj.find(DisplayName='Control opcode') + code.ValueType = 'singleValue' + code.SingleValue = '101' + + prio_enable_vector = pfc_stack_obj.find(DisplayName='priority_enable_vector') + prio_enable_vector.ValueType = 'singleValue' + val = 0 + for prio in pause_prio_list: + val += (1 << prio) + prio_enable_vector.SingleValue = hex(val) + + for i in range(8): + pause_duration = pfc_stack_obj.find(DisplayName='PFC Queue {}'.format(i)) + pause_duration.ValueType = 'singleValue' + + if i in pause_prio_list: + pause_duration.SingleValue = 'ffff' + else: + pause_duration.SingleValue = '0' + + diff --git a/tests/common/ixia/qos_fixtures.py b/tests/common/ixia/qos_fixtures.py index 76c71db75c4..61b69613357 100644 --- a/tests/common/ixia/qos_fixtures.py +++ b/tests/common/ixia/qos_fixtures.py @@ -2,7 +2,8 @@ @pytest.fixture(scope = "module") def lossless_prio_dscp_map(duthost): - config_facts = duthost.config_facts(host=duthost.hostname, source="persistent")['ansible_facts'] + config_facts = duthost.config_facts(host=duthost.hostname, + source="persistent")['ansible_facts'] if "PORT_QOS_MAP" not in config_facts.keys(): return None diff --git a/tests/ixia/test_pfc_pause_lossless.py b/tests/ixia/test_pfc_pause_lossless.py index d9fd501e682..8dd2e695a8b 100644 --- a/tests/ixia/test_pfc_pause_lossless.py +++ b/tests/ixia/test_pfc_pause_lossless.py @@ -11,8 +11,9 @@ ixia_api_serv_session_id, ixia_api_server_session from common.ixia.ixia_helpers import configure_ports,\ - create_topology, start_protocols, create_ipv4_traffic, create_pause_traffic, \ - start_traffc, stop_traffic, get_statistics, IxiaFanoutManager + create_topology, start_protocols, create_ipv4_traffic,\ + create_pause_traffic, start_traffic, stop_traffic,\ + get_traffic_statistics, IxiaFanoutManager, clean_configuration from common.ixia.common_helpers import get_vlan_subnet, get_addrs_in_subnet @@ -23,34 +24,43 @@ """ Data packet size in bytes """ DATA_PKT_SIZE = 1024 -""" -Run a PFC experiment - _________ - | | -IXIA tx_port ------ | DUT |------ IXIA rx_port - |_________| -IXIA sends test traffic and background traffic from tx_port -IXIA sends PFC pause frames from rx_port to pause priorities - -@param session: IXIA session -@param dut: Ansible instance of SONiC device under test (DUT) -@param tx_port: IXIA port to transmit traffic -@param rx_port: IXIA port to receive traffic -@param port_bw: bandwidth (in Mbps) of tx_port and rx_port -@param test_prio_list: PFC priorities of test traffic and PFC pause frames -@param test_dscp_list: DSCP values of test traffic -@param bg_dscp_list: DSCP values of background traffic -@param exp_dur: experiment duration in second -@param paused: if test traffic should be paused -""" -def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, test_dscp_list, bg_dscp_list,\ - exp_dur, paused): + +def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list,\ + test_dscp_list, bg_dscp_list, exp_dur, paused): + """ + Run a PFC experiment + _________ + | | + IXIA tx_port ------+ DUT +------ IXIA rx_port + |_________| + + IXIA sends test traffic and background traffic from tx_port + IXIA sends PFC pause frames from rx_port to pause priorities + + Args: + session (IxNetwork Session object): IxNetwork session. + dut (object): Ansible instance of SONiC device under test (DUT). + tx_port (object Ixia vport): IXIA port to transmit traffic. + rx_port (object Ixia vport): IXIA port to receive traffic. + port_bw (int): bandwidth (in Mbps) of tx_port and rx_port. + test_prio_list (list of integers): PFC priorities of test traffic and + PFC pause frames. + test_dscp_list (list of integers): DSCP values of test traffic. + bg_dscp_list (list of integers): DSCP values of background traffic. + exp_dur (integer): experiment duration in second. + paused (bool): if test traffic should be paused. + + Returns: + This function returns nothing. + """ """ Disable DUT's PFC watchdog """ dut.shell('sudo pfcwd stop') vlan_subnet = get_vlan_subnet(dut) - pytest_assert(vlan_subnet is not None, "Fail to get Vlan subnet information") + + pytest_assert(vlan_subnet is not None, + "Fail to get Vlan subnet information") gw_addr = vlan_subnet.split('/')[0] """ One for sender and the other one for receiver """ @@ -108,14 +118,13 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, test_ds global_pause=False, pause_prio_list=test_prio_list) - start_traffc(session) + start_traffic(session) """ Wait for test and background traffic to finish """ - time.sleep(exp_dur+1.5) + time.sleep(exp_dur + 1.5) """ Capture traffic statistics """ - - flow_statistics = get_statistics(session) + flow_statistics = get_traffic_statistics(session) logger.info(flow_statistics) for row_number, flow_stat in enumerate(flow_statistics.Rows): @@ -124,20 +133,25 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, test_ds if 'Test' in flow_stat['Traffic Item']: if paused: - pytest_assert(tx_frames>0 and rx_frames==0, "Test traffic should be fully paused") + pytest_assert(tx_frames > 0 and rx_frames == 0, + "Test traffic should be fully paused") else: - pytest_assert(tx_frames>0 and tx_frames==rx_frames, "Test traffic should not be impacted") + pytest_assert(tx_frames > 0 and tx_frames == rx_frames, + "Test traffic should not be impacted") elif 'PFC' in flow_stat['Traffic Item']: - pytest_assert(tx_frames>0 and rx_frames==0, "PFC packets should be dropped") + pytest_assert(tx_frames > 0 and rx_frames == 0, + "PFC packets should be dropped") else: - pytest_assert(tx_frames>0 and tx_frames==rx_frames, "Background traffic should not be impacted") + pytest_assert(tx_frames > 0 and tx_frames == rx_frames, + "Background traffic should not be impacted") stop_traffic(session) -def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map, duthost, ixia_dev, \ - ixia_api_server_session, fanout_graph_facts): - + +def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map,\ + duthost, ixia_dev, ixia_api_server_session,\ + fanout_graph_facts): port_list = list() fanout_devices = IxiaFanoutManager(fanout_graph_facts) fanout_devices.get_fanout_device_details(device_number = 0) @@ -148,19 +162,18 @@ def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map, d intf['speed'] = int(device_conn[peer_port]['speed']) * 100 port_list.append(intf) - """ The topology should have at least two interfaces """ - pytest_assert(len(device_conn)>=2, "The topology should have at least two interfaces") + pytest_assert(len(device_conn)>=2, + "The topology should have at least two interfaces") """ Test pausing each lossless priority individually """ - session = ixia_api_server_session for prio in lossless_prio_dscp_map: for i in range(len(port_list)): vports = configure_ports(session, port_list) rx_id = i - tx_id = (i+1) % len(port_list) + tx_id = (i + 1) % len(port_list) rx_port = vports[rx_id] tx_port = vports[tx_id] @@ -187,6 +200,5 @@ def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map, d exp_dur=exp_dur, paused=True) - ixNetwork = session.Ixnetwork - ixNetwork.NewConfig() + clean_configuration(session=session) From 398aa7ede8fc2876a859cbdfd0f3038d907a6df6 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Sun, 26 Jul 2020 11:08:07 +0000 Subject: [PATCH 36/52] [Ixia/Keysight] changing the import statements from common.xxx to tests.common.xxx --- tests/common/ixia/ixia_helpers.py | 2 +- tests/ixia/test_ixia_traffic.py | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index b621726763b..402aed5c202 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -10,7 +10,7 @@ chassis instead of reading it from fanout_graph_facts fixture. """ -from common.reboot import logger +from tests.common.reboot import logger from ixnetwork_restpy import SessionAssistant, Files class IxiaFanoutManager () : diff --git a/tests/ixia/test_ixia_traffic.py b/tests/ixia/test_ixia_traffic.py index 86fbd32e1ed..2d6563cf12c 100644 --- a/tests/ixia/test_ixia_traffic.py +++ b/tests/ixia/test_ixia_traffic.py @@ -12,21 +12,21 @@ import logging import time import pytest -from common.utilities import wait_until -from common.fixtures.conn_graph_facts import conn_graph_facts, \ - fanout_graph_facts +from tests.common.utilities import wait_until +from tests.common.fixtures.conn_graph_facts import conn_graph_facts, \ + fanout_graph_facts -from common.reboot import logger +from tests.common.reboot import logger -from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ - ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id, \ - ixia_api_server_session +from tests.common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ + ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id, \ + ixia_api_server_session -from common.ixia.ixia_helpers import IxiaFanoutManager, configure_ports,\ - create_topology, create_ip_traffic_item, start_protocols, \ - start_traffic, stop_traffic, get_traffic_statistics, stop_protocols +from tests.common.ixia.ixia_helpers import IxiaFanoutManager, configure_ports,\ + create_topology, create_ip_traffic_item, start_protocols, \ + start_traffic, stop_traffic, get_traffic_statistics, stop_protocols -from common.ixia.common_helpers import increment_ip_address +from tests.common.ixia.common_helpers import increment_ip_address def test_testbed(testbed, conn_graph_facts, duthost, fanout_graph_facts, ixia_api_server_session, fanouthosts): From c2f8b9c430de588c660e3f416a5611838066cd9c Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Mon, 27 Jul 2020 06:00:23 +0000 Subject: [PATCH 37/52] [Ixia/Keysight] Updating PFC PAUSE test case --- tests/common/ixia/ixia_helpers.py | 160 ++++++++++++++------------ tests/ixia/test_pfc_pause_lossless.py | 19 +-- 2 files changed, 98 insertions(+), 81 deletions(-) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index 35f95cd8763..e6ba0cb4ac5 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -532,51 +532,6 @@ def create_ip_traffic_item ( return traffic_item -def create_pkt_hdr(ixnetwork, - traffic_item, - pkt_hdr_to_add, - append_to_stack): - """ - - """ - #Add new packet header in traffic item - config_element = traffic_item.ConfigElement.find()[0] - - # Do the followings to add packet headers on the new traffic item - - # Uncomment this to show a list of all the available protocol templates - # to create (packet headers) - #for protocolHeader in ixNetwork.Traffic.ProtocolTemplate.find(): - # ixNetwork.info('Protocol header: -- {} --'. - # format(protocolHeader.DisplayName)) - - # 1> Get the protocol template from the ProtocolTemplate - # list. - pkt_hdr_proto_template = \ - ixnetwork.Traffic.ProtocolTemplate.find(DisplayName=pkt_hdr_to_add) - #ixNetwork.info('protocolTemplate: {}'.format(packetHeaderProtocolTemplate)) - - # 2> Append the object after the specified packet - # header stack. - append_to_stack_obj = config_element.Stack.find( - DisplayName=append_to_stack - ) - #ixNetwork.info('appendToStackObj: {}'.format(appendToStackObj)) - append_to_stack_obj.Append(Arg2=pkt_hdr_proto_template) - - # 3> Get the new packet header stack to use it for appending an - # IPv4 stack after it. Look for the packet header object and stack ID. - pkt_hdr_stack_obj = config_element.Stack.find(DisplayName=pkt_hdr_to_add) - - # 4> In order to modify the fields, get the field object - pkt_hdr_field_obj = pkt_hdr_stack_obj.Field.find() - #ixNetwork.info('packetHeaderFieldObj: {}'.format(packetHeaderFieldObj)) - - # 5> Save the above configuration to the base config file. - # ixNetwork.SaveConfig(Files('baseConfig.ixncfg', local_file=True)) - return pkt_hdr_field_obj - - def create_ipv4_traffic(session, name, source, @@ -620,7 +575,7 @@ def create_ipv4_traffic(session, traffic_config = traffic_item.ConfigElement.find()[0] - """ Todo: add sending rate support """ + # Todo: add sending rate support traffic_config.FrameRate.update(Type='percentLineRate', Rate=rate_percent) traffic_config.FrameRateDistribution.PortDistribution = 'splitRateEvenly' traffic_config.FrameSize.FixedSize = pkt_size @@ -658,7 +613,7 @@ def create_ipv4_traffic(session, phb_field.ValueType = 'valueList' phb_field.ValueList = dscp_list - """ Set ECN bits to 10 (ECN capable) """ + # Set ECN bits to 10 (ECN capable). if ecn_capable: phb_field = traffic_item.ConfigElement.find().Stack.find('IPv4').\ Field.find(FieldTypeId='ipv4.header.priority.ds.phb.defaultPHB.unused') @@ -677,7 +632,7 @@ def create_ipv4_traffic(session, traffic_item.Tracking.find()[0].TrackBy = ['flowGroup0'] - """ Push ConfigElement settings down to HighLevelStream resources """ + # Push ConfigElement settings down to HighLevelStream resources. traffic_item.Generate() return traffic_item @@ -717,8 +672,8 @@ def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, BiDirectional=False, TrafficType='raw') - """ Since PFC packets will not be forwarded by the switch, so - destinations are actually not used """ + # Since PFC packets will not be forwarded by the switch, so + # destinations are actually not used. traffic_item.EndpointSet.add(Sources=source.Protocols.find(), Destinations=source.Protocols.find()) @@ -755,59 +710,74 @@ def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, StartDelayUnits='nanoseconds', StartDelay=start_delay*(10**6)) - """ Add PFC header """ - pfc_stack_obj = create_pkt_hdr(ixnetwork=ixnetwork, - traffic_item=traffic_item, - pkt_hdr_to_add='^PFC PAUSE \(802.1Qbb\)', - append_to_stack='Ethernet II') + # Add PFC header + pfc_stack_obj = __create_pkt_hdr__( + ixnetwork=ixnetwork, + traffic_item=traffic_item, + pkt_hdr_to_add='^PFC PAUSE \(802.1Qbb\)', + append_to_stack='Ethernet II') - """ Construct global pause and PFC packets """ + # Construct global pause and PFC packets. if global_pause: set_global_pause_fields(pfc_stack_obj) else: - set_pfc_fields(pfc_stack_obj, pause_prio_list) + __set_pfc_fields__(pfc_stack_obj, pause_prio_list) - """ Remove Ethernet header """ + # Remove Ethernet header. traffic_item.ConfigElement.find()[0].Stack.\ find(DisplayName="Ethernet II").Remove() traffic_item.Tracking.find()[0].TrackBy = ['flowGroup0'] - """ Push ConfigElement settings down to HighLevelStream resources """ + # Push ConfigElement settings down to HighLevelStream resources. traffic_item.Generate() return traffic_item +# This secrion defines helper function used in the module. These functions +# should not be called from test script. +# 1. __set_global_pause_fields__ +# 2. __set_eth_fields__ +# 3. __set_eth_fields__ +# 4. __set_pfc_fields__ +# 5. __create_pkt_hdr__ -def set_global_pause_fields(pfc_stack_obj): +def __set_global_pause_fields__(pfc_stack_obj): code = pfc_stack_obj.find(DisplayName='Control opcode') code.ValueType = 'singleValue' code.SingleValue = '1' - """ This field is pause duration in global pause packet """ - prio_enable_vector = pfc_stack_obj.find(DisplayName='priority_enable_vector') + # This field is pause duration in global pause packet. + prio_enable_vector = pfc_stack_obj.\ + find(DisplayName='priority_enable_vector') + prio_enable_vector.ValueType = 'singleValue' prio_enable_vector.SingleValue = 'ffff' - """ pad bytes """ + # pad bytes for i in range(8): - pause_duration = pfc_stack_obj.find(DisplayName='PFC Queue {}'.format(i)) + pause_duration = pfc_stack_obj.\ + find(DisplayName='PFC Queue {}'.format(i)) + pause_duration.ValueType = 'singleValue' pause_duration.SingleValue = '0' -""" -def set_eth_fields(eth_stack_obj, src_mac, dst_mac): + +def __set_eth_fields__(eth_stack_obj, src_mac, dst_mac): if src_mac is not None: src_mac_field = eth_stack_obj.find(DisplayName='Source MAC Address') src_mac_field.ValueType = 'singleValue' src_mac_field.SingleValue = src_mac if dst_mac is not None: - dst_mac_field = eth_stack_obj.find(DisplayName='Destination MAC Address') + dst_mac_field = eth_stack_obj.\ + find(DisplayName='Destination MAC Address') + dst_mac_field.ValueType = 'singleValue' dst_mac_field.SingleValue = dst_mac -def set_ip_fields(ip_stack_obj, src_ip, dst_ip, dscp_list): + +def __set_ip_fields__(ip_stack_obj, src_ip, dst_ip, dscp_list): if src_ip is not None: src_ip_field = ip_stack_obj.find(DisplayName='Source Address') src_ip_field.ValueType = 'singleValue' @@ -823,13 +793,16 @@ def set_ip_fields(ip_stack_obj, src_ip, dst_ip, dscp_list): phb_field.ActiveFieldChoice = True phb_field.ValueType = 'valueList' phb_field.ValueList = dscp_list -""" -def set_pfc_fields(pfc_stack_obj, pause_prio_list): + + +def __set_pfc_fields__(pfc_stack_obj, pause_prio_list): code = pfc_stack_obj.find(DisplayName='Control opcode') code.ValueType = 'singleValue' code.SingleValue = '101' - prio_enable_vector = pfc_stack_obj.find(DisplayName='priority_enable_vector') + prio_enable_vector = pfc_stack_obj.\ + find(DisplayName='priority_enable_vector') + prio_enable_vector.ValueType = 'singleValue' val = 0 for prio in pause_prio_list: @@ -837,7 +810,9 @@ def set_pfc_fields(pfc_stack_obj, pause_prio_list): prio_enable_vector.SingleValue = hex(val) for i in range(8): - pause_duration = pfc_stack_obj.find(DisplayName='PFC Queue {}'.format(i)) + pause_duration = pfc_stack_obj.\ + find(DisplayName='PFC Queue {}'.format(i)) + pause_duration.ValueType = 'singleValue' if i in pause_prio_list: @@ -846,3 +821,44 @@ def set_pfc_fields(pfc_stack_obj, pause_prio_list): pause_duration.SingleValue = '0' +def __create_pkt_hdr__(ixnetwork, + traffic_item, + pkt_hdr_to_add, + append_to_stack): + #Add new packet header in traffic item + config_element = traffic_item.ConfigElement.find()[0] + + # Do the followings to add packet headers on the new traffic item + + # Uncomment this to show a list of all the available protocol templates + # to create (packet headers) + #for protocolHeader in ixNetwork.Traffic.ProtocolTemplate.find(): + # ixNetwork.info('Protocol header: -- {} --'. + # format(protocolHeader.DisplayName)) + + # 1> Get the protocol template from the ProtocolTemplate + # list. + pkt_hdr_proto_template = \ + ixnetwork.Traffic.ProtocolTemplate.find(DisplayName=pkt_hdr_to_add) + #ixNetwork.info('protocolTemplate: {}'.format(packetHeaderProtocolTemplate)) + + # 2> Append the object after the specified packet + # header stack. + append_to_stack_obj = config_element.Stack.find( + DisplayName=append_to_stack + ) + #ixNetwork.info('appendToStackObj: {}'.format(appendToStackObj)) + append_to_stack_obj.Append(Arg2=pkt_hdr_proto_template) + + # 3> Get the new packet header stack to use it for appending an + # IPv4 stack after it. Look for the packet header object and stack ID. + pkt_hdr_stack_obj = config_element.Stack.find(DisplayName=pkt_hdr_to_add) + + # 4> In order to modify the fields, get the field object + pkt_hdr_field_obj = pkt_hdr_stack_obj.Field.find() + #ixNetwork.info('packetHeaderFieldObj: {}'.format(packetHeaderFieldObj)) + + # 5> Save the above configuration to the base config file. + # ixNetwork.SaveConfig(Files('baseConfig.ixncfg', local_file=True)) + return pkt_hdr_field_obj + diff --git a/tests/ixia/test_pfc_pause_lossless.py b/tests/ixia/test_pfc_pause_lossless.py index 8dd2e695a8b..bcac349db1b 100644 --- a/tests/ixia/test_pfc_pause_lossless.py +++ b/tests/ixia/test_pfc_pause_lossless.py @@ -1,23 +1,24 @@ -from common.reboot import logger +from tests.common.reboot import logger import logging import time import pytest -from common.fixtures.conn_graph_facts import conn_graph_facts +from tests.common.fixtures.conn_graph_facts import conn_graph_facts -from common.helpers.assertions import pytest_assert +from tests.common.helpers.assertions import pytest_assert -from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ - ixia_api_serv_passwd, ixia_dev, ixia_api_serv_port,\ - ixia_api_serv_session_id, ixia_api_server_session +from tests.common.ixia.ixia_fixtures import ixia_api_serv_ip, \ + ixia_api_serv_user, ixia_api_serv_passwd, ixia_dev, ixia_api_serv_port,\ + ixia_api_serv_session_id, ixia_api_server_session -from common.ixia.ixia_helpers import configure_ports,\ +from tests.common.ixia.ixia_helpers import configure_ports,\ create_topology, start_protocols, create_ipv4_traffic,\ create_pause_traffic, start_traffic, stop_traffic,\ get_traffic_statistics, IxiaFanoutManager, clean_configuration -from common.ixia.common_helpers import get_vlan_subnet, get_addrs_in_subnet +from tests.common.ixia.common_helpers import get_vlan_subnet, \ + get_addrs_in_subnet -from common.ixia.qos_fixtures import lossless_prio_dscp_map +from tests.common.ixia.qos_fixtures import lossless_prio_dscp_map pytestmark = [pytest.mark.disable_loganalyzer] From 2e012604d0f9d9c7beca1f5ee8048567b535bab0 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Mon, 27 Jul 2020 07:19:53 +0000 Subject: [PATCH 38/52] [Ixia/Keysight] Updating PFC PAUSE test case indentations + comments --- tests/ixia/test_pfc_pause_lossless.py | 36 ++++++++++++++------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/tests/ixia/test_pfc_pause_lossless.py b/tests/ixia/test_pfc_pause_lossless.py index bcac349db1b..c315a5ea196 100644 --- a/tests/ixia/test_pfc_pause_lossless.py +++ b/tests/ixia/test_pfc_pause_lossless.py @@ -11,9 +11,9 @@ ixia_api_serv_session_id, ixia_api_server_session from tests.common.ixia.ixia_helpers import configure_ports,\ - create_topology, start_protocols, create_ipv4_traffic,\ - create_pause_traffic, start_traffic, stop_traffic,\ - get_traffic_statistics, IxiaFanoutManager, clean_configuration + create_topology, start_protocols, create_ipv4_traffic,\ + create_pause_traffic, start_traffic, stop_traffic,\ + get_traffic_statistics, IxiaFanoutManager, clean_configuration from tests.common.ixia.common_helpers import get_vlan_subnet, \ get_addrs_in_subnet @@ -22,21 +22,21 @@ pytestmark = [pytest.mark.disable_loganalyzer] -""" Data packet size in bytes """ +# Data packet size in bytes. DATA_PKT_SIZE = 1024 def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list,\ test_dscp_list, bg_dscp_list, exp_dur, paused): """ - Run a PFC experiment + Run a PFC experiment. _________ | | IXIA tx_port ------+ DUT +------ IXIA rx_port |_________| IXIA sends test traffic and background traffic from tx_port - IXIA sends PFC pause frames from rx_port to pause priorities + IXIA sends PFC pause frames from rx_port to pause priorities. Args: session (IxNetwork Session object): IxNetwork session. @@ -49,13 +49,13 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list,\ test_dscp_list (list of integers): DSCP values of test traffic. bg_dscp_list (list of integers): DSCP values of background traffic. exp_dur (integer): experiment duration in second. - paused (bool): if test traffic should be paused. + paused (bool): If test traffic should be paused. Returns: This function returns nothing. """ - """ Disable DUT's PFC watchdog """ + # Disable DUT's PFC watchdog. dut.shell('sudo pfcwd stop') vlan_subnet = get_vlan_subnet(dut) @@ -64,7 +64,8 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list,\ "Fail to get Vlan subnet information") gw_addr = vlan_subnet.split('/')[0] - """ One for sender and the other one for receiver """ + + # One for sender and the other one for receiver. vlan_ip_addrs = get_addrs_in_subnet(vlan_subnet, 2) topo_receiver = create_topology(session=session, @@ -107,10 +108,10 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list,\ dscp_list=bg_dscp_list, lossless_prio_list=None) - """ Pause time duration (in second) for each PFC pause frame """ + # Pause time duration (in second) for each PFC pause frame. pause_dur_per_pkt = 65535 * 64 * 8.0 / (port_bw * 1000000) - """ Do not specify duration here as we want it keep running """ + # Do not specify duration here as we want it keep running. pfc_traffic = create_pause_traffic(session=session, name='PFC Pause Storm', source=rx_port, @@ -121,10 +122,10 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list,\ start_traffic(session) - """ Wait for test and background traffic to finish """ + # Wait for test and background traffic to finish. time.sleep(exp_dur + 1.5) - """ Capture traffic statistics """ + # Capture traffic statistics. flow_statistics = get_traffic_statistics(session) logger.info(flow_statistics) @@ -163,11 +164,11 @@ def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map,\ intf['speed'] = int(device_conn[peer_port]['speed']) * 100 port_list.append(intf) - """ The topology should have at least two interfaces """ + # The topology should have at least two interfaces. pytest_assert(len(device_conn)>=2, "The topology should have at least two interfaces") - """ Test pausing each lossless priority individually """ + # Test pausing each lossless priority individually. session = ixia_api_server_session for prio in lossless_prio_dscp_map: for i in range(len(port_list)): @@ -183,9 +184,10 @@ def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map,\ pytest_assert(rx_port_bw == tx_port_bw) - """ All the DSCP values mapped to this priority """ + # All the DSCP values mapped to this priority. test_dscp_list = lossless_prio_dscp_map[prio] - """ The other DSCP values """ + + # The other DSCP values. bg_dscp_list = [x for x in range(64) if x not in test_dscp_list] exp_dur = 2 From 12875eda99ea60a05fe8d4b67b3c7b04652a5247 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Mon, 27 Jul 2020 09:56:41 +0000 Subject: [PATCH 39/52] [Ixia/Keysight] Updating qos_fixtures.py indentations + comments --- tests/common/ixia/qos_fixtures.py | 28 ++++++++++++++++++++++++--- tests/ixia/test_pfc_pause_lossless.py | 12 +++++------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/tests/common/ixia/qos_fixtures.py b/tests/common/ixia/qos_fixtures.py index 61b69613357..badce1ff1a5 100644 --- a/tests/common/ixia/qos_fixtures.py +++ b/tests/common/ixia/qos_fixtures.py @@ -1,20 +1,42 @@ import pytest - +""" +RDMA Test cases may require varity of traffic related fixtures. This file +is repository of all different kinds of traffic related fixtures. This +file holds the following fixture(s): + 1. lossless_prio_dscp_map +""" @pytest.fixture(scope = "module") def lossless_prio_dscp_map(duthost): + """ + This fixture reads the QOS parameters from SONiC DUT, and creates + lossless priority Vs. DSCP priority port map (dictionary key = lossless + priority). + + Args: + duthost (pytest fixture) : duthost + + Returns: + Lossless priority vs. DSCP map (dictionary, key = lossless priority). + Example: {3: [3], 4: [4]} + """ config_facts = duthost.config_facts(host=duthost.hostname, source="persistent")['ansible_facts'] if "PORT_QOS_MAP" not in config_facts.keys(): return None + # Read the port QOS map. If pfc_enable flag is false then return None. port_qos_map = config_facts["PORT_QOS_MAP"] lossless_priorities = list() intf = port_qos_map.keys()[0] if 'pfc_enable' not in port_qos_map[intf]: return None - lossless_priorities = [int(x) for x in port_qos_map[intf]['pfc_enable'].split(',')] + # lossless_priorities == list of priorities values for which frame loss + # should not happen + lossless_priorities = \ + [int(x) for x in port_qos_map[intf]['pfc_enable'].split(',')] + dscp_to_tc_map = config_facts["DSCP_TO_TC_MAP"] result = dict() @@ -28,5 +50,5 @@ def lossless_prio_dscp_map(duthost): if int(tc) in lossless_priorities: result[int(tc)].append(int(dscp)) - + return result diff --git a/tests/ixia/test_pfc_pause_lossless.py b/tests/ixia/test_pfc_pause_lossless.py index c315a5ea196..541c23aac1e 100644 --- a/tests/ixia/test_pfc_pause_lossless.py +++ b/tests/ixia/test_pfc_pause_lossless.py @@ -30,13 +30,11 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list,\ test_dscp_list, bg_dscp_list, exp_dur, paused): """ Run a PFC experiment. - _________ - | | - IXIA tx_port ------+ DUT +------ IXIA rx_port - |_________| - - IXIA sends test traffic and background traffic from tx_port - IXIA sends PFC pause frames from rx_port to pause priorities. + 1. IXIA sends test traffic and background traffic from tx_port + 2. IXIA sends PFC pause frames from rx_port to pause priorities. + 3. Background traffic should not be interupped - all background traffic + will be received at the rx_port. + 4. No PFC traffic will be received at the rx_port. Args: session (IxNetwork Session object): IxNetwork session. From 95e38b28fdad6f5603c31b10e5b2269d651617e2 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Mon, 27 Jul 2020 10:02:07 +0000 Subject: [PATCH 40/52] [Ixia/Keysight] Updating qos_fixtures.py indentations + comments --- tests/common/ixia/qos_fixtures.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/common/ixia/qos_fixtures.py b/tests/common/ixia/qos_fixtures.py index badce1ff1a5..0012d5a03ee 100644 --- a/tests/common/ixia/qos_fixtures.py +++ b/tests/common/ixia/qos_fixtures.py @@ -1,8 +1,8 @@ import pytest """ -RDMA Test cases may require varity of traffic related fixtures. This file +RDMA test cases may require variety of traffic related fixtures. This file is repository of all different kinds of traffic related fixtures. This -file holds the following fixture(s): +file currently holds the following fixture(s): 1. lossless_prio_dscp_map """ @pytest.fixture(scope = "module") From 1690344fc03a89e87241641d28d7950654d1777e Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Tue, 28 Jul 2020 02:48:12 +0000 Subject: [PATCH 41/52] deleting tgen POC --- tests/common/tgen_api/.gitignore | 3 - tests/common/tgen_api/__init__.py | 1 - tests/common/tgen_api/keystgenapi.py | 74 --------- tests/common/tgen_api/testcases.py | 28 ---- tests/common/tgen_api/tgenapi.py | 59 ------- tests/common/tgen_api/tgenfixtures.py | 66 -------- tests/common/tgen_api/tgenmodels.py | 218 -------------------------- tests/ixia/test_ixia_poc.py | 28 ---- 8 files changed, 477 deletions(-) delete mode 100644 tests/common/tgen_api/.gitignore delete mode 100644 tests/common/tgen_api/__init__.py delete mode 100644 tests/common/tgen_api/keystgenapi.py delete mode 100644 tests/common/tgen_api/testcases.py delete mode 100644 tests/common/tgen_api/tgenapi.py delete mode 100644 tests/common/tgen_api/tgenfixtures.py delete mode 100644 tests/common/tgen_api/tgenmodels.py delete mode 100644 tests/ixia/test_ixia_poc.py diff --git a/tests/common/tgen_api/.gitignore b/tests/common/tgen_api/.gitignore deleted file mode 100644 index b943e2c69ca..00000000000 --- a/tests/common/tgen_api/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -__pycache__ -*.pyc -.pytest_cache diff --git a/tests/common/tgen_api/__init__.py b/tests/common/tgen_api/__init__.py deleted file mode 100644 index 19dba67729a..00000000000 --- a/tests/common/tgen_api/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""This file is a place holder for Tgen API""" diff --git a/tests/common/tgen_api/keystgenapi.py b/tests/common/tgen_api/keystgenapi.py deleted file mode 100644 index 029816e2ee2..00000000000 --- a/tests/common/tgen_api/keystgenapi.py +++ /dev/null @@ -1,74 +0,0 @@ -""" The IxNetwork Test Generator API implementation - - Note: Please note that some of these functions is implemented - with the help of common/ixia/ixia_helpers.py for the demo - purpose. -""" -import json -from typing import Union, List, Dict -from common.reboot import logger - -from tgenapi import TgenApi -from tgenmodels import Config -from ixnetwork_restpy import SessionAssistant -import common.ixia.ixia_helpers as helpers - -class KeysTgenApi(TgenApi): - def __init__(self, config): - if config is None: - self.config = Config() - elif isinstance(config, dict): - self.config = Config(**json.loads(config)) - else: - self.config = config - - def connect(self, host, port, username, password): - """ - Connect to Ixia API server. - """ - try : - self._assistant = SessionAssistant(IpAddress=host, - RestPort=port, - UserName=username, - Password=password) - ixNetwork = self._assistant.Ixnetwork - ixNetwork.NewConfig() - except: - logger.info('unable to connect to the API server') - #pytest_assert(0) - return self._assistant - - def configure(self): - """ - restpy code goes here - take the configuration objects and use restpy to configure the - test tool - - Note: We expect port list to be a list of dictionaries. - """ - - self._vports = helpers.configure_ports(self._assistant, - self.config.ports._port_list) - - helpers.create_topology( - self._assistant, - self._vports, - name=self.config.topo._ip_address['topo_name'], - ip_type='ipv4', - ip_start=self.config.topo._ip_address['if_ip'], - ip_incr_step=self.config.topo._ip_address['if_ip_step'], - gw_start=self.config.topo._ip_address['gw_ip'], - gw_incr_step=self.config.topo._ip_address['gw_ip_step']) - - def deconfigure(self): - self._ixnetwork.NewConfig() - - def start(self): - helpers.start_protocols(self._assistant) - - def stop(self): - helpers.stop_protocols(self._assistant) - pass - - def json_config(self): - return json.dumps(self.config, default=lambda o: o.__dict__, indent=4) diff --git a/tests/common/tgen_api/testcases.py b/tests/common/tgen_api/testcases.py deleted file mode 100644 index b3e35437e82..00000000000 --- a/tests/common/tgen_api/testcases.py +++ /dev/null @@ -1,28 +0,0 @@ -""" A SONIC test case using the injected tgen_api fixture -""" -import pytest -from tgenapi import TgenApi -from tgenmodels import * -from tgenfixtures import * - - -def test_pfc_pause_lossless(tgen_api): - # configure dut - - # create a configuration - tgen_api.config.flows = [ - Flow('Test Traffic', tgen_api.config.ports[0], packet=[Ethernet(), Ipv4()]), - Flow('Background Traffic', tgen_api.config.ports[0], packet=[Ethernet()]), - Flow('Pause Traffic', tgen_api.config.ports[1], packet=[PfcPause()]) - ] - - # configure and control the test tool - tgen_api.configure() - tgen_api.start() - tgen_api.stop() - - # asserts for pass/fail - - # teardown the test tool - tgen_api.deconfigure() - diff --git a/tests/common/tgen_api/tgenapi.py b/tests/common/tgen_api/tgenapi.py deleted file mode 100644 index 819999410c0..00000000000 --- a/tests/common/tgen_api/tgenapi.py +++ /dev/null @@ -1,59 +0,0 @@ -""" The abstract Test Generator API -""" -#from abc import ABC, abstractmethod -from abc import ABCMeta, abstractmethod -from typing import Union, List, Dict -from tgenmodels import Config - - -class TgenApi(): - # __slots__ = ['config'] - __metaclass__ = ABCMeta - - @abstractmethod - def __init__(self, **kwargs): - super().__init__() - pass - - @abstractmethod - def connect(self, host, port, username, password): - """Information for establishing a connection to the test tool - """ - pass - - @abstractmethod - def configure(self, config = None): - """Configure the test tool using the data from the Config - """ - pass - - @abstractmethod - def deconfigure(self): - """Deconfigure the test tool - """ - pass - - @abstractmethod - def start(self): - """Start traffic on the test tool - """ - pass - - @abstractmethod - def stop(self): - """Stop traffic on the test tool - """ - pass - - @abstractmethod - def json_config(self): - """Get the json representation of the Config object - - Returns: - str: A json string representation of the Config object - """ - pass - - def __str__(self): - raise NotImplementedError - diff --git a/tests/common/tgen_api/tgenfixtures.py b/tests/common/tgen_api/tgenfixtures.py deleted file mode 100644 index f2411bff912..00000000000 --- a/tests/common/tgen_api/tgenfixtures.py +++ /dev/null @@ -1,66 +0,0 @@ -""" -A SONIC pytest fixture returning a TgenApi implementation -""" - -import pytest -from common.reboot import logger - -from common.fixtures.conn_graph_facts import conn_graph_facts, \ - fanout_graph_facts - -from common.ixia.ixia_helpers import IxiaFanoutManager -from common.ixia.common_helpers import incriment_ip_address - -from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ - ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id - - -from tgenmodels import Config, Port -from tgenapi import TgenApi -from keystgenapi import KeysTgenApi -import common.tgen_api - -from common.tgen_api.tgenmodels import Port, Layer1, Topology, Ethernet,\ - Ipv4, PfcPause, Flow, Config - -@pytest.fixture -def TgenApi(testbed, - conn_graph_facts, - duthost, - fanout_graph_facts, - fanouthosts, - ixia_api_serv_ip, - ixia_api_serv_user, - ixia_api_serv_passwd, - ixia_api_serv_port, - ixia_api_serv_session_id): - - mg_facts = duthost.minigraph_facts(host=duthost.hostname) - - gateway_ip = mg_facts['ansible_facts']['minigraph_vlan_interfaces'][0]['addr'] - start_interface_ip = incriment_ip_address(gateway_ip) - - # extract the ports from fanout_graph_facts fixture. - ixiaFanoutHostList = IxiaFanoutManager(fanout_graph_facts) - ixiaFanoutHostList.get_fanout_device_details(device_number = 0) - - logger.info("Configuring ports.") - config = Config(ports=Port(ixiaFanoutHostList.get_ports()), - topo=Topology({'topo_name':'T1', 'if_ip': start_interface_ip, 'if_ip_step': '0.0.0.1', 'gw_ip': gateway_ip, 'gw_ip_step': '0.0.0.0'})) - - tgen = KeysTgenApi(config) - session = tgen.connect( - host=ixia_api_serv_ip, - port=ixia_api_serv_port, - username=ixia_api_serv_user, - password=ixia_api_serv_passwd - ) - - ixNetwork = session.Ixnetwork - ixNetwork.NewConfig() - - yield tgen - - ixNetwork.NewConfig() - session.Session.remove() - diff --git a/tests/common/tgen_api/tgenmodels.py b/tests/common/tgen_api/tgenmodels.py deleted file mode 100644 index b14b6dd9577..00000000000 --- a/tests/common/tgen_api/tgenmodels.py +++ /dev/null @@ -1,218 +0,0 @@ -""" The abstract Test Generator Data Model -""" -from typing import Union, List, Dict -from common.reboot import logger - - -class Port(object): - """Port model - Port is a list of dict - """ - __slots__ = ['_port_list'] - - def __init__(self, port_list): - self._port_list = [] - if (isinstance(port_list, list)): - for port in port_list: - if (isinstance(port, dict)): - temp = {} - for key in port.keys(): - if key in ['ip', 'card_id', 'port_id']: - temp[key] = port[key] - self._port_list.append(temp) - logger.info(self._port_list) - else : - logger.info('Port must be a list of dict') - pytest_assert(0) - - -Ports = Union[Port, List[Port], Dict] -class Layer1(object): - #def __init__(ports: Union[Port, List[Port]]): - def __init__(ports): - self.ports = ports - - def novus_hundred_gig_lan(auto_instrumentation='floating', bad_blocks_number=4): - """Novus 100 Gb Lan card settings - """ - pass - - def uhd(auto_instrumentation='floating'): - """Uhd appliance settings - """ - pass - - def ethernet_vm(): - """Ethernet virtual machine settings - """ - pass - -class Topology (object): - def __init__(self, ip_address) : - self._ip_address = {} - if (isinstance(ip_address, dict)) : - for key in ip_address.keys() : - if key in ['if_ip', 'if_ip_step', 'gw_ip', 'gw_ip_step', 'topo_name']: - self._ip_address[key] = ip_address[key] - logger.info(self._ip_address) - else: - logger.info('Port must be a list of dict') - - -class Ethernet(object): - """Ethernet II traffic protocol header - - Properties - ---------- - dst_address (str=01:80:C2:00:00:01): Destination address - src_address (str=00:00:AA:00:00:01): Source address - ether_type (str=0x8808): Ethernet type - pfc_queue (str=0): PFC Queue - """ - __STACK_TYPE_ID = 'ethernet' - __FIELD_MAP = { - 'dst_address': { - 'fieldTypeId': 'ethernet.header.destinationAddress', - 'default': '00:00:00:00:00:00' - }, - 'src_address': { - 'fieldTypeId': 'ethernet.header.sourceAddress', - 'default': '00:00:00:00:00:00' - }, - 'ether_type': { - 'fieldTypeId': 'ethernet.header.etherType', - 'default': '0xFFFF' - }, - 'pfc_queue': { - 'fieldTypeId': 'ethernet.header.pfcQueue', - 'default': '0' - } - } - __slots__ = ['dst_address', 'src_address', 'ether_type', 'pfc_queue'] - - def __init__(self): - self.dst_address = Ethernet.__FIELD_MAP['dst_address']['default'] - self.src_address = Ethernet.__FIELD_MAP['src_address']['default'] - self.ether_type = Ethernet.__FIELD_MAP['ether_type']['default'] - self.pfc_queue = Ethernet.__FIELD_MAP['pfc_queue']['default'] - - -class Ipv4(object): - """Ethernet II traffic protocol header - - Properties - ---------- - dst_address (str=01:80:C2:00:00:01): Destination address - src_address (str=00:00:AA:00:00:01): Source address - """ - __STACK_TYPE_ID = 'ipv4' - __FIELD_MAP = { - 'dst_address': { - 'fieldTypeId': 'ipv4.header.dstIp', - 'default': '0.0.0.0' - }, - 'src_address': { - 'fieldTypeId': 'ipv4.header.srcIp', - 'default': '0.0.0.0' - } - } - __slots__ = ['dst_address', 'src_address'] - - def __init__(self): - self.dst_address = Ipv4.__FIELD_MAP['dst_address']['default'] - self.src_address = Ipv4.__FIELD_MAP['src_address']['default'] - - -class PfcPause(object): - """PFC PAUSE (802.1Qbb) traffic protocol header - - Properties - ---------- - dst_address (str=01:80:C2:00:00:01): Destination address - src_address (str=00:00:AA:00:00:01): Source address - ether_type (str=0x8808): Ethernet type - control_op_code (str=0x0101): Control operation code - """ - __FIELD_MAP = { - 'dst_address': { - 'fieldTypeId': 'pfcPause.header.header.dstAddress', - 'default': '01:80:C2:00:00:01' - }, - 'src_address': { - 'fieldTypeId': 'pfcPause.header.header.srcAddress', - 'default': '00:00:AA:00:00:01' - }, - 'ether_type': { - 'fieldTypeId': 'pfcPause.header.header.etherType', - 'default': '0x8808' - }, - 'control_op_code': { - 'fieldTypeId': 'pfcPause.header.macControl.controlOpCode', - 'default': '0x0101' - } - } - __slots__ = ['dst_address', 'src_address', 'ether_type', 'control_op_code'] - - def __init__(self): - self.dst_address = PfcPause.__FIELD_MAP['dst_address']['default'] - self.src_address = PfcPause.__FIELD_MAP['src_address']['default'] - self.ether_type = PfcPause.__FIELD_MAP['ether_type']['default'] - self.control_op_code = PfcPause.__FIELD_MAP['control_op_code']['default'] - - -class Flow(object): - """Traffic flow container - - Properties - ---------- - - name (str): Unique name of the traffic flow - - tx_port (Union[str, Port]): The name of a Port object that will transmit - traffic - - rx_ports (list(str)): Intended receive ports - - packet (list(Union[Ethernet, Ipv4, PfcPause])): The traffic protocols - that define the packet for the flow - """ - __slots__ = ['name', 'tx_port', 'rx_ports', 'packet'] - - def __init__(self, name, tx_port, rx_ports=None, packet=None): - self.name = name - self.tx_port = tx_port - self.rx_ports = rx_ports - self.packet = packet - - -Flows = Union[Flow, List[Flow]] -class Config(object): - """Test tool confguration container - - Properties - ---------- - - ports (list(Port)): A list of Port objects - - layer1 (list(Layer1)): A list of Layer1 objects - - flows (list(Flow)): A list of Flow objects - """ - __slots__ = ['ports', 'layer1', 'topo', 'flows'] - - def __init__(self, ports = None, layer1 = None, topo = None, flows = None): - - # Add port - if ports is not None: - if isinstance(ports, Port): - self.ports = ports - # Add layer 1 - if layer1 is not None: - if isinstance(layer1, Layer1): - self.layer1 = layer1 - # Add IP - if topo is not None: - if isinstance(topo, Topology): - self.topo = topo - - # Add flows - self.flows = [] - if flows is not None: - if isinstance(flows, list): - self.flows = flows - else: - self.flows.append(flows) - diff --git a/tests/ixia/test_ixia_poc.py b/tests/ixia/test_ixia_poc.py deleted file mode 100644 index 854c5653a88..00000000000 --- a/tests/ixia/test_ixia_poc.py +++ /dev/null @@ -1,28 +0,0 @@ -import logging -import time -import pytest -from common.utilities import wait_until - -from common.reboot import logger - - -from common.fixtures.conn_graph_facts import conn_graph_facts, \ - fanout_graph_facts - -from common.ixia.ixia_fixtures import ixia_api_serv_ip, ixia_api_serv_user,\ - ixia_api_serv_passwd, ixia_api_serv_port, ixia_api_serv_session_id, \ - ixia_api_server_session - -from common.tgen_api.tgenfixtures import TgenApi - - -def test_testbed(TgenApi): - - TgenApi.configure() - TgenApi.start() - - logger.info("wait for two seconds") - time.sleep(2) - - TgenApi.stop() - From e23970c7197c115db28c876160f25ddfcad4a5de Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Tue, 28 Jul 2020 04:00:21 +0000 Subject: [PATCH 42/52] [Ixia/Keysight] Updating first RDMA test cases --- tests/common/ixia/ixia_helpers.py | 2 +- tests/ixia/test_pfc_pause_lossless.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index e6ba0cb4ac5..8c96f5f2eb2 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -734,7 +734,7 @@ def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, return traffic_item -# This secrion defines helper function used in the module. These functions +# This section defines helper function used in the module. These functions # should not be called from test script. # 1. __set_global_pause_fields__ # 2. __set_eth_fields__ diff --git a/tests/ixia/test_pfc_pause_lossless.py b/tests/ixia/test_pfc_pause_lossless.py index 541c23aac1e..f759b02a235 100644 --- a/tests/ixia/test_pfc_pause_lossless.py +++ b/tests/ixia/test_pfc_pause_lossless.py @@ -32,7 +32,7 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list,\ Run a PFC experiment. 1. IXIA sends test traffic and background traffic from tx_port 2. IXIA sends PFC pause frames from rx_port to pause priorities. - 3. Background traffic should not be interupped - all background traffic + 3. Background traffic should not be interruped - all background traffic will be received at the rx_port. 4. No PFC traffic will be received at the rx_port. From 44eddb950999b551f3667addf5e108a37893628b Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Tue, 11 Aug 2020 04:55:27 +0000 Subject: [PATCH 43/52] Implemented the review comments of PR#1979 --- tests/common/ixia/ixia_helpers.py | 28 ++-- tests/common/ixia/qos_fixtures.py | 1 - tests/ixia/test_pfc_pause_lossless.py | 220 ++++++++++++++++++-------- 3 files changed, 163 insertions(+), 86 deletions(-) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index 8c96f5f2eb2..778263ca6a5 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -581,7 +581,7 @@ def create_ipv4_traffic(session, traffic_config.FrameSize.FixedSize = pkt_size if pkt_count is not None and duration is not None: - logger.info('You can only specify either pkt_count or duration') + logger.error('You can only specify either pkt_count or duration') return None if pkt_count is not None: @@ -589,7 +589,7 @@ def create_ipv4_traffic(session, FrameCount=pkt_count) elif duration is not None: if type(duration) != int or duration <= 0: - logger.info('Invalid duration value {} (positive integer only)'. + logger.error('Invalid duration value {} (positive integer only)'. format(duration)) return None @@ -663,7 +663,7 @@ def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, if pause_prio_list is not None: for prio in pause_prio_list: if prio < 0 or prio > 7: - logger.info('Invalid pause priorities {}'. + logger.error('Invalid pause priorities {}'. format(pause_prio_list)) return None @@ -683,7 +683,7 @@ def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, traffic_config.FrameSize.FixedSize = 64 if pkt_count is not None and duration is not None: - logger.info('You can only specify either pkt_count or duration') + logger.error('You can only specify either pkt_count or duration') return None if pkt_count is not None: @@ -693,7 +693,7 @@ def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, elif duration is not None: if type(duration) != int or duration <= 0: - logger.info('Invalid duration value {} (positive integer only)'. + logger.error('Invalid duration value {} (positive integer only)'. format(duration)) return None @@ -748,16 +748,14 @@ def __set_global_pause_fields__(pfc_stack_obj): code.SingleValue = '1' # This field is pause duration in global pause packet. - prio_enable_vector = pfc_stack_obj.\ - find(DisplayName='priority_enable_vector') + prio_enable_vector = pfc_stack_obj.find(DisplayName='priority_enable_vector') prio_enable_vector.ValueType = 'singleValue' prio_enable_vector.SingleValue = 'ffff' # pad bytes for i in range(8): - pause_duration = pfc_stack_obj.\ - find(DisplayName='PFC Queue {}'.format(i)) + pause_duration = pfc_stack_obj.find(DisplayName='PFC Queue {}'.format(i)) pause_duration.ValueType = 'singleValue' pause_duration.SingleValue = '0' @@ -770,8 +768,7 @@ def __set_eth_fields__(eth_stack_obj, src_mac, dst_mac): src_mac_field.SingleValue = src_mac if dst_mac is not None: - dst_mac_field = eth_stack_obj.\ - find(DisplayName='Destination MAC Address') + dst_mac_field = eth_stack_obj.find(DisplayName='Destination MAC Address') dst_mac_field.ValueType = 'singleValue' dst_mac_field.SingleValue = dst_mac @@ -800,19 +797,16 @@ def __set_pfc_fields__(pfc_stack_obj, pause_prio_list): code.ValueType = 'singleValue' code.SingleValue = '101' - prio_enable_vector = pfc_stack_obj.\ - find(DisplayName='priority_enable_vector') - + prio_enable_vector = pfc_stack_obj.find(DisplayName='priority_enable_vector') prio_enable_vector.ValueType = 'singleValue' + val = 0 for prio in pause_prio_list: val += (1 << prio) prio_enable_vector.SingleValue = hex(val) for i in range(8): - pause_duration = pfc_stack_obj.\ - find(DisplayName='PFC Queue {}'.format(i)) - + pause_duration = pfc_stack_obj.find(DisplayName='PFC Queue {}'.format(i)) pause_duration.ValueType = 'singleValue' if i in pause_prio_list: diff --git a/tests/common/ixia/qos_fixtures.py b/tests/common/ixia/qos_fixtures.py index 0012d5a03ee..dc32f25433f 100644 --- a/tests/common/ixia/qos_fixtures.py +++ b/tests/common/ixia/qos_fixtures.py @@ -27,7 +27,6 @@ def lossless_prio_dscp_map(duthost): # Read the port QOS map. If pfc_enable flag is false then return None. port_qos_map = config_facts["PORT_QOS_MAP"] - lossless_priorities = list() intf = port_qos_map.keys()[0] if 'pfc_enable' not in port_qos_map[intf]: return None diff --git a/tests/ixia/test_pfc_pause_lossless.py b/tests/ixia/test_pfc_pause_lossless.py index f759b02a235..b94368bbacc 100644 --- a/tests/ixia/test_pfc_pause_lossless.py +++ b/tests/ixia/test_pfc_pause_lossless.py @@ -1,7 +1,8 @@ -from tests.common.reboot import logger import logging import time import pytest + +from tests.common.reboot import logger from tests.common.fixtures.conn_graph_facts import conn_graph_facts from tests.common.helpers.assertions import pytest_assert @@ -24,17 +25,20 @@ # Data packet size in bytes. DATA_PKT_SIZE = 1024 +START_DELAY = 1.5 - -def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list,\ - test_dscp_list, bg_dscp_list, exp_dur, paused): +def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, + test_dscp_list, bg_dscp_list, exp_dur, start_delay=START_DELAY, + test_traffic_pause_expected=True, + send_pause_frame=True): """ Run a PFC experiment. - 1. IXIA sends test traffic and background traffic from tx_port + 1. IXIA sends test traffic and background traffic from tx_port. 2. IXIA sends PFC pause frames from rx_port to pause priorities. 3. Background traffic should not be interruped - all background traffic will be received at the rx_port. - 4. No PFC traffic will be received at the rx_port. + 4. No PFC traffic will be received at the rx_port when pause priority + is equal to test traffic priority. Args: session (IxNetwork Session object): IxNetwork session. @@ -47,43 +51,44 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list,\ test_dscp_list (list of integers): DSCP values of test traffic. bg_dscp_list (list of integers): DSCP values of background traffic. exp_dur (integer): experiment duration in second. - paused (bool): If test traffic should be paused. + start_delay (float): approximated initial delay to start the traffic. + test_traffic_pause_expected (bool): Do you expect test traffic to + be stopped? If yes, this should be true; false otherwise. + send_pause_frame (bool): True/False depending on whether you wand to + send pause frame Rx port or not. Returns: This function returns nothing. """ - + # Disable DUT's PFC watchdog. dut.shell('sudo pfcwd stop') - - vlan_subnet = get_vlan_subnet(dut) + vlan_subnet = get_vlan_subnet(dut) pytest_assert(vlan_subnet is not None, "Fail to get Vlan subnet information") - - gw_addr = vlan_subnet.split('/')[0] + gw_addr = vlan_subnet.split('/')[0] # One for sender and the other one for receiver. vlan_ip_addrs = get_addrs_in_subnet(vlan_subnet, 2) - + topo_receiver = create_topology(session=session, - name="Receiver", + name="Receiver", ports=list(rx_port), ip_start=vlan_ip_addrs[0], ip_incr_step='0.0.0.1', - gw_start=gw_addr, + gw_start=gw_addr, gw_incr_step='0.0.0.0') - topo_sender = create_topology(session=session, - name="Sender", - ports=list(tx_port), - ip_start=vlan_ip_addrs[1], - ip_incr_step='0.0.0.1', - gw_start=gw_addr, + topo_sender = create_topology(session=session, + name="Sender", + ports=list(tx_port), + ip_start=vlan_ip_addrs[1], + ip_incr_step='0.0.0.1', + gw_start=gw_addr, gw_incr_step='0.0.0.0') - start_protocols(session) - + test_traffic = create_ipv4_traffic(session=session, name='Test Data Traffic', source=topo_sender, @@ -95,6 +100,7 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list,\ dscp_list=test_dscp_list, lossless_prio_list=test_prio_list) + bg_priority_list = [b for b in range(8) if b not in test_prio_list] background_traffic = create_ipv4_traffic(session=session, name='Background Data Traffic', source=topo_sender, @@ -104,102 +110,180 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list,\ rate_percent=50, start_delay=1, dscp_list=bg_dscp_list, - lossless_prio_list=None) - + lossless_prio_list=bg_priority_list) + # Pause time duration (in second) for each PFC pause frame. - pause_dur_per_pkt = 65535 * 64 * 8.0 / (port_bw * 1000000) - + pause_dur_per_pkt = 65535 * 64 * 8.0 / (port_bw * 1000000) + # Do not specify duration here as we want it keep running. - pfc_traffic = create_pause_traffic(session=session, - name='PFC Pause Storm', - source=rx_port, - pkt_per_sec=1.1/pause_dur_per_pkt, - start_delay=0, - global_pause=False, - pause_prio_list=test_prio_list) - + if send_pause_frame: + pfc_traffic = create_pause_traffic(session=session, + name='PFC Pause Storm', + source=rx_port, + pkt_per_sec=1.1/pause_dur_per_pkt, + start_delay=0, + global_pause=False, + pause_prio_list=test_prio_list) + start_traffic(session) - + # Wait for test and background traffic to finish. - time.sleep(exp_dur + 1.5) - + time.sleep(exp_dur + start_delay) + # Capture traffic statistics. flow_statistics = get_traffic_statistics(session) logger.info(flow_statistics) - + for row_number, flow_stat in enumerate(flow_statistics.Rows): tx_frames = int(flow_stat['Tx Frames']) - rx_frames = int(flow_stat['Rx Frames']) - + rx_frames = int(flow_stat['Rx Frames']) + if 'Test' in flow_stat['Traffic Item']: - if paused: - pytest_assert(tx_frames > 0 and rx_frames == 0, + if test_traffic_pause_expected: + pytest_assert(tx_frames > 0 and rx_frames == 0, "Test traffic should be fully paused") else: - pytest_assert(tx_frames > 0 and tx_frames == rx_frames, + pytest_assert(tx_frames > 0 and tx_frames == rx_frames, "Test traffic should not be impacted") - + elif 'PFC' in flow_stat['Traffic Item']: pytest_assert(tx_frames > 0 and rx_frames == 0, "PFC packets should be dropped") - else: + else: pytest_assert(tx_frames > 0 and tx_frames == rx_frames, "Background traffic should not be impacted") - + stop_traffic(session) -def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map,\ - duthost, ixia_dev, ixia_api_server_session,\ +def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map, + duthost, ixia_dev, ixia_api_server_session, fanout_graph_facts): + """ + RDMA PFC - Pauses on lossless priorities. + 1. On SONiC DUT enable PFC on any two priorities say, m and n. + 2. Disable the PFC watchdog on the SONiC DUT. + 3. On the Ixia Tx port create two flows - a) 'Test Data Traffic' and + b) 'Background Data traffic'. + 4. The flow 'Test Data Traffic' can assume only one of the priority + values - either m or n (or both). + 5. The flow 'Background Data Traffic' can assume any priority values + which is not in 'Test Data Traffic' (including m or n). Note that: + a. Background data traffic can assume priority n if 'Test Data Traffic' + has the TC value m. + b. Background data traffic can assume priority m if 'Test Data Traffic' + has the TC value n. + c. Neither m or n when 'Test Data Traffic' has both the priorities. + 6. Start 'Test Data Traffic' and 'Background Data Traffic'. + 7. From Rx port send pause frames on priorities either m or n. Such that + priority of 'Test Data Traffic' at Tx end == Pause Priority at Rx end. + 8. You may repeat the steps 6 and 7 several times. + 9. Expected result - + a. No 'Test Data Traffic' will not flow. Since priority of + that is always equal to the priority pause frame priority. + b. 'Background Data Traffic' will always flow. + 10. Configure 'Test Data Traffic' such that it contains traffic items + of both the priorities m and n. + 11. Configure 'Background Data Traffic' it contains all other traffic + except priorities m and n. + 12. From Rx port send pause frames on priorities both m and n. + 13. When pause frames are started 'Test Data Traffic' will stop; + and when pause frames are stopped 'Test Data Traffic' will start. + 14. 'Background Data Traffic' will always flow. + 15. Repeat the steps 10, to 14 several times. + + Note: We are using default PFC enabled class of SONiC DUT as m and n. + So, m = 3, n = 4. + """ port_list = list() fanout_devices = IxiaFanoutManager(fanout_graph_facts) - fanout_devices.get_fanout_device_details(device_number = 0) - + fanout_devices.get_fanout_device_details(device_number=0) device_conn = conn_graph_facts['device_conn'] + for intf in fanout_devices.get_ports(): - peer_port = intf['peer_port'] - intf['speed'] = int(device_conn[peer_port]['speed']) * 100 + peer_port = intf['peer_port'] + intf['speed'] = int(device_conn[peer_port]['speed']) port_list.append(intf) # The topology should have at least two interfaces. - pytest_assert(len(device_conn)>=2, + pytest_assert(len(device_conn) >= 2, "The topology should have at least two interfaces") - + # Test pausing each lossless priority individually. + # Step - 6, 7, 8, and 9. session = ixia_api_server_session for prio in lossless_prio_dscp_map: - for i in range(len(port_list)): + for i in range(len(port_list)): vports = configure_ports(session, port_list) - + rx_id = i tx_id = (i + 1) % len(port_list) - + rx_port = vports[rx_id] tx_port = vports[tx_id] rx_port_bw = port_list[rx_id]['speed'] tx_port_bw = port_list[tx_id]['speed'] - + pytest_assert(rx_port_bw == tx_port_bw) - + # All the DSCP values mapped to this priority. test_dscp_list = lossless_prio_dscp_map[prio] - # The other DSCP values. bg_dscp_list = [x for x in range(64) if x not in test_dscp_list] - + exp_dur = 2 - - run_pfc_exp(session=session, - dut=duthost, - tx_port=tx_port, + + run_pfc_exp(session=session, + dut=duthost, + tx_port=tx_port, rx_port=rx_port, port_bw=tx_port_bw, test_prio_list=[prio], - test_dscp_list=test_dscp_list, + test_dscp_list=test_dscp_list, bg_dscp_list=bg_dscp_list, exp_dur=exp_dur, - paused=True) + test_traffic_pause_expected=True) clean_configuration(session=session) + # Step - 10, 11, 12, 13 and 14. + send_pause_frame = False + for i in range(len(port_list)): + vports = configure_ports(session, port_list) + + rx_id = i + tx_id = (i + 1) % len(port_list) + + rx_port = vports[rx_id] + tx_port = vports[tx_id] + rx_port_bw = port_list[rx_id]['speed'] + tx_port_bw = port_list[tx_id]['speed'] + + pytest_assert(rx_port_bw == tx_port_bw) + + test_dscp_list = [] + test_priority_list = [prio for prio in lossless_prio_dscp_map] + for prio in lossless_prio_dscp_map: + for p in lossless_prio_dscp_map[prio]: + test_dscp_list.append(p) + + bg_dscp_list = [x for x in range(64) if x not in test_dscp_list] + exp_dur = 2 + + send_pause_frame = False if send_pause_frame == True else True + paused = send_pause_frame + + run_pfc_exp(session=session, + dut=duthost, + tx_port=tx_port, + rx_port=rx_port, + port_bw=tx_port_bw, + test_prio_list=test_priority_list, + test_dscp_list=test_dscp_list, + bg_dscp_list=bg_dscp_list, + exp_dur=exp_dur, + test_traffic_pause_expected=paused, + send_pause_frame=send_pause_frame) + + clean_configuration(session=session) + From 4f03ba64ee554d06483f5a8e62d7b3aa6a2eafe5 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Fri, 14 Aug 2020 17:33:57 +0000 Subject: [PATCH 44/52] Implementing the comment given by Wei --- tests/ixia/test_pfc_pause_lossless.py | 114 +++++++++++++++++--------- 1 file changed, 76 insertions(+), 38 deletions(-) diff --git a/tests/ixia/test_pfc_pause_lossless.py b/tests/ixia/test_pfc_pause_lossless.py index b94368bbacc..10273baf874 100644 --- a/tests/ixia/test_pfc_pause_lossless.py +++ b/tests/ixia/test_pfc_pause_lossless.py @@ -28,7 +28,7 @@ START_DELAY = 1.5 def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, - test_dscp_list, bg_dscp_list, exp_dur, start_delay=START_DELAY, + test_dscp_list, bg_dscp_list, exp_dur, start_delay=START_DELAY, test_traffic_pause_expected=True, send_pause_frame=True): """ @@ -37,8 +37,11 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, 2. IXIA sends PFC pause frames from rx_port to pause priorities. 3. Background traffic should not be interruped - all background traffic will be received at the rx_port. - 4. No PFC traffic will be received at the rx_port when pause priority + 4. No test traffic will be received at the rx_port when pause priority is equal to test traffic priority. + + Note: PFC pause frames should always be dropped, regardless of their + pause priorities. Args: session (IxNetwork Session object): IxNetwork session. @@ -54,7 +57,7 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, start_delay (float): approximated initial delay to start the traffic. test_traffic_pause_expected (bool): Do you expect test traffic to be stopped? If yes, this should be true; false otherwise. - send_pause_frame (bool): True/False depending on whether you wand to + send_pause_frame (bool): True/False depending on whether you want to send pause frame Rx port or not. Returns: @@ -96,7 +99,7 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, pkt_size=DATA_PKT_SIZE, duration=exp_dur, rate_percent=50, - start_delay=1, + start_delay=start_delay, dscp_list=test_dscp_list, lossless_prio_list=test_prio_list) @@ -108,7 +111,7 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, pkt_size=DATA_PKT_SIZE, duration=exp_dur, rate_percent=50, - start_delay=1, + start_delay=start_delay, dscp_list=bg_dscp_list, lossless_prio_list=bg_priority_list) @@ -128,7 +131,7 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, start_traffic(session) # Wait for test and background traffic to finish. - time.sleep(exp_dur + start_delay) + time.sleep(exp_dur + start_delay + 1) # Capture traffic statistics. flow_statistics = get_traffic_statistics(session) @@ -156,44 +159,37 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, stop_traffic(session) -def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map, - duthost, ixia_dev, ixia_api_server_session, - fanout_graph_facts): +def test_pfc_pause_single_lossless_priority(testbed, + conn_graph_facts, + lossless_prio_dscp_map, + duthost, + ixia_dev, + ixia_api_server_session, + fanout_graph_facts): """ - RDMA PFC - Pauses on lossless priorities. - 1. On SONiC DUT enable PFC on any two priorities say, m and n. + RDMA PFC - Pauses on single lossless priority. + 1. On SONiC DUT enable PFC on any priorities Pi. (0 <= i <= 7). 2. Disable the PFC watchdog on the SONiC DUT. 3. On the Ixia Tx port create two flows - a) 'Test Data Traffic' and b) 'Background Data traffic'. - 4. The flow 'Test Data Traffic' can assume only one of the priority - values - either m or n (or both). - 5. The flow 'Background Data Traffic' can assume any priority values - which is not in 'Test Data Traffic' (including m or n). Note that: - a. Background data traffic can assume priority n if 'Test Data Traffic' - has the TC value m. - b. Background data traffic can assume priority m if 'Test Data Traffic' - has the TC value n. - c. Neither m or n when 'Test Data Traffic' has both the priorities. - 6. Start 'Test Data Traffic' and 'Background Data Traffic'. - 7. From Rx port send pause frames on priorities either m or n. Such that - priority of 'Test Data Traffic' at Tx end == Pause Priority at Rx end. - 8. You may repeat the steps 6 and 7 several times. + 4. The flow 'Test Data Traffic' can assume one of the lossless priority + values Pi. + 5. The flow 'Background Data Traffic' can assume all the priority values + which is not in 'Test Data Traffic'. For example if the priority of + 'Test Data Traffic' is 3, the priorities of the 'Background Data + Traffic' should be 0, 1, 2, 4, 5, 6, 7. + 6. From Rx port send pause frames on priority Pi. Such that priority of + 'Test Data Traffic' at Tx end == Pause Priority at Rx end. That is, + send pause frames on priority Pi. + 7. Start 'Test Data Traffic' and 'Background Data Traffic'. + 8. You may repeat step 6 and 7 for each lossless priorities. 9. Expected result - a. No 'Test Data Traffic' will not flow. Since priority of that is always equal to the priority pause frame priority. b. 'Background Data Traffic' will always flow. - 10. Configure 'Test Data Traffic' such that it contains traffic items - of both the priorities m and n. - 11. Configure 'Background Data Traffic' it contains all other traffic - except priorities m and n. - 12. From Rx port send pause frames on priorities both m and n. - 13. When pause frames are started 'Test Data Traffic' will stop; - and when pause frames are stopped 'Test Data Traffic' will start. - 14. 'Background Data Traffic' will always flow. - 15. Repeat the steps 10, to 14 several times. - - Note: We are using default PFC enabled class of SONiC DUT as m and n. - So, m = 3, n = 4. + + Note: Test and background traffic should be started after PFC pause storm. + """ port_list = list() fanout_devices = IxiaFanoutManager(fanout_graph_facts) @@ -210,7 +206,6 @@ def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map, "The topology should have at least two interfaces") # Test pausing each lossless priority individually. - # Step - 6, 7, 8, and 9. session = ixia_api_server_session for prio in lossless_prio_dscp_map: for i in range(len(port_list)): @@ -246,7 +241,50 @@ def test_pfc_pause_lossless(testbed, conn_graph_facts, lossless_prio_dscp_map, clean_configuration(session=session) - # Step - 10, 11, 12, 13 and 14. + +def test_pfc_pause_multi_lossless_priorities(testbed, + conn_graph_facts, + lossless_prio_dscp_map, + duthost, + ixia_dev, + ixia_api_server_session, + fanout_graph_facts): + """ + RDMA PFC - Pauses on multiple lossless priorities. + 1. On SONiC DUT enable PFC any two priorities Pm, Pn. (0 <= n<= 7) also + (0 <= m <= 7) + 2. Disable the PFC watchdog on the SONiC DUT. + 3. On the Ixia Tx port create two flows - a) 'Test Data Traffic' and + b) 'Background Data traffic'. + 4. Configure 'Test Data Traffic' such that it contains traffic items + of two lossless priorities Pm and Pn. + 5. Configure 'Background Data Traffic' it contains all other traffic + except priorities Pm and Pn. For example if Pm = 3 and Pn = 4 then + the priorities of the 'Background Data Traffic' should be 0, 1, 2, + 5, 6 and 7. + 6. From Rx port send pause frames on priorities both Pm and Pn. Then + start 'Test Data Traffic' and 'Background Data Traffic'. + 7. When pause frames are started 'Test Data Traffic' will stop; + and when pause frames are stopped 'Test Data Traffic' will start. + 8. 'Background Data Traffic' will always flow. + 9. Repeat the steps 4 to 8 several times. + + """ + port_list = list() + fanout_devices = IxiaFanoutManager(fanout_graph_facts) + fanout_devices.get_fanout_device_details(device_number=0) + device_conn = conn_graph_facts['device_conn'] + + for intf in fanout_devices.get_ports(): + peer_port = intf['peer_port'] + intf['speed'] = int(device_conn[peer_port]['speed']) + port_list.append(intf) + + # The topology should have at least two interfaces. + pytest_assert(len(device_conn) >= 2, + "The topology should have at least two interfaces") + + session = ixia_api_server_session send_pause_frame = False for i in range(len(port_list)): vports = configure_ports(session, port_list) From dabddb2efaf78f7d1a849429fad52cd582e574c9 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Sat, 15 Aug 2020 11:10:26 +0000 Subject: [PATCH 45/52] correcting the function name --- tests/common/ixia/ixia_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index 778263ca6a5..e87de0e9256 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -719,7 +719,7 @@ def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, # Construct global pause and PFC packets. if global_pause: - set_global_pause_fields(pfc_stack_obj) + __set_global_pause_fields__(pfc_stack_obj) else: __set_pfc_fields__(pfc_stack_obj, pause_prio_list) From 96d0ebff4841cf436601a0f13effe2065bc64dd0 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Mon, 17 Aug 2020 13:27:21 +0000 Subject: [PATCH 46/52] Updating the first RDMA test cases as per comments --- tests/common/ixia/ixia_helpers.py | 25 +++++++++---------- tests/rdma/files/__init__.py | 1 + .../ixia => rdma/files}/qos_fixtures.py | 0 .../{ixia => rdma}/test_pfc_pause_lossless.py | 3 ++- 4 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 tests/rdma/files/__init__.py rename tests/{common/ixia => rdma/files}/qos_fixtures.py (100%) rename tests/{ixia => rdma}/test_pfc_pause_lossless.py (99%) diff --git a/tests/common/ixia/ixia_helpers.py b/tests/common/ixia/ixia_helpers.py index e87de0e9256..161c8c0e03f 100644 --- a/tests/common/ixia/ixia_helpers.py +++ b/tests/common/ixia/ixia_helpers.py @@ -711,7 +711,7 @@ def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, StartDelay=start_delay*(10**6)) # Add PFC header - pfc_stack_obj = __create_pkt_hdr__( + pfc_stack_obj = __create_pkt_hdr( ixnetwork=ixnetwork, traffic_item=traffic_item, pkt_hdr_to_add='^PFC PAUSE \(802.1Qbb\)', @@ -719,9 +719,9 @@ def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, # Construct global pause and PFC packets. if global_pause: - __set_global_pause_fields__(pfc_stack_obj) + __set_global_pause_fields(pfc_stack_obj) else: - __set_pfc_fields__(pfc_stack_obj, pause_prio_list) + __set_pfc_fields(pfc_stack_obj, pause_prio_list) # Remove Ethernet header. traffic_item.ConfigElement.find()[0].Stack.\ @@ -736,13 +736,12 @@ def create_pause_traffic(session, name, source, pkt_per_sec, pkt_count=None, # This section defines helper function used in the module. These functions # should not be called from test script. -# 1. __set_global_pause_fields__ -# 2. __set_eth_fields__ -# 3. __set_eth_fields__ -# 4. __set_pfc_fields__ -# 5. __create_pkt_hdr__ +# 1. __set_global_pause_fields +# 2. __set_eth_fields +# 3. __set_pfc_fields +# 4. __create_pkt_hdr -def __set_global_pause_fields__(pfc_stack_obj): +def __set_global_pause_fields(pfc_stack_obj): code = pfc_stack_obj.find(DisplayName='Control opcode') code.ValueType = 'singleValue' code.SingleValue = '1' @@ -761,7 +760,7 @@ def __set_global_pause_fields__(pfc_stack_obj): pause_duration.SingleValue = '0' -def __set_eth_fields__(eth_stack_obj, src_mac, dst_mac): +def __set_eth_fields(eth_stack_obj, src_mac, dst_mac): if src_mac is not None: src_mac_field = eth_stack_obj.find(DisplayName='Source MAC Address') src_mac_field.ValueType = 'singleValue' @@ -774,7 +773,7 @@ def __set_eth_fields__(eth_stack_obj, src_mac, dst_mac): dst_mac_field.SingleValue = dst_mac -def __set_ip_fields__(ip_stack_obj, src_ip, dst_ip, dscp_list): +def __set_ip_fields(ip_stack_obj, src_ip, dst_ip, dscp_list): if src_ip is not None: src_ip_field = ip_stack_obj.find(DisplayName='Source Address') src_ip_field.ValueType = 'singleValue' @@ -792,7 +791,7 @@ def __set_ip_fields__(ip_stack_obj, src_ip, dst_ip, dscp_list): phb_field.ValueList = dscp_list -def __set_pfc_fields__(pfc_stack_obj, pause_prio_list): +def __set_pfc_fields(pfc_stack_obj, pause_prio_list): code = pfc_stack_obj.find(DisplayName='Control opcode') code.ValueType = 'singleValue' code.SingleValue = '101' @@ -815,7 +814,7 @@ def __set_pfc_fields__(pfc_stack_obj, pause_prio_list): pause_duration.SingleValue = '0' -def __create_pkt_hdr__(ixnetwork, +def __create_pkt_hdr(ixnetwork, traffic_item, pkt_hdr_to_add, append_to_stack): diff --git a/tests/rdma/files/__init__.py b/tests/rdma/files/__init__.py new file mode 100644 index 00000000000..3c545ee0333 --- /dev/null +++ b/tests/rdma/files/__init__.py @@ -0,0 +1 @@ +# Local library for rdma tests. diff --git a/tests/common/ixia/qos_fixtures.py b/tests/rdma/files/qos_fixtures.py similarity index 100% rename from tests/common/ixia/qos_fixtures.py rename to tests/rdma/files/qos_fixtures.py diff --git a/tests/ixia/test_pfc_pause_lossless.py b/tests/rdma/test_pfc_pause_lossless.py similarity index 99% rename from tests/ixia/test_pfc_pause_lossless.py rename to tests/rdma/test_pfc_pause_lossless.py index 10273baf874..2b64366f67d 100644 --- a/tests/ixia/test_pfc_pause_lossless.py +++ b/tests/rdma/test_pfc_pause_lossless.py @@ -19,7 +19,8 @@ from tests.common.ixia.common_helpers import get_vlan_subnet, \ get_addrs_in_subnet -from tests.common.ixia.qos_fixtures import lossless_prio_dscp_map +#from tests.rdma.files.qos_fixtures import lossless_prio_dscp_map +from files.qos_fixtures import lossless_prio_dscp_map pytestmark = [pytest.mark.disable_loganalyzer] From 867394b7ebc59cf971ebb612befca3373b242c96 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Thu, 20 Aug 2020 10:31:00 +0000 Subject: [PATCH 47/52] Changing the files as per review comments of PR1979 --- tests/pfc/files/__init__.py | 1 + tests/{rdma => pfc}/files/qos_fixtures.py | 0 .../{rdma => pfc}/test_pfc_pause_lossless.py | 62 +++++++++++++------ tests/rdma/files/__init__.py | 1 - 4 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 tests/pfc/files/__init__.py rename tests/{rdma => pfc}/files/qos_fixtures.py (100%) rename tests/{rdma => pfc}/test_pfc_pause_lossless.py (86%) delete mode 100644 tests/rdma/files/__init__.py diff --git a/tests/pfc/files/__init__.py b/tests/pfc/files/__init__.py new file mode 100644 index 00000000000..65d14a7c199 --- /dev/null +++ b/tests/pfc/files/__init__.py @@ -0,0 +1 @@ +# Local library for PFC tests. diff --git a/tests/rdma/files/qos_fixtures.py b/tests/pfc/files/qos_fixtures.py similarity index 100% rename from tests/rdma/files/qos_fixtures.py rename to tests/pfc/files/qos_fixtures.py diff --git a/tests/rdma/test_pfc_pause_lossless.py b/tests/pfc/test_pfc_pause_lossless.py similarity index 86% rename from tests/rdma/test_pfc_pause_lossless.py rename to tests/pfc/test_pfc_pause_lossless.py index 2b64366f67d..7b0feb89ece 100644 --- a/tests/rdma/test_pfc_pause_lossless.py +++ b/tests/pfc/test_pfc_pause_lossless.py @@ -26,12 +26,14 @@ # Data packet size in bytes. DATA_PKT_SIZE = 1024 -START_DELAY = 1.5 +START_DELAY = 1.5 +RATE_PERCENTAGE = 50 +TOLERANCE_PARAMETER = .97 def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, test_dscp_list, bg_dscp_list, exp_dur, start_delay=START_DELAY, test_traffic_pause_expected=True, - send_pause_frame=True): + send_pause_frame=True) : """ Run a PFC experiment. 1. IXIA sends test traffic and background traffic from tx_port. @@ -99,7 +101,7 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, destination=topo_receiver, pkt_size=DATA_PKT_SIZE, duration=exp_dur, - rate_percent=50, + rate_percent=RATE_PERCENTAGE, start_delay=start_delay, dscp_list=test_dscp_list, lossless_prio_list=test_prio_list) @@ -111,7 +113,7 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, destination=topo_receiver, pkt_size=DATA_PKT_SIZE, duration=exp_dur, - rate_percent=50, + rate_percent=RATE_PERCENTAGE, start_delay=start_delay, dscp_list=bg_dscp_list, lossless_prio_list=bg_priority_list) @@ -138,10 +140,13 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, flow_statistics = get_traffic_statistics(session) logger.info(flow_statistics) + exp_tx_byte = (exp_dur * port_bw * 1000000 * (RATE_PERCENTAGE / 100.0)) / 8 for row_number, flow_stat in enumerate(flow_statistics.Rows): tx_frames = int(flow_stat['Tx Frames']) rx_frames = int(flow_stat['Rx Frames']) + rx_bytes = int(flow_stat['Rx Bytes']) + tolerance_ratio = rx_bytes / exp_tx_byte if 'Test' in flow_stat['Traffic Item']: if test_traffic_pause_expected: pytest_assert(tx_frames > 0 and rx_frames == 0, @@ -150,12 +155,32 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, pytest_assert(tx_frames > 0 and tx_frames == rx_frames, "Test traffic should not be impacted") + if ((tolerance_ratio < TOLERANCE_PARAMETER) or + (tolerance_ratio > 1)) : + logger.error("Expected Tx/Rx = %s actual Rx = %s" + %(exp_tx_byte, exp_tx_byte)) + + logger.error("tolerance_ratio = %s" %(tolerance_ratio)) + + pytest_assert(False, + "expected % of packets not received at the RX port") + elif 'PFC' in flow_stat['Traffic Item']: pytest_assert(tx_frames > 0 and rx_frames == 0, "PFC packets should be dropped") else: pytest_assert(tx_frames > 0 and tx_frames == rx_frames, "Background traffic should not be impacted") + + if ((tolerance_ratio < TOLERANCE_PARAMETER) or + (tolerance_ratio > 1)) : + logger.error("Expected Tx/Rx = %s actual Rx = %s" + %(exp_tx_byte, exp_tx_byte)) + + logger.error("tolerance_ratio = %s" %(tolerance_ratio)) + + pytest_assert(False, + "expected % of packets not received at the RX port") stop_traffic(session) @@ -176,7 +201,7 @@ def test_pfc_pause_single_lossless_priority(testbed, 4. The flow 'Test Data Traffic' can assume one of the lossless priority values Pi. 5. The flow 'Background Data Traffic' can assume all the priority values - which is not in 'Test Data Traffic'. For example if the priority of + which are not in 'Test Data Traffic'. For example if the priority of 'Test Data Traffic' is 3, the priorities of the 'Background Data Traffic' should be 0, 1, 2, 4, 5, 6, 7. 6. From Rx port send pause frames on priority Pi. Such that priority of @@ -185,8 +210,8 @@ def test_pfc_pause_single_lossless_priority(testbed, 7. Start 'Test Data Traffic' and 'Background Data Traffic'. 8. You may repeat step 6 and 7 for each lossless priorities. 9. Expected result - - a. No 'Test Data Traffic' will not flow. Since priority of - that is always equal to the priority pause frame priority. + a. No 'Test Data Traffic' will flow. Since priority of + 'Test Data Traffic' equals to the priority of PFC pause frames. b. 'Background Data Traffic' will always flow. Note: Test and background traffic should be started after PFC pause storm. @@ -209,6 +234,7 @@ def test_pfc_pause_single_lossless_priority(testbed, # Test pausing each lossless priority individually. session = ixia_api_server_session for prio in lossless_prio_dscp_map: + send_pause_frame = False for i in range(len(port_list)): vports = configure_ports(session, port_list) @@ -229,6 +255,8 @@ def test_pfc_pause_single_lossless_priority(testbed, exp_dur = 2 + send_pause_frame = False if send_pause_frame == True else True + paused = send_pause_frame run_pfc_exp(session=session, dut=duthost, tx_port=tx_port, @@ -238,7 +266,8 @@ def test_pfc_pause_single_lossless_priority(testbed, test_dscp_list=test_dscp_list, bg_dscp_list=bg_dscp_list, exp_dur=exp_dur, - test_traffic_pause_expected=True) + test_traffic_pause_expected=paused, + send_pause_frame=send_pause_frame) clean_configuration(session=session) @@ -252,18 +281,15 @@ def test_pfc_pause_multi_lossless_priorities(testbed, fanout_graph_facts): """ RDMA PFC - Pauses on multiple lossless priorities. - 1. On SONiC DUT enable PFC any two priorities Pm, Pn. (0 <= n<= 7) also - (0 <= m <= 7) + 1. On SONiC DUT enable PFC on several priorities e.g priority 3 and 4. 2. Disable the PFC watchdog on the SONiC DUT. 3. On the Ixia Tx port create two flows - a) 'Test Data Traffic' and b) 'Background Data traffic'. 4. Configure 'Test Data Traffic' such that it contains traffic items - of two lossless priorities Pm and Pn. - 5. Configure 'Background Data Traffic' it contains all other traffic - except priorities Pm and Pn. For example if Pm = 3 and Pn = 4 then - the priorities of the 'Background Data Traffic' should be 0, 1, 2, - 5, 6 and 7. - 6. From Rx port send pause frames on priorities both Pm and Pn. Then + with all lossless priorities. + 5. Configure 'Background Data Traffic' it contains traffic items with + all lossy priorities. + 6. From Rx port send pause frames on all lossless priorities. Then start 'Test Data Traffic' and 'Background Data Traffic'. 7. When pause frames are started 'Test Data Traffic' will stop; and when pause frames are stopped 'Test Data Traffic' will start. @@ -297,14 +323,12 @@ def test_pfc_pause_multi_lossless_priorities(testbed, tx_port = vports[tx_id] rx_port_bw = port_list[rx_id]['speed'] tx_port_bw = port_list[tx_id]['speed'] - pytest_assert(rx_port_bw == tx_port_bw) test_dscp_list = [] test_priority_list = [prio for prio in lossless_prio_dscp_map] for prio in lossless_prio_dscp_map: - for p in lossless_prio_dscp_map[prio]: - test_dscp_list.append(p) + test_dscp_list += lossless_prio_dscp_map[prio] bg_dscp_list = [x for x in range(64) if x not in test_dscp_list] exp_dur = 2 diff --git a/tests/rdma/files/__init__.py b/tests/rdma/files/__init__.py deleted file mode 100644 index 3c545ee0333..00000000000 --- a/tests/rdma/files/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# Local library for rdma tests. From 2b82d018d88a45bdb83a7357471a9ec0090faf6c Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Mon, 24 Aug 2020 10:31:35 +0000 Subject: [PATCH 48/52] updating as per comments of wei --> for send_pause_frame in [True, False]: --- tests/pfc/test_pfc_pause_lossless.py | 117 +++++++++++++-------------- 1 file changed, 58 insertions(+), 59 deletions(-) diff --git a/tests/pfc/test_pfc_pause_lossless.py b/tests/pfc/test_pfc_pause_lossless.py index 7b0feb89ece..d1e133a73d3 100644 --- a/tests/pfc/test_pfc_pause_lossless.py +++ b/tests/pfc/test_pfc_pause_lossless.py @@ -28,7 +28,7 @@ DATA_PKT_SIZE = 1024 START_DELAY = 1.5 RATE_PERCENTAGE = 50 -TOLERANCE_PARAMETER = .97 +TOLERANCE_THRESHOLD = .97 def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, test_dscp_list, bg_dscp_list, exp_dur, start_delay=START_DELAY, @@ -153,9 +153,9 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, "Test traffic should be fully paused") else: pytest_assert(tx_frames > 0 and tx_frames == rx_frames, - "Test traffic should not be impacted") + "Test traffic packets should not be dropped") - if ((tolerance_ratio < TOLERANCE_PARAMETER) or + if ((tolerance_ratio < TOLERANCE_THRESHOLD) or (tolerance_ratio > 1)) : logger.error("Expected Tx/Rx = %s actual Rx = %s" %(exp_tx_byte, exp_tx_byte)) @@ -172,7 +172,7 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, pytest_assert(tx_frames > 0 and tx_frames == rx_frames, "Background traffic should not be impacted") - if ((tolerance_ratio < TOLERANCE_PARAMETER) or + if ((tolerance_ratio < TOLERANCE_THRESHOLD) or (tolerance_ratio > 1)) : logger.error("Expected Tx/Rx = %s actual Rx = %s" %(exp_tx_byte, exp_tx_byte)) @@ -208,7 +208,7 @@ def test_pfc_pause_single_lossless_priority(testbed, 'Test Data Traffic' at Tx end == Pause Priority at Rx end. That is, send pause frames on priority Pi. 7. Start 'Test Data Traffic' and 'Background Data Traffic'. - 8. You may repeat step 6 and 7 for each lossless priorities. + 8. Repeat step 6 and 7 for each lossless priorities. 9. Expected result - a. No 'Test Data Traffic' will flow. Since priority of 'Test Data Traffic' equals to the priority of PFC pause frames. @@ -234,30 +234,29 @@ def test_pfc_pause_single_lossless_priority(testbed, # Test pausing each lossless priority individually. session = ixia_api_server_session for prio in lossless_prio_dscp_map: - send_pause_frame = False for i in range(len(port_list)): - vports = configure_ports(session, port_list) + for send_pause_frame in [True, False]: + paused = send_pause_frame + vports = configure_ports(session, port_list) - rx_id = i - tx_id = (i + 1) % len(port_list) + rx_id = i + tx_id = (i + 1) % len(port_list) - rx_port = vports[rx_id] - tx_port = vports[tx_id] - rx_port_bw = port_list[rx_id]['speed'] - tx_port_bw = port_list[tx_id]['speed'] + rx_port = vports[rx_id] + tx_port = vports[tx_id] + rx_port_bw = port_list[rx_id]['speed'] + tx_port_bw = port_list[tx_id]['speed'] - pytest_assert(rx_port_bw == tx_port_bw) + pytest_assert(rx_port_bw == tx_port_bw) - # All the DSCP values mapped to this priority. - test_dscp_list = lossless_prio_dscp_map[prio] - # The other DSCP values. - bg_dscp_list = [x for x in range(64) if x not in test_dscp_list] + # All the DSCP values mapped to this priority. + test_dscp_list = lossless_prio_dscp_map[prio] + # The other DSCP values. + bg_dscp_list = [x for x in range(64) if x not in test_dscp_list] - exp_dur = 2 + exp_dur = 2 - send_pause_frame = False if send_pause_frame == True else True - paused = send_pause_frame - run_pfc_exp(session=session, + run_pfc_exp(session=session, dut=duthost, tx_port=tx_port, rx_port=rx_port, @@ -269,7 +268,7 @@ def test_pfc_pause_single_lossless_priority(testbed, test_traffic_pause_expected=paused, send_pause_frame=send_pause_frame) - clean_configuration(session=session) + clean_configuration(session=session) def test_pfc_pause_multi_lossless_priorities(testbed, @@ -294,7 +293,7 @@ def test_pfc_pause_multi_lossless_priorities(testbed, 7. When pause frames are started 'Test Data Traffic' will stop; and when pause frames are stopped 'Test Data Traffic' will start. 8. 'Background Data Traffic' will always flow. - 9. Repeat the steps 4 to 8 several times. + 9. Repeat the steps 4 to 8 on all ports. """ port_list = list() @@ -314,39 +313,39 @@ def test_pfc_pause_multi_lossless_priorities(testbed, session = ixia_api_server_session send_pause_frame = False for i in range(len(port_list)): - vports = configure_ports(session, port_list) - - rx_id = i - tx_id = (i + 1) % len(port_list) - - rx_port = vports[rx_id] - tx_port = vports[tx_id] - rx_port_bw = port_list[rx_id]['speed'] - tx_port_bw = port_list[tx_id]['speed'] - pytest_assert(rx_port_bw == tx_port_bw) - - test_dscp_list = [] - test_priority_list = [prio for prio in lossless_prio_dscp_map] - for prio in lossless_prio_dscp_map: - test_dscp_list += lossless_prio_dscp_map[prio] - - bg_dscp_list = [x for x in range(64) if x not in test_dscp_list] - exp_dur = 2 - - send_pause_frame = False if send_pause_frame == True else True - paused = send_pause_frame - - run_pfc_exp(session=session, - dut=duthost, - tx_port=tx_port, - rx_port=rx_port, - port_bw=tx_port_bw, - test_prio_list=test_priority_list, - test_dscp_list=test_dscp_list, - bg_dscp_list=bg_dscp_list, - exp_dur=exp_dur, - test_traffic_pause_expected=paused, - send_pause_frame=send_pause_frame) - - clean_configuration(session=session) + for send_pause_frame in [True, False]: + paused = send_pause_frame + vports = configure_ports(session, port_list) + + rx_id = i + tx_id = (i + 1) % len(port_list) + + rx_port = vports[rx_id] + tx_port = vports[tx_id] + rx_port_bw = port_list[rx_id]['speed'] + tx_port_bw = port_list[tx_id]['speed'] + pytest_assert(rx_port_bw == tx_port_bw) + + test_dscp_list = [] + test_priority_list = [prio for prio in lossless_prio_dscp_map] + for prio in lossless_prio_dscp_map: + test_dscp_list += lossless_prio_dscp_map[prio] + + bg_dscp_list = [x for x in range(64) if x not in test_dscp_list] + exp_dur = 2 + + + run_pfc_exp(session=session, + dut=duthost, + tx_port=tx_port, + rx_port=rx_port, + port_bw=tx_port_bw, + test_prio_list=test_priority_list, + test_dscp_list=test_dscp_list, + bg_dscp_list=bg_dscp_list, + exp_dur=exp_dur, + test_traffic_pause_expected=paused, + send_pause_frame=send_pause_frame) + + clean_configuration(session=session) From 8cc251eb9516cbd70744bfd1bd95104dbe6b6ec2 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Mon, 24 Aug 2020 12:33:34 +0000 Subject: [PATCH 49/52] removed an unwanted variable assignment --- tests/pfc/test_pfc_pause_lossless.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/pfc/test_pfc_pause_lossless.py b/tests/pfc/test_pfc_pause_lossless.py index d1e133a73d3..b6f3ea31538 100644 --- a/tests/pfc/test_pfc_pause_lossless.py +++ b/tests/pfc/test_pfc_pause_lossless.py @@ -311,7 +311,6 @@ def test_pfc_pause_multi_lossless_priorities(testbed, "The topology should have at least two interfaces") session = ixia_api_server_session - send_pause_frame = False for i in range(len(port_list)): for send_pause_frame in [True, False]: paused = send_pause_frame From 1e5a1cd97a1b52c9c54bd7b0d44eac945a550ad5 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Tue, 25 Aug 2020 11:03:08 +0000 Subject: [PATCH 50/52] added a Safety check 2 * RATE PERCENTAGE <= 100 --- tests/pfc/test_pfc_pause_lossless.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/pfc/test_pfc_pause_lossless.py b/tests/pfc/test_pfc_pause_lossless.py index b6f3ea31538..544e6f35be4 100644 --- a/tests/pfc/test_pfc_pause_lossless.py +++ b/tests/pfc/test_pfc_pause_lossless.py @@ -86,6 +86,11 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, gw_start=gw_addr, gw_incr_step='0.0.0.0') + # Assumption: Line rate percentage of background data traffic + # is equal to Line rate percentage of test data traffic. + pytest_assert(2 * RATE_PERCENTAGE <= 100, + "Value of RATE_PERCENTAGE should not be more than 50!") + topo_sender = create_topology(session=session, name="Sender", ports=list(tx_port), From a499c8904a5b8b9fa63baca890c83cdf8bce1ee7 Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Wed, 26 Aug 2020 03:12:05 +0000 Subject: [PATCH 51/52] changed 'exp_tx_byte' to 'exp_tx_bytes' --- tests/pfc/test_pfc_pause_lossless.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/pfc/test_pfc_pause_lossless.py b/tests/pfc/test_pfc_pause_lossless.py index 544e6f35be4..d963c681a2b 100644 --- a/tests/pfc/test_pfc_pause_lossless.py +++ b/tests/pfc/test_pfc_pause_lossless.py @@ -145,13 +145,13 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, flow_statistics = get_traffic_statistics(session) logger.info(flow_statistics) - exp_tx_byte = (exp_dur * port_bw * 1000000 * (RATE_PERCENTAGE / 100.0)) / 8 + exp_tx_bytes = (exp_dur * port_bw * 1000000 * (RATE_PERCENTAGE / 100.0)) / 8 for row_number, flow_stat in enumerate(flow_statistics.Rows): tx_frames = int(flow_stat['Tx Frames']) rx_frames = int(flow_stat['Rx Frames']) rx_bytes = int(flow_stat['Rx Bytes']) - tolerance_ratio = rx_bytes / exp_tx_byte + tolerance_ratio = rx_bytes / exp_tx_bytes if 'Test' in flow_stat['Traffic Item']: if test_traffic_pause_expected: pytest_assert(tx_frames > 0 and rx_frames == 0, @@ -163,7 +163,7 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, if ((tolerance_ratio < TOLERANCE_THRESHOLD) or (tolerance_ratio > 1)) : logger.error("Expected Tx/Rx = %s actual Rx = %s" - %(exp_tx_byte, exp_tx_byte)) + %(exp_tx_bytes, exp_tx_bytes)) logger.error("tolerance_ratio = %s" %(tolerance_ratio)) @@ -180,7 +180,7 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, if ((tolerance_ratio < TOLERANCE_THRESHOLD) or (tolerance_ratio > 1)) : logger.error("Expected Tx/Rx = %s actual Rx = %s" - %(exp_tx_byte, exp_tx_byte)) + %(exp_tx_bytes, rx_bytes)) logger.error("tolerance_ratio = %s" %(tolerance_ratio)) From 08cf89c1fd8c4afa5ceb022eba394511b20707bd Mon Sep 17 00:00:00 2001 From: abhijit dhar Date: Wed, 26 Aug 2020 03:42:16 +0000 Subject: [PATCH 52/52] changed 'exp_tx_byte' to 'rx_bytes' --- tests/pfc/test_pfc_pause_lossless.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pfc/test_pfc_pause_lossless.py b/tests/pfc/test_pfc_pause_lossless.py index d963c681a2b..214b2c2fbb1 100644 --- a/tests/pfc/test_pfc_pause_lossless.py +++ b/tests/pfc/test_pfc_pause_lossless.py @@ -163,7 +163,7 @@ def run_pfc_exp(session, dut, tx_port, rx_port, port_bw, test_prio_list, if ((tolerance_ratio < TOLERANCE_THRESHOLD) or (tolerance_ratio > 1)) : logger.error("Expected Tx/Rx = %s actual Rx = %s" - %(exp_tx_bytes, exp_tx_bytes)) + %(exp_tx_bytes, rx_bytes)) logger.error("tolerance_ratio = %s" %(tolerance_ratio))