diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/test_l2xc_multi_instance.py | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/test/test_l2xc_multi_instance.py b/test/test_l2xc_multi_instance.py new file mode 100644 index 00000000000..6f5ab6fe462 --- /dev/null +++ b/test/test_l2xc_multi_instance.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python +"""L2XC Multi-instance Test Case HLD: + +**NOTES:** + - higher number (more than 15) of pg-l2 interfaces causes problems => only \ + 14 pg-l2 interfaces and 10 cross-connects are tested + - jumbo packets in configuration with 14 l2-pg interfaces leads to \ + problems too + +**config 1** + - add 14 pg-l2 interfaces + - add 10 cross-connects (two cross-connects per pair of l2-pg interfaces) + +**test 1** + - send L2 MAC frames between all pairs of pg-l2 interfaces + +**verify 1** + - all packets received correctly in case of cross-connected l2-pg interfaces + - no packet received in case of not cross-connected l2-pg interfaces + +**config 2** + - delete 4 cross-connects + +**test 2** + - send L2 MAC frames between all pairs of pg-l2 interfaces + +**verify 2** + - all packets received correctly in case of cross-connected l2-pg interfaces + - no packet received in case of not cross-connected l2-pg interfaces + +**config 3** + - add new 4 cross-connects + +**test 3** + - send L2 MAC frames between all pairs of pg-l2 interfaces + +**verify 3** + - all packets received correctly in case of cross-connected l2-pg interfaces + - no packet received in case of not cross-connected l2-pg interfaces + +**config 4** + - delete 10 cross-connects + +**test 4** + - send L2 MAC frames between all pairs of pg-l2 interfaces + +**verify 4** + - no packet received on all of l2-pg interfaces (no cross-connect created) +""" + +import unittest +import random + +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 + + +class TestL2xcMultiInst(VppTestCase): + """ L2XC Multi-instance Test Case """ + + @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. + """ + super(TestL2xcMultiInst, cls).setUpClass() + + try: + # Create pg interfaces + cls.create_pg_interfaces(range(14)) + + # Packet flows mapping pg0 -> pg1 etc. + cls.flows = dict() + for i in range(len(cls.pg_interfaces)): + delta = 1 if i % 2 == 0 else -1 + cls.flows[cls.pg_interfaces[i]] = [cls.pg_interfaces[i+delta]] + + # Mapping between packet-generator index and lists of test hosts + cls.hosts_by_pg_idx = dict() + for pg_if in cls.pg_interfaces: + cls.hosts_by_pg_idx[pg_if.sw_if_index] = [] + + # Create test host entries + cls.create_hosts(70) + + # Packet sizes - jumbo packet (9018 bytes) skipped + cls.pg_if_packet_sizes = [64, 512, 1518] + + # Set up all interfaces + for i in cls.pg_interfaces: + i.admin_up() + + # Create list of x-connected pg_interfaces + cls.pg_in_xc = list() + + # Create list of not x-connected pg_interfaces + cls.pg_not_in_xc = list() + for pg_if in cls.pg_interfaces: + cls.pg_not_in_xc.append(pg_if) + + except Exception: + super(TestL2xcMultiInst, cls).tearDownClass() + raise + + def setUp(self): + """ + Clear trace and packet infos before running each test. + """ + super(TestL2xcMultiInst, self).setUp() + self.packet_infos = {} + + def tearDown(self): + """ + Show various debug prints after each test. + """ + super(TestL2xcMultiInst, self).tearDown() + if not self.vpp_dead: + self.logger.info(self.vapi.ppcli("show l2patch")) + + @classmethod + def create_hosts(cls, count): + """ + Create required number of host MAC addresses and distribute them among + interfaces. Create host IPv4 address for every host MAC address. + + :param int count: Number of hosts to create MAC/IPv4 addresses for. + """ + n_int = len(cls.pg_interfaces) + macs_per_if = count / n_int + i = -1 + for pg_if in cls.pg_interfaces: + i += 1 + start_nr = macs_per_if * i + end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1) + hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index] + for j in range(start_nr, end_nr): + host = Host( + "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j), + "172.17.1%02u.%u" % (pg_if.sw_if_index, j)) + hosts.append(host) + + def create_xconnects(self, count, start=0): + """ + Create required number of cross-connects (always two cross-connects per + pair of packet-generator interfaces). + + :param int count: Number of cross-connects to be created. + :param int start: Starting index of packet-generator interfaces. \ + (Default value = 0) + """ + for i in range(count): + rx_if = self.pg_interfaces[i+start] + delta = 1 if i % 2 == 0 else -1 + tx_if = self.pg_interfaces[i+start+delta] + self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index, + tx_if.sw_if_index, 1) + self.logger.info("Cross-connect from %s to %s created" + % (tx_if.name, rx_if.name)) + if self.pg_in_xc.count(rx_if) == 0: + self.pg_in_xc.append(rx_if) + if self.pg_not_in_xc.count(rx_if) == 1: + self.pg_not_in_xc.remove(rx_if) + + def delete_xconnects(self, count, start=0): + """ + Delete required number of cross-connects (always two cross-connects per + pair of packet-generator interfaces). + + :param int count: Number of cross-connects to be deleted. + :param int start: Starting index of packet-generator interfaces. \ + (Default value = 0) + """ + for i in range(count): + rx_if = self.pg_interfaces[i+start] + delta = 1 if i % 2 == 0 else -1 + tx_if = self.pg_interfaces[i+start+delta] + self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index, + tx_if.sw_if_index, 0) + self.logger.info("Cross-connect from %s to %s deleted" + % (tx_if.name, rx_if.name)) + if self.pg_not_in_xc.count(rx_if) == 0: + self.pg_not_in_xc.append(rx_if) + if self.pg_in_xc.count(rx_if) == 1: + self.pg_in_xc.remove(rx_if) + + def create_stream(self, src_if, packet_sizes): + """ + Create input packet stream for defined interface using hosts list. + + :param object src_if: Interface to create packet stream for. + :param list packet_sizes: List of required packet sizes. + :return: Stream of packets. + """ + pkts = [] + src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index] + for dst_if in self.flows[src_if]: + dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index] + n_int = len(dst_hosts) + for i in range(0, n_int): + dst_host = dst_hosts[i] + src_host = random.choice(src_hosts) + pkt_info = self.create_packet_info( + src_if.sw_if_index, dst_if.sw_if_index) + payload = self.info_to_payload(pkt_info) + 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() + size = random.choice(packet_sizes) + self.extend_packet(p, size) + pkts.append(p) + self.logger.debug("Input stream created for port %s. Length: %u pkt(s)" + % (src_if.name, len(pkts))) + return pkts + + 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.pg_interfaces: + last_info[i.sw_if_index] = None + dst_sw_if_index = pg_if.sw_if_index + for packet in capture: + payload_info = self.payload_to_info(str(packet[Raw])) + try: + ip = packet[IP] + udp = packet[UDP] + 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, 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(packet_index, next_info.index) + saved_packet = next_info.data + # Check standard fields + 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.logger.error("Unexpected or invalid packet:") + self.logger.error(packet.show()) + raise + for i in self.pg_interfaces: + remaining_packet = self.get_next_packet_info_for_interface2( + 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" % + (dst_sw_if_index, i.sw_if_index)) + + def run_verify_test(self): + """ + Create packet streams for all configured l2-pg interfaces, send all + prepared packet streams and verify that: + - all packets received correctly on all pg-l2 interfaces assigned \ + to cross-connects + - no packet received on all pg-l2 interfaces not assigned to \ + cross-connects + + :raise: RuntimeError if no packet captured on l2-pg interface assigned \ + to the cross-connect or if any packet is captured on l2-pg interface \ + not assigned to the cross-connect. + """ + # Test + # Create incoming packet streams for packet-generator interfaces + for pg_if in self.pg_interfaces: + pkts = self.create_stream(pg_if, self.pg_if_packet_sizes) + pg_if.add_stream(pkts) + + # Enable packet capture and start packet sending + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + # Verify + # Verify outgoing packet streams per packet-generator interface + for pg_if in self.pg_interfaces: + capture = pg_if.get_capture() + if pg_if in self.pg_in_xc: + if len(capture) == 0: + raise RuntimeError("Interface %s is cross-connect sink but " + "the capture is empty!" % pg_if.name) + self.verify_capture(pg_if, capture) + elif pg_if in self.pg_not_in_xc: + try: + self.assertEqual(len(capture), 0) + except AssertionError: + raise RuntimeError("Interface %s is not cross-connect sink " + "but the capture is not empty!" + % pg_if.name) + else: + self.logger.error("Unknown interface: %s" % pg_if.name) + + def test_l2xc_inst_01(self): + """ L2XC Multi-instance test 1 - create 10 cross-connects + """ + # Config 1 + # Create 10 cross-connects + self.create_xconnects(10) + + # Test 1 + self.run_verify_test() + + def test_l2xc_inst_02(self): + """ L2XC Multi-instance test 2 - delete 4 cross-connects + """ + # Config 2 + # Delete 4 cross-connects + self.delete_xconnects(4) + + # Test 2 + self.run_verify_test() + + def test_l2xc_inst_03(self): + """ L2BD Multi-instance 3 - add new 4 cross-connects + """ + # Config 3 + # Add new 4 cross-connects + self.create_xconnects(4, start=10) + + # Test 3 + self.run_verify_test() + + def test_l2xc_inst_04(self): + """ L2XC Multi-instance test 4 - delete 10 cross-connects + """ + # Config 4 + # Delete 10 cross-connects + self.delete_xconnects(10, start=4) + + # Test 4 + self.run_verify_test() + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) |