2 """L2XC Multi-instance Test Case HLD:
5 - 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
11 - add 14 pg-l2 interfaces
12 - add 10 cross-connects (two cross-connects per pair of l2-pg interfaces)
15 - send L2 MAC frames between all pairs of pg-l2 interfaces
18 - all packets received correctly in case of cross-connected l2-pg
20 - no packet received in case of not cross-connected l2-pg interfaces
23 - delete 4 cross-connects
26 - send L2 MAC frames between all pairs of pg-l2 interfaces
29 - all packets received correctly in case of cross-connected l2-pg
31 - no packet received in case of not cross-connected l2-pg interfaces
34 - add new 4 cross-connects
37 - send L2 MAC frames between all pairs of pg-l2 interfaces
40 - all packets received correctly in case of cross-connected l2-pg
42 - no packet received in case of not cross-connected l2-pg interfaces
45 - delete 10 cross-connects
48 - send L2 MAC frames between all pairs of pg-l2 interfaces
51 - no packet received on all of l2-pg interfaces (no cross-connect created)
57 from scapy.packet import Raw
58 from scapy.layers.l2 import Ether
59 from scapy.layers.inet import IP, UDP
61 from framework import VppTestCase, VppTestRunner
62 from util import Host, ppp
65 class TestL2xcMultiInst(VppTestCase):
66 """L2XC Multi-instance Test Case"""
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.
75 super(TestL2xcMultiInst, cls).setUpClass()
78 # Create pg interfaces
79 cls.create_pg_interfaces(range(14))
81 # Packet flows mapping pg0 -> pg1 etc.
83 for i in range(len(cls.pg_interfaces)):
84 delta = 1 if i % 2 == 0 else -1
85 cls.flows[cls.pg_interfaces[i]] = [cls.pg_interfaces[i + delta]]
87 # Mapping between packet-generator index and lists of test hosts
88 cls.hosts_by_pg_idx = dict()
89 for pg_if in cls.pg_interfaces:
90 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
92 # Create test host entries
95 # Packet sizes - jumbo packet (9018 bytes) skipped
96 cls.pg_if_packet_sizes = [64, 512, 1518]
98 # Set up all interfaces
99 for i in cls.pg_interfaces:
102 # Create list of x-connected pg_interfaces
103 cls.pg_in_xc = list()
105 # Create list of not x-connected pg_interfaces
106 cls.pg_not_in_xc = list()
107 for pg_if in cls.pg_interfaces:
108 cls.pg_not_in_xc.append(pg_if)
111 super(TestL2xcMultiInst, cls).tearDownClass()
115 def tearDownClass(cls):
116 super(TestL2xcMultiInst, cls).tearDownClass()
120 Clear trace and packet infos before running each test.
122 super(TestL2xcMultiInst, self).setUp()
123 self.reset_packet_infos()
127 Show various debug prints after each test.
129 super(TestL2xcMultiInst, self).tearDown()
131 def show_commands_at_teardown(self):
132 self.logger.info(self.vapi.ppcli("show l2patch"))
135 def create_hosts(cls, count):
137 Create required number of host MAC addresses and distribute them among
138 interfaces. Create host IPv4 address for every host MAC address.
140 :param int count: Number of hosts to create MAC/IPv4 addresses for.
142 n_int = len(cls.pg_interfaces)
143 macs_per_if = count // n_int
145 for pg_if in cls.pg_interfaces:
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):
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),
157 def create_xconnects(self, count, start=0):
159 Create required number of cross-connects (always two cross-connects per
160 pair of packet-generator interfaces).
162 :param int count: Number of cross-connects to be created.
163 :param int start: Starting index of packet-generator interfaces. \
166 for i in range(count):
167 rx_if = self.pg_interfaces[i + start]
168 delta = 1 if i % 2 == 0 else -1
169 tx_if = self.pg_interfaces[i + start + delta]
170 self.vapi.sw_interface_set_l2_xconnect(
171 rx_if.sw_if_index, tx_if.sw_if_index, 1
174 "Cross-connect from %s to %s created" % (tx_if.name, rx_if.name)
176 if self.pg_in_xc.count(rx_if) == 0:
177 self.pg_in_xc.append(rx_if)
178 if self.pg_not_in_xc.count(rx_if) == 1:
179 self.pg_not_in_xc.remove(rx_if)
181 def delete_xconnects(self, count, start=0):
183 Delete required number of cross-connects (always two cross-connects per
184 pair of packet-generator interfaces).
186 :param int count: Number of cross-connects to be deleted.
187 :param int start: Starting index of packet-generator interfaces. \
190 for i in range(count):
191 rx_if = self.pg_interfaces[i + start]
192 delta = 1 if i % 2 == 0 else -1
193 tx_if = self.pg_interfaces[i + start + delta]
194 self.vapi.sw_interface_set_l2_xconnect(
195 rx_if.sw_if_index, tx_if.sw_if_index, 0
198 "Cross-connect from %s to %s deleted" % (tx_if.name, rx_if.name)
200 if self.pg_not_in_xc.count(rx_if) == 0:
201 self.pg_not_in_xc.append(rx_if)
202 if self.pg_in_xc.count(rx_if) == 1:
203 self.pg_in_xc.remove(rx_if)
205 def create_stream(self, src_if, packet_sizes):
207 Create input packet stream for defined interface using hosts list.
209 :param object src_if: Interface to create packet stream for.
210 :param list packet_sizes: List of required packet sizes.
211 :return: Stream of packets.
214 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
215 for dst_if in self.flows[src_if]:
216 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
217 n_int = len(dst_hosts)
218 for i in range(0, n_int):
219 dst_host = dst_hosts[i]
220 src_host = random.choice(src_hosts)
221 pkt_info = self.create_packet_info(src_if, dst_if)
222 payload = self.info_to_payload(pkt_info)
224 Ether(dst=dst_host.mac, src=src_host.mac)
225 / IP(src=src_host.ip4, dst=dst_host.ip4)
226 / UDP(sport=1234, dport=1234)
229 pkt_info.data = p.copy()
230 size = random.choice(packet_sizes)
231 self.extend_packet(p, size)
234 "Input stream created for port %s. Length: %u pkt(s)"
235 % (src_if.name, len(pkts))
239 def verify_capture(self, pg_if, capture):
241 Verify captured input packet stream for defined interface.
243 :param object pg_if: Interface to verify captured packet stream for.
244 :param list capture: Captured packet stream.
247 for i in self.pg_interfaces:
248 last_info[i.sw_if_index] = None
249 dst_sw_if_index = pg_if.sw_if_index
250 for packet in capture:
251 payload_info = self.payload_to_info(packet[Raw])
255 packet_index = payload_info.index
256 self.assertEqual(payload_info.dst, dst_sw_if_index)
258 "Got packet on port %s: src=%u (id=%u)"
259 % (pg_if.name, payload_info.src, packet_index)
261 next_info = self.get_next_packet_info_for_interface2(
262 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
264 last_info[payload_info.src] = next_info
265 self.assertTrue(next_info is not None)
266 self.assertEqual(packet_index, next_info.index)
267 saved_packet = next_info.data
268 # Check standard fields
269 self.assertEqual(ip.src, saved_packet[IP].src)
270 self.assertEqual(ip.dst, saved_packet[IP].dst)
271 self.assertEqual(udp.sport, saved_packet[UDP].sport)
272 self.assertEqual(udp.dport, saved_packet[UDP].dport)
274 self.logger.error(ppp("Unexpected or invalid packet:", packet))
276 for i in self.pg_interfaces:
277 remaining_packet = self.get_next_packet_info_for_interface2(
278 i, dst_sw_if_index, last_info[i.sw_if_index]
281 remaining_packet is None,
282 "Port %u: Packet expected from source %u didn't arrive"
283 % (dst_sw_if_index, i.sw_if_index),
286 def run_verify_test(self):
288 Create packet streams for all configured l2-pg interfaces, send all \
289 prepared packet streams and verify that:
290 - all packets received correctly on all pg-l2 interfaces assigned
292 - no packet received on all pg-l2 interfaces not assigned to
295 :raise RuntimeError: if no packet captured on l2-pg interface assigned
296 to the cross-connect or if any packet is captured
297 on l2-pg interface not assigned to the
301 # Create incoming packet streams for packet-generator interfaces
302 for pg_if in self.pg_interfaces:
303 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
304 pg_if.add_stream(pkts)
306 # Enable packet capture and start packet sending
307 self.pg_enable_capture(self.pg_interfaces)
311 # Verify outgoing packet streams per packet-generator interface
312 for pg_if in self.pg_interfaces:
313 if pg_if in self.pg_in_xc:
314 capture = pg_if.get_capture(remark="interface is a cross-connect sink")
315 self.verify_capture(pg_if, capture)
316 elif pg_if in self.pg_not_in_xc:
317 pg_if.assert_nothing_captured(
318 remark="interface is not a cross-connect sink"
321 raise Exception("Unexpected interface: %s" % pg_if.name)
323 def test_l2xc_inst_01(self):
324 """L2XC Multi-instance test 1 - create 10 cross-connects"""
326 # Create 10 cross-connects
327 self.create_xconnects(10)
330 self.run_verify_test()
332 def test_l2xc_inst_02(self):
333 """L2XC Multi-instance test 2 - delete 4 cross-connects"""
335 # Delete 4 cross-connects
336 self.delete_xconnects(4)
339 self.run_verify_test()
341 def test_l2xc_inst_03(self):
342 """L2BD Multi-instance 3 - add new 4 cross-connects"""
344 # Add new 4 cross-connects
345 self.create_xconnects(4, start=10)
348 self.run_verify_test()
350 def test_l2xc_inst_04(self):
351 """L2XC Multi-instance test 4 - delete 10 cross-connects"""
353 # Delete 10 cross-connects
354 self.delete_xconnects(10, start=4)
357 self.run_verify_test()
360 if __name__ == "__main__":
361 unittest.main(testRunner=VppTestRunner)