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
62 from asfframework import VppTestRunner
63 from util import Host, ppp
66 class TestL2xcMultiInst(VppTestCase):
67 """L2XC Multi-instance Test Case"""
72 Perform standard class setup (defined by class method setUpClass in
73 class VppTestCase) before running the test case, set test case related
74 variables and configure VPP.
76 super(TestL2xcMultiInst, cls).setUpClass()
79 # Create pg interfaces
80 cls.create_pg_interfaces(range(14))
82 # Packet flows mapping pg0 -> pg1 etc.
84 for i in range(len(cls.pg_interfaces)):
85 delta = 1 if i % 2 == 0 else -1
86 cls.flows[cls.pg_interfaces[i]] = [cls.pg_interfaces[i + delta]]
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] = []
93 # Create test host entries
96 # Packet sizes - jumbo packet (9018 bytes) skipped
97 cls.pg_if_packet_sizes = [64, 512, 1518]
99 # Set up all interfaces
100 for i in cls.pg_interfaces:
103 # Create list of x-connected pg_interfaces
104 cls.pg_in_xc = list()
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)
112 super(TestL2xcMultiInst, cls).tearDownClass()
116 def tearDownClass(cls):
117 super(TestL2xcMultiInst, cls).tearDownClass()
121 Clear trace and packet infos before running each test.
123 super(TestL2xcMultiInst, self).setUp()
124 self.reset_packet_infos()
128 Show various debug prints after each test.
130 super(TestL2xcMultiInst, self).tearDown()
132 def show_commands_at_teardown(self):
133 self.logger.info(self.vapi.ppcli("show l2patch"))
136 def create_hosts(cls, count):
138 Create required number of host MAC addresses and distribute them among
139 interfaces. Create host IPv4 address for every host MAC address.
141 :param int count: Number of hosts to create MAC/IPv4 addresses for.
143 n_int = len(cls.pg_interfaces)
144 macs_per_if = count // n_int
146 for pg_if in cls.pg_interfaces:
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):
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),
158 def create_xconnects(self, count, start=0):
160 Create required number of cross-connects (always two cross-connects per
161 pair of packet-generator interfaces).
163 :param int count: Number of cross-connects to be created.
164 :param int start: Starting index of packet-generator interfaces. \
167 for i in range(count):
168 rx_if = self.pg_interfaces[i + start]
169 delta = 1 if i % 2 == 0 else -1
170 tx_if = self.pg_interfaces[i + start + delta]
171 self.vapi.sw_interface_set_l2_xconnect(
172 rx_if.sw_if_index, tx_if.sw_if_index, 1
175 "Cross-connect from %s to %s created" % (tx_if.name, rx_if.name)
177 if self.pg_in_xc.count(rx_if) == 0:
178 self.pg_in_xc.append(rx_if)
179 if self.pg_not_in_xc.count(rx_if) == 1:
180 self.pg_not_in_xc.remove(rx_if)
182 def delete_xconnects(self, count, start=0):
184 Delete required number of cross-connects (always two cross-connects per
185 pair of packet-generator interfaces).
187 :param int count: Number of cross-connects to be deleted.
188 :param int start: Starting index of packet-generator interfaces. \
191 for i in range(count):
192 rx_if = self.pg_interfaces[i + start]
193 delta = 1 if i % 2 == 0 else -1
194 tx_if = self.pg_interfaces[i + start + delta]
195 self.vapi.sw_interface_set_l2_xconnect(
196 rx_if.sw_if_index, tx_if.sw_if_index, 0
199 "Cross-connect from %s to %s deleted" % (tx_if.name, rx_if.name)
201 if self.pg_not_in_xc.count(rx_if) == 0:
202 self.pg_not_in_xc.append(rx_if)
203 if self.pg_in_xc.count(rx_if) == 1:
204 self.pg_in_xc.remove(rx_if)
206 def create_stream(self, src_if, packet_sizes):
208 Create input packet stream for defined interface using hosts list.
210 :param object src_if: Interface to create packet stream for.
211 :param list packet_sizes: List of required packet sizes.
212 :return: Stream of packets.
215 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
216 for dst_if in self.flows[src_if]:
217 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
218 n_int = len(dst_hosts)
219 for i in range(0, n_int):
220 dst_host = dst_hosts[i]
221 src_host = random.choice(src_hosts)
222 pkt_info = self.create_packet_info(src_if, dst_if)
223 payload = self.info_to_payload(pkt_info)
225 Ether(dst=dst_host.mac, src=src_host.mac)
226 / IP(src=src_host.ip4, dst=dst_host.ip4)
227 / UDP(sport=1234, dport=1234)
230 pkt_info.data = p.copy()
231 size = random.choice(packet_sizes)
232 self.extend_packet(p, size)
235 "Input stream created for port %s. Length: %u pkt(s)"
236 % (src_if.name, len(pkts))
240 def verify_capture(self, pg_if, capture):
242 Verify captured input packet stream for defined interface.
244 :param object pg_if: Interface to verify captured packet stream for.
245 :param list capture: Captured packet stream.
248 for i in self.pg_interfaces:
249 last_info[i.sw_if_index] = None
250 dst_sw_if_index = pg_if.sw_if_index
251 for packet in capture:
252 payload_info = self.payload_to_info(packet[Raw])
256 packet_index = payload_info.index
257 self.assertEqual(payload_info.dst, dst_sw_if_index)
259 "Got packet on port %s: src=%u (id=%u)"
260 % (pg_if.name, payload_info.src, packet_index)
262 next_info = self.get_next_packet_info_for_interface2(
263 payload_info.src, dst_sw_if_index, last_info[payload_info.src]
265 last_info[payload_info.src] = next_info
266 self.assertTrue(next_info is not None)
267 self.assertEqual(packet_index, next_info.index)
268 saved_packet = next_info.data
269 # Check standard fields
270 self.assertEqual(ip.src, saved_packet[IP].src)
271 self.assertEqual(ip.dst, saved_packet[IP].dst)
272 self.assertEqual(udp.sport, saved_packet[UDP].sport)
273 self.assertEqual(udp.dport, saved_packet[UDP].dport)
275 self.logger.error(ppp("Unexpected or invalid packet:", packet))
277 for i in self.pg_interfaces:
278 remaining_packet = self.get_next_packet_info_for_interface2(
279 i, dst_sw_if_index, last_info[i.sw_if_index]
282 remaining_packet is None,
283 "Port %u: Packet expected from source %u didn't arrive"
284 % (dst_sw_if_index, i.sw_if_index),
287 def run_verify_test(self):
289 Create packet streams for all configured l2-pg interfaces, send all \
290 prepared packet streams and verify that:
291 - all packets received correctly on all pg-l2 interfaces assigned
293 - no packet received on all pg-l2 interfaces not assigned to
296 :raise RuntimeError: if no packet captured on l2-pg interface assigned
297 to the cross-connect or if any packet is captured
298 on l2-pg interface not assigned to the
302 # Create incoming packet streams for packet-generator interfaces
303 for pg_if in self.pg_interfaces:
304 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
305 pg_if.add_stream(pkts)
307 # Enable packet capture and start packet sending
308 self.pg_enable_capture(self.pg_interfaces)
312 # Verify outgoing packet streams per packet-generator interface
313 for pg_if in self.pg_interfaces:
314 if pg_if in self.pg_in_xc:
315 capture = pg_if.get_capture(remark="interface is a cross-connect sink")
316 self.verify_capture(pg_if, capture)
317 elif pg_if in self.pg_not_in_xc:
318 pg_if.assert_nothing_captured(
319 remark="interface is not a cross-connect sink"
322 raise Exception("Unexpected interface: %s" % pg_if.name)
324 def test_l2xc_inst_01(self):
325 """L2XC Multi-instance test 1 - create 10 cross-connects"""
327 # Create 10 cross-connects
328 self.create_xconnects(10)
331 self.run_verify_test()
333 def test_l2xc_inst_02(self):
334 """L2XC Multi-instance test 2 - delete 4 cross-connects"""
336 # Delete 4 cross-connects
337 self.delete_xconnects(4)
340 self.run_verify_test()
342 def test_l2xc_inst_03(self):
343 """L2BD Multi-instance 3 - add new 4 cross-connects"""
345 # Add new 4 cross-connects
346 self.create_xconnects(4, start=10)
349 self.run_verify_test()
351 def test_l2xc_inst_04(self):
352 """L2XC Multi-instance test 4 - delete 10 cross-connects"""
354 # Delete 10 cross-connects
355 self.delete_xconnects(10, start=4)
358 self.run_verify_test()
361 if __name__ == "__main__":
362 unittest.main(testRunner=VppTestRunner)