VOM: mroutes
[vpp.git] / test / test_l2xc.py
index f5fc874..2ec4af9 100644 (file)
 #!/usr/bin/env python
-## @file test_l2xc.py
-#  Module to provide L2 cross-connect test case.
-#
-#  The module provides a set of tools for L2 cross-connect tests.
-
-import logging
-logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
 
 import unittest
 import random
-from framework import VppTestCase, VppTestRunner
-from scapy.layers.l2 import Ether, Raw
+
+from scapy.packet import Raw
+from scapy.layers.l2 import Ether
 from scapy.layers.inet import IP, UDP
 
+from framework import VppTestCase, VppTestRunner
+from util import Host, ppp
+
 
-## Subclass of the VppTestCase class.
-#
-#  This subclass is a class for L2 cross-connect test cases. It provides methods
-#  to create interfaces, configuring L2 cross-connects, creating and verifying
-#  packet streams.
 class TestL2xc(VppTestCase):
     """ L2XC Test Case """
 
-    # Test variables
-    interf_nr = 4           # Number of interfaces
-    hosts_nr = 10           # Number of hosts
-    pkts_per_burst = 257    # Number of packets per burst
-
-    ## Class method to start the test case.
-    #  Overrides setUpClass method in VppTestCase class.
-    #  There is used try..except statement to ensure that the tear down of
-    #  the class will be executed even if any exception is raised.
-    #  @param cls The class pointer.
     @classmethod
     def setUpClass(cls):
+        """
+        Perform standard class setup (defined by class method setUpClass in
+        class VppTestCase) before running the test case, set test case related
+        variables and configure VPP.
+
+        :var int hosts_nr: Number of hosts to be created.
+        :var int dl_pkts_per_burst: Number of packets in burst for dual-loop
+            test.
+        :var int sl_pkts_per_burst: Number of packets in burst for single-loop
+            test.
+        """
         super(TestL2xc, cls).setUpClass()
 
+        # Test variables
+        cls.hosts_nr = 10
+        cls.dl_pkts_per_burst = 257
+        cls.sl_pkts_per_burst = 2
+
         try:
-            ## Create interfaces
-            cls.interfaces = range(TestL2xc.interf_nr)
-            cls.create_interfaces(cls.interfaces)
+            # create 4 pg interfaces
+            cls.create_pg_interfaces(range(4))
+
+            # packet flows mapping pg0 -> pg1, pg2 -> pg3, etc.
+            cls.flows = dict()
+            cls.flows[cls.pg0] = [cls.pg1]
+            cls.flows[cls.pg1] = [cls.pg0]
+            cls.flows[cls.pg2] = [cls.pg3]
+            cls.flows[cls.pg3] = [cls.pg2]
+
+            # packet sizes
+            cls.pg_if_packet_sizes = [64, 512, 1518, 9018]
 
-            ## Create bi-directional cross-connects between pg0 and pg1
-            cls.api("sw_interface_set_l2_xconnect rx pg0 tx pg1 enable")
-            cls.api("sw_interface_set_l2_xconnect rx pg1 tx pg0 enable")
+            cls.interfaces = list(cls.pg_interfaces)
 
-            ## Create bi-directional cross-connects between pg2 and pg3
-            cls.api("sw_interface_set_l2_xconnect rx pg2 tx pg3 enable")
-            cls.api("sw_interface_set_l2_xconnect rx pg3 tx pg2 enable")
+            # Create bi-directional cross-connects between pg0 and pg1
+            cls.vapi.sw_interface_set_l2_xconnect(
+                cls.pg0.sw_if_index, cls.pg1.sw_if_index, enable=1)
+            cls.vapi.sw_interface_set_l2_xconnect(
+                cls.pg1.sw_if_index, cls.pg0.sw_if_index, enable=1)
 
-            cls.cli(0, "show l2patch")
+            # Create bi-directional cross-connects between pg2 and pg3
+            cls.vapi.sw_interface_set_l2_xconnect(
+                cls.pg2.sw_if_index, cls.pg3.sw_if_index, enable=1)
+            cls.vapi.sw_interface_set_l2_xconnect(
+                cls.pg3.sw_if_index, cls.pg2.sw_if_index, enable=1)
 
