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