From 195e4bda54d8fd49936213a8f28349b96fb5acd2 Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Fri, 8 Apr 2016 22:34:20 +0300 Subject: added filters.py ToggleFilter functionality, and its test file --- .../trex_control_plane/common/filters.py | 86 ++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 scripts/automation/trex_control_plane/common/filters.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/common/filters.py b/scripts/automation/trex_control_plane/common/filters.py new file mode 100644 index 00000000..3a05f4d1 --- /dev/null +++ b/scripts/automation/trex_control_plane/common/filters.py @@ -0,0 +1,86 @@ + +def shallow_copy(x): + return type(x)(x) + + +class ToggleFilter(object): + """ + This class provides a "sticky" filter, that works by "toggling" items of the original database on and off. + """ + def __init__(self, db_ref, show_by_default=True): + self._data = db_ref + self._toggle_db = set() + self._filter_method = filter + self.__set_initial_state(show_by_default) + + def toggle_item(self, item_key): + if item_key in self._toggle_db: + self._toggle_db.remove(item_key) + return False + elif item_key in self._data: + self._toggle_db.add(item_key) + return True + else: + raise KeyError("Provided item key isn't a key of the referenced data structure.") + + def filter_items(self): + """ + Filters the pointed database by showing only the items mapped at toggle_db set. + + :returns: + Filtered data of the original object. + + """ + return self._filter_method(self.__toggle_filter, self._data) + + # private methods + + def __set_initial_state(self, show_by_default): + try: + _ = (x for x in self._data) + if isinstance(self._data, dict): + self._filter_method = ToggleFilter.dict_filter + if show_by_default: + self._toggle_db = self._data.keys() + return + elif isinstance(self._data, list): + self._filter_method = ToggleFilter.list_filter + elif isinstance(self._data, set): + self._filter_method = ToggleFilter.set_filter + elif isinstance(self._data, tuple): + self._filter_method = ToggleFilter.tuple_filter + if show_by_default: + self._toggle_db = set(shallow_copy(self._data)) # assuming all relevant data with unique identifier + return + except TypeError: + raise TypeError("provided data object is not iterable") + + def __toggle_filter(self, x): + return (x in self._toggle_db) + + # static utility methods + + @staticmethod + def dict_filter(function, iterable): + assert isinstance(iterable, dict) + return {k: v + for k,v in iterable.iteritems() + if function(k, v)} + + @staticmethod + def list_filter(function, iterable): + return filter(function, iterable) + + @staticmethod + def set_filter(function, iterable): + return {x + for x in iterable + if function(x)} + + @staticmethod + def tuple_filter(function, iterable): + return tuple(filter(function, iterable)) + + +if __name__ == "__main__": + pass -- cgit From 9e63f57a80722751221e18ae1e42a7f7ac4983ab Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sat, 9 Apr 2016 01:08:51 +0300 Subject: added multiple toggle option all test passed! --- scripts/automation/trex_control_plane/common/filters.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/common/filters.py b/scripts/automation/trex_control_plane/common/filters.py index 3a05f4d1..d2ca7b16 100644 --- a/scripts/automation/trex_control_plane/common/filters.py +++ b/scripts/automation/trex_control_plane/common/filters.py @@ -23,6 +23,9 @@ class ToggleFilter(object): else: raise KeyError("Provided item key isn't a key of the referenced data structure.") + def toggle_items(self, *args): + return all(map(self.toggle_item, args)) + def filter_items(self): """ Filters the pointed database by showing only the items mapped at toggle_db set. @@ -41,7 +44,7 @@ class ToggleFilter(object): if isinstance(self._data, dict): self._filter_method = ToggleFilter.dict_filter if show_by_default: - self._toggle_db = self._data.keys() + self._toggle_db = set(self._data.keys()) return elif isinstance(self._data, list): self._filter_method = ToggleFilter.list_filter @@ -65,7 +68,7 @@ class ToggleFilter(object): assert isinstance(iterable, dict) return {k: v for k,v in iterable.iteritems() - if function(k, v)} + if function(k)} @staticmethod def list_filter(function, iterable): -- cgit From 330cf8f7950027efe7b4bbc96a7ee1af3ee0b10a Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Sat, 9 Apr 2016 13:34:35 +0300 Subject: basic modifications for TRex tui to support toggle filtering on ports --- .../trex_control_plane/stl/console/trex_tui.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stl/console/trex_tui.py b/scripts/automation/trex_control_plane/stl/console/trex_tui.py index 88c53d10..975017a5 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -13,6 +13,8 @@ else: from trex_stl_lib.utils.text_opts import * from trex_stl_lib.utils import text_tables from trex_stl_lib import trex_stl_stats +import trex_root_path +from common.filters import ToggleFilter # for STL exceptions from trex_stl_lib.api import * @@ -65,10 +67,10 @@ class TrexTUIDashBoard(TrexTUIPanel): self.key_actions['-'] = {'action': self.action_lower, 'legend': 'low 5%', 'show': True} self.ports = self.stateless_client.get_all_ports() - + self.toggle_filter = ToggleFilter(self.ports) def show (self): - stats = self.stateless_client._get_formatted_stats(self.ports) + stats = self.stateless_client._get_formatted_stats(self.toggle_filter.filter_items()) # print stats to screen for stat_type, stat_data in stats.items(): text_tables.print_table_with_header(stat_data.text_table, stat_type) @@ -289,7 +291,7 @@ class TrexTUIPanelManager(): self.key_actions['s'] = {'action': self.action_show_sstats, 'legend': 'streams stats', 'show': True} for port_id in self.ports: - self.key_actions[str(port_id)] = {'action': self.action_show_port(port_id), 'legend': 'port {0}'.format(port_id), 'show': False} + self.key_actions[str(port_id)] = {'action': self.action_toggle_port(port_id), 'legend': 'port {0}'.format(port_id), 'show': False} self.panels['port {0}'.format(port_id)] = TrexTUIPort(self, port_id) # start with dashboard @@ -387,6 +389,15 @@ class TrexTUIPanelManager(): return action_show_port_x + def action_toggle_port(self, port_id): + def action_toggle_port_x(): + self.panels['dashboard'].toggle_filter.toggle_item(port_id) + self.init() + return "" + + return action_toggle_port_x + + def action_show_sstats (self): self.main_panel = self.panels['sstats'] -- cgit From 882bc200c58a18a1eabd7b5db6c0ee7e6e5068f1 Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 17 Apr 2016 14:30:58 +0300 Subject: TUI might crash - do not include unicode chars if stdout encoding is not 'UTF-8' also, some fixes for the TUI port screen --- scripts/automation/trex_control_plane/stl/console/trex_tui.py | 11 ++++++++--- .../trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py | 11 ++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stl/console/trex_tui.py b/scripts/automation/trex_control_plane/stl/console/trex_tui.py index effcf55e..cbaae392 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -73,7 +73,10 @@ class TrexTUIDashBoard(TrexTUIPanel): self.key_actions['o'] = {'action': self.action_show_owned, 'legend': 'owned ports', 'show': True} self.key_actions['a'] = {'action': self.action_show_all, 'legend': 'all ports', 'show': True} - self.ports_filter = self.FILTER_ALL + if self.stateless_client.get_acquired_ports(): + self.ports_filter = self.FILTER_ACQUIRED + else: + self.ports_filter = self.FILTER_ALL def get_ports (self): @@ -99,7 +102,7 @@ class TrexTUIDashBoard(TrexTUIPanel): allowed['o'] = self.key_actions['o'] allowed['a'] = self.key_actions['a'] - if self.ports_filter == self.FILTER_ALL: + if self.ports_filter == self.FILTER_ALL and self.stateless_client.get_acquired_ports() != self.stateless_client.get_all_ports(): return allowed if len(self.stateless_client.get_transmitting_ports()) > 0: @@ -201,7 +204,7 @@ class TrexTUIPort(TrexTUIPanel): allowed['c'] = self.key_actions['c'] allowed['t'] = self.key_actions['t'] - if self.stateless_client.is_all_ports_acquired(): + if self.port_id not in self.stateless_client.get_acquired_ports(): return allowed if self.port.state == self.port.STATE_TX: @@ -535,7 +538,9 @@ class TrexTUI(): sys.stdout = old_stdout self.clear_screen() + print(mystdout.getvalue()) + sys.stdout.flush() self.draw_policer = 0 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 6b1185ef..4057c50d 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 @@ -201,6 +201,10 @@ class CTRexInfoGenerator(object): def _get_rational_block_char(value, range_start, interval): # in Konsole, utf-8 is sometimes printed with artifacts, return ascii for now #return 'X' if value >= range_start + float(interval) / 2 else ' ' + + if sys.__stdout__.encoding != 'UTF-8': + return 'X' if value >= range_start + float(interval) / 2 else ' ' + value -= range_start ratio = float(value) / interval if ratio <= 0.0625: @@ -532,7 +536,12 @@ class CTRexStats(object): v = self.get_trend(field, use_raw) value = abs(v) - arrow = u'\u25b2' if v > 0 else u'\u25bc' + + # use arrows if utf-8 is supported + if sys.__stdout__.encoding == 'UTF-8': + arrow = u'\u25b2' if v > 0 else u'\u25bc' + else: + arrow = '' if sys.version_info < (3,0): arrow = arrow.encode('utf-8') -- cgit From cd40f4a30b5535363e29c9d516976a549229d82d Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 17 Apr 2016 15:29:34 +0300 Subject: added example for get_warnings() --- scripts/automation/trex_control_plane/doc_stl/api/client_code.rst | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/doc_stl/api/client_code.rst b/scripts/automation/trex_control_plane/doc_stl/api/client_code.rst index 4ae2b9fd..d3e48dab 100755 --- a/scripts/automation/trex_control_plane/doc_stl/api/client_code.rst +++ b/scripts/automation/trex_control_plane/doc_stl/api/client_code.rst @@ -82,6 +82,11 @@ STLClient snippet # block until done c.wait_on_traffic(ports = [0, 1]) + # check for any warnings + if c.get_warnings(): + # handle warnings here + pass + finally: c.disconnect() -- cgit From 1fa7b64c13e6c485926eba5c40bd198af738e365 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 18 Apr 2016 13:13:00 +0300 Subject: simple intro while connecting to the server --- .../trex_control_plane/stl/console/trex_console.py | 29 ++++++++++++++++++++-- .../stl/trex_stl_lib/trex_stl_client.py | 5 ++++ .../stl/trex_stl_lib/utils/text_opts.py | 13 +++------- 3 files changed, 35 insertions(+), 12 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py index 2b53b7ec..00d2a028 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -289,7 +289,7 @@ class TRexConsole(TRexGeneralCmd): @verify_connected def do_ping (self, line): '''Ping the server\n''' - self.stateless_client.ping() + self.stateless_client.ping_line(line) # set verbose on / off @@ -774,7 +774,29 @@ def setParserOptions(): return parser - +# a simple info printed on log on +def show_intro (logger, c): + x = c.get_server_system_info() + ver = c.get_server_version().get('version', 'N/A') + + # find out which NICs the server has + port_types = {} + for port in x['ports']: + key = (port['speed'], port['driver']) + if not key in port_types: + port_types[key] = 0 + port_types[key] += 1 + + port_line = '' + for k, v in port_types.items(): + port_line += "{0} x {1}Gbps @ {2}".format(v, k[0], k[1]) + + logger.log(format_text("\nServer Info:\n", 'underline')) + logger.log("Server version: {:>}".format(format_text(ver, 'bold'))) + logger.log("Server CPU: {:>}".format(format_text("{:>} x {:>}".format(x.get('dp_core_count'), x.get('core_type')), 'bold'))) + logger.log("Ports count: {:>}".format(format_text(port_line, 'bold'))) + + def main(): parser = setParserOptions() options = parser.parse_args() @@ -824,6 +846,9 @@ def main(): if options.readonly: logger.log(format_text("\nRead only mode - only few commands will be available", 'bold')) + show_intro(logger, stateless_client) + + # a script mode if options.batch: cont = run_script_file(options.batch[0], stateless_client) 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 aa95f037..12ed1f81 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 @@ -2038,6 +2038,11 @@ class STLClient(object): return wrap + @__console + def ping_line (self, line): + '''pings the server''' + self.ping() + return True @__console def connect_line (self, line): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py index bc2d44f4..5c0dfb14 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py @@ -124,16 +124,9 @@ def underline(text): def text_attribute(text, attribute): - if isinstance(text, str): - return "{start}{txt}{stop}".format(start=TEXT_CODES[attribute]['start'], - txt=text, - stop=TEXT_CODES[attribute]['end']) - elif isinstance(text, unicode): - return u"{start}{txt}{stop}".format(start=TEXT_CODES[attribute]['start'], - txt=text, - stop=TEXT_CODES[attribute]['end']) - else: - raise Exception("not a string") + return "{start}{txt}{stop}".format(start=TEXT_CODES[attribute]['start'], + txt=text, + stop=TEXT_CODES[attribute]['end']) FUNC_DICT = {'blue': blue, -- cgit From 7e5993941829e5fd1bb9ab3c6ac624d9aa800c37 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 18 Apr 2016 15:12:05 +0300 Subject: support for reacquire command also better sync with the server about ownership of ports --- .../trex_control_plane/stl/console/trex_console.py | 3 + .../stl/trex_stl_lib/trex_stl_client.py | 70 ++++++++++++++++++---- .../stl/trex_stl_lib/trex_stl_port.py | 12 +++- 3 files changed, 70 insertions(+), 15 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py index 00d2a028..589f1d35 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -421,6 +421,9 @@ class TRexConsole(TRexGeneralCmd): '''Release ports\n''' self.stateless_client.release_line(line) + def do_reacquire (self, line): + '''reacquire all the ports under your logged user name''' + self.stateless_client.reacquire_line(line) def help_acquire (self): self.do_acquire("-h") 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 12ed1f81..76fbf3b8 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 @@ -264,22 +264,55 @@ class EventsHandler(object): self.__async_event_port_job_done(port_id) show_event = True - # port was stolen... + # port was acquired - maybe stolen... elif (type == 5): session_id = data['session_id'] - # false alarm, its us + port_id = int(data['port_id']) + who = data['who'] + force = data['force'] + + # if we hold the port and it was not taken by this session - show it + if port_id in self.client.get_acquired_ports() and session_id != self.client.session_id: + show_event = True + + # format the thief/us... if session_id == self.client.session_id: - return + user = 'you' + elif who == self.client.username: + user = 'another session of you' + else: + user = "'{0}'".format(who) - port_id = int(data['port_id']) - who = data['who'] + if force: + ev = "Port {0} was forcely taken by {1}".format(port_id, user) + else: + ev = "Port {0} was taken by {1}".format(port_id, user) - ev = "Port {0} was forcely taken by '{1}'".format(port_id, who) + # call the handler in case its not this session + if session_id != self.client.session_id: + self.__async_event_port_acquired(port_id, who) + + + # port was released + elif (type == 6): + port_id = int(data['port_id']) + who = data['who'] + session_id = data['session_id'] + + if session_id == self.client.session_id: + user = 'you' + elif who == self.client.username: + user = 'another session of you' + else: + user = "'{0}'".format(who) + + ev = "Port {0} was released by {1}".format(port_id, user) + + # call the handler in case its not this session + if session_id != self.client.session_id: + self.__async_event_port_released(port_id) - # call the handler - self.__async_event_port_forced_acquired(port_id, who) - show_event = True # server stopped elif (type == 100): @@ -317,9 +350,11 @@ class EventsHandler(object): self.client.ports[port_id].async_event_port_resumed() - def __async_event_port_forced_acquired (self, port_id, who): - self.client.ports[port_id].async_event_forced_acquired(who) + def __async_event_port_acquired (self, port_id, who): + self.client.ports[port_id].async_event_acquired(who) + def __async_event_port_released (self, port_id): + self.client.ports[port_id].async_event_released() def __async_event_server_stopped (self): self.client.connected = False @@ -506,7 +541,7 @@ class STLClient(object): # API classes - self.api_vers = [ {'type': 'core', 'major': 1, 'minor':1 } + self.api_vers = [ {'type': 'core', 'major': 1, 'minor':2 } ] self.api_h = {'core': None} @@ -2121,6 +2156,17 @@ class STLClient(object): return True + @__console + def reacquire_line (self, line): + '''reacquire all the ports under your username''' + my_unowned_ports = list_difference([k for k, v in self.ports.items() if v.get_owner() == self.username], self.get_acquired_ports()) + if not my_unowned_ports: + self.logger.log("reacquire - no unowned ports under '{0}'".format(self.username)) + return + + self.acquire(ports = my_unowned_ports, force = True) + return True + @__console def disconnect_line (self, line): self.disconnect() 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 6f6f50b1..16e55d73 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 @@ -96,9 +96,12 @@ class Port(object): "handler": self.handler} rc = self.transmit("release", params) - self.handler = None - + if rc.good(): + + self.handler = None + self.owner = '' + return self.ok() else: return self.err(rc.err()) @@ -679,7 +682,10 @@ class Port(object): if not self.is_acquired(): self.state = self.STATE_TX - def async_event_forced_acquired (self, who): + def async_event_acquired (self, who): self.handler = None self.owner = who + def async_event_released (self): + self.owner = '' + -- cgit From b12cc7fda4f8c488800696abdb2fba7c3cfc82a4 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 18 Apr 2016 15:18:59 +0300 Subject: minor fix --- .../trex_control_plane/stl/console/trex_console.py | 3 +++ .../trex_control_plane/stl/trex_stl_lib/trex_stl_client.py | 13 ++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stl/console/trex_console.py b/scripts/automation/trex_control_plane/stl/console/trex_console.py index 589f1d35..a126bf35 100755 --- a/scripts/automation/trex_control_plane/stl/console/trex_console.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_console.py @@ -431,6 +431,9 @@ class TRexConsole(TRexGeneralCmd): def help_release (self): self.do_release("-h") + def help_reacquire (self): + self.do_reacquire("-h") + ############### start def complete_start(self, text, line, begidx, endidx): 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 76fbf3b8..63d77637 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 @@ -2158,7 +2158,17 @@ class STLClient(object): @__console def reacquire_line (self, line): - '''reacquire all the ports under your username''' + '''reacquire all the ports under your username which are not acquired by your session''' + + parser = parsing_opts.gen_parser(self, + "reacquire", + self.reacquire_line.__doc__) + + opts = parser.parse_args(line.split()) + if opts is None: + return + + # find all the on-owned ports under your name my_unowned_ports = list_difference([k for k, v in self.ports.items() if v.get_owner() == self.username], self.get_acquired_ports()) if not my_unowned_ports: self.logger.log("reacquire - no unowned ports under '{0}'".format(self.username)) @@ -2167,6 +2177,7 @@ class STLClient(object): self.acquire(ports = my_unowned_ports, force = True) return True + @__console def disconnect_line (self, line): self.disconnect() -- cgit From e6d1ca8bc126b2efc5a15268cd2d9926f1be3ab9 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 18 Apr 2016 15:40:17 +0300 Subject: portattr console command fix --- .../trex_control_plane/stl/trex_stl_lib/trex_stl_client.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'scripts/automation/trex_control_plane') 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 63d77637..77fa40bb 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 @@ -2553,15 +2553,20 @@ class STLClient(object): '''Sets port attributes ''' parser = parsing_opts.gen_parser(self, - "port", + "port_attr", self.set_port_attr_line.__doc__, parsing_opts.PORT_LIST_WITH_ALL, parsing_opts.PROMISCUOUS_SWITCH) - opts = parser.parse_args(line.split()) + opts = parser.parse_args(line.split(), default_ports = self.get_acquired_ports(), verify_acquired = True) if opts is None: return + # if no attributes - fall back to printing the status + if opts.prom is None: + self.show_stats_line("--ps --port {0}".format(' '.join(str(port) for port in opts.ports))) + return + self.set_port_attr(opts.ports, opts.prom) @@ -2654,5 +2659,4 @@ class STLClient(object): if opts.clear: self.clear_events() - self.logger.log(format_text("\nEvent log was cleared\n")) - + \ No newline at end of file -- cgit From 3bafb0394c07ef2abb4ce34c7fb4ec01eb09f2df Mon Sep 17 00:00:00 2001 From: Dan Klein Date: Tue, 19 Apr 2016 01:44:04 +0300 Subject: All unit tests passes with both python 2 and python 3. Added more documentation to the ToggleFilter class. --- .../trex_control_plane/common/filters.py | 54 ++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/common/filters.py b/scripts/automation/trex_control_plane/common/filters.py index d2ca7b16..bf04a775 100644 --- a/scripts/automation/trex_control_plane/common/filters.py +++ b/scripts/automation/trex_control_plane/common/filters.py @@ -8,12 +8,43 @@ class ToggleFilter(object): This class provides a "sticky" filter, that works by "toggling" items of the original database on and off. """ def __init__(self, db_ref, show_by_default=True): + """ + Instantiate a ToggleFilter object + + :parameters: + db_ref : iterable + an iterable object (i.e. list, set etc) that would serve as the reference db of the instance. + Changes in that object will affect the output of ToggleFilter instance. + + show_by_default: bool + decide if by default all the items are "on", i.e. these items will be presented if no other + toggling occurred. + + default value : **True** + + """ self._data = db_ref self._toggle_db = set() self._filter_method = filter self.__set_initial_state(show_by_default) def toggle_item(self, item_key): + """ + Toggle a single item in/out. + + :parameters: + item_key : + an item the by its value the filter can decide to toggle or not. + Example: int, str and so on. + + :return: + + **True** if item toggled **into** the filtered items + + **False** if item toggled **out from** the filtered items + + :raises: + + KeyError, in case if item key is not part of the toggled list and not part of the referenced db. + + """ if item_key in self._toggle_db: self._toggle_db.remove(item_key) return False @@ -24,7 +55,23 @@ class ToggleFilter(object): raise KeyError("Provided item key isn't a key of the referenced data structure.") def toggle_items(self, *args): - return all(map(self.toggle_item, args)) + """ + Toggle multiple items in/out with a single call. Each item will be ha. + + :parameters: + args : iterable + an iterable object containing all item keys to be toggled in/out + + :return: + + **True** if all toggled items were toggled **into** the filtered items + + **False** if at least one of the items was toggled **out from** the filtered items + + :raises: + + KeyError, in case if ont of the item keys was not part of the toggled list and not part of the referenced db. + + """ + # in python 3, 'map' returns an iterator, so wrapping with 'list' call creates same effect for both python 2 and 3 + return all(list(map(self.toggle_item, args))) def filter_items(self): """ @@ -67,12 +114,13 @@ class ToggleFilter(object): def dict_filter(function, iterable): assert isinstance(iterable, dict) return {k: v - for k,v in iterable.iteritems() + for k,v in iterable.items() if function(k)} @staticmethod def list_filter(function, iterable): - return filter(function, iterable) + # in python 3, filter returns an iterator, so wrapping with list creates same effect for both python 2 and 3 + return list(filter(function, iterable)) @staticmethod def set_filter(function, iterable): -- cgit From f0d4ff5c450489c3ec6adb7f79896df75df2bd6b Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 19 Apr 2016 13:30:25 +0300 Subject: minor fix to TUI with no ports --- .../trex_control_plane/stl/console/trex_tui.py | 36 ++++++++-------------- 1 file changed, 13 insertions(+), 23 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stl/console/trex_tui.py b/scripts/automation/trex_control_plane/stl/console/trex_tui.py index 231eff93..0c3ea8d6 100644 --- a/scripts/automation/trex_control_plane/stl/console/trex_tui.py +++ b/scripts/automation/trex_control_plane/stl/console/trex_tui.py @@ -70,10 +70,9 @@ class TrexTUIDashBoard(TrexTUIPanel): self.key_actions['c'] = {'action': self.action_clear, 'legend': 'clear', 'show': True} self.key_actions['p'] = {'action': self.action_pause, 'legend': 'pause', 'show': True, 'color': 'red'} self.key_actions['r'] = {'action': self.action_resume, 'legend': 'resume', 'show': True, 'color': 'blue'} - self.key_actions['+'] = {'action': self.action_raise, 'legend': 'up 5%', 'show': True} - self.key_actions['-'] = {'action': self.action_lower, 'legend': 'low 5%', 'show': True} self.key_actions['o'] = {'action': self.action_show_owned, 'legend': 'owned ports', 'show': True} + self.key_actions['n'] = {'action': self.action_reset_view, 'legend': 'reset view', 'show': True} self.key_actions['a'] = {'action': self.action_show_all, 'legend': 'all ports', 'show': True} # register all the ports to the toggle action @@ -103,25 +102,29 @@ class TrexTUIDashBoard(TrexTUIPanel): def get_key_actions (self): allowed = OrderedDict() - allowed['c'] = self.key_actions['c'] + + allowed['n'] = self.key_actions['n'] allowed['o'] = self.key_actions['o'] allowed['a'] = self.key_actions['a'] for i in self.ports: allowed[str(i)] = self.key_actions[str(i)] + if self.get_showed_ports(): + allowed['c'] = self.key_actions['c'] + # if not all ports are acquired - no operations if not (set(self.get_showed_ports()) <= set(self.stateless_client.get_acquired_ports())): return allowed + # if any/some ports can be resumed + if set(self.get_showed_ports()) & set(self.stateless_client.get_paused_ports()): + allowed['r'] = self.key_actions['r'] + # if any/some ports are transmitting - support those actions if set(self.get_showed_ports()) & set(self.stateless_client.get_transmitting_ports()): allowed['p'] = self.key_actions['p'] - allowed['+'] = self.key_actions['+'] - allowed['-'] = self.key_actions['-'] - if set(self.get_showed_ports()) & set(self.stateless_client.get_paused_ports()): - allowed['r'] = self.key_actions['r'] return allowed @@ -146,24 +149,10 @@ class TrexTUIDashBoard(TrexTUIPanel): return "" - def action_raise (self): - try: - self.stateless_client.update(mult = "5%+", ports = self.get_showed_ports()) - except STLError: - pass - - return "" - - - def action_lower (self): - try: - self.stateless_client.update(mult = "5%-", ports = self.get_showed_ports()) - except STLError: - pass - + def action_reset_view (self): + self.toggle_filter.reset() return "" - def action_show_owned (self): self.toggle_filter.reset() self.toggle_filter.toggle_items(*self.stateless_client.get_acquired_ports()) @@ -280,6 +269,7 @@ class TrexTUIPanelManager(): self.legend += "\n{:<12}".format(self.main_panel.get_name() + ":") + for k, v in self.main_panel.get_key_actions().items(): if v['show']: x = "'{0}' - {1}, ".format(k, v['legend']) -- cgit From d42a2a5567958ea052aac0ab0dc291876f97f4e2 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Thu, 21 Apr 2016 11:14:52 +0300 Subject: regression: fix functional filters test path stateful API: add get_files, get_files_list --- .../trex_control_plane/server/trex_server.py | 66 +++++++++++++++++----- .../stf/trex_stf_lib/trex_client.py | 53 ++++++++++++++++- 2 files changed, 102 insertions(+), 17 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/server/trex_server.py b/scripts/automation/trex_control_plane/server/trex_server.py index 3f8bc374..e32fc9d1 100755 --- a/scripts/automation/trex_control_plane/server/trex_server.py +++ b/scripts/automation/trex_control_plane/server/trex_server.py @@ -84,10 +84,10 @@ class CTRexServer(object): def push_file (self, filename, bin_data): logger.info("Processing push_file() command.") try: - filepath = os.path.abspath(os.path.join(self.trex_files_path, filename)) + filepath = os.path.join(self.trex_files_path, os.path.basename(filename)) with open(filepath, 'wb') as f: f.write(binascii.a2b_base64(bin_data)) - logger.info("push_file() command finished. `{name}` was saved at {fpath}".format( name = filename, fpath = self.trex_files_path)) + logger.info("push_file() command finished. File is saved as %s" % filepath) return True except IOError as inst: logger.error("push_file method failed. " + str(inst)) @@ -125,28 +125,32 @@ class CTRexServer(object): # set further functionality and peripherals to server instance try: self.server.register_function(self.add) - self.server.register_function(self.get_trex_log) - self.server.register_function(self.get_trex_daemon_log) - self.server.register_function(self.get_trex_version) + self.server.register_function(self.cancel_reservation) self.server.register_function(self.connectivity_check) - self.server.register_function(self.start_trex) - self.server.register_function(self.stop_trex) - self.server.register_function(self.wait_until_kickoff_finish) - self.server.register_function(self.get_running_status) - self.server.register_function(self.is_running) + self.server.register_function(self.force_trex_kill) + self.server.register_function(self.get_file) + self.server.register_function(self.get_files_list) + self.server.register_function(self.get_files_path) self.server.register_function(self.get_running_info) + self.server.register_function(self.get_running_status) + self.server.register_function(self.get_trex_daemon_log) + self.server.register_function(self.get_trex_log) + self.server.register_function(self.get_trex_version) self.server.register_function(self.is_reserved) - self.server.register_function(self.get_files_path) + self.server.register_function(self.is_running) self.server.register_function(self.push_file) self.server.register_function(self.reserve_trex) - self.server.register_function(self.cancel_reservation) - self.server.register_function(self.force_trex_kill) + self.server.register_function(self.start_trex) + self.server.register_function(self.stop_trex) + self.server.register_function(self.wait_until_kickoff_finish) signal.signal(signal.SIGTSTP, self.stop_handler) signal.signal(signal.SIGTERM, self.stop_handler) self.zmq_monitor.start() self.server.serve_forever() except KeyboardInterrupt: logger.info("Daemon shutdown request detected." ) + except Exception as e: + logger.error(e) finally: self.zmq_monitor.join() # close ZMQ monitor thread resources self.server.shutdown() @@ -160,8 +164,40 @@ class CTRexServer(object): file_content = f.read() return binascii.b2a_base64(file_content) except Exception as e: - err_str = "Can't get requested file: {0}, possibly due to TRex that did not run".format(filepath) - logger.error('{0}, error: {1}'.format(err_str, e)) + err_str = "Can't get requested file %s: %s" % (filepath, e) + logger.error(err_str) + return Fault(-33, err_str) + + # returns True if given path is under TRex package or under /tmp/trex_files + def _check_path_under_TRex_or_temp(self, path): + if not os.path.relpath(path, self.trex_files_path).startswith(os.pardir): + return True + if not os.path.relpath(path, self.TREX_PATH).startswith(os.pardir): + return True + return False + + # gets the file content encoded base64 either from /tmp/trex_files or TRex server dir + def get_file(self, filepath): + try: + logger.info("Processing get_file() command.") + if not self._check_path_under_TRex_or_temp(filepath): + raise Exception('Given path should be under current TRex package or /tmp/trex_files') + return self._pull_file(filepath) + except Exception as e: + err_str = "Can't get requested file %s: %s" % (filepath, e) + logger.error(err_str) + return Fault(-33, err_str) + + # get tuple (dirs, files) with directories and files lists from given path (limited under TRex package or /tmp/trex_files) + def get_files_list(self, path): + try: + logger.info("Processing get_files_list() command, given path: %s" % path) + if not self._check_path_under_TRex_or_temp(path): + raise Exception('Given path should be under current TRex package or /tmp/trex_files') + return os.walk(path).next()[1:3] + except Exception as e: + err_str = "Error processing get_files_list(): %s" % e + logger.error(err_str) return Fault(-33, err_str) # get Trex log /tmp/trex.txt diff --git a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py index 4fd1e4c7..412f52d4 100755 --- a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py @@ -725,7 +725,56 @@ class CTRexClient(object): raise finally: self.prompt_verbose_data() - + + def get_files_list (self, path): + """ + Gets a tuple (dirs, files) with lists of dirs and files from given path. + Path is limited to /tmp/trex_files or TRex directory (can be used relative path) + + :return: + Tuple: list of dirs and list of files in given path + + :raises: + + :exc:`trex_exceptions.TRexRequestDenied`, in case TRex is reserved for another user than the one trying to cancel the reservation. + + ProtocolError, in case of error in JSON-RPC protocol. + + """ + + try: + return self.server.get_files_list(path) + except AppError as err: + self._handle_AppError_exception(err.args[0]) + except ProtocolError: + raise + finally: + self.prompt_verbose_data() + + def get_file(self, filepath): + """ + Gets a file from /tmp/trex_files or TRex server directory. + + :parameters: + filepath : str + a path to a file at server. + it can be either relative to TRex server or absolute path starting with /tmp/trex_files + + :return: + Content of the file + + :raises: + + :exc:`trex_exceptions.TRexRequestDenied`, in case TRex is reserved for another user than the one trying to cancel the reservation. + + ProtocolError, in case of error in JSON-RPC protocol. + """ + + try: + return binascii.a2b_base64(self.server.get_file(filepath)) + except AppError as err: + self._handle_AppError_exception(err.args[0]) + except ProtocolError: + raise + finally: + self.prompt_verbose_data() + def push_files (self, filepaths): """ Pushes a file (or a list of files) to store locally on server. @@ -761,7 +810,7 @@ class CTRexClient(object): filename = os.path.basename(filepath) with open(filepath, 'rb') as f: file_content = f.read() - self.server.push_file(filename, binascii.b2a_base64(file_content)) + self.server.push_file(filename, binascii.b2a_base64(file_content).decode()) finally: self.prompt_verbose_data() return True -- cgit From 6dc9c07fda0508d46ddccdb6888bae844b78361b Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Thu, 21 Apr 2016 15:07:05 +0300 Subject: stateful API: avoid zero division --- .../trex_control_plane/stf/trex_stf_lib/trex_client.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py index 412f52d4..27428e69 100755 --- a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py @@ -728,8 +728,11 @@ class CTRexClient(object): def get_files_list (self, path): """ - Gets a tuple (dirs, files) with lists of dirs and files from given path. - Path is limited to /tmp/trex_files or TRex directory (can be used relative path) + Gets a list of dirs and files either from /tmp/trex_files or path relative to TRex server. + + :parameters: + path : str + a path to directory to read. :return: Tuple: list of dirs and list of files in given path @@ -751,7 +754,7 @@ class CTRexClient(object): def get_file(self, filepath): """ - Gets a file from /tmp/trex_files or TRex server directory. + Gets content of file as bytes string from /tmp/trex_files or TRex server directory. :parameters: filepath : str @@ -1285,7 +1288,7 @@ class CTRexResult(object): for elem in reversed(res['histogram']): sum_high += elem['val'] hist_last_keys.append(elem['key']) - if sum_high / res['cnt'] >= filtered_latency_amount: + if sum_high >= filtered_latency_amount * res['cnt']: break result[max_port] = (hist_last_keys[0] + hist_last_keys[-1]) / 2 else: -- cgit From cc53c3d2d4b928221f2d51d3f1ace64f34029f1e Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Sun, 24 Apr 2016 15:54:07 +0300 Subject: stateful API: add get_ports_count based on result regression: be per core takes into account number of ports --- .../stf/trex_stf_lib/trex_client.py | 24 ++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py index 27428e69..f5887120 100755 --- a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py @@ -1094,7 +1094,7 @@ class CTRexResult(object): if not self.is_valid_hist(): return None else: - return CTRexResult.__get_value_by_path(self._history[len(self._history)-1], tree_path_to_key, regex) + return CTRexResult.__get_value_by_path(self._history[-1], tree_path_to_key, regex) def get_value_list (self, tree_path_to_key, regex = None, filter_none = True): """ @@ -1145,11 +1145,23 @@ class CTRexResult(object): + an empty dictionary if history is empty. """ - history_size = len(self._history) - if history_size != 0: - return self._history[len(self._history) - 1] - else: - return {} + if len(self._history): + return self._history[-1] + return {} + + def get_ports_count(self): + """ + Returns number of ports based on TRex result + + :return: + + number of ports in TRex result + + -1 if history is empty. + """ + + if not len(self._history): + return -1 + return len(self.__get_value_by_path(self._history[-1], 'trex-global.data', 'opackets-\d+')) + def update_result_data (self, latest_dump): """ -- cgit From cb55f42f183e631aec06ecb7d1ad953ad71b57d2 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Tue, 26 Apr 2016 05:32:09 +0300 Subject: stateful api: fix filter max latency regression: fix bw_per_core at trex09 based on regression --- .../stf/trex_stf_lib/trex_client.py | 30 ++++++++++------------ 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py index f5887120..7f241620 100755 --- a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py @@ -1289,26 +1289,24 @@ class CTRexResult(object): def __get_filtered_max_latency (src_dict, filtered_latency_amount = 0.001): result = {} for port, data in src_dict.items(): - if port.startswith('port-'): - max_port = 'max-%s' % port[5:] - res = data['hist'] - if not len(res['histogram']): - result[max_port] = 0 - continue - hist_last_keys = deque([res['histogram'][-1]['key']], maxlen = 2) - sum_high = 0.0 - for elem in reversed(res['histogram']): - sum_high += elem['val'] - hist_last_keys.append(elem['key']) - if sum_high >= filtered_latency_amount * res['cnt']: - break - result[max_port] = (hist_last_keys[0] + hist_last_keys[-1]) / 2 - else: - return {} + if not port.startswith('port-'): + continue + max_port = 'max-%s' % port[5:] + res = data['hist'] + if not len(res['histogram']): + result[max_port] = 0 + continue + sum_high = 0.0 + for elem in reversed(res['histogram']): + sum_high += elem['val'] + if sum_high >= filtered_latency_amount * res['cnt']: + result[max_port] = elem['key'] + int('5' + repr(elem['key'])[2:]) + break return result + if __name__ == "__main__": pass -- cgit From 429072c5997c76bd9338ad896127b5241d740b28 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Wed, 27 Apr 2016 05:46:01 +0300 Subject: stateful api: fix filter max latency --- scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py | 1 + 1 file changed, 1 insertion(+) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py index 7f241620..fd409b16 100755 --- a/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py +++ b/scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py @@ -1296,6 +1296,7 @@ class CTRexResult(object): if not len(res['histogram']): result[max_port] = 0 continue + result[max_port] = 5 # if sum below will not get to filtered amount, use this value sum_high = 0.0 for elem in reversed(res['histogram']): sum_high += elem['val'] -- cgit From e831a539aebac71d8f00c400416f4bef51108610 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Fri, 29 Apr 2016 17:07:14 +0300 Subject: regression: add GA --- .../stl/trex_stl_lib/utils/GAObjClass.py | 295 +++++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100755 scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py new file mode 100755 index 00000000..3101b45d --- /dev/null +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py @@ -0,0 +1,295 @@ +#import requests # need external lib for that +try: # Python2 + import Queue + from urllib2 import * +except: # Python3 + import queue as Queue + from urllib.request import * + from urllib.error import * +import threading +import sys +from time import sleep + +""" +GAObjClass is a class destined to send Google Analytics Information. + +cid - unique number per user. +command - the Event Category rubric appears on site. type: TEXT +action - the Event Action rubric appears on site - type: TEXT +label - the Event Label rubric - type: TEXT +value - the event value metric - type: INTEGER + +QUOTAS: +1 single payload - up to 8192Bytes +batched: +A maximum of 20 hits can be specified per request. +The total size of all hit payloads cannot be greater than 16K bytes. +No single hit payload can be greater than 8K bytes. +""" + +url_single = 'http://www.google-analytics.com/collect' #sending single event +url_batched = 'http://www.google-analytics.com/batch' #sending batched events +url_debug = 'http://www.google-analytics.com/debug/collect' #verifying hit is valid +url_conn = 'http://172.217.2.196' # testing internet connection to this address (google-analytics server) + + +#..................................................................class GA_EVENT_ObjClass................................................................ +class GA_EVENT_ObjClass: + def __init__(self,cid,trackerID,command,action,label,value,appName,appVer): + self.cid = cid + self.trackerID = trackerID + self.command = command + self.action = action + self.label = label + self.value = value + self.appName = appName + self.appVer = appVer + self.generate_payload() + self.size = sys.getsizeof(self.payload) + + def generate_payload(self): + self.payload ='v=1&t=event&tid='+str(self.trackerID) + self.payload+='&cid='+str(self.cid) + self.payload+='&ec='+str(self.command) + self.payload+='&ea='+str(self.action) + self.payload+='&el='+str(self.label) + self.payload+='&ev='+str(self.value) + self.payload+='&an='+str(self.appName) + self.payload+='&av='+str(self.appVer) + +#..................................................................class GA_EXCEPTION_ObjClass................................................................ +#ExceptionFatal - BOOLEAN +class GA_EXCEPTION_ObjClass: + def __init__(self,cid,trackerID,ExceptionName,ExceptionFatal,appName,appVer): + self.cid = cid + self.trackerID = trackerID + self.ExceptionName = ExceptionName + self.ExceptionFatal = ExceptionFatal + self.appName = appName + self.appVer = appVer + self.generate_payload() + + def generate_payload(self): + self.payload ='v=1&t=exception&tid='+str(self.trackerID) + self.payload+='&cid='+str(self.cid) + self.payload+='&exd='+str(self.ExceptionName) + self.payload+='&exf='+str(self.ExceptionFatal) + self.payload+='&an='+str(self.appName) + self.payload+='&av='+str(self.appVer) + +#.....................................................................class ga_Thread................................................................. +""" + +Google analytics thread manager: + +will report and empty queue of google analytics items to GA server, every Timeout (parameter given on initialization) +will perform connectivity check every timeout*10 seconds + + +""" + +class ga_Thread (threading.Thread): + def __init__(self,threadID,gManager): + threading.Thread.__init__(self) + self.threadID = threadID + self.gManager = gManager + + def run(self): + keepAliveCounter=0 + #sys.stdout.write('thread started \n') + #sys.stdout.flush() + while True: + if (keepAliveCounter==10): + keepAliveCounter=0 + if (self.gManager.internet_on()==True): + self.gManager.connectedToInternet=1 + else: + self.gManager.connectedToInternet=0 + sleep(self.gManager.Timeout) + keepAliveCounter+=1 + if not self.gManager.GA_q.empty(): + self.gManager.threadLock.acquire(1) +# sys.stdout.write('lock acquired: reporting to GA \n') +# sys.stdout.flush() + if (self.gManager.connectedToInternet==1): + self.gManager.emptyAndReportQ() + self.gManager.threadLock.release() +# sys.stdout.write('finished \n') +# sys.stdout.flush() + + + +#.....................................................................class GAmanager................................................................. +""" + +Google ID - specify tracker property, example: UA-75220362-2 (when the suffix '2' specifies the analytics property profile) + +UserID - unique userID, this will differ between users on GA + +appName - s string to determine app name + +appVer - a string to determine app version + +QueueSize - the size of the queue that holds reported items. once the Queue is full: + on blocking mode: + will block program until next submission to GA server, which will make new space + on non-blocking mode: + will drop new requests + +Timout - the timeout the queue uses between data transmissions. Timeout should be shorter than the time it takes to generate 20 events. MIN VALUE = 11 seconds + +User Permission - the user must accept data transmission, use this flag as 1/0 flag, when UserPermission=1 allows data collection + +BlockingMode - set to 1 if you wish every Google Analytic Object will be submitted and processed, with no drops allowed. + this will block the running of the program until every item is processed + +*** Restriction - Google's restriction for amount of packages being sent per session per second is: 1 event per second, per session. session length is 30min *** +""" +class GAmanager: + def __init__(self,GoogleID,UserID,appName,appVer,QueueSize,Timeout,UserPermission,BlockingMode): + self.UserID = UserID + self.GoogleID = GoogleID + self.QueueSize = QueueSize + self.Timeout = Timeout + self.appName = appName + self.appVer = appVer + self.UserPermission = UserPermission + self.GA_q = Queue.Queue(QueueSize) + self.thread = ga_Thread(UserID,self) + self.threadLock = threading.Lock() + self.BlockingMode = BlockingMode + self.connectedToInternet =0 + if (self.internet_on()==True): +# sys.stdout.write('internet connection active \n') +# sys.stdout.flush() + self.connectedToInternet=1 + else: + self.connectedToInternet=0 + + def gaAddAction(self,Event,action,label,value): + self.gaAddObject(GA_EVENT_ObjClass(self.UserID,self.GoogleID,Event,action,label,value,self.appName,self.appVer)) + + def gaAddException(self,ExceptionName,ExceptionFatal): + self.gaAddObject(GA_EXCEPTION_ObjClass(self.UserID,self.GoogleID,ExceptionName,ExceptionFatal,self.appName,self.appVer)) + + def gaAddObject(self,Object): + if self.BlockingMode==1: + while self.GA_q.full(): + sleep(self.Timeout) +# sys.stdout.write('blocking mode=1 \n queue full - sleeping for timeout \n') # within Timout, the thread will empty part of the queue +# sys.stdout.flush() + lockState = self.threadLock.acquire(self.BlockingMode) + if lockState==1: +# sys.stdout.write('got lock, adding item \n') +# sys.stdout.flush() + try: + self.GA_q.put_nowait(Object) +# sys.stdout.write('got lock, item added \n') +# sys.stdout.flush() + except Queue.Full: +# sys.stdout.write('Queue full \n') +# sys.stdout.flush() + pass + self.threadLock.release() + + def emptyQueueToList(self,obj_list): + items=0 + while ((not self.GA_q.empty()) and (items<20)): + obj_list.append(self.GA_q.get_nowait().payload) + items+=1 +# print items + + def reportBatched(self,batched): + req = Request(url_batched, data=batched) + urlopen(req) + #requests.post(url_batched,data=batched) + + def emptyAndReportQ(self): + obj_list = [] + self.emptyQueueToList(obj_list) + if not len(obj_list): + return + batched = '\n'.join(obj_list) +# print batched # - for debug + self.reportBatched(batched) + + def printSelf(self): + print('remaining in queue:') + while not self.GA_q.empty(): + obj = self.GA_q.get_nowait() + print(obj.payload) + + def internet_on(self): + try: + urlopen(url_conn,timeout=10) + return True + except URLError as err: pass + return False + + def activate(self): + if (self.UserPermission==1): + self.thread.start() + + + +#***************************************------TEST--------------************************************** + +if __name__ == '__main__': + g = GAmanager(GoogleID='UA-75220362-4',UserID="Foo",QueueSize=100,Timeout=5,UserPermission=1,BlockingMode=1,appName='TRex',appVer='1.11.232') #timeout in seconds +#for i in range(0,35,1): +#i = 42 + g.gaAddAction(Event='stl',action='stl/udp_1pkt_simple.py {packet_count:1000,packet_len:9000}',label='Boo',value=20) + #g.gaAddAction(Event='test',action='start',label='Boo1',value=20) + +#g.gaAddException('MEMFAULT',1) +#g.gaAddException('MEMFAULT',1) +#g.gaAddException('MEMFAULT',1) +#g.gaAddException('MEMFAULT',1) +#g.gaAddException('MEMFAULT',1) +#g.gaAddException('MEMFAULT',1) + g.emptyAndReportQ() +# g.printSelf() +#print g.payload +#print g.size + + + + +#g.activate() +#g.gaAddAction(Event='test',action='start',label='1',value='1') +#sys.stdout.write('element added \n') +#sys.stdout.flush() +#g.gaAddAction(Event='test',action='start',label='2',value='1') +#sys.stdout.write('element added \n') +#sys.stdout.flush() +#g.gaAddAction(Event='test',action='start',label='3',value='1') +#sys.stdout.write('element added \n') +#sys.stdout.flush() + +#testdata = "v=1&t=event&tid=UA-75220362-4&cid=2&ec=test&ea=testing&el=testpacket&ev=2" +#r = requests.post(url_debug,data=testdata) +#print r + +#thread1 = ga_Thread(1,g) +#thread1.start() +#thread1.join() +#for i in range(1,10,1): +# sys.stdout.write('yesh %d'% (i)) +# sys.stdout.flush() + + +# add timing mechanism - DONE +# add exception mechanism - DONE +# add version mechanism - DONE +# ask Itay for unique ID generation per user + + + + + + + + + + + -- cgit From 39aedd8ec2dd0313a097d78046445be11c7bc6d7 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Fri, 29 Apr 2016 23:31:32 +0300 Subject: GA fix for python3 --- .../automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts/automation/trex_control_plane') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py index 3101b45d..164aae7a 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/GAObjClass.py @@ -200,7 +200,7 @@ class GAmanager: # print items def reportBatched(self,batched): - req = Request(url_batched, data=batched) + req = Request(url_batched, data=batched.encode('ascii')) urlopen(req) #requests.post(url_batched,data=batched) -- cgit From a9af319885a9416dc11d036b19921c7fdadc26e7 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 2 May 2016 15:09:02 +0300 Subject: Python circular object reference - fix --- .../trex_control_plane/stl/trex_stl_lib/trex_stl_client.py | 10 ++++++---- .../stl/trex_stl_lib/trex_stl_jsonrpc_client.py | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'scripts/automation/trex_control_plane') 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 77fa40bb..864e00ad 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 @@ -497,6 +497,11 @@ class STLClient(object): self.session_id = random.getrandbits(32) self.connected = False + # API classes + self.api_vers = [ {'type': 'core', 'major': 1, 'minor':2 } + ] + self.api_h = {'core': None} + # logger self.logger = DefaultLogger() if not logger else logger @@ -540,10 +545,7 @@ class STLClient(object): self.flow_stats) - # API classes - self.api_vers = [ {'type': 'core', 'major': 1, 'minor':2 } - ] - self.api_h = {'core': None} + ############# private functions - used by the class itself ########### diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py index bd5ba8e7..fa04b9f6 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_jsonrpc_client.py @@ -47,7 +47,7 @@ class JsonRpcClient(object): MSG_COMPRESS_HEADER_MAGIC = 0xABE85CEA def __init__ (self, default_server, default_port, client): - self.client = client + self.client_api = client.api_h self.logger = client.logger self.connected = False @@ -104,7 +104,7 @@ class JsonRpcClient(object): # if this RPC has an API class - add it's handler if api_class: - msg["params"]["api_h"] = self.client.api_h[api_class] + msg["params"]["api_h"] = self.client_api[api_class] if encode: -- cgit From c227a2fff3e815c38cff89630e22c3b8485c32de Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 2 May 2016 16:35:17 +0300 Subject: two fixes: 1. history traversal with lock when adding stats objects 2. sync with ports only when you acquire the port, reset does not sync with the streams --- .../stl/trex_stl_lib/trex_stl_client.py | 15 +++++--- .../stl/trex_stl_lib/trex_stl_port.py | 42 +++++++++++++--------- .../stl/trex_stl_lib/trex_stl_stats.py | 13 +++---- 3 files changed, 42 insertions(+), 28 deletions(-) (limited to 'scripts/automation/trex_control_plane') 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 864e00ad..862a9979 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 @@ -582,13 +582,13 @@ class STLClient(object): return rc # acquire ports, if port_list is none - get all - def __acquire (self, port_id_list = None, force = False): + def __acquire (self, port_id_list = None, force = False, sync_streams = True): port_id_list = self.__ports(port_id_list) rc = RC() for port_id in port_id_list: - rc.add(self.ports[port_id].acquire(force)) + rc.add(self.ports[port_id].acquire(force, sync_streams)) return rc @@ -1372,16 +1372,20 @@ class STLClient(object): @__api_check(True) - def acquire (self, ports = None, force = False): + def acquire (self, ports = None, force = False, sync_streams = True): """ Acquires ports for executing commands :parameters: ports : list Ports on which to execute the command + force : bool Force acquire the ports. + sync_streams: bool + sync with the server about the configured streams + :raises: + :exc:`STLError` @@ -1396,7 +1400,7 @@ class STLClient(object): else: self.logger.pre_cmd("Acquiring ports {0}:".format(ports)) - rc = self.__acquire(ports, force) + rc = self.__acquire(ports, force, sync_streams) self.logger.post_cmd(rc) @@ -1496,7 +1500,8 @@ class STLClient(object): ports = ports if ports is not None else self.get_all_ports() ports = self._validate_port_list(ports) - self.acquire(ports, force = True) + # force take the port and ignore any streams on it + self.acquire(ports, force = True, sync_streams = False) self.stop(ports, rx_delay_ms = 0) self.remove_all_streams(ports) self.clear_stats(ports) 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 5cf94bda..e8f89b27 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 @@ -80,19 +80,39 @@ class Port(object): return "{0} Gbps".format(self.info['speed']) # take the port - def acquire(self, force = False): + def acquire(self, force = False, sync_streams = True): params = {"port_id": self.port_id, "user": self.user, "session_id": self.session_id, "force": force} rc = self.transmit("acquire", params) - if rc.good(): - self.handler = rc.data() - return self.ok() + if not rc: + return self.err(rc.err()) + + self.handler = rc.data() + + if sync_streams: + return self.sync_streams() else: + return self.ok() + + + # sync all the streams with the server + def sync_streams (self): + params = {"port_id": self.port_id} + + rc = self.transmit("get_all_streams", params) + if rc.bad(): return self.err(rc.err()) + for k, v in rc.data()['streams'].items(): + self.streams[k] = {'next_id': v['next_stream_id'], + 'pkt' : base64.b64decode(v['packet']['binary']), + 'mode' : v['mode']['type'], + 'rate' : STLStream.get_rate_from_field(v['mode']['rate'])} + return self.ok() + # release the port def release(self): params = {"port_id": self.port_id, @@ -157,19 +177,7 @@ class Port(object): # attributes self.attr = rc.data()['attr'] - # sync the streams - params = {"port_id": self.port_id} - - rc = self.transmit("get_all_streams", params) - if rc.bad(): - return self.err(rc.err()) - - for k, v in rc.data()['streams'].items(): - self.streams[k] = {'next_id': v['next_stream_id'], - 'pkt' : base64.b64decode(v['packet']['binary']), - 'mode' : v['mode']['type'], - 'rate' : STLStream.get_rate_from_field(v['mode']['rate'])} - + return self.ok() 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 42bef360..12c2c578 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 @@ -698,12 +698,13 @@ class CPortStats(CTRexStats): else: self.__merge_dicts(self.reference_stats, x.reference_stats) - # history - if not self.history: - self.history = copy.deepcopy(x.history) - else: - for h1, h2 in zip(self.history, x.history): - self.__merge_dicts(h1, h2) + # history - should be traverse with a lock + with self.lock, x.lock: + if not self.history: + self.history = copy.deepcopy(x.history) + else: + for h1, h2 in zip(self.history, x.history): + self.__merge_dicts(h1, h2) return self -- cgit From 7007bf04481b76b51e30d42148df710bccc9aa35 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Tue, 3 May 2016 04:28:08 +0300 Subject: bp_sim: correct description of supported stateful layers main_dpdk: send bw_per_core with json, return back precision of low values jsonrpcserver: disable logging regression: use bw_per_core from cpp calculation correct test name for GA --- .../automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'scripts/automation/trex_control_plane') 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 12c2c578..c7513144 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 @@ -212,7 +212,7 @@ class CTRexInfoGenerator(object): if ratio <= 0.1875: return u'\u2581' # 1/8 if ratio <= 0.3125: - return u'\u2582' # 2/4 + return u'\u2582' # 2/8 if ratio <= 0.4375: return u'\u2583' # 3/8 if ratio <= 0.5625: @@ -594,6 +594,7 @@ class CGlobalStats(CTRexStats): # absolute stats['cpu_util'] = self.get("m_cpu_util") stats['rx_cpu_util'] = self.get("m_rx_cpu_util") + stats['bw_per_core'] = self.get("m_bw_per_core") stats['tx_bps'] = self.get("m_tx_bps") stats['tx_pps'] = self.get("m_tx_pps") -- cgit