-            ## Create host MAC and IPv4 lists
-            cls.create_host_lists(TestL2xc.hosts_nr)
+            # mapping between packet-generator index and lists of test hosts
+            cls.hosts_by_pg_idx = dict()
 
-        except Exception as e:
-            cls.tearDownClass()
-            raise e
+            # Create host MAC and IPv4 lists
+            cls.create_host_lists(cls.hosts_nr)
+
+            # setup all interfaces
+            for i in cls.interfaces:
+                i.admin_up()
+
+        except Exception:
+            super(TestL2xc, cls).tearDownClass()
+            raise
+
+    def setUp(self):
+        super(TestL2xc, self).setUp()
+        self.reset_packet_infos()
 
-    ## Method to define tear down VPP actions of the test case.
-    #  Overrides tearDown method in VppTestCase class.
-    #  @param self The object pointer.
     def tearDown(self):
-        self.cli(2, "show int")
-        self.cli(2, "show trace")
-        self.cli(2, "show hardware")
-        self.cli(2, "show l2patch")
-        self.cli(2, "show error")
-        self.cli(2, "show run")
-
-    ## Class method to create required number of MAC and IPv4 addresses.
-    #  Create required number of host MAC addresses and distribute them among
-    #  interfaces. Create host IPv4 address for every host MAC address too.
-    #  @param cls The class pointer.
-    #  @param count Integer variable to store the number of MAC addresses to be
-    #  created.
+        """
+        Show various debug prints after each test.
+        """
+        super(TestL2xc, self).tearDown()
+        if not self.vpp_dead:
+            self.logger.info(self.vapi.ppcli("show l2patch"))
+
     @classmethod
     def create_host_lists(cls, count):
-        for i in cls.interfaces:
-            cls.MY_MACS[i] = []
-            cls.MY_IP4S[i] = []
+        """
+        Method to create required number of MAC and IPv4 addresses.
+        Create required number of host MAC addresses and distribute them among
+        interfaces. Create host IPv4 address for every host MAC address too.
+
+        :param count: Number of hosts to create MAC and IPv4 addresses for.
+        """
+        for pg_if in cls.pg_interfaces:
+            cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
+            hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
             for j in range(0, count):
-                cls.MY_MACS[i].append("00:00:00:ff:%02x:%02x" % (i, j))
-                cls.MY_IP4S[i].append("172.17.1%02x.%u" % (i, j))
-        ## @var MY_MACS
-        #  Dictionary variable to store list of MAC addresses per interface.
-        ## @var MY_IP4S
-        #  Dictionary variable to store list of IPv4 addresses per interface.
-
-    ## Method to create packet stream for the packet generator interface.
-    #  Create input packet stream for the given packet generator interface with
-    #  packets of different length targeted for all other created packet
-    #  generator interfaces.
-    #  @param self The object pointer.
-    #  @param pg_id Integer variable to store the index of the interface to
-    #  create the input packet stream.
-    #  @return pkts List variable to store created input stream of packets.
-    def create_stream(self, pg_id):
-        # TODO: use variables to create lists based on interface number
-        pg_targets = [None] * 4
-        pg_targets[0] = [1]
-        pg_targets[1] = [0]
-        pg_targets[2] = [3]
-        pg_targets[3] = [2]
+                host = Host(
+                    "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
+                    "172.17.1%02x.%u" % (pg_if.sw_if_index, j))
+                hosts.append(host)
+
+    def create_stream(self, src_if, packet_sizes, packets_per_burst):
+        """
+        Create input packet stream for defined interface.
+
+        :param object src_if: Interface to create packet stream for.
+        :param list packet_sizes: List of required packet sizes.
+        :param int packets_per_burst: Number of packets in burst.
+        :return: Stream of packets.
+        """
         pkts = []
-        for i in range(0, TestL2xc.pkts_per_burst):
-            target_pg_id = pg_targets[pg_id][0]
-            target_host_id = random.randrange(len(self.MY_MACS[target_pg_id]))
-            source_host_id = random.randrange(len(self.MY_MACS[pg_id]))
-            pkt_info = self.create_packet_info(pg_id, target_pg_id)
+        for i in range(0, packets_per_burst):
+            dst_if = self.flows[src_if][0]
+            dst_host = random.choice(self.hosts_by_pg_idx[dst_if.sw_if_index])
+            src_host = random.choice(self.hosts_by_pg_idx[src_if.sw_if_index])
+            pkt_info = self.create_packet_info(src_if, dst_if)
             payload = self.info_to_payload(pkt_info)
