summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--scripts/automation/trex_control_plane/client/trex_async_client.py14
-rw-r--r--scripts/automation/trex_control_plane/client/trex_port.py5
-rwxr-xr-xscripts/automation/trex_control_plane/common/text_opts.py42
-rwxr-xr-xscripts/automation/trex_control_plane/common/trex_stats.py69
-rwxr-xr-xscripts/automation/trex_control_plane/console/trex_console.py2
-rw-r--r--scripts/external_libs/texttable-0.8.4/texttable.py13
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):