blob: 2a6e41c8791e7e8fc2e2a809fa6800e74fdba79d [file] [log] [blame]
Renato Botelho do Coutoead1e532019-10-31 13:31:07 -05001#!/usr/bin/env python3
Jan0bd0b7f2016-12-05 16:22:41 +01002"""L2XC Multi-instance Test Case HLD:
3
4**NOTES:**
Klement Sekerada505f62017-01-04 12:58:53 +01005 - 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
Jan0bd0b7f2016-12-05 16:22:41 +01009
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**
Klement Sekerada505f62017-01-04 12:58:53 +010018 - all packets received correctly in case of cross-connected l2-pg
19 interfaces
Jan0bd0b7f2016-12-05 16:22:41 +010020 - 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**
Klement Sekerada505f62017-01-04 12:58:53 +010029 - all packets received correctly in case of cross-connected l2-pg
30 interfaces
Jan0bd0b7f2016-12-05 16:22:41 +010031 - 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**
Klement Sekerada505f62017-01-04 12:58:53 +010040 - all packets received correctly in case of cross-connected l2-pg
41 interfaces
Jan0bd0b7f2016-12-05 16:22:41 +010042 - 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
54import unittest
55import random
56
57from scapy.packet import Raw
58from scapy.layers.l2 import Ether
59from scapy.layers.inet import IP, UDP
60
61from framework import VppTestCase, VppTestRunner
Klement Sekera9225dee2016-12-12 08:36:58 +010062from util import Host, ppp
Jan0bd0b7f2016-12-05 16:22:41 +010063
64
65class 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
Klement Sekerada505f62017-01-04 12:58:53 +010085 cls.flows[cls.pg_interfaces[i]] =\
86 [cls.pg_interfaces[i + delta]]
Jan0bd0b7f2016-12-05 16:22:41 +010087
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
Paul Vinciguerra7f9b7f92019-03-12 19:23:27 -0700115 @classmethod
116 def tearDownClass(cls):
117 super(TestL2xcMultiInst, cls).tearDownClass()
118
Jan0bd0b7f2016-12-05 16:22:41 +0100119 def setUp(self):
120 """
121 Clear trace and packet infos before running each test.
122 """
123 super(TestL2xcMultiInst, self).setUp()
Klement Sekeradab231a2016-12-21 08:50:14 +0100124 self.reset_packet_infos()
Jan0bd0b7f2016-12-05 16:22:41 +0100125
126 def tearDown(self):
127 """
128 Show various debug prints after each test.
129 """
130 super(TestL2xcMultiInst, self).tearDown()
Paul Vinciguerra90cf21b2019-03-13 09:23:05 -0700131
132 def show_commands_at_teardown(self):
133 self.logger.info(self.vapi.ppcli("show l2patch"))
Jan0bd0b7f2016-12-05 16:22:41 +0100134
135 @classmethod
136 def create_hosts(cls, count):
137 """
138 Create required number of host MAC addresses and distribute them among
139 interfaces. Create host IPv4 address for every host MAC address.
140
141 :param int count: Number of hosts to create MAC/IPv4 addresses for.
142 """
143 n_int = len(cls.pg_interfaces)
Ole Troan3f2a9562019-10-21 20:37:30 +0200144 macs_per_if = count // n_int
Jan0bd0b7f2016-12-05 16:22:41 +0100145 i = -1
146 for pg_if in cls.pg_interfaces:
147 i += 1
148 start_nr = macs_per_if * i
149 end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
150 hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
151 for j in range(start_nr, end_nr):
152 host = Host(
153 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
154 "172.17.1%02u.%u" % (pg_if.sw_if_index, j))
155 hosts.append(host)
156
157 def create_xconnects(self, count, start=0):
158 """
159 Create required number of cross-connects (always two cross-connects per
160 pair of packet-generator interfaces).
161
162 :param int count: Number of cross-connects to be created.
163 :param int start: Starting index of packet-generator interfaces. \
164 (Default value = 0)
165 """
166 for i in range(count):
Klement Sekera9225dee2016-12-12 08:36:58 +0100167 rx_if = self.pg_interfaces[i + start]
Jan0bd0b7f2016-12-05 16:22:41 +0100168 delta = 1 if i % 2 == 0 else -1
Klement Sekera9225dee2016-12-12 08:36:58 +0100169 tx_if = self.pg_interfaces[i + start + delta]
Jan0bd0b7f2016-12-05 16:22:41 +0100170 self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
171 tx_if.sw_if_index, 1)
172 self.logger.info("Cross-connect from %s to %s created"
173 % (tx_if.name, rx_if.name))
174 if self.pg_in_xc.count(rx_if) == 0:
175 self.pg_in_xc.append(rx_if)
176 if self.pg_not_in_xc.count(rx_if) == 1:
177 self.pg_not_in_xc.remove(rx_if)
178
179 def delete_xconnects(self, count, start=0):
180 """
181 Delete required number of cross-connects (always two cross-connects per
182 pair of packet-generator interfaces).
183
184 :param int count: Number of cross-connects to be deleted.
185 :param int start: Starting index of packet-generator interfaces. \
186 (Default value = 0)
187 """
188 for i in range(count):
Klement Sekera9225dee2016-12-12 08:36:58 +0100189 rx_if = self.pg_interfaces[i + start]
Jan0bd0b7f2016-12-05 16:22:41 +0100190 delta = 1 if i % 2 == 0 else -1
Klement Sekera9225dee2016-12-12 08:36:58 +0100191 tx_if = self.pg_interfaces[i + start + delta]
Jan0bd0b7f2016-12-05 16:22:41 +0100192 self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
193 tx_if.sw_if_index, 0)
194 self.logger.info("Cross-connect from %s to %s deleted"
195 % (tx_if.name, rx_if.name))
196 if self.pg_not_in_xc.count(rx_if) == 0:
197 self.pg_not_in_xc.append(rx_if)
198 if self.pg_in_xc.count(rx_if) == 1:
199 self.pg_in_xc.remove(rx_if)
200
201 def create_stream(self, src_if, packet_sizes):
202 """
203 Create input packet stream for defined interface using hosts list.
204
205 :param object src_if: Interface to create packet stream for.
206 :param list packet_sizes: List of required packet sizes.
207 :return: Stream of packets.
208 """
209 pkts = []
210 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
211 for dst_if in self.flows[src_if]:
212 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
213 n_int = len(dst_hosts)
214 for i in range(0, n_int):
215 dst_host = dst_hosts[i]
216 src_host = random.choice(src_hosts)
Klement Sekeradab231a2016-12-21 08:50:14 +0100217 pkt_info = self.create_packet_info(src_if, dst_if)
Jan0bd0b7f2016-12-05 16:22:41 +0100218 payload = self.info_to_payload(pkt_info)
219 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
220 IP(src=src_host.ip4, dst=dst_host.ip4) /
221 UDP(sport=1234, dport=1234) /
222 Raw(payload))
223 pkt_info.data = p.copy()
224 size = random.choice(packet_sizes)
225 self.extend_packet(p, size)
226 pkts.append(p)
227 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
228 % (src_if.name, len(pkts)))
229 return pkts
230
231 def verify_capture(self, pg_if, capture):
232 """
233 Verify captured input packet stream for defined interface.
234
235 :param object pg_if: Interface to verify captured packet stream for.
236 :param list capture: Captured packet stream.
237 """
238 last_info = dict()
239 for i in self.pg_interfaces:
240 last_info[i.sw_if_index] = None
241 dst_sw_if_index = pg_if.sw_if_index
242 for packet in capture:
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800243 payload_info = self.payload_to_info(packet[Raw])
Jan0bd0b7f2016-12-05 16:22:41 +0100244 try:
245 ip = packet[IP]
246 udp = packet[UDP]
247 packet_index = payload_info.index
248 self.assertEqual(payload_info.dst, dst_sw_if_index)
249 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
250 (pg_if.name, payload_info.src, packet_index))
251 next_info = self.get_next_packet_info_for_interface2(
252 payload_info.src, dst_sw_if_index,
253 last_info[payload_info.src])
254 last_info[payload_info.src] = next_info
255 self.assertTrue(next_info is not None)
256 self.assertEqual(packet_index, next_info.index)
257 saved_packet = next_info.data
258 # Check standard fields
259 self.assertEqual(ip.src, saved_packet[IP].src)
260 self.assertEqual(ip.dst, saved_packet[IP].dst)
261 self.assertEqual(udp.sport, saved_packet[UDP].sport)
262 self.assertEqual(udp.dport, saved_packet[UDP].dport)
263 except:
Klement Sekera9225dee2016-12-12 08:36:58 +0100264 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Jan0bd0b7f2016-12-05 16:22:41 +0100265 raise
266 for i in self.pg_interfaces:
267 remaining_packet = self.get_next_packet_info_for_interface2(
268 i, dst_sw_if_index, last_info[i.sw_if_index])
269 self.assertTrue(
270 remaining_packet is None,
271 "Port %u: Packet expected from source %u didn't arrive" %
272 (dst_sw_if_index, i.sw_if_index))
273
274 def run_verify_test(self):
275 """
Matej Klottondeb69842016-12-09 15:05:46 +0100276 Create packet streams for all configured l2-pg interfaces, send all \
Jan0bd0b7f2016-12-05 16:22:41 +0100277 prepared packet streams and verify that:
Matej Klottondeb69842016-12-09 15:05:46 +0100278 - all packets received correctly on all pg-l2 interfaces assigned
279 to cross-connects
280 - no packet received on all pg-l2 interfaces not assigned to
281 cross-connects
Jan0bd0b7f2016-12-05 16:22:41 +0100282
Matej Klottondeb69842016-12-09 15:05:46 +0100283 :raise RuntimeError: if no packet captured on l2-pg interface assigned
284 to the cross-connect or if any packet is captured
285 on l2-pg interface not assigned to the
286 cross-connect.
Jan0bd0b7f2016-12-05 16:22:41 +0100287 """
288 # Test
289 # Create incoming packet streams for packet-generator interfaces
290 for pg_if in self.pg_interfaces:
291 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
292 pg_if.add_stream(pkts)
293
294 # Enable packet capture and start packet sending
295 self.pg_enable_capture(self.pg_interfaces)
296 self.pg_start()
297
298 # Verify
299 # Verify outgoing packet streams per packet-generator interface
300 for pg_if in self.pg_interfaces:
Jan0bd0b7f2016-12-05 16:22:41 +0100301 if pg_if in self.pg_in_xc:
Klement Sekera9225dee2016-12-12 08:36:58 +0100302 capture = pg_if.get_capture(
303 remark="interface is a cross-connect sink")
Jan0bd0b7f2016-12-05 16:22:41 +0100304 self.verify_capture(pg_if, capture)
305 elif pg_if in self.pg_not_in_xc:
Klement Sekera9225dee2016-12-12 08:36:58 +0100306 pg_if.assert_nothing_captured(
307 remark="interface is not a cross-connect sink")
Jan0bd0b7f2016-12-05 16:22:41 +0100308 else:
Klement Sekera9225dee2016-12-12 08:36:58 +0100309 raise Exception("Unexpected interface: %s" % pg_if.name)
Jan0bd0b7f2016-12-05 16:22:41 +0100310
311 def test_l2xc_inst_01(self):
312 """ L2XC Multi-instance test 1 - create 10 cross-connects
313 """
314 # Config 1
315 # Create 10 cross-connects
316 self.create_xconnects(10)
317
318 # Test 1
319 self.run_verify_test()
320
321 def test_l2xc_inst_02(self):
322 """ L2XC Multi-instance test 2 - delete 4 cross-connects
323 """
324 # Config 2
325 # Delete 4 cross-connects
326 self.delete_xconnects(4)
327
328 # Test 2
329 self.run_verify_test()
330
331 def test_l2xc_inst_03(self):
332 """ L2BD Multi-instance 3 - add new 4 cross-connects
333 """
334 # Config 3
335 # Add new 4 cross-connects
336 self.create_xconnects(4, start=10)
337
338 # Test 3
339 self.run_verify_test()
340
341 def test_l2xc_inst_04(self):
342 """ L2XC Multi-instance test 4 - delete 10 cross-connects
343 """
344 # Config 4
345 # Delete 10 cross-connects
346 self.delete_xconnects(10, start=4)
347
348 # Test 4
349 self.run_verify_test()
350
351
352if __name__ == '__main__':
353 unittest.main(testRunner=VppTestRunner)