summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Klein <danklein10@gmail.com>2015-12-07 08:29:17 +0200
committerDan Klein <danklein10@gmail.com>2015-12-07 08:29:17 +0200
commit503c10b024aa2ed6d4d8dc7fb5debf4a64bd9b1e (patch)
tree8e83b5944648851bd5146883414e40b8c2526083
parenta609111bc37ef88f14d4f2ebf7cd186b04b86402 (diff)
Re-designed the statistic building model based on agreed diagram.
WORKING: all polling stats
-rw-r--r--scripts/automation/trex_control_plane/client/trex_async_client.py8
-rwxr-xr-xscripts/automation/trex_control_plane/client/trex_stateless_client.py139
-rwxr-xr-xscripts/automation/trex_control_plane/common/trex_stats.py261
-rw-r--r--scripts/automation/trex_control_plane/console/trex_status.py2
4 files changed, 233 insertions, 177 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 a2bb4752..8b274134 100644
--- a/scripts/automation/trex_control_plane/client/trex_async_client.py
+++ b/scripts/automation/trex_control_plane/client/trex_async_client.py
@@ -51,7 +51,7 @@ class CTRexAsyncStats(object):
self.ref_point = self.current
- def get(self, field, format = False, suffix = ""):
+ def get(self, field, format=False, suffix=""):
if not field in self.current:
return "N/A"
@@ -61,8 +61,7 @@ class CTRexAsyncStats(object):
else:
return self.format_num(self.current[field], suffix)
-
- def get_rel (self, field, format = False, suffix = ""):
+ def get_rel (self, field, format=False, suffix=""):
if not field in self.current:
return "N/A"
@@ -204,7 +203,8 @@ class CTRexAsyncClient():
def __dispatch (self, name, type, data):
# stats
if name == "trex-global":
- self.stats.update(data)
+ # self.stats.update(data)
+ self.stateless_client.handle_async_stats_update(data)
# events
elif name == "trex-event":
self.stateless_client.handle_async_event(type, data)
diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py
index 7be7392e..e02620a6 100755
--- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py
+++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py
@@ -19,6 +19,7 @@ from common import trex_stats
from client_utils import parsing_opts, text_tables
import time
import datetime
+import re
from trex_async_client import CTRexAsyncClient
@@ -165,6 +166,7 @@ class Port(object):
self.driver = driver
self.speed = speed
self.streams = {}
+ self.port_stats = trex_stats.CPortStats(self)
def err(self, msg):
return RC_ERR("port {0} : {1}".format(self.port_id, msg))
@@ -189,7 +191,6 @@ class Port(object):
else:
return self.err(rc.data)
-
# release the port
def release(self):
params = {"port_id": self.port_id,
@@ -409,6 +410,16 @@ class Port(object):
def get_port_state_name(self):
return self.STATES_MAP.get(self.state, "Unknown")
+ ################# stats handler ######################
+ def generate_port_stats(self):
+ return self.port_stats.generate_stats()
+ pass
+
+ def generate_port_status(self):
+ return {"port-type": self.driver,
+ "maximum": "{speed} Gb/s".format(speed=self.speed),
+ "port-status": self.get_port_state_name()
+ }
################# events handler ######################
def async_event_port_stopped (self):
@@ -424,22 +435,24 @@ class CTRexStatelessClient(object):
self.system_info = None
self.comm_link = CTRexStatelessClient.CCommLink(server, sync_port, virtual)
self.verbose = False
- self.ports = []
- self._conn_handler = {}
- self._active_ports = set()
- self._system_info = None
- self._server_version = None
+ self.ports = {}
+ # self._conn_handler = {}
+ # self._active_ports = set()
+ self._connection_info = {"server": server,
+ "sync_port": sync_port,
+ "async_port": async_port}
+ self.system_info = {}
+ self.server_version = {}
self.__err_log = None
self._async_client = CTRexAsyncClient(server, async_port, self)
self.streams_db = CStreamsDB()
- self.info_and_stats = trex_stats.CTRexInformationCenter(self.user,
- {"server": server,
- "sync_port": sync_port,
- "async_port": async_port},
- self.ports,
- self.get_stats_async())
+ self.global_stats = trex_stats.CGlobalStats(self._connection_info,
+ self.server_version,
+ self.ports)
+ self.stats_generator = trex_stats.CTRexStatsGenerator(self.global_stats,
+ self.ports)
self.connected = False
@@ -447,6 +460,33 @@ class CTRexStatelessClient(object):
################# events handler ######################
+ def handle_async_stats_update(self, dump_data):
+ global_stats = {}
+ port_stats = {}
+
+ # filter the values per port and general
+ for key, value in dump_data.iteritems():
+ # match a pattern of ports
+ m = re.search('(.*)\-([0-8])', key)
+ if m:
+ port_id = int(m.group(2))
+ field_name = m.group(1)
+ if self.ports.has_key(port_id):
+ if not port_id in port_stats:
+ port_stats[port_id] = {}
+ port_stats[port_id][field_name] = value
+ else:
+ continue
+ else:
+ # no port match - general stats
+ global_stats[key] = value
+
+ # update the general object with the snapshot
+ self.global_stats.update(global_stats)
+ # update all ports
+ for port_id, data in port_stats.iteritems():
+ self.ports[port_id].port_stats.update(data)
+
def handle_async_event (self, type, data):
# DP stopped
@@ -556,7 +596,7 @@ class CTRexStatelessClient(object):
return RC_ERR(data)
self.server_version = data
- self.info_and_stats.server_version = data
+ self.global_stats.server_version = data
# cache system info
# self.get_system_info(refresh=True)
@@ -564,7 +604,6 @@ class CTRexStatelessClient(object):
if not rc:
return RC_ERR(data)
self.system_info = data
- self.info_and_stats.system_info = data
# cache supported commands
rc, data = self.transmit("get_supported_cmds")
@@ -577,7 +616,7 @@ class CTRexStatelessClient(object):
for port_id in xrange(self.get_port_count()):
speed = self.system_info['ports'][port_id]['speed']
driver = self.system_info['ports'][port_id]['driver']
- self.ports.append(Port(port_id, speed, driver, self.user, self.transmit))
+ self.ports[port_id] = Port(port_id, speed, driver, self.user, self.transmit)
# acquire all ports
rc = self.acquire()
@@ -634,11 +673,14 @@ class CTRexStatelessClient(object):
return self.comm_link.server
def get_acquired_ports(self):
- return [port.port_id for port in self.ports if port.is_acquired()]
-
+ return [port_id
+ for port_id, port_obj in self.ports.iteritems()
+ if port_obj.is_acquired()]
def get_active_ports(self):
- return [port.port_id for port in self.ports if port.is_active()]
+ return [port_id
+ for port_id, port_obj in self.ports.iteritems()
+ if port_obj.is_active()]
def set_verbose(self, mode):
self.comm_link.set_verbose(mode)
@@ -893,7 +935,7 @@ class CTRexStatelessClient(object):
# update cmd
def cmd_update (self, port_id_list, mult):
- # find the relveant ports
+ # find the relevant ports
active_ports = list(set(self.get_active_ports()).intersection(port_id_list))
if not active_ports:
@@ -909,15 +951,14 @@ class CTRexStatelessClient(object):
return RC_OK()
def cmd_clear(self, port_id_list):
- self.info_and_stats.clear(port_id_list)
+ # self.info_and_stats.clear(port_id_list)
return RC_OK()
-
# pause cmd
def cmd_pause (self, port_id_list):
- # find the relveant ports
+ # find the relevant ports
active_ports = list(set(self.get_active_ports()).intersection(port_id_list))
if not active_ports:
@@ -932,19 +973,6 @@ class CTRexStatelessClient(object):
return RC_OK()
- def cmd_pause_line (self, line):
- '''Pause active traffic in specified ports on TRex\n'''
- parser = parsing_opts.gen_parser(self,
- "pause",
- self.cmd_stop_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL)
-
- opts = parser.parse_args(line.split())
- if opts is None:
- return RC_ERR("bad command line parameters")
-
- return self.cmd_pause(opts.ports)
-
# resume cmd
def cmd_resume (self, port_id_list):
@@ -965,20 +993,6 @@ class CTRexStatelessClient(object):
return RC_OK()
- def cmd_resume_line (self, line):
- '''Resume active traffic in specified ports on TRex\n'''
- parser = parsing_opts.gen_parser(self,
- "resume",
- self.cmd_stop_line.__doc__,
- parsing_opts.PORT_LIST_WITH_ALL)
-
- opts = parser.parse_args(line.split())
- if opts is None:
- return RC_ERR("bad command line parameters")
-
- return self.cmd_resume(opts.ports)
-
-
# start cmd
def cmd_start (self, port_id_list, stream_list, mult, force, duration):
@@ -1020,7 +1034,7 @@ class CTRexStatelessClient(object):
stats_obj = {}
for stats_type in stats_opts:
- stats_obj.update(self.info_and_stats.generate_single_statistic(port_id_list, stats_type))
+ stats_obj.update(self.stats_generator.generate_single_statistic(port_id_list, stats_type))
return stats_obj
@@ -1066,6 +1080,19 @@ class CTRexStatelessClient(object):
return self.cmd_start(opts.ports, stream_list, opts.mult, opts.force, opts.duration)
+ def cmd_resume_line (self, line):
+ '''Resume active traffic in specified ports on TRex\n'''
+ parser = parsing_opts.gen_parser(self,
+ "resume",
+ self.cmd_stop_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL)
+
+ opts = parser.parse_args(line.split())
+ if opts is None:
+ return RC_ERR("bad command line parameters")
+
+ return self.cmd_resume(opts.ports)
+
def cmd_stop_line (self, line):
'''Stop active traffic in specified ports on TRex\n'''
parser = parsing_opts.gen_parser(self,
@@ -1079,6 +1106,18 @@ class CTRexStatelessClient(object):
return self.cmd_stop(opts.ports)
+ def cmd_pause_line (self, line):
+ '''Pause active traffic in specified ports on TRex\n'''
+ parser = parsing_opts.gen_parser(self,
+ "pause",
+ self.cmd_stop_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL)
+
+ opts = parser.parse_args(line.split())
+ if opts is None:
+ return RC_ERR("bad command line parameters")
+
+ return self.cmd_pause(opts.ports)
def cmd_update_line (self, line):
'''Update port(s) speed currently active\n'''
diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py
index ec455730..1f9d59e3 100755
--- a/scripts/automation/trex_control_plane/common/trex_stats.py
+++ b/scripts/automation/trex_control_plane/common/trex_stats.py
@@ -4,6 +4,8 @@ from client_utils import text_tables
from common.text_opts import format_text
from client.trex_async_client import CTRexAsyncStats
import copy
+import datetime
+import re
GLOBAL_STATS = 'g'
PORT_STATS = 'p'
@@ -12,28 +14,15 @@ ALL_STATS_OPTS = {GLOBAL_STATS, PORT_STATS, PORT_STATUS}
ExportableStats = namedtuple('ExportableStats', ['raw_data', 'text_table'])
-class CTRexInformationCenter(object):
+class CTRexStatsGenerator(object):
+ """
+ This object is responsible of generating stats from objects maintained at
+ CTRexStatelessClient and the ports.
+ """
- def __init__(self, username, connection_info, ports_ref, async_stats_ref):
- self.user = username
- self.connection_info = connection_info
- self.server_version = None
- self.system_info = None
- self._ports = ports_ref
- self._async_stats = async_stats_ref
-
- # def __getitem__(self, item):
- # stats_obj = getattr(self, item)
- # if stats_obj:
- # return stats_obj.get_stats()
- # else:
- # return None
-
- def clear(self, port_id_list):
- self._async_stats.get_general_stats().clear()
- for port_id in port_id_list:
- self._async_stats.get_port_stats(port_id).clear()
- pass
+ def __init__(self, global_stats_ref, ports_dict_ref):
+ self._global_stats = global_stats_ref
+ self._ports_dict = ports_dict_ref
def generate_single_statistic(self, port_id_list, statistic_type):
if statistic_type == GLOBAL_STATS:
@@ -48,32 +37,17 @@ class CTRexInformationCenter(object):
return {}
def _generate_global_stats(self):
- stats_obj = self._async_stats.get_general_stats()
- return_stats_data = \
- OrderedDict([("connection", "{host}, Port {port}".format(host=self.connection_info.get("server"),
- port=self.connection_info.get("sync_port"))),
- ("version", "{ver}, UUID: {uuid}".format(ver=self.server_version.get("version", "N/A"),
- uuid="N/A")),
- ("cpu_util", "{0}%".format(stats_obj.get("m_cpu_util"))),
- ("total_tx", stats_obj.get("m_tx_bps", format=True, suffix="b/sec")),
- ("total_rx", stats_obj.get("m_rx_bps", format=True, suffix="b/sec")),
- ("total_pps", stats_obj.format_num(stats_obj.get("m_tx_pps") + stats_obj.get("m_rx_pps"),
- suffix="pkt/sec")),
- ("total_streams", sum([len(port.streams)
- for port in self._ports])),
- ("active_ports", sum([port.is_active()
- for port in self._ports]))
- ]
- )
+ # stats_obj = self._async_stats.get_general_stats()
+ stats_data = self._global_stats.generate_stats()
# 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 return_stats_data.iteritems()],
+ for k, v in stats_data.iteritems()],
header=False)
- return {"global_statistics": ExportableStats(return_stats_data, stats_table)}
+ return {"global_statistics": ExportableStats(stats_data, stats_table)}
def _generate_port_stats(self, port_id_list):
relevant_ports = self.__get_relevant_ports(port_id_list)
@@ -94,41 +68,11 @@ class CTRexInformationCenter(object):
for port_obj in relevant_ports:
# fetch port data
- port_stats = self._async_stats.get_port_stats(port_obj.port_id)
-
- owner = self.user
- active = "YES" if port_obj.is_active() else "NO"
- tx_bytes = port_stats.get_rel("obytes", format = True, suffix = "B")
- rx_bytes = port_stats.get_rel("ibytes", format = True, suffix = "B")
- tx_pkts = port_stats.get_rel("opackets", format = True, suffix = "pkts")
- rx_pkts = port_stats.get_rel("ipackets", format = True, suffix = "pkts")
- tx_errors = port_stats.get_rel("oerrors", format = True)
- rx_errors = port_stats.get_rel("ierrors", format = True)
- tx_bw = port_stats.get("m_total_tx_bps", format = True, suffix = "bps")
- rx_bw = port_stats.get("m_total_rx_bps", format = True, suffix = "bps")
+ port_stats = port_obj.generate_port_stats()
# populate to data structures
- return_stats_data[port_obj.port_id] = {"owner": owner,
- "active": active,
- "tx-bytes": tx_bytes,
- "rx-bytes": rx_bytes,
- "tx-pkts": tx_pkts,
- "rx-pkts": rx_pkts,
- "tx-errors": tx_errors,
- "rx-errors": rx_errors,
- "Tx-BW": tx_bw,
- "Rx-BW": rx_bw
- }
- per_field_stats["owner"].append(owner)
- per_field_stats["active"].append(active)
- per_field_stats["tx-bytes"].append(tx_bytes)
- per_field_stats["rx-bytes"].append(rx_bytes)
- per_field_stats["tx-pkts"].append(tx_pkts)
- per_field_stats["rx-pkts"].append(rx_pkts)
- per_field_stats["tx-errors"].append(tx_errors)
- per_field_stats["rx-errors"].append(rx_errors)
- per_field_stats["tx-BW"].append(tx_bw)
- per_field_stats["rx-BW"].append(rx_bw)
+ return_stats_data[port_obj.port_id] = port_stats
+ self.__update_per_field_dict(port_stats, per_field_stats)
stats_table = text_tables.TRexTextTable()
stats_table.set_cols_align(["l"] + ["r"]*len(relevant_ports))
@@ -152,21 +96,13 @@ class CTRexInformationCenter(object):
for port_obj in relevant_ports:
# fetch port data
- port_stats = self._async_stats.get_port_stats(port_obj.port_id)
-
-
- port_type = port_obj.driver
- maximum = "{speed} Gb/s".format(speed=port_obj.speed)#CTRexAsyncStats.format_num(port_obj.get_speed_bps(), suffix="bps")
- port_status = port_obj.get_port_state_name()
+ # port_stats = self._async_stats.get_port_stats(port_obj.port_id)
+ port_status = port_obj.generate_port_status()
# populate to data structures
- return_stats_data[port_obj.port_id] = {"port-type": port_type,
- "maximum": maximum,
- "port-status": port_status,
- }
- per_field_status["port-type"].append(port_type)
- per_field_status["maximum"].append(maximum)
- per_field_status["port-status"].append(port_status)
+ return_stats_data[port_obj.port_id] = port_status
+
+ self.__update_per_field_dict(port_status, per_field_status)
stats_table = text_tables.TRexTextTable()
stats_table.set_cols_align(["l"] + ["c"]*len(relevant_ports))
@@ -181,7 +117,7 @@ class CTRexInformationCenter(object):
def __get_relevant_ports(self, port_id_list):
# fetch owned ports
ports = [port_obj
- for port_obj in self._ports
+ for _, port_obj in self._ports_dict.iteritems()
if port_obj.is_acquired() and port_obj.port_id in port_id_list]
# display only the first FOUR options, by design
if len(ports) > 4:
@@ -189,61 +125,142 @@ class CTRexInformationCenter(object):
ports = ports[:4]
return ports
+ def __update_per_field_dict(self, dict_src_data, dict_dest_ref):
+ for key, val in dict_src_data.iteritems():
+ if key in dict_dest_ref:
+ dict_dest_ref[key].append(val)
-class CTRexStatsManager(object):
- def __init__(self, *args):
- for stat_type in args:
- # register stat handler for each stats type
- setattr(self, stat_type, CTRexStatsManager.CSingleStatsHandler())
+class CTRexStats(object):
+ """ This is an abstract class to represent a stats object """
+
+ def __init__(self):
+ self.reference_stats = None
+ self.latest_stats = {}
+ self.last_update_ts = datetime.datetime.now()
+
def __getitem__(self, item):
- stats_obj = getattr(self, item)
- if stats_obj:
- return stats_obj.get_stats()
- else:
- return None
+ # override this to allow quick and clean access to fields
+ if not item in self.latest_stats:
+ return "N/A"
+
+ # item must exist
+ m = re.search('_(([a-z])ps)$', item)
+ if m:
+ # this is a non-relative item
+ unit = m.group(2)
+ if unit == "b":
+ return self.get(item, format=True, suffix="b/sec")
+ elif unit == "p":
+ return self.get(item, format=True, suffix="pkt/sec")
+ else:
+ return self.get(item, format=True, suffix=m.group(1))
+
+ m = re.search('^[i|o](a-z+)$', item)
+ if m:
+ # this is a non-relative item
+ type = m.group(1)
+ if type == "bytes":
+ return self.get_rel(item, format=True, suffix="B")
+ elif type == "packets":
+ return self.get_rel(item, format=True, suffix="pkts")
+ else:
+ # do not format with suffix
+ return self.get_rel(item, format=True)
- class CSingleStatsHandler(object):
+ # can't match to any known pattern, return N/A
+ return "N/A"
- def __init__(self):
- self._stats = {}
+ @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, obj_id, stats_obj):
- assert isinstance(stats_obj, CTRexStats)
- self._stats[obj_id] = stats_obj
+ def generate_stats(self):
+ # must be implemented by designated classes (such as port/ global stats)
+ raise NotImplementedError()
- def get_stats(self, obj_id=None):
- if obj_id:
- return copy.copy(self._stats.pop(obj_id))
- else:
- return copy.copy(self._stats)
+ def update(self, snapshot):
+ # update
+ self.last_update_ts = datetime.datetime.now()
+ self.latest_stats = snapshot
-class CTRexStats(object):
- def __init__(self, **kwargs):
- for k, v in kwargs.items():
- setattr(self, k, v)
+ if self.reference_stats == None:
+ self.reference_stats = self.latest_stats
+
+ def clear_stats(self):
+ self.reference_stats = self.latest_stats
+
+ def get(self, field, format=False, suffix=""):
+ if not field in self.latest_stats:
+ return "N/A"
+ if not format:
+ return self.latest_stats[field]
+ else:
+ return self.format_num(self.latest_stats[field], suffix)
+
+ def get_rel(self, field, format=False, suffix=""):
+ if not field in self.latest_stats:
+ return "N/A"
+ 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)
class CGlobalStats(CTRexStats):
- def __init__(self, **kwargs):
- super(CGlobalStats, self).__init__(kwargs)
- pass
+ pass
+
+ def __init__(self, connection_info, server_version, ports_dict_ref):
+ super(CGlobalStats, self).__init__()
+ self.connection_info = connection_info
+ self.server_version = server_version
+ self._ports_dict = ports_dict_ref
+ def generate_stats(self):
+ return OrderedDict([("connection", "{host}, Port {port}".format(host=self.connection_info.get("server"),
+ port=self.connection_info.get("sync_port"))),
+ ("version", "{ver}, UUID: {uuid}".format(ver=self.server_version.get("version", "N/A"),
+ uuid="N/A")),
+ ("cpu_util", "{0}%".format(self.get("m_cpu_util"))),
+ ("total_tx", self.get("m_tx_bps", format=True, suffix="b/sec")),
+ ("total_rx", self.get("m_rx_bps", format=True, suffix="b/sec")),
+ ("total_pps", self.format_num(self.get("m_tx_pps") + self.get("m_rx_pps"),
+ suffix="pkt/sec")),
+ ("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()]))
+ ]
+ )
class CPortStats(CTRexStats):
- def __init__(self, **kwargs):
- super(CPortStats, self).__init__(kwargs)
- pass
+ pass
+ def __init__(self, port_obj):
+ super(CPortStats, self).__init__()
+ self._port_obj = port_obj
+
+ def generate_stats(self):
+ return {"owner": self._port_obj.user,
+ "active": "YES" if self._port_obj.is_active() else "NO",
+ "tx-bytes": self.get_rel("obytes", format = True, suffix = "B"),
+ "rx-bytes": self.get_rel("ibytes", format = True, suffix = "B"),
+ "tx-pkts": self.get_rel("opackets", format = True, suffix = "pkts"),
+ "rx-pkts": self.get_rel("ipackets", format = True, suffix = "pkts"),
+ "tx-errors": self.get_rel("oerrors", format = True),
+ "rx-errors": self.get_rel("ierrors", format = True),
+ "tx-BW": self.get("m_total_tx_bps", format = True, suffix = "bps"),
+ "rx-BW": self.get("m_total_rx_bps", format = True, suffix = "bps")
+ }
-class CStreamStats(CTRexStats):
- def __init__(self, **kwargs):
- super(CStreamStats, self).__init__(kwargs)
- pass
if __name__ == "__main__":
diff --git a/scripts/automation/trex_control_plane/console/trex_status.py b/scripts/automation/trex_control_plane/console/trex_status.py
index 10ac75c9..cdf3fb69 100644
--- a/scripts/automation/trex_control_plane/console/trex_status.py
+++ b/scripts/automation/trex_control_plane/console/trex_status.py
@@ -494,7 +494,7 @@ class CTRexStatus():
self.stats_panel.panel.top()
self.stats_panel.draw()
- panel.update_panels();
+ panel.update_panels()
self.stdscr.refresh()
sleep(0.01)