diff options
6 files changed, 211 insertions, 22 deletions
diff --git a/scripts/automation/regression/stateless_tests/stl_general_test.py b/scripts/automation/regression/stateless_tests/stl_general_test.py index 7d44d04a..6470d8c2 100644 --- a/scripts/automation/regression/stateless_tests/stl_general_test.py +++ b/scripts/automation/regression/stateless_tests/stl_general_test.py @@ -37,6 +37,7 @@ class CStlGeneral_Test(CTRexGeneral_Test): sys.stdout.write('.') sys.stdout.flush() try: + CTRexScenario.stl_trex.remove_all_captures() CTRexScenario.stl_ports_map = stl_map_ports(self.stl_trex) if self.verify_bidirectional(CTRexScenario.stl_ports_map): print('') diff --git a/scripts/automation/trex_control_plane/stl/examples/stl_functional.py b/scripts/automation/trex_control_plane/stl/examples/stl_functional.py new file mode 100644 index 00000000..6322ce88 --- /dev/null +++ b/scripts/automation/trex_control_plane/stl/examples/stl_functional.py @@ -0,0 +1,78 @@ +import stl_path +from trex_stl_lib.api import * + +""" +An example on how to use TRex for functional tests + +It can be used for various tasks and can replace simple Pagent/Scapy +low rate tests +""" + +def test_dot1q (c, rx_port, tx_port): + + # activate service mode on RX code + c.set_service_mode(ports = rx_port) + + # generate a simple Dot1Q + pkt = Ether() / Dot1Q(vlan = 100) / IP() + + # start a capture + capture = c.start_capture(rx_ports = rx_port) + + # push the Dot1Q packet to TX port... we need 'force' because this is under service mode + print('\nSending 1 Dot1Q packet(s) on port {}'.format(tx_port)) + + c.push_packets(ports = tx_port, pkts = pkt, force = True) + c.wait_on_traffic(ports = tx_port) + + rx_pkts = [] + c.stop_capture(capture_id = capture['id'], output = rx_pkts) + + print('\nRecived {} packets on port {}:\n'.format(len(rx_pkts), rx_port)) + + c.set_service_mode(ports = rx_port, enabled = False) + + # got back one packet + assert(len(rx_pkts) == 1) + rx_scapy_pkt = Ether(rx_pkts[0]['binary']) + + # it's a Dot1Q with the same VLAN + assert('Dot1Q' in rx_scapy_pkt) + assert(rx_scapy_pkt.vlan == 100) + + + rx_scapy_pkt.show2() + + + +def main (): + + # create a client + c = STLClient() + + try: + # connect to the server + c.connect() + + # there should be at least two ports connected + tx_port, rx_port = stl_map_ports(c)['bi'][0] + c.reset(ports = [tx_port, rx_port]) + + # call the test + test_dot1q(c, tx_port, rx_port) + + + except STLError as e: + print(e) + + finally: + c.stop() + c.remove_all_captures() + c.set_service_mode(enabled = False) + c.disconnect() + + + +if __name__ == '__main__': + main() + 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 b95c190b..a1290a37 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 @@ -20,12 +20,14 @@ from texttable import ansi_len from collections import namedtuple, defaultdict from yaml import YAMLError +from contextlib import contextmanager import time import datetime import re import random import json import traceback +import tempfile import os.path ############################ logger ############################# @@ -2691,8 +2693,8 @@ class STLClient(object): self.remove_all_streams(ports = ports) id_list = self.add_streams(profile.get_streams(), ports) - - return self.start(ports = ports, duration = duration) + + return self.start(ports = ports, duration = duration, force = force) else: @@ -2728,11 +2730,91 @@ class STLClient(object): if profile_b: self.add_streams(profile_b.get_streams(), slave) - return self.start(ports = all_ports, duration = duration) + return self.start(ports = all_ports, duration = duration, force = force) + + + + + @__api_check(True) + def push_packets (self, + pkts, + ports = None, + ipg_usec = 100, + count = 1, + duration = -1, + force = False, + vm = None): + + """ + Pushes a list of packets to the server + a 'packet' can be anything with a bytes representation + such as Scapy object, a simple string, a byte array and etc. + + Total size, as for PCAP pushing is limited to 1MB + unless 'force' is specified + + the list of packets will be saved to a temporary file + which will be deleted when the function exists + + :parameters: + pkts : list or + PCAP filename (accessible locally) + + ports : list + Ports on which to execute the command + + ipg_usec : float + Inter-packet gap in microseconds. + + count: int + How many times to transmit the list + duration: float + Limit runtime by duration in seconds + force: bool + Ignore size limit - push any size to the server + vm: list of VM instructions + VM instructions to apply for every packet + :raises: + + :exc:`STLError` + """ + validate_type('ipg_usec', ipg_usec, (float, int, type(None))) + if ipg_usec < 0: + raise STLError("'ipg_usec' should not be negative") + + # create a temporary file while will be deleted when leaving scope + with tempfile.NamedTemporaryFile(delete = True) as f: + + # write packets to file + writer = RawPcapWriter(f.name, linktype = 1) + writer._write_header(None) + + # write to the file + for i, pkt in enumerate(listify(pkts)): + ts = (ipg_usec * i) / 1.0e6 + ts_sec, ts_usec = sec_split_usec(ts) + writer._write_packet(bytes(pkt), sec = ts_sec, usec = ts_usec) + + # close the writer + writer.close() + + # now inject the file + self.push_pcap(f.name, + ports = ports, + ipg_usec = ipg_usec, + speedup = 1, + count = count, + duration = duration, + force = force, + vm = vm, + packet_hook = None, + is_dual = False, + min_ipg_usec = None) + + @__api_check(True) def validate (self, ports = None, mult = "1", duration = -1, total = False): @@ -3011,7 +3093,15 @@ class STLClient(object): if not rc: raise STLError(rc) - + @contextmanager + def service_mode (self, ports): + self.set_service_mode(ports = ports) + try: + yield + finally: + self.set_service_mode(ports = ports, enabled = False) + + @__api_check(True) def resolve (self, ports = None, retries = 0, verbose = True): """ @@ -3206,11 +3296,19 @@ class STLClient(object): capture_id: int an active capture ID to stop - output: None/ str / list + output: None / str / list if output is None - all the packets will be discarded if output is a 'str' - it will be interpeted as output filename if it is a list, the API will populate the list with packet objects + in case 'output' is a list, each element in the list is an object + containing: + 'binary' - binary bytes of the packet + 'origin' - RX or TX origin + 'ts' - timestamp relative to the start of the capture + 'index' - order index in the capture + 'port' - on which port did the packet arrive or was transmitted from + :raises: + :exe:'STLError' diff --git a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_exceptions.py b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_exceptions.py index 2ca92cb8..fa8163b8 100644 --- a/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_exceptions.py +++ b/scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_exceptions.py @@ -17,14 +17,15 @@ class STLError(Exception): def __str__ (self): - fname = os.path.split(self.tb[-2][0])[1] - lineno = self.tb[-2][1] - func = self.tb[-2][2] - src = self.tb[-2][3] - - s = "\n******\n" - s += "Error at {0}:{1} - '{2}'\n\n".format(format_text(fname, 'bold'), format_text(lineno, 'bold'), format_text(src.strip(), 'bold')) - s += "specific error:\n\n{0}\n".format(format_text(self.msg, 'bold')) + s = format_text("\n******\n", 'bold') + s += format_text('\nSummary error report:\n\n', 'underline') + s += format_text(self.msg + '\n', 'bold') + + s += format_text("\nFull error report:\n\n", 'underline') + + for line in reversed(self.tb): + fname, lineno, func, src = os.path.split(line[0])[1], line[1], line[2], line[3] + s += " {:}:{:<20} - '{}'\n".format(format_text(fname, 'bold'), format_text(lineno, 'bold'), format_text(src.strip(), 'bold')) return s 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 efa450e1..8d727d6d 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 @@ -1047,6 +1047,9 @@ class STLProfile(object): if split_mode is None: pkts = PCAPReader(pcap_file).read_all() + if len(pkts) == 0: + raise STLError("'{0}' does not contain any packets".format(pcap_file)) + return STLProfile.__pkts_to_streams(pkts, ipg_usec, min_ipg_usec, @@ -1056,7 +1059,9 @@ class STLProfile(object): packet_hook) else: pkts_a, pkts_b = PCAPReader(pcap_file).read_all(split_mode = split_mode) - + if (len(pkts_a) + len(pkts_b)) == 0: + raise STLError("'{0}' does not contain any packets".format(pcap_file)) + # swap the packets if a is empty, or the ts of first packet in b is earlier if not pkts_a: pkts_a, pkts_b = pkts_b, pkts_a diff --git a/src/stateless/rx/trex_stateless_rx_port_mngr.cpp b/src/stateless/rx/trex_stateless_rx_port_mngr.cpp index b01665ec..6ebe0a97 100644 --- a/src/stateless/rx/trex_stateless_rx_port_mngr.cpp +++ b/src/stateless/rx/trex_stateless_rx_port_mngr.cpp @@ -367,18 +367,24 @@ RXServer::create(uint8_t port_id, CPortLatencyHWBase *io, const CManyIPInfo *src void RXServer::handle_pkt(const rte_mbuf_t *m) { - - RXPktParser parser(m); - - if (parser.m_icmp) { - handle_icmp(parser); - } else if (parser.m_arp) { - handle_arp(parser); - } else { + try { + + RXPktParser parser(m); + + if (parser.m_icmp) { + handle_icmp(parser); + } else if (parser.m_arp) { + handle_arp(parser); + } else { + return; + } + + } catch (const TrexException &e) { return; } } + void RXServer::handle_icmp(RXPktParser &parser) { |