-            p = (Ether(dst=self.MY_MACS[target_pg_id][target_host_id],
-                       src=self.MY_MACS[pg_id][source_host_id]) /
-                 IP(src=self.MY_IP4S[pg_id][source_host_id],
-                    dst=self.MY_IP4S[target_pg_id][target_host_id]) /
+            p = (Ether(dst=dst_host.mac, src=src_host.mac) /
+                 IP(src=src_host.ip4, dst=dst_host.ip4) /
                  UDP(sport=1234, dport=1234) /
                  Raw(payload))
             pkt_info.data = p.copy()
-            packet_sizes = [64, 512, 1518, 9018]
-            size = packet_sizes[(i / 2) % len(packet_sizes)]
+            size = random.choice(packet_sizes)
             self.extend_packet(p, size)
             pkts.append(p)
         return pkts
-        ## @var pg_targets
-        #  List variable to store list of indexes of target packet generator
-        #  interfaces for every source packet generator interface.
-        ## @var target_pg_id
-        #  Integer variable to store the index of the random target packet
-        #  generator interfaces.
-        ## @var target_host_id
-        #  Integer variable to store the index of the randomly chosen
-        #  destination host MAC/IPv4 address.
-        ## @var source_host_id
-        #  Integer variable to store the index of the randomly chosen source
-        #  host MAC/IPv4 address.
-        ## @var pkt_info
-        #  Object variable to store the information about the generated packet.
-        ## @var payload
-        #  String variable to store the payload of the packet to be generated.
-        ## @var p
-        #  Object variable to store the generated packet.
-        ## @var packet_sizes
-        #  List variable to store required packet sizes.
-        ## @var size
-        #  List variable to store required packet sizes.
-
-    ## Method to verify packet stream received on the packet generator interface.
-    #  Verify packet-by-packet the output stream captured on a given packet
-    #  generator (pg) interface using following packet payload data - order of
-    #  packet in the stream, index of the source and destination pg interface,
-    #  src and dst host IPv4 addresses and src port and dst port values of UDP
-    #  layer.
-    #  @param self The object pointer.
-    #  @param o Integer variable to store the index of the interface to
-    #  verify the output packet stream.
-    #  @param capture List variable to store the captured output packet stream.
-    def verify_capture(self, o, capture):
-        last_info = {}
+
+    def verify_capture(self, pg_if, capture):
+        """
+        Verify captured input packet stream for defined interface.
+
+        :param object pg_if: Interface to verify captured packet stream for.
+        :param list capture: Captured packet stream.
+        """
+        last_info = dict()
         for i in self.interfaces:
-            last_info[i] = None
+            last_info[i.sw_if_index] = None
+        dst_sw_if_index = pg_if.sw_if_index
         for packet in capture:
             try:
                 ip = packet[IP]
                 udp = packet[UDP]
                 payload_info = self.payload_to_info(str(packet[Raw]))
-                self.assertEqual(payload_info.dst, o)
-                self.log("Got packet on port %u: src=%u (id=%u)"
-                         % (o, payload_info.src, payload_info.index), 2)
+                packet_index = payload_info.index
+                self.assertEqual(payload_info.dst, dst_sw_if_index)
+                self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
+                                  (pg_if.name, payload_info.src, packet_index))
                 next_info = self.get_next_packet_info_for_interface2(
-                    payload_info.src, payload_info.dst,
+                    payload_info.src, dst_sw_if_index,
                     last_info[payload_info.src])
                 last_info[payload_info.src] = next_info
                 self.assertTrue(next_info is not None)
-                self.assertEqual(payload_info.index, next_info.index)
+                self.assertEqual(packet_index, next_info.index)
+                saved_packet = next_info.data
                 # Check standard fields
