summaryrefslogtreecommitdiffstats
path: root/scripts/automation
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/automation')
-rwxr-xr-xscripts/automation/regression/CPlatform.py15
-rwxr-xr-xscripts/automation/regression/aggregate_results.py63
-rw-r--r--scripts/automation/regression/cfg/client_cfg.yaml48
-rwxr-xr-xscripts/automation/regression/functional_tests/trex_cfg_creator_test.py25
-rw-r--r--scripts/automation/regression/setups/trex03/benchmark.yaml (renamed from scripts/automation/regression/setups/trex15/benchmark.yaml)0
-rw-r--r--scripts/automation/regression/setups/trex03/config.yaml (renamed from scripts/automation/regression/setups/trex15/config.yaml)2
-rw-r--r--scripts/automation/regression/setups/trex06/benchmark.yaml (renamed from scripts/automation/regression/setups/trex17/benchmark.yaml)2
-rw-r--r--scripts/automation/regression/setups/trex06/config.yaml (renamed from scripts/automation/regression/setups/trex17/config.yaml)2
-rw-r--r--scripts/automation/regression/setups/trex07/backup/benchmark.yaml244
-rw-r--r--scripts/automation/regression/setups/trex07/backup/config.yaml66
-rw-r--r--scripts/automation/regression/setups/trex07/benchmark.yaml173
-rw-r--r--scripts/automation/regression/setups/trex07/config.yaml29
-rw-r--r--scripts/automation/regression/setups/trex08/benchmark.yaml50
-rw-r--r--scripts/automation/regression/setups/trex09/benchmark.yaml6
-rw-r--r--scripts/automation/regression/setups/trex11/backup/benchmark.yaml155
-rw-r--r--scripts/automation/regression/setups/trex11/backup/config.yaml38
-rw-r--r--scripts/automation/regression/setups/trex11/benchmark.yaml67
-rw-r--r--scripts/automation/regression/setups/trex11/config.yaml23
-rw-r--r--scripts/automation/regression/setups/trex14/BU/benchmark.yaml245
-rw-r--r--scripts/automation/regression/setups/trex14/BU/config.yaml67
-rw-r--r--scripts/automation/regression/setups/trex14/benchmark.yaml21
-rw-r--r--scripts/automation/regression/setups/trex14/config.yaml15
-rw-r--r--scripts/automation/regression/stateful_tests/trex_client_cfg_test.py52
-rwxr-xr-xscripts/automation/regression/stateful_tests/trex_general_test.py20
-rw-r--r--scripts/automation/regression/stateless_tests/stl_client_test.py20
-rw-r--r--scripts/automation/regression/stateless_tests/stl_performance_test.py4
-rw-r--r--scripts/automation/regression/stateless_tests/stl_rx_test.py23
-rw-r--r--scripts/automation/regression/trex.py2
-rwxr-xr-xscripts/automation/regression/trex_unit_test.py36
-rwxr-xr-xscripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py4
-rwxr-xr-xscripts/automation/trex_control_plane/stl/console/trex_console.py50
-rw-r--r--scripts/automation/trex_control_plane/stl/console/trex_tui.py6
-rw-r--r--scripts/automation/trex_control_plane/stl/examples/stl_imix_bidir_update.py123
-rw-r--r--scripts/automation/trex_control_plane/stl/services/scapy_server/field_engine.json269
-rwxr-xr-xscripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py209
-rw-r--r--scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py3
-rw-r--r--scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py45
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py61
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py919
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py47
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py87
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py426
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py255
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py23
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py60
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py30
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py22
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py252
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py13
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/zipmsg.py16
50 files changed, 3816 insertions, 617 deletions
diff --git a/scripts/automation/regression/CPlatform.py b/scripts/automation/regression/CPlatform.py
index 0017e7db..606235a6 100755
--- a/scripts/automation/regression/CPlatform.py
+++ b/scripts/automation/regression/CPlatform.py
@@ -499,6 +499,8 @@ class CPlatform(object):
client_net_next_hop = misc_methods.get_single_net_client_addr(dual_if.server_if.get_ipv6_addr(), {'7':1}, ip_type = 'ipv6' )
server_net_next_hop = misc_methods.get_single_net_client_addr(dual_if.client_if.get_ipv6_addr(), {'7':1}, ip_type = 'ipv6' )
+ client_net_next_hop_v4 = misc_methods.get_single_net_client_addr(dual_if.server_if.get_ipv4_addr() )
+ server_net_next_hop_v4 = misc_methods.get_single_net_client_addr(dual_if.client_if.get_ipv4_addr() )
client_if_command_set.append ('{mode}ipv6 enable'.format(mode = unconfig_str))
@@ -526,12 +528,23 @@ class CPlatform(object):
next_hop = server_net_next_hop,
intf = dual_if.client_if.get_name(),
dest_mac = dual_if.client_if.get_ipv6_dest_mac()))
+ # For latency packets (which are IPv4), we need to configure also static ARP
+ conf_t_command_set.append('{mode}arp {next_hop} {dest_mac} arpa'.format(
+ mode = unconfig_str,
+ next_hop = server_net_next_hop_v4,
+ dest_mac = dual_if.client_if.get_ipv6_dest_mac()))
+
if dual_if.server_if.get_ipv6_dest_mac():
conf_t_command_set.append('{mode}ipv6 neighbor {next_hop} {intf} {dest_mac}'.format(
mode = unconfig_str,
- next_hop = client_net_next_hop,
+ next_hop = client_net_next_hop,
intf = dual_if.server_if.get_name(),
dest_mac = dual_if.server_if.get_ipv6_dest_mac()))
+ # For latency packets (which are IPv4), we need to configure also static ARP
+ conf_t_command_set.append('{mode}arp {next_hop} {dest_mac} arpa'.format(
+ mode = unconfig_str,
+ next_hop = client_net_next_hop_v4,
+ dest_mac = dual_if.server_if.get_ipv6_dest_mac()))
conf_t_command_set.append('{mode}route-map {pre}_{p1}_to_{p2} permit 10'.format(
mode = unconfig_str,
diff --git a/scripts/automation/regression/aggregate_results.py b/scripts/automation/regression/aggregate_results.py
index c7c61ea6..130e0545 100755
--- a/scripts/automation/regression/aggregate_results.py
+++ b/scripts/automation/regression/aggregate_results.py
@@ -8,10 +8,8 @@ import sys, os
from collections import OrderedDict
import copy
import datetime, time
-try:
- import cPickle as pickle
-except:
- import pickle
+import traceback
+import yaml
import subprocess, shlex
from ansi2html import Ansi2HTMLConverter
@@ -25,6 +23,15 @@ FUNCTIONAL_CATEGORY = 'Functional' # how to display those categories
ERROR_CATEGORY = 'Error'
+def try_write(file, text):
+ try:
+ file.write(text)
+ except:
+ try:
+ file.write(text.encode('utf-8'))
+ except:
+ file.write(text.decode('utf-8'))
+
def pad_tag(text, tag):
return '<%s>%s</%s>' % (tag, text, tag)
@@ -256,6 +263,7 @@ if __name__ == '__main__':
build_url = os.environ.get('BUILD_URL')
build_id = os.environ.get('BUILD_ID')
trex_repo = os.environ.get('TREX_CORE_REPO')
+ last_commit_info_file = os.environ.get('LAST_COMMIT_INFO')
python_ver = os.environ.get('PYTHON_VER')
if not scenario:
print('Warning: no environment variable SCENARIO, using default')
@@ -283,19 +291,25 @@ if __name__ == '__main__':
trex_last_commit_info = ''
trex_last_commit_hash = trex_info_dict.get('Git SHA')
- if trex_last_commit_hash and trex_repo:
+ if last_commit_info_file and os.path.exists(last_commit_info_file):
+ with open(last_commit_info_file) as f:
+ trex_last_commit_info = f.read().strip().replace('\n', '<br>\n')
+ elif trex_last_commit_hash and trex_repo:
try:
- print('Getting TRex commit with hash %s' % trex_last_commit_hash)
- command = 'git --git-dir %s show %s --quiet' % (trex_repo, trex_last_commit_hash)
+ command = 'git show %s -s' % trex_last_commit_hash
print('Executing: %s' % command)
- proc = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- (trex_last_commit_info, stderr) = proc.communicate()
- print('Stdout:\n\t' + trex_last_commit_info.replace('\n', '\n\t'))
- print('Stderr:', stderr)
- print('Return code:', proc.returncode)
- trex_last_commit_info = trex_last_commit_info.replace('\n', '<br>')
+ proc = subprocess.Popen(shlex.split(command), stdout = subprocess.PIPE, stderr = subprocess.STDOUT, cwd = trex_repo)
+ (stdout, stderr) = proc.communicate()
+ stdout = stdout.decode('utf-8', errors = 'replace')
+ print('Stdout:\n\t' + stdout.replace('\n', '\n\t'))
+ if stderr or proc.returncode:
+ print('Return code: %s' % proc.returncode)
+ trex_last_commit_info = stdout.replace('\n', '<br>\n')
except Exception as e:
+ traceback.print_exc()
print('Error getting last commit: %s' % e)
+ else:
+ print('Could not find info about commit!')
##### get xmls: report_<setup name>.xml
@@ -520,7 +534,7 @@ if __name__ == '__main__':
# save html
with open(args.output_htmlfile, 'w') as f:
print('Writing output file: %s' % args.output_htmlfile)
- f.write(html_output)
+ try_write(f, html_output)
html_output = None
# mail report (only error tests, expanded)
@@ -596,7 +610,7 @@ if __name__ == '__main__':
else:
mail_output += add_category_of_tests(ERROR_CATEGORY, error_tests, expanded=True)
else:
- mail_output += '<table><tr style="font-size:120;color:green;font-family:arial"><td>☺</td><td style="font-size:20">All passed.</td></tr></table>\n'
+ mail_output += u'<table><tr style="font-size:120;color:green;font-family:arial"><td>☺</td><td style="font-size:20">All passed.</td></tr></table>\n'
mail_output += '\n</body>\n</html>'
##### save outputs
@@ -605,17 +619,17 @@ if __name__ == '__main__':
# mail content
with open(args.output_mailfile, 'w') as f:
print('Writing output file: %s' % args.output_mailfile)
- f.write(mail_output)
+ try_write(f, mail_output)
# build status
category_dict_status = {}
if os.path.exists(args.build_status_file):
print('Reading: %s' % args.build_status_file)
- with open(args.build_status_file, 'rb') as f:
+ with open(args.build_status_file, 'r') as f:
try:
- category_dict_status = pickle.load(f)
+ category_dict_status = yaml.safe_load(f.read())
except Exception as e:
- print('Error during pickle load: %s' % e)
+ print('Error during YAML load: %s' % e)
if type(category_dict_status) is not dict:
print('%s is corrupt, truncating' % args.build_status_file)
category_dict_status = {}
@@ -635,15 +649,15 @@ if __name__ == '__main__':
current_status = 'Fixed'
category_dict_status[scenario] = current_status
- with open(args.build_status_file, 'wb') as f:
+ with open(args.build_status_file, 'w') as f:
print('Writing output file: %s' % args.build_status_file)
- pickle.dump(category_dict_status, f)
+ yaml.dump(category_dict_status, f)
# last successful commit
- if (current_status in ('Successful', 'Fixed')) and trex_last_commit_hash and jobs_list > 0 and scenario == 'nightly':
+ if (current_status in ('Successful', 'Fixed')) and trex_last_commit_hash and len(jobs_list) > 0 and scenario == 'nightly':
with open(args.last_passed_commit, 'w') as f:
print('Writing output file: %s' % args.last_passed_commit)
- f.write(trex_last_commit_hash)
+ try_write(f, trex_last_commit_hash)
# mail title
mailtitle_output = scenario.capitalize()
@@ -653,7 +667,8 @@ if __name__ == '__main__':
with open(args.output_titlefile, 'w') as f:
print('Writing output file: %s' % args.output_titlefile)
- f.write(mailtitle_output)
+ try_write(f, mailtitle_output)
# exit
+ print('Status: %s' % current_status)
sys.exit(exit_status)
diff --git a/scripts/automation/regression/cfg/client_cfg.yaml b/scripts/automation/regression/cfg/client_cfg.yaml
new file mode 100644
index 00000000..5c3d19ef
--- /dev/null
+++ b/scripts/automation/regression/cfg/client_cfg.yaml
@@ -0,0 +1,48 @@
+#vlan: true
+vlan: false
+
+groups:
+
+- ip_start : 16.0.0.1
+ ip_end : 16.0.1.255
+ initiator :
+ next_hop: 1.1.1.1
+ src_ip : 1.1.1.2
+ responder :
+ next_hop: 1.1.2.1
+ src_ip : 1.1.2.2
+
+ count : 1
+
+- ip_start : 17.0.0.1
+ ip_end : 17.0.1.255
+ initiator :
+ next_hop: 1.1.3.1
+ src_ip : 1.1.3.2
+ responder :
+ next_hop: 1.1.4.1
+ src_ip : 1.1.4.2
+
+ count : 1
+
+- ip_start : 18.0.0.1
+ ip_end : 18.0.1.255
+ initiator :
+ next_hop: 1.1.5.1
+ src_ip : 1.1.5.2
+ responder :
+ next_hop: 1.1.6.1
+ src_ip : 1.1.6.2
+
+ count : 1
+
+- ip_start : 19.0.0.1
+ ip_end : 19.0.1.255
+ initiator :
+ next_hop: 1.1.7.1
+ src_ip : 1.1.7.2
+ responder :
+ next_hop: 1.1.8.1
+ src_ip : 1.1.8.2
+
+ count : 1
diff --git a/scripts/automation/regression/functional_tests/trex_cfg_creator_test.py b/scripts/automation/regression/functional_tests/trex_cfg_creator_test.py
index 5ff6b318..66cb666c 100755
--- a/scripts/automation/regression/functional_tests/trex_cfg_creator_test.py
+++ b/scripts/automation/regression/functional_tests/trex_cfg_creator_test.py
@@ -25,7 +25,7 @@ def compare_lines(golden, output):
raise CompareLinesNumDiff('Number of lines on golden is: %s, in output: %s\nGolden:\n%s\nGenerated:\n%s\n' % (len(golden_lines), len(output_lines), golden, output))
for line_num, (golden_line, output_line) in enumerate(zip(golden_lines, output_lines)):
if golden_line != output_line:
- raise CompareLinesDiff('Produced YAML differs from golden at line %s.Golden: %s <-> Output: %s' % (line_num + 1, golden_line, output_line))
+ raise CompareLinesDiff('Produced YAML differs from golden at line %s.\nGolden: %s <-> Output: %s' % (line_num + 1, golden_line, output_line))
def create_config(cpu_topology, interfaces, *args, **kwargs):
config = ConfigCreator(cpu_topology, interfaces, *args, **kwargs)
@@ -112,7 +112,7 @@ class TRexCfgCreator_Test:
latency_thread_id: 1
dual_if:
- socket: 0
- threads: [2]
+ threads: [2,3,4]
'''
output = create_config(cpu_topology, interfaces)
verify_master_core0(output)
@@ -308,16 +308,16 @@ class TRexCfgCreator_Test:
platform:
master_thread_id: 0
- latency_thread_id: 16
+ latency_thread_id: 12
dual_if:
- socket: 0
- threads: [1,17,2,18,3,19,4]
+ threads: [1,2,3,16,17,18,19]
- socket: 1
- threads: [8,24,9,25,10,26,11]
+ threads: [8,9,10,11,24,25,26]
- socket: 0
- threads: [20,5,21,6,22,7,23]
+ threads: [4,5,6,7,20,21,22]
'''
output = create_config(cpu_topology, interfaces)
verify_master_core0(output)
@@ -446,10 +446,10 @@ class TRexCfgCreator_Test:
latency_thread_id: 31
dual_if:
- socket: 0
- threads: [1,17,2,18,3,19,4,20,5,21,6,22,7,23,16]
+ threads: [1,2,3,4,5,6,7,16,17,18,19,20,21,22,23]
- socket: 1
- threads: [8,24,9,25,10,26,11,27,12,28,13,29,14,30,15]
+ threads: [8,9,10,11,12,13,14,15,24,25,26,27,28,29,30]
'''
output = create_config(cpu_topology, interfaces)
verify_master_core0(output)
@@ -575,13 +575,13 @@ class TRexCfgCreator_Test:
platform:
master_thread_id: 0
- latency_thread_id: 16
+ latency_thread_id: 8
dual_if:
- socket: 0
- threads: [1,17,2,18,3,19,4]
+ threads: [1,2,3,16,17,18,19]
- socket: 0
- threads: [20,5,21,6,22,7,23]
+ threads: [4,5,6,7,20,21,22]
'''
output = create_config(cpu_topology, interfaces)
verify_master_core0(output)
@@ -694,5 +694,6 @@ class TRexCfgCreator_Test:
@classmethod
def tearDownClass(cls):
- sys.path.remove(CTRexScenario.scripts_path)
+ if CTRexScenario.scripts_path in sys.path:
+ sys.path.remove(CTRexScenario.scripts_path)
del sys.modules['dpdk_setup_ports']
diff --git a/scripts/automation/regression/setups/trex15/benchmark.yaml b/scripts/automation/regression/setups/trex03/benchmark.yaml
index b366b3fb..b366b3fb 100644
--- a/scripts/automation/regression/setups/trex15/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex03/benchmark.yaml
diff --git a/scripts/automation/regression/setups/trex15/config.yaml b/scripts/automation/regression/setups/trex03/config.yaml
index c5fc3b22..17c4c91e 100644
--- a/scripts/automation/regression/setups/trex15/config.yaml
+++ b/scripts/automation/regression/setups/trex03/config.yaml
@@ -34,6 +34,6 @@
# expected_bw - the "golden" bandwidth (in Gbps) results planned on receiving from the test
trex:
- hostname : csi-trex-15
+ hostname : csi-trex-03
cores : 1
modes : [loopback, virt_nics, VM]
diff --git a/scripts/automation/regression/setups/trex17/benchmark.yaml b/scripts/automation/regression/setups/trex06/benchmark.yaml
index 8bc9d29c..2f51a3fd 100644
--- a/scripts/automation/regression/setups/trex17/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex06/benchmark.yaml
@@ -140,7 +140,7 @@ test_CPU_benchmark:
bw_per_core : 1
- name : stl/pcap.py
- kwargs : {ipg_usec: 2, loop_count: 0}
+ kwargs : {ipg_usec: 3, loop_count: 0}
cpu_util : 1
bw_per_core : 1
diff --git a/scripts/automation/regression/setups/trex17/config.yaml b/scripts/automation/regression/setups/trex06/config.yaml
index 7ad6a20a..da0eb2dd 100644
--- a/scripts/automation/regression/setups/trex17/config.yaml
+++ b/scripts/automation/regression/setups/trex06/config.yaml
@@ -34,6 +34,6 @@
# expected_bw - the "golden" bandwidth (in Gbps) results planned on receiving from the test
trex:
- hostname : csi-trex-17
+ hostname : csi-trex-06
cores : 1
modes : [loopback, virt_nics, VM]
diff --git a/scripts/automation/regression/setups/trex07/backup/benchmark.yaml b/scripts/automation/regression/setups/trex07/backup/benchmark.yaml
new file mode 100644
index 00000000..0dc340b0
--- /dev/null
+++ b/scripts/automation/regression/setups/trex07/backup/benchmark.yaml
@@ -0,0 +1,244 @@
+###############################################################
+#### TRex benchmark configuration file ####
+###############################################################
+
+#### common templates ###
+
+stat_route_dict: &stat_route_dict
+ clients_start : 16.0.0.1
+ servers_start : 48.0.0.1
+ dual_port_mask : 1.0.0.0
+ client_destination_mask : 255.0.0.0
+ server_destination_mask : 255.0.0.0
+
+nat_dict: &nat_dict
+ clients_net_start : 16.0.0.0
+ client_acl_wildcard_mask : 0.0.0.255
+ dual_port_mask : 1.0.0.0
+ pool_start : 200.0.0.0
+ pool_netmask : 255.255.255.0
+
+
+### stateful ###
+
+test_jumbo:
+ multiplier : 17
+ cores : 1
+ bw_per_core : 543.232
+
+
+test_routing_imix:
+ multiplier : 10
+ cores : 1
+ bw_per_core : 34.128
+
+
+test_routing_imix_64:
+ multiplier : 430
+ cores : 1
+ bw_per_core : 5.893
+
+
+test_static_routing_imix: &test_static_routing_imix
+ stat_route_dict : *stat_route_dict
+ multiplier : 8
+ cores : 1
+ bw_per_core : 34.339
+
+test_static_routing_imix_asymmetric: *test_static_routing_imix
+
+
+test_ipv6_simple:
+ multiplier : 9
+ cores : 2
+ bw_per_core : 19.064
+
+
+test_nat_simple_mode1: &test_nat_simple
+ stat_route_dict : *stat_route_dict
+ nat_dict : *nat_dict
+ multiplier : 6000
+ cores : 1
+ nat_opened : 500000
+ allow_timeout_dev : True
+ bw_per_core : 44.445
+
+test_nat_simple_mode2: *test_nat_simple
+
+test_nat_simple_mode3: *test_nat_simple
+
+test_nat_learning: *test_nat_simple
+
+
+test_nbar_simple:
+ multiplier : 7.5
+ cores : 2
+ bw_per_core : 17.174
+ nbar_classification:
+ rtp : 32.57
+ http : 30.25
+ oracle_sqlnet : 11.23
+ exchange : 10.80
+ citrix : 5.62
+ rtsp : 2.84
+ dns : 1.95
+ smtp : 0.57
+ pop3 : 0.36
+ ssl : 0.17
+ sctp : 0.13
+ sip : 0.09
+ unknown : 3.41
+
+
+test_rx_check_http: &rx_http
+ multiplier : 15000
+ cores : 1
+ rx_sample_rate : 16
+ bw_per_core : 39.560
+
+test_rx_check_http_ipv6:
+ << : *rx_http
+ bw_per_core : 49.237
+
+test_rx_check_http_negative_disabled:
+ << : *rx_http
+ stat_route_dict : *stat_route_dict
+ nat_dict : *nat_dict
+
+
+test_rx_check_sfr: &rx_sfr
+ multiplier : 10
+ cores : 3
+ rx_sample_rate : 16
+ bw_per_core : 16.082
+
+test_rx_check_sfr_ipv6:
+ << : *rx_sfr
+ bw_per_core : 19.198
+
+
+
+### stateless ###
+
+test_CPU_benchmark:
+ profiles:
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# causes queue full
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 64, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# not enough memory + queue full if memory increase
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 9000, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/imix.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/pcap.py
+ kwargs : {ipg_usec: 2, loop_count: 0}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/hlt/hlt_udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+
diff --git a/scripts/automation/regression/setups/trex07/backup/config.yaml b/scripts/automation/regression/setups/trex07/backup/config.yaml
new file mode 100644
index 00000000..db6e9bf8
--- /dev/null
+++ b/scripts/automation/regression/setups/trex07/backup/config.yaml
@@ -0,0 +1,66 @@
+################################################################
+#### TRex nightly test configuration file ####
+################################################################
+
+
+### TRex configuration:
+# hostname - can be DNS name or IP for the TRex machine for ssh to the box
+# password - root password for TRex machine
+# is_dual - should the TRex inject with -p ?
+# version_path - path to the TRex version and executable
+# cores - how many cores should be used
+# latency - rate of latency packets injected by the TRex
+# modes - list of modes (tagging) of this setup (loopback etc.)
+# * loopback - Trex works via loopback. Router and TFTP configurations may be skipped.
+# * VM - Virtual OS (accept low CPU utilization in tests, latency can get spikes)
+# * virt_nics - NICs are virtual (VMXNET3 etc.)
+
+### Router configuration:
+# hostname - the router hostname as apears in ______# cli prefix
+# ip_address - the router's ip that can be used to communicate with
+# image - the desired imaged wished to be loaded as the router's running config
+# line_password - router password when access via Telent
+# en_password - router password when changing to "enable" mode
+# interfaces - an array of client-server pairs, representing the interfaces configurations of the router
+# configurations - an array of configurations that could possibly loaded into the router during the test.
+# The "clean" configuration is a mandatory configuration the router will load with to run the basic test bench
+
+### TFTP configuration:
+# hostname - the tftp hostname
+# ip_address - the tftp's ip address
+# images_path - the tftp's relative path in which the router's images are located
+
+### Test_misc configuration:
+# expected_bw - the "golden" bandwidth (in Gbps) results planned on receiving from the test
+
+trex:
+ hostname : csi-trex-07
+ cores : 4
+
+router:
+ model : ASR1001x
+ hostname : csi-asr-01
+ ip_address : 10.56.216.120
+ image : asr1001x-universalk9.03.13.02.S.154-3.S2-ext.SPA.bin
+ line_password : cisco
+ en_password : cisco
+ mgmt_interface : GigabitEthernet0
+ clean_config : clean_config.cfg
+ intf_masking : 255.255.255.0
+ ipv6_mask : 64
+ interfaces :
+ - client :
+ name : Te0/0/0
+ src_mac_addr : 0000.0001.0002
+ dest_mac_addr : 0000.0001.0001
+ server :
+ name : Te0/0/1
+ src_mac_addr : 0000.0002.0002
+ dest_mac_addr : 0000.0002.0001
+ vrf_name : null
+
+tftp:
+ hostname : ats-asr-srv-1
+ ip_address : 10.56.217.7
+ root_dir : /scratch/tftp/
+ images_path : /asr1001x/
diff --git a/scripts/automation/regression/setups/trex07/benchmark.yaml b/scripts/automation/regression/setups/trex07/benchmark.yaml
index 0dc340b0..6e861836 100644
--- a/scripts/automation/regression/setups/trex07/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex07/benchmark.yaml
@@ -4,120 +4,57 @@
#### common templates ###
-stat_route_dict: &stat_route_dict
- clients_start : 16.0.0.1
- servers_start : 48.0.0.1
- dual_port_mask : 1.0.0.0
- client_destination_mask : 255.0.0.0
- server_destination_mask : 255.0.0.0
-
-nat_dict: &nat_dict
- clients_net_start : 16.0.0.0
- client_acl_wildcard_mask : 0.0.0.255
- dual_port_mask : 1.0.0.0
- pool_start : 200.0.0.0
- pool_netmask : 255.255.255.0
-
-
-### stateful ###
-
test_jumbo:
- multiplier : 17
- cores : 1
- bw_per_core : 543.232
+ multiplier : 120
+ cores : 2
+ bw_per_core : 962.464
test_routing_imix:
- multiplier : 10
- cores : 1
- bw_per_core : 34.128
+ multiplier : 60
+ cores : 4
+ bw_per_core : 48.130
test_routing_imix_64:
- multiplier : 430
- cores : 1
- bw_per_core : 5.893
-
+ multiplier : 4000
+ cores : 7
+ bw_per_core : 12.699
-test_static_routing_imix: &test_static_routing_imix
- stat_route_dict : *stat_route_dict
- multiplier : 8
- cores : 1
- bw_per_core : 34.339
-test_static_routing_imix_asymmetric: *test_static_routing_imix
+test_static_routing_imix_asymmetric:
+ multiplier : 50
+ cores : 3
+ bw_per_core : 50.561
test_ipv6_simple:
- multiplier : 9
- cores : 2
- bw_per_core : 19.064
-
-
-test_nat_simple_mode1: &test_nat_simple
- stat_route_dict : *stat_route_dict
- nat_dict : *nat_dict
- multiplier : 6000
- cores : 1
- nat_opened : 500000
- allow_timeout_dev : True
- bw_per_core : 44.445
-
-test_nat_simple_mode2: *test_nat_simple
-
-test_nat_simple_mode3: *test_nat_simple
-
-test_nat_learning: *test_nat_simple
-
-
-test_nbar_simple:
- multiplier : 7.5
- cores : 2
- bw_per_core : 17.174
- nbar_classification:
- rtp : 32.57
- http : 30.25
- oracle_sqlnet : 11.23
- exchange : 10.80
- citrix : 5.62
- rtsp : 2.84
- dns : 1.95
- smtp : 0.57
- pop3 : 0.36
- ssl : 0.17
- sctp : 0.13
- sip : 0.09
- unknown : 3.41
+ multiplier : 50
+ cores : 7
+ bw_per_core : 19.5
test_rx_check_http: &rx_http
- multiplier : 15000
- cores : 1
- rx_sample_rate : 16
- bw_per_core : 39.560
+ multiplier : 99000
+ cores : 7
+ rx_sample_rate : 128
+ bw_per_core : 49.464
test_rx_check_http_ipv6:
<< : *rx_http
bw_per_core : 49.237
-test_rx_check_http_negative_disabled:
- << : *rx_http
- stat_route_dict : *stat_route_dict
- nat_dict : *nat_dict
-
-
test_rx_check_sfr: &rx_sfr
- multiplier : 10
- cores : 3
- rx_sample_rate : 16
- bw_per_core : 16.082
+ multiplier : 35
+ cores : 7
+ rx_sample_rate : 128
+ bw_per_core : 20.871
test_rx_check_sfr_ipv6:
<< : *rx_sfr
bw_per_core : 19.198
-
### stateless ###
test_CPU_benchmark:
@@ -178,10 +115,10 @@ test_CPU_benchmark:
cpu_util : 1
bw_per_core : 1
- - name : stl/udp_for_benchmarks.py
- kwargs : {packet_len: 9000, stream_count: 100}
- cpu_util : 1
- bw_per_core : 1
+ #- name : stl/udp_for_benchmarks.py
+ # kwargs : {packet_len: 9000, stream_count: 100}
+ # cpu_util : 1
+ # bw_per_core : 1
# not enough memory + queue full if memory increase
# - name : stl/udp_for_benchmarks.py
@@ -241,4 +178,56 @@ test_CPU_benchmark:
cpu_util : 1
bw_per_core : 1
-
+test_performance_vm_single_cpu:
+ cfg:
+ mult : "90%"
+ mpps_per_core_golden :
+ min: 9.6
+ max: 13.3
+
+
+test_performance_vm_single_cpu_cached:
+ cfg:
+ mult : "10%"
+ mpps_per_core_golden :
+ min: 16.0
+ max: 25.0
+
+
+
+test_performance_syn_attack_single_cpu:
+ cfg:
+ mult : "90%"
+ mpps_per_core_golden :
+ min: 9.0
+ max: 14.0
+
+test_performance_vm_multi_cpus:
+ cfg:
+ core_count : 7
+ mult : "90%"
+ mpps_per_core_golden :
+ min: 8.5
+ max: 12.0
+
+
+test_performance_vm_multi_cpus_cached:
+ cfg:
+ core_count : 7
+ mult : "35%"
+ mpps_per_core_golden :
+ min: 9.0
+ max: 15.0
+
+test_performance_syn_attack_multi_cpus:
+ cfg:
+ core_count : 7
+ mult : "90%"
+ mpps_per_core_golden :
+ min: 8.0
+ max: 16.0
+
+
+test_all_profiles :
+ mult : "5%"
+
diff --git a/scripts/automation/regression/setups/trex07/config.yaml b/scripts/automation/regression/setups/trex07/config.yaml
index db6e9bf8..10472c4f 100644
--- a/scripts/automation/regression/setups/trex07/config.yaml
+++ b/scripts/automation/regression/setups/trex07/config.yaml
@@ -35,32 +35,7 @@
trex:
hostname : csi-trex-07
- cores : 4
+ cores : 8
+ modes : ['loopback']
-router:
- model : ASR1001x
- hostname : csi-asr-01
- ip_address : 10.56.216.120
- image : asr1001x-universalk9.03.13.02.S.154-3.S2-ext.SPA.bin
- line_password : cisco
- en_password : cisco
- mgmt_interface : GigabitEthernet0
- clean_config : clean_config.cfg
- intf_masking : 255.255.255.0
- ipv6_mask : 64
- interfaces :
- - client :
- name : Te0/0/0
- src_mac_addr : 0000.0001.0002
- dest_mac_addr : 0000.0001.0001
- server :
- name : Te0/0/1
- src_mac_addr : 0000.0002.0002
- dest_mac_addr : 0000.0002.0001
- vrf_name : null
-tftp:
- hostname : ats-asr-srv-1
- ip_address : 10.56.217.7
- root_dir : /scratch/tftp/
- images_path : /asr1001x/
diff --git a/scripts/automation/regression/setups/trex08/benchmark.yaml b/scripts/automation/regression/setups/trex08/benchmark.yaml
index 8f83e8f9..935b3e55 100644
--- a/scripts/automation/regression/setups/trex08/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex08/benchmark.yaml
@@ -179,3 +179,53 @@ test_CPU_benchmark:
bw_per_core : 1
+test_performance_vm_single_cpu:
+ cfg:
+ mult : "90%"
+ mpps_per_core_golden :
+ min: 15.1
+ max: 20.3
+
+
+test_performance_vm_single_cpu_cached:
+ cfg:
+ mult : "10%"
+ mpps_per_core_golden :
+ min: 29.1
+ max: 32.0
+
+
+
+test_performance_syn_attack_single_cpu:
+ cfg:
+ mult : "90%"
+ mpps_per_core_golden :
+ min: 13.2
+ max: 15.0
+
+test_performance_vm_multi_cpus:
+ cfg:
+ core_count : 7
+ mult : "40%"
+ mpps_per_core_golden :
+ min: 15.0
+ max: 20.0
+
+
+test_performance_vm_multi_cpus_cached:
+ cfg:
+ core_count : 7
+ mult : "40%"
+ mpps_per_core_golden :
+ min: 29.0
+ max: 34.0
+
+test_performance_syn_attack_multi_cpus:
+ cfg:
+ core_count : 7
+ mult : "40%"
+ mpps_per_core_golden :
+ min: 13.0
+ max: 17.0
+
+
diff --git a/scripts/automation/regression/setups/trex09/benchmark.yaml b/scripts/automation/regression/setups/trex09/benchmark.yaml
index d1f5f56c..50f08351 100644
--- a/scripts/automation/regression/setups/trex09/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex09/benchmark.yaml
@@ -187,7 +187,7 @@ test_performance_vm_single_cpu:
cfg:
mult : "90%"
mpps_per_core_golden :
- min: 16.2
+ min: 13.0
max: 17.3
@@ -195,7 +195,7 @@ test_performance_vm_single_cpu_cached:
cfg:
mult : "90%"
mpps_per_core_golden :
- min: 29.5
+ min: 28.5
max: 31.2
@@ -221,7 +221,7 @@ test_performance_vm_multi_cpus_cached:
core_count : 2
mult : "90%"
mpps_per_core_golden :
- min: 28.8
+ min: 26.8
max: 29.5
test_performance_syn_attack_multi_cpus:
diff --git a/scripts/automation/regression/setups/trex11/backup/benchmark.yaml b/scripts/automation/regression/setups/trex11/backup/benchmark.yaml
new file mode 100644
index 00000000..b366b3fb
--- /dev/null
+++ b/scripts/automation/regression/setups/trex11/backup/benchmark.yaml
@@ -0,0 +1,155 @@
+################################################################
+#### TRex benchmark configuration file ####
+################################################################
+
+### stateful ###
+
+test_jumbo:
+ multiplier : 2.8
+ cores : 1
+ bw_per_core : 106.652
+
+
+test_routing_imix:
+ multiplier : 0.5
+ cores : 1
+ bw_per_core : 11.577
+
+
+test_routing_imix_64:
+ multiplier : 28
+ cores : 1
+ bw_per_core : 2.030
+
+
+test_static_routing_imix_asymmetric:
+ multiplier : 0.8
+ cores : 1
+ bw_per_core : 13.742
+
+
+
+### stateless ###
+
+test_CPU_benchmark:
+ profiles:
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# causes queue full
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 64, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# not enough memory + queue full if memory increase
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 9000, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/imix.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/pcap.py
+ kwargs : {ipg_usec: 4, loop_count: 0}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/hlt/hlt_udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+
diff --git a/scripts/automation/regression/setups/trex11/backup/config.yaml b/scripts/automation/regression/setups/trex11/backup/config.yaml
new file mode 100644
index 00000000..782b7542
--- /dev/null
+++ b/scripts/automation/regression/setups/trex11/backup/config.yaml
@@ -0,0 +1,38 @@
+################################################################
+#### TRex nightly test configuration file ####
+################################################################
+
+
+### TRex configuration:
+# hostname - can be DNS name or IP for the TRex machine for ssh to the box
+# password - root password for TRex machine
+# is_dual - should the TRex inject with -p ?
+# version_path - path to the TRex version and executable
+# cores - how many cores should be used
+# latency - rate of latency packets injected by the TRex
+# modes - list of modes (tagging) of this setup (loopback, virtual etc.)
+# * loopback - Trex works via loopback. Router and TFTP configurations may be skipped.
+# * virtual - virtual OS (accept low CPU utilization in tests)
+
+### Router configuration:
+# hostname - the router hostname as apears in ______# cli prefix
+# ip_address - the router's ip that can be used to communicate with
+# image - the desired imaged wished to be loaded as the router's running config
+# line_password - router password when access via Telent
+# en_password - router password when changing to "enable" mode
+# interfaces - an array of client-server pairs, representing the interfaces configurations of the router
+# configurations - an array of configurations that could possibly loaded into the router during the test.
+# The "clean" configuration is a mandatory configuration the router will load with to run the basic test bench
+
+### TFTP configuration:
+# hostname - the tftp hostname
+# ip_address - the tftp's ip address
+# images_path - the tftp's relative path in which the router's images are located
+
+### Test_misc configuration:
+# expected_bw - the "golden" bandwidth (in Gbps) results planned on receiving from the test
+
+trex:
+ hostname : csi-trex-11
+ cores : 1
+ modes : ['loopback', 'VM', 'virt_nics']
diff --git a/scripts/automation/regression/setups/trex11/benchmark.yaml b/scripts/automation/regression/setups/trex11/benchmark.yaml
index b366b3fb..5ebcdd55 100644
--- a/scripts/automation/regression/setups/trex11/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex11/benchmark.yaml
@@ -1,32 +1,58 @@
-################################################################
+###############################################################
#### TRex benchmark configuration file ####
-################################################################
+###############################################################
-### stateful ###
+#### common templates ###
-test_jumbo:
- multiplier : 2.8
- cores : 1
- bw_per_core : 106.652
+#test_jumbo:
+# multiplier : 2.8
+# cores : 1
+# bw_per_core : 962.464
test_routing_imix:
multiplier : 0.5
cores : 1
- bw_per_core : 11.577
+ bw_per_core : 48.130
test_routing_imix_64:
multiplier : 28
cores : 1
- bw_per_core : 2.030
+ bw_per_core : 12.699
test_static_routing_imix_asymmetric:
- multiplier : 0.8
+ multiplier : 0.5
+ cores : 1
+ bw_per_core : 50.561
+
+
+test_ipv6_simple:
+ multiplier : 0.5
+ cores : 1
+ bw_per_core : 19.5
+
+
+test_rx_check_http: &rx_http
+ multiplier : 1000
+ cores : 1
+ rx_sample_rate : 128
+ bw_per_core : 49.464
+
+test_rx_check_http_ipv6:
+ << : *rx_http
+ bw_per_core : 49.237
+
+test_rx_check_sfr: &rx_sfr
+ multiplier : 8
cores : 1
- bw_per_core : 13.742
+ rx_sample_rate : 128
+ bw_per_core : 20.9
+test_rx_check_sfr_ipv6:
+ << : *rx_sfr
+ bw_per_core : 23.9
### stateless ###
@@ -140,16 +166,21 @@ test_CPU_benchmark:
bw_per_core : 1
- name : stl/pcap.py
- kwargs : {ipg_usec: 4, loop_count: 0}
+ kwargs : {ipg_usec: 2, loop_count: 0}
cpu_util : 1
bw_per_core : 1
- - name : stl/udp_rand_len_9k.py
- cpu_util : 1
- bw_per_core : 1
+ #- name : stl/udp_rand_len_9k.py
+ # cpu_util : 1
+ # bw_per_core : 1
- - name : stl/hlt/hlt_udp_rand_len_9k.py
- cpu_util : 1
- bw_per_core : 1
+ #- name : stl/hlt/hlt_udp_rand_len_9k.py
+ # cpu_util : 1
+ # bw_per_core : 1
+
+test_all_profiles :
+ mult : "5%"
+ skip : ['udp_rand_len_9k.py','udp_inc_len_9k.py'] # due to VIC 9K defect trex-282
+
diff --git a/scripts/automation/regression/setups/trex11/config.yaml b/scripts/automation/regression/setups/trex11/config.yaml
index 782b7542..393c8749 100644
--- a/scripts/automation/regression/setups/trex11/config.yaml
+++ b/scripts/automation/regression/setups/trex11/config.yaml
@@ -4,15 +4,16 @@
### TRex configuration:
-# hostname - can be DNS name or IP for the TRex machine for ssh to the box
-# password - root password for TRex machine
-# is_dual - should the TRex inject with -p ?
-# version_path - path to the TRex version and executable
-# cores - how many cores should be used
-# latency - rate of latency packets injected by the TRex
-# modes - list of modes (tagging) of this setup (loopback, virtual etc.)
-# * loopback - Trex works via loopback. Router and TFTP configurations may be skipped.
-# * virtual - virtual OS (accept low CPU utilization in tests)
+# hostname - can be DNS name or IP for the TRex machine for ssh to the box
+# password - root password for TRex machine
+# is_dual - should the TRex inject with -p ?
+# version_path - path to the TRex version and executable
+# cores - how many cores should be used
+# latency - rate of latency packets injected by the TRex
+# modes - list of modes (tagging) of this setup (loopback etc.)
+# * loopback - Trex works via loopback. Router and TFTP configurations may be skipped.
+# * VM - Virtual OS (accept low CPU utilization in tests, latency can get spikes)
+# * virt_nics - NICs are virtual (VMXNET3 etc.)
### Router configuration:
# hostname - the router hostname as apears in ______# cli prefix
@@ -35,4 +36,6 @@
trex:
hostname : csi-trex-11
cores : 1
- modes : ['loopback', 'VM', 'virt_nics']
+ modes : ['loopback']
+
+
diff --git a/scripts/automation/regression/setups/trex14/BU/benchmark.yaml b/scripts/automation/regression/setups/trex14/BU/benchmark.yaml
new file mode 100644
index 00000000..04f13e79
--- /dev/null
+++ b/scripts/automation/regression/setups/trex14/BU/benchmark.yaml
@@ -0,0 +1,245 @@
+###############################################################
+#### TRex benchmark configuration file ####
+###############################################################
+
+#### common templates ###
+
+stat_route_dict: &stat_route_dict
+ clients_start : 16.0.0.1
+ servers_start : 48.0.0.1
+ dual_port_mask : 1.0.0.0
+ client_destination_mask : 255.0.0.0
+ server_destination_mask : 255.0.0.0
+
+nat_dict: &nat_dict
+ clients_net_start : 16.0.0.0
+ client_acl_wildcard_mask : 0.0.0.255
+ dual_port_mask : 1.0.0.0
+ pool_start : 200.0.0.0
+ pool_netmask : 255.255.255.0
+
+
+### stateful ###
+
+test_jumbo:
+ multiplier : 17
+ cores : 1
+ bw_per_core : 543.232
+
+
+test_routing_imix:
+ multiplier : 10
+ cores : 1
+ bw_per_core : 34.128
+
+
+test_routing_imix_64:
+ multiplier : 430
+ cores : 1
+ bw_per_core : 5.893
+
+
+test_static_routing_imix: &test_static_routing_imix
+ stat_route_dict : *stat_route_dict
+ multiplier : 8
+ cores : 1
+ bw_per_core : 34.339
+
+test_static_routing_imix_asymmetric: *test_static_routing_imix
+
+
+test_ipv6_simple:
+ multiplier : 9
+ cores : 2
+ bw_per_core : 19.064
+
+
+test_nat_simple_mode1: &test_nat_simple
+ stat_route_dict : *stat_route_dict
+ nat_dict : *nat_dict
+ multiplier : 6000
+ cores : 1
+ nat_opened : 500000
+ allow_timeout_dev : True
+ bw_per_core : 44.445
+
+test_nat_simple_mode2: *test_nat_simple
+
+test_nat_simple_mode3: *test_nat_simple
+
+test_nat_learning: *test_nat_simple
+
+
+test_nbar_simple:
+ multiplier : 7.5
+ cores : 2
+ bw_per_core : 17.174
+ nbar_classification:
+ http : 32.58
+ rtp-audio : 21.21
+ oracle_sqlnet : 11.41
+ exchange : 11.22
+ rtp : 11.2
+ citrix : 5.65
+ rtsp : 2.87
+ dns : 1.96
+ smtp : 0.57
+ pop3 : 0.37
+ ssl : 0.28
+ sctp : 0.13
+ sip : 0.09
+ unknown : 0.45
+
+
+test_rx_check_http: &rx_http
+ multiplier : 15000
+ cores : 1
+ rx_sample_rate : 16
+ bw_per_core : 39.560
+
+test_rx_check_http_ipv6:
+ << : *rx_http
+ bw_per_core : 49.237
+
+test_rx_check_http_negative_disabled:
+ << : *rx_http
+ stat_route_dict : *stat_route_dict
+ nat_dict : *nat_dict
+
+
+test_rx_check_sfr: &rx_sfr
+ multiplier : 10
+ cores : 3
+ rx_sample_rate : 16
+ bw_per_core : 16.082
+
+test_rx_check_sfr_ipv6:
+ << : *rx_sfr
+ bw_per_core : 19.198
+
+
+
+### stateless ###
+
+test_CPU_benchmark:
+ profiles:
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 64, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# causes queue full
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 64, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 10}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_for_benchmarks.py
+ kwargs : {packet_len: 9000, stream_count: 100}
+ cpu_util : 1
+ bw_per_core : 1
+
+# not enough memory + queue full if memory increase
+# - name : stl/udp_for_benchmarks.py
+# kwargs : {packet_len: 9000, stream_count: 1000}
+# cpu_util : 1
+# bw_per_core : 1
+
+ - name : stl/imix.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 64}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 128}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 256}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 512}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 1500}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 4000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_1pkt_tuple_gen.py
+ kwargs : {packet_len: 9000}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/pcap.py
+ kwargs : {ipg_usec: 2, loop_count: 0}
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+ - name : stl/hlt/hlt_udp_rand_len_9k.py
+ cpu_util : 1
+ bw_per_core : 1
+
+
diff --git a/scripts/automation/regression/setups/trex14/BU/config.yaml b/scripts/automation/regression/setups/trex14/BU/config.yaml
new file mode 100644
index 00000000..0fd6b70e
--- /dev/null
+++ b/scripts/automation/regression/setups/trex14/BU/config.yaml
@@ -0,0 +1,67 @@
+################################################################
+#### TRex nightly test configuration file ####
+################################################################
+
+
+### TRex configuration:
+# hostname - can be DNS name or IP for the TRex machine for ssh to the box
+# password - root password for TRex machine
+# is_dual - should the TRex inject with -p ?
+# version_path - path to the TRex version and executable
+# cores - how many cores should be used
+# latency - rate of latency packets injected by the TRex
+# modes - list of modes (tagging) of this setup (loopback etc.)
+# * loopback - Trex works via loopback. Router and TFTP configurations may be skipped.
+# * VM - Virtual OS (accept low CPU utilization in tests, latency can get spikes)
+# * virt_nics - NICs are virtual (VMXNET3 etc.)
+
+### Router configuration:
+# hostname - the router hostname as apears in ______# cli prefix
+# ip_address - the router's ip that can be used to communicate with
+# image - the desired imaged wished to be loaded as the router's running config
+# line_password - router password when access via Telent
+# en_password - router password when changing to "enable" mode
+# interfaces - an array of client-server pairs, representing the interfaces configurations of the router
+# configurations - an array of configurations that could possibly loaded into the router during the test.
+# The "clean" configuration is a mandatory configuration the router will load with to run the basic test bench
+
+### TFTP configuration:
+# hostname - the tftp hostname
+# ip_address - the tftp's ip address
+# images_path - the tftp's relative path in which the router's images are located
+
+### Test_misc configuration:
+# expected_bw - the "golden" bandwidth (in Gbps) results planned on receiving from the test
+
+trex:
+ hostname : csi-trex-14
+ cores : 4
+ modes : []
+
+router:
+ model : ASR1001x
+ hostname : csi-asr-01
+ ip_address : 10.56.216.103
+ image : asr1001x-universalk9.03.17.00.S.156-1.S-std.SPA.bin
+ line_password : cisco
+ en_password : cisco
+ mgmt_interface : GigabitEthernet0
+ clean_config : /Configurations/danklei/asr1001_TRex_clean_config.cfg
+ intf_masking : 255.255.255.0
+ ipv6_mask : 64
+ interfaces :
+ - client :
+ name : Te0/0/0
+ src_mac_addr : 0000.0001.0000
+ dest_mac_addr : 0000.0001.0000
+ server :
+ name : Te0/0/1
+ src_mac_addr : 0000.0001.0000
+ dest_mac_addr : 0000.0001.0000
+ vrf_name : null
+
+tftp:
+ hostname : ats-asr-srv-1
+ ip_address : 10.56.217.7
+ root_dir : /scratch/tftp/
+ images_path : /asr1001x/
diff --git a/scripts/automation/regression/setups/trex14/benchmark.yaml b/scripts/automation/regression/setups/trex14/benchmark.yaml
index 04f13e79..0dc340b0 100644
--- a/scripts/automation/regression/setups/trex14/benchmark.yaml
+++ b/scripts/automation/regression/setups/trex14/benchmark.yaml
@@ -75,20 +75,19 @@ test_nbar_simple:
cores : 2
bw_per_core : 17.174
nbar_classification:
- http : 32.58
- rtp-audio : 21.21
- oracle_sqlnet : 11.41
- exchange : 11.22
- rtp : 11.2
- citrix : 5.65
- rtsp : 2.87
- dns : 1.96
+ rtp : 32.57
+ http : 30.25
+ oracle_sqlnet : 11.23
+ exchange : 10.80
+ citrix : 5.62
+ rtsp : 2.84
+ dns : 1.95
smtp : 0.57
- pop3 : 0.37
- ssl : 0.28
+ pop3 : 0.36
+ ssl : 0.17
sctp : 0.13
sip : 0.09
- unknown : 0.45
+ unknown : 3.41
test_rx_check_http: &rx_http
diff --git a/scripts/automation/regression/setups/trex14/config.yaml b/scripts/automation/regression/setups/trex14/config.yaml
index 0fd6b70e..ffb61763 100644
--- a/scripts/automation/regression/setups/trex14/config.yaml
+++ b/scripts/automation/regression/setups/trex14/config.yaml
@@ -36,28 +36,27 @@
trex:
hostname : csi-trex-14
cores : 4
- modes : []
router:
model : ASR1001x
hostname : csi-asr-01
- ip_address : 10.56.216.103
- image : asr1001x-universalk9.03.17.00.S.156-1.S-std.SPA.bin
+ ip_address : 10.56.216.120
+ image : asr1001x-universalk9.03.13.02.S.154-3.S2-ext.SPA.bin
line_password : cisco
en_password : cisco
mgmt_interface : GigabitEthernet0
- clean_config : /Configurations/danklei/asr1001_TRex_clean_config.cfg
+ clean_config : clean_config.cfg
intf_masking : 255.255.255.0
ipv6_mask : 64
interfaces :
- client :
name : Te0/0/0
- src_mac_addr : 0000.0001.0000
- dest_mac_addr : 0000.0001.0000
+ src_mac_addr : 0000.0001.0002
+ dest_mac_addr : 0000.0001.0001
server :
name : Te0/0/1
- src_mac_addr : 0000.0001.0000
- dest_mac_addr : 0000.0001.0000
+ src_mac_addr : 0000.0002.0002
+ dest_mac_addr : 0000.0002.0001
vrf_name : null
tftp:
diff --git a/scripts/automation/regression/stateful_tests/trex_client_cfg_test.py b/scripts/automation/regression/stateful_tests/trex_client_cfg_test.py
new file mode 100644
index 00000000..852e745d
--- /dev/null
+++ b/scripts/automation/regression/stateful_tests/trex_client_cfg_test.py
@@ -0,0 +1,52 @@
+#!/router/bin/python
+from .trex_general_test import CTRexGeneral_Test, CTRexScenario
+from CPlatform import CStaticRouteConfig
+from .tests_exceptions import *
+#import sys
+import time
+from nose.tools import nottest
+
+# Testing client cfg ARP resolve. Actually, just need to check that TRex run finished with no errors.
+# If resolve will fail, TRex will exit with exit code != 0
+class CTRexClientCfg_Test(CTRexGeneral_Test):
+ """This class defines the IMIX testcase of the TRex traffic generator"""
+ def __init__(self, *args, **kwargs):
+ # super(CTRexClientCfg_Test, self).__init__()
+ CTRexGeneral_Test.__init__(self, *args, **kwargs)
+
+ def setUp(self):
+ if CTRexScenario.setup_name == 'kiwi02':
+ self.skip("Can't run currently on kiwi02")
+ super(CTRexClientCfg_Test, self).setUp() # launch super test class setUp process
+ pass
+
+ def test_client_cfg(self):
+ # test initializtion
+ if self.is_loopback:
+ return
+ else:
+ self.router.configure_basic_interfaces()
+ self.router.config_pbr(mode = "config")
+
+ ret = self.trex.start_trex(
+ c = 1,
+ m = 1,
+ d = 10,
+ f = 'cap2/dns.yaml',
+ v = 3,
+ client_cfg = 'automation/regression/cfg/client_cfg.yaml',
+ l = 1000)
+
+ trex_res = self.trex.sample_to_run_finish()
+
+ print("\nLATEST RESULT OBJECT:")
+ print(trex_res)
+
+ self.check_general_scenario_results(trex_res)
+
+ def tearDown(self):
+ CTRexGeneral_Test.tearDown(self)
+ pass
+
+if __name__ == "__main__":
+ pass
diff --git a/scripts/automation/regression/stateful_tests/trex_general_test.py b/scripts/automation/regression/stateful_tests/trex_general_test.py
index e968d380..fe38ed34 100755
--- a/scripts/automation/regression/stateful_tests/trex_general_test.py
+++ b/scripts/automation/regression/stateful_tests/trex_general_test.py
@@ -198,11 +198,14 @@ class CTRexGeneral_Test(unittest.TestCase):
def check_for_trex_crash(self):
pass
- def get_benchmark_param (self, param, sub_param = None, test_name = None):
+ def get_benchmark_param (self, param, sub_param = None, test_name = None,default=None):
if not test_name:
test_name = self.get_name()
if test_name not in self.benchmark:
- self.skip('No data in benchmark.yaml for test: %s, param: %s. Skipping.' % (test_name, param))
+ if default ==None:
+ self.skip('No data in benchmark.yaml for test: %s, param: %s. Skipping.' % (test_name, param))
+ else:
+ return default
if sub_param:
return self.benchmark[test_name][param].get(sub_param)
else:
@@ -254,7 +257,7 @@ class CTRexGeneral_Test(unittest.TestCase):
allowed_latency = 1000
if max(trex_res.get_max_latency().values()) > allowed_latency:
self.fail('LatencyError: Maximal latency exceeds %s (usec)' % allowed_latency)
-
+
# check that avg latency does not exceed 1 msec
if self.is_VM:
allowed_latency = 9999999
@@ -263,6 +266,15 @@ class CTRexGeneral_Test(unittest.TestCase):
if max(trex_res.get_avg_latency().values()) > allowed_latency:
self.fail('LatencyError: Average latency exceeds %s (usec)' % allowed_latency)
+ ports_names = trex_res.get_last_value('trex-latecny-v2.data', 'port\-\d+')
+ if not ports_names:
+ raise AbnormalResultError('Could not find ports info in TRex results, path: trex-latecny-v2.data.port-*')
+ for port_name in ports_names:
+ path = 'trex-latecny-v2.data.%s.hist.cnt' % port_name
+ lat_count = trex_res.get_last_value(path)
+ if lat_count == 0:
+ self.fail('LatencyError: Number of latency packets received on %s is 0' % port_name)
+
if not self.is_loopback:
# check router number of drops --> deliberately masked- need to be figured out!!!!!
pkt_drop_stats = self.router.get_drop_stats()
@@ -356,7 +368,7 @@ class CTRexGeneral_Test(unittest.TestCase):
print("Can't get TRex log:", e)
if len(self.fail_reasons):
sys.stdout.flush()
- raise Exception('The test is failed, reasons:\n%s' % '\n'.join(self.fail_reasons))
+ raise Exception('Test failed. Reasons:\n%s' % '\n'.join(self.fail_reasons))
sys.stdout.flush()
def check_for_trex_crash(self):
diff --git a/scripts/automation/regression/stateless_tests/stl_client_test.py b/scripts/automation/regression/stateless_tests/stl_client_test.py
index 36ac0ee1..73dac734 100644
--- a/scripts/automation/regression/stateless_tests/stl_client_test.py
+++ b/scripts/automation/regression/stateless_tests/stl_client_test.py
@@ -240,10 +240,26 @@ class STLClient_Test(CStlGeneral_Test):
self.skip('skipping profile tests for virtual / non loopback')
return
+ default_mult = self.get_benchmark_param('mult',default="30%")
+ skip_tests = self.get_benchmark_param('skip',default=[])
+
try:
-
+ print("\n");
+
+
for profile in self.profiles:
+ skip=False
+ if skip_tests:
+ for skip_test in skip_tests:
+ if skip_test in profile:
+ skip=True;
+ break;
+ if skip:
+ print("skipping testing profile due to config file {0}...\n".format(profile))
+ continue;
+
+
print("now testing profile {0}...\n".format(profile))
p1 = STLProfile.load(profile, port_id = self.tx_port)
@@ -269,7 +285,7 @@ class STLClient_Test(CStlGeneral_Test):
self.c.clear_stats()
- self.c.start(ports = [self.tx_port, self.rx_port], mult = "30%")
+ self.c.start(ports = [self.tx_port, self.rx_port], mult = default_mult)
time.sleep(100 / 1000.0)
if p1.is_pauseable() and p2.is_pauseable():
diff --git a/scripts/automation/regression/stateless_tests/stl_performance_test.py b/scripts/automation/regression/stateless_tests/stl_performance_test.py
index a556daf3..641f0a33 100644
--- a/scripts/automation/regression/stateless_tests/stl_performance_test.py
+++ b/scripts/automation/regression/stateless_tests/stl_performance_test.py
@@ -296,6 +296,10 @@ class STLPerformance_Test(CStlGeneral_Test):
# sample bps/pps
for _ in range(0, 20):
stats = self.c.get_stats(ports = 0)
+ if stats['global'][ 'queue_full']>10000:
+ assert 0, "Queue is full need to tune the multiplier"
+
+ # CPU results are not valid cannot use them
samples['bps'].append(stats[0]['tx_bps'])
samples['pps'].append(stats[0]['tx_pps'])
time.sleep(1)
diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py
index 524ad4bf..4dad712f 100644
--- a/scripts/automation/regression/stateless_tests/stl_rx_test.py
+++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py
@@ -51,6 +51,24 @@ class STLRX_Test(CStlGeneral_Test):
'latency_9k_enable': False,
'allow_packets_drop_num': 1, # allow 1 pkt drop
},
+
+ 'librte_pmd_mlx5': {
+ 'rate_percent': 80,
+ 'total_pkts': 1000,
+ 'rate_latency': 1,
+ 'latency_9k_enable': True,
+ 'latency_9k_max_average': 100,
+ 'latency_9k_max_latency': 250,
+ },
+
+ 'rte_enic_pmd': {
+ 'rate_percent': 1,
+ 'total_pkts': 50,
+ 'rate_latency': 1,
+ 'latency_9k_enable': False,
+ },
+
+
}
CStlGeneral_Test.setUp(self)
@@ -63,7 +81,6 @@ class STLRX_Test(CStlGeneral_Test):
port_info = self.c.get_port_info(ports = self.rx_port)[0]
self.speed = port_info['speed']
-
cap = port_info['rx']['caps']
if "flow_stats" not in cap or "latency" not in cap:
self.skip('port {0} does not support RX'.format(self.rx_port))
@@ -400,12 +417,14 @@ class STLRX_Test(CStlGeneral_Test):
s_port=random.sample(all_ports, random.randint(1, len(all_ports)) )
s_port=sorted(s_port)
- if self.speed == 40 :
+
+ if ((self.speed == 40) or (self.speed == 100)):
# the NIC does not support all full rate in case both port works let's filter odd ports
s_port=list(filter(lambda x: x % 2==0, s_port))
if len(s_port)==0:
s_port=[0];
+
error=1;
for j in range(0,5):
print(" {4} - duration {0} pgid {1} pkt_size {2} s_port {3} ".format(duration,pgid,pkt_size,s_port,j));
diff --git a/scripts/automation/regression/trex.py b/scripts/automation/regression/trex.py
index 7b96f2f8..416a6e3b 100644
--- a/scripts/automation/regression/trex.py
+++ b/scripts/automation/regression/trex.py
@@ -35,7 +35,7 @@ class CTRexScenario:
report_dir = 'reports'
# logger = None
test_types = {'functional_tests': [], 'stateful_tests': [], 'stateless_tests': []}
- is_copied = False
+ pkg_updated = False
GAManager = None
no_daemon = False
debug_image = False
diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py
index daa1abaf..09614770 100755
--- a/scripts/automation/regression/trex_unit_test.py
+++ b/scripts/automation/regression/trex_unit_test.py
@@ -137,35 +137,29 @@ class CTRexTestConfiguringPlugin(Plugin):
parser.add_option('--stf', '--stateful', action="store_true", default = False,
dest="stateful",
help="Run stateful tests.")
- parser.add_option('--pkg', action="store",
- dest="pkg",
- help="Run with given TRex package. Make sure the path available at server machine.")
+ parser.add_option('--pkg', type = str,
+ help="Run with given TRex package. Make sure the path available at server machine. Implies --restart-daemon.")
+ parser.add_option('--restart-daemon', action="store_true", default = False,
+ help="Flag that specifies to restart daemon. Implied by --pkg.")
parser.add_option('--collect', action="store_true", default = False,
- dest="collect",
help="Alias to --collect-only.")
parser.add_option('--warmup', action="store_true", default = False,
- dest="warmup",
help="Warm up the system for stateful: run 30 seconds 9k imix test without check of results.")
parser.add_option('--test-client-package', '--test_client_package', action="store_true", default = False,
- dest="test_client_package",
help="Includes tests of client package.")
parser.add_option('--long', action="store_true", default = False,
- dest="long",
help="Flag of long tests (stability).")
parser.add_option('--ga', action="store_true", default = False,
- dest="ga",
help="Flag to send benchmarks to GA.")
parser.add_option('--no-daemon', action="store_true", default = False,
dest="no_daemon",
help="Flag that specifies to use running stl server, no need daemons.")
parser.add_option('--debug-image', action="store_true", default = False,
- dest="debug_image",
help="Flag that specifies to use t-rex-64-debug as TRex executable.")
- parser.add_option('--trex-args', action='store', default = '',
- dest="trex_args",
+ parser.add_option('--trex-args', default = '',
help="Additional TRex arguments (--no-watchdog etc.).")
- parser.add_option('-t', '--test', action='store', default = '', dest='test',
- help='Test name to run (without file, class etc.)')
+ parser.add_option('-t', '--test', type = str,
+ help = 'Test name to run (without file, class etc.)')
def configure(self, options, conf):
@@ -174,10 +168,13 @@ class CTRexTestConfiguringPlugin(Plugin):
self.stateless = options.stateless
self.stateful = options.stateful
self.pkg = options.pkg
+ self.restart_daemon = options.restart_daemon
self.json_verbose = options.json_verbose
self.telnet_verbose = options.telnet_verbose
self.no_daemon = options.no_daemon
CTRexScenario.test = options.test
+ if self.no_daemon and (self.pkg or self.restart_daemon):
+ raise Exception('You have specified both --no-daemon and either --pkg or --restart-daemon at same time.')
if self.collect_only or self.functional:
return
if CTRexScenario.setup_dir and options.config_path:
@@ -212,6 +209,7 @@ class CTRexTestConfiguringPlugin(Plugin):
verbose = self.json_verbose,
debug_image = options.debug_image,
trex_args = options.trex_args)
+ if self.pkg or self.restart_daemon:
if not CTRexScenario.trex.check_master_connectivity():
print('Could not connect to master daemon')
sys.exit(-1)
@@ -228,20 +226,20 @@ class CTRexTestConfiguringPlugin(Plugin):
def begin (self):
client = CTRexScenario.trex
- if self.pkg and not CTRexScenario.is_copied:
+ if self.pkg and not CTRexScenario.pkg_updated:
if client.master_daemon.is_trex_daemon_running() and client.get_trex_cmds() and not self.kill_running:
- print("Can't update TRex, it's running")
+ print("Can't update TRex, it's running. Consider adding --kill-running flag.")
sys.exit(-1)
print('Updating TRex to %s' % self.pkg)
if not client.master_daemon.update_trex(self.pkg):
- print('Failed updating TRex')
+ print('Failed updating TRex.')
sys.exit(-1)
else:
- print('Updated')
- CTRexScenario.is_copied = True
+ print('Updated.')
+ CTRexScenario.pkg_updated = True
if self.functional or self.collect_only:
return
- if not self.no_daemon:
+ if self.pkg or self.restart_daemon:
print('Restarting TRex daemon server')
res = client.restart_trex_daemon()
if not res:
diff --git a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
index e9d2b8a0..5d992c6e 100755
--- a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
+++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py
@@ -150,10 +150,8 @@ class CTRexClient(object):
user = user or self.__default_user
try:
d = int(d)
- if d < 30 and not trex_development: # test duration should be at least 30 seconds, unless trex_development flag is specified.
- raise ValueError
except ValueError:
- raise ValueError('d parameter must be integer, specifying how long TRex run, and must be larger than 30 secs.')
+ raise ValueError('d parameter must be integer, specifying how long TRex run.')
trex_cmd_options.update( {'f' : f, 'd' : d} )
if not trex_cmd_options.get('l'):
diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py
index b23b5f1f..627761ff 100755
--- a/scripts/automation/trex_control_plane/stl/console/trex_console.py
+++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py
@@ -202,7 +202,7 @@ class TRexConsole(TRexGeneralCmd):
func_name = f.__name__
if func_name.startswith("do_"):
func_name = func_name[3:]
-
+
if not inst.stateless_client.is_connected():
print(format_text("\n'{0}' cannot be executed on offline mode\n".format(func_name), 'bold'))
return
@@ -313,6 +313,7 @@ class TRexConsole(TRexGeneralCmd):
def do_shell (self, line):
self.do_history(line)
+ @verify_connected
def do_push (self, line):
'''Push a local PCAP file\n'''
self.stateless_client.push_line(line)
@@ -320,6 +321,7 @@ class TRexConsole(TRexGeneralCmd):
def help_push (self):
self.do_push("-h")
+ @verify_connected
def do_portattr (self, line):
'''Change/show port(s) attributes\n'''
self.stateless_client.set_port_attr_line(line)
@@ -328,6 +330,42 @@ class TRexConsole(TRexGeneralCmd):
self.do_portattr("-h")
@verify_connected
+ def do_l2 (self, line):
+ '''Configures a port in L2 mode'''
+ self.stateless_client.set_l2_mode_line(line)
+
+ def help_l2 (self):
+ self.do_l2("-h")
+
+ @verify_connected
+ def do_l3 (self, line):
+ '''Configures a port in L3 mode'''
+ self.stateless_client.set_l3_mode_line(line)
+
+ def help_l3 (self):
+ self.do_l3("-h")
+
+
+ @verify_connected
+ def do_set_rx_sniffer (self, line):
+ '''Sets a port sniffer on RX channel as PCAP recorder'''
+ self.stateless_client.set_rx_sniffer_line(line)
+
+ def help_sniffer (self):
+ self.do_set_rx_sniffer("-h")
+
+ @verify_connected
+ def do_resolve (self, line):
+ '''Resolve ARP for ports'''
+ self.stateless_client.resolve_line(line)
+
+ def help_resolve (self):
+ self.do_resolve("-h")
+
+ do_arp = do_resolve
+ help_arp = help_resolve
+
+ @verify_connected
def do_map (self, line):
'''Maps ports topology\n'''
ports = self.stateless_client.get_acquired_ports()
@@ -416,6 +454,7 @@ class TRexConsole(TRexGeneralCmd):
'''Release ports\n'''
self.stateless_client.release_line(line)
+ @verify_connected
def do_reacquire (self, line):
'''reacquire all the ports under your logged user name'''
self.stateless_client.reacquire_line(line)
@@ -469,7 +508,7 @@ class TRexConsole(TRexGeneralCmd):
############# update
@verify_connected
def do_update(self, line):
- '''update speed of port(s)currently transmitting traffic\n'''
+ '''update speed of port(s) currently transmitting traffic\n'''
self.stateless_client.update_line(line)
@@ -530,6 +569,13 @@ class TRexConsole(TRexGeneralCmd):
'''Clear cached local statistics\n'''
self.stateless_client.clear_stats_line(line)
+ @verify_connected
+ def do_service (self, line):
+ '''Sets port(s) service mode state'''
+ self.stateless_client.service_line(line)
+
+ def help_service (self, line):
+ self.do_service("-h")
def help_clear(self):
self.do_clear("-h")
diff --git a/scripts/automation/trex_control_plane/stl/console/trex_tui.py b/scripts/automation/trex_control_plane/stl/console/trex_tui.py
index d7db6d30..37ef8000 100644
--- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py
+++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py
@@ -645,14 +645,14 @@ class TrexTUI():
# regular state
if self.state == self.STATE_ACTIVE:
# if no connectivity - move to lost connecitivty
- if not self.stateless_client.async_client.is_alive():
+ if not self.stateless_client.async_client.is_active():
self.stateless_client._invalidate_stats(self.pm.ports)
self.state = self.STATE_LOST_CONT
# lost connectivity
elif self.state == self.STATE_LOST_CONT:
- # got it back
+ # if the async is alive (might be zomibe, but alive) try to reconnect
if self.stateless_client.async_client.is_alive():
# move to state reconnect
self.state = self.STATE_RECONNECT
@@ -1153,7 +1153,7 @@ class AsyncKeysEngineConsole:
# errors
else:
err_msgs = ascii_split(str(func_rc))
- self.last_status = format_text(err_msgs[0], 'red')
+ self.last_status = format_text(clear_formatting(err_msgs[0]), 'red')
if len(err_msgs) > 1:
self.last_status += " [{0} more errors messages]".format(len(err_msgs) - 1)
color = 'red'
diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_imix_bidir_update.py b/scripts/automation/trex_control_plane/stl/examples/stl_imix_bidir_update.py
new file mode 100644
index 00000000..22cceb8f
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/examples/stl_imix_bidir_update.py
@@ -0,0 +1,123 @@
+import stl_path
+from trex_stl_lib.api import *
+
+import imp
+import time
+import json
+from pprint import pprint
+import argparse
+
+# IMIX test
+# it maps the ports to sides
+# then it load a predefind profile 'IMIX'
+# 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):
+
+
+ # create client
+ c = STLClient(server = server)
+ passed = True
+
+
+ try:
+
+ # connect to server
+ c.connect()
+
+ # take all the ports
+ c.reset()
+
+ dir_0 = [0]
+ dir_1 = [1]
+
+ print "Mapped ports to sides {0} <--> {1}".format(dir_0, dir_1)
+
+ # load IMIX profile
+ 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()
+
+ # add both streams to ports
+ c.add_streams(stream1, ports = dir_0)
+ c.add_streams(stream2, ports = dir_1)
+
+
+ # clear the stats before injecting
+ c.clear_stats()
+
+ c.start(ports = (dir_0 + dir_1), mult = "100kpps", total = True)
+
+ while True:
+ for rate in range(200,3100,10):
+
+ # choose rate and start traffic for 10 seconds on 5 mpps
+ #mult = "30%"
+ my_mult = ("%dkpps"%rate)
+ print "Injecting {0} <--> {1} on total rate of '{2}' ".format(dir_0, dir_1, my_mult)
+ c.clear_stats()
+
+
+ c.update(ports = (dir_0 + dir_1), mult = my_mult)
+
+ #time.sleep(1);
+
+ # block until done
+ #c.wait_on_traffic(ports = (dir_0 + dir_1))
+
+ # read the stats after the test
+ stats = c.get_stats()
+
+ # use this for debug info on all the stats
+ pprint(stats)
+
+ # sum dir 0
+ dir_0_opackets = sum([stats[i]["opackets"] for i in dir_0])
+ dir_0_ipackets = sum([stats[i]["ipackets"] for i in dir_0])
+
+ # sum dir 1
+ dir_1_opackets = sum([stats[i]["opackets"] for i in dir_1])
+ dir_1_ipackets = sum([stats[i]["ipackets"] for i in dir_1])
+
+
+ lost_0 = dir_0_opackets - dir_1_ipackets
+ lost_1 = dir_1_opackets - dir_0_ipackets
+
+ print "\nPackets injected from {0}: {1:,}".format(dir_0, dir_0_opackets)
+ print "Packets injected from {0}: {1:,}".format(dir_1, dir_1_opackets)
+
+ print "\npackets lost from {0} --> {1}: {2:,} pkts".format(dir_0, dir_1, lost_0)
+ print "packets lost from {0} --> {1}: {2:,} pkts".format(dir_1, dir_0, lost_1)
+
+ if (lost_0 <= 0) and (lost_1 <= 0): # less or equal because we might have incoming arps etc.
+ passed = True
+ else:
+ passed = False
+
+
+ except STLError as e:
+ passed = False
+ print e
+
+ finally:
+ c.disconnect()
+
+ if passed:
+ print "\nTest has passed :-)\n"
+ else:
+ print "\nTest has failed :-(\n"
+
+parser = argparse.ArgumentParser(description="Example for TRex Stateless, sending IMIX traffic")
+parser.add_argument('-s', '--server',
+ dest='server',
+ help='Remote trex address',
+ default='127.0.0.1',
+ type = str)
+args = parser.parse_args()
+
+# run the tests
+imix_test(args.server)
+
diff --git a/scripts/automation/trex_control_plane/stl/services/scapy_server/field_engine.json b/scripts/automation/trex_control_plane/stl/services/scapy_server/field_engine.json
new file mode 100644
index 00000000..85d10e65
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/services/scapy_server/field_engine.json
@@ -0,0 +1,269 @@
+{
+ "instructions": [
+ {
+ "id": "STLVmFlowVar",
+ "parameters": ["name", "init_value", "max_value","min_value","step", "size","op"]
+ },
+ {
+ "id": "STLVmWrFlowVar",
+ "parameters": ["fv_name", "pkt_offset","offset_fixup","add_val","is_big"]
+ },
+ {
+ "id": "STLVmWrMaskFlowVar",
+ "parameters": ["fv_name", "pkt_offset", "pkt_cast_size","mask", "shift","add_value","is_big"]
+ },
+ {
+ "id": "STLVmFixIpv4",
+ "parameters": ["offset"]
+ },
+ {
+ "id": "STLVmTrimPktSize",
+ "parameters": ["fv_name"]
+ },
+ {
+ "id": "STLVmTupleGen",
+ "parameters": ["name", "ip_min", "ip_max", "port_min", "port_max", "limit_flows", "flags"]
+ },
+ {
+ "id": "STLVmFlowVarRepetableRandom",
+ "parameters": ["name", "size", "limit", "seed", "min_value", "max_value"]
+ },
+ {
+ "id": "STLVmFixChecksumHw",
+ "parameters": ["l3_offset","l4_offset","l4_type"]
+ }
+ ],
+
+ "instruction_params_meta": [
+ {
+ "id": "name",
+ "name": "Name",
+ "type": "ENUM",
+ "editable": true,
+ "required": true,
+ "defaultValue": "Not defined"
+ },
+ {
+ "id": "init_value",
+ "name": "Initial value",
+ "type": "STRING",
+ "defaultValue": "0"
+ },
+ {
+ "id": "max_value",
+ "name": "Maximum value",
+ "type": "STRING",
+ "required": true,
+ "defaultValue": "0"
+ },
+ {
+ "id": "min_value",
+ "name": "Minimum value",
+ "type": "STRING",
+ "required": true,
+ "defaultValue": "0"
+ },
+ {
+ "id": "step",
+ "name": "Step",
+ "type": "NUMBER",
+ "required": true,
+ "defaultValue": "1"
+ },
+ {
+ "id": "op",
+ "name": "Operation",
+ "type": "ENUM",
+ "defaultValue": "inc",
+ "dict": {
+ "dec": "Decrement",
+ "inc": "Increment",
+ "random": "Random"
+ },
+ "required": true
+ },
+ {
+ "id": "size",
+ "name": "Size",
+ "type": "ENUM",
+ "defaultValue": "4",
+ "dict": {
+ "1": "1",
+ "2": "2",
+ "4": "4",
+ "8": "8"
+ }
+ },
+ {
+ "id": "fv_name",
+ "name": "Variable name",
+ "type": "ENUM",
+ "required": true,
+ "editable": true
+ },
+ {
+ "id": "pkt_offset",
+ "name": "Offset",
+ "type": "ENUM",
+ "required": true,
+ "editable": true,
+ "defaultValue": 0
+ },
+ {
+ "id": "pkt_cast_size",
+ "name": "Packet cast size",
+ "type": "ENUM",
+ "defaultValue": 1,
+ "dict":{
+ "1":1,
+ "2":2,
+ "4":4
+ }
+ },
+ {
+ "id": "shift",
+ "name": "Shift",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "mask",
+ "name": "Mask",
+ "type": "STRING",
+ "defaultValue": "0xff"
+ },
+ {
+ "id": "offset_fixup",
+ "name": "offset_fixup",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "add_val",
+ "name": "add_val",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "add_value",
+ "name": "add_value",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "is_big",
+ "name": "is_big",
+ "type": "ENUM",
+ "defaultValue": "true",
+ "dict": {
+ "true": "true",
+ "false": "false"
+ }
+ },
+ {
+ "id": "offset",
+ "name": "Offset",
+ "type": "ENUM",
+ "required": true,
+ "editable": true,
+ "defaultValue": 0
+ },
+ {
+ "id": "l3_offset",
+ "name": "L3 offset",
+ "type": "STRING",
+ "required": true,
+ "autocomplete": true,
+ "defaultValue": "IP"
+ },
+ {
+ "id": "l4_offset",
+ "name": "L4 offset",
+ "type": "STRING",
+ "required": true,
+ "defaultValue": "TCP"
+ },
+ {
+ "id": "ip_min",
+ "name": "Min IP",
+ "type": "STRING",
+ "defaultValue": "0.0.0.1"
+ },
+ {
+ "id": "ip_max",
+ "name": "Max IP",
+ "type": "STRING",
+ "defaultValue": "0.0.0.10"
+ },
+ {
+ "id": "port_max",
+ "name": "Max Port number",
+ "type": "NUMBER",
+ "defaultValue": 65535
+ },
+ {
+ "id": "port_min",
+ "name": "Min Port number",
+ "type": "NUMBER",
+ "defaultValue": 1025
+ },
+ {
+ "id": "limit_flows",
+ "name": "FLows limit",
+ "type": "NUMBER",
+ "defaultValue": 100000
+ },
+ {
+ "id": "limit",
+ "name": "Limit",
+ "type": "NUMBER",
+ "defaultValue": 100
+ },
+ {
+ "id": "seed",
+ "name": "Seed",
+ "type": "String",
+ "defaultValue": "None"
+ },
+ {
+ "id": "flags",
+ "name": "Flags",
+ "type": "NUMBER",
+ "defaultValue": 0
+ },
+ {
+ "id": "l4_type",
+ "name": "L4 type",
+ "type": "ENUM",
+ "required": true,
+ "editable": false,
+ "defaultValue": "13",
+ "dict": {
+ "11": "L4_TYPE_UDP",
+ "13": "L4_TYPE_TCP"
+ }
+ }
+ ],
+ "supported_protocols": ["IP","TCP","UDP"],
+ "templates":[
+ {
+ "id": "simple_flow_var",
+ "name": "Simple variable",
+ "instructionIds": ["STLVmFlowVar", "STLVmWrFlowVar"]
+ },
+ {
+ "id": "rep_rand_var",
+ "name": "Repeatable random",
+ "instructionIds": ["STLVmFlowVarRepetableRandom", "STLVmWrFlowVar"]
+ }
+ ],
+ "global_params_meta":[
+ {
+ "id": "cache_size",
+ "name": "Cache size",
+ "type": "NUMBER",
+ "defaultValue": "1000"
+ }
+ ]
+}
+
diff --git a/scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py b/scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py
index 8d99fe92..e5f1b20c 100755
--- a/scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py
+++ b/scripts/automation/trex_control_plane/stl/services/scapy_server/scapy_service.py
@@ -1,17 +1,20 @@
import os
import sys
+
stl_pathname = os.path.abspath(os.path.join(os.pardir, os.pardir))
sys.path.append(stl_pathname)
from trex_stl_lib.api import *
+import trex_stl_lib.trex_stl_packet_builder_scapy
import tempfile
import hashlib
import base64
import numbers
import random
-import inspect
+from inspect import getdoc
import json
+import re
from pprint import pprint
# add some layers as an example
@@ -121,6 +124,45 @@ class Scapy_service_api():
"""
pass
+ def build_pkt_ex(self, client_v_handler, pkt_model_descriptor, extra_options):
+ """ build_pkt_ex(self,client_v_handler,pkt_model_descriptor, extra_options) -> Dictionary (of Offsets,Show2 and Buffer)
+ Performs calculations on the given packet and returns results for that packet.
+
+ Parameters
+ ----------
+ pkt_descriptor - An array of dictionaries describing a network packet
+ extra_options - A dictionary of extra options required for building packet
+
+ Returns
+ -------
+ - The packets offsets: each field in every layer is mapped inside the Offsets Dictionary
+ - The Show2: A description of each field and its value in every layer of the packet
+ - The Buffer: The Hexdump of packet encoded in base64
+
+ Raises
+ ------
+ will raise an exception when the Scapy string format is illegal, contains syntax error, contains non-supported
+ protocl, etc.
+ """
+ pass
+
+ def load_instruction_parameter_values(self, client_v_handler, pkt_model_descriptor, vm_instructions_model, parameter_id):
+ """ load_instruction_parameter_values(self,client_v_handler,pkt_model_descriptor, vm_instructions_model, parameter_id) -> Dictionary (of possible parameter values)
+ Returns possible valies for given pararameter id depends on current pkt structure and vm_instructions
+ model.
+
+ Parameters
+ ----------
+ pkt_descriptor - An array of dictionaries describing a network packet
+ vm_instructions_model - A dictionary of extra options required for building packet
+ parameter_id - A string of parameter id
+
+ Returns
+ -------
+ Possible parameter values map.
+
+ """
+ pass
def get_tree(self,client_v_handler):
""" get_tree(self) -> Dictionary describing an example of hierarchy in layers
@@ -372,7 +414,15 @@ class Scapy_service(Scapy_service_api):
self.version_minor = '01'
self.server_v_hashed = self._generate_version_hash(self.version_major,self.version_minor)
self.protocol_definitions = {} # protocolId -> prococol definition overrides data
+ self.field_engine_supported_protocols = {}
+ self.instruction_parameter_meta_definitions = []
+ self.field_engine_parameter_meta_definitions = []
+ self.field_engine_templates_definitions = []
+ self.field_engine_instructions_meta = []
+ self.field_engine_instruction_expressions = []
self._load_definitions_from_json()
+ self._load_field_engine_meta_from_json()
+ self._vm_instructions = dict([m for m in inspect.getmembers(trex_stl_lib.trex_stl_packet_builder_scapy, inspect.isclass) if m[1].__module__ == 'trex_stl_lib.trex_stl_packet_builder_scapy'])
def _load_definitions_from_json(self):
# load protocol definitions from a json file
@@ -382,6 +432,27 @@ class Scapy_service(Scapy_service_api):
for protocol in protocols:
self.protocol_definitions[ protocol['id'] ] = protocol
+ def _load_field_engine_meta_from_json(self):
+ # load protocol definitions from a json file
+ self.instruction_parameter_meta_definitions = []
+ self.field_engine_supported_protocols = {}
+ self.field_engine_parameter_meta_definitions = []
+ self.field_engine_templates_definitions = []
+ with open('field_engine.json', 'r') as f:
+ metas = json.load(f)
+ self.instruction_parameter_meta_definitions = metas["instruction_params_meta"]
+ self.field_engine_instructions_meta = metas["instructions"]
+ self._append_intructions_help()
+ self.field_engine_supported_protocols = metas["supported_protocols"]
+ self.field_engine_parameter_meta_definitions = metas["global_params_meta"]
+ self.field_engine_templates_definitions = metas["templates"]
+
+
+ def _append_intructions_help(self):
+ for instruction_meta in self.field_engine_instructions_meta:
+ clazz = eval(instruction_meta['id'])
+ instruction_meta['help'] = base64.b64encode(getdoc(clazz.__init__)).decode('ascii')
+
def _all_protocol_structs(self):
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
@@ -708,6 +779,134 @@ class Scapy_service(Scapy_service_api):
pkt = self._packet_model_to_scapy_packet(pkt_model_descriptor)
return self._pkt_data(pkt)
+
+ def build_pkt_ex(self, client_v_handler, pkt_model_descriptor, extra_options):
+ res = self.build_pkt(client_v_handler, pkt_model_descriptor)
+ pkt = self._packet_model_to_scapy_packet(pkt_model_descriptor)
+
+ field_engine = {}
+ field_engine['instructions'] = []
+ field_engine['error'] = None
+ try:
+ field_engine['instructions'] = self._generate_vm_instructions(pkt, extra_options['field_engine'])
+ except AssertionError as e:
+ field_engine['error'] = e.message
+ except CTRexPacketBuildException as e:
+ field_engine['error'] = e.message
+
+ field_engine['vm_instructions_expressions'] = self.field_engine_instruction_expressions
+ res['field_engine'] = field_engine
+ return res
+
+ def load_instruction_parameter_values(self, client_v_handler, pkt_model_descriptor, vm_instructions_model, parameter_id):
+
+ given_protocol_ids = [str(proto['id']) for proto in pkt_model_descriptor]
+
+ values = {}
+ if parameter_id == "name":
+ values = self._curent_pkt_protocol_fields(given_protocol_ids, "_")
+
+ if parameter_id == "fv_name":
+ values = self._existed_flow_var_names(vm_instructions_model['field_engine']['instructions'])
+
+ if parameter_id == "pkt_offset":
+ values = self._curent_pkt_protocol_fields(given_protocol_ids, ".")
+
+ if parameter_id == "offset":
+ for ip_idx in range(given_protocol_ids.count("IP")):
+ value = "IP:{0}".format(ip_idx)
+ values[value] = value
+
+ return {"map": values}
+
+ def _existed_flow_var_names(self, instructions):
+ return dict((instruction['parameters']['name'], instruction['parameters']['name']) for instruction in instructions if self._nameParamterExist(instruction))
+
+ def _nameParamterExist(self, instruction):
+ try:
+ instruction['parameters']['name']
+ return True
+ except KeyError:
+ return False
+
+ def _curent_pkt_protocol_fields(self, given_protocol_ids, delimiter):
+ given_protocol_classes = [c for c in Packet.__subclasses__() if c.__name__ in given_protocol_ids]
+ protocol_fields = {}
+ for protocol_class in given_protocol_classes:
+ protocol_name = protocol_class.__name__
+ protocol_count = given_protocol_ids.count(protocol_name)
+ for field_desc in protocol_class.fields_desc:
+ if delimiter == '.' and protocol_count > 1:
+ for idx in range(protocol_count):
+ formatted_name = "{0}:{1}{2}{3}".format(protocol_name, idx, delimiter, field_desc.name)
+ protocol_fields[formatted_name] = formatted_name
+ else:
+ formatted_name = "{0}{1}{2}".format(protocol_name, delimiter, field_desc.name)
+ protocol_fields[formatted_name] = formatted_name
+
+ return protocol_fields
+
+ def _generate_vm_instructions(self, pkt, field_engine_model_descriptor):
+ self.field_engine_instruction_expressions = []
+ instructions = []
+ instructions_def = field_engine_model_descriptor['instructions']
+ for instruction_def in instructions_def:
+ instruction_id = instruction_def['id']
+ instruction_class = self._vm_instructions[instruction_id]
+ parameters = {k: self._sanitize_value(k, v) for (k, v) in instruction_def['parameters'].iteritems()}
+ instructions.append(instruction_class(**parameters))
+
+ fe_parameters = field_engine_model_descriptor['global_parameters']
+
+ cache_size = None
+ if "cache_size" in fe_parameters:
+ assert self._is_int(fe_parameters['cache_size']), 'Cache size must be a number'
+ cache_size = int(fe_parameters['cache_size'])
+
+
+ pkt_builder = STLPktBuilder(pkt=pkt, vm=STLScVmRaw(instructions, cache_size=cache_size))
+ pkt_builder.compile()
+ return pkt_builder.get_vm_data()
+
+ def _sanitize_value(self, param_id, val):
+ if param_id == "pkt_offset":
+ if self._is_int(val):
+ return int(val)
+ elif val == "Ether.src":
+ return 0
+ elif val == "Ether.dst":
+ return 6
+ elif val == "Ether.type":
+ return 12
+ else:
+ if val == "None" or val == "none":
+ return None
+ if val == "true":
+ return True
+ elif val == "false":
+ return False
+ elif re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", str(val.lower())):
+ return int(str(val).replace(":", ""), 16)
+
+ if self._is_int(val):
+ return int(val)
+
+ str_val = str(val)
+ return int(str_val, 16) if str_val.startswith("0x") else str_val
+
+ def _get_instruction_parameter_meta(self, param_id):
+ for meta in self.instruction_parameter_meta_definitions:
+ if meta['id'] == param_id:
+ return meta
+ raise Scapy_Exception("Unable to get meta for {0}" % param_id)
+
+ def _is_int(self, val):
+ try:
+ int(val)
+ return True
+ except ValueError:
+ return False
+
# @deprecated. to be removed
def get_all(self,client_v_handler):
if not (self._verify_version_handler(client_v_handler)):
@@ -796,7 +995,11 @@ class Scapy_service(Scapy_service_api):
"name": protoDef.get('name') or pkt_class.name,
"fields": self._get_fields_definition(pkt_class, protoDef.get('fields') or [])
})
- res = {"protocols": protocols}
+ res = {"protocols": protocols,
+ "feInstructionParameters": self.instruction_parameter_meta_definitions,
+ "feInstructions": self.field_engine_instructions_meta,
+ "feParameters": self.field_engine_parameter_meta_definitions,
+ "feTemplates": self.field_engine_templates_definitions}
return res
def get_payload_classes(self,client_v_handler, pkt_model_descriptor):
@@ -889,8 +1092,6 @@ class Scapy_service(Scapy_service_api):
pcap_bin = tmpPcap.read()
return bytes_to_b64(pcap_bin)
-
-
#---------------------------------------------------------------------------
diff --git a/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py b/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py
index 1db2c62b..e48880e8 100644
--- a/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py
+++ b/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/basetest.py
@@ -53,6 +53,9 @@ def get_version_handler():
def build_pkt(model_def):
return pass_result(service.build_pkt(v_handler, model_def))
+def build_pkt_ex(model_def, instructions_def):
+ return pass_result(service.build_pkt_ex(v_handler, model_def, instructions_def))
+
def build_pkt_get_scapy(model_def):
return build_pkt_to_scapy(build_pkt(model_def))
diff --git a/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py b/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py
index 91a457dc..e1094a79 100644
--- a/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py
+++ b/scripts/automation/trex_control_plane/stl/services/scapy_server/unit_tests/test_scapy_service.py
@@ -113,15 +113,23 @@ def test_get_all():
def test_get_definitions_all():
get_definitions(None)
- def_classnames = [pdef['id'] for pdef in get_definitions(None)['protocols']]
+ defs = get_definitions(None)
+ def_classnames = [pdef['id'] for pdef in defs['protocols']]
assert("IP" in def_classnames)
assert("Dot1Q" in def_classnames)
assert("TCP" in def_classnames)
+ # All instructions should have a help description.
+ fe_instructions = defs['feInstructions']
+ for instruction in fe_instructions:
+ print(instruction['help'])
+ assert("help" in instruction)
+
def test_get_definitions_ether():
res = get_definitions(["Ether"])
- assert(len(res) == 1)
- assert(res['protocols'][0]['id'] == "Ether")
+ protocols = res['protocols']
+ assert(len(protocols) == 1)
+ assert(protocols[0]['id'] == "Ether")
def test_get_payload_classes():
eth_payloads = get_payload_classes([{"id":"Ether"}])
@@ -250,3 +258,34 @@ def test_ip_definitions():
assert(fields[9]['id'] == 'chksum')
assert(fields[9]['auto'] == True)
+def test_generate_vm_instructions():
+ ip_pkt_model = [
+ layer_def("Ether"),
+ layer_def("IP", src="16.0.0.1", dst="48.0.0.1")
+ ]
+ ip_instructions_model = {"field_engine": {"instructions": [{"id": "STLVmFlowVar",
+ "parameters": {"op": "inc", "min_value": "192.168.0.10",
+ "size": "1", "name": "ip_src",
+ "step": "1",
+ "max_value": "192.168.0.100"}},
+ {"id": "STLVmWrFlowVar",
+ "parameters": {"pkt_offset": "IP.src", "is_big": "true",
+ "add_val": "0", "offset_fixup": "0",
+ "fv_name": "ip_src"}},
+ {"id": "STLVmFlowVar",
+ "parameters": {"op": "dec", "min_value": "32",
+ "size": "1", "name": "ip_ttl",
+ "step": "4", "max_value": "64"}},
+ {"id": "STLVmWrFlowVar",
+ "parameters": {"pkt_offset": "IP.ttl", "is_big": "true",
+ "add_val": "0", "offset_fixup": "0",
+ "fv_name": "ip_ttl"}}],
+ "global_parameters": {}}}
+ res = build_pkt_ex(ip_pkt_model, ip_instructions_model)
+ src_instruction = res['field_engine']['instructions']['instructions'][0]
+ assert(src_instruction['min_value'] == 3232235530)
+ assert(src_instruction['max_value'] == 3232235620)
+
+ ttl_instruction = res['field_engine']['instructions']['instructions'][2]
+ assert(ttl_instruction['min_value'] == 32)
+ assert(ttl_instruction['max_value'] == 64)
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py
index 2c95844b..11e87592 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py
@@ -137,6 +137,10 @@ class CTRexAsyncStatsManager():
class CTRexAsyncClient():
+ THREAD_STATE_ACTIVE = 1
+ THREAD_STATE_ZOMBIE = 2
+ THREAD_STATE_DEAD = 3
+
def __init__ (self, server, port, stateless_client):
self.port = port
@@ -159,7 +163,10 @@ class CTRexAsyncClient():
self.connected = False
self.zipped = ZippedMsg()
-
+
+ self.t_state = self.THREAD_STATE_DEAD
+
+
# connects the async channel
def connect (self):
@@ -173,8 +180,8 @@ class CTRexAsyncClient():
self.socket = self.context.socket(zmq.SUB)
- # before running the thread - mark as active
- self.active = True
+ # before running the thread - mark as active
+ self.t_state = self.THREAD_STATE_ACTIVE
self.t = threading.Thread(target = self._run)
# kill this thread on exit and don't add it to the join list
@@ -198,26 +205,26 @@ class CTRexAsyncClient():
return RC_OK()
-
-
# disconnect
def disconnect (self):
if not self.connected:
return
# mark for join
- self.active = False
-
- # signal that the context was destroyed (exit the thread loop)
+ self.t_state = self.THREAD_STATE_DEAD
self.context.term()
-
- # join
self.t.join()
+
# done
self.connected = False
+ # set the thread as a zombie (in case of server death)
+ def set_as_zombie (self):
+ self.last_data_recv_ts = None
+ self.t_state = self.THREAD_STATE_ZOMBIE
+
# thread function
def _run (self):
@@ -231,12 +238,19 @@ class CTRexAsyncClient():
self.monitor.reset()
- while self.active:
+ while self.t_state != self.THREAD_STATE_DEAD:
try:
with self.monitor:
line = self.socket.recv()
+ # last data recv.
+ self.last_data_recv_ts = time.time()
+
+ # if thread was marked as zomibe - it does nothing besides fetching messages
+ if self.t_state == self.THREAD_STATE_ZOMBIE:
+ continue
+
self.monitor.on_recv_msg(line)
# try to decomrpess
@@ -246,7 +260,6 @@ class CTRexAsyncClient():
line = line.decode()
- self.last_data_recv_ts = time.time()
# signal once
if not got_data:
@@ -259,13 +272,14 @@ class CTRexAsyncClient():
# signal once
if got_data:
self.event_handler.on_async_dead()
+ self.set_as_zombie()
got_data = False
continue
except zmq.ContextTerminated:
# outside thread signaled us to exit
- assert(not self.active)
+ assert(self.t_state != self.THREAD_STATE_ACTIVE)
break
msg = json.loads(line)
@@ -283,16 +297,29 @@ class CTRexAsyncClient():
# closing of socket must be from the same thread
self.socket.close(linger = 0)
- def is_thread_alive (self):
- return self.t.is_alive()
-
- # did we get info for the last 3 seconds ?
+
+ # return True if the subscriber got data in the last 3 seconds
+ # even if zombie - will return true if got data
def is_alive (self):
+
+ # maybe the thread has exited with exception
+ if not self.t.is_alive():
+ return False
+
+ # simply no data
if self.last_data_recv_ts == None:
return False
+ # timeout of data
return ( (time.time() - self.last_data_recv_ts) < 3 )
+
+ # more granular than active - it means that thread state is active we get info
+ # zomibes will return false
+ def is_active (self):
+ return self.is_alive() and self.t_state == self.THREAD_STATE_ACTIVE
+
+
def get_stats (self):
return self.stats
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 80a4c4dc..a42247e7 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
@@ -12,9 +12,10 @@ from .trex_stl_types import *
from .trex_stl_async_client import CTRexAsyncClient
from .utils import parsing_opts, text_tables, common
-from .utils.common import list_intersect, list_difference, is_sub_list, PassiveTimer
+from .utils.common import *
from .utils.text_opts import *
from functools import wraps
+from texttable import ansi_len
from collections import namedtuple
from yaml import YAMLError
@@ -24,6 +25,8 @@ import re
import random
import json
import traceback
+import os.path
+
############################ logger #############################
############################ #############################
@@ -32,9 +35,10 @@ import traceback
# logger API for the client
class LoggerApi(object):
# verbose levels
- VERBOSE_QUIET = 0
- VERBOSE_REGULAR = 1
- VERBOSE_HIGH = 2
+ VERBOSE_QUIET = 0
+ VERBOSE_REGULAR_SYNC = 1
+ VERBOSE_REGULAR = 2
+ VERBOSE_HIGH = 3
def __init__(self):
self.level = LoggerApi.VERBOSE_REGULAR
@@ -62,7 +66,7 @@ class LoggerApi(object):
# simple log message with verbose
- def log (self, msg, level = VERBOSE_REGULAR, newline = True):
+ def log (self, msg, level = VERBOSE_REGULAR_SYNC, newline = True):
if not self.check_verbose(level):
return
@@ -90,19 +94,21 @@ class LoggerApi(object):
# supress object getter
- def supress (self):
+ def supress (self, level = VERBOSE_QUIET):
class Supress(object):
- def __init__ (self, logger):
+ def __init__ (self, logger, level):
self.logger = logger
+ self.level = level
def __enter__ (self):
self.saved_level = self.logger.get_verbose()
- self.logger.set_verbose(LoggerApi.VERBOSE_QUIET)
+ if self.level < self.saved_level:
+ self.logger.set_verbose(self.level)
def __exit__ (self, type, value, traceback):
self.logger.set_verbose(self.saved_level)
- return Supress(self)
+ return Supress(self, level)
@@ -175,8 +181,8 @@ class EventsHandler(object):
def on_async_dead (self):
if self.client.connected:
msg = 'Lost connection to server'
- self.__add_event_log('local', 'info', msg, True)
self.client.connected = False
+ self.__add_event_log('local', 'info', msg, True)
def on_async_alive (self):
@@ -322,25 +328,30 @@ class EventsHandler(object):
# port attr changed
elif (event_type == 8):
+
port_id = int(data['port_id'])
- if data['attr'] == self.client.ports[port_id].attr:
- return # false alarm
- old_info = self.client.ports[port_id].get_info()
- self.__async_event_port_attr_changed(port_id, data['attr'])
- new_info = self.client.ports[port_id].get_info()
+
+ diff = self.__async_event_port_attr_changed(port_id, data['attr'])
+ if not diff:
+ return
+
+
ev = "port {0} attributes changed".format(port_id)
- for key, old_val in old_info.items():
- new_val = new_info[key]
- if old_val != new_val:
- ev += '\n {key}: {old} -> {new}'.format(
- key = key,
- old = old_val.lower() if type(old_val) is str else old_val,
- new = new_val.lower() if type(new_val) is str else new_val)
+ for key, (old_val, new_val) in diff.items():
+ ev += '\n {key}: {old} -> {new}'.format(
+ key = key,
+ old = old_val.lower() if type(old_val) is str else old_val,
+ new = new_val.lower() if type(new_val) is str else new_val)
+
show_event = True
-
+
+
+
# server stopped
elif (event_type == 100):
ev = "Server has stopped"
+ # to avoid any new messages on async
+ self.client.async_client.set_as_zombie()
self.__async_event_server_stopped()
show_event = True
@@ -392,7 +403,7 @@ class EventsHandler(object):
def __async_event_port_attr_changed (self, port_id, attr):
if port_id in self.client.ports:
- self.client.ports[port_id].async_event_port_attr_changed(attr)
+ return self.client.ports[port_id].async_event_port_attr_changed(attr)
# add event to log
def __add_event_log (self, origin, ev_type, msg, show = False):
@@ -650,7 +661,7 @@ class STLClient(object):
return rc
-
+
def __add_streams(self, stream_list, port_id_list = None):
port_id_list = self.__ports(port_id_list)
@@ -767,7 +778,7 @@ class STLClient(object):
return rc
- def __push_remote (self, pcap_filename, port_id_list, ipg_usec, speedup, count, duration, is_dual):
+ def __push_remote (self, pcap_filename, port_id_list, ipg_usec, speedup, count, duration, is_dual, min_ipg_usec):
port_id_list = self.__ports(port_id_list)
rc = RC()
@@ -783,7 +794,8 @@ class STLClient(object):
count,
duration,
is_dual,
- slave_handler))
+ slave_handler,
+ min_ipg_usec))
return rc
@@ -799,17 +811,85 @@ class STLClient(object):
return rc
+ def __resolve (self, port_id_list = None, retries = 0):
+ port_id_list = self.__ports(port_id_list)
+
+ rc = RC()
+
+ for port_id in port_id_list:
+ rc.add(self.ports[port_id].arp_resolve(retries))
+
+ return rc
+
+
def __set_port_attr (self, port_id_list = None, attr_dict = None):
port_id_list = self.__ports(port_id_list)
rc = RC()
+ for port_id, port_attr_dict in zip(port_id_list, attr_dict):
+ rc.add(self.ports[port_id].set_attr(**port_attr_dict))
+
+ return rc
+
+
+ def __set_rx_sniffer (self, port_id_list, base_filename, limit):
+ port_id_list = self.__ports(port_id_list)
+ rc = RC()
+
+ for port_id in port_id_list:
+ head, tail = os.path.splitext(base_filename)
+ filename = "{0}-{1}{2}".format(head, port_id, tail)
+ rc.add(self.ports[port_id].set_rx_sniffer(filename, limit))
+
+ return rc
+
+
+ def __remove_rx_sniffer (self, port_id_list):
+ port_id_list = self.__ports(port_id_list)
+ rc = RC()
+
for port_id in port_id_list:
- rc.add(self.ports[port_id].set_attr(attr_dict))
+ rc.add(self.ports[port_id].remove_rx_sniffer())
return rc
+ def __set_rx_queue (self, port_id_list, size):
+ port_id_list = self.__ports(port_id_list)
+ rc = RC()
+ for port_id in port_id_list:
+ rc.add(self.ports[port_id].set_rx_queue(size))
+
+ return rc
+
+ def __remove_rx_queue (self, port_id_list):
+ port_id_list = self.__ports(port_id_list)
+ rc = RC()
+
+ for port_id in port_id_list:
+ rc.add(self.ports[port_id].remove_rx_queue())
+
+ return rc
+
+ def __get_rx_queue_pkts (self, port_id_list):
+ port_id_list = self.__ports(port_id_list)
+ rc = RC()
+
+ for port_id in port_id_list:
+ rc.add(self.ports[port_id].get_rx_queue_pkts())
+
+ return rc
+
+ def __set_service_mode (self, port_id_list, enabled):
+ port_id_list = self.__ports(port_id_list)
+ rc = RC()
+
+ for port_id in port_id_list:
+ rc.add(self.ports[port_id].set_service_mode(enabled))
+
+ return rc
+
# connect to server
def __connect(self):
@@ -1008,7 +1088,7 @@ class STLClient(object):
if not port_id in valid_ports:
raise STLError("Port ID '{0}' is not a valid port ID - valid values: {1}".format(port_id, valid_ports))
- return port_id_list
+ return list_remove_dup(port_id_list)
# transmit request on the RPC link
@@ -1313,6 +1393,18 @@ class STLClient(object):
if port_obj.is_active()]
+ def get_resolvable_ports (self):
+ return [port_id
+ for port_id, port_obj in self.ports.items()
+ if port_obj.is_acquired() and port_obj.get_dst_addr()['ipv4'] is not None]
+
+
+ def get_service_enabled_ports(self):
+ return [port_id
+ for port_id, port_obj in self.ports.items()
+ if port_obj.is_acquired() and port_obj.is_service_mode_on()]
+
+
# get paused ports
def get_paused_ports (self, owned = True):
if owned:
@@ -1338,6 +1430,7 @@ class STLClient(object):
# get stats
+ @__api_check(True)
def get_stats (self, ports = None, sync_now = True):
"""
Return dictionary containing statistics information gathered from the server.
@@ -1585,7 +1678,7 @@ class STLClient(object):
ports = ports if ports is not None else self.get_all_ports()
ports = self._validate_port_list(ports)
- return [self.ports[port_id].get_info() for port_id in ports]
+ return [self.ports[port_id].get_formatted_info() for port_id in ports]
############################ Commands #############################
@@ -1700,6 +1793,9 @@ class STLClient(object):
self.__release(ports)
raise STLError(rc)
+ for port_id in ports:
+ if not self.ports[port_id].is_resolved():
+ self.logger.log(format_text('*** Warning - Port {0} destination is unresolved ***'.format(port_id), 'bold'))
@__api_check(True)
def release (self, ports = None):
@@ -1725,30 +1821,144 @@ class STLClient(object):
if not rc:
raise STLError(rc)
+
+
@__api_check(True)
- def ping(self):
+ def ping_rpc_server(self):
"""
- Pings the server
+ Pings the RPC server
:parameters:
- None
-
+ None
:raises:
+ :exc:`STLError`
"""
-
+
self.logger.pre_cmd("Pinging the server on '{0}' port '{1}': ".format(self.connection_info['server'],
self.connection_info['sync_port']))
rc = self._transmit("ping", api_class = None)
+
+ self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+ @__api_check(True)
+ def set_l2_mode (self, port, dst_mac):
+ """
+ Sets the port mode to L2
+
+ :parameters:
+ port - the port to set the source address
+ dst_mac - destination MAC
+ :raises:
+ + :exc:`STLError`
+ """
+ validate_type('port', port, int)
+ if port not in self.get_all_ports():
+ raise STLError("port {0} is not a valid port id".format(port))
+
+ if not is_valid_mac(dst_mac):
+ raise STLError("dest_mac is not a valid MAC address: '{0}'".format(dst_mac))
+
+ self.logger.pre_cmd("Setting port {0} in L2 mode: ".format(port))
+ rc = self.ports[port].set_l2_mode(dst_mac)
self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+ @__api_check(True)
+ def set_l3_mode (self, port, src_ipv4, dst_ipv4):
+ """
+ Sets the port mode to L3
+ :parameters:
+ port - the port to set the source address
+ src_ipv4 - IPv4 source address for the port
+ dst_ipv4 - IPv4 destination address
+ :raises:
+ + :exc:`STLError`
+ """
+
+ validate_type('port', port, int)
+ if port not in self.get_all_ports():
+ raise STLError("port {0} is not a valid port id".format(port))
+
+ if not is_valid_ipv4(src_ipv4):
+ raise STLError("src_ipv4 is not a valid IPv4 address: '{0}'".format(src_ipv4))
+
+ if not is_valid_ipv4(dst_ipv4):
+ raise STLError("dst_ipv4 is not a valid IPv4 address: '{0}'".format(dst_ipv4))
+
+ self.logger.pre_cmd("Setting port {0} in L3 mode: ".format(port))
+ rc = self.ports[port].set_l3_mode(src_ipv4, dst_ipv4)
+ self.logger.post_cmd(rc)
+
if not rc:
raise STLError(rc)
+
+ # try to resolve
+ with self.logger.supress(level = LoggerApi.VERBOSE_REGULAR_SYNC):
+ self.logger.pre_cmd("ARP resolving address '{0}': ".format(dst_ipv4))
+ rc = self.ports[port].arp_resolve(0)
+ self.logger.post_cmd(rc)
+ if not rc:
+ raise STLError(rc)
+
@__api_check(True)
+ def ping_ip (self, src_port, dst_ipv4, pkt_size = 64, count = 5):
+ """
+ Pings an IP address through a port
+
+ :parameters:
+ src_port - on which port_id to send the ICMP PING request
+ dst_ipv4 - which IP to ping
+ pkt_size - packet size to use
+ count - how many times to ping
+ :raises:
+ + :exc:`STLError`
+
+ """
+ # validate src port
+ validate_type('src_port', src_port, int)
+ if src_port not in self.get_all_ports():
+ raise STLError("src port is not a valid port id")
+
+ if not is_valid_ipv4(dst_ipv4):
+ raise STLError("dst_ipv4 is not a valid IPv4 address: '{0}'".format(dst_ipv4))
+
+ if (pkt_size < 64) or (pkt_size > 9216):
+ raise STLError("pkt_size should be a value between 64 and 9216: '{0}'".format(pkt_size))
+
+ validate_type('count', count, int)
+
+ self.logger.pre_cmd("Pinging {0} from port {1} with {2} bytes of data:".format(dst_ipv4,
+ src_port,
+ pkt_size))
+
+ # no async messages
+ with self.logger.supress(level = LoggerApi.VERBOSE_REGULAR_SYNC):
+ self.logger.log('')
+ for i in range(count):
+ rc = self.ports[src_port].ping(ping_ipv4 = dst_ipv4, pkt_size = pkt_size)
+ if not rc:
+ raise STLError(rc)
+
+ self.logger.log(rc.data())
+
+ if i != (count - 1):
+ time.sleep(1)
+
+
+
+ @__api_check(True)
def server_shutdown (self, force = False):
"""
Sends the server a request for total shutdown
@@ -1795,6 +2005,8 @@ class STLClient(object):
if not rc:
raise STLError(rc)
+ return rc.data()
+
@__api_check(True)
def get_util_stats(self):
"""
@@ -1814,7 +2026,6 @@ class STLClient(object):
@__api_check(True)
def get_xstats(self, port_id):
- print(port_id)
"""
Get extended stats of port: all the counters as dict.
@@ -1833,7 +2044,7 @@ class STLClient(object):
@__api_check(True)
- def reset(self, ports = None):
+ def reset(self, ports = None, restart = False):
"""
Force acquire ports, stop the traffic, remove all streams and clear stats
@@ -1841,7 +2052,9 @@ class STLClient(object):
ports : list
Ports on which to execute the command
-
+ restart: bool
+ Restart the NICs (link down / up)
+
:raises:
+ :exc:`STLError`
@@ -1851,12 +2064,34 @@ class STLClient(object):
ports = ports if ports is not None else self.get_all_ports()
ports = self._validate_port_list(ports)
- # force take the port and ignore any streams on it
- self.acquire(ports, force = True, sync_streams = False)
- self.stop(ports, rx_delay_ms = 0)
- self.remove_all_streams(ports)
- self.clear_stats(ports)
+
+ if restart:
+ self.logger.pre_cmd("Hard resetting ports {0}:".format(ports))
+ else:
+ self.logger.pre_cmd("Resetting ports {0}:".format(ports))
+
+
+ try:
+ with self.logger.supress():
+ # force take the port and ignore any streams on it
+ self.acquire(ports, force = True, sync_streams = False)
+ self.stop(ports, rx_delay_ms = 0)
+ self.remove_all_streams(ports)
+ self.clear_stats(ports)
+ self.set_port_attr(ports,
+ promiscuous = False,
+ link_up = True if restart else None)
+ self.set_service_mode(ports, False)
+ self.remove_rx_sniffer(ports)
+ self.remove_rx_queue(ports)
+
+ except STLError as e:
+ self.logger.post_cmd(False)
+ raise e
+
+ self.logger.post_cmd(RC_OK())
+
@__api_check(True)
def remove_all_streams (self, ports = None):
@@ -1995,7 +2230,28 @@ class STLClient(object):
raise STLError(rc)
-
+ # common checks for start API
+ def __pre_start_check (self, ports, force):
+
+ # verify link status
+ ports_link_down = [port_id for port_id in ports if not self.ports[port_id].is_up()]
+ if ports_link_down and not force:
+ raise STLError("Port(s) %s - link DOWN - check the connection or specify 'force'" % ports_link_down)
+
+ # verify ports are stopped or force stop them
+ active_ports = [port_id for port_id in ports if self.ports[port_id].is_active()]
+ if active_ports and not force:
+ raise STLError("Port(s) {0} are active - please stop them or specify 'force'".format(active_ports))
+
+ # warn if ports are not resolved
+ unresolved_ports = [port_id for port_id in ports if not self.ports[port_id].is_resolved()]
+ if unresolved_ports and not force:
+ raise STLError("Port(s) {0} have unresolved destination addresses - please resolve them or specify 'force'".format(unresolved_ports))
+
+ if self.get_service_enabled_ports() and not force:
+ raise STLError("Port(s) {0} are under service mode - please disable service mode or specify 'force'".format(self.get_service_enabled_ports()))
+
+
@__api_check(True)
def start (self,
ports = None,
@@ -2050,11 +2306,10 @@ class STLClient(object):
validate_type('total', total, bool)
validate_type('core_mask', core_mask, (int, list))
- # verify link status
- ports_link_down = [port_id for port_id in ports if self.ports[port_id].attr.get('link',{}).get('up') == False]
- if not force and ports_link_down:
- raise STLError("Port(s) %s - link DOWN - check the connection or specify 'force'" % ports_link_down)
-
+
+ # some sanity checks before attempting start
+ self.__pre_start_check(ports, force)
+
#########################
# decode core mask argument
decoded_mask = self.__decode_core_mask(ports, core_mask)
@@ -2068,17 +2323,12 @@ class STLClient(object):
raise STLArgumentError('mult', mult)
- # verify ports are stopped or force stop them
+ # stop active ports if needed
active_ports = list(set(self.get_active_ports()).intersection(ports))
- if active_ports:
- if not force:
- raise STLError("Port(s) {0} are active - please stop them or specify 'force'".format(active_ports))
- else:
- rc = self.stop(active_ports)
- if not rc:
- raise STLError(rc)
-
+ if active_ports and force:
+ self.stop(active_ports)
+
# start traffic
self.logger.pre_cmd("Starting traffic on port(s) {0}:".format(ports))
rc = self.__start(mult_obj, duration, ports, force, decoded_mask)
@@ -2115,7 +2365,7 @@ class STLClient(object):
return
ports = self._validate_port_list(ports)
-
+
self.logger.pre_cmd("Stopping traffic on port(s) {0}:".format(ports))
rc = self.__stop(ports)
self.logger.post_cmd(rc)
@@ -2245,7 +2495,8 @@ class STLClient(object):
speedup = 1.0,
count = 1,
duration = -1,
- is_dual = False):
+ is_dual = False,
+ min_ipg_usec = None):
"""
Push a remote server-reachable PCAP file
the path must be fullpath accessible to the server
@@ -2258,7 +2509,8 @@ class STLClient(object):
Ports on which to execute the command
ipg_usec : float
- Inter-packet gap in microseconds
+ Inter-packet gap in microseconds.
+ Exclusive with min_ipg_usec
speedup : float
A factor to adjust IPG. effectively IPG = IPG / speedup
@@ -2275,6 +2527,10 @@ class STLClient(object):
also requires that all the ports will be in master mode
with their adjacent ports as slaves
+ min_ipg_usec : float
+ Minimum inter-packet gap in microseconds to guard from too small ipg.
+ Exclusive with ipg_usec
+
:raises:
+ :exc:`STLError`
@@ -2288,6 +2544,7 @@ class STLClient(object):
validate_type('count', count, int)
validate_type('duration', duration, (float, int))
validate_type('is_dual', is_dual, bool)
+ validate_type('min_ipg_usec', min_ipg_usec, (float, int, type(None)))
# for dual mode check that all are masters
if is_dual:
@@ -2306,7 +2563,7 @@ class STLClient(object):
self.logger.pre_cmd("Pushing remote PCAP on port(s) {0}:".format(ports))
- rc = self.__push_remote(pcap_filename, ports, ipg_usec, speedup, count, duration, is_dual)
+ rc = self.__push_remote(pcap_filename, ports, ipg_usec, speedup, count, duration, is_dual, min_ipg_usec)
self.logger.post_cmd(rc)
if not rc:
@@ -2324,7 +2581,8 @@ class STLClient(object):
force = False,
vm = None,
packet_hook = None,
- is_dual = False):
+ is_dual = False,
+ min_ipg_usec = None):
"""
Push a local PCAP to the server
This is equivalent to loading a PCAP file to a profile
@@ -2340,7 +2598,8 @@ class STLClient(object):
Ports on which to execute the command
ipg_usec : float
- Inter-packet gap in microseconds
+ Inter-packet gap in microseconds.
+ Exclusive with min_ipg_usec
speedup : float
A factor to adjust IPG. effectively IPG = IPG / speedup
@@ -2366,6 +2625,10 @@ class STLClient(object):
also requires that all the ports will be in master mode
with their adjacent ports as slaves
+ min_ipg_usec : float
+ Minimum inter-packet gap in microseconds to guard from too small ipg.
+ Exclusive with ipg_usec
+
:raises:
+ :exc:`STLError`
@@ -2380,6 +2643,9 @@ class STLClient(object):
validate_type('duration', duration, (float, int))
validate_type('vm', vm, (list, type(None)))
validate_type('is_dual', is_dual, bool)
+ validate_type('min_ipg_usec', min_ipg_usec, (float, int, type(None)))
+ if all([ipg_usec, min_ipg_usec]):
+ raise STLError('Please specify either ipg or minimal ipg, not both.')
# no support for > 1MB PCAP - use push remote
@@ -2392,7 +2658,7 @@ class STLClient(object):
slave = port ^ 0x1
if slave in ports:
- raise STLError("dual mode: cannot provide adjacent ports ({0}, {1}) in a batch".format(master, slave))
+ raise STLError("dual mode: please specify only one of adjacent ports ({0}, {1}) in a batch".format(master, slave))
if not slave in self.get_acquired_ports():
raise STLError("dual mode: adjacent port {0} must be owned during dual mode".format(slave))
@@ -2408,7 +2674,8 @@ class STLClient(object):
speedup,
count,
vm = vm,
- packet_hook = packet_hook)
+ packet_hook = packet_hook,
+ min_ipg_usec = min_ipg_usec)
self.logger.post_cmd(RC_OK)
except STLError as e:
self.logger.post_cmd(RC_ERR(e))
@@ -2433,7 +2700,8 @@ class STLClient(object):
count,
vm = vm,
packet_hook = packet_hook,
- split_mode = split_mode)
+ split_mode = split_mode,
+ min_ipg_usec = min_ipg_usec)
self.logger.post_cmd(RC_OK())
@@ -2441,7 +2709,7 @@ class STLClient(object):
self.logger.post_cmd(RC_ERR(e))
raise
- all_ports = ports + [p ^ 0x1 for p in ports]
+ all_ports = ports + [p ^ 0x1 for p in ports if profile_b]
self.remove_all_streams(ports = all_ports)
@@ -2450,7 +2718,8 @@ class STLClient(object):
slave = port ^ 0x1
self.add_streams(profile_a.get_streams(), master)
- self.add_streams(profile_b.get_streams(), slave)
+ if profile_b:
+ self.add_streams(profile_b.get_streams(), slave)
return self.start(ports = all_ports, duration = duration)
@@ -2612,7 +2881,7 @@ class STLClient(object):
while set(self.get_active_ports()).intersection(ports):
# make sure ASYNC thread is still alive - otherwise we will be stuck forever
- if not self.async_client.is_thread_alive():
+ if not self.async_client.is_active():
raise STLError("subscriber thread is dead")
time.sleep(0.01)
@@ -2626,16 +2895,22 @@ class STLClient(object):
@__api_check(True)
- def set_port_attr (self, ports = None, promiscuous = None, link_up = None, led_on = None, flow_ctrl = None):
+ def set_port_attr (self,
+ ports = None,
+ promiscuous = None,
+ link_up = None,
+ led_on = None,
+ flow_ctrl = None,
+ resolve = True):
"""
Set port attributes
:parameters:
- promiscuous - True or False
- link_up - True or False
- led_on - True or False
- flow_ctrl - 0: disable all, 1: enable tx side, 2: enable rx side, 3: full enable
-
+ promiscuous - True or False
+ link_up - True or False
+ led_on - True or False
+ flow_ctrl - 0: disable all, 1: enable tx side, 2: enable rx side, 3: full enable
+ resolve - if true, in case a destination address is configured as IPv4 try to resolve it
:raises:
+ :exe:'STLError'
@@ -2649,29 +2924,221 @@ class STLClient(object):
validate_type('link_up', link_up, (bool, type(None)))
validate_type('led_on', led_on, (bool, type(None)))
validate_type('flow_ctrl', flow_ctrl, (int, type(None)))
+
+ # common attributes for all ports
+ cmn_attr_dict = {}
- # build attributes
- attr_dict = {}
- if promiscuous is not None:
- attr_dict['promiscuous'] = {'enabled': promiscuous}
- if link_up is not None:
- attr_dict['link_status'] = {'up': link_up}
- if led_on is not None:
- attr_dict['led_status'] = {'on': led_on}
- if flow_ctrl is not None:
- attr_dict['flow_ctrl_mode'] = {'mode': flow_ctrl}
+ cmn_attr_dict['promiscuous'] = promiscuous
+ cmn_attr_dict['link_status'] = link_up
+ cmn_attr_dict['led_status'] = led_on
+ cmn_attr_dict['flow_ctrl_mode'] = flow_ctrl
- # no attributes to set
- if not attr_dict:
- return
-
+ # each port starts with a set of the common attributes
+ attr_dict = [dict(cmn_attr_dict) for _ in ports]
+
self.logger.pre_cmd("Applying attributes on port(s) {0}:".format(ports))
rc = self.__set_port_attr(ports, attr_dict)
self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+
+
+ @__api_check(True)
+ def set_service_mode (self, ports = None, enabled = True):
+ """
+ Set service mode for port(s)
+ In service mode ports will respond to ARP, PING and etc.
+
+ :parameters:
+ ports - for which ports to configure service mode on/off
+ enabled - True for activating service mode, False for disabling
+ :raises:
+ + :exe:'STLError'
+
+ """
+ # by default take all acquired ports
+ ports = ports if ports is not None else self.get_acquired_ports()
+ ports = self._validate_port_list(ports)
+
+ if enabled:
+ self.logger.pre_cmd('Enabling service mode on port(s) {0}:'.format(ports))
+ else:
+ self.logger.pre_cmd('Disabling service mode on port(s) {0}:'.format(ports))
+
+ rc = self.__set_service_mode(ports, enabled)
+ self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+ @__api_check(True)
+ def resolve (self, ports = None, retries = 0, verbose = True):
+ """
+ Resolves ports (ARP resolution)
+
+ :parameters:
+ ports - for which ports to apply a unique sniffer (each port gets a unique file)
+ retires - how many times to retry on each port (intervals of 100 milliseconds)
+ verbose - log for each request the response
+ :raises:
+ + :exe:'STLError'
+
+ """
+ # by default - resolve all the ports that are configured with IPv4 dest
+ ports = ports if ports is not None else self.get_resolvable_ports()
+ ports = self._validate_port_list(ports)
+
+ self.logger.pre_cmd('Resolving destination on port(s) {0}:'.format(ports))
+ with self.logger.supress():
+ rc = self.__resolve(ports, retries)
+
+ self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+ # print the ARP transaction
+ if verbose:
+ self.logger.log(rc)
+ self.logger.log('')
+
+
+
+ @__api_check(True)
+ def set_rx_sniffer (self, ports = None, base_filename = 'rx.pcap', limit = 1000):
+ """
+ Sets a RX sniffer for port(s) written to a PCAP file
+
+ :parameters:
+ ports - for which ports to apply a unique sniffer (each port gets a unique file)
+ base_filename - filename will be appended with '-<port_number>', e.g. rx.pcap --> rx-0.pcap, rx-1.pcap etc.
+ limit - limit how many packets will be written
+ :raises:
+ + :exe:'STLError'
+
+ """
+ ports = ports if ports is not None else self.get_acquired_ports()
+ ports = self._validate_port_list(ports)
+
+ # check arguments
+ validate_type('base_filename', base_filename, basestring)
+ validate_type('limit', limit, (int))
+ if limit <= 0:
+ raise STLError("'limit' must be a positive value")
+
+ self.logger.pre_cmd("Setting RX sniffers on port(s) {0}:".format(ports))
+ rc = self.__set_rx_sniffer(ports, base_filename, limit)
+ self.logger.post_cmd(rc)
+
if not rc:
raise STLError(rc)
+
+
+ @__api_check(True)
+ def remove_rx_sniffer (self, ports = None):
+ """
+ Removes RX sniffer from port(s)
+
+ :raises:
+ + :exe:'STLError'
+
+ """
+ ports = ports if ports is not None else self.get_acquired_ports()
+ ports = self._validate_port_list(ports)
+
+ self.logger.pre_cmd("Removing RX sniffers on port(s) {0}:".format(ports))
+ rc = self.__remove_rx_sniffer(ports)
+ self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+ @__api_check(True)
+ def set_rx_queue (self, ports = None, size = 1000):
+ """
+ Sets RX queue for port(s)
+ The queue is cyclic and will hold last 'size' packets
+
+ :parameters:
+ ports - for which ports to apply a queue
+ size - size of the queue
+ :raises:
+ + :exe:'STLError'
+
+ """
+ ports = ports if ports is not None else self.get_acquired_ports()
+ ports = self._validate_port_list(ports)
+
+ # check arguments
+ validate_type('size', size, (int))
+ if size <= 0:
+ raise STLError("'size' must be a positive value")
+
+ self.logger.pre_cmd("Setting RX queue on port(s) {0}:".format(ports))
+ rc = self.__set_rx_queue(ports, size)
+ self.logger.post_cmd(rc)
+
+
+ if not rc:
+ raise STLError(rc)
+
+
+
+ @__api_check(True)
+ def remove_rx_queue (self, ports = None):
+ """
+ Removes RX queue from port(s)
+
+ :parameters:
+ ports - for which ports to remove the RX queue
+ :raises:
+ + :exe:'STLError'
+
+ """
+ ports = ports if ports is not None else self.get_acquired_ports()
+ ports = self._validate_port_list(ports)
+
+ self.logger.pre_cmd("Removing RX queue on port(s) {0}:".format(ports))
+ rc = self.__remove_rx_queue(ports)
+ self.logger.post_cmd(rc)
+
+ if not rc:
+ raise STLError(rc)
+
+
+
+ @__api_check(True)
+ def get_rx_queue_pkts (self, ports = None):
+ """
+ Returns any packets queued on the RX side by the server
+ return value is a dictonary per port
+
+ :parameters:
+ ports - for which ports to fetch
+ """
+
+ ports = ports if ports is not None else self.get_acquired_ports()
+ ports = self._validate_port_list(ports)
+
+ rc = self.__get_rx_queue_pkts(ports)
+ if not rc:
+ raise STLError(rc)
+
+ # decode the data back to the user
+ result = {}
+ for port, r in zip(ports, rc.data()):
+ result[port] = r
+
+ return result
+
+
def clear_events (self):
"""
Clear all events
@@ -2701,7 +3168,7 @@ class STLClient(object):
try:
rc = f(*args)
except STLError as e:
- client.logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold'))
+ client.logger.log("\nAction has failed with the following error:\n" + format_text(e.brief() + "\n", 'bold'))
return RC_ERR(e.brief())
# if got true - print time
@@ -2715,10 +3182,30 @@ class STLClient(object):
@__console
def ping_line (self, line):
- '''pings the server'''
- self.ping()
- return RC_OK()
+ '''pings the server / specific IP'''
+
+ # no parameters - so ping server
+ if not line:
+ self.ping_rpc_server()
+ return True
+
+ parser = parsing_opts.gen_parser(self,
+ "ping",
+ self.ping_line.__doc__,
+ parsing_opts.SINGLE_PORT,
+ parsing_opts.PING_IPV4,
+ parsing_opts.PKT_SIZE,
+ parsing_opts.PING_COUNT)
+ opts = parser.parse_args(line.split())
+ if not opts:
+ return opts
+
+ # IP ping
+ # source ports maps to ports as a single port
+ self.ping_ip(opts.ports[0], opts.ping_ipv4, opts.pkt_size, opts.count)
+
+
@__console
def shutdown_line (self, line):
'''shutdown the server'''
@@ -2846,13 +3333,14 @@ class STLClient(object):
parser = parsing_opts.gen_parser(self,
"reset",
self.reset_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL)
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.PORT_RESTART)
opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True)
if not opts:
return opts
- self.reset(ports = opts.ports)
+ self.reset(ports = opts.ports, restart = opts.restart)
return RC_OK()
@@ -2888,14 +3376,19 @@ class STLClient(object):
# just for sanity - will be checked on the API as well
self.__decode_core_mask(opts.ports, core_mask)
+ # for better use experience - check this first
+ try:
+ self.__pre_start_check(opts.ports, opts.force)
+ except STLError as e:
+ msg = e.brief()
+ self.logger.log(format_text(msg, 'bold'))
+ return RC_ERR(msg)
+
+
+ # stop ports if needed
active_ports = list_intersect(self.get_active_ports(), opts.ports)
- if active_ports:
- if not opts.force:
- msg = "Port(s) {0} are active - please stop them or add '--force'\n".format(active_ports)
- self.logger.log(format_text(msg, 'bold'))
- return RC_ERR(msg)
- else:
- self.stop(active_ports)
+ if active_ports and opts.force:
+ self.stop(active_ports)
# process tunables
@@ -2920,9 +3413,8 @@ class STLClient(object):
self.add_streams(profile.get_streams(), ports = port)
except STLError as e:
- error = 'Unknown error.'
- for line in e.brief().split('\n'):
- if line:
+ for line in e.brief().splitlines():
+ if ansi_len(line.strip()):
error = line
msg = format_text("\nError loading profile '{0}'".format(opts.file[0]), 'bold')
self.logger.log(msg + '\n')
@@ -3171,21 +3663,29 @@ class STLClient(object):
@__console
def push_line (self, line):
'''Push a pcap file '''
+ args = [self,
+ "push",
+ self.push_line.__doc__,
+ parsing_opts.REMOTE_FILE,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.COUNT,
+ parsing_opts.DURATION,
+ parsing_opts.IPG,
+ parsing_opts.MIN_IPG,
+ parsing_opts.SPEEDUP,
+ parsing_opts.FORCE,
+ parsing_opts.DUAL]
+
+ parser = parsing_opts.gen_parser(*(args + [parsing_opts.FILE_PATH_NO_CHECK]))
+ opts = parser.parse_args(line.split(), verify_acquired = True)
- parser = parsing_opts.gen_parser(self,
- "push",
- self.push_line.__doc__,
- parsing_opts.FILE_PATH,
- parsing_opts.REMOTE_FILE,
- parsing_opts.PORT_LIST_WITH_ALL,
- parsing_opts.COUNT,
- parsing_opts.DURATION,
- parsing_opts.IPG,
- parsing_opts.SPEEDUP,
- parsing_opts.FORCE,
- parsing_opts.DUAL)
+ if not opts:
+ return opts
+
+ if not opts.remote:
+ parser = parsing_opts.gen_parser(*(args + [parsing_opts.FILE_PATH]))
+ opts = parser.parse_args(line.split(), verify_acquired = True)
- opts = parser.parse_args(line.split(), verify_acquired = True)
if not opts:
return opts
@@ -3202,22 +3702,24 @@ class STLClient(object):
if opts.remote:
self.push_remote(opts.file[0],
- ports = opts.ports,
- ipg_usec = opts.ipg_usec,
- speedup = opts.speedup,
- count = opts.count,
- duration = opts.duration,
- is_dual = opts.dual)
+ ports = opts.ports,
+ ipg_usec = opts.ipg_usec,
+ min_ipg_usec = opts.min_ipg_usec,
+ speedup = opts.speedup,
+ count = opts.count,
+ duration = opts.duration,
+ is_dual = opts.dual)
else:
self.push_pcap(opts.file[0],
- ports = opts.ports,
- ipg_usec = opts.ipg_usec,
- speedup = opts.speedup,
- count = opts.count,
- duration = opts.duration,
- force = opts.force,
- is_dual = opts.dual)
+ ports = opts.ports,
+ ipg_usec = opts.ipg_usec,
+ min_ipg_usec = opts.min_ipg_usec,
+ speedup = opts.speedup,
+ count = opts.count,
+ duration = opts.duration,
+ force = opts.force,
+ is_dual = opts.dual)
@@ -3230,7 +3732,7 @@ class STLClient(object):
'''Sets port attributes '''
parser = parsing_opts.gen_parser(self,
- "port_attr",
+ "portattr",
self.set_port_attr_line.__doc__,
parsing_opts.PORT_LIST_WITH_ALL,
parsing_opts.PROMISCUOUS,
@@ -3244,18 +3746,18 @@ class STLClient(object):
if not opts:
return opts
- opts.prom = parsing_opts.ON_OFF_DICT.get(opts.prom)
- opts.link = parsing_opts.UP_DOWN_DICT.get(opts.link)
- opts.led = parsing_opts.ON_OFF_DICT.get(opts.led)
- opts.flow_ctrl = parsing_opts.FLOW_CTRL_DICT.get(opts.flow_ctrl)
+ opts.prom = parsing_opts.ON_OFF_DICT.get(opts.prom)
+ opts.link = parsing_opts.UP_DOWN_DICT.get(opts.link)
+ opts.led = parsing_opts.ON_OFF_DICT.get(opts.led)
+ opts.flow_ctrl = parsing_opts.FLOW_CTRL_DICT.get(opts.flow_ctrl)
# if no attributes - fall back to printing the status
- if not filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl, opts.supp]):
+ if not list(filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl, opts.supp])):
self.show_stats_line("--ps --port {0}".format(' '.join(str(port) for port in opts.ports)))
return
if opts.supp:
- info = self.ports[0].get_info() # assume for now all ports are same
+ info = self.ports[0].get_formatted_info() # assume for now all ports are same
print('')
print('Supported attributes for current NICs:')
print(' Promiscuous: yes')
@@ -3264,15 +3766,115 @@ class STLClient(object):
print(' Flow control: %s' % info['fc_supported'])
print('')
else:
- return self.set_port_attr(opts.ports, opts.prom, opts.link, opts.led, opts.flow_ctrl)
+ self.set_port_attr(opts.ports,
+ opts.prom,
+ opts.link,
+ opts.led,
+ opts.flow_ctrl)
+
+
+
+
+ @__console
+ def set_rx_sniffer_line (self, line):
+ '''Sets a port sniffer on RX channel in form of a PCAP file'''
+
+ parser = parsing_opts.gen_parser(self,
+ "set_rx_sniffer",
+ self.set_rx_sniffer_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.OUTPUT_FILENAME,
+ parsing_opts.LIMIT)
+
+ opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True)
+ if not opts:
+ return opts
+
+ self.set_rx_sniffer(opts.ports, opts.output_filename, opts.limit)
+
+ return RC_OK()
+
+
+ @__console
+ def resolve_line (self, line):
+ '''Performs a port ARP resolution'''
+
+ parser = parsing_opts.gen_parser(self,
+ "resolve",
+ self.resolve_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.RETRIES)
+
+ opts = parser.parse_args(line.split(), default_ports = self.get_resolvable_ports(), verify_acquired = True)
+ if not opts:
+ return opts
+
+ ports = list_intersect(opts.ports, self.get_resolvable_ports())
+ if not ports:
+ if not opts.ports:
+ msg = 'resolve - no ports with IPv4 destination'
+ else:
+ msg = 'pause - none of ports {0} are configured with IPv4 destination'.format(opts.ports)
+
+ self.logger.log(msg)
+ return RC_ERR(msg)
+
+ self.resolve(ports = ports, retries = opts.retries)
+
+ return RC_OK()
+
+
+ @__console
+ def set_l2_mode_line (self, line):
+ '''Configures a port in L2 mode'''
+ parser = parsing_opts.gen_parser(self,
+ "port",
+ self.set_l2_mode_line.__doc__,
+ parsing_opts.SINGLE_PORT,
+ parsing_opts.DST_MAC,
+ )
+ opts = parser.parse_args(line.split())
+ if not opts:
+ return opts
+
+
+ # source ports maps to ports as a single port
+ self.set_l2_mode(opts.ports[0], dst_mac = opts.dst_mac)
+
+ return RC_OK()
+
+
+ @__console
+ def set_l3_mode_line (self, line):
+ '''Configures a port in L3 mode'''
+
+ parser = parsing_opts.gen_parser(self,
+ "port",
+ self.set_l3_mode_line.__doc__,
+ parsing_opts.SINGLE_PORT,
+ parsing_opts.SRC_IPV4,
+ parsing_opts.DST_IPV4,
+ )
+
+ opts = parser.parse_args(line.split())
+ if not opts:
+ return opts
+
+
+ # source ports maps to ports as a single port
+ self.set_l3_mode(opts.ports[0], src_ipv4 = opts.src_ipv4, dst_ipv4 = opts.dst_ipv4)
+
+ return RC_OK()
+
+
@__console
def show_profile_line (self, line):
'''Shows profile information'''
parser = parsing_opts.gen_parser(self,
- "port",
+ "profile",
self.show_profile_line.__doc__,
parsing_opts.FILE_PATH)
@@ -3364,7 +3966,38 @@ class STLClient(object):
return "{0}(read-only)>".format(prefix)
elif self.is_all_ports_acquired():
- return "{0}>".format(prefix)
+
+ p = prefix
+
+ if self.get_service_enabled_ports():
+ if self.get_service_enabled_ports() == self.get_acquired_ports():
+ p += '(service)'
+ else:
+ p += '(service: {0})'.format(', '.join(map(str, self.get_service_enabled_ports())))
+
+ return "{0}>".format(p)
else:
- return "{0} {1}>".format(prefix, self.get_acquired_ports())
+ return "{0} (ports: {1})>".format(prefix, ', '.join(map(str, self.get_acquired_ports())))
+
+
+
+ @__console
+ def service_line (self, line):
+ '''Configures port for service mode.
+ In service mode ports will reply to ARP, PING
+ and etc.
+ '''
+
+ parser = parsing_opts.gen_parser(self,
+ "service",
+ self.service_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.SERVICE_OFF)
+
+ opts = parser.parse_args(line.split())
+ if not opts:
+ return opts
+
+ self.set_service_mode(ports = opts.ports, enabled = opts.enabled)
+
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py
index 1461fcec..72c9317a 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py
@@ -32,13 +32,29 @@ class BatchMessage(object):
id, msg = self.rpc_client.create_jsonrpc_v2(method_name, params, api_class, encode = False)
self.batch_list.append(msg)
- def invoke(self, block = False):
+ def invoke(self, block = False, chunk_size = 500000):
if not self.rpc_client.connected:
return RC_ERR("Not connected to server")
- msg = json.dumps(self.batch_list)
-
- return self.rpc_client.send_msg(msg)
+ if chunk_size:
+ response_batch = RC()
+ size = 0
+ new_batch = []
+ for msg in self.batch_list:
+ size += len(json.dumps(msg))
+ new_batch.append(msg)
+ if size > chunk_size:
+ batch_json = json.dumps(new_batch)
+ response_batch.add(self.rpc_client.send_msg(batch_json))
+ size = 0
+ new_batch = []
+ if new_batch:
+ batch_json = json.dumps(new_batch)
+ response_batch.add(self.rpc_client.send_msg(batch_json))
+ return response_batch
+ else:
+ batch_json = json.dumps(self.batch_list)
+ return self.rpc_client.send_msg(batch_json)
# JSON RPC v2.0 client
@@ -130,13 +146,13 @@ class JsonRpcClient(object):
if self.zipper.check_threshold(buffer):
response = self.send_raw_msg(self.zipper.compress(buffer))
- if response:
- response = self.zipper.decompress(response)
else:
response = self.send_raw_msg(buffer)
if not response:
return response
+ elif self.zipper.is_compressed(response):
+ response = self.zipper.decompress(response)
# return to string
response = response.decode()
@@ -172,6 +188,10 @@ class JsonRpcClient(object):
self.disconnect()
return RC_ERR("*** [RPC] - Failed to send message to server")
+ except KeyboardInterrupt as e:
+ # must restore the socket to a sane state
+ self.reconnect()
+ raise e
tries = 0
while True:
@@ -184,6 +204,10 @@ class JsonRpcClient(object):
self.disconnect()
return RC_ERR("*** [RPC] - Failed to get server response from {0}".format(self.transport))
+ except KeyboardInterrupt as e:
+ # must restore the socket to a sane state
+ self.reconnect()
+ raise e
return response
@@ -255,11 +279,6 @@ class JsonRpcClient(object):
self.connected = True
- rc = self.invoke_rpc_method('ping', api_class = None)
- if not rc:
- self.connected = False
- return rc
-
return RC_OK()
@@ -267,12 +286,6 @@ class JsonRpcClient(object):
# connect using current values
return self.connect()
- if not self.connected:
- return RC_ERR("Not connected to server")
-
- # reconnect
- return self.connect(self.server, self.port)
-
def is_connected(self):
return self.connected
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py
index dc06f9fb..6431b74a 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py
@@ -486,7 +486,7 @@ class CTRexScapyPktUtl(object):
if f.name == field_name:
return (l_offset+f.offset,f.get_size_bytes ());
- raise CTRexPacketBuildException(-11, "No layer %s-%d." % (name, save_cnt, field_name));
+ raise CTRexPacketBuildException(-11, "No layer %s-%d." % (field_name, layer_cnt))
def get_layer_offet_by_str(self, layer_des):
"""
@@ -827,6 +827,7 @@ class STLVmFixChecksumHw(CTRexVmDescBase):
self.l3_offset = l3_offset; # could be a name of offset
self.l4_offset = l4_offset; # could be a name of offset
self.l4_type = l4_type
+ self.l2_len = 0
def get_obj (self):
@@ -838,8 +839,8 @@ class STLVmFixChecksumHw(CTRexVmDescBase):
if type(self.l4_offset)==str:
self.l4_offset = parent._pkt_layer_offset(self.l4_offset);
- assert self.l4_offset >= self.l2_len+8, 'l4_offset should be higher than l3_offset offset'
- self.l3_len = self.l4_offset - self.l2_len;
+ assert self.l4_offset >= self.l2_len+8, 'l4_offset should be higher than l3_offset offset'
+ self.l3_len = self.l4_offset - self.l2_len;
class STLVmFixIpv4(CTRexVmDescBase):
@@ -1084,58 +1085,58 @@ class STLVmWrMaskFlowVar(CTRexVmDescBase):
class STLVmTrimPktSize(CTRexVmDescBase):
- """
- Trim the packet size by the stream variable size. This instruction only changes the total packet size, and does not repair the fields to match the new size.
+ def __init__(self,fv_name):
+ """
+ Trim the packet size by the stream variable size. This instruction only changes the total packet size, and does not repair the fields to match the new size.
- :parameters:
- fv_name : string
- Stream variable name. The value of this variable is the new total packet size.
+ :parameters:
+ fv_name : string
+ Stream variable name. The value of this variable is the new total packet size.
- For Example::
+ For Example::
- def create_stream (self):
- # pkt
- p_l2 = Ether();
- p_l3 = IP(src="16.0.0.1",dst="48.0.0.1")
- p_l4 = UDP(dport=12,sport=1025)
- pyld_size = max(0, self.max_pkt_size_l3 - len(p_l3/p_l4));
- base_pkt = p_l2/p_l3/p_l4/('\x55'*(pyld_size))
-
- l3_len_fix =-(len(p_l2));
- l4_len_fix =-(len(p_l2/p_l3));
-
-
- # vm
- vm = STLScVmRaw( [ STLVmFlowVar(name="fv_rand", min_value=64,
- max_value=len(base_pkt),
- size=2, op="inc"),
+ def create_stream (self):
+ # pkt
+ p_l2 = Ether();
+ p_l3 = IP(src="16.0.0.1",dst="48.0.0.1")
+ p_l4 = UDP(dport=12,sport=1025)
+ pyld_size = max(0, self.max_pkt_size_l3 - len(p_l3/p_l4));
+ base_pkt = p_l2/p_l3/p_l4/('\x55'*(pyld_size))
- STLVmTrimPktSize("fv_rand"), # change total packet size <<<
+ l3_len_fix =-(len(p_l2));
+ l4_len_fix =-(len(p_l2/p_l3));
- STLVmWrFlowVar(fv_name="fv_rand",
- pkt_offset= "IP.len",
- add_val=l3_len_fix), # fix ip len
- STLVmFixIpv4(offset = "IP"), # fix checksum
+ # vm
+ vm = STLScVmRaw( [ STLVmFlowVar(name="fv_rand", min_value=64,
+ max_value=len(base_pkt),
+ size=2, op="inc"),
- STLVmWrFlowVar(fv_name="fv_rand",
- pkt_offset= "UDP.len",
- add_val=l4_len_fix) # fix udp len
- ]
- )
-
- pkt = STLPktBuilder(pkt = base_pkt,
- vm = vm)
-
- return STLStream(packet = pkt,
- mode = STLTXCont())
+ STLVmTrimPktSize("fv_rand"), # change total packet size <<<
+ STLVmWrFlowVar(fv_name="fv_rand",
+ pkt_offset= "IP.len",
+ add_val=l3_len_fix), # fix ip len
- """
+ STLVmFixIpv4(offset = "IP"), # fix checksum
+
+ STLVmWrFlowVar(fv_name="fv_rand",
+ pkt_offset= "UDP.len",
+ add_val=l4_len_fix) # fix udp len
+ ]
+ )
+
+ pkt = STLPktBuilder(pkt = base_pkt,
+ vm = vm)
+
+ return STLStream(packet = pkt,
+ mode = STLTXCont())
+
+
+ """
- def __init__(self,fv_name):
super(STLVmTrimPktSize, self).__init__()
self.name = fv_name
validate_type('fv_name', fv_name, str)
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 cec3761f..9eefc177 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
@@ -4,12 +4,14 @@ from collections import namedtuple, OrderedDict
from .trex_stl_packet_builder_scapy import STLPktBuilder
from .trex_stl_streams import STLStream
from .trex_stl_types import *
+from .trex_stl_rx_features import ARPResolver, PingResolver
from . import trex_stl_stats
from .utils.constants import FLOW_CTRL_DICT_REVERSED
import base64
import copy
from datetime import datetime, timedelta
+import threading
StreamOnPort = namedtuple('StreamOnPort', ['compiled_stream', 'metadata'])
@@ -50,7 +52,9 @@ class Port(object):
def __init__ (self, port_id, user, comm_link, session_id, info):
self.port_id = port_id
+
self.state = self.STATE_IDLE
+
self.handler = None
self.comm_link = comm_link
self.transmit = comm_link.transmit
@@ -62,7 +66,7 @@ class Port(object):
self.streams = {}
self.profile = None
self.session_id = session_id
- self.attr = {}
+ self.status = {}
self.port_stats = trex_stl_stats.CPortStats(self)
@@ -72,31 +76,31 @@ class Port(object):
self.owner = ''
self.last_factor_type = None
-
+
+ self.__attr = {}
+ self.attr_lock = threading.Lock()
+
# decorator to verify port is up
def up(func):
- def func_wrapper(*args):
+ def func_wrapper(*args, **kwargs):
port = args[0]
if not port.is_up():
return port.err("{0} - port is down".format(func.__name__))
- return func(*args)
+ return func(*args, **kwargs)
return func_wrapper
# owned
def owned(func):
- def func_wrapper(*args):
+ def func_wrapper(*args, **kwargs):
port = args[0]
- if not port.is_up():
- return port.err("{0} - port is down".format(func.__name__))
-
if not port.is_acquired():
return port.err("{0} - port is not owned".format(func.__name__))
- return func(*args)
+ return func(*args, **kwargs)
return func_wrapper
@@ -106,14 +110,11 @@ class Port(object):
def func_wrapper(*args, **kwargs):
port = args[0]
- if not port.is_up():
- return port.err("{0} - port is down".format(func.__name__))
-
if not port.is_acquired():
return port.err("{0} - port is not owned".format(func.__name__))
if not port.is_writeable():
- return port.err("{0} - port is not in a writeable state".format(func.__name__))
+ return port.err("{0} - port is active, please stop the port before executing command".format(func.__name__))
return func(*args, **kwargs)
@@ -122,22 +123,22 @@ class Port(object):
def err(self, msg):
- return RC_ERR("port {0} : {1}\n".format(self.port_id, msg))
+ return RC_ERR("port {0} : *** {1}".format(self.port_id, msg))
def ok(self, data = ""):
return RC_OK(data)
def get_speed_bps (self):
- return (self.info['speed'] * 1000 * 1000 * 1000)
+ return (self.get_speed_gbps() * 1000 * 1000 * 1000)
- def get_formatted_speed (self):
- return "{0} Gbps".format(self.info['speed'])
+ def get_speed_gbps (self):
+ return self.__attr['speed']
def is_acquired(self):
return (self.handler != None)
def is_up (self):
- return (self.state != self.STATE_DOWN)
+ return self.__attr['link']['up']
def is_active(self):
return (self.state == self.STATE_TX ) or (self.state == self.STATE_PAUSE) or (self.state == self.STATE_PCAP_TX)
@@ -165,7 +166,6 @@ class Port(object):
# take the port
- @up
def acquire(self, force = False, sync_streams = True):
params = {"port_id": self.port_id,
"user": self.user,
@@ -185,7 +185,6 @@ class Port(object):
# sync all the streams with the server
- @up
def sync_streams (self):
params = {"port_id": self.port_id}
@@ -201,7 +200,6 @@ class Port(object):
return self.ok()
# release the port
- @up
def release(self):
params = {"port_id": self.port_id,
"handler": self.handler}
@@ -219,7 +217,6 @@ class Port(object):
- @up
def sync(self):
params = {"port_id": self.port_id}
@@ -250,10 +247,10 @@ class Port(object):
self.next_available_id = int(rc.data()['max_stream_id']) + 1
- # attributes
- self.attr = rc.data()['attr']
- if 'speed' in rc.data():
- self.info['speed'] = rc.data()['speed'] // 1000
+ self.status = rc.data()
+
+ # replace the attributes in a thread safe manner
+ self.set_ts_attr(rc.data()['attr'])
return self.ok()
@@ -424,8 +421,8 @@ class Port(object):
# save this for TUI
self.last_factor_type = mul['type']
-
- return self.ok()
+
+ return rc
# stop traffic
@@ -445,8 +442,9 @@ class Port(object):
return self.err(rc.err())
self.state = self.STATE_STREAMS
+
self.last_factor_type = None
-
+
# timestamp for last tx
self.tx_stopped_ts = datetime.now()
@@ -487,6 +485,122 @@ class Port(object):
return self.ok()
+
+ @owned
+ def set_rx_sniffer (self, pcap_filename, limit):
+
+ if not self.is_service_mode_on():
+ return self.err('port service mode must be enabled for performing RX capturing. Please enable service mode')
+
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "type": "capture",
+ "enabled": True,
+ "pcap_filename": pcap_filename,
+ "limit": limit}
+
+ rc = self.transmit("set_rx_feature", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.ok()
+
+
+ @owned
+ def remove_rx_sniffer (self):
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "type": "capture",
+ "enabled": False}
+
+ rc = self.transmit("set_rx_feature", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.ok()
+
+ @writeable
+ def set_l2_mode (self, dst_mac):
+ if not self.is_service_mode_on():
+ return self.err('port service mode must be enabled for configuring L2 mode. Please enable service mode')
+
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "dst_mac": dst_mac}
+
+ rc = self.transmit("set_l2", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.sync()
+
+
+ @writeable
+ def set_l3_mode (self, src_addr, dest_addr, resolved_mac = None):
+ if not self.is_service_mode_on():
+ return self.err('port service mode must be enabled for configuring L3 mode. Please enable service mode')
+
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "src_addr": src_addr,
+ "dst_addr": dest_addr}
+
+ if resolved_mac:
+ params["resolved_mac"] = resolved_mac
+
+ rc = self.transmit("set_l3", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.sync()
+
+
+ @owned
+ def set_rx_queue (self, size):
+
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "type": "queue",
+ "enabled": True,
+ "size": size}
+
+ rc = self.transmit("set_rx_feature", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.ok()
+
+ @owned
+ def remove_rx_queue (self):
+ params = {"handler": self.handler,
+ "port_id": self.port_id,
+ "type": "queue",
+ "enabled": False}
+
+ rc = self.transmit("set_rx_feature", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ return self.ok()
+
+ @owned
+ def get_rx_queue_pkts (self):
+ params = {"handler": self.handler,
+ "port_id": self.port_id}
+
+ rc = self.transmit("get_rx_queue_pkts", params)
+ if rc.bad():
+ return self.err(rc.err())
+
+ pkts = rc.data()['pkts']
+
+ # decode the packets from base64 to binary
+ for i in range(len(pkts)):
+ pkts[i]['binary'] = base64.b64decode(pkts[i]['binary'])
+
+ return RC_OK(pkts)
+
+
@owned
def pause (self):
@@ -568,23 +682,60 @@ class Port(object):
@owned
- def set_attr (self, attr_dict):
+ def set_attr (self, **kwargs):
+
+ json_attr = {}
+
+ if kwargs.get('promiscuous') is not None:
+ json_attr['promiscuous'] = {'enabled': kwargs.get('promiscuous')}
+
+ if kwargs.get('link_status') is not None:
+ json_attr['link_status'] = {'up': kwargs.get('link_status')}
+
+ if kwargs.get('led_status') is not None:
+ json_attr['led_status'] = {'on': kwargs.get('led_status')}
+
+ if kwargs.get('flow_ctrl_mode') is not None:
+ json_attr['flow_ctrl_mode'] = {'mode': kwargs.get('flow_ctrl_mode')}
+
+ if kwargs.get('rx_filter_mode') is not None:
+ json_attr['rx_filter_mode'] = {'mode': kwargs.get('rx_filter_mode')}
+
params = {"handler": self.handler,
"port_id": self.port_id,
- "attr": attr_dict}
+ "attr": json_attr}
rc = self.transmit("set_port_attr", params)
if rc.bad():
return self.err(rc.err())
+ # update the dictionary from the server explicitly
+ return self.sync()
- #self.attr.update(attr_dict)
-
+
+ @owned
+ def set_service_mode (self, enabled):
+ rc = self.set_attr(rx_filter_mode = 'all' if enabled else 'hw')
+ if not rc:
+ return rc
+
+ if not enabled:
+ rc = self.remove_rx_queue()
+ if not rc:
+ return rc
+
+ rc = self.remove_rx_sniffer()
+ if not rc:
+ return rc
+
return self.ok()
+ def is_service_mode_on (self):
+ return self.get_rx_filter_mode() == 'all'
+
@writeable
- def push_remote (self, pcap_filename, ipg_usec, speedup, count, duration, is_dual, slave_handler):
+ def push_remote (self, pcap_filename, ipg_usec, speedup, count, duration, is_dual, slave_handler, min_ipg_usec):
params = {"handler": self.handler,
"port_id": self.port_id,
@@ -594,7 +745,8 @@ class Port(object):
"count": count,
"duration": duration,
"is_dual": is_dual,
- "slave_handler": slave_handler}
+ "slave_handler": slave_handler,
+ "min_ipg_usec": min_ipg_usec if min_ipg_usec else 0}
rc = self.transmit("push_remote", params)
if rc.bad():
@@ -607,7 +759,16 @@ class Port(object):
def get_profile (self):
return self.profile
-
+ # invalidates the current ARP
+ def invalidate_arp (self):
+ dest = self.__attr['dest']
+
+ if dest['type'] != 'mac':
+ return self.set_attr(dest = dest['ipv4'])
+ else:
+ return self.ok()
+
+
def print_profile (self, mult, duration):
if not self.get_profile():
return
@@ -648,24 +809,32 @@ class Port(object):
format_time(exp_time_factor_sec)))
print("\n")
- # generate port info
- def get_info (self):
+ # generate formatted (console friendly) port info
+ def get_formatted_info (self, sync = True):
+
+ # sync the status
+ if sync:
+ self.sync()
+
+ # get a copy of the current attribute set (safe against manipulation)
+ attr = self.get_ts_attr()
+
info = dict(self.info)
info['status'] = self.get_port_state_name()
- if 'link' in self.attr:
- info['link'] = 'UP' if self.attr['link']['up'] else 'DOWN'
+ if 'link' in attr:
+ info['link'] = 'UP' if attr['link']['up'] else 'DOWN'
else:
info['link'] = 'N/A'
- if 'fc' in self.attr:
- info['fc'] = FLOW_CTRL_DICT_REVERSED.get(self.attr['fc']['mode'], 'N/A')
+ if 'fc' in attr:
+ info['fc'] = FLOW_CTRL_DICT_REVERSED.get(attr['fc']['mode'], 'N/A')
else:
info['fc'] = 'N/A'
- if 'promiscuous' in self.attr:
- info['prom'] = "on" if self.attr['promiscuous']['enabled'] else "off"
+ if 'promiscuous' in attr:
+ info['prom'] = "on" if attr['promiscuous']['enabled'] else "off"
else:
info['prom'] = "N/A"
@@ -692,34 +861,139 @@ class Port(object):
else:
info['is_virtual'] = 'N/A'
+ # speed
+ info['speed'] = self.get_speed_gbps()
+
+ # RX filter mode
+ info['rx_filter_mode'] = 'hardware match' if attr['rx_filter_mode'] == 'hw' else 'fetch all'
+
+ # src MAC and IPv4
+ info['src_mac'] = attr['src_mac']
+ info['src_ipv4'] = attr['src_ipv4']
+
+ if info['src_ipv4'] is None:
+ info['src_ipv4'] = '-'
+
+ # dest
+ dest = attr['dest']
+ if dest['type'] == 'mac':
+ info['dest'] = dest['mac']
+ info['arp'] = '-'
+
+ elif dest['type'] == 'ipv4':
+ info['dest'] = dest['ipv4']
+ info['arp'] = dest['arp']
+
+ elif dest['type'] == 'ipv4_u':
+ info['dest'] = dest['ipv4']
+ info['arp'] = 'unresolved'
+
+
+ # RX info
+ rx_info = self.status['rx_info']
+
+ # RX sniffer
+ sniffer = rx_info['sniffer']
+ info['rx_sniffer'] = '{0}\n[{1} / {2}]'.format(sniffer['pcap_filename'], sniffer['count'], sniffer['limit']) if sniffer['is_active'] else 'off'
+
+
+ # RX queue
+ queue = rx_info['queue']
+ info['rx_queue'] = '[{0} / {1}]'.format(queue['count'], queue['size']) if queue['is_active'] else 'off'
+
+ # Grat ARP
+ grat_arp = rx_info['grat_arp']
+ if grat_arp['is_active']:
+ info['grat_arp'] = "every {0} seconds".format(grat_arp['interval_sec'])
+ else:
+ info['grat_arp'] = "off"
+
+
return info
def get_port_state_name(self):
return self.STATES_MAP.get(self.state, "Unknown")
+ def get_src_addr (self):
+ src_mac = self.__attr['src_mac']
+ src_ipv4 = self.__attr['src_ipv4']
+
+ return {'mac': src_mac, 'ipv4': src_ipv4}
+
+ def get_rx_filter_mode (self):
+ return self.__attr['rx_filter_mode']
+
+ def get_dst_addr (self):
+ dest = self.__attr['dest']
+
+ if dest['type'] == 'mac':
+ return {'ipv4': None, 'mac': dest['mac']}
+
+ elif dest['type'] == 'ipv4':
+ return {'ipv4': dest['ipv4'], 'mac': dest['arp']}
+
+ elif dest['type'] == 'ipv4_u':
+ return {'ipv4': dest['ipv4'], 'mac': None}
+
+ else:
+ assert(0)
+
+
+ # port is considered resolved if it's dest is either MAC or resolved IPv4
+ def is_resolved (self):
+ return (self.get_dst_addr()['mac'] is not None)
+
+ # return True if the port is valid for resolve (has an IPv4 address as dest)
+ def is_resolvable (self):
+ return (self.get_dst_addr()['ipv4'] is not None)
+
+ @writeable
+ def arp_resolve (self, retries):
+ if not self.is_service_mode_on():
+ return self.err('port service mode must be enabled for performing ARP resolution. Please enable service mode')
+
+ return ARPResolver(self).resolve(retries)
+
+ @writeable
+ def ping (self, ping_ipv4, pkt_size):
+ if not self.is_service_mode_on():
+ return self.err('port service mode must be enabled for performing ping. Please enable service mode')
+
+ return PingResolver(self, ping_ipv4, pkt_size).resolve()
+
+
################# stats handler ######################
def generate_port_stats(self):
return self.port_stats.generate_stats()
def generate_port_status(self):
- info = self.get_info()
+ info = self.get_formatted_info()
- return {"driver": info['driver'],
- "description": info.get('description', 'N/A')[:18],
- "HW src mac": info['hw_macaddr'],
- "SW src mac": info['src_macaddr'],
- "SW dst mac": info['dst_macaddr'],
- "PCI Address": info['pci_addr'],
- "NUMA Node": info['numa'],
+ return {"driver": info['driver'],
+ "description": info.get('description', 'N/A')[:18],
+ "src MAC": info['src_mac'],
+ "src IPv4": info['src_ipv4'],
+ "Destination": info['dest'],
+ "ARP Resolution": format_text("{0}".format(info['arp']), 'bold', 'red') if info['arp'] == 'unresolved' else info['arp'],
+ "PCI Address": info['pci_addr'],
+ "NUMA Node": info['numa'],
"--": "",
"---": "",
- "link speed": "{speed} Gb/s".format(speed=info['speed']),
+ "----": "",
+ "-----": "",
+ "link speed": "%g Gb/s" % info['speed'],
"port status": info['status'],
"link status": info['link'],
"promiscuous" : info['prom'],
"flow ctrl" : info['fc'],
+
+ "RX Filter Mode": info['rx_filter_mode'],
+ "RX Queueing": info['rx_queue'],
+ "RX sniffer": info['rx_sniffer'],
+ "Grat ARP": info['grat_arp'],
+
}
def clear_stats(self):
@@ -756,17 +1030,54 @@ class Port(object):
return {"streams" : OrderedDict(sorted(data.items())) }
-
+ ######## attributes are a complex type (dict) that might be manipulated through the async thread #############
+
+ # get in a thread safe manner a duplication of attributes
+ def get_ts_attr (self):
+ with self.attr_lock:
+ return dict(self.__attr)
+
+ # set in a thread safe manner a new dict of attributes
+ def set_ts_attr (self, new_attr):
+ with self.attr_lock:
+ self.__attr = new_attr
+
+
################# 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
+
self.last_factor_type = None
- def async_event_port_attr_changed (self, attr):
- self.info['speed'] = attr['speed'] // 1000
- self.attr = attr
+ def async_event_port_attr_changed (self, new_attr):
+
+ # get a thread safe duplicate
+ cur_attr = self.get_ts_attr()
+
+ # check if anything changed
+ if new_attr == cur_attr:
+ return None
+
+ # generate before
+ before = self.get_formatted_info(sync = False)
+
+ # update
+ self.set_ts_attr(new_attr)
+
+ # generate after
+ after = self.get_formatted_info(sync = False)
+
+ # return diff
+ diff = {}
+ for key, new_value in after.items():
+ old_value = before.get(key, 'N/A')
+ if new_value != old_value:
+ diff[key] = (old_value, new_value)
+
+ return diff
+
# rest of the events are used for TUI / read only sessions
def async_event_port_stopped (self):
@@ -792,3 +1103,4 @@ class Port(object):
def async_event_released (self):
self.owner = ''
+
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py
new file mode 100644
index 00000000..ec83de5d
--- /dev/null
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_rx_features.py
@@ -0,0 +1,255 @@
+
+from .trex_stl_streams import STLStream, STLTXSingleBurst
+from .trex_stl_packet_builder_scapy import STLPktBuilder
+
+from scapy.layers.l2 import Ether, ARP
+from scapy.layers.inet import IP, ICMP
+
+import time
+
+# a generic abstract class for resolving using the server
+class Resolver(object):
+ def __init__ (self, port, queue_size = 100):
+ self.port = port
+
+ # code to execute before sending any request - return RC object
+ def pre_send (self):
+ raise NotImplementedError()
+
+ # return a list of streams for request
+ def generate_request (self):
+ raise NotImplementedError()
+
+ # return None for more packets otherwise RC object
+ def on_pkt_rx (self, pkt):
+ raise NotImplementedError()
+
+ # return value in case of timeout
+ def on_timeout_err (self, retries):
+ raise NotImplementedError()
+
+ ##################### API ######################
+ def resolve (self, retries = 0):
+
+ # first cleanup
+ rc = self.port.remove_all_streams()
+ if not rc:
+ return rc
+
+ # call the specific class implementation
+ rc = self.pre_send()
+ if not rc:
+ return rc
+
+ # start the iteration
+ try:
+
+ # add the stream(s)
+ self.port.add_streams(self.generate_request())
+ rc = self.port.set_rx_queue(size = 100)
+ if not rc:
+ return rc
+
+ return self.resolve_wrapper(retries)
+
+ finally:
+ # best effort restore
+ self.port.remove_rx_queue()
+ self.port.remove_all_streams()
+
+
+ # main resolve function
+ def resolve_wrapper (self, retries):
+
+ # retry for 'retries'
+ index = 0
+ while True:
+ rc = self.resolve_iteration()
+ if rc is not None:
+ return rc
+
+ if index >= retries:
+ return self.on_timeout_err(retries)
+
+ index += 1
+ time.sleep(0.1)
+
+
+
+ def resolve_iteration (self):
+
+ mult = {'op': 'abs', 'type' : 'percentage', 'value': 100}
+ rc = self.port.start(mul = mult, force = False, duration = -1, mask = 0xffffffff)
+ if not rc:
+ return rc
+
+ # save the start timestamp
+ self.start_ts = rc.data()['ts']
+
+ # block until traffic finishes
+ while self.port.is_active():
+ time.sleep(0.01)
+
+ return self.wait_for_rx_response()
+
+
+ def wait_for_rx_response (self):
+
+ # we try to fetch response for 5 times
+ polling = 5
+
+ while polling > 0:
+
+ # fetch the queue
+ rx_pkts = self.port.get_rx_queue_pkts()
+
+ # might be an error
+ if not rx_pkts:
+ return rx_pkts
+
+ # for each packet - examine it
+ for pkt in rx_pkts.data():
+ rc = self.on_pkt_rx(pkt)
+ if rc is not None:
+ return rc
+
+ if polling == 0:
+ return None
+
+ polling -= 1
+ time.sleep(0.1)
+
+
+
+
+
+class ARPResolver(Resolver):
+ def __init__ (self, port_id):
+ super(ARPResolver, self).__init__(port_id)
+
+ # before resolve
+ def pre_send (self):
+ self.dst = self.port.get_dst_addr()
+ self.src = self.port.get_src_addr()
+
+ if self.dst['ipv4'] is None:
+ return self.port.err("Port has a non-IPv4 destination: '{0}'".format(self.dst['mac']))
+
+ if self.src['ipv4'] is None:
+ return self.port.err('Port must have an IPv4 source address configured')
+
+ # invalidate the current ARP resolution (if exists)
+ return self.port.invalidate_arp()
+
+
+ # return a list of streams for request
+ def generate_request (self):
+
+ base_pkt = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(psrc = self.src['ipv4'], pdst = self.dst['ipv4'], hwsrc = self.src['mac'])
+ s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
+
+ return [s1]
+
+
+ # return None in case more packets are needed else the status rc
+ def on_pkt_rx (self, pkt):
+ scapy_pkt = Ether(pkt['binary'])
+ if not 'ARP' in scapy_pkt:
+ return None
+
+ arp = scapy_pkt['ARP']
+
+ # check this is the right ARP (ARP reply with the address)
+ if (arp.op != 2) or (arp.psrc != self.dst['ipv4']):
+ return None
+
+
+ # update the port with L3 full configuration
+ rc = self.port.set_l3_mode(self.src['ipv4'], self.dst['ipv4'], arp.hwsrc)
+ if not rc:
+ return rc
+
+ return self.port.ok('Port {0} - Recieved ARP reply from: {1}, hw: {2}'.format(self.port.port_id, arp.psrc, arp.hwsrc))
+
+
+ def on_timeout_err (self, retries):
+ return self.port.err('failed to receive ARP response ({0} retries)'.format(retries))
+
+
+
+
+ #################### ping resolver ####################
+
+class PingResolver(Resolver):
+ def __init__ (self, port, ping_ip, pkt_size):
+ super(PingResolver, self).__init__(port)
+ self.ping_ip = ping_ip
+ self.pkt_size = pkt_size
+
+ def pre_send (self):
+
+ self.src = self.port.get_src_addr()
+ self.dst = self.port.get_dst_addr()
+
+ if self.src['ipv4'] is None:
+ return self.port.err('Ping - port does not have an IPv4 address configured')
+
+ if self.dst['mac'] is None:
+ return self.port.err('Ping - port has an unresolved destination, cannot determine next hop MAC address')
+
+ return self.port.ok()
+
+
+ # return a list of streams for request
+ def generate_request (self):
+
+ base_pkt = Ether(dst = self.dst['mac'])/IP(src = self.src['ipv4'], dst = self.ping_ip)/ICMP(type = 8)
+ pad = max(0, self.pkt_size - len(base_pkt))
+
+ base_pkt = base_pkt / (pad * 'x')
+
+ #base_pkt.show2()
+ s1 = STLStream( packet = STLPktBuilder(pkt = base_pkt), mode = STLTXSingleBurst(total_pkts = 1) )
+
+ self.base_pkt = base_pkt
+
+ return [s1]
+
+ # return None for more packets otherwise RC object
+ def on_pkt_rx (self, pkt):
+ scapy_pkt = Ether(pkt['binary'])
+ if not 'ICMP' in scapy_pkt:
+ return None
+
+ ip = scapy_pkt['IP']
+ if ip.dst != self.src['ipv4']:
+ return None
+
+ icmp = scapy_pkt['ICMP']
+
+ dt = pkt['ts'] - self.start_ts
+
+ # echo reply
+ if icmp.type == 0:
+ # check seq
+ if icmp.seq != self.base_pkt['ICMP'].seq:
+ return None
+ return self.port.ok('Reply from {0}: bytes={1}, time={2:.2f}ms, TTL={3}'.format(ip.src, len(pkt['binary']), dt * 1000, ip.ttl))
+
+ # unreachable
+ elif icmp.type == 3:
+ # check seq
+ if icmp.payload.seq != self.base_pkt['ICMP'].seq:
+ return None
+ return self.port.ok('Reply from {0}: Destination host unreachable'.format(icmp.src))
+
+ else:
+ # skip any other types
+ #scapy_pkt.show2()
+ return None
+
+
+
+ # return the str of a timeout err
+ def on_timeout_err (self, retries):
+ return self.port.ok('Request timed out.')
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
index 9f601484..c08a0af8 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py
@@ -670,12 +670,19 @@ class CTRexInfoGenerator(object):
("promiscuous", []),
("flow ctrl", []),
("--", []),
- ("HW src mac", []),
- ("SW src mac", []),
- ("SW dst mac", []),
+ ("src IPv4", []),
+ ("src MAC", []),
("---", []),
+ ("Destination", []),
+ ("ARP Resolution", []),
+ ("----", []),
("PCI Address", []),
("NUMA Node", []),
+ ("-----", []),
+ ("RX Filter Mode", []),
+ ("RX Queueing", []),
+ ("RX sniffer", []),
+ ("Grat ARP", []),
]
)
@@ -1103,13 +1110,7 @@ class CPortStats(CTRexStats):
port_state = format_text(port_state, 'bold')
if self._port_obj:
- if 'link' in self._port_obj.attr:
- if self._port_obj.attr.get('link', {}).get('up') == False:
- link_state = format_text('DOWN', 'red', 'bold')
- else:
- link_state = 'UP'
- else:
- link_state = 'N/A'
+ link_state = 'UP' if self._port_obj.is_up() else format_text('DOWN', 'red', 'bold')
else:
link_state = ''
@@ -1130,7 +1131,7 @@ class CPortStats(CTRexStats):
return {"owner": owner,
"state": "{0}".format(port_state),
'link': link_state,
- "speed": self._port_obj.get_formatted_speed() if self._port_obj else '',
+ "speed": "%g Gb/s" % self._port_obj.get_speed_gbps() if self._port_obj else '',
"CPU util.": "{0} {1}%".format(self.get_trend_gui("m_cpu_util", use_raw = True),
format_threshold(round_float(self.get("m_cpu_util")), [85, 100], [0, 85])) if self._port_obj else '' ,
"--": " ",
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 e63f9125..26613e56 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
@@ -987,7 +987,8 @@ class STLProfile(object):
loop_count = 1,
vm = None,
packet_hook = None,
- split_mode = None):
+ split_mode = None,
+ min_ipg_usec = None):
""" Convert a pcap file with a number of packets to a list of connected streams.
packet1->packet2->packet3 etc
@@ -1017,6 +1018,9 @@ class STLProfile(object):
used for dual mode
can be 'MAC' or 'IP'
+ min_ipg_usec : float
+ Minumum inter packet gap in usec. Used to guard from too small IPGs.
+
:return: STLProfile
"""
@@ -1024,9 +1028,15 @@ class STLProfile(object):
# check filename
if not os.path.isfile(pcap_file):
raise STLError("file '{0}' does not exists".format(pcap_file))
+ if speedup <= 0:
+ raise STLError('Speedup should not be negative.')
+ if min_ipg_usec and min_ipg_usec < 0:
+ raise STLError('min_ipg_usec should not be negative.')
+
- # make sure IPG is not less than 1 usec
- if ipg_usec is not None and ipg_usec < 0.001:
+ # make sure IPG is not less than 0.001 usec
+ if (ipg_usec is not None and (ipg_usec < 0.001 * speedup) and
+ (min_ipg_usec is None or min_ipg_usec < 0.001)):
raise STLError("ipg_usec cannot be less than 0.001 usec: '{0}'".format(ipg_usec))
if loop_count < 0:
@@ -1039,6 +1049,7 @@ class STLProfile(object):
pkts = PCAPReader(pcap_file).read_all()
return STLProfile.__pkts_to_streams(pkts,
ipg_usec,
+ min_ipg_usec,
speedup,
loop_count,
vm,
@@ -1046,8 +1057,20 @@ class STLProfile(object):
else:
pkts_a, pkts_b = PCAPReader(pcap_file).read_all(split_mode = split_mode)
+ # swap the packets if a is empty, or the ts of first packet in b is earlier
+ if not pkts_a:
+ pkts_a, pkts_b = pkts_b, pkts_a
+ elif (ipg_usec is None) and pkts_b:
+ meta = pkts_a[0][1]
+ start_time_a = meta[0] * 1e6 + meta[1]
+ meta = pkts_b[0][1]
+ start_time_b = meta[0] * 1e6 + meta[1]
+ if start_time_b < start_time_a:
+ pkts_a, pkts_b = pkts_b, pkts_a
+
profile_a = STLProfile.__pkts_to_streams(pkts_a,
ipg_usec,
+ min_ipg_usec,
speedup,
loop_count,
vm,
@@ -1056,6 +1079,7 @@ class STLProfile(object):
profile_b = STLProfile.__pkts_to_streams(pkts_b,
ipg_usec,
+ min_ipg_usec,
speedup,
loop_count,
vm,
@@ -1070,23 +1094,27 @@ class STLProfile(object):
@staticmethod
- def __pkts_to_streams (pkts, ipg_usec, speedup, loop_count, vm, packet_hook, start_delay_usec = 0):
+ def __pkts_to_streams (pkts, ipg_usec, min_ipg_usec, speedup, loop_count, vm, packet_hook, start_delay_usec = 0):
streams = []
-
- # 10 ms delay before starting the PCAP
- last_ts_usec = -(start_delay_usec)
-
if packet_hook:
pkts = [(packet_hook(cap), meta) for (cap, meta) in pkts]
-
for i, (cap, meta) in enumerate(pkts, start = 1):
# IPG - if not provided, take from cap
- if ipg_usec == None:
- ts_usec = (meta[0] * 1e6 + meta[1]) / float(speedup)
- else:
- ts_usec = (ipg_usec * i) / float(speedup)
+ if ipg_usec is None:
+ packet_time = meta[0] * 1e6 + meta[1]
+ if i == 1:
+ prev_time = packet_time
+ isg = (packet_time - prev_time) / float(speedup)
+ if min_ipg_usec and isg < min_ipg_usec:
+ isg = min_ipg_usec
+ prev_time = packet_time
+ else: # user specified ipg
+ if min_ipg_usec:
+ isg = min_ipg_usec
+ else:
+ isg = ipg_usec / float(speedup)
# handle last packet
if i == len(pkts):
@@ -1100,13 +1128,11 @@ class STLProfile(object):
packet = STLPktBuilder(pkt_buffer = cap, vm = vm),
mode = STLTXSingleBurst(total_pkts = 1, percentage = 100),
self_start = True if (i == 1) else False,
- isg = (ts_usec - last_ts_usec), # seconds to usec
+ isg = isg, # usec
action_count = action_count,
next = next))
-
- last_ts_usec = ts_usec
-
+
profile = STLProfile(streams)
profile.meta = {'type': 'pcap'}
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py
index aa6c4218..a60a7ede 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py
@@ -50,11 +50,25 @@ class RC():
return (e if len(e) != 1 else e[0])
def __str__ (self):
- s = ""
- for x in self.rc_list:
- if x.data:
- s += format_text("\n{0}".format(x.data), 'bold')
- return s
+ if self.good():
+ s = ""
+ for x in self.rc_list:
+ if x.data:
+ s += format_text("\n{0}".format(x.data), 'bold')
+ return s
+ else:
+ show_count = 10
+ err_list = []
+ err_count = 0
+ for x in filter(len, listify(self.err())):
+ err_count += 1
+ if len(err_list) < show_count:
+ err_list.append(format_text(x, 'bold'))
+ s = '\n' if len(err_list) > 1 else ''
+ if err_count > show_count:
+ s += format_text('Occurred %s errors, showing first %s:\n' % (err_count, show_count), 'bold')
+ s += '\n'.join(err_list)
+ return s
def __iter__(self):
return self.rc_list.__iter__()
@@ -135,6 +149,12 @@ def validate_type(arg_name, arg, valid_types):
else:
raise STLError('validate_type: valid_types should be type or list or tuple of types')
+
+def validate_choice (arg_name, arg, choices):
+ if arg is not None and not arg in choices:
+ raise STLError("validate_choice: argument '{0}' can only be one of '{1}'".format(arg_name, choices))
+
+
# throws STLError if not exactly one argument is present
def verify_exclusive_arg (args_list):
if not (len(list(filter(lambda x: x is not None, args_list))) == 1):
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py
index 72ee8972..cbbacb27 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py
@@ -3,6 +3,8 @@ import sys
import string
import random
import time
+import socket
+import re
try:
import pwd
@@ -86,3 +88,23 @@ class PassiveTimer(object):
return (time.time() > self.expr_sec)
+def is_valid_ipv4 (addr):
+ try:
+ socket.inet_pton(socket.AF_INET, addr)
+ return True
+ except (socket.error, TypeError):
+ return False
+
+def is_valid_mac (mac):
+ return bool(re.match("[0-9a-f]{2}([-:])[0-9a-f]{2}(\\1[0-9a-f]{2}){4}$", mac.lower()))
+
+def list_remove_dup (l):
+ tmp = list()
+
+ for x in l:
+ if not x in tmp:
+ tmp.append(x)
+
+ return tmp
+
+
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
index 7eda8635..f5dab30c 100755
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py
@@ -1,6 +1,6 @@
import argparse
from collections import namedtuple, OrderedDict
-from .common import list_intersect, list_difference
+from .common import list_intersect, list_difference, is_valid_ipv4, is_valid_mac, list_remove_dup
from .text_opts import format_text
from ..trex_stl_types import *
from .constants import ON_OFF_DICT, UP_DOWN_DICT, FLOW_CTRL_DICT
@@ -14,56 +14,85 @@ ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options'])
# list of available parsing options
-MULTIPLIER = 1
-MULTIPLIER_STRICT = 2
-PORT_LIST = 3
-ALL_PORTS = 4
-PORT_LIST_WITH_ALL = 5
-FILE_PATH = 6
-FILE_FROM_DB = 7
-SERVER_IP = 8
-STREAM_FROM_PATH_OR_FILE = 9
-DURATION = 10
-FORCE = 11
-DRY_RUN = 12
-XTERM = 13
-TOTAL = 14
-FULL_OUTPUT = 15
-IPG = 16
-SPEEDUP = 17
-COUNT = 18
-PROMISCUOUS = 19
-LINK_STATUS = 20
-LED_STATUS = 21
-TUNABLES = 22
-REMOTE_FILE = 23
-LOCKED = 24
-PIN_CORES = 25
-CORE_MASK = 26
-DUAL = 27
-FLOW_CTRL = 28
-SUPPORTED = 29
-
-GLOBAL_STATS = 50
-PORT_STATS = 51
-PORT_STATUS = 52
-STREAMS_STATS = 53
-STATS_MASK = 54
-CPU_STATS = 55
-MBUF_STATS = 56
-EXTENDED_STATS = 57
-EXTENDED_INC_ZERO_STATS = 58
-
-STREAMS_MASK = 60
-CORE_MASK_GROUP = 61
-
-# ALL_STREAMS = 61
-# STREAM_LIST_WITH_ALL = 62
+_constants = '''
+
+MULTIPLIER
+MULTIPLIER_STRICT
+PORT_LIST
+ALL_PORTS
+PORT_LIST_WITH_ALL
+FILE_PATH
+FILE_FROM_DB
+SERVER_IP
+STREAM_FROM_PATH_OR_FILE
+DURATION
+FORCE
+DRY_RUN
+XTERM
+TOTAL
+FULL_OUTPUT
+IPG
+MIN_IPG
+SPEEDUP
+COUNT
+PROMISCUOUS
+LINK_STATUS
+LED_STATUS
+TUNABLES
+REMOTE_FILE
+LOCKED
+PIN_CORES
+CORE_MASK
+DUAL
+FLOW_CTRL
+SUPPORTED
+FILE_PATH_NO_CHECK
+
+OUTPUT_FILENAME
+LIMIT
+PORT_RESTART
+
+RETRIES
+
+SINGLE_PORT
+DST_MAC
+
+PING_IPV4
+PING_COUNT
+PKT_SIZE
+
+SERVICE_OFF
+
+SRC_IPV4
+DST_IPV4
+
+GLOBAL_STATS
+PORT_STATS
+PORT_STATUS
+STREAMS_STATS
+STATS_MASK
+CPU_STATS
+MBUF_STATS
+EXTENDED_STATS
+EXTENDED_INC_ZERO_STATS
+
+STREAMS_MASK
+CORE_MASK_GROUP
+
+# ALL_STREAMS
+# STREAM_LIST_WITH_ALL
+# list of ArgumentGroup types
+MUTEX
+'''
+
+for index, line in enumerate(_constants.splitlines()):
+ var = line.strip().split()
+ if not var or '#' in var[0]:
+ continue
+ exec('%s = %s' % (var[0], index))
-# list of ArgumentGroup types
-MUTEX = 1
def check_negative(value):
ivalue = int(value)
@@ -217,8 +246,30 @@ def is_valid_file(filename):
return filename
+def check_ipv4_addr (ipv4_str):
+ if not is_valid_ipv4(ipv4_str):
+ raise argparse.ArgumentTypeError("invalid IPv4 address: '{0}'".format(ipv4_str))
+ return ipv4_str
+
+def check_pkt_size (pkt_size):
+ try:
+ pkt_size = int(pkt_size)
+ except ValueError:
+ raise argparse.ArgumentTypeError("invalid packet size type: '{0}'".format(pkt_size))
+
+ if (pkt_size < 64) or (pkt_size > 9216):
+ raise argparse.ArgumentTypeError("invalid packet size: '{0}' - valid range is 64 to 9216".format(pkt_size))
+
+ return pkt_size
+
+def check_mac_addr (addr):
+ if not is_valid_mac(addr):
+ raise argparse.ArgumentTypeError("not a valid MAC address: '{0}'".format(addr))
+
+ return addr
+
def decode_tunables (tunable_str):
tunables = {}
@@ -273,6 +324,11 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'default': None,
'type': float}),
+ MIN_IPG: ArgumentPack(['--min-ipg'],
+ {'help': "Minimal IPG value in usec between packets. Used to guard from too small IPGs.",
+ 'dest': "min_ipg_usec",
+ 'default': None,
+ 'type': float}),
SPEEDUP: ArgumentPack(['-s', '--speedup'],
{'help': "Factor to accelerate the injection. effectively means IPG = IPG / SPEEDUP",
@@ -303,6 +359,53 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'dest': 'flow_ctrl',
'choices': FLOW_CTRL_DICT}),
+ SRC_IPV4: ArgumentPack(['--src'],
+ {'help': 'Configure source IPv4 address',
+ 'dest': 'src_ipv4',
+ 'required': True,
+ 'type': check_ipv4_addr}),
+
+ DST_IPV4: ArgumentPack(['--dst'],
+ {'help': 'Configure destination IPv4 address',
+ 'dest': 'dst_ipv4',
+ 'required': True,
+ 'type': check_ipv4_addr}),
+
+
+ DST_MAC: ArgumentPack(['--dst'],
+ {'help': 'Configure destination MAC address',
+ 'dest': 'dst_mac',
+ 'required': True,
+ 'type': check_mac_addr}),
+
+ RETRIES: ArgumentPack(['-r', '--retries'],
+ {'help': 'retries count [default is zero]',
+ 'dest': 'retries',
+ 'default': 0,
+ 'type': int}),
+
+
+ OUTPUT_FILENAME: ArgumentPack(['-o', '--output'],
+ {'help': 'Output PCAP filename',
+ 'dest': 'output_filename',
+ 'default': None,
+ 'required': True,
+ 'type': str}),
+
+
+ PORT_RESTART: ArgumentPack(['-r', '--restart'],
+ {'help': 'hard restart port(s)',
+ 'dest': 'restart',
+ 'default': False,
+ 'action': 'store_true'}),
+
+ LIMIT: ArgumentPack(['-l', '--limit'],
+ {'help': 'Limit the packet count to be written to the file',
+ 'dest': 'limit',
+ 'default': 1000,
+ 'type': int}),
+
+
SUPPORTED: ArgumentPack(['--supp'],
{'help': 'Show which attributes are supported by current NICs',
'default': None,
@@ -325,6 +428,33 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'help': "A list of ports on which to apply the command",
'default': []}),
+
+ SINGLE_PORT: ArgumentPack(['--port', '-p'],
+ {'dest':'ports',
+ 'type': int,
+ 'metavar': 'PORT',
+ 'help': 'source port for the action',
+ 'required': True}),
+
+ PING_IPV4: ArgumentPack(['-d'],
+ {'help': 'which IPv4 to ping',
+ 'dest': 'ping_ipv4',
+ 'required': True,
+ 'type': check_ipv4_addr}),
+
+ PING_COUNT: ArgumentPack(['-n', '--count'],
+ {'help': 'How many times to ping [default is 5]',
+ 'dest': 'count',
+ 'default': 5,
+ 'type': int}),
+
+ PKT_SIZE: ArgumentPack(['-s'],
+ {'dest':'pkt_size',
+ 'help': 'packet size to use',
+ 'default': 64,
+ 'type': check_pkt_size}),
+
+
ALL_PORTS: ArgumentPack(['-a'],
{"action": "store_true",
"dest": "all_ports",
@@ -362,6 +492,14 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'type': is_valid_file,
'help': "File path to load"}),
+ FILE_PATH_NO_CHECK: ArgumentPack(['-f'],
+ {'metavar': 'FILE',
+ 'dest': 'file',
+ 'nargs': 1,
+ 'required': True,
+ 'type': str,
+ 'help': "File path to load"}),
+
FILE_FROM_DB: ArgumentPack(['--db'],
{'metavar': 'LOADED_STREAM_PACK',
'help': "A stream pack which already loaded into console cache."}),
@@ -447,11 +585,18 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
'default': None,
'help': "Core mask - only cores responding to the bit mask will be active"}),
+ SERVICE_OFF: ArgumentPack(['--off'],
+ {'action': 'store_false',
+ 'dest': 'enabled',
+ 'default': True,
+ 'help': 'Deactivates services on port(s)'}),
+
# advanced options
PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST,
ALL_PORTS],
{'required': False}),
+
STREAM_FROM_PATH_OR_FILE: ArgumentGroup(MUTEX, [FILE_PATH,
FILE_FROM_DB],
{'required': True}),
@@ -515,6 +660,8 @@ class CCmdArgParser(argparse.ArgumentParser):
if not self.has_ports_cfg(opts):
return opts
+ opts.ports = listify(opts.ports)
+
# if all ports are marked or
if (getattr(opts, "all_ports", None) == True) or (getattr(opts, "ports", None) == []):
if default_ports is None:
@@ -522,10 +669,17 @@ class CCmdArgParser(argparse.ArgumentParser):
else:
opts.ports = default_ports
+ opts.ports = list_remove_dup(opts.ports)
+
# so maybe we have ports configured
invalid_ports = list_difference(opts.ports, self.stateless_client.get_all_ports())
if invalid_ports:
- msg = "{0}: port(s) {1} are not valid port IDs".format(self.cmd_name, invalid_ports)
+
+ if len(invalid_ports) > 1:
+ msg = "{0}: port(s) {1} are not valid port IDs".format(self.cmd_name, invalid_ports)
+ else:
+ msg = "{0}: port {1} is not a valid port ID".format(self.cmd_name, invalid_ports[0])
+
self.stateless_client.logger.log(format_text(msg, 'bold'))
return RC_ERR(msg)
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py
index bfb96950..63b05bf4 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py
@@ -27,6 +27,9 @@ class TextCodesStripper:
def strip (s):
return re.sub(TextCodesStripper.pattern, '', s)
+def clear_formatting(s):
+ return TextCodesStripper.strip(s)
+
def format_num (size, suffix = "", compact = True, opts = None):
if opts is None:
opts = ()
@@ -128,11 +131,13 @@ def yellow(text):
def underline(text):
return text_attribute(text, 'underline')
-
+# apply attribute on each non-empty line
def text_attribute(text, attribute):
- return "{start}{txt}{stop}".format(start=TEXT_CODES[attribute]['start'],
- txt=text,
- stop=TEXT_CODES[attribute]['end'])
+ return '\n'.join(['{start}{txt}{end}'.format(
+ start = TEXT_CODES[attribute]['start'],
+ txt = line,
+ end = TEXT_CODES[attribute]['end'])
+ if line else '' for line in ('%s' % text).split('\n')])
FUNC_DICT = {'blue': blue,
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/zipmsg.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/zipmsg.py
index 397ada16..a2a47927 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/zipmsg.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/zipmsg.py
@@ -6,7 +6,7 @@ class ZippedMsg:
MSG_COMPRESS_THRESHOLD = 256
MSG_COMPRESS_HEADER_MAGIC = 0xABE85CEA
- def check_threshold (self, msg):
+ def check_threshold(self, msg):
return len(msg) >= self.MSG_COMPRESS_THRESHOLD
def compress (self, msg):
@@ -16,7 +16,7 @@ class ZippedMsg:
return new_msg
- def decompress (self, msg):
+ def decompress(self, msg):
if len(msg) < 8:
return None
@@ -30,3 +30,15 @@ class ZippedMsg:
return x
+
+ def is_compressed(self, msg):
+ if len(msg) < 8:
+ return False
+
+ t = struct.unpack(">II", msg[:8])
+ if (t[0] != self.MSG_COMPRESS_HEADER_MAGIC):
+ return False
+
+ return True
+
+