blob: 6c28cebbcbce25e01ae5c2821862cdf4f75076bc [file] [log] [blame]
Jan0bd0b7f2016-12-05 16:22:41 +01001#!/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
51import unittest
52import random
53
54from scapy.packet import Raw
55from scapy.layers.l2 import Ether
56from scapy.layers.inet import IP, UDP
57
58from framework import VppTestCase, VppTestRunner
Klement Sekera9225dee2016-12-12 08:36:58 +010059from util import Host, ppp
Jan0bd0b7f2016-12-05 16:22:41 +010060
61
62class 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
Klement Sekera9225dee2016-12-12 08:36:58 +010082 cls.flows[cls.pg_interfaces[i]] = [cls.pg_interfaces[i + delta]]
Jan0bd0b7f2016-12-05 16:22:41 +010083
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()
Klement Sekeradab231a2016-12-21 08:50:14 +0100116 self.reset_packet_infos()
Jan0bd0b7f2016-12-05 16:22:41 +0100117
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):
Klement Sekera9225dee2016-12-12 08:36:58 +0100158 rx_if = self.pg_interfaces[i + start]
Jan0bd0b7f2016-12-05 16:22:41 +0100159 delta = 1 if i % 2 == 0 else -1
Klement Sekera9225dee2016-12-12 08:36:58 +0100160 tx_if = self.pg_interfaces[i + start + delta]
Jan0bd0b7f2016-12-05 16:22:41 +0100161 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):
Klement Sekera9225dee2016-12-12 08:36:58 +0100180 rx_if = self.pg_interfaces[i + start]
Jan0bd0b7f2016-12-05 16:22:41 +0100181 delta = 1 if i % 2 == 0 else -1
Klement Sekera9225dee2016-12-12 08:36:58 +0100182 tx_if = self.pg_interfaces[i + start + delta]
Jan0bd0b7f2016-12-05 16:22:41 +0100183 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)
Klement Sekeradab231a2016-12-21 08:50:14 +0100208 pkt_info = self.create_packet_info(src_if, dst_if)
Jan0bd0b7f2016-12-05 16:22:41 +0100209 payload = self.info_to_payload(pkt_info)
210 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
211 IP(src=src_host.ip4, dst=dst_host.ip4) /
212 UDP(sport=1234, dport=1234) /
213 Raw(payload))
214 pkt_info.data = p.copy()
215 size = random.choice(packet_sizes)
216 self.extend_packet(p, size)
217 pkts.append(p)
218 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
219 % (src_if.name, len(pkts)))
220 return pkts
221
222 def verify_capture(self, pg_if, capture):
223 """
224 Verify captured input packet stream for defined interface.
225
226 :param object pg_if: Interface to verify captured packet stream for.
227 :param list capture: Captured packet stream.
228 """
229 last_info = dict()
230 for i in self.pg_interfaces:
231 last_info[i.sw_if_index] = None
232 dst_sw_if_index = pg_if.sw_if_index
233 for packet in capture:
234 payload_info = self.payload_to_info(str(packet[Raw]))
235 try:
236 ip = packet[IP]
237 udp = packet[UDP]
238 packet_index = payload_info.index
239 self.assertEqual(payload_info.dst, dst_sw_if_index)
240 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
241 (pg_if.name, payload_info.src, packet_index))
242 next_info = self.get_next_packet_info_for_interface2(
243 payload_info.src, dst_sw_if_index,
244 last_info[payload_info.src])
245 last_info[payload_info.src] = next_info
246 self.assertTrue(next_info is not None)
247 self.assertEqual(packet_index, next_info.index)
248 saved_packet = next_info.data
249 # Check standard fields
250 self.assertEqual(ip.src, saved_packet[IP].src)
251 self.assertEqual(ip.dst, saved_packet[IP].dst)
252 self.assertEqual(udp.sport, saved_packet[UDP].sport)
253 self.assertEqual(udp.dport, saved_packet[UDP].dport)
254 except:
Klement Sekera9225dee2016-12-12 08:36:58 +0100255 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Jan0bd0b7f2016-12-05 16:22:41 +0100256 raise
257 for i in self.pg_interfaces:
258 remaining_packet = self.get_next_packet_info_for_interface2(
259 i, dst_sw_if_index, last_info[i.sw_if_index])
260 self.assertTrue(
261 remaining_packet is None,
262 "Port %u: Packet expected from source %u didn't arrive" %
263 (dst_sw_if_index, i.sw_if_index))
264
265 def run_verify_test(self):
266 """
Matej Klottondeb69842016-12-09 15:05:46 +0100267 Create packet streams for all configured l2-pg interfaces, send all \
Jan0bd0b7f2016-12-05 16:22:41 +0100268 prepared packet streams and verify that:
Matej Klottondeb69842016-12-09 15:05:46 +0100269 - all packets received correctly on all pg-l2 interfaces assigned
270 to cross-connects
271 - no packet received on all pg-l2 interfaces not assigned to
272 cross-connects
Jan0bd0b7f2016-12-05 16:22:41 +0100273
Matej Klottondeb69842016-12-09 15:05:46 +0100274 :raise RuntimeError: if no packet captured on l2-pg interface assigned
275 to the cross-connect or if any packet is captured
276 on l2-pg interface not assigned to the
277 cross-connect.
Jan0bd0b7f2016-12-05 16:22:41 +0100278 """
279 # Test
280 # Create incoming packet streams for packet-generator interfaces
281 for pg_if in self.pg_interfaces:
282 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
283 pg_if.add_stream(pkts)
284
285 # Enable packet capture and start packet sending
286 self.pg_enable_capture(self.pg_interfaces)
287 self.pg_start()
288
289 # Verify
290 # Verify outgoing packet streams per packet-generator interface
291 for pg_if in self.pg_interfaces:
Jan0bd0b7f2016-12-05 16:22:41 +0100292 if pg_if in self.pg_in_xc:
Klement Sekera9225dee2016-12-12 08:36:58 +0100293 capture = pg_if.get_capture(
294 remark="interface is a cross-connect sink")
Jan0bd0b7f2016-12-05 16:22:41 +0100295 self.verify_capture(pg_if, capture)
296 elif pg_if in self.pg_not_in_xc:
Klement Sekera9225dee2016-12-12 08:36:58 +0100297 pg_if.assert_nothing_captured(
298 remark="interface is not a cross-connect sink")
Jan0bd0b7f2016-12-05 16:22:41 +0100299 else:
Klement Sekera9225dee2016-12-12 08:36:58 +0100300 raise Exception("Unexpected interface: %s" % pg_if.name)
Jan0bd0b7f2016-12-05 16:22:41 +0100301
302 def test_l2xc_inst_01(self):
303 """ L2XC Multi-instance test 1 - create 10 cross-connects
304 """
305 # Config 1
306 # Create 10 cross-connects
307 self.create_xconnects(10)
308
309 # Test 1
310 self.run_verify_test()
311
312 def test_l2xc_inst_02(self):
313 """ L2XC Multi-instance test 2 - delete 4 cross-connects
314 """
315 # Config 2
316 # Delete 4 cross-connects
317 self.delete_xconnects(4)
318
319 # Test 2
320 self.run_verify_test()
321
322 def test_l2xc_inst_03(self):
323 """ L2BD Multi-instance 3 - add new 4 cross-connects
324 """
325 # Config 3
326 # Add new 4 cross-connects
327 self.create_xconnects(4, start=10)
328
329 # Test 3
330 self.run_verify_test()
331
332 def test_l2xc_inst_04(self):
333 """ L2XC Multi-instance test 4 - delete 10 cross-connects
334 """
335 # Config 4
336 # Delete 10 cross-connects
337 self.delete_xconnects(10, start=4)
338
339 # Test 4
340 self.run_verify_test()
341
342
343if __name__ == '__main__':
344 unittest.main(testRunner=VppTestRunner)