-                self.assertEqual(ip.src, next_info.data[IP].src)
-                self.assertEqual(ip.dst, next_info.data[IP].dst)
-                self.assertEqual(udp.sport, next_info.data[UDP].sport)
-                self.assertEqual(udp.dport, next_info.data[UDP].dport)
+                self.assertEqual(ip.src, saved_packet[IP].src)
+                self.assertEqual(ip.dst, saved_packet[IP].dst)
+                self.assertEqual(udp.sport, saved_packet[UDP].sport)
+                self.assertEqual(udp.dport, saved_packet[UDP].dport)
             except:
-                self.log("Unexpected or invalid packet:")
-                packet.show()
+                self.logger.error(ppp("Unexpected or invalid packet:", packet))
                 raise
         for i in self.interfaces:
             remaining_packet = self.get_next_packet_info_for_interface2(
-                i, o, last_info[i])
+                i, dst_sw_if_index, last_info[i.sw_if_index])
             self.assertTrue(remaining_packet is None,
                             "Port %u: Packet expected from source %u didn't"
-                            " arrive" % (o, i))
-        ## @var last_info
-        #  Dictionary variable to store verified packets per packet generator
-        #  interface.
-        ## @var ip
-        #  Object variable to store the IP layer of the packet.
-        ## @var udp
-        #  Object variable to store the UDP layer of the packet.
-        ## @var payload_info
-        #  Object variable to store required information about the packet.
-        ## @var next_info
-        #  Object variable to store information about next packet.
-        ## @var remaining_packet
-        #  Object variable to store information about remaining packet.
-
-    ## Method defining L2 cross-connect test case.
-    #  Contains steps of the test case.
-    #  @param self The object pointer.
-    def test_l2xc(self):
-        """ L2XC test
+                            " arrive" % (dst_sw_if_index, i.sw_if_index))
+
+    def run_l2xc_test(self, pkts_per_burst):
+        """ L2XC test """
+
+        # Create incoming packet streams for packet-generator interfaces
+        for i in self.interfaces:
+            pkts = self.create_stream(i, self.pg_if_packet_sizes,
+                                      pkts_per_burst)
+            i.add_stream(pkts)
+
+        # Enable packet capturing and start packet sending
+        self.pg_enable_capture(self.pg_interfaces)
+        self.pg_start()
+
+        # Verify outgoing packet streams per packet-generator interface
+        for i in self.pg_interfaces:
+            capture = i.get_capture()
+            self.logger.info("Verifying capture on interface %s" % i.name)
+            self.verify_capture(i, capture)
+
+    def test_l2xc_sl(self):
+        """ L2XC single-loop test
 
         Test scenario:
-        1.config
-            2 pairs of 2 interfaces, l2xconnected
+            1. config
+                2 pairs of 2 interfaces, l2xconnected
 
-        2.sending l2 eth packets between 4 interfaces
-            64B, 512B, 1518B, 9018B (ether_size)
-            burst of packets per interface
+            2. sending l2 eth packets between 4 interfaces
+                64B, 512B, 1518B, 9018B (ether_size)
+                burst of 2 packets per interface
         """
 
-        ## Create incoming packet streams for packet-generator interfaces
-        for i in self.interfaces:
-            pkts = self.create_stream(i)
-            self.pg_add_stream(i, pkts)
+        self.run_l2xc_test(self.sl_pkts_per_burst)
 
-        ## Enable packet capturing and start packet sending
-        self.pg_enable_capture(self.interfaces)
-        self.pg_start()
+    def test_l2xc_dl(self):
+        """ L2XC dual-loop test
 
-        ## Verify outgoing packet streams per packet-generator interface
-        for i in self.interfaces:
-            out = self.pg_get_capture(i)
-            self.log("Verifying capture %u" % i)
-            self.verify_capture(i, out)
-        ## @var pkts
-        #  List variable to store created input stream of packets for the packet
-        #  generator interface.
-        ## @var out
-        #  List variable to store captured output stream of packets for
-        #  the packet generator interface.
+        Test scenario:
+            1. config
+                2 pairs of 2 interfaces, l2xconnected
+
+            2. sending l2 eth packets between 4 interfaces
+                64B, 512B, 1518B, 9018B (ether_size)
+                burst of 257 packets per interface
+        """
+
+        self.run_l2xc_test(self.dl_pkts_per_burst)
 
 
 if __name__ == '__main__':
-    unittest.main(testRunner = VppTestRunner)
+    unittest.main(testRunner=VppTestRunner)