From 4c6450049d82fb9f98dbafe98d3ea1e229bf2a6d Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 25 Jan 2016 10:04:26 -0500 Subject: API cleaning up --- api/stl/examples/stl_simple_burst.py | 47 +- .../trex_control_plane/client/trex_port.py | 1 + .../client/trex_stateless_client.py | 654 +++++++++++++-------- .../trex_control_plane/console/trex_console.py | 9 +- 4 files changed, 448 insertions(+), 263 deletions(-) diff --git a/api/stl/examples/stl_simple_burst.py b/api/stl/examples/stl_simple_burst.py index 7efb574a..b60df4bf 100644 --- a/api/stl/examples/stl_simple_burst.py +++ b/api/stl/examples/stl_simple_burst.py @@ -12,36 +12,49 @@ def simple_burst (): passed = True + c = STLClient() + try: - with STLClient() as c: + # activate this for some logging information + #c.logger.set_verbose(c.logger.VERBOSE_REGULAR) + + # connect to server + c.connect() + + # prepare port 0,1 + c.reset(ports = [0, 1]) - # activate this for some logging information - #c.logger.set_verbose(c.logger.VERBOSE_REGULAR) + # load profile to both ports + c.load_profile('../profiles/burst.yaml', ports = [0, 1]) - # repeat for 5 times - for i in xrange(1, 6): + # repeat for 5 times + for i in xrange(1, 6): - # read the stats before - before_ipackets = c.get_stats()['total']['ipackets'] + c.clear_stats() - # inject burst profile on two ports and block until done - c.start(profiles = '../profiles/burst.yaml', ports = [0, 1], mult = "1gbps") - c.wait_on_traffic(ports = [0, 1]) + # start traffic and block until done + c.start(ports = [0, 1], mult = "1gbps", duration = 5) + c.wait_on_traffic(ports = [0, 1]) - after_ipackets = c.get_stats()['total']['ipackets'] + # read the stats + stats = c.get_stats() + ipackets = stats['total']['ipackets'] - print "Test iteration {0} - Packets Received: {1} ".format(i, (after_ipackets - before_ipackets)) + print "Test iteration {0} - Packets Received: {1} ".format(i, ipackets) - # we have 600 packets in the burst and two ports - if (after_ipackets - before_ipackets) != (600 * 2): - passed = False + # we have 600 packets in the burst and two ports + if (ipackets != (600 * 2)): + passed = False - # error handling + + # error handling except STLError as e: passed = False print e - + # cleanup + finally: + c.disconnect() if passed: print "\nTest has passed :-)\n" diff --git a/scripts/automation/trex_control_plane/client/trex_port.py b/scripts/automation/trex_control_plane/client/trex_port.py index dcb03da3..438ff4be 100644 --- a/scripts/automation/trex_control_plane/client/trex_port.py +++ b/scripts/automation/trex_control_plane/client/trex_port.py @@ -3,6 +3,7 @@ from collections import namedtuple, OrderedDict from common.trex_types import * from common import trex_stats from client_utils import packet_builder + StreamOnPort = namedtuple('StreamOnPort', ['compiled_stream', 'metadata']) ########## utlity ############ 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 32618a05..6b8e42a6 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -605,7 +605,7 @@ class STLClient(object): return self.ports[port_id].get_stream_id_list() - def __start_traffic (self, multiplier, duration, port_id_list = None, force = False): + def __start (self, multiplier, duration, port_id_list = None, force = False): port_id_list = self.__ports(port_id_list) @@ -617,7 +617,7 @@ class STLClient(object): return rc - def __resume_traffic (self, port_id_list = None, force = False): + def __resume (self, port_id_list = None, force = False): port_id_list = self.__ports(port_id_list) rc = RC() @@ -627,7 +627,7 @@ class STLClient(object): return rc - def __pause_traffic (self, port_id_list = None, force = False): + def __pause (self, port_id_list = None, force = False): port_id_list = self.__ports(port_id_list) rc = RC() @@ -638,7 +638,7 @@ class STLClient(object): return rc - def __stop_traffic (self, port_id_list = None, force = False): + def __stop (self, port_id_list = None, force = False): port_id_list = self.__ports(port_id_list) rc = RC() @@ -649,7 +649,7 @@ class STLClient(object): return rc - def __update_traffic (self, mult, port_id_list = None, force = False): + def __update (self, mult, port_id_list = None, force = False): port_id_list = self.__ports(port_id_list) rc = RC() @@ -660,7 +660,7 @@ class STLClient(object): return rc - def __validate_traffic (self, port_id_list = None): + def __validate (self, port_id_list = None): port_id_list = self.__ports(port_id_list) rc = RC() @@ -672,7 +672,6 @@ class STLClient(object): - # connect to server def __connect(self): @@ -691,13 +690,7 @@ class STLClient(object): if not rc: return rc - # connect async channel - self.logger.pre_cmd("connecting to publisher server on {0}:{1}".format(self.connection_info['server'], self.connection_info['async_port'])) - rc = self.async_client.connect() - self.logger.post_cmd(rc) - - if not rc: - return rc + # version rc = self._transmit("get_version") @@ -740,7 +733,16 @@ class STLClient(object): return rc + # connect async channel + self.logger.pre_cmd("connecting to publisher server on {0}:{1}".format(self.connection_info['server'], self.connection_info['async_port'])) + rc = self.async_client.connect() + self.logger.post_cmd(rc) + + if not rc: + return rc + self.connected = True + return RC_OK() @@ -758,101 +760,6 @@ class STLClient(object): return RC_OK() - # ping server - def __ping (self): - return self._transmit("ping") - - - # start command - def __start (self, port_id_list, stream_list, mult, force, duration, dry): - - - self.logger.pre_cmd("Removing all streams from port(s) {0}:".format(port_id_list)) - rc = self.__remove_all_streams(port_id_list) - self.logger.post_cmd(rc) - - if not rc: - return rc - - - self.logger.pre_cmd("Attaching {0} streams to port(s) {1}:".format(len(stream_list.compiled), port_id_list)) - rc = self.__add_stream_pack(stream_list, port_id_list) - self.logger.post_cmd(rc) - - if not rc: - return rc - - # when not on dry - start the traffic , otherwise validate only - if not dry: - - self.logger.pre_cmd("Starting traffic on port(s) {0}:".format(port_id_list)) - rc = self.__start_traffic(mult, duration, port_id_list, force) - self.logger.post_cmd(rc) - - return rc - else: - - rc = self.__validate(port_id_list) - if rc.bad(): - return rc - - # show a profile on one port for illustration - self.ports[port_id_list[0]].print_profile(mult, duration) - - return rc - - - # stop cmd - def __stop (self, port_id_list): - - self.logger.pre_cmd("Stopping traffic on port(s) {0}:".format(port_id_list)) - rc = self.__stop_traffic(port_id_list) - self.logger.post_cmd(rc) - - if not rc: - return rc - - return RC_OK() - - #update cmd - def __update (self, port_id_list, mult, force): - - self.logger.pre_cmd("Updating traffic on port(s) {0}:".format(port_id_list)) - rc = self.__update_traffic(mult, port_id_list, force) - self.logger.post_cmd(rc) - - return rc - - - # pause cmd - def __pause (self, port_id_list): - - self.logger.pre_cmd("Pausing traffic on port(s) {0}:".format(port_id_list)) - rc = self.__pause_traffic(port_id_list) - self.logger.post_cmd(rc) - - return rc - - - # resume cmd - def __resume (self, port_id_list): - - self.logger.pre_cmd("Resume traffic on port(s) {0}:".format(port_id_list)) - rc = self.__resume_traffic(port_id_list) - self.logger.post_cmd(rc) - - return rc - - - # validate port(s) profile - def __validate (self, port_id_list): - self.logger.pre_cmd("Validating streams on port(s) {0}:".format(port_id_list)) - rc = self.__validate_traffic(port_id_list) - self.logger.post_cmd(rc) - - return rc - - # clear stats def __clear_stats(self, port_id_list, clear_global): @@ -862,9 +769,7 @@ class STLClient(object): if clear_global: self.global_stats.clear_stats() - self.logger.pre_cmd("clearing stats on port(s) {0}:".format(port_id_list)) - rc = RC_OK() - self.logger.post_cmd(rc) + self.logger.log_cmd("clearing stats on port(s) {0}:".format(port_id_list)) return RC @@ -891,30 +796,6 @@ class STLClient(object): return stats - def __process_profiles (self, profiles, out): - - for profile in (profiles if isinstance(profiles, list) else [profiles]): - # filename - if isinstance(profile, str): - - if not os.path.isfile(profile): - return RC_ERR("file '{0}' does not exists".format(profile)) - - try: - stream_list = self.streams_db.load_yaml_file(profile) - except Exception as e: - rc = RC_ERR(str(e)) - return rc - - out.append(stream_list) - - else: - return RC_ERR("unknown profile '{0}'".format(profile)) - - - return RC_OK() - - ############ functions used by other classes but not users ############## def _verify_port_id_list (self, port_id_list): @@ -1014,7 +895,8 @@ class STLClient(object): ############################ ############################# ############################ ############################# def __enter__ (self): - self.connect(mode = "RWF") + self.connect() + self.acquire(force = True) self.reset() return self @@ -1134,36 +1016,79 @@ class STLClient(object): ############################ ############################# - # set the log on verbose level + """ + Sets verbose level + + :parameters: + level : enum + LoggerApi.VERBOSE_QUIET + LoggerApi.VERBOSE_NORMAL + LoggerApi.VERBOSE_HIGH + + :raises: + None + + """ def set_verbose (self, level): self.logger.set_verbose(level) - # connects to the server - # mode can be: - # 'RO' - read only - # 'RW' - read/write - # 'RWF' - read write forced (take ownership) + """ + Connects to the TRex server + + :parameters: + None + + :raises: + + :exc:`STLError` + + """ @__api_check(False) - def connect (self, mode = "RW"): - modes = ['RO', 'RW', 'RWF'] - if not mode in modes: - raise STLArgumentError('mode', mode, modes) - + def connect (self): rc = self.__connect() if not rc: raise STLError(rc) - # acquire all ports for 'RW' or 'RWF' - if (mode == "RW") or (mode == "RWF"): - self.acquire(ports = self.get_all_ports(), force = True if mode == "RWF" else False) + """ + Disconnects from the server + :parameters: + stop_traffic : bool + tries to stop traffic before disconnecting + + + """ + @__api_check(False) + def disconnect (self, stop_traffic = True): + # try to stop ports but do nothing if not possible + if stop_traffic: + try: + self.stop() + except STLError: + pass - # acquire ports - # this is not needed if connect was called with "RW" or "RWF" - # but for "RO" this might be needed + self.logger.pre_cmd("Disconnecting from server at '{0}':'{1}'".format(self.connection_info['server'], + self.connection_info['sync_port'])) + rc = self.__disconnect() + self.logger.post_cmd(rc) + + + + """ + Acquires ports for executing commands + + :parameters: + ports : list + ports to execute the command + force : bool + force acquire the ports + + :raises: + + :exc:`STLError` + + """ @__api_check(True) def acquire (self, ports = None, force = False): # by default use all ports @@ -1186,102 +1111,204 @@ class STLClient(object): self.logger.post_cmd(rc) if not rc: + # cleanup self.__release(ports) raise STLError(rc) - # force connect syntatic sugar - @__api_check(False) - def fconnect (self): - self.connect(mode = "RWF") + """ + Pings the server + :parameters: + None + + + :raises: + + :exc:`STLError` + + """ + @__api_check(True) + def ping(self): + self.logger.pre_cmd( "Pinging the server on '{0}' port '{1}': ".format(self.connection_info['server'], + self.connection_info['sync_port'])) + rc = self._transmit("ping") + + self.logger.post_cmd(rc) - # disconnects from the server - @__api_check(False) - def disconnect (self, log = True): - rc = self.__disconnect() - if log: - self.logger.log_cmd("Disconnecting from server at '{0}':'{1}'".format(self.connection_info['server'], - self.connection_info['sync_port'])) if not rc: raise STLError(rc) - # teardown - call after test is done - # NEVER throws an exception - @__api_check(False) - def teardown (self, stop_traffic = True): - - # try to stop traffic - if stop_traffic and self.get_active_ports(): - try: - self.stop() - except STLError: - pass - - # disconnect - self.__disconnect() + """ + force acquire ports, stop the traffic, remove all streams and clear stats + :parameters: + ports : list + ports to execute the command + + :raises: + + :exc:`STLError` - # pings the server on the RPC channel + """ @__api_check(True) - def ping(self): - self.logger.pre_cmd( "Pinging the server on '{0}' port '{1}': ".format(self.connection_info['server'], - self.connection_info['sync_port'])) - rc = self.__ping() - - self.logger.post_cmd(rc) + def reset(self, ports = None): + + # by default use all ports + if ports == None: + ports = self.get_all_ports() + # verify ports + rc = self._validate_port_list(ports) if not rc: - raise STLError(rc) + raise STLArgumentError('ports', ports, valid_values = self.get_all_ports()) + + self.acquire(ports, force = True) + self.stop(ports) + self.remove_all_streams(ports) + self.clear_stats(ports) + """ + remove all streams from port(s) - # reset the server by performing - # force acquire, stop, and remove all streams + :parameters: + ports : list + ports to execute the command + + + :raises: + + :exc:`STLError` + + """ @__api_check(True) - def reset(self): + def remove_all_streams (self, ports = None): + + # by default use all ports + if ports == None: + ports = self.get_acquired_ports() - self.logger.pre_cmd("Force acquiring all ports:") - rc = self.__acquire(force = True) + # verify valid port id list + rc = self._validate_port_list(ports) + if not rc: + raise STLArgumentError('ports', ports, valid_values = self.get_all_ports()) + + self.logger.pre_cmd("Removing all streams from port(s) {0}:".format(ports)) + rc = self.__remove_all_streams(ports) self.logger.post_cmd(rc) if not rc: raise STLError(rc) + + """ + add a list of streams to port(s) + + :parameters: + ports : list + ports to execute the command + + + :raises: + + :exc:`STLError` + + """ + @__api_check(True) + def add_streams (self, streams, ports = None): + # by default use all ports + if ports == None: + ports = self.get_acquired_ports() + + # verify valid port id list + rc = self._validate_port_list(ports) + if not rc: + raise STLArgumentError('ports', ports, valid_values = self.get_all_ports()) - # force stop all ports - self.logger.pre_cmd("Stop traffic on all ports:") - rc = self.__stop_traffic(self.get_all_ports(), True) + self.logger.pre_cmd("Attaching {0} streams to port(s) {1}:".format(len(streams.compiled), ports)) + rc = self.__add_stream_pack(streams, ports) self.logger.post_cmd(rc) if not rc: raise STLError(rc) - # remove all streams - self.logger.pre_cmd("Removing all streams from all ports:") - rc = self.__remove_all_streams(self.get_all_ports()) - self.logger.post_cmd(rc) + """ + load a profile file to port(s) + + :parameters: + filename : str + filename to load + ports : list + ports to execute the command + + + :raises: + + :exc:`STLError` + + """ + @__api_check(True) + def load_profile (self, filename, ports = None): + + # check filename + if not os.path.isfile(filename): + raise STLError("file '{0}' does not exists".format(filename)) + + # by default use all ports + if ports == None: + ports = self.get_acquired_ports() + # verify valid port id list + rc = self._validate_port_list(ports) if not rc: - raise STLError(rc) + raise STLArgumentError('ports', ports, valid_values = self.get_all_ports()) + + # load the YAML + try: + streams = self.streams_db.load_yaml_file(filename) + except Exception as e: + raise STLError(str(e)) - self.clear_stats() + # attach + self.add_streams(streams, ports) - # start cmd + + """ + start traffic on port(s) + + :parameters: + ports : list + ports to execute command + + mult : str + multiplier in a form of pps, bps, or line util in % + examples: "5kpps", "10gbps", "85%", "32mbps" + + force : bool + imply stopping the port of active and also + forces a profile that exceeds the L1 BW + + duration : int + limit the run for time in seconds + -1 means unlimited + + total : bool + should the B/W be divided by the ports + or duplicated for each + + + :raises: + + :exc:`STLError` + + """ @__api_check(True) def start (self, - profiles, ports = None, mult = "1", force = False, duration = -1, - dry = False, total = False): @@ -1313,57 +1340,87 @@ class STLClient(object): raise STLArgumentError('total', total) - # process profiles - stream_list = [] - rc = self.__process_profiles(profiles, stream_list) - if not rc: - raise STLError(rc) - - # verify ports are stopped or force stop them active_ports = list(set(self.get_active_ports()).intersection(ports)) if active_ports: if not force: - msg = "Port(s) {0} are active - please stop them or specify 'force'".format(active_ports) - raise STLError(msg) + raise STLError("Port(s) {0} are active - please stop them or specify 'force'".format(active_ports)) else: - rc = self.__stop(active_ports) + rc = self.stop(active_ports) if not rc: raise STLError(rc) - # dry run - if dry: - self.logger.log(format_text("\n*** DRY RUN ***", 'bold')) - - # call private method to start + # start traffic + self.logger.pre_cmd("Starting traffic on port(s) {0}:".format(ports)) + rc = self.__start(mult_obj, duration, ports, force) + self.logger.post_cmd(rc) - rc = self.__start(ports, stream_list[0], mult_obj, force, duration, dry) if not rc: raise STLError(rc) - # stop traffic on ports + + """ + stop port(s) + + :parameters: + ports : list + ports to execute the command + + + :raises: + + :exc:`STLError` + + """ @__api_check(True) def stop (self, ports = None): # by default the user means all the active ports if ports == None: ports = self.get_active_ports() + if not ports: + return # verify valid port id list rc = self._validate_port_list(ports) if not rc: raise STLArgumentError('ports', ports, valid_values = self.get_all_ports()) + self.logger.pre_cmd("Stopping traffic on port(s) {0}:".format(ports)) rc = self.__stop(ports) + self.logger.post_cmd(rc) + if not rc: raise STLError(rc) - # update traffic + """ + update traffic on port(s) + + :parameters: + ports : list + ports to execute command + + mult : str + multiplier in a form of pps, bps, or line util in % + and also with +/- + examples: "5kpps+", "10gbps-", "85%", "32mbps", "20%+" + + force : bool + forces a profile that exceeds the L1 BW + + total : bool + should the B/W be divided by the ports + or duplicated for each + + + :raises: + + :exc:`STLError` + + """ @__api_check(True) def update (self, ports = None, mult = "1", total = False, force = False): @@ -1389,13 +1446,26 @@ class STLClient(object): # call low level functions - rc = self.__update(ports, mult_obj, force) + self.logger.pre_cmd("Updating traffic on port(s) {0}:".format(ports)) + rc = self.__update(mult, ports, force) + self.logger.post_cmd(rc) + if not rc: raise STLError(rc) - # pause traffic on ports + """ + pause traffic on port(s) + + :parameters: + ports : list + ports to execute command + + :raises: + + :exc:`STLError` + + """ @__api_check(True) def pause (self, ports = None): @@ -1408,13 +1478,26 @@ class STLClient(object): if not rc: raise STLArgumentError('ports', ports, valid_values = self.get_all_ports()) + self.logger.pre_cmd("Pausing traffic on port(s) {0}:".format(ports)) rc = self.__pause(ports) + self.logger.post_cmd(rc) + if not rc: raise STLError(rc) - # resume traffic on ports + """ + resume traffic on port(s) + + :parameters: + ports : list + ports to execute command + + :raises: + + :exc:`STLError` + + """ @__api_check(True) def resume (self, ports = None): @@ -1427,13 +1510,39 @@ class STLClient(object): if not rc: raise STLArgumentError('ports', ports, valid_values = self.get_all_ports()) + self.logger.pre_cmd("Resume traffic on port(s) {0}:".format(ports)) rc = self.__resume(ports) + self.logger.post_cmd(rc) + if not rc: raise STLError(rc) + """ + validate port(s) configuration + + :parameters: + ports : list + ports to execute command + + mult : str + multiplier in a form of pps, bps, or line util in % + examples: "5kpps", "10gbps", "85%", "32mbps" + + duration : int + limit the run for time in seconds + -1 means unlimited + + total : bool + should the B/W be divided by the ports + or duplicated for each + + :raises: + + :exc:`STLError` + + """ @__api_check(True) - def validate (self, ports = None): + def validate (self, ports = None, mult = "1", duration = "-1", total = False): if ports == None: ports = self.get_acquired_ports() @@ -1442,18 +1551,47 @@ class STLClient(object): if not rc: raise STLArgumentError('ports', ports, valid_values = self.get_all_ports()) + # verify multiplier + mult_obj = parsing_opts.decode_multiplier(mult, + allow_update = True, + divide_count = len(ports) if total else 1) + if not mult_obj: + raise STLArgumentError('mult', mult) + + + if not isinstance(duration, (int, float)): + raise STLArgumentError('duration', duration) + + + self.logger.pre_cmd("Validating streams on port(s) {0}:".format(ports)) rc = self.__validate(ports) - if not rc: - raise STLError(rc) + self.logger.post_cmd(rc) - # clear stats + for port in ports: + self.ports[port].print_profile(mult_obj, duration) + + + """ + clear stats on port(s) + + :parameters: + ports : list + ports to execute command + + clear_global : bool + clear the global stats + + :raises: + + :exc:`STLError` + + """ @__api_check(False) def clear_stats (self, ports = None, clear_global = True): # by default use all ports if ports == None: - ports = self.get_acquired_ports() + ports = self.get_all_ports() # verify valid port id list rc = self._validate_port_list(ports) @@ -1473,7 +1611,21 @@ class STLClient(object): - # wait while traffic is on, on timeout throw STLTimeoutError + """ + block until specify port(s) traffic has ended + + :parameters: + ports : list + ports to execute command + + timeout : int + timeout in seconds + + :raises: + + :exc:`STLTimeoutError` - in case timeout has expired + + :exe:'STLError' + + """ @__api_check(True) def wait_on_traffic (self, ports = None, timeout = 60): @@ -1495,13 +1647,24 @@ class STLClient(object): raise STLTimeoutError(timeout) - # clear all async events + """ + clear all events + + :parameters: + None + + :raises: + None + + """ def clear_events (self): self.event_handler.clear_events() + ############################ Line ############################# ############################ Commands ############################# ############################ ############################# + # console decorator def __console(f): def wrap(*args): @@ -1539,7 +1702,8 @@ class STLClient(object): return # call the API - self.connect("RWF" if opts.force else "RW") + self.connect() + self.acquire(force = opts.force) # true means print time return True @@ -1587,18 +1751,24 @@ class STLClient(object): msg = "Port(s) {0} are active - please stop them or add '--force'\n".format(active_ports) self.logger.log(format_text(msg, 'bold')) return + else: + self.stop(active_ports) + # remove all streams + self.remove_all_streams(opts.ports) + # pack the profile - profiles = [opts.file[0]] - - self.start(profiles, - opts.ports, - opts.mult, - opts.force, - opts.duration, - opts.dry, - opts.total) + self.load_profile(opts.file[0], opts.ports) + + if opts.dry: + self.validate(opts.ports, opts.mult, opts.duration, opts.total) + else: + self.start(opts.ports, + opts.mult, + opts.force, + opts.duration, + opts.total) # true means print time return True diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 88ff45dc..62b68861 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -36,7 +36,6 @@ from client_utils import parsing_opts import trex_tui from functools import wraps - __version__ = "1.1" # console custom logger @@ -56,7 +55,7 @@ class ConsoleLogger(LoggerApi): # override this for the prompt fix def async_log (self, msg, level = LoggerApi.VERBOSE_REGULAR, newline = True): self.log(msg, level, newline) - if self.prompt_redraw: + if ( (self.level >= LoggerApi.VERBOSE_REGULAR) and self.prompt_redraw ): self.prompt_redraw() self.flush() @@ -717,13 +716,14 @@ def main(): # TUI or no acquire will give us READ ONLY mode try: - stateless_client.connect("RO") + stateless_client.connect() except STLError as e: logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold')) return if not options.tui and options.acquire: try: + # acquire all ports stateless_client.acquire() except STLError as e: logger.log("Log:\n" + format_text(e.brief() + "\n", 'bold')) @@ -751,7 +751,8 @@ def main(): print "\n\n*** Caught Ctrl + C... Exiting...\n\n" finally: - stateless_client.teardown(stop_traffic = False) + with stateless_client.logger.supress(): + stateless_client.disconnect() if __name__ == '__main__': -- cgit 1.2.3-korg From 2828fc9aab33b742c59a499dbf06ea2239ec6220 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 26 Jan 2016 08:57:43 -0500 Subject: API simplification and example --- api/stl/examples/stl_simple_burst.py | 69 +++++---- api/stl/trex_stl_api.py | 18 ++- .../trex_control_plane/client/trex_port.py | 60 +++----- .../client/trex_stateless_client.py | 77 +++------- .../trex_control_plane/common/trex_stats.py | 3 +- .../common/trex_stl_exceptions.py | 53 +++++++ .../trex_control_plane/common/trex_streams.py | 162 +++++++++++++++++++++ .../trex_control_plane/console/trex_console.py | 5 +- 8 files changed, 315 insertions(+), 132 deletions(-) create mode 100644 scripts/automation/trex_control_plane/common/trex_stl_exceptions.py diff --git a/api/stl/examples/stl_simple_burst.py b/api/stl/examples/stl_simple_burst.py index b60df4bf..49355890 100644 --- a/api/stl/examples/stl_simple_burst.py +++ b/api/stl/examples/stl_simple_burst.py @@ -1,66 +1,83 @@ import sys sys.path.insert(0, "../") -import trex_stl_api - -from trex_stl_api import STLClient, STLError - +from trex_stl_api import * +import dpkt import time -# define a simple burst test def simple_burst (): - - passed = True - + + # build a simple packet + + pkt_bld = STLPktBuilder() + pkt_bld.add_pkt_layer("l2", dpkt.ethernet.Ethernet()) + # set Ethernet layer attributes + pkt_bld.set_eth_layer_addr("l2", "src", "00:15:17:a7:75:a3") + pkt_bld.set_eth_layer_addr("l2", "dst", "e0:5f:b9:69:e9:22") + pkt_bld.set_layer_attr("l2", "type", dpkt.ethernet.ETH_TYPE_IP) + # set IP layer attributes + pkt_bld.add_pkt_layer("l3_ip", dpkt.ip.IP()) + pkt_bld.set_ip_layer_addr("l3_ip", "src", "21.0.0.2") + pkt_bld.set_ip_layer_addr("l3_ip", "dst", "22.0.0.12") + pkt_bld.set_layer_attr("l3_ip", "p", dpkt.ip.IP_PROTO_TCP) + # set TCP layer attributes + pkt_bld.add_pkt_layer("l4_tcp", dpkt.tcp.TCP()) + pkt_bld.set_layer_attr("l4_tcp", "sport", 13311) + pkt_bld.set_layer_attr("l4_tcp", "dport", 80) + pkt_bld.set_layer_attr("l4_tcp", "flags", 0) + pkt_bld.set_layer_attr("l4_tcp", "win", 32768) + pkt_bld.set_layer_attr("l4_tcp", "seq", 0) + pkt_bld.set_pkt_payload("abcdefgh") + pkt_bld.set_layer_attr("l3_ip", "len", len(pkt_bld.get_layer('l3_ip'))) + + + # create client c = STLClient() + passed = True try: - # activate this for some logging information - #c.logger.set_verbose(c.logger.VERBOSE_REGULAR) + #c.logger.set_verbose(c.logger.VERBOSE_NORMAL) + + # create two bursts and link them + s1 = STLSingleBurstStream(packet = pkt_bld, total_pkts = 5000) + s2 = STLSingleBurstStream(packet = pkt_bld, total_pkts = 3000, next_stream_id = s1.get_id()) # connect to server c.connect() - # prepare port 0,1 + # prepare our ports c.reset(ports = [0, 1]) - # load profile to both ports - c.load_profile('../profiles/burst.yaml', ports = [0, 1]) + # add both streams to ports + c.add_streams([s1, s2], ports = [0, 1]) - # repeat for 5 times + # run 5 times for i in xrange(1, 6): - c.clear_stats() - - # start traffic and block until done - c.start(ports = [0, 1], mult = "1gbps", duration = 5) + c.start(ports = [0, 1], mult = "1gbps") c.wait_on_traffic(ports = [0, 1]) - # read the stats stats = c.get_stats() ipackets = stats['total']['ipackets'] print "Test iteration {0} - Packets Received: {1} ".format(i, ipackets) - - # we have 600 packets in the burst and two ports - if (ipackets != (600 * 2)): + # (5000 + 3000) * 2 ports = 16,000 + if (ipackets != (16000)): passed = False - - # error handling except STLError as e: passed = False print e - # cleanup finally: c.disconnect() - + if passed: print "\nTest has passed :-)\n" else: print "\nTest has failed :-(\n" +# run the tests simple_burst() diff --git a/api/stl/trex_stl_api.py b/api/stl/trex_stl_api.py index aad39916..63a0963b 100644 --- a/api/stl/trex_stl_api.py +++ b/api/stl/trex_stl_api.py @@ -6,12 +6,18 @@ import time # update the import path to include the stateless client root_path = os.path.dirname(os.path.abspath(__file__)) -sys.path.insert(0, os.path.join(root_path, '../../scripts/automation/trex_control_plane/client/')) -sys.path.insert(0, os.path.join(root_path, '../../scripts/automation/trex_control_plane/client_utils/')) -sys.path.insert(0, os.path.join(root_path, '../../scripts/automation/trex_control_plane/client_utils/')) +sys.path.insert(0, os.path.join(root_path, '../../scripts/automation/trex_control_plane/')) # aliasing -import trex_stateless_client -STLClient = trex_stateless_client.STLClient -STLError = trex_stateless_client.STLError +import common.trex_streams +from client_utils.packet_builder import CTRexPktBuilder +import common.trex_stl_exceptions +import client.trex_stateless_client + +STLClient = client.trex_stateless_client.STLClient +STLError = common.trex_stl_exceptions.STLError +STLContStream = common.trex_streams.STLContStream +STLSingleBurstStream = common.trex_streams.STLSingleBurstStream +STLMultiBurstStream = common.trex_streams.STLMultiBurstStream +STLPktBuilder = CTRexPktBuilder diff --git a/scripts/automation/trex_control_plane/client/trex_port.py b/scripts/automation/trex_control_plane/client/trex_port.py index 438ff4be..c8147faf 100644 --- a/scripts/automation/trex_control_plane/client/trex_port.py +++ b/scripts/automation/trex_control_plane/client/trex_port.py @@ -49,7 +49,6 @@ class Port(object): self.streams = {} self.profile = None self.session_id = session_id - self.loaded_stream_pack = None self.port_stats = trex_stats.CPortStats(self) @@ -139,63 +138,44 @@ class Port(object): # operations on port can be done on state idle or state streams return ((self.state == self.STATE_IDLE) or (self.state == self.STATE_STREAMS)) - # add stream to the port - def add_stream (self, stream_id, stream_obj): - if not self.is_port_writable(): - return self.err("Please stop port before attempting to add streams") - - - params = {"handler": self.handler, - "port_id": self.port_id, - "stream_id": stream_id, - "stream": stream_obj} + # add streams + def add_streams (self, streams_list): - rc = self.transmit("add_stream", params) - if rc.bad(): - return self.err(rc.err()) - - # add the stream - self.streams[stream_id] = StreamOnPort(stream_obj, Port._generate_stream_metadata(stream_id, stream_obj)) - - # the only valid state now - self.state = self.STATE_STREAMS + if not self.is_acquired(): + return self.err("port is not owned") - return self.ok() + if not self.is_port_writable(): + return self.err("Please stop port before attempting to add streams") - # add multiple streams - def add_streams (self, LoadedStreamList_obj): batch = [] + for stream in (streams_list if isinstance(streams_list, list) else [streams_list]): - self.loaded_stream_pack = LoadedStreamList_obj - compiled_stream_list = LoadedStreamList_obj.compiled - - for stream_pack in compiled_stream_list: params = {"handler": self.handler, "port_id": self.port_id, - "stream_id": stream_pack.stream_id, - "stream": stream_pack.stream} + "stream_id": stream.get_id(), + "stream": stream.to_json()} cmd = RpcCmdData('add_stream', params) batch.append(cmd) + # meta data for show streams + self.streams[stream.get_id()] = StreamOnPort(stream.to_json(), + Port._generate_stream_metadata(stream.get_id(), stream.to_json())) + rc = self.transmit_batch(batch) - if rc.bad(): + if not rc: return self.err(rc.err()) - # validate that every action succeeded - - # add the stream - for stream_pack in compiled_stream_list: - self.streams[stream_pack.stream_id] = StreamOnPort(stream_pack.stream, - Port._generate_stream_metadata(stream_pack.stream_id, - stream_pack.stream)) + # the only valid state now self.state = self.STATE_STREAMS return self.ok() + + # remove stream from port def remove_stream (self, stream_id): @@ -461,10 +441,6 @@ class Port(object): def generate_loaded_streams_sum(self, stream_id_list): if self.state == self.STATE_DOWN or self.state == self.STATE_STREAMS: return {} - elif self.loaded_stream_pack is None: - # avoid crashing when sync with remote server isn't operational - # TODO: MAKE SURE TO HANDLE THIS CASE FOR BETTER UX - return {} streams_data = {} if not stream_id_list: @@ -477,7 +453,7 @@ class Port(object): if stream_id in self.streams} - return {"referring_file" : self.loaded_stream_pack.name, + return {"referring_file" : "", "streams" : streams_data} @staticmethod 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 6b8e42a6..886edb61 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -23,56 +23,9 @@ import re import random from trex_port import Port from common.trex_types import * +from common.trex_stl_exceptions import * from trex_async_client import CTRexAsyncClient -# basic error for API -class STLError(Exception): - def __init__ (self, msg): - self.msg = str(msg) - - def __str__ (self): - exc_type, exc_obj, exc_tb = sys.exc_info() - fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] - - - s = "\n******\n" - s += "Error at {0}:{1}\n\n".format(format_text(fname, 'bold'), format_text(exc_tb.tb_lineno), 'bold') - s += "specific error:\n\n{0}\n".format(format_text(self.msg, 'bold')) - - return s - - def brief (self): - return self.msg - - -# raised when the client state is invalid for operation -class STLStateError(STLError): - def __init__ (self, op, state): - self.msg = "Operation '{0}' is not valid while '{1}'".format(op, state) - - -# port state error -class STLPortStateError(STLError): - def __init__ (self, port, op, state): - self.msg = "Operation '{0}' on port(s) '{1}' is not valid while port(s) '{2}'".format(op, port, state) - - -# raised when argument is not valid for operation -class STLArgumentError(STLError): - def __init__ (self, name, got, valid_values = None, extended = None): - self.msg = "Argument: '{0}' invalid value: '{1}'".format(name, got) - if valid_values: - self.msg += " - valid values are '{0}'".format(valid_values) - - if extended: - self.msg += "\n{0}".format(extended) - -# raised when timeout occurs -class STLTimeoutError(STLError): - def __init__ (self, timeout): - self.msg = "Timeout: operation took more than '{0}' seconds".format(timeout) - - ############################ logger ############################# ############################ ############################# @@ -541,14 +494,14 @@ class STLClient(object): return rc - def __add_stream(self, stream_id, stream_obj, port_id_list = None): + def __add_streams(self, stream_list, port_id_list = None): port_id_list = self.__ports(port_id_list) rc = RC() for port_id in port_id_list: - rc.add(self.ports[port_id].add_stream(stream_id, stream_obj)) + rc.add(self.ports[port_id].add_streams(stream_list)) return rc @@ -1209,7 +1162,8 @@ class STLClient(object): :parameters: ports : list ports to execute the command - + streams: list + streams to attach :raises: + :exc:`STLError` @@ -1226,8 +1180,16 @@ class STLClient(object): if not rc: raise STLArgumentError('ports', ports, valid_values = self.get_all_ports()) - self.logger.pre_cmd("Attaching {0} streams to port(s) {1}:".format(len(streams.compiled), ports)) - rc = self.__add_stream_pack(streams, ports) + # transform single stream + if not isinstance(streams, list): + streams = [streams] + + # check streams + if not all([isinstance(stream, STLStream) for stream in streams]): + raise STLArgumentError('streams', streams) + + self.logger.pre_cmd("Attaching {0} streams to port(s) {1}:".format(len(streams), ports)) + rc = self.__add_streams(streams, ports) self.logger.post_cmd(rc) if not rc: @@ -1266,11 +1228,16 @@ class STLClient(object): # load the YAML try: - streams = self.streams_db.load_yaml_file(filename) + streams_pack = self.streams_db.load_yaml_file(filename) except Exception as e: raise STLError(str(e)) - # attach + # HACK - convert the stream pack to simple streams + streams = [] + for stream in streams_pack.compiled: + s = HACKSTLStream(stream) + streams.append(s) + self.add_streams(streams, ports) diff --git a/scripts/automation/trex_control_plane/common/trex_stats.py b/scripts/automation/trex_control_plane/common/trex_stats.py index 52c0c0a1..464ee56a 100755 --- a/scripts/automation/trex_control_plane/common/trex_stats.py +++ b/scripts/automation/trex_control_plane/common/trex_stats.py @@ -223,7 +223,8 @@ class CTRexInfoGenerator(object): info_table = text_tables.TRexTextTable() info_table.set_cols_align(["c"] + ["l"] + ["r"] + ["c"] + ["r"] + ["c"]) - info_table.set_cols_width([4] + [20] + [8] + [16] + [10] + [12]) + info_table.set_cols_width([10] + [20] + [8] + [16] + [10] + [12]) + info_table.set_cols_dtype(["t"] + ["t"] + ["t"] + ["t"] + ["t"] + ["t"]) info_table.add_rows([v.values() for k, v in return_streams_data['streams'].iteritems()], diff --git a/scripts/automation/trex_control_plane/common/trex_stl_exceptions.py b/scripts/automation/trex_control_plane/common/trex_stl_exceptions.py new file mode 100644 index 00000000..9be20db9 --- /dev/null +++ b/scripts/automation/trex_control_plane/common/trex_stl_exceptions.py @@ -0,0 +1,53 @@ +import os +import sys +from common.text_opts import * + +# basic error for API +class STLError(Exception): + def __init__ (self, msg): + self.msg = str(msg) + + def __str__ (self): + exc_type, exc_obj, exc_tb = sys.exc_info() + fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + + + s = "\n******\n" + s += "Error at {0}:{1}\n\n".format(format_text(fname, 'bold'), format_text(exc_tb.tb_lineno), 'bold') + s += "specific error:\n\n{0}\n".format(format_text(self.msg, 'bold')) + + return s + + def brief (self): + return self.msg + + +# raised when the client state is invalid for operation +class STLStateError(STLError): + def __init__ (self, op, state): + self.msg = "Operation '{0}' is not valid while '{1}'".format(op, state) + + +# port state error +class STLPortStateError(STLError): + def __init__ (self, port, op, state): + self.msg = "Operation '{0}' on port(s) '{1}' is not valid while port(s) '{2}'".format(op, port, state) + + +# raised when argument is not valid for operation +class STLArgumentError(STLError): + def __init__ (self, name, got, valid_values = None, extended = None): + self.msg = "Argument: '{0}' invalid value: '{1}'".format(name, got) + if valid_values: + self.msg += " - valid values are '{0}'".format(valid_values) + + if extended: + self.msg += "\n{0}".format(extended) + +# raised when timeout occurs +class STLTimeoutError(STLError): + def __init__ (self, timeout): + self.msg = "Timeout: operation took more than '{0}' seconds".format(timeout) + + + diff --git a/scripts/automation/trex_control_plane/common/trex_streams.py b/scripts/automation/trex_control_plane/common/trex_streams.py index ea3d71d1..90cb812d 100755 --- a/scripts/automation/trex_control_plane/common/trex_streams.py +++ b/scripts/automation/trex_control_plane/common/trex_streams.py @@ -4,10 +4,12 @@ import external_packages from client_utils.packet_builder import CTRexPktBuilder from collections import OrderedDict, namedtuple from client_utils.yaml_utils import * +import trex_stl_exceptions import dpkt import struct import copy import os +import random StreamPack = namedtuple('StreamPack', ['stream_id', 'stream']) LoadedStreamList = namedtuple('LoadedStreamList', ['name', 'loaded', 'compiled']) @@ -323,3 +325,163 @@ class CStreamsDB(object): else: return self.stream_packs.get(name) + +########################### Simple Streams ########################### +from trex_stl_exceptions import * + +class STLStream(object): + + def __init__ (self, + packet, + pps = 1, + enabled = True, + self_start = True, + isg = 0.0, + rx_stats = None, + next_stream_id = -1): + + # type checking + if not isinstance(pps, (int, float)): + raise STLArgumentError('pps', pps) + + if not isinstance(packet, CTRexPktBuilder): + raise STLArgumentError('packet', packet) + + if not isinstance(enabled, bool): + raise STLArgumentError('enabled', enabled) + + if not isinstance(self_start, bool): + raise STLArgumentError('self_start', self_start) + + if not isinstance(isg, (int, float)): + raise STLArgumentError('isg', isg) + + # use a random 31 bit for ID + self.stream_id = random.getrandbits(31) + + self.fields = {} + + # basic fields + self.fields['enabled'] = enabled + self.fields['self_start'] = self_start + self.fields['isg'] = isg + + self.fields['next_stream_id'] = next_stream_id + + # mode + self.fields['mode'] = {} + self.fields['mode']['pps'] = pps + + # packet and VM + self.fields['packet'] = packet.dump_pkt() + self.fields['vm'] = packet.get_vm_data() + + self.fields['rx_stats'] = {} + if not rx_stats: + self.fields['rx_stats']['enabled'] = False + + + def __str__ (self): + return json.dumps(self.fields, indent = 4, separators=(',', ': '), sort_keys = True) + + def to_json (self): + return self.fields + + def get_id (self): + return self.stream_id + + +# continuous stream +class STLContStream(STLStream): + def __init__ (self, + packet, + pps = 1, + enabled = True, + self_start = True, + isg = 0.0, + rx_stats = None): + + super(STLContStream, self).__init__(packet, + pps, + enabled, + self_start, + isg, + rx_stats, + next_stream_id = -1) + + # type + self.fields['mode']['type'] = "continuous" + + + +# single burst +class STLSingleBurstStream(STLStream): + def __init__ (self, + packet, + total_pkts, + pps = 1, + enabled = True, + self_start = True, + isg = 0.0, + rx_stats = None, + next_stream_id = -1): + + + if not isinstance(total_pkts, int): + raise STLArgumentError('total_pkts', total_pkts) + + super(STLSingleBurstStream, self).__init__(packet, + pps, + enabled, + self_start, + isg, + rx_stats, + next_stream_id) + + self.fields['mode']['type'] = "single_burst" + self.fields['mode']['total_pkts'] = total_pkts + + +# multi burst stream +class STLMultiBurstStream(STLStream): + def __init__ (self, + packet, + pkts_per_burst = 1, + pps = 1, + ibg = 0.0, + count = 1, + enabled = True, + self_start = True, + isg = 0.0, + rx_stats = None, + next_stream_id = -1): + + + if not isinstance(pkts_per_burst, int): + raise STLArgumentError('pkts_per_burst', pkts_per_burst) + + if not isinstance(count, int): + raise STLArgumentError('count', count) + + if not isinstance(ibg, (int, float)): + raise STLArgumentError('ibg', ibg) + + super(STLMultiBurstStream, self).__init__(packet, enabled, self_start, isg, rx_stats) + + self.fields['mode']['type'] = "single_burst" + self.fields['mode']['pkts_per_burst'] = pkts_per_burst + self.fields['mode']['ibg'] = ibg + self.fields['mode']['count'] = count + + +# REMOVE ME when can - convert from stream pack to a simple stream +class HACKSTLStream(STLStream): + def __init__ (self, stream_pack): + if not isinstance(stream_pack, StreamPack): + raise Exception("internal error") + + packet = CTRexPktBuilder() + packet.load_from_stream_obj(stream_pack.stream) + super(HACKSTLStream, self).__init__(packet) + + self.fields = stream_pack.stream diff --git a/scripts/automation/trex_control_plane/console/trex_console.py b/scripts/automation/trex_control_plane/console/trex_console.py index 62b68861..1defc6b2 100755 --- a/scripts/automation/trex_control_plane/console/trex_console.py +++ b/scripts/automation/trex_control_plane/console/trex_console.py @@ -29,11 +29,12 @@ import sys import tty, termios import trex_root_path from common.trex_streams import * -from client.trex_stateless_client import STLClient, LoggerApi, STLError +from client.trex_stateless_client import STLClient, LoggerApi from common.text_opts import * from client_utils.general_utils import user_input, get_current_user from client_utils import parsing_opts import trex_tui +from common.trex_stl_exceptions import * from functools import wraps __version__ = "1.1" @@ -752,7 +753,7 @@ def main(): finally: with stateless_client.logger.supress(): - stateless_client.disconnect() + stateless_client.disconnect(stop_traffic = False) if __name__ == '__main__': -- cgit 1.2.3-korg From db6254e3c59ef887ff41ff2aff44349010bd6bad Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 26 Jan 2016 09:53:21 -0500 Subject: packet is now base64 encoded --- linux/ws_main.py | 3 +- linux_dpdk/ws_main.py | 1 + .../client/trex_stateless_client.py | 5 +- .../client/trex_stateless_sim.py | 2 +- .../client_utils/jsonrpc_client.py | 7 +- .../client_utils/packet_builder.py | 10 +- src/common/base64.cpp | 123 +++++++++++++++++++++ src/common/base64.h | 9 ++ src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 10 +- 9 files changed, 155 insertions(+), 15 deletions(-) create mode 100644 src/common/base64.cpp create mode 100644 src/common/base64.h diff --git a/linux/ws_main.py b/linux/ws_main.py index a41fab1e..ec3ef6d7 100755 --- a/linux/ws_main.py +++ b/linux/ws_main.py @@ -133,7 +133,8 @@ cmn_src = SrcGroup(dir='src/common', 'basic_utils.cpp', 'captureFile.cpp', 'erf.cpp', - 'pcap.cpp' + 'pcap.cpp', + 'base64.cpp' ]); diff --git a/linux_dpdk/ws_main.py b/linux_dpdk/ws_main.py index f098e193..9dcb3bae 100755 --- a/linux_dpdk/ws_main.py +++ b/linux_dpdk/ws_main.py @@ -124,6 +124,7 @@ cmn_src = SrcGroup(dir='src/common', 'captureFile.cpp', 'erf.cpp', 'pcap.cpp', + 'base64.cpp' ]); net_src = SrcGroup(dir='src/common/Network/Packet', 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 886edb61..65e69938 100755 --- a/scripts/automation/trex_control_plane/client/trex_stateless_client.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_client.py @@ -636,20 +636,19 @@ class STLClient(object): self.connected = False # connect sync channel - self.logger.pre_cmd("connecting to RPC server on {0}:{1}".format(self.connection_info['server'], self.connection_info['sync_port'])) + self.logger.pre_cmd("Connecting to RPC server on {0}:{1}".format(self.connection_info['server'], self.connection_info['sync_port'])) rc = self.comm_link.connect() self.logger.post_cmd(rc) if not rc: return rc - - # version rc = self._transmit("get_version") if not rc: return rc + self.server_version = rc.data() self.global_stats.server_version = rc.data() diff --git a/scripts/automation/trex_control_plane/client/trex_stateless_sim.py b/scripts/automation/trex_control_plane/client/trex_stateless_sim.py index 3196c666..a8f24d65 100644 --- a/scripts/automation/trex_control_plane/client/trex_stateless_sim.py +++ b/scripts/automation/trex_control_plane/client/trex_stateless_sim.py @@ -247,7 +247,7 @@ def setParserOptions(): parser.add_argument('-m', '--multiplier', help = parsing_opts.match_multiplier_help, dest = 'mult', - default = {'type':'raw', 'value':1, 'op': 'abs'}, + default = "1", type = parsing_opts.match_multiplier_strict) group = parser.add_mutually_exclusive_group() 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 05a32bc4..9c351175 100755 --- a/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py +++ b/scripts/automation/trex_control_plane/client_utils/jsonrpc_client.py @@ -81,7 +81,7 @@ class JsonRpcClient(object): return pretty_str def verbose_msg (self, msg): - self.logger.log("[verbose] " + msg, level = self.logger.VERBOSE_HIGH) + self.logger.log("\n\n[verbose] " + msg, level = self.logger.VERBOSE_HIGH) # batch messages @@ -215,6 +215,11 @@ class JsonRpcClient(object): self.connected = True + rc = self.invoke_rpc_method('ping') + if not rc: + self.connected = False + return rc + return RC_OK() 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 fe96789e..c7d3308e 100755 --- a/scripts/automation/trex_control_plane/client_utils/packet_builder.py +++ b/scripts/automation/trex_control_plane/client_utils/packet_builder.py @@ -12,7 +12,7 @@ import re import itertools from abc import ABCMeta, abstractmethod from collections import namedtuple - +import base64 class CTRexPktBuilder(object): """ @@ -332,6 +332,7 @@ class CTRexPktBuilder(object): def load_packet_from_byte_list(self, byte_list): # convert byte array into buffer + byte_list = [ord(c) for c in base64.b64decode(byte_list)] buf = struct.pack('B'*len(byte_list), *byte_list) # thn, load it based on dpkt parsing @@ -480,11 +481,10 @@ class CTRexPktBuilder(object): """ if self._packet is None: raise CTRexPktBuilder.EmptyPacketError() - pkt_in_hex = binascii.hexlify(str(self._packet)) - return {"binary": [int(pkt_in_hex[i:i+2], 16) - for i in range(0, len(pkt_in_hex), 2)], + + return {"binary": base64.b64encode(str(self._packet)), "meta": self.metadata} - # return [pkt_in_hex[i:i+2] for i in range(0, len(pkt_in_hex), 2)] + def dump_pkt_to_pcap(self, file_path, ts=None): """ diff --git a/src/common/base64.cpp b/src/common/base64.cpp new file mode 100644 index 00000000..50006d4f --- /dev/null +++ b/src/common/base64.cpp @@ -0,0 +1,123 @@ +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#include "base64.h" +#include + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for(i = 0; (i <4) ; i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) + { + for(j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while((i++ < 3)) + ret += '='; + + } + + return ret; + +} + +std::string base64_decode(std::string const& encoded_string) { + int in_len = encoded_string.size(); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; in_++; + if (i ==4) { + for (i = 0; i <4; i++) + char_array_4[i] = base64_chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j <4; j++) + char_array_4[j] = 0; + + for (j = 0; j <4; j++) + char_array_4[j] = base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} \ No newline at end of file diff --git a/src/common/base64.h b/src/common/base64.h new file mode 100644 index 00000000..48a13fe5 --- /dev/null +++ b/src/common/base64.h @@ -0,0 +1,9 @@ +#ifndef __BASE64_H__ +#define __BASE64_H__ + +#include + +std::string base64_encode(unsigned char const* , unsigned int len); +std::string base64_decode(std::string const& s); + +#endif /* __BASE64_H__ */ diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index 821479f5..dc8a9ccd 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -24,7 +24,7 @@ limitations under the License. #include #include #include - +#include #include using namespace std; @@ -63,7 +63,7 @@ TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { stream->m_next_stream_id = parse_int(section, "next_stream_id", result); const Json::Value &pkt = parse_object(section, "packet", result); - const Json::Value &pkt_binary = parse_array(pkt, "binary", result); + std::string pkt_binary = base64_decode(parse_string(pkt, "binary", result)); /* fetch the packet from the message */ @@ -73,9 +73,11 @@ TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { generate_internal_err(result, "unable to allocate memory"); } - /* parse the packet */ + /* copy the packet */ + const char *pkt_buffer = pkt_binary.c_str(); + for (int i = 0; i < pkt_binary.size(); i++) { - stream->m_pkt.binary[i] = parse_byte(pkt_binary, i, result); + stream->m_pkt.binary[i] = pkt_buffer[i]; } /* meta data */ -- cgit 1.2.3-korg From ea3f39cd42ea667d1928564a25bffca19275cfeb Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 26 Jan 2016 16:57:41 -0500 Subject: 1. fixed failing regression test 2. enabled STL nose tests 3. fixed packet size padding to 60 --- api/stl/examples/stl_simple_burst.py | 2 +- .../functional_tests/pkt_builder_test.py | 2 +- .../unit_tests/functional_tests/stl_basic_tests.py | 1 - src/rpc-server/commands/trex_rpc_cmd_stream.cpp | 24 ++++++++++++---------- src/stateless/cp/trex_stream.h | 2 +- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/api/stl/examples/stl_simple_burst.py b/api/stl/examples/stl_simple_burst.py index 49355890..ff159289 100644 --- a/api/stl/examples/stl_simple_burst.py +++ b/api/stl/examples/stl_simple_burst.py @@ -27,7 +27,7 @@ def simple_burst (): pkt_bld.set_layer_attr("l4_tcp", "flags", 0) pkt_bld.set_layer_attr("l4_tcp", "win", 32768) pkt_bld.set_layer_attr("l4_tcp", "seq", 0) - pkt_bld.set_pkt_payload("abcdefgh") + #pkt_bld.set_pkt_payload("abcdefgh") pkt_bld.set_layer_attr("l3_ip", "len", len(pkt_bld.get_layer('l3_ip'))) diff --git a/scripts/automation/regression/unit_tests/functional_tests/pkt_builder_test.py b/scripts/automation/regression/unit_tests/functional_tests/pkt_builder_test.py index b8831c04..c2946798 100755 --- a/scripts/automation/regression/unit_tests/functional_tests/pkt_builder_test.py +++ b/scripts/automation/regression/unit_tests/functional_tests/pkt_builder_test.py @@ -251,7 +251,7 @@ class CTRexPktBuilder_Test(pkt_bld_general_test.CGeneralPktBld_Test): self.print_packet(self.pkt_bld.get_packet()) assert_equal(self.pkt_bld.dump_pkt(), { - 'binary': [224, 95, 185, 105, 233, 34, 0, 21, 23, 167, 117, 163, 8, 0, 69, 0, 0, 39, 0, 0, 0, 0, 64, 1, 79, 201, 21, 0, 0, 2, 22, 0, 0, 12, 8, 0, 217, 134, 95, 208, 44, 218, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100], + 'binary': '4F+5aekiABUXp3WjCABFAAAnAAAAAEABT8kVAAACFgAADAgA2YZf0CzaaGVsbG8gd29ybGQ=', 'meta': '', }) diff --git a/scripts/automation/regression/unit_tests/functional_tests/stl_basic_tests.py b/scripts/automation/regression/unit_tests/functional_tests/stl_basic_tests.py index ab4a8a87..125866ba 100644 --- a/scripts/automation/regression/unit_tests/functional_tests/stl_basic_tests.py +++ b/scripts/automation/regression/unit_tests/functional_tests/stl_basic_tests.py @@ -13,7 +13,6 @@ import os import subprocess # should be set to run explicitly, not as part of all regression tests -@nottest class CStlBasic_Test(functional_general_test.CGeneralFunctional_Test): def setUp (self): self.test_path = os.path.abspath(os.getcwd()) diff --git a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp index dc8a9ccd..69e5dd18 100644 --- a/src/rpc-server/commands/trex_rpc_cmd_stream.cpp +++ b/src/rpc-server/commands/trex_rpc_cmd_stream.cpp @@ -65,17 +65,27 @@ TrexRpcCmdAddStream::_run(const Json::Value ¶ms, Json::Value &result) { const Json::Value &pkt = parse_object(section, "packet", result); std::string pkt_binary = base64_decode(parse_string(pkt, "binary", result)); + /* check packet size */ + if ( (pkt_binary.size() < TrexStream::MIN_PKT_SIZE_BYTES) || (pkt_binary.size() > TrexStream::MAX_PKT_SIZE_BYTES) ) { + std::stringstream ss; + ss << "bad packet size provided: should be between " << TrexStream::MIN_PKT_SIZE_BYTES << " and " << TrexStream::MAX_PKT_SIZE_BYTES; + delete stream; + generate_execute_err(result, ss.str()); + } + /* fetch the packet from the message */ - stream->m_pkt.len = pkt_binary.size(); - stream->m_pkt.binary = new uint8_t[pkt_binary.size()]; + stream->m_pkt.len = std::max(pkt_binary.size(), 60UL); + + /* allocate and init to zero ( with () ) */ + stream->m_pkt.binary = new uint8_t[pkt_binary.size()](); if (!stream->m_pkt.binary) { generate_internal_err(result, "unable to allocate memory"); } - /* copy the packet */ const char *pkt_buffer = pkt_binary.c_str(); + /* copy the packet - if less than 60 it will remain zeroes */ for (int i = 0; i < pkt_binary.size(); i++) { stream->m_pkt.binary[i] = pkt_buffer[i]; } @@ -306,14 +316,6 @@ TrexRpcCmdAddStream::parse_vm(const Json::Value &vm, TrexStream *stream, Json::V void TrexRpcCmdAddStream::validate_stream(const TrexStream *stream, Json::Value &result) { - /* check packet size */ - if ( (stream->m_pkt.len < TrexStream::MIN_PKT_SIZE_BYTES) || (stream->m_pkt.len > TrexStream::MAX_PKT_SIZE_BYTES) ) { - std::stringstream ss; - ss << "bad packet size provided: should be between " << TrexStream::MIN_PKT_SIZE_BYTES << " and " << TrexStream::MAX_PKT_SIZE_BYTES; - delete stream; - generate_execute_err(result, ss.str()); - } - /* add the stream to the port's stream table */ TrexStatelessPort * port = get_stateless_obj()->get_port_by_id(stream->m_port_id); diff --git a/src/stateless/cp/trex_stream.h b/src/stateless/cp/trex_stream.h index 6bb30cf9..80d64ec5 100644 --- a/src/stateless/cp/trex_stream.h +++ b/src/stateless/cp/trex_stream.h @@ -125,7 +125,7 @@ public: virtual ~TrexStream(); /* defines the min max per packet supported */ - static const uint32_t MIN_PKT_SIZE_BYTES = 60; + static const uint32_t MIN_PKT_SIZE_BYTES = 14; static const uint32_t MAX_PKT_SIZE_BYTES = MAX_PKT_SIZE; /* provides storage for the stream json*/ -- cgit 1.2.3-korg From 1e69b27f272fef2e3124693b865521a4927418f5 Mon Sep 17 00:00:00 2001 From: imarom Date: Wed, 27 Jan 2016 03:05:36 -0500 Subject: ZMQ stubbed on simulation --- linux/ws_main.py | 23 +++++------- src/stub/trex_stateless_stub.cpp | 23 ------------ src/stub/zmq_stub.c | 81 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 37 deletions(-) delete mode 100644 src/stub/trex_stateless_stub.cpp create mode 100644 src/stub/zmq_stub.c diff --git a/linux/ws_main.py b/linux/ws_main.py index ec3ef6d7..22c52eea 100755 --- a/linux/ws_main.py +++ b/linux/ws_main.py @@ -224,8 +224,13 @@ yaml_src = SrcGroup(dir='external_libs/yaml-cpp/src/', 'tag.cpp']); +# stubs +stubs = SrcGroup(dir='/src/stub/', + src_list=['zmq_stub.c']) + rpc_server_mock = SrcGroups([ main_src, + stubs, cmn_src, rpc_server_src, rpc_server_mock_src, @@ -235,25 +240,17 @@ rpc_server_mock = SrcGroups([ net_src, ]) -# REMOVE ME - need to decide if stateless is part of bp sim or not -bp_hack_for_compile = SrcGroup(dir='/src/stub/', - src_list=['trex_stateless_stub.cpp' - ]) - bp =SrcGroups([ bp_sim_main, bp_sim_gtest, main_src, cmn_src , - + stubs, net_src , yaml_src, json_src, stateless_src, rpc_server_src - #rpc_server_mock_src, - - #bp_hack_for_compile, ]); @@ -407,15 +404,15 @@ class build_option: build_types = [ - build_option(name = "bp-sim", src = bp, use = ['zmq'],debug_mode= DEBUG_, platform = PLATFORM_64, is_pie = False, + build_option(name = "bp-sim", src = bp, use = [''],debug_mode= DEBUG_, platform = PLATFORM_64, is_pie = False, flags = ['-Wall', '-Werror', '-Wno-sign-compare', '-Wno-strict-aliasing'], rpath = ['.']), - build_option(name = "bp-sim", src = bp, use = ['zmq'],debug_mode= RELEASE_,platform = PLATFORM_64, is_pie = False, + build_option(name = "bp-sim", src = bp, use = [''],debug_mode= RELEASE_,platform = PLATFORM_64, is_pie = False, flags = ['-Wall', '-Werror', '-Wno-sign-compare', '-Wno-strict-aliasing'], rpath = ['.']), - build_option(name = "mock-rpc-server", use = ['zmq'], src = rpc_server_mock, debug_mode= DEBUG_,platform = PLATFORM_64, is_pie = False, + build_option(name = "mock-rpc-server", use = [''], src = rpc_server_mock, debug_mode= DEBUG_,platform = PLATFORM_64, is_pie = False, flags = ['-DTREX_RPC_MOCK_SERVER', '-Wall', '-Werror', '-Wno-sign-compare'], rpath = ['.']), ] @@ -423,8 +420,6 @@ build_types = [ def build_prog (bld, build_obj): - zmq_lib_path='external_libs/zmq/' - bld.read_shlib( name='zmq' , paths=[top + zmq_lib_path] ) bld.program(features='cxx cxxprogram', includes =includes_path, diff --git a/src/stub/trex_stateless_stub.cpp b/src/stub/trex_stateless_stub.cpp deleted file mode 100644 index 199356d8..00000000 --- a/src/stub/trex_stateless_stub.cpp +++ /dev/null @@ -1,23 +0,0 @@ - -#include - -class CFlowGenListPerThread; -class TrexStatelessCpToDpMsgBase; - -void -TrexStatelessDpCore::create(unsigned char, CFlowGenListPerThread*) { - m_thread_id = 0; - m_core = NULL; - - m_state = STATE_IDLE; - - CMessagingManager * cp_dp = CMsgIns::Ins()->getCpDp(); - - m_ring_from_cp = cp_dp->getRingCpToDp(0); - m_ring_to_cp = cp_dp->getRingDpToCp(0); -} - -void TrexStatelessDpCore::start(){} - -void TrexStatelessDpCore::handle_cp_msg(TrexStatelessCpToDpMsgBase*) {} - diff --git a/src/stub/zmq_stub.c b/src/stub/zmq_stub.c new file mode 100644 index 00000000..b94f93e4 --- /dev/null +++ b/src/stub/zmq_stub.c @@ -0,0 +1,81 @@ +#include +#include + +void *zmq_ctx_new (void) { + return NULL; +} + +void *zmq_socket (void *, int type) { + return NULL; +} + +int zmq_close (void *s) { + return (-1); +} + +int zmq_setsockopt (void *s, int option, const void *optval,size_t optvallen) { + return (-1); +} + +int zmq_getsockopt (void *s, int option, void *optval, + size_t *optvallen) { + return (-1); +} + +int zmq_bind (void *s, const char *addr) { + return (-1); +} + +void *zmq_init (int io_threads) { + return NULL; +} + +int zmq_term (void *context) { + return (-1); +} + +int zmq_ctx_destroy (void *context) { + return (-1); +} + + +int zmq_connect (void *s, const char *addr) { + return (-1); +} + +int zmq_send (void *s, const void *buf, size_t len, int flags) { + return (-1); +} + +int zmq_recv (void *s, void *buf, size_t len, int flags) { + return (-1); +} + +int zmq_errno (void) { + return (-1); +} + +const char *zmq_strerror (int errnum) { + return ""; +} + +int zmq_msg_init (zmq_msg_t *msg) { + return (-1); +} + +int zmq_msg_recv (zmq_msg_t *msg, void *s, int flags) { + return (-1); +} + +int zmq_msg_close (zmq_msg_t *msg) { + return (-1); +} + +void *zmq_msg_data (zmq_msg_t *msg) { + return NULL; +} + +size_t zmq_msg_size (zmq_msg_t *msg) { + return (0); +} + -- cgit 1.2.3-korg