summaryrefslogtreecommitdiffstats
path: root/scripts/automation/trex_control_plane
diff options
context:
space:
mode:
authorYaroslav Brustinov <ybrustin@cisco.com>2016-10-22 10:38:27 +0200
committerYaroslav Brustinov <ybrustin@cisco.com>2016-10-28 14:38:32 +0200
commit2dab6b6d09f9f1474d2ade6b465ebfa5ce3b3f5e (patch)
treebab2898d6b631d5495c62a8ad4b86ccea4eae786 /scripts/automation/trex_control_plane
parent00bfc58e6f76f7a67a6b62f297f72792534fef52 (diff)
dpdk_setup_ports.py: fix add of help in case of "t-rex-64 --help"
dpdk_setup_ports.py: fix warning of TRex is already running if different NICs are being used singleton_daemon.py: fix error socket in use immediately after check if in use trex-console: fix crash in case of "tui --help" trex-console: try-catch commands instead of crashing add async notification on port status/atttibutes change add port xstats support add description of interfaces main_dpdk.cpp: fix --client_cfg not working with Python API Signed-off-by: Yaroslav Brustinov <ybrustin@cisco.com>
Diffstat (limited to 'scripts/automation/trex_control_plane')
-rwxr-xr-xscripts/automation/trex_control_plane/server/singleton_daemon.py1
-rwxr-xr-xscripts/automation/trex_control_plane/stl/console/trex_console.py37
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py45
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py2
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py59
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py144
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/common.py15
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/utils/constants.py26
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py43
9 files changed, 280 insertions, 92 deletions
diff --git a/scripts/automation/trex_control_plane/server/singleton_daemon.py b/scripts/automation/trex_control_plane/server/singleton_daemon.py
index cd16d173..507967aa 100755
--- a/scripts/automation/trex_control_plane/server/singleton_daemon.py
+++ b/scripts/automation/trex_control_plane/server/singleton_daemon.py
@@ -37,6 +37,7 @@ class SingletonDaemon(object):
lock_socket.close()
except socket.error: # Unix socket in use
return True
+ sleep(0.5)
# Unix socket is not used, but maybe it's old version of daemon not using socket
return bool(self.get_pid_by_listening_port())
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 b11ddbe3..ec7e6eef 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
@@ -320,6 +320,13 @@ class EventsHandler(object):
ev = "port {0} job failed".format(port_id)
show_event = True
+ # port attr changed
+ elif (type == 8):
+ port_id = int(data['port_id'])
+ ev = "port {0} attributes changed".format(port_id)
+ show_event = True
+ self.__async_event_port_attr_changed(port_id, data['attr'])
+
# server stopped
elif (type == 100):
ev = "Server has stopped"
@@ -372,6 +379,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):
@@ -887,7 +897,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()
@@ -904,6 +914,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
@@ -2501,6 +2514,9 @@ class STLClient(object):
clear_latency_stats : bool
Clear the latency stats
+ clear_xstats : bool
+ Clear the extended stats
+
:raises:
+ :exc:`STLError`
@@ -2513,7 +2529,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)
@@ -2605,7 +2621,7 @@ class STLClient(object):
flow_ctrl - 0: disable all, 1: enable tx side, 2: enable rx side, 3: full enable
:raises:
- None
+ + :exe:'STLError'
"""
@@ -3205,23 +3221,34 @@ class STLClient(object):
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.on_off_dict.get(opts.link)
- opts.led = parsing_opts.on_off_dict.get(opts.led)
- opts.flow_ctrl = parsing_opts.flow_ctrl_dict.get(opts.flow_ctrl)
+ opts.prom = parsing_opts.ON_OFF_DICT.get(opts.prom)
+ opts.link = parsing_opts.UP_DOWN_DICT.get(opts.link)
+ opts.led = parsing_opts.ON_OFF_DICT.get(opts.led)
+ opts.flow_ctrl = parsing_opts.FLOW_CTRL_DICT.get(opts.flow_ctrl)
# if no attributes - fall back to printing the status
- if not filter(lambda x:x is not None, [opts.prom, opts.link, opts.led, opts.flow_ctrl]):
+ 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
- return self.set_port_attr(opts.ports, opts.prom, opts.link, opts.led, opts.flow_ctrl)
+ 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
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_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py
index f0e3b109..4a094c5d 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
@@ -250,9 +251,10 @@ class Port(object):
self.next_available_id = int(rc.data()['max_stream_id']) + 1
# attributes
- self.attr = rc.data()['attr']
+ self.attr = ['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,48 @@ 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 '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 self.attr.get('promiscuous'):
+ if 'promiscuous' in self.attr:
info['prom'] = "on" if self.attr['promiscuous']['enabled'] else "off"
else:
info['prom'] = "N/A"
+ if 'description' in info:
+ if len(info['description']) > 18:
+ info['description'] = info['description'][:18]
+ else:
+ 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 +709,7 @@ class Port(object):
info = self.get_info()
return {"driver": info['driver'],
+ "description": info.get('description', 'N/A'),
"HW src mac": info['hw_macaddr'],
"SW src mac": info['src_macaddr'],
"SW dst mac": info['dst_macaddr'],
@@ -679,9 +717,10 @@ 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']),
+ "status": '%s (link %s)' % (info['status'], info['link']),
+ "promiscuous" : info['prom'],
+ "flow ctrl" : info['fc'],
}
def clear_stats(self):
@@ -726,6 +765,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 eaa5e8b0..783387ca 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
@@ -184,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 {}
@@ -206,7 +215,7 @@ 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")),
@@ -222,14 +231,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"),
@@ -238,8 +246,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'),
@@ -249,19 +255,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
@@ -505,6 +521,29 @@ 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)):
+ if len(key) > 28:
+ 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
@@ -558,6 +597,7 @@ class CTRexInfoGenerator(object):
return_stats_data = {}
per_field_stats = OrderedDict([("owner", []),
+ ('link', []),
("state", []),
("speed", []),
("CPU util.", []),
@@ -585,8 +625,7 @@ class CTRexInfoGenerator(object):
("oerrors", []),
("ierrors", []),
- ]
- )
+ ])
total_stats = CPortStats(None)
@@ -626,9 +665,11 @@ class CTRexInfoGenerator(object):
return_stats_data = {}
per_field_status = OrderedDict([("driver", []),
- ("maximum", []),
+ ("description", []),
+ ("link speed", []),
("status", []),
("promiscuous", []),
+ ("flow ctrl", []),
("--", []),
("HW src mac", []),
("SW src mac", []),
@@ -1054,13 +1095,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 == "ACTIVE":
+ port_state = format_text('TRANSMITTING', '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'}
@@ -1077,7 +1129,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 '' ,
@@ -1439,7 +1492,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:
@@ -1452,33 +1505,44 @@ class CXStats(CTRexStats):
def __init__(self, client):
super(CXStats, self).__init__()
self.client = client
- self.history = deque(maxlen = 1)
- self.names = {}
+ self.names = []
self.last_update_ts = -999
- def get_stats(self, port_id, use_1sec_cache = False):
+ 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.history or not use_1sec_cache:
+ 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 Exception(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 Exception(rc)
+ raise STLError(rc)
self.names = rc.data().get('xstats_names', [])
if len(values) != len(self.names):
- raise Exception('Length of get_xstats_names: %s and get_port_xstats_values: %s' % (len(self.names), len(values)))
- self.history.append(dict([(key, val) for key, val in zip(self.names, values)]))
- else:
- self.history.append({})
+ 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 = {}
- for key, val in self.history[-1].items():
- stats[key] = self.history[-1][key] - self.reference_stats.get(key, 0)
+ 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__":
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..0214d7d7 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,7 +27,7 @@ def user_input():
return raw_input()
-def random_id_gen(length=8):
+def random_id_gen_unsafe(length=8):
"""
A generator for creating a random chars id of specific length
@@ -47,6 +47,19 @@ def random_id_gen(length=8):
return_id += random.choice(id_chars)
yield return_id
+
+class random_id_gen:
+ """ Thread safe version of random_id_gen_unsafe """
+ 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):
try:
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 148f7715..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
@@ -3,6 +3,7 @@ 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
@@ -11,18 +12,6 @@ import os
ArgumentPack = namedtuple('ArgumentPack', ['name_or_flags', 'options'])
ArgumentGroup = namedtuple('ArgumentGroup', ['type', 'args', 'options'])
-on_off_dict = OrderedDict([
- ('on', True),
- ('off', 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
-])
-
# list of available parsing options
MULTIPLIER = 1
@@ -53,6 +42,7 @@ PIN_CORES = 25
CORE_MASK = 26
DUAL = 27
FLOW_CTRL = 28
+SUPPORTED = 29
GLOBAL_STATS = 50
PORT_STATS = 51
@@ -61,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
@@ -296,20 +288,25 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'],
PROMISCUOUS: ArgumentPack(['--prom'],
{'help': "Set port promiscuous on/off",
- 'choices': on_off_dict}),
+ 'choices': ON_OFF_DICT}),
LINK_STATUS: ArgumentPack(['--link'],
- {'help': 'Set link status on/off',
- 'choices': on_off_dict}),
+ {'help': 'Set link status up/down',
+ 'choices': UP_DOWN_DICT}),
LED_STATUS: ArgumentPack(['--led'],
{'help': 'Set LED status on/off',
- 'choices': on_off_dict}),
+ 'choices': ON_OFF_DICT}),
FLOW_CTRL: ArgumentPack(['--fc'],
{'help': 'Set Flow Control type',
'dest': 'flow_ctrl',
- 'choices': flow_ctrl_dict}),
+ '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'",
@@ -419,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',
@@ -455,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,],
{}),