MAP: Convert from DPO to input feature.
[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     def setUp(self):
116         """
117         Clear trace and packet infos before running each test.
118         """
119         super(TestL2xcMultiInst, self).setUp()
120         self.reset_packet_infos()
121
122     def tearDown(self):
123         """
124         Show various debug prints after each test.
125         """
126         super(TestL2xcMultiInst, self).tearDown()
127         if not self.vpp_dead:
128             self.logger.info(self.vapi.ppcli("show l2patch"))
129
130     @classmethod
131     def create_hosts(cls, count):
132         """
133         Create required number of host MAC addresses and distribute them among
134         interfaces. Create host IPv4 address for every host MAC address.
135
136         :param int count: Number of hosts to create MAC/IPv4 addresses for.
137         """
138         n_int = len(cls.pg_interfaces)
139         macs_per_if = count / n_int
140         i = -1
141         for pg_if in cls.pg_interfaces:
142             i += 1
143             start_nr = macs_per_if * i
144             end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
145             hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
146             for j in range(start_nr, end_nr):
147                 host = Host(
148                     "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
149                     "172.17.1%02u.%u" % (pg_if.sw_if_index, j))
150                 hosts.append(host)
151
152     def create_xconnects(self, count, start=0):
153         """
154         Create required number of cross-connects (always two cross-connects per
155         pair of packet-generator interfaces).
156
157         :param int count: Number of cross-connects to be created.
158         :param int start: Starting index of packet-generator interfaces. \
159         (Default value = 0)
160         """
161         for i in range(count):
162             rx_if = self.pg_interfaces[i + start]
163             delta = 1 if i % 2 == 0 else -1
164             tx_if = self.pg_interfaces[i + start + delta]
165             self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
166                                                    tx_if.sw_if_index, 1)
167             self.logger.info("Cross-connect from %s to %s created"
168                              % (tx_if.name, rx_if.name))
169             if self.pg_in_xc.count(rx_if) == 0:
170                 self.pg_in_xc.append(rx_if)
171             if self.pg_not_in_xc.count(rx_if) == 1:
172                 self.pg_not_in_xc.remove(rx_if)
173
174     def delete_xconnects(self, count, start=0):
175         """
176         Delete required number of cross-connects (always two cross-connects per
177         pair of packet-generator interfaces).
178
179         :param int count: Number of cross-connects to be deleted.
180         :param int start: Starting index of packet-generator interfaces. \
181         (Default value = 0)
182         """
183         for i in range(count):
184             rx_if = self.pg_interfaces[i + start]
185             delta = 1 if i % 2 == 0 else -1
186             tx_if = self.pg_interfaces[i + start + delta]
187             self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
188                                                    tx_if.sw_if_index, 0)
189             self.logger.info("Cross-connect from %s to %s deleted"
190                              % (tx_if.name, rx_if.name))
191             if self.pg_not_in_xc.count(rx_if) == 0:
192                 self.pg_not_in_xc.append(rx_if)
193             if self.pg_in_xc.count(rx_if) == 1:
194                 self.pg_in_xc.remove(rx_if)
195
196     def create_stream(self, src_if, packet_sizes):
197         """
198         Create input packet stream for defined interface using hosts list.
199
200         :param object src_if: Interface to create packet stream for.
201         :param list packet_sizes: List of required packet sizes.
202         :return: Stream of packets.
203         """
204         pkts = []
205         src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
206         for dst_if in self.flows[src_if]:
207             dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
208             n_int = len(dst_hosts)
209             for i in range(0, n_int):
210                 dst_host = dst_hosts[i]
211                 src_host = random.choice(src_hosts)
212                 pkt_info = self.create_packet_info(src_if, dst_if)
213                 payload = self.info_to_payload(pkt_info)
214                 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
215                      IP(src=src_host.ip4, dst=dst_host.ip4) /
216                      UDP(sport=1234, dport=1234) /
217                      Raw(payload))
218                 pkt_info.data = p.copy()
219                 size = random.choice(packet_sizes)
220                 self.extend_packet(p, size)
221                 pkts.append(p)
222         self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
223                           % (src_if.name, len(pkts)))
224         return pkts
225
226     def verify_capture(self, pg_if, capture):
227         """
228         Verify captured input packet stream for defined interface.
229
230         :param object pg_if: Interface to verify captured packet stream for.
231         :param list capture: Captured packet stream.
232         """
233         last_info = dict()
234         for i in self.pg_interfaces:
235             last_info[i.sw_if_index] = None
236         dst_sw_if_index = pg_if.sw_if_index
237         for packet in capture:
238             payload_info = self.payload_to_info(str(packet[Raw]))
239             try:
240                 ip = packet[IP]
241                 udp = packet[UDP]
242                 packet_index = payload_info.index
243                 self.assertEqual(payload_info.dst, dst_sw_if_index)
244                 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
245                                   (pg_if.name, payload_info.src, packet_index))
246                 next_info = self.get_next_packet_info_for_interface2(
247                     payload_info.src, dst_sw_if_index,
248                     last_info[payload_info.src])
249                 last_info[payload_info.src] = next_info
250                 self.assertTrue(next_info is not None)
251                 self.assertEqual(packet_index, next_info.index)
252                 saved_packet = next_info.data
253                 # Check standard fields
254                 self.assertEqual(ip.src, saved_packet[IP].src)
255                 self.assertEqual(ip.dst, saved_packet[IP].dst)
256                 self.assertEqual(udp.sport, saved_packet[UDP].sport)
257                 self.assertEqual(udp.dport, saved_packet[UDP].dport)
258             except:
259                 self.logger.error(ppp("Unexpected or invalid packet:", packet))
260                 raise
261         for i in self.pg_interfaces:
262             remaining_packet = self.get_next_packet_info_for_interface2(
263                 i, dst_sw_if_index, last_info[i.sw_if_index])
264             self.assertTrue(
265                 remaining_packet is None,
266                 "Port %u: Packet expected from source %u didn't arrive" %
267                 (dst_sw_if_index, i.sw_if_index))
268
269     def run_verify_test(self):
270         """
271         Create packet streams for all configured l2-pg interfaces, send all \
272         prepared packet streams and verify that:
273             - all packets received correctly on all pg-l2 interfaces assigned
274               to cross-connects
275             - no packet received on all pg-l2 interfaces not assigned to
276               cross-connects
277
278         :raise RuntimeError: if no packet captured on l2-pg interface assigned
279                              to the cross-connect or if any packet is captured
280                              on l2-pg interface not assigned to the
281                              cross-connect.
282         """
283         # Test
284         # Create incoming packet streams for packet-generator interfaces
285         for pg_if in self.pg_interfaces:
286             pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
287             pg_if.add_stream(pkts)
288
289         # Enable packet capture and start packet sending
290         self.pg_enable_capture(self.pg_interfaces)
291         self.pg_start()
292
293         # Verify
294         # Verify outgoing packet streams per packet-generator interface
295         for pg_if in self.pg_interfaces:
296             if pg_if in self.pg_in_xc:
297                 capture = pg_if.get_capture(
298                     remark="interface is a cross-connect sink")
299                 self.verify_capture(pg_if, capture)
300             elif pg_if in self.pg_not_in_xc:
301                 pg_if.assert_nothing_captured(
302                     remark="interface is not a cross-connect sink")
303             else:
304                 raise Exception("Unexpected interface: %s" % pg_if.name)
305
306     def test_l2xc_inst_01(self):
307         """ L2XC Multi-instance test 1 - create 10 cross-connects
308         """
309         # Config 1
310         # Create 10 cross-connects
311         self.create_xconnects(10)
312
313         # Test 1
314         self.run_verify_test()
315
316     def test_l2xc_inst_02(self):
317         """ L2XC Multi-instance test 2 - delete 4 cross-connects
318         """
319         # Config 2
320         # Delete 4 cross-connects
321         self.delete_xconnects(4)
322
323         # Test 2
324         self.run_verify_test()
325
326     def test_l2xc_inst_03(self):
327         """ L2BD Multi-instance 3 - add new 4 cross-connects
328         """
329         # Config 3
330         # Add new 4 cross-connects
331         self.create_xconnects(4, start=10)
332
333         # Test 3
334         self.run_verify_test()
335
336     def test_l2xc_inst_04(self):
337         """ L2XC Multi-instance test 4 - delete 10 cross-connects
338         """
339         # Config 4
340         # Delete 10 cross-connects
341         self.delete_xconnects(10, start=4)
342
343         # Test 4
344         self.run_verify_test()
345
346
347 if __name__ == '__main__':
348     unittest.main(testRunner=VppTestRunner)