blob: e24a8613fdf602b165431a11c25a5d114d0cf319 [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]]
Klement Sekerada505f62017-01-04 12:58:53 +010098 cls.flows[cls.pg_interfaces[i + 1]] = \
99 [cls.pg_interfaces[i], cls.pg_interfaces[i + 2]]
100 cls.flows[cls.pg_interfaces[i + 2]] = \
101 [cls.pg_interfaces[i], 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 """
Klement Sekerada505f62017-01-04 12:58:53 +0100175 Create required number of bridge domains with MAC learning enabled,
176 put 3 l2-pg interfaces to every bridge domain and send MAC learning
177 packets.
Jan00dad122016-11-29 10:04:53 +0100178
179 :param int count: Number of bridge domains to be created.
180 :param int start: Starting number of the bridge domain ID.
181 (Default value = 1)
182 """
183 for i in range(count):
184 bd_id = i + start
185 self.vapi.bridge_domain_add_del(bd_id=bd_id)
186 self.logger.info("Bridge domain ID %d created" % bd_id)
187 if self.bd_list.count(bd_id) == 0:
188 self.bd_list.append(bd_id)
189 if self.bd_deleted_list.count(bd_id) == 1:
190 self.bd_deleted_list.remove(bd_id)
191 for j in range(3):
Klement Sekera9225dee2016-12-12 08:36:58 +0100192 pg_if = self.pg_interfaces[(i + start - 1) * 3 + j]
Jan00dad122016-11-29 10:04:53 +0100193 self.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
194 bd_id=bd_id)
195 self.logger.info("pg-interface %s added to bridge domain ID %d"
196 % (pg_if.name, bd_id))
197 self.pg_in_bd.append(pg_if)
198 self.pg_not_in_bd.remove(pg_if)
199 packets = []
200 for host in self.hosts_by_pg_idx[pg_if.sw_if_index]:
201 packet = (Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac))
202 packets.append(packet)
203 pg_if.add_stream(packets)
204 self.logger.info("Sending broadcast eth frames for MAC learning")
205 self.pg_start()
206 self.logger.info(self.vapi.ppcli("show bridge-domain"))
207 self.logger.info(self.vapi.ppcli("show l2fib"))
208
209 def delete_bd(self, count, start=1):
Jan65209ed2016-12-05 23:29:17 +0100210 """
Jan00dad122016-11-29 10:04:53 +0100211 Delete required number of bridge domains.
212
213 :param int count: Number of bridge domains to be created.
214 :param int start: Starting number of the bridge domain ID.
215 (Default value = 1)
216 """
217 for i in range(count):
218 bd_id = i + start
219 self.vapi.bridge_domain_add_del(bd_id=bd_id, is_add=0)
220 if self.bd_list.count(bd_id) == 1:
221 self.bd_list.remove(bd_id)
222 if self.bd_deleted_list.count(bd_id) == 0:
223 self.bd_deleted_list.append(bd_id)
224 for j in range(3):
Klement Sekera9225dee2016-12-12 08:36:58 +0100225 pg_if = self.pg_interfaces[(i + start - 1) * 3 + j]
Jan00dad122016-11-29 10:04:53 +0100226 self.pg_in_bd.remove(pg_if)
227 self.pg_not_in_bd.append(pg_if)
228 self.logger.info("Bridge domain ID %d deleted" % bd_id)
229
230 def create_stream(self, src_if, packet_sizes):
231 """
232 Create input packet stream for defined interface using hosts list.
233
234 :param object src_if: Interface to create packet stream for.
235 :param list packet_sizes: List of required packet sizes.
236 :return: Stream of packets.
237 """
238 pkts = []
239 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
240 for dst_if in self.flows[src_if]:
241 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
242 n_int = len(dst_hosts)
243 for i in range(0, n_int):
244 dst_host = dst_hosts[i]
245 src_host = random.choice(src_hosts)
Klement Sekeradab231a2016-12-21 08:50:14 +0100246 pkt_info = self.create_packet_info(src_if, dst_if)
Jan00dad122016-11-29 10:04:53 +0100247 payload = self.info_to_payload(pkt_info)
248 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
249 IP(src=src_host.ip4, dst=dst_host.ip4) /
250 UDP(sport=1234, dport=1234) /
251 Raw(payload))
252 pkt_info.data = p.copy()
253 size = random.choice(packet_sizes)
254 self.extend_packet(p, size)
255 pkts.append(p)
256 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
257 % (src_if.name, len(pkts)))
258 return pkts
259
260 def verify_capture(self, pg_if, capture):
261 """
262 Verify captured input packet stream for defined interface.
263
264 :param object pg_if: Interface to verify captured packet stream for.
265 :param list capture: Captured packet stream.
266 """
267 last_info = dict()
268 for i in self.pg_interfaces:
269 last_info[i.sw_if_index] = None
270 dst_sw_if_index = pg_if.sw_if_index
271 for packet in capture:
272 payload_info = self.payload_to_info(str(packet[Raw]))
273 try:
274 ip = packet[IP]
275 udp = packet[UDP]
276 packet_index = payload_info.index
277 self.assertEqual(payload_info.dst, dst_sw_if_index)
278 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
279 (pg_if.name, payload_info.src, packet_index))
280 next_info = self.get_next_packet_info_for_interface2(
281 payload_info.src, dst_sw_if_index,
282 last_info[payload_info.src])
283 last_info[payload_info.src] = next_info
284 self.assertTrue(next_info is not None)
285 self.assertEqual(packet_index, next_info.index)
286 saved_packet = next_info.data
287 # Check standard fields
288 self.assertEqual(ip.src, saved_packet[IP].src)
289 self.assertEqual(ip.dst, saved_packet[IP].dst)
290 self.assertEqual(udp.sport, saved_packet[UDP].sport)
291 self.assertEqual(udp.dport, saved_packet[UDP].dport)
292 except:
Klement Sekera9225dee2016-12-12 08:36:58 +0100293 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Jan00dad122016-11-29 10:04:53 +0100294 raise
295 for i in self.pg_interfaces:
296 remaining_packet = self.get_next_packet_info_for_interface2(
297 i, dst_sw_if_index, last_info[i.sw_if_index])
298 self.assertTrue(
299 remaining_packet is None,
300 "Port %u: Packet expected from source %u didn't arrive" %
301 (dst_sw_if_index, i.sw_if_index))
302
303 def set_bd_flags(self, bd_id, **args):
304 """
305 Enable/disable defined feature(s) of the bridge domain.
306
307 :param int bd_id: Bridge domain ID.
Jan65209ed2016-12-05 23:29:17 +0100308 :param list args: List of feature/status pairs. Allowed features: \
309 learn, forward, flood, uu_flood and arp_term. Status False means \
310 disable, status True means enable the feature.
Jan00dad122016-11-29 10:04:53 +0100311 :raise: ValueError in case of unknown feature in the input.
312 """
313 for flag in args:
314 if flag == "learn":
315 feature_bitmap = 1 << 0
316 elif flag == "forward":
317 feature_bitmap = 1 << 1
318 elif flag == "flood":
319 feature_bitmap = 1 << 2
320 elif flag == "uu_flood":
321 feature_bitmap = 1 << 3
322 elif flag == "arp_term":
323 feature_bitmap = 1 << 4
324 else:
325 raise ValueError("Unknown feature used: %s" % flag)
326 is_set = 1 if args[flag] else 0
327 self.vapi.bridge_flags(bd_id, is_set, feature_bitmap)
328 self.logger.info("Bridge domain ID %d updated" % bd_id)
329
330 def verify_bd(self, bd_id, **args):
331 """
332 Check if the bridge domain is configured and verify expected status
333 of listed features.
334
335 :param int bd_id: Bridge domain ID.
Jan65209ed2016-12-05 23:29:17 +0100336 :param list args: List of feature/status pairs. Allowed features: \
337 learn, forward, flood, uu_flood and arp_term. Status False means \
338 disable, status True means enable the feature.
Jan00dad122016-11-29 10:04:53 +0100339 :return: 1 if bridge domain is configured, otherwise return 0.
340 :raise: ValueError in case of unknown feature in the input.
341 """
342 bd_dump = self.vapi.bridge_domain_dump(bd_id)
343 if len(bd_dump) == 0:
344 self.logger.info("Bridge domain ID %d is not configured" % bd_id)
345 return 0
346 else:
347 bd_dump = bd_dump[0]
348 if len(args) > 0:
349 for flag in args:
350 expected_status = 1 if args[flag] else 0
351 if flag == "learn":
352 flag_status = bd_dump[6]
353 elif flag == "forward":
354 flag_status = bd_dump[5]
355 elif flag == "flood":
356 flag_status = bd_dump[3]
357 elif flag == "uu_flood":
358 flag_status = bd_dump[4]
359 elif flag == "arp_term":
360 flag_status = bd_dump[7]
361 else:
362 raise ValueError("Unknown feature used: %s" % flag)
363 self.assertEqual(expected_status, flag_status)
364 return 1
365
366 def run_verify_test(self):
367 """
Matej Klottondeb69842016-12-09 15:05:46 +0100368 Create packet streams for all configured l2-pg interfaces, send all \
Jan00dad122016-11-29 10:04:53 +0100369 prepared packet streams and verify that:
Matej Klottondeb69842016-12-09 15:05:46 +0100370 - all packets received correctly on all pg-l2 interfaces assigned
371 to bridge domains
372 - no packet received on all pg-l2 interfaces not assigned to
373 bridge domains
Jan00dad122016-11-29 10:04:53 +0100374
Matej Klottondeb69842016-12-09 15:05:46 +0100375 :raise RuntimeError: if no packet captured on l2-pg interface assigned
376 to the bridge domain or if any packet is captured
377 on l2-pg interface not assigned to the bridge
378 domain.
Jan00dad122016-11-29 10:04:53 +0100379 """
380 # Test
381 # Create incoming packet streams for packet-generator interfaces
382 for pg_if in self.pg_interfaces:
383 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
384 pg_if.add_stream(pkts)
385
386 # Enable packet capture and start packet sending
387 self.pg_enable_capture(self.pg_interfaces)
388 self.pg_start()
389
390 # Verify
391 # Verify outgoing packet streams per packet-generator interface
392 for pg_if in self.pg_interfaces:
393 capture = pg_if.get_capture()
394 if pg_if in self.pg_in_bd:
Jan00dad122016-11-29 10:04:53 +0100395 self.verify_capture(pg_if, capture)
Klement Sekeradab231a2016-12-21 08:50:14 +0100396 elif pg_if not in self.pg_not_in_bd:
Jan00dad122016-11-29 10:04:53 +0100397 self.logger.error("Unknown interface: %s" % pg_if.name)
398
399 def test_l2bd_inst_01(self):
400 """ L2BD Multi-instance test 1 - create 5 BDs
401 """
402 # Config 1
403 # Create 5 BDs, put interfaces to these BDs and send MAC learning
404 # packets
405 self.create_bd_and_mac_learn(5)
406
407 # Verify 1
408 for bd_id in self.bd_list:
409 self.assertEqual(self.verify_bd(bd_id), 1)
410
411 # Test 1
412 # self.vapi.cli("clear trace")
413 self.run_verify_test()
414
415 def test_l2bd_inst_02(self):
416 """ L2BD Multi-instance test 2 - update data of 5 BDs
417 """
418 # Config 2
419 # Update data of 5 BDs (disable learn, forward, flood, uu-flood)
420 self.set_bd_flags(self.bd_list[0], learn=False, forward=False,
421 flood=False, uu_flood=False)
422 self.set_bd_flags(self.bd_list[1], forward=False)
423 self.set_bd_flags(self.bd_list[2], flood=False)
424 self.set_bd_flags(self.bd_list[3], uu_flood=False)
425 self.set_bd_flags(self.bd_list[4], learn=False)
426
427 # Verify 2
428 # Skipping check of uu_flood as it is not returned by
429 # bridge_domain_dump api command
430 self.verify_bd(self.bd_list[0], learn=False, forward=False,
431 flood=False, uu_flood=False)
432 self.verify_bd(self.bd_list[1], learn=True, forward=False,
433 flood=True, uu_flood=True)
434 self.verify_bd(self.bd_list[2], learn=True, forward=True,
435 flood=False, uu_flood=True)
436 self.verify_bd(self.bd_list[3], learn=True, forward=True,
437 flood=True, uu_flood=False)
438 self.verify_bd(self.bd_list[4], learn=False, forward=True,
439 flood=True, uu_flood=True)
440
441 def test_l2bd_inst_03(self):
442 """ L2BD Multi-instance 3 - delete 2 BDs
443 """
444 # Config 3
445 # Delete 2 BDs
446 self.delete_bd(2)
447
448 # Verify 3
449 for bd_id in self.bd_deleted_list:
450 self.assertEqual(self.verify_bd(bd_id), 0)
451 for bd_id in self.bd_list:
452 self.assertEqual(self.verify_bd(bd_id), 1)
453
454 # Test 3
455 self.run_verify_test()
456
457 def test_l2bd_inst_04(self):
458 """ L2BD Multi-instance test 4 - add 2 BDs
459 """
460 # Config 4
461 # Create 5 BDs, put interfaces to these BDs and send MAC learning
462 # packets
463 self.create_bd_and_mac_learn(2)
464
465 # Verify 4
466 for bd_id in self.bd_list:
467 self.assertEqual(self.verify_bd(bd_id), 1)
468
469 # Test 4
470 # self.vapi.cli("clear trace")
471 self.run_verify_test()
472
Jan00dad122016-11-29 10:04:53 +0100473 def test_l2bd_inst_05(self):
474 """ L2BD Multi-instance 5 - delete 5 BDs
475 """
476 # Config 5
477 # Delete 5 BDs
478 self.delete_bd(5)
479
480 # Verify 5
481 for bd_id in self.bd_deleted_list:
482 self.assertEqual(self.verify_bd(bd_id), 0)
483 for bd_id in self.bd_list:
484 self.assertEqual(self.verify_bd(bd_id), 1)
485
486
487if __name__ == '__main__':
488 unittest.main(testRunner=VppTestRunner)