summaryrefslogtreecommitdiffstats
path: root/scripts/automation/trex_control_plane/stl/trex_stl_lib
diff options
context:
space:
mode:
authorHanoh Haim <hhaim@cisco.com>2016-04-13 12:40:21 +0300
committerHanoh Haim <hhaim@cisco.com>2016-04-13 12:40:21 +0300
commit8076b8111991a790886537a5c1d7b54a01f479e4 (patch)
treed3615814b3a62a4e19d64fd9cf1269c36ba62ed0 /scripts/automation/trex_control_plane/stl/trex_stl_lib
parent1f450703d3a51ed454af26aa494a7c6e2579686d (diff)
parent0b39ec305e80999c7dbe36d4b0d3850b04709571 (diff)
Merge v2.0
Diffstat (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib')
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py6
-rwxr-xr-xscripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py246
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py11
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py50
-rw-r--r--scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py5
5 files changed, 275 insertions, 43 deletions
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py
index 0f0fe83e..022077a9 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py
@@ -275,18 +275,18 @@ class CTRexAsyncClient():
# stats
if name == "trex-global":
- self.event_handler.handle_async_stats_update(data, baseline)
+ self.event_handler.on_async_stats_update(data, baseline)
# events
elif name == "trex-event":
- self.event_handler.handle_async_event(type, data)
+ self.event_handler.on_async_event(type, data)
# barriers
elif name == "trex-barrier":
self.handle_async_barrier(type, data)
elif name == "flow_stats":
- self.event_handler.handle_async_rx_stats_event(data, baseline)
+ self.event_handler.on_async_rx_stats_event(data, baseline)
else:
pass
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 1c4984eb..98f3fe3a 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
@@ -125,8 +125,26 @@ class DefaultLogger(LoggerApi):
############################ #############################
############################ #############################
+# an event
+class Event(object):
+
+ def __init__ (self, origin, ev_type, msg):
+ self.origin = origin
+ self.ev_type = ev_type
+ self.msg = msg
+
+ self.ts = datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S')
+
+ def __str__ (self):
+
+ prefix = "[{:^}][{:^}]".format(self.origin, self.ev_type)
+
+ return "{:<10} - {:18} - {:}".format(self.ts, prefix, format_text(self.msg, 'bold'))
+
+
# handles different async events given to the client
-class AsyncEventHandler(object):
+class EventsHandler(object):
+
def __init__ (self, client):
self.client = client
@@ -136,31 +154,41 @@ class AsyncEventHandler(object):
# public functions
- def get_events (self):
- return self.events
+ def get_events (self, ev_type_filter = None):
+ if ev_type_filter:
+ return [ev for ev in self.events if ev.ev_type in listify(ev_type_filter)]
+ else:
+ return [ev for ev in self.events]
def clear_events (self):
self.events = []
+ def log_warning (self, msg, show = True):
+ self.__add_event_log('local', 'warning', msg, show)
+
+
+ # events called internally
+
def on_async_dead (self):
if self.client.connected:
msg = 'Lost connection to server'
- self.__add_event_log(msg, 'local', True)
+ self.__add_event_log('local', 'info', msg, True)
self.client.connected = False
def on_async_alive (self):
pass
+
- def handle_async_rx_stats_event (self, data, baseline):
+ def on_async_rx_stats_event (self, data, baseline):
self.client.flow_stats.update(data, baseline)
# handles an async stats update from the subscriber
- def handle_async_stats_update(self, dump_data, baseline):
+ def on_async_stats_update(self, dump_data, baseline):
global_stats = {}
port_stats = {}
@@ -189,8 +217,9 @@ class AsyncEventHandler(object):
self.client.ports[port_id].port_stats.update(data, baseline)
+
# dispatcher for server async events (port started, port stopped and etc.)
- def handle_async_event (self, type, data):
+ def on_async_event (self, type, data):
# DP stopped
show_event = False
@@ -248,7 +277,7 @@ class AsyncEventHandler(object):
ev = "Port {0} was forcely taken by '{1}'".format(port_id, who)
# call the handler
- self.__async_event_port_forced_acquired(port_id)
+ self.__async_event_port_forced_acquired(port_id, who)
show_event = True
# server stopped
@@ -263,7 +292,7 @@ class AsyncEventHandler(object):
return
- self.__add_event_log(ev, 'server', show_event)
+ self.__add_event_log('server', 'info', ev, show_event)
# private functions
@@ -287,8 +316,8 @@ class AsyncEventHandler(object):
self.client.ports[port_id].async_event_port_resumed()
- def __async_event_port_forced_acquired (self, port_id):
- self.client.ports[port_id].async_event_forced_acquired()
+ def __async_event_port_forced_acquired (self, port_id, who):
+ self.client.ports[port_id].async_event_forced_acquired(who)
def __async_event_server_stopped (self):
@@ -296,19 +325,12 @@ class AsyncEventHandler(object):
# add event to log
- def __add_event_log (self, msg, ev_type, show = False):
-
- if ev_type == "server":
- prefix = "[server]"
- elif ev_type == "local":
- prefix = "[local]"
-
- ts = time.time()
- st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
- self.events.append("{:<10} - {:^8} - {:}".format(st, prefix, format_text(msg, 'bold')))
+ def __add_event_log (self, origin, ev_type, msg, show = False):
+ event = Event(origin, ev_type, msg)
+ self.events.append(event)
if show:
- self.logger.async_log(format_text("\n\n{:^8} - {:}".format(prefix, format_text(msg, 'bold'))))
+ self.logger.async_log("\n\n{0}".format(str(event)))
@@ -452,7 +474,7 @@ class STLClient(object):
self)
# async event handler manager
- self.event_handler = AsyncEventHandler(self)
+ self.event_handler = EventsHandler(self)
# async subscriber level
self.async_client = CTRexAsyncClient(server,
@@ -472,7 +494,8 @@ class STLClient(object):
self.global_stats = trex_stl_stats.CGlobalStats(self.connection_info,
self.server_version,
- self.ports)
+ self.ports,
+ self.event_handler)
self.flow_stats = trex_stl_stats.CRxStats(self.ports)
@@ -482,7 +505,7 @@ class STLClient(object):
# API classes
- self.api_vers = [ {'type': 'core', 'major': 1, 'minor':0 }
+ self.api_vers = [ {'type': 'core', 'major': 1, 'minor':1 }
]
self.api_h = {'core': None}
@@ -977,7 +1000,8 @@ class STLClient(object):
"""
- return not (self.get_all_ports() == self.get_acquired_ports())
+ return (self.get_all_ports() == self.get_acquired_ports())
+
# is the client connected ?
def is_connected (self):
@@ -1155,9 +1179,58 @@ class STLClient(object):
return self.__get_stats(ports)
- # return all async events
- def get_events (self):
- return self.event_handler.get_events()
+
+ def get_events (self, ev_type_filter = None):
+ """
+ returns all the logged events
+
+ :parameters:
+ ev_type_filter - 'info', 'warning' or a list of those
+ default is no filter
+
+ :return:
+ logged events
+
+ :raises:
+ None
+
+ """
+ return self.event_handler.get_events(ev_type_filter)
+
+
+ def get_warnings (self):
+ """
+ returns all the warnings logged events
+
+ :parameters:
+ None
+
+ :return:
+ warning logged events
+
+ :raises:
+ None
+
+ """
+ return self.get_events(ev_type_filter = 'warning')
+
+
+ def get_info (self):
+ """
+ returns all the info logged events
+
+ :parameters:
+ None
+
+ :return:
+ warning logged events
+
+ :raises:
+ None
+
+ """
+ return self.get_events(ev_type_filter = 'info')
+
# get port(s) info as a list of dicts
@__api_check(True)
@@ -1951,11 +2024,11 @@ class STLClient(object):
@__console
def connect_line (self, line):
- '''Connects to the TRex server'''
- # define a parser
+ '''Connects to the TRex server and acquire ports'''
parser = parsing_opts.gen_parser(self,
"connect",
self.connect_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL,
parsing_opts.FORCE)
opts = parser.parse_args(line.split())
@@ -1963,9 +2036,62 @@ class STLClient(object):
if opts is None:
return
- # call the API
self.connect()
- self.acquire(force = opts.force)
+ self.acquire(ports = opts.ports, force = opts.force)
+
+ # true means print time
+ return True
+
+ @__console
+ def acquire_line (self, line):
+ '''Acquire ports\n'''
+
+ # define a parser
+ parser = parsing_opts.gen_parser(self,
+ "acquire",
+ self.acquire_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL,
+ parsing_opts.FORCE)
+
+ opts = parser.parse_args(line.split())
+
+ if opts is None:
+ return
+
+ # call the API
+ ports = [x for x in opts.ports if x not in self.get_acquired_ports()]
+ if not ports:
+ self.logger.log("Port(s) {0} are already acquired\n".format(opts.ports))
+ return
+
+ self.acquire(ports = ports, force = opts.force)
+
+ # true means print time
+ return True
+
+
+ #
+ @__console
+ def release_line (self, line):
+ '''Release ports\n'''
+
+ parser = parsing_opts.gen_parser(self,
+ "release",
+ self.release_line.__doc__,
+ parsing_opts.PORT_LIST_WITH_ALL)
+
+ opts = parser.parse_args(line.split())
+
+ if opts is None:
+ return
+
+ # call the API
+ ports = [x for x in opts.ports if x in self.get_acquired_ports()]
+ if not ports:
+ self.logger.log("Port(s) {0} are not owned by you\n".format(opts.ports))
+ return
+
+ self.release(ports = ports)
# true means print time
return True
@@ -2212,7 +2338,7 @@ class STLClient(object):
mask = self.__get_mask_keys(**self.__filter_namespace_args(opts, trex_stl_stats.ALL_STATS_OPTS))
if not mask:
# set to show all stats if no filter was given
- mask = trex_stl_stats.ALL_STATS_OPTS
+ mask = trex_stl_stats.COMPACT
stats_opts = common.list_intersect(trex_stl_stats.ALL_STATS_OPTS, mask)
@@ -2371,3 +2497,55 @@ class STLClient(object):
self.logger.log("")
+
+ @__console
+ def get_events_line (self, line):
+ '''shows events recieved from server\n'''
+
+ x = [parsing_opts.ArgumentPack(['-c','--clear'],
+ {'action' : "store_true",
+ 'default': False,
+ 'help': "clear the events log"}),
+
+ parsing_opts.ArgumentPack(['-i','--info'],
+ {'action' : "store_true",
+ 'default': False,
+ 'help': "show info events"}),
+
+ parsing_opts.ArgumentPack(['-w','--warn'],
+ {'action' : "store_true",
+ 'default': False,
+ 'help': "show warning events"}),
+
+ ]
+
+
+ parser = parsing_opts.gen_parser(self,
+ "events",
+ self.get_events_line.__doc__,
+ *x)
+
+ opts = parser.parse_args(line.split())
+ if opts is None:
+ return
+
+
+ ev_type_filter = []
+
+ if opts.info:
+ ev_type_filter.append('info')
+
+ if opts.warn:
+ ev_type_filter.append('warning')
+
+ if not ev_type_filter:
+ ev_type_filter = None
+
+ events = self.get_events(ev_type_filter)
+ for ev in events:
+ self.logger.log(ev)
+
+ if opts.clear:
+ self.clear_events()
+ self.logger.log(format_text("\nEvent log was cleared\n"))
+
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 87f7b437..6f6f50b1 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
@@ -64,6 +64,8 @@ class Port(object):
self.tx_stopped_ts = None
self.has_rx_streams = False
+ self.owner = ''
+
def err(self, msg):
return RC_ERR("port {0} : {1}\n".format(self.port_id, msg))
@@ -113,6 +115,11 @@ class Port(object):
def is_paused (self):
return (self.state == self.STATE_PAUSE)
+ def get_owner (self):
+ if self.is_acquired():
+ return self.user
+ else:
+ return self.owner
def sync(self):
params = {"port_id": self.port_id}
@@ -137,6 +144,7 @@ class Port(object):
else:
raise Exception("port {0}: bad state received from server '{1}'".format(self.port_id, port_state))
+ self.owner = rc.data()['owner']
self.next_available_id = int(rc.data()['max_stream_id']) + 1
@@ -671,6 +679,7 @@ class Port(object):
if not self.is_acquired():
self.state = self.STATE_TX
- def async_event_forced_acquired (self):
+ def async_event_forced_acquired (self, who):
self.handler = None
+ self.owner = who
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 dd597648..f0ac5c33 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
@@ -91,6 +91,33 @@ def calculate_diff_raw (samples):
return total
+# a simple object to keep a watch over a field
+class WatchedField(object):
+
+ def __init__ (self, name, suffix, high_th, low_th, events_handler):
+ self.name = name
+ self.suffix = suffix
+ self.high_th = high_th
+ self.low_th = low_th
+ self.events_handler = events_handler
+
+ self.hot = False
+ self.current = None
+
+ def update (self, value):
+ if value is None:
+ return
+
+ if value > self.high_th and not self.hot:
+ self.events_handler.log_warning("{0} is high: {1}{2}".format(self.name, value, self.suffix))
+ self.hot = True
+
+ if value < self.low_th and self.hot:
+ self.hot = False
+
+ self.current = value
+
+
class CTRexInfoGenerator(object):
"""
@@ -540,17 +567,24 @@ class CTRexStats(object):
class CGlobalStats(CTRexStats):
- def __init__(self, connection_info, server_version, ports_dict_ref):
+ def __init__(self, connection_info, server_version, ports_dict_ref, events_handler):
super(CGlobalStats, self).__init__()
+
self.connection_info = connection_info
- self.server_version = server_version
- self._ports_dict = ports_dict_ref
+ self.server_version = server_version
+ self._ports_dict = ports_dict_ref
+ self.events_handler = events_handler
+
+ self.watched_cpu_util = WatchedField('CPU util.', '%', 85, 60, events_handler)
+ self.watched_rx_cpu_util = WatchedField('RX core util.', '%', 85, 60, events_handler)
def get_stats (self):
stats = {}
# absolute
- stats['cpu_util'] = self.get("m_cpu_util")
+ stats['cpu_util'] = self.get("m_cpu_util")
+ stats['rx_cpu_util'] = self.get("m_rx_cpu_util")
+
stats['tx_bps'] = self.get("m_tx_bps")
stats['tx_pps'] = self.get("m_tx_pps")
@@ -576,6 +610,9 @@ class CGlobalStats(CTRexStats):
# simple...
self.latest_stats = snapshot
+ self.watched_cpu_util.update(snapshot.get('m_cpu_util'))
+ self.watched_rx_cpu_util.update(snapshot.get('m_rx_cpu_util'))
+
return True
@@ -588,6 +625,9 @@ class CGlobalStats(CTRexStats):
("cpu_util", "{0}% {1}".format( format_threshold(self.get("m_cpu_util"), [85, 100], [0, 85]),
self.get_trend_gui("m_cpu_util", use_raw = True))),
+ ("rx_cpu_util", "{0}% {1}".format( format_threshold(self.get("m_rx_cpu_util"), [85, 100], [0, 85]),
+ self.get_trend_gui("m_rx_cpu_util", use_raw = True))),
+
(" ", ""),
("total_tx_L2", "{0} {1}".format( self.get("m_tx_bps", format=True, suffix="b/sec"),
@@ -707,7 +747,7 @@ class CPortStats(CTRexStats):
state = format_text(state, 'bold')
- return {"owner": self._port_obj.user if self._port_obj else "",
+ return {"owner": self._port_obj.get_owner() if self._port_obj else "",
"state": "{0}".format(state),
"--": " ",
diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py
index f3ac5c65..f6718fda 100644
--- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py
+++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_types.py
@@ -140,6 +140,11 @@ def verify_exclusive_arg (args_list):
if not (len(list(filter(lambda x: x is not None, args_list))) == 1):
raise STLError('exactly one parameter from {0} should be provided'.format(args_list))
+def listify (x):
+ if isinstance(x, list):
+ return x
+ else:
+ return [x]
# shows as 'N/A', but does not let any compares for user to not mistake in automation
class StatNotAvailable(object):