Tests Cleanup: Fix missing calls to setUpClass/tearDownClass.
[vpp.git] / test / test_l2xc_multi_instance.py
1 #!/usr/bin/env python
2 """L2XC Multi-instance Test Case HLD:
3
4 **NOTES:**
5     - higher number (more than 15) of pg-l2 interfaces causes problems => only
6       14 pg-l2 interfaces and 10 cross-connects are tested
7     - jumbo packets in configuration with 14 l2-pg interfaces leads to
8       problems too
9
10 **config 1**
11     - add 14 pg-l2 interfaces
12     - add 10 cross-connects (two cross-connects per pair of l2-pg interfaces)
13
14 **test 1**
15     - send L2 MAC frames between all pairs of pg-l2 interfaces
16
17 **verify 1**
18     - all packets received correctly in case of cross-connected l2-pg
19       interfaces
20     - no packet received in case of not cross-connected l2-pg interfaces
21
22 **config 2**
23     - delete 4 cross-connects
24
25 **test 2**
26     - send L2 MAC frames between all pairs of pg-l2 interfaces
27
28 **verify 2**
29     - all packets received correctly in case of cross-connected l2-pg
30       interfaces
31     - no packet received in case of not cross-connected l2-pg interfaces
32
33 **config 3**
34     - add new 4 cross-connects
35
36 **test 3**
37     - send L2 MAC frames between all pairs of pg-l2 interfaces
38
39 **verify 3**
40     - all packets received correctly in case of cross-connected l2-pg
41       interfaces
42     - no packet received in case of not cross-connected l2-pg interfaces
43
44 **config 4**
45     - delete 10 cross-connects
46
47 **test 4**
48     - send L2 MAC frames between all pairs of pg-l2 interfaces
49
50 **verify 4**
51     - no packet received on all of l2-pg interfaces (no cross-connect created)
52 """
53
54 import unittest
55 import random
56
57 from scapy.packet import Raw
58 from scapy.layers.l2 import Ether
59 from scapy.layers.inet import IP, UDP
60
61 from framework import VppTestCase, VppTestRunner
62 from util import Host, ppp
63
64
65 class TestL2xcMultiInst(VppTestCase):
66     """ L2XC Multi-instance Test Case """
67
68     @classmethod
69     def setUpClass(cls):
70         """
71         Perform standard class setup (defined by class method setUpClass in
72         class VppTestCase) before running the test case, set test case related
73         variables and configure VPP.
74         """
75         super(TestL2xcMultiInst, cls).setUpClass()
76
77         try:
78             # Create pg interfaces
79             cls.create_pg_interfaces(range(14))
80
81             # Packet flows mapping pg0 -> pg1 etc.
82             cls.flows = dict()
83             for i in range(len(cls.pg_interfaces)):
84                 delta = 1 if i % 2 == 0 else -1
85                 cls.flows[cls.pg_interfaces[i]] =\
86                     [cls.pg_interfaces[i + delta]]
87
88             # Mapping between packet-generator index and lists of test hosts
89             cls.hosts_by_pg_idx = dict()
90             for pg_if in cls.pg_interfaces:
91                 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
92
93             # Create test host entries
94             cls.create_hosts(70)
95
96             # Packet sizes - jumbo packet (9018 bytes) skipped
97             cls.pg_if_packet_sizes = [64, 512, 1518]
98
99             # Set up all interfaces
100             for i in cls.pg_interfaces:
101                 i.admin_up()
102
103             # Create list of x-connected pg_interfaces
104             cls.pg_in_xc = list()
105
106             # Create list of not x-connected pg_interfaces
107             cls.pg_not_in_xc = list()
108             for pg_if in cls.pg_interfaces:
109                 cls.pg_not_in_xc.append(pg_if)
110
111         except Exception:
112             super(TestL2xcMultiInst, cls).tearDownClass()
113             raise
114
115     @classmethod
116     def tearDownClass(cls):
117         super(TestL2xcMultiInst, cls).tearDownClass()
118
119     def setUp(self):
120         """
121         Clear trace and packet infos before running each test.
122         """
123         super(TestL2xcMultiInst, self).setUp()
124         self.reset_packet_infos()
125
126     def tearDown(self):
127         """
128         Show various debug prints after each test.
129         """
130         super(TestL2xcMultiInst, self).tearDown()
131         if not self.vpp_dead:
132             self.logger.info(self.vapi.ppcli("show l2patch"))
133
134     @classmethod
135     def create_hosts(cls, count):
136         """
137         Create required number of host MAC addresses and distribute them among
138         interfaces. Create host IPv4 address for every host MAC address.
139
140         :param int count: Number of hosts to create MAC/IPv4 addresses for.
141         """
142         n_int = len(cls.pg_interfaces)
143         macs_per_if = count / n_int
144         i = -1
145         for pg_if in cls.pg_interfaces:
146             i += 1
147             start_nr = macs_per_if * i
148             end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
149             hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
150             for j in range(start_nr, end_nr):
151                 host = Host(
152                     "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
153                     "172.17.1%02u.%u" % (pg_if.sw_if_index, j))
154                 hosts.append(host)
155
156     def create_xconnects(self, count, start=0):
157         """
158         Create required number of cross-connects (always two cross-connects per
159         pair of packet-generator interfaces).
160
161         :param int count: Number of cross-connects to be created.
162         :param int start: Starting index of packet-generator interfaces. \
163         (Default value = 0)
164         """
165         for i in range(count):
166             rx_if = self.pg_interfaces[i + start]
167             delta = 1 if i % 2 == 0 else -1
168             tx_if = self.pg_interfaces[i + start + delta]
169             self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
170                                                    tx_if.sw_if_index, 1)
171             self.logger.info("Cross-connect from %s to %s created"
172                              % (tx_if.name, rx_if.name))
173             if self.pg_in_xc.count(rx_if) == 0:
174                 self.pg_in_xc.append(rx_if)
175             if self.pg_not_in_xc.count(rx_if) == 1:
176                 self.pg_not_in_xc.remove(rx_if)
177
178     def delete_xconnects(self, count, start=0):
179         """
180         Delete required number of cross-connects (always two cross-connects per
181         pair of packet-generator interfaces).
182
183         :param int count: Number of cross-connects to be deleted.
184         :param int start: Starting index of packet-generator interfaces. \
185         (Default value = 0)
186         """
187         for i in range(count):
188             rx_if = self.pg_interfaces[i + start]
189             delta = 1 if i % 2 == 0 else -1
190             tx_if = self.pg_interfaces[i + start + delta]
191             self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
192                                                    tx_if.sw_if_index, 0)
193             self.logger.info("Cross-connect from %s to %s deleted"
194                              % (tx_if.name, rx_if.name))
195             if self.pg_not_in_xc.count(rx_if) == 0:
196                 self.pg_not_in_xc.append(rx_if)
197             if self.pg_in_xc.count(rx_if) == 1:
198                 self.pg_in_xc.remove(rx_if)
199
200     def create_stream(self, src_if, packet_sizes):
201         """
202         Create input packet stream for defined interface using hosts list.
203
204         :param object src_if: Interface to create packet stream for.
205         :param list packet_sizes: List of required packet sizes.
206         :return: Stream of packets.
207         """
208         pkts = []
209         src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
210         for dst_if in self.flows[src_if]:
211             dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
212             n_int = len(dst_hosts)
213             for i in range(0, n_int):
214                 dst_host = dst_hosts[i]
215                 src_host = random.choice(src_hosts)
216                 pkt_info = self.create_packet_info(src_if, dst_if)
217                 payload = self.info_to_payload(pkt_info)
218                 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
219                      IP(src=src_host.ip4, dst=dst_host.ip4) /
220                      UDP(sport=1234, dport=1234) /
221                      Raw(payload))
222                 pkt_info.data = p.copy()
223                 size = random.choice(packet_sizes)
224                 self.extend_packet(p, size)
225                 pkts.append(p)
226         self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
227                           % (src_if.name, len(pkts)))
228         return pkts
229
230     def verify_capture(self, pg_if, capture):
231         """
232         Verify captured input packet stream for defined interface.
233
234         :param object pg_if: Interface to verify captured packet stream for.
235         :param list capture: Captured packet stream.
236         """
237         last_info = dict()
238         for i in self.pg_interfaces:
239             last_info[i.sw_if_index] = None
240         dst_sw_if_index = pg_if.sw_if_index
241         for packet in capture:
242             payload_info = self.payload_to_info(packet[Raw])
243             try:
244                 ip = packet[IP]
245                 udp = packet[UDP]
246                 packet_index = payload_info.index
247                 self.assertEqual(payload_info.dst, dst_sw_if_index)
248                 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
249                                   (pg_if.name, payload_info.src, packet_index))
250                 next_info = self.get_next_packet_info_for_interface2(
251                     payload_info.src, dst_sw_if_index,
252                     last_info[payload_info.src])
253                 last_info[payload_info.src] = next_info
254                 self.assertTrue(next_info is not None)
255                 self.assertEqual(packet_index, next_info.index)
256                 saved_packet = next_info.data
257                 # Check standard fields
258                 self.assertEqual(ip.src, saved_packet[IP].src)
259                 self.assertEqual(ip.dst, saved_packet[IP].dst)
260                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
261                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
262             except:
263                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
264                 raise
265         for i in self.pg_interfaces:
266             remaining_packet = self.get_next_packet_info_for_interface2(
267                 i, dst_sw_if_index, last_info[i.sw_if_index])
268             self.assertTrue(
269                 remaining_packet is None,
270                 "Port %u: Packet expected from source %u didn't arrive" %
271                 (dst_sw_if_index, i.sw_if_index))
272
273     def run_verify_test(self):
274         """
275         Create packet streams for all configured l2-pg interfaces, send all \
276         prepared packet streams and verify that:
277             - all packets received correctly on all pg-l2 interfaces assigned
278               to cross-connects
279             - no packet received on all pg-l2 interfaces not assigned to
280               cross-connects
281
282         :raise RuntimeError: if no packet captured on l2-pg interface assigned
283                              to the cross-connect or if any packet is captured
284                              on l2-pg interface not assigned to the
285                              cross-connect.
286         """
287         # Test
288         # Create incoming packet streams for packet-generator interfaces
289         for pg_if in self.pg_interfaces:
290             pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
291             pg_if.add_stream(pkts)
292
293         # Enable packet capture and start packet sending
294         self.pg_enable_capture(self.pg_interfaces)
295         self.pg_start()
296
297         # Verify
298         # Verify outgoing packet streams per packet-generator interface
299         for pg_if in self.pg_interfaces:
300             if pg_if in self.pg_in_xc:
301                 capture = pg_if.get_capture(
302                     remark="interface is a cross-connect sink")
303                 self.verify_capture(pg_if, capture)
304             elif pg_if in self.pg_not_in_xc:
305                 pg_if.assert_nothing_captured(
306                     remark="interface is not a cross-connect sink")
307             else:
308                 raise Exception("Unexpected interface: %s" % pg_if.name)
309
310     def test_l2xc_inst_01(self):
311         """ L2XC Multi-instance test 1 - create 10 cross-connects
312         """
313         # Config 1
314         # Create 10 cross-connects
315         self.create_xconnects(10)
316
317         # Test 1
318         self.run_verify_test()
319
320     def test_l2xc_inst_02(self):
321         """ L2XC Multi-instance test 2 - delete 4 cross-connects
322         """
323         # Config 2
324         # Delete 4 cross-connects
325         self.delete_xconnects(4)
326
327         # Test 2
328         self.run_verify_test()
329
330     def test_l2xc_inst_03(self):
331         """ L2BD Multi-instance 3 - add new 4 cross-connects
332         """
333         # Config 3
334         # Add new 4 cross-connects
335         self.create_xconnects(4, start=10)
336
337         # Test 3
338         self.run_verify_test()
339
340     def test_l2xc_inst_04(self):
341         """ L2XC Multi-instance test 4 - delete 10 cross-connects
342         """
343         # Config 4
344         # Delete 10 cross-connects
345         self.delete_xconnects(10, start=4)
346
347         # Test 4
348         self.run_verify_test()
349
350
351 if __name__ == '__main__':
352     unittest.main(testRunner=VppTestRunner)