blob: a12262224d0e746ef1e7ea04266cea6f9d433e22 [file] [log] [blame]
Jan00dad122016-11-29 10:04:53 +01001#!/usr/bin/env python
2"""L2BD Multi-instance Test Case HLD:
3
4**NOTES:**
5 - higher number of pg-l2 interfaces causes problems => only 15 pg-l2 \
6 interfaces in 5 bridge domains are tested
Jan65209ed2016-12-05 23:29:17 +01007 - jumbo packets in configuration with 14 l2-pg interfaces leads to \
8 problems too
Jan00dad122016-11-29 10:04:53 +01009
10**config 1**
11 - add 15 pg-l2 interfaces
12 - configure one host per pg-l2 interface
13 - configure 5 bridge domains (BD)
14 - add 3 pg-l2 interfaces per BD
15
16**test 1**
17 - send L2 MAC frames between all pg-l2 interfaces of all BDs
18
19**verify 1**
20 - check BD data by parsing output of bridge_domain_dump API command
21 - all packets received correctly
22
23**config 2**
24 - update data of 5 BD
25 - disable learning, forwarding, flooding and uu_flooding for BD1
26 - disable forwarding for BD2
27 - disable flooding for BD3
28 - disable uu_flooding for BD4
29 - disable learning for BD5
30
31**verify 2**
32 - check BD data by parsing output of bridge_domain_dump API command
33
34**config 3**
35 - delete 2 BDs
36
37**test 3**
38 - send L2 MAC frames between all pg-l2 interfaces of all BDs
39 - send L2 MAC frames between all pg-l2 interfaces formerly assigned to \
40 deleted BDs
41
42**verify 3**
43 - check BD data by parsing output of bridge_domain_dump API command
44 - all packets received correctly on all 3 pg-l2 interfaces assigned to BDs
45 - no packet received on all 3 pg-l2 interfaces of all deleted BDs
46
47**config 4**
48 - add 2 BDs
49 - add 3 pg-l2 interfaces per BD
50
51**test 4**
52 - send L2 MAC frames between all pg-l2 interfaces of all BDs
53
54**verify 4**
55 - check BD data by parsing output of bridge_domain_dump API command
56 - all packets received correctly
57
58**config 5**
59 - delete 5 BDs
60
61**verify 5**
62 - check BD data by parsing output of bridge_domain_dump API command
63"""
64
65import unittest
66import random
67
68from scapy.packet import Raw
69from scapy.layers.l2 import Ether
70from scapy.layers.inet import IP, UDP
71
72from framework import VppTestCase, VppTestRunner
Klement Sekera9225dee2016-12-12 08:36:58 +010073from util import Host, ppp
74
Jan00dad122016-11-29 10:04:53 +010075
Ole Troan399ca1c2016-12-06 23:00:38 +010076@unittest.skip("Crashes VPP")
Jan00dad122016-11-29 10:04:53 +010077class TestL2bdMultiInst(VppTestCase):
78 """ L2BD Multi-instance Test Case """
79
80 @classmethod
81 def setUpClass(cls):
82 """
83 Perform standard class setup (defined by class method setUpClass in
84 class VppTestCase) before running the test case, set test case related
85 variables and configure VPP.
86 """
87 super(TestL2bdMultiInst, cls).setUpClass()
88
89 try:
90 # Create pg interfaces
91 cls.create_pg_interfaces(range(15))
92
93 # Packet flows mapping pg0 -> pg1, pg2 etc.
94 cls.flows = dict()
95 for i in range(0, len(cls.pg_interfaces), 3):
Klement Sekera9225dee2016-12-12 08:36:58 +010096 cls.flows[cls.pg_interfaces[i]] = [cls.pg_interfaces[i + 1],
97 cls.pg_interfaces[i + 2]]
98 cls.flows[cls.pg_interfaces[i + 1]] = [cls.pg_interfaces[i],
99 cls.pg_interfaces[i + 2]]
100 cls.flows[cls.pg_interfaces[i + 2]] = [cls.pg_interfaces[i],
101 cls.pg_interfaces[i + 1]]
Jan00dad122016-11-29 10:04:53 +0100102
103 # Mapping between packet-generator index and lists of test hosts
104 cls.hosts_by_pg_idx = dict()
105 for pg_if in cls.pg_interfaces:
106 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
107
108 # Create test host entries
Jan65209ed2016-12-05 23:29:17 +0100109 cls.create_hosts(75)
Jan00dad122016-11-29 10:04:53 +0100110
Jan65209ed2016-12-05 23:29:17 +0100111 # Packet sizes - jumbo packet (9018 bytes) skipped
112 cls.pg_if_packet_sizes = [64, 512, 1518]
Jan00dad122016-11-29 10:04:53 +0100113
114 # Set up all interfaces
115 for i in cls.pg_interfaces:
116 i.admin_up()
117
118 # Create list of BDs
119 cls.bd_list = list()
120
121 # Create list of deleted BDs
122 cls.bd_deleted_list = list()
123
124 # Create list of pg_interfaces in BDs
125 cls.pg_in_bd = list()
126
127 # Create list of pg_interfaces not in BDs
128 cls.pg_not_in_bd = list()
129 for pg_if in cls.pg_interfaces:
130 cls.pg_not_in_bd.append(pg_if)
131
132 except Exception:
133 super(TestL2bdMultiInst, cls).tearDownClass()
134 raise
135
136 def setUp(self):
137 """
138 Clear trace and packet infos before running each test.
139 """
140 super(TestL2bdMultiInst, self).setUp()
Jan00dad122016-11-29 10:04:53 +0100141
142 def tearDown(self):
143 """
144 Show various debug prints after each test.
145 """
146 super(TestL2bdMultiInst, self).tearDown()
147 if not self.vpp_dead:
148 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
149 self.logger.info(self.vapi.ppcli("show bridge-domain"))
150
151 @classmethod
152 def create_hosts(cls, count):
153 """
154 Create required number of host MAC addresses and distribute them among
155 interfaces. Create host IPv4 address for every host MAC address.
156
157 :param int count: Number of hosts to create MAC/IPv4 addresses for.
158 """
159 n_int = len(cls.pg_interfaces)
160 macs_per_if = count / n_int
161 i = -1
162 for pg_if in cls.pg_interfaces:
163 i += 1
164 start_nr = macs_per_if * i
165 end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
166 hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
167 for j in range(start_nr, end_nr):
168 host = Host(
169 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
170 "172.17.1%02u.%u" % (pg_if.sw_if_index, j))
171 hosts.append(host)
172
173 def create_bd_and_mac_learn(self, count, start=1):
Jan65209ed2016-12-05 23:29:17 +0100174 """
Jan00dad122016-11-29 10:04:53 +0100175 Create required number of bridge domains with MAC learning enabled, put
176 3 l2-pg interfaces to every bridge domain and send MAC learning packets.
177
178 :param int count: Number of bridge domains to be created.
179 :param int start: Starting number of the bridge domain ID.
180 (Default value = 1)
181 """
182 for i in range(count):
183 bd_id = i + start
184 self.vapi.bridge_domain_add_del(bd_id=bd_id)
185 self.logger.info("Bridge domain ID %d created" % bd_id)
186 if self.bd_list.count(bd_id) == 0:
187 self.bd_list.append(bd_id)
188 if self.bd_deleted_list.count(bd_id) == 1:
189 self.bd_deleted_list.remove(bd_id)
190 for j in range(3):
Klement Sekera9225dee2016-12-12 08:36:58 +0100191 pg_if = self.pg_interfaces[(i + start - 1) * 3 + j]
Jan00dad122016-11-29 10:04:53 +0100192 self.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
193 bd_id=bd_id)
194 self.logger.info("pg-interface %s added to bridge domain ID %d"
195 % (pg_if.name, bd_id))
196 self.pg_in_bd.append(pg_if)
197 self.pg_not_in_bd.remove(pg_if)
198 packets = []
199 for host in self.hosts_by_pg_idx[pg_if.sw_if_index]:
200 packet = (Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac))
201 packets.append(packet)
202 pg_if.add_stream(packets)
203 self.logger.info("Sending broadcast eth frames for MAC learning")
204 self.pg_start()
205 self.logger.info(self.vapi.ppcli("show bridge-domain"))
206 self.logger.info(self.vapi.ppcli("show l2fib"))
207
208 def delete_bd(self, count, start=1):
Jan65209ed2016-12-05 23:29:17 +0100209 """
Jan00dad122016-11-29 10:04:53 +0100210 Delete required number of bridge domains.
211
212 :param int count: Number of bridge domains to be created.
213 :param int start: Starting number of the bridge domain ID.
214 (Default value = 1)
215 """
216 for i in range(count):
217 bd_id = i + start
218 self.vapi.bridge_domain_add_del(bd_id=bd_id, is_add=0)
219 if self.bd_list.count(bd_id) == 1:
220 self.bd_list.remove(bd_id)
221 if self.bd_deleted_list.count(bd_id) == 0:
222 self.bd_deleted_list.append(bd_id)
223 for j in range(3):
Klement Sekera9225dee2016-12-12 08:36:58 +0100224 pg_if = self.pg_interfaces[(i + start - 1) * 3 + j]
Jan00dad122016-11-29 10:04:53 +0100225 self.pg_in_bd.remove(pg_if)
226 self.pg_not_in_bd.append(pg_if)
227 self.logger.info("Bridge domain ID %d deleted" % bd_id)
228
229 def create_stream(self, src_if, packet_sizes):
230 """
231 Create input packet stream for defined interface using hosts list.
232
233 :param object src_if: Interface to create packet stream for.
234 :param list packet_sizes: List of required packet sizes.
235 :return: Stream of packets.
236 """
237 pkts = []
238 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
239 for dst_if in self.flows[src_if]:
240 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
241 n_int = len(dst_hosts)
242 for i in range(0, n_int):
243 dst_host = dst_hosts[i]
244 src_host = random.choice(src_hosts)
Klement Sekeradab231a2016-12-21 08:50:14 +0100245 pkt_info = self.create_packet_info(src_if, dst_if)
Jan00dad122016-11-29 10:04:53 +0100246 payload = self.info_to_payload(pkt_info)
247 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
248 IP(src=src_host.ip4, dst=dst_host.ip4) /
249 UDP(sport=1234, dport=1234) /
250 Raw(payload))
251 pkt_info.data = p.copy()
252 size = random.choice(packet_sizes)
253 self.extend_packet(p, size)
254 pkts.append(p)
255 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
256 % (src_if.name, len(pkts)))
257 return pkts
258
259 def verify_capture(self, pg_if, capture):
260 """
261 Verify captured input packet stream for defined interface.
262
263 :param object pg_if: Interface to verify captured packet stream for.
264 :param list capture: Captured packet stream.
265 """
266 last_info = dict()
267 for i in self.pg_interfaces:
268 last_info[i.sw_if_index] = None
269 dst_sw_if_index = pg_if.sw_if_index
270 for packet in capture:
271 payload_info = self.payload_to_info(str(packet[Raw]))
272 try:
273 ip = packet[IP]
274 udp = packet[UDP]
275 packet_index = payload_info.index
276 self.assertEqual(payload_info.dst, dst_sw_if_index)
277 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
278 (pg_if.name, payload_info.src, packet_index))
279 next_info = self.get_next_packet_info_for_interface2(
280 payload_info.src, dst_sw_if_index,
281 last_info[payload_info.src])
282 last_info[payload_info.src] = next_info
283 self.assertTrue(next_info is not None)
284 self.assertEqual(packet_index, next_info.index)
285 saved_packet = next_info.data
286 # Check standard fields
287 self.assertEqual(ip.src, saved_packet[IP].src)
288 self.assertEqual(ip.dst, saved_packet[IP].dst)
289 self.assertEqual(udp.sport, saved_packet[UDP].sport)
290 self.assertEqual(udp.dport, saved_packet[UDP].dport)
291 except:
Klement Sekera9225dee2016-12-12 08:36:58 +0100292 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Jan00dad122016-11-29 10:04:53 +0100293 raise
294 for i in self.pg_interfaces:
295 remaining_packet = self.get_next_packet_info_for_interface2(
296 i, dst_sw_if_index, last_info[i.sw_if_index])
297 self.assertTrue(
298 remaining_packet is None,
299 "Port %u: Packet expected from source %u didn't arrive" %
300 (dst_sw_if_index, i.sw_if_index))
301
302 def set_bd_flags(self, bd_id, **args):
303 """
304 Enable/disable defined feature(s) of the bridge domain.
305
306 :param int bd_id: Bridge domain ID.
Jan65209ed2016-12-05 23:29:17 +0100307 :param list args: List of feature/status pairs. Allowed features: \
308 learn, forward, flood, uu_flood and arp_term. Status False means \
309 disable, status True means enable the feature.
Jan00dad122016-11-29 10:04:53 +0100310 :raise: ValueError in case of unknown feature in the input.
311 """
312 for flag in args:
313 if flag == "learn":
314 feature_bitmap = 1 << 0
315 elif flag == "forward":
316 feature_bitmap = 1 << 1
317 elif flag == "flood":
318 feature_bitmap = 1 << 2
319 elif flag == "uu_flood":
320 feature_bitmap = 1 << 3
321 elif flag == "arp_term":
322 feature_bitmap = 1 << 4
323 else:
324 raise ValueError("Unknown feature used: %s" % flag)
325 is_set = 1 if args[flag] else 0
326 self.vapi.bridge_flags(bd_id, is_set, feature_bitmap)
327 self.logger.info("Bridge domain ID %d updated" % bd_id)
328
329 def verify_bd(self, bd_id, **args):
330 """
331 Check if the bridge domain is configured and verify expected status
332 of listed features.
333
334 :param int bd_id: Bridge domain ID.
Jan65209ed2016-12-05 23:29:17 +0100335 :param list args: List of feature/status pairs. Allowed features: \
336 learn, forward, flood, uu_flood and arp_term. Status False means \
337 disable, status True means enable the feature.
Jan00dad122016-11-29 10:04:53 +0100338 :return: 1 if bridge domain is configured, otherwise return 0.
339 :raise: ValueError in case of unknown feature in the input.
340 """
341 bd_dump = self.vapi.bridge_domain_dump(bd_id)
342 if len(bd_dump) == 0:
343 self.logger.info("Bridge domain ID %d is not configured" % bd_id)
344 return 0
345 else:
346 bd_dump = bd_dump[0]
347 if len(args) > 0:
348 for flag in args:
349 expected_status = 1 if args[flag] else 0
350 if flag == "learn":
351 flag_status = bd_dump[6]
352 elif flag == "forward":
353 flag_status = bd_dump[5]
354 elif flag == "flood":
355 flag_status = bd_dump[3]
356 elif flag == "uu_flood":
357 flag_status = bd_dump[4]
358 elif flag == "arp_term":
359 flag_status = bd_dump[7]
360 else:
361 raise ValueError("Unknown feature used: %s" % flag)
362 self.assertEqual(expected_status, flag_status)
363 return 1
364
365 def run_verify_test(self):
366 """
Matej Klottondeb69842016-12-09 15:05:46 +0100367 Create packet streams for all configured l2-pg interfaces, send all \
Jan00dad122016-11-29 10:04:53 +0100368 prepared packet streams and verify that:
Matej Klottondeb69842016-12-09 15:05:46 +0100369 - all packets received correctly on all pg-l2 interfaces assigned
370 to bridge domains
371 - no packet received on all pg-l2 interfaces not assigned to
372 bridge domains
Jan00dad122016-11-29 10:04:53 +0100373
Matej Klottondeb69842016-12-09 15:05:46 +0100374 :raise RuntimeError: if no packet captured on l2-pg interface assigned
375 to the bridge domain or if any packet is captured
376 on l2-pg interface not assigned to the bridge
377 domain.
Jan00dad122016-11-29 10:04:53 +0100378 """
379 # Test
380 # Create incoming packet streams for packet-generator interfaces
381 for pg_if in self.pg_interfaces:
382 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
383 pg_if.add_stream(pkts)
384
385 # Enable packet capture and start packet sending
386 self.pg_enable_capture(self.pg_interfaces)
387 self.pg_start()
388
389 # Verify
390 # Verify outgoing packet streams per packet-generator interface
391 for pg_if in self.pg_interfaces:
392 capture = pg_if.get_capture()
393 if pg_if in self.pg_in_bd:
Jan00dad122016-11-29 10:04:53 +0100394 self.verify_capture(pg_if, capture)
Klement Sekeradab231a2016-12-21 08:50:14 +0100395 elif pg_if not in self.pg_not_in_bd:
Jan00dad122016-11-29 10:04:53 +0100396 self.logger.error("Unknown interface: %s" % pg_if.name)
397
398 def test_l2bd_inst_01(self):
399 """ L2BD Multi-instance test 1 - create 5 BDs
400 """
401 # Config 1
402 # Create 5 BDs, put interfaces to these BDs and send MAC learning
403 # packets
404 self.create_bd_and_mac_learn(5)
405
406 # Verify 1
407 for bd_id in self.bd_list:
408 self.assertEqual(self.verify_bd(bd_id), 1)
409
410 # Test 1
411 # self.vapi.cli("clear trace")
412 self.run_verify_test()
413
414 def test_l2bd_inst_02(self):
415 """ L2BD Multi-instance test 2 - update data of 5 BDs
416 """
417 # Config 2
418 # Update data of 5 BDs (disable learn, forward, flood, uu-flood)
419 self.set_bd_flags(self.bd_list[0], learn=False, forward=False,
420 flood=False, uu_flood=False)
421 self.set_bd_flags(self.bd_list[1], forward=False)
422 self.set_bd_flags(self.bd_list[2], flood=False)
423 self.set_bd_flags(self.bd_list[3], uu_flood=False)
424 self.set_bd_flags(self.bd_list[4], learn=False)
425
426 # Verify 2
427 # Skipping check of uu_flood as it is not returned by
428 # bridge_domain_dump api command
429 self.verify_bd(self.bd_list[0], learn=False, forward=False,
430 flood=False, uu_flood=False)
431 self.verify_bd(self.bd_list[1], learn=True, forward=False,
432 flood=True, uu_flood=True)
433 self.verify_bd(self.bd_list[2], learn=True, forward=True,
434 flood=False, uu_flood=True)
435 self.verify_bd(self.bd_list[3], learn=True, forward=True,
436 flood=True, uu_flood=False)
437 self.verify_bd(self.bd_list[4], learn=False, forward=True,
438 flood=True, uu_flood=True)
439
440 def test_l2bd_inst_03(self):
441 """ L2BD Multi-instance 3 - delete 2 BDs
442 """
443 # Config 3
444 # Delete 2 BDs
445 self.delete_bd(2)
446
447 # Verify 3
448 for bd_id in self.bd_deleted_list:
449 self.assertEqual(self.verify_bd(bd_id), 0)
450 for bd_id in self.bd_list:
451 self.assertEqual(self.verify_bd(bd_id), 1)
452
453 # Test 3
454 self.run_verify_test()
455
456 def test_l2bd_inst_04(self):
457 """ L2BD Multi-instance test 4 - add 2 BDs
458 """
459 # Config 4
460 # Create 5 BDs, put interfaces to these BDs and send MAC learning
461 # packets
462 self.create_bd_and_mac_learn(2)
463
464 # Verify 4
465 for bd_id in self.bd_list:
466 self.assertEqual(self.verify_bd(bd_id), 1)
467
468 # Test 4
469 # self.vapi.cli("clear trace")
470 self.run_verify_test()
471
Jan00dad122016-11-29 10:04:53 +0100472 def test_l2bd_inst_05(self):
473 """ L2BD Multi-instance 5 - delete 5 BDs
474 """
475 # Config 5
476 # Delete 5 BDs
477 self.delete_bd(5)
478
479 # Verify 5
480 for bd_id in self.bd_deleted_list:
481 self.assertEqual(self.verify_bd(bd_id), 0)
482 for bd_id in self.bd_list:
483 self.assertEqual(self.verify_bd(bd_id), 1)
484
485
486if __name__ == '__main__':
487 unittest.main(testRunner=VppTestRunner)