docs: Use newer Ubuntu LTS in tutorial
[vpp.git] / test / test_bond.py
1 #!/usr/bin/env python3
2
3 import unittest
4
5 from scapy.packet import Raw
6 from scapy.layers.l2 import Ether
7 from scapy.layers.inet import IP, UDP
8
9 from framework import VppTestCase
10 from asfframework import VppTestRunner
11 from vpp_bond_interface import VppBondInterface
12 from vpp_papi import MACAddress, VppEnum
13
14
15 class TestBondInterface(VppTestCase):
16     """Bond Test Case"""
17
18     @classmethod
19     def setUpClass(cls):
20         super(TestBondInterface, cls).setUpClass()
21         # Test variables
22         cls.pkts_per_burst = 257  # Number of packets per burst
23         # create 3 pg interfaces
24         cls.create_pg_interfaces(range(4))
25
26         # packet sizes
27         cls.pg_if_packet_sizes = [64, 512, 1518]  # , 9018]
28
29         # setup all interfaces
30         for i in cls.pg_interfaces:
31             i.admin_up()
32
33     @classmethod
34     def tearDownClass(cls):
35         super(TestBondInterface, cls).tearDownClass()
36
37     def setUp(self):
38         super(TestBondInterface, self).setUp()
39
40     def tearDown(self):
41         super(TestBondInterface, self).tearDown()
42
43     def show_commands_at_teardown(self):
44         self.logger.info(self.vapi.ppcli("show interface"))
45
46     def test_bond_traffic(self):
47         """Bond traffic test"""
48
49         # topology
50         #
51         # RX->              TX->
52         #
53         # pg2 ------+        +------pg0 (member)
54         #           |        |
55         #          BondEthernet0 (10.10.10.1)
56         #           |        |
57         # pg3 ------+        +------pg1 (memberx)
58         #
59
60         # create interface (BondEthernet0)
61         #        self.logger.info("create bond")
62         bond0_mac = "02:fe:38:30:59:3c"
63         mac = MACAddress(bond0_mac).packed
64         bond0 = VppBondInterface(
65             self,
66             mode=VppEnum.vl_api_bond_mode_t.BOND_API_MODE_XOR,
67             lb=VppEnum.vl_api_bond_lb_algo_t.BOND_API_LB_ALGO_L34,
68             numa_only=0,
69             use_custom_mac=1,
70             mac_address=mac,
71         )
72         bond0.add_vpp_config()
73         bond0.admin_up()
74         self.vapi.sw_interface_add_del_address(
75             sw_if_index=bond0.sw_if_index, prefix="10.10.10.1/24"
76         )
77
78         self.pg2.config_ip4()
79         self.pg2.resolve_arp()
80         self.pg3.config_ip4()
81         self.pg3.resolve_arp()
82
83         self.logger.info(self.vapi.cli("show interface"))
84         self.logger.info(self.vapi.cli("show interface address"))
85         self.logger.info(self.vapi.cli("show ip neighbors"))
86
87         # add member pg0 and pg1 to BondEthernet0
88         self.logger.info("bond add member interface pg0 to BondEthernet0")
89         bond0.add_member_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index)
90         self.logger.info("bond add_member interface pg1 to BondEthernet0")
91         bond0.add_member_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index)
92
93         # verify both members in BondEthernet0
94         if_dump = self.vapi.sw_member_interface_dump(bond0.sw_if_index)
95         self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
96         self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
97
98         # generate a packet from pg2 -> BondEthernet0 -> pg1
99         # BondEthernet0 TX hashes this packet to pg1
100         p2 = (
101             Ether(src=bond0_mac, dst=self.pg2.local_mac)
102             / IP(src=self.pg2.local_ip4, dst="10.10.10.12")
103             / UDP(sport=1235, dport=1235)
104             / Raw(b"\xa5" * 100)
105         )
106         self.pg2.add_stream(p2)
107
108         # generate a packet from pg3 -> BondEthernet0 -> pg0
109         # BondEthernet0 TX hashes this packet to pg0
110         # notice the ip address and ports are different than p2 packet
111         p3 = (
112             Ether(src=bond0_mac, dst=self.pg3.local_mac)
113             / IP(src=self.pg3.local_ip4, dst="10.10.10.11")
114             / UDP(sport=1234, dport=1234)
115             / Raw(b"\xa5" * 100)
116         )
117         self.pg3.add_stream(p3)
118
119         self.pg_enable_capture(self.pg_interfaces)
120
121         # set up the static arp entries pointing to the BondEthernet0 interface
122         # so that it does not try to resolve the ip address
123         self.logger.info(
124             self.vapi.cli(
125                 "set ip neighbor static BondEthernet0 10.10.10.12 abcd.abcd.0002"
126             )
127         )
128         self.logger.info(
129             self.vapi.cli(
130                 "set ip neighbor static BondEthernet0 10.10.10.11 abcd.abcd.0004"
131             )
132         )
133
134         # clear the interface counters
135         self.logger.info(self.vapi.cli("clear interfaces"))
136
137         self.pg_start()
138
139         self.logger.info("check the interface counters")
140
141         # verify counters
142
143         # BondEthernet0 tx bytes = 284
144         intfs = self.vapi.cli("show interface BondEthernet0").split("\n")
145         found = 0
146         for intf in intfs:
147             if "tx bytes" in intf and "284" in intf:
148                 found = 1
149         self.assertEqual(found, 1)
150
151         # BondEthernet0 tx bytes = 284
152         intfs = self.vapi.cli("show interface BondEthernet0").split("\n")
153         found = 0
154         for intf in intfs:
155             if "tx bytes" in intf and "284" in intf:
156                 found = 1
157         self.assertEqual(found, 1)
158
159         # pg2 rx bytes = 142
160         intfs = self.vapi.cli("show interface pg2").split("\n")
161         found = 0
162         for intf in intfs:
163             if "rx bytes" in intf and "142" in intf:
164                 found = 1
165         self.assertEqual(found, 1)
166
167         # pg3 rx bytes = 142
168         intfs = self.vapi.cli("show interface pg3").split("\n")
169         found = 0
170         for intf in intfs:
171             if "rx bytes" in intf and "142" in intf:
172                 found = 1
173         self.assertEqual(found, 1)
174
175         bond0.remove_vpp_config()
176
177     def test_bond_add_member(self):
178         """Bond add_member/detach member test"""
179
180         # create interface (BondEthernet0) and set bond mode to LACP
181         self.logger.info("create bond")
182         bond0 = VppBondInterface(
183             self, mode=VppEnum.vl_api_bond_mode_t.BOND_API_MODE_LACP, enable_gso=0
184         )
185         bond0.add_vpp_config()
186         bond0.admin_up()
187
188         # verify that interfaces can be added as_member and detached two times
189         for i in range(2):
190             # verify pg0 and pg1 not in BondEthernet0
191             if_dump = self.vapi.sw_member_interface_dump(bond0.sw_if_index)
192             self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
193             self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
194
195             # add_member pg0 and pg1 to BondEthernet0
196             self.logger.info("bond add_member interface pg0 to BondEthernet0")
197             bond0.add_member_vpp_bond_interface(
198                 sw_if_index=self.pg0.sw_if_index, is_passive=0, is_long_timeout=0
199             )
200
201             self.logger.info("bond add_member interface pg1 to BondEthernet0")
202             bond0.add_member_vpp_bond_interface(
203                 sw_if_index=self.pg1.sw_if_index, is_passive=0, is_long_timeout=0
204             )
205             # verify both members in BondEthernet0
206             if_dump = self.vapi.sw_member_interface_dump(bond0.sw_if_index)
207             self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
208             self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
209
210             # detach interface pg0
211             self.logger.info("detach interface pg0")
212             bond0.detach_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index)
213
214             # verify pg0 is not in BondEthernet0, but pg1 is
215             if_dump = self.vapi.sw_member_interface_dump(bond0.sw_if_index)
216             self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
217             self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
218
219             # detach interface pg1
220             self.logger.info("detach interface pg1")
221             bond0.detach_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index)
222
223             # verify pg0 and pg1 not in BondEthernet0
224             if_dump = self.vapi.sw_member_interface_dump(bond0.sw_if_index)
225             self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
226             self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
227
228         bond0.remove_vpp_config()
229
230     def test_bond(self):
231         """Bond add/delete interface test"""
232         self.logger.info("Bond add interfaces")
233
234         # create interface 1 (BondEthernet0)
235         bond0 = VppBondInterface(
236             self, mode=VppEnum.vl_api_bond_mode_t.BOND_API_MODE_LACP
237         )
238         bond0.add_vpp_config()
239         bond0.admin_up()
240
241         # create interface 2 (BondEthernet1)
242         bond1 = VppBondInterface(
243             self, mode=VppEnum.vl_api_bond_mode_t.BOND_API_MODE_XOR
244         )
245         bond1.add_vpp_config()
246         bond1.admin_up()
247
248         # verify both interfaces in the show
249         ifs = self.vapi.cli("show interface")
250         self.assertIn("BondEthernet0", ifs)
251         self.assertIn("BondEthernet1", ifs)
252
253         # verify they are in the dump also
254         if_dump = self.vapi.sw_bond_interface_dump(sw_if_index=0xFFFFFFFF)
255         self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
256         self.assertTrue(bond1.is_interface_config_in_dump(if_dump))
257
258         # delete BondEthernet1
259         self.logger.info("Deleting BondEthernet1")
260         bond1.remove_vpp_config()
261
262         self.logger.info("Verifying BondEthernet1 is deleted")
263
264         ifs = self.vapi.cli("show interface")
265         # verify BondEthernet0 still in the show
266         self.assertIn("BondEthernet0", ifs)
267
268         # verify BondEthernet1 not in the show
269         self.assertNotIn("BondEthernet1", ifs)
270
271         # verify BondEthernet1 is not in the dump
272         if_dump = self.vapi.sw_bond_interface_dump(sw_if_index=0xFFFFFFFF)
273         self.assertFalse(bond1.is_interface_config_in_dump(if_dump))
274
275         # verify BondEthernet0 is still in the dump
276         self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
277
278         # delete BondEthernet0
279         self.logger.info("Deleting BondEthernet0")
280         bond0.remove_vpp_config()
281
282         self.logger.info("Verifying BondEthernet0 is deleted")
283
284         # verify BondEthernet0 not in the show
285         ifs = self.vapi.cli("show interface")
286         self.assertNotIn("BondEthernet0", ifs)
287
288         # verify BondEthernet0 is not in the dump
289         if_dump = self.vapi.sw_bond_interface_dump(sw_if_index=bond0.sw_if_index)
290         self.assertFalse(bond0.is_interface_config_in_dump(if_dump))
291
292     def test_bond_link(self):
293         """Bond hw interface link state test"""
294
295         # for convenience
296         bond_modes = VppEnum.vl_api_bond_mode_t
297         intf_flags = VppEnum.vl_api_if_status_flags_t
298
299         # create interface 1 (BondEthernet0)
300         self.logger.info("Create bond interface")
301         # use round-robin mode to avoid negotiation required by LACP
302         bond0 = VppBondInterface(self, mode=bond_modes.BOND_API_MODE_ROUND_ROBIN)
303         bond0.add_vpp_config()
304
305         # set bond admin up.
306         self.logger.info("set interface BondEthernet0 admin up")
307         bond0.admin_up()
308         # confirm link up
309         bond0.assert_interface_state(
310             intf_flags.IF_STATUS_API_FLAG_ADMIN_UP,
311             intf_flags.IF_STATUS_API_FLAG_LINK_UP,
312         )
313
314         # toggle bond admin state
315         self.logger.info("toggle interface BondEthernet0")
316         bond0.admin_down()
317         bond0.admin_up()
318
319         # confirm link is still up
320         bond0.assert_interface_state(
321             intf_flags.IF_STATUS_API_FLAG_ADMIN_UP,
322             intf_flags.IF_STATUS_API_FLAG_LINK_UP,
323         )
324
325         # delete BondEthernet0
326         self.logger.info("Deleting BondEthernet0")
327         bond0.remove_vpp_config()
328
329
330 if __name__ == "__main__":
331     unittest.main(testRunner=VppTestRunner)