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]] =\
86 [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))
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(rx_if.sw_if_index,
171 tx_if.sw_if_index, 1)
172 self.logger.info("Cross-connect from %s to %s created"
173 % (tx_if.name, rx_if.name))
174 if self.pg_in_xc.count(rx_if) == 0:
175 self.pg_in_xc.append(rx_if)
176 if self.pg_not_in_xc.count(rx_if) == 1:
177 self.pg_not_in_xc.remove(rx_if)
179 def delete_xconnects(self, count, start=0):
181 Delete required number of cross-connects (always two cross-connects per
182 pair of packet-generator interfaces).
184 :param int count: Number of cross-connects to be deleted.
185 :param int start: Starting index of packet-generator interfaces. \
188 for i in range(count):
189 rx_if = self.pg_interfaces[i + start]
190 delta = 1 if i % 2 == 0 else -1
191 tx_if = self.pg_interfaces[i + start + delta]
192 self.vapi.sw_interface_set_l2_xconnect(rx_if.sw_if_index,
193 tx_if.sw_if_index, 0)
194 self.logger.info("Cross-connect from %s to %s deleted"
195 % (tx_if.name, rx_if.name))
196 if self.pg_not_in_xc.count(rx_if) == 0:
197 self.pg_not_in_xc.append(rx_if)
198 if self.pg_in_xc.count(rx_if) == 1:
199 self.pg_in_xc.remove(rx_if)
201 def create_stream(self, src_if, packet_sizes):
203 Create input packet stream for defined interface using hosts list.
205 :param object src_if: Interface to create packet stream for.
206 :param list packet_sizes: List of required packet sizes.
207 :return: Stream of packets.
210 src_hosts = self.hosts_by_pg_idx[src_if.sw_if_index]
211 for dst_if in self.flows[src_if]:
212 dst_hosts = self.hosts_by_pg_idx[dst_if.sw_if_index]
213 n_int = len(dst_hosts)
214 for i in range(0, n_int):
215 dst_host = dst_hosts[i]
216 src_host = random.choice(src_hosts)
217 pkt_info = self.create_packet_info(src_if, dst_if)
218 payload = self.info_to_payload(pkt_info)
219 p = (Ether(dst=dst_host.mac, src=src_host.mac) /
220 IP(src=src_host.ip4, dst=dst_host.ip4) /
221 UDP(sport=1234, dport=1234) /
223 pkt_info.data = p.copy()
224 size = random.choice(packet_sizes)
225 self.extend_packet(p, size)
227 self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
228 % (src_if.name, len(pkts)))
231 def verify_capture(self, pg_if, capture):
233 Verify captured input packet stream for defined interface.
235 :param object pg_if: Interface to verify captured packet stream for.
236 :param list capture: Captured packet stream.
239 for i in self.pg_interfaces:
240 last_info[i.sw_if_index] = None
241 dst_sw_if_index = pg_if.sw_if_index
242 for packet in capture:
243 payload_info = self.payload_to_info(packet[Raw])
247 packet_index = payload_info.index
248 self.assertEqual(payload_info.dst, dst_sw_if_index)
249 self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
250 (pg_if.name, payload_info.src, packet_index))
251 next_info = self.get_next_packet_info_for_interface2(
252 payload_info.src, dst_sw_if_index,
253 last_info[payload_info.src])
254 last_info[payload_info.src] = next_info
255 self.assertTrue(next_info is not None)
256 self.assertEqual(packet_index, next_info.index)
257 saved_packet = next_info.data
258 # Check standard fields
259 self.assertEqual(ip.src, saved_packet[IP].src)
260 self.assertEqual(ip.dst, saved_packet[IP].dst)
261 self.assertEqual(udp.sport, saved_packet[UDP].sport)
262 self.assertEqual(udp.dport, saved_packet[UDP].dport)
264 self.logger.error(ppp("Unexpected or invalid packet:", packet))
266 for i in self.pg_interfaces:
267 remaining_packet = self.get_next_packet_info_for_interface2(
268 i, dst_sw_if_index, last_info[i.sw_if_index])
270 remaining_packet is None,
271 "Port %u: Packet expected from source %u didn't arrive" %
272 (dst_sw_if_index, i.sw_if_index))
274 def run_verify_test(self):
276 Create packet streams for all configured l2-pg interfaces, send all \
277 prepared packet streams and verify that:
278 - all packets received correctly on all pg-l2 interfaces assigned
280 - no packet received on all pg-l2 interfaces not assigned to
283 :raise RuntimeError: if no packet captured on l2-pg interface assigned
284 to the cross-connect or if any packet is captured
285 on l2-pg interface not assigned to the
289 # Create incoming packet streams for packet-generator interfaces
290 for pg_if in self.pg_interfaces:
291 pkts = self.create_stream(pg_if, self.pg_if_packet_sizes)
292 pg_if.add_stream(pkts)
294 # Enable packet capture and start packet sending
295 self.pg_enable_capture(self.pg_interfaces)
299 # Verify outgoing packet streams per packet-generator interface
300 for pg_if in self.pg_interfaces:
301 if pg_if in self.pg_in_xc:
302 capture = pg_if.get_capture(
303 remark="interface is a cross-connect sink")
304 self.verify_capture(pg_if, capture)
305 elif pg_if in self.pg_not_in_xc:
306 pg_if.assert_nothing_captured(
307 remark="interface is not a cross-connect sink")
309 raise Exception("Unexpected interface: %s" % pg_if.name)
311 def test_l2xc_inst_01(self):
312 """ L2XC Multi-instance test 1 - create 10 cross-connects
315 # Create 10 cross-connects
316 self.create_xconnects(10)
319 self.run_verify_test()
321 def test_l2xc_inst_02(self):
322 """ L2XC Multi-instance test 2 - delete 4 cross-connects
325 # Delete 4 cross-connects
326 self.delete_xconnects(4)
329 self.run_verify_test()
331 def test_l2xc_inst_03(self):
332 """ L2BD Multi-instance 3 - add new 4 cross-connects
335 # Add new 4 cross-connects
336 self.create_xconnects(4, start=10)
339 self.run_verify_test()
341 def test_l2xc_inst_04(self):
342 """ L2XC Multi-instance test 4 - delete 10 cross-connects
345 # Delete 10 cross-connects
346 self.delete_xconnects(10, start=4)
349 self.run_verify_test()
352 if __name__ == '__main__':
353 unittest.main(testRunner=VppTestRunner)