diff options
Diffstat (limited to 'scripts/automation')
28 files changed, 539 insertions, 39 deletions
diff --git a/scripts/automation/regression/outer_packages.py b/scripts/automation/regression/outer_packages.py index bec9fe21..61ddc5cd 100755 --- a/scripts/automation/regression/outer_packages.py +++ b/scripts/automation/regression/outer_packages.py @@ -9,6 +9,7 @@ if not TREX_PATH or not os.path.isfile('%s/trex_daemon_server' % TREX_PATH): TREX_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir, os.pardir)) PATH_TO_PYTHON_LIB = os.path.abspath(os.path.join(TREX_PATH, 'external_libs')) PATH_TO_CTRL_PLANE = os.path.abspath(os.path.join(TREX_PATH, 'automation', 'trex_control_plane')) +PATH_STF_API = os.path.abspath(os.path.join(PATH_TO_CTRL_PLANE, 'stf')) PATH_STL_API = os.path.abspath(os.path.join(PATH_TO_CTRL_PLANE, 'stl')) @@ -57,8 +58,9 @@ def import_module_list(modules_list): def import_nightly_modules (): sys.path.append(TREX_PATH) - sys.path.append(PATH_TO_CTRL_PLANE) + #sys.path.append(PATH_TO_CTRL_PLANE) sys.path.append(PATH_STL_API) + sys.path.append(PATH_STF_API) import_module_list(NIGHTLY_MODULES) diff --git a/scripts/automation/regression/stateless_tests/stl_client_test.py b/scripts/automation/regression/stateless_tests/stl_client_test.py new file mode 100644 index 00000000..132109c0 --- /dev/null +++ b/scripts/automation/regression/stateless_tests/stl_client_test.py @@ -0,0 +1,274 @@ +#!/router/bin/python +from stl_general_test import CStlGeneral_Test, CTRexScenario +from trex_stl_lib.api import * +import os, sys +import glob + + +def get_error_in_percentage (golden, value): + return abs(golden - value) / float(golden) + +def get_stl_profiles (): + profiles_path = os.path.join(CTRexScenario.scripts_path, 'stl/') + profiles = glob.glob(profiles_path + "/*.py") + glob.glob(profiles_path + "yaml/*.yaml") + + return profiles + + +class STLClient_Test(CStlGeneral_Test): + """Tests for stateless client""" + + def setUp(self): + CStlGeneral_Test.setUp(self) + + assert 'bi' in CTRexScenario.stl_ports_map + + self.c = CTRexScenario.stl_trex + + self.tx_port, self.rx_port = CTRexScenario.stl_ports_map['bi'][0] + + self.c.connect() + self.c.reset(ports = [self.tx_port, self.rx_port]) + + self.pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/IP()/'a_payload_example') + self.profiles = get_stl_profiles() + + + @classmethod + def tearDownClass(cls): + # connect back at end of tests + if not cls.is_connected(): + CTRexScenario.stl_trex.connect() + + + def test_basic_connect_disconnect (self): + try: + self.c.connect() + assert self.c.is_connected(), 'client should be connected' + self.c.disconnect() + assert not self.c.is_connected(), 'client should be disconnected' + + except STLError as e: + assert False , '{0}'.format(e) + + + def test_basic_single_burst (self): + try: + b1 = STLStream(name = 'burst', + packet = self.pkt, + mode = STLTXSingleBurst(total_pkts = 100, + pps = 1000) + ) + + for i in range(0, 5): + self.c.add_streams([b1], ports = [self.tx_port, self.rx_port]) + + self.c.clear_stats() + self.c.start(ports = [self.tx_port, self.rx_port]) + + assert self.c.ports[self.tx_port].is_transmitting(), 'port should be active' + assert self.c.ports[self.rx_port].is_transmitting(), 'port should be active' + + self.c.wait_on_traffic(ports = [self.tx_port, self.rx_port]) + stats = self.c.get_stats() + + assert self.tx_port in stats + assert self.rx_port in stats + + assert stats[self.tx_port]['opackets'] == 100 + assert stats[self.rx_port]['ipackets'] == 100 + + assert stats[self.rx_port]['opackets'] == 100 + assert stats[self.tx_port]['ipackets'] == 100 + + self.c.remove_all_streams(ports = [self.tx_port, self.rx_port]) + + + + except STLError as e: + assert False , '{0}'.format(e) + + + # + def test_basic_multi_burst (self): + try: + b1 = STLStream(name = 'burst', + packet = self.pkt, + mode = STLTXMultiBurst(pkts_per_burst = 10, + count = 20, + pps = 1000) + ) + + for i in range(0, 5): + self.c.add_streams([b1], ports = [self.tx_port, self.rx_port]) + + self.c.clear_stats() + self.c.start(ports = [self.tx_port, self.rx_port]) + + assert self.c.ports[self.tx_port].is_transmitting(), 'port should be active' + assert self.c.ports[self.rx_port].is_transmitting(), 'port should be active' + + self.c.wait_on_traffic(ports = [self.tx_port, self.rx_port]) + stats = self.c.get_stats() + + assert self.tx_port in stats + assert self.rx_port in stats + + assert stats[self.tx_port]['opackets'] == 200 + assert stats[self.rx_port]['ipackets'] == 200 + + assert stats[self.rx_port]['opackets'] == 200 + assert stats[self.tx_port]['ipackets'] == 200 + + self.c.remove_all_streams(ports = [self.tx_port, self.rx_port]) + + + + except STLError as e: + assert False , '{0}'.format(e) + + + # + def test_basic_cont (self): + pps = 49182 + duration = 0.1 + golden = pps * duration + + try: + b1 = STLStream(name = 'burst', + packet = self.pkt, + mode = STLTXCont(pps = pps) + ) + + for i in range(0, 5): + self.c.add_streams([b1], ports = [self.tx_port, self.rx_port]) + + self.c.clear_stats() + self.c.start(ports = [self.tx_port, self.rx_port], duration = duration) + + assert self.c.ports[self.tx_port].is_transmitting(), 'port should be active' + assert self.c.ports[self.rx_port].is_transmitting(), 'port should be active' + + self.c.wait_on_traffic(ports = [self.tx_port, self.rx_port]) + stats = self.c.get_stats() + + assert self.tx_port in stats + assert self.rx_port in stats + + # cont. with duration should be quite percise - 5% error is relaxed enough + assert get_error_in_percentage(stats[self.tx_port]['opackets'], golden) < 0.05 + assert get_error_in_percentage(stats[self.rx_port]['ipackets'], golden) < 0.05 + + assert get_error_in_percentage(stats[self.rx_port]['opackets'], golden) < 0.05 + assert get_error_in_percentage(stats[self.tx_port]['ipackets'], golden) < 0.05 + + + self.c.remove_all_streams(ports = [self.tx_port, self.rx_port]) + + + + except STLError as e: + assert False , '{0}'.format(e) + + + def test_stress_connect_disconnect (self): + try: + for i in range(0, 100): + self.c.connect() + assert self.c.is_connected(), 'client should be connected' + self.c.disconnect() + assert not self.c.is_connected(), 'client should be disconnected' + + + except STLError as e: + assert False , '{0}'.format(e) + + + + def test_stress_tx (self): + try: + s1 = STLStream(name = 'stress', + packet = self.pkt, + mode = STLTXCont(percentage = 60)) + + # add both streams to ports + self.c.add_streams([s1], ports = [self.tx_port, self.rx_port]) + for i in range(0, 100): + + self.c.start(ports = [self.tx_port, self.rx_port]) + + assert self.c.ports[self.tx_port].is_transmitting(), 'port should be active' + assert self.c.ports[self.rx_port].is_transmitting(), 'port should be active' + + self.c.pause(ports = [self.tx_port, self.rx_port]) + + assert self.c.ports[self.tx_port].is_paused(), 'port should be paused' + assert self.c.ports[self.rx_port].is_paused(), 'port should be paused' + + self.c.resume(ports = [self.tx_port, self.rx_port]) + + assert self.c.ports[self.tx_port].is_transmitting(), 'port should be active' + assert self.c.ports[self.rx_port].is_transmitting(), 'port should be active' + + self.c.stop(ports = [self.tx_port, self.rx_port]) + + assert not self.c.ports[self.tx_port].is_active(), 'port should be idle' + assert not self.c.ports[self.rx_port].is_active(), 'port should be idle' + + except STLError as e: + assert False , '{0}'.format(e) + + + def test_all_profiles (self): + + if self.is_virt_nics or not self.is_loopback: + print("skipping for non loopback / virtual") + return + + try: + self.c.set_port_attr(ports = [self.tx_port, self.rx_port], promiscuous = True) + + for profile in self.profiles: + print("now testing profile {0}...\n").format(profile) + + p1 = STLProfile.load(profile, port_id = self.tx_port) + p2 = STLProfile.load(profile, port_id = self.rx_port) + + if p1.has_flow_stats(): + print("profile needs RX caps - skipping...") + continue + + self.c.add_streams(p1, ports = self.tx_port) + self.c.add_streams(p2, ports = self.rx_port) + + self.c.clear_stats() + + self.c.start(ports = [self.tx_port, self.rx_port], mult = "30%") + time.sleep(100 / 1000.0) + + if p1.is_pauseable() and p2.is_pauseable(): + self.c.pause(ports = [self.tx_port, self.rx_port]) + time.sleep(100 / 1000.0) + + self.c.resume(ports = [self.tx_port, self.rx_port]) + time.sleep(100 / 1000.0) + + self.c.stop(ports = [self.tx_port, self.rx_port]) + + stats = self.c.get_stats() + + assert self.tx_port in stats, '{0} - no stats for TX port'.format(profile) + assert self.rx_port in stats, '{0} - no stats for RX port'.format(profile) + + assert stats[self.tx_port]['opackets'] == stats[self.rx_port]['ipackets'], '{0} - number of TX packets differ from RX packets'.format(profile) + + assert stats[self.rx_port]['opackets'] == stats[self.tx_port]['ipackets'], '{0} - number of TX packets differ from RX packets'.format(profile) + + self.c.remove_all_streams(ports = [self.tx_port, self.rx_port]) + + except STLError as e: + assert False , '{0}'.format(e) + + + finally: + self.c.set_port_attr(ports = [self.tx_port, self.rx_port], promiscuous = False) diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py index fb666382..3b1a25f0 100755 --- a/scripts/automation/regression/trex_unit_test.py +++ b/scripts/automation/regression/trex_unit_test.py @@ -36,8 +36,8 @@ import misc_methods from rednose import RedNose import termstyle from trex import CTRexScenario -from stf.trex_client import * -from stf.trex_exceptions import * +from trex_stf_lib.trex_client import * +from trex_stf_lib.trex_exceptions import * from trex_stl_lib.api import * import trex import socket @@ -67,10 +67,10 @@ STATEFUL_STOP_COMMAND = './trex_daemon_server stop; sleep 1; ./trex_daemon_serve STATEFUL_RUN_COMMAND = 'rm /var/log/trex/trex_daemon_server.log; ./trex_daemon_server start; sleep 2; ./trex_daemon_server show' TREX_FILES = ('_t-rex-64', '_t-rex-64-o', '_t-rex-64-debug', '_t-rex-64-debug-o') -def trex_remote_command(trex_data, command, background = False, from_scripts = True): +def trex_remote_command(trex_data, command, background = False, from_scripts = True, timeout = 20): if from_scripts: - return misc_methods.run_remote_command(trex_data['trex_name'], ('cd %s; ' % CTRexScenario.scripts_path)+ command, background) - return misc_methods.run_remote_command(trex_data['trex_name'], command, background) + return misc_methods.run_remote_command(trex_data['trex_name'], ('cd %s; ' % CTRexScenario.scripts_path)+ command, background, timeout) + return misc_methods.run_remote_command(trex_data['trex_name'], command, background, timeout) # 1 = running, 0 - not running def check_trex_running(trex_data): @@ -183,7 +183,7 @@ class CTRexTestConfiguringPlugin(Plugin): new_path = '/tmp/trex-scripts' rsync_template = 'rm -rf /tmp/trex-scripts; mkdir -p %s; rsync -Lc %s /tmp; tar -mxzf /tmp/%s -C %s; mv %s/v*.*/* %s' rsync_command = rsync_template % (new_path, self.pkg, os.path.basename(self.pkg), new_path, new_path, new_path) - return_code, stdout, stderr = trex_remote_command(self.configuration.trex, rsync_command, from_scripts = False) + return_code, stdout, stderr = trex_remote_command(self.configuration.trex, rsync_command, from_scripts = False, timeout = 300) if return_code: print('Failed copying') sys.exit(-1) @@ -339,8 +339,9 @@ if __name__ == "__main__": finally: save_setup_info() - if (result == True and not CTRexScenario.is_test_list): - print(termstyle.green(""" + if not CTRexScenario.is_test_list: + if result == True: + print(termstyle.green(""" ..::''''::.. .;'' ``;. :: :: :: :: @@ -358,8 +359,18 @@ if __name__ == "__main__": /_/ /_/ |_/___/___(_) """)) - sys.exit(0) - sys.exit(-1) + sys.exit(0) + else: + print(termstyle.red(""" + /\_/\ + ( o.o ) + > ^ < + +This cat is sad, test failed. + """)) + sys.exit(-1) + + diff --git a/scripts/automation/trex_control_plane/doc/_templates/layout.html b/scripts/automation/trex_control_plane/doc/_templates/layout.html new file mode 100644 index 00000000..8c1c709c --- /dev/null +++ b/scripts/automation/trex_control_plane/doc/_templates/layout.html @@ -0,0 +1,17 @@ +{% extends "!layout.html" %} + +{% block footer %} +{{ super() }} +<script> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-75220362-1', 'auto'); + ga('send', 'pageview'); + +</script> +{% endblock %} + + diff --git a/scripts/automation/trex_control_plane/doc/conf.py b/scripts/automation/trex_control_plane/doc/conf.py index a2641ffc..ec133a1c 100755 --- a/scripts/automation/trex_control_plane/doc/conf.py +++ b/scripts/automation/trex_control_plane/doc/conf.py @@ -20,7 +20,7 @@ import shlex # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../stf')) +sys.path.insert(0, os.path.abspath('../stf/trex_stf_lib')) sys.path.insert(0, os.path.abspath('../client_utils')) sys.path.insert(0, os.path.abspath('../examples')) sys.path.insert(0, os.path.abspath('../common')) diff --git a/scripts/automation/trex_control_plane/doc_stl/_templates/layout.html b/scripts/automation/trex_control_plane/doc_stl/_templates/layout.html new file mode 100644 index 00000000..8c1c709c --- /dev/null +++ b/scripts/automation/trex_control_plane/doc_stl/_templates/layout.html @@ -0,0 +1,17 @@ +{% extends "!layout.html" %} + +{% block footer %} +{{ super() }} +<script> + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + + ga('create', 'UA-75220362-1', 'auto'); + ga('send', 'pageview'); + +</script> +{% endblock %} + + diff --git a/scripts/automation/trex_control_plane/stf/examples/stf_example.py b/scripts/automation/trex_control_plane/stf/examples/stf_example.py new file mode 100755 index 00000000..f6ebffe7 --- /dev/null +++ b/scripts/automation/trex_control_plane/stf/examples/stf_example.py @@ -0,0 +1,53 @@ +import argparse +import stf_path +from trex_stf_lib.trex_client import CTRexClient + +# sample TRex stateless run +# assuming server daemon is running. + +def minimal_stateful_test(server): + print('Connecting to %s' % server) + trex_client = CTRexClient(server) + + print('Connected, start TRex') + trex_client.start_trex( + c = 1, + m = 700, + f = 'cap2/http_simple.yaml', + d = 5, + l = 1000, + trex_development = True, + ) + + print('Sample until end') + result = trex_client.sample_to_run_finish() + + print('Test results:') + print(result) + + print('TX by ports:') + tx_ptks_dict = result.get_last_value('trex-global.data', 'opackets-*') + print(' | '.join(['%s: %s' % (k.split('-')[-1], tx_ptks_dict[k]) for k in sorted(tx_ptks_dict.keys())])) + + print('RX by ports:') + rx_ptks_dict = result.get_last_value('trex-global.data', 'ipackets-*') + print(' | '.join(['%s: %s' % (k.split('-')[-1], rx_ptks_dict[k]) for k in sorted(rx_ptks_dict.keys())])) + + print('CPU utilization:') + print(result.get_value_list('trex-global.data.m_cpu_util')) + + #print('Dump of *latest* result sample, uncomment to see it all') + #print(result.get_latest_dump()) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description="Example for TRex Stateful, assuming server daemon is running.") + parser.add_argument('-s', '--server', + dest='server', + help='Remote trex address', + default='127.0.0.1', + type = str) + args = parser.parse_args() + + minimal_stateful_test(args.server) + diff --git a/scripts/automation/trex_control_plane/stf/examples/stf_path.py b/scripts/automation/trex_control_plane/stf/examples/stf_path.py new file mode 100755 index 00000000..bb401148 --- /dev/null +++ b/scripts/automation/trex_control_plane/stf/examples/stf_path.py @@ -0,0 +1,4 @@ +import sys + +# FIXME to the write path for trex_stf_lib +sys.path.insert(0, "../") diff --git a/scripts/automation/trex_control_plane/stf/CCustomLogger.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/CCustomLogger.py index ecf7d519..ecf7d519 100755 --- a/scripts/automation/trex_control_plane/stf/CCustomLogger.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/CCustomLogger.py diff --git a/scripts/automation/trex_control_plane/stf/__init__.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/__init__.py index 5a1da046..5a1da046 100755 --- a/scripts/automation/trex_control_plane/stf/__init__.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/__init__.py diff --git a/scripts/automation/trex_control_plane/stf/external_packages.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/external_packages.py index 7353c397..7353c397 100755 --- a/scripts/automation/trex_control_plane/stf/external_packages.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/external_packages.py diff --git a/scripts/automation/trex_control_plane/stf/general_utils.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/general_utils.py index d2521f02..d2521f02 100755 --- a/scripts/automation/trex_control_plane/stf/general_utils.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/general_utils.py diff --git a/scripts/automation/trex_control_plane/stf/outer_packages.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/outer_packages.py index 5e29f8d6..f8d50ce6 100755 --- a/scripts/automation/trex_control_plane/stf/outer_packages.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/outer_packages.py @@ -5,8 +5,8 @@ import os CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) -PARENT_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir, 'external_libs')) -SCRIPTS_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir, os.pardir, os.pardir, 'external_libs')) +PACKAGE_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir, os.pardir, 'external_libs')) +SCRIPTS_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir, os.pardir, os.pardir, os.pardir, 'external_libs')) CLIENT_MODULES = ['enum34-1.0.4', 'jsonrpclib-pelix-0.2.5', @@ -22,9 +22,9 @@ def import_module_list(ext_libs_path): raise Exception('Library %s is absent in path %s' % (p, ext_libs_path)) sys.path.insert(1, full_path) -if os.path.exists(PARENT_PATH): - import_module_list(PARENT_PATH) +if os.path.exists(PACKAGE_PATH): + import_module_list(PACKAGE_PATH) elif os.path.exists(SCRIPTS_PATH): import_module_list(SCRIPTS_PATH) else: - raise Exception('Could not find external libs in path: %s' % [PARENT_PATH, SCRIPTS_PATH]) + raise Exception('Could not find external libs in path: %s' % [PACKAGE_PATH, SCRIPTS_PATH]) diff --git a/scripts/automation/trex_control_plane/stf/text_opts.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/text_opts.py index 78a0ab1f..78a0ab1f 100755 --- a/scripts/automation/trex_control_plane/stf/text_opts.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/text_opts.py diff --git a/scripts/automation/trex_control_plane/stf/trex_client.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py index 919253d1..919253d1 100755 --- a/scripts/automation/trex_control_plane/stf/trex_client.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py diff --git a/scripts/automation/trex_control_plane/stf/trex_daemon_server.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_daemon_server.py index 9784d42a..9784d42a 100755 --- a/scripts/automation/trex_control_plane/stf/trex_daemon_server.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_daemon_server.py diff --git a/scripts/automation/trex_control_plane/stf/trex_exceptions.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_exceptions.py index 0de38411..0de38411 100755 --- a/scripts/automation/trex_control_plane/stf/trex_exceptions.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_exceptions.py diff --git a/scripts/automation/trex_control_plane/stf/trex_status.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_status.py index f132720c..f132720c 100644 --- a/scripts/automation/trex_control_plane/stf/trex_status.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_status.py diff --git a/scripts/automation/trex_control_plane/stf/trex_status_e.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_status_e.py index 79a25acc..79a25acc 100755 --- a/scripts/automation/trex_control_plane/stf/trex_status_e.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_status_e.py diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_imix.py b/scripts/automation/trex_control_plane/stl/examples/stl_imix.py index 56fd3cfd..ecf0fe69 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_imix.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_imix.py @@ -12,11 +12,12 @@ import argparse # and attach it to both sides and inject # at a certain rate for some time # finally it checks that all packets arrived -def imix_test (server): +def imix_test (server, mult): # create client c = STLClient(server = server) + passed = True @@ -27,6 +28,7 @@ def imix_test (server): # take all the ports c.reset() + # map ports - identify the routes table = stl_map_ports(c) @@ -37,7 +39,8 @@ def imix_test (server): print("Mapped ports to sides {0} <--> {1}".format(dir_0, dir_1)) # load IMIX profile - profile = STLProfile.load_py('../../../../stl/imix.py') + profile_file = os.path.join(stl_path.STL_PROFILES_PATH, 'imix.py') + profile = STLProfile.load_py(profile_file) streams = profile.get_streams() # add both streams to ports @@ -47,9 +50,8 @@ def imix_test (server): # clear the stats before injecting c.clear_stats() - # choose rate and start traffic for 10 seconds on 5 mpps + # choose rate and start traffic for 10 seconds duration = 10 - mult = "30%" print("Injecting {0} <--> {1} on total rate of '{2}' for {3} seconds".format(dir_0, dir_1, mult, duration)) c.start(ports = (dir_0 + dir_1), mult = mult, duration = duration, total = True) @@ -107,8 +109,13 @@ parser.add_argument('-s', '--server', help='Remote trex address', default='127.0.0.1', type = str) +parser.add_argument('-m', '--mult', + dest='mult', + help='Multiplier of traffic, see Stateless help for more info', + default='30%', + type = str) args = parser.parse_args() # run the tests -imix_test(args.server) +imix_test(args.server, args.mult) diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_imix_bidir.py b/scripts/automation/trex_control_plane/stl/examples/stl_imix_bidir.py index 05a8777b..05615aeb 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_imix_bidir.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_imix_bidir.py @@ -29,14 +29,15 @@ def imix_test (server): # take all the ports c.reset() - dir_0 = [0] - dir_1 = [1] + dir_0 = [0] + dir_1 = [1] print "Mapped ports to sides {0} <--> {1}".format(dir_0, dir_1) # load IMIX profile - profile1 = STLProfile.load_py('../../../../stl/imix.py', direction=0) - profile2 = STLProfile.load_py('../../../../stl/imix.py', direction=1) + profile_file = os.path.join(stl_path.STL_PROFILES_PATH, 'imix.py') + profile1 = STLProfile.load_py(profile_file, direction=0) + profile2 = STLProfile.load_py(profile_file, direction=1) stream1 = profile1.get_streams() stream2 = profile2.get_streams() diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_path.py b/scripts/automation/trex_control_plane/stl/examples/stl_path.py index 8f400d23..f1592571 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_path.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_path.py @@ -1,4 +1,7 @@ -import sys +import sys, os # FIXME to the write path for trex_stl_lib sys.path.insert(0, "../") + +STL_PROFILES_PATH = os.path.join(os.pardir, os.pardir, os.pardir, os.pardir, 'stl') + diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_profile.py b/scripts/automation/trex_control_plane/stl/examples/stl_profile.py index 3ae5f855..16d5238e 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_profile.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_profile.py @@ -18,8 +18,7 @@ def simple (): # prepare our ports c.reset(ports = my_ports) - - profile_file = "../../../../stl/udp_1pkt_simple.py" + profile_file = os.path.join(stl_path.STL_PROFILES_PATH, 'hlt', 'udp_1pkt_simple.py') try: profile = STLProfile.load(profile_file) diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_simple_console_like.py b/scripts/automation/trex_control_plane/stl/examples/stl_simple_console_like.py index 03909e65..1d4ef250 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_simple_console_like.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_simple_console_like.py @@ -29,9 +29,10 @@ def simple (): print(c.get_port_info(my_ports)) c.ping() + profile_file = os.path.join(stl_path.STL_PROFILES_PATH, 'udp_1pkt_simple.py') print("start") - c.start_line (" -f ../../../../stl/udp_1pkt_simple.py -m 10mpps --port 0 1 ") + c.start_line (" -f %s -m 10mpps --port 0 1 " % profile_file) time.sleep(2); c.pause_line("--port 0 1"); time.sleep(2); diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 9b6a9e25..7fbd2808 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -852,8 +852,26 @@ class STLClient(object): return RC_OK() - - + # remove all RX filters in a safe manner + def _remove_rx_filters (self, ports, rx_delay_ms): + + # get the enabled RX ports + rx_ports = [port_id for port_id in ports if self.ports[port_id].has_rx_enabled()] + + if not rx_ports: + return RC_OK() + + # block while any RX configured port has not yet have it's delay expired + while any([not self.ports[port_id].has_rx_delay_expired(rx_delay_ms) for port_id in rx_ports]): + time.sleep(0.01) + + # remove RX filters + rc = RC() + for port_id in rx_ports: + rc.add(self.ports[port_id].remove_rx_filters()) + + return rc + ################################# # ------ private methods ------ # @@ -1089,6 +1107,7 @@ class STLClient(object): for port_id, port_obj in self.ports.items() if port_obj.is_active()] + # get paused ports def get_paused_ports (self): return [port_id @@ -1336,7 +1355,7 @@ class STLClient(object): ports = self._validate_port_list(ports) self.acquire(ports, force = True) - self.stop(ports) + self.stop(ports, rx_delay_ms = 0) self.remove_all_streams(ports) self.clear_stats(ports) @@ -1537,7 +1556,7 @@ class STLClient(object): @__api_check(True) - def stop (self, ports = None): + def stop (self, ports = None, rx_delay_ms = 10): """ Stop port(s) @@ -1545,6 +1564,13 @@ class STLClient(object): ports : list Ports on which to execute the command + rx_delay_ms : int + time to wait until RX filters are removed + this value should reflect the time it takes + packets which were transmitted to arrive + to the destination. + after this time the RX filters will be removed + :raises: + :exc:`STLError` @@ -1563,6 +1589,11 @@ class STLClient(object): if not rc: raise STLError(rc) + # remove any RX filters + rc = self._remove_rx_filters(ports, rx_delay_ms = rx_delay_ms) + if not rc: + raise STLError(rc) + @__api_check(True) def update (self, ports = None, mult = "1", total = False, force = False): @@ -1784,7 +1815,7 @@ class STLClient(object): @__api_check(True) - def wait_on_traffic (self, ports = None, timeout = 60): + def wait_on_traffic (self, ports = None, timeout = 60, rx_delay_ms = 10): """ Block until traffic on specified port(s) has ended @@ -1795,6 +1826,14 @@ class STLClient(object): timeout : int timeout in seconds + rx_delay_ms : int + time to wait until RX filters are removed + this value should reflect the time it takes + packets which were transmitted to arrive + to the destination. + after this time the RX filters will be removed + + :raises: + :exc:`STLTimeoutError` - in case timeout has expired + :exe:'STLError' @@ -1813,6 +1852,11 @@ class STLClient(object): if time.time() > expr: raise STLTimeoutError(timeout) + # remove any RX filters + rc = self._remove_rx_filters(ports, rx_delay_ms = rx_delay_ms) + if not rc: + raise STLError(rc) + @__api_check(True) def set_port_attr (self, ports = None, promiscuous = None): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_ext.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_ext.py index d6d66ec3..ed0c393d 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_ext.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_ext.py @@ -9,7 +9,7 @@ TREX_STL_EXT_PATH = os.environ.get('TREX_STL_EXT_PATH') # take default if not TREX_STL_EXT_PATH or not os.path.exists(TREX_STL_EXT_PATH): CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) - TREX_STL_EXT_PATH = os.path.normpath(os.path.join(CURRENT_PATH, os.pardir, 'external_libs')) + TREX_STL_EXT_PATH = os.path.normpath(os.path.join(CURRENT_PATH, os.pardir, os.pardir, 'external_libs')) if not os.path.exists(TREX_STL_EXT_PATH): # ../../../../external_libs TREX_STL_EXT_PATH = os.path.normpath(os.path.join(CURRENT_PATH, os.pardir, os.pardir, os.pardir, os.pardir, 'external_libs')) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py index 47124114..049929ae 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py @@ -7,8 +7,8 @@ from .trex_stl_types import * from . import trex_stl_stats import base64 -import time import copy +from datetime import datetime, timedelta StreamOnPort = namedtuple('StreamOnPort', ['compiled_stream', 'metadata']) @@ -61,6 +61,8 @@ class Port(object): self.port_stats = trex_stl_stats.CPortStats(self) self.next_available_id = 1 + self.tx_stopped_ts = None + self.has_rx_streams = False def err(self, msg): @@ -239,6 +241,9 @@ class Port(object): 'rate' : streams_list[i].get_rate()} ret.add(RC_OK(data = stream_id)) + + self.has_rx_streams = self.has_rx_streams or streams_list[i].has_flow_stats() + else: ret.add(RC(*single_rc)) @@ -283,6 +288,9 @@ class Port(object): self.state = self.STATE_STREAMS if (len(self.streams) > 0) else self.STATE_IDLE + # recheck if any RX stats streams present on the port + self.has_rx_streams = any([stream.has_flow_stats() for stream in self.streams]) + return self.ok() if rc else self.err(rc.err()) @@ -305,6 +313,7 @@ class Port(object): self.streams = {} self.state = self.STATE_IDLE + self.has_rx_streams = False return self.ok() @@ -351,7 +360,7 @@ class Port(object): # stop traffic # with force ignores the cached state and sends the command def stop (self, force = False): - + if not self.is_acquired(): return self.err("port is not owned") @@ -360,7 +369,6 @@ class Port(object): if (self.state == self.STATE_IDLE) or (self.state == self.state == self.STATE_STREAMS): return self.ok() - params = {"handler": self.handler, "port_id": self.port_id} @@ -370,8 +378,56 @@ class Port(object): self.state = self.STATE_STREAMS + # timestamp for last tx + self.tx_stopped_ts = datetime.now() + + return self.ok() + + + # return True if port has any stream configured with RX stats + def has_rx_enabled (self): + return self.has_rx_streams + + + # return true if rx_delay_ms has passed since the last port stop + def has_rx_delay_expired (self, rx_delay_ms): + assert(self.has_rx_enabled()) + + # if active - it's not safe to remove RX filters + if self.is_active(): + return False + + # either no timestamp present or time has already passed + return not self.tx_stopped_ts or (datetime.now() - self.tx_stopped_ts) > timedelta(milliseconds = rx_delay_ms) + + + + def remove_rx_filters (self): + assert(self.has_rx_enabled()) + + if not self.is_acquired(): + return self.err("port is not owned") + + if self.state == self.STATE_DOWN: + return self.err("Unable to remove RX filters - port is down") + + if self.state == self.STATE_TX: + return self.err("Unable to remove RX filters - port is transmitting") + + if self.state == self.STATE_IDLE: + return self.ok() + + + params = {"handler": self.handler, + "port_id": self.port_id} + + rc = self.transmit("remove_rx_filters", params) + if rc.bad(): + return self.err(rc.err()) + return self.ok() + def pause (self): if not self.is_acquired(): @@ -597,6 +653,8 @@ class Port(object): ################# events handler ###################### def async_event_port_job_done (self): + # until thread is locked - order is important + self.tx_stopped_ts = datetime.now() self.state = self.STATE_STREAMS # rest of the events are used for TUI / read only sessions diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py index e66f917b..78f51ec6 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py @@ -430,6 +430,10 @@ class STLStream(object): return self.next + def has_flow_stats (self): + """ Return True if stream was configured with flow stats """ + return self.fields['flow_stats']['enabled'] + def get_pkt (self): """ Get packet as string """ return self.pkt @@ -828,6 +832,11 @@ class STLProfile(object): def __str__ (self): return '\n'.join([str(stream) for stream in self.streams]) + def is_pauseable (self): + return all([x.get_mode() == "Continuous" for x in (self.get_streams())]) + + def has_flow_stats (self): + return any([x.has_flow_stats() for x in self.get_streams()]) @staticmethod def load_yaml (yaml_file): |