added example for functional tests using start_capture/stop_capture APIs 97/5297/1
authorimarom <[email protected]>
Mon, 6 Feb 2017 11:20:41 +0000 (13:20 +0200)
committerimarom <[email protected]>
Mon, 6 Feb 2017 11:21:44 +0000 (13:21 +0200)
Signed-off-by: imarom <[email protected]>
scripts/automation/regression/stateless_tests/stl_general_test.py
scripts/automation/trex_control_plane/stl/examples/stl_functional.py [new file with mode: 0644]
scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_client.py
scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_exceptions.py
scripts/automation/trex_control_plane/stl/trex_stl_lib/trex_stl_streams.py
src/stateless/rx/trex_stateless_rx_port_mngr.cpp

index 7d44d04..6470d8c 100644 (file)
@@ -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 (file)
index 0000000..6322ce8
--- /dev/null
@@ -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()
+
index b95c190..a1290a3 100755 (executable)
@@ -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'
 
index 2ca92cb..fa8163b 100644 (file)
@@ -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
 
index efa450e..8d727d6 100755 (executable)
@@ -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
index b01665e..6ebe0a9 100644 (file)
@@ -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) {