diff options
author | 2016-04-13 12:40:21 +0300 | |
---|---|---|
committer | 2016-04-13 12:40:21 +0300 | |
commit | 8076b8111991a790886537a5c1d7b54a01f479e4 (patch) | |
tree | d3615814b3a62a4e19d64fd9cf1269c36ba62ed0 /scripts/automation/trex_control_plane/stl/trex_stl_lib | |
parent | 1f450703d3a51ed454af26aa494a7c6e2579686d (diff) | |
parent | 0b39ec305e80999c7dbe36d4b0d3850b04709571 (diff) |
Merge v2.0
Diffstat (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib')
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): |