blob: 6192bd1ad57f30f93443b59b84dbfbbfc5acb0c5 [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
115 def setUp(self):
116 """
117 Clear trace and packet infos before running each test.
118 """
119 super(TestL2xcMultiInst, self).setUp()
Klement Sekeradab231a2016-12-21 08:50:14 +0100120 self.reset_packet_infos()
Jan0bd0b7f2016-12-05 16:22:41 +0100121
122 def tearDown(self):
123 """
124 Show various debug prints after each test.
125 """
126 super(TestL2xcMultiInst, self).tearDown()
127 if not self.vpp_dead:
128 self.logger.info(self.vapi.ppcli("show l2patch"))
129
130 @classmethod
131 def create_hosts(cls, count):
132 """
133 Create required number of host MAC addresses and distribute them among
134 interfaces. Create host IPv4 address for every host MAC address.
135
136 :param int count: Number of hosts to create MAC/IPv4 addresses for.
137 """
138 n_int = len(cls.pg_interfaces)
139 macs_per_if = count / n_int
140 i = -1
141 for pg_if in cls.pg_interfaces:
142 i += 1
143 start_nr = macs_per_if * i
144 end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
145 hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
146 for j in range(start_nr, end_nr):
147 host = Host(
148 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
149 "172.17.1%02u.%u" % (pg_if.sw_if_index, j))
150 hosts.append(host)
151
152 def create_xconnects(self, count, start=0):
153 """
154 Create required number of cross-connects (always two cross-connects per
155 pair of packet-generator interfaces).
156
157 :param int count: Number of cross-connects to be created.
158 :param int start: Starting index of packet-generator interfaces. \
159 (Default value = 0)
160 """
161 for i in range(count):
Klement Sekera9225dee2016-12-12 08:36:58 +0100162 rx_if = self.pg_interfaces[i + start]
Jan0bd0b7f2016-12-05 16:22:41 +0100163 delta = 1 if i % 2 == 0 else -1
Klement Sekera9225dee2016-12-12 08:36:58 +0100164 tx_if = self.pg_interfaces[i + start + delta]
Jan0bd0b7f2016-12-05 16:22:41 +0100165 self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
166 tx_if.sw_if_index, 1)
167 self.logger.info("Cross-connect from %s to %s created"
168 % (tx_if.name, rx_if.name))
169 if self.pg_in_xc.count(rx_if) == 0:
170 self.pg_in_xc.append(rx_if)
171 if self.pg_not_in_xc.count(rx_if) == 1:
172 self.pg_not_in_xc.remove(rx_if)
173
174 def delete_xconnects(self, count, start=0):
175 """
176 Delete required number of cross-connects (always two cross-connects per
177 pair of packet-generator interfaces).
178
179 :param int count: Number of cross-connects to be deleted.
180 :param int start: Starting index of packet-generator interfaces. \
181 (Default value = 0)
182 """
183 for i in range(count):
Klement Sekera9225dee2016-12-12 08:36:58 +0100184 rx_if = self.pg_interfaces[i + start]
Jan0bd0b7f2016-12-05 16:22:41 +0100185 delta = 1 if i % 2 == 0 else -1
Klement Sekera9225dee2016-12-12 08:36:58 +0100186 tx_if = self.pg_interfaces[i + start + delta]
Jan0bd0b7f2016-12-05 16:22:41 +0100187 self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
188 tx_if.sw_if_index, 0)
189 self.logger.info("Cross-connect from %s to %s deleted"
190 % (tx_if.name, rx_if.name))
191 if self.pg_not_in_xc.count(rx_if) == 0:
192 self.pg_not_in_xc.append(rx_if)
193 if self.pg_in_xc.count(rx_if) == 1:
194 self.pg_in_xc.remove(rx_if)
195
196 def create_stream(self, src_if, packet_sizes):
197 """
198 Create input packet stream for defined interface using hosts list.
199
200 :param object src_if: Interface to create packet stream for.
201 :param list packet_sizes: List of required packet sizes.
202 :return: Stream of packets.
203 """
204 pkts = []
205 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
206 for dst_if in self.flows[src_if]:
207 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
208 n_int = len(dst_hosts)
209 for i in range(0, n_int):
210 dst_host = dst_hosts[i]
211 src_host = random.choice(src_hosts)
Klement Sekeradab231a2016-12-21 08:50:14 +0100212 pkt_info = self.create_packet_info(src_if, dst_if)
Jan0bd0b7f2016-12-05 16:22:41 +0100213 payload = self.info_to_payload(pkt_info)
214 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
215 IP(src=src_host.ip4, dst=dst_host.ip4) /
216 UDP(sport=1234, dport=1234) /
217 Raw(payload))
218 pkt_info.data = p.copy()
219 size = random.choice(packet_sizes)
220 self.extend_packet(p, size)
221 pkts.append(p)
222 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
223 % (src_if.name, len(pkts)))
224 return pkts
225
226 def verify_capture(self, pg_if, capture):
227 """
228 Verify captured input packet stream for defined interface.
229
230 :param object pg_if: Interface to verify captured packet stream for.
231 :param list capture: Captured packet stream.
232 """
233 last_info = dict()
234 for i in self.pg_interfaces:
235 last_info[i.sw_if_index] = None
236 dst_sw_if_index = pg_if.sw_if_index
237 for packet in capture:
Paul Vinciguerraeaea4212019-03-06 11:58:06 -0800238 payload_info = self.payload_to_info(packet[Raw])
Jan0bd0b7f2016-12-05 16:22:41 +0100239 try:
240 ip = packet[IP]
241 udp = packet[UDP]
242 packet_index = payload_info.index
243 self.assertEqual(payload_info.dst, dst_sw_if_index)
244 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
245 (pg_if.name, payload_info.src, packet_index))
246 next_info = self.get_next_packet_info_for_interface2(
247 payload_info.src, dst_sw_if_index,
248 last_info[payload_info.src])
249 last_info[payload_info.src] = next_info
250 self.assertTrue(next_info is not None)
251 self.assertEqual(packet_index, next_info.index)
252 saved_packet = next_info.data
253 # Check standard fields
254 self.assertEqual(ip.src, saved_packet[IP].src)
255 self.assertEqual(ip.dst, saved_packet[IP].dst)
256 self.assertEqual(udp.sport, saved_packet[UDP].sport)
257 self.assertEqual(udp.dport, saved_packet[UDP].dport)
258 except:
Klement Sekera9225dee2016-12-12 08:36:58 +0100259 self.logger.error(ppp("Unexpected or invalid packet:", packet))
Jan0bd0b7f2016-12-05 16:22:41 +0100260 raise
261 for i in self.pg_interfaces:
262 remaining_packet = self.get_next_packet_info_for_interface2(
263 i, dst_sw_if_index, last_info[i.sw_if_index])
264 self.assertTrue(
265 remaining_packet is None,
266 "Port %u: Packet expected from source %u didn't arrive" %
267 (dst_sw_if_index, i.sw_if_index))
268
269 def run_verify_test(self):
270 """
Matej Klottondeb69842016-12-09 15:05:46 +0100271 Create packet streams for all configured l2-pg interfaces, send all \
Jan0bd0b7f2016-12-05 16:22:41 +0100272 prepared packet streams and verify that:
Matej Klottondeb69842016-12-09 15:05:46 +0100273 - all packets received correctly on all pg-l2 interfaces assigned
274 to cross-connects
275 - no packet received on all pg-l2 interfaces not assigned to
276 cross-connects
Jan0bd0b7f2016-12-05 16:22:41 +0100277
Matej Klottondeb69842016-12-09 15:05:46 +0100278 :raise RuntimeError: if no packet captured on l2-pg interface assigned
279 to the cross-connect or if any packet is captured
280 on l2-pg interface not assigned to the
281 cross-connect.
Jan0bd0b7f2016-12-05 16:22:41 +0100282 """
283 # Test
284 # Create incoming packet streams for packet-generator interfaces
285 for pg_if in self.pg_interfaces:
286 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
287 pg_if.add_stream(pkts)
288
289 # Enable packet capture and start packet sending
290 self.pg_enable_capture(self.pg_interfaces)
291 self.pg_start()
292
293 # Verify
294 # Verify outgoing packet streams per packet-generator interface
295 for pg_if in self.pg_interfaces:
Jan0bd0b7f2016-12-05 16:22:41 +0100296 if pg_if in self.pg_in_xc:
Klement Sekera9225dee2016-12-12 08:36:58 +0100297 capture = pg_if.get_capture(
298 remark="interface is a cross-connect sink")
Jan0bd0b7f2016-12-05 16:22:41 +0100299 self.verify_capture(pg_if, capture)
300 elif pg_if in self.pg_not_in_xc:
Klement Sekera9225dee2016-12-12 08:36:58 +0100301 pg_if.assert_nothing_captured(
302 remark="interface is not a cross-connect sink")
Jan0bd0b7f2016-12-05 16:22:41 +0100303 else:
Klement Sekera9225dee2016-12-12 08:36:58 +0100304 raise Exception("Unexpected interface: %s" % pg_if.name)
Jan0bd0b7f2016-12-05 16:22:41 +0100305
306 def test_l2xc_inst_01(self):
307 """ L2XC Multi-instance test 1 - create 10 cross-connects
308 """
309 # Config 1
310 # Create 10 cross-connects
311 self.create_xconnects(10)
312
313 # Test 1
314 self.run_verify_test()
315
316 def test_l2xc_inst_02(self):
317 """ L2XC Multi-instance test 2 - delete 4 cross-connects
318 """
319 # Config 2
320 # Delete 4 cross-connects
321 self.delete_xconnects(4)
322
323 # Test 2
324 self.run_verify_test()
325
326 def test_l2xc_inst_03(self):
327 """ L2BD Multi-instance 3 - add new 4 cross-connects
328 """
329 # Config 3
330 # Add new 4 cross-connects
331 self.create_xconnects(4, start=10)
332
333 # Test 3
334 self.run_verify_test()
335
336 def test_l2xc_inst_04(self):
337 """ L2XC Multi-instance test 4 - delete 10 cross-connects
338 """
339 # Config 4
340 # Delete 10 cross-connects
341 self.delete_xconnects(10, start=4)
342
343 # Test 4
344 self.run_verify_test()
345
346
347if __name__ == '__main__':
348 unittest.main(testRunner=VppTestRunner)