blob: 0fb080aea2b57bc50a13a75038a8ef5486034520 [file] [log] [blame]
Jan0bd0b7f2016-12-05 16:22:41 +01001#!/usr/bin/env python
2"""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()
131 if not self.vpp_dead:
132 self.logger.info(self.vapi.ppcli("show l2patch"))
133
134 @classmethod
135 def create_hosts(cls, count):
136 """
137 Create required number of host MAC addresses and distribute them among
138 interfaces. Create host IPv4 address for every host MAC address.
139
140 :param int count: Number of hosts to create MAC/IPv4 addresses for.
141 """
142 n_int = len(cls.pg_interfaces)
143 macs_per_if = count / n_int
144 i = -1
145 for pg_if in cls.pg_interfaces:
146 i += 1
147 start_nr = macs_per_if * i
148 end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
149 hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
150 for j in range(start_nr, end_nr):
151 host = Host(
152 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
153 "172.17.1%02u.%u" % (pg_if.sw_if_index, j))
154 hosts.append(host)
155
156 def create_xconnects(self, count, start=0):
157 """
158 Create required number of cross-connects (always two cross-connects per
159 pair of packet-generator interfaces).
160
161 :param int count: Number of cross-connects to be created.
162 :param int start: Starting index of packet-generator interfaces. \
163 (Default value = 0)
164 """
165 for i in range(count):
Klement Sekera9225dee2016-12-12 08:36:58 +0100166 rx_if = self.pg_interfaces[i + start]
Jan0bd0b7f2016-12-05 16:22:41 +0100167 delta = 1 if i % 2 == 0 else -1
Klement Sekera9225dee2016-12-12 08:36:58 +0100168 tx_if = self.pg_interfaces[i + start + delta]
Jan0bd0b7f2016-12-05 16:22:41 +0100169 self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
170 tx_if.sw_if_index, 1)
171 self.logger.info("Cross-connect from %s to %s created"
172 % (tx_if.name, rx_if.name))
173 if self.pg_in_xc.count(rx_if) == 0:
174 self.pg_in_xc.append(rx_if)
175 if self.pg_not_in_xc.count(rx_if) == 1:
176 self.pg_not_in_xc.remove(rx_if)
177
178 def delete_xconnects(self, count, start=0):
179 """
180 Delete required number of cross-connects (always two cross-connects per
181 pair of packet-generator interfaces).
182
183 :param int count: Number of cross-connects to be deleted.
184 :param int start: Starting index of packet-generator interfaces. \
185 (Default value = 0)
186 """
187 for i in range(count):
Klement Sekera9225dee2016-12-12 08:36:58 +0100188 rx_if = self.pg_interfaces[i + start]
Jan0bd0b7f2016-12-05 16:22:41 +0100189 delta = 1 if i % 2 == 0 else -1
Klement Sekera9225dee2016-12-12 08:36:58 +0100190 tx_if = self.pg_interfaces[i + start + delta]
Jan0bd0b7f2016-12-05 16:22:41 +0100191 self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
192 tx_if.sw_if_index, 0)
193 self.logger.info("Cross-connect from %s to %s deleted"
194 % (tx_if.name, rx_if.name))
195 if self.pg_not_in_xc.count(rx_if) == 0:
196 self.pg_not_in_xc.append(rx_if)
197 if self.pg_in_xc.count(rx_if) == 1:
198 self.pg_in_xc.remove(rx_if)
199
200 def create_stream(self, src_if, packet_sizes):
201 """
202 Create input packet stream for defined interface using hosts list.
203
204 :param object src_if: Interface to create packet stream for.
205 :param list packet_sizes: List of required packet sizes.
206 :return: Stream of packets.
207 """
208 pkts = []
209 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
210 for dst_if in self.flows[src_if]:
211 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
212 n_int = len(dst_hosts)
213 for i in range(0, n_int):
214 dst_host = dst_hosts[i]
215 src_host = random.choice(src_hosts)
Klement Sekeradab231a2016-12-21 08:50:14 +0100216 pkt_info = self.create_packet_info(src_if, dst_if)
Jan0bd0b7f2016-12-05 16:22:41 +0100217 payload = self.info_to_payload(pkt_info)
218 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
219 IP(src=src_host.ip4, dst=dst_host.ip4) /
220 UDP(sport=1234, dport=1234) /
221 Raw(payload))
222 pkt_info.data = p.copy()
223 size = random.choice(packet_sizes)
224 self.extend_packet(p, size)
225 pkts.append(p)
226 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
227 % (src_if.name, len(pkts)))
228 return pkts
229
230 def verify_capture(self, pg_if, capture):
231 """
232 Verify captured input packet stream for defined interface.
233
234 :param object pg_if: Interface to verify captured packet stream for.
235 :param list capture: Captured packet stream.
236 """
237 last_info = dict()
238 for i in self.pg_interfaces:
239 last_info[i.sw_if_index] = None
240 dst_sw_if_index = pg_if.sw_if_index
241 for packet in capture:
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800242 payload_info = self.payload_to_info(packet[Raw])
Jan0bd0b7f2016-12-05 16:22:41 +0100243 try:
244 ip = packet[IP]
245 udp = packet[UDP]
246 packet_index = payload_info.index
247 self.assertEqual(payload_info.dst, dst_sw_if_index)
248 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
249 (pg_if.name, payload_info.src, packet_index))
250 next_info = self.get_next_packet_info_for_interface2(
251 payload_info.src, dst_sw_if_index,
252 last_info[payload_info.src])
253 last_info[payload_info.src] = next_info
254 self.assertTrue(next_info is not None)
255 self.assertEqual(packet_index, next_info.index)
256 saved_packet = next_info.data
257 # Check standard fields
258 self.assertEqual(ip.src, saved_packet[IP].src)
259 self.assertEqual(ip.dst, saved_packet[IP].dst)
260 self.assertEqual(udp.sport, saved_packet[UDP].sport)
261 self.assertEqual(udp.dport, saved_packet[UDP].dport)
262 except:
Klement Sekera9225dee2016-12-12 08:36:58 +0100263 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Jan0bd0b7f2016-12-05 16:22:41 +0100264 raise
265 for i in self.pg_interfaces:
266 remaining_packet = self.get_next_packet_info_for_interface2(
267 i, dst_sw_if_index, last_info[i.sw_if_index])
268 self.assertTrue(
269 remaining_packet is None,
270 "Port %u: Packet expected from source %u didn't arrive" %
271 (dst_sw_if_index, i.sw_if_index))
272
273 def run_verify_test(self):
274 """
Matej Klottondeb69842016-12-09 15:05:46 +0100275 Create packet streams for all configured l2-pg interfaces, send all \
Jan0bd0b7f2016-12-05 16:22:41 +0100276 prepared packet streams and verify that:
Matej Klottondeb69842016-12-09 15:05:46 +0100277 - all packets received correctly on all pg-l2 interfaces assigned
278 to cross-connects
279 - no packet received on all pg-l2 interfaces not assigned to
280 cross-connects
Jan0bd0b7f2016-12-05 16:22:41 +0100281
Matej Klottondeb69842016-12-09 15:05:46 +0100282 :raise RuntimeError: if no packet captured on l2-pg interface assigned
283 to the cross-connect or if any packet is captured
284 on l2-pg interface not assigned to the
285 cross-connect.
Jan0bd0b7f2016-12-05 16:22:41 +0100286 """
287 # Test
288 # Create incoming packet streams for packet-generator interfaces
289 for pg_if in self.pg_interfaces:
290 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
291 pg_if.add_stream(pkts)
292
293 # Enable packet capture and start packet sending
294 self.pg_enable_capture(self.pg_interfaces)
295 self.pg_start()
296
297 # Verify
298 # Verify outgoing packet streams per packet-generator interface
299 for pg_if in self.pg_interfaces:
Jan0bd0b7f2016-12-05 16:22:41 +0100300 if pg_if in self.pg_in_xc:
Klement Sekera9225dee2016-12-12 08:36:58 +0100301 capture = pg_if.get_capture(
302 remark="interface is a cross-connect sink")
Jan0bd0b7f2016-12-05 16:22:41 +0100303 self.verify_capture(pg_if, capture)
304 elif pg_if in self.pg_not_in_xc:
Klement Sekera9225dee2016-12-12 08:36:58 +0100305 pg_if.assert_nothing_captured(
306 remark="interface is not a cross-connect sink")
Jan0bd0b7f2016-12-05 16:22:41 +0100307 else:
Klement Sekera9225dee2016-12-12 08:36:58 +0100308 raise Exception("Unexpected interface: %s" % pg_if.name)
Jan0bd0b7f2016-12-05 16:22:41 +0100309
310 def test_l2xc_inst_01(self):
311 """ L2XC Multi-instance test 1 - create 10 cross-connects
312 """
313 # Config 1
314 # Create 10 cross-connects
315 self.create_xconnects(10)
316
317 # Test 1
318 self.run_verify_test()
319
320 def test_l2xc_inst_02(self):
321 """ L2XC Multi-instance test 2 - delete 4 cross-connects
322 """
323 # Config 2
324 # Delete 4 cross-connects
325 self.delete_xconnects(4)
326
327 # Test 2
328 self.run_verify_test()
329
330 def test_l2xc_inst_03(self):
331 """ L2BD Multi-instance 3 - add new 4 cross-connects
332 """
333 # Config 3
334 # Add new 4 cross-connects
335 self.create_xconnects(4, start=10)
336
337 # Test 3
338 self.run_verify_test()
339
340 def test_l2xc_inst_04(self):
341 """ L2XC Multi-instance test 4 - delete 10 cross-connects
342 """
343 # Config 4
344 # Delete 10 cross-connects
345 self.delete_xconnects(10, start=4)
346
347 # Test 4
348 self.run_verify_test()
349
350
351if __name__ == '__main__':
352 unittest.main(testRunner=VppTestRunner)