diff options
6 files changed, 93 insertions, 52 deletions
diff --git a/scripts/automation/trex_control_plane/client/trex_async_client.py b/scripts/automation/trex_control_plane/client/trex_async_client.py index 459d6915..66e65a32 100644 --- a/scripts/automation/trex_control_plane/client/trex_async_client.py +++ b/scripts/automation/trex_control_plane/client/trex_async_client.py @@ -28,16 +28,6 @@ class CTRexAsyncStats(object): self.current = {} self.last_update_ts = datetime.datetime.now() - @staticmethod - def format_num (size, suffix = ""): - - for unit in ['','K','M','G','T','P']: - if abs(size) < 1000.0: - return "%3.2f %s%s" % (size, unit, suffix) - size /= 1000.0 - - return "NaN" - def update (self, snapshot): #update @@ -60,7 +50,7 @@ class CTRexAsyncStats(object): if not format: return self.current[field] else: - return self.format_num(self.current[field], suffix) + return format_num(self.current[field], suffix) def get_rel (self, field, format=False, suffix=""): if not field in self.current: @@ -69,7 +59,7 @@ class CTRexAsyncStats(object): if not format: return (self.current[field] - self.ref_point[field]) else: - return self.format_num(self.current[field] - self.ref_point[field], suffix) + return format_num(self.current[field] - self.ref_point[field], suffix) # return true if new data has arrived in the past 2 seconds diff --git a/scripts/automation/trex_control_plane/client/trex_port.py b/scripts/automation/trex_control_plane/client/trex_port.py index fc63cf0d..66d87f9d 100644 --- a/scripts/automation/trex_control_plane/client/trex_port.py +++ b/scripts/automation/trex_control_plane/client/trex_port.py @@ -403,12 +403,11 @@ class Port(object): ################# stats handler ###################### def generate_port_stats(self): return self.port_stats.generate_stats() - pass def generate_port_status(self): - return {"port-type": self.driver, + return {"type": self.driver, "maximum": "{speed} Gb/s".format(speed=self.speed), - "port-status": self.get_port_state_name() + "status": self.get_port_state_name() } def clear_stats(self): diff --git a/scripts/automation/trex_control_plane/common/text_opts.py b/scripts/automation/trex_control_plane/common/text_opts.py index 29fbd69b..30d38d3f 100755 --- a/scripts/automation/trex_control_plane/common/text_opts.py +++ b/scripts/automation/trex_control_plane/common/text_opts.py @@ -18,14 +18,42 @@ TEXT_CODES = {'bold': {'start': '\x1b[1m', 'underline': {'start': '\x1b[4m', 'end': '\x1b[24m'}} +class TextCodesStripper: + keys = [re.escape(v['start']) for k,v in TEXT_CODES.iteritems()] + keys += [re.escape(v['end']) for k,v in TEXT_CODES.iteritems()] + pattern = re.compile("|".join(keys)) + + @staticmethod + def strip (s): + return re.sub(TextCodesStripper.pattern, '', s) + +def format_num (size, suffix = "", compact = True, opts = ()): + txt = "NaN" + + if type(size) == str: + return "N/A" + + u = '' + + if compact: + for unit in ['','K','M','G','T','P']: + if abs(size) < 1000.0: + #txt = "%3.2f %s%s" % (size, unit, suffix) + u = unit + break + size /= 1000.0 + + if isinstance(size, float): + txt = "%3.2f %s%s" % (size, u, suffix) + else: + txt = "{:,} {:}{:}".format(size, u, suffix) + + if isinstance(opts, tuple): + return format_text(txt, *opts) + else: + return format_text(txt, (opts)) -def format_num (size, suffix = ""): - for unit in ['','K','M','G','T','P']: - if abs(size) < 1000.0: - return "%3.2f %s%s" % (size, unit, suffix) - size /= 1000.0 - return "NaN" def format_time (t_sec): if t_sec < 0: @@ -122,8 +150,10 @@ def format_text(text, *args): func = FUNC_DICT.get(i) if func: return_string = func(return_string) + return return_string + def format_threshold (value, red_zone, green_zone): if value >= red_zone[0] and value <= red_zone[1]: return format_text("{0}".format(value), 'red') diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py index aecf44b4..6d617b3c 100755 --- a/scripts/automation/trex_control_plane/common/trex_stats.py +++ b/scripts/automation/trex_control_plane/common/trex_stats.py @@ -1,13 +1,14 @@ #!/router/bin/python from collections import namedtuple, OrderedDict, deque from client_utils import text_tables -from common.text_opts import format_text, format_threshold +from common.text_opts import format_text, format_threshold, format_num from client.trex_async_client import CTRexAsyncStats import copy import datetime import time import re import math +import copy GLOBAL_STATS = 'g' PORT_STATS = 'p' @@ -80,7 +81,8 @@ class CTRexInfoGenerator(object): def generate_streams_info(self, port_id_list, stream_id_list): relevant_ports = self.__get_relevant_ports(port_id_list) - return_data = {} + return_data = OrderedDict() + for port_obj in relevant_ports: streams_data = self._generate_single_port_streams_info(port_obj, stream_id_list) if not streams_data: @@ -90,6 +92,7 @@ class CTRexInfoGenerator(object): # TODO: test for other ports with same stream structure, and join them return_data[hdr_key] = streams_data + return return_data def _generate_global_stats(self): @@ -99,6 +102,7 @@ class CTRexInfoGenerator(object): # build table representation stats_table = text_tables.TRexTextInfo() stats_table.set_cols_align(["l", "l"]) + stats_table.add_rows([[k.replace("_", " ").title(), v] for k, v in stats_data.iteritems()], header=False) @@ -156,9 +160,9 @@ class CTRexInfoGenerator(object): relevant_ports = self.__get_relevant_ports(port_id_list) return_stats_data = {} - per_field_status = OrderedDict([("port-type", []), + per_field_status = OrderedDict([("type", []), ("maximum", []), - ("port-status", []) + ("status", []) ] ) @@ -193,8 +197,12 @@ class CTRexInfoGenerator(object): return None # FORMAT VALUES ON DEMAND + + # because we mutate this - deep copy before + return_streams_data = copy.deepcopy(return_streams_data) + for stream_id, stream_id_sum in return_streams_data['streams'].iteritems(): - stream_id_sum['rate_pps'] = CTRexStats.format_num(stream_id_sum['rate_pps'], suffix='pps') + stream_id_sum['rate_pps'] = format_num(stream_id_sum['rate_pps'], suffix='pps') stream_id_sum['packet_type'] = self._trim_packet_headers(stream_id_sum['packet_type'], 20) info_table = text_tables.TRexTextTable() @@ -277,16 +285,6 @@ class CTRexStats(object): # can't match to any known pattern, return N/A return "N/A" - @staticmethod - def format_num(size, suffix = ""): - if type(size) == str: - return "N/A" - - for unit in ['','K','M','G','T','P']: - if abs(size) < 1000.0: - return "%3.2f %s%s" % (size, unit, suffix) - size /= 1000.0 - return "NaN" def generate_stats(self): # must be implemented by designated classes (such as port/ global stats) @@ -317,7 +315,7 @@ class CTRexStats(object): if not format: return self.latest_stats[field] else: - return self.format_num(self.latest_stats[field], suffix) + return format_num(self.latest_stats[field], suffix) def get_rel(self, field, format=False, suffix=""): if not field in self.latest_stats: @@ -326,15 +324,20 @@ class CTRexStats(object): if not format: return (self.latest_stats[field] - self.reference_stats[field]) else: - return self.format_num(self.latest_stats[field] - self.reference_stats[field], suffix) + return format_num(self.latest_stats[field] - self.reference_stats[field], suffix) # get trend for a field - def get_trend (self, field, use_raw = False): + def get_trend (self, field, use_raw = False, percision = 10.0): if not field in self.latest_stats: return 0 + # not enough history - no trend if len(self.history) < 5: return 0 + + # absolute value is too low 0 considered noise + if self.latest_stats[field] < percision: + return 0 field_samples = [sample[field] for sample in self.history] @@ -344,7 +347,7 @@ class CTRexStats(object): return calculate_diff(field_samples) - def get_trend_gui (self, field, show_value = True, use_raw = False, up_color = 'red', down_color = 'green'): + def get_trend_gui (self, field, show_value = False, use_raw = False, up_color = 'red', down_color = 'green'): v = self.get_trend(field, use_raw) value = abs(v) @@ -394,6 +397,8 @@ class CGlobalStats(CTRexStats): ("cpu_util", u"{0}% {1}".format( format_threshold(self.get("m_cpu_util"), [85, 100], [0, 85]), self.get_trend_gui("m_cpu_util", use_raw = True))), + (" ", ""), + ("total_tx", u"{0} {1}".format( self.get("m_tx_bps", format=True, suffix="b/sec"), self.get_trend_gui("m_tx_bps"))), @@ -403,10 +408,17 @@ class CGlobalStats(CTRexStats): ("total_pps", u"{0} {1}".format( self.get("m_tx_pps", format=True, suffix="pkt/sec"), self.get_trend_gui("m_tx_pps"))), - ("total_streams", sum([len(port_obj.streams) - for _, port_obj in self._ports_dict.iteritems()])), - ("active_ports", sum([port_obj.is_active() - for _, port_obj in self._ports_dict.iteritems()])) + (" ", ""), + + ("drop_rate", "{0}".format( format_num(self.get("m_rx_drop_bps"), + suffix = 'b/sec', + opts = 'green' if (self.get("m_rx_drop_bps")== 0) else 'red'))), + + ("queue_full", "{0}".format( format_num(self.get_rel("m_total_queue_full"), + suffix = 'pkts', + compact = False, + opts = 'green' if (self.get_rel("m_total_queue_full")== 0) else 'red'))), + ] ) @@ -417,9 +429,10 @@ class CPortStats(CTRexStats): self._port_obj = port_obj def generate_stats(self): + return {"owner": self._port_obj.user, "state": self._port_obj.get_port_state_name(), - "--": "", + "--": " ", "opackets" : self.get_rel("opackets"), "obytes" : self.get_rel("obytes"), "ipackets" : self.get_rel("ipackets"), @@ -433,17 +446,17 @@ class CPortStats(CTRexStats): "rx-pkts": self.get_rel("ipackets", format = True, suffix = "pkts"), "---": "", - "Tx bps": u"{0} {1}".format(self.get_trend_gui("m_total_tx_bps", show_value = False, up_color = None, down_color = None), + "Tx bps": u"{0} {1}".format(self.get_trend_gui("m_total_tx_bps", show_value = False), self.get("m_total_tx_bps", format = True, suffix = "bps")), - "Rx bps": u"{0} {1}".format(self.get_trend_gui("m_total_rx_bps", show_value = False, up_color = None, down_color = None), + "Rx bps": u"{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": u"{0} {1}".format(self.get_trend_gui("m_total_tx_pps", show_value = False, up_color = None, down_color = None), + "Tx pps": u"{0} {1}".format(self.get_trend_gui("m_total_tx_pps", show_value = False), self.get("m_total_tx_pps", format = True, suffix = "pps")), - "Rx pps": u"{0} {1}".format(self.get_trend_gui("m_total_rx_pps", show_value = False, up_color = None, down_color = None), + "Rx pps": u"{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/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index fe4001b2..49d58fe0 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -483,7 +483,7 @@ class TRexConsole(TRexGeneralCmd): exe += './trex-console -t -q -s {0} -p {1}'.format(self.stateless_client.get_server_ip(), self.stateless_client.get_server_port()) - cmd = ['xterm', '-geometry', '105x40', '-title', 'trex_tui', '-e', exe] + cmd = ['xterm', '-geometry', '105x42', '-title', 'trex_tui', '-e', exe] subprocess.Popen(cmd) return diff --git a/scripts/external_libs/texttable-0.8.4/texttable.py b/scripts/external_libs/texttable-0.8.4/texttable.py index 684e63bd..d0348f6c 100644 --- a/scripts/external_libs/texttable-0.8.4/texttable.py +++ b/scripts/external_libs/texttable-0.8.4/texttable.py @@ -98,6 +98,7 @@ Maximilian Hils: import sys import string +from common import text_opts try: if sys.version >= '2.3': @@ -128,6 +129,10 @@ def len(iterable): return iterable.__len__() +def ansi_len (iterable): + return len(text_opts.TextCodesStripper.strip(iterable)) + + class ArraySizeError(Exception): """Exception raised when specified rows don't fit the required size """ @@ -532,7 +537,7 @@ class Texttable: for cell, width, align in zip(line, self._width, self._align): length += 1 cell_line = cell[i] - fill = width - len(cell_line) + fill = width - ansi_len(cell_line) if isheader: align = "c" if align == "r": @@ -569,7 +574,11 @@ class Texttable: c = str(c, 'utf', 'replace') else: c = unicode(c, 'utf', 'replace') - array.extend(textwrap.wrap(c, width)) + + # imarom - no wrap for now + #array.extend(textwrap.wrap(c, width)) + array.extend([c]) + line_wrapped.append(array) max_cell_lines = reduce(max, list(map(len, line_wrapped))) for cell, valign in zip(line_wrapped, self._valign): |