blob: 1cf1b13d728eafade6fa4f50313ea08ae179cc00 [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
Jan00dad122016-11-29 10:04:53 +010076class TestL2bdMultiInst(VppTestCase):
77 """ L2BD Multi-instance Test Case """
78
79 @classmethod
80 def setUpClass(cls):
81 """
82 Perform standard class setup (defined by class method setUpClass in
83 class VppTestCase) before running the test case, set test case related
84 variables and configure VPP.
85 """
86 super(TestL2bdMultiInst, cls).setUpClass()
87
88 try:
89 # Create pg interfaces
Eyal Baric83c8ed2017-05-10 16:08:19 +030090 n_bd = 5
91 cls.ifs_per_bd = ifs_per_bd = 3
92 n_ifs = n_bd * ifs_per_bd
93 cls.create_pg_interfaces(range(n_ifs))
Jan00dad122016-11-29 10:04:53 +010094
95 # Packet flows mapping pg0 -> pg1, pg2 etc.
96 cls.flows = dict()
Eyal Baric83c8ed2017-05-10 16:08:19 +030097 for b in range(n_bd):
98 bd_ifs = cls.bd_if_range(b + 1)
99 for j in bd_ifs:
100 cls.flows[cls.pg_interfaces[j]] = [
101 cls.pg_interfaces[x] for x in bd_ifs if x != j]
102 assert(
103 len(cls.flows[cls.pg_interfaces[j]]) == ifs_per_bd - 1)
Jan00dad122016-11-29 10:04:53 +0100104
105 # Mapping between packet-generator index and lists of test hosts
106 cls.hosts_by_pg_idx = dict()
Jan00dad122016-11-29 10:04:53 +0100107
108 # Create test host entries
Eyal Baric83c8ed2017-05-10 16:08:19 +0300109 cls.create_hosts(5)
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
Jan00dad122016-11-29 10:04:53 +0100127 except Exception:
128 super(TestL2bdMultiInst, cls).tearDownClass()
129 raise
130
131 def setUp(self):
132 """
133 Clear trace and packet infos before running each test.
134 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300135 self.reset_packet_infos()
Jan00dad122016-11-29 10:04:53 +0100136 super(TestL2bdMultiInst, self).setUp()
Jan00dad122016-11-29 10:04:53 +0100137
138 def tearDown(self):
139 """
140 Show various debug prints after each test.
141 """
142 super(TestL2bdMultiInst, self).tearDown()
143 if not self.vpp_dead:
144 self.logger.info(self.vapi.ppcli("show l2fib verbose"))
145 self.logger.info(self.vapi.ppcli("show bridge-domain"))
146
147 @classmethod
Eyal Baric83c8ed2017-05-10 16:08:19 +0300148 def create_hosts(cls, hosts_per_if):
Jan00dad122016-11-29 10:04:53 +0100149 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300150 Create required number of host MAC addresses and distribute them
151 among interfaces. Create host IPv4 address for every host MAC
152 address.
Jan00dad122016-11-29 10:04:53 +0100153
Eyal Baric83c8ed2017-05-10 16:08:19 +0300154 :param int hosts_per_if: Number of hosts per if to create MAC/IPv4
155 addresses for.
Jan00dad122016-11-29 10:04:53 +0100156 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300157 c = hosts_per_if
158 assert(not cls.hosts_by_pg_idx)
159 for i in range(len(cls.pg_interfaces)):
160 pg_idx = cls.pg_interfaces[i].sw_if_index
161 cls.hosts_by_pg_idx[pg_idx] = [Host(
162 "00:00:00:ff:%02x:%02x" % (pg_idx, j + 1),
163 "172.17.1%02u.%u" % (pg_idx, j + 1)) for j in range(c)]
164
165 @classmethod
166 def bd_if_range(cls, b):
167 n = cls.ifs_per_bd
168 start = (b - 1) * n
169 return range(start, start + n)
Jan00dad122016-11-29 10:04:53 +0100170
171 def create_bd_and_mac_learn(self, count, start=1):
Jan65209ed2016-12-05 23:29:17 +0100172 """
Klement Sekerada505f62017-01-04 12:58:53 +0100173 Create required number of bridge domains with MAC learning enabled,
174 put 3 l2-pg interfaces to every bridge domain and send MAC learning
175 packets.
Jan00dad122016-11-29 10:04:53 +0100176
177 :param int count: Number of bridge domains to be created.
178 :param int start: Starting number of the bridge domain ID.
179 (Default value = 1)
180 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300181 for b in range(start, start + count):
182 self.vapi.bridge_domain_add_del(bd_id=b)
183 self.logger.info("Bridge domain ID %d created" % b)
184 if self.bd_list.count(b) == 0:
185 self.bd_list.append(b)
186 if self.bd_deleted_list.count(b) == 1:
187 self.bd_deleted_list.remove(b)
188 for j in self.bd_if_range(b):
189 pg_if = self.pg_interfaces[j]
Jan00dad122016-11-29 10:04:53 +0100190 self.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
Eyal Baric83c8ed2017-05-10 16:08:19 +0300191 bd_id=b)
Jan00dad122016-11-29 10:04:53 +0100192 self.logger.info("pg-interface %s added to bridge domain ID %d"
Eyal Baric83c8ed2017-05-10 16:08:19 +0300193 % (pg_if.name, b))
Jan00dad122016-11-29 10:04:53 +0100194 self.pg_in_bd.append(pg_if)
Eyal Baric83c8ed2017-05-10 16:08:19 +0300195 hosts = self.hosts_by_pg_idx[pg_if.sw_if_index]
196 packets = [Ether(dst="ff:ff:ff:ff:ff:ff", src=host.mac)
197 for host in hosts]
Jan00dad122016-11-29 10:04:53 +0100198 pg_if.add_stream(packets)
199 self.logger.info("Sending broadcast eth frames for MAC learning")
200 self.pg_start()
201 self.logger.info(self.vapi.ppcli("show bridge-domain"))
202 self.logger.info(self.vapi.ppcli("show l2fib"))
203
204 def delete_bd(self, count, start=1):
Jan65209ed2016-12-05 23:29:17 +0100205 """
Jan00dad122016-11-29 10:04:53 +0100206 Delete required number of bridge domains.
207
208 :param int count: Number of bridge domains to be created.
209 :param int start: Starting number of the bridge domain ID.
210 (Default value = 1)
211 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300212 for b in range(start, start + count):
213 for j in self.bd_if_range(b):
214 pg_if = self.pg_interfaces[j]
215 self.vapi.sw_interface_set_l2_bridge(pg_if.sw_if_index,
216 bd_id=b, enable=0)
Jan00dad122016-11-29 10:04:53 +0100217 self.pg_in_bd.remove(pg_if)
Eyal Baric83c8ed2017-05-10 16:08:19 +0300218 self.vapi.bridge_domain_add_del(bd_id=b, is_add=0)
219 self.bd_list.remove(b)
220 self.bd_deleted_list.append(b)
221 self.logger.info("Bridge domain ID %d deleted" % b)
Jan00dad122016-11-29 10:04:53 +0100222
Eyal Baric83c8ed2017-05-10 16:08:19 +0300223 def create_stream(self, src_if):
Jan00dad122016-11-29 10:04:53 +0100224 """
225 Create input packet stream for defined interface using hosts list.
226
227 :param object src_if: Interface to create packet stream for.
228 :param list packet_sizes: List of required packet sizes.
229 :return: Stream of packets.
230 """
Eyal Baric83c8ed2017-05-10 16:08:19 +0300231 packet_sizes = self.pg_if_packet_sizes
Jan00dad122016-11-29 10:04:53 +0100232 pkts = []
233 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
234 for dst_if in self.flows[src_if]:
235 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
Eyal Baric83c8ed2017-05-10 16:08:19 +0300236 for dst_host in dst_hosts:
Klement Sekeradab231a2016-12-21 08:50:14 +0100237 pkt_info = self.create_packet_info(src_if, dst_if)
Jan00dad122016-11-29 10:04:53 +0100238 payload = self.info_to_payload(pkt_info)
Eyal Baric83c8ed2017-05-10 16:08:19 +0300239 src_host = random.choice(src_hosts)
Jan00dad122016-11-29 10:04:53 +0100240 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
241 IP(src=src_host.ip4, dst=dst_host.ip4) /
242 UDP(sport=1234, dport=1234) /
243 Raw(payload))
244 pkt_info.data = p.copy()
245 size = random.choice(packet_sizes)
246 self.extend_packet(p, size)
247 pkts.append(p)
248 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
249 % (src_if.name, len(pkts)))
250 return pkts
251
Eyal Baric83c8ed2017-05-10 16:08:19 +0300252 def verify_capture(self, dst_if):
Jan00dad122016-11-29 10:04:53 +0100253 """
254 Verify captured input packet stream for defined interface.
255
Eyal Baric83c8ed2017-05-10 16:08:19 +0300256 :param object dst_if: Interface to verify captured packet stream for.
Jan00dad122016-11-29 10:04:53 +0100257 """
258 last_info = dict()
Eyal Baric83c8ed2017-05-10 16:08:19 +0300259 for i in self.flows[dst_if]:
Jan00dad122016-11-29 10:04:53 +0100260 last_info[i.sw_if_index] = None
Eyal Baric83c8ed2017-05-10 16:08:19 +0300261 dst = dst_if.sw_if_index
262 for packet in dst_if.get_capture():
Jan00dad122016-11-29 10:04:53 +0100263 try:
264 ip = packet[IP]
265 udp = packet[UDP]
Eyal Baric83c8ed2017-05-10 16:08:19 +0300266 info = self.payload_to_info(str(packet[Raw]))
267 self.assertEqual(info.dst, dst)
Jan00dad122016-11-29 10:04:53 +0100268 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
Eyal Baric83c8ed2017-05-10 16:08:19 +0300269 (dst_if.name, info.src, info.index))
270 last_info[info.src] = self.get_next_packet_info_for_interface2(
271 info.src, dst, last_info[info.src])
272 pkt_info = last_info[info.src]
273 self.assertTrue(pkt_info is not None)
274 self.assertEqual(info.index, pkt_info.index)
275 # Check standard fields against saved data in pkt
276 saved = pkt_info.data
277 self.assertEqual(ip.src, saved[IP].src)
278 self.assertEqual(ip.dst, saved[IP].dst)
279 self.assertEqual(udp.sport, saved[UDP].sport)
280 self.assertEqual(udp.dport, saved[UDP].dport)
Jan00dad122016-11-29 10:04:53 +0100281 except:
Klement Sekera9225dee2016-12-12 08:36:58 +0100282 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Jan00dad122016-11-29 10:04:53 +0100283 raise
Eyal Baric83c8ed2017-05-10 16:08:19 +0300284 s = ""
285 remaining = 0
286 for src in self.flows[dst_if]:
Jan00dad122016-11-29 10:04:53 +0100287 remaining_packet = self.get_next_packet_info_for_interface2(
Eyal Baric83c8ed2017-05-10 16:08:19 +0300288 src.sw_if_index, dst, last_info[src.sw_if_index])
289 if remaining_packet is None:
290 s += "Port %u: Packet expected from source %u didn't arrive\n"\
291 % (dst, src.sw_if_index)
292 remaining += 1
293 self.assertNotEqual(0, remaining, s)
Jan00dad122016-11-29 10:04:53 +0100294
295 def set_bd_flags(self, bd_id, **args):
296 """
297 Enable/disable defined feature(s) of the bridge domain.
298
299 :param int bd_id: Bridge domain ID.
Jan65209ed2016-12-05 23:29:17 +0100300 :param list args: List of feature/status pairs. Allowed features: \
301 learn, forward, flood, uu_flood and arp_term. Status False means \
302 disable, status True means enable the feature.
Jan00dad122016-11-29 10:04:53 +0100303 :raise: ValueError in case of unknown feature in the input.
304 """
305 for flag in args:
306 if flag == "learn":
307 feature_bitmap = 1 << 0
308 elif flag == "forward":
309 feature_bitmap = 1 << 1
310 elif flag == "flood":
311 feature_bitmap = 1 << 2
312 elif flag == "uu_flood":
313 feature_bitmap = 1 << 3
314 elif flag == "arp_term":
315 feature_bitmap = 1 << 4
316 else:
317 raise ValueError("Unknown feature used: %s" % flag)
318 is_set = 1 if args[flag] else 0
319 self.vapi.bridge_flags(bd_id, is_set, feature_bitmap)
320 self.logger.info("Bridge domain ID %d updated" % bd_id)
321
322 def verify_bd(self, bd_id, **args):
323 """
324 Check if the bridge domain is configured and verify expected status
325 of listed features.
326
327 :param int bd_id: Bridge domain ID.
Jan65209ed2016-12-05 23:29:17 +0100328 :param list args: List of feature/status pairs. Allowed features: \
329 learn, forward, flood, uu_flood and arp_term. Status False means \
330 disable, status True means enable the feature.
Jan00dad122016-11-29 10:04:53 +0100331 :return: 1 if bridge domain is configured, otherwise return 0.
332 :raise: ValueError in case of unknown feature in the input.
333 """
334 bd_dump = self.vapi.bridge_domain_dump(bd_id)
335 if len(bd_dump) == 0:
336 self.logger.info("Bridge domain ID %d is not configured" % bd_id)
337 return 0
338 else:
339 bd_dump = bd_dump[0]
340 if len(args) > 0:
341 for flag in args:
342 expected_status = 1 if args[flag] else 0
343 if flag == "learn":
344 flag_status = bd_dump[6]
345 elif flag == "forward":
346 flag_status = bd_dump[5]
347 elif flag == "flood":
348 flag_status = bd_dump[3]
349 elif flag == "uu_flood":
350 flag_status = bd_dump[4]
351 elif flag == "arp_term":
352 flag_status = bd_dump[7]
353 else:
354 raise ValueError("Unknown feature used: %s" % flag)
355 self.assertEqual(expected_status, flag_status)
356 return 1
357
358 def run_verify_test(self):
359 """
Matej Klottondeb69842016-12-09 15:05:46 +0100360 Create packet streams for all configured l2-pg interfaces, send all \
Jan00dad122016-11-29 10:04:53 +0100361 prepared packet streams and verify that:
Matej Klottondeb69842016-12-09 15:05:46 +0100362 - all packets received correctly on all pg-l2 interfaces assigned
363 to bridge domains
364 - no packet received on all pg-l2 interfaces not assigned to
365 bridge domains
Jan00dad122016-11-29 10:04:53 +0100366
Matej Klottondeb69842016-12-09 15:05:46 +0100367 :raise RuntimeError: if no packet captured on l2-pg interface assigned
368 to the bridge domain or if any packet is captured
369 on l2-pg interface not assigned to the bridge
370 domain.
Jan00dad122016-11-29 10:04:53 +0100371 """
372 # Test
373 # Create incoming packet streams for packet-generator interfaces
Eyal Baric83c8ed2017-05-10 16:08:19 +0300374 # for pg_if in self.pg_interfaces:
375 assert(len(self._packet_count_for_dst_if_idx) == 0)
376 for pg_if in self.pg_in_bd:
377 pkts = self.create_stream(pg_if)
Jan00dad122016-11-29 10:04:53 +0100378 pg_if.add_stream(pkts)
379
380 # Enable packet capture and start packet sending
Eyal Baric83c8ed2017-05-10 16:08:19 +0300381 self.pg_enable_capture(self.pg_in_bd)
Jan00dad122016-11-29 10:04:53 +0100382 self.pg_start()
383
384 # Verify
385 # Verify outgoing packet streams per packet-generator interface
Eyal Baric83c8ed2017-05-10 16:08:19 +0300386 for pg_if in self.pg_in_bd:
387 self.verify_capture(pg_if)
Jan00dad122016-11-29 10:04:53 +0100388
389 def test_l2bd_inst_01(self):
390 """ L2BD Multi-instance test 1 - create 5 BDs
391 """
392 # Config 1
393 # Create 5 BDs, put interfaces to these BDs and send MAC learning
394 # packets
395 self.create_bd_and_mac_learn(5)
396
397 # Verify 1
398 for bd_id in self.bd_list:
399 self.assertEqual(self.verify_bd(bd_id), 1)
400
401 # Test 1
402 # self.vapi.cli("clear trace")
403 self.run_verify_test()
404
405 def test_l2bd_inst_02(self):
406 """ L2BD Multi-instance test 2 - update data of 5 BDs
407 """
408 # Config 2
409 # Update data of 5 BDs (disable learn, forward, flood, uu-flood)
410 self.set_bd_flags(self.bd_list[0], learn=False, forward=False,
411 flood=False, uu_flood=False)
412 self.set_bd_flags(self.bd_list[1], forward=False)
413 self.set_bd_flags(self.bd_list[2], flood=False)
414 self.set_bd_flags(self.bd_list[3], uu_flood=False)
415 self.set_bd_flags(self.bd_list[4], learn=False)
416
417 # Verify 2
418 # Skipping check of uu_flood as it is not returned by
419 # bridge_domain_dump api command
420 self.verify_bd(self.bd_list[0], learn=False, forward=False,
421 flood=False, uu_flood=False)
422 self.verify_bd(self.bd_list[1], learn=True, forward=False,
423 flood=True, uu_flood=True)
424 self.verify_bd(self.bd_list[2], learn=True, forward=True,
425 flood=False, uu_flood=True)
426 self.verify_bd(self.bd_list[3], learn=True, forward=True,
427 flood=True, uu_flood=False)
428 self.verify_bd(self.bd_list[4], learn=False, forward=True,
429 flood=True, uu_flood=True)
430
431 def test_l2bd_inst_03(self):
432 """ L2BD Multi-instance 3 - delete 2 BDs
433 """
434 # Config 3
435 # Delete 2 BDs
436 self.delete_bd(2)
437
438 # Verify 3
439 for bd_id in self.bd_deleted_list:
440 self.assertEqual(self.verify_bd(bd_id), 0)
441 for bd_id in self.bd_list:
442 self.assertEqual(self.verify_bd(bd_id), 1)
443
444 # Test 3
445 self.run_verify_test()
446
447 def test_l2bd_inst_04(self):
448 """ L2BD Multi-instance test 4 - add 2 BDs
449 """
450 # Config 4
451 # Create 5 BDs, put interfaces to these BDs and send MAC learning
452 # packets
453 self.create_bd_and_mac_learn(2)
454
455 # Verify 4
456 for bd_id in self.bd_list:
457 self.assertEqual(self.verify_bd(bd_id), 1)
458
459 # Test 4
460 # self.vapi.cli("clear trace")
461 self.run_verify_test()
462
Jan00dad122016-11-29 10:04:53 +0100463 def test_l2bd_inst_05(self):
464 """ L2BD Multi-instance 5 - delete 5 BDs
465 """
466 # Config 5
467 # Delete 5 BDs
468 self.delete_bd(5)
469
470 # Verify 5
471 for bd_id in self.bd_deleted_list:
472 self.assertEqual(self.verify_bd(bd_id), 0)
473 for bd_id in self.bd_list:
474 self.assertEqual(self.verify_bd(bd_id), 1)
475
476
477if __name__ == '__main__':
478 unittest.main(testRunner=VppTestRunner)