From 300ec24108211721d99ac9faf67fd1f91057b95b Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Thu, 2 Jun 2016 16:24:49 +0300 Subject: cpu utilization per thread + mbufs per socket + add in tui --- .../trex_control_plane/server/singleton_daemon.py | 37 +++++---- .../trex_control_plane/stl/console/trex_tui.py | 26 +++++- .../stl/trex_stl_lib/trex_stl_client.py | 24 +++++- .../stl/trex_stl_lib/trex_stl_stats.py | 93 +++++++++++++++++++++- .../stl/trex_stl_lib/utils/parsing_opts.py | 14 +++- 5 files changed, 166 insertions(+), 28 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/trex_control_plane/server/singleton_daemon.py b/scripts/automation/trex_control_plane/server/singleton_daemon.py index 7cfbc3bc..0a3b9c09 100755 --- a/scripts/automation/trex_control_plane/server/singleton_daemon.py +++ b/scripts/automation/trex_control_plane/server/singleton_daemon.py @@ -88,8 +88,7 @@ class SingletonDaemon(object): ret_code, stdout, stderr = run_command('kill -9 %s' % pid) # unconditional kill if ret_code: raise Exception('Failed to run kill -9 command for %s: %s' % (self.name, [ret_code, stdout, stderr])) - poll_rate = 0.1 - for i in range(inr(timeout / poll_rate)): + for i in range(int(timeout / poll_rate)): if not self.is_running(): return True sleep(poll_rate) @@ -105,23 +104,23 @@ class SingletonDaemon(object): raise Exception('No starting command registered for %s' % self.name) if type(self.run_cmd) is types.FunctionType: self.run_cmd() - return - with tempfile.TemporaryFile() as stdout_file, tempfile.TemporaryFile() as stderr_file: - proc = Popen(shlex.split('%s -p %s' % (self.run_cmd, self.port)), cwd = self.dir, close_fds = True, - stdout = stdout_file, stderr = stderr_file) - if timeout > 0: - poll_rate = 0.1 - for i in range(int(timeout/poll_rate)): - sleep(poll_rate) - if bool(proc.poll()): # process ended with error - stdout_file.seek(0) - stderr_file.seek(0) - raise Exception('Run of %s ended unexpectfully: %s' % (self.name, [proc.returncode, stdout_file.read().decode(errors = 'replace'), stderr_file.read().decode(errors = 'replace')])) - elif proc.poll() == 0: # process runs other process, and ended - break - if self.is_running(): - return True - raise Exception('%s failed to run.' % self.name) + else: + with tempfile.TemporaryFile() as stdout_file, tempfile.TemporaryFile() as stderr_file: + proc = Popen(shlex.split('%s -p %s' % (self.run_cmd, self.port)), cwd = self.dir, close_fds = True, + stdout = stdout_file, stderr = stderr_file) + if timeout > 0: + poll_rate = 0.1 + for i in range(int(timeout/poll_rate)): + sleep(poll_rate) + if bool(proc.poll()): # process ended with error + stdout_file.seek(0) + stderr_file.seek(0) + raise Exception('Run of %s ended unexpectfully: %s' % (self.name, [proc.returncode, stdout_file.read().decode(errors = 'replace'), stderr_file.read().decode(errors = 'replace')])) + elif proc.poll() == 0: # process runs other process, and ended + break + if self.is_running(): + return True + raise Exception('%s failed to run.' % self.name) # restart the daemon def restart(self, timeout = 5): 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 d3be4435..a2ffcad6 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -210,7 +210,7 @@ class TrexTUILatencyStats(TrexTUIPanel): super(TrexTUILatencyStats, self).__init__(mng, "lstats") self.key_actions = OrderedDict() self.key_actions['c'] = {'action': self.action_clear, 'legend': 'clear', 'show': True} - self.key_actions['t'] = {'action': self.action_toggle_histogram, 'legend': 'toggle histogram', 'show': True} + self.key_actions['h'] = {'action': self.action_toggle_histogram, 'legend': 'histogram toggle', 'show': True} self.is_histogram = False @@ -238,6 +238,23 @@ class TrexTUILatencyStats(TrexTUIPanel): self.stateless_client.latency_stats.clear_stats() return "" + +# utilization stats +class TrexTUIUtilizationStats(TrexTUIPanel): + def __init__ (self, mng): + super(TrexTUIUtilizationStats, self).__init__(mng, "ustats") + self.key_actions = {} + + def show (self): + stats = self.stateless_client._get_formatted_stats(port_id_list = None, stats_mask = trex_stl_stats.UT_COMPAT) + # print stats to screen + for stat_type, stat_data in stats.items(): + text_tables.print_table_with_header(stat_data.text_table, stat_type) + + def get_key_actions (self): + return self.key_actions + + # log class TrexTUILog(): def __init__ (self): @@ -270,12 +287,14 @@ class TrexTUIPanelManager(): self.panels['dashboard'] = TrexTUIDashBoard(self) self.panels['sstats'] = TrexTUIStreamsStats(self) self.panels['lstats'] = TrexTUILatencyStats(self) + self.panels['ustats'] = TrexTUIUtilizationStats(self) self.key_actions = OrderedDict() self.key_actions['q'] = {'action': self.action_quit, 'legend': 'quit', 'show': True} self.key_actions['d'] = {'action': self.action_show_dash, 'legend': 'dashboard', 'show': True} self.key_actions['s'] = {'action': self.action_show_sstats, 'legend': 'streams', 'show': True} self.key_actions['l'] = {'action': self.action_show_lstats, 'legend': 'latency', 'show': True} + self.key_actions['u'] = {'action': self.action_show_ustats, 'legend': 'util', 'show': True} # start with dashboard self.main_panel = self.panels['dashboard'] @@ -392,6 +411,11 @@ class TrexTUIPanelManager(): self.init(self.show_log) return "" + def action_show_ustats(self): + self.main_panel = self.panels['ustats'] + self.init(self.show_log) + return "" + # shows a textual top style window class TrexTUI(): 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 22895a75..70f38bb0 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 @@ -555,14 +555,17 @@ class STLClient(object): self.latency_stats = trex_stl_stats.CLatencyStats(self.ports) + self.util_stats = trex_stl_stats.CUtilStats(self) + self.stats_generator = trex_stl_stats.CTRexInfoGenerator(self.global_stats, self.ports, self.flow_stats, self.latency_stats, + self.util_stats, self.async_client.monitor) - + ############# private functions - used by the class itself ########### @@ -1519,6 +1522,23 @@ class STLClient(object): if not rc: raise STLError(rc) + @__api_check(True) + def get_util_stats(self): + """ + Get utilization stats: + History of TRex CPU utilization per thread (list of lists) + MBUFs memory consumption per CPU socket. + + :parameters: + None + + :raises: + + :exc:`STLError` + + """ + self.logger.pre_cmd('Getting Utilization stats') + return self.util_stats.get_stats() + @__api_check(True) def reset(self, ports = None): @@ -1739,8 +1759,6 @@ class STLClient(object): raise STLError(rc) - - @__api_check(True) def stop (self, ports = None, rx_delay_ms = 10): """ 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 94a45577..06009ac3 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 @@ -11,7 +11,6 @@ import datetime import time import re import math -import copy import threading import pprint @@ -22,13 +21,16 @@ PORT_STATUS = 'ps' STREAMS_STATS = 's' LATENCY_STATS = 'ls' LATENCY_HISTOGRAM = 'lh' +CPU_STATS = 'c' +MBUF_STATS = 'm' -ALL_STATS_OPTS = [GLOBAL_STATS, PORT_STATS, PORT_STATUS, STREAMS_STATS, LATENCY_STATS, PORT_GRAPH, LATENCY_HISTOGRAM] +ALL_STATS_OPTS = [GLOBAL_STATS, PORT_STATS, PORT_STATUS, STREAMS_STATS, LATENCY_STATS, PORT_GRAPH, LATENCY_HISTOGRAM, CPU_STATS, MBUF_STATS] COMPACT = [GLOBAL_STATS, PORT_STATS] GRAPH_PORT_COMPACT = [GLOBAL_STATS, PORT_GRAPH] SS_COMPAT = [GLOBAL_STATS, STREAMS_STATS] # stream stats LS_COMPAT = [GLOBAL_STATS, LATENCY_STATS] # latency stats LH_COMPAT = [GLOBAL_STATS, LATENCY_HISTOGRAM] # latency histogram +UT_COMPAT = [GLOBAL_STATS, CPU_STATS, MBUF_STATS] # utilization ExportableStats = namedtuple('ExportableStats', ['raw_data', 'text_table']) @@ -104,6 +106,13 @@ def calculate_diff_raw (samples): return total +# used to sort '64b', '9kb' etc. +def key_cmp_bytes(val): + multiplier = 1 + if 'kb' in val: + multiplier = 1000 + return multiplier * int(val.replace('k', '').replace('b', '')) + # a simple object to keep a watch over a field class WatchedField(object): @@ -138,11 +147,12 @@ class CTRexInfoGenerator(object): STLClient and the ports. """ - def __init__(self, global_stats_ref, ports_dict_ref, rx_stats_ref, latency_stats_ref, async_monitor): + def __init__(self, global_stats_ref, ports_dict_ref, rx_stats_ref, latency_stats_ref, util_stats_ref, async_monitor): self._global_stats = global_stats_ref self._ports_dict = ports_dict_ref self._rx_stats_ref = rx_stats_ref self._latency_stats_ref = latency_stats_ref + self._util_stats_ref = util_stats_ref self._async_monitor = async_monitor def generate_single_statistic(self, port_id_list, statistic_type): @@ -167,6 +177,12 @@ class CTRexInfoGenerator(object): elif statistic_type == LATENCY_HISTOGRAM: return self._generate_latency_histogram() + elif statistic_type == CPU_STATS: + return self._generate_cpu_util_stats() + + elif statistic_type == MBUF_STATS: + return self._generate_mbuf_util_stats() + else: # ignore by returning empty object return {} @@ -404,6 +420,59 @@ class CTRexInfoGenerator(object): stats_table.header(header) return {"latency_histogram": ExportableStats(None, stats_table)} + def _generate_cpu_util_stats(self): + util_stats = self._util_stats_ref.get_stats() + if not util_stats or 'cpu' not in util_stats: + raise Exception("Excepting 'cpu' section in stats %s" % util_stats) + cpu_stats = util_stats['cpu'] + hist_len = len(cpu_stats[0]) + avg_len = min(5, hist_len) + show_len = min(15, hist_len) + stats_table = text_tables.TRexTextTable() + stats_table.header(['Thread', 'Avg', 'Latest'] + list(range(-1, 0 - show_len, -1))) + stats_table.set_cols_align(['l'] + ['r'] * (show_len + 1)) + stats_table.set_cols_width([8, 3, 6] + [3] * (show_len - 1)) + stats_table.set_cols_dtype(['t'] * (show_len + 2)) + for i in range(min(14, len(cpu_stats))): + avg = int(round(sum(cpu_stats[i][:avg_len]) / avg_len)) + stats_table.add_row([i, avg] + cpu_stats[i][:show_len]) + return {'cpu_util(%)': ExportableStats(None, stats_table)} + + def _generate_mbuf_util_stats(self): + util_stats = self._util_stats_ref.get_stats() + if not util_stats or 'mbuf_stats' not in util_stats: + raise Exception("Excepting 'mbuf_stats' section in stats %s" % util_stats) + mbuf_stats = util_stats['mbuf_stats'] + for mbufs_per_socket in mbuf_stats.values(): + first_socket_mbufs = mbufs_per_socket + break + if not self._util_stats_ref.mbuf_types_list: + mbuf_keys = list(first_socket_mbufs.keys()) + mbuf_keys.sort(key = key_cmp_bytes) + self._util_stats_ref.mbuf_types_list = mbuf_keys + types_len = len(self._util_stats_ref.mbuf_types_list) + stats_table = text_tables.TRexTextTable() + stats_table.set_cols_align(['l'] + ['r'] * types_len) + stats_table.set_cols_width([10] + [7] * types_len) + stats_table.set_cols_dtype(['t'] * (types_len + 1)) + stats_table.header([''] + self._util_stats_ref.mbuf_types_list) + total_list = [] + for mbuf_type in self._util_stats_ref.mbuf_types_list: + total_list.append(first_socket_mbufs[mbuf_type][1]) + stats_table.add_row(['Total:'] + total_list) + stats_table.add_row(['Used:'] + [''] * types_len) + for socket_name, mbufs in mbuf_stats.items(): + socket_show_name = socket_name.replace('cpu-', '').replace('-', ' ').capitalize() + ':' + used_list = [] + percentage_list = [] + for mbuf_type in self._util_stats_ref.mbuf_types_list: + used = mbufs[mbuf_type][1] - mbufs[mbuf_type][0] + used_list.append(used) + percentage_list.append('%s%%' % int(100 * used / mbufs[mbuf_type][1])) + stats_table.add_row([socket_show_name] + used_list) + stats_table.add_row(['Percent:'] + percentage_list) + return {'mbuf_util': ExportableStats(None, stats_table)} + @staticmethod def _get_rational_block_char(value, range_start, interval): # in Konsole, utf-8 is sometimes printed with artifacts, return ascii for now @@ -1280,7 +1349,23 @@ class CRxStats(CTRexStats): return stats - +class CUtilStats(CTRexStats): + + def __init__(self, client): + super(CUtilStats, self).__init__() + self.client = client + self.history = deque(maxlen = 1) + self.mbuf_types_list = None + + def get_stats(self, force = False): + time_now = time.time() + if self.last_update_ts + 1 < time_now or not self.history or force: + rc = self.client._transmit('get_utilization') + if not rc: + raise Exception(rc) + self.last_update_ts = time_now + self.history.append(rc.data()) + return self.history[-1] if __name__ == "__main__": pass 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 98e3ca6a..a435e54e 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 @@ -39,6 +39,8 @@ PORT_STATS = 51 PORT_STATUS = 52 STREAMS_STATS = 53 STATS_MASK = 54 +CPU_STATS = 55 +MBUF_STATS = 56 STREAMS_MASK = 60 # ALL_STREAMS = 61 @@ -346,6 +348,14 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'action': 'store_true', 'help': "Fetch only streams stats"}), + CPU_STATS: ArgumentPack(['-c'], + {'action': 'store_true', + 'help': "Fetch only CPU utilization stats"}), + + MBUF_STATS: ArgumentPack(['-m'], + {'action': 'store_true', + 'help': "Fetch only MBUF utilization stats"}), + STREAMS_MASK: ArgumentPack(['--streams'], {"nargs": '+', 'dest':'streams', @@ -371,7 +381,9 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], STATS_MASK: ArgumentGroup(MUTEX, [GLOBAL_STATS, PORT_STATS, PORT_STATUS, - STREAMS_STATS], + STREAMS_STATS, + CPU_STATS, + MBUF_STATS], {}) } -- cgit 1.2.3-korg From 5216c6a22a2324bf555b590b34594a63d8aeeb7d Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Sun, 5 Jun 2016 13:19:54 +0300 Subject: cpu utilization Python get_stats replace "force" flag with "use_1sec_cache", default False. --- .../trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'scripts') 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 132d34ee..d23dd33f 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 @@ -421,7 +421,7 @@ class CTRexInfoGenerator(object): return {"latency_histogram": ExportableStats(None, stats_table)} def _generate_cpu_util_stats(self): - util_stats = self._util_stats_ref.get_stats() + util_stats = self._util_stats_ref.get_stats(use_1sec_cache = True) if not util_stats or 'cpu' not in util_stats: raise Exception("Excepting 'cpu' section in stats %s" % util_stats) cpu_stats = util_stats['cpu'] @@ -439,7 +439,7 @@ class CTRexInfoGenerator(object): return {'cpu_util(%)': ExportableStats(None, stats_table)} def _generate_mbuf_util_stats(self): - util_stats = self._util_stats_ref.get_stats() + util_stats = self._util_stats_ref.get_stats(use_1sec_cache = True) if not util_stats or 'mbuf_stats' not in util_stats: raise Exception("Excepting 'mbuf_stats' section in stats %s" % util_stats) mbuf_stats = util_stats['mbuf_stats'] @@ -1358,9 +1358,9 @@ class CUtilStats(CTRexStats): self.history = deque(maxlen = 1) self.mbuf_types_list = None - def get_stats(self, force = False): + def get_stats(self, use_1sec_cache = False): time_now = time.time() - if self.last_update_ts + 1 < time_now or not self.history or force: + if self.last_update_ts + 1 < time_now or not self.history or not use_1sec_cache: rc = self.client._transmit('get_utilization') if not rc: raise Exception(rc) -- cgit 1.2.3-korg From 1eed7e59f23d3ab9b957d9822eefe72877e291da Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Mon, 6 Jun 2016 14:32:36 +0300 Subject: MBUF utilization: add size of RAM (MB) the MBUFs take --- .../stl/trex_stl_lib/trex_stl_stats.py | 124 ++++++++++++--------- 1 file changed, 71 insertions(+), 53 deletions(-) (limited to 'scripts') 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 d23dd33f..990a40da 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 @@ -106,12 +106,12 @@ def calculate_diff_raw (samples): return total -# used to sort '64b', '9kb' etc. -def key_cmp_bytes(val): - multiplier = 1 - if 'kb' in val: - multiplier = 1000 - return multiplier * int(val.replace('k', '').replace('b', '')) +get_number_of_bytes_cache = {} +# get number of bytes: '64b'->64, '9kb'->9000 etc. +def get_number_of_bytes(val): + if val not in get_number_of_bytes_cache: + get_number_of_bytes_cache[val] = int(val[:-1].replace('k', '000')) + return get_number_of_bytes_cache[val] # a simple object to keep a watch over a field class WatchedField(object): @@ -422,55 +422,69 @@ class CTRexInfoGenerator(object): def _generate_cpu_util_stats(self): util_stats = self._util_stats_ref.get_stats(use_1sec_cache = True) - if not util_stats or 'cpu' not in util_stats: - raise Exception("Excepting 'cpu' section in stats %s" % util_stats) - cpu_stats = util_stats['cpu'] - hist_len = len(cpu_stats[0]) - avg_len = min(5, hist_len) - show_len = min(15, hist_len) stats_table = text_tables.TRexTextTable() - stats_table.header(['Thread', 'Avg', 'Latest'] + list(range(-1, 0 - show_len, -1))) - stats_table.set_cols_align(['l'] + ['r'] * (show_len + 1)) - stats_table.set_cols_width([8, 3, 6] + [3] * (show_len - 1)) - stats_table.set_cols_dtype(['t'] * (show_len + 2)) - for i in range(min(14, len(cpu_stats))): - avg = int(round(sum(cpu_stats[i][:avg_len]) / avg_len)) - stats_table.add_row([i, avg] + cpu_stats[i][:show_len]) + if util_stats: + if 'cpu' not in util_stats: + raise Exception("Excepting 'cpu' section in stats %s" % util_stats) + cpu_stats = util_stats['cpu'] + hist_len = len(cpu_stats[0]) + avg_len = min(5, hist_len) + show_len = min(15, hist_len) + stats_table.header(['Thread', 'Avg', 'Latest'] + list(range(-1, 0 - show_len, -1))) + stats_table.set_cols_align(['l'] + ['r'] * (show_len + 1)) + stats_table.set_cols_width([8, 3, 6] + [3] * (show_len - 1)) + stats_table.set_cols_dtype(['t'] * (show_len + 2)) + for i in range(min(14, len(cpu_stats))): + avg = int(round(sum(cpu_stats[i][:avg_len]) / avg_len)) + stats_table.add_row([i, avg] + cpu_stats[i][:show_len]) + else: + stats_table.add_row(['No Data.']) return {'cpu_util(%)': ExportableStats(None, stats_table)} def _generate_mbuf_util_stats(self): util_stats = self._util_stats_ref.get_stats(use_1sec_cache = True) - if not util_stats or 'mbuf_stats' not in util_stats: - raise Exception("Excepting 'mbuf_stats' section in stats %s" % util_stats) - mbuf_stats = util_stats['mbuf_stats'] - for mbufs_per_socket in mbuf_stats.values(): - first_socket_mbufs = mbufs_per_socket - break - if not self._util_stats_ref.mbuf_types_list: - mbuf_keys = list(first_socket_mbufs.keys()) - mbuf_keys.sort(key = key_cmp_bytes) - self._util_stats_ref.mbuf_types_list = mbuf_keys - types_len = len(self._util_stats_ref.mbuf_types_list) stats_table = text_tables.TRexTextTable() - stats_table.set_cols_align(['l'] + ['r'] * types_len) - stats_table.set_cols_width([10] + [7] * types_len) - stats_table.set_cols_dtype(['t'] * (types_len + 1)) - stats_table.header([''] + self._util_stats_ref.mbuf_types_list) - total_list = [] - for mbuf_type in self._util_stats_ref.mbuf_types_list: - total_list.append(first_socket_mbufs[mbuf_type][1]) - stats_table.add_row(['Total:'] + total_list) - stats_table.add_row(['Used:'] + [''] * types_len) - for socket_name, mbufs in mbuf_stats.items(): - socket_show_name = socket_name.replace('cpu-', '').replace('-', ' ').capitalize() + ':' - used_list = [] - percentage_list = [] + if util_stats: + if 'mbuf_stats' not in util_stats: + raise Exception("Excepting 'mbuf_stats' section in stats %s" % util_stats) + mbuf_stats = util_stats['mbuf_stats'] + for mbufs_per_socket in mbuf_stats.values(): + first_socket_mbufs = mbufs_per_socket + break + if not self._util_stats_ref.mbuf_types_list: + mbuf_keys = list(first_socket_mbufs.keys()) + mbuf_keys.sort(key = get_number_of_bytes) + self._util_stats_ref.mbuf_types_list = mbuf_keys + types_len = len(self._util_stats_ref.mbuf_types_list) + stats_table.set_cols_align(['l'] + ['r'] * (types_len + 1)) + stats_table.set_cols_width([10] + [7] * (types_len + 1)) + stats_table.set_cols_dtype(['t'] * (types_len + 2)) + stats_table.header([''] + self._util_stats_ref.mbuf_types_list + ['RAM(MB)']) + total_list = [] + sum_totals = 0 for mbuf_type in self._util_stats_ref.mbuf_types_list: - used = mbufs[mbuf_type][1] - mbufs[mbuf_type][0] - used_list.append(used) - percentage_list.append('%s%%' % int(100 * used / mbufs[mbuf_type][1])) - stats_table.add_row([socket_show_name] + used_list) - stats_table.add_row(['Percent:'] + percentage_list) + sum_totals += first_socket_mbufs[mbuf_type][1] * get_number_of_bytes(mbuf_type) + 64 + total_list.append(first_socket_mbufs[mbuf_type][1]) + sum_totals *= len(list(mbuf_stats.values())) + total_list.append(int(sum_totals/1e6)) + stats_table.add_row(['Total:'] + total_list) + stats_table.add_row(['Used:'] + [''] * (types_len + 1)) + for socket_name in sorted(list(mbuf_stats.keys())): + mbufs = mbuf_stats[socket_name] + socket_show_name = socket_name.replace('cpu-', '').replace('-', ' ').capitalize() + ':' + sum_used = 0 + used_list = [] + percentage_list = [] + for mbuf_type in self._util_stats_ref.mbuf_types_list: + used = mbufs[mbuf_type][1] - mbufs[mbuf_type][0] + sum_used += used * get_number_of_bytes(mbuf_type) + 64 + used_list.append(used) + percentage_list.append('%s%%' % int(100 * used / mbufs[mbuf_type][1])) + used_list.append(int(sum_used/1e6)) + stats_table.add_row([socket_show_name] + used_list) + stats_table.add_row(['Percent:'] + percentage_list + ['']) + else: + stats_table.add_row(['No Data.']) return {'mbuf_util': ExportableStats(None, stats_table)} @staticmethod @@ -1357,15 +1371,19 @@ class CUtilStats(CTRexStats): self.client = client self.history = deque(maxlen = 1) self.mbuf_types_list = None + self.last_update_ts = 0 def get_stats(self, use_1sec_cache = False): time_now = time.time() if self.last_update_ts + 1 < time_now or not self.history or not use_1sec_cache: - rc = self.client._transmit('get_utilization') - if not rc: - raise Exception(rc) - self.last_update_ts = time_now - self.history.append(rc.data()) + if self.client.is_connected(): + rc = self.client._transmit('get_utilization') + if not rc: + raise Exception(rc) + self.last_update_ts = time_now + self.history.append(rc.data()) + else: + self.history.append({}) return self.history[-1] if __name__ == "__main__": -- cgit 1.2.3-korg From e5ec923e6d837cc5e2d77b7ac93bd652d2563d92 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Wed, 8 Jun 2016 18:06:07 +0300 Subject: regression: fix representation of errors in setUpClass() --- scripts/automation/regression/trex_unit_test.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'scripts') diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py index 83650164..c6aeee4d 100755 --- a/scripts/automation/regression/trex_unit_test.py +++ b/scripts/automation/regression/trex_unit_test.py @@ -48,6 +48,13 @@ import re import time from distutils.dir_util import mkpath +# override nose's strange representation of setUpClass errors +def __suite_repr__(self): + return "%s.%s" % (nose.suite._strclass(self.context), getattr(self.context, '__name__', self.context)) + +nose.suite.ContextSuite.__repr__ = __suite_repr__ +nose.suite.ContextSuite.__str__ = __suite_repr__ + def check_trex_path(trex_path): if os.path.isfile('%s/trex_daemon_server' % trex_path): return os.path.abspath(trex_path) -- cgit 1.2.3-korg From 07d5732a49bab856ab773e4a4762a7983cb5a538 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Thu, 9 Jun 2016 11:55:31 +0300 Subject: typo (GitHub pull request #14) --- scripts/automation/trex_control_plane/stl/trex_stl_lib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/__init__.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/__init__.py index ba9459c1..c6e14df3 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/__init__.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/__init__.py @@ -1,7 +1,7 @@ import sys if sys.version_info < (2, 7): - print("\n**** TRex STL pacakge requires Python version >= 2.7 ***\n") + print("\n**** TRex STL package requires Python version >= 2.7 ***\n") exit(-1) from . import trex_stl_ext -- cgit 1.2.3-korg From a78ab130e5e344989e2de9c62e061180416bae12 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Thu, 9 Jun 2016 19:42:50 +0300 Subject: fix TRex output not redirected --- scripts/automation/regression/trex_unit_test.py | 1 + .../server/trex_launch_thread.py | 88 +++++++++++----------- .../trex_control_plane/server/trex_server.py | 5 +- 3 files changed, 48 insertions(+), 46 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py index c6aeee4d..606556d3 100755 --- a/scripts/automation/regression/trex_unit_test.py +++ b/scripts/automation/regression/trex_unit_test.py @@ -209,6 +209,7 @@ class CTRexTestConfiguringPlugin(Plugin): if not res: print('Could not restart TRex daemon server') sys.exit(-1) + print('Restarted.') if self.kill_running: CTRexScenario.trex.kill_all_trexes() diff --git a/scripts/automation/trex_control_plane/server/trex_launch_thread.py b/scripts/automation/trex_control_plane/server/trex_launch_thread.py index 82a7f996..22606753 100755 --- a/scripts/automation/trex_control_plane/server/trex_launch_thread.py +++ b/scripts/automation/trex_control_plane/server/trex_launch_thread.py @@ -30,49 +30,51 @@ class AsynchronousTRexSession(threading.Thread): self.trexObj.zmq_dump = {} def run (self): - - with open(os.devnull, 'w') as DEVNULL: - self.time_stamps['start'] = self.time_stamps['run_time'] = time.time() - self.session = subprocess.Popen(shlex.split(self.cmd), cwd = self.launch_path, stdin = DEVNULL, stderr = subprocess.PIPE, preexec_fn=os.setsid, close_fds = True) - logger.info("TRex session initialized successfully, Parent process pid is {pid}.".format( pid = self.session.pid )) - while self.session.poll() is None: # subprocess is NOT finished - time.sleep(0.5) - if self.stoprequest.is_set(): - logger.debug("Abort request received by handling thread. Terminating TRex session." ) - os.killpg(self.session.pid, signal.SIGUSR1) - self.trexObj.set_status(TRexStatus.Idle) - self.trexObj.set_verbose_status("TRex is Idle") - break - - self.time_stamps['run_time'] = time.time() - self.time_stamps['start'] - - try: - if self.time_stamps['run_time'] < 5: - logger.error("TRex run failed due to wrong input parameters, or due to readability issues.") - self.trexObj.set_verbose_status("TRex run failed due to wrong input parameters, or due to readability issues.\n\nTRex command: {cmd}\n\nRun output:\n{output}".format( - cmd = self.cmd, output = self.load_trex_output(self.export_path))) - self.trexObj.errcode = -11 - elif (self.session.returncode is not None and self.session.returncode != 0) or ( (self.time_stamps['run_time'] < self.duration) and (not self.stoprequest.is_set()) ): - if (self.session.returncode is not None and self.session.returncode != 0): - logger.debug("Failed TRex run due to session return code ({ret_code})".format( ret_code = self.session.returncode ) ) - elif ( (self.time_stamps['run_time'] < self.duration) and not self.stoprequest.is_set()): - logger.debug("Failed TRex run due to running time ({runtime}) combined with no-stopping request.".format( runtime = self.time_stamps['run_time'] ) ) - - logger.warning("TRex run was terminated unexpectedly by outer process or by the hosting OS") - self.trexObj.set_verbose_status("TRex run was terminated unexpectedly by outer process or by the hosting OS.\n\nRun output:\n{output}".format( - output = self.load_trex_output(self.export_path))) - self.trexObj.errcode = -15 - else: - logger.info("TRex run session finished.") - self.trexObj.set_verbose_status('TRex finished.') - self.trexObj.errcode = None - - finally: - self.trexObj.set_status(TRexStatus.Idle) - logger.info("TRex running state changed to 'Idle'.") - self.trexObj.expect_trex.clear() - logger.debug("Finished handling a single run of TRex.") - self.trexObj.zmq_dump = None + try: + with open(self.export_path, 'w') as output_file: + self.time_stamps['start'] = self.time_stamps['run_time'] = time.time() + self.session = subprocess.Popen(shlex.split(self.cmd), cwd = self.launch_path, stdout = output_file, preexec_fn=os.setsid, close_fds = True) + logger.info("TRex session initialized successfully, Parent process pid is {pid}.".format( pid = self.session.pid )) + while self.session.poll() is None: # subprocess is NOT finished + time.sleep(0.5) + if self.stoprequest.is_set(): + logger.debug("Abort request received by handling thread. Terminating TRex session." ) + os.killpg(self.session.pid, signal.SIGUSR1) + self.trexObj.set_status(TRexStatus.Idle) + self.trexObj.set_verbose_status("TRex is Idle") + break + except Exception as e: + logger.error(e) + + self.time_stamps['run_time'] = time.time() - self.time_stamps['start'] + + try: + if self.time_stamps['run_time'] < 5: + logger.error("TRex run failed due to wrong input parameters, or due to readability issues.") + self.trexObj.set_verbose_status("TRex run failed due to wrong input parameters, or due to readability issues.\n\nTRex command: {cmd}\n\nRun output:\n{output}".format( + cmd = self.cmd, output = self.load_trex_output(self.export_path))) + self.trexObj.errcode = -11 + elif (self.session.returncode is not None and self.session.returncode != 0) or ( (self.time_stamps['run_time'] < self.duration) and (not self.stoprequest.is_set()) ): + if (self.session.returncode is not None and self.session.returncode != 0): + logger.debug("Failed TRex run due to session return code ({ret_code})".format( ret_code = self.session.returncode ) ) + elif ( (self.time_stamps['run_time'] < self.duration) and not self.stoprequest.is_set()): + logger.debug("Failed TRex run due to running time ({runtime}) combined with no-stopping request.".format( runtime = self.time_stamps['run_time'] ) ) + + logger.warning("TRex run was terminated unexpectedly by outer process or by the hosting OS") + self.trexObj.set_verbose_status("TRex run was terminated unexpectedly by outer process or by the hosting OS.\n\nRun output:\n{output}".format( + output = self.load_trex_output(self.export_path))) + self.trexObj.errcode = -15 + else: + logger.info("TRex run session finished.") + self.trexObj.set_verbose_status('TRex finished.') + self.trexObj.errcode = None + + finally: + self.trexObj.set_status(TRexStatus.Idle) + logger.info("TRex running state changed to 'Idle'.") + self.trexObj.expect_trex.clear() + logger.debug("Finished handling a single run of TRex.") + self.trexObj.zmq_dump = None def join (self, timeout = None): self.stoprequest.set() diff --git a/scripts/automation/trex_control_plane/server/trex_server.py b/scripts/automation/trex_control_plane/server/trex_server.py index 8f7e99f0..5e278a74 100755 --- a/scripts/automation/trex_control_plane/server/trex_server.py +++ b/scripts/automation/trex_control_plane/server/trex_server.py @@ -466,12 +466,11 @@ class CTRexServer(object): if 'd' not in kwargs: raise Exception('Argument -d should be specified in stateful command') - cmd = "{nice}{run_command} --iom {io} {cmd_options} --no-key > {export}".format( # -- iom 0 disables the periodic log to the screen (not needed) + cmd = "{nice}{run_command} --iom {io} {cmd_options} --no-key".format( # -- iom 0 disables the periodic log to the screen (not needed) nice = '' if self.trex_nice == 0 else 'nice -n %s ' % self.trex_nice, run_command = self.TREX_START_CMD, cmd_options = trex_cmd_options, - io = iom, - export = export_path ) + io = iom) logger.info("TREX FULL COMMAND: {command}".format(command = cmd) ) -- cgit 1.2.3-korg From 0dc80ee5d7888675db28902a52efacb69dbd3954 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Fri, 10 Jun 2016 00:50:41 +0300 Subject: master daemon logger fixes --- scripts/master_daemon.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) (limited to 'scripts') diff --git a/scripts/master_daemon.py b/scripts/master_daemon.py index 390db0a3..3f130bdb 100755 --- a/scripts/master_daemon.py +++ b/scripts/master_daemon.py @@ -12,13 +12,12 @@ from glob import glob import signal sys.path.append(os.path.join('automation', 'trex_control_plane', 'server')) +import CCustomLogger import outer_packages from singleton_daemon import SingletonDaemon, register_socket, run_command from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer import termstyle -logger = logging.getLogger('Master daemon') - ### Server functions ### def check_connectivity(): @@ -90,22 +89,21 @@ def start_master_daemon(): fail(termstyle.red('Master daemon failed to run. Please look in log: %s' % logging_file)) def set_logger(): + log_dir = os.path.dirname(logging_file) + if not os.path.exists(log_dir): + os.makedirs(log_dir) if os.path.exists(logging_file): if os.path.exists(logging_file_bu): os.unlink(logging_file_bu) os.rename(logging_file, logging_file_bu) - hdlr = logging.FileHandler(logging_file) - formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s', datefmt = '%Y-%m-%d %H:%M:%S') - hdlr.setFormatter(formatter) - logger.addHandler(hdlr) - logger.setLevel(logging.INFO) + CCustomLogger.setup_daemon_logger('Master daemon', logging_file) def start_master_daemon_func(): try: set_logger() register_socket(master_daemon.tag) server = SimpleJSONRPCServer(('0.0.0.0', master_daemon.port)) - logger.info('Started master daemon (port %s)' % master_daemon.port) + logging.info('Started master daemon (port %s)' % master_daemon.port) server.register_function(add) server.register_function(check_connectivity) server.register_function(get_trex_path) @@ -125,10 +123,10 @@ def start_master_daemon_func(): signal.signal(signal.SIGTERM, stop_handler) server.serve_forever() except Exception as e: - logger.error('Closing due to error: %s' % e) + logging.error('Closing due to error: %s' % e) def stop_handler(*args, **kwargs): - logger.info('Got killed explicitly.') + logging.info('Got killed explicitly.') sys.exit(0) # returns True if given path is under current dir or /tmp -- cgit 1.2.3-korg From 661d54e1fa7c946757204ce364eb2e5312c5487f Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Tue, 14 Jun 2016 13:43:09 +0300 Subject: add some gdb scripts --- scripts/gdb_script.txt | 3 +++ scripts/gdb_script1.txt | 16 ++++++++++++++++ scripts/t-rex-64-debug-gdb-bt | 12 ++++++++++++ scripts/t-rex-64-debug-gdb-core | 12 ++++++++++++ 4 files changed, 43 insertions(+) create mode 100644 scripts/gdb_script.txt create mode 100644 scripts/gdb_script1.txt create mode 100644 scripts/t-rex-64-debug-gdb-bt create mode 100644 scripts/t-rex-64-debug-gdb-core (limited to 'scripts') diff --git a/scripts/gdb_script.txt b/scripts/gdb_script.txt new file mode 100644 index 00000000..d6a7d22c --- /dev/null +++ b/scripts/gdb_script.txt @@ -0,0 +1,3 @@ +run +bt full + diff --git a/scripts/gdb_script1.txt b/scripts/gdb_script1.txt new file mode 100644 index 00000000..b6854743 --- /dev/null +++ b/scripts/gdb_script1.txt @@ -0,0 +1,16 @@ +run +set logging overwrite on +set logging file gdb.bt +set logging on +set pagination off +echo backtrace +bt +bt full +gcore +info registers +set logging off +quit + + + + diff --git a/scripts/t-rex-64-debug-gdb-bt b/scripts/t-rex-64-debug-gdb-bt new file mode 100644 index 00000000..2d64ce62 --- /dev/null +++ b/scripts/t-rex-64-debug-gdb-bt @@ -0,0 +1,12 @@ +#! /bin/bash +export LD_LIBRARY_PATH=`pwd` +/bin/gdb --batch --command=gdb_script1.txt --args ./_t-rex-64-debug $@ +RESULT=$? + +if [ $RESULT -ne 0 ]; then + exit $RESULT +fi + + + + diff --git a/scripts/t-rex-64-debug-gdb-core b/scripts/t-rex-64-debug-gdb-core new file mode 100644 index 00000000..cc790448 --- /dev/null +++ b/scripts/t-rex-64-debug-gdb-core @@ -0,0 +1,12 @@ +#! /bin/bash +export LD_LIBRARY_PATH=`pwd` +/bin/gdb ./_t-rex-64-debug $@ +RESULT=$? + +if [ $RESULT -ne 0 ]; then + exit $RESULT +fi + + + + -- cgit 1.2.3-korg From 14774ed2eabf8a6f218e8674a04ee7d8ce373d64 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Tue, 14 Jun 2016 13:46:59 +0300 Subject: add simple script for testing crash --- scripts/simple_start_server.py | 150 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 scripts/simple_start_server.py (limited to 'scripts') diff --git a/scripts/simple_start_server.py b/scripts/simple_start_server.py new file mode 100644 index 00000000..2a3908cb --- /dev/null +++ b/scripts/simple_start_server.py @@ -0,0 +1,150 @@ +#!/usr/bin/python +import os +import sys +from time import time, sleep +import shlex +import threading +import subprocess +import multiprocessing +import tempfile +import fnmatch + + +sys.path.append('automation/trex_control_plane/stl') +from trex_stl_lib.api import * + +def run_server(command): + return subprocess.Popen(shlex.split(command), stdout = subprocess.PIPE, stderr = subprocess.PIPE, close_fds = True) + + +def run_command(command, timeout = 15, cwd = None): + # pipes might stuck, even with timeout + with tempfile.TemporaryFile() as stdout_file, tempfile.TemporaryFile() as stderr_file: + proc = subprocess.Popen(shlex.split(command), stdout = stdout_file, stderr = stderr_file, cwd = cwd, close_fds = True) + if timeout > 0: + poll_rate = 0.1 + for i in range(int(timeout/poll_rate)): + sleep(poll_rate) + if proc.poll() is not None: # process stopped + break + if proc.poll() is None: + proc.kill() # timeout + return (errno.ETIME, '', 'Timeout on running: %s' % command) + else: + proc.wait() + stdout_file.seek(0) + stderr_file.seek(0) + return (proc.returncode, stdout_file.read().decode(errors = 'replace'), stderr_file.read().decode(errors = 'replace')) + +def get_trex_cmds(): + ret_code, stdout, stderr = run_command('ps -u root --format pid,comm,cmd') + if ret_code: + raise Exception('Failed to determine running processes, stderr: %s' % stderr) + trex_cmds_list = [] + for line in stdout.splitlines(): + pid, proc_name, full_cmd = line.strip().split(' ', 2) + pid = pid.strip() + full_cmd = full_cmd.strip() + if proc_name.find('t-rex-64') >= 0: + trex_cmds_list.append((pid, full_cmd)) + else: + if full_cmd.find('t-rex-64') >= 0: + trex_cmds_list.append((pid, full_cmd)) + + return trex_cmds_list + +def is_any_core (): + ret_code, stdout, stderr = run_command('ls') + assert(ret_code==0); + l= stdout.split() + for file in l: + if fnmatch.fnmatch(file, 'core.*'): + return True + return False + + +def kill_all_trexes(): + trex_cmds_list = get_trex_cmds() + if not trex_cmds_list: + return False + for pid, cmd in trex_cmds_list: + run_command('kill %s' % pid) + ret_code_ps, _, _ = run_command('ps -p %s' % pid) + if not ret_code_ps: + run_command('kill -9 %s' % pid) + ret_code_ps, _, _ = run_command('ps -p %s' % pid) + if not ret_code_ps: + pass; + return True + +def term_all_trexes(): + trex_cmds_list = get_trex_cmds() + if not trex_cmds_list: + return False + for pid, cmd in trex_cmds_list: + print pid + run_command('kill -INT %s' % pid) + return True + + + +def run_one_iter (): + try: + server = run_server('./t-rex-64-debug-gdb-bt -i -c 4 --iom 0') + + print "sleep 1 sec" + time.sleep(1); + crash=True; + + if True: + c = STLClient() + print 'Connecting to server' + c.connect() + print 'Connected' + + print 'Mapping' + print 'Map: %s' % stl_map_ports(c) + c.disconnect() + crash=False; + + except Exception as e: + print(e) + finally : + if crash: + print "Crash seen, wait for the info" + # wait the process to make the core file + loop=0; + while True: + if server.poll() is not None: # server ended + print 'Server stopped.\nReturn code: %s\nStderr: %s\nStdout: %s' % (server.returncode, server.stdout.read().decode(errors = 'replace'), server.stderr.read().decode(errors = 'replace')) + break; + time.sleep(1); + loop=loop+1; + if loop >600: + print "Timeout on crash!!" + break; + return 1 + else: + print "kill process ",server.pid + term_all_trexes(); + kill_all_trexes(); + return 0 + + +def loop_inter (): + kill_all_trexes() + cnt=0; + while True: + + print (time.strftime("%H:%M:%S")), + print "Iter",cnt + ret=run_one_iter () + if ret==1: + break; + cnt=cnt+1; + if is_any_core (): + print "stop due to core file" + break; + +loop_inter () + -- cgit 1.2.3-korg From 6824df7cf39cfaee127033f48cebf6de3f3e5b56 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Thu, 16 Jun 2016 15:45:20 +0300 Subject: fix python doc strings --- scripts/automation/trex_control_plane/doc_stl/conf.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/automation/trex_control_plane/doc_stl/conf.py b/scripts/automation/trex_control_plane/doc_stl/conf.py index 45738b6e..c8788ca7 100644 --- a/scripts/automation/trex_control_plane/doc_stl/conf.py +++ b/scripts/automation/trex_control_plane/doc_stl/conf.py @@ -15,6 +15,21 @@ import sys import os import shlex +import functools + +def no_op_wraps(func): + """Replaces functools.wraps in order to undo wrapping. + + Can be used to preserve the decorated function's signature + in the documentation generated by Sphinx. + + """ + def wrapper(decorator): + return func + return wrapper + +functools.wraps = no_op_wraps + # If extensions (or modules to document with autodoc) are in another directory, @@ -309,4 +324,5 @@ autoclass_content = "both" # A workaround for the responsive tables always having annoying scrollbars. def setup(app): - app.add_stylesheet("no_scrollbars.css") \ No newline at end of file + app.add_stylesheet("no_scrollbars.css") + -- cgit 1.2.3-korg From 3207f14a91cc01b44b28ba541e5968f58a7e5ec2 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Fri, 17 Jun 2016 15:39:00 +0300 Subject: add ctrl+c as first try to kill daemons/TRex. master daemon change directory to "/" to deal with case it's directory was deleted and update_trex() was used. remove check connection to daemons as part of init of client (can be done manually per needed daemon) --- .../trex_control_plane/server/singleton_daemon.py | 31 +++++----- .../trex_control_plane/server/trex_server.py | 19 ++---- .../stf/trex_stf_lib/trex_client.py | 67 +++++++++------------- scripts/master_daemon.py | 11 ++-- 4 files changed, 52 insertions(+), 76 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/trex_control_plane/server/singleton_daemon.py b/scripts/automation/trex_control_plane/server/singleton_daemon.py index 8fdedc6e..79deace1 100755 --- a/scripts/automation/trex_control_plane/server/singleton_daemon.py +++ b/scripts/automation/trex_control_plane/server/singleton_daemon.py @@ -2,6 +2,7 @@ import errno import os import shlex import socket +import signal import tempfile import types from subprocess import Popen @@ -73,31 +74,27 @@ class SingletonDaemon(object): if pid: return pid - - # kill daemon - def kill(self, timeout = 10): - pid = self.get_pid() - if not pid: - return False - ret_code, stdout, stderr = run_command('kill %s' % pid) # usual kill - if ret_code: - raise Exception('Failed to run kill command for %s: %s' % (self.name, [ret_code, stdout, stderr])) + def kill_by_signal(self, pid, signal_name, timeout): + os.kill(pid, signal_name) poll_rate = 0.1 for i in range(int(timeout / poll_rate)): if not self.is_running(): return True sleep(poll_rate) - ret_code, stdout, stderr = run_command('kill -9 %s' % pid) # unconditional kill - if ret_code: - raise Exception('Failed to run kill -9 command for %s: %s' % (self.name, [ret_code, stdout, stderr])) - for i in range(int(timeout / poll_rate)): - if not self.is_running(): + + # kill daemon, with verification + def kill(self, timeout = 15): + pid = self.get_pid() + if not pid: + return False + # try Ctrl+C, usual kill, kill -9 + for signal_name in [signal.SIGINT, signal.SIGTERM, signal.SIGKILL]: + if self.kill_by_signal(pid, signal_name, timeout): return True - sleep(poll_rate) raise Exception('Could not kill %s, even with -9' % self.name) # try connection as RPC client, return True upon success, False if fail - def check_connectivity(self, timeout = 5): + def check_connectivity(self, timeout = 15): daemon = jsonrpclib.Server('http://127.0.0.1:%s/' % self.port) poll_rate = 0.1 for i in range(int(timeout/poll_rate)): @@ -140,7 +137,7 @@ class SingletonDaemon(object): raise Exception('%s failed to run.' % self.name) # restart the daemon - def restart(self, timeout = 5): + def restart(self, timeout = 15): if self.is_running(): self.kill(timeout) return self.start(timeout) diff --git a/scripts/automation/trex_control_plane/server/trex_server.py b/scripts/automation/trex_control_plane/server/trex_server.py index 5e278a74..3da629ec 100755 --- a/scripts/automation/trex_control_plane/server/trex_server.py +++ b/scripts/automation/trex_control_plane/server/trex_server.py @@ -387,23 +387,14 @@ class CTRexServer(object): return trex_cmds_list - def kill_all_trexes(self): + # Silently tries to kill TRexes with given signal. + # Responsibility of client to verify with get_trex_cmds. + def kill_all_trexes(self, signal_name): logger.info('Processing kill_all_trexes() command.') trex_cmds_list = self.get_trex_cmds() - if not trex_cmds_list: - return False for pid, cmd in trex_cmds_list: - logger.info('Killing process %s %s' % (pid, cmd)) - run_command('kill %s' % pid) - ret_code_ps, _, _ = run_command('ps -p %s' % pid) - if not ret_code_ps: - logger.info('Killing with -9.') - run_command('kill -9 %s' % pid) - ret_code_ps, _, _ = run_command('ps -p %s' % pid) - if not ret_code_ps: - logger.info('Could not kill process.') - raise Exception('Could not kill process %s %s' % (pid, cmd)) - return True + logger.info('Killing with signal %s process %s %s' % (signal_name, pid, cmd)) + os.kill(int(pid), signal_name) def wait_until_kickoff_finish (self, timeout = 40): 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 3b01560a..e1b298db 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 @@ -13,6 +13,7 @@ from distutils.util import strtobool from collections import deque, OrderedDict from json import JSONDecoder import traceback +import signal try: from . import outer_packages @@ -92,42 +93,9 @@ class CTRexClient(object): self.decoder = JSONDecoder() self.history = jsonrpclib.history.History() self.master_daemon_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = master_daemon_port ) + self.master_daemon = jsonrpclib.Server(self.master_daemon_path, history = self.history) self.trex_server_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = trex_daemon_port ) - self.connect_master() - self.connect_server() - - - def connect_master(self): - ''' - Connects to Master daemon via JsonRPC. - This daemon controls TRex daemon server. - Return true if success, false if fail - ''' - try: - print('Connecting to Master daemon @ %s ...' % self.master_daemon_path) - self.master_daemon = jsonrpclib.Server(self.master_daemon_path, history = self.history) - self.check_master_connectivity() - print('Connected to Master daemon.') - return True - except Exception as e: - print(e) - return False - - def connect_server(self): - ''' - Connects to TRex daemon server via JsonRPC. - This daemon controls TRex. (start/stop) - Return true if success, false if fail - ''' - try: - print('Connecting to TRex daemon server @ %s ...' % self.trex_server_path) - self.server = jsonrpclib.Server(self.trex_server_path, history = self.history) - self.check_server_connectivity() - print('Connected TRex server daemon.') - return True - except Exception as e: - print(e) - return False + self.server = jsonrpclib.Server(self.trex_server_path, history = self.history) def add (self, x, y): @@ -322,18 +290,28 @@ class CTRexClient(object): finally: self.prompt_verbose_data() - def kill_all_trexes(self): + def kill_all_trexes(self, timeout = 15): """ Kills running TRex processes (if exists) on the server, not only owned by current daemon. Raises exception upon error killing. :return: - + **True** if any process killed + + **True** if processes killed/not running + **False** otherwise. """ try: - return self.server.kill_all_trexes() + poll_rate = 0.1 + # try Ctrl+C, usual kill, -9 + for signal_name in [signal.SIGINT, signal.SIGTERM, signal.SIGKILL]: + self.server.kill_all_trexes(signal_name) + for i in range(int(timeout / poll_rate)): + if not self.get_trex_cmds(): + return True + sleep(poll_rate) + if self.get_trex_cmds(): + return False + return True except AppError as err: self._handle_AppError_exception(err.args[0]) finally: @@ -1518,8 +1496,15 @@ class CTRexResult(object): return result - - if __name__ == "__main__": - pass + c = CTRexClient('127.0.0.1') + print('restarting daemon') + c.restart_trex_daemon() + print('kill any running') + c.kill_all_trexes() + print('start') + c.start_stateless() + print('sleep') + time.sleep(5) + print('done') diff --git a/scripts/master_daemon.py b/scripts/master_daemon.py index 3f130bdb..e50d49ee 100755 --- a/scripts/master_daemon.py +++ b/scripts/master_daemon.py @@ -119,14 +119,16 @@ def start_master_daemon_func(): server.register_function(stl_rpc_proxy.start, 'start_stl_rpc_proxy') server.register_function(stl_rpc_proxy.stop, 'stop_stl_rpc_proxy') server.register_function(server.funcs.keys, 'get_methods') # should be last - signal.signal(signal.SIGTSTP, stop_handler) - signal.signal(signal.SIGTERM, stop_handler) + signal.signal(signal.SIGTSTP, stop_handler) # ctrl+z + signal.signal(signal.SIGTERM, stop_handler) # kill server.serve_forever() + except KeyboardInterrupt: + logging.info('Ctrl+C') except Exception as e: logging.error('Closing due to error: %s' % e) -def stop_handler(*args, **kwargs): - logging.info('Got killed explicitly.') +def stop_handler(signalnum, *args, **kwargs): + logging.info('Got signal %s, exiting.' % signalnum) sys.exit(0) # returns True if given path is under current dir or /tmp @@ -191,6 +193,7 @@ master_daemon = SingletonDaemon('Master daemon', 'trex_master_daemon', args tmp_dir = '/tmp/trex-tmp' logging_file = '/var/log/trex/master_daemon.log' logging_file_bu = '/var/log/trex/master_daemon.log_bu' +os.chdir('/') if not _check_path_under_current_or_temp(args.trex_dir): raise Exception('Only allowed to use path under /tmp or current directory') -- cgit 1.2.3-korg From d93a7ff83d3d29fc6c254dd56af235dbe7abacb3 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Fri, 17 Jun 2016 18:47:56 +0300 Subject: master_daemon: show better status of executed command --- .../trex_control_plane/server/singleton_daemon.py | 2 +- scripts/master_daemon.py | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/trex_control_plane/server/singleton_daemon.py b/scripts/automation/trex_control_plane/server/singleton_daemon.py index 79deace1..1784cc42 100755 --- a/scripts/automation/trex_control_plane/server/singleton_daemon.py +++ b/scripts/automation/trex_control_plane/server/singleton_daemon.py @@ -86,7 +86,7 @@ class SingletonDaemon(object): def kill(self, timeout = 15): pid = self.get_pid() if not pid: - return False + raise Exception('%s is not running' % self.name) # try Ctrl+C, usual kill, kill -9 for signal_name in [signal.SIGINT, signal.SIGTERM, signal.SIGKILL]: if self.kill_by_signal(pid, signal_name, timeout): diff --git a/scripts/master_daemon.py b/scripts/master_daemon.py index e50d49ee..aa49f207 100755 --- a/scripts/master_daemon.py +++ b/scripts/master_daemon.py @@ -83,8 +83,7 @@ def start_master_daemon(): proc.start() for i in range(50): if master_daemon.is_running(): - print(termstyle.green('Master daemon is started.')) - os._exit(0) + return True sleep(0.1) fail(termstyle.red('Master daemon failed to run. Please look in log: %s' % logging_file)) @@ -226,9 +225,13 @@ if args.action != 'show': print(termstyle.red(e)) sys.exit(1) -# prints running status -if daemon.is_running(): - print(termstyle.green('%s is running' % daemon.name)) +passive = {'start': 'started', 'restart': 'restarted', 'stop': 'stopped', 'show': 'running'} + +if args.action in ('show', 'start', 'restart') and daemon.is_running() or \ + args.action == 'stop' and not daemon.is_running(): + print(termstyle.green('%s is %s' % (daemon.name, passive[args.action]))) + os._exit(0) else: - print(termstyle.red('%s is NOT running' % daemon.name)) + print(termstyle.red('%s is NOT %s' % (daemon.name, passive[args.action]))) + os._exit(-1) -- cgit 1.2.3-korg From df65f7b829c9520b8814d1540be4bbdd9670c1b5 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Sun, 19 Jun 2016 18:10:12 +0300 Subject: add advance cfg files --- scripts/cfg/trex_advanced_cfg-10g.yaml | 15 ++++++++++++++ scripts/cfg/trex_advanced_dont_use_x710-card1.yaml | 23 ++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 scripts/cfg/trex_advanced_cfg-10g.yaml create mode 100644 scripts/cfg/trex_advanced_dont_use_x710-card1.yaml (limited to 'scripts') diff --git a/scripts/cfg/trex_advanced_cfg-10g.yaml b/scripts/cfg/trex_advanced_cfg-10g.yaml new file mode 100644 index 00000000..d2ddfe15 --- /dev/null +++ b/scripts/cfg/trex_advanced_cfg-10g.yaml @@ -0,0 +1,15 @@ +- port_limit : 2 + version : 2 + #interfaces : ["04:00.0", "04:00.1", "06:00.0", "06:00.1"] # list of the interfaces to bind run ./dpdk_nic_bind.py --status + interfaces : ["04:00.0", "04:00.1"] # list of the interfaces to bind run ./dpdk_nic_bind.py --status + port_info : # set eh mac addr + prefix : setup1 + limit_memory : 1024 + port_info : # set eh mac addr + - dest_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x65] + src_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x64] + - dest_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x64] + src_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x65] + + + diff --git a/scripts/cfg/trex_advanced_dont_use_x710-card1.yaml b/scripts/cfg/trex_advanced_dont_use_x710-card1.yaml new file mode 100644 index 00000000..2a9f1c94 --- /dev/null +++ b/scripts/cfg/trex_advanced_dont_use_x710-card1.yaml @@ -0,0 +1,23 @@ +- port_limit : 2 + version : 2 + interfaces : ["06:00.0", "06:00.1"] + prefix : setup2 # WARNING,WARNING,WARNING DON'T USE THIS IF YOU DON'T KNOW WHAT YOU ARE DOING + limit_memory : 1024 # WARNING,WARNING,WARNING DON'T USE THIS IF YOU DON'T KNOW WHAT YOU ARE DOING + zmq_pub_port : 4510 # WARNING,WARNING,WARNING DON'T USE THIS IF YOU DON'T KNOW WHAT YOU ARE DOING + zmq_rpc_port : 4511 # WARNING,WARNING,WARNING DON'T USE THIS IF YOU DON'T KNOW WHAT YOU ARE DOING + port_info : # set eh mac addr + - dest_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x65] + src_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x64] + - dest_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x64] + src_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x65] + - dest_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x67] + src_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x66] + - dest_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x66] + src_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x67] + platform : + master_thread_id : 4 + latency_thread_id : 7 + dual_if : + - socket : 0 + threads : [5,6] + -- cgit 1.2.3-korg From fed15ec32feda1e79d69040834b5653047b96739 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Sun, 19 Jun 2016 18:10:25 +0300 Subject: update latency profile --- scripts/cfg/trex_advanced_cfg-10g.yaml | 4 ++-- scripts/stl/udp_1pkt_src_ip_split_latency.py | 15 ++++++++++++--- scripts/stl/udp_for_benchmarks.py | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/cfg/trex_advanced_cfg-10g.yaml b/scripts/cfg/trex_advanced_cfg-10g.yaml index d2ddfe15..4450bb5b 100644 --- a/scripts/cfg/trex_advanced_cfg-10g.yaml +++ b/scripts/cfg/trex_advanced_cfg-10g.yaml @@ -3,8 +3,8 @@ #interfaces : ["04:00.0", "04:00.1", "06:00.0", "06:00.1"] # list of the interfaces to bind run ./dpdk_nic_bind.py --status interfaces : ["04:00.0", "04:00.1"] # list of the interfaces to bind run ./dpdk_nic_bind.py --status port_info : # set eh mac addr - prefix : setup1 - limit_memory : 1024 + prefix : setup1 # WARNING,WARNING,WARNING DON'T USE THIS IF YOU DON'T KNOW WHAT YOU ARE DOING + limit_memory : 1024 # WARNING,WARNING,WARNING DON'T USE THIS IF YOU DON'T KNOW WHAT YOU ARE DOING port_info : # set eh mac addr - dest_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x65] src_mac : [0x00, 0xe0, 0xed, 0x5d, 0x84, 0x64] diff --git a/scripts/stl/udp_1pkt_src_ip_split_latency.py b/scripts/stl/udp_1pkt_src_ip_split_latency.py index af8d4cd7..2d81f756 100644 --- a/scripts/stl/udp_1pkt_src_ip_split_latency.py +++ b/scripts/stl/udp_1pkt_src_ip_split_latency.py @@ -2,11 +2,18 @@ from trex_stl_lib.api import * # split the range of IP to cores +# add tunable by fsize to change the size of the frame +# latency frame is always 64 +# trex>start -f stl/udp_1pkt_src_ip_split_latency.py -t fsize=64 -m 30% --port 0 --force # +# + class STLS1(object): def __init__ (self): self.fsize =64; + self.lfsize =64; + def create_stream (self, dir,port_id): # create a base packet and pad it to size @@ -22,6 +29,7 @@ class STLS1(object): base_pkt = Ether()/IP(src=src_ip,dst=dst_ip)/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' + pad_latency = max(0, (self.lfsize-4) - len(base_pkt)) * 'x' vm = STLScVmRaw( [ STLVmFlowVar ( "ip_src", min_value="10.0.0.1", max_value="10.0.0.255", size=4, step=1,op="inc"), @@ -39,8 +47,7 @@ class STLS1(object): # latency stream - STLStream(packet = STLPktBuilder(pkt = "yaml/udp_64B_no_crc.pcap", - path_relative_to_profile = True), + STLStream(packet = STLPktBuilder(pkt = base_pkt/pad_latency), mode = STLTXCont(pps=1000), flow_stats = STLFlowLatencyStats(pg_id = 12+port_id)) @@ -48,7 +55,9 @@ class STLS1(object): return stream - def get_streams (self, direction = 0, **kwargs): + def get_streams (self, direction = 0, fsize = 64,lfsize = 64, **kwargs): + self.fsize =fsize; + self.lfsize =lfsize return self.create_stream(direction,kwargs['port_id']) diff --git a/scripts/stl/udp_for_benchmarks.py b/scripts/stl/udp_for_benchmarks.py index 2033e80e..956498d0 100644 --- a/scripts/stl/udp_for_benchmarks.py +++ b/scripts/stl/udp_for_benchmarks.py @@ -1,5 +1,23 @@ from trex_stl_lib.api import * +# Tunable example +# +#trex>profile -f stl/udp_for_benchmarks.py +# +#Profile Information: +# +# +#General Information: +#Filename: stl/udp_for_benchmarks.py +#Stream count: 1 +# +#Specific Information: +#Type: Python Module +#Tunables: ['stream_count = 1', 'direction = 0', 'packet_len = 64'] +# +#trex>start -f stl/udp_for_benchmarks.py -t packet_len=128 --port 0 +# + class STLS1(object): ''' Generalization of udp_1pkt_simple, can specify number of streams and packet length -- cgit 1.2.3-korg From 1bc9c49fa89a3942d3d1516217635d0c6e6b0c56 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Mon, 20 Jun 2016 11:50:42 +0300 Subject: regression: fix nose represenation of errors in setUpModule --- scripts/automation/regression/trex_unit_test.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py index 606556d3..5eb9e222 100755 --- a/scripts/automation/regression/trex_unit_test.py +++ b/scripts/automation/regression/trex_unit_test.py @@ -50,7 +50,11 @@ from distutils.dir_util import mkpath # override nose's strange representation of setUpClass errors def __suite_repr__(self): - return "%s.%s" % (nose.suite._strclass(self.context), getattr(self.context, '__name__', self.context)) + if hasattr(self.context, '__module__'): # inside class, setUpClass etc. + class_repr = nose.suite._strclass(self.context) + else: # outside of class, setUpModule etc. + class_repr = nose.suite._strclass(self.__class__) + return '%s.%s' % (class_repr, getattr(self.context, '__name__', self.context)) nose.suite.ContextSuite.__repr__ = __suite_repr__ nose.suite.ContextSuite.__str__ = __suite_repr__ -- cgit 1.2.3-korg From 35efdda889977a7b0bf5d1cda213d0e7ffcd4367 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Mon, 20 Jun 2016 12:56:17 +0300 Subject: Add 4 bytes for each packet rx/tx bytes in flow stats, to artificially count ethernet CRC --- scripts/automation/regression/stateless_tests/stl_rx_test.py | 4 ++-- .../trex_control_plane/stl/examples/stl_flow_latency_stats.py | 2 +- src/main_dpdk.cpp | 2 +- src/stateless/rx/trex_stateless_rx_core.cpp | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index 80e6bee6..41eabd65 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -80,7 +80,7 @@ class STLRX_Test(CStlGeneral_Test): tmp = 'TX pkts mismatch - got: {0}, expected: {1}'.format(tx_pkts, total_pkts) assert False, tmp - if tx_bytes != (total_pkts * pkt_len): + if tx_bytes != (total_pkts * (pkt_len + 4)): # + 4 for ethernet CRC pprint.pprint(flow_stats) tmp = 'TX bytes mismatch - got: {0}, expected: {1}'.format(tx_bytes, (total_pkts * pkt_len)) assert False, tmp @@ -92,7 +92,7 @@ class STLRX_Test(CStlGeneral_Test): if "rx_bytes" in self.cap: rx_bytes = flow_stats['rx_bytes'].get(self.rx_port, 0) - if rx_bytes != (total_pkts * pkt_len) and not self.drops_expected: + if rx_bytes != (total_pkts * (pkt_len + 4)) and not self.drops_expected: # +4 for ethernet CRC pprint.pprint(flow_stats) tmp = 'RX bytes mismatch - got: {0}, expected: {1}'.format(rx_bytes, (total_pkts * pkt_len)) assert False, tmp diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py b/scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py index 86b91728..ac0e212b 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py @@ -114,7 +114,7 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw): else: print("TX pkts match - {0}".format(tx_pkts)) - if tx_bytes != (total_pkts * pkt_len): + if tx_bytes != (total_pkts * (pkt_len + 4)): # +4 for ethernet CRC print("TX bytes mismatch - got: {0}, expected: {1}".format(tx_bytes, (total_pkts * pkt_len))) pprint.pprint(flow_stats) return False diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index ef1668ab..3dd020ae 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -2074,7 +2074,7 @@ int CCoreEthIFStateless::send_node_flow_stat(rte_mbuf *m, CGenNodeStateless * no } tx_per_flow_t *lp_s = &lp_stats->m_tx_per_flow[hw_id]; lp_s->add_pkts(1); - lp_s->add_bytes(mi->pkt_len); + lp_s->add_bytes(mi->pkt_len + 4); // We add 4 because of ethernet CRC if (hw_id >= MAX_FLOW_STATS) { fsp_head->time_stamp = os_get_hr_tick_64(); diff --git a/src/stateless/rx/trex_stateless_rx_core.cpp b/src/stateless/rx/trex_stateless_rx_core.cpp index 847dbc97..a622ee7a 100644 --- a/src/stateless/rx/trex_stateless_rx_core.cpp +++ b/src/stateless/rx/trex_stateless_rx_core.cpp @@ -235,7 +235,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t * curr_rfc2544.set_seq(pkt_seq + 1); } lp->m_port.m_rx_pg_stat_payload[hw_id].add_pkts(1); - lp->m_port.m_rx_pg_stat_payload[hw_id].add_bytes(m->pkt_len); + lp->m_port.m_rx_pg_stat_payload[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC uint64_t d = (os_get_hr_tick_64() - fsp_head->time_stamp ); dsec_t ctime = ptime_convert_hr_dsec(d); curr_rfc2544.add_sample(ctime); @@ -243,7 +243,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t * } else { hw_id = get_hw_id(ip_id); lp->m_port.m_rx_pg_stat[hw_id].add_pkts(1); - lp->m_port.m_rx_pg_stat[hw_id].add_bytes(m->pkt_len); + lp->m_port.m_rx_pg_stat[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC } } } -- cgit 1.2.3-korg From e058f6e034fbf51bc5e29d449ea806e7a8633b70 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Mon, 20 Jun 2016 13:47:18 +0300 Subject: regression: add flag to run with debug image --- scripts/automation/regression/trex.py | 1 + scripts/automation/regression/trex_unit_test.py | 12 ++++++++++-- scripts/automation/trex_control_plane/server/trex_server.py | 9 +++++---- .../trex_control_plane/stf/trex_stf_lib/trex_client.py | 7 ++++--- 4 files changed, 20 insertions(+), 9 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/trex.py b/scripts/automation/regression/trex.py index 9541ad76..44f2faba 100644 --- a/scripts/automation/regression/trex.py +++ b/scripts/automation/regression/trex.py @@ -39,6 +39,7 @@ class CTRexScenario: GAManager = None no_daemon = False router_image = None + debug_image = False class CTRexRunner: """This is an instance for generating a CTRexRunner""" diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py index 5eb9e222..11902a1a 100755 --- a/scripts/automation/regression/trex_unit_test.py +++ b/scripts/automation/regression/trex_unit_test.py @@ -143,6 +143,9 @@ class CTRexTestConfiguringPlugin(Plugin): 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.") def configure(self, options, conf): @@ -179,9 +182,11 @@ class CTRexTestConfiguringPlugin(Plugin): CTRexScenario.benchmark = self.benchmark CTRexScenario.modes = set(self.modes) CTRexScenario.server_logs = self.server_logs + CTRexScenario.debug_image = options.debug_image if not self.no_daemon: - CTRexScenario.trex = CTRexClient(trex_host = self.configuration.trex['trex_name'], - verbose = self.json_verbose) + CTRexScenario.trex = CTRexClient(trex_host = self.configuration.trex['trex_name'], + verbose = self.json_verbose, + debug_image = options.debug_image) if not CTRexScenario.trex.check_master_connectivity(): print('Could not connect to master daemon') sys.exit(-1) @@ -267,6 +272,9 @@ def save_setup_info(): setup_info += 'Server: %s, Modes: %s' % (cfg.trex.get('trex_name'), cfg.trex.get('modes')) if cfg.router: setup_info += '\nRouter: Model: %s, Image: %s' % (cfg.router.get('model'), CTRexScenario.router_image) + if CTRexScenario.debug_image: + setup_info += '\nDebug image: %s' % CTRexScenario.debug_image + with open('%s/report_%s.info' % (CTRexScenario.report_dir, CTRexScenario.setup_name), 'w') as f: f.write(setup_info) except Exception as err: diff --git a/scripts/automation/trex_control_plane/server/trex_server.py b/scripts/automation/trex_control_plane/server/trex_server.py index 3da629ec..091b729b 100755 --- a/scripts/automation/trex_control_plane/server/trex_server.py +++ b/scripts/automation/trex_control_plane/server/trex_server.py @@ -310,7 +310,7 @@ class CTRexServer(object): return False - def start_trex(self, trex_cmd_options, user, block_to_success = True, timeout = 40, stateless = False): + def start_trex(self, trex_cmd_options, user, block_to_success = True, timeout = 40, stateless = False, debug_image = False): with self.start_lock: logger.info("Processing start_trex() command.") if self.is_reserved(): @@ -323,7 +323,7 @@ class CTRexServer(object): return Fault(-13, '') # raise at client TRexInUseError try: - server_cmd_data = self.generate_run_cmd(stateless = stateless, **trex_cmd_options) + server_cmd_data = self.generate_run_cmd(stateless = stateless, debug_image = debug_image, **trex_cmd_options) self.zmq_monitor.first_dump = True self.trex.start_trex(self.TREX_PATH, server_cmd_data) logger.info("TRex session has been successfully initiated.") @@ -413,7 +413,7 @@ class CTRexServer(object): return self.trex.get_running_info() - def generate_run_cmd (self, iom = 0, export_path="/tmp/trex.txt", stateless = False, **kwargs): + def generate_run_cmd (self, iom = 0, export_path="/tmp/trex.txt", stateless = False, debug_image = False, **kwargs): """ generate_run_cmd(self, iom, export_path, kwargs) -> str Generates a custom running command for the kick-off of the TRex traffic generator. @@ -457,9 +457,10 @@ class CTRexServer(object): if 'd' not in kwargs: raise Exception('Argument -d should be specified in stateful command') - cmd = "{nice}{run_command} --iom {io} {cmd_options} --no-key".format( # -- iom 0 disables the periodic log to the screen (not needed) + cmd = "{nice}{run_command}{debug_image} --iom {io} {cmd_options} --no-key".format( # -- iom 0 disables the periodic log to the screen (not needed) nice = '' if self.trex_nice == 0 else 'nice -n %s ' % self.trex_nice, run_command = self.TREX_START_CMD, + debug_image = '-debug' if debug_image else '', cmd_options = trex_cmd_options, io = iom) 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 e1b298db..91fe2075 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 @@ -40,7 +40,7 @@ class CTRexClient(object): This class defines the client side of the RESTfull interaction with TRex """ - def __init__(self, trex_host, max_history_size = 100, filtered_latency_amount = 0.001, trex_daemon_port = 8090, master_daemon_port = 8091, trex_zmq_port = 4500, verbose = False): + def __init__(self, trex_host, max_history_size = 100, filtered_latency_amount = 0.001, trex_daemon_port = 8090, master_daemon_port = 8091, trex_zmq_port = 4500, verbose = False, debug_image = False): """ Instantiate a TRex client object, and connecting it to listening daemon-server @@ -96,6 +96,7 @@ class CTRexClient(object): self.master_daemon = jsonrpclib.Server(self.master_daemon_path, history = self.history) self.trex_server_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = trex_daemon_port ) self.server = jsonrpclib.Server(self.trex_server_path, history = self.history) + self.debug_image = debug_image def add (self, x, y): @@ -159,7 +160,7 @@ class CTRexClient(object): self.result_obj.clear_results() try: issue_time = time.time() - retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout) + retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, self.debug_image) except AppError as err: self._handle_AppError_exception(err.args[0]) except ProtocolError: @@ -205,7 +206,7 @@ class CTRexClient(object): """ try: user = user or self.__default_user - retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, True) + retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, True, self.debug_image) except AppError as err: self._handle_AppError_exception(err.args[0]) except ProtocolError: -- cgit 1.2.3-korg From 0422016ab056245449e0e5bdf0dceef2c4f06e31 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 20 Jun 2016 14:30:46 +0300 Subject: bug: multiplier can be string or unicode --- .../stl/trex_stl_lib/trex_stl_client.py | 41 +++++++++------------- .../stl/trex_stl_lib/trex_stl_exceptions.py | 8 ++++- .../stl/trex_stl_lib/utils/common.py | 1 + .../stl/trex_stl_lib/utils/parsing_opts.py | 4 --- 4 files changed, 25 insertions(+), 29 deletions(-) (limited to 'scripts') 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 22895a75..4d1125c8 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 @@ -1700,6 +1700,11 @@ class STLClient(object): ports = self._validate_port_list(ports) + validate_type('mult', mult, basestring) + validate_type('force', force, bool) + validate_type('duration', duration, (int, float)) + validate_type('total', total, bool) + # verify multiplier mult_obj = parsing_opts.decode_multiplier(mult, allow_update = False, @@ -1707,17 +1712,6 @@ class STLClient(object): if not mult_obj: raise STLArgumentError('mult', mult) - # some type checkings - - if not type(force) is bool: - raise STLArgumentError('force', force) - - if not isinstance(duration, (int, float)): - raise STLArgumentError('duration', duration) - - if not type(total) is bool: - raise STLArgumentError('total', total) - # verify ports are stopped or force stop them active_ports = list(set(self.get_active_ports()).intersection(ports)) @@ -1762,11 +1756,12 @@ class STLClient(object): """ - ports = ports if ports is not None else self.get_active_ports() - ports = self._validate_port_list(ports) + if ports is None: + ports = self.get_active_ports() + if not ports: + return - if not ports: - return + ports = self._validate_port_list(ports) self.logger.pre_cmd("Stopping traffic on port(s) {0}:".format(ports)) rc = self.__stop(ports) @@ -1815,6 +1810,9 @@ class STLClient(object): ports = ports if ports is not None else self.get_active_ports() ports = self._validate_port_list(ports) + validate_type('mult', mult, basestring) + validate_type('force', force, bool) + validate_type('total', total, bool) # verify multiplier mult_obj = parsing_opts.decode_multiplier(mult, @@ -1823,10 +1821,6 @@ class STLClient(object): if not mult_obj: raise STLArgumentError('mult', mult) - # verify total - if not type(total) is bool: - raise STLArgumentError('total', total) - # call low level functions self.logger.pre_cmd("Updating traffic on port(s) {0}:".format(ports)) @@ -2050,6 +2044,10 @@ class STLClient(object): ports = ports if ports is not None else self.get_acquired_ports() ports = self._validate_port_list(ports) + validate_type('mult', mult, basestring) + validate_type('duration', duration, (int, float)) + validate_type('total', total, bool) + # verify multiplier mult_obj = parsing_opts.decode_multiplier(mult, @@ -2058,11 +2056,6 @@ class STLClient(object): if not mult_obj: raise STLArgumentError('mult', mult) - - if not isinstance(duration, (int, float)): - raise STLArgumentError('duration', duration) - - self.logger.pre_cmd("Validating streams on port(s) {0}:".format(ports)) rc = self.__validate(ports) self.logger.post_cmd(rc) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_exceptions.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_exceptions.py index 3c443ba6..b6fad865 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_exceptions.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_exceptions.py @@ -4,6 +4,11 @@ import linecache from .utils.text_opts import * +try: + basestring +except NameError: + basestring = str + # basic error for API class STLError(Exception): def __init__ (self, msg): @@ -56,7 +61,8 @@ class STLArgumentError(STLError): # raised when argument type is not valid for operation class STLTypeError(STLError): def __init__ (self, arg_name, arg_type, valid_types): - self.msg = "Argument: '%s' invalid type: %s, expecting type(s): %s." % (arg_name, arg_type, valid_types) + self.msg = "Argument: '%s' invalid type: '%s', expecting type(s): %s." % (arg_name, arg_type.__name__, + [t.__name__ for t in valid_types] if isinstance(valid_types, tuple) else valid_types.__name__) # raised when timeout occurs class STLTimeoutError(STLError): 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 b4903e81..6835ea5f 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 @@ -64,3 +64,4 @@ def list_difference (l1, l2): def is_sub_list (l1, l2): return set(l1) <= set(l2) + 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 98e3ca6a..9ef14612 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 @@ -90,10 +90,6 @@ match_multiplier_help = """Multiplier should be passed in the following format: # value should be divided def decode_multiplier(val, allow_update = False, divide_count = 1): - # must be string - if not isinstance(val, str): - return None - # do we allow updates ? +/- if not allow_update: match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)$", val) -- cgit 1.2.3-korg From 131bb55c38c0e59d14043766b0d3a38d5f775771 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Tue, 21 Jun 2016 14:29:22 +0300 Subject: --debug-image flag fix bug --- scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') 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 91fe2075..57d19459 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 @@ -160,7 +160,7 @@ class CTRexClient(object): self.result_obj.clear_results() try: issue_time = time.time() - retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, self.debug_image) + retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, False, self.debug_image) except AppError as err: self._handle_AppError_exception(err.args[0]) except ProtocolError: -- cgit 1.2.3-korg From 3bf54917e0d8817dbadaae73a7545a011676cccf Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Tue, 21 Jun 2016 15:30:16 +0300 Subject: profile fixes --- .../stl/trex_stl_lib/utils/parsing_opts.py | 12 +++---- scripts/stl/burst_3st_1000pkt.py | 10 +++--- scripts/stl/burst_3st_600pkt.py | 10 +++--- scripts/stl/burst_3st_loop_x_times.py | 10 +++--- scripts/stl/flow_stats.py | 34 +++++++++++-------- scripts/stl/flow_stats_latency.py | 38 ++++++++++++++-------- scripts/stl/imix.py | 2 +- scripts/stl/multi_burst_2st_1000pkt.py | 8 ++--- scripts/stl/simple_3st.py | 6 ++-- scripts/stl/udp_1pkt.py | 4 +-- scripts/stl/udp_1pkt_1mac.py | 4 +-- scripts/stl/udp_1pkt_1mac_override.py | 4 +-- scripts/stl/udp_1pkt_1mac_step.py | 4 +-- scripts/stl/udp_1pkt_mac.py | 4 +-- scripts/stl/udp_1pkt_mac_mask1.py | 4 +-- scripts/stl/udp_1pkt_mac_mask2.py | 4 +-- scripts/stl/udp_1pkt_mac_mask3.py | 4 +-- scripts/stl/udp_1pkt_mac_mask5.py | 4 +-- scripts/stl/udp_1pkt_mac_step.py | 4 +-- scripts/stl/udp_1pkt_range_clients.py | 4 +-- scripts/stl/udp_1pkt_range_clients_split.py | 4 +-- scripts/stl/udp_1pkt_range_clients_split_garp.py | 2 +- scripts/stl/udp_1pkt_src_ip_split.py | 4 +-- scripts/stl/udp_1pkt_src_ip_split_latency.py | 4 +-- scripts/stl/udp_1pkt_tuple_gen.py | 2 +- scripts/stl/udp_1pkt_tuple_gen_split.py | 4 +-- scripts/stl/udp_3pkt_pcap.py | 6 ++-- 27 files changed, 109 insertions(+), 91 deletions(-) (limited to 'scripts') 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 9ef14612..bb28d3af 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 @@ -240,26 +240,24 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'type': int}), PROMISCUOUS: ArgumentPack(['--prom'], - {'help': "sets port promiscuous on", + {'help': "Sets port promiscuous on", 'dest': "prom", 'default': None, 'action': "store_true"}), - TUNABLES: ArgumentPack(['-t'], - {'help': "sets tunable for a profile", + {'help': "Sets tunables for a profile. Example: '-t fsize=100,pg_id=7'", + 'metavar': 'T1=VAL[,T2=VAL ...]', 'dest': "tunables", 'default': None, 'type': decode_tunables}), - NO_PROMISCUOUS: ArgumentPack(['--no_prom'], - {'help': "sets port promiscuous off", + {'help': "Sets port promiscuous off", 'dest': "prom", 'default': None, 'action': "store_false"}), - PORT_LIST: ArgumentPack(['--port', '-p'], {"nargs": '+', 'dest':'ports', @@ -465,4 +463,4 @@ def gen_parser(stateless_client, op_name, description, *args): if __name__ == "__main__": - pass \ No newline at end of file + pass diff --git a/scripts/stl/burst_3st_1000pkt.py b/scripts/stl/burst_3st_1000pkt.py index 8fcdca57..88a30c84 100644 --- a/scripts/stl/burst_3st_1000pkt.py +++ b/scripts/stl/burst_3st_1000pkt.py @@ -10,27 +10,27 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) base_pkt1 = Ether()/IP(src="16.0.0.2",dst="48.0.0.1")/UDP(dport=12,sport=1025) base_pkt2 = Ether()/IP(src="16.0.0.3",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' - return STLProfile( [ STLStream( isg = 10.0, # star in delay + return STLProfile( [ STLStream( isg = 10.0, # start in delay name ='S0', packet = STLPktBuilder(pkt = base_pkt/pad), mode = STLTXSingleBurst( pps = 10, total_pkts = self.burst_size), next = 'S1'), # point to next stream - STLStream( self_start = False, # stream is disabled enable trow S0 + STLStream( self_start = False, # Stream is disabled. Will run because it is pointed from S0 name ='S1', packet = STLPktBuilder(pkt = base_pkt1/pad), mode = STLTXSingleBurst( pps = 10, total_pkts = self.burst_size), next = 'S2' ), - STLStream( self_start = False, # stream is disabled enable trow S0 + STLStream( self_start = False, # Stream is disabled. Will run because it is pointed from S1 name ='S2', packet = STLPktBuilder(pkt = base_pkt2/pad), mode = STLTXSingleBurst( pps = 10, total_pkts = self.burst_size ) diff --git a/scripts/stl/burst_3st_600pkt.py b/scripts/stl/burst_3st_600pkt.py index 978c8920..b81f256b 100644 --- a/scripts/stl/burst_3st_600pkt.py +++ b/scripts/stl/burst_3st_600pkt.py @@ -9,27 +9,27 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) base_pkt1 = Ether()/IP(src="16.0.0.2",dst="48.0.0.1")/UDP(dport=12,sport=1025) base_pkt2 = Ether()/IP(src="16.0.0.3",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' - return STLProfile( [ STLStream( isg = 10.0, # star in delay + return STLProfile( [ STLStream( isg = 10.0, # start in delay name ='S0', packet = STLPktBuilder(pkt = base_pkt/pad), mode = STLTXSingleBurst( pps = 10, total_pkts = 10), next = 'S1'), # point to next stream - STLStream( self_start = False, # stream is disabled enable trow S0 + STLStream( self_start = False, # Stream is disabled. Will run because it is pointed from S0 name ='S1', packet = STLPktBuilder(pkt = base_pkt1/pad), mode = STLTXSingleBurst( pps = 10, total_pkts = 20), next = 'S2' ), - STLStream( self_start = False, # stream is disabled enable trow S0 + STLStream( self_start = False, # Stream is disabled. Will run because it is pointed from S1 name ='S2', packet = STLPktBuilder(pkt = base_pkt2/pad), mode = STLTXSingleBurst( pps = 10, total_pkts = 30 ) diff --git a/scripts/stl/burst_3st_loop_x_times.py b/scripts/stl/burst_3st_loop_x_times.py index 175b8315..ec217e9f 100644 --- a/scripts/stl/burst_3st_loop_x_times.py +++ b/scripts/stl/burst_3st_loop_x_times.py @@ -9,27 +9,27 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) base_pkt1 = Ether()/IP(src="16.0.0.2",dst="48.0.0.1")/UDP(dport=12,sport=1025) base_pkt2 = Ether()/IP(src="16.0.0.3",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' - return STLProfile( [ STLStream( isg = 10.0, # star in delay + return STLProfile( [ STLStream( isg = 10.0, # start in delay name ='S0', packet = STLPktBuilder(pkt = base_pkt/pad), mode = STLTXSingleBurst( pps = 10, total_pkts = 1), next = 'S1'), # point to next stream - STLStream( self_start = False, # stream is disabled enable trow S0 + STLStream( self_start = False, # Stream is disabled. Will run because it is pointed from S0 name ='S1', packet = STLPktBuilder(pkt = base_pkt1/pad), mode = STLTXSingleBurst( pps = 10, total_pkts = 2), next = 'S2' ), - STLStream( self_start = False, # stream is disabled enable trow S0 + STLStream( self_start = False, # Stream is disabled. Will run because it is pointed from S1 name ='S2', packet = STLPktBuilder(pkt = base_pkt2/pad), mode = STLTXSingleBurst( pps = 10, total_pkts = 3 ), diff --git a/scripts/stl/flow_stats.py b/scripts/stl/flow_stats.py index cbb5ac21..a50ba848 100644 --- a/scripts/stl/flow_stats.py +++ b/scripts/stl/flow_stats.py @@ -1,21 +1,29 @@ from trex_stl_lib.api import * -import os - -# stream from pcap file. continues pps 10 in sec -CP = os.path.join(os.path.dirname(__file__)) class STLS1(object): - - def get_streams (self, direction = 0, **kwargs): - return [STLStream(packet = STLPktBuilder(pkt = os.path.join(CP, "yaml/udp_64B_no_crc.pcap")), # path relative to pwd - mode = STLTXCont(pps=1000), - flow_stats = STLFlowStats(pg_id = 7)), - - STLStream(packet = STLPktBuilder(pkt = os.path.join(CP, "yaml/udp_594B_no_crc.pcap")), # path relative to pwd - mode = STLTXCont(pps=5000), - flow_stats = STLFlowStats(pg_id = 12)) + """ + Create flow stat stream of UDP packet. + Can specify using tunables the packet length (fsize) and packet group id (pg_id) + """ + def __init__ (self): + self.fsize = 64 + self.pg_id = 0 + + def _create_stream (self): + size = self.fsize - 4; # HW will add 4 bytes ethernet CRC + base_pkt = Ether() / IP(src = "16.0.0.1", dst = "48.0.0.1") / UDP(dport = 12, sport = 1025) + pad = max(0, size - len(base_pkt)) * 'x' + pkt = STLPktBuilder(pkt = base_pkt/pad) + + return [STLStream(packet = pkt, + mode = STLTXCont(pps=1), + flow_stats = STLFlowStats(pg_id = self.pg_id)) ] + def get_streams (self, fsize = 64, pg_id = 7, **kwargs): + self.fsize = fsize + self.pg_id = pg_id + return self._create_stream() # dynamic load - used for trex console or simulator def register(): diff --git a/scripts/stl/flow_stats_latency.py b/scripts/stl/flow_stats_latency.py index e1541272..8460fcfc 100644 --- a/scripts/stl/flow_stats_latency.py +++ b/scripts/stl/flow_stats_latency.py @@ -1,21 +1,33 @@ from trex_stl_lib.api import * -import os - -# stream from pcap file. continues pps 10 in sec -CP = os.path.join(os.path.dirname(__file__)) class STLS1(object): - - def get_streams (self, direction = 0, **kwargs): - return [STLStream(packet = STLPktBuilder(pkt = os.path.join(CP, "yaml/udp_64B_no_crc.pcap")), # path relative to pwd - mode = STLTXCont(pps=1000), - flow_stats = STLFlowLatencyStats(pg_id = 1 + kwargs['port_id'])), - - STLStream(packet = STLPktBuilder(pkt = os.path.join(CP, "yaml/udp_594B_no_crc.pcap")), # path relative to pwd - mode = STLTXCont(pps=5000), - flow_stats = STLFlowLatencyStats(pg_id = 50 + kwargs['port_id'])) + """ + Create flow stat latency stream of UDP packet. + Can specify using tunables the packet length (fsize) and packet group id (pg_id) + Since we can't have two latency streams with same pg_id, in order to be able to start this profile + on more than one port, we add port_id to the pg_id + """ + + def __init__ (self): + self.fsize = 64 + self.pg_id = 0 + + def _create_stream (self): + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS + base_pkt = Ether() / IP(src = "16.0.0.1", dst = "48.0.0.1") / UDP(dport = 12, sport = 1025) + pad = max(0, size - len(base_pkt)) * 'x' + pkt = STLPktBuilder(pkt = base_pkt/pad) + + return [STLStream(packet = pkt, + mode = STLTXCont(pps=1), + flow_stats = STLFlowLatencyStats(pg_id = self.pg_id)) ] + def get_streams (self, fsize = 64, pg_id = 7, **kwargs): + self.fsize = fsize + self.pg_id = pg_id + kwargs['port_id'] + return self._create_stream() + # dynamic load - used for trex console or simulator def register(): diff --git a/scripts/stl/imix.py b/scripts/stl/imix.py index 82edbfa5..c9b1ff17 100644 --- a/scripts/stl/imix.py +++ b/scripts/stl/imix.py @@ -18,7 +18,7 @@ class STLImix(object): def create_stream (self, size, pps, isg, vm ): - # create a base packet and pad it to size + # Create base packet and pad it to size base_pkt = Ether()/IP()/UDP() pad = max(0, size - len(base_pkt)) * 'x' diff --git a/scripts/stl/multi_burst_2st_1000pkt.py b/scripts/stl/multi_burst_2st_1000pkt.py index 1a43ae96..fe4b4eac 100644 --- a/scripts/stl/multi_burst_2st_1000pkt.py +++ b/scripts/stl/multi_burst_2st_1000pkt.py @@ -10,20 +10,20 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) base_pkt1 = Ether()/IP(src="16.0.0.2",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' - return STLProfile( [ STLStream( isg = 10.0, # star in delay + return STLProfile( [ STLStream( isg = 10.0, # start in delay name ='S0', packet = STLPktBuilder(pkt = base_pkt/pad), mode = STLTXSingleBurst( pps = 10, total_pkts = self.burst_size), next = 'S1'), # point to next stream - STLStream( self_start = False, # stream is disabled enable trow S0 + STLStream( self_start = False, # Stream is disabled. Will run because it is pointed from S0 name ='S1', packet = STLPktBuilder(pkt = base_pkt1/pad), mode = STLTXMultiBurst( pps = 1000,pkts_per_burst = 4,ibg = 1000000.0,count = 5) diff --git a/scripts/stl/simple_3st.py b/scripts/stl/simple_3st.py index 8979057c..ae388f13 100644 --- a/scripts/stl/simple_3st.py +++ b/scripts/stl/simple_3st.py @@ -8,15 +8,15 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) base_pkt1 = Ether()/IP(src="16.0.0.2",dst="48.0.0.1")/UDP(dport=12,sport=1025) base_pkt2 = Ether()/IP(src="16.0.0.3",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' - return STLProfile( [ STLStream( isg = 1.0, # star in delay in usec + return STLProfile( [ STLStream( isg = 1.0, # start in delay in usec packet = STLPktBuilder(pkt = base_pkt/pad), mode = STLTXCont( pps = 10), ), diff --git a/scripts/stl/udp_1pkt.py b/scripts/stl/udp_1pkt.py index 13516ecd..f2601d79 100644 --- a/scripts/stl/udp_1pkt.py +++ b/scripts/stl/udp_1pkt.py @@ -18,8 +18,8 @@ class STLS1(object): return t[self.mode] def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = self.create_pkt_base () diff --git a/scripts/stl/udp_1pkt_1mac.py b/scripts/stl/udp_1pkt_1mac.py index 4adffd7a..ade5b592 100644 --- a/scripts/stl/udp_1pkt_1mac.py +++ b/scripts/stl/udp_1pkt_1mac.py @@ -8,8 +8,8 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' diff --git a/scripts/stl/udp_1pkt_1mac_override.py b/scripts/stl/udp_1pkt_1mac_override.py index 04700420..410c2630 100644 --- a/scripts/stl/udp_1pkt_1mac_override.py +++ b/scripts/stl/udp_1pkt_1mac_override.py @@ -10,8 +10,8 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS # Ether(src="00:bb:12:34:56:01") this will tell TRex to take the src-mac from packet and not from config file base_pkt = Ether(src="00:bb:12:34:56:01")/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) diff --git a/scripts/stl/udp_1pkt_1mac_step.py b/scripts/stl/udp_1pkt_1mac_step.py index 1e5e4bd8..69a84d67 100644 --- a/scripts/stl/udp_1pkt_1mac_step.py +++ b/scripts/stl/udp_1pkt_1mac_step.py @@ -9,8 +9,8 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' diff --git a/scripts/stl/udp_1pkt_mac.py b/scripts/stl/udp_1pkt_mac.py index 598e2074..93376aff 100644 --- a/scripts/stl/udp_1pkt_mac.py +++ b/scripts/stl/udp_1pkt_mac.py @@ -9,8 +9,8 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' diff --git a/scripts/stl/udp_1pkt_mac_mask1.py b/scripts/stl/udp_1pkt_mac_mask1.py index efb45da7..9a4862a9 100644 --- a/scripts/stl/udp_1pkt_mac_mask1.py +++ b/scripts/stl/udp_1pkt_mac_mask1.py @@ -9,8 +9,8 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' diff --git a/scripts/stl/udp_1pkt_mac_mask2.py b/scripts/stl/udp_1pkt_mac_mask2.py index b95a32e3..748ddbb1 100644 --- a/scripts/stl/udp_1pkt_mac_mask2.py +++ b/scripts/stl/udp_1pkt_mac_mask2.py @@ -9,8 +9,8 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' diff --git a/scripts/stl/udp_1pkt_mac_mask3.py b/scripts/stl/udp_1pkt_mac_mask3.py index 7a5d2864..f3593ccb 100644 --- a/scripts/stl/udp_1pkt_mac_mask3.py +++ b/scripts/stl/udp_1pkt_mac_mask3.py @@ -9,8 +9,8 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' diff --git a/scripts/stl/udp_1pkt_mac_mask5.py b/scripts/stl/udp_1pkt_mac_mask5.py index 75f9bbf1..901c2d98 100644 --- a/scripts/stl/udp_1pkt_mac_mask5.py +++ b/scripts/stl/udp_1pkt_mac_mask5.py @@ -9,8 +9,8 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' diff --git a/scripts/stl/udp_1pkt_mac_step.py b/scripts/stl/udp_1pkt_mac_step.py index 0ebd035d..a2444905 100644 --- a/scripts/stl/udp_1pkt_mac_step.py +++ b/scripts/stl/udp_1pkt_mac_step.py @@ -9,8 +9,8 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' diff --git a/scripts/stl/udp_1pkt_range_clients.py b/scripts/stl/udp_1pkt_range_clients.py index 9bd3c335..f1fc57f4 100644 --- a/scripts/stl/udp_1pkt_range_clients.py +++ b/scripts/stl/udp_1pkt_range_clients.py @@ -16,8 +16,8 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether(src="00:00:dd:dd:00:01")/IP(src="55.55.1.1",dst="58.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' diff --git a/scripts/stl/udp_1pkt_range_clients_split.py b/scripts/stl/udp_1pkt_range_clients_split.py index a8c71c0a..9bf09ba4 100644 --- a/scripts/stl/udp_1pkt_range_clients_split.py +++ b/scripts/stl/udp_1pkt_range_clients_split.py @@ -16,8 +16,8 @@ class STLS1(object): def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether(src="00:00:dd:dd:00:01")/IP(src="55.55.1.1",dst="58.0.0.1")/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' diff --git a/scripts/stl/udp_1pkt_range_clients_split_garp.py b/scripts/stl/udp_1pkt_range_clients_split_garp.py index d7f48ed7..4bad8afd 100644 --- a/scripts/stl/udp_1pkt_range_clients_split_garp.py +++ b/scripts/stl/udp_1pkt_range_clients_split_garp.py @@ -10,7 +10,7 @@ class STLS1(object): self.num_clients =3000; # max is 16bit def create_stream (self): - # create a base packet and pad it to size + # Create base packet and pad it to size base_pkt = Ether(src="00:00:dd:dd:00:01",dst="ff:ff:ff:ff:ff:ff")/ARP(psrc="55.55.1.1",hwsrc="00:00:dd:dd:00:01", hwdst="00:00:dd:dd:00:01", pdst="55.55.1.1") vm = STLScVmRaw( [ STLVmFlowVar(name="mac_src", min_value=1, max_value=self.num_clients, size=2, op="inc"), diff --git a/scripts/stl/udp_1pkt_src_ip_split.py b/scripts/stl/udp_1pkt_src_ip_split.py index 778ccf54..48e02433 100644 --- a/scripts/stl/udp_1pkt_src_ip_split.py +++ b/scripts/stl/udp_1pkt_src_ip_split.py @@ -9,8 +9,8 @@ class STLS1(object): self.fsize =64; def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) diff --git a/scripts/stl/udp_1pkt_src_ip_split_latency.py b/scripts/stl/udp_1pkt_src_ip_split_latency.py index 2d81f756..4b297d70 100644 --- a/scripts/stl/udp_1pkt_src_ip_split_latency.py +++ b/scripts/stl/udp_1pkt_src_ip_split_latency.py @@ -16,8 +16,8 @@ class STLS1(object): def create_stream (self, dir,port_id): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS if dir==0: src_ip="16.0.0.1" diff --git a/scripts/stl/udp_1pkt_tuple_gen.py b/scripts/stl/udp_1pkt_tuple_gen.py index 4e9ab12d..733d511b 100644 --- a/scripts/stl/udp_1pkt_tuple_gen.py +++ b/scripts/stl/udp_1pkt_tuple_gen.py @@ -3,7 +3,7 @@ from trex_stl_lib.api import * class STLS1(object): def create_stream (self, packet_len): - # create a base packet and pad it to size + # Create base packet and pad it to size base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) diff --git a/scripts/stl/udp_1pkt_tuple_gen_split.py b/scripts/stl/udp_1pkt_tuple_gen_split.py index e7a33b22..cc9eb5fc 100644 --- a/scripts/stl/udp_1pkt_tuple_gen_split.py +++ b/scripts/stl/udp_1pkt_tuple_gen_split.py @@ -9,8 +9,8 @@ class STLS1(object): self.fsize =64; def create_stream (self): - # create a base packet and pad it to size - size = self.fsize - 4; # no FCS + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) diff --git a/scripts/stl/udp_3pkt_pcap.py b/scripts/stl/udp_3pkt_pcap.py index 19ff46bc..2983f9a1 100644 --- a/scripts/stl/udp_3pkt_pcap.py +++ b/scripts/stl/udp_3pkt_pcap.py @@ -9,19 +9,19 @@ class STLS1(object): def create_stream (self): - return STLProfile( [ STLStream( isg = 10.0, # star in delay + return STLProfile( [ STLStream( isg = 10.0, # start in delay name ='S0', packet = STLPktBuilder(pkt = os.path.join(CP, "yaml/udp_64B_no_crc.pcap")), mode = STLTXSingleBurst( pps = 10, total_pkts = 10), next = 'S1'), # point to next stream - STLStream( self_start = False, # stream is disabled enable trow S0 + STLStream( self_start = False, # Stream is disabled. Will run because it is pointed from S0 name ='S1', packet = STLPktBuilder(pkt = os.path.join(CP, "yaml/udp_594B_no_crc.pcap")), mode = STLTXSingleBurst( pps = 10, total_pkts = 20), next = 'S2' ), - STLStream( self_start = False, # stream is disabled enable trow S0 + STLStream( self_start = False, # Stream is disabled. Will run because it is pointed from S1 name ='S2', packet = STLPktBuilder(pkt = os.path.join(CP, "yaml/udp_1518B_no_crc.pcap")), mode = STLTXSingleBurst( pps = 10, total_pkts = 30 ) -- cgit 1.2.3-korg From ddbed4330eee8b4c40d8ca83535dbf5ac69bb81f Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Tue, 21 Jun 2016 18:17:55 +0300 Subject: api: add passing of additional arguments to TRex --- scripts/automation/regression/trex_unit_test.py | 6 +++++- scripts/automation/trex_control_plane/server/trex_server.py | 8 +++++--- .../automation/trex_control_plane/stf/trex_stf_lib/trex_client.py | 7 ++++--- 3 files changed, 14 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py index 11902a1a..915cd682 100755 --- a/scripts/automation/regression/trex_unit_test.py +++ b/scripts/automation/regression/trex_unit_test.py @@ -146,6 +146,9 @@ class CTRexTestConfiguringPlugin(Plugin): 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", + help="Additional TRex arguments (--no-watchdog etc.).") def configure(self, options, conf): @@ -186,7 +189,8 @@ class CTRexTestConfiguringPlugin(Plugin): if not self.no_daemon: CTRexScenario.trex = CTRexClient(trex_host = self.configuration.trex['trex_name'], verbose = self.json_verbose, - debug_image = options.debug_image) + debug_image = options.debug_image, + trex_args = options.trex_args) if not CTRexScenario.trex.check_master_connectivity(): print('Could not connect to master daemon') sys.exit(-1) diff --git a/scripts/automation/trex_control_plane/server/trex_server.py b/scripts/automation/trex_control_plane/server/trex_server.py index 091b729b..9fe7d70b 100755 --- a/scripts/automation/trex_control_plane/server/trex_server.py +++ b/scripts/automation/trex_control_plane/server/trex_server.py @@ -310,7 +310,7 @@ class CTRexServer(object): return False - def start_trex(self, trex_cmd_options, user, block_to_success = True, timeout = 40, stateless = False, debug_image = False): + def start_trex(self, trex_cmd_options, user, block_to_success = True, timeout = 40, stateless = False, debug_image = False, trex_args = ''): with self.start_lock: logger.info("Processing start_trex() command.") if self.is_reserved(): @@ -323,7 +323,7 @@ class CTRexServer(object): return Fault(-13, '') # raise at client TRexInUseError try: - server_cmd_data = self.generate_run_cmd(stateless = stateless, debug_image = debug_image, **trex_cmd_options) + server_cmd_data = self.generate_run_cmd(stateless = stateless, debug_image = debug_image, trex_args = trex_args, **trex_cmd_options) self.zmq_monitor.first_dump = True self.trex.start_trex(self.TREX_PATH, server_cmd_data) logger.info("TRex session has been successfully initiated.") @@ -413,7 +413,7 @@ class CTRexServer(object): return self.trex.get_running_info() - def generate_run_cmd (self, iom = 0, export_path="/tmp/trex.txt", stateless = False, debug_image = False, **kwargs): + def generate_run_cmd (self, iom = 0, export_path="/tmp/trex.txt", stateless = False, debug_image = False, trex_args = '', **kwargs): """ generate_run_cmd(self, iom, export_path, kwargs) -> str Generates a custom running command for the kick-off of the TRex traffic generator. @@ -450,6 +450,8 @@ class CTRexServer(object): continue else: trex_cmd_options += (dash + '{k} {val}'.format( k = tmp_key, val = value )) + if trex_args: + trex_cmd_options += ' %s' % trex_args if not stateless: if 'f' not in kwargs: 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 57d19459..d3362d0d 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 @@ -40,7 +40,7 @@ class CTRexClient(object): This class defines the client side of the RESTfull interaction with TRex """ - def __init__(self, trex_host, max_history_size = 100, filtered_latency_amount = 0.001, trex_daemon_port = 8090, master_daemon_port = 8091, trex_zmq_port = 4500, verbose = False, debug_image = False): + def __init__(self, trex_host, max_history_size = 100, filtered_latency_amount = 0.001, trex_daemon_port = 8090, master_daemon_port = 8091, trex_zmq_port = 4500, verbose = False, debug_image = False, trex_args = ''): """ Instantiate a TRex client object, and connecting it to listening daemon-server @@ -97,6 +97,7 @@ class CTRexClient(object): self.trex_server_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = trex_daemon_port ) self.server = jsonrpclib.Server(self.trex_server_path, history = self.history) self.debug_image = debug_image + self.trex_args = trex_args def add (self, x, y): @@ -160,7 +161,7 @@ class CTRexClient(object): self.result_obj.clear_results() try: issue_time = time.time() - retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, False, self.debug_image) + retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, self.debug_image, self.trex_args) except AppError as err: self._handle_AppError_exception(err.args[0]) except ProtocolError: @@ -206,7 +207,7 @@ class CTRexClient(object): """ try: user = user or self.__default_user - retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, True, self.debug_image) + retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, True, self.debug_image, self.trex_args) except AppError as err: self._handle_AppError_exception(err.args[0]) except ProtocolError: -- cgit 1.2.3-korg From a5d8474b3f5598561298efc6e696378f584011c9 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Wed, 22 Jun 2016 00:14:17 +0300 Subject: typo at last commit --- scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') 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 d3362d0d..a13fe31f 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 @@ -161,7 +161,7 @@ class CTRexClient(object): self.result_obj.clear_results() try: issue_time = time.time() - retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, self.debug_image, self.trex_args) + retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, False, self.debug_image, self.trex_args) except AppError as err: self._handle_AppError_exception(err.args[0]) except ProtocolError: -- cgit 1.2.3-korg From 9f641b7c0b7d99cf868f563b83cb047ced8cb275 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Wed, 22 Jun 2016 10:58:17 +0300 Subject: adding flow stat profiles to simulator tests --- .../regression/functional_tests/stl_basic_tests.py | 2 ++ scripts/exp/flow_stats.pcap | Bin 0 -> 100 bytes scripts/exp/flow_stats_latency.pcap | Bin 0 -> 136 bytes src/bp_sim.cpp | 28 ++++++++++----------- src/flow_stat.cpp | 10 ++++++++ 5 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 scripts/exp/flow_stats.pcap create mode 100644 scripts/exp/flow_stats_latency.pcap (limited to 'scripts') diff --git a/scripts/automation/regression/functional_tests/stl_basic_tests.py b/scripts/automation/regression/functional_tests/stl_basic_tests.py index 863307f1..a4e28ca9 100644 --- a/scripts/automation/regression/functional_tests/stl_basic_tests.py +++ b/scripts/automation/regression/functional_tests/stl_basic_tests.py @@ -201,6 +201,8 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test): ["multi_burst_2st_1000pkt.py","-m 1 -l 100",True], ["pcap.py", "-m 1", True], ["pcap_with_vm.py", "-m 1", True], + ["flow_stats.py", "-m 1 -l 1", True], + ["flow_stats_latency.py", "-m 1 -l 1", True], # YAML test ["yaml/burst_1000_pkt.yaml","-m 1 -l 100",True], diff --git a/scripts/exp/flow_stats.pcap b/scripts/exp/flow_stats.pcap new file mode 100644 index 00000000..267eeaab Binary files /dev/null and b/scripts/exp/flow_stats.pcap differ diff --git a/scripts/exp/flow_stats_latency.pcap b/scripts/exp/flow_stats_latency.pcap new file mode 100644 index 00000000..89d27701 Binary files /dev/null and b/scripts/exp/flow_stats_latency.pcap differ diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp index f4eb6f7e..f9e96b6b 100755 --- a/src/bp_sim.cpp +++ b/src/bp_sim.cpp @@ -4991,29 +4991,27 @@ int CErfIFStl::send_sl_node(CGenNodeStateless *node_sl) { bool is_const = false; if (m) { is_const = true; + rte_pktmbuf_refcnt_update(m,1); }else{ m=node_sl->alloc_node_with_vm(); assert(m); } - if (node_sl->is_stat_needed()) { + if (node_sl->is_stat_needed() && (node_sl->get_stat_hw_id() >= MAX_FLOW_STATS) ) { + /* latency packet. flow stat without latency handled like normal packet in simulation */ uint16_t hw_id = node_sl->get_stat_hw_id(); - if (hw_id >= MAX_FLOW_STATS) { - rte_mbuf_t *mi; - struct flow_stat_payload_header *fsp_head; - mi = node_sl->alloc_flow_stat_mbuf(m, fsp_head, is_const); - fsp_head->seq = 0x12345678; - fsp_head->hw_id = hw_id - MAX_FLOW_STATS; - fsp_head->magic = FLOW_STAT_PAYLOAD_MAGIC; - fsp_head->time_stamp = 0x8899aabbccddeeff; - fill_raw_packet(m,(CGenNode *)node_sl,dir); - rte_pktmbuf_free(mi); - } + rte_mbuf_t *mi; + struct flow_stat_payload_header *fsp_head; + mi = node_sl->alloc_flow_stat_mbuf(m, fsp_head, is_const); + fsp_head->seq = 0x12345678; + fsp_head->hw_id = hw_id - MAX_FLOW_STATS; + fsp_head->magic = FLOW_STAT_PAYLOAD_MAGIC; + fsp_head->time_stamp = 0x8899aabbccddeeff; + fill_raw_packet(mi, (CGenNode *)node_sl, dir); + rte_pktmbuf_free(mi); } else { fill_raw_packet(m,(CGenNode *)node_sl,dir); - if (! is_const) { - rte_pktmbuf_free(m); - } + rte_pktmbuf_free(m); } } /* check that we have mbuf */ diff --git a/src/flow_stat.cpp b/src/flow_stat.cpp index 366c3aa8..cb7a1bf9 100644 --- a/src/flow_stat.cpp +++ b/src/flow_stat.cpp @@ -472,6 +472,16 @@ CFlowStatRuleMgr::CFlowStatRuleMgr() { CFlowStatRuleMgr::~CFlowStatRuleMgr() { delete m_parser; +#ifdef TREX_SIM + // In simulator, nobody handles the messages to RX, so need to free them to have clean valgrind run. + if (m_ring_to_rx) { + CGenNode *msg = NULL; + while (! m_ring_to_rx->isEmpty()) { + m_ring_to_rx->Dequeue(msg); + delete msg; + } + } +#endif } void CFlowStatRuleMgr::create() { -- cgit 1.2.3-korg From 9a1356bc05d663555b9b62971aff6219e17a767c Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 21 Jun 2016 09:54:04 +0300 Subject: FLOW_STATS: extract start_stream from the compiler to start_traffic --- .../trex_control_plane/common/text_opts.py | 3 + .../stl/trex_stl_lib/utils/text_opts.py | 3 + src/stateless/cp/trex_stateless_port.cpp | 73 ++++++++++++++++++++-- src/stateless/cp/trex_streams_compiler.cpp | 31 +++++---- src/stateless/cp/trex_streams_compiler.h | 4 +- 5 files changed, 91 insertions(+), 23 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/trex_control_plane/common/text_opts.py b/scripts/automation/trex_control_plane/common/text_opts.py index 78a0ab1f..ab0fd2f2 100755 --- a/scripts/automation/trex_control_plane/common/text_opts.py +++ b/scripts/automation/trex_control_plane/common/text_opts.py @@ -61,6 +61,9 @@ def format_time (t_sec): if t_sec < 0: return "infinite" + if t_sec == 0: + return "zero" + if t_sec < 1: # low numbers for unit in ['ms', 'usec', 'ns']: 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 7e0bf9e4..26e64dae 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 @@ -61,6 +61,9 @@ def format_time (t_sec): if t_sec < 0: return "infinite" + if t_sec == 0: + return "zero" + if t_sec < 1: # low numbers for unit in ['ms', 'usec', 'ns']: diff --git a/src/stateless/cp/trex_stateless_port.cpp b/src/stateless/cp/trex_stateless_port.cpp index 4dc3e449..d736d09e 100644 --- a/src/stateless/cp/trex_stateless_port.cpp +++ b/src/stateless/cp/trex_stateless_port.cpp @@ -87,6 +87,68 @@ protected: } }; +/************************************* + * Streams Feeder + * A class that holds a temporary + * clone of streams that can be + * manipulated + * + * this is a RAII object meant for + * graceful cleanup + ************************************/ +class StreamsFeeder { +public: + StreamsFeeder(TrexStatelessPort *port) { + + /* start pesimistic */ + m_success = false; + + /* fetch the original streams */ + port->get_object_list(m_in_streams); + + for (const TrexStream *in_stream : m_in_streams) { + TrexStream *out_stream = in_stream->clone(true); + + get_stateless_obj()->m_rx_flow_stat.start_stream(out_stream); + + m_out_streams.push_back(out_stream); + } + } + + void set_status(bool status) { + m_success = status; + } + + vector &get_streams() { + return m_out_streams; + } + + /** + * RAII + */ + ~StreamsFeeder() { + for (int i = 0; i < m_out_streams.size(); i++) { + TrexStream *out_stream = m_out_streams[i]; + TrexStream *in_stream = m_in_streams[i]; + + if (m_success) { + /* success path */ + get_stateless_obj()->m_rx_flow_stat.copy_state(out_stream, in_stream); + } else { + /* fail path */ + get_stateless_obj()->m_rx_flow_stat.stop_stream(out_stream); + } + delete out_stream; + } + } + +private: + vector m_in_streams; + vector m_out_streams; + bool m_success; +}; + + /*************************** * trex stateless port * @@ -193,10 +255,7 @@ TrexStatelessPort::start_traffic(const TrexPortMultiplier &mul, double duration, /* caclulate the effective factor for DP */ double factor = calculate_effective_factor(mul, force); - /* fetch all the streams from the table */ - vector streams; - get_object_list(streams); - + StreamsFeeder feeder(this); /* compiler it */ std::vector compiled_objs; @@ -204,15 +263,19 @@ TrexStatelessPort::start_traffic(const TrexPortMultiplier &mul, double duration, TrexStreamsCompiler compiler; bool rc = compiler.compile(m_port_id, - streams, + feeder.get_streams(), compiled_objs, get_dp_core_count(), factor, &fail_msg); + if (!rc) { + feeder.set_status(false); throw TrexException(fail_msg); } + feeder.set_status(true); + /* generate a message to all the relevant DP cores to start transmitting */ assert(m_pending_async_stop_event == TrexDpPortEvents::INVALID_ID); m_pending_async_stop_event = m_dp_events.create_event(new AsyncStopEvent()); diff --git a/src/stateless/cp/trex_streams_compiler.cpp b/src/stateless/cp/trex_streams_compiler.cpp index d2fe416a..e54c5f9c 100644 --- a/src/stateless/cp/trex_streams_compiler.cpp +++ b/src/stateless/cp/trex_streams_compiler.cpp @@ -382,7 +382,13 @@ TrexStreamsCompiler::compile(uint8_t port_id, assert(dp_core_count > 0); try { - return compile_internal(port_id,streams,objs,dp_core_count,factor,fail_msg); + return compile_internal(port_id, + streams, + objs, + dp_core_count, + factor, + fail_msg); + } catch (const TrexException &ex) { if (fail_msg) { *fail_msg = ex.what(); @@ -411,7 +417,6 @@ TrexStreamsCompiler::compile_internal(uint8_t por GraphNodeMap nodes; - /* compile checks */ pre_compile_check(streams, nodes); @@ -474,7 +479,7 @@ TrexStreamsCompiler::compile_on_single_core(uint8_t } /* compile all the streams */ - for (auto stream : streams) { + for (auto const stream : streams) { /* skip non-enabled streams */ if (!stream->m_enabled) { @@ -507,7 +512,7 @@ TrexStreamsCompiler::compile_on_all_cores(uint8_t } /* compile all the streams */ - for (auto stream : streams) { + for (auto const stream : streams) { /* skip non-enabled streams */ if (!stream->m_enabled) { @@ -527,7 +532,7 @@ TrexStreamsCompiler::compile_on_all_cores(uint8_t * */ void -TrexStreamsCompiler::compile_stream(TrexStream *stream, +TrexStreamsCompiler::compile_stream(const TrexStream *stream, double factor, uint8_t dp_core_count, std::vector &objs, @@ -543,31 +548,25 @@ TrexStreamsCompiler::compile_stream(TrexStream *stream, new_next_id = nodes.get(stream->m_next_stream_id)->m_compressed_stream_id; } - TrexStream *fixed_rx_flow_stat_stream = stream->clone(true); - - get_stateless_obj()->m_rx_flow_stat.start_stream(fixed_rx_flow_stat_stream); - // CFlowStatRuleMgr keeps state of the stream object. We duplicated the stream here (in order not - // change the packet kept in the stream). We want the state to be saved in the original stream. - get_stateless_obj()->m_rx_flow_stat.copy_state(fixed_rx_flow_stat_stream, stream); - - fixed_rx_flow_stat_stream->update_rate_factor(factor); + /* we clone because we alter the stream now */ + std::unique_ptr tmp_stream(stream->clone(true)); + tmp_stream->update_rate_factor(factor); /* can this stream be split to many cores ? */ if ( (dp_core_count == 1) || (!stream->is_splitable(dp_core_count)) ) { - compile_stream_on_single_core(fixed_rx_flow_stat_stream, + compile_stream_on_single_core(tmp_stream.get(), dp_core_count, objs, new_id, new_next_id); } else { - compile_stream_on_all_cores(fixed_rx_flow_stat_stream, + compile_stream_on_all_cores(tmp_stream.get(), dp_core_count, objs, new_id, new_next_id); } - delete fixed_rx_flow_stat_stream; } /** diff --git a/src/stateless/cp/trex_streams_compiler.h b/src/stateless/cp/trex_streams_compiler.h index 0ce71b49..7e674364 100644 --- a/src/stateless/cp/trex_streams_compiler.h +++ b/src/stateless/cp/trex_streams_compiler.h @@ -141,7 +141,7 @@ private: bool all_continues); - void compile_stream(TrexStream *stream, + void compile_stream(const TrexStream *stream, double factor, uint8_t dp_core_count, std::vector &objs, @@ -244,7 +244,7 @@ public: } double get_factor_pps(double req_pps) const { - if ( (req_pps - m_fixed.m_pps) <= 0 ) { + if ( (req_pps - m_fixed.m_pps) < 0 ) { std::stringstream ss; ss << "current stream configuration enforces a minimum rate of '" << m_fixed.m_pps << "' pps"; throw TrexException(ss.str()); -- cgit 1.2.3-korg From 9249859480c57960905f37282e9fa8047cf17484 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Wed, 22 Jun 2016 14:22:54 +0300 Subject: STL Python API stats - add histogram key if it's absent --- scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py | 1 + 1 file changed, 1 insertion(+) (limited to 'scripts') 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 0ec98a0d..88a94865 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 @@ -1036,6 +1036,7 @@ class CLatencyStats(CTRexStats): output[int_pg_id]['latency']['total_min'] = min_val else: output[int_pg_id]['latency']['total_min'] = StatNotAvailable('total_min') + output[int_pg_id]['latency']['histogram'] = {} self.latest_stats = output return True -- cgit 1.2.3-korg From d97b7b7159c8c7f37f45cd2c98de042b1c7b5d69 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Wed, 22 Jun 2016 16:05:54 +0300 Subject: add latency regression test - XL710/82599 setups --- .../regression/stateless_tests/stl_rx_test.py | 131 ++++++++++++++++++++- 1 file changed, 129 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index 41eabd65..9e275118 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -3,6 +3,8 @@ from .stl_general_test import CStlGeneral_Test, CTRexScenario from trex_stl_lib.api import * import os, sys +ERROR_LATENCY_TOO_HIGH = 1 + class STLRX_Test(CStlGeneral_Test): """Tests for RX feature""" @@ -11,8 +13,8 @@ class STLRX_Test(CStlGeneral_Test): # self.skip('This test makes trex08 and trex09 sick. Fix those ASAP.') if self.is_virt_nics: self.skip('Skip this for virtual NICs for now') - per_driver_params = {"rte_vmxnet3_pmd": [1, 50, 1], "rte_ixgbe_pmd": [30, 5000, 1], "rte_i40e_pmd": [80, 5000, 1], - "rte_igb_pmd": [80, 500, 1], "rte_em_pmd": [1, 50, 1], "rte_virtio_pmd": [1, 50, 1]} + per_driver_params = {"rte_vmxnet3_pmd": [1, 50, 1,False], "rte_ixgbe_pmd": [30, 5000, 1,True,200,400], "rte_i40e_pmd": [80, 5000, 1,True,100,250], + "rte_igb_pmd": [80, 500, 1,False], "rte_em_pmd": [1, 50, 1,False], "rte_virtio_pmd": [1, 50, 1,False]} CStlGeneral_Test.setUp(self) assert 'bi' in CTRexScenario.stl_ports_map @@ -38,6 +40,15 @@ class STLRX_Test(CStlGeneral_Test): self.pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('Your_paylaod_comes_here')) self.large_pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*1000)) + self.pkt_9k = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*9000)) + + + drv_name=port_info['driver'] + self.latency_9k_enable=per_driver_params[drv_name][3] + + self.latency_9k_max_average = per_driver_params[drv_name][4] + self.latency_9k_max_latency = per_driver_params[drv_name][5] + @classmethod def tearDownClass(cls): @@ -48,6 +59,36 @@ class STLRX_Test(CStlGeneral_Test): CTRexScenario.stl_trex.connect() + def __verify_latency (self, latency_stats,max_latency,max_average): + + error=0; + err_latency = latency_stats['err_cntrs'] + latency = latency_stats['latency'] + + for key in err_latency : + error +=err_latency[key] + if error !=0 : + pprint.pprint(err_latency) + tmp = 'RX pkts ERROR - one of the error is on' + print(tmp) + #assert False, tmp + + if latency['average']> max_average: + pprint.pprint(latency_stats) + tmp = 'Average latency is too high {0} {1} '.format(latency['average'], max_average) + print(tmp) + return ERROR_LATENCY_TOO_HIGH + + if latency['total_max']> max_latency: + pprint.pprint(latency_stats) + tmp = 'Max latency is too high {0} {1} '.format(latency['total_max'], max_latency) + print(tmp) + return ERROR_LATENCY_TOO_HIGH + + return 0 + + + def __verify_flow (self, pg_id, total_pkts, pkt_len, stats): flow_stats = stats['flow_stats'].get(pg_id) latency_stats = stats['latency'].get(pg_id) @@ -216,6 +257,92 @@ class STLRX_Test(CStlGeneral_Test): assert False , '{0}'.format(e) + + def __test_9k_stream(self,pgid,ports,precet,max_latency,avg_latency,duration,pkt_size): + my_pg_id=pgid + s_ports=ports; + all_ports=list(CTRexScenario.stl_ports_map['map'].keys()); + if ports == None: + s_ports=all_ports + assert( type(s_ports)==list) + + stream_pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*pkt_size)) + + try: + # reset all ports + self.c.reset(ports = all_ports) + + + for pid in s_ports: + s1 = STLStream(name = 'rx', + packet = self.pkt, + flow_stats = STLFlowLatencyStats(pg_id = my_pg_id+pid), + mode = STLTXCont(pps = 1000)) + + s2 = STLStream(name = 'bulk', + packet = stream_pkt, + mode = STLTXCont(percentage =precet)) + + + # add both streams to ports + self.c.add_streams([s1,s2], ports = [pid]) + + self.c.clear_stats() + + self.c.start(ports = s_ports,duration = duration) + self.c.wait_on_traffic(ports = s_ports,timeout = duration+10,rx_delay_ms = 100) + stats = self.c.get_stats() + + for pid in s_ports: + latency_stats = stats['latency'].get(my_pg_id+pid) + #pprint.pprint(latency_stats) + if self.__verify_latency (latency_stats,max_latency,avg_latency) !=0: + return (ERROR_LATENCY_TOO_HIGH); + + return 0 + + except STLError as e: + assert False , '{0}'.format(e) + + + + + + # check low latency when you have stream of 9K stream + def test_9k_stream(self): + + if self.latency_9k_enable == False: + print("SKIP") + return + + for i in range(0,5): + print("Iteration {0}".format(i)); + duration=random.randint(10, 70); + pgid=random.randint(1, 65000); + pkt_size=random.randint(1000, 9000); + all_ports = list(CTRexScenario.stl_ports_map['map'].keys()); + s_port=random.sample(all_ports, random.randint(1, len(all_ports)) ) + s_port=sorted(s_port) + 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)); + if self.__test_9k_stream(pgid, + s_port,90, + self.latency_9k_max_latency, + self.latency_9k_max_average, + duration, + pkt_size)==0: + error=0; + break; + + if error: + assert False , "Latency too high" + else: + print("===>Iteration {0} PASS {1}".format(i,j)); + + + + # this test adds more and more latency streams and re-test with incremental def test_incremental_latency_streams (self): -- cgit 1.2.3-korg From 06b5ac67155c1c0bbe5d779755f9b3ff759cb8ad Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Wed, 22 Jun 2016 19:13:25 +0300 Subject: fix 9k regression test for XL710 DA2 NIC --- .../regression/stateless_tests/stl_rx_test.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index 9e275118..bf46bc20 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -24,6 +24,9 @@ class STLRX_Test(CStlGeneral_Test): self.tx_port, self.rx_port = CTRexScenario.stl_ports_map['bi'][0] 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)) @@ -45,9 +48,9 @@ class STLRX_Test(CStlGeneral_Test): drv_name=port_info['driver'] self.latency_9k_enable=per_driver_params[drv_name][3] - - self.latency_9k_max_average = per_driver_params[drv_name][4] - self.latency_9k_max_latency = per_driver_params[drv_name][5] + if self.latency_9k_enable: + self.latency_9k_max_average = per_driver_params[drv_name][4] + self.latency_9k_max_latency = per_driver_params[drv_name][5] @classmethod @@ -321,8 +324,20 @@ class STLRX_Test(CStlGeneral_Test): pgid=random.randint(1, 65000); pkt_size=random.randint(1000, 9000); all_ports = list(CTRexScenario.stl_ports_map['map'].keys()); + + s_port=random.sample(all_ports, random.randint(1, len(all_ports)) ) s_port=sorted(s_port) + if self.speed == 40 : + # the NIC does not support all full rate in case both port works let's + tmp_l=[] + for port in s_port: + if ((int(port) % 2) ==0): + tmp_l.append(port); + s_port=tmp_l; + 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)); -- cgit 1.2.3-korg From 776aff59c30d204a953e5b3a42172e828f030da6 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Wed, 22 Jun 2016 19:16:05 +0300 Subject: minor --- scripts/automation/regression/stateless_tests/stl_rx_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index bf46bc20..84f32081 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -329,7 +329,7 @@ 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 : - # the NIC does not support all full rate in case both port works let's + # the NIC does not support all full rate in case both port works let's filter odd ports tmp_l=[] for port in s_port: if ((int(port) % 2) ==0): -- cgit 1.2.3-korg From 24c22cf22f429c5214b1f1beba5145e7c4a2c4a8 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Thu, 23 Jun 2016 09:19:54 +0300 Subject: get_stats documentation --- .../stl/trex_stl_lib/trex_stl_client.py | 153 ++++++++++++++++++++- scripts/stl/flow_stats_latency.py | 4 +- 2 files changed, 151 insertions(+), 6 deletions(-) (limited to 'scripts') 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 4d1125c8..6dec6fa7 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 @@ -1254,18 +1254,161 @@ class STLClient(object): # get stats - def get_stats (self, ports = None, async_barrier = True): + def get_stats (self, ports = None, sync_now = True): + """ + Return dictionary containing statistics information gathered from the server. + + :parameters: + + ports - List of ports to retreive stats on. + If None, assume the request is for all acquired ports. + + sync_now - Boolean - If true, create a call to the server to get latest stats, and wait for result to arrive. Otherwise, return last stats saved in client cache. + Downside of putting True is a slight delay (few 10th msecs) in getting the result. For practical uses, value should be True. + :return: + Statistics dictionary of dictionaries with the following format: + + =============================== =============== + key Meaning + =============================== =============== + :ref:`numbers (0,1,..` Statistcs per port number + :ref:`total ` Sum of port statistics + :ref:`flow_stats ` Per flow statistics + :ref:`global ` Global statistics + :ref:`latency ` Per flow statistics regarding flow latency + =============================== =============== + + Below is description of each of the inner dictionaries. + + .. _total: + + **total** and per port statistics contain dictionary with following format. + + =============================== =============== + key Meaning + =============================== =============== + ibytes Number of input bytes + ierrors Number of input errors + ipackets Number of input packets + obytes Number of output bytes + oerrors Number of output errors + opackets Number of output packets + rx_bps Receive bytes per second rate + rx_pps Receive packet per second rate + tx_bps Transmit bytes per second rate + tx_pps Transmit packet per second rate + =============================== =============== + + .. _flow_stats: + + **flow_stats** contains dictionaries per packet group id (pg id). Each one with the following structure. + + ================= =============== + key Meaning + ================= =============== + rx_bps Receivd bytes per second rate + rx_bps_l1 Receivd bytes per second rate, including layer one + rx_bytes Total number of received bytes + rx_pkts Total number of received packets + rx_pps Received packets per second + tx_bps Transmitted bytes per second rate + tx_bps_l1 Transmitted bytes per second rate, including layer one + tx_bytes Total number of sent bytes + tx_pkts Total number of sent packets + tx_pps Transmit packets per second + ================= =============== + + .. _global: + + **global** + + ================= =============== + key Meaning + ================= =============== + bw_per_core Estimated byte rate Trex can support per core. This is calculated by extrapolation of current rate and load on transmitting cores. + cpu_util Estimate of the average utilization percentage of the transimitting cores + queue_full Total number of packets we could not transmit because NIC TX queue was full. This usually indicates that the rate we trying to transmit is too high for this port + rx_cpu_util Estimate of the utilization percentage of the core handling RX traffic + rx_drop_bps Received bytes per second drop rate + rx_bps Received bytes per second rate + rx_pps Received packets per second rate + tx_bps Transmit bytes per second rate + tx_pps Transmit packets per second rate + ================= =============== + + .. _latency: + + **latency** contains dictionary per packet group id (pg id). Each one with the following structure. + + =========================== =============== + key Meaning + =========================== =============== + :ref:`err_cntrs` Counters describing errors that occured with this pg id + :ref:`latency` Information regarding packet latency + =========================== =============== + + Following are the inner dictionaries of latency + + .. _err-cntrs: + + **err-cntrs** + + ================= =============== + key Meaning (see better explanation below the table) + ================= =============== + dropped How many packets were dropped. + dup How many packets were duplicated. + out_of_order How many packets we received out of order. + seq_too_high How many events of packet with sequence number too high we saw. + seq_too_low How many events of packet with sequence number too low we saw. + ================= =============== + + For calculating packet error events, we add sequence number to each packet's payload. We decide what went wrong only according to sequence number + of last packet received and that of the previous packet. 'seq_too_low' and 'seq_too_high' count events we see. 'dup', 'out_of_order' and 'dropped' + are heuristics we apply to try and understand what happened. They will be accurate in common error scenarios. + We describe few scenarios below to help understand this. + + Scenario 1: Received packet with seq num 10, and another one with seq num 10. We increment 'dup' and 'seq_too_low' by 1. + + Scenario 2: Received pacekt with seq num 10 and then packet with seq num 15. We assume 4 packets were dropped, and increment 'dropped' by 4, and 'seq_too_high' by 1. + We expect next packet to arrive with sequence number 16. + + Scenario 2 continue: Received packet with seq num 11. We increment 'seq_too_low' by 1. We increment 'out_of_order' by 1. We *decrement* 'dropped' by 1. + (We assume here that one of the packets we considered as dropped before, actually arrived out of order). + + + .. _lat_inner: + + **latency** + + ================= =============== + key Meaning + ================= =============== + average Average latency over the stream lifetime (usec). Total average is computed each sampling period by following formula: = /2 + /2 + histogram Dictionary describing logarithmic distribution histogram of packet latencies. Keys in the dictionary represent range of latencies (in usec). Values are the total number of packets received in this latency range. For example, an entry {100:13} would mean that we saw 13 packets with latency in the range between 100 and 200 usec. + jitter Jitter of latency samples, computed as described in :rfc:`3550#appendix-A.8` + last_max Maximum latency measured between last two data reads from server. + total_max Maximum latency measured over the stream lifetime (in usec). + total_min Minimum latency measured over the stream lifetime (in usec). + ================= =============== + + + + :raises: + None + + """ # by default use all acquired ports ports = ports if ports is not None else self.get_acquired_ports() ports = self._validate_port_list(ports) # check async barrier - if not type(async_barrier) is bool: - raise STLArgumentError('async_barrier', async_barrier) + if not type(sync_now) is bool: + raise STLArgumentError('sync_now', sync_now) # if the user requested a barrier - use it - if async_barrier: + if sync_now: rc = self.async_client.barrier() if not rc: raise STLError(rc) @@ -1279,7 +1422,7 @@ class STLClient(object): :parameters: ev_type_filter - 'info', 'warning' or a list of those - default is no filter + default: no filter :return: logged events diff --git a/scripts/stl/flow_stats_latency.py b/scripts/stl/flow_stats_latency.py index 8460fcfc..e053549e 100644 --- a/scripts/stl/flow_stats_latency.py +++ b/scripts/stl/flow_stats_latency.py @@ -6,6 +6,8 @@ class STLS1(object): Can specify using tunables the packet length (fsize) and packet group id (pg_id) Since we can't have two latency streams with same pg_id, in order to be able to start this profile on more than one port, we add port_id to the pg_id + Notice that for perfomance reasons, latency streams are not affected by -m flag, so + you can only change the pps value by editing the code. """ def __init__ (self): @@ -19,7 +21,7 @@ class STLS1(object): pkt = STLPktBuilder(pkt = base_pkt/pad) return [STLStream(packet = pkt, - mode = STLTXCont(pps=1), + mode = STLTXCont(pps=1000), flow_stats = STLFlowLatencyStats(pg_id = self.pg_id)) ] -- cgit 1.2.3-korg From d04442ab671f768a1b645fb887d4a9cd575c7852 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Thu, 23 Jun 2016 09:56:30 +0300 Subject: daemons logger: create log dir if does not exist --- scripts/automation/trex_control_plane/server/CCustomLogger.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/trex_control_plane/server/CCustomLogger.py b/scripts/automation/trex_control_plane/server/CCustomLogger.py index a8823cea..6d3974a6 100755 --- a/scripts/automation/trex_control_plane/server/CCustomLogger.py +++ b/scripts/automation/trex_control_plane/server/CCustomLogger.py @@ -3,15 +3,13 @@ import sys import os import logging +def prepare_dir(log_path): + log_dir = os.path.dirname(log_path) + if not os.path.exists(log_dir): + os.makedirs(log_dir) def setup_custom_logger(name, log_path = None): # first make sure path availabe -# if log_path is None: -# log_path = os.getcwd()+'/trex_log.log' -# else: -# directory = os.path.dirname(log_path) -# if not os.path.exists(directory): -# os.makedirs(directory) logging.basicConfig(level = logging.INFO, format = '%(asctime)s %(name)-10s %(module)-20s %(levelname)-8s %(message)s', datefmt = '%m-%d %H:%M') @@ -31,6 +29,7 @@ def setup_custom_logger(name, log_path = None): def setup_daemon_logger (name, log_path = None): # first make sure path availabe + prepare_dir(log_path) try: os.unlink(log_path) except: -- cgit 1.2.3-korg From bc3c25d78a3aa8051d7247465181c5afd6ac8ca2 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Thu, 23 Jun 2016 10:53:41 +0300 Subject: skip 9k_latency test until defect will be solved --- scripts/automation/regression/stateless_tests/stl_rx_test.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index 84f32081..d8a25564 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -261,7 +261,7 @@ class STLRX_Test(CStlGeneral_Test): - def __test_9k_stream(self,pgid,ports,precet,max_latency,avg_latency,duration,pkt_size): + def __9k_stream(self,pgid,ports,precet,max_latency,avg_latency,duration,pkt_size): my_pg_id=pgid s_ports=ports; all_ports=list(CTRexScenario.stl_ports_map['map'].keys()); @@ -314,6 +314,8 @@ class STLRX_Test(CStlGeneral_Test): # check low latency when you have stream of 9K stream def test_9k_stream(self): + self.skip('Skip due to bug trex-215') + if self.latency_9k_enable == False: print("SKIP") return @@ -330,18 +332,14 @@ class STLRX_Test(CStlGeneral_Test): s_port=sorted(s_port) if self.speed == 40 : # the NIC does not support all full rate in case both port works let's filter odd ports - tmp_l=[] - for port in s_port: - if ((int(port) % 2) ==0): - tmp_l.append(port); - s_port=tmp_l; + 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)); - if self.__test_9k_stream(pgid, + if self.__9k_stream(pgid, s_port,90, self.latency_9k_max_latency, self.latency_9k_max_average, -- cgit 1.2.3-korg From 590accee66691009003adcdc12bdd6eefbb566b0 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Thu, 23 Jun 2016 10:57:00 +0300 Subject: regression functional test stl_basic_tests: add error message to assert --- scripts/automation/regression/functional_tests/stl_basic_tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/functional_tests/stl_basic_tests.py b/scripts/automation/regression/functional_tests/stl_basic_tests.py index a4e28ca9..e03c0742 100644 --- a/scripts/automation/regression/functional_tests/stl_basic_tests.py +++ b/scripts/automation/regression/functional_tests/stl_basic_tests.py @@ -85,7 +85,7 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test): pkts1 = list(RawPcapReader(output)) pkts2 = list(RawPcapReader(golden)) - assert_equal(len(pkts1), len(pkts2)) + assert_equal(len(pkts1), len(pkts2), 'Lengths of generated pcap (%s) and golden (%s) are different' % (output, golden)) for pkt1, pkt2, i in zip(pkts1, pkts2, range(1, len(pkts1))): ts1 = float(pkt1[1][0]) + (float(pkt1[1][1]) / 1e6) @@ -143,7 +143,7 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test): os.unlink(output_cap) try: rc = self.run_sim(input_file, output_cap, options, silent) - assert_equal(rc, True) + assert_equal(rc, True, 'Simulation on profile %s failed.' % profile) #s='cp '+output_cap+' '+golden_file; #print s #os.system(s) @@ -165,7 +165,7 @@ class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test): profile.dump_to_code(generated_filename) rc = self.run_sim(generated_filename, output_cap, options, silent) - assert_equal(rc, True) + assert_equal(rc, True, 'Simulation on profile %s (generated) failed.' % profile) if compare: self.compare_caps(output_cap, golden_file) -- cgit 1.2.3-korg From e43c2856b2aec77c70a5c70d9ea0b87cf7d34eea Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Thu, 23 Jun 2016 11:10:28 +0300 Subject: minor update for the stats doc --- .../stl/trex_stl_lib/trex_stl_client.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'scripts') 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 6dec6fa7..c1833754 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 @@ -1284,18 +1284,20 @@ class STLClient(object): **total** and per port statistics contain dictionary with following format. + Most of the bytes counters (unless specified otherwise) are in L2 layer including the FCS. e.g. minimum packet size in 64 bytes + =============================== =============== key Meaning =============================== =============== - ibytes Number of input bytes + ibytes Number of input bytes ierrors Number of input errors - ipackets Number of input packets - obytes Number of output bytes + ipackets Number of input packets + obytes Number of output bytes oerrors Number of output errors opackets Number of output packets - rx_bps Receive bytes per second rate + rx_bps Receive bytes per second rate (L2 layer) rx_pps Receive packet per second rate - tx_bps Transmit bytes per second rate + tx_bps Transmit bytes per second rate (L2 layer) tx_pps Transmit packet per second rate =============================== =============== @@ -1327,8 +1329,8 @@ class STLClient(object): ================= =============== bw_per_core Estimated byte rate Trex can support per core. This is calculated by extrapolation of current rate and load on transmitting cores. cpu_util Estimate of the average utilization percentage of the transimitting cores - queue_full Total number of packets we could not transmit because NIC TX queue was full. This usually indicates that the rate we trying to transmit is too high for this port - rx_cpu_util Estimate of the utilization percentage of the core handling RX traffic + queue_full Total number of packets transmitted while the NIC TX queue was full. The packets will be transmitted, eventually, but will create high CPU%due to polling the queue. This usually indicates that the rate we trying to transmit is too high for this port. + rx_cpu_util Estimate of the utilization percentage of the core handling RX traffic. Too high value of this CPU utilization could cause drop of latency streams. rx_drop_bps Received bytes per second drop rate rx_bps Received bytes per second rate rx_pps Received packets per second rate @@ -1356,7 +1358,7 @@ class STLClient(object): ================= =============== key Meaning (see better explanation below the table) ================= =============== - dropped How many packets were dropped. + dropped How many packets were dropped (estimation) dup How many packets were duplicated. out_of_order How many packets we received out of order. seq_too_high How many events of packet with sequence number too high we saw. @@ -1384,10 +1386,10 @@ class STLClient(object): ================= =============== key Meaning ================= =============== - average Average latency over the stream lifetime (usec). Total average is computed each sampling period by following formula: = /2 + /2 + average Average latency over the stream lifetime (usec).Low pass filter is applied to the last window average.It is computed each sampling period by following formula: = /2 + /2 histogram Dictionary describing logarithmic distribution histogram of packet latencies. Keys in the dictionary represent range of latencies (in usec). Values are the total number of packets received in this latency range. For example, an entry {100:13} would mean that we saw 13 packets with latency in the range between 100 and 200 usec. jitter Jitter of latency samples, computed as described in :rfc:`3550#appendix-A.8` - last_max Maximum latency measured between last two data reads from server. + last_max Maximum latency measured between last two data reads from server (0.5 sec window). total_max Maximum latency measured over the stream lifetime (in usec). total_min Minimum latency measured over the stream lifetime (in usec). ================= =============== -- cgit 1.2.3-korg From f672c6c1ac980fa248298b679603da3645735787 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 22 Jun 2016 18:03:43 +0300 Subject: FLOW_STATS: removed line_util fields --- .../stl/trex_stl_lib/trex_stl_stats.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'scripts') 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 88a94865..3effa1f0 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 @@ -1047,12 +1047,7 @@ class CRxStats(CTRexStats): def __init__(self, ports): super(CRxStats, self).__init__() self.ports = ports - self.ports_speed = {} - def get_ports_speed(self): - for port in self.ports: - self.ports_speed[str(port)] = self.ports[port].get_speed_bps() - self.ports_speed['total'] = sum(self.ports_speed.values()) # calculates a diff between previous snapshot # and current one @@ -1170,8 +1165,8 @@ class CRxStats(CTRexStats): return # TX - self.get_ports_speed() for port in pg_current['tx_pkts'].keys(): + prev_tx_pps = pg_prev['tx_pps'].get(port) now_tx_pkts = pg_current['tx_pkts'].get(port) prev_tx_pkts = pg_prev['tx_pkts'].get(port) @@ -1180,19 +1175,20 @@ class CRxStats(CTRexStats): prev_tx_bps = pg_prev['tx_bps'].get(port) now_tx_bytes = pg_current['tx_bytes'].get(port) prev_tx_bytes = pg_prev['tx_bytes'].get(port) + pg_current['tx_bps'][port], pg_current['tx_bps_lpf'][port] = self.calc_bps(prev_tx_bps, now_tx_bytes, prev_tx_bytes, diff_sec) if pg_current['tx_bps'].get(port) != None and pg_current['tx_pps'].get(port) != None: pg_current['tx_bps_L1'][port] = calc_bps_L1(pg_current['tx_bps'][port], pg_current['tx_pps'][port]) pg_current['tx_bps_L1_lpf'][port] = calc_bps_L1(pg_current['tx_bps_lpf'][port], pg_current['tx_pps_lpf'][port]) - pg_current['tx_line_util'][port] = 100.0 * pg_current['tx_bps_L1'][port] / self.ports_speed[port] else: pg_current['tx_bps_L1'][port] = None pg_current['tx_bps_L1_lpf'][port] = None - pg_current['tx_line_util'][port] = None + # RX for port in pg_current['rx_pkts'].keys(): + prev_rx_pps = pg_prev['rx_pps'].get(port) now_rx_pkts = pg_current['rx_pkts'].get(port) prev_rx_pkts = pg_prev['rx_pkts'].get(port) @@ -1205,11 +1201,9 @@ class CRxStats(CTRexStats): if pg_current['rx_bps'].get(port) != None and pg_current['rx_pps'].get(port) != None: pg_current['rx_bps_L1'][port] = calc_bps_L1(pg_current['rx_bps'][port], pg_current['rx_pps'][port]) pg_current['rx_bps_L1_lpf'][port] = calc_bps_L1(pg_current['rx_bps_lpf'][port], pg_current['rx_pps_lpf'][port]) - pg_current['rx_line_util'][port] = 100.0 * pg_current['rx_bps_L1'][port] / self.ports_speed[port] else: pg_current['rx_bps_L1'][port] = None pg_current['rx_bps_L1_lpf'][port] = None - pg_current['rx_line_util'][port] = None def calc_pps (self, prev_bw, now, prev, diff_sec): @@ -1272,7 +1266,7 @@ class CRxStats(CTRexStats): stats[int(pg_id)][field][int(port)] = val if val != 'N/A' else StatNotAvailable(field) # BW values - for field in ['tx_pps', 'tx_bps', 'tx_bps_L1', 'rx_pps', 'rx_bps', 'rx_bps_L1', 'tx_line_util', 'rx_line_util']: + for field in ['tx_pps', 'tx_bps', 'tx_bps_L1', 'rx_pps', 'rx_bps', 'rx_bps_L1']: val = self.get([pg_id, field, 'total']) stats[int(pg_id)][field] = {'total': val if val != 'N/A' else StatNotAvailable(field)} for port in value[field].keys(): -- cgit 1.2.3-korg From a36aff0da82e5391049559017ab9afb38b084d81 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Thu, 23 Jun 2016 17:10:19 +0300 Subject: add 64byte FCS test --- .../regression/stateless_tests/stl_rx_test.py | 93 ++++++++++++++++++++++ 1 file changed, 93 insertions(+) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index d8a25564..0dbc7f31 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -354,8 +354,101 @@ class STLRX_Test(CStlGeneral_Test): print("===>Iteration {0} PASS {1}".format(i,j)); + def check_stats (self,stats,a,b,err): + if a != b: + tmp = 'ERROR field : {0}, read : {1} != expected : {2} '.format(err,a,b) + pprint.pprint(stats) + assert False,tmp + + def send_1_burst(self,from_port,is_latency,pkts): + + pid = from_port + base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) + + pad = (60 - len(base_pkt)) * 'x' + + stream_pkt = STLPktBuilder(pkt = base_pkt/pad) + + all_ports=list(CTRexScenario.stl_ports_map['map'].keys()); + + dpid = CTRexScenario.stl_ports_map['map'][pid] + + s_ports =[pid] + + try: + # reset all ports + self.c.reset(ports = all_ports) + + + for pid in s_ports: + if is_latency: + s1 = STLStream(name = 'rx', + packet = stream_pkt, + flow_stats = STLFlowLatencyStats(pg_id = 5 + pid), + mode = STLTXSingleBurst(total_pkts = pkts,pps = 1000)) + else: + s1 = STLStream(name = 'rx', + packet = stream_pkt, + mode = STLTXSingleBurst(total_pkts = pkts,pps = 1000)) + + + # add both streams to ports + self.c.add_streams(s1, ports = [pid]) + + self.c.clear_stats() + + self.c.start(ports = s_ports) + self.c.wait_on_traffic(ports = s_ports) + + stats = self.c.get_stats() + + ips = stats[dpid] + ops = stats[pid] + tps = stats['total'] + tbytes = pkts*64 + + self.check_stats (stats,ops["obytes"], tbytes,"ops[obytes]") + self.check_stats (stats,ops["opackets"], pkts,"ops[opackets]") + + self.check_stats (stats,ips["ibytes"], tbytes,"ips[ibytes]") + self.check_stats (stats,ips["ipackets"], pkts,"ips[ipackets]") + + self.check_stats (stats,tps['ibytes'], tbytes,"tps[ibytes]") + self.check_stats (stats,tps['obytes'], tbytes,"tps[obytes]") + self.check_stats (stats,tps['ipackets'], pkts,"tps[ipackets]") + self.check_stats (stats,tps['opackets'], pkts,"tps[opackets]") + + if is_latency: + ls=stats['flow_stats'][5+ pid] + self.check_stats (stats,ls['rx_pkts']['total'], pkts,"ls['rx_pkts']['total']") + self.check_stats (stats,ls['rx_pkts'][dpid], pkts,"ls['rx_pkts'][dpid]") + + self.check_stats (stats,ls['tx_pkts']['total'], pkts,"ls['tx_pkts']['total']") + self.check_stats (stats,ls['tx_pkts'][pid], pkts,"ls['tx_pkts'][pid]") + + self.check_stats (stats,ls['tx_bytes']['total'], tbytes,"ls['tx_bytes']['total']") + self.check_stats (stats,ls['tx_bytes'][pid], pkts+1,"ls['tx_bytes'][pid]") + + + return 0 + + except STLError as e: + assert False , '{0}'.format(e) + + + + def test_fcs_stream(self): + """ this test send 1 64 byte packet with latency and check that all counters are reported as 64 bytes""" + self.skip('Skip due to bug trex-213') + + all_ports=list(CTRexScenario.stl_ports_map['map'].keys()); + for port in all_ports: + for l in [True,False]: + print(" test port {0} latency : {1} ".format(port,l)) + self.send_1_burst(port,l,100) + # this test adds more and more latency streams and re-test with incremental def test_incremental_latency_streams (self): -- cgit 1.2.3-korg From 021f0d49b2607baa05d13f8badfc8f8e51edc43b Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Thu, 23 Jun 2016 17:23:45 +0300 Subject: enable fcs test --- scripts/automation/regression/stateless_tests/stl_rx_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index 0dbc7f31..2b6684fd 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -441,13 +441,13 @@ class STLRX_Test(CStlGeneral_Test): def test_fcs_stream(self): """ this test send 1 64 byte packet with latency and check that all counters are reported as 64 bytes""" - self.skip('Skip due to bug trex-213') + #self.skip('Skip due to bug trex-213') all_ports=list(CTRexScenario.stl_ports_map['map'].keys()); for port in all_ports: for l in [True,False]: print(" test port {0} latency : {1} ".format(port,l)) - self.send_1_burst(port,l,100) + self.send_1_burst(port,False,100) # this test adds more and more latency streams and re-test with incremental -- cgit 1.2.3-korg From ff5799ae624b6e74faf26e821558964a08f33577 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Fri, 24 Jun 2016 13:09:04 +0300 Subject: v2.04 --- VERSION | 3 ++- scripts/automation/regression/stateless_tests/stl_rx_test.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/VERSION b/VERSION index f85ec405..d73be704 100755 --- a/VERSION +++ b/VERSION @@ -1,4 +1,5 @@ -v2.03 +v2.04 + diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index 2b6684fd..23ebf081 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -74,7 +74,7 @@ class STLRX_Test(CStlGeneral_Test): pprint.pprint(err_latency) tmp = 'RX pkts ERROR - one of the error is on' print(tmp) - #assert False, tmp + assert False, tmp if latency['average']> max_average: pprint.pprint(latency_stats) @@ -314,7 +314,7 @@ class STLRX_Test(CStlGeneral_Test): # check low latency when you have stream of 9K stream def test_9k_stream(self): - self.skip('Skip due to bug trex-215') + #self.skip('Skip due to bug trex-215') if self.latency_9k_enable == False: print("SKIP") -- cgit 1.2.3-korg From efcae8fa373bf0ea6ceac051a698f621d5d65aa1 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Fri, 24 Jun 2016 18:54:36 +0300 Subject: run_regression: fix passing argument with spaces --- scripts/run_regression | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/run_regression b/scripts/run_regression index bdc2f185..407b7f84 100755 --- a/scripts/run_regression +++ b/scripts/run_regression @@ -1,6 +1,9 @@ #!/bin/bash -INPUT_ARGS=${@//--python[23]/} +ARGS=() +for var in "$@"; do + [[ "$var" != '--python2' && "$var" != '--python3' ]] && ARGS+=("$var") +done if [[ $@ =~ '--python2' || ! $@ =~ '--python3' ]]; then source find_python.sh --python2 @@ -8,7 +11,7 @@ if [[ $@ =~ '--python2' || ! $@ =~ '--python3' ]]; then # Python 2 echo Python2 test - $PYTHON trex_unit_test.py $INPUT_ARGS + $PYTHON trex_unit_test.py "${ARGS[@]}" if [ $? -eq 0 ]; then printf "\n$PYTHON test succeeded\n\n" else @@ -24,7 +27,7 @@ if [[ $@ =~ '--python3' ]]; then # Python 3 echo Python3 test - $PYTHON trex_unit_test.py $INPUT_ARGS + $PYTHON trex_unit_test.py "${ARGS[@]}" if [ $? -eq 0 ]; then printf "\n$PYTHON test succeeded\n\n" else -- cgit 1.2.3-korg From 582e6dddb5693d5fa7576c19b0ef7c1c0723ff59 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Sun, 26 Jun 2016 14:22:04 +0300 Subject: stf api: don't fail if no histogram in result --- .../stf/trex_stf_lib/trex_client.py | 31 +++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'scripts') 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 a13fe31f..f044f623 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 @@ -1480,21 +1480,22 @@ class CTRexResult(object): @staticmethod def __get_filtered_max_latency (src_dict, filtered_latency_amount = 0.001): result = {} - for port, data in src_dict.items(): - if not port.startswith('port-'): - continue - max_port = 'max-%s' % port[5:] - res = data['hist'] - if not len(res['histogram']): - result[max_port] = 0 - continue - result[max_port] = 5 # if sum below will not get to filtered amount, use this value - sum_high = 0.0 - for elem in reversed(res['histogram']): - sum_high += elem['val'] - if sum_high >= filtered_latency_amount * res['cnt']: - result[max_port] = elem['key'] + int('5' + repr(elem['key'])[2:]) - break + if src_dict: + for port, data in src_dict.items(): + if not port.startswith('port-'): + continue + max_port = 'max-%s' % port[5:] + res = data['hist'] + if not len(res['histogram']): + result[max_port] = 0 + continue + result[max_port] = 5 # if sum below will not get to filtered amount, use this value + sum_high = 0.0 + for elem in reversed(res['histogram']): + sum_high += elem['val'] + if sum_high >= filtered_latency_amount * res['cnt']: + result[max_port] = elem['key'] + int('5' + repr(elem['key'])[2:]) + break return result -- cgit 1.2.3-korg From 8635fce7827e0ee75a2fe032e8dce139a746e1c4 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Sun, 26 Jun 2016 17:41:29 +0300 Subject: Fx trex-216 - Crash on VM setups when running latency flows --- .../regression/stateless_tests/stl_rx_test.py | 17 ++-- src/bp_sim.cpp | 1 + src/bp_sim.h | 12 +-- src/flow_stat.cpp | 4 - src/flow_stat.h | 6 +- src/main_dpdk.cpp | 3 +- src/stateless/rx/trex_stateless_rx_core.cpp | 91 ++++++++++++---------- src/stateless/rx/trex_stateless_rx_core.h | 17 ++-- 8 files changed, 83 insertions(+), 68 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index 23ebf081..36f17170 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -9,10 +9,6 @@ class STLRX_Test(CStlGeneral_Test): """Tests for RX feature""" def setUp(self): - #if CTRexScenario.setup_name in ('trex08', 'trex09'): - # self.skip('This test makes trex08 and trex09 sick. Fix those ASAP.') - if self.is_virt_nics: - self.skip('Skip this for virtual NICs for now') per_driver_params = {"rte_vmxnet3_pmd": [1, 50, 1,False], "rte_ixgbe_pmd": [30, 5000, 1,True,200,400], "rte_i40e_pmd": [80, 5000, 1,True,100,250], "rte_igb_pmd": [80, 500, 1,False], "rte_em_pmd": [1, 50, 1,False], "rte_virtio_pmd": [1, 50, 1,False]} @@ -182,6 +178,9 @@ class STLRX_Test(CStlGeneral_Test): def test_multiple_streams(self): + if self.is_virt_nics: + self.skip('Skip this for virtual NICs') + num_latency_streams = 128 num_flow_stat_streams = 127 total_pkts = int(self.total_pkts / (num_latency_streams + num_flow_stat_streams)) @@ -313,8 +312,8 @@ class STLRX_Test(CStlGeneral_Test): # check low latency when you have stream of 9K stream def test_9k_stream(self): - - #self.skip('Skip due to bug trex-215') + if self.is_virt_nics: + self.skip('Skip this for virtual NICs') if self.latency_9k_enable == False: print("SKIP") @@ -441,7 +440,9 @@ class STLRX_Test(CStlGeneral_Test): def test_fcs_stream(self): """ this test send 1 64 byte packet with latency and check that all counters are reported as 64 bytes""" - #self.skip('Skip due to bug trex-213') + + if self.is_virt_nics: + self.skip('Skip this for virtual NICs') all_ports=list(CTRexScenario.stl_ports_map['map'].keys()); for port in all_ports: @@ -452,6 +453,8 @@ class STLRX_Test(CStlGeneral_Test): # this test adds more and more latency streams and re-test with incremental def test_incremental_latency_streams (self): + if self.is_virt_nics: + self.skip('Skip this for virtual NICs') total_pkts = self.total_pkts percent = 0.5 diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp index f9e96b6b..077a3d45 100755 --- a/src/bp_sim.cpp +++ b/src/bp_sim.cpp @@ -5006,6 +5006,7 @@ int CErfIFStl::send_sl_node(CGenNodeStateless *node_sl) { fsp_head->seq = 0x12345678; fsp_head->hw_id = hw_id - MAX_FLOW_STATS; fsp_head->magic = FLOW_STAT_PAYLOAD_MAGIC; + fsp_head->flow_seq = FLOW_STAT_PAYLOAD_INITIAL_FLOW_SEQ; fsp_head->time_stamp = 0x8899aabbccddeeff; fill_raw_packet(mi, (CGenNode *)node_sl, dir); rte_pktmbuf_free(mi); diff --git a/src/bp_sim.h b/src/bp_sim.h index 56e37272..e396a710 100755 --- a/src/bp_sim.h +++ b/src/bp_sim.h @@ -259,18 +259,20 @@ class CPreviewMode ; class CLatencyPktData { public: - CLatencyPktData() {m_magic = 0xaa;} + CLatencyPktData() {m_flow_seq = FLOW_STAT_PAYLOAD_INITIAL_FLOW_SEQ;} inline uint32_t get_seq_num() {return m_seq_num;} inline void inc_seq_num() {m_seq_num++;} - inline uint32_t get_magic() {return m_magic;} + inline uint32_t get_flow_seq() {return m_flow_seq;} void reset() { m_seq_num = UINT32_MAX - 1; // catch wrap around issues early - m_magic++; + m_flow_seq++; + if (m_flow_seq == FLOW_STAT_PAYLOAD_INITIAL_FLOW_SEQ) + m_flow_seq++; } private: - uint32_t m_seq_num; // seq num to put in packet for payload rules - uint16_t m_magic; // magic to put in packet for payload rules + uint32_t m_seq_num; // seq num to put in packet for payload rules. Increased every packet. + uint16_t m_flow_seq; // Seq num of flow. Changed when we start new flow on this id. }; /* represent the virtual interface diff --git a/src/flow_stat.cpp b/src/flow_stat.cpp index cb7a1bf9..5503434f 100644 --- a/src/flow_stat.cpp +++ b/src/flow_stat.cpp @@ -784,10 +784,6 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) { m_parser->set_ip_id(IP_ID_RESERVE_BASE + hw_id); stream->m_rx_check.m_hw_id = hw_id; } else { - struct flow_stat_payload_header *fsp_head = (struct flow_stat_payload_header *) - (stream->m_pkt.binary + stream->m_pkt.len - sizeof(struct flow_stat_payload_header)); - fsp_head->hw_id = hw_id; - fsp_head->magic = FLOW_STAT_PAYLOAD_MAGIC; m_parser->set_ip_id(FLOW_STAT_PAYLOAD_IP_ID); // for payload rules, we use the range right after ip id rules stream->m_rx_check.m_hw_id = hw_id + MAX_FLOW_STATS; diff --git a/src/flow_stat.h b/src/flow_stat.h index a2137198..25d16173 100644 --- a/src/flow_stat.h +++ b/src/flow_stat.h @@ -35,7 +35,8 @@ // Do not change this value. In i350 cards, we filter according to first byte of IP ID // In other places, we identify packets by if (ip_id > IP_ID_RESERVE_BASE) #define IP_ID_RESERVE_BASE 0xff00 -#define FLOW_STAT_PAYLOAD_MAGIC 0xABCD +#define FLOW_STAT_PAYLOAD_MAGIC 0xAB +#define FLOW_STAT_PAYLOAD_INITIAL_FLOW_SEQ 0x01 extern const uint16_t FLOW_STAT_PAYLOAD_IP_ID; typedef std::map flow_stat_map_t; @@ -44,7 +45,8 @@ typedef std::map::iterator flow_stat_map_it_t; class CRxCoreStateless; struct flow_stat_payload_header { - uint16_t magic; + uint8_t magic; + uint8_t flow_seq; uint16_t hw_id; uint32_t seq; uint64_t time_stamp; diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 906aa2b7..d132df51 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -2055,7 +2055,8 @@ int CCoreEthIFStateless::send_node_flow_stat(rte_mbuf *m, CGenNodeStateless * no mi = node_sl->alloc_flow_stat_mbuf(m, fsp_head, is_const); fsp_head->seq = lp_stats->m_lat_data[hw_id_payload].get_seq_num(); fsp_head->hw_id = hw_id_payload; - fsp_head->magic = lp_stats->m_lat_data[hw_id_payload].get_magic(); + fsp_head->flow_seq = lp_stats->m_lat_data[hw_id_payload].get_flow_seq(); + fsp_head->magic = FLOW_STAT_PAYLOAD_MAGIC; lp_stats->m_lat_data[hw_id_payload].inc_seq_num(); #ifdef ERR_CNTRS_TEST diff --git a/src/stateless/rx/trex_stateless_rx_core.cpp b/src/stateless/rx/trex_stateless_rx_core.cpp index e5831129..0bd601b6 100644 --- a/src/stateless/rx/trex_stateless_rx_core.cpp +++ b/src/stateless/rx/trex_stateless_rx_core.cpp @@ -29,15 +29,15 @@ void CRFC2544Info::create() { m_latency.Create(); - m_exp_magic = 0; - m_prev_magic = 0; + m_exp_flow_seq = 0; + m_prev_flow_seq = 0; reset(); } // after calling stop, packets still arriving will be considered error void CRFC2544Info::stop() { - m_prev_magic = m_exp_magic; - m_exp_magic = FLOW_STAT_PAYLOAD_MAGIC_NONE; + m_prev_flow_seq = m_exp_flow_seq; + m_exp_flow_seq = FLOW_STAT_PAYLOAD_INITIAL_FLOW_SEQ; } void CRFC2544Info::reset() { @@ -195,82 +195,95 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t * if (parser.get_ip_id(ip_id) == 0) { if (is_flow_stat_id(ip_id)) { uint16_t hw_id; - bool good_packet = true; + if (is_flow_stat_payload_id(ip_id)) { + bool good_packet = true; uint8_t *p = rte_pktmbuf_mtod(m, uint8_t*); struct flow_stat_payload_header *fsp_head = (struct flow_stat_payload_header *) (p + m->pkt_len - sizeof(struct flow_stat_payload_header)); hw_id = fsp_head->hw_id; - CRFC2544Info &curr_rfc2544 = m_rfc2544[hw_id]; - if (unlikely(fsp_head->magic != curr_rfc2544.get_exp_magic())) { - // bad magic. - // Might be the first packet of a new flow, packet from an old flow or just garbage. - if (fsp_head->magic == curr_rfc2544.get_prev_magic()) { - // packet from previous flow using this hw_id that arrived late - good_packet = false; - } else { - if (curr_rfc2544.no_magic()) { - // first packet we see from this flow - good_packet = true; - curr_rfc2544.set_exp_magic(fsp_head->magic); - } else { - // garbage packet + CRFC2544Info *curr_rfc2544; + + if (unlikely(fsp_head->magic != FLOW_STAT_PAYLOAD_MAGIC) || hw_id >= MAX_FLOW_STATS_PAYLOAD) { + good_packet = false; + } else { + curr_rfc2544 = &m_rfc2544[hw_id]; + + if (fsp_head->flow_seq != curr_rfc2544->get_exp_flow_seq()) { + // bad flow seq num + // Might be the first packet of a new flow, packet from an old flow, or garbage. + + if (fsp_head->flow_seq == curr_rfc2544->get_prev_flow_seq()) { + // packet from previous flow using this hw_id that arrived late good_packet = false; + } else { + if (curr_rfc2544->no_flow_seq()) { + // first packet we see from this flow + good_packet = true; + curr_rfc2544->set_exp_flow_seq(fsp_head->flow_seq); + } else { + // garbage packet + good_packet = false; + } } } } if (good_packet) { uint32_t pkt_seq = fsp_head->seq; - uint32_t exp_seq = curr_rfc2544.get_seq(); + uint32_t exp_seq = curr_rfc2544->get_seq(); if (unlikely(pkt_seq != exp_seq)) { if (pkt_seq < exp_seq) { if (exp_seq - pkt_seq > 100000) { // packet loss while we had wrap around - curr_rfc2544.inc_seq_err(pkt_seq - exp_seq); - curr_rfc2544.inc_seq_err_too_big(); - curr_rfc2544.set_seq(pkt_seq + 1); + curr_rfc2544->inc_seq_err(pkt_seq - exp_seq); + curr_rfc2544->inc_seq_err_too_big(); + curr_rfc2544->set_seq(pkt_seq + 1); } else { if (pkt_seq == (exp_seq - 1)) { - curr_rfc2544.inc_dup(); + curr_rfc2544->inc_dup(); } else { - curr_rfc2544.inc_ooo(); + curr_rfc2544->inc_ooo(); // We thought it was lost, but it was just out of order - curr_rfc2544.dec_seq_err(); + curr_rfc2544->dec_seq_err(); } - curr_rfc2544.inc_seq_err_too_low(); + curr_rfc2544->inc_seq_err_too_low(); } } else { if (unlikely (pkt_seq - exp_seq > 100000)) { // packet reorder while we had wrap around if (pkt_seq == (exp_seq - 1)) { - curr_rfc2544.inc_dup(); + curr_rfc2544->inc_dup(); } else { - curr_rfc2544.inc_ooo(); + curr_rfc2544->inc_ooo(); // We thought it was lost, but it was just out of order - curr_rfc2544.dec_seq_err(); + curr_rfc2544->dec_seq_err(); } - curr_rfc2544.inc_seq_err_too_low(); + curr_rfc2544->inc_seq_err_too_low(); } else { - // seq > curr_rfc2544.seq. Assuming lost packets - curr_rfc2544.inc_seq_err(pkt_seq - exp_seq); - curr_rfc2544.inc_seq_err_too_big(); - curr_rfc2544.set_seq(pkt_seq + 1); + // seq > curr_rfc2544->seq. Assuming lost packets + curr_rfc2544->inc_seq_err(pkt_seq - exp_seq); + curr_rfc2544->inc_seq_err_too_big(); + curr_rfc2544->set_seq(pkt_seq + 1); } } } else { - curr_rfc2544.set_seq(pkt_seq + 1); + curr_rfc2544->set_seq(pkt_seq + 1); } lp->m_port.m_rx_pg_stat_payload[hw_id].add_pkts(1); lp->m_port.m_rx_pg_stat_payload[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC uint64_t d = (os_get_hr_tick_64() - fsp_head->time_stamp ); dsec_t ctime = ptime_convert_hr_dsec(d); - curr_rfc2544.add_sample(ctime); + curr_rfc2544->add_sample(ctime); } } else { hw_id = get_hw_id(ip_id); - lp->m_port.m_rx_pg_stat[hw_id].add_pkts(1); - lp->m_port.m_rx_pg_stat[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC + if (hw_id >= MAX_FLOW_STATS) { + // increase some error counter + } else { + lp->m_port.m_rx_pg_stat[hw_id].add_pkts(1); + lp->m_port.m_rx_pg_stat[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC + } } } } diff --git a/src/stateless/rx/trex_stateless_rx_core.h b/src/stateless/rx/trex_stateless_rx_core.h index 140fedf4..209dc29f 100644 --- a/src/stateless/rx/trex_stateless_rx_core.h +++ b/src/stateless/rx/trex_stateless_rx_core.h @@ -77,14 +77,11 @@ class CRFC2544Info { inline void inc_seq_err_too_low() {m_seq_err_events_too_low++;} inline void inc_dup() {m_dup++;} inline void inc_ooo() {m_ooo++;} - inline uint16_t get_exp_magic() {return m_exp_magic;} - inline void set_exp_magic(uint16_t magic) {m_exp_magic = magic;} - inline uint16_t get_prev_magic() {return m_prev_magic;} - inline bool no_magic() {return (m_exp_magic == FLOW_STAT_PAYLOAD_MAGIC_NONE) ? true : false;} + inline uint16_t get_exp_flow_seq() {return m_exp_flow_seq;} + inline void set_exp_flow_seq(uint16_t flow_seq) {m_exp_flow_seq = flow_seq;} + inline uint16_t get_prev_flow_seq() {return m_prev_flow_seq;} + inline bool no_flow_seq() {return (m_exp_flow_seq == FLOW_STAT_PAYLOAD_INITIAL_FLOW_SEQ) ? true : false;} private: - enum payload_e { - FLOW_STAT_PAYLOAD_MAGIC_NONE = 0 - }; uint32_t m_seq; // expected next seq num CTimeHistogram m_latency; // latency info CJitter m_jitter; @@ -93,9 +90,9 @@ class CRFC2544Info { uint64_t m_seq_err_events_too_low; // How many packet seq num lower than expected events we had uint64_t m_ooo; // Packets we got with seq num lower than expected (We guess they are out of order) uint64_t m_dup; // Packets we got with same seq num - uint16_t m_exp_magic; // magic number we should see in latency header - // magic number previously used with this id. We use this to catch packets arriving late from old flow - uint16_t m_prev_magic; + uint16_t m_exp_flow_seq; // flow sequence number we should see in latency header + // flow sequence number previously used with this id. We use this to catch packets arriving late from an old flow + uint16_t m_prev_flow_seq; }; class CRxCoreStateless { -- cgit 1.2.3-korg From 3c693c4d3a57c8567c5e2a8327deb70a08be9a14 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Mon, 27 Jun 2016 13:43:01 +0300 Subject: fix trex-218 & trex-219 --- .../regression/stateless_tests/stl_rx_test.py | 4 +-- scripts/stl/burst_simple.py | 36 ++++++++++++++++++++++ src/dpdk22/drivers/net/i40e/i40e_ethdev.c | 14 ++++++--- src/main_dpdk.cpp | 2 +- 4 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 scripts/stl/burst_simple.py (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index 36f17170..16029908 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -428,7 +428,7 @@ class STLRX_Test(CStlGeneral_Test): self.check_stats (stats,ls['tx_pkts'][pid], pkts,"ls['tx_pkts'][pid]") self.check_stats (stats,ls['tx_bytes']['total'], tbytes,"ls['tx_bytes']['total']") - self.check_stats (stats,ls['tx_bytes'][pid], pkts+1,"ls['tx_bytes'][pid]") + self.check_stats (stats,ls['tx_bytes'][pid], tbytes,"ls['tx_bytes'][pid]") return 0 @@ -448,7 +448,7 @@ class STLRX_Test(CStlGeneral_Test): for port in all_ports: for l in [True,False]: print(" test port {0} latency : {1} ".format(port,l)) - self.send_1_burst(port,False,100) + self.send_1_burst(port,l,100) # this test adds more and more latency streams and re-test with incremental diff --git a/scripts/stl/burst_simple.py b/scripts/stl/burst_simple.py new file mode 100644 index 00000000..87d7a5a1 --- /dev/null +++ b/scripts/stl/burst_simple.py @@ -0,0 +1,36 @@ +from trex_stl_lib.api import * + +class STLS1(object): + + def __init__ (self): + self.fsize =64; # the size of the packet + + + def create_stream (self): + + # Create base packet and pad it to size + size = self.fsize - 4; # HW will add 4 bytes ethernet FCS + base_pkt = Ether(dst="00:00:00:00:00:01")/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) + pad = max(0, size - len(base_pkt)) * 'x' + + + return STLProfile( [ STLStream( isg = 1.0, # start in delay in usec + packet = STLPktBuilder(pkt = base_pkt/pad), + mode = STLTXSingleBurst( pps = 1000), + ) + + ]).get_streams() + + + def get_streams (self, direction = 0, **kwargs): + # create 1 stream + return self.create_stream() + + +# dynamic load - used for trex console or simulator +def register(): + return STLS1() + + + + diff --git a/src/dpdk22/drivers/net/i40e/i40e_ethdev.c b/src/dpdk22/drivers/net/i40e/i40e_ethdev.c index 5646eb53..623c071c 100644 --- a/src/dpdk22/drivers/net/i40e/i40e_ethdev.c +++ b/src/dpdk22/drivers/net/i40e/i40e_ethdev.c @@ -2145,10 +2145,16 @@ i40e_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) pf->main_vsi->eth_stats.rx_multicast + pf->main_vsi->eth_stats.rx_broadcast - pf->main_vsi->eth_stats.rx_discards; - stats->opackets = pf->main_vsi->eth_stats.tx_unicast + - pf->main_vsi->eth_stats.tx_multicast + - pf->main_vsi->eth_stats.tx_broadcast; - stats->ibytes = ns->eth.rx_bytes; + + stats->opackets = ns->eth.tx_unicast +ns->eth.tx_multicast +ns->eth.tx_broadcast; + /*TREX PATCH move to global transmit and not pf->vsi and we have two high and low priorty + pf->main_vsi->eth_stats.tx_unicast + + pf->main_vsi->eth_stats.tx_multicast + + pf->main_vsi->eth_stats.tx_broadcast; + */ + + stats->ibytes = pf->main_vsi->eth_stats.rx_bytes; + stats->obytes = ns->eth.tx_bytes; stats->oerrors = ns->eth.tx_errors + pf->main_vsi->eth_stats.tx_errors; diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index d132df51..2ef06157 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -5505,7 +5505,7 @@ void CTRexExtendedDriverBase40G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSta stats->ipackets = stats1.ipackets; - stats->ibytes = stats1.ibytes + (stats1.ipackets<<2); + stats->ibytes = stats1.ibytes ; stats->opackets = stats1.opackets; stats->obytes = stats1.obytes + (stats1.opackets<<2); -- cgit 1.2.3-korg From 7bf6055dc8f8ae50ef0f6b612f68eaa77e1b4014 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Mon, 27 Jun 2016 14:25:19 +0300 Subject: Make all latency tests work on Virtual machines --- .../regression/stateless_tests/stl_rx_test.py | 20 ++++++++++---------- src/main_dpdk.cpp | 15 +++++---------- 2 files changed, 15 insertions(+), 20 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index 16029908..3127a196 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -28,10 +28,11 @@ class STLRX_Test(CStlGeneral_Test): self.skip('port {0} does not support RX'.format(self.rx_port)) self.cap = cap - self.rate_percent = per_driver_params[port_info['driver']][0] - self.total_pkts = per_driver_params[port_info['driver']][1] - if len(per_driver_params[port_info['driver']]) > 2: - self.rate_lat = per_driver_params[port_info['driver']][2] + drv_name = port_info['driver'] + self.rate_percent = per_driver_params[drv_name][0] + self.total_pkts = per_driver_params[drv_name][1] + if len(per_driver_params[drv_name]) > 2: + self.rate_lat = per_driver_params[drv_name][2] else: self.rate_lat = self.rate_percent self.drops_expected = False @@ -41,8 +42,6 @@ class STLRX_Test(CStlGeneral_Test): self.large_pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*1000)) self.pkt_9k = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*9000)) - - drv_name=port_info['driver'] self.latency_9k_enable=per_driver_params[drv_name][3] if self.latency_9k_enable: self.latency_9k_max_average = per_driver_params[drv_name][4] @@ -120,7 +119,7 @@ class STLRX_Test(CStlGeneral_Test): tmp = 'TX pkts mismatch - got: {0}, expected: {1}'.format(tx_pkts, total_pkts) assert False, tmp - if tx_bytes != (total_pkts * (pkt_len + 4)): # + 4 for ethernet CRC + if tx_bytes != (total_pkts * pkt_len): pprint.pprint(flow_stats) tmp = 'TX bytes mismatch - got: {0}, expected: {1}'.format(tx_bytes, (total_pkts * pkt_len)) assert False, tmp @@ -132,7 +131,7 @@ class STLRX_Test(CStlGeneral_Test): if "rx_bytes" in self.cap: rx_bytes = flow_stats['rx_bytes'].get(self.rx_port, 0) - if rx_bytes != (total_pkts * (pkt_len + 4)) and not self.drops_expected: # +4 for ethernet CRC + if rx_bytes != (total_pkts * pkt_len) and not self.drops_expected: pprint.pprint(flow_stats) tmp = 'RX bytes mismatch - got: {0}, expected: {1}'.format(rx_bytes, (total_pkts * pkt_len)) assert False, tmp @@ -148,12 +147,13 @@ class STLRX_Test(CStlGeneral_Test): stats = self.c.get_stats() for exp in exp_list: - self.__verify_flow(exp['pg_id'], exp['total_pkts'], exp['pkt_len'], stats) + # Expecting to get pkt_len + 4 because of ethernet FCS + self.__verify_flow(exp['pg_id'], exp['total_pkts'], exp['pkt_len'] + 4, stats) # one stream on TX --> RX def test_one_stream(self): - total_pkts = self.total_pkts * 10 + total_pkts = self.total_pkts try: s1 = STLStream(name = 'rx', diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 2ef06157..45e46810 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -4341,20 +4341,15 @@ bool CCoreEthIF::process_rx_pkt(pkt_dir_t dir, } bool send=false; + // e1000 on ESXI hands us the packet with the ethernet FCS + if (parser.getPktSize() < m->pkt_len) { + rte_pktmbuf_trim(m, m->pkt_len - parser.getPktSize()); + } + if ( get_is_stateless() ) { // In stateless RX, we only care about flow stat packets if ((parser.getIpId() & 0xff00) == IP_ID_RESERVE_BASE) { send = true; - if (parser.getIpId() == FLOW_STAT_PAYLOAD_IP_ID) { - // e1000 on ESXI appends 4 bytes to the packet. - // This is a best effort hack to get our latency info which we put at the end of the packet - uint8_t *p = rte_pktmbuf_mtod(m, uint8_t*); - struct flow_stat_payload_header *fsp_head = (struct flow_stat_payload_header *) - (p + m->pkt_len - sizeof(struct flow_stat_payload_header)); - if (fsp_head->magic != FLOW_STAT_PAYLOAD_MAGIC) { - rte_pktmbuf_trim(m, 4); - } - } } } else { CLatencyPktMode *c_l_pkt_mode = g_trex.m_mg.c_l_pkt_mode; -- cgit 1.2.3-korg From b958f538f4f2255d3c7d39932c4e7ae6d81d67e5 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Mon, 27 Jun 2016 15:01:42 +0300 Subject: example for more flows support >10M --- scripts/cap2/http_very_long.yaml | 21 ++++++++++++++++ scripts/cfg/x710_advance_more_flows.yaml | 41 ++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 scripts/cap2/http_very_long.yaml create mode 100644 scripts/cfg/x710_advance_more_flows.yaml (limited to 'scripts') diff --git a/scripts/cap2/http_very_long.yaml b/scripts/cap2/http_very_long.yaml new file mode 100644 index 00000000..0e7bb46f --- /dev/null +++ b/scripts/cap2/http_very_long.yaml @@ -0,0 +1,21 @@ +- duration : 0.1 + generator : + distribution : "seq" + clients_start : "16.0.0.1" + clients_end : "16.0.0.255" + servers_start : "48.0.0.1" + servers_end : "48.0.255.255" + clients_per_gb : 201 + min_clients : 101 + dual_port_mask : "1.0.0.0" + tcp_aging : 0 + udp_aging : 0 + mac : [0x0,0x0,0x0,0x1,0x0,0x00] + #cap_ipg : true + cap_info : + - name: avl/delay_10_http_browsing_0.pcap + cps : 2.776 + ipg : 4000000 + rtt : 4000000 + w : 1 + diff --git a/scripts/cfg/x710_advance_more_flows.yaml b/scripts/cfg/x710_advance_more_flows.yaml new file mode 100644 index 00000000..074420ef --- /dev/null +++ b/scripts/cfg/x710_advance_more_flows.yaml @@ -0,0 +1,41 @@ +- port_limit : 4 + version : 2 + interfaces : ["02:00.0","02:00.1","84:00.0","84:00.1"] # list of the interfaces to bind run ./dpdk_nic_bind.py --status + c : 4 + #port_bandwidth_gb : 40 + port_bandwidth_gb : 10 + + port_info : # set eh mac addr + + - dest_mac : [0x68, 0x05, 0xca, 0x32, 0x0c, 0x39] + src_mac : [0x68, 0x05, 0xca, 0x32, 0x15, 0xb0] + + + - dest_mac : [0x68, 0x05, 0xca, 0x32, 0x0c, 0x38] + src_mac : [0x68, 0x05, 0xca, 0x32, 0x15, 0xb1] + + - dest_mac : [0x68, 0x05, 0xca, 0x32, 0x15, 0xb1] + src_mac : [0x68, 0x05, 0xca, 0x32, 0x0c, 0x38] + + - dest_mac : [0x68, 0x05, 0xca, 0x32, 0x15, 0xb0] + src_mac : [0x68, 0x05, 0xca, 0x32, 0x0c, 0x39] + + + platform : + master_thread_id : 0 + latency_thread_id : 8 + dual_if : + - socket : 0 + threads : [1, 2, 3, 4, 5, 6, 7] + - socket : 1 + threads : [9, 10, 11, 12, 13, 14, 15] + + + + memory : + dp_flows : 10048576 + + + + + -- cgit 1.2.3-korg From 4b5fc9567db2f56b8eecfc0b86d69dbf995e8708 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Mon, 27 Jun 2016 16:50:24 +0300 Subject: latency field engine tests --- .../regression/stateless_tests/stl_rx_test.py | 91 +++++++++++++--------- 1 file changed, 56 insertions(+), 35 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index 3127a196..238ff53d 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -38,9 +38,26 @@ class STLRX_Test(CStlGeneral_Test): self.drops_expected = False self.c.reset(ports = [self.tx_port, self.rx_port]) + vm = STLScVmRaw( [ STLVmFlowVar ( "ip_src", min_value="10.0.0.1", + max_value="10.0.0.255", size=4, step=1,op="inc"), + STLVmWrFlowVar (fv_name="ip_src", pkt_offset= "IP.src" ), # write ip to packet IP.src + STLVmFixIpv4(offset = "IP") # fix checksum + ] + # Latency is bound to one core. We test that this option is not causing trouble + ,split_by_field = "ip_src" + ,cache_size =255 # Cache is ignored by latency flows. Need to test it is not crashing. + ); + self.pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('Your_paylaod_comes_here')) self.large_pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*1000)) self.pkt_9k = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*9000)) + self.vm_pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1") + / UDP(dport=12,sport=1025)/('Your_paylaod_comes_here') + , vm = vm) + self.vm_large_pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*1000) + , vm = vm) + self.vm_9k_pkt = STLPktBuilder(pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025)/('a'*9000) + ,vm = vm) self.latency_9k_enable=per_driver_params[drv_name][3] if self.latency_9k_enable: @@ -147,8 +164,7 @@ class STLRX_Test(CStlGeneral_Test): stats = self.c.get_stats() for exp in exp_list: - # Expecting to get pkt_len + 4 because of ethernet FCS - self.__verify_flow(exp['pg_id'], exp['total_pkts'], exp['pkt_len'] + 4, stats) + self.__verify_flow(exp['pg_id'], exp['total_pkts'], exp['pkt_len'], stats) # one stream on TX --> RX @@ -168,7 +184,7 @@ class STLRX_Test(CStlGeneral_Test): print("\ninjecting {0} packets on port {1}\n".format(total_pkts, self.tx_port)) - exp = {'pg_id': 5, 'total_pkts': total_pkts, 'pkt_len': self.pkt.get_pkt_len()} + exp = {'pg_id': 5, 'total_pkts': total_pkts, 'pkt_len': s1.get_pkt_len()} self.__rx_iteration( [exp] ) @@ -199,7 +215,7 @@ class STLRX_Test(CStlGeneral_Test): flow_stats = STLFlowLatencyStats(pg_id = pg_id), mode = STLTXSingleBurst(total_pkts = total_pkts+pg_id, percentage = percent))) - exp.append({'pg_id': pg_id, 'total_pkts': total_pkts+pg_id, 'pkt_len': self.pkt.get_pkt_len()}) + exp.append({'pg_id': pg_id, 'total_pkts': total_pkts+pg_id, 'pkt_len': streams[-1].get_pkt_len()}) for pg_id in range(num_latency_streams + 1, num_latency_streams + num_flow_stat_streams): @@ -208,7 +224,7 @@ class STLRX_Test(CStlGeneral_Test): flow_stats = STLFlowStats(pg_id = pg_id), mode = STLTXSingleBurst(total_pkts = total_pkts+pg_id, percentage = percent))) - exp.append({'pg_id': pg_id, 'total_pkts': total_pkts+pg_id, 'pkt_len': self.pkt.get_pkt_len()}) + exp.append({'pg_id': pg_id, 'total_pkts': total_pkts+pg_id, 'pkt_len': streams[-1].get_pkt_len()}) # add both streams to ports self.c.add_streams(streams, ports = [self.tx_port]) @@ -223,36 +239,41 @@ class STLRX_Test(CStlGeneral_Test): total_pkts = self.total_pkts try: - s1 = STLStream(name = 'rx', - packet = self.pkt, - flow_stats = STLFlowStats(pg_id = 5), - mode = STLTXSingleBurst(total_pkts = total_pkts, - percentage = self.rate_percent - )) - - s_lat = STLStream(name = 'rx', - packet = self.pkt, - flow_stats = STLFlowLatencyStats(pg_id = 5), - mode = STLTXSingleBurst(total_pkts = total_pkts, - percentage = self.rate_lat - )) - - print("\ninjecting {0} packets on port {1}\n".format(total_pkts, self.tx_port)) - - exp = {'pg_id': 5, 'total_pkts': total_pkts, 'pkt_len': self.pkt.get_pkt_len()} - exp_lat = {'pg_id': 5, 'total_pkts': total_pkts, 'pkt_len': self.pkt.get_pkt_len()} - - self.c.add_streams([s1], ports = [self.tx_port]) - for i in range(0, 10): - print("starting iteration {0}".format(i)) - self.__rx_iteration( [exp] ) - - self.c.remove_all_streams(ports = [self.tx_port]) - self.c.add_streams([s_lat], ports = [self.tx_port]) - for i in range(0, 10): - print("starting iteration {0} latency".format(i)) - self.__rx_iteration( [exp_lat] ) + streams_data = [ + {'name': 'Flow stat. No latency', 'pkt': self.pkt, 'lat': False}, + {'name': 'Latency, no field engine', 'pkt': self.pkt, 'lat': True}, + {'name': 'Latency, short packet with field engine', 'pkt': self.vm_pkt, 'lat': True}, + {'name': 'Latency, large packet field engine', 'pkt': self.vm_large_pkt, 'lat': True} + ] + if self.latency_9k_enable: + streams_data.append({'name': 'Latency, 9k packet with field engine', 'pkt': self.vm_9k_pkt, 'lat': True}) + streams = [] + for data in streams_data: + if data['lat']: + flow_stats = STLFlowLatencyStats(pg_id = 5) + else: + flow_stats = STLFlowStats(pg_id = 5) + + s = STLStream(name = data['name'], + packet = data['pkt'], + flow_stats = flow_stats, + mode = STLTXSingleBurst(total_pkts = total_pkts, + percentage = self.rate_percent + )) + streams.append(s) + + print("\ninjecting {0} packets on port {1}".format(total_pkts, self.tx_port)) + exp = {'pg_id': 5, 'total_pkts': total_pkts} + + for stream in streams: + self.c.add_streams([stream], ports = [self.tx_port]) + print("Stream: {0}".format(stream.name)) + exp['pkt_len'] = stream.get_pkt_len() + for i in range(0, 10): + print("Iteration {0}".format(i)) + self.__rx_iteration( [exp] ) + self.c.remove_all_streams(ports = [self.tx_port]) except STLError as e: @@ -487,7 +508,7 @@ class STLRX_Test(CStlGeneral_Test): print("port {0} : {1} streams at {2}% of line rate\n".format(self.tx_port, i, total_percent)) - exp.append({'pg_id': i, 'total_pkts': total_pkts, 'pkt_len': my_pkt.get_pkt_len()}) + exp.append({'pg_id': i, 'total_pkts': total_pkts, 'pkt_len': s1.get_pkt_len()}) self.__rx_iteration( exp ) -- cgit 1.2.3-korg From 7e4ec3d452e0a3d8517147b2bec45d4abdbc041d Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Tue, 28 Jun 2016 13:21:26 +0300 Subject: increase MTU on regression routers to 9500, to make jumbo packets tests work --- scripts/automation/regression/CPlatform.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/CPlatform.py b/scripts/automation/regression/CPlatform.py index de1c22ce..256d7411 100755 --- a/scripts/automation/regression/CPlatform.py +++ b/scripts/automation/regression/CPlatform.py @@ -20,7 +20,7 @@ class CPlatform(object): self.tftp_cfg = None self.config_history = { 'basic_if_config' : False, 'tftp_server_config' : False } - def configure_basic_interfaces(self, mtu = 4000): + def configure_basic_interfaces(self, mtu = 9050): cache = CCommandCache() for dual_if in self.if_mngr.get_dual_if_list(): @@ -46,7 +46,7 @@ class CPlatform(object): - def configure_basic_filtered_interfaces(self, intf_list, mtu = 4000): + def configure_basic_filtered_interfaces(self, intf_list, mtu = 9050): cache = CCommandCache() for intf in intf_list: -- cgit 1.2.3-korg From 0e717f4637cd70eebd0613a1090f54367c1fd379 Mon Sep 17 00:00:00 2001 From: Ryan Whitworth Date: Tue, 28 Jun 2016 11:39:46 -0400 Subject: fix spelling of Ubuntu --- scripts/ko/src/readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/ko/src/readme.txt b/scripts/ko/src/readme.txt index a8d028c9..0e9cddad 100755 --- a/scripts/ko/src/readme.txt +++ b/scripts/ko/src/readme.txt @@ -5,7 +5,7 @@ Fedora $sudo yum install kernel-headers or -Ubunto +Ubuntu $sudo apt-get install linux-headers-$(uname -r) 2. from this dir do this -- cgit 1.2.3-korg From 7772d0893579d0627c10515aeb6d9c9c8204316d Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Wed, 29 Jun 2016 16:18:13 +0300 Subject: flow stat/latency error counters code + documentation --- .../stl/examples/stl_flow_latency_stats.py | 25 +++++++---- .../stl/examples/stl_flow_stats.py | 45 +++++++++++-------- .../stl/trex_stl_lib/trex_stl_client.py | 51 ++++++++++++++++------ .../stl/trex_stl_lib/trex_stl_stats.py | 18 ++++++++ src/flow_stat.cpp | 26 ++++++++--- src/internal_api/trex_platform_api.h | 3 ++ src/main_dpdk.cpp | 4 ++ src/stateless/rx/trex_stateless_rx_core.cpp | 12 +++-- src/stateless/rx/trex_stateless_rx_core.h | 26 ++++++++++- 9 files changed, 161 insertions(+), 49 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py b/scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py index ac0e212b..d8a99479 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_flow_latency_stats.py @@ -1,10 +1,12 @@ +# Example showing how to define stream for latency measurement, and how to parse the latency information + import stl_path from trex_stl_lib.api import * import time import pprint -def rx_example (tx_port, rx_port, burst_size, bw): +def rx_example (tx_port, rx_port, burst_size, pps): print("\nGoing to inject {0} packets on port {1} - checking RX stats on port {2}\n".format(burst_size, tx_port, rx_port)) @@ -19,7 +21,7 @@ def rx_example (tx_port, rx_port, burst_size, bw): packet = pkt, flow_stats = STLFlowLatencyStats(pg_id = 5), mode = STLTXSingleBurst(total_pkts = total_pkts, - percentage = bw)) + pps = pps)) # connect to server c.connect() @@ -32,7 +34,7 @@ def rx_example (tx_port, rx_port, burst_size, bw): print("\nInjecting {0} packets on port {1}\n".format(total_pkts, tx_port)) - rc = rx_iteration(c, tx_port, rx_port, total_pkts, pkt.get_pkt_len(), bw) + rc = rx_iteration(c, tx_port, rx_port, total_pkts, pkt.get_pkt_len()) if not rc: passed = False @@ -44,12 +46,12 @@ def rx_example (tx_port, rx_port, burst_size, bw): c.disconnect() if passed: - print("\nTest has passed :-)\n") + print("\nTest passed :-)\n") else: - print("\nTest has failed :-(\n") + print("\nTest failed :-(\n") # RX one iteration -def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw): +def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len): c.clear_stats() @@ -58,7 +60,8 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw): stats = c.get_stats() flow_stats = stats['flow_stats'].get(5) - lat_stats = stats['latency'].get(5) + global_lat_stats = stats['latency'] + lat_stats = global_lat_stats.get(5) if not flow_stats: print("no flow stats available") return False @@ -74,6 +77,8 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw): dup = lat_stats['err_cntrs']['dup'] sth = lat_stats['err_cntrs']['seq_too_high'] stl = lat_stats['err_cntrs']['seq_too_low'] + old_flow = global_lat_stats['global']['old_flow'] + bad_hdr = global_lat_stats['global']['bad_hdr'] lat = lat_stats['latency'] jitter = lat['jitter'] avg = lat['average'] @@ -89,6 +94,10 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw): return False print('Error counters: dropped:{0}, ooo:{1} dup:{2} seq too high:{3} seq too low:{4}'.format(drops, ooo, dup, sth, stl)) + if old_flow: + print ('Packets arriving too late after flow stopped: {0}'.format(old_flow)) + if bad_hdr: + print ('Latency packets with corrupted info: {0}'.format(bad_hdr)) print('Latency info:') print(" Maximum latency(usec): {0}".format(tot_max)) print(" Minimum latency(usec): {0}".format(tot_min)) @@ -131,5 +140,5 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw): return True # run the tests -rx_example(tx_port = 1, rx_port = 0, burst_size = 500000, bw = 50) +rx_example(tx_port = 0, rx_port = 1, burst_size = 1000, pps = 1000) diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py b/scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py index ed4902fa..3c630ece 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_flow_stats.py @@ -1,3 +1,5 @@ +# Example showing how to define stream for getting per flow statistics, and how to parse the received statistics + import stl_path from trex_stl_lib.api import * @@ -27,18 +29,14 @@ def rx_example (tx_port, rx_port, burst_size, bw): # prepare our ports c.reset(ports = [tx_port, rx_port]) - # add both streams to ports + # add stream to port c.add_streams([s1], ports = [tx_port]) - print("\ninjecting {0} packets on port {1}\n".format(total_pkts, tx_port)) - - for i in range(0, 10): - print("\nStarting iteration: {0}:".format(i)) - rc = rx_iteration(c, tx_port, rx_port, total_pkts, pkt.get_pkt_len(), bw) - if not rc: - passed = False - break + print("\ngoing to inject {0} packets on port {1}\n".format(total_pkts, tx_port)) + rc = rx_iteration(c, tx_port, rx_port, total_pkts, s1.get_pkt_len()) + if not rc: + passed = False except STLError as e: passed = False @@ -48,19 +46,21 @@ def rx_example (tx_port, rx_port, burst_size, bw): c.disconnect() if passed: - print("\nTest has passed :-)\n") + print("\nTest passed :-)\n") else: - print("\nTest has failed :-(\n") + print("\nTest failed :-(\n") # RX one iteration -def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw): - +def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len): + ret = True + c.clear_stats() c.start(ports = [tx_port]) c.wait_on_traffic(ports = [tx_port]) - flow_stats = c.get_stats()['flow_stats'].get(5) + global_flow_stats = c.get_stats()['flow_stats'] + flow_stats = global_flow_stats.get(5) if not flow_stats: print("no flow stats available") return False @@ -78,26 +78,33 @@ def rx_iteration (c, tx_port, rx_port, total_pkts, pkt_len, bw): if tx_pkts != total_pkts: print("TX pkts mismatch - got: {0}, expected: {1}".format(tx_pkts, total_pkts)) pprint.pprint(flow_stats) - return False + ret = False else: print("TX pkts match - {0}".format(tx_pkts)) if tx_bytes != (total_pkts * pkt_len): print("TX bytes mismatch - got: {0}, expected: {1}".format(tx_bytes, (total_pkts * pkt_len))) pprint.pprint(flow_stats) - return False + ret = False else: print("TX bytes match - {0}".format(tx_bytes)) if rx_pkts != total_pkts: print("RX pkts mismatch - got: {0}, expected: {1}".format(rx_pkts, total_pkts)) pprint.pprint(flow_stats) - return False + ret = False else: print("RX pkts match - {0}".format(rx_pkts)) - return True + + for field in ['rx_err', 'tx_err']: + for port in global_flow_stats['global'][field].keys(): + if global_flow_stats['global'][field][port] != 0: + print ("\n{0} on port {1}: {2} - You should consider increasing rx_delay_ms value in wait_on_traffic" + .format(field, port, global_flow_stats['global'][field][port])) + + return ret # run the tests -rx_example(tx_port = 1, rx_port = 2, burst_size = 500000, bw = 50) +rx_example(tx_port = 0, rx_port = 1, burst_size = 500, bw = 50) 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 c1833754..acedf24f 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 @@ -1284,7 +1284,7 @@ class STLClient(object): **total** and per port statistics contain dictionary with following format. - Most of the bytes counters (unless specified otherwise) are in L2 layer including the FCS. e.g. minimum packet size in 64 bytes + Most of the bytes counters (unless specified otherwise) are in L2 layer, including the Ethernet FCS. e.g. minimum packet size is 64 bytes =============================== =============== key Meaning @@ -1303,21 +1303,34 @@ class STLClient(object): .. _flow_stats: - **flow_stats** contains dictionaries per packet group id (pg id). Each one with the following structure. + **flow_stats** contains :ref:`global dictionary `, and dictionaries per packet group id (pg id). See structures below. + + **per pg_id flow stat** dictionaries have following structure: ================= =============== key Meaning ================= =============== - rx_bps Receivd bytes per second rate - rx_bps_l1 Receivd bytes per second rate, including layer one + rx_bps Received bytes per second rate + rx_bps_l1 Received bytes per second rate, including layer one rx_bytes Total number of received bytes rx_pkts Total number of received packets rx_pps Received packets per second - tx_bps Transmitted bytes per second rate - tx_bps_l1 Transmitted bytes per second rate, including layer one + tx_bps Transmit bytes per second rate + tx_bps_l1 Transmit bytes per second rate, including layer one tx_bytes Total number of sent bytes tx_pkts Total number of sent packets - tx_pps Transmit packets per second + tx_pps Transmit packets per second rate + ================= =============== + + .. _flow_stats_global: + + **global flow stats** dictionary has the following structure: + + ================= =============== + key Meaning + ================= =============== + rx_err Number of flow statistics packets received that we could not associate to any pg_id. This can happen if latency on the used setup is large. See :ref:`wait_on_traffic ` rx_delay_ms parameter for details. + tx_err Number of flow statistics packets transmitted that we could not associate to any pg_id. This is never expected. If you see this different than 0, please report. ================= =============== .. _global: @@ -1340,7 +1353,9 @@ class STLClient(object): .. _latency: - **latency** contains dictionary per packet group id (pg id). Each one with the following structure. + **latency** contains :ref:`global dictionary `, and dictionaries per packet group id (pg id). Each one with the following structure. + + **per pg_id latency stat** dictionaries have following structure: =========================== =============== key Meaning @@ -1394,7 +1409,16 @@ class STLClient(object): total_min Minimum latency measured over the stream lifetime (in usec). ================= =============== + .. _lat_stats_global: + **global latency stats** dictionary has the following structure: + + ================= =============== + key Meaning + ================= =============== + old_flow Number of latency statistics packets received that we could not associate to any pg_id. This can happen if latency on the used setup is large. See :ref:`wait_on_traffic ` rx_delay_ms parameter for details. + bad_hdr Number of latency packets received with bad latency data. This can happen becuase of garbage packets in the network, or if the DUT causes packet corruption. + ================= =============== :raises: None @@ -2274,6 +2298,8 @@ class STLClient(object): @__api_check(True) def wait_on_traffic (self, ports = None, timeout = 60, rx_delay_ms = 10): """ + .. _wait_on_traffic: + Block until traffic on specified port(s) has ended :parameters: @@ -2284,12 +2310,11 @@ class STLClient(object): timeout in seconds rx_delay_ms : int - time to wait until RX filters are removed - this value should reflect the time it takes - packets which were transmitted to arrive + Time to wait (in milliseconds) after last packet was sent, until RX filters used for + measuring flow statistics and latency are removed. + This value should reflect the time it takes packets which were transmitted to arrive to the destination. - after this time the RX filters will be removed - + After this time, RX filters will be removed, and packets arriving for per flow statistics feature and latency flows will be counted as errors. :raises: + :exc:`STLTimeoutError` - in case timeout has expired 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 3effa1f0..f74c8fa5 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 @@ -1010,6 +1010,13 @@ class CLatencyStats(CTRexStats): snapshot = {} output = {} + output['global'] = {} + for field in ['bad_hdr', 'old_flow']: + if 'global' in snapshot and field in snapshot['global']: + output['global'][field] = snapshot['global'][field] + else: + output['global'][field] = 0 + # we care only about the current active keys pg_ids = list(filter(is_intable, snapshot.keys())) @@ -1107,6 +1114,14 @@ class CRxStats(CTRexStats): # copy timestamp field output['ts'] = current['ts'] + # global (not per pg_id) error counters + output['global'] = {} + for field in ['rx_err', 'tx_err']: + output['global'][field] = {} + if 'global' in current and field in current['global']: + for port in current['global'][field]: + output['global'][field][int(port)] = current['global'][field][port] + # we care only about the current active keys pg_ids = list(filter(is_intable, current.keys())) @@ -1254,6 +1269,9 @@ class CRxStats(CTRexStats): for pg_id, value in self.latest_stats.items(): # skip non ints if not is_intable(pg_id): + # 'global' stats are in the same level of the pg_ids. We do want them to go to the user + if pg_id == 'global': + stats[pg_id] = value continue # bare counters stats[int(pg_id)] = {} diff --git a/src/flow_stat.cpp b/src/flow_stat.cpp index 5503434f..8c2f2566 100644 --- a/src/flow_stat.cpp +++ b/src/flow_stat.cpp @@ -796,6 +796,9 @@ int CFlowStatRuleMgr::start_stream(TrexStream * stream) { if (m_num_started_streams == 0) { send_start_stop_msg_to_rx(true); // First transmitting stream. Rx core should start reading packets; + //also good time to zero global counters + memset(m_rx_cant_count_err, 0, sizeof(m_rx_cant_count_err)); + memset(m_tx_cant_count_err, 0, sizeof(m_tx_cant_count_err)); // wait to make sure that message is acknowledged. RX core might be in deep sleep mode, and we want to // start transmitting packets only after it is working, otherwise, packets will get lost. @@ -910,7 +913,7 @@ int CFlowStatRuleMgr::stop_stream(TrexStream * stream) { m_num_started_streams--; assert (m_num_started_streams >= 0); if (m_num_started_streams == 0) { - send_start_stop_msg_to_rx(false); // No more transmittig streams. Rx core shoulde get into idle loop. + send_start_stop_msg_to_rx(false); // No more transmittig streams. Rx core should get into idle loop. } return 0; } @@ -947,6 +950,7 @@ bool CFlowStatRuleMgr::dump_json(std::string & s_json, std::string & l_json, boo tx_per_flow_t tx_stats[MAX_FLOW_STATS]; tx_per_flow_t tx_stats_payload[MAX_FLOW_STATS_PAYLOAD]; rfc2544_info_t rfc2544_info[MAX_FLOW_STATS_PAYLOAD]; + CRxCoreErrCntrs rx_err_cntrs; Json::FastWriter writer; Json::Value s_root; Json::Value l_root; @@ -973,6 +977,7 @@ bool CFlowStatRuleMgr::dump_json(std::string & s_json, std::string & l_json, boo } m_api->get_rfc2544_info(rfc2544_info, 0, m_max_hw_id_payload, false); + m_api->get_rx_err_cntrs(&rx_err_cntrs); // read hw counters, and update for (uint8_t port = 0; port < m_num_ports; port++) { @@ -1046,10 +1051,21 @@ bool CFlowStatRuleMgr::dump_json(std::string & s_json, std::string & l_json, boo // general per port data for (uint8_t port = 0; port < m_num_ports; port++) { std::string str_port = static_cast( &(std::ostringstream() << int(port) ) )->str(); - if (m_rx_cant_count_err[port] != 0) - s_data_section["port_data"][str_port]["rx_err"] = m_rx_cant_count_err[port]; - if (m_tx_cant_count_err[port] != 0) - s_data_section["port_data"][str_port]["tx_err"] = m_tx_cant_count_err[port]; + if ((m_rx_cant_count_err[port] != 0) || baseline) + s_data_section["global"]["rx_err"][str_port] = m_rx_cant_count_err[port]; + if ((m_tx_cant_count_err[port] != 0) || baseline) + s_data_section["global"]["tx_err"][str_port] = m_tx_cant_count_err[port]; + } + + // payload rules rx errors + uint64_t tmp_cnt; + tmp_cnt = rx_err_cntrs.get_bad_header(); + if (tmp_cnt || baseline) { + l_data_section["global"]["bad_hdr"] = Json::Value::UInt64(tmp_cnt); + } + tmp_cnt = rx_err_cntrs.get_old_flow(); + if (tmp_cnt || baseline) { + l_data_section["global"]["old_flow"] = Json::Value::UInt64(tmp_cnt); } flow_stat_user_id_map_it_t it; diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h index a52f9e60..4f19ec9d 100644 --- a/src/internal_api/trex_platform_api.h +++ b/src/internal_api/trex_platform_api.h @@ -148,6 +148,7 @@ public: virtual int get_flow_stats(uint8_t port_id, void *stats, void *tx_stats, int min, int max, bool reset , TrexPlatformApi::driver_stat_cap_e type) const = 0; virtual int get_rfc2544_info(void *rfc2544_info, int min, int max, bool reset) const = 0; + virtual int get_rx_err_cntrs(void *rx_err_cntrs) const = 0; virtual int reset_hw_flow_stats(uint8_t port_id) const = 0; virtual void get_port_num(uint8_t &port_num) const = 0; virtual int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const = 0; @@ -180,6 +181,7 @@ public: int get_flow_stats(uint8_t port_id, void *stats, void *tx_stats, int min, int max, bool reset , TrexPlatformApi::driver_stat_cap_e type) const; int get_rfc2544_info(void *rfc2544_info, int min, int max, bool reset) const; + int get_rx_err_cntrs(void *rx_err_cntrs) const; int reset_hw_flow_stats(uint8_t port_id) const; void get_port_num(uint8_t &port_num) const; int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const; @@ -237,6 +239,7 @@ public: int get_flow_stats(uint8_t port_id, void *stats, void *tx_stats, int min, int max, bool reset , TrexPlatformApi::driver_stat_cap_e type) const {return 0;}; virtual int get_rfc2544_info(void *rfc2544_info, int min, int max, bool reset) const {return 0;}; + virtual int get_rx_err_cntrs(void *rx_err_cntrs) const {return 0;}; virtual int reset_hw_flow_stats(uint8_t port_id) const {return 0;}; virtual void get_port_num(uint8_t &port_num) const {port_num = 2;}; virtual int add_rx_flow_stat_rule(uint8_t port_id, uint8_t type, uint16_t proto, uint16_t id) const {return 0;} diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 45e46810..941612b1 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -5760,6 +5760,10 @@ int TrexDpdkPlatformApi::get_rfc2544_info(void *rfc2544_info, int min, int max, return g_trex.m_rx_sl.get_rfc2544_info((rfc2544_info_t *)rfc2544_info, min, max, reset); } +int TrexDpdkPlatformApi::get_rx_err_cntrs(void *rx_err_cntrs) const { + return g_trex.m_rx_sl.get_rx_err_cntrs((CRxCoreErrCntrs *)rx_err_cntrs); +} + int TrexDpdkPlatformApi::reset_hw_flow_stats(uint8_t port_id) const { return g_trex.m_ports[port_id].reset_hw_flow_stats(); } diff --git a/src/stateless/rx/trex_stateless_rx_core.cpp b/src/stateless/rx/trex_stateless_rx_core.cpp index 0bd601b6..853fc868 100644 --- a/src/stateless/rx/trex_stateless_rx_core.cpp +++ b/src/stateless/rx/trex_stateless_rx_core.cpp @@ -206,6 +206,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t * if (unlikely(fsp_head->magic != FLOW_STAT_PAYLOAD_MAGIC) || hw_id >= MAX_FLOW_STATS_PAYLOAD) { good_packet = false; + m_err_cntrs.m_bad_header++; } else { curr_rfc2544 = &m_rfc2544[hw_id]; @@ -216,6 +217,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t * if (fsp_head->flow_seq == curr_rfc2544->get_prev_flow_seq()) { // packet from previous flow using this hw_id that arrived late good_packet = false; + m_err_cntrs.m_old_flow++; } else { if (curr_rfc2544->no_flow_seq()) { // first packet we see from this flow @@ -224,6 +226,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t * } else { // garbage packet good_packet = false; + m_err_cntrs.m_bad_header++; } } } @@ -278,9 +281,7 @@ void CRxCoreStateless::handle_rx_pkt(CLatencyManagerPerPortStl *lp, rte_mbuf_t * } } else { hw_id = get_hw_id(ip_id); - if (hw_id >= MAX_FLOW_STATS) { - // increase some error counter - } else { + if (hw_id < MAX_FLOW_STATS) { lp->m_port.m_rx_pg_stat[hw_id].add_pkts(1); lp->m_port.m_rx_pg_stat[hw_id].add_bytes(m->pkt_len + 4); // +4 for ethernet CRC } @@ -444,6 +445,11 @@ int CRxCoreStateless::get_rfc2544_info(rfc2544_info_t *rfc2544_info, int min, in return 0; } +int CRxCoreStateless::get_rx_err_cntrs(CRxCoreErrCntrs *rx_err) { + *rx_err = m_err_cntrs; + return 0; +} + void CRxCoreStateless::set_working_msg_ack(bool val) { sanb_smp_memory_barrier(); m_ack_start_work_msg = val; diff --git a/src/stateless/rx/trex_stateless_rx_core.h b/src/stateless/rx/trex_stateless_rx_core.h index 209dc29f..fc66704e 100644 --- a/src/stateless/rx/trex_stateless_rx_core.h +++ b/src/stateless/rx/trex_stateless_rx_core.h @@ -95,6 +95,25 @@ class CRFC2544Info { uint16_t m_prev_flow_seq; }; +class CRxCoreErrCntrs { + friend CRxCoreStateless; + + public: + uint64_t get_bad_header() {return m_bad_header;} + uint64_t get_old_flow() {return m_old_flow;} + CRxCoreErrCntrs() { + reset(); + } + void reset() { + m_bad_header = 0; + m_old_flow = 0; + } + + private: + uint64_t m_bad_header; + uint64_t m_old_flow; +}; + class CRxCoreStateless { enum state_e { STATE_IDLE, @@ -109,7 +128,11 @@ class CRxCoreStateless { int get_rx_stats(uint8_t port_id, rx_per_flow_t *rx_stats, int min, int max, bool reset , TrexPlatformApi::driver_stat_cap_e type); int get_rfc2544_info(rfc2544_info_t *rfc2544_info, int min, int max, bool reset); - void work() {m_state = STATE_WORKING;} + int get_rx_err_cntrs(CRxCoreErrCntrs *rx_err); + void work() { + m_state = STATE_WORKING; + m_err_cntrs.reset(); // When starting to work, reset global counters + } void idle() {m_state = STATE_IDLE;} void quit() {m_state = STATE_QUIT;} bool is_working() const {return (m_ack_start_work_msg == true);} @@ -146,6 +169,7 @@ class CRxCoreStateless { CCpuUtlCp m_cpu_cp_u; // Used for acking "work" (go out of idle) messages from cp volatile bool m_ack_start_work_msg __rte_cache_aligned; + CRxCoreErrCntrs m_err_cntrs; CRFC2544Info m_rfc2544[MAX_FLOW_STATS_PAYLOAD]; }; #endif -- cgit 1.2.3-korg From 0f118a01d28ba17344aa3eb5fe8655412d9f9fa9 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Thu, 30 Jun 2016 17:47:43 +0300 Subject: Latency test fixes for regression --- scripts/automation/regression/stateless_tests/stl_rx_test.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index 238ff53d..f83529b0 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -9,7 +9,7 @@ class STLRX_Test(CStlGeneral_Test): """Tests for RX feature""" def setUp(self): - per_driver_params = {"rte_vmxnet3_pmd": [1, 50, 1,False], "rte_ixgbe_pmd": [30, 5000, 1,True,200,400], "rte_i40e_pmd": [80, 5000, 1,True,100,250], + per_driver_params = {"rte_vmxnet3_pmd": [1, 50, 1,False], "rte_ixgbe_pmd": [30, 5000, 1,True,300,400], "rte_i40e_pmd": [80, 5000, 1,True,100,250], "rte_igb_pmd": [80, 500, 1,False], "rte_em_pmd": [1, 50, 1,False], "rte_virtio_pmd": [1, 50, 1,False]} CStlGeneral_Test.setUp(self) @@ -35,6 +35,7 @@ class STLRX_Test(CStlGeneral_Test): self.rate_lat = per_driver_params[drv_name][2] else: self.rate_lat = self.rate_percent + self.lat_pps = 5000 self.drops_expected = False self.c.reset(ports = [self.tx_port, self.rx_port]) @@ -252,15 +253,16 @@ class STLRX_Test(CStlGeneral_Test): for data in streams_data: if data['lat']: flow_stats = STLFlowLatencyStats(pg_id = 5) + mode = STLTXSingleBurst(total_pkts = total_pkts, percentage = self.rate_percent) else: flow_stats = STLFlowStats(pg_id = 5) + mode = STLTXSingleBurst(total_pkts = total_pkts, pps = self.lat_pps) s = STLStream(name = data['name'], packet = data['pkt'], flow_stats = flow_stats, - mode = STLTXSingleBurst(total_pkts = total_pkts, - percentage = self.rate_percent - )) + mode = mode + ) streams.append(s) print("\ninjecting {0} packets on port {1}".format(total_pkts, self.tx_port)) -- cgit 1.2.3-korg From a7bda0a07919e23bfe8b8c97dd856d9db3a75d70 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Fri, 1 Jul 2016 10:09:03 +0300 Subject: reducing rate of latency packets inregression --- scripts/automation/regression/stateless_tests/stl_rx_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_rx_test.py b/scripts/automation/regression/stateless_tests/stl_rx_test.py index f83529b0..090261ff 100644 --- a/scripts/automation/regression/stateless_tests/stl_rx_test.py +++ b/scripts/automation/regression/stateless_tests/stl_rx_test.py @@ -9,7 +9,7 @@ class STLRX_Test(CStlGeneral_Test): """Tests for RX feature""" def setUp(self): - per_driver_params = {"rte_vmxnet3_pmd": [1, 50, 1,False], "rte_ixgbe_pmd": [30, 5000, 1,True,300,400], "rte_i40e_pmd": [80, 5000, 1,True,100,250], + per_driver_params = {"rte_vmxnet3_pmd": [1, 50, 1,False], "rte_ixgbe_pmd": [30, 1000, 1,True,300,400], "rte_i40e_pmd": [80, 1000, 1,True,100,250], "rte_igb_pmd": [80, 500, 1,False], "rte_em_pmd": [1, 50, 1,False], "rte_virtio_pmd": [1, 50, 1,False]} CStlGeneral_Test.setUp(self) @@ -35,7 +35,7 @@ class STLRX_Test(CStlGeneral_Test): self.rate_lat = per_driver_params[drv_name][2] else: self.rate_lat = self.rate_percent - self.lat_pps = 5000 + self.lat_pps = 1000 self.drops_expected = False self.c.reset(ports = [self.tx_port, self.rx_port]) -- cgit 1.2.3-korg From ed196079a710ddc30552ecbc0329db7a75a4ca60 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 30 Jun 2016 16:29:39 +0300 Subject: added a template for client config file --- scripts/cap2/client_cfg_template.yaml | 56 +++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 scripts/cap2/client_cfg_template.yaml (limited to 'scripts') diff --git a/scripts/cap2/client_cfg_template.yaml b/scripts/cap2/client_cfg_template.yaml new file mode 100644 index 00000000..8257b981 --- /dev/null +++ b/scripts/cap2/client_cfg_template.yaml @@ -0,0 +1,56 @@ +# +# Client configuration example file +# The file must contain the following fields +# +# 'vlan' - is the entire configuration under VLAN +# if so, each client group must include vlan +# configuration +# +# 'groups' - each client group must contain a range of IP +# and initiator and responder maps +# 'count' represents the number of MAC devices +# on the group. +# +# initiator and responder can contain 'vlan', 'src_mac', 'dst_mac' +# + +vlan: true + +groups: + +- ip_start : 5.0.0.1 + ip_end : 5.0.0.4 + initiator : + vlan : 100 + dst_mac : "00:00:00:01:00:00" + responder : + vlan : 200 + dst_mac : "00:00:00:01:00:00" + + count : 2 + +- ip_start : 5.0.0.5 + ip_end : 5.0.0.12 + initiator : + vlan : 101 + dst_mac : "01:00:00:00:01:01" + + responder: + vlan : 201 + dst_mac : "01:00:00:00:02:01" + + count : 4 + +- ip_start : 5.0.0.13 + ip_end : 5.0.0.30 + + initiator : + vlan : 103 + dst_mac : "02:00:00:00:01:01" + responder : + + vlan : 203 + dst_mac : "02:00:00:00:02:01" + + count : 8 + -- cgit 1.2.3-korg From 4960031835b92ae34c7b2e1f1512fe2b34c0d8a6 Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 3 Jul 2016 14:02:29 +0300 Subject: moved client config template file to ./cfg --- scripts/cap2/client_cfg_template.yaml | 56 ----------------------------------- scripts/cfg/client_cfg_template.yaml | 56 +++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 56 deletions(-) delete mode 100644 scripts/cap2/client_cfg_template.yaml create mode 100644 scripts/cfg/client_cfg_template.yaml (limited to 'scripts') diff --git a/scripts/cap2/client_cfg_template.yaml b/scripts/cap2/client_cfg_template.yaml deleted file mode 100644 index 8257b981..00000000 --- a/scripts/cap2/client_cfg_template.yaml +++ /dev/null @@ -1,56 +0,0 @@ -# -# Client configuration example file -# The file must contain the following fields -# -# 'vlan' - is the entire configuration under VLAN -# if so, each client group must include vlan -# configuration -# -# 'groups' - each client group must contain a range of IP -# and initiator and responder maps -# 'count' represents the number of MAC devices -# on the group. -# -# initiator and responder can contain 'vlan', 'src_mac', 'dst_mac' -# - -vlan: true - -groups: - -- ip_start : 5.0.0.1 - ip_end : 5.0.0.4 - initiator : - vlan : 100 - dst_mac : "00:00:00:01:00:00" - responder : - vlan : 200 - dst_mac : "00:00:00:01:00:00" - - count : 2 - -- ip_start : 5.0.0.5 - ip_end : 5.0.0.12 - initiator : - vlan : 101 - dst_mac : "01:00:00:00:01:01" - - responder: - vlan : 201 - dst_mac : "01:00:00:00:02:01" - - count : 4 - -- ip_start : 5.0.0.13 - ip_end : 5.0.0.30 - - initiator : - vlan : 103 - dst_mac : "02:00:00:00:01:01" - responder : - - vlan : 203 - dst_mac : "02:00:00:00:02:01" - - count : 8 - diff --git a/scripts/cfg/client_cfg_template.yaml b/scripts/cfg/client_cfg_template.yaml new file mode 100644 index 00000000..8257b981 --- /dev/null +++ b/scripts/cfg/client_cfg_template.yaml @@ -0,0 +1,56 @@ +# +# Client configuration example file +# The file must contain the following fields +# +# 'vlan' - is the entire configuration under VLAN +# if so, each client group must include vlan +# configuration +# +# 'groups' - each client group must contain a range of IP +# and initiator and responder maps +# 'count' represents the number of MAC devices +# on the group. +# +# initiator and responder can contain 'vlan', 'src_mac', 'dst_mac' +# + +vlan: true + +groups: + +- ip_start : 5.0.0.1 + ip_end : 5.0.0.4 + initiator : + vlan : 100 + dst_mac : "00:00:00:01:00:00" + responder : + vlan : 200 + dst_mac : "00:00:00:01:00:00" + + count : 2 + +- ip_start : 5.0.0.5 + ip_end : 5.0.0.12 + initiator : + vlan : 101 + dst_mac : "01:00:00:00:01:01" + + responder: + vlan : 201 + dst_mac : "01:00:00:00:02:01" + + count : 4 + +- ip_start : 5.0.0.13 + ip_end : 5.0.0.30 + + initiator : + vlan : 103 + dst_mac : "02:00:00:00:01:01" + responder : + + vlan : 203 + dst_mac : "02:00:00:00:02:01" + + count : 8 + -- cgit 1.2.3-korg From a76479bc269cad96475f9c71381b4f826d47709f Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Mon, 4 Jul 2016 12:55:36 +0300 Subject: trex_client: sample each second stateful result object: add method to get avg value at steady state regression: add -t flag to run specific test by name (without file and class) update trex07 benchmarks --- .../regression/setups/trex07/benchmark.yaml | 370 ++++++++++++--------- .../regression/stateful_tests/trex_general_test.py | 71 ++-- .../regression/stateful_tests/trex_rx_test.py | 2 +- scripts/automation/regression/trex.py | 1 + scripts/automation/regression/trex_unit_test.py | 43 ++- .../stf/trex_stf_lib/trex_client.py | 98 ++++-- scripts/master_daemon.py | 2 +- 7 files changed, 362 insertions(+), 225 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/setups/trex07/benchmark.yaml b/scripts/automation/regression/setups/trex07/benchmark.yaml index 4778de91..38e25162 100644 --- a/scripts/automation/regression/setups/trex07/benchmark.yaml +++ b/scripts/automation/regression/setups/trex07/benchmark.yaml @@ -2,169 +2,243 @@ #### TRex benchmark configuration file #### ############################################################### -test_nbar_simple : - multiplier : 7.5 - cores : 2 - exp_gbps : 3.5 - cpu_to_core_ratio : 20800000 - cpu2core_custom_dev: YES - cpu2core_dev : 0.07 - exp_max_latency : 1000 - - 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 : - multiplier : 13 - cores : 3 - rx_sample_rate : 128 - exp_gbps : 6 - cpu_to_core_ratio : 37270000 - exp_bw : 13 - exp_latency : 1 - -test_nat_simple : &test_nat_simple - stat_route_dict : +#### 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: &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 - multiplier : 12000 - cores : 1 - cpu_to_core_ratio : 37270000 - exp_bw : 1 - exp_latency : 1 - allow_timeout_dev : YES - -test_nat_simple_mode1 : *test_nat_simple -test_nat_simple_mode2 : *test_nat_simple - -test_nat_learning : - 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 - multiplier : 12000 - cores : 1 - nat_opened : 40000 - cpu_to_core_ratio : 270 - exp_bw : 8 - exp_latency : 1 - allow_timeout_dev : YES - -test_routing_imix_64 : - multiplier : 430 - cores : 1 - cpu_to_core_ratio : 280 - exp_latency : 1 - -test_routing_imix : - multiplier : 10 - cores : 1 - cpu_to_core_ratio : 1800 - exp_latency : 1 - -test_static_routing_imix : - 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 - multiplier : 8 - cores : 1 - cpu_to_core_ratio : 1800 - exp_latency : 1 -test_static_routing_imix_asymmetric: - 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 - multiplier : 8 - cores : 1 - cpu_to_core_ratio : 1800 - exp_latency : 1 - -test_ipv6_simple : - multiplier : 9 - cores : 2 - cpu_to_core_ratio : 30070000 - cpu2core_custom_dev: YES - cpu2core_dev : 0.07 - - -test_rx_check_sfr: - multiplier : 10 - cores : 2 - rx_sample_rate : 16 - # allow 0.03% errors, bad router - error_tolerance : 0.03 - -test_rx_check_http: - multiplier : 15000 - cores : 1 - rx_sample_rate : 16 - # allow 0.03% errors, bad routerifconfig - error_tolerance : 0.03 -test_rx_check_sfr_ipv6: - multiplier : 10 - cores : 2 - rx_sample_rate : 16 - # allow 0.03% errors, bad router - error_tolerance : 0.03 +### 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 : 12000 + cores : 1 + nat_opened : 40000 + allow_timeout_dev : True + bw_per_core : 44.445 + +test_nat_simple_mode2: *test_nat_simple + +test_nat_learning: + << : *test_nat_simple + nat_opened : 40000 + + +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: - multiplier : 15000 - cores : 1 - rx_sample_rate : 16 - # allow 0.03% errors, bad router - error_tolerance : 0.03 + << : *rx_http + bw_per_core : 49.237 test_rx_check_http_negative: - multiplier : 13000 - cores : 1 - rx_sample_rate : 16 - # allow 0.03% errors, bad router - error_tolerance : 0.03 - 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 : - 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 + << : *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 -test_jumbo: - multiplier : 17 - cores : 1 diff --git a/scripts/automation/regression/stateful_tests/trex_general_test.py b/scripts/automation/regression/stateful_tests/trex_general_test.py index 82b1d9d1..8ff4fdaf 100755 --- a/scripts/automation/regression/stateful_tests/trex_general_test.py +++ b/scripts/automation/regression/stateful_tests/trex_general_test.py @@ -139,41 +139,42 @@ class CTRexGeneral_Test(unittest.TestCase): if res[name] != float(val): self.fail('TRex results[%s]==%f and not as expected %f ' % (name, res[name], val)) - def check_CPU_benchmark (self, trex_res, err = 25, minimal_cpu = 30, maximal_cpu = 85): - #cpu_util = float(trex_res.get_last_value("trex-global.data.m_cpu_util")) - cpu_util = sum(trex_res.get_value_list("trex-global.data.m_cpu_util")[-4:-1]) / 3.0 # mean of 3 values before last - - if '1G' in self.modes: - minimal_cpu /= 10.0 - - if not self.is_virt_nics: - if cpu_util > maximal_cpu: - self.fail("CPU is too high (%s%%), probably queue full." % cpu_util ) - #if cpu_util < minimal_cpu: - # self.fail("CPU is too low (%s%%), can't verify performance in such low CPU%%." % cpu_util ) - - test_norm_cpu = sum(trex_res.get_value_list("trex-global.data.m_bw_per_core")[-4:-1]) / 3.0 - - print("TRex CPU utilization: %g%%, norm_cpu is : %g Gb/core" % (round(cpu_util, 2), round(test_norm_cpu))) - - expected_norm_cpu = self.get_benchmark_param('bw_per_core') - if not expected_norm_cpu: - expected_norm_cpu = 1 - - calc_error_precent = abs(100.0 * test_norm_cpu / expected_norm_cpu - 100) - print('Err percent: %s' % calc_error_precent) - #if calc_error_precent > err and cpu_util > 10: - # self.fail('Excepted bw_per_core ratio: %s, got: %g' % (expected_norm_cpu, round(test_norm_cpu))) - - # report benchmarks - if self.GAManager: - try: - setup_test = '%s.%s' % (CTRexScenario.setup_name, self.get_name()) - self.GAManager.gaAddAction(Event = 'stateful_test', action = setup_test, label = 'bw_per_core', value = int(test_norm_cpu)) - self.GAManager.gaAddAction(Event = 'stateful_test', action = setup_test, label = 'bw_per_core_exp', value = int(expected_norm_cpu)) - self.GAManager.emptyAndReportQ() - except Exception as e: - print('Sending GA failed: %s' % e) + def check_CPU_benchmark (self, trex_res, err = 25, minimal_cpu = 10, maximal_cpu = 85): + cpu_util = trex_res.get_avg_steady_state_value('trex-global.data.m_cpu_util_raw') + trex_tx_bps = trex_res.get_avg_steady_state_value('trex-global.data.m_tx_bps') + expected_norm_cpu = self.get_benchmark_param('bw_per_core') + cores = self.get_benchmark_param('cores') + ports_count = trex_res.get_ports_count() + test_norm_cpu = trex_tx_bps / (cpu_util * ports_count * cores * 2.5e6) + + if '1G' in self.modes: + minimal_cpu /= 10.0 + + if not self.is_virt_nics: + if cpu_util > maximal_cpu: + self.fail("CPU is too high (%s%%), probably queue full." % cpu_util ) + #if cpu_util < minimal_cpu: + # self.fail("CPU is too low (%s%%), can't verify performance in such low CPU%%." % cpu_util ) + + print("TRex CPU utilization: %g%%, norm_cpu is : %g Gb/core" % (round(cpu_util, 2), round(test_norm_cpu, 2))) + + if not expected_norm_cpu: + expected_norm_cpu = 1 + + calc_error_precent = abs(100.0 * test_norm_cpu / expected_norm_cpu - 100) + print('Err percent: %s' % calc_error_precent) + #if calc_error_precent > err and cpu_util > 10: + # self.fail('Excepted bw_per_core ratio: %s, got: %g' % (expected_norm_cpu, round(test_norm_cpu))) + + # report benchmarks + if self.GAManager: + try: + setup_test = '%s.%s' % (CTRexScenario.setup_name, self.get_name()) + self.GAManager.gaAddAction(Event = 'stateful_test', action = setup_test, label = 'bw_per_core', value = int(test_norm_cpu)) + self.GAManager.gaAddAction(Event = 'stateful_test', action = setup_test, label = 'bw_per_core_exp', value = int(expected_norm_cpu)) + self.GAManager.emptyAndReportQ() + except Exception as e: + print('Sending GA failed: %s' % e) def check_results_gt (self, res, name, val): if res is None: diff --git a/scripts/automation/regression/stateful_tests/trex_rx_test.py b/scripts/automation/regression/stateful_tests/trex_rx_test.py index 40528d16..c08ad1ea 100755 --- a/scripts/automation/regression/stateful_tests/trex_rx_test.py +++ b/scripts/automation/regression/stateful_tests/trex_rx_test.py @@ -250,7 +250,7 @@ class CTRexRx_Test(CTRexGeneral_Test): print('Run until finish, expect errors') old_errors = copy.deepcopy(self.fail_reasons) - nat_dict = self.get_benchmark_param('nat_dict', test_name = 'test_nat_simple') + nat_dict = self.get_benchmark_param('nat_dict', test_name = 'test_nat_simple_mode1') nat_obj = CNatConfig(nat_dict) self.router.config_nat(nat_obj) self.router.config_zbf() diff --git a/scripts/automation/regression/trex.py b/scripts/automation/regression/trex.py index 44f2faba..7286b166 100644 --- a/scripts/automation/regression/trex.py +++ b/scripts/automation/regression/trex.py @@ -40,6 +40,7 @@ class CTRexScenario: no_daemon = False router_image = None debug_image = False + test = None class CTRexRunner: """This is an instance for generating a CTRexRunner""" diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py index 915cd682..4ff21b80 100755 --- a/scripts/automation/regression/trex_unit_test.py +++ b/scripts/automation/regression/trex_unit_test.py @@ -30,7 +30,7 @@ import outer_packages import nose from nose.plugins import Plugin -import logging +from nose.selector import Selector import CustomLogger import misc_methods from rednose import RedNose @@ -43,11 +43,30 @@ from trex_stl_lib.utils.GAObjClass import GAmanager import trex import socket from pprint import pprint -import subprocess -import re import time from distutils.dir_util import mkpath +# nose overrides + +# option to select wanted test by name without file, class etc. +def new_Selector_wantMethod(self, method, orig_Selector_wantMethod = Selector.wantMethod): + result = orig_Selector_wantMethod(self, method) + if not CTRexScenario.test: + return result + else: + return CTRexScenario.test in getattr(method, '__name__', '') + +Selector.wantMethod = new_Selector_wantMethod + +def new_Selector_wantFunction(self, function, orig_Selector_wantFunction = Selector.wantFunction): + result = orig_Selector_wantFunction(self, function) + if not CTRexScenario.test: + return result + else: + return CTRexScenario.test in getattr(function, '__name__', '') + +Selector.wantFunction = new_Selector_wantFunction + # override nose's strange representation of setUpClass errors def __suite_repr__(self): if hasattr(self.context, '__module__'): # inside class, setUpClass etc. @@ -59,6 +78,8 @@ def __suite_repr__(self): nose.suite.ContextSuite.__repr__ = __suite_repr__ nose.suite.ContextSuite.__str__ = __suite_repr__ +# /nose overrides + def check_trex_path(trex_path): if os.path.isfile('%s/trex_daemon_server' % trex_path): return os.path.abspath(trex_path) @@ -78,7 +99,7 @@ def get_trex_path(): def address_to_ip(address): - for i in range(10): + for i in range(5): try: return socket.gethostbyname(address) except: @@ -149,6 +170,8 @@ class CTRexTestConfiguringPlugin(Plugin): parser.add_option('--trex-args', action='store', default = '', dest="trex_args", 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.)') def configure(self, options, conf): @@ -160,6 +183,7 @@ class CTRexTestConfiguringPlugin(Plugin): self.json_verbose = options.json_verbose self.telnet_verbose = options.telnet_verbose self.no_daemon = options.no_daemon + CTRexScenario.test = options.test if self.collect_only or self.functional: return if CTRexScenario.setup_dir and options.config_path: @@ -181,7 +205,7 @@ class CTRexTestConfiguringPlugin(Plugin): self.loggerPath = options.log_path # initialize CTRexScenario global testing class, to be used by all tests CTRexScenario.configuration = self.configuration - CTRexScenario.no_daemon = self.no_daemon + CTRexScenario.no_daemon = options.no_daemon CTRexScenario.benchmark = self.benchmark CTRexScenario.modes = set(self.modes) CTRexScenario.server_logs = self.server_logs @@ -347,15 +371,14 @@ if __name__ == "__main__": nose_argv += sys_args - config_plugin = CTRexTestConfiguringPlugin() - red_nose = RedNose() + addplugins = [RedNose(), CTRexTestConfiguringPlugin()] result = True try: if len(CTRexScenario.test_types['functional_tests']): additional_args = ['--func'] + CTRexScenario.test_types['functional_tests'] if xml_arg: additional_args += ['--with-xunit', xml_arg.replace('.xml', '_functional.xml')] - result = nose.run(argv = nose_argv + additional_args, addplugins = [red_nose, config_plugin]) + result = nose.run(argv = nose_argv + additional_args, addplugins = addplugins) if len(CTRexScenario.test_types['stateful_tests']): additional_args = ['--stf'] if '--warmup' in sys.argv: @@ -365,14 +388,14 @@ if __name__ == "__main__": additional_args.extend(['-a', '!client_package']) if xml_arg: additional_args += ['--with-xunit', xml_arg.replace('.xml', '_stateful.xml')] - result = nose.run(argv = nose_argv + additional_args, addplugins = [red_nose, config_plugin]) and result + result = nose.run(argv = nose_argv + additional_args, addplugins = addplugins) and result if len(CTRexScenario.test_types['stateless_tests']): additional_args = ['--stl', 'stateless_tests/stl_general_test.py:STLBasic_Test.test_connectivity'] + CTRexScenario.test_types['stateless_tests'] if not test_client_package: additional_args.extend(['-a', '!client_package']) if xml_arg: additional_args += ['--with-xunit', xml_arg.replace('.xml', '_stateless.xml')] - result = nose.run(argv = nose_argv + additional_args, addplugins = [red_nose, config_plugin]) and result + result = nose.run(argv = nose_argv + additional_args, addplugins = addplugins) and result #except Exception as e: # result = False # print(e) 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 a13fe31f..c8827afe 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 @@ -535,7 +535,7 @@ class CTRexClient(object): finally: self.prompt_verbose_data() - def sample_until_condition (self, condition_func, time_between_samples = 5): + def sample_until_condition (self, condition_func, time_between_samples = 1): """ Automatically sets ongoing sampling of TRex data, with sampling rate described by time_between_samples. @@ -549,7 +549,7 @@ class CTRexClient(object): time_between_samples : int determines the time between each sample of the server - default value : **5** + default value : **1** :return: the first result object (see :class:`CTRexResult` for further details) of the TRex run on which the condition has been met. @@ -579,7 +579,7 @@ class CTRexClient(object): # this could come from provided method 'condition_func' raise - def sample_to_run_finish (self, time_between_samples = 5): + def sample_to_run_finish (self, time_between_samples = 1): """ Automatically sets automatically sampling of TRex data with sampling rate described by time_between_samples until TRex run finished. @@ -587,7 +587,7 @@ class CTRexClient(object): time_between_samples : int determines the time between each sample of the server - default value : **5** + default value : **1** :return: the latest result object (see :class:`CTRexResult` for further details) with sampled data. @@ -609,7 +609,7 @@ class CTRexClient(object): results = self.get_result_obj() return results - def sample_x_seconds (self, sample_time, time_between_samples = 5): + def sample_x_seconds (self, sample_time, time_between_samples = 1): """ Automatically sets ongoing sampling of TRex data for sample_time seconds, with sampling rate described by time_between_samples. Does not stop the TRex afterwards! @@ -623,7 +623,7 @@ class CTRexClient(object): time_between_samples : int determines the time between each sample of the server - default value : **5** + default value : **1** :return: the first result object (see :class:`CTRexResult` for further details) of the TRex run after given sample_time. @@ -1271,7 +1271,7 @@ class CTRexResult(object): .. tip:: | Use '.' to enter one level deeper in dictionary hierarchy. | Use '[i]' to access the i'th indexed object of an array. - tree_path_to_key : regex + regex : regex apply a regex to filter results out from a multiple results set. Filter applies only on keys of dictionary type. @@ -1299,7 +1299,7 @@ class CTRexResult(object): .. tip:: | Use '.' to enter one level deeper in dictionary hierarchy. | Use '[i]' to access the i'th indexed object of an array. - tree_path_to_key : regex + regex : regex apply a regex to filter results out from a multiple results set. Filter applies only on keys of dictionary type. @@ -1352,7 +1352,7 @@ class CTRexResult(object): if not len(self._history): return -1 - return len(self.__get_value_by_path(self._history[-1], 'trex-global.data', 'opackets-\d+')) + return len(self.get_last_value('trex-global.data', 'opackets-\d+')) def update_result_data (self, latest_dump): @@ -1383,6 +1383,7 @@ class CTRexResult(object): # check for up to 2% change between expected and actual if (self._current_tx_rate['m_tx_bps'] > 0.98 * self._expected_tx_rate['m_tx_expected_bps']): self._done_warmup = True + latest_dump['warmup_barrier'] = True # handle latency data if self.latency_checked: @@ -1427,12 +1428,12 @@ class CTRexResult(object): for i, p in re.findall(r'(\d+)|([\w|-]+)', tree_path): dct = dct[p or int(i)] if regex is not None and isinstance(dct, dict): - res = {} - for key,val in dct.items(): - match = re.match(regex, key) - if match: - res[key]=val - return res + res = {} + for key,val in dct.items(): + match = re.match(regex, key) + if match: + res[key]=val + return res else: return dct except (KeyError, TypeError): @@ -1480,24 +1481,61 @@ class CTRexResult(object): @staticmethod def __get_filtered_max_latency (src_dict, filtered_latency_amount = 0.001): result = {} - for port, data in src_dict.items(): - if not port.startswith('port-'): - continue - max_port = 'max-%s' % port[5:] - res = data['hist'] - if not len(res['histogram']): - result[max_port] = 0 - continue - result[max_port] = 5 # if sum below will not get to filtered amount, use this value - sum_high = 0.0 - for elem in reversed(res['histogram']): - sum_high += elem['val'] - if sum_high >= filtered_latency_amount * res['cnt']: - result[max_port] = elem['key'] + int('5' + repr(elem['key'])[2:]) - break + if src_dict: + for port, data in src_dict.items(): + if not port.startswith('port-'): + continue + max_port = 'max-%s' % port[5:] + res = data['hist'] + if not len(res['histogram']): + result[max_port] = 0 + continue + result[max_port] = 5 # if sum below will not get to filtered amount, use this value + sum_high = 0.0 + for elem in reversed(res['histogram']): + sum_high += elem['val'] + if sum_high >= filtered_latency_amount * res['cnt']: + result[max_port] = elem['key'] + int('5' + repr(elem['key'])[2:]) + break return result + # history iterator after warmup period + def _get_steady_state_history_iterator(self): + if not self.is_done_warmup(): + raise Exception('Warm-up period not finished') + for index, res in enumerate(self._history): + if 'warmup_barrier' in res: + for steady_state_index in range(index, max(index, len(self._history) - 1)): + yield self._history[steady_state_index] + return + for index in range(len(self._history) - 1): + yield self._history[index] + + + def get_avg_steady_state_value(self, tree_path_to_key): + ''' + Gets average value after warmup period. + For example: .get_avg_steady_state_value('trex-global.data.m_tx_bps') + Usually more accurate than latest history value. + + :parameters: + tree_path_to_key : str + defines a path to desired data. + + :return: + average value at steady state + + :raises: + Exception in case steady state period was not reached or tree_path_to_key was not found in result. + ''' + values_arr = [self.__get_value_by_path(res, tree_path_to_key) for res in self._get_steady_state_history_iterator()] + values_arr = list(filter(lambda x: x is not None, values_arr)) + if not values_arr: + raise Exception('All the keys are None, probably wrong tree_path_to_key: %s' % tree_path_to_key) + return sum(values_arr) / float(len(values_arr)) + + if __name__ == "__main__": c = CTRexClient('127.0.0.1') print('restarting daemon') diff --git a/scripts/master_daemon.py b/scripts/master_daemon.py index aa49f207..a44f55a8 100755 --- a/scripts/master_daemon.py +++ b/scripts/master_daemon.py @@ -181,7 +181,7 @@ parser.add_argument('--type', '--daemon-type', '--daemon_type', choices = daemon action = 'store', help = 'Specify daemon type to start/stop etc.\nDefault is master_daemon.') args = parser.parse_args() -args.trex_dir = os.path.normpath(args.trex_dir) +args.trex_dir = os.path.abspath(args.trex_dir) args.daemon_type = args.daemon_type or 'master_daemon' stl_rpc_proxy_dir = os.path.join(args.trex_dir, 'automation', 'trex_control_plane', 'stl', 'examples') -- cgit 1.2.3-korg From e2b4f166726e8e2149a2a0ddd763fa8d44055844 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Mon, 4 Jul 2016 15:51:45 +0300 Subject: trex14 and trex07 nat benchmarks update --- scripts/automation/regression/setups/trex07/benchmark.yaml | 8 +++----- scripts/automation/regression/setups/trex14/benchmark.yaml | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/setups/trex07/benchmark.yaml b/scripts/automation/regression/setups/trex07/benchmark.yaml index 38e25162..98bbaf14 100644 --- a/scripts/automation/regression/setups/trex07/benchmark.yaml +++ b/scripts/automation/regression/setups/trex07/benchmark.yaml @@ -57,17 +57,15 @@ test_ipv6_simple: test_nat_simple_mode1: &test_nat_simple stat_route_dict : *stat_route_dict nat_dict : *nat_dict - multiplier : 12000 + multiplier : 6000 cores : 1 - nat_opened : 40000 + nat_opened : 500000 allow_timeout_dev : True bw_per_core : 44.445 test_nat_simple_mode2: *test_nat_simple -test_nat_learning: - << : *test_nat_simple - nat_opened : 40000 +test_nat_learning: *test_nat_simple test_nbar_simple: diff --git a/scripts/automation/regression/setups/trex14/benchmark.yaml b/scripts/automation/regression/setups/trex14/benchmark.yaml index aa4ac2d4..8380cbb1 100644 --- a/scripts/automation/regression/setups/trex14/benchmark.yaml +++ b/scripts/automation/regression/setups/trex14/benchmark.yaml @@ -57,17 +57,15 @@ test_ipv6_simple: test_nat_simple_mode1: &test_nat_simple stat_route_dict : *stat_route_dict nat_dict : *nat_dict - multiplier : 12000 + multiplier : 6000 cores : 1 - nat_opened : 40000 + nat_opened : 500000 allow_timeout_dev : True bw_per_core : 44.445 test_nat_simple_mode2: *test_nat_simple -test_nat_learning: - << : *test_nat_simple - nat_opened : 40000 +test_nat_learning: *test_nat_simple test_nbar_simple: -- cgit 1.2.3-korg From 087464fbd4011b1a357f0567e9793dde641e9120 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Mon, 4 Jul 2016 17:20:35 +0300 Subject: regression: disable rx negative on trex14 --- scripts/automation/regression/setups/trex14/benchmark.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/automation/regression/setups/trex14/benchmark.yaml b/scripts/automation/regression/setups/trex14/benchmark.yaml index 8380cbb1..3cd22ce2 100644 --- a/scripts/automation/regression/setups/trex14/benchmark.yaml +++ b/scripts/automation/regression/setups/trex14/benchmark.yaml @@ -99,7 +99,7 @@ test_rx_check_http_ipv6: << : *rx_http bw_per_core : 49.237 -test_rx_check_http_negative: +test_rx_check_http_negative_disabled: << : *rx_http stat_route_dict : *stat_route_dict nat_dict : *nat_dict -- cgit 1.2.3-korg From c5d7c2ee9859b5ba772c6a9be46ced61f2ad25c3 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 5 Jul 2016 10:46:04 +0300 Subject: profile console command was broken on Python 3 --- .../automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') 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 ae7c23f2..575d4025 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 @@ -2974,7 +2974,7 @@ class STLClient(object): if profile_type == 'python': self.logger.log('Type: {:^12}'.format('Python Module')) - self.logger.log('Tunables: {:^12}'.format(['{0} = {1}'.format(k ,v) for k, v in info['tunables'].items()])) + self.logger.log('Tunables: {:^12}'.format(str(['{0} = {1}'.format(k ,v) for k, v in info['tunables'].items()]))) elif profile_type == 'yaml': self.logger.log('Type: {:^12}'.format('YAML')) -- cgit 1.2.3-korg From 0aa97153697e58d575660af9881cb43d21897c13 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 5 Jul 2016 11:16:46 +0300 Subject: TUI integrated with console --- .../trex_control_plane/stl/console/trex_console.py | 3 +- .../trex_control_plane/stl/console/trex_tui.py | 268 ++++++++++++++++++--- 2 files changed, 242 insertions(+), 29 deletions(-) (limited to 'scripts') 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 ab70d357..4bfeb2f3 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -19,6 +19,7 @@ limitations under the License. from __future__ import print_function import subprocess +import threading import cmd import json import ast @@ -590,7 +591,7 @@ class TRexConsole(TRexGeneralCmd): with self.stateless_client.logger.supress(): - self.tui.show() + self.tui.show(self) def help_tui (self): 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 a2ffcad6..b5d11bbb 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -15,6 +15,10 @@ from trex_stl_lib.utils import text_tables from trex_stl_lib import trex_stl_stats from trex_stl_lib.utils.filters import ToggleFilter +class TUIQuit(Exception): + pass + + # for STL exceptions from trex_stl_lib.api import * @@ -290,7 +294,8 @@ class TrexTUIPanelManager(): self.panels['ustats'] = TrexTUIUtilizationStats(self) self.key_actions = OrderedDict() - self.key_actions['q'] = {'action': self.action_quit, 'legend': 'quit', 'show': True} + self.key_actions['q'] = {'action': self.action_none, 'legend': 'quit', 'show': True} + self.key_actions['ESC'] = {'action': self.action_none, 'legend': 'console', 'show': True} self.key_actions['d'] = {'action': self.action_show_dash, 'legend': 'dashboard', 'show': True} self.key_actions['s'] = {'action': self.action_show_sstats, 'legend': 'streams', 'show': True} self.key_actions['l'] = {'action': self.action_show_lstats, 'legend': 'latency', 'show': True} @@ -350,10 +355,12 @@ class TrexTUIPanelManager(): self.show_log = show_log self.generate_legend() - def show (self): + def show (self, show_legend): self.main_panel.show() self.print_connection_status() - self.print_legend() + + if show_legend: + self.print_legend() if self.show_log: self.log.show() @@ -383,7 +390,7 @@ class TrexTUIPanelManager(): # actions - def action_quit (self): + def action_none (self): return None def action_show_dash (self): @@ -428,15 +435,14 @@ class TrexTUI(): self.stateless_client = stateless_client self.pm = TrexTUIPanelManager(self) - + def handle_key_input (self): # try to read a single key ch = os.read(sys.stdin.fileno(), 1).decode() - if ch != None and len(ch) > 0: + if ch: return (self.pm.handle_key(ch), True) - else: return (True, False) @@ -447,15 +453,14 @@ class TrexTUI(): sys.stdout.write("\x1b[2J\x1b[H") + def show (self, cmd, show_log = False): + with AsyncKeys(cmd) as async_keys: + self.async_keys = async_keys + self.show_internal(show_log) + - def show (self, show_log = False): - # init termios - old_settings = termios.tcgetattr(sys.stdin) - new_settings = termios.tcgetattr(sys.stdin) - new_settings[3] = new_settings[3] & ~(termios.ECHO | termios.ICANON) # lflags - new_settings[6][termios.VMIN] = 0 # cc - new_settings[6][termios.VTIME] = 0 # cc - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new_settings) + + def show_internal (self, show_log = False): self.pm.init(show_log) @@ -465,11 +470,11 @@ class TrexTUI(): try: while True: # draw and handle user input - cont, force_draw = self.handle_key_input() - self.draw_screen(force_draw) - if not cont: - break - time.sleep(0.1) + status = self.async_keys.tick(self.pm) + + self.draw_screen(status) + if not status: + time.sleep(0.001) # regular state if self.state == self.STATE_ACTIVE: @@ -497,34 +502,241 @@ class TrexTUI(): self.state = self.STATE_LOST_CONT - finally: - # restore - termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) + except TUIQuit: + print("\nExiting TUI...") print("") # draw once - def draw_screen (self, force_draw = False): + def draw_screen (self, status): - if (self.draw_policer >= 5) or (force_draw): + # only redraw the keys line + if status == AsyncKeys.STATUS_REDRAW_KEYS: + self.clear_screen() + sys.stdout.write(self.last_snap) + self.async_keys.draw() + sys.stdout.flush() + return + + if (self.draw_policer >= 500) or (status == AsyncKeys.STATUS_REDRAW_ALL): # capture stdout to a string old_stdout = sys.stdout sys.stdout = mystdout = StringIO() - self.pm.show() + self.pm.show(show_legend = self.async_keys.is_legend_mode()) + self.last_snap = mystdout.getvalue() + + self.async_keys.draw() sys.stdout = old_stdout self.clear_screen() - print(mystdout.getvalue()) - + sys.stdout.write(mystdout.getvalue()) + sys.stdout.flush() self.draw_policer = 0 else: self.draw_policer += 1 + def get_state (self): return self.state + + + + +# handles async IO +class AsyncKeys: + + MODE_LEGEND = 1 + MODE_CONSOLE = 2 + + STATUS_NONE = 0 + STATUS_REDRAW_KEYS = 1 + STATUS_REDRAW_ALL = 2 + + def __init__ (self, cmd): + self.engine_console = AsyncKeysEngineConsole(self, cmd) + self.engine_legend = AsyncKeysEngineLegend(self) + self.engine = self.engine_console + + def __enter__ (self): + # init termios + self.old_settings = termios.tcgetattr(sys.stdin) + new_settings = termios.tcgetattr(sys.stdin) + new_settings[3] = new_settings[3] & ~(termios.ECHO | termios.ICANON) # lflags + new_settings[6][termios.VMIN] = 0 # cc + new_settings[6][termios.VTIME] = 0 # cc + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new_settings) + return self + + def __exit__ (self, type, value, traceback): + termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old_settings) + + + def is_legend_mode (self): + return self.engine.get_type() == AsyncKeys.MODE_LEGEND + + def is_console_mode (self): + return self.engine.get_type == AsyncKeys.MODE_CONSOLE + + def switch (self): + if self.is_legend_mode(): + self.engine = self.engine_console + else: + self.engine = self.engine_legend + + + def tick (self, pm): + try: + rc = self.engine.tick(pm) + return self.STATUS_REDRAW_KEYS if rc else self.STATUS_NONE + + except AsyncKeysSwitch: + self.switch() + return self.STATUS_REDRAW_ALL + + def draw (self): + self.engine.draw() + + +# used for switching engines +class AsyncKeysSwitch(Exception): + pass + + +# Legend engine +class AsyncKeysEngineLegend: + def __init__ (self, async): + self.async = async + + def get_type (self): + return self.async.MODE_LEGEND + + def tick (self, pm): + ch = os.read(sys.stdin.fileno(), 1).decode() + if not ch: + return False + + if ch == '\x1b': + raise AsyncKeysSwitch() + + elif ch == 'q': + raise TUIQuit() + + return pm.handle_key(ch) + + def draw (self): + pass + + +# console engine +class AsyncKeysEngineConsole: + def __init__ (self, async, cmd): + self.async = async + self.cmd = cmd + self.line = "" + self.history = deque(maxlen = 100) + + # fetch readline history + for i in range(0, readline.get_current_history_length()): + self.history.appendleft(readline.get_history_item(i)) + + # reset index + self.history_index = -1 + + + def get_type (self): + return self.async.MODE_CONSOLE + + + def handle_escape_char (self, seq): + # up + if seq == '\x1b[A': + self.history_index = min(self.history_index + 1, len(self.history) - 1) + self.line = self.history[self.history_index] if self.history_index > -1 else '' + return True + + # down + elif seq == '\x1b[B': + self.history_index = max(self.history_index - 1, -1) + self.line = self.history[self.history_index] if self.history_index > -1 else '' + return True + + # left + #elif seq == '\x1b[D': + # self.line_index = min(self.line_index - 1, 0) + # return True + + # right + #elif seq == '\x1b[C': + # self.line_index = min(self.line_index - 1, 0) + # return True + + return False + + def tick (self, _): + seq = '' + # drain all chars + while True: + ch = os.read(sys.stdin.fileno(), 1).decode() + if not ch: + break + seq += ch + + # ignore - nothing to do + if not seq: + return False + + # handle escape chars + if len(seq) > 1: + return self.handle_escape_char(seq) + + # handle each char + for ch in seq: + return self.handle_single_key(ch) + + + + def handle_single_key (self, ch): + + # ecsape + if ch == '\x1b': + raise AsyncKeysSwitch() + + # newline + elif ch == '\n': + self.handle_cmd() + + # backspace + elif ch == '\x7f': + self.line = self.line[:-1] + + # TAB + elif ch == '\t': + self.line = "TAB" + + # simple char + else: + self.line += ch + + return True + + + def handle_cmd (self): + if self.line in ['quit', 'exit', 'q']: + raise TUIQuit() + + self.cmd.onecmd(self.line) + self.history.appendleft(self.line) + self.history_index = -1 + self.line = "" + + + def draw (self): + sys.stdout.write("\nPress 'ESC' for navigation panel...\n") + sys.stdout.write("\n" + "tui>" + self.line) + -- cgit 1.2.3-korg From 851829288eed21a388ddaa1c6d2de6dc23bdf2d9 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 5 Jul 2016 13:26:44 +0300 Subject: TUI with console - awesome ! --- .../trex_control_plane/stl/console/trex_console.py | 1 - .../trex_control_plane/stl/console/trex_tui.py | 175 ++++++++++++++------- 2 files changed, 117 insertions(+), 59 deletions(-) (limited to 'scripts') 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 4bfeb2f3..02a99893 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -19,7 +19,6 @@ limitations under the License. from __future__ import print_function import subprocess -import threading import cmd import json import ast 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 b5d11bbb..1d7892bb 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -4,6 +4,7 @@ import os import time from collections import OrderedDict, deque import datetime +import readline if sys.version_info > (3,0): from io import StringIO @@ -376,9 +377,11 @@ class TrexTUIPanelManager(): msg = self.main_panel.get_key_actions()[ch]['action']() else: - msg = "" + return False + #msg = "" self.generate_legend() + return True if msg == None: return False @@ -591,22 +594,29 @@ class AsyncKeys: def tick (self, pm): - try: - rc = self.engine.tick(pm) - return self.STATUS_REDRAW_KEYS if rc else self.STATUS_NONE + seq = '' + # drain all chars + while True: + ch = os.read(sys.stdin.fileno(), 1).decode() + if not ch: + break + seq += ch + + if not seq: + return self.STATUS_NONE - except AsyncKeysSwitch: + if seq == '\x1b': self.switch() return self.STATUS_REDRAW_ALL + # pass tick to engine + return self.engine.tick(seq, pm) + + def draw (self): self.engine.draw() -# used for switching engines -class AsyncKeysSwitch(Exception): - pass - # Legend engine class AsyncKeysEngineLegend: @@ -616,18 +626,17 @@ class AsyncKeysEngineLegend: def get_type (self): return self.async.MODE_LEGEND - def tick (self, pm): - ch = os.read(sys.stdin.fileno(), 1).decode() - if not ch: - return False - - if ch == '\x1b': - raise AsyncKeysSwitch() + def tick (self, seq, pm): - elif ch == 'q': + if seq == 'q': raise TUIQuit() - return pm.handle_key(ch) + # ignore escapes + if len(seq) > 1: + return AsyncKeys.STATUS_NONE + + rc = pm.handle_key(seq) + return AsyncKeys.STATUS_REDRAW_ALL if rc else AsyncKeys.STATUS_NONE def draw (self): pass @@ -638,15 +647,17 @@ class AsyncKeysEngineConsole: def __init__ (self, async, cmd): self.async = async self.cmd = cmd - self.line = "" - self.history = deque(maxlen = 100) + self.lines = deque(maxlen = 100) # fetch readline history for i in range(0, readline.get_current_history_length()): - self.history.appendleft(readline.get_history_item(i)) + self.lines.appendleft(CmdLine(readline.get_history_item(i))) + + # new line + self.lines.appendleft(CmdLine('')) + self.line_index = 0 - # reset index - self.history_index = -1 + self.ac = ['start', 'stop', 'pause', 'resume', 'update', 'quit', 'exit'] def get_type (self): @@ -656,16 +667,15 @@ class AsyncKeysEngineConsole: def handle_escape_char (self, seq): # up if seq == '\x1b[A': - self.history_index = min(self.history_index + 1, len(self.history) - 1) - self.line = self.history[self.history_index] if self.history_index > -1 else '' - return True + self.line_index = min(self.line_index + 1, len(self.lines) - 1) + return AsyncKeys.STATUS_REDRAW_KEYS # down elif seq == '\x1b[B': - self.history_index = max(self.history_index - 1, -1) - self.line = self.history[self.history_index] if self.history_index > -1 else '' - return True + self.line_index = max(self.line_index - 1, 0) + return AsyncKeys.STATUS_REDRAW_KEYS + # TODO: left and right # left #elif seq == '\x1b[D': # self.line_index = min(self.line_index - 1, 0) @@ -676,21 +686,10 @@ class AsyncKeysEngineConsole: # self.line_index = min(self.line_index - 1, 0) # return True - return False - - def tick (self, _): - seq = '' - # drain all chars - while True: - ch = os.read(sys.stdin.fileno(), 1).decode() - if not ch: - break - seq += ch - - # ignore - nothing to do - if not seq: - return False + return AsyncKeys.STATUS_NONE + def tick (self, seq, _): + # handle escape chars if len(seq) > 1: return self.handle_escape_char(seq) @@ -703,40 +702,100 @@ class AsyncKeysEngineConsole: def handle_single_key (self, ch): - # ecsape - if ch == '\x1b': - raise AsyncKeysSwitch() - # newline - elif ch == '\n': + if ch == '\n': self.handle_cmd() # backspace elif ch == '\x7f': - self.line = self.line[:-1] + self.lines[self.line_index].set(self.lines[self.line_index].get()[:-1]) # TAB elif ch == '\t': - self.line = "TAB" + cur = self.lines[self.line_index].get() + common = os.path.commonprefix([x for x in self.ac if x.startswith(cur)]) + if common: + if common in self.ac: + self.lines[self.line_index].set(common + ' ') + else: + self.lines[self.line_index].set(common) + # simple char else: - self.line += ch + self.lines[self.line_index] += ch - return True + return AsyncKeys.STATUS_REDRAW_KEYS def handle_cmd (self): - if self.line in ['quit', 'exit', 'q']: + cmd = self.lines[self.line_index].get().strip() + if not cmd: + return + + if cmd in ['quit', 'exit', 'q']: raise TUIQuit() - self.cmd.onecmd(self.line) - self.history.appendleft(self.line) - self.history_index = -1 - self.line = "" + self.cmd.onecmd(cmd) + + # take out the empty line + empty_line = self.lines.popleft() + assert(empty_line.ro_line == '') + + if self.lines[0].ro_line != cmd: + self.lines.appendleft(CmdLine(cmd)) + + # back in + self.lines.appendleft(empty_line) + self.line_index = 0 + + # back to readonly + for line in self.lines: + line.invalidate() + assert(self.lines[0].modified == False) def draw (self): sys.stdout.write("\nPress 'ESC' for navigation panel...\n") - sys.stdout.write("\n" + "tui>" + self.line) + sys.stdout.write("\n" + "tui>" + self.lines[self.line_index].get()) + + +# a readline alike command line - can be modified during edit +class CmdLine(object): + def __init__ (self, line): + self.ro_line = line + self.w_line = None + self.modified = False + + def get (self): + if self.modified: + return self.w_line + else: + return self.ro_line + + def set (self, line): + self.w_line = line + self.modified = True + + def __add__ (self, other): + if self.modified: + return self.ro_line + other + else: + return self.wo_line + other + + def __iadd__ (self, other): + if not self.modified: + self.w_line = self.ro_line + self.modified = True + + self.w_line += other + return self + + def __str__ (self): + return self.get() + + def invalidate (self): + self.modified = False + self.w_line = None + -- cgit 1.2.3-korg From e8296c89b60b6ebee39111aea07aedee2fd0400f Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 5 Jul 2016 15:28:11 +0300 Subject: TUI phase 500 ! --- .../trex_control_plane/stl/console/trex_console.py | 3 +- .../trex_control_plane/stl/console/trex_tui.py | 77 +++++++++++++--------- .../stl/trex_stl_lib/trex_stl_client.py | 74 ++++++++++++--------- 3 files changed, 89 insertions(+), 65 deletions(-) (limited to 'scripts') 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 02a99893..7fd34e6e 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -459,7 +459,6 @@ class TRexConsole(TRexGeneralCmd): self.stateless_client.start_line(line) - def help_start(self): @@ -590,7 +589,7 @@ class TRexConsole(TRexGeneralCmd): with self.stateless_client.logger.supress(): - self.tui.show(self) + self.tui.show(self.stateless_client) def help_tui (self): 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 1d7892bb..f60ee170 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -378,17 +378,16 @@ class TrexTUIPanelManager(): else: return False - #msg = "" self.generate_legend() return True - if msg == None: - return False - else: - if msg: - self.log.add_event(msg) - return True + #if msg == None: + # return False + #else: + # if msg: + # self.log.add_event(msg) + # return True # actions @@ -438,17 +437,7 @@ class TrexTUI(): self.stateless_client = stateless_client self.pm = TrexTUIPanelManager(self) - - - - def handle_key_input (self): - # try to read a single key - ch = os.read(sys.stdin.fileno(), 1).decode() - if ch: - return (self.pm.handle_key(ch), True) - else: - return (True, False) - + def clear_screen (self): #os.system('clear') @@ -456,8 +445,8 @@ class TrexTUI(): sys.stdout.write("\x1b[2J\x1b[H") - def show (self, cmd, show_log = False): - with AsyncKeys(cmd) as async_keys: + def show (self, client, show_log = False): + with AsyncKeys(client) as async_keys: self.async_keys = async_keys self.show_internal(show_log) @@ -476,7 +465,7 @@ class TrexTUI(): status = self.async_keys.tick(self.pm) self.draw_screen(status) - if not status: + if status == AsyncKeys.STATUS_NONE: time.sleep(0.001) # regular state @@ -561,8 +550,8 @@ class AsyncKeys: STATUS_REDRAW_KEYS = 1 STATUS_REDRAW_ALL = 2 - def __init__ (self, cmd): - self.engine_console = AsyncKeysEngineConsole(self, cmd) + def __init__ (self, client): + self.engine_console = AsyncKeysEngineConsole(self, client) self.engine_legend = AsyncKeysEngineLegend(self) self.engine = self.engine_console @@ -644,21 +633,29 @@ class AsyncKeysEngineLegend: # console engine class AsyncKeysEngineConsole: - def __init__ (self, async, cmd): + def __init__ (self, async, client): self.async = async - self.cmd = cmd + self.client = client self.lines = deque(maxlen = 100) - # fetch readline history + self.ac = {'start' : client.start_line, + 'stop' : client.stop_line, + 'pause' : client.pause_line, + 'resume': client.resume_line, + 'update': client.update_line, + 'quit' : None, + 'exit' : None} + + # fetch readline history and add relevants for i in range(0, readline.get_current_history_length()): - self.lines.appendleft(CmdLine(readline.get_history_item(i))) + cmd = readline.get_history_item(i) + if cmd and cmd.split()[0] in self.ac: + self.lines.appendleft(CmdLine(cmd)) # new line self.lines.appendleft(CmdLine('')) self.line_index = 0 - - self.ac = ['start', 'stop', 'pause', 'resume', 'update', 'quit', 'exit'] - + self.last_status = '' def get_type (self): return self.async.MODE_CONSOLE @@ -727,6 +724,12 @@ class AsyncKeysEngineConsole: return AsyncKeys.STATUS_REDRAW_KEYS + def split_cmd (self, cmd): + s = cmd.split(' ', 1) + op = s[0] + param = s[1] if len(s) == 2 else '' + return op, param + def handle_cmd (self): cmd = self.lines[self.line_index].get().strip() @@ -736,7 +739,11 @@ class AsyncKeysEngineConsole: if cmd in ['quit', 'exit', 'q']: raise TUIQuit() - self.cmd.onecmd(cmd) + op, param = self.split_cmd(cmd) + + func = self.ac.get(op) + if func: + func_rc = func(param) # take out the empty line empty_line = self.lines.popleft() @@ -754,10 +761,16 @@ class AsyncKeysEngineConsole: line.invalidate() assert(self.lines[0].modified == False) + if not func: + self.last_status = "unknown command: '{0}'".format(cmd.split()[0]) + else: + self.last_status = format_text("[OK]", 'green') if func_rc else format_text(str(func_rc).replace('\n', ''), 'red') + def draw (self): sys.stdout.write("\nPress 'ESC' for navigation panel...\n") - sys.stdout.write("\n" + "tui>" + self.lines[self.line_index].get()) + sys.stdout.write("status: {0}\n".format(format_text(self.last_status, 'red'))) + sys.stdout.write("\ntui>" + self.lines[self.line_index].get()) # a readline alike command line - can be modified during edit 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 575d4025..153985ae 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 @@ -2428,13 +2428,14 @@ class STLClient(object): rc = f(*args) except STLError as e: client.logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold')) - return + return RC_ERR(e.brief()) # if got true - print time if rc: delta = time.time() - time1 client.logger.log(format_time(delta) + "\n") + return rc return wrap @@ -2587,14 +2588,14 @@ class STLClient(object): opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) if opts is None: - return + return RC_ERR("invalid arguments for 'start'") 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 + return RC_ERR(msg) else: self.stop(active_ports) @@ -2612,8 +2613,10 @@ class STLClient(object): else: # must be exact if len(opts.ports) != len(opts.tunables): - self.logger.log('tunables section count must be 1 or exactly as the number of ports: got {0}'.format(len(opts.tunables))) - return + msg = 'tunables section count must be 1 or exactly as the number of ports: got {0}'.format(len(opts.tunables)) + self.logger.log(msg) + return RC_ERR(msg) + tunables = opts.tunables @@ -2633,9 +2636,10 @@ class STLClient(object): self.add_streams(profile.get_streams(), ports = port) except STLError as e: - self.logger.log(format_text("\nError while loading profile '{0}'\n".format(opts.file[0]), 'bold')) + msg = format_text("\nError while loading profile '{0}'\n".format(opts.file[0]), 'bold') + self.logger.log(msg) self.logger.log(e.brief() + "\n") - return + return RC_ERR(msg) if opts.dry: @@ -2647,8 +2651,7 @@ class STLClient(object): opts.duration, opts.total) - # true means print time - return True + return RC_OK() @@ -2662,23 +2665,25 @@ class STLClient(object): opts = parser.parse_args(line.split(), default_ports = self.get_active_ports(), verify_acquired = True) if opts is None: - return + return RC_ERR("invalid arguments for 'stop'") # find the relevant ports ports = list_intersect(opts.ports, self.get_active_ports()) if not ports: if not opts.ports: - self.logger.log('stop - no active ports') + msg = 'stop - no active ports' else: - self.logger.log('stop - no active traffic on ports {0}'.format(opts.ports)) - return + msg = 'stop - no active traffic on ports {0}'.format(opts.ports) + + self.logger.log(msg) + return RC_ERR(msg) # call API self.stop(ports) # true means print time - return True + return RC_OK() @__console @@ -2694,22 +2699,24 @@ class STLClient(object): opts = parser.parse_args(line.split(), default_ports = self.get_active_ports(), verify_acquired = True) if opts is None: - return + return RC_ERR("invalid arguments for 'update'") # find the relevant ports ports = list_intersect(opts.ports, self.get_active_ports()) if not ports: if not opts.ports: - self.logger.log('update - no active ports') + msg = 'update - no active ports' else: - self.logger.log('update - no active traffic on ports {0}'.format(opts.ports)) - return + msg = 'update - no active traffic on ports {0}'.format(opts.ports) + + self.logger.log(msg) + return RC_ERR(msg) self.update(ports, opts.mult, opts.total, opts.force) # true means print time - return True + return RC_OK() @__console @@ -2722,26 +2729,29 @@ class STLClient(object): opts = parser.parse_args(line.split(), default_ports = self.get_transmitting_ports(), verify_acquired = True) if opts is None: - return + return RC_ERR("invalid arguments for 'pause'") # check for already paused case if opts.ports and is_sub_list(opts.ports, self.get_paused_ports()): - self.logger.log('pause - all of port(s) {0} are already paused'.format(opts.ports)) - return + msg = 'pause - all of port(s) {0} are already paused'.format(opts.ports) + self.logger.log(msg) + return RC_ERR(msg) # find the relevant ports ports = list_intersect(opts.ports, self.get_transmitting_ports()) if not ports: if not opts.ports: - self.logger.log('pause - no transmitting ports') + msg = 'pause - no transmitting ports' else: - self.logger.log('pause - none of ports {0} are transmitting'.format(opts.ports)) - return + msg = 'pause - none of ports {0} are transmitting'.format(opts.ports) + + self.logger.log(msg) + return RC_ERR(msg) self.pause(ports) # true means print time - return True + return RC_OK() @__console @@ -2754,22 +2764,24 @@ class STLClient(object): opts = parser.parse_args(line.split(), default_ports = self.get_paused_ports(), verify_acquired = True) if opts is None: - return + return RC_ERR("invalid arguments for 'resume'") # find the relevant ports ports = list_intersect(opts.ports, self.get_paused_ports()) if not ports: if not opts.ports: - self.logger.log('resume - no paused ports') + msg = 'resume - no paused ports' else: - self.logger.log('resume - none of ports {0} are paused'.format(opts.ports)) - return + msg = 'resume - none of ports {0} are paused'.format(opts.ports) + + self.logger.log(msg) + return RC_ERR(msg) self.resume(ports) # true means print time - return True + return RC_OK() @__console -- cgit 1.2.3-korg From 51db963aa7d8414b708f0a978cd16c67039a8d6e Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Tue, 5 Jul 2016 20:44:45 +0300 Subject: various fixes --- scripts/automation/regression/CPlatform.py | 3 +-- .../regression/setups/trex07/benchmark.yaml | 2 +- .../regression/stateless_tests/stl_general_test.py | 28 ++++++++++++---------- scripts/automation/regression/trex_unit_test.py | 5 +--- .../stf/trex_stf_lib/trex_client.py | 2 +- .../trex_control_plane/stl/examples/stl_imix.py | 2 ++ 6 files changed, 22 insertions(+), 20 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/CPlatform.py b/scripts/automation/regression/CPlatform.py index 256d7411..dc5418cb 100755 --- a/scripts/automation/regression/CPlatform.py +++ b/scripts/automation/regression/CPlatform.py @@ -70,11 +70,10 @@ class CPlatform(object): res = self.cmd_link.run_single_command(cache) if 'Rollback Done' not in res: print('Failed to load clean config, trying again') + time.sleep(2) if i < 4: continue raise Exception('Could not load clean config, response: %s' % res) - if i > 0: # were errors, better to wait - time.sleep(2) def config_pbr (self, mode = 'config'): idx = 1 diff --git a/scripts/automation/regression/setups/trex07/benchmark.yaml b/scripts/automation/regression/setups/trex07/benchmark.yaml index 98bbaf14..1475abec 100644 --- a/scripts/automation/regression/setups/trex07/benchmark.yaml +++ b/scripts/automation/regression/setups/trex07/benchmark.yaml @@ -98,7 +98,7 @@ test_rx_check_http_ipv6: << : *rx_http bw_per_core : 49.237 -test_rx_check_http_negative: +test_rx_check_http_negative_disabled: << : *rx_http stat_route_dict : *stat_route_dict nat_dict : *nat_dict diff --git a/scripts/automation/regression/stateless_tests/stl_general_test.py b/scripts/automation/regression/stateless_tests/stl_general_test.py index 82738f96..3a22e596 100644 --- a/scripts/automation/regression/stateless_tests/stl_general_test.py +++ b/scripts/automation/regression/stateless_tests/stl_general_test.py @@ -85,18 +85,22 @@ class STLBasic_Test(CStlGeneral_Test): @nottest def test_connectivity(self): if not self.is_loopback: - if CTRexScenario.router_cfg['forceImageReload']: - CTRexScenario.router.load_clean_config() - CTRexScenario.router.configure_basic_interfaces() - CTRexScenario.router.config_pbr(mode = "config") - - err = 'Client could not connect' - CTRexScenario.stl_init_error = err + try: + if CTRexScenario.router_cfg['forceImageReload']: + CTRexScenario.router.load_clean_config() + CTRexScenario.router.configure_basic_interfaces() + CTRexScenario.router.config_pbr(mode = "config") + except Exception as e: + CTRexScenario.stl_init_error = 'Could not configure device, err: %s' % e + self.fail(CTRexScenario.stl_init_error) + return if not self.connect(): - self.fail(err) - err = 'Client could not map ports' - CTRexScenario.stl_init_error = err + CTRexScenario.stl_init_error = 'Client could not connect' + self.fail(CTRexScenario.stl_init_error) + return + print('Connected') if not self.map_ports(): - self.fail(err) + CTRexScenario.stl_init_error = 'Client could not map ports' + self.fail(CTRexScenario.stl_init_error) + return print('Got ports mapping: %s' % CTRexScenario.stl_ports_map) - CTRexScenario.stl_init_error = None diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py index 4ff21b80..e68c5c47 100755 --- a/scripts/automation/regression/trex_unit_test.py +++ b/scripts/automation/regression/trex_unit_test.py @@ -51,10 +51,7 @@ from distutils.dir_util import mkpath # option to select wanted test by name without file, class etc. def new_Selector_wantMethod(self, method, orig_Selector_wantMethod = Selector.wantMethod): result = orig_Selector_wantMethod(self, method) - if not CTRexScenario.test: - return result - else: - return CTRexScenario.test in getattr(method, '__name__', '') + return result and (not CTRexScenario.test or CTRexScenario.test in getattr(method, '__name__', '')) Selector.wantMethod = new_Selector_wantMethod 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 c8827afe..e46a4ee3 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 @@ -310,7 +310,7 @@ class CTRexClient(object): for i in range(int(timeout / poll_rate)): if not self.get_trex_cmds(): return True - sleep(poll_rate) + time.sleep(poll_rate) if self.get_trex_cmds(): return False return True diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_imix.py b/scripts/automation/trex_control_plane/stl/examples/stl_imix.py index 46d86b2b..875186ba 100644 --- a/scripts/automation/trex_control_plane/stl/examples/stl_imix.py +++ b/scripts/automation/trex_control_plane/stl/examples/stl_imix.py @@ -5,6 +5,7 @@ import time import json from pprint import pprint import argparse +import sys # IMIX test # it maps the ports to sides @@ -97,6 +98,7 @@ def imix_test (server, mult): except STLError as e: passed = False print(e) + sys.exit(1) finally: c.disconnect() -- cgit 1.2.3-korg From 0dbcda6008849a5d909371e319aa4b195cd98a9f Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Wed, 6 Jul 2016 02:10:30 +0300 Subject: regression: fix to wantFunction override --- scripts/automation/regression/trex_unit_test.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/trex_unit_test.py b/scripts/automation/regression/trex_unit_test.py index e68c5c47..4f13a50f 100755 --- a/scripts/automation/regression/trex_unit_test.py +++ b/scripts/automation/regression/trex_unit_test.py @@ -57,10 +57,7 @@ Selector.wantMethod = new_Selector_wantMethod def new_Selector_wantFunction(self, function, orig_Selector_wantFunction = Selector.wantFunction): result = orig_Selector_wantFunction(self, function) - if not CTRexScenario.test: - return result - else: - return CTRexScenario.test in getattr(function, '__name__', '') + return result and (not CTRexScenario.test or CTRexScenario.test in getattr(function, '__name__', '')) Selector.wantFunction = new_Selector_wantFunction -- cgit 1.2.3-korg From ea4e4160d8ba3b5503ca6da0ceb3acd6d33b3eac Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 6 Jul 2016 13:03:19 +0300 Subject: TUI - added left, right, end , del keys also history is updated when leaving TUI --- .../trex_control_plane/stl/console/trex_tui.py | 141 +++++++++++++++------ 1 file changed, 105 insertions(+), 36 deletions(-) (limited to 'scripts') 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 f60ee170..7b26e31e 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -643,8 +643,11 @@ class AsyncKeysEngineConsole: 'pause' : client.pause_line, 'resume': client.resume_line, 'update': client.update_line, - 'quit' : None, - 'exit' : None} + 'quit' : self.action_quit, + 'q' : self.action_quit, + 'exit' : self.action_quit, + 'help' : self.action_help, + '?' : self.action_help} # fetch readline history and add relevants for i in range(0, readline.get_current_history_length()): @@ -657,6 +660,12 @@ class AsyncKeysEngineConsole: self.line_index = 0 self.last_status = '' + def action_quit (self, _): + raise TUIQuit() + + def action_help (self, _): + return ' '.join([format_text(cmd, 'bold') for cmd in self.ac.keys()]) + def get_type (self): return self.async.MODE_CONSOLE @@ -665,25 +674,38 @@ class AsyncKeysEngineConsole: # up if seq == '\x1b[A': self.line_index = min(self.line_index + 1, len(self.lines) - 1) - return AsyncKeys.STATUS_REDRAW_KEYS # down elif seq == '\x1b[B': self.line_index = max(self.line_index - 1, 0) - return AsyncKeys.STATUS_REDRAW_KEYS - # TODO: left and right # left - #elif seq == '\x1b[D': - # self.line_index = min(self.line_index - 1, 0) - # return True + elif seq == '\x1b[D': + self.lines[self.line_index].go_left() # right - #elif seq == '\x1b[C': - # self.line_index = min(self.line_index - 1, 0) - # return True + elif seq == '\x1b[C': + self.lines[self.line_index].go_right() + + # del + elif seq == '\x1b[3~': + self.lines[self.line_index].del_key() + + # home + elif seq == '\x1b[H': + self.lines[self.line_index].home_key() + + # end + elif seq == '\x1b[F': + self.lines[self.line_index].end_key() + return True + + # unknown key + else: + return AsyncKeys.STATUS_NONE + + return AsyncKeys.STATUS_REDRAW_KEYS - return AsyncKeys.STATUS_NONE def tick (self, seq, _): @@ -705,18 +727,25 @@ class AsyncKeysEngineConsole: # backspace elif ch == '\x7f': - self.lines[self.line_index].set(self.lines[self.line_index].get()[:-1]) + self.lines[self.line_index].backspace() # TAB elif ch == '\t': cur = self.lines[self.line_index].get() + if not cur: + return + + matching_cmds = [x for x in self.ac if x.startswith(cur)] + common = os.path.commonprefix([x for x in self.ac if x.startswith(cur)]) if common: - if common in self.ac: + if len(matching_cmds) == 1: self.lines[self.line_index].set(common + ' ') + self.last_status = '' else: self.lines[self.line_index].set(common) - + self.last_status = 'ambigious: '+ ' '.join([format_text(cmd, 'bold') for cmd in matching_cmds]) + # simple char else: @@ -724,6 +753,7 @@ class AsyncKeysEngineConsole: return AsyncKeys.STATUS_REDRAW_KEYS + def split_cmd (self, cmd): s = cmd.split(' ', 1) op = s[0] @@ -736,9 +766,6 @@ class AsyncKeysEngineConsole: if not cmd: return - if cmd in ['quit', 'exit', 'q']: - raise TUIQuit() - op, param = self.split_cmd(cmd) func = self.ac.get(op) @@ -755,6 +782,7 @@ class AsyncKeysEngineConsole: # back in self.lines.appendleft(empty_line) self.line_index = 0 + readline.add_history(cmd) # back to readonly for line in self.lines: @@ -762,23 +790,28 @@ class AsyncKeysEngineConsole: assert(self.lines[0].modified == False) if not func: - self.last_status = "unknown command: '{0}'".format(cmd.split()[0]) + self.last_status = "unknown command: '{0}'".format(format_text(cmd.split()[0], 'bold')) else: - self.last_status = format_text("[OK]", 'green') if func_rc else format_text(str(func_rc).replace('\n', ''), 'red') + if isinstance(func_rc, str): + self.last_status = func_rc + else: + self.last_status = format_text("[OK]", 'green') if func_rc else format_text(str(func_rc).replace('\n', ''), 'red') def draw (self): sys.stdout.write("\nPress 'ESC' for navigation panel...\n") - sys.stdout.write("status: {0}\n".format(format_text(self.last_status, 'red'))) - sys.stdout.write("\ntui>" + self.lines[self.line_index].get()) + sys.stdout.write("status: {0}\n".format(self.last_status)) + sys.stdout.write("\ntui>") + self.lines[self.line_index].draw() # a readline alike command line - can be modified during edit class CmdLine(object): def __init__ (self, line): - self.ro_line = line - self.w_line = None - self.modified = False + self.ro_line = line + self.w_line = None + self.modified = False + self.cursor_index = len(line) def get (self): if self.modified: @@ -786,29 +819,65 @@ class CmdLine(object): else: return self.ro_line - def set (self, line): + def set (self, line, cursor_pos = None): self.w_line = line self.modified = True - def __add__ (self, other): - if self.modified: - return self.ro_line + other + if cursor_pos is None: + self.cursor_index = len(self.w_line) else: - return self.wo_line + other + self.cursor_index = cursor_pos - def __iadd__ (self, other): - if not self.modified: - self.w_line = self.ro_line - self.modified = True - self.w_line += other - return self + def __add__ (self, other): + assert(0) + def __str__ (self): return self.get() + + def __iadd__ (self, other): + + self.set(self.get()[:self.cursor_index] + other + self.get()[self.cursor_index:], + cursor_pos = self.cursor_index + len(other)) + + return self + + + def backspace (self): + if self.cursor_index == 0: + return + + self.set(self.get()[:self.cursor_index - 1] + self.get()[self.cursor_index:], + self.cursor_index - 1) + + + def del_key (self): + if self.cursor_index == len(self.get()): + return + + self.set(self.get()[:self.cursor_index] + self.get()[self.cursor_index + 1:], + self.cursor_index) + + def home_key (self): + self.cursor_index = 0 + + def end_key (self): + self.cursor_index = len(self.get()) + def invalidate (self): self.modified = False self.w_line = None + self.cursor_index = len(self.ro_line) + + def go_left (self): + self.cursor_index = max(0, self.cursor_index - 1) + def go_right (self): + self.cursor_index = min(len(self.get()), self.cursor_index + 1) + + def draw (self): + sys.stdout.write(self.get()) + sys.stdout.write('\b' * (len(self.get()) - self.cursor_index)) -- cgit 1.2.3-korg From 1ccf48c7a63d11d51ebf2fedcb80022124503355 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 6 Jul 2016 15:35:13 +0300 Subject: TUI mode (--tui) locked on legend --- .../trex_control_plane/stl/console/trex_console.py | 8 ++-- .../trex_control_plane/stl/console/trex_tui.py | 56 ++++++++++++++++------ .../stl/trex_stl_lib/utils/parsing_opts.py | 6 +++ 3 files changed, 53 insertions(+), 17 deletions(-) (limited to 'scripts') 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 7fd34e6e..9e3f2600 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -566,7 +566,8 @@ class TRexConsole(TRexGeneralCmd): parser = parsing_opts.gen_parser(self, "tui", self.do_tui.__doc__, - parsing_opts.XTERM) + parsing_opts.XTERM, + parsing_opts.LOCKED) opts = parser.parse_args(line.split()) if opts is None: @@ -589,7 +590,7 @@ class TRexConsole(TRexGeneralCmd): with self.stateless_client.logger.supress(): - self.tui.show(self.stateless_client) + self.tui.show(self.stateless_client, locked = opts.locked) def help_tui (self): @@ -871,7 +872,8 @@ def main(): # TUI if options.tui: - console.do_tui("-x" if options.xtui else "") + console.do_tui("-x" if options.xtui else "-l") + else: console.start() 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 7b26e31e..64aa60ab 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -280,13 +280,24 @@ class TrexTUILog(): print(msg) +# a predicate to wrap function as a bool +class Predicate(object): + def __init__ (self, func): + self.func = func + + def __nonzero__ (self): + return True if self.func() else False + def __bool__ (self): + return True if self.func() else False + + # Panels manager (contains server panels) class TrexTUIPanelManager(): def __init__ (self, tui): self.tui = tui self.stateless_client = tui.stateless_client self.ports = self.stateless_client.get_all_ports() - + self.locked = False self.panels = {} self.panels['dashboard'] = TrexTUIDashBoard(self) @@ -295,12 +306,16 @@ class TrexTUIPanelManager(): self.panels['ustats'] = TrexTUIUtilizationStats(self) self.key_actions = OrderedDict() - self.key_actions['q'] = {'action': self.action_none, 'legend': 'quit', 'show': True} - self.key_actions['ESC'] = {'action': self.action_none, 'legend': 'console', 'show': True} + + # we allow console only when ports are acquired + self.key_actions['ESC'] = {'action': self.action_none, 'legend': 'console', 'show': Predicate(lambda : not self.locked)} + + self.key_actions['q'] = {'action': self.action_none, 'legend': 'quit', 'show': True} self.key_actions['d'] = {'action': self.action_show_dash, 'legend': 'dashboard', 'show': True} self.key_actions['s'] = {'action': self.action_show_sstats, 'legend': 'streams', 'show': True} self.key_actions['l'] = {'action': self.action_show_lstats, 'legend': 'latency', 'show': True} self.key_actions['u'] = {'action': self.action_show_ustats, 'legend': 'util', 'show': True} + # start with dashboard self.main_panel = self.panels['dashboard'] @@ -315,7 +330,6 @@ class TrexTUIPanelManager(): self.show_log = False - def generate_legend (self): self.legend = "\n{:<12}".format("browse:") @@ -352,8 +366,9 @@ class TrexTUIPanelManager(): # on window switch or turn on / off of the TUI we call this - def init (self, show_log = False): + def init (self, show_log = False, locked = False): self.show_log = show_log + self.locked = locked self.generate_legend() def show (self, show_legend): @@ -361,6 +376,7 @@ class TrexTUIPanelManager(): self.print_connection_status() if show_legend: + self.generate_legend() self.print_legend() if self.show_log: @@ -445,16 +461,16 @@ class TrexTUI(): sys.stdout.write("\x1b[2J\x1b[H") - def show (self, client, show_log = False): - with AsyncKeys(client) as async_keys: + def show (self, client, show_log = False, locked = False): + with AsyncKeys(client, locked) as async_keys: self.async_keys = async_keys - self.show_internal(show_log) + self.show_internal(show_log, locked) - def show_internal (self, show_log = False): + def show_internal (self, show_log, locked): - self.pm.init(show_log) + self.pm.init(show_log, locked) self.state = self.STATE_ACTIVE self.draw_policer = 0 @@ -550,10 +566,17 @@ class AsyncKeys: STATUS_REDRAW_KEYS = 1 STATUS_REDRAW_ALL = 2 - def __init__ (self, client): + def __init__ (self, client, locked = False): self.engine_console = AsyncKeysEngineConsole(self, client) self.engine_legend = AsyncKeysEngineLegend(self) - self.engine = self.engine_console + self.locked = locked + + if locked: + self.engine = self.engine_legend + self.locked = True + else: + self.engine = self.engine_console + self.locked = False def __enter__ (self): # init termios @@ -594,10 +617,16 @@ class AsyncKeys: if not seq: return self.STATUS_NONE + # ESC for switch if seq == '\x1b': - self.switch() + if not self.locked: + self.switch() return self.STATUS_REDRAW_ALL + # EOF (ctrl + D) + if seq == '\x04': + raise TUIQuit() + # pass tick to engine return self.engine.tick(seq, pm) @@ -635,7 +664,6 @@ class AsyncKeysEngineLegend: class AsyncKeysEngineConsole: def __init__ (self, async, client): self.async = async - self.client = client self.lines = deque(maxlen = 100) self.ac = {'start' : client.start_line, 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 ceaf1af8..1b417d65 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 @@ -33,6 +33,7 @@ NO_PROMISCUOUS = 20 PROMISCUOUS_SWITCH = 21 TUNABLES = 22 REMOTE_FILE = 23 +LOCKED = 24 GLOBAL_STATS = 50 PORT_STATS = 51 @@ -321,6 +322,11 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'default': False, 'help': "Starts TUI in xterm window"}), + LOCKED: ArgumentPack(['-l', '--locked'], + {'action': 'store_true', + 'dest': 'locked', + 'default': False, + 'help': "Locks TUI on legend mode"}), FULL_OUTPUT: ArgumentPack(['--full'], {'action': 'store_true', -- cgit 1.2.3-korg From 198a5ae7358d139a935163d739e0343fc899538b Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 6 Jul 2016 15:55:03 +0300 Subject: TUI: crash when history is empty --- scripts/automation/trex_control_plane/stl/console/trex_tui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') 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 64aa60ab..32c6b1e0 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -804,7 +804,7 @@ class AsyncKeysEngineConsole: empty_line = self.lines.popleft() assert(empty_line.ro_line == '') - if self.lines[0].ro_line != cmd: + if not self.lines or self.lines[0].ro_line != cmd: self.lines.appendleft(CmdLine(cmd)) # back in -- cgit 1.2.3-korg From f374d4fda8db1862311355c1b189f2d1e94f15de Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Wed, 6 Jul 2016 17:25:50 +0300 Subject: increase server<->client API version from 1.2 to 1.3 various fixes --- .../trex_control_plane/stl/trex_stl_lib/trex_stl_client.py | 3 +-- scripts/master_daemon.py | 10 +++++++--- scripts/stl/udp_1pkt_src_ip_split.py | 14 +++++++++----- src/main_dpdk.cpp | 2 +- src/stateless/cp/trex_stateless.cpp | 6 +++++- 5 files changed, 23 insertions(+), 12 deletions(-) (limited to 'scripts') 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 153985ae..7c8a5fbf 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 @@ -511,8 +511,7 @@ class STLClient(object): self.connected = False # API classes - self.api_vers = [ {'type': 'core', 'major': 1, 'minor':2 } - ] + self.api_vers = [ {'type': 'core', 'major': 1, 'minor': 3 } ] self.api_h = {'core': None} # logger diff --git a/scripts/master_daemon.py b/scripts/master_daemon.py index a44f55a8..3a9d7ffe 100755 --- a/scripts/master_daemon.py +++ b/scripts/master_daemon.py @@ -221,9 +221,13 @@ if args.action != 'show': raise Exception('%s does not have function %s' % (daemon.name, args.action)) try: func() - except Exception as e: - print(termstyle.red(e)) - sys.exit(1) + except: + try: # give it another try + sleep(1) + func() + except Exception as e: + print(termstyle.red(e)) + sys.exit(1) passive = {'start': 'started', 'restart': 'restarted', 'stop': 'stopped', 'show': 'running'} diff --git a/scripts/stl/udp_1pkt_src_ip_split.py b/scripts/stl/udp_1pkt_src_ip_split.py index 48e02433..99e7496c 100644 --- a/scripts/stl/udp_1pkt_src_ip_split.py +++ b/scripts/stl/udp_1pkt_src_ip_split.py @@ -8,11 +8,15 @@ class STLS1(object): def __init__ (self): self.fsize =64; - def create_stream (self): + def create_stream (self, direction, cache_size): # Create base packet and pad it to size size = self.fsize - 4; # HW will add 4 bytes ethernet FCS + src_ip = '16.0.0.1' + dst_ip = '48.0.0.1' + if direction: + src_ip, dst_ip = dst_ip, src_ip - base_pkt = Ether()/IP(src="16.0.0.1",dst="48.0.0.1")/UDP(dport=12,sport=1025) + base_pkt = Ether()/IP(src=src_ip,dst=dst_ip)/UDP(dport=12,sport=1025) pad = max(0, size - len(base_pkt)) * 'x' @@ -22,7 +26,7 @@ class STLS1(object): STLVmFixIpv4(offset = "IP") # fix checksum ] ,split_by_field = "ip_src" # split to cores base on the tuple generator - ,cache_size =255 # the cache size + ,cache_size = cache_size # the cache size ); pkt = STLPktBuilder(pkt = base_pkt/pad, @@ -33,9 +37,9 @@ class STLS1(object): return stream - def get_streams (self, direction = 0, **kwargs): + def get_streams (self, direction = 0, cache_size = 255, **kwargs): # create 1 stream - return [ self.create_stream() ] + return [ self.create_stream(direction, cache_size) ] # dynamic load - used for trex console or simulator diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index adf460d7..1e206f27 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -677,7 +677,7 @@ static int usage(){ printf(" \n"); printf(" -k [sec] : run latency test before starting the test. it will wait for x sec sending packet and x sec after that \n"); printf(" \n"); - printf(" -w [sec] : wait between init of interfaces and sending traffic,\n"); + printf(" -w [sec] : wait between init of interfaces and sending traffic, default is 1\n"); printf(" \n"); printf(" --cfg [platform_yaml] : load and configure platform using this file see example in cfg/cfg_examplexx.yaml file \n"); diff --git a/src/stateless/cp/trex_stateless.cpp b/src/stateless/cp/trex_stateless.cpp index 698ede90..8633897e 100644 --- a/src/stateless/cp/trex_stateless.cpp +++ b/src/stateless/cp/trex_stateless.cpp @@ -54,7 +54,11 @@ TrexStateless::TrexStateless(const TrexStatelessCfg &cfg) { m_publisher = cfg.m_publisher; /* API core version */ - m_api_classes[APIClass::API_CLASS_TYPE_CORE].init(APIClass::API_CLASS_TYPE_CORE, 1, 2); + const int API_VER_MAJOR = 1; + const int API_VER_MINOR = 3; + m_api_classes[APIClass::API_CLASS_TYPE_CORE].init(APIClass::API_CLASS_TYPE_CORE, + API_VER_MAJOR, + API_VER_MINOR); } /** -- cgit 1.2.3-korg From c19193cff9413a03dd85cc9facda0c28b28d37c2 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Fri, 8 Jul 2016 15:45:22 +0300 Subject: fix multicast being counted as ierror --- .../stateless_tests/stl_examples_test.py | 2 - .../regression/stateless_tests/stl_general_test.py | 19 ------- .../stateless_tests/trex_client_pkg_test.py | 6 +-- scripts/automation/regression/trex.py | 1 - .../stf/trex_stf_lib/trex_client.py | 59 +++++++++++++--------- src/main_dpdk.cpp | 6 +-- 6 files changed, 37 insertions(+), 56 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/regression/stateless_tests/stl_examples_test.py b/scripts/automation/regression/stateless_tests/stl_examples_test.py index d8b85dfc..71fc3287 100755 --- a/scripts/automation/regression/stateless_tests/stl_examples_test.py +++ b/scripts/automation/regression/stateless_tests/stl_examples_test.py @@ -10,14 +10,12 @@ class STLExamples_Test(CStlGeneral_Test): def explicitSetUp(self): # examples connect by their own if self.is_connected(): - self.recover_after_trex_210_issue() CTRexScenario.stl_trex.disconnect() def explicitTearDown(self): # connect back at end of tests if not self.is_connected(): self.stl_trex.connect() - self.recover_after_trex_210_issue() def test_stl_examples(self): examples_dir = '../trex_control_plane/stl/examples' diff --git a/scripts/automation/regression/stateless_tests/stl_general_test.py b/scripts/automation/regression/stateless_tests/stl_general_test.py index 3a22e596..5ae2b326 100644 --- a/scripts/automation/regression/stateless_tests/stl_general_test.py +++ b/scripts/automation/regression/stateless_tests/stl_general_test.py @@ -6,10 +6,6 @@ from trex_stl_lib.api import * import time from nose.tools import nottest -def setUpModule(): - if CTRexScenario.stl_trex.is_connected(): - CStlGeneral_Test.recover_after_trex_210_issue() - class CStlGeneral_Test(CTRexGeneral_Test): """This class defines the general stateless testcase of the TRex traffic generator""" @@ -20,21 +16,6 @@ class CStlGeneral_Test(CTRexGeneral_Test): if CTRexScenario.stl_init_error: self.skip(CTRexScenario.stl_init_error) - # workaround of http://trex-tgn.cisco.com/youtrack/issue/trex-210 - @staticmethod - def recover_after_trex_210_issue(): - return - for i in range(20): - try: - stl_map_ports(CTRexScenario.stl_trex) - break - except: - CTRexScenario.stl_trex.disconnect() - time.sleep(0.5) - CTRexScenario.stl_trex.connect() - # verify problem is solved - stl_map_ports(CTRexScenario.stl_trex) - def connect(self, timeout = 100): # need delay and check only because TRex process might be still starting sys.stdout.write('Connecting') diff --git a/scripts/automation/regression/stateless_tests/trex_client_pkg_test.py b/scripts/automation/regression/stateless_tests/trex_client_pkg_test.py index 905882fe..14ef36f7 100755 --- a/scripts/automation/regression/stateless_tests/trex_client_pkg_test.py +++ b/scripts/automation/regression/stateless_tests/trex_client_pkg_test.py @@ -3,9 +3,6 @@ from .stl_general_test import CStlGeneral_Test, CTRexScenario from misc_methods import run_command from nose.plugins.attrib import attr -def setUpModule(): - CStlGeneral_Test.unzip_client_package() - @attr('client_package') class CTRexClientPKG_Test(CStlGeneral_Test): """This class tests TRex client package""" @@ -14,14 +11,13 @@ class CTRexClientPKG_Test(CStlGeneral_Test): CStlGeneral_Test.setUp(self) # examples connect by their own if CTRexScenario.stl_trex.is_connected(): - self.recover_after_trex_210_issue() CTRexScenario.stl_trex.disconnect() + CStlGeneral_Test.unzip_client_package() def tearDown(self): # connect back at end of tests if not CTRexScenario.stl_trex.is_connected(): CTRexScenario.stl_trex.connect() - self.recover_after_trex_210_issue() CStlGeneral_Test.tearDown(self) def run_client_package_stl_example(self, python_version): diff --git a/scripts/automation/regression/trex.py b/scripts/automation/regression/trex.py index 7286b166..aad8f041 100644 --- a/scripts/automation/regression/trex.py +++ b/scripts/automation/regression/trex.py @@ -38,7 +38,6 @@ class CTRexScenario: is_copied = False GAManager = None no_daemon = False - router_image = None debug_image = False test = None 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 e46a4ee3..ecf6083b 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 @@ -73,6 +73,8 @@ class CTRexClient(object): sets a verbose output on supported class method. default value : **False** + trex_args : string + additional arguments passed to TRex. For example, "-w 3 --no-watchdog" :raises: socket errors, in case server could not be reached. @@ -82,22 +84,23 @@ class CTRexClient(object): self.trex_host = socket.gethostbyname(trex_host) except: # give it another try self.trex_host = socket.gethostbyname(trex_host) - self.trex_daemon_port = trex_daemon_port - self.master_daemon_port = master_daemon_port - self.trex_zmq_port = trex_zmq_port - self.seq = None - self._last_sample = time.time() - self.__default_user = get_current_user() - self.verbose = verbose - self.result_obj = CTRexResult(max_history_size, filtered_latency_amount) - self.decoder = JSONDecoder() - self.history = jsonrpclib.history.History() - self.master_daemon_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = master_daemon_port ) - self.master_daemon = jsonrpclib.Server(self.master_daemon_path, history = self.history) - self.trex_server_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = trex_daemon_port ) - self.server = jsonrpclib.Server(self.trex_server_path, history = self.history) - self.debug_image = debug_image - self.trex_args = trex_args + self.trex_daemon_port = trex_daemon_port + self.master_daemon_port = master_daemon_port + self.trex_zmq_port = trex_zmq_port + self.seq = None + self._last_sample = time.time() + self.__default_user = get_current_user() + self.verbose = verbose + self.result_obj = CTRexResult(max_history_size, filtered_latency_amount) + self.decoder = JSONDecoder() + self.history = jsonrpclib.history.History() + self.master_daemon_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = master_daemon_port ) + self.master_daemon = jsonrpclib.Server(self.master_daemon_path, history = self.history) + self.trex_server_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = trex_daemon_port ) + self.server = jsonrpclib.Server(self.trex_server_path, history = self.history) + self.debug_image = debug_image + self.trex_args = trex_args + self.sample_to_run_finish = self.sample_until_finish # alias for legacy def add (self, x, y): @@ -579,9 +582,9 @@ class CTRexClient(object): # this could come from provided method 'condition_func' raise - def sample_to_run_finish (self, time_between_samples = 1): + def sample_until_finish (self, time_between_samples = 1): """ - Automatically sets automatically sampling of TRex data with sampling rate described by time_between_samples until TRex run finished. + Automatically samples TRex data with sampling rate described by time_between_samples until TRex run finishes. :parameters: time_between_samples : int @@ -637,12 +640,12 @@ class CTRexClient(object): """ # make sure TRex is running. raise exceptions here if any self.wait_until_kickoff_finish() - elapsed_time = 0 + end_time = time.time() + sample_time while self.is_running(): - if elapsed_time >= sample_time: + if time.time() < end_time: + time.sleep(time_between_samples) + else: return self.get_result_obj() - time.sleep(time_between_samples) - elapsed_time += time_between_samples raise UserWarning("TRex has stopped at %s seconds (before expected %s seconds)\nTry increasing test duration or decreasing sample_time" % (elapsed_time, sample_time)) def get_result_obj (self, copy_obj = True): @@ -1387,11 +1390,17 @@ class CTRexResult(object): # handle latency data if self.latency_checked: - latency_per_port = self.get_last_value("trex-latecny-v2.data", "port-") + # fix typos, by "pointer" + if 'trex-latecny-v2' in latest_dump and 'trex-latency-v2' not in latest_dump: + latest_dump['trex-latency-v2'] = latest_dump['trex-latecny-v2'] + if 'trex-latecny' in latest_dump and 'trex-latency' not in latest_dump: + latest_dump['trex-latency'] = latest_dump['trex-latecny'] + + latency_per_port = self.get_last_value("trex-latency-v2.data", "port-") self._max_latency = self.__get_filtered_max_latency(latency_per_port, self.filtered_latency_amount) - avg_latency = self.get_last_value("trex-latecny.data", "avg-") + avg_latency = self.get_last_value("trex-latency.data", "avg-") self._avg_latency = CTRexResult.__avg_all_and_rename_keys(avg_latency) - avg_win_latency_list = self.get_value_list("trex-latecny.data", "avg-") + avg_win_latency_list = self.get_value_list("trex-latency.data", "avg-") self._avg_window_latency = CTRexResult.__calc_latency_win_stats(avg_win_latency_list) tx_pkts = CTRexResult.__get_value_by_path(latest_dump, "trex-global.data.m_total_tx_pkts") diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 1e206f27..a2bfefe3 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -5542,11 +5542,10 @@ void CTRexExtendedDriverBase40G::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSta stats->f_ibytes = 0; - stats->ierrors = stats1.ierrors + stats1.imissed + stats1.ibadcrc + + stats->ierrors = stats1.imissed + stats1.ibadcrc + stats1.ibadlen + stats1.ierrors + stats1.oerrors + - stats1.imcasts + stats1.rx_nombuf + stats1.tx_pause_xon + stats1.rx_pause_xon + @@ -5616,11 +5615,10 @@ void CTRexExtendedDriverBase1GVm::get_extended_stats(CPhyEthIF * _if,CPhyEthIFSt stats->f_ibytes = 0; - stats->ierrors = stats1.ierrors + stats1.imissed + stats1.ibadcrc + + stats->ierrors = stats1.imissed + stats1.ibadcrc + stats1.ibadlen + stats1.ierrors + stats1.oerrors + - stats1.imcasts + stats1.rx_nombuf + stats1.tx_pause_xon + stats1.rx_pause_xon + -- cgit 1.2.3-korg From cc5cc5631e9df4ef0eee9c26705208dfcf035e8c Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Wed, 6 Jul 2016 11:04:52 +0300 Subject: NAT seq num randomization working version - Missing some functionality --- scripts/exp/no_tcp_syn_ack.pcap | Bin 0 -> 264 bytes scripts/exp/tcp_2_pkts.pcap | Bin 0 -> 184 bytes scripts/exp/tcp_low_ipg.pcap | Bin 0 -> 264 bytes src/bp_gtest.cpp | 72 ++++++--- src/bp_sim.cpp | 284 ++++++++++++++++++++++------------ src/bp_sim.h | 114 +++++++++----- src/common/Network/Packet/TcpHeader.h | 5 + src/latency.cpp | 17 +- src/latency.h | 34 ++-- src/main_dpdk.cpp | 5 +- src/nat_check.cpp | 78 ++++++++-- src/nat_check.h | 21 ++- 12 files changed, 438 insertions(+), 192 deletions(-) create mode 100644 scripts/exp/no_tcp_syn_ack.pcap create mode 100644 scripts/exp/tcp_2_pkts.pcap create mode 100644 scripts/exp/tcp_low_ipg.pcap (limited to 'scripts') diff --git a/scripts/exp/no_tcp_syn_ack.pcap b/scripts/exp/no_tcp_syn_ack.pcap new file mode 100644 index 00000000..0cdcf91b Binary files /dev/null and b/scripts/exp/no_tcp_syn_ack.pcap differ diff --git a/scripts/exp/tcp_2_pkts.pcap b/scripts/exp/tcp_2_pkts.pcap new file mode 100644 index 00000000..37c4f46b Binary files /dev/null and b/scripts/exp/tcp_2_pkts.pcap differ diff --git a/scripts/exp/tcp_low_ipg.pcap b/scripts/exp/tcp_low_ipg.pcap new file mode 100644 index 00000000..78fb987e Binary files /dev/null and b/scripts/exp/tcp_low_ipg.pcap differ diff --git a/src/bp_gtest.cpp b/src/bp_gtest.cpp index 79ea2458..3c6b2e40 100755 --- a/src/bp_gtest.cpp +++ b/src/bp_gtest.cpp @@ -2416,7 +2416,7 @@ public: assert(ipv4->getTimeToLive()==255); /* ip option packet */ printf(" rx got ip option packet ! \n"); - mg->handle_packet_ipv4(option,ipv4); + mg->handle_packet_ipv4(option, ipv4, true); delay(10); // delay for queue flush mg->handle_aging(); // flush the RxRing } @@ -2481,8 +2481,10 @@ protected: m_flow_info.Delete(); } public: - CCapFileFlowInfo m_flow_info; + void load_cap_file_errors_helper(std::string cap_file, enum CCapFileFlowInfo::load_cap_file_err expect); +public: + CCapFileFlowInfo m_flow_info; }; TEST_F(file_flow_info, f1) { @@ -2612,30 +2614,58 @@ TEST_F(file_flow_info, http_add_ipv6_option) { po->preview.set_ipv6_mode_enable(false); } +void file_flow_info::load_cap_file_errors_helper(std::string cap_file, enum CCapFileFlowInfo::load_cap_file_err expect) { + enum CCapFileFlowInfo::load_cap_file_err err; + + err = m_flow_info.load_cap_file(cap_file, 1, 0); + if (err == 0) err = m_flow_info.is_valid_template_load_time(); + if (err != expect) { + printf("Error in testing file %s. Expected error to be %d, but it is %d\n", cap_file.c_str(), expect, err); + } + assert (err == expect); +} + // Test error conditions when loading cap file TEST_F(file_flow_info, load_cap_file_errors) { - enum CCapFileFlowInfo::load_cap_file_err err; - CParserOption * po =&CGlobalInfo::m_options; - po->m_learn_mode = CParserOption::LEARN_MODE_TCP_ACK; + CParserOption *po = &CGlobalInfo::m_options; - // file does not exist - err = m_flow_info.load_cap_file("/tmp/not_exist",1,0); - assert (err == CCapFileFlowInfo::kFileNotExist); + po->m_learn_mode = CParserOption::LEARN_MODE_DISABLED; + load_cap_file_errors_helper("/tmp/not_exist", CCapFileFlowInfo::kFileNotExist); // file format not supported - err = m_flow_info.load_cap_file("cap2/dns.yaml",1,0); - assert (err == CCapFileFlowInfo::kFileNotExist); - // udp in tcp learn mode - err = m_flow_info.load_cap_file("./cap2/dns.pcap",1,0); - assert (err == CCapFileFlowInfo::kNoTCPFromServer); - // First TCP packet without syn - err = m_flow_info.load_cap_file("./exp/tcp_no_syn.pcap",1,0); - assert (err == CCapFileFlowInfo::kNoSyn); - // TCP flags offset is too big - err = m_flow_info.load_cap_file("./exp/many_ip_options.pcap",1,0); - assert (err == CCapFileFlowInfo::kTCPOffsetTooBig); + load_cap_file_errors_helper("cap2/dns.yaml", CCapFileFlowInfo::kFileNotExist); + load_cap_file_errors_helper("cap2/dns.pcap", CCapFileFlowInfo::kOK); + load_cap_file_errors_helper("./exp/tcp_no_syn.pcap", CCapFileFlowInfo::kOK); + load_cap_file_errors_helper("./exp/many_ip_options.pcap", CCapFileFlowInfo::kOK); // Non IP packet - err = m_flow_info.load_cap_file("./exp/bad_not_ip.pcap",1,0); - assert (err == CCapFileFlowInfo::kPktProcessFail); + load_cap_file_errors_helper("./exp/bad_not_ip.pcap", CCapFileFlowInfo::kPktProcessFail); + load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kOK); + // more than 1 flow in cap file + load_cap_file_errors_helper("./exp/syn_attack.pcap", CCapFileFlowInfo::kCapFileErr); + + po->m_learn_mode = CParserOption::LEARN_MODE_IP_OPTION; + load_cap_file_errors_helper("cap2/dns.pcap", CCapFileFlowInfo::kOK); + load_cap_file_errors_helper("./exp/tcp_no_syn.pcap", CCapFileFlowInfo::kOK); + load_cap_file_errors_helper("./exp/many_ip_options.pcap", CCapFileFlowInfo::kIPOptionNotAllowed); + load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kOK); + + po->m_learn_mode = CParserOption::LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND; + // udp in tcp learn mode + load_cap_file_errors_helper("cap2/dns.pcap", CCapFileFlowInfo::kNoTCPFromServer); + // no SYN in first packet + load_cap_file_errors_helper("./exp/tcp_no_syn.pcap", CCapFileFlowInfo::kNoSyn); + // TCP flags offset is too big. We don't allow IP option, so can comment this. + // open this if we do allow IP options in the future + // load_cap_file_errors_helper("./exp/many_ip_options.pcap", CCapFileFlowInfo::kTCPOffsetTooBig); + load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kOK); + load_cap_file_errors_helper("./exp/no_tcp_syn_ack.pcap", CCapFileFlowInfo::kOK); + + po->m_learn_mode = CParserOption::LEARN_MODE_TCP_ACK; + // too short. only two packets + load_cap_file_errors_helper("./exp/tcp_2_pkts.pcap", CCapFileFlowInfo::kTCPLearnModeBadFlow); + // no SYN+ACK + load_cap_file_errors_helper("./exp/no_tcp_syn_ack.pcap", CCapFileFlowInfo::kNoTCPSynAck); + // IPG between TCP handshake packets too low + load_cap_file_errors_helper("./exp/tcp_low_ipg.pcap", CCapFileFlowInfo::kTCPIpgTooLow); } ////////////////////////////////////////////////////////////// diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp index b93c0461..f46f2824 100755 --- a/src/bp_sim.cpp +++ b/src/bp_sim.cpp @@ -1938,42 +1938,114 @@ typedef CTmpFlowInfo * flow_tmp_t; typedef std::map flow_tmp_map_t; typedef flow_tmp_map_t::iterator flow_tmp_map_iter_t; - - -bool CCapFileFlowInfo::is_valid_template_load_time(std::string & err){ - err=""; +enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::is_valid_template_load_time(){ int i; for (i=0; im_pkt_indication; if ( lpd->getEtherOffset() !=0 ){ - err=" supported template Ether offset start is 0 \n"; - return (false); - } - if ( lpd->getIpOffset() !=14 ){ - err=" supported template ip offset is 14 \n"; - return (false); + fprintf(stderr, "Error: Bad CAP file. Ether offset start is not 0 in packet %d \n", i+1); + return kPktNotSupp; } - if ( lpd->is_ipv6() ){ - if ( lpd->getTcpOffset() != (14+40) ){ - err=" supported template tcp/udp offset is 54, no ipv6 option header is supported \n"; - return (false); + + if ( CGlobalInfo::is_learn_mode() ) { + // We change TCP ports. Need them to be in first 64 byte mbuf. + // Since we also add IP option, and rx-check feature might add another IP option, better not allow + // OP options in this mode. If needed this limitation can be refined a bit. + if ( lpd->getTcpOffset() - lpd->getIpOffset() != 20 ) { + fprintf(stderr, "Error: Bad CAP file. In learn (NAT) mode, no IP options allowed \n"); + return kIPOptionNotAllowed; } - }else{ - if ( lpd->getTcpOffset() != (14+20) ){ - err=" supported template tcp/udp offset is 34, no ipv4 option is allowed in this version \n"; - return (false); + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP)) { + if (lpd->getIpProto() != IPPROTO_TCP && !lpd->m_desc.IsInitSide()) { + fprintf(stderr, "Error: In the chosen learn mode, all packets from server to client in CAP file should be TCP.\n"); + fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); + return kNoTCPFromServer; + } } } } if ( CGlobalInfo::is_learn_mode() ) { - if ( GetPacket(0)->m_pkt_indication.m_desc.IsPluginEnable() ) { - err="plugins are not supported with --learn mode \n"; - return(false); + CPacketIndication &pkt_0_indication = GetPacket(0)->m_pkt_indication; + + if ( pkt_0_indication.m_desc.IsPluginEnable() ) { + fprintf(stderr, "Error: plugins are not supported with --learn mode \n"); + return kPlugInWithLearn; + } + + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP)) { + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { + if (Size() < 3) { + fprintf(stderr + , "Error: In the chosen learn mode, need at least the 3 TCP handshake packets.\n"); + fprintf(stderr + , " Please give different CAP file, or try different --learn-mode\n"); + return kTCPLearnModeBadFlow; + } + } + CPacketIndication &pkt_1_indication = GetPacket(1)->m_pkt_indication; + + + // verify first packet is TCP SYN from client + TCPHeader *tcp = (TCPHeader *)(pkt_0_indication.getBasePtr() + pkt_0_indication.getTcpOffset()); + if ( (! pkt_0_indication.m_desc.IsInitSide()) || (! tcp->getSynFlag()) ) { + fprintf(stderr, "Error: In the chosen learn mode, first TCP packet should be SYN from client side.\n"); + fprintf(stderr, " In cap file, first packet side direction is %s. TCP header is:\n" + , pkt_0_indication.m_desc.IsInitSide() ? "outside":"inside"); + tcp->dump(stderr); + fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); + return kNoSyn; + } + + // We want at least the TCP flags to be inside first mbuf + if (pkt_0_indication.getTcpOffset() + 14 > FIRST_PKT_SIZE) { + fprintf(stderr + , "Error: In the chosen learn mode, TCP flags offset should be less than %d, but it is %d.\n" + , FIRST_PKT_SIZE, pkt_0_indication.getTcpOffset() + 14); + fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); + return kTCPOffsetTooBig; + } + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { + // To support TCP seq randomization from server to client, we need second packet in flow to be the server SYN+ACK + bool error = false; + if (pkt_1_indication.getIpProto() != IPPROTO_TCP) { + error = true; + } else { + TCPHeader *tcp = (TCPHeader *)(pkt_1_indication.getBasePtr() + pkt_1_indication.getTcpOffset()); + if ( (! tcp->getSynFlag()) || (! tcp->getAckFlag()) || ( pkt_1_indication.m_desc.IsInitSide())) { + error = true; + } + } + if (error) { + fprintf(stderr, "Error: In the chosen learn mode, second packet should be SYN+ACK from server.\n"); + fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); + return kNoTCPSynAck; + } + + CPacketIndication &pkt_2_indication = GetPacket(2)->m_pkt_indication; + if ( (! pkt_2_indication.m_desc.IsInitSide()) ) { + fprintf(stderr + , "Error: Wrong third packet. In the chosen learn mode, need at least the 3 TCP handshake packets.\n"); + fprintf(stderr + , " Please give different CAP file, or try different --learn-mode\n"); + return kTCPLearnModeBadFlow; + } + if ((pkt_0_indication.m_cap_ipg < LEARN_MODE_MIN_IPG / 1000) || (pkt_1_indication.m_cap_ipg < LEARN_MODE_MIN_IPG / 1000)) { + fprintf(stderr + , "Error: Bad cap file timings. In the chosen learn mode"); + fprintf(stderr, "IPG between TCP handshake packets should be at least %d msec.\n", LEARN_MODE_MIN_IPG); + fprintf(stderr, " Current delay is %f between second and first, %f between third and second" + , pkt_0_indication.m_cap_ipg, pkt_1_indication.m_cap_ipg); + fprintf(stderr + , " Please give different CAP file, try different --learn-mode, or edit ipg parameters in template file\n"); + return kTCPIpgTooLow; + } + } } } - return(true); + + return(kOK); } @@ -2061,6 +2133,13 @@ void CCapFileFlowInfo::update_info(){ if ( lp->m_pkt_indication.m_desc.IsBiDirectionalFlow() ){ lp->mask_as_learn(); } + + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { + // In this mode, we need to see the SYN+ACK as well. + lp = GetPacket(1); + assert(lp); + lp->m_pkt_indication.setTTL(TTL_RESERVE_DUPLICATE); + } } if ( ft.empty() ) @@ -2100,7 +2179,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st m_total_errors=0; CFlow * first_flow=0; bool first_flow_fif_is_swap=false; - bool time_was_set=false; double last_time=0.0; CCapPktRaw raw_packet; @@ -2175,35 +2253,10 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st m_total_errors++; } } - - if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { - // in this mode, first TCP packet must be SYN from client. - if (pkt_indication.getIpProto() == IPPROTO_TCP) { - TCPHeader *tcp = (TCPHeader *)(pkt_indication.getBasePtr() + pkt_indication.getTcpOffset()); - if ( (! pkt_indication.m_desc.IsInitSide()) || (! tcp->getSynFlag()) ) { - fprintf(stderr, "Error: In the chosen learn mode, first TCP packet should be SYN from client side.\n"); - fprintf(stderr, " In cap file, first packet side direction is %s. TCP header is:\n", pkt_indication.m_desc.IsInitSide() ? "outside":"inside"); - tcp->dump(stderr); - fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); - - return kNoSyn; - } - // We want at least the TCP flags to be inside first mbuf - if (pkt_indication.getTcpOffset() + 14 > FIRST_PKT_SIZE) { - fprintf(stderr, "Error: In the chosen learn mode, first TCP packet TCP flags offset should be less than %d, but it is %d.\n" - , FIRST_PKT_SIZE, pkt_indication.getTcpOffset() + 14); - fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); - return kTCPOffsetTooBig; - } - } - } - }else{ /* no FIF */ - pkt_indication.m_desc.SetFlowId(lpflow->flow_id); if ( multi_flow_enable ==false ){ - if (lpflow == first_flow) { // add to bool init_side= @@ -2225,16 +2278,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st } } - - if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { - // This test must be down here, after initializing init side indication - if (pkt_indication.getIpProto() != IPPROTO_TCP && !pkt_indication.m_desc.IsInitSide()) { - fprintf(stderr, "Error: In the chosen learn mode, all packets from server to client in CAP file should be TCP.\n"); - fprintf(stderr, " Please give different CAP file, or try different --learn-mode\n"); - return kNoTCPFromServer; - } - } - }else{ fprintf(stderr, "ERROR packet %d is not supported, should be Ethernet/IP(0x0800)/(TCP|UDP) format try to convert it using Wireshark !\n",cnt); return kPktNotSupp; @@ -2245,7 +2288,6 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st } } - /* set the last */ CFlowPktInfo * last_pkt =GetPacket((uint32_t)(Size()-1)); last_pkt->m_pkt_indication.m_desc.SetIsLastPkt(true); @@ -2261,6 +2303,7 @@ enum CCapFileFlowInfo::load_cap_file_err CCapFileFlowInfo::load_cap_file(std::st + printf("%d: IPG:%f", i, lp_prev->m_pkt_indication.m_cap_ipg); //??? remove if ( lp->m_pkt_indication.m_desc.IsInitSide() != lp_prev->m_pkt_indication.m_desc.IsInitSide()) { lp_prev->m_pkt_indication.m_desc.SetRtt(true); @@ -3122,11 +3165,8 @@ bool CFlowGeneratorRec::Create(CFlowYamlInfo * info, int res=m_flow_info.load_cap_file(info->m_name.c_str(),_id,m_info->m_plugin_id); if ( res==0 ) { fixup_ipg_if_needed(); - std::string err; - /* verify that template are valid */ - bool is_valid=m_flow_info.is_valid_template_load_time(err); - if (!is_valid) { - printf("\n ERROR template file is not valid '%s' \n",err.c_str()); + + if (m_flow_info.is_valid_template_load_time() != 0) { return (false); } m_flow_info.update_info(); @@ -3700,11 +3740,11 @@ inline int CNodeGenerator::teardown(CFlowGenListPerThread * thread, } return (0); } - + template -inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, +inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, dsec_t d_time, bool always, CFlowGenListPerThread * thread, @@ -3723,9 +3763,9 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, sch_state_t state = scINIT; node = m_p_queue.top(); - n_time = node->m_time + offset; + n_time = node->m_time + offset; cur_time = now_sec(); - + while (state!=scTERMINATE) { switch (state) { @@ -3751,7 +3791,7 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, break; } node = m_p_queue.top(); - n_time = node->m_time + offset; + n_time = node->m_time + offset; if ((n_time-cur_time)>EAT_WINDOW_DTIME) { state=scINIT; @@ -3761,7 +3801,7 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, break; case scWAIT: - do_sleep(cur_time,thread,n_time); // estimate loop + do_sleep(cur_time,thread,n_time); // estimate loop state=scWORK; break; default: @@ -3772,7 +3812,7 @@ inline int CNodeGenerator::flush_file_realtime(dsec_t max_time, return (teardown(thread,always,old_offset,offset)); } -FORCE_NO_INLINE int CNodeGenerator::flush_file_sim(dsec_t max_time, +FORCE_NO_INLINE int CNodeGenerator::flush_file_sim(dsec_t max_time, dsec_t d_time, bool always, CFlowGenListPerThread * thread, @@ -3799,7 +3839,7 @@ FORCE_NO_INLINE int CNodeGenerator::flush_file_sim(dsec_t max_time, return (teardown(thread,always,old_offset,0)); } -int CNodeGenerator::flush_file(dsec_t max_time, +int CNodeGenerator::flush_file(dsec_t max_time, dsec_t d_time, bool always, CFlowGenListPerThread * thread, @@ -3820,7 +3860,7 @@ int CNodeGenerator::flush_file(dsec_t max_time, void CNodeGenerator::handle_flow_pkt(CGenNode *node, CFlowGenListPerThread *thread) { - /*repeat and NAT is not supported */ + /*repeat and NAT is not supported together */ if ( node->is_nat_first_state() ) { node->set_nat_wait_state(); flush_one_node_to_file(node); @@ -3831,7 +3871,7 @@ void CNodeGenerator::handle_flow_pkt(CGenNode *node, CFlowGenListPerThread *thre if ( node->is_nat_wait_state() ) { if (node->is_responder_pkt()) { m_p_queue.pop(); - /* time out, need to free the flow and remove the association , we didn't get convertion yet*/ + /* time out, need to free the flow and remove the association , we didn't get conversion yet*/ thread->terminate_nat_flows(node); return; @@ -3842,7 +3882,22 @@ void CNodeGenerator::handle_flow_pkt(CGenNode *node, CFlowGenListPerThread *thre #endif } } else { - assert(0); + if ( node->is_nat_wait_ack_state() ) { + if (node->is_initiator_pkt()) { + m_p_queue.pop(); + /* time out, need to free the flow and remove the association , we didn't get conversion yet*/ + thread->terminate_nat_flows(node); + return; + + } else { + flush_one_node_to_file(node); +#ifdef _DEBUG + update_stats(node); +#endif + } + } else { + assert(0); + } } } m_p_queue.pop(); @@ -4146,38 +4201,72 @@ void CFlowGenListPerThread::handle_latency_pkt_msg(CGenNodeLatencyPktInfo * msg) m_node_gen.m_v_if->send_one_pkt((pkt_dir_t)msg->m_dir,msg->m_pkt); } - void CFlowGenListPerThread::handle_nat_msg(CGenNodeNatInfo * msg){ int i; + bool first = true, second = true; + for (i=0; im_cnt; i++) { + first = true; + second = true; CNatFlowInfo * nat_msg=&msg->m_data[i]; CGenNode * node=m_flow_id_to_node_lookup.lookup(nat_msg->m_fid); if (!node) { - /* this should be move to a notification module */ - #ifdef NAT_TRACE_ + /* this should be moved to a notification module */ +#ifdef NAT_TRACE_ printf(" ERORR not valid flow_id %d probably flow was aged \n",nat_msg->m_fid); - #endif +#endif m_stats.m_nat_lookup_no_flow_id++; continue; } - #ifdef NAT_TRACE_ - printf(" %.03f RX :set node %p:%x %x:%x:%x \n",now_sec() ,node,nat_msg->m_fid,nat_msg->m_external_ip,nat_msg->m_external_ip_server,nat_msg->m_external_port); - #endif - node->set_nat_ipv4_addr(nat_msg->m_external_ip); - node->set_nat_ipv4_port(nat_msg->m_external_port); - node->set_nat_ipv4_addr_server(nat_msg->m_external_ip_server); - - assert(node->is_nat_wait_state()); - if ( CGlobalInfo::is_learn_verify_mode() ){ - if (!node->is_external_is_eq_to_internal_ip() ){ - m_stats.m_nat_flow_learn_error++; + + // Calculate diff between tcp seq of SYN packet, and TCP ack of SYN+ACK packet + // For supporting firewalls who do TCP seq num randomization + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP)) { + if (node->is_nat_wait_state()) { + char *syn_pkt = node->m_flow_info->GetPacket(0)->m_packet->raw; + TCPHeader *tcp = (TCPHeader *)(syn_pkt + node->m_pkt_info->m_pkt_indication.getFastTcpOffset()); + node->set_nat_tcp_seq_diff_client(nat_msg->m_tcp_seq - tcp->getSeqNumber()); + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { + node->set_nat_wait_ack_state(); + second = false; + } else { + node->set_nat_learn_state(); + } + } else { + char *syn_ack_pkt = node->m_flow_info->GetPacket(1)->m_packet->raw; + TCPHeader *tcp = (TCPHeader *)(syn_ack_pkt + node->m_pkt_info->m_pkt_indication.getFastTcpOffset()); + node->set_nat_tcp_seq_diff_server(nat_msg->m_tcp_seq - tcp->getSeqNumber()); + assert(node->is_nat_wait_ack_state()); + node->set_nat_learn_state(); + first = false; } + } else { + assert(node->is_nat_wait_state()); + node->set_nat_learn_state(); } - node->set_nat_learn_state(); - /* remove from the hash */ - m_flow_id_to_node_lookup.remove_no_lookup(nat_msg->m_fid); - m_stats.m_nat_lookup_remove_flow_id++; + if (first) { +#ifdef NAT_TRACE_ + printf(" %.03f RX :set node %p:%x %x:%x TCP diff %x\n" + , now_sec(), node,nat_msg->m_fid, nat_msg->m_external_ip, nat_msg->m_external_port + , node->get_nat_tcp_seq_diff_client()); +#endif + + node->set_nat_ipv4_addr(nat_msg->m_external_ip); + node->set_nat_ipv4_port(nat_msg->m_external_port); + + if ( CGlobalInfo::is_learn_verify_mode() ){ + if (!node->is_external_is_eq_to_internal_ip() ){ + m_stats.m_nat_flow_learn_error++; + } + } + } + + if (second) { + /* remove from the hash */ + m_flow_id_to_node_lookup.remove_no_lookup(nat_msg->m_fid); + m_stats.m_nat_lookup_remove_flow_id++; + } } } @@ -4273,7 +4362,7 @@ void CFlowGenListPerThread::start_generate_stateful(std::string erf_file_name, fprintf(stderr," nothing to generate no template loaded \n"); return; } - + m_preview_mode = preview; m_node_gen.open_file(erf_file_name,&m_preview_mode); dsec_t d_time_flow=get_delta_flow_is_sec(); @@ -5055,7 +5144,7 @@ void CErfIF::apply_client_config(const ClientCfg *cfg, pkt_dir_t dir) { /* VLAN */ if (cfg_dir.has_vlan()) { add_vlan(cfg_dir.get_vlan()); - } + } } int CErfIF::send_node(CGenNode *node){ @@ -6422,7 +6511,7 @@ void CGenNodeBase::free_base(){ CGenNodePCAP *p = (CGenNodePCAP *)this; p->destroy(); return; - } + } if ( m_type == COMMAND ) { CGenNodeCommand* p=(CGenNodeCommand*)this; @@ -6430,4 +6519,3 @@ void CGenNodeBase::free_base(){ } } - diff --git a/src/bp_sim.h b/src/bp_sim.h index d2812688..49e0e8dc 100755 --- a/src/bp_sim.h +++ b/src/bp_sim.h @@ -65,8 +65,6 @@ limitations under the License. class CGenNodePCAP; -#undef NAT_TRACE_ - #define FORCE_NO_INLINE __attribute__ ((noinline)) #define FORCE_INLINE __attribute__((always_inline)) @@ -84,10 +82,6 @@ typedef struct { */ #define INET_PORTSTRLEN 5 - - - - /* VM commands */ class CMiniVMCmdBase { @@ -757,7 +751,10 @@ public: LEARN_MODE_DISABLED=0, LEARN_MODE_TCP_ACK=1, LEARN_MODE_IP_OPTION=2, - LEARN_MODE_MAX=LEARN_MODE_IP_OPTION + LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND=3, + LEARN_MODE_MAX=LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND, + // This is used to check if 1 or 3 exist + LEARN_MODE_TCP=100 }; public: @@ -1246,7 +1243,11 @@ public: } static inline bool is_learn_mode(CParserOption::trex_learn_mode_e mode){ - return ( (m_options.m_learn_mode == mode)); + if (mode == CParserOption::LEARN_MODE_TCP) { + return ((m_options.m_learn_mode == CParserOption::LEARN_MODE_TCP_ACK_NO_SERVER_SEQ_RAND) + || (m_options.m_learn_mode == CParserOption::LEARN_MODE_TCP_ACK)); + } else + return (m_options.m_learn_mode == mode); } static inline bool is_ipv6_enable(void){ @@ -1558,11 +1559,11 @@ public: CTupleGeneratorSmart *m_tuple_gen; // cache line 1 - 64bytes waste of space ! - uint32_t m_nat_external_ipv4; /* client */ - uint32_t m_nat_external_ipv4_server; - uint16_t m_nat_external_port; - - uint16_t m_nat_pad[3]; + uint32_t m_nat_external_ipv4; // NAT client IP + uint32_t m_nat_tcp_seq_diff_client; // support for firewalls that do TCP seq num randomization + uint32_t m_nat_tcp_seq_diff_server; // And some do seq num randomization for server->client also + uint16_t m_nat_external_port; // NAT client port + uint16_t m_nat_pad[1]; const ClientCfg *m_client_cfg; uint32_t m_src_idx; uint32_t m_dest_idx; @@ -1699,6 +1700,15 @@ public: return (btGetMaskBit16(m_flags,4,3)==2?true:false) ; } + // We saw first TCP SYN. Waiting for SYN+ACK + inline void set_nat_wait_ack_state() { + btSetMaskBit16(m_flags, 4, 3, 3); + } + + inline bool is_nat_wait_ack_state(){ + return (btGetMaskBit16(m_flags,4,3) == 3) ? true : false; + } + inline void set_nat_learn_state(){ m_type=FLOW_PKT; /* normal operation .. repeat might work too */ } @@ -1712,14 +1722,21 @@ public: return (m_thread_id); } - inline void set_nat_ipv4_addr_server(uint32_t ip){ - m_nat_external_ipv4_server =ip; + inline void set_nat_tcp_seq_diff_client(uint32_t diff) { + m_nat_tcp_seq_diff_client = diff; + } + + inline uint32_t get_nat_tcp_seq_diff_client() { + return m_nat_tcp_seq_diff_client; } - inline uint32_t get_nat_ipv4_addr_server(){ - return ( m_nat_external_ipv4_server ); + inline void set_nat_tcp_seq_diff_server(uint32_t diff) { + m_nat_tcp_seq_diff_server = diff; } + inline uint32_t get_nat_tcp_seq_diff_server() { + return m_nat_tcp_seq_diff_server; + } inline void set_nat_ipv4_addr(uint32_t ip){ m_nat_external_ipv4 =ip; @@ -1740,8 +1757,7 @@ public: bool is_external_is_eq_to_internal_ip(){ /* this API is used to check TRex itself */ if ( (get_nat_ipv4_addr() == m_src_ip ) && - (get_nat_ipv4_port()==m_src_port) && - ( get_nat_ipv4_addr_server() == m_dest_ip) ) { + (get_nat_ipv4_port()==m_src_port)) { return (true); }else{ return (false); @@ -3004,6 +3020,8 @@ inline void CFlowPktInfo::update_pkt_info(char *p, (void)et; uint16_t src_port = node->m_src_port; + uint32_t tcp_seq_diff_client = 0; + uint32_t tcp_seq_diff_server = 0; pkt_dir_t ip_dir = node->cur_pkt_ip_addr_dir(); pkt_dir_t port_dir = node->cur_pkt_port_addr_dir(); @@ -3024,7 +3042,6 @@ inline void CFlowPktInfo::update_pkt_info(char *p, }else{ if ( unlikely ( CGlobalInfo::is_learn_mode() ) ){ - if (m_pkt_indication.m_desc.IsLearn()) { /* might be done twice */ #ifdef NAT_TRACE_ @@ -3033,42 +3050,48 @@ inline void CFlowPktInfo::update_pkt_info(char *p, ipv4->setTimeToLive(TTL_RESERVE_DUPLICATE); /* first ipv4 option add the info in case of learn packet, usualy only the first packet */ - if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_IP_OPTION)) { - CNatOption *lpNat =(CNatOption *)ipv4->getOption(); - lpNat->set_fid(node->get_short_fid()); - lpNat->set_thread_id(node->get_thread_id()); - } else { - // This method only work on first TCP SYN - if (ipv4->getProtocol() == IPPROTO_TCP) { - TCPHeader *tcp = (TCPHeader *)(((uint8_t *)ipv4) + ipv4->getHeaderLength()); - if (tcp->getSynFlag()) { - tcp->setAckNumber(CNatRxManager::calc_tcp_ack_val(node->get_short_fid(), node->get_thread_id())); - } + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_IP_OPTION)) { + CNatOption *lpNat =(CNatOption *)ipv4->getOption(); + lpNat->set_fid(node->get_short_fid()); + lpNat->set_thread_id(node->get_thread_id()); + } else { + // This method only work on first TCP SYN + if (ipv4->getProtocol() == IPPROTO_TCP) { + TCPHeader *tcp = (TCPHeader *)(((uint8_t *)ipv4) + ipv4->getHeaderLength()); + if (tcp->getSynFlag()) { + tcp->setAckNumber(CNatRxManager::calc_tcp_ack_val(node->get_short_fid(), node->get_thread_id())); + } #ifdef NAT_TRACE_ - printf(" %.3f : flow_id: %x thread_id %x TCP ack %x\n",now_sec(), node->get_short_fid(), node->get_thread_id(), tcp->getAckNumber()); + printf(" %.3f : flow_id: %x thread_id %x TCP ack %x seq %x\n" + ,now_sec(), node->get_short_fid(), node->get_thread_id(), tcp->getAckNumber() + , tcp->getSeqNumber()); #endif - } - } + } + } } /* in all cases update the ip using the outside ip */ if ( m_pkt_indication.m_desc.IsInitSide() ) { #ifdef NAT_TRACE_ if (node->m_flags != CGenNode::NODE_FLAGS_LATENCY ) { - printf(" %.3f : DP : i %x:%x -> %x flow_id: %lx\n",now_sec(),node->m_src_ip,node->m_src_port,node->m_dest_ip,node->m_flow_id); + printf(" %.3f : DP : i %x:%x -> %x flow_id: %lx\n",now_sec(), node->m_src_ip + , node->m_src_port, node->m_dest_ip, node->m_flow_id); } #endif + tcp_seq_diff_server = node->get_nat_tcp_seq_diff_server(); ipv4->updateIpSrc(node->m_src_ip); ipv4->updateIpDst(node->m_dest_ip); - }else{ + } else { #ifdef NAT_TRACE_ if (node->m_flags != CGenNode::NODE_FLAGS_LATENCY ) { - printf(" %.3f : r %x -> %x:%x flow_id: %lx \n",now_sec(),node->m_dest_ip,node->m_src_ip,node->m_src_port,node->m_flow_id); + printf(" %.3f : r %x -> %x:%x flow_id: %lx \n", now_sec(), node->m_dest_ip + , node->m_src_ip, node->m_src_port, node->m_flow_id); } #endif src_port = node->get_nat_ipv4_port(); - ipv4->updateIpSrc(node->get_nat_ipv4_addr_server()); + tcp_seq_diff_client = node->get_nat_tcp_seq_diff_client(); + ipv4->updateIpSrc(node->m_dest_ip); ipv4->updateIpDst(node->get_nat_ipv4_addr()); } @@ -3076,7 +3099,7 @@ inline void CFlowPktInfo::update_pkt_info(char *p, #ifdef NAT_TRACE_ if (node->m_flags != CGenNode::NODE_FLAGS_LATENCY ) { if ( m_pkt_indication.m_desc.IsInitSide() ==false ){ - printf(" %.3f : pkt ==> %x:%x %x:%x \n",now_sec(),node->get_nat_ipv4_addr(),node->get_nat_ipv4_addr_server(), + printf(" %.3f : pkt ==> %x %x:%x \n",now_sec(),node->get_nat_ipv4_addr(), node->get_nat_ipv4_port(),node->m_src_port); }else{ printf(" %.3f : pkt ==> init pkt sent \n",now_sec()); @@ -3116,8 +3139,10 @@ inline void CFlowPktInfo::update_pkt_info(char *p, /* replace port */ if ( port_dir == CLIENT_SIDE ) { m_tcp->setSourcePort(src_port); + m_tcp->setAckNumber(m_tcp->getAckNumber() + tcp_seq_diff_server); }else{ m_tcp->setDestPort(src_port); + m_tcp->setAckNumber(m_tcp->getAckNumber() + tcp_seq_diff_client); } }else { if ( m_pkt_indication.m_desc.IsUdp() ){ @@ -3352,6 +3377,8 @@ public: class CCapFileFlowInfo { public: + const int LEARN_MODE_MIN_IPG = 10; // msec + enum load_cap_file_err { kOK = 0, kFileNotExist, @@ -3359,9 +3386,14 @@ public: kNoSyn, kTCPOffsetTooBig, kNoTCPFromServer, + kNoTCPSynAck, + kTCPLearnModeBadFlow, kPktNotSupp, kPktProcessFail, - kCapFileErr + kCapFileErr, + kPlugInWithLearn, + kIPOptionNotAllowed, + kTCPIpgTooLow }; bool Create(); @@ -3378,7 +3410,7 @@ public: /* update flow info */ void update_info(); - bool is_valid_template_load_time(std::string & err); + enum CCapFileFlowInfo::load_cap_file_err is_valid_template_load_time(); void save_to_erf(std::string cap_file_name,int pcap); diff --git a/src/common/Network/Packet/TcpHeader.h b/src/common/Network/Packet/TcpHeader.h index c19cd262..97575a60 100755 --- a/src/common/Network/Packet/TcpHeader.h +++ b/src/common/Network/Packet/TcpHeader.h @@ -23,6 +23,11 @@ class TCPHeader { public: + enum TCPHeader_enum_t + { + TCP_INVALID_PORT = 0 + }; + TCPHeader(){} TCPHeader(uint16_t argSourcePort, diff --git a/src/latency.cpp b/src/latency.cpp index 841913cf..b9ce2177 100644 --- a/src/latency.cpp +++ b/src/latency.cpp @@ -387,6 +387,12 @@ bool CCPortLatency::check_packet(rte_mbuf_t * m,CRx_check_header * & rx_p) { if ( ! is_lateancy_pkt) { +#if 0 + TCPHeader *tcp = (TCPHeader *)parser.m_l4; //????? remove + if (parser.m_ipv4->getProtocol() == 0x6 && tcp->getSynFlag()) { + tcp->dump(stdout); //???? remove + } +#endif #ifdef NAT_TRACE_ printf(" %.3f RX : got packet !!! \n",now_sec() ); #endif @@ -436,7 +442,7 @@ bool CCPortLatency::check_packet(rte_mbuf_t * m,CRx_check_header * & rx_p) { m_no_ipv4_option++; return (false); } - m_parent->get_nat_manager()->handle_packet_ipv4(lp,parser.m_ipv4); + m_parent->get_nat_manager()->handle_packet_ipv4(lp, parser.m_ipv4, true); opt_len -= CNatOption::noOPTION_LEN; opt_ptr += CNatOption::noOPTION_LEN; break; @@ -445,10 +451,11 @@ bool CCPortLatency::check_packet(rte_mbuf_t * m,CRx_check_header * & rx_p) { return (false); } // End of switch } // End of while - if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK) - && parser.IsNatInfoPkt()) { - m_parent->get_nat_manager()->handle_packet_ipv4(NULL, parser.m_ipv4); - } + + bool first; + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP) && parser.IsNatInfoPkt(first)) { + m_parent->get_nat_manager()->handle_packet_ipv4(NULL, parser.m_ipv4, first); + } return (true); } // End of check for non-latency packet diff --git a/src/latency.h b/src/latency.h index 724621f0..552b3999 100644 --- a/src/latency.h +++ b/src/latency.h @@ -107,19 +107,27 @@ public: } // Check if this packet contains NAT info in TCP ack - inline bool IsNatInfoPkt() { - if (!m_ipv4 || (m_protocol != IPPROTO_TCP)) { - return false; - } - if (! m_l4 || (m_l4 - rte_pktmbuf_mtod(m_m, uint8_t*) + TCP_HEADER_LEN) > m_m->data_len) { - return false; - } - // If we are here, relevant fields from tcp header are guaranteed to be in first mbuf - TCPHeader *tcp = (TCPHeader *)m_l4; - if (!tcp->getSynFlag() || (tcp->getAckNumber() == 0)) { - return false; - } - return true; + // first - set to true if this is the first packet of the flow. false otherwise. + // relevant only if return value is true + inline bool IsNatInfoPkt(bool &first) { + if (!m_ipv4 || (m_protocol != IPPROTO_TCP)) { + return false; + } + if (! m_l4 || (m_l4 - rte_pktmbuf_mtod(m_m, uint8_t*) + TCP_HEADER_LEN) > m_m->data_len) { + return false; + } + // If we are here, relevant fields from tcp header are guaranteed to be in first mbuf + // We want to handle SYN and SYN+ACK packets + TCPHeader *tcp = (TCPHeader *)m_l4; + if (! tcp->getSynFlag()) + return false; + + if (! tcp->getAckFlag()) { + first = true; + } else { + first = false; + } + return true; } public: diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index a2bfefe3..d5e7c9b5 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -687,9 +687,10 @@ static int usage(){ printf(" --ipv6 : work in ipv6 mode\n"); printf(" --learn (deprecated). Replaced by --learn-mode. To get older behaviour, use --learn-mode 2\n"); - printf(" --learn-mode [1-2] : Work in NAT environments, learn the dynamic NAT translation and ALG \n"); - printf(" 1 Use TCP ACK in first SYN to pass NAT translation information. Will work only for TCP streams. Initial SYN packet must be present in stream.\n"); + printf(" --learn-mode [1-3] : Work in NAT environments, learn the dynamic NAT translation and ALG \n"); + printf(" 1 Use TCP ACK in first SYN to pass NAT translation information. Will work only for TCP streams. Initial SYN packet must be first packet in stream.\n"); printf(" 2 Add special IP option to pass NAT translation information. Will not work on certain firewalls if they drop packets with IP options\n"); + printf(" 3 Like 1, but without support for sequence number randomization in server->clien direction. Performance (flow/second) better than 1\n"); printf(" --learn-verify : Learn the translation, but intended for verification of the mechanism in cases that NAT does not exist \n"); printf(" \n"); printf(" --l-pkt-mode [0-3] : Set mode for sending latency packets.\n"); diff --git a/src/nat_check.cpp b/src/nat_check.cpp index 7e224430..c7262e50 100755 --- a/src/nat_check.cpp +++ b/src/nat_check.cpp @@ -41,7 +41,8 @@ void CGenNodeNatInfo::dump(FILE *fd){ int i; for (i=0; im_external_ip,lp->m_external_ip_server,lp->m_external_port,lp->m_fid); + fprintf (fd," id:%d , external ip:%08x , ex_port: %04x , TCP seq:%x fid: %d \n" + , i, lp->m_external_ip, lp->m_external_port, lp->m_tcp_seq, lp->m_fid); } } @@ -51,6 +52,28 @@ void CGenNodeNatInfo::init(){ m_cnt=0; } +bool CNatCheckFlowTableMap::find(uint64_t key, uint32 &val) { + nat_check_flow_map_t::iterator iter; + iter = m_map.find(key); + if (iter != m_map.end() ) { + val = (*iter).second; + return true; + }else{ + return false; + } +} + +void CNatCheckFlowTableMap::dump(FILE *fd) { + nat_check_flow_map_iter_t it; + uint32_t val; + uint64_t key; + + for (it = m_map.begin(); it != m_map.end(); it++) { + val = it->second; + key = it->first; + fprintf(fd, "%lx->%x\n", key, val); + } +} void CNatStats::reset(){ m_total_rx=0; @@ -147,14 +170,17 @@ void CNatRxManager::get_info_from_tcp_ack(uint32_t tcp_ack, uint32_t &fid, uint8 * option - pointer to our proprietary NAT info IP option. * If it is NULL, the NAT info is in the TCP ACK number * ipv4 - pointer to ipv4 header to extract info from. + * is_first - Is this the first packet of the flow or second. To handle firewalls that do + * TCP seq randomization on the server->client side, we also look at the second + * packet of the flow (SYN+ACK), and extract its seq num. */ -void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4) { +void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4, bool is_first) { CNatPerThreadInfo * thread_info; uint32_t fid=0; + uint32_t tcp_seq; /* Extract info from the packet ! */ uint32_t ext_ip = ipv4->getSourceIp(); - uint32_t ext_ip_server = ipv4->getDestIp(); uint8_t proto = ipv4->getProtocol(); /* must be TCP/UDP this is the only supported proto */ if (!( (proto==6) || (proto==17) )){ @@ -165,21 +191,49 @@ void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4) { TCPHeader *tcp = (TCPHeader *) (((char *)ipv4)+ ipv4->getHeaderLength()); uint16_t ext_port = tcp->getSourcePort(); + tcp_seq = tcp->getSeqNumber(); + if (option) { thread_info = get_thread_info(option->get_thread_id()); fid = option->get_fid(); } else { - uint8_t thread_id; - get_info_from_tcp_ack(tcp->getAckNumber(), fid, thread_id); - thread_info = get_thread_info(thread_id); + uint8_t thread_id; + + if (is_first) { + uint32_t tcp_ack = tcp->getAckNumber(); + get_info_from_tcp_ack(tcp_ack, fid, thread_id); + thread_info = get_thread_info(thread_id); + if (CGlobalInfo::is_learn_mode(CParserOption::LEARN_MODE_TCP_ACK)) { + uint32_t dst_ip = ipv4->getDestIp(); + uint16_t dst_port = tcp->getDestPort(); + uint64_t map_key = (dst_ip << 16) + dst_port; + m_fm.insert(map_key, tcp_ack); + } + } else { + uint32_t val; + // server->client packet. IP/port reversed in regard to first SYN packet + uint64_t map_key = (ext_ip << 16) + ext_port; + + if (m_fm.find(map_key, val)) { + get_info_from_tcp_ack(val, fid, thread_id); + thread_info = get_thread_info(thread_id); + m_fm.erase(map_key); + } else { + thread_info = 0; + // ??? Handle error + // ??? handle aging of flow info + } + } } + if (unlikely(!thread_info)) { return; } #ifdef NAT_TRACE_ - printf("rx msg ext ip : %08x:%08x ext port : %04x flow_id : %d \n",ext_ip,ext_ip_server,ext_port, fid); + printf("rx msg ext ip: %08x ext port: %04x TCP Seq: %08x flow_id : %d (%s) \n", ext_ip, ext_port, tcp_seq, fid + , is_first ? "first":"second"); #endif CGenNodeNatInfo * node=thread_info->m_cur_nat_msg; @@ -194,9 +248,13 @@ void CNatRxManager::handle_packet_ipv4(CNatOption *option, IPHeader *ipv4) { CNatFlowInfo * msg=node->get_next_msg(); /* fill the message */ - msg->m_external_ip = ext_ip; - msg->m_external_ip_server = ext_ip_server; - msg->m_external_port = ext_port; + if (is_first) { + msg->m_external_ip = ext_ip; + msg->m_external_port = ext_port; + } else { + msg->m_external_port = TCPHeader::TCP_INVALID_PORT; + } + msg->m_tcp_seq = tcp_seq; msg->m_fid = fid; msg->m_pad = 0xee; diff --git a/src/nat_check.h b/src/nat_check.h index 133501ae..3b526c0b 100755 --- a/src/nat_check.h +++ b/src/nat_check.h @@ -21,6 +21,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +#include #include "msg_manager.h" #include #include @@ -121,7 +122,7 @@ private: struct CNatFlowInfo { uint32_t m_external_ip; - uint32_t m_external_ip_server; + uint32_t m_tcp_seq; uint32_t m_fid; uint16_t m_external_port; uint16_t m_pad; @@ -210,13 +211,28 @@ public: void Dump(FILE *fd); }; +typedef std::map > nat_check_flow_map_t; +typedef nat_check_flow_map_t::iterator nat_check_flow_map_iter_t; + +class CNatCheckFlowTableMap { +public: + void erase(uint64_t key) {m_map.erase(key);} + bool find(uint64_t fid, uint32_t &val); + void insert(uint64_t key, uint32_t val) {m_map.insert(std::pair(key, val));} + void clear(void) {m_map.clear();} + void dump(FILE *fd); + uint64_t size(void) {return m_map.size();} + +public: + nat_check_flow_map_t m_map; +}; class CNatRxManager { public: bool Create(); void Delete(); - void handle_packet_ipv4(CNatOption * option, IPHeader * ipv4); + void handle_packet_ipv4(CNatOption * option, IPHeader * ipv4, bool is_first); void handle_aging(); void Dump(FILE *fd); void DumpShort(FILE *fd); @@ -232,6 +248,7 @@ private: uint8_t m_max_threads; CNatPerThreadInfo * m_per_thread; CNatStats m_stats; + CNatCheckFlowTableMap m_fm; }; -- cgit 1.2.3-korg From 333f41bb7de9ce9b0155c834df8a0c4bb67292b0 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Tue, 12 Jul 2016 09:36:21 +0300 Subject: NAT seq num randomization regression tests --- scripts/automation/regression/setups/kiwi02/benchmark.yaml | 2 ++ scripts/automation/regression/setups/trex-dan/benchmark.yaml | 2 ++ scripts/automation/regression/setups/trex07/benchmark.yaml | 2 ++ scripts/automation/regression/setups/trex14/benchmark.yaml | 2 ++ scripts/automation/regression/setups/trex25/benchmark.yaml | 2 ++ scripts/automation/regression/stateful_tests/trex_nat_test.py | 3 +++ 6 files changed, 13 insertions(+) (limited to 'scripts') diff --git a/scripts/automation/regression/setups/kiwi02/benchmark.yaml b/scripts/automation/regression/setups/kiwi02/benchmark.yaml index 3332aa5e..e6621085 100644 --- a/scripts/automation/regression/setups/kiwi02/benchmark.yaml +++ b/scripts/automation/regression/setups/kiwi02/benchmark.yaml @@ -69,6 +69,8 @@ test_nat_simple_mode1: &test_nat_simple test_nat_simple_mode2: *test_nat_simple +test_nat_simple_mode3: *test_nat_simple + test_nat_learning: << : *test_nat_simple nat_opened : 100000 diff --git a/scripts/automation/regression/setups/trex-dan/benchmark.yaml b/scripts/automation/regression/setups/trex-dan/benchmark.yaml index e9f12c45..de56089b 100644 --- a/scripts/automation/regression/setups/trex-dan/benchmark.yaml +++ b/scripts/automation/regression/setups/trex-dan/benchmark.yaml @@ -69,6 +69,8 @@ test_nat_simple_mode1: &test_nat_simple test_nat_simple_mode2: *test_nat_simple +test_nat_simple_mode3: *test_nat_simple + test_nat_learning: << : *test_nat_simple bw_per_core : 7.377 diff --git a/scripts/automation/regression/setups/trex07/benchmark.yaml b/scripts/automation/regression/setups/trex07/benchmark.yaml index 1475abec..0dc340b0 100644 --- a/scripts/automation/regression/setups/trex07/benchmark.yaml +++ b/scripts/automation/regression/setups/trex07/benchmark.yaml @@ -65,6 +65,8 @@ test_nat_simple_mode1: &test_nat_simple test_nat_simple_mode2: *test_nat_simple +test_nat_simple_mode3: *test_nat_simple + test_nat_learning: *test_nat_simple diff --git a/scripts/automation/regression/setups/trex14/benchmark.yaml b/scripts/automation/regression/setups/trex14/benchmark.yaml index 3cd22ce2..04f13e79 100644 --- a/scripts/automation/regression/setups/trex14/benchmark.yaml +++ b/scripts/automation/regression/setups/trex14/benchmark.yaml @@ -65,6 +65,8 @@ test_nat_simple_mode1: &test_nat_simple test_nat_simple_mode2: *test_nat_simple +test_nat_simple_mode3: *test_nat_simple + test_nat_learning: *test_nat_simple diff --git a/scripts/automation/regression/setups/trex25/benchmark.yaml b/scripts/automation/regression/setups/trex25/benchmark.yaml index 19fab1fe..ccbdf6f5 100644 --- a/scripts/automation/regression/setups/trex25/benchmark.yaml +++ b/scripts/automation/regression/setups/trex25/benchmark.yaml @@ -70,6 +70,8 @@ test_nat_simple_mode1: &test_nat_simple test_nat_simple_mode2: *test_nat_simple +test_nat_simple_mode3: *test_nat_simple + test_nat_learning: << : *test_nat_simple nat_opened : 40000 diff --git a/scripts/automation/regression/stateful_tests/trex_nat_test.py b/scripts/automation/regression/stateful_tests/trex_nat_test.py index 6e030ffe..c23f67c4 100755 --- a/scripts/automation/regression/stateful_tests/trex_nat_test.py +++ b/scripts/automation/regression/stateful_tests/trex_nat_test.py @@ -93,6 +93,9 @@ class CTRexNat_Test(CTRexGeneral_Test):#(unittest.TestCase): def test_nat_simple_mode2(self): self.nat_simple_helper(learn_mode=2) + def test_nat_simple_mode3(self): + self.nat_simple_helper(learn_mode=3) + def nat_simple_helper(self, learn_mode=1): # test initializtion self.router.configure_basic_interfaces() -- cgit 1.2.3-korg From a6bb20813c667c765e431687fc747f890af2fe83 Mon Sep 17 00:00:00 2001 From: Ido Barnea Date: Wed, 13 Jul 2016 10:10:11 +0300 Subject: better error handling for trex-cfg --- scripts/trex-cfg | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/scripts/trex-cfg b/scripts/trex-cfg index 7a533b1f..44ef678c 100755 --- a/scripts/trex-cfg +++ b/scripts/trex-cfg @@ -11,7 +11,7 @@ fi MACHINE_TYPE=`uname -m` if [ ${MACHINE_TYPE} != 'x86_64' ]; then - echo "ERROR, only 64bit OS are supported, please reinstall x86 64 bit OS" + echo "ERROR, only 64bit operating systems are supported, please reinstall x86 64 bit OS" exit 1 fi @@ -20,7 +20,7 @@ fi if [ -d /mnt/huge ]; then echo >> /dev/null else - echo "Create huge node" + echo "Creating huge node" mkdir -p /mnt/huge fi @@ -38,19 +38,27 @@ for file in /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepag done if ! lsmod | grep -q igb_uio ; then - echo "Load the drivers for the first time " + echo "Loading kernel drivers for the first time" modprobe uio + if [ $? -ne 0 ]; then + echo "Failed inserting uio module, please check if it is installed" + exit 1 + fi km=ko/$SYS/igb_uio.ko if [ -e $km ] ; then insmod $km + if [ $? -ne 0 ]; then + echo "Failed inserting igb_uio module" + exit 1 + fi else - echo "ERROR kernel module is not supported for this OS" - echo "Please run the following commands: " + echo "ERROR: We don't have precompiled igb_uio.ko module for your kernel version" + echo "You can try compiling yourself, using the following commands:" echo "\$cd ko/src " echo "\$make " echo "\$make install " echo "\$cd - " - echo "and try to run it again " + echo "Then try to run Trex again" exit 1 fi fi @@ -60,5 +68,3 @@ PARENT_ARGS="$0 $@" if ! ./dpdk_setup_ports.py --parent "$PARENT_ARGS"; then exit 1 fi - - -- cgit 1.2.3-korg From 340372e2cbe5b0744896dd97793e2b0e246c25a4 Mon Sep 17 00:00:00 2001 From: Hanoh Haim Date: Fri, 15 Jul 2016 19:06:42 +0300 Subject: remove IMIX for now due to an issue in trex09 --- scripts/automation/regression/stateful_tests/trex_imix_test.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'scripts') diff --git a/scripts/automation/regression/stateful_tests/trex_imix_test.py b/scripts/automation/regression/stateful_tests/trex_imix_test.py index f8fe0ed1..5dd15ed3 100755 --- a/scripts/automation/regression/stateful_tests/trex_imix_test.py +++ b/scripts/automation/regression/stateful_tests/trex_imix_test.py @@ -95,6 +95,8 @@ class CTRexIMIX_Test(CTRexGeneral_Test): def test_static_routing_imix (self): + self.skip('Skip due to an issue with trex09 ') + if self.is_loopback: self.skip('In loopback mode the test is same as test_routing_imix') # test initializtion -- cgit 1.2.3-korg From 6d028fcd21df1d1954f679443ffddc35364552f7 Mon Sep 17 00:00:00 2001 From: imarom Date: Thu, 21 Jul 2016 17:21:04 +0300 Subject: faster TUI: no flickers and more... 1. made stdout full buffered (no in-middle print) 2. added TAB for filenames auto-complete --- .../trex_control_plane/stl/console/trex_tui.py | 163 +++++++++++++++++---- .../stl/trex_stl_lib/utils/parsing_opts.py | 4 + 2 files changed, 135 insertions(+), 32 deletions(-) (limited to 'scripts') 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 32c6b1e0..385cd098 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -455,14 +455,25 @@ class TrexTUI(): self.pm = TrexTUIPanelManager(self) - def clear_screen (self): - #os.system('clear') - # maybe this is faster ? - sys.stdout.write("\x1b[2J\x1b[H") + def clear_screen (self, lines = 50): + # reposition the cursor + sys.stdout.write("\x1b[0;0H") + + # clear all lines + for i in range(lines): + sys.stdout.write("\x1b[0K") + if i < (lines - 1): + sys.stdout.write("\n") + + # reposition the cursor + sys.stdout.write("\x1b[0;0H") + + #sys.stdout.write("\x1b[2J\x1b[H") def show (self, client, show_log = False, locked = False): with AsyncKeys(client, locked) as async_keys: + sys.stdout.write("\x1bc") self.async_keys = async_keys self.show_internal(show_log, locked) @@ -473,7 +484,7 @@ class TrexTUI(): self.pm.init(show_log, locked) self.state = self.STATE_ACTIVE - self.draw_policer = 0 + self.last_redraw_ts = 0 try: while True: @@ -481,7 +492,11 @@ class TrexTUI(): status = self.async_keys.tick(self.pm) self.draw_screen(status) + + # speedup for keys, slower for no keys if status == AsyncKeys.STATUS_NONE: + time.sleep(0.01) + else: time.sleep(0.001) # regular state @@ -518,17 +533,10 @@ class TrexTUI(): # draw once def draw_screen (self, status): + t = time.time() - self.last_redraw_ts + redraw = (t >= 0.5) or (status == AsyncKeys.STATUS_REDRAW_ALL) - # only redraw the keys line - if status == AsyncKeys.STATUS_REDRAW_KEYS: - self.clear_screen() - sys.stdout.write(self.last_snap) - self.async_keys.draw() - sys.stdout.flush() - return - - if (self.draw_policer >= 500) or (status == AsyncKeys.STATUS_REDRAW_ALL): - + if redraw: # capture stdout to a string old_stdout = sys.stdout sys.stdout = mystdout = StringIO() @@ -543,10 +551,16 @@ class TrexTUI(): sys.stdout.write(mystdout.getvalue()) sys.stdout.flush() + self.last_redraw_ts = time.time() + + elif status == AsyncKeys.STATUS_REDRAW_KEYS: + sys.stdout.write("\x1b[4A") + + self.async_keys.draw() + sys.stdout.flush() + + return - self.draw_policer = 0 - else: - self.draw_policer += 1 def get_state (self): @@ -586,11 +600,22 @@ class AsyncKeys: new_settings[6][termios.VMIN] = 0 # cc new_settings[6][termios.VTIME] = 0 # cc termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new_settings) + + # huge buffer - no print without flush + tmp_fd = os.dup(sys.stdout.fileno()) + sys.stdout.close() + sys.stdout = os.fdopen(tmp_fd, 'w', 80 * 25 * 2) + return self def __exit__ (self, type, value, traceback): termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old_settings) + # restore sys.stdout + tmp_fd = os.dup(sys.stdout.fileno()) + sys.stdout.close() + sys.stdout = os.fdopen(tmp_fd, 'w', -1) + def is_legend_mode (self): return self.engine.get_type() == AsyncKeys.MODE_LEGEND @@ -759,20 +784,14 @@ class AsyncKeysEngineConsole: # TAB elif ch == '\t': - cur = self.lines[self.line_index].get() - if not cur: + tokens = self.lines[self.line_index].get().split() + if not tokens: return - matching_cmds = [x for x in self.ac if x.startswith(cur)] - - common = os.path.commonprefix([x for x in self.ac if x.startswith(cur)]) - if common: - if len(matching_cmds) == 1: - self.lines[self.line_index].set(common + ' ') - self.last_status = '' - else: - self.lines[self.line_index].set(common) - self.last_status = 'ambigious: '+ ' '.join([format_text(cmd, 'bold') for cmd in matching_cmds]) + if len(tokens) == 1: + self.handle_tab_names(tokens[0]) + else: + self.handle_tab_files(tokens) # simple char @@ -782,6 +801,86 @@ class AsyncKeysEngineConsole: return AsyncKeys.STATUS_REDRAW_KEYS + # handle TAB key for completing function names + def handle_tab_names (self, cur): + matching_cmds = [x for x in self.ac if x.startswith(cur)] + + common = os.path.commonprefix([x for x in self.ac if x.startswith(cur)]) + if common: + if len(matching_cmds) == 1: + self.lines[self.line_index].set(common + ' ') + self.last_status = '' + else: + self.lines[self.line_index].set(common) + self.last_status = 'ambigious: '+ ' '.join([format_text(cmd, 'bold') for cmd in matching_cmds]) + + + # handle TAB for completing filenames + def handle_tab_files (self, tokens): + # we support only start command with files + if tokens[0] != 'start': + return + + # '-f' with no paramters - no partial and use current dir + if tokens[-1] == '-f': + partial = '' + d = '.' + + # got a partial path + elif tokens[-2] == '-f': + partial = tokens.pop() + + # check for dirs + dirname, basename = os.path.dirname(partial), os.path.basename(partial) + if os.path.isdir(dirname): + d = dirname + partial = basename + else: + d = '.' + else: + return + + # fetch all dirs and files matching wildcard + files = [] + for x in os.listdir(d): + if os.path.isdir(os.path.join(d, x)): + files.append(x + '/') + elif x.endswith('.py') or x.endswith('yaml') or x.endswith('pcap') or x.endswith('cap'): + files.append(x) + + # dir might not have the files + if not files: + self.last_status = format_text('no loadble files under path', 'bold') + return + + + # find all the matching files + matching_files = [x for x in files if x.startswith(partial)] if partial else files + + # do we have a longer common than partial ? + common = os.path.commonprefix([x for x in files if x.startswith(partial)]) + if not common: + common = partial + + tokens.append(os.path.join(d, common) if d is not '.' else common) + + # reforge the line + newline = ' '.join(tokens) + + if len(matching_files) == 1: + if os.path.isfile(tokens[-1]): + newline += ' ' + + self.lines[self.line_index].set(newline) + self.last_status = '' + else: + self.lines[self.line_index].set(newline) + self.last_status = ' '.join([format_text(f, 'bold') for f in matching_files[:5]]) + if len(matching_files) > 5: + self.last_status += ' ... [{0} more matches]'.format(len(matching_files) - 5) + + + def split_cmd (self, cmd): s = cmd.split(' ', 1) op = s[0] @@ -828,8 +927,8 @@ class AsyncKeysEngineConsole: def draw (self): sys.stdout.write("\nPress 'ESC' for navigation panel...\n") - sys.stdout.write("status: {0}\n".format(self.last_status)) - sys.stdout.write("\ntui>") + sys.stdout.write("status: \x1b[0K{0}\n".format(self.last_status)) + sys.stdout.write("\ntui>\x1b[0K") self.lines[self.line_index].draw() 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 1b417d65..caa5aea8 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 @@ -396,6 +396,10 @@ class CCmdArgParser(argparse.ArgumentParser): self.cmd_name = kwargs.get('prog') + # hook this to the logger + def _print_message(self, message, file=None): + self.stateless_client.logger.log(message) + def has_ports_cfg (self, opts): return hasattr(opts, "all_ports") or hasattr(opts, "ports") -- cgit 1.2.3-korg From a913ed85424bd1ab38c8842dd16dd10b90db12fe Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 25 Jul 2016 11:20:16 +0300 Subject: TUI tweaks --- .../trex_control_plane/stl/console/trex_console.py | 43 +++---- .../trex_control_plane/stl/console/trex_tui.py | 50 ++++++-- .../stl/trex_stl_lib/trex_stl_client.py | 130 +++++++++++---------- .../stl/trex_stl_lib/utils/parsing_opts.py | 31 +++-- 4 files changed, 144 insertions(+), 110 deletions(-) (limited to 'scripts') 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 9e3f2600..41a04617 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -241,20 +241,7 @@ class TRexConsole(TRexGeneralCmd): def postcmd(self, stop, line): - - if not self.stateless_client.is_connected(): - self.prompt = "trex(offline)>" - self.supported_rpc = None - - elif not self.stateless_client.get_acquired_ports(): - self.prompt = "trex(read-only)>" - - elif self.stateless_client.is_all_ports_acquired(): - self.prompt = "trex>" - - else: - self.prompt = "trex {0}>".format(self.stateless_client.get_acquired_ports()) - + self.prompt = self.stateless_client.generate_prompt(prefix = 'trex') return stop @@ -316,25 +303,21 @@ class TRexConsole(TRexGeneralCmd): self.do_history("-h") def do_shell (self, line): - return self.do_history(line) + self.do_history(line) def do_push (self, line): '''Push a local PCAP file\n''' - return self.stateless_client.push_line(line) - - #def do_push_remote (self, line): - # '''Push a remote accessible PCAP file\n''' - # return self.stateless_client.push_remote_line(line) + self.stateless_client.push_line(line) def help_push (self): - return self.do_push("-h") + self.do_push("-h") def do_portattr (self, line): '''Change/show port(s) attributes\n''' - return self.stateless_client.set_port_attr_line(line) + self.stateless_client.set_port_attr_line(line) def help_portattr (self): - return self.do_portattr("-h") + self.do_portattr("-h") @verify_connected def do_map (self, line): @@ -548,7 +531,7 @@ class TRexConsole(TRexGeneralCmd): def do_events (self, line): '''shows events recieved from server\n''' - return self.stateless_client.get_events_line(line) + self.stateless_client.get_events_line(line) def complete_profile(self, text, line, begidx, endidx): @@ -562,7 +545,6 @@ class TRexConsole(TRexGeneralCmd): @verify_connected def do_tui (self, line): '''Shows a graphical console\n''' - parser = parsing_opts.gen_parser(self, "tui", self.do_tui.__doc__, @@ -581,16 +563,19 @@ class TRexConsole(TRexGeneralCmd): info = self.stateless_client.get_connection_info() exe = './trex-console --top -t -q -s {0} -p {1} --async_port {2}'.format(info['server'], info['sync_port'], info['async_port']) - cmd = ['/usr/bin/xterm', '-geometry', '111x49', '-sl', '0', '-title', 'trex_tui', '-e', exe] + cmd = ['/usr/bin/xterm', '-geometry', '{0}x{1}'.format(self.tui.MIN_COLS, self.tui.MIN_ROWS), '-sl', '0', '-title', 'trex_tui', '-e', exe] # detach child self.terminal = subprocess.Popen(cmd, preexec_fn = os.setpgrp) return - - with self.stateless_client.logger.supress(): - self.tui.show(self.stateless_client, locked = opts.locked) + + try: + with self.stateless_client.logger.supress(): + self.tui.show(self.stateless_client, locked = opts.locked) + except self.tui.ScreenSizeException as e: + print(format_text(str(e) + "\n", 'bold')) def help_tui (self): 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 385cd098..2e26fdfc 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -5,6 +5,7 @@ import time from collections import OrderedDict, deque import datetime import readline +from texttable import ansi_len if sys.version_info > (3,0): from io import StringIO @@ -449,6 +450,17 @@ class TrexTUI(): STATE_RECONNECT = 2 is_graph = False + MIN_ROWS = 50 + MIN_COLS = 111 + + class ScreenSizeException(Exception): + def __init__ (self, cols, rows): + msg = "TUI requires console screen size of at least {0}x{1}, current is {2}x{3}".format(TrexTUI.MIN_COLS, + TrexTUI.MIN_ROWS, + cols, + rows) + super(TrexTUI.ScreenSizeException, self).__init__(msg) + def __init__ (self, stateless_client): self.stateless_client = stateless_client @@ -472,6 +484,11 @@ class TrexTUI(): def show (self, client, show_log = False, locked = False): + + rows, cols = os.popen('stty size', 'r').read().split() + if (int(rows) < TrexTUI.MIN_ROWS) or (int(cols) < TrexTUI.MIN_COLS): + raise self.ScreenSizeException(rows = rows, cols = cols) + with AsyncKeys(client, locked) as async_keys: sys.stdout.write("\x1bc") self.async_keys = async_keys @@ -691,16 +708,22 @@ class AsyncKeysEngineConsole: self.async = async self.lines = deque(maxlen = 100) - self.ac = {'start' : client.start_line, - 'stop' : client.stop_line, - 'pause' : client.pause_line, - 'resume': client.resume_line, - 'update': client.update_line, - 'quit' : self.action_quit, - 'q' : self.action_quit, - 'exit' : self.action_quit, - 'help' : self.action_help, - '?' : self.action_help} + self.generate_prompt = client.generate_prompt + + self.ac = {'start' : client.start_line, + 'stop' : client.stop_line, + 'pause' : client.pause_line, + 'resume' : client.resume_line, + 'update' : client.update_line, + 'connect' : client.connect_line, + 'disconnect' : client.disconnect_line, + 'acquire' : client.acquire_line, + 'release' : client.release_line, + 'quit' : self.action_quit, + 'q' : self.action_quit, + 'exit' : self.action_quit, + 'help' : self.action_help, + '?' : self.action_help} # fetch readline history and add relevants for i in range(0, readline.get_current_history_length()): @@ -916,6 +939,7 @@ class AsyncKeysEngineConsole: line.invalidate() assert(self.lines[0].modified == False) + color = None if not func: self.last_status = "unknown command: '{0}'".format(format_text(cmd.split()[0], 'bold')) else: @@ -923,12 +947,16 @@ class AsyncKeysEngineConsole: self.last_status = func_rc else: self.last_status = format_text("[OK]", 'green') if func_rc else format_text(str(func_rc).replace('\n', ''), 'red') + color = 'red' + # trim too long lines + if ansi_len(self.last_status) > 100: + self.last_status = format_text(self.last_status[:100] + "...", color, 'bold') def draw (self): sys.stdout.write("\nPress 'ESC' for navigation panel...\n") sys.stdout.write("status: \x1b[0K{0}\n".format(self.last_status)) - sys.stdout.write("\ntui>\x1b[0K") + sys.stdout.write("\n{0}\x1b[0K".format(self.generate_prompt(prefix = 'tui'))) self.lines[self.line_index].draw() 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 7c8a5fbf..a4f26f69 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 @@ -2442,7 +2442,7 @@ class STLClient(object): def ping_line (self, line): '''pings the server''' self.ping() - return True + return RC_OK() @__console def connect_line (self, line): @@ -2454,14 +2454,13 @@ class STLClient(object): parsing_opts.FORCE) opts = parser.parse_args(line.split(), default_ports = self.get_all_ports()) - if opts is None: - return + if not opts: + return opts self.connect() self.acquire(ports = opts.ports, force = opts.force) - # true means print time - return True + return RC_OK() @__console @@ -2476,19 +2475,19 @@ class STLClient(object): parsing_opts.FORCE) opts = parser.parse_args(line.split(), default_ports = self.get_all_ports()) - if opts is None: - return + if not opts: + return opts # filter out all the already owned ports ports = list_difference(opts.ports, self.get_acquired_ports()) if not ports: - self.logger.log("acquire - all port(s) {0} are already acquired".format(opts.ports)) - return + msg = "acquire - all of port(s) {0} are already acquired".format(opts.ports) + self.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) self.acquire(ports = ports, force = opts.force) - # true means print time - return True + return RC_OK() # @@ -2502,23 +2501,24 @@ class STLClient(object): parsing_opts.PORT_LIST_WITH_ALL) opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports()) - if opts is None: - return + if not opts: + return opts ports = list_intersect(opts.ports, self.get_acquired_ports()) if not ports: if not opts.ports: - self.logger.log("release - no acquired ports") - return + msg = "release - no acquired ports" + self.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) else: - self.logger.log("release - none of port(s) {0} are acquired".format(opts.ports)) - return + msg = "release - none of port(s) {0} are acquired".format(opts.ports) + self.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) self.release(ports = ports) - # true means print time - return True + return RC_OK() @__console @@ -2530,23 +2530,23 @@ class STLClient(object): self.reacquire_line.__doc__) opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts # find all the on-owned ports under your name my_unowned_ports = list_difference([k for k, v in self.ports.items() if v.get_owner() == self.username], self.get_acquired_ports()) if not my_unowned_ports: - self.logger.log("reacquire - no unowned ports under '{0}'".format(self.username)) - return + msg = "reacquire - no unowned ports under '{0}'".format(self.username) + self.logger.log(msg) + return RC_ERR(msg) self.acquire(ports = my_unowned_ports, force = True) - return True + return RC_OK() @__console def disconnect_line (self, line): self.disconnect() - @__console @@ -2559,13 +2559,12 @@ class STLClient(object): parsing_opts.PORT_LIST_WITH_ALL) opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) - if opts is None: - return + if not opts: + return opts self.reset(ports = opts.ports) - # true means print time - return True + return RC_OK() @@ -2586,8 +2585,8 @@ class STLClient(object): parsing_opts.DRY_RUN) opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) - if opts is None: - return RC_ERR("invalid arguments for 'start'") + if not opts: + return opts active_ports = list_intersect(self.get_active_ports(), opts.ports) if active_ports: @@ -2663,8 +2662,8 @@ class STLClient(object): parsing_opts.PORT_LIST_WITH_ALL) opts = parser.parse_args(line.split(), default_ports = self.get_active_ports(), verify_acquired = True) - if opts is None: - return RC_ERR("invalid arguments for 'stop'") + if not opts: + return opts # find the relevant ports @@ -2681,7 +2680,6 @@ class STLClient(object): # call API self.stop(ports) - # true means print time return RC_OK() @@ -2697,8 +2695,8 @@ class STLClient(object): parsing_opts.FORCE) opts = parser.parse_args(line.split(), default_ports = self.get_active_ports(), verify_acquired = True) - if opts is None: - return RC_ERR("invalid arguments for 'update'") + if not opts: + return opts # find the relevant ports @@ -2714,7 +2712,6 @@ class STLClient(object): self.update(ports, opts.mult, opts.total, opts.force) - # true means print time return RC_OK() @@ -2727,8 +2724,8 @@ class STLClient(object): parsing_opts.PORT_LIST_WITH_ALL) opts = parser.parse_args(line.split(), default_ports = self.get_transmitting_ports(), verify_acquired = True) - if opts is None: - return RC_ERR("invalid arguments for 'pause'") + if not opts: + return opts # check for already paused case if opts.ports and is_sub_list(opts.ports, self.get_paused_ports()): @@ -2749,7 +2746,6 @@ class STLClient(object): self.pause(ports) - # true means print time return RC_OK() @@ -2762,8 +2758,8 @@ class STLClient(object): parsing_opts.PORT_LIST_WITH_ALL) opts = parser.parse_args(line.split(), default_ports = self.get_paused_ports(), verify_acquired = True) - if opts is None: - return RC_ERR("invalid arguments for 'resume'") + if not opts: + return opts # find the relevant ports ports = list_intersect(opts.ports, self.get_paused_ports()) @@ -2794,8 +2790,8 @@ class STLClient(object): opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts self.clear_stats(opts.ports) @@ -2814,8 +2810,8 @@ class STLClient(object): opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts # determine stats mask mask = self.__get_mask_keys(**self.__filter_namespace_args(opts, trex_stl_stats.ALL_STATS_OPTS)) @@ -2845,8 +2841,8 @@ class STLClient(object): opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts streams = self._get_streams(opts.ports, set(opts.streams)) if not streams: @@ -2872,8 +2868,8 @@ class STLClient(object): parsing_opts.PORT_LIST_WITH_ALL) opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts self.validate(opts.ports) @@ -2897,8 +2893,8 @@ class STLClient(object): parsing_opts.FORCE) opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts active_ports = list(set(self.get_active_ports()).intersection(opts.ports)) @@ -2906,7 +2902,7 @@ class STLClient(object): 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 + return RC_ERR(msg) else: self.stop(active_ports) @@ -2930,7 +2926,7 @@ class STLClient(object): - return True + return RC_OK() @@ -2945,8 +2941,8 @@ class STLClient(object): parsing_opts.PROMISCUOUS_SWITCH) opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) - if opts is None: - return + if not opts: + return opts # if no attributes - fall back to printing the status if opts.prom is None: @@ -2954,7 +2950,7 @@ class STLClient(object): return self.set_port_attr(opts.ports, opts.prom) - + return RC_OK() @__console @@ -2967,8 +2963,8 @@ class STLClient(object): parsing_opts.FILE_PATH) opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts info = STLProfile.get_info(opts.file[0]) @@ -3024,8 +3020,8 @@ class STLClient(object): *x) opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts ev_type_filter = [] @@ -3046,3 +3042,15 @@ class STLClient(object): if opts.clear: self.clear_events() + def generate_prompt (self, prefix = 'trex'): + if not self.is_connected(): + return "{0}(offline)>".format(prefix) + + elif not self.get_acquired_ports(): + return "{0}(read-only)>".format(prefix) + + elif self.is_all_ports_acquired(): + return "{0}>".format(prefix) + + else: + return "{0} {1}>".format(prefix, self.get_acquired_ports()) 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 caa5aea8..8be154af 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,9 @@ import argparse from collections import namedtuple from .common import list_intersect, list_difference +from .text_opts import format_text +from ..trex_stl_types import * + import sys import re import os @@ -400,6 +403,11 @@ class CCmdArgParser(argparse.ArgumentParser): def _print_message(self, message, file=None): self.stateless_client.logger.log(message) + def error(self, message): + self.print_usage() + self._print_message(('%s: error: %s\n') % (self.prog, message)) + raise ValueError(message) + def has_ports_cfg (self, opts): return hasattr(opts, "all_ports") or hasattr(opts, "ports") @@ -407,7 +415,7 @@ class CCmdArgParser(argparse.ArgumentParser): try: opts = super(CCmdArgParser, self).parse_args(args, namespace) if opts is None: - return None + return RC_ERR("'{0}' - invalid arguments".format(self.cmd_name)) if not self.has_ports_cfg(opts): return opts @@ -422,8 +430,9 @@ class CCmdArgParser(argparse.ArgumentParser): # so maybe we have ports configured invalid_ports = list_difference(opts.ports, self.stateless_client.get_all_ports()) if invalid_ports: - self.stateless_client.logger.log("{0}: port(s) {1} are not valid port IDs".format(self.cmd_name, invalid_ports)) - return None + msg = "{0}: port(s) {1} are not valid port IDs".format(self.cmd_name, invalid_ports) + self.stateless_client.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) # verify acquired ports if verify_acquired: @@ -431,21 +440,25 @@ class CCmdArgParser(argparse.ArgumentParser): diff = list_difference(opts.ports, acquired_ports) if diff: - self.stateless_client.logger.log("{0} - port(s) {1} are not acquired".format(self.cmd_name, diff)) - return None + msg = "{0} - port(s) {1} are not acquired".format(self.cmd_name, diff) + self.stateless_client.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) # no acquire ports at all if not acquired_ports: - self.stateless_client.logger.log("{0} - no acquired ports".format(self.cmd_name)) - return None - + msg = "{0} - no acquired ports".format(self.cmd_name) + self.stateless_client.logger.log(format_text(msg, 'bold')) + return RC_ERR(msg) return opts + except ValueError as e: + return RC_ERR("'{0}' - {1}".format(self.cmd_name, str(e))) + except SystemExit: # recover from system exit scenarios, such as "help", or bad arguments. - return None + return RC_ERR("'{0}' - {1}".format(self.cmd_name, "no action")) def get_flags (opt): -- cgit 1.2.3-korg From 4e40d7059985e968eb547581865b603e3d7f78d3 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 25 Jul 2016 12:06:51 +0300 Subject: TUI - stdout close/reopen issues --- scripts/automation/trex_control_plane/stl/console/trex_tui.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'scripts') 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 2e26fdfc..81ba335c 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -619,19 +619,15 @@ class AsyncKeys: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, new_settings) # huge buffer - no print without flush - tmp_fd = os.dup(sys.stdout.fileno()) - sys.stdout.close() - sys.stdout = os.fdopen(tmp_fd, 'w', 80 * 25 * 2) - + sys.stdout = open('/dev/stdout', 'w', TrexTUI.MIN_COLS * TrexTUI.MIN_COLS * 2) return self def __exit__ (self, type, value, traceback): termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self.old_settings) # restore sys.stdout - tmp_fd = os.dup(sys.stdout.fileno()) sys.stdout.close() - sys.stdout = os.fdopen(tmp_fd, 'w', -1) + sys.stdout = sys.__stdout__ def is_legend_mode (self): -- cgit 1.2.3-korg From 34a4ff7b92ee7cc09e38f599d83d9fdf1fa3a8bc Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Mon, 25 Jul 2016 13:34:53 +0300 Subject: Add Ubuntu 16.04.1 LTS ko Regression: Remove skip of test_static_routing_imix Add trex15 setup details --- .../regression/setups/trex15/benchmark.yaml | 155 +++++++++++++++++++++ .../regression/setups/trex15/config.yaml | 39 ++++++ .../regression/stateful_tests/trex_imix_test.py | 2 - scripts/ko/4.4.0-31-generic/igb_uio.ko | Bin 0 -> 17320 bytes 4 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 scripts/automation/regression/setups/trex15/benchmark.yaml create mode 100644 scripts/automation/regression/setups/trex15/config.yaml create mode 100644 scripts/ko/4.4.0-31-generic/igb_uio.ko (limited to 'scripts') diff --git a/scripts/automation/regression/setups/trex15/benchmark.yaml b/scripts/automation/regression/setups/trex15/benchmark.yaml new file mode 100644 index 00000000..b366b3fb --- /dev/null +++ b/scripts/automation/regression/setups/trex15/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/trex15/config.yaml b/scripts/automation/regression/setups/trex15/config.yaml new file mode 100644 index 00000000..c5fc3b22 --- /dev/null +++ b/scripts/automation/regression/setups/trex15/config.yaml @@ -0,0 +1,39 @@ +################################################################ +#### 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. +# * VM - Virtual OS (accept low CPU utilization in tests, latency can get spikes) +# * virt_nics - NICs are virtual (VMXNET3 etc. have their limitations 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-15 + cores : 1 + modes : [loopback, virt_nics, VM] diff --git a/scripts/automation/regression/stateful_tests/trex_imix_test.py b/scripts/automation/regression/stateful_tests/trex_imix_test.py index 5dd15ed3..f8fe0ed1 100755 --- a/scripts/automation/regression/stateful_tests/trex_imix_test.py +++ b/scripts/automation/regression/stateful_tests/trex_imix_test.py @@ -95,8 +95,6 @@ class CTRexIMIX_Test(CTRexGeneral_Test): def test_static_routing_imix (self): - self.skip('Skip due to an issue with trex09 ') - if self.is_loopback: self.skip('In loopback mode the test is same as test_routing_imix') # test initializtion diff --git a/scripts/ko/4.4.0-31-generic/igb_uio.ko b/scripts/ko/4.4.0-31-generic/igb_uio.ko new file mode 100644 index 00000000..86a0a5bb Binary files /dev/null and b/scripts/ko/4.4.0-31-generic/igb_uio.ko differ -- cgit 1.2.3-korg From e3b43560ff867c35ee726da9a98aed8acdc53b70 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 25 Jul 2016 15:21:30 +0300 Subject: TRex console - add support for L1 BPS https://trex-tgn.cisco.com/youtrack/issue/trex-230 --- .../stl/trex_stl_lib/utils/parsing_opts.py | 92 +++++++++++----------- src/stateless/cp/trex_stateless_port.cpp | 8 +- src/stateless/cp/trex_stateless_port.h | 1 + 3 files changed, 52 insertions(+), 49 deletions(-) (limited to 'scripts') 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 8be154af..af7e90c1 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 @@ -96,78 +96,74 @@ match_multiplier_help = """Multiplier should be passed in the following format: # value should be divided def decode_multiplier(val, allow_update = False, divide_count = 1): + factor_table = {None: 1, 'k': 1e3, 'm': 1e6, 'g': 1e9} + pattern = "^(\d+(\.\d+)?)(((k|m|g)?(bpsl1|pps|bps))|%)?" + # do we allow updates ? +/- if not allow_update: - match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)$", val) + pattern += "$" + match = re.match(pattern, val) op = None else: - match = re.match("^(\d+(\.\d+)?)(bps|kbps|mbps|gbps|pps|kpps|mpps|%?)([\+\-])?$", val) + pattern += "([\+\-])?$" + match = re.match(pattern, val) if match: - op = match.group(4) + op = match.group(7) else: op = None result = {} - if match: - - value = float(match.group(1)) - unit = match.group(3) - + if not match: + return None - - # raw type (factor) - if not unit: - result['type'] = 'raw' - result['value'] = value + # value in group 1 + value = float(match.group(1)) - elif unit == 'bps': - result['type'] = 'bps' - result['value'] = value + # decode unit as whole + unit = match.group(3) - elif unit == 'kbps': - result['type'] = 'bps' - result['value'] = value * 1000 + # k,m,g + factor = match.group(5) - elif unit == 'mbps': - result['type'] = 'bps' - result['value'] = value * 1000 * 1000 + # type of multiplier + m_type = match.group(6) - elif unit == 'gbps': - result['type'] = 'bps' - result['value'] = value * 1000 * 1000 * 1000 + # raw type (factor) + if not unit: + result['type'] = 'raw' + result['value'] = value - elif unit == 'pps': - result['type'] = 'pps' - result['value'] = value + # percentage + elif unit == '%': + result['type'] = 'percentage' + result['value'] = value - elif unit == "kpps": - result['type'] = 'pps' - result['value'] = value * 1000 + elif m_type == 'bps': + result['type'] = 'bps' + result['value'] = value * factor_table[factor] - elif unit == "mpps": - result['type'] = 'pps' - result['value'] = value * 1000 * 1000 + elif m_type == 'pps': + result['type'] = 'pps' + result['value'] = value * factor_table[factor] - elif unit == "%": - result['type'] = 'percentage' - result['value'] = value + elif m_type == 'bpsl1': + result['type'] = 'bpsl1' + result['value'] = value * factor_table[factor] - if op == "+": - result['op'] = "add" - elif op == "-": - result['op'] = "sub" - else: - result['op'] = "abs" + if op == "+": + result['op'] = "add" + elif op == "-": + result['op'] = "sub" + else: + result['op'] = "abs" - if result['op'] != 'percentage': - result['value'] = result['value'] / divide_count + if result['op'] != 'percentage': + result['value'] = result['value'] / divide_count - return result + return result - else: - return None def match_multiplier(val): diff --git a/src/stateless/cp/trex_stateless_port.cpp b/src/stateless/cp/trex_stateless_port.cpp index d736d09e..0fe4b410 100644 --- a/src/stateless/cp/trex_stateless_port.cpp +++ b/src/stateless/cp/trex_stateless_port.cpp @@ -691,6 +691,9 @@ TrexStatelessPort::calculate_effective_factor_internal(const TrexPortMultiplier case TrexPortMultiplier::MUL_BPS: return m_graph_obj->get_factor_bps_l2(mul.m_value); + case TrexPortMultiplier::MUL_BPSL1: + return m_graph_obj->get_factor_bps_l1(mul.m_value); + case TrexPortMultiplier::MUL_PPS: return m_graph_obj->get_factor_pps(mul.m_value); @@ -741,7 +744,7 @@ TrexStatelessPort::delete_streams_graph() { * port multiplier * **************************/ -const std::initializer_list TrexPortMultiplier::g_types = {"raw", "bps", "pps", "percentage"}; +const std::initializer_list TrexPortMultiplier::g_types = {"raw", "bps", "bpsl1", "pps", "percentage"}; const std::initializer_list TrexPortMultiplier::g_ops = {"abs", "add", "sub"}; TrexPortMultiplier:: @@ -755,6 +758,9 @@ TrexPortMultiplier(const std::string &type_str, const std::string &op_str, doubl } else if (type_str == "bps") { type = MUL_BPS; + } else if (type_str == "bpsl1") { + type = MUL_BPSL1; + } else if (type_str == "pps") { type = MUL_PPS; diff --git a/src/stateless/cp/trex_stateless_port.h b/src/stateless/cp/trex_stateless_port.h index 8856b429..915c5325 100644 --- a/src/stateless/cp/trex_stateless_port.h +++ b/src/stateless/cp/trex_stateless_port.h @@ -477,6 +477,7 @@ public: enum mul_type_e { MUL_FACTOR, MUL_BPS, + MUL_BPSL1, MUL_PPS, MUL_PERCENTAGE }; -- cgit 1.2.3-korg From 90c64917b59e83556454d1338634473cdcd952a9 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 26 Jul 2016 11:47:15 +0300 Subject: some more TUI fixes --- .../trex_control_plane/common/text_opts.py | 5 ++- .../trex_control_plane/stl/console/trex_console.py | 3 +- .../trex_control_plane/stl/console/trex_tui.py | 45 +++++++++++++++++----- .../stl/trex_stl_lib/trex_stl_port.py | 14 +++++-- .../stl/trex_stl_lib/trex_stl_stats.py | 22 ++++++----- .../stl/trex_stl_lib/utils/text_opts.py | 5 ++- 6 files changed, 69 insertions(+), 25 deletions(-) (limited to 'scripts') diff --git a/scripts/automation/trex_control_plane/common/text_opts.py b/scripts/automation/trex_control_plane/common/text_opts.py index ab0fd2f2..c9ab7ca8 100755 --- a/scripts/automation/trex_control_plane/common/text_opts.py +++ b/scripts/automation/trex_control_plane/common/text_opts.py @@ -27,7 +27,10 @@ class TextCodesStripper: def strip (s): return re.sub(TextCodesStripper.pattern, '', s) -def format_num (size, suffix = "", compact = True, opts = ()): +def format_num (size, suffix = "", compact = True, opts = None): + if opts is None: + opts = () + txt = "NaN" if type(size) == str: 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 41a04617..7ad0cfa4 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -573,7 +573,8 @@ class TRexConsole(TRexGeneralCmd): try: with self.stateless_client.logger.supress(): - self.tui.show(self.stateless_client, locked = opts.locked) + self.tui.show(self.stateless_client, self.save_console_history, locked = opts.locked) + except self.tui.ScreenSizeException as e: print(format_text(str(e) + "\n", 'bold')) 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 81ba335c..d3da738b 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -24,6 +24,16 @@ class TUIQuit(Exception): # for STL exceptions from trex_stl_lib.api import * +def ascii_split (s): + output = [] + + lines = s.split('\n') + for elem in lines: + if ansi_len(elem) > 0: + output.append(elem) + + return output + class SimpleBar(object): def __init__ (self, desc, pattern): self.desc = desc @@ -483,13 +493,13 @@ class TrexTUI(): #sys.stdout.write("\x1b[2J\x1b[H") - def show (self, client, show_log = False, locked = False): + def show (self, client, save_console_history, show_log = False, locked = False): rows, cols = os.popen('stty size', 'r').read().split() if (int(rows) < TrexTUI.MIN_ROWS) or (int(cols) < TrexTUI.MIN_COLS): raise self.ScreenSizeException(rows = rows, cols = cols) - with AsyncKeys(client, locked) as async_keys: + with AsyncKeys(client, save_console_history, locked) as async_keys: sys.stdout.write("\x1bc") self.async_keys = async_keys self.show_internal(show_log, locked) @@ -597,8 +607,8 @@ class AsyncKeys: STATUS_REDRAW_KEYS = 1 STATUS_REDRAW_ALL = 2 - def __init__ (self, client, locked = False): - self.engine_console = AsyncKeysEngineConsole(self, client) + def __init__ (self, client, save_console_history, locked = False): + self.engine_console = AsyncKeysEngineConsole(self, client, save_console_history) self.engine_legend = AsyncKeysEngineLegend(self) self.locked = locked @@ -700,11 +710,12 @@ class AsyncKeysEngineLegend: # console engine class AsyncKeysEngineConsole: - def __init__ (self, async, client): + def __init__ (self, async, client, save_console_history): self.async = async self.lines = deque(maxlen = 100) - self.generate_prompt = client.generate_prompt + self.generate_prompt = client.generate_prompt + self.save_console_history = save_console_history self.ac = {'start' : client.start_line, 'stop' : client.stop_line, @@ -929,6 +940,7 @@ class AsyncKeysEngineConsole: self.lines.appendleft(empty_line) self.line_index = 0 readline.add_history(cmd) + self.save_console_history() # back to readonly for line in self.lines: @@ -939,15 +951,28 @@ class AsyncKeysEngineConsole: if not func: self.last_status = "unknown command: '{0}'".format(format_text(cmd.split()[0], 'bold')) else: + # internal commands if isinstance(func_rc, str): self.last_status = func_rc + + # RC response else: - self.last_status = format_text("[OK]", 'green') if func_rc else format_text(str(func_rc).replace('\n', ''), 'red') - color = 'red' + # success + if func_rc: + self.last_status = format_text("[OK]", 'green') + + # errors + else: + err_msgs = ascii_split(str(func_rc)) + self.last_status = format_text(err_msgs[0], 'red') + if len(err_msgs) > 1: + self.last_status += " [{0} more errors messages]".format(len(err_msgs) - 1) + color = 'red' # trim too long lines - if ansi_len(self.last_status) > 100: - self.last_status = format_text(self.last_status[:100] + "...", color, 'bold') + if ansi_len(self.last_status) > TrexTUI.MIN_COLS: + self.last_status = format_text(self.last_status[:TrexTUI.MIN_COLS] + "...", color, 'bold') + def draw (self): sys.stdout.write("\nPress 'ESC' for navigation panel...\n") 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 be46e95f..d239fc57 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 @@ -68,7 +68,7 @@ class Port(object): self.has_rx_streams = False self.owner = '' - + self.last_factor_type = None # decorator to verify port is up def up(func): @@ -417,6 +417,9 @@ class Port(object): self.state = last_state return self.err(rc.err()) + # save this for TUI + self.last_factor_type = mul['type'] + return self.ok() @@ -424,7 +427,7 @@ class Port(object): # with force ignores the cached state and sends the command @owned def stop (self, force = False): - + # if not is not active and not force - go back if not self.is_active() and not force: return self.ok() @@ -437,10 +440,11 @@ 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() - + return self.ok() @@ -535,6 +539,9 @@ class Port(object): if rc.bad(): return self.err(rc.err()) + # save this for TUI + self.last_factor_type = mul['type'] + return self.ok() @owned @@ -712,6 +719,7 @@ class Port(object): # until thread is locked - order is important self.tx_stopped_ts = datetime.now() self.state = self.STATE_STREAMS + self.last_factor_type = None # rest of the events are used for TUI / read only sessions def async_event_port_stopped (self): 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 af4d6f69..1bf0a9a4 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 @@ -776,12 +776,12 @@ class CTRexStats(object): return value - def get(self, field, format=False, suffix=""): + def get(self, field, format=False, suffix="", opts = None): value = self._get(self.latest_stats, field) if value == None: return 'N/A' - return value if not format else format_num(value, suffix) + return value if not format else format_num(value, suffix = suffix, opts = opts) def get_rel(self, field, format=False, suffix=""): @@ -1020,14 +1020,20 @@ class CPortStats(CTRexStats): else: state = format_text(state, 'bold') + # default rate format modifiers + rate_format = {'bpsl1': None, 'bps': None, 'pps': None, 'percentage': 'bold'} + # mark owned ports by color if self._port_obj: owner = self._port_obj.get_owner() + rate_format[self._port_obj.last_factor_type] = ('blue', 'bold') if self._port_obj.is_acquired(): owner = format_text(owner, 'green') + else: owner = '' + return {"owner": owner, "state": "{0}".format(state), "speed": self._port_obj.get_formatted_speed() if self._port_obj else '', @@ -1038,21 +1044,19 @@ class CPortStats(CTRexStats): "-----": " ", "Tx bps L1": "{0} {1}".format(self.get_trend_gui("m_total_tx_bps_L1", show_value = False), - self.get("m_total_tx_bps_L1", format = True, suffix = "bps")), + self.get("m_total_tx_bps_L1", format = True, suffix = "bps", opts = rate_format['bpsl1'])), "Tx bps L2": "{0} {1}".format(self.get_trend_gui("m_total_tx_bps", show_value = False), - self.get("m_total_tx_bps", format = True, suffix = "bps")), + self.get("m_total_tx_bps", format = True, suffix = "bps", opts = rate_format['bps'])), - "Line Util.": "{0} {1}".format(self.get_trend_gui("m_percentage", show_value = False), - format_text( - self.get("m_percentage", format = True, suffix = "%") if self._port_obj else "", - 'bold')) if self._port_obj else "", + "Line Util.": "{0} {1}".format(self.get_trend_gui("m_percentage", show_value = False) if self._port_obj else "", + self.get("m_percentage", format = True, suffix = "%", opts = rate_format['percentage']) if self._port_obj else ""), "Rx bps": "{0} {1}".format(self.get_trend_gui("m_total_rx_bps", show_value = False), self.get("m_total_rx_bps", format = True, suffix = "bps")), "Tx pps": "{0} {1}".format(self.get_trend_gui("m_total_tx_pps", show_value = False), - self.get("m_total_tx_pps", format = True, suffix = "pps")), + self.get("m_total_tx_pps", format = True, suffix = "pps", opts = rate_format['pps'])), "Rx pps": "{0} {1}".format(self.get_trend_gui("m_total_rx_pps", show_value = False), self.get("m_total_rx_pps", format = True, suffix = "pps")), 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 26e64dae..bfb96950 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,7 +27,10 @@ class TextCodesStripper: def strip (s): return re.sub(TextCodesStripper.pattern, '', s) -def format_num (size, suffix = "", compact = True, opts = ()): +def format_num (size, suffix = "", compact = True, opts = None): + if opts is None: + opts = () + txt = "NaN" if type(size) == str: -- cgit 1.2.3-korg From a5cc1c90587d859d5f1e7f6d9ee49024cd2e9481 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Tue, 26 Jul 2016 15:01:26 +0300 Subject: regression: aggregate_results script exit with fail/success status of build. --- scripts/automation/regression/aggregate_results.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'scripts') diff --git a/scripts/automation/regression/aggregate_results.py b/scripts/automation/regression/aggregate_results.py index eb0632ec..c7c61ea6 100755 --- a/scripts/automation/regression/aggregate_results.py +++ b/scripts/automation/regression/aggregate_results.py @@ -622,11 +622,13 @@ if __name__ == '__main__': last_status = category_dict_status.get(scenario, 'Successful') # assume last is passed if no history if err or len(error_tests): # has fails + exit_status = 1 if is_good_status(last_status): current_status = 'Failure' else: current_status = 'Still Failing' else: + exit_status = 0 if is_good_status(last_status): current_status = 'Successful' else: @@ -652,3 +654,6 @@ if __name__ == '__main__': with open(args.output_titlefile, 'w') as f: print('Writing output file: %s' % args.output_titlefile) f.write(mailtitle_output) + +# exit + sys.exit(exit_status) -- cgit 1.2.3-korg From 344e3045d8346b4b204692e591e1556fc2333f97 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 27 Jul 2016 11:08:09 +0300 Subject: support for graceful shutdown --- .../trex_control_plane/stl/console/trex_console.py | 5 + .../trex_control_plane/stl/console/trex_tui.py | 6 +- .../stl/trex_stl_lib/trex_stl_client.py | 47 +++++- src/internal_api/trex_platform_api.h | 5 + src/main_dpdk.cpp | 183 ++++++++++++++++----- src/rpc-server/commands/trex_rpc_cmd_general.cpp | 26 +++ src/rpc-server/commands/trex_rpc_cmds.h | 2 + src/rpc-server/trex_rpc_cmds_table.cpp | 2 + src/stateless/cp/trex_stateless.cpp | 32 +++- src/stateless/cp/trex_stateless.h | 6 + 10 files changed, 265 insertions(+), 49 deletions(-) (limited to 'scripts') 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 7ad0cfa4..110457d6 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -279,6 +279,11 @@ class TRexConsole(TRexGeneralCmd): self.stateless_client.ping_line(line) + @verify_connected + def do_shutdown (self, line): + '''Sends the server a shutdown request\n''' + self.stateless_client.shutdown_line(line) + # set verbose on / off def do_verbose(self, line): '''Shows or set verbose mode\n''' 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 d3da738b..a69c4165 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -720,6 +720,7 @@ class AsyncKeysEngineConsole: self.ac = {'start' : client.start_line, 'stop' : client.stop_line, 'pause' : client.pause_line, + 'push' : client.push_line, 'resume' : client.resume_line, 'update' : client.update_line, 'connect' : client.connect_line, @@ -847,8 +848,9 @@ class AsyncKeysEngineConsole: # handle TAB for completing filenames def handle_tab_files (self, tokens): - # we support only start command with files - if tokens[0] != 'start': + + # only commands with files + if tokens[0] not in {'start', 'push'}: return # '-f' with no paramters - no partial and use current dir 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 a4f26f69..4e3d3092 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 @@ -1658,8 +1658,8 @@ class STLClient(object): """ - self.logger.pre_cmd( "Pinging the server on '{0}' port '{1}': ".format(self.connection_info['server'], - self.connection_info['sync_port'])) + 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) @@ -1667,6 +1667,30 @@ class STLClient(object): if not rc: raise STLError(rc) + @__api_check(True) + def server_shutdown (self, force = False): + """ + Sends the server a request for total shutdown + + :parameters: + force - shutdown server even if some ports are owned by another + user + + :raises: + + :exc:`STLError` + + """ + + self.logger.pre_cmd("Sending shutdown request for the server") + + rc = self._transmit("shutdown", params = {'force': force, 'user': self.username}) + + self.logger.post_cmd(rc) + + if not rc: + raise STLError(rc) + + @__api_check(True) def get_active_pgids(self): """ @@ -2107,7 +2131,7 @@ class STLClient(object): ports = ports if ports is not None else self.get_acquired_ports() ports = self._validate_port_list(ports) - validate_type('pcap_filename', pcap_filename, str) + validate_type('pcap_filename', pcap_filename, basestring) validate_type('ipg_usec', ipg_usec, (float, int, type(None))) validate_type('speedup', speedup, (float, int)) validate_type('count', count, int) @@ -2174,7 +2198,7 @@ class STLClient(object): ports = ports if ports is not None else self.get_acquired_ports() ports = self._validate_port_list(ports) - validate_type('pcap_filename', pcap_filename, str) + validate_type('pcap_filename', pcap_filename, basestring) validate_type('ipg_usec', ipg_usec, (float, int, type(None))) validate_type('speedup', speedup, (float, int)) validate_type('count', count, int) @@ -2444,6 +2468,21 @@ class STLClient(object): self.ping() return RC_OK() + @__console + def shutdown_line (self, line): + '''shutdown the server''' + parser = parsing_opts.gen_parser(self, + "shutdown", + self.shutdown_line.__doc__, + parsing_opts.FORCE) + + opts = parser.parse_args(line.split()) + if not opts: + return opts + + self.server_shutdown(force = opts.force) + return RC_OK() + @__console def connect_line (self, line): '''Connects to the TRex server and acquire ports''' diff --git a/src/internal_api/trex_platform_api.h b/src/internal_api/trex_platform_api.h index 7037584b..72bab7cb 100644 --- a/src/internal_api/trex_platform_api.h +++ b/src/internal_api/trex_platform_api.h @@ -161,6 +161,8 @@ public: virtual int get_cpu_util_full(cpu_util_full_t &result) const = 0; virtual int get_mbuf_util(Json::Value &result) const = 0; virtual CFlowStatParser *get_flow_stat_parser() const = 0; + virtual void mark_for_shutdown(const char *cause) const = 0; + virtual ~TrexPlatformApi() {} }; @@ -195,6 +197,7 @@ public: int get_active_pgids(flow_stat_active_t &result) const; int get_cpu_util_full(cpu_util_full_t &result) const; int get_mbuf_util(Json::Value &result) const; + void mark_for_shutdown(const char *cause) const; CFlowStatParser *get_flow_stat_parser() const; }; @@ -264,6 +267,8 @@ public: int get_mbuf_util(Json::Value &result) const {return 0;} CFlowStatParser *get_flow_stat_parser() const {return new CFlowStatParser();} + void mark_for_shutdown(const char *cause) const {} + private: int m_dp_core_count; }; diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index 7a6f972a..b6205f33 100644 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -20,6 +20,7 @@ */ #include #include +#include #include #include #include @@ -2486,40 +2487,40 @@ public: void DumpAllPorts(FILE *fd); void dump_json(std::string & json, bool baseline); private: - std::string get_field(std::string name,float &f); - std::string get_field(std::string name,uint64_t &f); - std::string get_field_port(int port,std::string name,float &f); - std::string get_field_port(int port,std::string name,uint64_t &f); + std::string get_field(const char *name, float &f); + std::string get_field(const char *name, uint64_t &f); + std::string get_field_port(int port, const char *name, float &f); + std::string get_field_port(int port, const char *name, uint64_t &f); }; -std::string CGlobalStats::get_field(std::string name,float &f){ +std::string CGlobalStats::get_field(const char *name, float &f){ char buff[200]; if(f <= -10.0 or f >= 10.0) - snprintf(buff, sizeof(buff), "\"%s\":%.1f,",name.c_str(),f); + snprintf(buff, sizeof(buff), "\"%s\":%.1f,",name,f); else - snprintf(buff, sizeof(buff), "\"%s\":%.3e,",name.c_str(),f); + snprintf(buff, sizeof(buff), "\"%s\":%.3e,",name,f); return (std::string(buff)); } -std::string CGlobalStats::get_field(std::string name,uint64_t &f){ +std::string CGlobalStats::get_field(const char *name, uint64_t &f){ char buff[200]; - snprintf(buff, sizeof(buff), "\"%s\":%llu,",name.c_str(), (unsigned long long)f); + snprintf(buff, sizeof(buff), "\"%s\":%llu,", name, (unsigned long long)f); return (std::string(buff)); } -std::string CGlobalStats::get_field_port(int port,std::string name,float &f){ +std::string CGlobalStats::get_field_port(int port, const char *name, float &f){ char buff[200]; if(f <= -10.0 or f >= 10.0) - snprintf(buff, sizeof(buff), "\"%s-%d\":%.1f,",name.c_str(),port,f); + snprintf(buff, sizeof(buff), "\"%s-%d\":%.1f,", name, port, f); else - snprintf(buff, sizeof(buff), "\"%s-%d\":%.3e,",name.c_str(),port,f); + snprintf(buff, sizeof(buff), "\"%s-%d\":%.3e,", name, port, f); return (std::string(buff)); } -std::string CGlobalStats::get_field_port(int port,std::string name,uint64_t &f){ +std::string CGlobalStats::get_field_port(int port, const char *name, uint64_t &f){ char buff[200]; - snprintf(buff, sizeof(buff), "\"%s-%d\":%llu,",name.c_str(),port, (unsigned long long)f); + snprintf(buff, sizeof(buff), "\"%s-%d\":%llu,",name, port, (unsigned long long)f); return (std::string(buff)); } @@ -2538,8 +2539,8 @@ void CGlobalStats::dump_json(std::string & json, bool baseline){ snprintf(ts_buff , sizeof(ts_buff), "\"ts\":{\"value\":%lu, \"freq\":%lu},", os_get_hr_tick_64(), os_get_hr_freq()); json+= std::string(ts_buff); -#define GET_FIELD(f) get_field(std::string(#f),f) -#define GET_FIELD_PORT(p,f) get_field_port(p,std::string(#f),lp->f) +#define GET_FIELD(f) get_field(#f, f) +#define GET_FIELD_PORT(p,f) get_field_port(p, #f, lp->f) json+=GET_FIELD(m_cpu_util); json+=GET_FIELD(m_cpu_util_raw); @@ -2781,6 +2782,7 @@ public: m_expected_cps=0.0; m_expected_bps=0.0; m_trex_stateless = NULL; + m_mark_for_shutdown = false; } bool Create(); @@ -2795,7 +2797,19 @@ public: bool is_all_links_are_up(bool dump=false); int reset_counters(); + /** + * mark for shutdown + * on the next check - the control plane will + * call shutdown() + */ + void mark_for_shutdown(const char *cause) { + printf("\n *** TRex shutting down - cause: '%s'\n", cause); + m_mark_for_shutdown = true; + } + private: + void register_signals(); + /* try to stop all datapath cores and RX core */ void try_stop_all_cores(); /* send message to all dp cores */ @@ -2803,6 +2817,16 @@ private: int send_message_to_rx(TrexStatelessCpToRxMsgBase *msg); void check_for_dp_message_from_core(int thread_id); + bool is_marked_for_shutdown() const { + return m_mark_for_shutdown; + } + + /** + * shutdown sequence + * + */ + void shutdown(); + public: void check_for_dp_messages(); int start_master_statefull(); @@ -2898,6 +2922,7 @@ private: std::mutex m_cp_lock; TrexMonitor m_monitor; + bool m_mark_for_shutdown; public: TrexStateless *m_trex_stateless; @@ -3272,9 +3297,32 @@ int CGlobalTRex::ixgbe_start(void){ return (0); } +static void trex_termination_handler(int signum); + +void CGlobalTRex::register_signals() { + struct sigaction action; + + /* handler */ + action.sa_handler = trex_termination_handler; + + /* blocked signals during handling */ + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGINT); + sigaddset(&action.sa_mask, SIGTERM); + + /* no flags */ + action.sa_flags = 0; + + /* register */ + sigaction(SIGINT, &action, NULL); + sigaction(SIGTERM, &action, NULL); +} + bool CGlobalTRex::Create(){ CFlowsYamlInfo pre_yaml_info; + register_signals(); + m_stats_cnt =0; if (!get_is_stateless()) { pre_yaml_info.load_from_yaml_file(CGlobalInfo::m_options.cfg_file); @@ -3908,17 +3956,16 @@ CGlobalTRex::handle_slow_path(bool &was_stopped) { if ( CGlobalInfo::m_options.preview.get_no_keyboard() ==false ) { if ( m_io_modes.handle_io_modes() ) { - printf(" CTRL -C ... \n"); - was_stopped=true; + mark_for_shutdown("CTRL + C detected"); return false; } } if ( sanity_check() ) { - printf(" Test was stopped \n"); - was_stopped=true; + mark_for_shutdown("Test was stopped"); return false; } + if (m_io_modes.m_g_mode != CTrexGlobalIoMode::gDISABLE ) { fprintf(stdout,"\033[2J"); fprintf(stdout,"\033[2H"); @@ -4032,6 +4079,41 @@ CGlobalTRex::handle_fast_path() { return true; } + +/** + * shutdown sequence + * + */ +void CGlobalTRex::shutdown() { + + /* first stop the WD */ + TrexWatchDog::getInstance().stop(); + + /* stateless shutdown */ + if (get_is_stateless()) { + m_trex_stateless->shutdown(); + } + + if (!is_all_cores_finished()) { + try_stop_all_cores(); + } + + m_mg.stop(); + + delay(1000); + + /* shutdown drivers */ + for (int i = 0; i < m_max_ports; i++) { + rte_eth_dev_stop(i); + } + + if (is_marked_for_shutdown()) { + /* we should stop latency and exit to stop agents */ + exit(-1); + } +} + + int CGlobalTRex::run_in_master() { bool was_stopped=false; @@ -4052,7 +4134,7 @@ int CGlobalTRex::run_in_master() { TrexWatchDog::getInstance().start(); - while ( true ) { + while (!is_marked_for_shutdown()) { /* fast path */ if (!handle_fast_path()) { @@ -4079,22 +4161,9 @@ int CGlobalTRex::run_in_master() { /* on exit release the lock */ cp_lock.unlock(); - /* first stop the WD */ - TrexWatchDog::getInstance().stop(); - - if (!is_all_cores_finished()) { - /* probably CLTR-C */ - try_stop_all_cores(); - } - - m_mg.stop(); - - - delay(1000); - if ( was_stopped ){ - /* we should stop latency and exit to stop agents */ - exit(-1); - } + /* shutdown everything gracefully */ + shutdown(); + return (0); } @@ -5702,6 +5771,37 @@ struct rte_mbuf * rte_mbuf_convert_to_one_seg(struct rte_mbuf *m){ return(r); } +/** + * handle a signal for termination + * + * @author imarom (7/27/2016) + * + * @param signum + */ +static void trex_termination_handler(int signum) { + std::stringstream ss; + + /* be sure that this was given on the main process */ + assert(rte_eal_process_type() == RTE_PROC_PRIMARY); + + const char *signame = ""; + switch (signum) { + case SIGINT: + signame = "SIGINT"; + break; + + case SIGTERM: + signame = "SIGTERM"; + break; + + default: + assert(0); + } + + ss << "receieved signal '" << signame << "'"; + g_trex.mark_for_shutdown(ss.str().c_str()); +} + /*********************************************************** * platfrom API object * TODO: REMOVE THIS TO A SEPERATE FILE @@ -5871,3 +5971,12 @@ int TrexDpdkPlatformApi::get_mbuf_util(Json::Value &mbuf_pool) const { CFlowStatParser *TrexDpdkPlatformApi::get_flow_stat_parser() const { return CTRexExtendedDriverDb::Ins()->get_drv()->get_flow_stat_parser(); } + +/** + * marks the control plane for a total server shutdown + * + * @author imarom (7/27/2016) + */ +void TrexDpdkPlatformApi::mark_for_shutdown(const char *cause) const { + g_trex.mark_for_shutdown(cause); +} diff --git a/src/rpc-server/commands/trex_rpc_cmd_general.cpp b/src/rpc-server/commands/trex_rpc_cmd_general.cpp index 27010e0e..c3fba8e1 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_general.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_general.cpp @@ -91,6 +91,32 @@ TrexRpcCmdPing::_run(const Json::Value ¶ms, Json::Value &result) { return (TREX_RPC_CMD_OK); } +/** + * shutdown command + */ +trex_rpc_cmd_rc_e +TrexRpcCmdShutdown::_run(const Json::Value ¶ms, Json::Value &result) { + + const string &user = parse_string(params, "user", result); + bool force = parse_bool(params, "force", result); + + /* verify every port is either free or owned by the issuer */ + for (auto port : get_stateless_obj()->get_port_list()) { + TrexPortOwner &owner = port->get_owner(); + if ( (!owner.is_free()) && (!owner.is_owned_by(user)) && !force) { + std::stringstream ss; + ss << "port " << int(port->get_port_id()) << " is owned by '" << owner.get_name() << "' - specify 'force' for override"; + generate_execute_err(result, ss.str()); + } + } + + /* signal that we got a shutdown request */ + get_stateless_obj()->get_platform_api()->mark_for_shutdown("server received RPC 'shutdown' request"); + + result["result"] = Json::objectValue; + return (TREX_RPC_CMD_OK); +} + /** * query command */ diff --git a/src/rpc-server/commands/trex_rpc_cmds.h b/src/rpc-server/commands/trex_rpc_cmds.h index 2776727d..24b95227 100644 --- a/src/rpc-server/commands/trex_rpc_cmds.h +++ b/src/rpc-server/commands/trex_rpc_cmds.h @@ -133,5 +133,7 @@ TREX_RPC_CMD_DEFINE(TrexRpcCmdValidate, "validate", 2, false, APIClass::API_CLAS TREX_RPC_CMD_DEFINE(TrexRpcCmdPushRemote, "push_remote", 6, true, APIClass::API_CLASS_TYPE_CORE); +TREX_RPC_CMD_DEFINE(TrexRpcCmdShutdown, "shutdown", 2, false, APIClass::API_CLASS_TYPE_CORE); + #endif /* __TREX_RPC_CMD_H__ */ diff --git a/src/rpc-server/trex_rpc_cmds_table.cpp b/src/rpc-server/trex_rpc_cmds_table.cpp index 6144d265..762dd614 100644 --- a/src/rpc-server/trex_rpc_cmds_table.cpp +++ b/src/rpc-server/trex_rpc_cmds_table.cpp @@ -67,6 +67,8 @@ TrexRpcCommandsTable::TrexRpcCommandsTable() { register_command(new TrexRpcCmdValidate()); register_command(new TrexRpcCmdPushRemote()); + + register_command(new TrexRpcCmdShutdown()); } diff --git a/src/stateless/cp/trex_stateless.cpp b/src/stateless/cp/trex_stateless.cpp index 8633897e..6d80539c 100644 --- a/src/stateless/cp/trex_stateless.cpp +++ b/src/stateless/cp/trex_stateless.cpp @@ -68,6 +68,8 @@ TrexStateless::TrexStateless(const TrexStatelessCfg &cfg) { */ TrexStateless::~TrexStateless() { + shutdown(); + /* release memory for ports */ for (auto port : m_ports) { delete port; @@ -75,15 +77,33 @@ TrexStateless::~TrexStateless() { m_ports.clear(); /* stops the RPC server */ - m_rpc_server->stop(); - delete m_rpc_server; - - m_rpc_server = NULL; + if (m_rpc_server) { + delete m_rpc_server; + m_rpc_server = NULL; + } - delete m_platform_api; - m_platform_api = NULL; + if (m_platform_api) { + delete m_platform_api; + m_platform_api = NULL; + } } +/** +* shutdown the server +*/ +void TrexStateless::shutdown() { + + /* stop ports */ + for (TrexStatelessPort *port : m_ports) { + /* safe to call stop even if not active */ + port->stop_traffic(); + } + + /* shutdown the RPC server */ + if (m_rpc_server) { + m_rpc_server->stop(); + } +} /** * starts the control plane side diff --git a/src/stateless/cp/trex_stateless.h b/src/stateless/cp/trex_stateless.h index 83ab6976..7ea669df 100644 --- a/src/stateless/cp/trex_stateless.h +++ b/src/stateless/cp/trex_stateless.h @@ -131,6 +131,11 @@ public: uint8_t get_dp_core_count(); + /** + * shutdown the server + */ + void shutdown(); + /** * fetch all the stats * @@ -188,6 +193,7 @@ protected: /* API */ APIClass m_api_classes[APIClass::API_CLASS_TYPE_MAX]; + }; /** -- cgit 1.2.3-korg