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