diff options
60 files changed, 1946 insertions, 891 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 57d19820..3cda10ac 100755 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1,4 +1,4 @@ -Hanoh haim +Hanoch haim hhaim@cisco.com Dave Johnson Wenxian Li Dan Klein @@ -1,4 +1,4 @@ -v1.77 +v1.78 diff --git a/scripts/automation/readme.txt b/scripts/automation/readme.txt index 2541a1a3..152eee16 100755 --- a/scripts/automation/readme.txt +++ b/scripts/automation/readme.txt @@ -1,7 +1,7 @@ README - trex_perf.py ===================== -This script uses the T-Rex RESTfull client-server conrtol plane achitecture and tries to find the maximum M (platform factor) for trex before hitting one of two stopping conditions: +This script uses the TRex RESTfull client-server conrtol plane achitecture and tries to find the maximum M (platform factor) for trex before hitting one of two stopping conditions: (*) Packet drops (*) High latency. Since high latency can change from one platform to another, and might suffer from kickoff peak (espicially at VM), it is the user responsibility to provide the latency condition. @@ -9,7 +9,7 @@ This script uses the T-Rex RESTfull client-server conrtol plane achitecture and please note that '-f' and '-c' options are mandatory. -Also, this is the user's responsibility to make sure a T-Rex is running, listening to relevant client request coming from this script. +Also, this is the user's responsibility to make sure a TRex is running, listening to relevant client request coming from this script. example for finding max M (between 10 to 100) with imix_fast_1g.yaml traffic profile: ./trex_perf.py -m 10 100 -c config/trex-hhaim.cfg all drop -f cap2/imix_fast_1g.yaml diff --git a/scripts/automation/report_template.html b/scripts/automation/report_template.html index 779d5429..ccd5388d 100755 --- a/scripts/automation/report_template.html +++ b/scripts/automation/report_template.html @@ -76,7 +76,7 @@ vertical-align:top; <body> <H1> -T-Rex Performance Report +TRex Performance Report </H1> <H2> diff --git a/scripts/automation/sshpass.exp b/scripts/automation/sshpass.exp index f27210c8..3b5ce560 100755 --- a/scripts/automation/sshpass.exp +++ b/scripts/automation/sshpass.exp @@ -12,4 +12,6 @@ match_max 100000 expect "*?assword:*" send -- "$pass\r" send -- "\r" -interact +expect eof +wait +#interact diff --git a/scripts/automation/trex_control_plane/client/outer_packages.py b/scripts/automation/trex_control_plane/client/outer_packages.py index 5facad20..206d4b4c 100755 --- a/scripts/automation/trex_control_plane/client/outer_packages.py +++ b/scripts/automation/trex_control_plane/client/outer_packages.py @@ -1,7 +1,6 @@ #!/router/bin/python
import sys
-import site
import os
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
@@ -25,6 +24,6 @@ def import_module_list(modules_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)
- site.addsitedir(full_path)
+ sys.path.insert(1, full_path)
import_client_modules()
diff --git a/scripts/automation/trex_control_plane/client/trex_adv_client.py b/scripts/automation/trex_control_plane/client/trex_adv_client.py index b3fe3dad..bf7ccf58 100755 --- a/scripts/automation/trex_control_plane/client/trex_adv_client.py +++ b/scripts/automation/trex_control_plane/client/trex_adv_client.py @@ -8,7 +8,7 @@ class CTRexAdvClient(trex_client.CTRexClient): super(CTRexAdvClient, self).__init__(trex_host, max_history_size, trex_daemon_port, trex_zmq_port, verbose) pass - # T-REX KIWI advanced methods + # TRex KIWI advanced methods def start_quick_trex(self, pcap_file, d, delay, dual, ipv6, times, interfaces): try: return self.server.start_quick_trex(pcap_file = pcap_file, duration = d, dual = dual, delay = delay, ipv6 = ipv6, times = times, interfaces = interfaces) diff --git a/scripts/automation/trex_control_plane/client/trex_client.py b/scripts/automation/trex_control_plane/client/trex_client.py index 56775766..c3677132 100755 --- a/scripts/automation/trex_control_plane/client/trex_client.py +++ b/scripts/automation/trex_control_plane/client/trex_client.py @@ -30,18 +30,18 @@ from distutils.util import strtobool class CTRexClient(object): """ - This class defines the client side of the RESTfull interaction with T-Rex + 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 T-Rex client object, and connecting it to listening daemon-server + Instantiate a TRex client object, and connecting it to listening daemon-server :parameters: trex_host : str - a string of the t-rex ip address or hostname. + a string of the TRex ip address or hostname. max_history_size : int - a number to set the maximum history size of a single T-Rex run. Each sampling adds a new item to history. + 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 @@ -69,7 +69,7 @@ class CTRexClient(object): self.result_obj = CTRexResult(max_history_size) self.decoder = JSONDecoder() self.trex_server_path = "http://{hostname}:{port}/".format( hostname = trex_host, port = trex_daemon_port ) - self.__verbose_print("Connecting to T-Rex @ {trex_path} ...".format( trex_path = self.trex_server_path ) ) + 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() @@ -90,7 +90,7 @@ class CTRexClient(object): def start_trex (self, f, d, block_to_success = True, timeout = 30, user = None, **trex_cmd_options): """ - Request to start a T-Rex run on server. + Request to start a TRex run on server. :parameters: f : str @@ -98,17 +98,17 @@ class CTRexClient(object): 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 T-Rex changes state from 'Starting' to either 'Idle' or 'Running' + 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 T-Rex changes state from 'Starting' to either 'Idle' or 'Running' + maximum time (in seconds) to wait in blocking state until TRex changes state from 'Starting' to either 'Idle' or 'Running' default value: **30** user : str the identity of the the run issuer. trex_cmd_options : key, val - sets desired T-Rex options using key=val syntax, separated by comma. + sets desired TRex options using key=val syntax, separated by comma. for keys with no value, state key=True :return: @@ -117,8 +117,8 @@ class CTRexClient(object): :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 T-Rex is already taken. - + :exc:`trex_exceptions.TRexRequestDenied`, in case T-Rex is reserved for another user than the one trying start T-Rex. + + :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. """ @@ -128,7 +128,7 @@ class CTRexClient(object): if d < 30: # specify a test should take at least 30 seconds long. raise ValueError except ValueError: - raise ValueError('d parameter must be integer, specifying how long T-Rex run, and must be larger than 30 secs.') + 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} ) @@ -146,25 +146,25 @@ class CTRexClient(object): if retval!=0: self.seq = retval # update seq num only on successful submission return True - else: # T-Rex is has been started by another user - raise TRexInUseError('T-Rex is already being used by another user or process. Try again once T-Rex is back in IDLE state.') + 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 T-Rex run on server. + Request to stop a TRex run on server. - The request is only valid if the stop initiator is the same client as the T-Rex run initiator. + 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 T-Rex wasn't running. + + **False** if request issued but TRex wasn't running. :raises: - + :exc:`trex_exceptions.TRexRequestDenied`, in case T-Rex ir running but started by another user. - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed T-Rex run (unexpected termination). + + :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. """ @@ -179,16 +179,16 @@ class CTRexClient(object): def force_kill (self, confirm = True): """ - Force killing of running T-Rex process (if exists) on the server. + 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, T-Rex will return to IDLE state with no reservation. + 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 T-Rex session + Prompt a user confirmation before continue terminating TRex session :return: + **True** on successful termination @@ -199,7 +199,7 @@ class CTRexClient(object): """ if confirm: - prompt = "WARNING: This will terminate active T-Rex session indiscriminately.\nAre you sure? " + prompt = "WARNING: This will terminate active TRex session indiscriminately.\nAre you sure? " sys.stdout.write('%s [y/n]\n' % prompt) while True: try: @@ -221,20 +221,20 @@ class CTRexClient(object): def wait_until_kickoff_finish(self, timeout = 40): """ - Block the client application until T-Rex changes state from 'Starting' to either 'Idle' or 'Running' + 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 T-Rex run initiator. + 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 T-Rex changes state from 'Starting' to either 'Idle' or 'Running' + 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 T-Rex wasn't running. + + **False** if request issued but TRex wasn't running. :raises: - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed T-Rex run (unexpected termination). + + :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` @@ -252,22 +252,22 @@ class CTRexClient(object): def is_running (self, dump_out = False): """ - Poll for T-Rex running status. + Poll for TRex running status. - If T-Rex is running, a history item will be added into result_obj and processed. + If TRex is running, a history item will be added into result_obj and processed. - .. tip:: This method is especially useful for iterating until T-Rex run is finished. + .. 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 T-Rex is running. - + **False** if T-Rex is not running. + + **True** if TRex is running. + + **False** if TRex is not running. :raises: - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed T-Rex run (unexpected termination). + + :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. @@ -292,7 +292,7 @@ class CTRexClient(object): def get_trex_files_path (self): """ - Fetches the local path in which files are stored when pushed to t-rex server from client. + Fetches the local path in which files are stored when pushed to TRex server from client. :parameters: None @@ -300,7 +300,7 @@ class CTRexClient(object): :return: string representation of the desired path - .. note:: The returned path represents a path on the T-Rex server **local machine** + .. note:: The returned path represents a path on the TRex server **local machine** :raises: ProtocolError, in case of error in JSON-RPC protocol. @@ -317,7 +317,7 @@ class CTRexClient(object): def get_running_status (self): """ - Fetches the current T-Rex status. + Fetches the current TRex status. If available, a verbose data will accompany the state itself. @@ -344,18 +344,18 @@ class CTRexClient(object): def get_running_info (self): """ - Performs single poll of T-Rex running data and process it into the result object (named `result_obj`). + 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 T-Rex isn't running. Always consider using :func:`trex_client.CTRexClient.is_running` which handles a single poll operation in safer manner. + .. 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 T-Rex. + dictionary containing the most updated data dump from TRex. :raises: - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed T-Rex run (unexpected termination). + + :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. @@ -379,7 +379,7 @@ class CTRexClient(object): def sample_until_condition (self, condition_func, time_between_samples = 5): """ - Automatically sets ongoing sampling of T-Rex data, with sampling rate described by time_between_samples. + 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. @@ -394,36 +394,36 @@ class CTRexClient(object): default value : **5** :return: - the first result object (see :class:`CTRexResult` for further details) of the T-Rex run on which the condition has been met. + 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 T-Rex run (unexpected termination). + + :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 T-Rex is running. raise exceptions here if any + # 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 T-Rex and return result object + # 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("T-Rex results condition wasn't met during T-Rex run.") + 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 T-Rex data with sampling rate described by time_between_samples until T-Rex run finished. + Automatically sets automatically sampling of TRex data with sampling rate described by time_between_samples until TRex run finished. :parameters: time_between_samples : int @@ -436,7 +436,7 @@ class CTRexClient(object): :raises: + :exc:`UserWarning`, in case the condition_func method condition hasn't been met - + :exc:`trex_exceptions.TRexIncompleteRunError`, in case one of failed T-Rex run (unexpected termination). + + :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. @@ -475,13 +475,13 @@ class CTRexClient(object): def is_reserved (self): """ - Checks if T-Rex is currently reserved to any user or not. + Checks if TRex is currently reserved to any user or not. :parameters: None :return: - + **True** if T-Rex is reserved. + + **True** if TRex is reserved. + **False** otherwise. :raises: @@ -499,13 +499,13 @@ class CTRexClient(object): def reserve_trex (self, user = None): """ - Reserves the usage of T-Rex to a certain user. + Reserves the usage of TRex to a certain user. - When T-Rex is reserved, it can't be reserved. + When TRex is reserved, it can't be reserved. :parameters: user : str - a username of the desired owner of T-Rex + a username of the desired owner of TRex default: current logged user @@ -513,8 +513,8 @@ class CTRexClient(object): **True** if reservation made successfully :raises: - + :exc:`trex_exceptions.TRexRequestDenied`, in case T-Rex is reserved for another user than the one trying to make the reservation. - + :exc:`trex_exceptions.TRexInUseError`, in case T-Rex is currently running. + + :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. """ @@ -530,14 +530,14 @@ class CTRexClient(object): def cancel_reservation (self, user = None): """ - Cancels a current reservation of T-Rex to a certain user. + Cancels a current reservation of TRex to a certain user. - When T-Rex is reserved, no other user can start new T-Rex runs. + When TRex is reserved, no other user can start new TRex runs. :parameters: user : str - a username of the desired owner of T-Rex + a username of the desired owner of TRex default: current logged user @@ -546,7 +546,7 @@ class CTRexClient(object): + **False** if there was no reservation at all. :raises: - + :exc:`trex_exceptions.TRexRequestDenied`, in case T-Rex is reserved for another user than the one trying to cancel the reservation. + + :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. """ @@ -627,7 +627,7 @@ class CTRexClient(object): return method_to_call() except socket.error as e: if e.errno == errno.ECONNREFUSED: - raise SocketError(errno.ECONNREFUSED, "Connection from T-Rex server was refused. Please make sure the server is up.") + raise SocketError(errno.ECONNREFUSED, "Connection from TRex server was refused. Please make sure the server is up.") def check_server_connectivity (self): """ @@ -640,7 +640,7 @@ class CTRexClient(object): 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 T-Rex server was refused. Please make sure the server is up.") + raise socket.error(errno.ECONNREFUSED, "Connection from TRex server was refused. Please make sure the server is up.") finally: self.prompt_verbose_data() @@ -671,7 +671,7 @@ class CTRexClient(object): def _handle_AppError_exception(self, err): """ - This private method triggres the T-Rex dedicated exception generation in case a general ProtocolError has been raised. + 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 @@ -680,17 +680,17 @@ class CTRexClient(object): class CTRexResult(object): """ - A class containing all results received from T-Rex. + 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 T-Rex result object + Instatiate a TRex result object :parameters: max_history_size : int - a number to set the maximum history size of a single T-Rex run. Each sampling adds a new item to history. + 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) @@ -749,7 +749,7 @@ class CTRexResult(object): def get_avg_latency (self): """ - Fetches the average latency measured on each of the interfaces from the start of T-Rex run + Fetches the average latency measured on each of the interfaces from the start of TRex run :parameters: None @@ -779,7 +779,7 @@ class CTRexResult(object): def get_total_drops (self): """ - Fetches the total number of drops identified from the moment T-Rex run began. + Fetches the total number of drops identified from the moment TRex run began. :parameters: None @@ -835,7 +835,7 @@ class CTRexResult(object): def is_done_warmup (self): """ - Checks if T-Rex latest results TX-rate indicates that T-Rex has reached its expected TX-rate. + Checks if TRex latest results TX-rate indicates that TRex has reached its expected TX-rate. :parameters: None @@ -856,7 +856,7 @@ class CTRexResult(object): defines a path to desired data. .. tip:: | Use '.' to enter one level deeper in dictionary hierarchy. - | Use '[i]' to access the i'th indexed obejct of an array. + | 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. diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_client.py b/scripts/automation/trex_control_plane/client/trex_stateless_client.py index 5513f420..334496d1 100644..100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -6,22 +6,314 @@ try: except ImportError: # support import for Python 3 import client.outer_packages -from client_utils.jsonrpc_client import JsonRpcClient - +from client_utils.jsonrpc_client import JsonRpcClient, BatchMessage +from client_utils.packet_builder import CTRexPktBuilder +import json +from common.trex_stats import * +from collections import namedtuple class CTRexStatelessClient(object): """docstring for CTRexStatelessClient""" - def __init__(self, server="localhost", port=5050, virtual=False): + RpcCmdData = namedtuple('RpcCmdData', ['method', 'params']) + + def __init__(self, username, server="localhost", port=5050, virtual=False): super(CTRexStatelessClient, self).__init__() + self.user = username self.tx_link = CTRexStatelessClient.CTxLink(server, port, virtual) + self._conn_handler = {} + self._active_ports = set() + self._stats = CTRexStatsManager("port", "stream") + self._system_info = None + + # ----- decorator methods ----- # + def force_status(owned=True, active_and_owned=False): + def wrapper(func): + def wrapper_f(self, *args, **kwargs): + port_ids = kwargs.get("port_id") + if isinstance(port_ids, int): + # make sure port_ids is a list + port_ids = [port_ids] + bad_ids = set() + for port_id in port_ids: + port_owned = self._conn_handler.get(kwargs.get(port_id)) + if owned and not port_owned: + bad_ids.add(port_ids) + elif active_and_owned: # stronger condition than just owned, hence gets precedence + if port_owned and port_id in self._active_ports: + continue + else: + bad_ids.add(port_ids) + else: + continue + if bad_ids: + # Some port IDs are not according to desires status + raise RuntimeError("The requested method ('{0}') cannot be invoked since port IDs {1} are not" + "at allowed stated".format(func.__name__)) + else: + func(self, *args, **kwargs) + return wrapper_f + return wrapper + + @property + def system_info(self): + if not self._system_info: + self._system_info = self.get_system_info() + return self._system_info + + # ----- user-access methods ----- # + def ping(self): + return self.transmit("ping") + + def get_supported_cmds(self): + return self.transmit("get_supported_cmds") + + def get_version(self): + return self.transmit("get_version") + + def get_system_info(self): + return self.transmit("get_system_info") + + def get_port_count(self): + return self.system_info.get("port_count") + + def acquire(self, port_id, force=False): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [self.RpcCmdData("acquire", {"port_id": p_id, "user": self.user, "force": force}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + if rc: + self._process_batch_result(commands, resp_list, self._handle_acquire_response) + else: + params = {"port_id": port_id, + "user": self.user, + "force": force} + command = self.RpcCmdData("acquire", params) + self._handle_acquire_response(command, self.transmit(command.method, command.params)) + return self._conn_handler.get(port_id) + + @force_status(owned=True) + def release(self, port_id=None): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [self.RpcCmdData("release", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + if rc: + self._process_batch_result(commands, resp_list, self._handle_release_response) + else: + self._conn_handler.pop(port_id) + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id} + command = self.RpcCmdData("release", params) + self._handle_release_response(command, self.transmit(command.method, command.params)) + return + + @force_status(owned=True) + def add_stream(self, stream_id, stream_obj, port_id=None): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + assert isinstance(stream_obj, CStream) + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id, + "stream_id": stream_id, + "stream": stream_obj.dump()} + return self.transmit("add_stream", params) + + @force_status(owned=True) + def remove_stream(self, stream_id, port_id=None): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id, + "stream_id": stream_id} + return self.transmit("remove_stream", params) + + @force_status(owned=True, active_and_owned=True) + def get_stream_id_list(self, port_id=None): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id} + return self.transmit("get_stream_list", params) + + @force_status(owned=True, active_and_owned=True) + def get_stream(self, stream_id, port_id=None): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id, + "stream_id": stream_id} + return self.transmit("get_stream_list", params) + @force_status(owned=True) + def start_traffic(self, port_id=None): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [self.RpcCmdData("start_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + if rc: + self._process_batch_result(commands, resp_list, self._handle_start_traffic_response) + else: + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id} + command = self.RpcCmdData("start_traffic", params) + self._handle_start_traffic_response(command, self.transmit(command.method, command.params)) + return - def transmit(self, method_name, params = {}): + @force_status(owned=False, active_and_owned=True) + def stop_traffic(self, port_id=None): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [self.RpcCmdData("stop_traffic", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + if rc: + self._process_batch_result(commands, resp_list, self._handle_stop_traffic_response) + else: + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id} + command = self.RpcCmdData("stop_traffic", params) + self._handle_start_traffic_response(command, self.transmit(command.method, command.params)) + return + + def get_global_stats(self): + command = self.RpcCmdData("get_global_stats", {}) + return self._handle_get_global_stats_response(command, self.transmit(command.method, command.params)) + # return self.transmit("get_global_stats") + + @force_status(owned=True, active_and_owned=True) + def get_port_stats(self, port_id=None): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [self.RpcCmdData("get_port_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + if rc: + self._process_batch_result(commands, resp_list, self._handle_get_port_stats_response) + else: + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id} + command = self.RpcCmdData("get_port_stats", params) + return self._handle_get_port_stats_response(command, self.transmit(command.method, command.params)) + + @force_status(owned=True, active_and_owned=True) + def get_stream_stats(self, port_id=None): + if not self._is_ports_valid(port_id): + raise ValueError("Provided illegal port id input") + if isinstance(port_id, list) or isinstance(port_id, set): + # handle as batch mode + port_ids = set(port_id) # convert to set to avoid duplications + commands = [self.RpcCmdData("get_stream_stats", {"handler": self._conn_handler.get(p_id), "port_id": p_id}) + for p_id in port_ids] + rc, resp_list = self.transmit_batch(commands) + if rc: + self._process_batch_result(commands, resp_list, self._handle_get_stream_stats_response) + else: + params = {"handler": self._conn_handler.get(port_id), + "port_id": port_id} + command = self.RpcCmdData("get_stream_stats", params) + return self._handle_get_stream_stats_response(command, self.transmit(command.method, command.params)) + + # ----- internal methods ----- # + def transmit(self, method_name, params={}): return self.tx_link.transmit(method_name, params) + def transmit_batch(self, batch_list): + return self.tx_link.transmit_batch(batch_list) + + @staticmethod + def _object_decoder(obj_type, obj_data): + if obj_type == "global": + return CGlobalStats(**obj_data) + elif obj_type == "port": + return CPortStats(**obj_data) + elif obj_type == "stream": + return CStreamStats(**obj_data) + else: + # Do not serialize the data into class + return obj_data + + @staticmethod + def default_success_test(result_obj): + if result_obj.success: + return True + else: + return False + + # ----- handler internal methods ----- # + def _handle_acquire_response(self, request, response): + if response.success: + self._conn_handler[request.get("port_id")] = response.data + + def _handle_release_response(self, request, response): + if response.success: + del self._conn_handler[request.get("port_id")] + def _handle_start_traffic_response(self, request, response): + if response.success: + self._active_ports.add(request.get("port_id")) + + def _handle_stop_traffic_response(self, request, response): + if response.success: + self._active_ports.remove(request.get("port_id")) + + def _handle_get_global_stats_response(self, request, response): + if response.success: + return CGlobalStats(**response.success) + else: + return False + + def _handle_get_port_stats_response(self, request, response): + if response.success: + return CPortStats(**response.success) + else: + return False + + def _handle_get_stream_stats_response(self, request, response): + if response.success: + return CStreamStats(**response.success) + else: + return False + + def _is_ports_valid(self, port_id): + if isinstance(port_id, list) or isinstance(port_id, set): + # check each item of the sequence + return all([self._is_ports_valid(port) + for port in port_id]) + elif (isinstance(port_id, int)) and (port_id > 0) and (port_id <= self.get_port_count()): + return True + else: + return False + + def _process_batch_result(self, req_list, resp_list, handler_func=None, success_test=default_success_test): + for i, response in enumerate(resp_list): + # testing each result with success test so that a conclusion report could be deployed in future. + if success_test(response): + # run handler method with its params + handler_func(req_list[i], response) + else: + continue # TODO: mark in this case somehow the bad result + + # ------ private classes ------ # class CTxLink(object): """describes the connectivity of the stateless client method""" def __init__(self, server="localhost", port=5050, virtual=False): @@ -33,18 +325,170 @@ class CTRexStatelessClient(object): if not self.virtual: self.rpc_link.connect() - def transmit(self, method_name, params = {}): + def transmit(self, method_name, params={}): if self.virtual: - print "Transmitting virtually over tcp://{server}:{port}".format( - server=self.server, - port=self.port) - id, msg = self.rpc_link.create_jsonrpc_v2(method_name, params) + self._prompt_virtual_tx_msg() + _, msg = self.rpc_link.create_jsonrpc_v2(method_name, params) print msg return else: return self.rpc_link.invoke_rpc_method(method_name, params) + def transmit_batch(self, batch_list): + if self.virtual: + self._prompt_virtual_tx_msg() + print [msg + for _, msg in [self.rpc_link.create_jsonrpc_v2(command.method, command.params) + for command in batch_list]] + else: + batch = self.rpc_link.create_batch() + for command in batch_list: + batch.add(command.method, command.params) + # invoke the batch + return batch.invoke() + + def _prompt_virtual_tx_msg(self): + print "Transmitting virtually over tcp://{server}:{port}".format(server=self.server, + port=self.port) + + +class CStream(object): + """docstring for CStream""" + DEFAULTS = {"rx_stats": CRxStats, + "mode": CTxMode, + "isg": 5.0, + "next_stream": -1, + "self_start": True, + "enabled": True} + + def __init__(self, **kwargs): + super(CStream, self).__init__() + for k, v in kwargs.items(): + setattr(self, k, v) + # set default values to unset attributes, according to DEFAULTS dict + set_keys = set(kwargs.keys()) + keys_to_set = [x + for x in self.DEFAULTS + if x not in set_keys] + for key in keys_to_set: + default = self.DEFAULTS.get(key) + if type(default) == type: + setattr(self, key, default()) + else: + setattr(self, key, default) + + @property + def packet(self): + return self._packet + + @packet.setter + def packet(self, packet_obj): + assert isinstance(packet_obj, CTRexPktBuilder) + self._packet = packet_obj + + @property + def enabled(self): + return self._enabled + + @enabled.setter + def enabled(self, bool_value): + self._enabled = bool(bool_value) + + @property + def self_start(self): + return self._self_start + + @self_start.setter + def self_start(self, bool_value): + self._self_start = bool(bool_value) + + @property + def next_stream(self): + return self._next_stream + + @next_stream.setter + def next_stream(self, value): + self._next_stream = int(value) + + def dump(self): + pass + return {"enabled": self.enabled, + "self_start": self.self_start, + "isg": self.isg, + "next_stream": self.next_stream, + "packet": self.packet.dump_pkt(), + "mode": self.mode.dump(), + "vm": self.packet.get_vm_data(), + "rx_stats": self.rx_stats.dump()} + +class CRxStats(object): + + def __init__(self, enabled=False, seq_enabled=False, latency_enabled=False): + self._rx_dict = {"enabled": enabled, + "seq_enabled": seq_enabled, + "latency_enabled": latency_enabled} + + @property + def enabled(self): + return self._rx_dict.get("enabled") + + @enabled.setter + def enabled(self, bool_value): + self._rx_dict['enabled'] = bool(bool_value) + + @property + def seq_enabled(self): + return self._rx_dict.get("seq_enabled") + + @seq_enabled.setter + def seq_enabled(self, bool_value): + self._rx_dict['seq_enabled'] = bool(bool_value) + + @property + def latency_enabled(self): + return self._rx_dict.get("latency_enabled") + + @latency_enabled.setter + def latency_enabled(self, bool_value): + self._rx_dict['latency_enabled'] = bool(bool_value) + + def dump(self): + return {k: v + for k, v in self._rx_dict.items() + if v + } + + +class CTxMode(object): + """docstring for CTxMode""" + def __init__(self, tx_mode, pps): + super(CTxMode, self).__init__() + if tx_mode not in ["continuous", "single_burst", "multi_burst"]: + raise ValueError("Unknown TX mode ('{0}')has been initialized.".format(tx_mode)) + self._tx_mode = tx_mode + self._fields = {'pps': float(pps)} + if tx_mode == "single_burst": + self._fields['total_pkts'] = 0 + elif tx_mode == "multi_burst": + self._fields['pkts_per_burst'] = 0 + self._fields['ibg'] = 0.0 + self._fields['count'] = 0 + else: + pass + + def set_tx_mode_attr(self, attr, val): + if attr in self._fields: + self._fields[attr] = type(self._fields.get(attr))(val) + else: + raise ValueError("The provided attribute ('{0}') is not a legal attribute in selected TX mode ('{1}')". + format(attr, self._tx_mode)) + def dump(self): + dump = {"type": self._tx_mode} + dump.update({k: v + for k, v in self._fields.items() + }) + return dump if __name__ == "__main__": diff --git a/scripts/automation/trex_control_plane/client_utils/outer_packages.py b/scripts/automation/trex_control_plane/client_utils/external_packages.py index a6c9a2eb..4b10609b 100644..100755 --- a/scripts/automation/trex_control_plane/client_utils/outer_packages.py +++ b/scripts/automation/trex_control_plane/client_utils/external_packages.py @@ -1,7 +1,6 @@ #!/router/bin/python import sys -import site import os CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) @@ -12,7 +11,6 @@ CLIENT_UTILS_MODULES = ['zmq', 'dpkt-1.8.6' ] - def import_client_utils_modules(): # must be in a higher priority sys.path.insert(0, PATH_TO_PYTHON_LIB) @@ -25,7 +23,7 @@ def import_module_list(modules_list): for p in modules_list: full_path = os.path.join(PATH_TO_PYTHON_LIB, p) fix_path = os.path.normcase(full_path) - site.addsitedir(full_path) + sys.path.insert(1, full_path) import_client_utils_modules() diff --git a/scripts/automation/trex_control_plane/client_utils/general_utils.py b/scripts/automation/trex_control_plane/client_utils/general_utils.py index b5912628..5488b9dd 100755 --- a/scripts/automation/trex_control_plane/client_utils/general_utils.py +++ b/scripts/automation/trex_control_plane/client_utils/general_utils.py @@ -37,7 +37,7 @@ def find_path_to_pardir (pardir, base_path = os.getcwd() ): """ Finds the absolute path for some parent dir `pardir`, starting from base_path - The request is only valid if the stop intitiator is the same client as the T-Rex run intitiator. + The request is only valid if the stop initiator is the same client as the TRex run initiator. :parameters: pardir : str diff --git a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py index db52d633..8c8987b6 100644..100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -1,6 +1,6 @@ #!/router/bin/python -import outer_packages +import external_packages import zmq import json import general_utils diff --git a/scripts/automation/trex_control_plane/client_utils/packet_builder.py b/scripts/automation/trex_control_plane/client_utils/packet_builder.py index fc34d931..c687126b 100644..100755 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -1,7 +1,6 @@ #!/router/bin/python - -import outer_packages +import external_packages import dpkt import socket import binascii @@ -10,6 +9,8 @@ import random import string import struct import re +from abc import ABCMeta, abstractmethod +from collections import namedtuple class CTRexPktBuilder(object): @@ -30,7 +31,7 @@ class CTRexPktBuilder(object): self._pkt_by_hdr = {} self._pkt_top_layer = None self._max_pkt_size = max_pkt_size - self.payload_generator = CTRexPktBuilder.CTRexPayloadGen(self._packet, self._max_pkt_size) + self.payload_gen = CTRexPktBuilder.CTRexPayloadGen(self._packet, self._max_pkt_size) self.vm = CTRexPktBuilder.CTRexVM() def add_pkt_layer(self, layer_name, pkt_layer): @@ -63,6 +64,38 @@ class CTRexPktBuilder(object): return def set_ip_layer_addr(self, layer_name, attr, ip_addr, ip_type="ipv4"): + """ + This method sets the IP address fields of an IP header (source or destination, for both IPv4 and IPv6) + using a human readable addressing representation. + + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l3_ip", etc. + + attr: str + a string representation of the sub-field to be set: + + + "src" for source + + "dst" for destination + + ip_addr: str + a string representation of the IP address to be set. + Example: "10.0.0.1" for IPv4, or "5001::DB8:1:3333:1:1" for IPv6 + + ip_type : str + a string representation of the IP version to be set: + + + "ipv4" for IPv4 + + "ipv6" for IPv6 + + Default: **ipv4** + + :raises: + + :exc:`ValueError`, in case the desired layer_name is not an IP layer + + :exc:`KeyError`, in case the desired layer_name does not exists. + + """ try: layer = self._pkt_by_hdr[layer_name.lower()] if not (isinstance(layer, dpkt.ip.IP) or isinstance(layer, dpkt.ip6.IP6)): @@ -74,9 +107,55 @@ class CTRexPktBuilder(object): raise KeyError("Specified layer '{0}' doesn't exist on packet.".format(layer_name)) def set_ipv6_layer_addr(self, layer_name, attr, ip_addr): + """ + This method sets the IPv6 address fields of an IP header (source or destination) + + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l3_ip", etc. + + attr: str + a string representation of the sub-field to be set: + + + "src" for source + + "dst" for destination + + ip_addr: str + a string representation of the IP address to be set. + Example: "5001::DB8:1:3333:1:1" + + :raises: + + :exc:`ValueError`, in case the desired layer_name is not an IPv6 layer + + :exc:`KeyError`, in case the desired layer_name does not exists. + + """ self.set_ip_layer_addr(layer_name, attr, ip_addr, ip_type="ipv6") def set_eth_layer_addr(self, layer_name, attr, mac_addr): + """ + This method sets the ethernet address fields of an Ethernet header (source or destination) + using a human readable addressing representation. + + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l2", etc. + + attr: str + a string representation of the sub-field to be set: + + "src" for source + + "dst" for destination + + mac_addr: str + a string representation of the MAC address to be set. + Example: "00:de:34:ef:2e:f4". + + :raises: + + :exc:`ValueError`, in case the desired layer_name is not an Ethernet layer + + :exc:`KeyError`, in case the desired layer_name does not exists. + + """ try: layer = self._pkt_by_hdr[layer_name.lower()] if not isinstance(layer, dpkt.ethernet.Ethernet): @@ -135,6 +214,30 @@ class CTRexPktBuilder(object): raise KeyError("Specified layer '{0}' doesn't exist on packet.".format(layer_name)) def set_layer_bit_attr(self, layer_name, attr, val): + """ + This method enables the user to set the value of a field smaller that 1 Byte in size. + This method isn't used to set full-sized fields value (>= 1 byte). + Use :func:`packet_builder.CTRexPktBuilder.set_layer_attr` instead. + + :parameters: + layer_name: str + a string representing the name of the layer. + Example: "l2", "l4_tcp", etc. + + attr : str + a string representing the attribute to be set on desired layer + + val : int + value of attribute. + This value will be set "ontop" of the existing value using bitwise "OR" operation. + + .. tip:: It is very useful to use dpkt constants to define the values of these fields. + + :raises: + + :exc:`KeyError`, in case of missing layer (the desired layer isn't part of packet) + + :exc:`ValueError`, in case invalid attribute to the specified layer. + + """ return self.set_layer_attr(layer_name, attr, val, True) def set_pkt_payload(self, payload): @@ -238,17 +341,88 @@ class CTRexPktBuilder(object): return copy.copy(layer) if layer else None # VM access methods - def set_vm_ip_range(self, ip_start, ip_end, ip_type="ipv4"): - pass - - def set_vm_range_type(self, ip_type): - pass + def set_vm_ip_range(self, ip_layer_name, ip_field, + ip_init, ip_start, ip_end, add_value, + operation, is_big_endian=False, val_size=4, + ip_type="ipv4", add_checksum_inst=True): + if ip_field not in ["src", "dst"]: + raise ValueError("set_vm_ip_range only available for source ('src') or destination ('dst') ip addresses") + # set differences between IPv4 and IPv6 + if ip_type == "ipv4": + ip_class = dpkt.ip.IP + ip_addr_size = val_size if val_size <= 4 else 4 + elif ip_type == "ipv6": + ip_class = dpkt.ip6.IP6 + ip_addr_size = val_size if val_size <= 8 else 4 + else: + raise CTRexPktBuilder.IPAddressError() - def set_vm_core_mask(self, ip_type): - pass + self._verify_layer_prop(ip_layer_name, ip_class) + trim_size = ip_addr_size*2 + init_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_init, ip_type))[-trim_size:], 16) + start_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_start, ip_type))[-trim_size:], 16) + end_val = int(binascii.hexlify(CTRexPktBuilder._decode_ip_addr(ip_end, ip_type))[-trim_size:], 16) + # All validations are done, start adding VM instructions + flow_var_name = "{layer}__{field}".format(layer=ip_layer_name, field=ip_field) + hdr_offset, field_abs_offset = self._calc_offset(ip_layer_name, ip_field, ip_addr_size) + self.vm.add_flow_man_inst(flow_var_name, size=ip_addr_size, operation=operation, + init_value=init_val, + min_value=start_val, + max_value=end_val) + self.vm.add_write_flow_inst(flow_var_name, field_abs_offset) + self.vm.set_vm_off_inst_field(flow_var_name, "add_value", add_value) + self.vm.set_vm_off_inst_field(flow_var_name, "is_big_endian", is_big_endian) + if ip_type == "ipv4" and add_checksum_inst: + self.vm.add_fix_checksum_inst(self._pkt_by_hdr.get(ip_layer_name), hdr_offset) + + def set_vm_eth_range(self, eth_layer_name, eth_field, + mac_init, mac_start, mac_end, add_value, + operation, val_size=4, is_big_endian=False): + if eth_field not in ["src", "dst"]: + raise ValueError("set_vm_eth_range only available for source ('src') or destination ('dst') eth addresses") + self._verify_layer_prop(eth_layer_name, dpkt.ethernet.Ethernet) + eth_addr_size = val_size if val_size <= 4 else 4 + trim_size = eth_addr_size*2 + init_val = int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_init))[-trim_size:], 16) + start_val = int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_start))[-trim_size:], 16) + end_val = int(binascii.hexlify(CTRexPktBuilder._decode_mac_addr(mac_end))[-trim_size:], 16) + # All validations are done, start adding VM instructions + flow_var_name = "{layer}__{field}".format(layer=eth_layer_name, field=eth_field) + hdr_offset, field_abs_offset = self._calc_offset(eth_layer_name, eth_field, eth_addr_size) + self.vm.add_flow_man_inst(flow_var_name, size=8, operation=operation, + init_value=init_val, + min_value=start_val, + max_value=end_val) + self.vm.add_write_flow_inst(flow_var_name, field_abs_offset) + self.vm.set_vm_off_inst_field(flow_var_name, "add_value", add_value) + self.vm.set_vm_off_inst_field(flow_var_name, "is_big_endian", is_big_endian) + + def set_vm_custom_range(self, layer_name, hdr_field, + init_val, start_val, end_val, add_val, val_size, + operation, is_big_endian=False, range_name="", + add_checksum_inst=True): + # verify input validity for init/start/end values + for val in [init_val, start_val, end_val]: + if not isinstance(val, int): + raise ValueError("init/start/end values are expected integers, but received type '{0}'". + format(type(val))) + self._verify_layer_prop(layer_name=layer_name, field_name=hdr_field) + if not range_name: + range_name = "{layer}__{field}".format(layer=layer_name, field=hdr_field) + trim_size = val_size*2 + hdr_offset, field_abs_offset = self._calc_offset(layer_name, hdr_field, val_size) + self.vm.add_flow_man_inst(range_name, size=val_size, operation=operation, + init_value=init_val, + min_value=start_val, + max_value=end_val) + self.vm.add_write_flow_inst(range_name, field_abs_offset) + self.vm.set_vm_off_inst_field(range_name, "add_value", add_val) + self.vm.set_vm_off_inst_field(range_name, "is_big_endian", is_big_endian) + if isinstance(self._pkt_by_hdr.get(layer_name), dpkt.ip.IP) and add_checksum_inst: + self.vm.add_fix_checksum_inst(self._pkt_by_hdr.get(layer_name), hdr_offset) def get_vm_data(self): - pass + return self.vm.dump() def dump_pkt(self): """ @@ -302,9 +476,7 @@ class CTRexPktBuilder(object): except IOError: raise IOError(2, "The provided path could not be accessed") - - # ----- useful shortcut methods ----- # - def gen_dns_packet(self): + def export_pkt(self, file_path, link_pcap=False, pcap_name=None, pcap_ts=None): pass # ----- internal methods ----- # @@ -342,6 +514,41 @@ class CTRexPktBuilder(object): if self._pkt_by_hdr[layer] is layer_obj: return layer + def _calc_offset(self, layer_name, hdr_field, hdr_field_size): + pkt_header = self._pkt_by_hdr.get(layer_name) + hdr_offset = len(self._packet) - len(pkt_header) + inner_hdr_offsets = [] + for field in pkt_header.__hdr__: + if field[0] == hdr_field: + field_size = struct.calcsize(field[1]) + if field_size == hdr_field_size: + break + elif field_size < hdr_field_size: + raise CTRexPktBuilder.PacketLayerError(layer_name, + "The specified field '{0}' size is smaller than given range" + " size ('{1}')".format(hdr_field, hdr_field_size)) + else: + inner_hdr_offsets.append(field_size - hdr_field_size) + break + else: + inner_hdr_offsets.append(struct.calcsize(field[1])) + return hdr_offset, hdr_offset + sum(inner_hdr_offsets) + + def _verify_layer_prop(self, layer_name, layer_type=None, field_name=None): + if layer_name not in self._pkt_by_hdr: + raise CTRexPktBuilder.PacketLayerError(layer_name) + pkt_layer = self._pkt_by_hdr.get(layer_name) + if layer_type: + # check for layer type + if not isinstance(pkt_layer, layer_type): + raise CTRexPktBuilder.PacketLayerTypeError(layer_name, type(pkt_layer), layer_type) + if field_name and not hasattr(pkt_layer, field_name): + # check if field exists on certain header + raise CTRexPktBuilder.PacketLayerError(layer_name, "The specified field '{0}' does not exists on " + "given packet layer ('{1}')".format(field_name, + layer_name)) + return + @staticmethod def _decode_mac_addr(mac_addr): """ @@ -449,6 +656,8 @@ class CTRexPktBuilder(object): This class defines the TRex VM which represents how TRex will regenerate packets. The packets will be regenerated based on the built packet containing this class. """ + InstStore = namedtuple('InstStore', ['type', 'inst']) + def __init__(self): """ Instantiate a CTRexVM object @@ -458,8 +667,10 @@ class CTRexPktBuilder(object): """ super(CTRexPktBuilder.CTRexVM, self).__init__() self.vm_variables = {} + self._inst_by_offset = {} # this data structure holds only offset-related instructions, ordered in tuples + self._off_inst_by_name = {} - def set_vm_var_field(self, var_name, field_name, val): + def set_vm_var_field(self, var_name, field_name, val, offset_inst=False): """ Set VM variable field. Only existing variables are allowed to be changed. @@ -477,9 +688,15 @@ class CTRexPktBuilder(object): + :exc:`CTRexPktBuilder.VMVarValueError`, in case val isn't one of allowed options of field_name. """ - return self.vm_variables[var_name].set_field(field_name, val) + if offset_inst: + return self._off_inst_by_name[var_name].inst.set_field(field_name, val) + else: + return self.vm_variables[var_name].set_field(field_name, val) + + def set_vm_off_inst_field(self, var_name, field_name, val): + return self.set_vm_var_field(var_name, field_name, val, True) - def add_flow_man_simple(self, name, **kwargs): + def add_flow_man_inst(self, name, **kwargs): """ Adds a new flow manipulation object to the VM instance. @@ -488,7 +705,7 @@ class CTRexPktBuilder(object): name of the manipulation, must be distinct. Example: 'source_ip_change' - **kwargs : dict + **kwargs** : dict optional, set flow_man fields on initialization (key = field_name, val = field_val). Must be used with legit fields, see :func:`CTRexPktBuilder.CTRexVM.CTRexVMVariable.set_field`. @@ -500,14 +717,40 @@ class CTRexPktBuilder(object): + Exceptions from :func:`CTRexPktBuilder.CTRexVM.CTRexVMVariable.set_field` method. Will rise when VM variables were misconfiguration. """ - if name not in self.vm_variables.keys(): - self.vm_variables[name] = self.CTRexVMVariable(name) - # try configuring VM var attributes + if name not in self.vm_variables: + self.vm_variables[name] = self.CTRexVMFlowVariable(name) + # try configuring VM instruction attributes for (field, value) in kwargs.items(): self.vm_variables[name].set_field(field, value) else: raise CTRexPktBuilder.VMVarNameExistsError(name) + def add_fix_checksum_inst(self, linked_ipv4_obj, offset_to_obj=14, name=None): + # check if specified linked_ipv4_obj is indeed an ipv4 object + if not (isinstance(linked_ipv4_obj, dpkt.ip.IP)): + raise ValueError("The provided layer object is not of IPv4.") + if not name: + name = "checksum_{off}".format(off=offset_to_obj) # name will override previous checksum inst, OK + new_checksum_inst = self.CTRexVMChecksumInst(name, offset_to_obj) + # store the checksum inst in the end of the IP header (20 Bytes long) + inst = self.InstStore('checksum', new_checksum_inst) + self._inst_by_offset[offset_to_obj + 20] = inst + self._off_inst_by_name[name] = inst + + def add_write_flow_inst(self, name, pkt_offset, **kwargs): + if name not in self.vm_variables: + raise KeyError("Trying to add write_flow_var instruction to a not-exists VM flow variable ('{0}')". + format(name)) + else: + new_write_inst = self.CTRexVMWrtFlowVarInst(name, pkt_offset) + # try configuring VM instruction attributes + for (field, value) in kwargs.items(): + new_write_inst.set_field(field, value) + # add the instruction to the date-structure + inst = self.InstStore('write', new_write_inst) + self._inst_by_offset[pkt_offset] = inst + self._off_inst_by_name[name] = inst + def load_flow_man(self, flow_obj): """ Loads an outer VM variable (instruction) into current VM. @@ -521,7 +764,7 @@ class CTRexPktBuilder(object): list holds variables data of VM """ - assert isinstance(flow_obj, CTRexPktBuilder.CTRexVM.CTRexVMVariable) + assert isinstance(flow_obj, CTRexPktBuilder.CTRexVM.CTRexVMFlowVariable) if flow_obj.name not in self.vm_variables.keys(): self.vm_variables[flow_obj.name] = flow_obj else: @@ -529,7 +772,7 @@ class CTRexPktBuilder(object): def dump(self): """ - dumps a VM variables (instructions) into an list data structure. + dumps a VM variables (instructions) into a list data structure. :parameters: None @@ -538,14 +781,42 @@ class CTRexPktBuilder(object): list holds variables data of VM """ - return [var.dump() - for key, var in self.vm_variables.items()] + # at first, dump all CTRexVMFlowVariable instructions + ret_val = [var.dump() + for key, var in self.vm_variables.items()] + # then, dump all the CTRexVMWrtFlowVarInst and CTRexVMChecksumInst instructions + ret_val += [self._inst_by_offset.get(key).inst.dump() + for key in sorted(self._inst_by_offset)] + return ret_val + + class CVMAbstractInstruction(object): + __metaclass__ = ABCMeta + + def __init__(self, name): + """ + Instantiate a CTRexVMVariable object + + :parameters: + name : str + a string representing the name of the VM variable. + """ + super(CTRexPktBuilder.CTRexVM.CVMAbstractInstruction, self).__init__() + self.name = name + + def set_field(self, field_name, val): + if not hasattr(self, field_name): + raise CTRexPktBuilder.VMFieldNameError(field_name) + setattr(self, field_name, val) + + @abstractmethod + def dump(self): + pass - class CTRexVMVariable(object): + class CTRexVMFlowVariable(CVMAbstractInstruction): """ This class defines a single VM variable to be used as part of CTRexVar object. """ - VALID_SIZE = [1, 2, 4, 8] + VALID_SIZE = [1, 2, 4, 8] # size in Bytes VALID_OPERATION = ["inc", "dec", "random"] def __init__(self, name): @@ -556,12 +827,12 @@ class CTRexPktBuilder(object): name : str a string representing the name of the VM variable. """ - super(CTRexPktBuilder.CTRexVM.CTRexVMVariable, self).__init__() - self.name = name + super(CTRexPktBuilder.CTRexVM.CTRexVMFlowVariable, self).__init__(name) + # self.name = name self.size = 4 self.big_endian = True self.operation = "inc" - self.split_by_core = False + # self.split_by_core = False self.init_value = 1 self.min_value = self.init_value self.max_value = self.init_value @@ -586,32 +857,25 @@ class CTRexPktBuilder(object): """ if not hasattr(self, field_name): - raise CTRexPktBuilder.VMVarNameError(field_name) + raise CTRexPktBuilder.VMFieldNameError(field_name) elif field_name == "size": if type(val) != int: - raise CTRexPktBuilder.VMVarFieldTypeError("size", int) + raise CTRexPktBuilder.VMFieldTypeError("size", int) elif val not in self.VALID_SIZE: - raise CTRexPktBuilder.VMVarValueError("size", self.VALID_SIZE) - elif field_name == "init_value": + raise CTRexPktBuilder.VMFieldValueError("size", self.VALID_SIZE) + elif field_name in ["init_value", "min_value", "max_value"]: if type(val) != int: - raise CTRexPktBuilder.VMVarFieldTypeError("init_value", int) + raise CTRexPktBuilder.VMFieldTypeError(field_name, int) elif field_name == "operation": if type(val) != str: - raise CTRexPktBuilder.VMVarFieldTypeError("operation", str) + raise CTRexPktBuilder.VMFieldTypeError("operation", str) elif val not in self.VALID_OPERATION: - raise CTRexPktBuilder.VMVarValueError("operation", self.VALID_OPERATION) - elif field_name == "split_by_core": - val = bool(val) + raise CTRexPktBuilder.VMFieldValueError("operation", self.VALID_OPERATION) + # elif field_name == "split_by_core": + # val = bool(val) # update field value on success setattr(self, field_name, val) - def is_valid(self): - if self.size not in self.VALID_SIZE: - return False - if self.type not in self.VALID_OPERATION: - return False - return True - def dump(self): """ dumps a variable fields in a dictionary data structure. @@ -623,15 +887,116 @@ class CTRexPktBuilder(object): dictionary holds variable data of VM variable """ - return {"ins_name": "flow_man_simple", # VM variable dump always refers to manipulate instruction. - "flow_variable_name": self.name, - "object_size": self.size, - # "big_endian": self.big_endian, - "Operation": self.operation, - "split_by_core": self.split_by_core, - "init_value": self.init_value, - "min_value": self.min_value, - "max_value": self.max_value} + return {"ins_name": "flow_var", # VM variable dump always refers to manipulate instruction. + "name": self.name, + "size": self.size, + "op": self.operation, + # "split_by_core": self.split_by_core, + "init_value": str(self.init_value), + "min_value": str(self.min_value), + "max_value": str(self.max_value)} + + class CTRexVMChecksumInst(CVMAbstractInstruction): + + def __init__(self, name, offset): + """ + Instantiate a CTRexVMChecksumInst object + + :parameters: + name : str + a string representing the name of the VM variable. + """ + super(CTRexPktBuilder.CTRexVM.CTRexVMChecksumInst, self).__init__(name) + self.pkt_offset = offset + + def dump(self): + return {"type": "fix_checksum_ipv4", + "pkt_offset": int(self.pkt_offset)} + + class CTRexVMWrtFlowVarInst(CVMAbstractInstruction): + + def __init__(self, name, pkt_offset): + """ + Instantiate a CTRexVMWrtFlowVarInst object + + :parameters: + name : str + a string representing the name of the VM variable. + """ + super(CTRexPktBuilder.CTRexVM.CTRexVMWrtFlowVarInst, self).__init__(name) + self.pkt_offset = int(pkt_offset) + self.add_value = 0 + self.is_big_endian = False + + def set_field(self, field_name, val): + if not hasattr(self, field_name): + raise CTRexPktBuilder.VMFieldNameError(field_name) + elif field_name == 'pkt_offset': + raise ValueError("pkt_offset value cannot be changed") + cur_attr_type = type(getattr(self, field_name)) + if cur_attr_type == type(val): + setattr(self, field_name, val) + else: + CTRexPktBuilder.VMFieldTypeError(field_name, cur_attr_type) + + def dump(self): + return {"type": "write_flow_var", + "name": self.name, + "pkt_offset": self.pkt_offset, + "add_value": int(self.add_value), + "is_big_endian": bool(self.is_big_endian) + } + + class CTRexVMChecksumInst(CVMAbstractInstruction): + + def __init__(self, name, offset): + """ + Instantiate a CTRexVMChecksumInst object + + :parameters: + name : str + a string representing the name of the VM variable. + """ + super(CTRexPktBuilder.CTRexVM.CTRexVMChecksumInst, self).__init__(name) + self.pkt_offset = offset + + def dump(self): + return {"type": "fix_checksum_ipv4", + "pkt_offset": int(self.pkt_offset)} + + class CTRexVMWrtFlowVarInst(CVMAbstractInstruction): + + def __init__(self, name, pkt_offset): + """ + Instantiate a CTRexVMWrtFlowVarInst object + + :parameters: + name : str + a string representing the name of the VM variable. + """ + super(CTRexPktBuilder.CTRexVM.CTRexVMWrtFlowVarInst, self).__init__(name) + self.pkt_offset = int(pkt_offset) + self.add_value = 0 + self.is_big_endian = False + + def set_field(self, field_name, val): + if not hasattr(self, field_name): + raise CTRexPktBuilder.VMFieldNameError(field_name) + elif field_name == 'pkt_offset': + raise ValueError("pkt_offset value cannot be changed") + cur_attr_type = type(getattr(self, field_name)) + if cur_attr_type == type(val): + setattr(self, field_name, val) + else: + CTRexPktBuilder.VMFieldTypeError(field_name, cur_attr_type) + + def dump(self): + return {"type": "write_flow_var", + "name": self.name, + "pkt_offset": self.pkt_offset, + "add_value": int(self.add_value), + "is_big_endian": bool(self.is_big_endian) + } class CPacketBuildException(Exception): """ @@ -672,7 +1037,28 @@ class CTRexPktBuilder(object): def __init__(self, message=''): self._default_message = 'Illegal MAC address has been provided.' self.message = message or self._default_message - super(CTRexPktBuilder.MACAddressError, self).__init__(-11, self.message) + super(CTRexPktBuilder.MACAddressError, self).__init__(-12, self.message) + + class PacketLayerError(CPacketBuildException): + """ + This exception is used to indicate an error caused by operation performed on an non-exists layer of the packet. + """ + def __init__(self, name, message=''): + self._default_message = "The given packet layer name ({0}) does not exists.".format(name) + self.message = message or self._default_message + super(CTRexPktBuilder.PacketLayerError, self).__init__(-13, self.message) + + class PacketLayerTypeError(CPacketBuildException): + """ + This exception is used to indicate an error caused by operation performed on an non-exists layer of the packet. + """ + def __init__(self, name, layer_type, ok_type, message=''): + self._default_message = "The type of packet layer {layer_name} is of type {layer_type}, " \ + "and not of the expected {allowed_type}.".format(layer_name=name, + layer_type=layer_type, + allowed_type=ok_type.__name__) + self.message = message or self._default_message + super(CTRexPktBuilder.PacketLayerTypeError, self).__init__(-13, self.message) class VMVarNameExistsError(CPacketBuildException): """ @@ -683,37 +1069,37 @@ class CTRexPktBuilder(object): self.message = message or self._default_message super(CTRexPktBuilder.VMVarNameExistsError, self).__init__(-21, self.message) - class VMVarNameError(CPacketBuildException): + class VMFieldNameError(CPacketBuildException): """ This exception is used to indicate that an undefined VM var field name has been accessed. """ def __init__(self, name, message=''): self._default_message = "The given VM field name ({0}) is not defined and isn't legal.".format(name) self.message = message or self._default_message - super(CTRexPktBuilder.VMVarNameError, self).__init__(-22, self.message) + super(CTRexPktBuilder.VMFieldNameError, self).__init__(-22, self.message) - class VMVarFieldTypeError(CPacketBuildException): + class VMFieldTypeError(CPacketBuildException): """ This exception is used to indicate an illegal value has type has been given to VM variable field. """ def __init__(self, name, ok_type, message=''): - self._default_message = 'The desired value of field {field_name} is of type {field_type}, \ - and not of the allowed {allowed_type}.'.format(field_name=name, - field_type=type(name).__name__, - allowed_type=ok_type.__name__) + self._default_message = "The desired value of field {field_name} is of type {field_type}, " \ + "and not of the allowed {allowed_type}.".format(field_name=name, + field_type=type(name).__name__, + allowed_type=ok_type.__name__) self.message = message or self._default_message - super(CTRexPktBuilder.VMVarFieldTypeError, self).__init__(-31, self.message) + super(CTRexPktBuilder.VMFieldTypeError, self).__init__(-31, self.message) - class VMVarValueError(CPacketBuildException): + class VMFieldValueError(CPacketBuildException): """ This exception is used to indicate an error an illegal value has been assigned to VM variable field. """ def __init__(self, name, ok_opts, message=''): - self._default_message = 'The desired value of field {field_name} is illegal.\n \ - The only allowed options are: {allowed_opts}.'.format(field_name=name, - allowed_opts=ok_opts) + self._default_message = "The desired value of field {field_name} is illegal.\n" \ + "The only allowed options are: {allowed_opts}.".format(field_name=name, + allowed_opts=ok_opts) self.message = message or self._default_message - super(CTRexPktBuilder.VMVarValueError, self).__init__(-32, self.message) + super(CTRexPktBuilder.VMFieldValueError, self).__init__(-32, self.message) if __name__ == "__main__": diff --git a/scripts/automation/trex_control_plane/client_utils/trex_yaml_gen.py b/scripts/automation/trex_control_plane/client_utils/trex_yaml_gen.py index 755674ea..c26fef29 100755 --- a/scripts/automation/trex_control_plane/client_utils/trex_yaml_gen.py +++ b/scripts/automation/trex_control_plane/client_utils/trex_yaml_gen.py @@ -7,7 +7,7 @@ import os class CTRexYaml(object):
"""
- This class functions as a YAML generator according to T-Rex YAML format.
+ This class functions as a YAML generator according to TRex YAML format.
CTRexYaml is compatible with both Python 2 and Python 3.
"""
@@ -38,7 +38,7 @@ class CTRexYaml(object): :parameters:
trex_files_path : str
- a path (on T-Rex server side) for the pcap files using which T-Rex can access it.
+ a path (on TRex server side) for the pcap files using which TRex can access it.
"""
self.yaml_obj = list(CTRexYaml.YAML_TEMPLATE)
@@ -114,7 +114,7 @@ class CTRexYaml(object): :parameters:
None
- :reaturn:
+ :return:
None
"""
@@ -193,7 +193,7 @@ class CTRexYaml(object): """
Returns a list of all files related to the YAML object, including the YAML filename itself.
- .. tip:: This method is especially useful for listing all the files that should be pushed to T-Rex server as part of the same yaml selection.
+ .. tip:: This method is especially useful for listing all the files that should be pushed to TRex server as part of the same yaml selection.
:parameters:
None
diff --git a/scripts/automation/trex_control_plane/common/outer_packages.py b/scripts/automation/trex_control_plane/common/outer_packages.py deleted file mode 100644 index 4d0afd1a..00000000 --- a/scripts/automation/trex_control_plane/common/outer_packages.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/router/bin/python - -import sys -import site -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')) - -COMMON_UTILS_MODULES = ['enum34-1.0.4' - ] - - -def import_common_utils_modules(): - # must be in a higher priority - sys.path.insert(0, PATH_TO_PYTHON_LIB) - sys.path.append(ROOT_PATH) - import_module_list(COMMON_UTILS_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) - site.addsitedir(full_path) - -import_common_utils_modules() - diff --git a/scripts/automation/trex_control_plane/common/trex_exceptions.py b/scripts/automation/trex_control_plane/common/trex_exceptions.py index 1353fd00..0de38411 100755 --- a/scripts/automation/trex_control_plane/common/trex_exceptions.py +++ b/scripts/automation/trex_control_plane/common/trex_exceptions.py @@ -27,7 +27,7 @@ class RPCError(Exception): class TRexException(RPCError): """ - This is the most general T-Rex exception. + This is the most general TRex exception. All exceptions inherits from this class has an error code and a default message which describes the most common use case of the error. @@ -35,55 +35,55 @@ class TRexException(RPCError): """ code = -10 - _default_message = 'T-Rex encountered an unexpected error. please contact T-Rex dev team.' + _default_message = 'TRex encountered an unexpected error. please contact TRex dev team.' # api_name = 'TRex' class TRexError(TRexException): """ - This is the most general T-Rex exception. + This is the most general TRex exception. This exception isn't used by default and will only when an unrelated to ProtocolError will occur, and it can't be resolved to any of the deriviate exceptions. """ code = -11 - _default_message = 'T-Rex run failed due to wrong input parameters, or due to reachability issues.' + _default_message = 'TRex run failed due to wrong input parameters, or due to reachability issues.' class TRexWarning(TRexException): - """ Indicates a warning from T-Rex server. When this exception raises it normally used to indicate required data isn't ready yet """ + """ Indicates a warning from TRex server. When this exception raises it normally used to indicate required data isn't ready yet """ code = -12 - _default_message = 'T-Rex is starting (data is not available yet).' + _default_message = 'TRex is starting (data is not available yet).' class TRexRequestDenied(TRexException): """ Indicates the desired reques was denied by the server """ code = -33 - _default_message = 'T-Rex desired request denied because the requested resource is already taken. Try again once T-Rex is back in IDLE state.' + _default_message = 'TRex desired request denied because the requested resource is already taken. Try again once TRex is back in IDLE state.' class TRexInUseError(TRexException): """ - Indicates that T-Rex is currently in use + Indicates that TRex is currently in use """ code = -13 - _default_message = 'T-Rex is already being used by another user or process. Try again once T-Rex is back in IDLE state.' + _default_message = 'TRex is already being used by another user or process. Try again once TRex is back in IDLE state.' class TRexRunFailedError(TRexException): - """ Indicates that T-Rex has failed due to some reason. This Exception is used when T-Rex process itself terminates due to unknown reason """ + """ Indicates that TRex has failed due to some reason. This Exception is used when TRex process itself terminates due to unknown reason """ code = -14 _default_message = '' class TRexIncompleteRunError(TRexException): """ - Indicates that T-Rex has failed due to some reason. - This Exception is used when T-Rex process itself terminated with error fault or it has been terminated by an external intervention in the OS. + Indicates that TRex has failed due to some reason. + This Exception is used when TRex process itself terminated with error fault or it has been terminated by an external intervention in the OS. """ code = -15 - _default_message = 'T-Rex run was terminated unexpectedly by outer process or by the hosting OS' + _default_message = 'TRex run was terminated unexpectedly by outer process or by the hosting OS' EXCEPTIONS = [TRexException, TRexError, TRexWarning, TRexInUseError, TRexRequestDenied, TRexRunFailedError, TRexIncompleteRunError] class CExceptionHandler(object): """ - CExceptionHandler is responsible for generating T-Rex API related exceptions in client side. + CExceptionHandler is responsible for generating TRex API related exceptions in client side. """ def __init__(self, exceptions): """ @@ -92,7 +92,7 @@ class CExceptionHandler(object): :parameters: exceptions : list - a list of all T-Rex acceptable exception objects. + a list of all TRex acceptable exception objects. default list: - :exc:`trex_exceptions.TRexException` @@ -113,7 +113,7 @@ class CExceptionHandler(object): """ Generates an exception based on a general ProtocolError exception object `err`. - When T-Rex is reserved, no other user can start new T-Rex runs. + When TRex is reserved, no other user can start new TRex runs. :parameters: @@ -122,7 +122,7 @@ class CExceptionHandler(object): a ProtocolError exception raised by :class:`trex_client.CTRexClient` class :return: - A T-Rex exception from the exception list defined in class creation. + A TRex exception from the exception list defined in class creation. If such exception wasn't found, returns a TRexException exception diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py new file mode 100755 index 00000000..b7e768c1 --- /dev/null +++ b/scripts/automation/trex_control_plane/common/trex_stats.py @@ -0,0 +1,60 @@ +#!/router/bin/python +import copy + + +class CTRexStatsManager(object): + + def __init__(self, *args): + for stat_type in args: + # register stat handler for each stats type + setattr(self, stat_type, CTRexStatsManager.CSingleStatsHandler()) + + def __getitem__(self, item): + stats_obj = getattr(self, item) + if stats_obj: + return stats_obj.get_stats() + else: + return None + + class CSingleStatsHandler(object): + + def __init__(self): + self._stats = {} + + def update(self, obj_id, stats_obj): + assert isinstance(stats_obj, CTRexStats) + self._stats[obj_id] = stats_obj + + def get_stats(self, obj_id=None): + if obj_id: + return copy.copy(self._stats.pop(obj_id)) + else: + return copy.copy(self._stats) + + +class CTRexStats(object): + def __init__(self, **kwargs): + for k, v in kwargs.items(): + setattr(self, k, v) + + +class CGlobalStats(CTRexStats): + def __init__(self, **kwargs): + super(CGlobalStats, self).__init__(kwargs) + pass + + +class CPortStats(CTRexStats): + def __init__(self, **kwargs): + super(CPortStats, self).__init__(kwargs) + pass + + +class CStreamStats(CTRexStats): + def __init__(self, **kwargs): + super(CStreamStats, self).__init__(kwargs) + pass + + +if __name__ == "__main__": + pass diff --git a/scripts/automation/trex_control_plane/common/trex_status_e.py b/scripts/automation/trex_control_plane/common/trex_status_e.py index a14901a1..fbfe92af 100755 --- a/scripts/automation/trex_control_plane/common/trex_status_e.py +++ b/scripts/automation/trex_control_plane/common/trex_status_e.py @@ -4,5 +4,5 @@ import outer_packages # import this to overcome doc building import error by sp from enum import Enum
-# define the states in which a T-Rex can hold during its lifetime
+# define the states in which a TRex can hold during its lifetime
TRexStatus = Enum('TRexStatus', 'Idle Starting Running')
diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 9bf92174..e9d8ad84 100644..100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -430,11 +430,11 @@ class TrexConsole(cmd.Cmd): def setParserOptions (): parser = argparse.ArgumentParser(prog="trex_console.py") - parser.add_argument("-s", "--server", help = "T-Rex Server [default is localhost]", + parser.add_argument("-s", "--server", help = "TRex Server [default is localhost]", default = "localhost", type = str) - parser.add_argument("-p", "--port", help = "T-Rex Server Port [default is 5050]\n", + parser.add_argument("-p", "--port", help = "TRex Server Port [default is 5050]\n", default = 5050, type = int) diff --git a/scripts/automation/trex_control_plane/console/trex_root_path.py b/scripts/automation/trex_control_plane/console/trex_root_path.py index de4ec03b..de4ec03b 100644..100755 --- a/scripts/automation/trex_control_plane/console/trex_root_path.py +++ b/scripts/automation/trex_control_plane/console/trex_root_path.py diff --git a/scripts/automation/trex_control_plane/doc/about_trex.rst b/scripts/automation/trex_control_plane/doc/about_trex.rst deleted file mode 100755 index 97cad97d..00000000 --- a/scripts/automation/trex_control_plane/doc/about_trex.rst +++ /dev/null @@ -1,16 +0,0 @@ -=================== -About T-Rex project -=================== - -Full project's official site ----------------------------- - -To learn all about T-Rex project, visit Cisco's internal `official site <http://csi-wiki-01:8080/display/bpsim/Home>`_ - -Even more ---------- - -.. toctree:: - :maxdepth: 2 - - authors
\ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/api/index.rst b/scripts/automation/trex_control_plane/doc/api/index.rst index 8233a634..cfdc6917 100755 --- a/scripts/automation/trex_control_plane/doc/api/index.rst +++ b/scripts/automation/trex_control_plane/doc/api/index.rst @@ -1,9 +1,8 @@ API Reference ============= -The T-Rex API reference section is currently a work in progress. -**T-Rex Modules** +**TRex Modules** .. toctree:: :maxdepth: 4 @@ -11,7 +10,7 @@ The T-Rex API reference section is currently a work in progress. client_code exceptions -**T-Rex JSON Template** +**TRex JSON Template** .. toctree:: :maxdepth: 4 diff --git a/scripts/automation/trex_control_plane/doc/api/json_fields.rst b/scripts/automation/trex_control_plane/doc/api/json_fields.rst index b1a2af7c..9e32d23e 100755 --- a/scripts/automation/trex_control_plane/doc/api/json_fields.rst +++ b/scripts/automation/trex_control_plane/doc/api/json_fields.rst @@ -1,23 +1,23 @@ -T-Rex JSON Template
-===================
+TRex JSON Template
+==================
-Whenever T-Rex is publishing live data, it uses JSON notation to describe the data-object.
+Whenever TRex is publishing live data, it uses JSON notation to describe the data-object.
-Each client may parse it diffrently, however this page will describe the values meaning when published by T-Rex server.
+Each client may parse it differently, however this page will describe the values meaning when published by TRex server.
Main Fields
-----------
-Each T-Rex server-published JSON object contains data divided to main fields under which the actual data lays.
+Each TRex server-published JSON object contains data divided to main fields under which the actual data lays.
These main fields are:
+-----------------------------+----------------------------------------------------+---------------------------+
| Main field | Contains | Comments |
+=============================+====================================================+===========================+
-| :ref:`trex-global-field` | Must-have data on T-Rex run, | |
+| :ref:`trex-global-field` | Must-have data on TRex run, | |
| | mainly regarding Tx/Rx and packet drops | |
+-----------------------------+----------------------------------------------------+---------------------------+
| :ref:`tx-gen-field` | Data indicate the quality of the transmit process. | |
@@ -117,7 +117,7 @@ trex-global field .. _tx-gen-field:
tx-gen field
-~~~~~~~~~~~~~~
+~~~~~~~~~~~~
+-------------------+-------+-----------------------------------------------------------+
| Sub-key | Type | Meaning |
diff --git a/scripts/automation/trex_control_plane/doc/authors.rst b/scripts/automation/trex_control_plane/doc/authors.rst deleted file mode 100755 index 3b85f020..00000000 --- a/scripts/automation/trex_control_plane/doc/authors.rst +++ /dev/null @@ -1,12 +0,0 @@ -======= -Authors -======= - -T-Rex is developed in Cisco Systems Inc. as the next generation traffic generator. - -T-Rex core-team developers are: - - - Hanoch Haim - - Dave Johnson - - Wenxian Li - - Dan Klein
\ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/client_utils.rst b/scripts/automation/trex_control_plane/doc/client_utils.rst index 224dfe19..32728a57 100755 --- a/scripts/automation/trex_control_plane/doc/client_utils.rst +++ b/scripts/automation/trex_control_plane/doc/client_utils.rst @@ -1,14 +1,19 @@ -
-Client Utilities
-================
-
-T-Rex YAML generator
---------------------
-
-.. automodule:: trex_yaml_gen
- :members:
-
-General Utilities
------------------
-.. automodule:: general_utils
+ +Client Utilities +================ + +.. toctree:: + :maxdepth: 2 + + packet_generator/index + +TRex YAML generator +------------------- + +.. automodule:: trex_yaml_gen + :members: + +General Utilities +----------------- +.. automodule:: general_utils :members:
\ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/conf.py b/scripts/automation/trex_control_plane/doc/conf.py index fb9ea83c..46d0435d 100755 --- a/scripts/automation/trex_control_plane/doc/conf.py +++ b/scripts/automation/trex_control_plane/doc/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# T-Rex Control Plain documentation build configuration file, created by +# TRex Control Plain documentation build configuration file, created by # sphinx-quickstart on Tue Jun 2 07:48:10 2015. # # This file is execfile()d with the current directory set to its @@ -54,7 +54,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'T-Rex Control Plain' +project = u'TRex Control Plain' copyright = u'2015, Cisco Systems Inc.' author = u'Dan Klein for Cisco Systems Inc.' @@ -211,7 +211,7 @@ html_static_path = ['_static'] #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'T-RexControlPlaindoc' +htmlhelp_basename = 'TRexControlPlaindoc' # -- Options for LaTeX output --------------------------------------------- @@ -233,7 +233,7 @@ latex_elements = { # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'T-RexControlPlain.tex', u'T-Rex Control Plain Documentation', + (master_doc, 'TRexControlPlain.tex', u'TRex Control Plain Documentation', u'Dan Klein for Cisco Systems Inc', 'manual'), ] @@ -263,7 +263,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 't-rexcontrolplain', u'T-Rex Control Plain Documentation', + (master_doc, 'TRexcontrolplain', u'TRex Control Plain Documentation', [author], 1) ] @@ -277,8 +277,8 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'T-RexControlPlain', u'T-Rex Control Plain Documentation', - author, 'T-RexControlPlain', 'One line description of project.', + (master_doc, 'TRexControlPlain', u'TRex Control Plain Documentation', + author, 'TRexControlPlain', 'One line description of project.', 'Miscellaneous'), ] diff --git a/scripts/automation/trex_control_plane/doc/index.rst b/scripts/automation/trex_control_plane/doc/index.rst index e7a619d8..62fd9975 100755 --- a/scripts/automation/trex_control_plane/doc/index.rst +++ b/scripts/automation/trex_control_plane/doc/index.rst @@ -1,32 +1,23 @@ -.. T-Rex Control Plain documentation master file, created by +.. TRex Control Plain documentation master file, created by sphinx-quickstart on Tue Jun 2 07:48:10 2015. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to T-Rex Control Plain's documentation! -=============================================== +Welcome to TRex Control Plain's documentation! +============================================== -T-Rex is a **realistic traffic generator** that enables you to do get learn more about your under developement devices. +TRex is a **realistic traffic generator** that enables you to do get learn more about your under development devices. -This site covers the Python API of T-Rex control plane, and explains how to utilize it to your needs. +This site covers the Python API of TRex control plane, and explains how to utilize it to your needs. However, since the entire API is JSON-RPC [#f1]_ based, you may want to check out other implementations that could suit you. -To understand the entirely how the API works and how to set up the server side, check out the `API documentation <http://csi-wiki-01:8080/display/bpsim/Documentation>`_ undee the documentation section of T-Rex website. +To understand the entirely how the API works and how to set up the server side, check out the `trex-core Wiki <https://github.com/cisco-system-traffic-generator/trex-core/wiki>`_ under the documentation section of TRex website. **Use the table of contents below or the menu to your left to navigate through the site** -Getting Started -=============== -.. toctree:: - :maxdepth: 2 - - installation - client_utils - usage_examples - API Reference ============= .. toctree:: @@ -34,14 +25,19 @@ API Reference api/index -About T-Rex -=========== +Client Utilities +================ .. toctree:: :maxdepth: 2 - All about T-Rex <about_trex> - license + client_utils +Usage Examples +============== +.. toctree:: + :maxdepth: 2 + + usage_examples Indices and tables diff --git a/scripts/automation/trex_control_plane/doc/installation.rst b/scripts/automation/trex_control_plane/doc/installation.rst deleted file mode 100755 index dda32f56..00000000 --- a/scripts/automation/trex_control_plane/doc/installation.rst +++ /dev/null @@ -1,25 +0,0 @@ -============
-Installation
-============
-
-Prerequisites
--------------
-The T-Rex control plane is based on client-server model that interacts using JSON-RPC.
-
-In order to use the client-side API documented a T-Rex server daemon must be up and listening on the same host and port that the client tries to connect with.
-
-Compatibility
--------------
-Both client and server side were developed for Linux platform.
-The client-side module is also compatible with windows python.
-
-The client side can be used with both Python 2 and Python 3 versions.
-However, the server side was desined to and best fits with Python 2.7.6 and on (all 2.x series, assuming > 2.6.9).
-
-
-Installation manual
--------------------
-
-T-Rex Control Plane is a cross-platform, cross-operatin system APi to control and run T-Rex.
-
-The full, most updated manual (which refers to all programming languages) can be found under the `Automation API documentation <http://csi-wiki-01:8080/display/bpsim/Documentation>`_ .
\ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/json_dictionary.yaml b/scripts/automation/trex_control_plane/doc/json_dictionary.yaml index 853ded65..89535b56 100755 --- a/scripts/automation/trex_control_plane/doc/json_dictionary.yaml +++ b/scripts/automation/trex_control_plane/doc/json_dictionary.yaml @@ -1,6 +1,6 @@ -################################################################ -#### T-Rex JSON Dictionary definitions #### -################################################################ +############################################################### +#### TRex JSON Dictionary definitions #### +############################################################### trex-global : diff --git a/scripts/automation/trex_control_plane/doc/license.rst b/scripts/automation/trex_control_plane/doc/license.rst deleted file mode 100755 index b83dd4b3..00000000 --- a/scripts/automation/trex_control_plane/doc/license.rst +++ /dev/null @@ -1,18 +0,0 @@ -======= -License -======= - - -Copyright 2015 Cisco Systems Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/scripts/automation/trex_control_plane/doc/packet_generator/examples.rst b/scripts/automation/trex_control_plane/doc/packet_generator/examples.rst new file mode 100755 index 00000000..bff1ef7f --- /dev/null +++ b/scripts/automation/trex_control_plane/doc/packet_generator/examples.rst @@ -0,0 +1,5 @@ +
+Packet Builder Usage Examples
+=============================
+
+Here I'll add usage examples, very similar to those I added to RPC document
\ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/packet_generator/export_format.yaml b/scripts/automation/trex_control_plane/doc/packet_generator/export_format.yaml new file mode 100755 index 00000000..9f8c8f7b --- /dev/null +++ b/scripts/automation/trex_control_plane/doc/packet_generator/export_format.yaml @@ -0,0 +1,47 @@ +#################################################### +#### TRex packet export format #### +#################################################### + +# PACKET REP - OPTION #1 +packet: + is_pcap : YES/NO # <1> + binary : [] # <2> + pcap : path/to/pcap/file.pcap # <3> + meta : any metadata wished to # <4> + +# PACKET REP - OPTION #2 +packet: + data : [] / path/to/pcap/file.pcap # <5> + meta : any metadata wished to # <4> + +vm: # <6> + - vm instruction #1 + - vm instruction #2 + ... + - vm instruction #N + + +################################### +#### Comments #### +################################### +# +# <1>: is_pcap is a boolean field that indicates if packet is transferred by pcap referencs +# ('YES') or binary representation ('NO'). +# +# <2>: binary field encodes the packet in binary representation. in a sequence (array) data. +# Each array item is an integer ranging 0-255. +# **LEAVE BLANK IF USING PCAP REFERENCE** +# +# <3>: path to the linked pcap file. Make sure to provide path with reading credentials. +# **LEAVE BLANK IF USING BINARY REP FOR THE PACKET** +# +# <4>: meta data is any JSON formatted data ment to be passed on. +# +# <5>: data field can be both binary representation or pcap file refernce, +# without the need for user's explicit typing. +# The application logic differs between the cases by the object type +# (array/string ending in '.pcap') +# Less configuration, little more confusing, LESS similar to RPC spec +# +# <6>: vm instructions passed in array representation (sequence). +# Each instruction is deifned according to the structures of the supported VM instructions.
\ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/packet_generator/index.rst b/scripts/automation/trex_control_plane/doc/packet_generator/index.rst new file mode 100755 index 00000000..ed1d460d --- /dev/null +++ b/scripts/automation/trex_control_plane/doc/packet_generator/index.rst @@ -0,0 +1,18 @@ + +TRex Packet Builder +------------------- +The TRex Packet Generator is a module designed to generate single-packet and set its ranging options, later to be transmitted using TRex. + +The packet generator module does extensive usage with `dkpt <https://github.com/kbandla/dpkt>`_ python module to create packet headers. + +.. toctree:: + :maxdepth: 4 + + packet_builder_code + +.. toctree:: + :maxdepth: 0 + :titlesonly: + + examples + stream_export
\ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/packet_generator/packet_builder_code.rst b/scripts/automation/trex_control_plane/doc/packet_generator/packet_builder_code.rst new file mode 100755 index 00000000..3a6e8d5f --- /dev/null +++ b/scripts/automation/trex_control_plane/doc/packet_generator/packet_builder_code.rst @@ -0,0 +1,12 @@ +
+CTRexPktBuilder class
+---------------------
+
+.. autoclass:: packet_builder.CTRexPktBuilder
+ :members:
+ :member-order: bysource
+
+Packet Builder Exceptions
+-------------------------
+
+For exceptions documentation see here: :exc:`Packet Builder Exceptions <packet_builder.CTRexPktBuilder.CPacketBuildException>`
\ No newline at end of file diff --git a/scripts/automation/trex_control_plane/doc/packet_generator/stream_export.rst b/scripts/automation/trex_control_plane/doc/packet_generator/stream_export.rst new file mode 100755 index 00000000..eb639f7c --- /dev/null +++ b/scripts/automation/trex_control_plane/doc/packet_generator/stream_export.rst @@ -0,0 +1,29 @@ +
+Stream Export YAML syntax
+=========================
+
+In order to provide a fluent work-flow that utilize the best TRex user's time, an export-import mini language has been created.
+
+This enables a work-flow that supports saving and sharing a built packets and its scenarios, so that other tools
+(such as TRex Console) could use them.
+
+The TRex Packet Builder module supports (using ___ method) the export of built stream according to the format described below.
+
+Guidelines
+----------
+
+1. The YAML file can either contain Byte representation of the packet of refer to a .pcap file that contains it.
+2. The YAML file is similar as much as possible to the `add_stream method <http://trex-tgn.cisco.com/trex/doc/trex_rpc_server_spec.html#_add_stream>`_ of TRex RPC server spec, which defines the raw interaction with TRex server.
+3. Only packet binary data and VM instructions are to be saved. Any meta-data packet builder module used while creating the packet will be stripped out.
+
+Export Format
+-------------
+
+.. literalinclude:: export_format.yaml
+ :lines: 4-
+ :linenos:
+
+Example
+-------
+
+The following files snapshot represents each of the options (Binary/pcap) for the very same HTTP GET request packet.
diff --git a/scripts/automation/trex_control_plane/doc/requirements.rst b/scripts/automation/trex_control_plane/doc/requirements.rst deleted file mode 100755 index e69de29b..00000000 --- a/scripts/automation/trex_control_plane/doc/requirements.rst +++ /dev/null diff --git a/scripts/automation/trex_control_plane/doc/usage_examples.rst b/scripts/automation/trex_control_plane/doc/usage_examples.rst index 7116f28c..ff5c026d 100755 --- a/scripts/automation/trex_control_plane/doc/usage_examples.rst +++ b/scripts/automation/trex_control_plane/doc/usage_examples.rst @@ -6,7 +6,7 @@ Usage Examples Full-featured interactive shell
-------------------------------
-The `client_interactive_example.py` extends and uses the `Cmd <https://docs.python.org/2/library/cmd.html>`_ built in python class to create a Full-featured shell using which one can interact with T-Rex server and get instant results.
+The `client_interactive_example.py` extends and uses the `Cmd <https://docs.python.org/2/library/cmd.html>`_ built in python class to create a Full-featured shell using which one can interact with TRex server and get instant results.
The help menu of this application is:
@@ -14,21 +14,21 @@ The help menu of this application is: usage: client_interactive_example [options]
- Run T-Rex client API demos and scenarios.
+ Run TRex client API demos and scenarios.
optional arguments:
-h, --help show this help message and exit
-v, --version show program's version number and exit
-t HOST, --trex-host HOST
- Specify the hostname or ip to connect with T-Rex
+ Specify the hostname or ip to connect with TRex
server.
-p PORT, --trex-port PORT
- Select port on which the T-Rex server listens. Default
+ Select port on which the TRex server listens. Default
port is 8090.
-m SIZE, --maxhist SIZE
Specify maximum history size saved at client side.
Default size is 100.
- --verbose Switch ON verbose option at T-Rex client. Default is:
+ --verbose Switch ON verbose option at TRex client. Default is:
OFF.
**Code Excerpt**
@@ -51,8 +51,8 @@ The demo takes the user a full circle: 2. exporting the generated packets into .pcap file named `dns_traffic.pcap`.
3. Use the :class:`trex_yaml_gen.CTRexYaml` generator to include that pcap file in the yaml object.
4. Export the YAML object onto a YAML file named `dns_traffic.yaml`
- 5. Push the generated files to T-Rex server.
- 6. Run T-Rex based on the generated (and pushed) files.
+ 5. Push the generated files to TRex server.
+ 6. Run TRex based on the generated (and pushed) files.
**Code Excerpt** [#f1]_
diff --git a/scripts/automation/trex_control_plane/examples/client_interactive_example.py b/scripts/automation/trex_control_plane/examples/client_interactive_example.py index 05028463..d21b2b15 100755 --- a/scripts/automation/trex_control_plane/examples/client_interactive_example.py +++ b/scripts/automation/trex_control_plane/examples/client_interactive_example.py @@ -15,7 +15,7 @@ import errno class InteractiveTRexClient(cmd.Cmd): - intro = termstyle.green("\nInteractive shell to play with Cisco's T-Rex API.\nType help to view available pre-defined scenarios\n(c) All rights reserved.\n") + intro = termstyle.green("\nInteractive shell to play with Cisco's TRex API.\nType help to view available pre-defined scenarios\n(c) All rights reserved.\n") prompt = '> ' def __init__(self, trex_host, max_history_size = 100, trex_port = 8090, verbose_mode = False ): @@ -33,89 +33,89 @@ class InteractiveTRexClient(cmd.Cmd): def do_push_files (self, filepaths): - """Pushes a custom file to be stored locally on T-Rex server.\nPush multiple files by spefiying their path separated by ' ' (space).""" + """Pushes a custom file to be stored locally on TRex server.\nPush multiple files by spefiying their path separated by ' ' (space).""" try: filepaths = filepaths.split(' ') - print termstyle.green("*** Starting pushing files ({trex_files}) to T-Rex. ***".format (trex_files = ', '.join(filepaths)) ) + print termstyle.green("*** Starting pushing files ({trex_files}) to TRex. ***".format (trex_files = ', '.join(filepaths)) ) ret_val = self.trex.push_files(filepaths) if ret_val: - print termstyle.green("*** End of T-Rex push_files method (success) ***") + print termstyle.green("*** End of TRex push_files method (success) ***") else: - print termstyle.magenta("*** End of T-Rex push_files method (failed) ***") + print termstyle.magenta("*** End of TRex push_files method (failed) ***") except IOError as inst: print termstyle.magenta(inst) def do_show_default_run_params(self,line): - """Outputs the default T-Rex running parameters""" + """Outputs the default TRex running parameters""" pprint(self.DEFAULT_RUN_PARAMS) - print termstyle.green("*** End of default T-Rex running parameters ***") + print termstyle.green("*** End of default TRex running parameters ***") def do_show_run_params(self,line): - """Outputs the currently configured T-Rex running parameters""" + """Outputs the currently configured TRex running parameters""" pprint(self.run_params) - print termstyle.green("*** End of T-Rex running parameters ***") + print termstyle.green("*** End of TRex running parameters ***") def do_update_run_params(self, json_str): - """Updates provided parameters on T-Rex running configuration. Provide using JSON string""" + """Updates provided parameters on TRex running configuration. Provide using JSON string""" if json_str: try: upd_params = self.decoder.decode(json_str) self.run_params.update(upd_params) - print termstyle.green("*** End of T-Rex parameters update ***") + print termstyle.green("*** End of TRex parameters update ***") except ValueError as inst: print termstyle.magenta("Provided illegal JSON string. Please try again.\n[", inst,"]") else: print termstyle.magenta("JSON configuration string is missing. Please try again.") def do_show_status (self, line): - """Prompts T-Rex current status""" + """Prompts TRex current status""" print self.trex.get_running_status() - print termstyle.green("*** End of T-Rex status prompt ***") + print termstyle.green("*** End of TRex status prompt ***") def do_show_trex_files_path (self, line): - """Prompts the local path in which files are stored when pushed to t-rex server from client""" + """Prompts the local path in which files are stored when pushed to trex server from client""" print self.trex.get_trex_files_path() print termstyle.green("*** End of trex_files_path prompt ***") def do_show_reservation_status (self, line): - """Prompts if T-Rex is currently reserved or not""" + """Prompts if TRex is currently reserved or not""" if self.trex.is_reserved(): - print "T-Rex is reserved" + print "TRex is reserved" else: - print "T-Rex is NOT reserved" + print "TRex is NOT reserved" print termstyle.green("*** End of reservation status prompt ***") def do_reserve_trex (self, user): - """Reserves the usage of T-Rex to a certain user""" + """Reserves the usage of TRex to a certain user""" try: if not user: ret = self.trex.reserve_trex() else: ret = self.trex.reserve_trex(user.split(' ')[0]) - print termstyle.green("*** T-Rex reserved successfully ***") + print termstyle.green("*** TRex reserved successfully ***") except TRexException as inst: print termstyle.red(inst) def do_cancel_reservation (self, user): - """Cancels a current reservation of T-Rex to a certain user""" + """Cancels a current reservation of TRex to a certain user""" try: if not user: ret = self.trex.cancel_reservation() else: ret = self.trex.cancel_reservation(user.split(' ')[0]) - print termstyle.green("*** T-Rex reservation canceled successfully ***") + print termstyle.green("*** TRex reservation canceled successfully ***") except TRexException as inst: print termstyle.red(inst) def do_restore_run_default (self, line): - """Restores original T-Rex running configuration""" + """Restores original TRex running configuration""" self.run_params = dict(self.DEFAULT_RUN_PARAMS) print termstyle.green("*** End of restoring default run parameters ***") def do_run_until_finish (self, sample_rate): - """Starts T-Rex and sample server until run is done.""" - print termstyle.green("*** Starting T-Rex run_until_finish scenario ***") + """Starts TRex and sample server until run is done.""" + print termstyle.green("*** Starting TRex run_until_finish scenario ***") if not sample_rate: # use default sample rate if not passed sample_rate = 5 @@ -123,15 +123,15 @@ class InteractiveTRexClient(cmd.Cmd): sample_rate = int(sample_rate) ret = self.trex.start_trex(**self.run_params) self.trex.sample_to_run_finish(sample_rate) - print termstyle.green("*** End of T-Rex run ***") + print termstyle.green("*** End of TRex run ***") except ValueError as inst: print termstyle.magenta("Provided illegal sample rate value. Please try again.\n[", inst,"]") except TRexException as inst: print termstyle.red(inst) def do_run_and_poll (self, sample_rate): - """Starts T-Rex and sample server manually until run is done.""" - print termstyle.green("*** Starting T-Rex run and manually poll scenario ***") + """Starts TRex and sample server manually until run is done.""" + print termstyle.green("*** Starting TRex run and manually poll scenario ***") if not sample_rate: # use default sample rate if not passed sample_rate = 5 try: @@ -145,7 +145,7 @@ class InteractiveTRexClient(cmd.Cmd): # do WHATEVER here time.sleep(sample_rate) - print termstyle.green("*** End of T-Rex run ***") + print termstyle.green("*** End of TRex run ***") except ValueError as inst: print termstyle.magenta("Provided illegal sample rate value. Please try again.\n[", inst,"]") except TRexException as inst: @@ -153,8 +153,8 @@ class InteractiveTRexClient(cmd.Cmd): def do_run_until_condition (self, sample_rate): - """Starts T-Rex and sample server until condition is satisfied.""" - print termstyle.green("*** Starting T-Rex run until condition is satisfied scenario ***") + """Starts TRex and sample server until condition is satisfied.""" + print termstyle.green("*** Starting TRex run until condition is satisfied scenario ***") def condition (result_obj): return result_obj.get_current_tx_rate()['m_tx_pps'] > 200000 @@ -166,55 +166,55 @@ class InteractiveTRexClient(cmd.Cmd): ret = self.trex.start_trex(**self.run_params) ret_val = self.trex.sample_until_condition(condition, sample_rate) print ret_val - print termstyle.green("*** End of T-Rex run ***") + print termstyle.green("*** End of TRex run ***") except ValueError as inst: print termstyle.magenta("Provided illegal sample rate value. Please try again.\n[", inst,"]") except TRexException as inst: print termstyle.red(inst) def do_start_and_return (self, line): - """Start T-Rex run and once in 'Running' mode, return to cmd prompt""" - print termstyle.green("*** Starting T-Rex run, wait until in 'Running' state ***") + """Start TRex run and once in 'Running' mode, return to cmd prompt""" + print termstyle.green("*** Starting TRex run, wait until in 'Running' state ***") try: ret = self.trex.start_trex(**self.run_params) - print termstyle.green("*** End of scenario (T-Rex is probably still running!) ***") + print termstyle.green("*** End of scenario (TRex is probably still running!) ***") except TRexException as inst: print termstyle.red(inst) def do_poll_once (self, line): - """Performs a single poll of T-Rex current data dump (if T-Rex is running) and prompts and short version of latest result_obj""" - print termstyle.green("*** Trying T-Rex single poll ***") + """Performs a single poll of TRex current data dump (if TRex is running) and prompts and short version of latest result_obj""" + print termstyle.green("*** Trying TRex single poll ***") try: last_res = dict() if self.trex.is_running(dump_out = last_res): obj = self.trex.get_result_obj() print obj else: - print termstyle.magenta("T-Rex isn't currently running.") - print termstyle.green("*** End of scenario (T-Rex is posssibly still running!) ***") + print termstyle.magenta("TRex isn't currently running.") + print termstyle.green("*** End of scenario (TRex is posssibly still running!) ***") except TRexException as inst: print termstyle.red(inst) def do_stop_trex (self, line): - """Try to stop T-Rex run (if T-Rex is currently running)""" - print termstyle.green("*** Starting T-Rex termination ***") + """Try to stop TRex run (if TRex is currently running)""" + print termstyle.green("*** Starting TRex termination ***") try: ret = self.trex.stop_trex() - print termstyle.green("*** End of scenario (T-Rex is not running now) ***") + print termstyle.green("*** End of scenario (TRex is not running now) ***") except TRexException as inst: print termstyle.red(inst) def do_kill_indiscriminately (self, line): - """Force killing of running T-Rex process (if exists) on the server.""" - print termstyle.green("*** Starting T-Rex termination ***") + """Force killing of running TRex process (if exists) on the server.""" + print termstyle.green("*** Starting TRex termination ***") ret = self.trex.force_kill() if ret: - print termstyle.green("*** End of scenario (T-Rex is not running now) ***") + print termstyle.green("*** End of scenario (TRex is not running now) ***") elif ret is None: - print termstyle.magenta("*** End of scenario (T-Rex termination aborted) ***") + print termstyle.magenta("*** End of scenario (TRex termination aborted) ***") else: - print termstyle.red("*** End of scenario (T-Rex termination failed) ***") + print termstyle.red("*** End of scenario (TRex termination failed) ***") def do_exit(self, arg): """Quits the application""" @@ -223,20 +223,20 @@ class InteractiveTRexClient(cmd.Cmd): if __name__ == "__main__": - parser = ArgumentParser(description = termstyle.cyan('Run T-Rex client API demos and scenarios.'), + parser = ArgumentParser(description = termstyle.cyan('Run TRex client API demos and scenarios.'), usage = """client_interactive_example [options]""" ) parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0 \t (C) Cisco Systems Inc.\n') parser.add_argument("-t", "--trex-host", required = True, dest="trex_host", - action="store", help="Specify the hostname or ip to connect with T-Rex server.", + action="store", help="Specify the hostname or ip to connect with TRex server.", metavar="HOST" ) parser.add_argument("-p", "--trex-port", type=int, default = 8090, metavar="PORT", dest="trex_port", - help="Select port on which the T-Rex server listens. Default port is 8090.", action="store") + help="Select port on which the TRex server listens. Default port is 8090.", action="store") parser.add_argument("-m", "--maxhist", type=int, default = 100, metavar="SIZE", dest="hist_size", help="Specify maximum history size saved at client side. Default size is 100.", action="store") parser.add_argument("--verbose", dest="verbose", - action="store_true", help="Switch ON verbose option at T-Rex client. Default is: OFF.", + action="store_true", help="Switch ON verbose option at TRex client. Default is: OFF.", default = False ) args = parser.parse_args() @@ -248,7 +248,7 @@ if __name__ == "__main__": exit(-1) except socket.error, e: if e.errno == errno.ECONNREFUSED: - raise socket.error(errno.ECONNREFUSED, "Connection from T-Rex server was terminated. Please make sure the server is up.") + raise socket.error(errno.ECONNREFUSED, "Connection from TRex server was terminated. Please make sure the server is up.") diff --git a/scripts/automation/trex_control_plane/examples/interactive_stateless.py b/scripts/automation/trex_control_plane/examples/interactive_stateless.py new file mode 100644 index 00000000..e64b4755 --- /dev/null +++ b/scripts/automation/trex_control_plane/examples/interactive_stateless.py @@ -0,0 +1,128 @@ +#!/router/bin/python + +import trex_root_path +from client.trex_stateless_client import * +from common.trex_exceptions import * +import cmd +from termstyle import termstyle +# import termstyle +import os +from argparse import ArgumentParser +import socket +import errno +import ast +import json + + +class InteractiveStatelessTRex(cmd.Cmd): + + intro = termstyle.green("\nInteractive shell to play with Cisco's TRex stateless API.\ + \nType help to view available pre-defined scenarios\n(c) All rights reserved.\n") + prompt = '> ' + + def __init__(self, trex_host, trex_port, virtual, verbose): + cmd.Cmd.__init__(self) + + self.verbose = verbose + self.virtual = virtual + self.trex = CTRexStatelessClient(trex_host, trex_port, self.virtual) + self.DEFAULT_RUN_PARAMS = dict(m=1.5, + nc=True, + p=True, + d=100, + f='avl/sfr_delay_10_1g.yaml', + l=1000) + self.run_params = dict(self.DEFAULT_RUN_PARAMS) + + def do_transmit(self, line): + """Transmits a request over using a given link to server.\ + \nuse: transmit [method_name] [method_params]""" + if line == "": + print "\nUsage: [method name] [param dict as string]\n" + print "Example: rpc test_add {'x': 12, 'y': 17}\n" + return + + args = line.split(' ', 1) # args will have max length of 2 + method_name = args[0] + params = None + bad_parse = False + + try: + params = ast.literal_eval(args[1]) + if not isinstance(params, dict): + bad_parse = True + except ValueError as e1: + bad_parse = True + except SyntaxError as e2: + bad_parse = True + + if bad_parse: + print "\nValue should be a valid dict: '{0}'".format(args[1]) + print "\nUsage: [method name] [param dict as string]\n" + print "Example: rpc test_add {'x': 12, 'y': 17}\n" + return + + response = self.trex.transmit(method_name, params) + if not self.virtual: + # expect response + rc, msg = response + if rc: + print "\nServer Response:\n\n" + json.dumps(msg) + "\n" + else: + print "\n*** " + msg + "\n" + + + + + + def do_push_files(self, filepaths): + """Pushes a custom file to be stored locally on TRex server.\ + \nPush multiple files by specifying their path separated by ' ' (space).""" + try: + filepaths = filepaths.split(' ') + print termstyle.green("*** Starting pushing files ({trex_files}) to TRex. ***".format( + trex_files=', '.join(filepaths)) + ) + ret_val = self.trex.push_files(filepaths) + if ret_val: + print termstyle.green("*** End of TRex push_files method (success) ***") + else: + print termstyle.magenta("*** End of TRex push_files method (failed) ***") + + except IOError as inst: + print termstyle.magenta(inst) + +if __name__ == "__main__": + parser = ArgumentParser(description=termstyle.cyan('Run TRex client stateless API demos and scenarios.'), + usage="client_interactive_example [options]") + + parser.add_argument('-v', '--version', action='version', version='%(prog)s 1.0 \t (C) Cisco Systems Inc.\n') + + parser.add_argument("-t", "--trex-host", required = True, dest="trex_host", + action="store", help="Specify the hostname or ip to connect with TRex server.", + metavar="HOST" ) + parser.add_argument("-p", "--trex-port", type=int, default = 5050, metavar="PORT", dest="trex_port", + help="Select port on which the TRex server listens. Default port is 5050.", action="store") + # parser.add_argument("-m", "--maxhist", type=int, default = 100, metavar="SIZE", dest="hist_size", + # help="Specify maximum history size saved at client side. Default size is 100.", action="store") + parser.add_argument("--virtual", dest="virtual", + action="store_true", + help="Switch ON virtual option at TRex client. Default is: OFF.", + default=False) + parser.add_argument("--verbose", dest="verbose", + action="store_true", + help="Switch ON verbose option at TRex client. Default is: OFF.", + default=False) + args = parser.parse_args() + + try: + InteractiveStatelessTRex(**vars(args)).cmdloop() + + except KeyboardInterrupt: + print termstyle.cyan('Bye Bye!') + exit(-1) + except socket.error, e: + if e.errno == errno.ECONNREFUSED: + raise socket.error(errno.ECONNREFUSED, + "Connection from TRex server was terminated. \ + Please make sure the server is up.") diff --git a/scripts/automation/trex_control_plane/examples/pkt_generation_for_trex.py b/scripts/automation/trex_control_plane/examples/pkt_generation_for_trex.py index 7e7f6139..acaa95d3 100755 --- a/scripts/automation/trex_control_plane/examples/pkt_generation_for_trex.py +++ b/scripts/automation/trex_control_plane/examples/pkt_generation_for_trex.py @@ -2,7 +2,7 @@ ###################################################################################### ### ### -### T-Rex end-to-end demo script, written by T-Rex dev-team ### +### TRex end-to-end demo script, written by TRex dev-team ### ### THIS SCRIPT ASSUMES PyYaml and Scapy INSTALLED ON PYTHON'S RUNNING MACHINE ### ### (for any question please contact trex-dev team @ trex-dev@cisco.com) ### ### ### @@ -33,13 +33,13 @@ def pkts_to_pcap (pcap_filename, packets): def main (args): - # instantiate T-Rex client + # instantiate TRex client trex = CTRexClient('trex-dan', verbose = args.verbose) if args.steps: print "\nNext step: .pcap generation." raw_input("Press Enter to continue...") - # generate T-Rex traffic. + # generate TRex traffic. pkts = generate_dns_packets('21.0.0.2', '22.0.0.12') # In this case - DNS traffic (request-response) print "\ngenerated traffic:" print "==================" @@ -50,7 +50,7 @@ def main (args): print "\nNext step: .yaml generation." raw_input("Press Enter to continue...") # Generate .yaml file that uses the generated .pcap file - trex_files_path = trex.get_trex_files_path() # fetch the path in which packets are saved on T-Rex server + trex_files_path = trex.get_trex_files_path() # fetch the path in which packets are saved on TRex server yaml_obj = CTRexYaml(trex_files_path) # instantiate CTRexYaml obj # set .yaml file parameters according to need and use @@ -65,12 +65,12 @@ def main (args): yaml_obj.dump() if args.steps: - print "\nNext step: run T-Rex with provided files." + print "\nNext step: run TRex with provided files." raw_input("Press Enter to continue...") # push all relevant files to server trex.push_files( yaml_obj.get_file_list() ) - print "\nStarting T-Rex..." + print "\nStarting TRex..." trex.start_trex(c = 2, m = 1.5, nc = True, @@ -80,8 +80,8 @@ def main (args): l = 1000) if args.verbose: - print "T-Rex state changed to 'Running'." - print "Sampling T-Rex in 0.2 samples/sec (single sample every 5 secs)" + print "TRex state changed to 'Running'." + print "Sampling TRex in 0.2 samples/sec (single sample every 5 secs)" last_res = dict() while trex.is_running(dump_out = last_res): @@ -92,14 +92,14 @@ def main (args): if __name__ == "__main__": - parser = ArgumentParser(description = 'Run T-Rex client API end-to-end example.', + parser = ArgumentParser(description = 'Run TRex client API end-to-end example.', usage = """pkt_generation_for_trex [options]""" ) parser.add_argument("-s", "--step-by-step", dest="steps", action="store_false", help="Switch OFF step-by-step script overview. Default is: ON.", default = True ) parser.add_argument("--verbose", dest="verbose", - action="store_true", help="Switch ON verbose option at T-Rex client. Default is: OFF.", + action="store_true", help="Switch ON verbose option at TRex client. Default is: OFF.", default = False ) args = parser.parse_args() main(args)
\ No newline at end of file diff --git a/scripts/automation/trex_control_plane/server/extended_daemon_runner.py b/scripts/automation/trex_control_plane/server/extended_daemon_runner.py index 1813ed48..734fa22e 100755 --- a/scripts/automation/trex_control_plane/server/extended_daemon_runner.py +++ b/scripts/automation/trex_control_plane/server/extended_daemon_runner.py @@ -97,9 +97,9 @@ class ExtendedDaemonRunner(runner.DaemonRunner): @staticmethod
def _show(self):
if self.pidfile.is_locked():
- print termstyle.red("T-Rex server daemon is running")
+ print termstyle.red("TRex server daemon is running")
else:
- print termstyle.red("T-Rex server daemon is NOT running")
+ print termstyle.red("TRex server daemon is NOT running")
def do_action(self):
self.__prevent_duplicate_runs()
diff --git a/scripts/automation/trex_control_plane/server/outer_packages.py b/scripts/automation/trex_control_plane/server/outer_packages.py index 976e478d..2234c734 100755 --- a/scripts/automation/trex_control_plane/server/outer_packages.py +++ b/scripts/automation/trex_control_plane/server/outer_packages.py @@ -1,7 +1,6 @@ #!/router/bin/python import sys -import site import os CURRENT_PATH = os.path.dirname(os.path.realpath(__file__)) @@ -29,6 +28,6 @@ def import_module_list(modules_list): for p in modules_list: full_path = os.path.join(PATH_TO_PYTHON_LIB, p) fix_path = os.path.normcase(full_path) - site.addsitedir(full_path) + sys.path.insert(1, full_path) import_server_modules() diff --git a/scripts/automation/trex_control_plane/server/trex_daemon_server.py b/scripts/automation/trex_control_plane/server/trex_daemon_server.py index 5032423a..ec07cb8a 100755 --- a/scripts/automation/trex_control_plane/server/trex_daemon_server.py +++ b/scripts/automation/trex_control_plane/server/trex_daemon_server.py @@ -54,7 +54,7 @@ def main (): logger.addHandler(handler) except EnvironmentError, e: if e.errno == errno.EACCES: # catching permission denied error - print "Launching user must have sudo privileges in order to run T-Rex daemon.\nTerminating daemon process." + print "Launching user must have sudo privileges in order to run TRex daemon.\nTerminating daemon process." exit(-1) try: diff --git a/scripts/automation/trex_control_plane/server/trex_launch_thread.py b/scripts/automation/trex_control_plane/server/trex_launch_thread.py index b4be60a9..59c382ea 100755 --- a/scripts/automation/trex_control_plane/server/trex_launch_thread.py +++ b/scripts/automation/trex_control_plane/server/trex_launch_thread.py @@ -33,44 +33,44 @@ class AsynchronousTRexSession(threading.Thread): with open(os.devnull, 'w') as DEVNULL:
self.time_stamps['start'] = self.time_stamps['run_time'] = time.time()
self.session = subprocess.Popen("exec "+self.cmd, cwd = self.launch_path, shell=True, stdin = DEVNULL, stderr = subprocess.PIPE, preexec_fn=os.setsid)
- logger.info("T-Rex session initialized successfully, Parent process pid is {pid}.".format( pid = self.session.pid ))
+ logger.info("TRex session initialized successfully, Parent process pid is {pid}.".format( pid = self.session.pid ))
while self.session.poll() is None: # subprocess is NOT finished
time.sleep(0.5)
if self.stoprequest.is_set():
- logger.debug("Abort request received by handling thread. Terminating T-Rex session." )
+ logger.debug("Abort request received by handling thread. Terminating TRex session." )
os.killpg(self.session.pid, signal.SIGUSR1)
self.trexObj.set_status(TRexStatus.Idle)
- self.trexObj.set_verbose_status("T-Rex is Idle")
+ self.trexObj.set_verbose_status("TRex is Idle")
break
self.time_stamps['run_time'] = time.time() - self.time_stamps['start']
try:
if self.time_stamps['run_time'] < 5:
- logger.error("T-Rex run failed due to wrong input parameters, or due to reachability issues.")
- self.trexObj.set_verbose_status("T-Rex run failed due to wrong input parameters, or due to reachability issues.\n\nT-Rex command: {cmd}\n\nRun output:\n{output}".format(
+ logger.error("TRex run failed due to wrong input parameters, or due to readability issues.")
+ self.trexObj.set_verbose_status("TRex run failed due to wrong input parameters, or due to readability issues.\n\nTRex command: {cmd}\n\nRun output:\n{output}".format(
cmd = self.cmd, output = self.load_trex_output(self.export_path)))
self.trexObj.errcode = -11
elif (self.session.returncode is not None and self.session.returncode < 0) or ( (self.time_stamps['run_time'] < self.duration) and (not self.stoprequest.is_set()) ):
if (self.session.returncode is not None and self.session.returncode < 0):
- logger.debug("Failed T-Rex run due to session return code ({ret_code})".format( ret_code = self.session.returncode ) )
+ logger.debug("Failed TRex run due to session return code ({ret_code})".format( ret_code = self.session.returncode ) )
elif ( (self.time_stamps['run_time'] < self.duration) and not self.stoprequest.is_set()):
- logger.debug("Failed T-Rex run due to running time ({runtime}) combined with no-stopping request.".format( runtime = self.time_stamps['run_time'] ) )
+ logger.debug("Failed TRex run due to running time ({runtime}) combined with no-stopping request.".format( runtime = self.time_stamps['run_time'] ) )
- logger.warning("T-Rex run was terminated unexpectedly by outer process or by the hosting OS")
- self.trexObj.set_verbose_status("T-Rex run was terminated unexpectedly by outer process or by the hosting OS.\n\nRun output:\n{output}".format(
+ logger.warning("TRex run was terminated unexpectedly by outer process or by the hosting OS")
+ self.trexObj.set_verbose_status("TRex run was terminated unexpectedly by outer process or by the hosting OS.\n\nRun output:\n{output}".format(
output = self.load_trex_output(self.export_path)))
self.trexObj.errcode = -15
else:
- logger.info("T-Rex run session finished.")
- self.trexObj.set_verbose_status('T-Rex finished.')
+ logger.info("TRex run session finished.")
+ self.trexObj.set_verbose_status('TRex finished.')
self.trexObj.errcode = None
finally:
self.trexObj.set_status(TRexStatus.Idle)
logger.info("TRex running state changed to 'Idle'.")
self.trexObj.expect_trex.clear()
- logger.debug("Finished handling a single run of T-Rex.")
+ logger.debug("Finished handling a single run of TRex.")
self.trexObj.zmq_dump = None
def join (self, timeout = None):
diff --git a/scripts/automation/trex_control_plane/server/trex_server.py b/scripts/automation/trex_control_plane/server/trex_server.py index 35b2669a..e48f8963 100755 --- a/scripts/automation/trex_control_plane/server/trex_server.py +++ b/scripts/automation/trex_control_plane/server/trex_server.py @@ -19,7 +19,6 @@ from common.trex_status_e import TRexStatus from common.trex_exceptions import * import subprocess from random import randrange -#import shlex import logging import threading import CCustomLogger @@ -34,12 +33,12 @@ CCustomLogger.setup_custom_logger('TRexServer') logger = logging.getLogger('TRexServer') class CTRexServer(object): - """This class defines the server side of the RESTfull interaction with T-Rex""" + """This class defines the server side of the RESTfull interaction with TRex""" DEFAULT_TREX_PATH = '/auto/proj-pcube-b/apps/PL-b/tools/bp_sim2/v1.55/' #'/auto/proj-pcube-b/apps/PL-b/tools/nightly/trex_latest' TREX_START_CMD = './t-rex-64' DEFAULT_FILE_PATH = '/tmp/trex_files/' - def __init__(self, trex_path, trex_files_path, trex_host = socket.gethostname(), trex_daemon_port = 8090, trex_zmq_port = 4500): + def __init__(self, trex_path, trex_files_path, trex_host='0.0.0.0', trex_daemon_port=8090, trex_zmq_port=4500): """ Parameters ---------- @@ -53,7 +52,7 @@ class CTRexServer(object): the port number on which trex's zmq module will interact with daemon server default value: 4500 - Instantiate a T-Rex client object, and connecting it to listening daemon-server + Instantiate a TRex client object, and connecting it to listening daemon-server """ self.TREX_PATH = os.path.abspath(os.path.dirname(trex_path+'/')) self.trex_files_path = os.path.abspath(os.path.dirname(trex_files_path+'/')) @@ -92,19 +91,27 @@ class CTRexServer(object): def start(self): """This method fires up the daemon server based on initialized parameters of the class""" - # initialize the server instance with given reasources - try: - print "Firing up T-Rex REST daemon @ port {trex_port} ...\n".format( trex_port = self.trex_daemon_port ) - logger.info("Firing up T-Rex REST daemon @ port {trex_port} ...".format( trex_port = self.trex_daemon_port )) + # initialize the server instance with given resources + try: + print "Firing up TRex REST daemon @ port {trex_port} ...\n".format( trex_port = self.trex_daemon_port ) + logger.info("Firing up TRex REST daemon @ port {trex_port} ...".format( trex_port = self.trex_daemon_port )) logger.info("current working dir is: {0}".format(self.TREX_PATH) ) logger.info("current files dir is : {0}".format(self.trex_files_path) ) logger.debug("Starting TRex server. Registering methods to process.") self.server = SimpleJSONRPCServer( (self.trex_host, self.trex_daemon_port) ) except socket.error as e: if e.errno == errno.EADDRINUSE: - logger.error("T-Rex server requested address already in use. Aborting server launching.") - print "T-Rex server requested address already in use. Aborting server launching." - raise socket.error(errno.EADDRINUSE, "T-Rex daemon requested address already in use. Server launch aborted. Please make sure no other process is using the desired server properties.") + logger.error("TRex server requested address already in use. Aborting server launching.") + print "TRex server requested address already in use. Aborting server launching." + raise socket.error(errno.EADDRINUSE, "TRex daemon requested address already in use. " + "Server launch aborted. Please make sure no other process is " + "using the desired server properties.") + elif isinstance(e, socket.gaierror) and e.errno == -3: + # handling Temporary failure in name resolution exception + raise socket.gaierror(-3, "Temporary failure in name resolution.\n" + "Make sure provided hostname has DNS resolving.") + else: + raise # set further functionality and peripherals to server instance try: @@ -129,14 +136,14 @@ class CTRexServer(object): except KeyboardInterrupt: logger.info("Daemon shutdown request detected." ) finally: - self.zmq_monitor.join() # close ZMQ monitor thread reasources + self.zmq_monitor.join() # close ZMQ monitor thread resources self.server.shutdown() pass def stop_handler (self, signum, frame): logger.info("Daemon STOP request detected.") if self.is_running(): - # in case T-Rex process is currently running, stop it before terminating server process + # in case TRex process is currently running, stop it before terminating server process self.stop_trex(self.trex.get_seq()) sys.exit(0) @@ -163,25 +170,25 @@ class CTRexServer(object): def reserve_trex (self, user): if user == "": - logger.info("T-Rex reservation cannot apply to empty string user. Request denied.") - return Fault(-33, "T-Rex reservation cannot apply to empty string user. Request denied.") + logger.info("TRex reservation cannot apply to empty string user. Request denied.") + return Fault(-33, "TRex reservation cannot apply to empty string user. Request denied.") with self.start_lock: logger.info("Processing reserve_trex() command.") if self.is_reserved(): if user == self.__reservation['user']: # return True is the same user is asking and already has the resrvation - logger.info("the same user is asking and already has the resrvation. Re-reserving T-Rex.") + logger.info("the same user is asking and already has the resrvation. Re-reserving TRex.") return True - logger.info("T-Rex is already reserved to another user ({res_user}), cannot reserve to another user.".format( res_user = self.__reservation['user'] )) - return Fault(-33, "T-Rex is already reserved to another user ({res_user}). Please make sure T-Rex is free before reserving it.".format( + logger.info("TRex is already reserved to another user ({res_user}), cannot reserve to another user.".format( res_user = self.__reservation['user'] )) + return Fault(-33, "TRex is already reserved to another user ({res_user}). Please make sure TRex is free before reserving it.".format( res_user = self.__reservation['user']) ) # raise at client TRexInUseError elif self.trex.get_status() != TRexStatus.Idle: - logger.info("T-Rex is currently running, cannot reserve T-Rex unless in Idle state.") - return Fault(-13, 'T-Rex is currently running, cannot reserve T-Rex unless in Idle state. Please try again when T-Rex run finished.') # raise at client TRexInUseError + logger.info("TRex is currently running, cannot reserve TRex unless in Idle state.") + return Fault(-13, 'TRex is currently running, cannot reserve TRex unless in Idle state. Please try again when TRex run finished.') # raise at client TRexInUseError else: - logger.info("T-Rex is now reserved for user ({res_user}).".format( res_user = user )) + logger.info("TRex is now reserved for user ({res_user}).".format( res_user = user )) self.__reservation = {'user' : user, 'since' : time.ctime()} logger.debug("Reservation details: "+ str(self.__reservation)) return True @@ -191,15 +198,15 @@ class CTRexServer(object): logger.info("Processing cancel_reservation() command.") if self.is_reserved(): if self.__reservation['user'] == user: - logger.info("T-Rex reservation to {res_user} has been canceled successfully.".format(res_user = self.__reservation['user'])) + logger.info("TRex reservation to {res_user} has been canceled successfully.".format(res_user = self.__reservation['user'])) self.__reservation = None return True else: - logger.warning("T-Rex is reserved to different user than the provided one. Reservation wasn't canceled.") + logger.warning("TRex is reserved to different user than the provided one. Reservation wasn't canceled.") return Fault(-33, "Cancel reservation request is available to the user that holds the reservation. Request denied") # raise at client TRexRequestDenied else: - logger.info("T-Rex is not reserved to anyone. No need to cancel anything") + logger.info("TRex is not reserved to anyone. No need to cancel anything") assert(self.__reservation is None) return False @@ -208,21 +215,21 @@ class CTRexServer(object): with self.start_lock: logger.info("Processing start_trex() command.") if self.is_reserved(): - # check if this is not the user to which T-Rex is reserved + # check if this is not the user to which TRex is reserved if self.__reservation['user'] != user: - logger.info("T-Rex is reserved to another user ({res_user}). Only that user is allowed to initiate new runs.".format(res_user = self.__reservation['user'])) - return Fault(-33, "T-Rex is reserved to another user ({res_user}). Only that user is allowed to initiate new runs.".format(res_user = self.__reservation['user'])) # raise at client TRexRequestDenied + logger.info("TRex is reserved to another user ({res_user}). Only that user is allowed to initiate new runs.".format(res_user = self.__reservation['user'])) + return Fault(-33, "TRex is reserved to another user ({res_user}). Only that user is allowed to initiate new runs.".format(res_user = self.__reservation['user'])) # raise at client TRexRequestDenied elif self.trex.get_status() != TRexStatus.Idle: - logger.info("T-Rex is already taken, cannot create another run until done.") + logger.info("TRex is already taken, cannot create another run until done.") return Fault(-13, '') # raise at client TRexInUseError try: server_cmd_data = self.generate_run_cmd(**trex_cmd_options) self.zmq_monitor.first_dump = True self.trex.start_trex(self.TREX_PATH, server_cmd_data) - logger.info("T-Rex session has been successfully initiated.") + logger.info("TRex session has been successfully initiated.") if block_to_success: - # delay server response until T-Rex is at 'Running' state. + # delay server response until TRex is at 'Running' state. start_time = time.time() trex_state = None while (time.time() - start_time) < timeout : @@ -232,20 +239,20 @@ class CTRexServer(object): else: time.sleep(0.5) - # check for T-Rex run started normally + # check for TRex run started normally if trex_state == TRexStatus.Starting: # reached timeout - logger.warning("TimeoutError: T-Rex initiation outcome could not be obtained, since T-Rex stays at Starting state beyond defined timeout.") - return Fault(-12, 'TimeoutError: T-Rex initiation outcome could not be obtained, since T-Rex stays at Starting state beyond defined timeout.') # raise at client TRexWarning + logger.warning("TimeoutError: TRex initiation outcome could not be obtained, since TRex stays at Starting state beyond defined timeout.") + return Fault(-12, 'TimeoutError: TRex initiation outcome could not be obtained, since TRex stays at Starting state beyond defined timeout.') # raise at client TRexWarning elif trex_state == TRexStatus.Idle: return Fault(-11, self.trex.get_verbose_status()) # raise at client TRexError - # reach here only if T-Rex is at 'Running' state + # reach here only if TRex is at 'Running' state self.trex.gen_seq() return self.trex.get_seq() # return unique seq number to client except TypeError as e: - logger.error("T-Rex command generation failed, probably because either -f (traffic generation .yaml file) and -c (num of cores) was not specified correctly.\nReceived params: {params}".format( params = trex_cmd_options) ) - raise TypeError('T-Rex -f (traffic generation .yaml file) and -c (num of cores) must be specified.') + logger.error("TRex command generation failed, probably because either -f (traffic generation .yaml file) and -c (num of cores) was not specified correctly.\nReceived params: {params}".format( params = trex_cmd_options) ) + raise TypeError('TRex -f (traffic generation .yaml file) and -c (num of cores) must be specified.') def stop_trex(self, seq): @@ -262,11 +269,11 @@ class CTRexServer(object): return False def force_trex_kill (self): - logger.info("Processing force_trex_kill() command. --> Killing T-Rex session indiscriminately.") + logger.info("Processing force_trex_kill() command. --> Killing TRex session indiscriminately.") return self.trex.stop_trex() def wait_until_kickoff_finish (self, timeout = 40): - # block until T-Rex exits Starting state + # block until TRex exits Starting state logger.info("Processing wait_until_kickoff_finish() command.") trex_state = None start_time = time.time() @@ -274,7 +281,7 @@ class CTRexServer(object): trex_state = self.trex.get_status() if trex_state != TRexStatus.Starting: return - return Fault(-12, 'TimeoutError: T-Rex initiation outcome could not be obtained, since T-Rex stays at Starting state beyond defined timeout.') # raise at client TRexWarning + return Fault(-12, 'TimeoutError: TRex initiation outcome could not be obtained, since TRex stays at Starting state beyond defined timeout.') # raise at client TRexWarning def get_running_info (self): logger.info("Processing get_running_info() command.") @@ -283,7 +290,7 @@ class CTRexServer(object): def generate_run_cmd (self, f, d, iom = 0, export_path="/tmp/trex.txt", **kwargs): """ generate_run_cmd(self, trex_cmd_options, export_path) -> str - Generates a custom running command for the kick-off of the T-Rex traffic generator. + Generates a custom running command for the kick-off of the TRex traffic generator. Returns a tuple of command (string) and export path (string) to be issued on the trex server Parameters @@ -318,21 +325,21 @@ class CTRexServer(object): io = iom, export = export_path ) - logger.info("T-REX FULL COMMAND: {command}".format(command = cmd) ) + logger.info("TREX FULL COMMAND: {command}".format(command = cmd) ) return (cmd, export_path, long(d)) def __check_trex_path_validity(self): # check for executable existance if not os.path.exists(self.TREX_PATH+'/t-rex-64'): - print "The provided T-Rex path do not contain an executable T-Rex file.\nPlease check the path and retry." - logger.error("The provided T-Rex path do not contain an executable T-Rex file") + print "The provided TRex path do not contain an executable TRex file.\nPlease check the path and retry." + logger.error("The provided TRex path do not contain an executable TRex file") exit(-1) # check for executable permissions st = os.stat(self.TREX_PATH+'/t-rex-64') if not bool(st.st_mode & (stat.S_IXUSR ) ): - print "The provided T-Rex path do not contain an T-Rex file with execution privileges.\nPlease check the files permissions and retry." - logger.error("The provided T-Rex path do not contain an T-Rex file with execution privileges") + print "The provided TRex path do not contain an TRex file with execution privileges.\nPlease check the files permissions and retry." + logger.error("The provided TRex path do not contain an TRex file with execution privileges") exit(-1) else: return @@ -357,7 +364,7 @@ class CTRexServer(object): class CTRex(object): def __init__(self): self.status = TRexStatus.Idle - self.verbose_status = 'T-Rex is Idle' + self.verbose_status = 'TRex is Idle' self.errcode = None self.session = None self.zmq_monitor = None @@ -388,34 +395,34 @@ class CTRex(object): if self.status == TRexStatus.Running: return self.encoder.encode(self.zmq_dump) else: - logger.info("T-Rex isn't running. Running information isn't available.") + logger.info("TRex isn't running. Running information isn't available.") if self.status == TRexStatus.Idle: if self.errcode is not None: # some error occured - logger.info("T-Rex is in Idle state, with errors. returning fault") + logger.info("TRex is in Idle state, with errors. returning fault") return Fault(self.errcode, self.verbose_status) # raise at client relevant exception, depending on the reason the error occured else: - logger.info("T-Rex is in Idle state, no errors. returning {}") + logger.info("TRex is in Idle state, no errors. returning {}") return u'{}' - return Fault(-12, self.verbose_status) # raise at client TRexWarning, indicating T-Rex is back to Idle state or still in Starting state + return Fault(-12, self.verbose_status) # raise at client TRexWarning, indicating TRex is back to Idle state or still in Starting state def stop_trex(self): if self.status == TRexStatus.Idle: # t-rex isn't running, nothing to abort - logger.info("T-Rex isn't running. No need to stop anything.") - if self.errcode is not None: # some error occured, notify client despite T-Rex already stopped + logger.info("TRex isn't running. No need to stop anything.") + if self.errcode is not None: # some error occurred, notify client despite TRex already stopped return Fault(self.errcode, self.verbose_status) # raise at client relevant exception, depending on the reason the error occured return False else: # handle stopping t-rex's run self.session.join() - logger.info("T-Rex session has been successfully aborted.") + logger.info("TRex session has been successfully aborted.") return True def start_trex(self, trex_launch_path, trex_cmd): self.set_status(TRexStatus.Starting) logger.info("TRex running state changed to 'Starting'.") - self.set_verbose_status('T-Rex is starting (data is not available yet)') + self.set_verbose_status('TRex is starting (data is not available yet)') self.errcode = None self.session = AsynchronousTRexSession(self, trex_launch_path, trex_cmd) @@ -430,7 +437,7 @@ def generate_trex_parser (): default_path = os.path.abspath(os.path.join(outer_packages.CURRENT_PATH, os.pardir, os.pardir, os.pardir)) default_files_path = os.path.abspath(CTRexServer.DEFAULT_FILE_PATH) - parser = ArgumentParser(description = 'Run server application for T-Rex traffic generator', + parser = ArgumentParser(description = 'Run server application for TRex traffic generator', formatter_class = RawTextHelpFormatter, usage = """ trex_daemon_server [options] @@ -440,14 +447,18 @@ trex_daemon_server [options] parser.add_argument("-p", "--daemon-port", type=int, default = 8090, metavar="PORT", dest="daemon_port", help="Select port on which the daemon runs.\nDefault port is 8090.", action="store") parser.add_argument("-z", "--zmq-port", dest="zmq_port", type=int, - action="store", help="Select port on which the ZMQ module listens to T-Rex.\nDefault port is 4500.", metavar="PORT", + action="store", help="Select port on which the ZMQ module listens to TRex.\nDefault port is 4500.", metavar="PORT", default = 4500) parser.add_argument("-t", "--trex-path", dest="trex_path", - action="store", help="Specify the compiled T-Rex directory from which T-Rex would run.\nDefault path is: {def_path}.".format( def_path = default_path ), + action="store", help="Specify the compiled TRex directory from which TRex would run.\nDefault path is: {def_path}.".format( def_path = default_path ), metavar="PATH", default = default_path ) parser.add_argument("-f", "--files-path", dest="files_path", action="store", help="Specify a path to directory on which pushed files will be saved at.\nDefault path is: {def_path}.".format( def_path = default_files_path ), metavar="PATH", default = default_files_path ) + parser.add_argument("--trex-host", dest="trex_host", + action="store", help="Specify a hostname to be registered as the TRex server.\n" + "Default is to bind all IPs using '0.0.0.0'.", + metavar="HOST", default = '0.0.0.0') return parser trex_parser = generate_trex_parser() @@ -455,8 +466,9 @@ trex_parser = generate_trex_parser() def do_main_program (): args = trex_parser.parse_args() - - server = CTRexServer(trex_daemon_port = args.daemon_port, trex_zmq_port = args.zmq_port, trex_path = args.trex_path, trex_files_path = args.files_path) + server = CTRexServer(trex_path = args.trex_path, trex_files_path = args.files_path, + trex_host = args.trex_host, trex_daemon_port = args.daemon_port, + trex_zmq_port = args.zmq_port) server.start() diff --git a/scripts/automation/trex_control_plane/server/zmq_monitor_thread.py b/scripts/automation/trex_control_plane/server/zmq_monitor_thread.py index 7a278af8..db9bf7da 100755 --- a/scripts/automation/trex_control_plane/server/zmq_monitor_thread.py +++ b/scripts/automation/trex_control_plane/server/zmq_monitor_thread.py @@ -22,7 +22,7 @@ class ZmqMonitorSession(threading.Thread): self.zmq_port = zmq_port
self.zmq_publisher = "tcp://localhost:{port}".format(port=self.zmq_port)
self.trexObj = trexObj
- self.expect_trex = self.trexObj.expect_trex # used to signal if T-Rex is expected to run and if data should be considered
+ self.expect_trex = self.trexObj.expect_trex # used to signal if TRex is expected to run and if data should be considered
self.decoder = JSONDecoder()
logger.info("ZMQ monitor initialization finished")
@@ -69,7 +69,7 @@ class ZmqMonitorSession(threading.Thread): # change TRexStatus from starting to Running once the first ZMQ dump is obtained and parsed successfully
self.first_dump = False
self.trexObj.set_status(TRexStatus.Running)
- self.trexObj.set_verbose_status("T-Rex is Running")
+ self.trexObj.set_verbose_status("TRex is Running")
logger.info("First ZMQ dump received and successfully parsed. TRex running state changed to 'Running'.")
diff --git a/scripts/automation/trex_control_plane/unit_tests/control_plane_general_test.py b/scripts/automation/trex_control_plane/unit_tests/control_plane_general_test.py index 95f259b8..32ad5243 100755 --- a/scripts/automation/trex_control_plane/unit_tests/control_plane_general_test.py +++ b/scripts/automation/trex_control_plane/unit_tests/control_plane_general_test.py @@ -9,7 +9,7 @@ Name: Description: - This script creates the functionality to test the performance of the T-Rex traffic generator control plane. + This script creates the functionality to test the performance of the TRex traffic generator control plane. The scenarios assumes a WORKING server is listening and processing the requests. :: diff --git a/scripts/automation/trex_control_plane/unit_tests/control_plane_unit_test.py b/scripts/automation/trex_control_plane/unit_tests/control_plane_unit_test.py index 37130ee4..1120256c 100755 --- a/scripts/automation/trex_control_plane/unit_tests/control_plane_unit_test.py +++ b/scripts/automation/trex_control_plane/unit_tests/control_plane_unit_test.py @@ -18,7 +18,7 @@ class TRexCPConfiguringPlugin(Plugin): super(TRexCPConfiguringPlugin, self).options(parser, env) parser.add_option('-t', '--trex-server', action='store', dest='trex_server', default='trex-dan', - help='Specify T-Rex server hostname. This server will be used to test control-plane functionality.') + help='Specify TRex server hostname. This server will be used to test control-plane functionality.') def configure(self, options, conf): if options.trex_server: diff --git a/scripts/automation/trex_control_plane/unit_tests/functional_test.py b/scripts/automation/trex_control_plane/unit_tests/functional_test.py index f742403d..30836985 100755 --- a/scripts/automation/trex_control_plane/unit_tests/functional_test.py +++ b/scripts/automation/trex_control_plane/unit_tests/functional_test.py @@ -37,7 +37,7 @@ class CTRexStartStop_Test(CControlPlaneGeneral_Test): def test_parameter_name_error(self): ret = self.trex.start_trex( c = 4, - wrong_key = 1.1, # <----- This key does not exists in T-Rex API + wrong_key = 1.1, # <----- This key does not exists in TRex API d = 70, f = 'avl/sfr_delay_10_1g.yaml', nc = True, @@ -50,7 +50,7 @@ class CTRexStartStop_Test(CControlPlaneGeneral_Test): run_status = self.trex.get_running_status() assert isinstance(run_status, dict) assert_equal (run_status['state'], TRexStatus.Idle ) - assert_equal (run_status['verbose'], "T-Rex run failed due to wrong input parameters, or due to reachability issues.") + assert_equal (run_status['verbose'], "TRex run failed due to wrong input parameters, or due to reachability issues.") assert_raises(TRexError, self.trex.get_running_info) def test_too_early_sample(self): @@ -83,33 +83,33 @@ class CTRexStartStop_Test(CControlPlaneGeneral_Test): assert self.trex.is_running() == False def test_start_more_than_once_same_user(self): - assert self.trex.is_running() == False # first, make sure T-Rex is not running - ret = self.trex.start_trex(**self.valid_start_params) # start 1st T-Rex run + assert self.trex.is_running() == False # first, make sure TRex is not running + ret = self.trex.start_trex(**self.valid_start_params) # start 1st TRex run assert ret == True # make sure 1st run submitted successfuly # time.sleep(1) - assert_raises(TRexInUseError, self.trex.start_trex, **self.valid_start_params) # try to start T-Rex again + assert_raises(TRexInUseError, self.trex.start_trex, **self.valid_start_params) # try to start TRex again ret = self.trex.stop_trex() assert ret==True # make sure stop succeeded assert self.trex.is_running() == False def test_start_more_than_once_different_users(self): - assert self.trex.is_running() == False # first, make sure T-Rex is not running - ret = self.trex.start_trex(**self.valid_start_params) # start 1st T-Rex run + assert self.trex.is_running() == False # first, make sure TRex is not running + ret = self.trex.start_trex(**self.valid_start_params) # start 1st TRex run assert ret == True # make sure 1st run submitted successfuly # time.sleep(1) tmp_trex = CTRexClient(self.trex_server_name) # initialize another client connecting same server - assert_raises(TRexInUseError, tmp_trex.start_trex, **self.valid_start_params) # try to start T-Rex again + assert_raises(TRexInUseError, tmp_trex.start_trex, **self.valid_start_params) # try to start TRex again ret = self.trex.stop_trex() assert ret==True # make sure stop succeeded assert self.trex.is_running() == False def test_simultaneous_sampling(self): - assert self.trex.is_running() == False # first, make sure T-Rex is not running + assert self.trex.is_running() == False # first, make sure TRex is not running tmp_trex = CTRexClient(self.trex_server_name) # initialize another client connecting same server - ret = self.trex.start_trex(**self.valid_start_params) # start T-Rex run + ret = self.trex.start_trex(**self.valid_start_params) # start TRex run assert ret == True # make sure 1st run submitted successfuly time.sleep(6) @@ -123,7 +123,7 @@ class CTRexStartStop_Test(CControlPlaneGeneral_Test): assert tmp_trex.get_result_obj().is_valid_hist() == True if self.trex.get_result_obj().is_done_warmup(): assert tmp_trex.get_result_obj().is_done_warmup() == True - # except TRexError as inst: # T-Rex might have stopped between is_running result and get_running_info() call + # except TRexError as inst: # TRex might have stopped between is_running result and get_running_info() call # # hence, ingore that case # break @@ -132,7 +132,7 @@ class CTRexStartStop_Test(CControlPlaneGeneral_Test): def test_fast_toggling(self): assert self.trex.is_running() == False for i in range(20): - ret = self.trex.start_trex(**self.valid_start_params) # start T-Rex run + ret = self.trex.start_trex(**self.valid_start_params) # start TRex run assert ret == True assert self.trex.is_running() == False # we expect the status to be 'Starting' ret = self.trex.stop_trex() diff --git a/scripts/automation/trex_perf.py b/scripts/automation/trex_perf.py index 5d11f549..beec5061 100755 --- a/scripts/automation/trex_perf.py +++ b/scripts/automation/trex_perf.py @@ -950,7 +950,7 @@ def generate_job_id (): return (str(int(random.getrandbits(32)))) def print_header (): - logger.log("--== T-Trex Performance Tool v1.0 (2014) ==--") + logger.log("--== TRex Performance Tool v1.0 (2014) ==--") # print startup summary def log_startup_summary (job_summary): @@ -960,10 +960,10 @@ def log_startup_summary (job_summary): logger.log("\nWork Request Details:\n") logger.log("Setup Details:\n") - logger.log("T-Rex Config File: {0}".format(job_summary['config_file'])) + logger.log("TRex Config File: {0}".format(job_summary['config_file'])) logger.log("Machine Name: {0}".format(trex_config['trex_name'])) - logger.log("T-Rex Type: {0}".format(trex_config['trex_machine_type'])) - logger.log("T-Rex Dual Int. Tx: {0}".format(trex_config['trex_is_dual'])) + logger.log("TRex Type: {0}".format(trex_config['trex_machine_type'])) + logger.log("TRex Dual Int. Tx: {0}".format(trex_config['trex_is_dual'])) logger.log("Router Interface: {0}".format(trex_config['router_interface'])) logger.log("\nJob Details:\n") diff --git a/scripts/cfg/cfg_example1.yaml b/scripts/cfg/cfg_example1.yaml index bfd7fd88..224fb15e 100755 --- a/scripts/cfg/cfg_example1.yaml +++ b/scripts/cfg/cfg_example1.yaml @@ -6,8 +6,15 @@ enable_zmq_pub : true # enable publisher for stats data zmq_pub_port : 4507 telnet_port : 4508 # the telnet port in case it is enable ( with intercative mode ) + platform : + master_thread_id : 12 + latency_thread_id : 13 + dual_if : + - socket : 1 + threads : [8,9,10,11] + port_info : # set eh mac addr - - dest_mac : [0x1,0x0,0x0,0x1,0x0,0x00] # port 0 + - dest_mac : [1,0x0,0x0,0x1,0x0,0x00] # port 0 src_mac : [0x2,0x0,0x0,0x2,0x0,0x00] - dest_mac : [0x3,0x0,0x0,0x3,0x0,0x00] # port 1 src_mac : [0x4,0x0,0x0,0x4,0x0,0x00] diff --git a/src/bp_sim.cpp b/src/bp_sim.cpp index 7cbeb09d..c3581c55 100755 --- a/src/bp_sim.cpp +++ b/src/bp_sim.cpp @@ -2350,21 +2350,13 @@ void operator >> (const YAML::Node& node, CFlowYamlDpPkt & fi) { void operator >> (const YAML::Node& node, CVlanYamlInfo & fi) { uint32_t tmp; - try { - node["enable"] >> tmp ; - fi.m_enable=tmp; - }catch ( const std::exception& e ) { - - } - - try { - node["vlan0"] >> tmp; - fi.m_vlan_per_port[0] = tmp; - node["vlan1"] >> tmp; - fi.m_vlan_per_port[1] = tmp; - }catch ( const std::exception& e ) { - // there is a default - + if ( node.FindValue("enable") ){ + node["enable"] >> tmp ; + fi.m_enable=tmp; + node["vlan0"] >> tmp; + fi.m_vlan_per_port[0] = tmp; + node["vlan1"] >> tmp; + fi.m_vlan_per_port[1] = tmp; } } @@ -2372,15 +2364,15 @@ void operator >> (const YAML::Node& node, CVlanYamlInfo & fi) { void operator >> (const YAML::Node& node, CFlowYamlInfo & fi) { node["name"] >> fi.m_name; - - try { + + if ( node.FindValue("client_pool") ){ node["client_pool"] >> fi.m_client_pool_name; - } catch ( const std::exception& e ) { + }else{ fi.m_client_pool_name = "default"; } - try { + if ( node.FindValue("server_pool") ){ node["server_pool"] >> fi.m_server_pool_name; - } catch ( const std::exception& e ) { + }else{ fi.m_server_pool_name = "default"; } @@ -2393,37 +2385,38 @@ void operator >> (const YAML::Node& node, CFlowYamlInfo & fi) { fi.m_rtt_sec = t/1000000.0; node["w"] >> fi.m_w; - try { + if ( node.FindValue("cap_ipg") ){ node["cap_ipg"] >> fi.m_cap_mode; fi.m_cap_mode_was_set =true; - } catch ( const std::exception& e ) { + }else{ fi.m_cap_mode_was_set =false; } - try { + if ( node.FindValue("wlength") ){ node["wlength"] >> fi.m_wlength; fi.m_wlength_set=true; - } catch ( const std::exception& e ) { + }else{ fi.m_wlength_set=false; fi.m_wlength =500; } - try { + if ( node.FindValue("limit") ){ node["limit"] >> fi.m_limit; fi.m_limit_was_set = true; - } catch ( const std::exception& e ) { + }else{ fi.m_limit_was_set = false; fi.m_limit = 0; } - try { + if ( node.FindValue("plugin_id") ){ uint32_t plugin_val; node["plugin_id"] >> plugin_val; fi.m_plugin_id=plugin_val; - } catch ( const std::exception& e ) { + }else{ fi.m_plugin_id=0; } + fi.m_one_app_server_was_set = false; fi.m_one_app_server = false; if ( utl_yaml_read_ip_addr(node, @@ -2446,30 +2439,26 @@ void operator >> (const YAML::Node& node, CFlowYamlInfo & fi) { } - try { - int i; - const YAML::Node& dyn_pyload = node["dyn_pyload"]; - for(unsigned i=0;i<dyn_pyload.size();i++) { - CFlowYamlDpPkt fd; - dyn_pyload[i] >> fd; - if ( fi.m_dpPkt == 0 ){ - fi.m_dpPkt = new CFlowYamlDynamicPyloadPlugin(); - if (fi.m_plugin_id == 0) { - fi.m_plugin_id = mpDYN_PYLOAD; - }else{ - fprintf(stderr," plugin should be zero with dynamic pyload program"); - exit(-1); - } - } - - fd.Dump(stdout); - - fi.m_dpPkt->Add(fd); - printf(" here "); - } - } catch ( const std::exception& e ) { - fi.m_dpPkt=0; - } + if ( node.FindValue("dyn_pyload") ){ + int i; + const YAML::Node& dyn_pyload = node["dyn_pyload"]; + for(unsigned i=0;i<dyn_pyload.size();i++) { + CFlowYamlDpPkt fd; + dyn_pyload[i] >> fd; + if ( fi.m_dpPkt == 0 ){ + fi.m_dpPkt = new CFlowYamlDynamicPyloadPlugin(); + if (fi.m_plugin_id == 0) { + fi.m_plugin_id = mpDYN_PYLOAD; + }else{ + fprintf(stderr," plugin should be zero with dynamic pyload program"); + exit(-1); + } + } + fi.m_dpPkt->Add(fd); + } + }else{ + fi.m_dpPkt=0; + } } @@ -2478,13 +2467,12 @@ void operator >> (const YAML::Node& node, CFlowsYamlInfo & flows_info) { node["duration"] >> flows_info.m_duration_sec; - try { - node["generator"] >> flows_info.m_tuple_gen; - flows_info.m_tuple_gen_was_set =true; - } catch ( const std::exception& e ) { - flows_info.m_tuple_gen_was_set =false; + if ( node.FindValue("generator") ) { + node["generator"] >> flows_info.m_tuple_gen; + flows_info.m_tuple_gen_was_set =true; + }else{ + flows_info.m_tuple_gen_was_set =false; } - // m_ipv6_set will be true if and only if both src_ipv6 // and dst_ipv6 are provided. These are used to set @@ -2500,7 +2488,8 @@ void operator >> (const YAML::Node& node, CFlowsYamlInfo & flows_info) { // formed by providing src_ipv6,dst_ipv6 and specifying // {0,0,0,0,0,0xffff} flows_info.m_ipv6_set=true; - try { + + if ( node.FindValue("src_ipv6") ) { const YAML::Node& src_ipv6_info = node["src_ipv6"]; if (src_ipv6_info.size() == 6 ){ for(unsigned i=0;i<src_ipv6_info.size();i++) { @@ -2509,14 +2498,13 @@ void operator >> (const YAML::Node& node, CFlowsYamlInfo & flows_info) { node[i] >> fi; flows_info.m_src_ipv6.push_back(fi); } - }else{ - flows_info.m_ipv6_set=false; } - } catch ( const std::exception& e ) { + }else{ flows_info.m_ipv6_set=false; } - try { + + if ( node.FindValue("dst_ipv6") ) { const YAML::Node& dst_ipv6_info = node["dst_ipv6"]; if (dst_ipv6_info.size() == 6 ){ for(unsigned i=0;i<dst_ipv6_info.size();i++) { @@ -2525,67 +2513,65 @@ void operator >> (const YAML::Node& node, CFlowsYamlInfo & flows_info) { node[i] >> fi; flows_info.m_dst_ipv6.push_back(fi); } - }else{ - flows_info.m_ipv6_set=false; } - } catch ( const std::exception& e ) { + }else{ flows_info.m_ipv6_set=false; } - try { + if ( node.FindValue("cap_ipg") ) { node["cap_ipg"] >> flows_info.m_cap_mode; flows_info.m_cap_mode_set=true; - } catch ( const std::exception& e ) { + }else{ flows_info.m_cap_mode=false; flows_info.m_cap_mode_set=false; } - double t; - try { + double t=0.0; + + if ( node.FindValue("cap_ipg_min") ) { node["cap_ipg_min"] >> t ; flows_info.m_cap_ipg_min = t/1000000.0; flows_info.m_cap_ipg_min_set=true; - } catch ( const std::exception& e ) { + }else{ flows_info.m_cap_ipg_min_set=false; flows_info.m_cap_ipg_min = 20; } - try { + if ( node.FindValue("cap_override_ipg") ) { node["cap_override_ipg"] >> t; flows_info.m_cap_overide_ipg = t/1000000.0; flows_info.m_cap_overide_ipg_set = true; - } catch ( const std::exception& e ) { + }else{ flows_info.m_cap_overide_ipg_set = false; flows_info.m_cap_overide_ipg = 0; } - try { + if (node.FindValue("wlength")) { node["wlength"] >> flows_info.m_wlength; flows_info.m_wlength_set=true; - } catch ( const std::exception& e ) { + }else{ flows_info.m_wlength_set=false; flows_info.m_wlength =100; } - try { + if (node.FindValue("one_app_server")) { node["one_app_server"] >> flows_info.m_one_app_server; flows_info.m_one_app_server_was_set=true; - } catch ( const std::exception& e ) { + }else{ flows_info.m_one_app_server =false; flows_info.m_one_app_server_was_set=false; } - try { - node["vlan"] >> flows_info.m_vlan_info; - } catch ( const std::exception& e ) { - } - try { - node["mac_override_by_ip"] >> flows_info.m_mac_replace_by_ip; - } catch ( const std::exception& e ) { - flows_info.m_mac_replace_by_ip =false; + if (node.FindValue("vlan")) { + node["vlan"] >> flows_info.m_vlan_info; } + if (node.FindValue("mac_override_by_ip")) { + node["mac_override_by_ip"] >> flows_info.m_mac_replace_by_ip; + }else{ + flows_info.m_mac_replace_by_ip =false; + } const YAML::Node& mac_info = node["mac"]; for(unsigned i=0;i<mac_info.size();i++) { @@ -2593,7 +2579,7 @@ void operator >> (const YAML::Node& node, CFlowsYamlInfo & flows_info) { const YAML::Node & node =mac_info; node[i] >> fi; flows_info.m_mac_base.push_back(fi); - } + } const YAML::Node& cap_info = node["cap_info"]; for(unsigned i=0;i<cap_info.size();i++) { @@ -3177,7 +3163,7 @@ bool CFlowGenListPerThread::Create(uint32_t thread_id, /* split the clients to threads */ CTupleGenYamlInfo * tuple_gen = &m_flow_list->m_yaml_info.m_tuple_gen; - m_smart_gen.Create(0,m_thread_id,m_flow_list->is_mac_info_configured); + m_smart_gen.Create(0,m_thread_id,m_flow_list->get_is_mac_conf()); /* split the clients to threads using the mask */ CIpPortion portion; @@ -3191,7 +3177,7 @@ bool CFlowGenListPerThread::Create(uint32_t thread_id, portion.m_ip_end, get_longest_flow(i,true), get_total_kcps(i,true)*1000, - m_flow_list, + &m_flow_list->m_mac_info, tuple_gen->m_client_pool[i].m_tcp_aging_sec, tuple_gen->m_client_pool[i].m_udp_aging_sec ); @@ -3962,7 +3948,7 @@ int CFlowGenList::load_from_mac_file(std::string file_name) { printf(" ERROR no mac_file is set, file %s does not exist \n",file_name.c_str()); exit(-1); } - is_mac_info_configured = true; + m_mac_info.set_configured(true); try { std::ifstream fin((char *)file_name.c_str()); @@ -3970,7 +3956,7 @@ int CFlowGenList::load_from_mac_file(std::string file_name) { YAML::Node doc; parser.GetNextDocument(doc); - doc[0] >> m_mac_info; + doc[0] >> m_mac_info.get_mac_info(); } catch ( const std::exception& e ) { std::cout << e.what() << "\n"; m_mac_info.clear(); @@ -3982,7 +3968,6 @@ int CFlowGenList::load_from_mac_file(std::string file_name) { int CFlowGenList::load_from_yaml(std::string file_name, uint32_t num_threads){ - is_mac_info_configured = false; uint8_t idx; m_yaml_info.load_from_yaml_file(file_name); if (m_yaml_info.verify_correctness(num_threads) ==false){ @@ -6643,25 +6628,6 @@ void CFlowYamlDynamicPyloadPlugin::Dump(FILE *fd){ } } -bool is_mac_info_conf(CFlowGenList *fl_list) { - if (fl_list) { - return fl_list->is_mac_info_configured; - } - return false; -} - -mac_addr_align_t * get_mac_addr_by_ip(CFlowGenList *fl_list, - uint32_t ip) { - if (fl_list && - fl_list->is_mac_info_configured && - fl_list->m_mac_info.count(ip)>0) { - return &fl_list->m_mac_info[ip]; - } - return NULL; -} - - - uint16_t CSimplePacketParser::getPktSize(){ uint16_t ip_len=0; if (m_ipv4) { diff --git a/src/bp_sim.h b/src/bp_sim.h index 18cd9b97..b7cfb20b 100755 --- a/src/bp_sim.h +++ b/src/bp_sim.h @@ -3448,6 +3448,7 @@ inline void CFlowGenListPerThread::free_last_flow_node(CGenNode *p){ free_node( p); } + class CFlowGenList { public: @@ -3475,12 +3476,12 @@ public: double get_total_tx_bps(); uint32_t get_total_repeat_flows(); double get_delta_flow_is_sec(); + bool get_is_mac_conf() { return m_mac_info.is_configured();} public: std::vector<CFlowGeneratorRec *> m_cap_gen; /* global info */ CFlowsYamlInfo m_yaml_info; /* global yaml*/ std::vector<CFlowGenListPerThread *> m_threads_info; - bool is_mac_info_configured; - std::map<uint32_t, mac_addr_align_t> m_mac_info; /* global mac info loaded form mac_file*/ + CFlowGenListMac m_mac_info; }; diff --git a/src/gtest/tuple_gen_test.cpp b/src/gtest/tuple_gen_test.cpp index 8791b67d..8a774e38 100755 --- a/src/gtest/tuple_gen_test.cpp +++ b/src/gtest/tuple_gen_test.cpp @@ -334,7 +334,8 @@ TEST(tuple_gen,GenerateTupleMac) { CClientPool gen; gen.Create(cdSEQ_DIST, - 0x10000001, 0x1000000f, 64000,2, &fl,true,0,0); + 0x10000001, 0x1000000f, 64000,2, &fl.m_mac_info,true,0,0); + CTupleBase result; uint32_t result_src; uint16_t result_port; @@ -348,6 +349,7 @@ TEST(tuple_gen,GenerateTupleMac) { result_mac = result.getClientMac(); EXPECT_EQ(result_src, (uint32_t)(0x10000001+i%2)); EXPECT_EQ(result_port, 1024+i/2); + printf("i:%d,mac:%d\n",i,result_mac->mac[3]); if (i%2==0) EXPECT_EQ(result_mac->mac[3], 5); else diff --git a/src/mac_mapping.h b/src/mac_mapping.h new file mode 100644 index 00000000..84151e8c --- /dev/null +++ b/src/mac_mapping.h @@ -0,0 +1,60 @@ +#ifndef MAC_MAPPING_H_ +#define MAC_MAPPING_H_ + +#define INUSED 0 +#define UNUSED 1 +typedef struct mac_addr_align_ { +public: + uint8_t mac[6]; + uint8_t inused; + uint8_t pad; +} mac_addr_align_t; + +typedef struct mac_mapping_ { + mac_addr_align_t mac; + uint32_t ip; +} mac_mapping_t; + +class CFlowGenListMac { +public: + CFlowGenListMac() { + set_configured(false); + } + + std::map<uint32_t, mac_addr_align_t> & + get_mac_info () { + return m_mac_info; + } + + bool is_configured() { + return is_mac_info_configured; + } + + void set_configured(bool is_conf) { + is_mac_info_configured = is_conf; + } + + void clear() { + set_configured(false); + m_mac_info.clear(); + } + + uint32_t is_mac_exist(uint32_t ip) { + if (is_configured()) { + return m_mac_info.count(ip); + } else { + return 0; + } + } + mac_addr_align_t* get_mac_addr_by_ip(uint32_t ip) { + if (is_mac_exist(ip)!=0) { + return &(m_mac_info[ip]); + } + return NULL; + } +private: + bool is_mac_info_configured; + std::map<uint32_t, mac_addr_align_t> m_mac_info; /* global mac info loaded form mac_file*/ +}; + +#endif //MAC_MAPPING_H_ diff --git a/src/main_dpdk.cpp b/src/main_dpdk.cpp index fe972d2f..a0af9fdf 100755 --- a/src/main_dpdk.cpp +++ b/src/main_dpdk.cpp @@ -4140,9 +4140,9 @@ int CGlobalPortCfg::start_send_master(){ if (CGlobalInfo::m_options.mac_file != "") { CGlobalInfo::m_options.preview.set_mac_ip_mapping_enable(true); m_fl.load_from_mac_file(CGlobalInfo::m_options.mac_file); - m_fl.is_mac_info_configured = true; + m_fl.m_mac_info.set_configured(true); } else { - m_fl.is_mac_info_configured = false; + m_fl.m_mac_info.set_configured(false); } m_expected_pps = m_fl.get_total_pps(); diff --git a/src/platform_cfg.cpp b/src/platform_cfg.cpp index a226a9ac..f0911611 100755 --- a/src/platform_cfg.cpp +++ b/src/platform_cfg.cpp @@ -189,107 +189,70 @@ void operator >> (const YAML::Node& node, CMacYamlInfo & mac_info) { } void operator >> (const YAML::Node& node, CPlatformMemoryYamlInfo & plat_info) { - try { - node["mbuf_64"] >> plat_info.m_mbuf[MBUF_64]; - } catch ( const std::exception& e ) { - } - try { - node["mbuf_128"] >> plat_info.m_mbuf[MBUF_128]; - } catch ( const std::exception& e ) { + if ( node.FindValue("mbuf_64") ){ + node["mbuf_64"] >> plat_info.m_mbuf[MBUF_64]; } - try { - node["mbuf_256"] >> plat_info.m_mbuf[MBUF_256]; - } catch ( const std::exception& e ) { + if ( node.FindValue("mbuf_128") ){ + node["mbuf_128"] >> plat_info.m_mbuf[MBUF_128]; } - try { - node["mbuf_512"] >> plat_info.m_mbuf[MBUF_512]; - } catch ( const std::exception& e ) { + if ( node.FindValue("mbuf_256") ){ + node["mbuf_256"] >> plat_info.m_mbuf[MBUF_256]; } - try { - node["mbuf_1024"] >> plat_info.m_mbuf[MBUF_1024]; - } catch ( const std::exception& e ) { + if ( node.FindValue("mbuf_512") ){ + node["mbuf_512"] >> plat_info.m_mbuf[MBUF_512]; } - try { - node["mbuf_2048"] >> plat_info.m_mbuf[MBUF_2048]; - } catch ( const std::exception& e ) { + if ( node.FindValue("mbuf_1024") ){ + node["mbuf_1024"] >> plat_info.m_mbuf[MBUF_1024]; } - try { - node["traffic_mbuf_64"] >> plat_info.m_mbuf[TRAFFIC_MBUF_64]; - } catch ( const std::exception& e ) { + if ( node.FindValue("mbuf_2048") ){ + node["mbuf_2048"] >> plat_info.m_mbuf[MBUF_2048]; } - try { - node["traffic_mbuf_128"] >> plat_info.m_mbuf[TRAFFIC_MBUF_128]; - } catch ( const std::exception& e ) { + if ( node.FindValue("traffic_mbuf_64") ){ + node["traffic_mbuf_64"] >> plat_info.m_mbuf[TRAFFIC_MBUF_64]; } - try { - node["traffic_mbuf_256"] >> plat_info.m_mbuf[TRAFFIC_MBUF_256]; - } catch ( const std::exception& e ) { + if ( node.FindValue("traffic_mbuf_128") ){ + node["traffic_mbuf_128"] >> plat_info.m_mbuf[TRAFFIC_MBUF_128]; } - try { - node["traffic_mbuf_512"] >> plat_info.m_mbuf[TRAFFIC_MBUF_512]; - } catch ( const std::exception& e ) { + if ( node.FindValue("traffic_mbuf_256") ){ + node["traffic_mbuf_256"] >> plat_info.m_mbuf[TRAFFIC_MBUF_256]; } - try { - node["traffic_mbuf_1024"] >> plat_info.m_mbuf[TRAFFIC_MBUF_1024]; - } catch ( const std::exception& e ) { + if ( node.FindValue("traffic_mbuf_512") ){ + node["traffic_mbuf_512"] >> plat_info.m_mbuf[TRAFFIC_MBUF_512]; } - try { - node["traffic_mbuf_2048"] >> plat_info.m_mbuf[TRAFFIC_MBUF_2048]; - } catch ( const std::exception& e ) { + if ( node.FindValue("traffic_mbuf_1024") ){ + node["traffic_mbuf_1024"] >> plat_info.m_mbuf[TRAFFIC_MBUF_1024]; } - try { - node["dp_flows"] >> plat_info.m_mbuf[MBUF_DP_FLOWS]; - } catch ( const std::exception& e ) { + if ( node.FindValue("traffic_mbuf_2048") ){ + node["traffic_mbuf_2048"] >> plat_info.m_mbuf[TRAFFIC_MBUF_2048]; } - try { - node["global_flows"] >> plat_info.m_mbuf[MBUF_GLOBAL_FLOWS]; - } catch ( const std::exception& e ) { + if ( node.FindValue("dp_flows") ){ + node["dp_flows"] >> plat_info.m_mbuf[MBUF_DP_FLOWS]; } -} - -void operator >> (const YAML::Node& node, CPlatformYamlInfo & plat_info) { - try { - node["port_limit"] >> plat_info.m_port_limit; - plat_info.m_port_limit_exist=true; - } catch ( const std::exception& e ) { - plat_info.m_port_limit=0xffffffff; + if ( node.FindValue("global_flows") ){ + node["global_flows"] >> plat_info.m_mbuf[MBUF_GLOBAL_FLOWS]; } +} - try { - const YAML::Node& interface_mask = node["interface_mask"]; - for(unsigned i=0;i<interface_mask.size();i++) { - std::string fi; - const YAML::Node & node = interface_mask; - node[i] >> fi; - plat_info.m_if_mask.push_back(fi); - } - plat_info.m_if_mask_exist=true; - } catch ( const std::exception& e ) { - - } +void operator >> (const YAML::Node& node, CPlatformYamlInfo & plat_info) { - try { - node["enable_zmq_pub"] >> plat_info.m_enable_zmq_pub; - node["zmq_pub_port"] >> plat_info.m_zmq_pub_port; - plat_info.m_enable_zmq_pub_exist = true; - } catch ( const std::exception& e ) { - plat_info.m_enable_zmq_pub_exist = false; + if (node.FindValue("interface_mask")) { + printf("WARNING interface_mask in not used any more !\n"); } /* must have interfaces */ @@ -301,31 +264,44 @@ void operator >> (const YAML::Node& node, CPlatformYamlInfo & plat_info) { plat_info.m_if_list.push_back(fi); } - try { - node["prefix"] >> plat_info.m_prefix; - } catch ( const std::exception& e ) { + + if ( node.FindValue("port_limit") ){ + node["port_limit"] >> plat_info.m_port_limit; + plat_info.m_port_limit_exist=true; } - try { - node["limit_memory"] >> plat_info.m_limit_memory; - } catch ( const std::exception& e ) { + + + plat_info.m_enable_zmq_pub_exist = true; + + if ( node.FindValue("enable_zmq_pub") ){ + node["enable_zmq_pub"] >> plat_info.m_enable_zmq_pub; + plat_info.m_enable_zmq_pub_exist = true; } - try { - node["c"] >> plat_info.m_thread_per_dual_if; - } catch ( const std::exception& e ) { + + if ( node.FindValue("zmq_pub_port") ){ + node["zmq_pub_port"] >> plat_info.m_zmq_pub_port; + plat_info.m_enable_zmq_pub_exist = true; + } + + if ( node.FindValue("prefix") ){ + node["prefix"] >> plat_info.m_prefix; } + if ( node.FindValue("limit_memory") ){ + node["limit_memory"] >> plat_info.m_limit_memory; + } + if ( node.FindValue("c") ){ + node["c"] >> plat_info.m_thread_per_dual_if; + } - try { - node["telnet_port"] >> plat_info.m_telnet_port; - plat_info.m_telnet_exist=true; - } catch ( const std::exception& e ) { - plat_info.m_telnet_port=4501; + if ( node.FindValue("telnet_port") ){ + node["telnet_port"] >> plat_info.m_telnet_port; + plat_info.m_telnet_exist=true; } - try { - node["port_bandwidth_gb"] >> plat_info.m_port_bandwidth_gb; - } catch ( const std::exception& e ) { + if ( node.FindValue("port_bandwidth_gb") ){ + node["port_bandwidth_gb"] >> plat_info.m_port_bandwidth_gb; } if ( node.FindValue("memory") ){ @@ -337,7 +313,7 @@ void operator >> (const YAML::Node& node, CPlatformYamlInfo & plat_info) { plat_info.m_platform.m_is_exists=true; } - try { + if ( node.FindValue("port_info") ) { const YAML::Node& mac_info = node["port_info"]; for(unsigned i=0;i<mac_info.size();i++) { CMacYamlInfo fi; @@ -346,7 +322,6 @@ void operator >> (const YAML::Node& node, CPlatformYamlInfo & plat_info) { plat_info.m_mac_info.push_back(fi); } plat_info.m_mac_info_exist = true; - }catch ( const std::exception& e ) { } } diff --git a/src/tuple_gen.cpp b/src/tuple_gen.cpp index e408f275..3c05cf05 100755 --- a/src/tuple_gen.cpp +++ b/src/tuple_gen.cpp @@ -57,7 +57,7 @@ void CClientPool::Create(IP_DIST_t dist_value, uint32_t max_ip, double l_flow, double t_cps, - CFlowGenList* fl_list, + CFlowGenListMac* mac_info, bool has_mac_map, uint16_t tcp_aging, uint16_t udp_aging) { @@ -65,10 +65,10 @@ void CClientPool::Create(IP_DIST_t dist_value, set_dist(dist_value); uint32_t total_ip = max_ip - min_ip +1; uint32_t avail_ip = total_ip; - if (has_mac_map && (fl_list!=NULL)) { + if (has_mac_map && (mac_info!=NULL)) { for(int idx=0;idx<total_ip;idx++){ mac_addr_align_t *mac_adr = NULL; - mac_adr = get_mac_addr_by_ip(fl_list, min_ip+idx); + mac_adr = mac_info->get_mac_addr_by_ip(min_ip+idx); if (mac_adr == NULL) { avail_ip--; } @@ -86,7 +86,7 @@ void CClientPool::Create(IP_DIST_t dist_value, if (has_mac_map) { for(int idx=0;idx<total_ip;idx++){ mac_addr_align_t *mac_adr = NULL; - mac_adr = get_mac_addr_by_ip(fl_list, min_ip+idx); + mac_adr = mac_info->get_mac_addr_by_ip( min_ip+idx); if (mac_adr != NULL) { m_ip_info[idx] = new CClientInfoL(has_mac_map); m_ip_info[idx]->set_ip(min_ip+idx); @@ -103,7 +103,7 @@ void CClientPool::Create(IP_DIST_t dist_value, if (has_mac_map) { for(int idx=0;idx<total_ip;idx++){ mac_addr_align_t *mac_adr = NULL; - mac_adr = get_mac_addr_by_ip(fl_list, min_ip+idx); + mac_adr = mac_info->get_mac_addr_by_ip(min_ip+idx); if (mac_adr != NULL) { m_ip_info[idx] = new CClientInfo(has_mac_map); m_ip_info[idx]->set_ip(min_ip+idx); @@ -123,20 +123,19 @@ void CClientPool::Create(IP_DIST_t dist_value, CreateBase(); } -void delay(int msec); bool CTupleGeneratorSmart::add_client_pool(IP_DIST_t client_dist, uint32_t min_client, uint32_t max_client, double l_flow, double t_cps, - CFlowGenList* fl_list, + CFlowGenListMac* mac_info, uint16_t tcp_aging, uint16_t udp_aging){ assert(max_client>=min_client); CClientPool* pool = new CClientPool(); pool->Create(client_dist, min_client, max_client, - l_flow, t_cps, fl_list, has_mac_mapping, + l_flow, t_cps, mac_info, m_has_mac_mapping, tcp_aging, udp_aging); m_client_pool.push_back(pool); @@ -171,13 +170,13 @@ bool CTupleGeneratorSmart::Create(uint32_t _id, m_thread_id = thread_id; m_id = _id; m_was_init=true; - has_mac_mapping = has_mac; + m_has_mac_mapping = has_mac; return(true); } void CTupleGeneratorSmart::Delete(){ m_was_init=false; - has_mac_mapping = false; + m_has_mac_mapping = false; for (int idx=0;idx<m_client_pool.size();idx++) { m_client_pool[idx]->Delete(); @@ -246,30 +245,12 @@ void operator >> (const YAML::Node& node, CTupleGenPoolYaml & fi) { fi.m_tcp_aging_sec = 0; fi.m_udp_aging_sec = 0; fi.m_dual_interface_mask = 0; - try { - utl_yaml_read_uint32(node,"clients_per_gb",fi.m_number_of_clients_per_gb); - } catch ( const std::exception& e ) { - ;} - try { - utl_yaml_read_uint32(node,"min_clients",fi.m_min_clients); - } catch ( const std::exception& e ) { - ;} - try { - utl_yaml_read_ip_addr(node,"dual_port_mask",fi.m_dual_interface_mask); - } catch ( const std::exception& e ) { - ;} - try { - utl_yaml_read_uint16(node,"tcp_aging",fi.m_tcp_aging_sec); - } catch ( const std::exception& e ) { - ;} - try { - utl_yaml_read_uint16(node,"udp_aging",fi.m_udp_aging_sec); - } catch ( const std::exception& e ) { - ;} - try { - node["track_ports"] >> fi.m_is_bundling; - } catch ( const std::exception& e ) { - ;} + utl_yaml_read_uint32(node,"clients_per_gb",fi.m_number_of_clients_per_gb); + utl_yaml_read_uint32(node,"min_clients",fi.m_min_clients); + utl_yaml_read_ip_addr(node,"dual_port_mask",fi.m_dual_interface_mask); + utl_yaml_read_uint16(node,"tcp_aging",fi.m_tcp_aging_sec); + utl_yaml_read_uint16(node,"udp_aging",fi.m_udp_aging_sec); + utl_yaml_read_bool(node,"track_ports",fi.m_is_bundling); } void copy_global_pool_para(CTupleGenPoolYaml & src, CTupleGenPoolYaml & dst) { if (src.m_number_of_clients_per_gb == 0) diff --git a/src/tuple_gen.h b/src/tuple_gen.h index c0add0be..29adbd69 100755 --- a/src/tuple_gen.h +++ b/src/tuple_gen.h @@ -37,10 +37,97 @@ limitations under the License. #include "common/c_common.h" #include <bitset> #include <yaml-cpp/yaml.h> - +#include <mac_mapping.h> #include <random> +class CTupleBase { +public: + CTupleBase() { + m_client_mac.inused = UNUSED; + } + uint32_t getClient() { + return m_client_ip; + } + void setClient(uint32_t ip) { + m_client_ip = ip; + } + uint32_t getClientId() { + return m_client_idx; + } + void setClientId(uint32_t id) { + m_client_idx = id; + } + + uint32_t getServer(){ + return m_server_ip; + } + void setServer(uint32_t ip) { + m_server_ip = ip; + } + uint32_t getServerId(){ + return m_server_idx; + } + void setServerId(uint32_t id) { + m_server_idx = id; + } + uint16_t getServerPort() { + return m_server_port; + } + void setServerPort(uint16_t port) { + m_server_port = port; + } + uint16_t getClientPort() { + return m_client_port; + } + void setClientPort(uint16_t port) { + m_client_port = port; + } + mac_addr_align_t* getClientMac() { + return &m_client_mac; + } + void setClientMac(mac_addr_align_t* mac_info) { + if (mac_info != NULL) { + memcpy(&m_client_mac, mac_info, sizeof(mac_addr_align_t)); + m_client_mac.inused = INUSED; + } else { + m_client_mac.inused = UNUSED; + } + } + void setClientTuple(uint32_t ip,mac_addr_align_t*mac,uint16_t port) { + setClient(ip); + setClientMac(mac); + setClientPort(port); + } + void setClientAll2(uint32_t id, uint32_t ip,uint16_t port) { + setClientId(id); + setClient(ip); + setClientPort(port); + } + + void setServerAll(uint32_t id, uint32_t ip) { + setServerId(id); + setServer(ip); + } + void getClientAll(uint32_t & id, uint32_t & ip, uint32_t & port) { + id = getClientId(); + ip = getClient(); + port = getClientPort(); + } + void getServerAll(uint32_t & id, uint32_t & ip) { + id = getServerId(); + ip = getServer(); + } +private: + uint32_t m_client_ip; + uint32_t m_client_idx; + uint32_t m_server_ip; + uint32_t m_server_idx; + mac_addr_align_t m_client_mac; + uint16_t m_client_port; + uint16_t m_server_port; +}; + /* @@ -59,9 +146,8 @@ limitations under the License. #define FOREACH(vector) for(int i=0;i<vector.size();i++) -/* Client distribution */ - +/* Client distribution */ typedef enum { cdSEQ_DIST = 0, cdRANDOM_DIST = 1, @@ -69,22 +155,6 @@ typedef enum { cdMAX_DIST = 3 } IP_DIST_t ; -#define INUSED 0 -#define UNUSED 1 -typedef struct mac_addr_align_ { -public: - uint8_t mac[6]; - uint8_t inused; - uint8_t pad; -} mac_addr_align_t; - -typedef struct mac_mapping_ { - mac_addr_align_t mac; - uint32_t ip; -} mac_mapping_t; - - - /* For type 1, we generator port by maintaining a 64K bit array for each port. * In this case, we cannot support large number of clients due to memory exhausted. * @@ -99,13 +169,13 @@ typedef struct mac_mapping_ { #define TYPE2 1 #define MAX_TYPE 3 - class CIpInfoBase { public: virtual mac_addr_align_t* get_mac() { return NULL;} virtual void set_mac(mac_addr_align_t*){;} virtual uint16_t get_new_free_port() = 0; virtual void return_port(uint16_t a) = 0; + virtual void generate_tuple(CTupleBase & tuple) = 0; virtual void return_all_ports() = 0; uint32_t get_ip() { return m_ip; @@ -113,10 +183,8 @@ class CIpInfoBase { void set_ip(uint32_t ip) { m_ip = ip; } - virtual ~CIpInfoBase() {} - - public: + protected: uint32_t m_ip; }; @@ -236,31 +304,37 @@ class CIpInfo : public CIpInfoBase { }; class CClientInfo : public CIpInfo { - public: - CClientInfo (bool has_mac) { - if (has_mac==true) { - m_mac = new mac_addr_align_t(); - } else { - m_mac = NULL; - } - } - CClientInfo () { +public: + CClientInfo (bool has_mac) { + if (has_mac==true) { + m_mac = new mac_addr_align_t(); + } else { m_mac = NULL; } - - mac_addr_align_t* get_mac() { - return m_mac; - } - void set_mac(mac_addr_align_t *mac) { - memcpy(m_mac, mac, sizeof(mac_addr_align_t)); - } - ~CClientInfo() { - if (m_mac!=NULL){ - delete m_mac; - } + } + CClientInfo () { + m_mac = NULL; + } + + mac_addr_align_t* get_mac() { + return m_mac; + } + void set_mac(mac_addr_align_t *mac) { + memcpy(m_mac, mac, sizeof(mac_addr_align_t)); + } + ~CClientInfo() { + if (m_mac!=NULL){ + delete m_mac; + m_mac=NULL; } - private: - mac_addr_align_t *m_mac; + } + + void generate_tuple(CTupleBase & tuple) { + tuple.setClientTuple(m_ip, m_mac, + get_new_free_port()); + } +private: + mac_addr_align_t *m_mac; }; class CClientInfoL : public CIpInfoL { @@ -275,100 +349,44 @@ public: CClientInfoL () { m_mac = NULL; } - + mac_addr_align_t* get_mac() { return m_mac; } + void set_mac(mac_addr_align_t *mac) { memcpy(m_mac, mac, sizeof(mac_addr_align_t)); } + ~CClientInfoL() { - if (m_mac!=NULL) { + if (m_mac!=NULL){ delete m_mac; + m_mac=NULL; } } + + void generate_tuple(CTupleBase & tuple) { + tuple.setClientTuple(m_ip, m_mac, + get_new_free_port()); + } private: mac_addr_align_t *m_mac; }; class CServerInfo : public CIpInfo { - ; + void generate_tuple(CTupleBase & tuple) { + tuple.setServer(m_ip); + } }; class CServerInfoL : public CIpInfoL { - ; -}; - - -class CTupleBase { -public: - CTupleBase() { - m_client_mac.inused = UNUSED; - } - uint32_t getClient() { - return m_client_ip; - } - void setClient(uint32_t ip) { - m_client_ip = ip; - } - uint32_t getClientId() { - return m_client_idx; - } - void setClientId(uint32_t id) { - m_client_idx = id; - } - - uint32_t getServer(){ - return m_server_ip; - } - void setServer(uint32_t ip) { - m_server_ip = ip; - } - uint32_t getServerId(){ - return m_server_idx; - } - void setServerId(uint32_t id) { - m_server_idx = id; - } - uint16_t getServerPort() { - return m_server_port; - } - void setServerPort(uint16_t port) { - m_server_port = port; - } - uint16_t getClientPort() { - return m_client_port; - } - void setClientPort(uint16_t port) { - m_client_port = port; - } - mac_addr_align_t* getClientMac() { - return &m_client_mac; - } - void setClientMac(mac_addr_align_t* mac_info) { - if (mac_info != NULL) { - memcpy(&m_client_mac, mac_info, sizeof(mac_addr_align_t)); - m_client_mac.inused = INUSED; - } else { - m_client_mac.inused = UNUSED; - } - } -private: - uint32_t m_client_ip; - uint32_t m_client_idx; - uint32_t m_server_ip; - uint32_t m_server_idx; - mac_addr_align_t m_client_mac; - uint16_t m_client_port; - uint16_t m_server_port; + void generate_tuple(CTupleBase & tuple) { + tuple.setServer(m_ip); + } }; -class CFlowGenList; -mac_addr_align_t * get_mac_addr_by_ip(CFlowGenList *fl_list, - uint32_t ip); -bool is_mac_info_conf(CFlowGenList *fl_list); class CIpPool { public: @@ -384,7 +402,8 @@ class CIpPool { m_active_alloc++; return (port); } - bool is_valid_ip(uint32_t ip){ + + bool is_valid_ip(uint32_t ip){ CIpInfoBase* ip_front = m_ip_info.front(); CIpInfoBase* ip_back = m_ip_info.back(); if ((ip>=ip_front->get_ip()) && @@ -425,6 +444,9 @@ class CIpPool { inc_cur_idx(); return res_idx; } + + + void set_dist(IP_DIST_t dist) { if (dist>=cdMAX_DIST) { m_dist = cdSEQ_DIST; @@ -494,13 +516,20 @@ class CIpPool { class CClientPool : public CIpPool { public: - void GenerateTuple(CTupleBase & tuple) { - uint32_t idx = generate_ip(); - tuple.setClientId(idx); - tuple.setClient(get_ip(idx)); - tuple.setClientMac(get_mac(idx)); - tuple.setClientPort(GenerateOnePort(idx)); + + uint32_t GenerateTuple(CTupleBase & tuple) { + uint32_t idx = generate_ip(); + CIpInfoBase* ip_info = m_ip_info[idx]; + ip_info->generate_tuple(tuple); + + tuple.setClientId(idx); + if (tuple.getClientPort()==ILLEGAL_PORT) { + m_port_allocation_error++; + } + m_active_alloc++; + return idx; } + uint16_t get_tcp_aging() { return m_tcp_aging; } @@ -512,10 +541,11 @@ public: uint32_t max_ip, double l_flow, double t_cps, - CFlowGenList* fl_list, + CFlowGenListMac* mac_info, bool has_mac_map, uint16_t tcp_aging, uint16_t udp_aging); + public: uint16_t m_tcp_aging; uint16_t m_udp_aging; @@ -571,11 +601,9 @@ private: class CServerPool : public CServerPoolBase { public: - CIpPool *gen; void GenerateTuple(CTupleBase & tuple) { uint32_t idx = gen->generate_ip(); - tuple.setServerId(idx); - tuple.setServer(gen->get_ip(idx)); + tuple.setServerAll(idx, gen->get_ip(idx)); } uint16_t GenerateOnePort(uint32_t idx) { return gen->GenerateOnePort(idx); @@ -590,11 +618,14 @@ public: if (gen!=NULL) { gen->Delete(); delete gen; + gen=NULL; } } uint32_t get_total_ips() { return gen->m_ip_info.size(); } +private: + CIpPool *gen; }; /* generate for each template */ @@ -651,7 +682,7 @@ public: public: CTupleGeneratorSmart(){ m_was_init=false; - has_mac_mapping = false; + m_has_mac_mapping = false; } bool Create(uint32_t _id, uint32_t thread_id, bool has_mac=false); @@ -675,7 +706,7 @@ public: uint32_t max_client, double l_flow, double t_cps, - CFlowGenList* fl_list, + CFlowGenListMac* mac_info, uint16_t tcp_aging, uint16_t udp_aging); bool add_server_pool(IP_DIST_t server_dist, @@ -702,7 +733,7 @@ private: std::vector<CClientPool*> m_client_pool; std::vector<CServerPoolBase*> m_server_pool; bool m_was_init; - bool has_mac_mapping; + bool m_has_mac_mapping; }; class CTupleTemplateGeneratorSmart { @@ -721,15 +752,13 @@ public: m_server_gen->GenerateTuple(tuple); m_cache_client_ip = tuple.getClient(); m_cache_client_idx = tuple.getClientId(); - m_cache_server_ip = tuple.getServer(); - m_cache_server_idx = tuple.getServerId(); + tuple.getServerAll(m_cache_server_idx, m_cache_server_ip); }else{ - tuple.setServer(m_cache_server_ip); - tuple.setServerId(m_cache_server_idx); - tuple.setClient(m_cache_client_ip); - tuple.setClientId(m_cache_client_idx); - tuple.setClientPort( - m_client_gen->GenerateOnePort(m_cache_client_idx)); + tuple.setServerAll(m_cache_server_idx, + m_cache_server_ip); + tuple.setClientAll2(m_cache_client_idx, + m_cache_client_ip, + m_client_gen->GenerateOnePort(m_cache_client_idx)); } m_cnt++; if (m_cnt>=m_w) { diff --git a/src/utl_yaml.cpp b/src/utl_yaml.cpp index 237e85af..5f3ca735 100755 --- a/src/utl_yaml.cpp +++ b/src/utl_yaml.cpp @@ -70,13 +70,15 @@ bool utl_yaml_read_ip_addr(const YAML::Node& node, std::string tmp; uint32_t ip; bool res=false; - try { + if ( node.FindValue(name) ) { node[name] >> tmp ; if ( my_inet_pton4((char *)tmp.c_str(), (unsigned char *)&ip) ){ val=PKT_NTOHL(ip); res=true; + }else{ + printf(" ERROR not a valid ip %s \n",(char *)tmp.c_str()); + exit(-1); } - }catch ( const std::exception& e ) { } return (res); } @@ -85,14 +87,11 @@ bool utl_yaml_read_uint32(const YAML::Node& node, std::string name, uint32_t & val){ bool res=false; - - try { + if ( node.FindValue(name) ) { node[name] >> val ; res=true; - }catch ( const std::exception& e ) { } return (res); - } bool utl_yaml_read_uint16(const YAML::Node& node, @@ -100,12 +99,10 @@ bool utl_yaml_read_uint16(const YAML::Node& node, uint16_t & val){ uint32_t val_tmp; bool res=false; - - try { + if ( node.FindValue(name) ) { node[name] >> val_tmp ; val = (uint16_t)val_tmp; res=true; - }catch ( const std::exception& e ) { } } @@ -113,10 +110,9 @@ bool utl_yaml_read_bool(const YAML::Node& node, std::string name, bool & val){ bool res=false; - try { + if ( node.FindValue(name) ) { node[name] >> val ; res=true; - }catch ( const std::exception& e ) { } return( res); } |