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 interfaces
19 - no packet received in case of not cross-connected l2-pg interfaces
22 - delete 4 cross-connects
25 - send L2 MAC frames between all pairs of pg-l2 interfaces
28 - all packets received correctly in case of cross-connected l2-pg interfaces
29 - no packet received in case of not cross-connected l2-pg interfaces
32 - add new 4 cross-connects
35 - send L2 MAC frames between all pairs of pg-l2 interfaces
38 - all packets received correctly in case of cross-connected l2-pg interfaces
39 - no packet received in case of not cross-connected l2-pg interfaces
42 - delete 10 cross-connects
45 - send L2 MAC frames between all pairs of pg-l2 interfaces
48 - no packet received on all of l2-pg interfaces (no cross-connect created)
54 from scapy.packet import Raw
55 from scapy.layers.l2 import Ether
56 from scapy.layers.inet import IP, UDP
58 from framework import VppTestCase, VppTestRunner
62 class TestL2xcMultiInst(VppTestCase):
63 """ L2XC Multi-instance Test Case """
68 Perform standard class setup (defined by class method setUpClass in
69 class VppTestCase) before running the test case, set test case related
70 variables and configure VPP.
72 super(TestL2xcMultiInst, cls).setUpClass()
75 # Create pg interfaces
76 cls.create_pg_interfaces(range(14))
78 # Packet flows mapping pg0 -> pg1 etc.
80 for i in range(len(cls.pg_interfaces)):
81 delta = 1 if i % 2 == 0 else -1
82 cls.flows[cls.pg_interfaces[i]] = [cls.pg_interfaces[i+delta]]
84 # Mapping between packet-generator index and lists of test hosts
85 cls.hosts_by_pg_idx = dict()
86 for pg_if in cls.pg_interfaces:
87 cls.hosts_by_pg_idx[pg_if.sw_if_index] = []
89 # Create test host entries
92 # Packet sizes - jumbo packet (9018 bytes) skipped
93 cls.pg_if_packet_sizes = [64, 512, 1518]
95 # Set up all interfaces
96 for i in cls.pg_interfaces:
99 # Create list of x-connected pg_interfaces
100 cls.pg_in_xc = list()
102 # Create list of not x-connected pg_interfaces
103 cls.pg_not_in_xc = list()
104 for pg_if in cls.pg_interfaces:
105 cls.pg_not_in_xc.append(pg_if)
108 super(TestL2xcMultiInst, cls).tearDownClass()
113 Clear trace and packet infos before running each test.
115 super(TestL2xcMultiInst, self).setUp()
116 self.packet_infos = {}
120 Show various debug prints after each test.
122 super(TestL2xcMultiInst, self).tearDown()
123 if not self.vpp_dead:
124 self.logger.info(self.vapi.ppcli("show l2patch"))
127 def create_hosts(cls, count):
129 Create required number of host MAC addresses and distribute them among
130 interfaces. Create host IPv4 address for every host MAC address.
132 :param int count: Number of hosts to create MAC/IPv4 addresses for.
134 n_int = len(cls.pg_interfaces)
135 macs_per_if = count / n_int
137 for pg_if in cls.pg_interfaces:
139 start_nr = macs_per_if * i
140 end_nr = count if i == (n_int - 1) else macs_per_if * (i + 1)
141 hosts = cls.hosts_by_pg_idx[pg_if.sw_if_index]
142 for j in range(start_nr, end_nr):
144 "00:00:00:ff:%02x:%02x" % (pg_if.sw_if_index, j),
145 "172.17.1%02u.%u" % (pg_if.sw_if_index, j))
148 def create_xconnects(self, count, start=0):
150 Create required number of cross-connects (always two cross-connects per
151 pair of packet-generator interfaces).
153 :param int count: Number of cross-connects to be created.
154 :param int start: Starting index of packet-generator interfaces. \
157 for i in range(count):
158 rx_if = self.pg_interfaces[i+start]
159 delta = 1 if i % 2 == 0 else -1
160 tx_if = self.pg_interfaces[i+start+delta]
161 self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
162 tx_if.sw_if_index, 1)
163 self.logger.info("Cross-connect from %s to %s created"
164 % (tx_if.name, rx_if.name))
165 if self.pg_in_xc.count(rx_if) == 0:
166 self.pg_in_xc.append(rx_if)
167 if self.pg_not_in_xc.count(rx_if) == 1:
168 self.pg_not_in_xc.remove(rx_if)
170 def delete_xconnects(self, count, start=0):
172 Delete required number of cross-connects (always two cross-connects per
173 pair of packet-generator interfaces).
175 :param int count: Number of cross-connects to be deleted.
176 :param int start: Starting index of packet-generator interfaces. \
179 for i in range(count):
180 rx_if = self.pg_interfaces[i+start]
181 delta = 1 if i % 2 == 0 else -1
182 tx_if = self.pg_interfaces[i+start+delta]
183 self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
184 tx_if.sw_if_index, 0)
185 self.logger.info("Cross-connect from %s to %s deleted"
186 % (tx_if.name, rx_if.name))
187 if self.pg_not_in_xc.count(rx_if) == 0:
188 self.pg_not_in_xc.append(rx_if)
189 if self.pg_in_xc.count(rx_if) == 1:
190 self.pg_in_xc.remove(rx_if)
192 def create_stream(self, src_if, packet_sizes):
194 Create input packet stream for defined interface using hosts list.
196 :param object src_if: Interface to create packet stream for.
197 :param list packet_sizes: List of required packet sizes.
198 :return: Stream of packets.
201 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
202 for dst_if in self.flows[src_if]:
203 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
204 n_int = len(dst_hosts)
205 for i in range(0, n_int):
206 dst_host = dst_hosts[i]
207 src_host = random.choice(src_hosts)
208 pkt_info = self.create_packet_info(
209 src_if.sw_if_index, dst_if.sw_if_index)
210 payload = self.info_to_payload(pkt_info)
211 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
212 IP(src=src_host.ip4, dst=dst_host.ip4) /
213 UDP(sport=1234, dport=1234) /
215 pkt_info.data = p.copy()
216 size = random.choice(packet_sizes)
217 self.extend_packet(p, size)
219 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
220 % (src_if.name, len(pkts)))
223 def verify_capture(self, pg_if, capture):
225 Verify captured input packet stream for defined interface.
227 :param object pg_if: Interface to verify captured packet stream for.
228 :param list capture: Captured packet stream.
231 for i in self.pg_interfaces:
232 last_info[i.sw_if_index] = None
233 dst_sw_if_index = pg_if.sw_if_index
234 for packet in capture:
235 payload_info = self.payload_to_info(str(packet[Raw]))
239 packet_index = payload_info.index
240 self.assertEqual(payload_info.dst, dst_sw_if_index)
241 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
242 (pg_if.name, payload_info.src, packet_index))
243 next_info = self.get_next_packet_info_for_interface2(
244 payload_info.src, dst_sw_if_index,
245 last_info[payload_info.src])
246 last_info[payload_info.src] = next_info
247 self.assertTrue(next_info is not None)
248 self.assertEqual(packet_index, next_info.index)
249 saved_packet = next_info.data
250 # Check standard fields
251 self.assertEqual(ip.src, saved_packet[IP].src)
252 self.assertEqual(ip.dst, saved_packet[IP].dst)
253 self.assertEqual(udp.sport, saved_packet[UDP].sport)
254 self.assertEqual(udp.dport, saved_packet[UDP].dport)
256 self.logger.error("Unexpected or invalid packet:")
257 self.logger.error(packet.show())
259 for i in self.pg_interfaces:
260 remaining_packet = self.get_next_packet_info_for_interface2(
261 i, dst_sw_if_index, last_info[i.sw_if_index])
263 remaining_packet is None,
264 "Port %u: Packet expected from source %u didn't arrive" %
265 (dst_sw_if_index, i.sw_if_index))
267 def run_verify_test(self):
269 Create packet streams for all configured l2-pg interfaces, send all \
270 prepared packet streams and verify that:
271 - all packets received correctly on all pg-l2 interfaces assigned
273 - no packet received on all pg-l2 interfaces not assigned to
276 :raise RuntimeError: if no packet captured on l2-pg interface assigned
277 to the cross-connect or if any packet is captured
278 on l2-pg interface not assigned to the
282 # Create incoming packet streams for packet-generator interfaces
283 for pg_if in self.pg_interfaces:
284 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
285 pg_if.add_stream(pkts)
287 # Enable packet capture and start packet sending
288 self.pg_enable_capture(self.pg_interfaces)
292 # Verify outgoing packet streams per packet-generator interface
293 for pg_if in self.pg_interfaces:
294 capture = pg_if.get_capture()
295 if pg_if in self.pg_in_xc:
296 if len(capture) == 0:
297 raise RuntimeError("Interface %s is cross-connect sink but "
298 "the capture is empty!" % pg_if.name)
299 self.verify_capture(pg_if, capture)
300 elif pg_if in self.pg_not_in_xc:
302 self.assertEqual(len(capture), 0)
303 except AssertionError:
304 raise RuntimeError("Interface %s is not cross-connect sink "
305 "but the capture is not empty!"
308 self.logger.error("Unknown interface: %s" % pg_if.name)
310 def test_l2xc_inst_01(self):
311 """ L2XC Multi-instance test 1 - create 10 cross-connects
314 # Create 10 cross-connects
315 self.create_xconnects(10)
318 self.run_verify_test()
320 def test_l2xc_inst_02(self):
321 """ L2XC Multi-instance test 2 - delete 4 cross-connects
324 # Delete 4 cross-connects
325 self.delete_xconnects(4)
328 self.run_verify_test()
330 def test_l2xc_inst_03(self):
331 """ L2BD Multi-instance 3 - add new 4 cross-connects
334 # Add new 4 cross-connects
335 self.create_xconnects(4, start=10)
338 self.run_verify_test()
340 def test_l2xc_inst_04(self):
341 """ L2XC Multi-instance test 4 - delete 10 cross-connects
344 # Delete 10 cross-connects
345 self.delete_xconnects(10, start=4)
348 self.run_verify_test()
351 if __name__ == '__main__':
352 unittest.main(testRunner=VppTestRunner)