From 8691f4019dc2123c1aa7413cf3666138756c2f66 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 3 May 2016 14:57:34 +0300 Subject: first remote PCAP push - draft --- .../stl/trex_stl_lib/trex_stl_client.py | 81 ++++++++++++++++++++-- .../stl/trex_stl_lib/trex_stl_port.py | 23 ++++++ .../stl/trex_stl_lib/trex_stl_streams.py | 7 +- .../stl/trex_stl_lib/utils/parsing_opts.py | 6 ++ 4 files changed, 109 insertions(+), 8 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 862a9979..c7d59690 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -313,6 +313,10 @@ class EventsHandler(object): if session_id != self.client.session_id: self.__async_event_port_released(port_id) + elif (type == 7): + port_id = int(data['port_id']) + ev = "port {0} job failed".format(port_id) + show_event = True # server stopped elif (type == 100): @@ -711,6 +715,17 @@ class STLClient(object): return rc + def __push_remote (self, pcap_filename, port_id_list, ipg_usec, speedup, count): + + port_id_list = self.__ports(port_id_list) + rc = RC() + + for port_id in port_id_list: + rc.add(self.ports[port_id].push_remote(pcap_filename, ipg_usec, speedup, count)) + + return rc + + def __validate (self, port_id_list = None): port_id_list = self.__ports(port_id_list) @@ -1851,6 +1866,49 @@ class STLClient(object): raise STLError(rc) + @__api_check(True) + def push_remote (self, pcap_filename, ports = None, ipg_usec = None, speedup = 1.0, count = 1): + """ + Push a remote reachable PCAP file + the path must be fullpath accessible to the server + + :parameters: + pcap_filename : str + PCAP file name in full path and accessible to the server + + ports : list + Ports on which to execute the command + + ipg_usec : float + Inter-packet gap in microseconds + + speedup : float + A factor to adjust IPG. effectively IPG = IPG / speedup + + count: int + How many times to transmit the cap + + + :raises: + + :exc:`STLError` + + """ + ports = ports if ports is not None else self.get_acquired_ports() + ports = self._validate_port_list(ports) + + validate_type('pcap_filename', pcap_filename, str) + validate_type('ipg_usec', ipg_usec, (float, int, type(None))) + validate_type('speedup', speedup, float) + validate_type('count', count, int) + + self.logger.pre_cmd("Pushing remote pcap on port(s) {0}:".format(ports)) + rc = self.__push_remote(pcap_filename, ports, ipg_usec, speedup, count) + self.logger.post_cmd(rc) + + if not rc: + raise STLError(rc) + + @__api_check(True) def validate (self, ports = None, mult = "1", duration = "-1", total = False): """ @@ -2519,6 +2577,7 @@ class STLClient(object): "push", self.push_line.__doc__, parsing_opts.FILE_PATH, + parsing_opts.REMOTE_FILE, parsing_opts.PORT_LIST_WITH_ALL, parsing_opts.COUNT, parsing_opts.DURATION, @@ -2541,15 +2600,23 @@ class STLClient(object): self.stop(active_ports) # pcap injection removes all previous streams from the ports - self.remove_all_streams(ports = opts.ports) + if opts.remote: + self.push_remote(opts.file[0], + ports = opts.ports, + ipg_usec = opts.ipg_usec, + speedup = opts.speedup, + count = opts.count) + + else: + self.remove_all_streams(ports = opts.ports) - profile = STLProfile.load_pcap(opts.file[0], - opts.ipg_usec, - opts.speedup, - opts.count) + profile = STLProfile.load_pcap(opts.file[0], + opts.ipg_usec, + opts.speedup, + opts.count) - id_list = self.add_streams(profile.get_streams(), opts.ports) - self.start(ports = opts.ports, duration = opts.duration, force = opts.force) + id_list = self.add_streams(profile.get_streams(), opts.ports) + self.start(ports = opts.ports, duration = opts.duration, force = opts.force) return True diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py index e8f89b27..986cb3c6 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py @@ -148,6 +148,7 @@ class Port(object): return self.owner def sync(self): + params = {"port_id": self.port_id} rc = self.transmit("get_port_status", params) @@ -553,6 +554,28 @@ class Port(object): return self.ok() + def push_remote (self, pcap_filename, ipg_usec, speedup, count): + if not self.is_acquired(): + return self.err("port is not owned") + + if (self.state == self.STATE_DOWN): + return self.err("port is down") + + params = {"handler": self.handler, + "port_id": self.port_id, + "pcap_filename": pcap_filename, + "ipg_usec": ipg_usec if ipg_usec is not None else -1, + "speedup": speedup, + "count": count} + + rc = self.transmit("push_remote", params) + if rc.bad(): + return self.err(rc.err()) + + self.state = self.STATE_TX + return self.ok() + + def get_profile (self): return self.profile diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py index 165942d8..6ee587c3 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py @@ -7,6 +7,7 @@ from .trex_stl_packet_builder_scapy import STLPktBuilder, Ether, IP, UDP, TCP, R from collections import OrderedDict, namedtuple from scapy.utils import ltoa +from scapy.error import Scapy_Exception import random import yaml import base64 @@ -967,7 +968,11 @@ class STLProfile(object): streams = [] last_ts_usec = 0 - pkts = RawPcapReader(pcap_file).read_all() + try: + pkts = RawPcapReader(pcap_file).read_all() + except Scapy_Exception as e: + raise STLError("failed to open PCAP file '{0}'".format(pcap_file)) + for i, (cap, meta) in enumerate(pkts, start = 1): # IPG - if not provided, take from cap diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py index ad46625b..98e3ca6a 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/parsing_opts.py @@ -32,6 +32,7 @@ PROMISCUOUS = 19 NO_PROMISCUOUS = 20 PROMISCUOUS_SWITCH = 21 TUNABLES = 22 +REMOTE_FILE = 23 GLOBAL_STATS = 50 PORT_STATS = 51 @@ -290,6 +291,11 @@ OPTIONS_DB = {MULTIPLIER: ArgumentPack(['-m', '--multiplier'], 'default': False, 'help': "Set if you want to stop active ports before appyling command."}), + REMOTE_FILE: ArgumentPack(['-r', '--remote'], + {"action": "store_true", + 'default': False, + 'help': "file path should be interpeted by the server (remote file)"}), + FILE_PATH: ArgumentPack(['-f'], {'metavar': 'FILE', 'dest': 'file', -- cgit From 75ce59e5652f9094beab854d263a850cfc81a3de Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 8 May 2016 10:57:57 +0300 Subject: PCAP refinement --- .../stl/trex_stl_lib/trex_stl_client.py | 25 ++- .../stl/trex_stl_lib/trex_stl_port.py | 222 +++++++++++---------- .../stl/trex_stl_lib/trex_stl_streams.py | 2 +- .../stl/trex_stl_lib/utils/text_opts.py | 12 +- 4 files changed, 137 insertions(+), 124 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index c7d59690..0d95131f 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -335,30 +335,37 @@ class EventsHandler(object): # private functions + # on rare cases events may come on a non existent prot + # (server was re-run with different config) def __async_event_port_job_done (self, port_id): - self.client.ports[port_id].async_event_port_job_done() + if port_id in self.client.ports: + self.client.ports[port_id].async_event_port_job_done() def __async_event_port_stopped (self, port_id): - self.client.ports[port_id].async_event_port_stopped() + if port_id in self.client.ports: + self.client.ports[port_id].async_event_port_stopped() def __async_event_port_started (self, port_id): - self.client.ports[port_id].async_event_port_started() - + if port_id in self.client.ports: + self.client.ports[port_id].async_event_port_started() def __async_event_port_paused (self, port_id): - self.client.ports[port_id].async_event_port_paused() + if port_id in self.client.ports: + self.client.ports[port_id].async_event_port_paused() def __async_event_port_resumed (self, port_id): - self.client.ports[port_id].async_event_port_resumed() - + if port_id in self.client.ports: + self.client.ports[port_id].async_event_port_resumed() def __async_event_port_acquired (self, port_id, who): - self.client.ports[port_id].async_event_acquired(who) + if port_id in self.client.ports: + self.client.ports[port_id].async_event_acquired(who) def __async_event_port_released (self, port_id): - self.client.ports[port_id].async_event_released() + if port_id in self.client.ports: + self.client.ports[port_id].async_event_released() def __async_event_server_stopped (self): self.client.connected = False diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py index 986cb3c6..43fc29e6 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py @@ -29,17 +29,20 @@ def mult_to_factor (mult, max_bps_l2, max_pps, line_util): # describes a single port class Port(object): - STATE_DOWN = 0 - STATE_IDLE = 1 - STATE_STREAMS = 2 - STATE_TX = 3 - STATE_PAUSE = 4 + STATE_DOWN = 0 + STATE_IDLE = 1 + STATE_STREAMS = 2 + STATE_TX = 3 + STATE_PAUSE = 4 + STATE_PCAP_TX = 5 + PortState = namedtuple('PortState', ['state_id', 'state_name']) STATES_MAP = {STATE_DOWN: "DOWN", STATE_IDLE: "IDLE", STATE_STREAMS: "IDLE", STATE_TX: "ACTIVE", - STATE_PAUSE: "PAUSE"} + STATE_PAUSE: "PAUSE", + STATE_PCAP_TX : "ACTIVE"} def __init__ (self, port_id, user, comm_link, session_id, info): @@ -67,6 +70,54 @@ class Port(object): self.owner = '' + # decorator to verify port is up + def up(func): + def func_wrapper(*args): + port = args[0] + + if not port.is_up(): + return port.err("{0} - port is down".format(func.__name__)) + + return func(*args) + + return func_wrapper + + # owned + def owned(func): + def func_wrapper(*args): + port = args[0] + + if not port.is_up(): + return port.err("{0} - port is down".format(func.__name__)) + + if not port.is_acquired(): + return port.err("{0} - port is not owned".format(func.__name__)) + + return func(*args) + + return func_wrapper + + + # decorator to check server is readable (port not down and etc.) + def writeable(func): + def func_wrapper(*args): + port = args[0] + + if not port.is_up(): + return port.err("{0} - port is down".format(func.__name__)) + + if not port.is_acquired(): + return port.err("{0} - port is not owned".format(func.__name__)) + + if not port.is_writeable(): + return port.err("{0} - port is not in a writeable state".format(func.__name__)) + + return func(*args) + + return func_wrapper + + + def err(self, msg): return RC_ERR("port {0} : {1}\n".format(self.port_id, msg)) @@ -79,7 +130,39 @@ class Port(object): def get_formatted_speed (self): return "{0} Gbps".format(self.info['speed']) + def is_acquired(self): + return (self.handler != None) + + def is_up (self): + return (self.state != self.STATE_DOWN) + + def is_active(self): + return (self.state == self.STATE_TX ) or (self.state == self.STATE_PAUSE) or (self.state == self.STATE_PCAP_TX) + + def is_transmitting (self): + return (self.state == self.STATE_TX) or (self.state == self.STATE_PCAP_TX) + + def is_paused (self): + return (self.state == self.STATE_PAUSE) + + def is_writeable (self): + # operations on port can be done on state idle or state streams + return ((self.state == self.STATE_IDLE) or (self.state == self.STATE_STREAMS)) + + def get_owner (self): + if self.is_acquired(): + return self.user + else: + return self.owner + + def __allocate_stream_id (self): + id = self.next_available_id + self.next_available_id += 1 + return id + + # take the port + @up def acquire(self, force = False, sync_streams = True): params = {"port_id": self.port_id, "user": self.user, @@ -99,6 +182,7 @@ class Port(object): # sync all the streams with the server + @up def sync_streams (self): params = {"port_id": self.port_id} @@ -114,6 +198,7 @@ class Port(object): return self.ok() # release the port + @up def release(self): params = {"port_id": self.port_id, "handler": self.handler} @@ -129,24 +214,9 @@ class Port(object): else: return self.err(rc.err()) - def is_acquired(self): - return (self.handler != None) - - def is_active(self): - return(self.state == self.STATE_TX ) or (self.state == self.STATE_PAUSE) - - def is_transmitting (self): - return (self.state == self.STATE_TX) - - def is_paused (self): - return (self.state == self.STATE_PAUSE) - - def get_owner (self): - if self.is_acquired(): - return self.user - else: - return self.owner + + @up def sync(self): params = {"port_id": self.port_id} @@ -168,6 +238,8 @@ class Port(object): self.state = self.STATE_TX elif port_state == "PAUSE": self.state = self.STATE_PAUSE + elif port_state == "PCAP_TX": + self.state = self.STATE_PCAP_TX else: raise Exception("port {0}: bad state received from server '{1}'".format(self.port_id, port_state)) @@ -182,27 +254,11 @@ class Port(object): return self.ok() - # return TRUE if write commands - def is_port_writable (self): - # operations on port can be done on state idle or state streams - return ((self.state == self.STATE_IDLE) or (self.state == self.STATE_STREAMS)) - - - def __allocate_stream_id (self): - id = self.next_available_id - self.next_available_id += 1 - return id - # add streams + @writeable def add_streams (self, streams_list): - if not self.is_acquired(): - return self.err("port is not owned") - - if not self.is_port_writable(): - return self.err("Please stop port before attempting to add streams") - # listify streams_list = streams_list if isinstance(streams_list, list) else [streams_list] @@ -274,14 +330,9 @@ class Port(object): # remove stream from port + @writeable def remove_streams (self, stream_id_list): - if not self.is_acquired(): - return self.err("port is not owned") - - if not self.is_port_writable(): - return self.err("Please stop port before attempting to remove streams") - # single element to list stream_id_list = stream_id_list if isinstance(stream_id_list, list) else [stream_id_list] @@ -315,14 +366,9 @@ class Port(object): # remove all the streams + @writeable def remove_all_streams (self): - if not self.is_acquired(): - return self.err("port is not owned") - - if not self.is_port_writable(): - return self.err("Please stop port before attempting to remove streams") - params = {"handler": self.handler, "port_id": self.port_id} @@ -349,19 +395,11 @@ class Port(object): return self.streams - # start traffic + @writeable def start (self, mul, duration, force): - if not self.is_acquired(): - return self.err("port is not owned") - - if self.state == self.STATE_DOWN: - return self.err("Unable to start traffic - port is down") if self.state == self.STATE_IDLE: - return self.err("Unable to start traffic - no streams attached to port") - - if self.state == self.STATE_TX: - return self.err("Unable to start traffic - port is already transmitting") + return self.err("unable to start traffic - no streams attached to port") params = {"handler": self.handler, "port_id": self.port_id, @@ -379,15 +417,12 @@ class Port(object): # stop traffic # with force ignores the cached state and sends the command + @owned def stop (self, force = False): - if not self.is_acquired(): - return self.err("port is not owned") - - # port is already stopped - if not force: - if (self.state == self.STATE_IDLE) or (self.state == self.state == self.STATE_STREAMS): - return self.ok() + # if not is not active and not force - go back + if not self.is_active() and not force: + return self.ok() params = {"handler": self.handler, "port_id": self.port_id} @@ -421,19 +456,10 @@ class Port(object): return not self.tx_stopped_ts or (datetime.now() - self.tx_stopped_ts) > timedelta(milliseconds = rx_delay_ms) - + @writeable def remove_rx_filters (self): assert(self.has_rx_enabled()) - if not self.is_acquired(): - return self.err("port is not owned") - - if self.state == self.STATE_DOWN: - return self.err("Unable to remove RX filters - port is down") - - if self.state == self.STATE_TX: - return self.err("Unable to remove RX filters - port is transmitting") - if self.state == self.STATE_IDLE: return self.ok() @@ -447,12 +473,9 @@ class Port(object): return self.ok() - + @owned def pause (self): - if not self.is_acquired(): - return self.err("port is not owned") - if (self.state != self.STATE_TX) : return self.err("port is not transmitting") @@ -467,12 +490,9 @@ class Port(object): return self.ok() - + @owned def resume (self): - if not self.is_acquired(): - return self.err("port is not owned") - if (self.state != self.STATE_PAUSE) : return self.err("port is not in pause mode") @@ -489,12 +509,9 @@ class Port(object): return self.ok() - + @owned def update (self, mul, force): - if not self.is_acquired(): - return self.err("port is not owned") - if (self.state != self.STATE_TX) : return self.err("port is not transmitting") @@ -509,15 +526,9 @@ class Port(object): return self.ok() - + @owned def validate (self): - if not self.is_acquired(): - return self.err("port is not owned") - - if (self.state == self.STATE_DOWN): - return self.err("port is down") - if (self.state == self.STATE_IDLE): return self.err("no streams attached to port") @@ -533,12 +544,8 @@ class Port(object): return self.ok() + @owned def set_attr (self, attr_dict): - if not self.is_acquired(): - return self.err("port is not owned") - - if (self.state == self.STATE_DOWN): - return self.err("port is down") params = {"handler": self.handler, "port_id": self.port_id, @@ -553,13 +560,8 @@ class Port(object): return self.ok() - + @writeable def push_remote (self, pcap_filename, ipg_usec, speedup, count): - if not self.is_acquired(): - return self.err("port is not owned") - - if (self.state == self.STATE_DOWN): - return self.err("port is down") params = {"handler": self.handler, "port_id": self.port_id, @@ -572,7 +574,7 @@ class Port(object): if rc.bad(): return self.err(rc.err()) - self.state = self.STATE_TX + self.state = self.STATE_PCAP_TX return self.ok() diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py index 6ee587c3..66aeaef4 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py @@ -939,7 +939,7 @@ class STLProfile(object): Name of the pcap file ipg_usec : float - Inter packet gap in usec. If IPG=0, IPG is taken from pcap file + Inter packet gap in usec. If IPG is None, IPG is taken from pcap file speedup : float When reading the pcap file, divide IPG by this "speedup" factor. Resulting IPG is sped up by this factor. diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py index 5c0dfb14..7e0bf9e4 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/utils/text_opts.py @@ -150,11 +150,15 @@ def format_text(text, *args): def format_threshold (value, red_zone, green_zone): - if value >= red_zone[0] and value <= red_zone[1]: - return format_text("{0}".format(value), 'red') + try: + if value >= red_zone[0] and value <= red_zone[1]: + return format_text("{0}".format(value), 'red') - if value >= green_zone[0] and value <= green_zone[1]: - return format_text("{0}".format(value), 'green') + if value >= green_zone[0] and value <= green_zone[1]: + return format_text("{0}".format(value), 'green') + except TypeError: + # if value is not comparable or not a number - skip this + pass return "{0}".format(value) -- cgit From db9145d248720c633dd4da6c785e071198986326 Mon Sep 17 00:00:00 2001 From: imarom Date: Sun, 8 May 2016 17:32:12 +0300 Subject: added duration to remote push --- .../stl/trex_stl_lib/trex_stl_client.py | 112 +++++++++++++++++---- .../stl/trex_stl_lib/trex_stl_port.py | 11 +- .../stl/trex_stl_lib/trex_stl_streams.py | 5 +- 3 files changed, 104 insertions(+), 24 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 0d95131f..9f96b237 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -722,13 +722,13 @@ class STLClient(object): return rc - def __push_remote (self, pcap_filename, port_id_list, ipg_usec, speedup, count): + def __push_remote (self, pcap_filename, port_id_list, ipg_usec, speedup, count, duration): port_id_list = self.__ports(port_id_list) rc = RC() for port_id in port_id_list: - rc.add(self.ports[port_id].push_remote(pcap_filename, ipg_usec, speedup, count)) + rc.add(self.ports[port_id].push_remote(pcap_filename, ipg_usec, speedup, count, duration)) return rc @@ -1874,9 +1874,15 @@ class STLClient(object): @__api_check(True) - def push_remote (self, pcap_filename, ports = None, ipg_usec = None, speedup = 1.0, count = 1): + def push_remote (self, + pcap_filename, + ports = None, + ipg_usec = None, + speedup = 1.0, + count = 1, + duration = -1): """ - Push a remote reachable PCAP file + Push a remote server-reachable PCAP file the path must be fullpath accessible to the server :parameters: @@ -1895,7 +1901,8 @@ class STLClient(object): count: int How many times to transmit the cap - + duration: float + Limit runtime by duration in seconds :raises: + :exc:`STLError` @@ -1905,11 +1912,12 @@ class STLClient(object): validate_type('pcap_filename', pcap_filename, str) validate_type('ipg_usec', ipg_usec, (float, int, type(None))) - validate_type('speedup', speedup, float) + validate_type('speedup', speedup, (float, int)) validate_type('count', count, int) + validate_type('duration', duration, (float, int)) - self.logger.pre_cmd("Pushing remote pcap on port(s) {0}:".format(ports)) - rc = self.__push_remote(pcap_filename, ports, ipg_usec, speedup, count) + self.logger.pre_cmd("Pushing remote PCAP on port(s) {0}:".format(ports)) + rc = self.__push_remote(pcap_filename, ports, ipg_usec, speedup, count, duration) self.logger.post_cmd(rc) if not rc: @@ -1917,7 +1925,71 @@ class STLClient(object): @__api_check(True) - def validate (self, ports = None, mult = "1", duration = "-1", total = False): + def push_pcap (self, + pcap_filename, + ports = None, + ipg_usec = None, + speedup = 1.0, + count = 1, + duration = -1): + """ + Push a local PCAP to the server + This is equivalent to loading a PCAP file to a profile + and attaching the profile to port(s) + + file size is limited to 1MB + + :parameters: + pcap_filename : str + PCAP filename (accessible locally) + + ports : list + Ports on which to execute the command + + ipg_usec : float + Inter-packet gap in microseconds + + speedup : float + A factor to adjust IPG. effectively IPG = IPG / speedup + + count: int + How many times to transmit the cap + + duration: float + Limit runtime by duration in seconds + + :raises: + + :exc:`STLError` + + """ + ports = ports if ports is not None else self.get_acquired_ports() + ports = self._validate_port_list(ports) + + validate_type('pcap_filename', pcap_filename, str) + validate_type('ipg_usec', ipg_usec, (float, int, type(None))) + validate_type('speedup', speedup, (float, int)) + validate_type('count', count, int) + validate_type('duration', duration, (float, int)) + + # no support for > 1MB PCAP - use push remote + if os.path.getsize(pcap_filename) > (1024 * 1024): + raise STLError("PCAP size of {:,} B is too big for local push - consider using remote push".format(os.path.getsize(pcap_filename))) + + self.remove_all_streams(ports = ports) + + profile = STLProfile.load_pcap(pcap_filename, + ipg_usec, + speedup, + count) + + + id_list = self.add_streams(profile.get_streams(), ports) + + return self.start(ports = ports, duration = duration) + + + @__api_check(True) + def validate (self, ports = None, mult = "1", duration = -1, total = False): """ Validate port(s) configuration @@ -1964,6 +2036,8 @@ class STLClient(object): rc = self.__validate(ports) self.logger.post_cmd(rc) + if not rc: + raise STLError(rc) for port in ports: self.ports[port].print_profile(mult_obj, duration) @@ -2606,24 +2680,24 @@ class STLClient(object): else: self.stop(active_ports) - # pcap injection removes all previous streams from the ports + if opts.remote: self.push_remote(opts.file[0], ports = opts.ports, ipg_usec = opts.ipg_usec, speedup = opts.speedup, - count = opts.count) + count = opts.count, + duration = opts.duration) else: - self.remove_all_streams(ports = opts.ports) - - profile = STLProfile.load_pcap(opts.file[0], - opts.ipg_usec, - opts.speedup, - opts.count) + self.push_pcap(opts.file[0], + ports = opts.ports, + ipg_usec = opts.ipg_usec, + speedup = opts.speedup, + count = opts.count, + duration = opts.duration) - id_list = self.add_streams(profile.get_streams(), opts.ports) - self.start(ports = opts.ports, duration = opts.duration, force = opts.force) + return True diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py index 43fc29e6..391b2076 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_port.py @@ -476,6 +476,9 @@ class Port(object): @owned def pause (self): + if (self.state == self.STATE_PCAP_TX) : + return self.err("pause is not supported during PCAP TX") + if (self.state != self.STATE_TX) : return self.err("port is not transmitting") @@ -512,6 +515,9 @@ class Port(object): @owned def update (self, mul, force): + if (self.state == self.STATE_PCAP_TX) : + return self.err("update is not supported during PCAP TX") + if (self.state != self.STATE_TX) : return self.err("port is not transmitting") @@ -561,14 +567,15 @@ class Port(object): return self.ok() @writeable - def push_remote (self, pcap_filename, ipg_usec, speedup, count): + def push_remote (self, pcap_filename, ipg_usec, speedup, count, duration): params = {"handler": self.handler, "port_id": self.port_id, "pcap_filename": pcap_filename, "ipg_usec": ipg_usec if ipg_usec is not None else -1, "speedup": speedup, - "count": count} + "count": count, + "duration": duration} rc = self.transmit("push_remote", params) if rc.bad(): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py index 66aeaef4..92598312 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py @@ -959,8 +959,8 @@ class STLProfile(object): raise STLError("file '{0}' does not exists".format(pcap_file)) # make sure IPG is not less than 1 usec - if ipg_usec is not None and ipg_usec < 1: - raise STLError("ipg_usec cannot be less than 1 usec: '{0}'".format(ipg_usec)) + if ipg_usec is not None and ipg_usec < 0.001: + raise STLError("ipg_usec cannot be less than 0.001 usec: '{0}'".format(ipg_usec)) if loop_count < 0: raise STLError("'loop_count' cannot be negative") @@ -989,7 +989,6 @@ class STLProfile(object): next = i + 1 action_count = 0 - streams.append(STLStream(name = i, packet = STLPktBuilder(pkt_buffer = cap, vm = vm), mode = STLTXSingleBurst(total_pkts = 1, percentage = 100), -- cgit From f2b38a3de49bd73ac1c29103c9c3305b18222c6a Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 9 May 2016 17:00:24 +0300 Subject: small fix for TUI (shows CPU util. in round up float) --- .../trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py index c7513144..61122e79 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_stats.py @@ -28,6 +28,9 @@ SS_COMPAT = [GLOBAL_STATS, STREAMS_STATS] ExportableStats = namedtuple('ExportableStats', ['raw_data', 'text_table']) +def round_float (f): + return float("%.2f" % f) + # deep mrege of dicts dst = src + dst def deep_merge_dicts (dst, src): for k, v in src.items(): @@ -633,10 +636,10 @@ class CGlobalStats(CTRexStats): ("version", "{ver}, UUID: {uuid}".format(ver=self.server_version.get("version", "N/A"), uuid="N/A")), - ("cpu_util", "{0}% {1}".format( format_threshold(self.get("m_cpu_util"), [85, 100], [0, 85]), + ("cpu_util", "{0}% {1}".format( format_threshold(round_float(self.get("m_cpu_util")), [85, 100], [0, 85]), self.get_trend_gui("m_cpu_util", use_raw = True))), - ("rx_cpu_util", "{0}% {1}".format( format_threshold(self.get("m_rx_cpu_util"), [85, 100], [0, 85]), + ("rx_cpu_util", "{0}% {1}".format( format_threshold(round_float(self.get("m_rx_cpu_util")), [85, 100], [0, 85]), self.get_trend_gui("m_rx_cpu_util", use_raw = True))), (" ", ""), -- cgit From 1142e8c4678b2c357b080f5ae31eca30fac46478 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 9 May 2016 17:16:58 +0300 Subject: added force option to local PCAP - for > 1MB files --- .../trex_control_plane/stl/trex_stl_lib/trex_stl_client.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 9f96b237..d1352804 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -1931,7 +1931,8 @@ class STLClient(object): ipg_usec = None, speedup = 1.0, count = 1, - duration = -1): + duration = -1, + force = False): """ Push a local PCAP to the server This is equivalent to loading a PCAP file to a profile @@ -1958,6 +1959,9 @@ class STLClient(object): duration: float Limit runtime by duration in seconds + force: bool + Ignore file size limit - push any file size to the server + :raises: + :exc:`STLError` @@ -1972,8 +1976,8 @@ class STLClient(object): validate_type('duration', duration, (float, int)) # no support for > 1MB PCAP - use push remote - if os.path.getsize(pcap_filename) > (1024 * 1024): - raise STLError("PCAP size of {:,} B is too big for local push - consider using remote push".format(os.path.getsize(pcap_filename))) + if not force and os.path.getsize(pcap_filename) > (1024 * 1024): + raise STLError("PCAP size of {:} is too big for local push - consider using remote push or provide 'force'".format(format_num(os.path.getsize(pcap_filename), suffix = 'B'))) self.remove_all_streams(ports = ports) @@ -2695,7 +2699,8 @@ class STLClient(object): ipg_usec = opts.ipg_usec, speedup = opts.speedup, count = opts.count, - duration = opts.duration) + duration = opts.duration, + force = opts.force) -- cgit From 38369dfc189a84a5e99d8a0d1c18276192995c69 Mon Sep 17 00:00:00 2001 From: imarom Date: Mon, 9 May 2016 22:47:30 +0300 Subject: clear_stats must first call an async barrier otherwise we might clean unsycned stats and it will be useless next time the client gets an async update --- .../automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index d1352804..5e20adb8 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -854,6 +854,9 @@ class STLClient(object): # clear stats def __clear_stats(self, port_id_list, clear_global, clear_flow_stats): + # we must be sync with the server + self.async_client.barrier() + for port_id in port_id_list: self.ports[port_id].clear_stats() -- cgit From ba731d02eadc3c2f0c13493e518a167d199b41e7 Mon Sep 17 00:00:00 2001 From: Yaroslav Brustinov Date: Tue, 10 May 2016 11:36:55 +0300 Subject: hltapi: use flow_stats only for ipv4 packets, add flag to disable flow_stats, add argument for custom flow_stat id for use in profiles. --- .../trex_control_plane/stl/trex_stl_lib/trex_stl_hltapi.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_hltapi.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_hltapi.py index 0afeff20..33a7b3af 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_hltapi.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_hltapi.py @@ -23,7 +23,9 @@ traffic_config_kwargs = { 'split_by_cores': 'split', # ( split | duplicate | single ) TRex extention: split = split traffic by cores, duplicate = duplicate traffic for all cores, single = run only with sinle core (not implemented yet) 'load_profile': None, # TRex extention: path to filename with stream profile (stream builder parameters will be ignored, limitation: modify) 'consistent_random': False, # TRex extention: False (default) = random sequence will be different every run, True = random sequence will be same every run - 'ignore_macs': False, # TRex extention: True = use MACs from server configuration , no MAC VM (workaround on lack of ARP) + 'ignore_macs': False, # TRex extention: True = use MACs from server configuration, no MAC VM (workaround on lack of ARP) + 'disable_flow_stats': False, # TRex extention: True = don't use flow stats for this stream, (workaround for limitation on type of packet for flow_stats) + 'flow_stats_id': None, # TRex extention: uint, for use of STLHltStream, specifies id for flow stats (see stateless manual for flow_stats details) 'port_handle': None, 'port_handle2': None, 'bidirectional': False, @@ -446,7 +448,7 @@ class CTRexHltApi(object): kwargs = merge_kwargs(traffic_config_kwargs, user_kwargs) stream_id = kwargs['stream_id'] mode = kwargs['mode'] - pg_id = None + pg_id = kwargs['flow_stats_id'] port_handle = port_list = self._parse_port_list(kwargs['port_handle']) ALLOWED_MODES = ['create', 'modify', 'remove', 'enable', 'disable', 'reset'] @@ -864,7 +866,10 @@ def STLHltStream(**user_kwargs): raise STLError('Could not create transmit_mode object %s: %s' % (transmit_mode, e if isinstance(e, STLError) else traceback.format_exc())) try: - pg_id = kwargs.get('pg_id') + if kwargs['l3_protocol'] == 'ipv4' and not kwargs['disable_flow_stats']: + pg_id = kwargs.get('pg_id', kwargs.get('flow_stats_id')) + else: + pg_id = None stream = STLStream(packet = packet, random_seed = 1 if is_true(kwargs['consistent_random']) else 0, #enabled = True, -- cgit From 6daea438c392fda4c765d2afa46a34d691799bd7 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 10 May 2016 14:20:30 +0300 Subject: wait_on_traffic might get stuck if the async thread unexpectedly crashed or exited --- .../trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py | 9 +++++++-- .../trex_control_plane/stl/trex_stl_lib/trex_stl_client.py | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py index 022077a9..5c9faf0f 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_async_client.py @@ -194,11 +194,13 @@ class CTRexAsyncClient(): if not self.connected: return + # mark for join + self.active = False + # signal that the context was destroyed (exit the thread loop) self.context.term() - # mark for join and join - self.active = False + # join self.t.join() # done @@ -239,6 +241,7 @@ class CTRexAsyncClient(): except zmq.ContextTerminated: # outside thread signaled us to exit + assert(not self.active) break msg = json.loads(line) @@ -256,6 +259,8 @@ class CTRexAsyncClient(): # closing of socket must be from the same thread self.socket.close(linger = 0) + def is_thread_alive (self): + return self.t.is_alive() # did we get info for the last 3 seconds ? def is_alive (self): diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 5e20adb8..7f6f3c0d 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -2141,6 +2141,11 @@ class STLClient(object): # wait while any of the required ports are active while set(self.get_active_ports()).intersection(ports): + + # make sure ASYNC thread is still alive - otherwise we will be stuck forever + if not self.async_client.is_thread_alive(): + raise STLError("subscriber thread is dead") + time.sleep(0.01) if time.time() > expr: raise STLTimeoutError(timeout) -- cgit From 63bf6aba10075a03fe6609369c1c7008afb85ba7 Mon Sep 17 00:00:00 2001 From: imarom Date: Tue, 10 May 2016 15:36:33 +0300 Subject: PCAP API - added packet hook example is much simpler now --- .../stl/trex_stl_lib/trex_stl_client.py | 19 +++++++++++++----- .../trex_stl_lib/trex_stl_packet_builder_scapy.py | 23 ++++++++++++++++++++++ .../stl/trex_stl_lib/trex_stl_streams.py | 10 ++++++++-- 3 files changed, 45 insertions(+), 7 deletions(-) (limited to 'scripts/automation/trex_control_plane/stl/trex_stl_lib') diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py index 7f6f3c0d..010d966c 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py @@ -1935,7 +1935,9 @@ class STLClient(object): speedup = 1.0, count = 1, duration = -1, - force = False): + force = False, + vm = None, + packet_hook = None): """ Push a local PCAP to the server This is equivalent to loading a PCAP file to a profile @@ -1965,6 +1967,12 @@ class STLClient(object): force: bool Ignore file size limit - push any file size to the server + vm: list of VM instructions + VM instructions to apply for every packet + + packet_hook : Callable or function + Will be applied to every packet + :raises: + :exc:`STLError` @@ -1977,7 +1985,8 @@ class STLClient(object): validate_type('speedup', speedup, (float, int)) validate_type('count', count, int) validate_type('duration', duration, (float, int)) - + validate_type('vm', vm, (list, type(None))) + # no support for > 1MB PCAP - use push remote if not force and os.path.getsize(pcap_filename) > (1024 * 1024): raise STLError("PCAP size of {:} is too big for local push - consider using remote push or provide 'force'".format(format_num(os.path.getsize(pcap_filename), suffix = 'B'))) @@ -1987,8 +1996,9 @@ class STLClient(object): profile = STLProfile.load_pcap(pcap_filename, ipg_usec, speedup, - count) - + count, + vm = vm, + packet_hook = packet_hook) id_list = self.add_streams(profile.get_streams(), ports) @@ -2077,7 +2087,6 @@ class STLClient(object): if not type(clear_global) is bool: raise STLArgumentError('clear_global', clear_global) - rc = self.__clear_stats(ports, clear_global, clear_flow_stats) if not rc: raise STLError(rc) diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py index f8517a47..8c711563 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_packet_builder_scapy.py @@ -1479,4 +1479,27 @@ class STLPktBuilder(CTrexPktBuilderInterface): pass; +def STLIPRange (src = None, + dst = None, + fix_chksum = True): + + vm = [] + + if src: + vm += [ + STLVmFlowVar(name="src", min_value = src['start'], max_value = src['end'], size = 4, op = "inc", step = src['step']), + STLVmWrFlowVar(fv_name="src",pkt_offset= "IP.src") + ] + + if dst: + vm += [ + STLVmFlowVar(name="dst", min_value = dst['start'], max_value = dst['end'], size = 4, op = "inc", step = dst['step']), + STLVmWrFlowVar(fv_name="dst",pkt_offset= "IP.dst") + ] + + if fix_chksum: + vm.append( STLVmFixIpv4(offset = "IP")) + + + return vm diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py index 92598312..a7fd3026 100755 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py @@ -928,7 +928,7 @@ class STLProfile(object): # loop_count = 0 means loop forever @staticmethod - def load_pcap (pcap_file, ipg_usec = None, speedup = 1.0, loop_count = 1, vm = None): + def load_pcap (pcap_file, ipg_usec = None, speedup = 1.0, loop_count = 1, vm = None, packet_hook = None): """ Convert a pcap file with a number of packets to a list of connected streams. packet1->packet2->packet3 etc @@ -950,6 +950,9 @@ class STLProfile(object): vm : list List of Field engine instructions + packet_hook : Callable or function + will be applied to every packet + :return: STLProfile """ @@ -973,7 +976,10 @@ class STLProfile(object): except Scapy_Exception as e: raise STLError("failed to open PCAP file '{0}'".format(pcap_file)) - + if packet_hook: + pkts = [(packet_hook(cap), meta) for (cap, meta) in pkts] + + for i, (cap, meta) in enumerate(pkts, start = 1): # IPG - if not provided, take from cap if ipg_usec == None: -- cgit