diff options
author | 2016-07-31 11:56:41 +0300 | |
---|---|---|
committer | 2016-07-31 11:56:41 +0300 | |
commit | 893d0feef9ba6fa3fb36c49f4b5bcad47cb2bf60 (patch) | |
tree | 689a09fa656f990672d2d62143dc173a46fe0316 /scripts/automation/trex_control_plane/stf | |
parent | abf329075bd14f5f41c3753d560260ac809ec4f3 (diff) | |
parent | dceb010b01e9f8a0e9c905370d39f149f01cab7e (diff) |
Merge branch 'master' into scapy_server
Diffstat (limited to 'scripts/automation/trex_control_plane/stf')
-rwxr-xr-x | scripts/automation/trex_control_plane/stf/trex_stf_lib/trex_client.py | 218 |
1 files changed, 126 insertions, 92 deletions
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 3b01560a..ecf6083b 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 @@ -13,6 +13,7 @@ from distutils.util import strtobool from collections import deque, OrderedDict from json import JSONDecoder import traceback +import signal try: from . import outer_packages @@ -39,7 +40,7 @@ class CTRexClient(object): This class defines the client side of the RESTfull interaction with TRex """ - def __init__(self, trex_host, max_history_size = 100, filtered_latency_amount = 0.001, trex_daemon_port = 8090, master_daemon_port = 8091, trex_zmq_port = 4500, verbose = False): + def __init__(self, trex_host, max_history_size = 100, filtered_latency_amount = 0.001, trex_daemon_port = 8090, master_daemon_port = 8091, trex_zmq_port = 4500, verbose = False, debug_image = False, trex_args = ''): """ Instantiate a TRex client object, and connecting it to listening daemon-server @@ -72,6 +73,8 @@ class CTRexClient(object): sets a verbose output on supported class method. default value : **False** + trex_args : string + additional arguments passed to TRex. For example, "-w 3 --no-watchdog" :raises: socket errors, in case server could not be reached. @@ -81,53 +84,23 @@ class CTRexClient(object): self.trex_host = socket.gethostbyname(trex_host) except: # give it another try self.trex_host = socket.gethostbyname(trex_host) - self.trex_daemon_port = trex_daemon_port - self.master_daemon_port = master_daemon_port - self.trex_zmq_port = trex_zmq_port - self.seq = None - self._last_sample = time.time() - self.__default_user = get_current_user() - self.verbose = verbose - self.result_obj = CTRexResult(max_history_size, filtered_latency_amount) - self.decoder = JSONDecoder() - self.history = jsonrpclib.history.History() - self.master_daemon_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = master_daemon_port ) - self.trex_server_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = trex_daemon_port ) - self.connect_master() - self.connect_server() - - - def connect_master(self): - ''' - Connects to Master daemon via JsonRPC. - This daemon controls TRex daemon server. - Return true if success, false if fail - ''' - try: - print('Connecting to Master daemon @ %s ...' % self.master_daemon_path) - self.master_daemon = jsonrpclib.Server(self.master_daemon_path, history = self.history) - self.check_master_connectivity() - print('Connected to Master daemon.') - return True - except Exception as e: - print(e) - return False - - def connect_server(self): - ''' - Connects to TRex daemon server via JsonRPC. - This daemon controls TRex. (start/stop) - Return true if success, false if fail - ''' - try: - print('Connecting to TRex daemon server @ %s ...' % self.trex_server_path) - self.server = jsonrpclib.Server(self.trex_server_path, history = self.history) - self.check_server_connectivity() - print('Connected TRex server daemon.') - return True - except Exception as e: - print(e) - return False + self.trex_daemon_port = trex_daemon_port + self.master_daemon_port = master_daemon_port + self.trex_zmq_port = trex_zmq_port + self.seq = None + self._last_sample = time.time() + self.__default_user = get_current_user() + self.verbose = verbose + self.result_obj = CTRexResult(max_history_size, filtered_latency_amount) + self.decoder = JSONDecoder() + self.history = jsonrpclib.history.History() + self.master_daemon_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = master_daemon_port ) + self.master_daemon = jsonrpclib.Server(self.master_daemon_path, history = self.history) + self.trex_server_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = trex_daemon_port ) + self.server = jsonrpclib.Server(self.trex_server_path, history = self.history) + self.debug_image = debug_image + self.trex_args = trex_args + self.sample_to_run_finish = self.sample_until_finish # alias for legacy def add (self, x, y): @@ -191,7 +164,7 @@ class CTRexClient(object): self.result_obj.clear_results() try: issue_time = time.time() - retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout) + retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, False, self.debug_image, self.trex_args) except AppError as err: self._handle_AppError_exception(err.args[0]) except ProtocolError: @@ -237,7 +210,7 @@ class CTRexClient(object): """ try: user = user or self.__default_user - retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, True) + retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout, True, self.debug_image, self.trex_args) except AppError as err: self._handle_AppError_exception(err.args[0]) except ProtocolError: @@ -322,18 +295,28 @@ class CTRexClient(object): finally: self.prompt_verbose_data() - def kill_all_trexes(self): + def kill_all_trexes(self, timeout = 15): """ Kills running TRex processes (if exists) on the server, not only owned by current daemon. Raises exception upon error killing. :return: - + **True** if any process killed + + **True** if processes killed/not running + **False** otherwise. """ try: - return self.server.kill_all_trexes() + poll_rate = 0.1 + # try Ctrl+C, usual kill, -9 + for signal_name in [signal.SIGINT, signal.SIGTERM, signal.SIGKILL]: + self.server.kill_all_trexes(signal_name) + for i in range(int(timeout / poll_rate)): + if not self.get_trex_cmds(): + return True + time.sleep(poll_rate) + if self.get_trex_cmds(): + return False + return True except AppError as err: self._handle_AppError_exception(err.args[0]) finally: @@ -555,7 +538,7 @@ class CTRexClient(object): finally: self.prompt_verbose_data() - def sample_until_condition (self, condition_func, time_between_samples = 5): + def sample_until_condition (self, condition_func, time_between_samples = 1): """ Automatically sets ongoing sampling of TRex data, with sampling rate described by time_between_samples. @@ -569,7 +552,7 @@ class CTRexClient(object): time_between_samples : int determines the time between each sample of the server - default value : **5** + default value : **1** :return: the first result object (see :class:`CTRexResult` for further details) of the TRex run on which the condition has been met. @@ -599,15 +582,15 @@ class CTRexClient(object): # this could come from provided method 'condition_func' raise - def sample_to_run_finish (self, time_between_samples = 5): + def sample_until_finish (self, time_between_samples = 1): """ - Automatically sets automatically sampling of TRex data with sampling rate described by time_between_samples until TRex run finished. + Automatically samples TRex data with sampling rate described by time_between_samples until TRex run finishes. :parameters: time_between_samples : int determines the time between each sample of the server - default value : **5** + default value : **1** :return: the latest result object (see :class:`CTRexResult` for further details) with sampled data. @@ -629,7 +612,7 @@ class CTRexClient(object): results = self.get_result_obj() return results - def sample_x_seconds (self, sample_time, time_between_samples = 5): + def sample_x_seconds (self, sample_time, time_between_samples = 1): """ Automatically sets ongoing sampling of TRex data for sample_time seconds, with sampling rate described by time_between_samples. Does not stop the TRex afterwards! @@ -643,7 +626,7 @@ class CTRexClient(object): time_between_samples : int determines the time between each sample of the server - default value : **5** + default value : **1** :return: the first result object (see :class:`CTRexResult` for further details) of the TRex run after given sample_time. @@ -657,12 +640,12 @@ class CTRexClient(object): """ # make sure TRex is running. raise exceptions here if any self.wait_until_kickoff_finish() - elapsed_time = 0 + end_time = time.time() + sample_time while self.is_running(): - if elapsed_time >= sample_time: + if time.time() < end_time: + time.sleep(time_between_samples) + else: return self.get_result_obj() - time.sleep(time_between_samples) - elapsed_time += time_between_samples raise UserWarning("TRex has stopped at %s seconds (before expected %s seconds)\nTry increasing test duration or decreasing sample_time" % (elapsed_time, sample_time)) def get_result_obj (self, copy_obj = True): @@ -1291,7 +1274,7 @@ class CTRexResult(object): .. tip:: | Use '.' to enter one level deeper in dictionary hierarchy. | Use '[i]' to access the i'th indexed object of an array. - tree_path_to_key : regex + regex : regex apply a regex to filter results out from a multiple results set. Filter applies only on keys of dictionary type. @@ -1319,7 +1302,7 @@ class CTRexResult(object): .. tip:: | Use '.' to enter one level deeper in dictionary hierarchy. | Use '[i]' to access the i'th indexed object of an array. - tree_path_to_key : regex + regex : regex apply a regex to filter results out from a multiple results set. Filter applies only on keys of dictionary type. @@ -1372,7 +1355,7 @@ class CTRexResult(object): if not len(self._history): return -1 - return len(self.__get_value_by_path(self._history[-1], 'trex-global.data', 'opackets-\d+')) + return len(self.get_last_value('trex-global.data', 'opackets-\d+')) def update_result_data (self, latest_dump): @@ -1403,14 +1386,21 @@ class CTRexResult(object): # check for up to 2% change between expected and actual if (self._current_tx_rate['m_tx_bps'] > 0.98 * self._expected_tx_rate['m_tx_expected_bps']): self._done_warmup = True + latest_dump['warmup_barrier'] = True # handle latency data if self.latency_checked: - latency_per_port = self.get_last_value("trex-latecny-v2.data", "port-") + # fix typos, by "pointer" + if 'trex-latecny-v2' in latest_dump and 'trex-latency-v2' not in latest_dump: + latest_dump['trex-latency-v2'] = latest_dump['trex-latecny-v2'] + if 'trex-latecny' in latest_dump and 'trex-latency' not in latest_dump: + latest_dump['trex-latency'] = latest_dump['trex-latecny'] + + latency_per_port = self.get_last_value("trex-latency-v2.data", "port-") self._max_latency = self.__get_filtered_max_latency(latency_per_port, self.filtered_latency_amount) - avg_latency = self.get_last_value("trex-latecny.data", "avg-") + avg_latency = self.get_last_value("trex-latency.data", "avg-") self._avg_latency = CTRexResult.__avg_all_and_rename_keys(avg_latency) - avg_win_latency_list = self.get_value_list("trex-latecny.data", "avg-") + avg_win_latency_list = self.get_value_list("trex-latency.data", "avg-") self._avg_window_latency = CTRexResult.__calc_latency_win_stats(avg_win_latency_list) tx_pkts = CTRexResult.__get_value_by_path(latest_dump, "trex-global.data.m_total_tx_pkts") @@ -1447,12 +1437,12 @@ class CTRexResult(object): for i, p in re.findall(r'(\d+)|([\w|-]+)', tree_path): dct = dct[p or int(i)] if regex is not None and isinstance(dct, dict): - res = {} - for key,val in dct.items(): - match = re.match(regex, key) - if match: - res[key]=val - return res + res = {} + for key,val in dct.items(): + match = re.match(regex, key) + if match: + res[key]=val + return res else: return dct except (KeyError, TypeError): @@ -1500,26 +1490,70 @@ class CTRexResult(object): @staticmethod def __get_filtered_max_latency (src_dict, filtered_latency_amount = 0.001): result = {} - for port, data in src_dict.items(): - 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 - 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'] - if sum_high >= filtered_latency_amount * res['cnt']: - result[max_port] = elem['key'] + int('5' + repr(elem['key'])[2:]) - break + if src_dict: + for port, data in src_dict.items(): + 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 + 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'] + if sum_high >= filtered_latency_amount * res['cnt']: + result[max_port] = elem['key'] + int('5' + repr(elem['key'])[2:]) + break return result + # history iterator after warmup period + def _get_steady_state_history_iterator(self): + if not self.is_done_warmup(): + raise Exception('Warm-up period not finished') + for index, res in enumerate(self._history): + if 'warmup_barrier' in res: + for steady_state_index in range(index, max(index, len(self._history) - 1)): + yield self._history[steady_state_index] + return + for index in range(len(self._history) - 1): + yield self._history[index] + + + def get_avg_steady_state_value(self, tree_path_to_key): + ''' + Gets average value after warmup period. + For example: <result object>.get_avg_steady_state_value('trex-global.data.m_tx_bps') + Usually more accurate than latest history value. + + :parameters: + tree_path_to_key : str + defines a path to desired data. + + :return: + average value at steady state + + :raises: + Exception in case steady state period was not reached or tree_path_to_key was not found in result. + ''' + values_arr = [self.__get_value_by_path(res, tree_path_to_key) for res in self._get_steady_state_history_iterator()] + values_arr = list(filter(lambda x: x is not None, values_arr)) + if not values_arr: + raise Exception('All the keys are None, probably wrong tree_path_to_key: %s' % tree_path_to_key) + return sum(values_arr) / float(len(values_arr)) if __name__ == "__main__": - pass + c = CTRexClient('127.0.0.1') + print('restarting daemon') + c.restart_trex_daemon() + print('kill any running') + c.kill_all_trexes() + print('start') + c.start_stateless() + print('sleep') + time.sleep(5) + print('done') |