diff options
author | 2016-03-27 14:18:31 +0300 | |
---|---|---|
committer | 2016-03-27 14:18:31 +0300 | |
commit | a2fdf41ac599bdef6657642152c0acdaf488ee6c (patch) | |
tree | 9e8249cdcd438365db50c65f00dab418c8b5c1c7 /scripts | |
parent | d9001d53cf1a0e9bf66cfe805c71bb6927c6ca85 (diff) | |
parent | 1f749d9b568893aa26ff115d18b8f9ef4d0b2603 (diff) |
merge exit value
Diffstat (limited to 'scripts')
3 files changed, 2 insertions, 1242 deletions
diff --git a/scripts/automation/regression/misc_methods.py b/scripts/automation/regression/misc_methods.py index 54e3ba5d..97d168b0 100755 --- a/scripts/automation/regression/misc_methods.py +++ b/scripts/automation/regression/misc_methods.py @@ -42,8 +42,8 @@ def run_command(cmd, background = False): return (proc.returncode, stdout, stderr) -def run_remote_command(host, command_string, background = False): - cmd = 'ssh -tt %s \'sudo sh -ec "%s"\'' % (host, command_string) +def run_remote_command(host, command_string, background = False, timeout = 20): + cmd = 'ssh -tt %s \'sudo%s sh -ec "%s"\'' % (host, (' timeout %s' % timeout) if (timeout and not background) else '', command_string) return run_command(cmd, background) diff --git a/scripts/automation/trex_control_plane/client/outer_packages.py b/scripts/automation/trex_control_plane/client/outer_packages.py deleted file mode 100755 index 2565be08..00000000 --- a/scripts/automation/trex_control_plane/client/outer_packages.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/router/bin/python - -import sys -import os - - -CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) -ROOT_PATH = os.path.abspath(os.path.join(CURRENT_PATH, os.pardir)) # path to trex_control_plane directory -PATH_TO_PYTHON_LIB = os.path.abspath(os.path.join(ROOT_PATH, os.pardir, os.pardir, 'external_libs')) - -CLIENT_MODULES = ['enum34-1.0.4', - 'jsonrpclib-pelix-0.2.5', - 'termstyle', - 'rpc_exceptions-0.1', - 'yaml-3.11' - ] - - -def import_client_modules(): - sys.path.append(ROOT_PATH) - import_module_list(CLIENT_MODULES) - - -def import_module_list(modules_list): - assert(isinstance(modules_list, list)) - for p in modules_list: - full_path = os.path.join(PATH_TO_PYTHON_LIB, p) - fix_path = os.path.normcase(full_path) # (CURRENT_PATH+p) - sys.path.insert(1, full_path) - -import_client_modules() diff --git a/scripts/automation/trex_control_plane/client/trex_client.py b/scripts/automation/trex_control_plane/client/trex_client.py deleted file mode 100755 index dfd3dc01..00000000 --- a/scripts/automation/trex_control_plane/client/trex_client.py +++ /dev/null @@ -1,1209 +0,0 @@ -#!/router/bin/python - -import sys -import os - -if __package__: - from . import outer_packages -else: - import outer_packages - -import jsonrpclib -from jsonrpclib import ProtocolError, AppError -from common.trex_status_e import TRexStatus -from common.trex_exceptions import * -from common.trex_exceptions import exception_handler -from client_utils.general_utils import * -from enum import Enum -import socket -import errno -import time -import re -import copy -import binascii -from collections import deque, OrderedDict -from json import JSONDecoder -from distutils.util import strtobool - - - -class CTRexClient(object): - """ - This class defines the client side of the RESTfull interaction with TRex - """ - - def __init__(self, trex_host, max_history_size = 100, trex_daemon_port = 8090, trex_zmq_port = 4500, verbose = False): - """ - Instantiate a TRex client object, and connecting it to listening daemon-server - - :parameters: - trex_host : str - a string of the TRex ip address or hostname. - max_history_size : int - a number to set the maximum history size of a single TRex run. Each sampling adds a new item to history. - - default value : **100** - trex_daemon_port : int - the port number on which the trex-daemon server can be reached - - default value: **8090** - trex_zmq_port : int - the port number on which trex's zmq module will interact with daemon server - - default value: **4500** - verbose : bool - sets a verbose output on supported class method. - - default value : **False** - - :raises: - socket errors, in case server could not be reached. - - """ - try: - 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.trex_zmq_port = trex_zmq_port - self.seq = None - self.verbose = verbose - self.result_obj = CTRexResult(max_history_size) - self.decoder = JSONDecoder() - self.trex_server_path = "http://{hostname}:{port}/".format( hostname = self.trex_host, port = trex_daemon_port ) - self.__verbose_print("Connecting to TRex @ {trex_path} ...".format( trex_path = self.trex_server_path ) ) - self.history = jsonrpclib.history.History() - self.server = jsonrpclib.Server(self.trex_server_path, history = self.history) - self.check_server_connectivity() - self.__verbose_print("Connection established successfully!") - self._last_sample = time.time() - self.__default_user = get_current_user() - - - def add (self, x, y): - try: - return self.server.add(x,y) - except AppError as err: - self._handle_AppError_exception(err.args[0]) - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - def start_trex (self, f, d, block_to_success = True, timeout = 40, user = None, trex_development = False, **trex_cmd_options): - """ - Request to start a TRex run on server. - - :parameters: - f : str - a path (on server) for the injected traffic data (.yaml file) - d : int - the desired duration of the test. must be at least 30 seconds long. - block_to_success : bool - determine if this method blocks until TRex changes state from 'Starting' to either 'Idle' or 'Running' - - default value : **True** - timeout : int - maximum time (in seconds) to wait in blocking state until TRex changes state from 'Starting' to either 'Idle' or 'Running' - - default value: **40** - user : str - the identity of the the run issuer. - trex_cmd_options : key, val - sets desired TRex options using key=val syntax, separated by comma. - for keys with no value, state key=True - - :return: - **True** on success - - :raises: - + :exc:`ValueError`, in case 'd' parameter inserted with wrong value. - + :exc:`trex_exceptions.TRexError`, in case one of the trex_cmd_options raised an exception at server. - + :exc:`trex_exceptions.TRexInUseError`, in case TRex is already taken. - + :exc:`trex_exceptions.TRexRequestDenied`, in case TRex is reserved for another user than the one trying start TRex. - + ProtocolError, in case of error in JSON-RPC protocol. - - """ - user = user or self.__default_user - try: - d = int(d) - if d < 30 and not trex_development: # test duration should be at least 30 seconds, unless trex_development flag is specified. - raise ValueError - except ValueError: - raise ValueError('d parameter must be integer, specifying how long TRex run, and must be larger than 30 secs.') - - trex_cmd_options.update( {'f' : f, 'd' : d} ) - if not trex_cmd_options.get('l'): - self.result_obj.latency_checked = False - if 'k' in trex_cmd_options: - timeout += int(trex_cmd_options['k']) # during 'k' seconds TRex stays in 'Starting' state - - self.result_obj.clear_results() - try: - issue_time = time.time() - retval = self.server.start_trex(trex_cmd_options, user, block_to_success, timeout) - except AppError as err: - self._handle_AppError_exception(err.args[0]) - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - if retval!=0: - self.seq = retval # update seq num only on successful submission - return True - else: # TRex is has been started by another user - raise TRexInUseError('TRex is already being used by another user or process. Try again once TRex is back in IDLE state.') - - def stop_trex (self): - """ - Request to stop a TRex run on server. - - The request is only valid if the stop initiator is the same client as the TRex run initiator. - - :parameters: - None - - :return: - + **True** on successful termination - + **False** if request issued but TRex wasn't running. - - :raises: - + :exc:`trex_exceptions.TRexRequestDenied`, in case TRex ir running but started by another user. - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). - + ProtocolError, in case of error in JSON-RPC protocol. - - """ - try: - return self.server.stop_trex(self.seq) - except AppError as err: - self._handle_AppError_exception(err.args[0]) - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - def force_kill (self, confirm = True): - """ - Force killing of running TRex process (if exists) on the server. - - .. tip:: This method is a safety method and **overrides any running or reserved resources**, and as such isn't designed to be used on a regular basis. - Always consider using :func:`trex_client.CTRexClient.stop_trex` instead. - - In the end of this method, TRex will return to IDLE state with no reservation. - - :parameters: - confirm : bool - Prompt a user confirmation before continue terminating TRex session - - :return: - + **True** on successful termination - + **False** otherwise. - - :raises: - + ProtocolError, in case of error in JSON-RPC protocol. - - """ - if confirm: - prompt = "WARNING: This will terminate active TRex session indiscriminately.\nAre you sure? " - sys.stdout.write('%s [y/n]\n' % prompt) - while True: - try: - if strtobool(user_input().lower()): - break - else: - return - except ValueError: - sys.stdout.write('Please respond with \'y\' or \'n\'.\n') - try: - return self.server.force_trex_kill() - except AppError as err: - # Silence any kind of application errors- by design - return False - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - def wait_until_kickoff_finish(self, timeout = 40): - """ - Block the client application until TRex changes state from 'Starting' to either 'Idle' or 'Running' - - The request is only valid if the stop initiator is the same client as the TRex run initiator. - - :parameters: - timeout : int - maximum time (in seconds) to wait in blocking state until TRex changes state from 'Starting' to either 'Idle' or 'Running' - - :return: - + **True** on successful termination - + **False** if request issued but TRex wasn't running. - - :raises: - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). - + ProtocolError, in case of error in JSON-RPC protocol. - - .. note:: Exceptions are throws only when start_trex did not block in the first place, i.e. `block_to_success` parameter was set to `False` - - """ - - try: - return self.server.wait_until_kickoff_finish(timeout) - except AppError as err: - self._handle_AppError_exception(err.args[0]) - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - def is_running (self, dump_out = False): - """ - Poll for TRex running status. - - If TRex is running, a history item will be added into result_obj and processed. - - .. tip:: This method is especially useful for iterating until TRex run is finished. - - :parameters: - dump_out : dict - if passed, the pointer object is cleared and the latest dump stored in it. - - :return: - + **True** if TRex is running. - + **False** if TRex is not running. - - :raises: - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). - + :exc:`TypeError`, in case JSON stream decoding error. - + ProtocolError, in case of error in JSON-RPC protocol. - - """ - try: - res = self.get_running_info() - if res == {}: - return False - if (dump_out != False) and (isinstance(dump_out, dict)): # save received dump to given 'dump_out' pointer - dump_out.clear() - dump_out.update(res) - return True - except TRexWarning as err: - if err.code == -12: # TRex is either still at 'Starting' state or in Idle state, however NO error occured - return False - except TRexException: - raise - except ProtocolError as err: - raise - finally: - self.prompt_verbose_data() - - def is_idle (self): - """ - Poll for TRex running status, check if TRex is in Idle state. - - :parameters: - None - - :return: - + **True** if TRex is idle. - + **False** if TRex is starting or running. - - :raises: - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). - + :exc:`TypeError`, in case JSON stream decoding error. - + ProtocolError, in case of error in JSON-RPC protocol. - - """ - try: - if self.get_running_status()['state'] == TRexStatus.Idle: - return True - return False - except TRexException: - raise - except ProtocolError as err: - raise - finally: - self.prompt_verbose_data() - - def get_trex_files_path (self): - """ - Fetches the local path in which files are stored when pushed to TRex server from client. - - :parameters: - None - - :return: - string representation of the desired path - - .. note:: The returned path represents a path on the TRex server **local machine** - - :raises: - ProtocolError, in case of error in JSON-RPC protocol. - - """ - try: - return (self.server.get_files_path() + '/') - except AppError as err: - self._handle_AppError_exception(err.args[0]) - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - def get_running_status (self): - """ - Fetches the current TRex status. - - If available, a verbose data will accompany the state itself. - - :parameters: - None - - :return: - dictionary with 'state' and 'verbose' keys. - - :raises: - ProtocolError, in case of error in JSON-RPC protocol. - - """ - try: - res = self.server.get_running_status() - res['state'] = TRexStatus(res['state']) - return res - except AppError as err: - self._handle_AppError_exception(err.args[0]) - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - def get_running_info (self): - """ - Performs single poll of TRex running data and process it into the result object (named `result_obj`). - - .. tip:: This method will throw an exception if TRex isn't running. Always consider using :func:`trex_client.CTRexClient.is_running` which handles a single poll operation in safer manner. - - :parameters: - None - - :return: - dictionary containing the most updated data dump from TRex. - - :raises: - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). - + :exc:`TypeError`, in case JSON stream decoding error. - + ProtocolError, in case of error in JSON-RPC protocol. - - """ - if not self.is_query_relevance(): - # if requested in timeframe smaller than the original sample rate, return the last known data without interacting with server - return self.result_obj.get_latest_dump() - else: - try: - latest_dump = self.decoder.decode( self.server.get_running_info() ) # latest dump is not a dict, but json string. decode it. - self.result_obj.update_result_data(latest_dump) - return latest_dump - except TypeError as inst: - raise TypeError('JSON-RPC data decoding failed. Check out incoming JSON stream.') - except AppError as err: - self._handle_AppError_exception(err.args[0]) - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - def sample_until_condition (self, condition_func, time_between_samples = 5): - """ - Automatically sets ongoing sampling of TRex data, with sampling rate described by time_between_samples. - - On each fetched dump, the condition_func is applied on the result objects, and if returns True, the sampling will stop. - - :parameters: - condition_func : function - function that operates on result_obj and checks if a condition has been met - - .. note:: `condition_finc` is applied on `CTRexResult` object. Make sure to design a relevant method. - time_between_samples : int - determines the time between each sample of the server - - default value : **5** - - :return: - the first result object (see :class:`CTRexResult` for further details) of the TRex run on which the condition has been met. - - :raises: - + :exc:`UserWarning`, in case the condition_func method condition hasn't been met - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). - + :exc:`TypeError`, in case JSON stream decoding error. - + ProtocolError, in case of error in JSON-RPC protocol. - + :exc:`Exception`, in case the condition_func suffered from any kind of exception - - """ - # make sure TRex is running. raise exceptions here if any - self.wait_until_kickoff_finish() - try: - while self.is_running(): - results = self.get_result_obj() - if condition_func(results): - # if condition satisfied, stop TRex and return result object - self.stop_trex() - return results - time.sleep(time_between_samples) - except TRexWarning: - # means we're back to Idle state, and didn't meet our condition - raise UserWarning("TRex results condition wasn't met during TRex run.") - except Exception: - # this could come from provided method 'condition_func' - raise - - def sample_to_run_finish (self, time_between_samples = 5): - """ - Automatically sets automatically sampling of TRex data with sampling rate described by time_between_samples until TRex run finished. - - :parameters: - time_between_samples : int - determines the time between each sample of the server - - default value : **5** - - :return: - the latest result object (see :class:`CTRexResult` for further details) with sampled data. - - :raises: - + :exc:`UserWarning`, in case the condition_func method condition hasn't been met - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). - + :exc:`TypeError`, in case JSON stream decoding error. - + ProtocolError, in case of error in JSON-RPC protocol. - - """ - self.wait_until_kickoff_finish() - - try: - while self.is_running(): - time.sleep(time_between_samples) - except TRexWarning: - pass - results = self.get_result_obj() - return results - - def sample_x_seconds (self, sample_time, time_between_samples = 5): - """ - 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! - - .. tip:: Useful for changing the device (Router, ASA etc.) configuration after given time. - - :parameters: - sample_time : int - sample the TRex this number of seconds - - time_between_samples : int - determines the time between each sample of the server - - default value : **5** - - :return: - the first result object (see :class:`CTRexResult` for further details) of the TRex run after given sample_time. - - :raises: - + :exc:`UserWarning`, in case the TRex run ended before sample_time duration - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed TRex run (unexpected termination). - + :exc:`TypeError`, in case JSON stream decoding error. - + ProtocolError, in case of error in JSON-RPC protocol. - - """ - # make sure TRex is running. raise exceptions here if any - self.wait_until_kickoff_finish() - elapsed_time = 0 - while self.is_running(): - if elapsed_time >= sample_time: - 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): - """ - Returns the result object of the trex_client's instance. - - By default, returns a **copy** of the objects (so that changes to the original object are masked). - - :parameters: - copy_obj : bool - False means that a reference to the original (possibly changing) object are passed - - defaul value : **True** - - :return: - the latest result object (see :class:`CTRexResult` for further details) with sampled data. - - """ - if copy_obj: - return copy.deepcopy(self.result_obj) - else: - return self.result_obj - - def is_reserved (self): - """ - Checks if TRex is currently reserved to any user or not. - - :parameters: - None - - :return: - + **True** if TRex is reserved. - + **False** otherwise. - - :raises: - ProtocolError, in case of error in JSON-RPC protocol. - - """ - try: - return self.server.is_reserved() - except AppError as err: - self._handle_AppError_exception(err.args[0]) - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - def get_trex_daemon_log (self): - """ - Get Trex daemon log. - - :return: - String representation of TRex daemon log - - :raises: - + :exc:`trex_exceptions.TRexRequestDenied`, in case file could not be read. - + ProtocolError, in case of error in JSON-RPC protocol. - - """ - try: - return binascii.a2b_base64(self.server.get_trex_daemon_log()) - except AppError as err: - self._handle_AppError_exception(err.args[0]) - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - def get_trex_log (self): - """ - Get TRex CLI output log - - :return: - String representation of TRex log - - :raises: - + :exc:`trex_exceptions.TRexRequestDenied`, in case file could not be fetched at server side. - + ProtocolError, in case of error in JSON-RPC protocol. - - """ - try: - return binascii.a2b_base64(self.server.get_trex_log()) - except AppError as err: - self._handle_AppError_exception(err.args[0]) - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - def get_trex_version (self): - """ - Get TRex version details. - - :return: - Trex details (Version, User, Date, Uuid, Git SHA) as ordered dictionary - - :raises: - + :exc:`trex_exceptions.TRexRequestDenied`, in case TRex version could not be determined. - + ProtocolError, in case of error in JSON-RPC protocol. - + General Exception is case one of the keys is missing in response - """ - - try: - version_dict = OrderedDict() - result_lines = binascii.a2b_base64(self.server.get_trex_version()).split('\n') - for line in result_lines: - if not line: - continue - key, value = line.strip().split(':', 1) - version_dict[key.strip()] = value.strip() - for key in ('Version', 'User', 'Date', 'Uuid', 'Git SHA'): - if key not in version_dict: - raise Exception('get_trex_version: got server response without key: {0}'.format(key)) - return version_dict - except AppError as err: - self._handle_AppError_exception(err.args[0]) - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - def reserve_trex (self, user = None): - """ - Reserves the usage of TRex to a certain user. - - When TRex is reserved, it can't be reserved. - - :parameters: - user : str - a username of the desired owner of TRex - - default: current logged user - - :return: - **True** if reservation made successfully - - :raises: - + :exc:`trex_exceptions.TRexRequestDenied`, in case TRex is reserved for another user than the one trying to make the reservation. - + :exc:`trex_exceptions.TRexInUseError`, in case TRex is currently running. - + ProtocolError, in case of error in JSON-RPC protocol. - - """ - username = user or self.__default_user - try: - return self.server.reserve_trex(user = username) - except AppError as err: - self._handle_AppError_exception(err.args[0]) - except ProtocolError: - raise - finally: - self.prompt_verbose_data() - - def cancel_reservation (self, user = None): - """ - Cancels a current reservation of TRex to a certain user. - - When TRex is reserved, no other user can start new TRex runs. - - - :parameters: - user : str - a username of the desired owner of TRex - - default: current logged user - - :return: - + **True** if reservation canceled successfully, - + **False** if there was no reservation at all. - - :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. - - """ - - username = user or self.__default_user - try: - return self.server.cancel_reservation(user = username) - 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. - - :parameters: - filepaths : str or list - a path to a file to be pushed to server. - if a list of paths is passed, all of those will be pushed to server - - :return: - + **True** if file(s) copied successfully. - + **False** otherwise. - - :raises: - + :exc:`IOError`, in case specified file wasn't found or could not be accessed. - + ProtocolError, in case of error in JSON-RPC protocol. - - """ - paths_list = None - if isinstance(filepaths, str): - paths_list = [filepaths] - elif isinstance(filepaths, list): - paths_list = filepaths - else: - raise TypeError("filepaths argument must be of type str or list") - - for filepath in paths_list: - try: - if not os.path.exists(filepath): - raise IOError(errno.ENOENT, "The requested `{fname}` file wasn't found. Operation aborted.".format( - fname = filepath) ) - else: - 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)) - finally: - self.prompt_verbose_data() - return True - - def is_query_relevance(self): - """ - Checks if time between any two consecutive server queries (asking for live running data) passed. - - .. note:: The allowed minimum time between each two consecutive samples is 0.5 seconds. - - :parameters: - None - - :return: - + **True** if more than 0.5 seconds has been past from last server query. - + **False** otherwise. - - """ - cur_time = time.time() - if cur_time-self._last_sample < 0.5: - return False - else: - self._last_sample = cur_time - return True - - def call_server_mathod_safely (self, method_to_call): - try: - return method_to_call() - except socket.error as e: - if e.errno == errno.ECONNREFUSED: - raise SocketError(errno.ECONNREFUSED, "Connection from TRex server was refused. Please make sure the server is up.") - - def check_server_connectivity (self): - """ - Checks for server valid connectivity. - """ - try: - socket.gethostbyname(self.trex_host) - return self.server.connectivity_check() - except socket.gaierror as e: - raise socket.gaierror(e.errno, "Could not resolve server hostname. Please make sure hostname entered correctly.") - except socket.error as e: - if e.errno == errno.ECONNREFUSED: - raise socket.error(errno.ECONNREFUSED, "Connection from TRex server was refused. Please make sure the server is up.") - finally: - self.prompt_verbose_data() - - def prompt_verbose_data(self): - """ - This method prompts any verbose data available, only if `verbose` option has been turned on. - """ - if self.verbose: - print ('\n') - print ("(*) JSON-RPC request:", self.history.request) - print ("(*) JSON-RPC response:", self.history.response) - - def __verbose_print(self, print_str): - """ - This private method prints the `print_str` string only in case self.verbose flag is turned on. - - :parameters: - print_str : str - a string to be printed - - :returns: - None - """ - if self.verbose: - print (print_str) - - - - def _handle_AppError_exception(self, err): - """ - This private method triggres the TRex dedicated exception generation in case a general ProtocolError has been raised. - """ - # handle known exceptions based on known error codes. - # if error code is not known, raise ProtocolError - raise exception_handler.gen_exception(err) - - -class CTRexResult(object): - """ - A class containing all results received from TRex. - - Ontop to containing the results, this class offers easier data access and extended results processing options - """ - def __init__(self, max_history_size): - """ - Instatiate a TRex result object - - :parameters: - max_history_size : int - a number to set the maximum history size of a single TRex run. Each sampling adds a new item to history. - - """ - self._history = deque(maxlen = max_history_size) - self.clear_results() - self.latency_checked = True - - def __repr__(self): - return ("Is valid history? {arg}\n".format( arg = self.is_valid_hist() ) + - "Done warmup? {arg}\n".format( arg = self.is_done_warmup() ) + - "Expected tx rate: {arg}\n".format( arg = self.get_expected_tx_rate() ) + - "Current tx rate: {arg}\n".format( arg = self.get_current_tx_rate() ) + - "Maximum latency: {arg}\n".format( arg = self.get_max_latency() ) + - "Average latency: {arg}\n".format( arg = self.get_avg_latency() ) + - "Average window latency: {arg}\n".format( arg = self.get_avg_window_latency() ) + - "Total drops: {arg}\n".format( arg = self.get_total_drops() ) + - "Drop rate: {arg}\n".format( arg = self.get_drop_rate() ) + - "History size so far: {arg}\n".format( arg = len(self._history) ) ) - - def get_expected_tx_rate (self): - """ - Fetches the expected TX rate in various units representation - - :parameters: - None - - :return: - dictionary containing the expected TX rate, where the key is the measurement units, and the value is the measurement value. - - """ - return self._expected_tx_rate - - def get_current_tx_rate (self): - """ - Fetches the current TX rate in various units representation - - :parameters: - None - - :return: - dictionary containing the current TX rate, where the key is the measurement units, and the value is the measurement value. - - """ - return self._current_tx_rate - - def get_max_latency (self): - """ - Fetches the maximum latency measured on each of the interfaces - - :parameters: - None - - :return: - dictionary containing the maximum latency, where the key is the measurement interface (`c` indicates client), and the value is the measurement value. - - """ - return self._max_latency - - def get_avg_latency (self): - """ - Fetches the average latency measured on each of the interfaces from the start of TRex run - - :parameters: - None - - :return: - dictionary containing the average latency, where the key is the measurement interface (`c` indicates client), and the value is the measurement value. - - The `all` key represents the average of all interfaces' average - - """ - return self._avg_latency - - def get_avg_window_latency (self): - """ - Fetches the average latency measured on each of the interfaces from all the sampled currently stored in window. - - :parameters: - None - - :return: - dictionary containing the average latency, where the key is the measurement interface (`c` indicates client), and the value is the measurement value. - - The `all` key represents the average of all interfaces' average - - """ - return self._avg_window_latency - - def get_total_drops (self): - """ - Fetches the total number of drops identified from the moment TRex run began. - - :parameters: - None - - :return: - total drops count (as int) - - """ - return self._total_drops - - def get_drop_rate (self): - """ - Fetches the most recent drop rate in pkts/sec units. - - :parameters: - None - - :return: - current drop rate (as float) - - """ - return self._drop_rate - - def is_valid_hist (self): - """ - Checks if result obejct contains valid data. - - :parameters: - None - - :return: - + **True** if history is valid. - + **False** otherwise. - - """ - return self.valid - - def set_valid_hist (self, valid_stat = True): - """ - Sets result obejct validity status. - - :parameters: - valid_stat : bool - defines the validity status - - dafault value : **True** - - :return: - None - - """ - self.valid = valid_stat - - def is_done_warmup (self): - """ - Checks if TRex latest results TX-rate indicates that TRex has reached its expected TX-rate. - - :parameters: - None - - :return: - + **True** if expected TX-rate has been reached. - + **False** otherwise. - - """ - return self._done_warmup - - def get_last_value (self, tree_path_to_key, regex = None): - """ - A dynamic getter from the latest sampled data item stored in the result object. - - :parameters: - tree_path_to_key : str - defines a path to desired data. - - .. 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 - apply a regex to filter results out from a multiple results set. - - Filter applies only on keys of dictionary type. - - dafault value : **None** - - :return: - + a list of values relevant to the specified path - + None if no results were fetched or the history isn't valid. - - """ - 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) - - def get_value_list (self, tree_path_to_key, regex = None, filter_none = True): - """ - A dynamic getter from all sampled data items stored in the result object. - - :parameters: - tree_path_to_key : str - defines a path to desired data. - - .. 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 - apply a regex to filter results out from a multiple results set. - - Filter applies only on keys of dictionary type. - - dafault value : **None** - - filter_none : bool - specify if None results should be filtered out or not. - - dafault value : **True** - - :return: - + a list of values relevant to the specified path. Each item on the list refers to a single server sample. - + None if no results were fetched or the history isn't valid. - """ - - if not self.is_valid_hist(): - return None - else: - raw_list = list( map(lambda x: CTRexResult.__get_value_by_path(x, tree_path_to_key, regex), self._history) ) - if filter_none: - return list (filter(lambda x: x!=None, raw_list) ) - else: - return raw_list - - def get_latest_dump(self): - """ - A getter to the latest sampled data item stored in the result object. - - :parameters: - None - - :return: - + a dictionary of the latest data item - + 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 {} - - def update_result_data (self, latest_dump): - """ - Integrates a `latest_dump` dictionary into the CTRexResult object. - - :parameters: - latest_dump : dict - a dictionary with the items desired to be integrated into the object history and stats - - :return: - None - - """ - # add latest dump to history - if latest_dump != {}: - self._history.append(latest_dump) - if not self.valid: - self.valid = True - - # parse important fields and calculate averages and others - if self._expected_tx_rate is None: - # get the expected data only once since it doesn't change - self._expected_tx_rate = CTRexResult.__get_value_by_path(latest_dump, "trex-global.data", "m_tx_expected_\w+") - - self._current_tx_rate = CTRexResult.__get_value_by_path(latest_dump, "trex-global.data", "m_tx_(?!expected_)\w+") - if not self._done_warmup and self._expected_tx_rate is not None: - # check for up to 2% change between expected and actual - if (self._current_tx_rate['m_tx_bps']/self._expected_tx_rate['m_tx_expected_bps'] > 0.98): - self._done_warmup = True - - # handle latency data - if self.latency_checked: - latency_pre = "trex-latency" - self._max_latency = self.get_last_value("{latency}.data".format(latency = latency_pre), "max-")#None # TBC - # support old typo - if self._max_latency is None: - latency_pre = "trex-latecny" - self._max_latency = self.get_last_value("{latency}.data".format(latency = latency_pre), "max-") - - self._avg_latency = self.get_last_value("{latency}.data".format(latency = latency_pre), "avg-")#None # TBC - self._avg_latency = CTRexResult.__avg_all_and_rename_keys(self._avg_latency) - - avg_win_latency_list = self.get_value_list("{latency}.data".format(latency = latency_pre), "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") - rx_pkts = CTRexResult.__get_value_by_path(latest_dump, "trex-global.data.m_total_rx_pkts") - if tx_pkts is not None and rx_pkts is not None: - self._total_drops = tx_pkts - rx_pkts - self._drop_rate = CTRexResult.__get_value_by_path(latest_dump, "trex-global.data.m_rx_drop_bps") - - def clear_results (self): - """ - Clears all results and sets the history's validity to `False` - - :parameters: - None - - :return: - None - - """ - self.valid = False - self._done_warmup = False - self._expected_tx_rate = None - self._current_tx_rate = None - self._max_latency = None - self._avg_latency = None - self._avg_window_latency = None - self._total_drops = None - self._drop_rate = None - self._history.clear() - - @staticmethod - def __get_value_by_path (dct, tree_path, regex = None): - try: - 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 - else: - return dct - except (KeyError, TypeError): - return None - - @staticmethod - def __calc_latency_win_stats (latency_win_list): - res = {'all' : None } - port_dict = {'all' : []} - list( map(lambda x: CTRexResult.__update_port_dict(x, port_dict), latency_win_list) ) - - # finally, calculate everages for each list - res['all'] = float("%.3f" % (sum(port_dict['all'])/float(len(port_dict['all']))) ) - port_dict.pop('all') - for port, avg_list in port_dict.items(): - res[port] = float("%.3f" % (sum(avg_list)/float(len(avg_list))) ) - - return res - - @staticmethod - def __update_port_dict (src_avg_dict, dest_port_dict): - all_list = src_avg_dict.values() - dest_port_dict['all'].extend(all_list) - for key, val in src_avg_dict.items(): - reg_res = re.match("avg-(\d+)", key) - if reg_res: - tmp_key = "port"+reg_res.group(1) - if tmp_key in dest_port_dict: - dest_port_dict[tmp_key].append(val) - else: - dest_port_dict[tmp_key] = [val] - - @staticmethod - def __avg_all_and_rename_keys (src_dict): - res = {} - all_list = src_dict.values() - res['all'] = float("%.3f" % (sum(all_list)/float(len(all_list))) ) - for key, val in src_dict.items(): - reg_res = re.match("avg-(\d+)", key) - if reg_res: - tmp_key = "port"+reg_res.group(1) - res[tmp_key] = val # don't touch original fields values - return res - - - -if __name__ == "__main__": - pass - |