diff options
Diffstat (limited to 'scripts/automation/trex_control_plane/stl')
10 files changed, 434 insertions, 200 deletions
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 5d23d8da..b23b5f1f 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -230,14 +230,17 @@ class TRexConsole(TRexGeneralCmd): self.save_console_history() lines = line.split(';') - - for line in lines: - stop = self.onecmd(line) - stop = self.postcmd(stop, line) - if stop: - return "quit" - - return "" + try: + for line in lines: + stop = self.onecmd(line) + stop = self.postcmd(stop, line) + if stop: + return "quit" + + return "" + except STLError as e: + print(e) + return '' def postcmd(self, stop, line): @@ -364,7 +367,7 @@ class TRexConsole(TRexGeneralCmd): 'help': "an history item index", 'default': 0}) - parser = parsing_opts.gen_parser(self, + parser = parsing_opts.gen_parser(self.stateless_client, "history", self.do_history.__doc__, item) @@ -551,16 +554,16 @@ class TRexConsole(TRexGeneralCmd): @verify_connected def do_tui (self, line): '''Shows a graphical console\n''' - parser = parsing_opts.gen_parser(self, + parser = parsing_opts.gen_parser(self.stateless_client, "tui", self.do_tui.__doc__, parsing_opts.XTERM, parsing_opts.LOCKED) opts = parser.parse_args(line.split()) - if opts is None: - return + if not opts: + return opts if opts.xterm: if not os.path.exists('/usr/bin/xterm'): print(format_text("XTERM does not exists on this machine", 'bold')) @@ -784,14 +787,18 @@ def show_intro (logger, c): # find out which NICs the server has port_types = {} for port in x['ports']: - key = (port['speed'], port['driver']) - if not key in port_types: + if 'supp_speeds' in port: + speed = max(port['supp_speeds']) // 1000 + else: + speed = port['speed'] + key = (speed, port.get('description', port['driver'])) + if key not in port_types: port_types[key] = 0 port_types[key] += 1 port_line = '' for k, v in port_types.items(): - port_line += "{0} x {1}Gbps @ {2}".format(v, k[0], k[1]) + port_line += "{0} x {1}Gbps @ {2}\t".format(v, k[0], k[1]) logger.log(format_text("\nServer Info:\n", 'underline')) logger.log("Server version: {:>}".format(format_text(ver, 'bold'))) 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 82aa932d..80a4c4dc 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 @@ -222,18 +222,18 @@ class EventsHandler(object): # dispatcher for server async events (port started, port stopped and etc.) - def on_async_event (self, type, data): + def on_async_event (self, event_type, data): # DP stopped show_event = False # port started - if (type == 0): + if (event_type == 0): port_id = int(data['port_id']) ev = "Port {0} has started".format(port_id) self.__async_event_port_started(port_id) # port stopped - elif (type == 1): + elif (event_type == 1): port_id = int(data['port_id']) ev = "Port {0} has stopped".format(port_id) @@ -242,7 +242,7 @@ class EventsHandler(object): # port paused - elif (type == 2): + elif (event_type == 2): port_id = int(data['port_id']) ev = "Port {0} has paused".format(port_id) @@ -250,7 +250,7 @@ class EventsHandler(object): self.__async_event_port_paused(port_id) # port resumed - elif (type == 3): + elif (event_type == 3): port_id = int(data['port_id']) ev = "Port {0} has resumed".format(port_id) @@ -258,7 +258,7 @@ class EventsHandler(object): self.__async_event_port_resumed(port_id) # port finished traffic - elif (type == 4): + elif (event_type == 4): port_id = int(data['port_id']) ev = "Port {0} job done".format(port_id) @@ -267,7 +267,7 @@ class EventsHandler(object): show_event = True # port was acquired - maybe stolen... - elif (type == 5): + elif (event_type == 5): session_id = data['session_id'] port_id = int(data['port_id']) @@ -297,7 +297,7 @@ class EventsHandler(object): # port was released - elif (type == 6): + elif (event_type == 6): port_id = int(data['port_id']) who = data['who'] session_id = data['session_id'] @@ -315,13 +315,31 @@ class EventsHandler(object): if session_id != self.client.session_id: self.__async_event_port_released(port_id) - elif (type == 7): + elif (event_type == 7): port_id = int(data['port_id']) ev = "port {0} job failed".format(port_id) show_event = True + # port attr changed + elif (event_type == 8): + port_id = int(data['port_id']) + if data['attr'] == self.client.ports[port_id].attr: + return # false alarm + old_info = self.client.ports[port_id].get_info() + self.__async_event_port_attr_changed(port_id, data['attr']) + new_info = self.client.ports[port_id].get_info() + ev = "port {0} attributes changed".format(port_id) + for key, old_val in old_info.items(): + new_val = new_info[key] + if old_val != new_val: + ev += '\n {key}: {old} -> {new}'.format( + key = key, + old = old_val.lower() if type(old_val) is str else old_val, + new = new_val.lower() if type(new_val) is str else new_val) + show_event = True + # server stopped - elif (type == 100): + elif (event_type == 100): ev = "Server has stopped" self.__async_event_server_stopped() show_event = True @@ -372,6 +390,9 @@ class EventsHandler(object): def __async_event_server_stopped (self): self.client.connected = False + def __async_event_port_attr_changed (self, port_id, attr): + if port_id in self.client.ports: + self.client.ports[port_id].async_event_port_attr_changed(attr) # add event to log def __add_event_log (self, origin, ev_type, msg, show = False): @@ -560,11 +581,14 @@ class STLClient(object): self.util_stats = trex_stl_stats.CUtilStats(self) + self.xstats = trex_stl_stats.CXStats(self) + self.stats_generator = trex_stl_stats.CTRexInfoGenerator(self.global_stats, self.ports, self.flow_stats, self.latency_stats, self.util_stats, + self.xstats, self.async_client.monitor) @@ -884,7 +908,7 @@ class STLClient(object): # clear stats - def __clear_stats(self, port_id_list, clear_global, clear_flow_stats, clear_latency_stats): + def __clear_stats(self, port_id_list, clear_global, clear_flow_stats, clear_latency_stats, clear_xstats): # we must be sync with the server self.async_client.barrier() @@ -901,6 +925,9 @@ class STLClient(object): if clear_latency_stats: self.latency_stats.clear_stats() + if clear_xstats: + self.xstats.clear_stats() + self.logger.log_cmd("Clearing stats on port(s) {0}:".format(port_id_list)) return RC @@ -1785,6 +1812,25 @@ class STLClient(object): self.logger.pre_cmd('Getting Utilization stats') return self.util_stats.get_stats() + @__api_check(True) + def get_xstats(self, port_id): + print(port_id) + """ + Get extended stats of port: all the counters as dict. + + :parameters: + port_id: int + + :returns: + Dict with names of counters as keys and values of uint64. Actual keys may vary per NIC. + + :raises: + + :exc:`STLError` + + """ + self.logger.pre_cmd('Getting xstats') + return self.xstats.get_stats(port_id) + @__api_check(True) def reset(self, ports = None): @@ -2004,6 +2050,11 @@ class STLClient(object): validate_type('total', total, bool) validate_type('core_mask', core_mask, (int, list)) + # verify link status + ports_link_down = [port_id for port_id in ports if self.ports[port_id].attr.get('link',{}).get('up') == False] + if not force and ports_link_down: + raise STLError("Port(s) %s - link DOWN - check the connection or specify 'force'" % ports_link_down) + ######################### # decode core mask argument decoded_mask = self.__decode_core_mask(ports, core_mask) @@ -2462,7 +2513,7 @@ class STLClient(object): @__api_check(False) - def clear_stats (self, ports = None, clear_global = True, clear_flow_stats = True, clear_latency_stats = True): + def clear_stats (self, ports = None, clear_global = True, clear_flow_stats = True, clear_latency_stats = True, clear_xstats = True): """ Clear stats on port(s) @@ -2479,6 +2530,9 @@ class STLClient(object): clear_latency_stats : bool Clear the latency stats + clear_xstats : bool + Clear the extended stats + :raises: + :exc:`STLError` @@ -2491,7 +2545,7 @@ class STLClient(object): if not type(clear_global) is bool: raise STLArgumentError('clear_global', clear_global) - rc = self.__clear_stats(ports, clear_global, clear_flow_stats, clear_latency_stats) + rc = self.__clear_stats(ports, clear_global, clear_flow_stats, clear_latency_stats, clear_xstats) if not rc: raise STLError(rc) @@ -2572,15 +2626,18 @@ class STLClient(object): @__api_check(True) - def set_port_attr (self, ports = None, promiscuous = None): + def set_port_attr (self, ports = None, promiscuous = None, link_up = None, led_on = None, flow_ctrl = None): """ Set port attributes :parameters: promiscuous - True or False + link_up - True or False + led_on - True or False + flow_ctrl - 0: disable all, 1: enable tx side, 2: enable rx side, 3: full enable :raises: - None + + :exe:'STLError' """ @@ -2589,11 +2646,20 @@ class STLClient(object): # check arguments validate_type('promiscuous', promiscuous, (bool, type(None))) + validate_type('link_up', link_up, (bool, type(None))) + validate_type('led_on', led_on, (bool, type(None))) + validate_type('flow_ctrl', flow_ctrl, (int, type(None))) # build attributes attr_dict = {} if promiscuous is not None: - attr_dict['promiscuous'] = {'enabled': bool(promiscuous)} + attr_dict['promiscuous'] = {'enabled': promiscuous} + if link_up is not None: + attr_dict['link_status'] = {'up': link_up} + if led_on is not None: + attr_dict['led_status'] = {'on': led_on} + if flow_ctrl is not None: + attr_dict['flow_ctrl_mode'] = {'mode': flow_ctrl} # no attributes to set if not attr_dict: @@ -3167,20 +3233,39 @@ class STLClient(object): "port_attr", self.set_port_attr_line.__doc__, parsing_opts.PORT_LIST_WITH_ALL, - parsing_opts.PROMISCUOUS_SWITCH) + parsing_opts.PROMISCUOUS, + parsing_opts.LINK_STATUS, + parsing_opts.LED_STATUS, + parsing_opts.FLOW_CTRL, + parsing_opts.SUPPORTED, + ) opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) if not opts: return opts + opts.prom = parsing_opts.ON_OFF_DICT.get(opts.prom) + opts.link = parsing_opts.UP_DOWN_DICT.get(opts.link) + opts.led = parsing_opts.ON_OFF_DICT.get(opts.led) + opts.flow_ctrl = parsing_opts.FLOW_CTRL_DICT.get(opts.flow_ctrl) + # if no attributes - fall back to printing the status - if opts.prom is None: + if not filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl, opts.supp]): self.show_stats_line("--ps --port {0}".format(' '.join(str(port) for port in opts.ports))) return - self.set_port_attr(opts.ports, opts.prom) - return RC_OK() - + if opts.supp: + info = self.ports[0].get_info() # assume for now all ports are same + print('') + print('Supported attributes for current NICs:') + print(' Promiscuous: yes') + print(' Link status: %s' % info['link_change_supported']) + print(' LED status: %s' % info['led_change_supported']) + print(' Flow control: %s' % info['fc_supported']) + print('') + else: + return self.set_port_attr(opts.ports, opts.prom, opts.link, opts.led, opts.flow_ctrl) + @__console def show_profile_line (self, line): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py index 609ea076..1461fcec 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py @@ -182,7 +182,7 @@ class JsonRpcClient(object): tries += 1 if tries > 5: self.disconnect() - return RC_ERR("*** [RPC] - Failed to get server response at {0}".format(self.transport)) + return RC_ERR("*** [RPC] - Failed to get server response from {0}".format(self.transport)) return response diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py index aa002d59..dc06f9fb 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py @@ -738,8 +738,8 @@ class STLVmFlowVarRepetableRandom(CTRexVmDescBase): .. code-block:: python - :caption: Example1 + # Example1 # input , 1 byte or random with limit of 5 STLVmFlowVarRepetableRandom("var1",size=1,limit=5) 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 f0e3b109..cec3761f 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 @@ -5,6 +5,7 @@ from .trex_stl_packet_builder_scapy import STLPktBuilder from .trex_stl_streams import STLStream from .trex_stl_types import * from . import trex_stl_stats +from .utils.constants import FLOW_CTRL_DICT_REVERSED import base64 import copy @@ -42,9 +43,9 @@ class Port(object): STATES_MAP = {STATE_DOWN: "DOWN", STATE_IDLE: "IDLE", STATE_STREAMS: "IDLE", - STATE_TX: "ACTIVE", + STATE_TX: "TRANSMITTING", STATE_PAUSE: "PAUSE", - STATE_PCAP_TX : "ACTIVE"} + STATE_PCAP_TX : "TRANSMITTING"} def __init__ (self, port_id, user, comm_link, session_id, info): @@ -251,8 +252,9 @@ class Port(object): # attributes self.attr = rc.data()['attr'] + if 'speed' in rc.data(): + self.info['speed'] = rc.data()['speed'] // 1000 - return self.ok() @@ -577,7 +579,7 @@ class Port(object): return self.err(rc.err()) - self.attr.update(attr_dict) + #self.attr.update(attr_dict) return self.ok() @@ -650,13 +652,46 @@ class Port(object): def get_info (self): info = dict(self.info) - info['status'] = self.get_port_state_name() + info['status'] = self.get_port_state_name() - if self.attr.get('promiscuous'): + if 'link' in self.attr: + info['link'] = 'UP' if self.attr['link']['up'] else 'DOWN' + else: + info['link'] = 'N/A' + + if 'fc' in self.attr: + info['fc'] = FLOW_CTRL_DICT_REVERSED.get(self.attr['fc']['mode'], 'N/A') + else: + info['fc'] = 'N/A' + + if 'promiscuous' in self.attr: info['prom'] = "on" if self.attr['promiscuous']['enabled'] else "off" else: info['prom'] = "N/A" + if 'description' not in info: + info['description'] = "N/A" + + if 'is_fc_supported' in info: + info['fc_supported'] = 'yes' if info['is_fc_supported'] else 'no' + else: + info['fc_supported'] = 'N/A' + + if 'is_led_supported' in info: + info['led_change_supported'] = 'yes' if info['is_led_supported'] else 'no' + else: + info['led_change_supported'] = 'N/A' + + if 'is_link_supported' in info: + info['link_change_supported'] = 'yes' if info['is_link_supported'] else 'no' + else: + info['link_change_supported'] = 'N/A' + + if 'is_virtual' in info: + info['is_virtual'] = 'yes' if info['is_virtual'] else 'no' + else: + info['is_virtual'] = 'N/A' + return info @@ -672,6 +707,7 @@ class Port(object): info = self.get_info() return {"driver": info['driver'], + "description": info.get('description', 'N/A')[:18], "HW src mac": info['hw_macaddr'], "SW src mac": info['src_macaddr'], "SW dst mac": info['dst_macaddr'], @@ -679,9 +715,11 @@ class Port(object): "NUMA Node": info['numa'], "--": "", "---": "", - "maximum": "{speed} Gb/s".format(speed=info['speed']), - "status": info['status'], - "promiscuous" : info['prom'] + "link speed": "{speed} Gb/s".format(speed=info['speed']), + "port status": info['status'], + "link status": info['link'], + "promiscuous" : info['prom'], + "flow ctrl" : info['fc'], } def clear_stats(self): @@ -726,6 +764,10 @@ class Port(object): self.state = self.STATE_STREAMS self.last_factor_type = None + def async_event_port_attr_changed (self, attr): + self.info['speed'] = attr['speed'] // 1000 + self.attr = attr + # rest of the events are used for TUI / read only sessions def async_event_port_stopped (self): if not self.is_acquired(): 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 5d9cdcaa..9f601484 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 @@ -3,6 +3,7 @@ from .utils import text_tables from .utils.text_opts import format_text, format_threshold, format_num from .trex_stl_types import StatNotAvailable, is_integer +from .trex_stl_exceptions import STLError from collections import namedtuple, OrderedDict, deque import sys @@ -23,8 +24,10 @@ LATENCY_STATS = 'ls' LATENCY_HISTOGRAM = 'lh' CPU_STATS = 'c' MBUF_STATS = 'm' +EXTENDED_STATS = 'x' +EXTENDED_INC_ZERO_STATS = 'xz' -ALL_STATS_OPTS = [GLOBAL_STATS, PORT_STATS, PORT_STATUS, STREAMS_STATS, LATENCY_STATS, PORT_GRAPH, LATENCY_HISTOGRAM, CPU_STATS, MBUF_STATS] +ALL_STATS_OPTS = [GLOBAL_STATS, PORT_STATS, PORT_STATUS, STREAMS_STATS, LATENCY_STATS, PORT_GRAPH, LATENCY_HISTOGRAM, CPU_STATS, MBUF_STATS, EXTENDED_STATS, EXTENDED_INC_ZERO_STATS] COMPACT = [GLOBAL_STATS, PORT_STATS] GRAPH_PORT_COMPACT = [GLOBAL_STATS, PORT_GRAPH] SS_COMPAT = [GLOBAL_STATS, STREAMS_STATS] # stream stats @@ -147,12 +150,13 @@ class CTRexInfoGenerator(object): STLClient and the ports. """ - def __init__(self, global_stats_ref, ports_dict_ref, rx_stats_ref, latency_stats_ref, util_stats_ref, async_monitor): + def __init__(self, global_stats_ref, ports_dict_ref, rx_stats_ref, latency_stats_ref, util_stats_ref, xstats_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._xstats_ref = xstats_ref self._async_monitor = async_monitor def generate_single_statistic(self, port_id_list, statistic_type): @@ -183,6 +187,12 @@ class CTRexInfoGenerator(object): elif statistic_type == MBUF_STATS: return self._generate_mbuf_util_stats() + elif statistic_type == EXTENDED_STATS: + return self._generate_xstats(port_id_list, include_zero_lines = False) + + elif statistic_type == EXTENDED_INC_ZERO_STATS: + return self._generate_xstats(port_id_list, include_zero_lines = True) + else: # ignore by returning empty object return {} @@ -205,10 +215,9 @@ class CTRexInfoGenerator(object): def _generate_global_stats(self): global_stats = self._global_stats - stats_data = OrderedDict([("connection", "{host}, Port {port}".format(host=global_stats.connection_info.get("server"), + stats_data_left = OrderedDict([("connection", "{host}, Port {port}".format(host=global_stats.connection_info.get("server"), port=global_stats.connection_info.get("sync_port"))), - ("version", "{ver}, UUID: {uuid}".format(ver=global_stats.server_version.get("version", "N/A"), - uuid="N/A")), + ("version", "{ver}".format(ver=global_stats.server_version.get("version", "N/A"))), ("cpu_util.", "{0}% @ {2} cores ({3} per port) {1}".format( format_threshold(round_float(global_stats.get("m_cpu_util")), [85, 100], [0, 85]), global_stats.get_trend_gui("m_cpu_util", use_raw = True), @@ -221,14 +230,13 @@ class CTRexInfoGenerator(object): ("async_util.", "{0}% / {1}".format( format_threshold(round_float(self._async_monitor.get_cpu_util()), [85, 100], [0, 85]), format_num(self._async_monitor.get_bps() / 8.0, suffix = "B/sec"))), - - - (" ", ""), + ]) + stats_data_right = OrderedDict([ ("total_tx_L2", "{0} {1}".format( global_stats.get("m_tx_bps", format=True, suffix="b/sec"), global_stats.get_trend_gui("m_tx_bps"))), - ("total_tx_L1", "{0} {1}".format( global_stats.get("m_tx_bps_L1", format=True, suffix="b/sec"), + ("total_tx_L1", "{0} {1}".format( global_stats.get("m_tx_bps_L1", format=True, suffix="b/sec"), global_stats.get_trend_gui("m_tx_bps_L1"))), ("total_rx", "{0} {1}".format( global_stats.get("m_rx_bps", format=True, suffix="b/sec"), @@ -237,8 +245,6 @@ class CTRexInfoGenerator(object): ("total_pps", "{0} {1}".format( global_stats.get("m_tx_pps", format=True, suffix="pkt/sec"), global_stats.get_trend_gui("m_tx_pps"))), - #(" ", ""), - ("drop_rate", "{0}".format( format_num(global_stats.get("m_rx_drop_bps"), suffix = 'b/sec', opts = 'green' if (global_stats.get("m_rx_drop_bps")== 0) else 'red'), @@ -248,19 +254,29 @@ class CTRexInfoGenerator(object): suffix = 'pkts', compact = False, opts = 'green' if (global_stats.get_rel("m_total_queue_full")== 0) else 'red'))), - - ] - ) + ]) # build table representation stats_table = text_tables.TRexTextInfo() stats_table.set_cols_align(["l", "l"]) + stats_table.set_deco(0) + stats_table.set_cols_width([50, 45]) + max_lines = max(len(stats_data_left), len(stats_data_right)) + for line_num in range(max_lines): + row = [] + if line_num < len(stats_data_left): + key = list(stats_data_left.keys())[line_num] + row.append('{:<12} : {}'.format(key, stats_data_left[key])) + else: + row.append('') + if line_num < len(stats_data_right): + key = list(stats_data_right.keys())[line_num] + row.append('{:<12} : {}'.format(key, stats_data_right[key])) + else: + row.append('') + stats_table.add_row(row) - stats_table.add_rows([[k.replace("_", " ").title(), v] - for k, v in stats_data.items()], - header=False) - - return {"global_statistics": ExportableStats(stats_data, stats_table)} + return {"global_statistics": ExportableStats(None, stats_table)} def _generate_streams_stats (self): flow_stats = self._rx_stats_ref @@ -325,7 +341,7 @@ class CTRexInfoGenerator(object): def _generate_latency_stats(self): lat_stats = self._latency_stats_ref - latency_window_size = 10 + latency_window_size = 14 # for TUI - maximum 5 pg_ids = list(filter(is_intable, lat_stats.latest_stats.keys()))[:5] @@ -439,7 +455,7 @@ class CTRexInfoGenerator(object): stats_table.set_cols_width([10, 3, 6] + [3] * (show_len - 1)) stats_table.set_cols_dtype(['t'] * (show_len + 2)) - for i in range(min(14, len(cpu_stats))): + for i in range(min(18, len(cpu_stats))): history = cpu_stats[i]["history"] ports = cpu_stats[i]["ports"] avg = int(round(sum(history[:avg_len]) / avg_len)) @@ -504,6 +520,28 @@ class CTRexInfoGenerator(object): stats_table.add_row(['No Data.']) return {'mbuf_util': ExportableStats(None, stats_table)} + def _generate_xstats(self, port_id_list, include_zero_lines = False): + relevant_ports = [port.port_id for port in self.__get_relevant_ports(port_id_list)] + # get the data on relevant ports + xstats_data = OrderedDict() + for port_id in relevant_ports: + for key, val in self._xstats_ref.get_stats(port_id).items(): + if key not in xstats_data: + xstats_data[key] = [] + xstats_data[key].append(val) + + # put into table + stats_table = text_tables.TRexTextTable() + stats_table.header(['Name:'] + ['Port %s:' % port_id for port_id in relevant_ports]) + stats_table.set_cols_align(['l'] + ['r'] * len(relevant_ports)) + stats_table.set_cols_width([30] + [15] * len(relevant_ports)) + stats_table.set_cols_dtype(['t'] * (len(relevant_ports) + 1)) + for key, arr in xstats_data.items(): + if include_zero_lines or list(filter(None, arr)): + key = key[:28] + stats_table.add_row([key] + arr) + return {'xstats:': 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 @@ -557,6 +595,7 @@ class CTRexInfoGenerator(object): return_stats_data = {} per_field_stats = OrderedDict([("owner", []), + ('link', []), ("state", []), ("speed", []), ("CPU util.", []), @@ -584,8 +623,7 @@ class CTRexInfoGenerator(object): ("oerrors", []), ("ierrors", []), - ] - ) + ]) total_stats = CPortStats(None) @@ -625,9 +663,12 @@ class CTRexInfoGenerator(object): return_stats_data = {} per_field_status = OrderedDict([("driver", []), - ("maximum", []), - ("status", []), + ("description", []), + ("link status", []), + ("link speed", []), + ("port status", []), ("promiscuous", []), + ("flow ctrl", []), ("--", []), ("HW src mac", []), ("SW src mac", []), @@ -1003,6 +1044,7 @@ class CPortStats(CTRexStats): def _update(self, snapshot): + speed = self._port_obj.get_speed_bps() # L1 bps tx_bps = snapshot.get("m_total_tx_bps") @@ -1015,22 +1057,34 @@ class CPortStats(CTRexStats): bps_rx_L1 = calc_bps_L1(rx_bps, rx_pps) snapshot['m_total_tx_bps_L1'] = bps_tx_L1 - snapshot['m_tx_util'] = (bps_tx_L1 / self._port_obj.get_speed_bps()) * 100.0 + if speed: + snapshot['m_tx_util'] = (bps_tx_L1 / speed) * 100.0 + else: + snapshot['m_tx_util'] = 0 snapshot['m_total_rx_bps_L1'] = bps_rx_L1 - snapshot['m_rx_util'] = (bps_rx_L1 / self._port_obj.get_speed_bps()) * 100.0 + if speed: + snapshot['m_rx_util'] = (bps_rx_L1 / speed) * 100.0 + else: + snapshot['m_rx_util'] = 0 # TX line util not smoothed diff_tx_pkts = snapshot.get('opackets', 0) - self.latest_stats.get('opackets', 0) diff_tx_bytes = snapshot.get('obytes', 0) - self.latest_stats.get('obytes', 0) tx_bps_L1 = calc_bps_L1(8.0 * diff_tx_bytes / ts_diff, float(diff_tx_pkts) / ts_diff) - snapshot['tx_percentage'] = 100.0 * tx_bps_L1 / self._port_obj.get_speed_bps() + if speed: + snapshot['tx_percentage'] = 100.0 * tx_bps_L1 / speed + else: + snapshot['tx_percentage'] = 0 # RX line util not smoothed diff_rx_pkts = snapshot.get('ipackets', 0) - self.latest_stats.get('ipackets', 0) diff_rx_bytes = snapshot.get('ibytes', 0) - self.latest_stats.get('ibytes', 0) rx_bps_L1 = calc_bps_L1(8.0 * diff_rx_bytes / ts_diff, float(diff_rx_pkts) / ts_diff) - snapshot['rx_percentage'] = 100.0 * rx_bps_L1 / self._port_obj.get_speed_bps() + if speed: + snapshot['rx_percentage'] = 100.0 * rx_bps_L1 / speed + else: + snapshot['rx_percentage'] = 0 # simple... self.latest_stats = snapshot @@ -1040,13 +1094,24 @@ class CPortStats(CTRexStats): def generate_stats(self): - state = self._port_obj.get_port_state_name() if self._port_obj else "" - if state == "ACTIVE": - state = format_text(state, 'green', 'bold') - elif state == "PAUSE": - state = format_text(state, 'magenta', 'bold') + port_state = self._port_obj.get_port_state_name() if self._port_obj else "" + if port_state == "TRANSMITTING": + port_state = format_text(port_state, 'green', 'bold') + elif port_state == "PAUSE": + port_state = format_text(port_state, 'magenta', 'bold') + else: + port_state = format_text(port_state, 'bold') + + if self._port_obj: + if 'link' in self._port_obj.attr: + if self._port_obj.attr.get('link', {}).get('up') == False: + link_state = format_text('DOWN', 'red', 'bold') + else: + link_state = 'UP' + else: + link_state = 'N/A' else: - state = format_text(state, 'bold') + link_state = '' # default rate format modifiers rate_format = {'bpsl1': None, 'bps': None, 'pps': None, 'percentage': 'bold'} @@ -1063,7 +1128,8 @@ class CPortStats(CTRexStats): return {"owner": owner, - "state": "{0}".format(state), + "state": "{0}".format(port_state), + 'link': link_state, "speed": self._port_obj.get_formatted_speed() if self._port_obj else '', "CPU util.": "{0} {1}%".format(self.get_trend_gui("m_cpu_util", use_raw = True), format_threshold(round_float(self.get("m_cpu_util")), [85, 100], [0, 85])) if self._port_obj else '' , @@ -1417,7 +1483,7 @@ class CUtilStats(CTRexStats): self.client = client self.history = deque(maxlen = 1) self.mbuf_types_list = None - self.last_update_ts = 0 + self.last_update_ts = -999 def get_stats(self, use_1sec_cache = False): time_now = time.time() @@ -1425,7 +1491,7 @@ class CUtilStats(CTRexStats): if self.client.is_connected(): rc = self.client._transmit('get_utilization') if not rc: - raise Exception(rc) + raise STLError(rc) self.last_update_ts = time_now self.history.append(rc.data()) else: @@ -1433,6 +1499,51 @@ class CUtilStats(CTRexStats): return self.history[-1] +class CXStats(CTRexStats): + + def __init__(self, client): + super(CXStats, self).__init__() + self.client = client + self.names = [] + self.last_update_ts = -999 + + def clear_stats(self, port_id = None): + if port_id == None: + ports = self.client.get_all_ports() + elif type(port_id) is list: + ports = port_id + else: + ports = [port_id] + + for port_id in ports: + self.reference_stats[port_id] = self.get_stats(port_id, relative = False) + + def get_stats(self, port_id, use_1sec_cache = False, relative = True): + time_now = time.time() + if self.last_update_ts + 1 < time_now or not self.latest_stats or not use_1sec_cache: + if self.client.is_connected(): + rc = self.client._transmit('get_port_xstats_values', params = {'port_id': port_id}) + if not rc: + raise STLError(rc) + self.last_update_ts = time_now + values = rc.data().get('xstats_values', []) + if len(values) != len(self.names): # need to update names ("keys") + rc = self.client._transmit('get_port_xstats_names', params = {'port_id': port_id}) + if not rc: + raise STLError(rc) + self.names = rc.data().get('xstats_names', []) + if len(values) != len(self.names): + raise STLError('Length of get_xstats_names: %s and get_port_xstats_values: %s' % (len(self.names), len(values))) + self.latest_stats[port_id] = OrderedDict([(key, val) for key, val in zip(self.names, values)]) + + stats = OrderedDict() + for key, val in self.latest_stats[port_id].items(): + if relative: + stats[key] = self.get_rel([port_id, key]) + else: + stats[key] = self.get([port_id, key]) + return stats + if __name__ == "__main__": pass diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py index 3993ad5e..fe4fc893 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py @@ -85,27 +85,29 @@ class GA_EXCEPTION_ObjClass(GA_ObjClass): #..................................................................class GA_TESTING_ObjClass................................................................
class GA_TESTING_ObjClass(GA_ObjClass):
- def __init__(self,cid,uuid,trackerID,TRexMode,test_name,setup_name,appName,appVer,commitID,bandwidthPerCore,goldenBPC):
+ def __init__(self,cid,trackerID,TRexMode,TestName,SetupName,appName,ActionNumber,appVer,TestType,Mppspc,GoldenMin,GoldenMax):
GA_ObjClass.__init__(self,cid,trackerID,appName,appVer)
- self.uid = uuid
+ self.ActionNumber = ActionNumber
self.TRexMode = TRexMode
- self.test_name = test_name
- self.setup_name = setup_name
- self.commitID = commitID
- self.bandwidthPerCore = bandwidthPerCore
- self.goldenBPC = goldenBPC
+ self.TestName = TestName
+ self.SetupName = SetupName
+ self.TestType = TestType
+ self.Mppspc = Mppspc
+ self.GoldenMin = GoldenMin
+ self.GoldenMax = GoldenMax
self.payload = self.generate_payload()
self.size = sys.getsizeof(self.payload)
def generate_payload(self):
- self.payload+='&ec='+str(self.TRexMode)
- self.payload+='&ea=RegressionReport'
- self.payload+='&cd5='+str(self.uid)
- self.payload+='&cd1='+str(self.test_name)
- self.payload+='&cd2='+str(self.setup_name)
- self.payload+='&cd3='+str(self.commitID)
- self.payload+='&cm1='+str(self.bandwidthPerCore)
- self.payload+='&cm2='+str(self.goldenBPC)
+ self.payload+='&ec=TRexTests'
+ self.payload+='&ea='+str(self.ActionNumber)
+ self.payload+='&cd2='+str(self.TRexMode)
+ self.payload+='&cd1='+str(self.TestName)
+ self.payload+='&cd3='+str(self.SetupName)
+ self.payload+='&cd4='+str(self.TestType)
+ self.payload+='&cm1='+str(self.Mppspc)
+ self.payload+='&cm2='+str(self.GoldenMin)
+ self.payload+='&cm3='+str(self.GoldenMax)
return self.payload
#.....................................................................class ga_Thread.................................................................
"""
@@ -265,7 +267,6 @@ class GAmanager: attributes:
GoogleID - the tracker ID that Google uses in order to track the activity of a property. for regression use: 'UA-75220362-4'
AnalyticsUserID - text value - used by Google to differ between 2 users sending data. (will not be presented on reports). use only as a way to differ between different users
-UUID - text - will be presented on analysis. put here UUID
TRexMode - text - will be presented on analysis. put here TRexMode
appName - text - will be presented on analysis. put here appName as string describing app name
appVer - text - will be presented on analysis. put here the appVer
@@ -274,83 +275,21 @@ Timeout - integer (seconds) - the timeout in seconds between automated reports w UserPermission - boolean (1/0) - required in order to send packets, should be 1.
BlockingMode - boolean (1/0) - required when each tracked event is critical and program should halt until the event is reported
SetupName - text - will be presented on analysis. put here setup name as string.
-CommitID - text - will be presented on analysis. put here CommitID
"""
class GAmanager_Regression(GAmanager):
- def __init__(self,GoogleID,AnalyticsUserID,UUID,TRexMode,appName,appVer,
- QueueSize,Timeout,UserPermission,BlockingMode,SetupName,CommitID):
- GAmanager.__init__(self,GoogleID,AnalyticsUserID,appName,appVer,
- QueueSize,Timeout,UserPermission,BlockingMode)
- self.UUID = UUID
- self.TRexMode = TRexMode
- self.SetupName = SetupName
- self.CommitID = CommitID
-
- def gaAddTestQuery(self,TestName,BandwidthPerCore,GoldenBPC):
- self.gaAddObject(GA_TESTING_ObjClass(self.UserID,self.UUID,self.GoogleID,
- self.TRexMode,TestName,self.SetupName,
- self.appName,self.appVer,self.CommitID,
- BandwidthPerCore,GoldenBPC))
-
-
-
-
-#***************************************------TEST--------------**************************************
-
-
-#if __name__ == '__main__':
-
-#g= GAmanager_Regression(GoogleID='UA-75220362-4',AnalyticsUserID=3845,UUID='trex18UUID_GA_TEST',TRexMode='stateFull_GA_TEST',
-# appName='TRex_GA_TEST',appVer='1.1_GA_TEST',QueueSize=20,Timeout=11,UserPermission=1,BlockingMode=0,SetupName='setup1_GA_TEST',CommitID='commitID1_GA_TEST')
-#for j in range(1,3,1):
-#for i in range(100,118,1):
-# g.gaAddTestQuery('test_name_GA_TEST',i+0.5,150)
-# sleep(11)
-# print "finished batch"
-#g.emptyAndReportQ()
-
-#g.printSelf()
-#g.emptyAndReportQ()
-
-#g = GAmanager(GoogleID='UA-75220362-4',UserID=1,QueueSize=100,Timeout=5,UserPermission=1,BlockingMode=0,appName='TRex',appVer='1.11.232') #timeout in seconds
-#for i in range(0,35,1):
-# g.gaAddAction(Event='test',action='start',label='1',value=i)
-#g.gaAddException('MEMFAULT',1)
-#g.emptyAndReportQ()
-#g.printSelf()
-#print g.payload
-#print g.size
-
-
-
-
-#g.activate()
-#g.gaAddAction(Event='test',action='start',label='1',value='1')
-#sys.stdout.write('element added \n')
-#sys.stdout.flush()
-#g.gaAddAction(Event='test',action='start',label='2',value='1')
-#sys.stdout.write('element added \n')
-#sys.stdout.flush()
-#g.gaAddAction(Event='test',action='start',label='3',value='1')
-#sys.stdout.write('element added \n')
-#sys.stdout.flush()
-
-#testdata = "v=1&t=event&tid=UA-75220362-4&cid=2&ec=test&ea=testing&el=testpacket&ev=2"
-#r = requests.post(url_debug,data=testdata)
-#print r
-
-#thread1 = ga_Thread(1,g)
-#thread1.start()
-#thread1.join()
-#for i in range(1,10,1):
-# sys.stdout.write('yesh %d'% (i))
-# sys.stdout.flush()
+ def __init__(self, GoogleID, AnalyticsUserID, appName, appVer,
+ QueueSize, Timeout, UserPermission, BlockingMode):
+ GAmanager.__init__(self, GoogleID, AnalyticsUserID, appName, appVer,
+ QueueSize, Timeout, UserPermission, BlockingMode)
+ self.GoogleID = GoogleID
+ self.AnalyticsUserID = AnalyticsUserID
+
+ def gaAddTestQuery(self, TestName, TRexMode, SetupName, ActionNumber, TestType, Mppspc, GoldenMin, GoldenMax):
+ self.gaAddObject(GA_TESTING_ObjClass(self.AnalyticsUserID, self.GoogleID, TRexMode, TestName, SetupName, self.appName, ActionNumber, self.appVer, TestType, Mppspc, GoldenMin, GoldenMax))
-
-
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 638684c3..72ee8972 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 @@ -27,9 +27,9 @@ def user_input(): return raw_input() -def random_id_gen(length=8): +class random_id_gen: """ - A generator for creating a random chars id of specific length + Emulated generator for creating a random chars id of specific length :parameters: length : int @@ -40,12 +40,15 @@ def random_id_gen(length=8): :return: a random id with each next() request. """ - id_chars = string.ascii_lowercase + string.digits - while True: - return_id = '' - for i in range(length): - return_id += random.choice(id_chars) - yield return_id + def __init__(self, length=8): + self.id_chars = string.ascii_lowercase + string.digits + self.length = length + + def next(self): + return ''.join(random.choice(self.id_chars) for _ in range(self.length)) + + __next__ = next + # try to get number from input, return None in case of fail def get_number(input): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/constants.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/constants.py new file mode 100755 index 00000000..a4942094 --- /dev/null +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/constants.py @@ -0,0 +1,26 @@ +from collections import OrderedDict + +ON_OFF_DICT = OrderedDict([ + ('on', True), + ('off', False), +]) + +UP_DOWN_DICT = OrderedDict([ + ('up', True), + ('down', False), +]) + +FLOW_CTRL_DICT = OrderedDict([ + ('none', 0), # Disable flow control + ('tx', 1), # Enable flowctrl on TX side (RX pause frames) + ('rx', 2), # Enable flowctrl on RX side (TX pause frames) + ('full', 3), # Enable flow control on both sides +]) + + + +# generate reverse dicts + +for var_name in list(vars().keys()): + if var_name.endswith('_DICT'): + exec('{0}_REVERSED = OrderedDict([(val, key) for key, val in {0}.items()])'.format(var_name)) 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 8ae86981..7eda8635 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,8 +1,9 @@ import argparse -from collections import namedtuple +from collections import namedtuple, OrderedDict from .common import list_intersect, list_difference from .text_opts import format_text from ..trex_stl_types import * +from .constants import ON_OFF_DICT, UP_DOWN_DICT, FLOW_CTRL_DICT import sys import re @@ -32,14 +33,16 @@ IPG = 16 SPEEDUP = 17 COUNT = 18 PROMISCUOUS = 19 -NO_PROMISCUOUS = 20 -PROMISCUOUS_SWITCH = 21 +LINK_STATUS = 20 +LED_STATUS = 21 TUNABLES = 22 REMOTE_FILE = 23 LOCKED = 24 PIN_CORES = 25 CORE_MASK = 26 DUAL = 27 +FLOW_CTRL = 28 +SUPPORTED = 29 GLOBAL_STATS = 50 PORT_STATS = 51 @@ -48,6 +51,8 @@ STREAMS_STATS = 53 STATS_MASK = 54 CPU_STATS = 55 MBUF_STATS = 56 +EXTENDED_STATS = 57 +EXTENDED_INC_ZERO_STATS = 58 STREAMS_MASK = 60 CORE_MASK_GROUP = 61 @@ -282,10 +287,26 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'type': int}), PROMISCUOUS: ArgumentPack(['--prom'], - {'help': "Sets port promiscuous on", - 'dest': "prom", - 'default': None, - 'action': "store_true"}), + {'help': "Set port promiscuous on/off", + 'choices': ON_OFF_DICT}), + + LINK_STATUS: ArgumentPack(['--link'], + {'help': 'Set link status up/down', + 'choices': UP_DOWN_DICT}), + + LED_STATUS: ArgumentPack(['--led'], + {'help': 'Set LED status on/off', + 'choices': ON_OFF_DICT}), + + FLOW_CTRL: ArgumentPack(['--fc'], + {'help': 'Set Flow Control type', + 'dest': 'flow_ctrl', + 'choices': FLOW_CTRL_DICT}), + + SUPPORTED: ArgumentPack(['--supp'], + {'help': 'Show which attributes are supported by current NICs', + 'default': None, + 'action': 'store_true'}), TUNABLES: ArgumentPack(['-t'], {'help': "Sets tunables for a profile. Example: '-t fsize=100,pg_id=7'", @@ -295,12 +316,6 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'action': 'merge', 'type': decode_tunables}), - NO_PROMISCUOUS: ArgumentPack(['--no-prom', '--no_prom'], - {'help': "Sets port promiscuous off", - 'dest': "prom", - 'default': None, - 'action': "store_false"}), - PORT_LIST: ArgumentPack(['--port', '-p'], {"nargs": '+', 'dest':'ports', @@ -401,6 +416,14 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], {'action': 'store_true', 'help': "Fetch only MBUF utilization stats"}), + EXTENDED_STATS: ArgumentPack(['-x'], + {'action': 'store_true', + 'help': "Fetch xstats of port, excluding lines with zero values"}), + + EXTENDED_INC_ZERO_STATS: ArgumentPack(['--xz'], + {'action': 'store_true', + 'help': "Fetch xstats of port, including lines with zero values"}), + STREAMS_MASK: ArgumentPack(['--streams'], {"nargs": '+', 'dest':'streams', @@ -424,12 +447,6 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'default': None, 'help': "Core mask - only cores responding to the bit mask will be active"}), - - # promiscuous - PROMISCUOUS_SWITCH: ArgumentGroup(MUTEX, [PROMISCUOUS, - NO_PROMISCUOUS], - {'required': False}), - # advanced options PORT_LIST_WITH_ALL: ArgumentGroup(MUTEX, [PORT_LIST, ALL_PORTS], @@ -443,7 +460,9 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], PORT_STATUS, STREAMS_STATS, CPU_STATS, - MBUF_STATS], + MBUF_STATS, + EXTENDED_STATS, + EXTENDED_INC_ZERO_STATS,], {}), @@ -462,6 +481,8 @@ class _MergeAction(argparse._AppendAction): items.extend(values) elif type(items) is dict and type(values) is dict: # tunables are dict items.update(values) + else: + raise Exception("Argparser 'merge' option should be used on dict or list.") setattr(namespace, self.dest, items) |