bond: Add bonding driver and LACP protocol
[vpp.git] / test / test_bond.py
1 #!/usr/bin/env python
2
3 import socket
4 import unittest
5
6 from framework import VppTestCase, VppTestRunner
7 from scapy.packet import Raw
8 from scapy.layers.l2 import Ether
9 from scapy.layers.inet import IP, UDP
10 from util import mactobinary
11 from vpp_bond_interface import VppBondInterface
12
13
14 class TestBondInterface(VppTestCase):
15     """Bond Test Case
16
17     """
18
19     @classmethod
20     def setUpClass(cls):
21         super(TestBondInterface, cls).setUpClass()
22         # Test variables
23         cls.pkts_per_burst = 257    # Number of packets per burst
24         # create 3 pg interfaces
25         cls.create_pg_interfaces(range(4))
26
27         # packet sizes
28         cls.pg_if_packet_sizes = [64, 512, 1518]  # , 9018]
29
30         # setup all interfaces
31         for i in cls.pg_interfaces:
32             i.admin_up()
33
34     def setUp(self):
35         super(TestBondInterface, self).setUp()
36
37     def tearDown(self):
38         super(TestBondInterface, self).tearDown()
39         if not self.vpp_dead:
40             self.logger.info(self.vapi.ppcli("show interface"))
41
42     def test_bond_traffic(self):
43         """ Bond traffic test """
44
45         # topology
46         #
47         # RX->              TX->
48         #
49         # pg2 ------+        +------pg0 (slave)
50         #           |        |
51         #          BondEthernet0 (10.10.10.1)
52         #           |        |
53         # pg3 ------+        +------pg1 (slave)
54         #
55
56         # create interface (BondEthernet0)
57         #        self.logger.info("create bond")
58         bond0_mac = "02:fe:38:30:59:3c"
59         mac = mactobinary(bond0_mac)
60         bond0 = VppBondInterface(self,
61                                  mode=3,
62                                  lb=1,
63                                  use_custom_mac=1,
64                                  mac_address=mac)
65         bond0.add_vpp_config()
66         bond0.admin_up()
67         bond0_addr = socket.inet_pton(socket.AF_INET, "10.10.10.1")
68         self.vapi.sw_interface_add_del_address(bond0.sw_if_index,
69                                                bond0_addr,
70                                                24)
71
72         self.pg2.config_ip4()
73         self.pg2.resolve_arp()
74         self.pg3.config_ip4()
75         self.pg3.resolve_arp()
76
77         self.logger.info(self.vapi.cli("show interface"))
78         self.logger.info(self.vapi.cli("show interface address"))
79         self.logger.info(self.vapi.cli("show ip arp"))
80
81         # enslave pg0 and pg1 to BondEthernet0
82         self.logger.info("bond enslave interface pg0 to BondEthernet0")
83         bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index,
84                                          is_passive=0,
85                                          is_long_timeout=0)
86         self.logger.info("bond enslave interface pg1 to BondEthernet0")
87         bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index,
88                                          is_passive=0,
89                                          is_long_timeout=0)
90
91         # verify both slaves in BondEthernet0
92         if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
93         self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
94         self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
95
96         # generate a packet from pg2 -> BondEthernet0 -> pg1
97         # BondEthernet0 TX hashes this packet to pg1
98         p2 = (Ether(src=bond0_mac, dst=self.pg2.local_mac) /
99               IP(src=self.pg2.local_ip4, dst="10.10.10.12") /
100               UDP(sport=1235, dport=1235) /
101               Raw('\xa5' * 100))
102         self.pg2.add_stream(p2)
103
104         # generate a packet from pg3 -> BondEthernet0 -> pg0
105         # BondEthernet0 TX hashes this packet to pg0
106         # notice the ip address and ports are different than p2 packet
107         p3 = (Ether(src=bond0_mac, dst=self.pg3.local_mac) /
108               IP(src=self.pg3.local_ip4, dst="10.10.10.11") /
109               UDP(sport=1234, dport=1234) /
110               Raw('\xa5' * 100))
111         self.pg3.add_stream(p3)
112
113         self.pg_enable_capture(self.pg_interfaces)
114
115         # set up the static arp entries pointing to the BondEthernet0 interface
116         # so that it does not try to resolve the ip address
117         self.logger.info(self.vapi.cli(
118             "set ip arp static BondEthernet0 10.10.10.12 abcd.abcd.0002"))
119         self.logger.info(self.vapi.cli(
120             "set ip arp static BondEthernet0 10.10.10.11 abcd.abcd.0004"))
121
122         # clear the interface counters
123         self.logger.info(self.vapi.cli("clear interfaces"))
124
125         self.pg_start()
126
127         self.logger.info("check the interface counters")
128
129         # verify counters
130
131         # BondEthernet0 tx bytes = 284
132         intfs = self.vapi.cli("show interface BondEthernet0").split("\n")
133         found = 0
134         for intf in intfs:
135             if "tx bytes" in intf and "284" in intf:
136                 found = 1
137         self.assertEqual(found, 1)
138
139         # pg0 tx bytes = 142
140         intfs = self.vapi.cli("show interface pg0").split("\n")
141         found = 0
142         for intf in intfs:
143             if "tx bytes" in intf and "142" in intf:
144                 found = 1
145         self.assertEqual(found, 1)
146
147         # pg0 tx bytes = 142
148         intfs = self.vapi.cli("show interface pg1").split("\n")
149         found = 0
150         for intf in intfs:
151             if "tx bytes" in intf and "142" in intf:
152                 found = 1
153         self.assertEqual(found, 1)
154
155         # pg2 rx bytes = 142
156         intfs = self.vapi.cli("show interface pg2").split("\n")
157         found = 0
158         for intf in intfs:
159             if "rx bytes" in intf and "142" in intf:
160                 found = 1
161         self.assertEqual(found, 1)
162
163         # pg3 rx bytes = 142
164         intfs = self.vapi.cli("show interface pg3").split("\n")
165         found = 0
166         for intf in intfs:
167             if "rx bytes" in intf and "142" in intf:
168                 found = 1
169         self.assertEqual(found, 1)
170
171         bond0.remove_vpp_config()
172
173     def test_bond_enslave(self):
174         """ Bond enslave/detach slave test """
175
176         # create interface (BondEthernet0)
177         self.logger.info("create bond")
178         bond0 = VppBondInterface(self, mode=3)
179         bond0.add_vpp_config()
180         bond0.admin_up()
181
182         # verify pg0 and pg1 not in BondEthernet0
183         if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
184         self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
185         self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
186
187         # enslave pg0 and pg1 to BondEthernet0
188         self.logger.info("bond enslave interface pg0 to BondEthernet0")
189         bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index,
190                                          is_passive=0,
191                                          is_long_timeout=0)
192
193         self.logger.info("bond enslave interface pg1 to BondEthernet0")
194         bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index,
195                                          is_passive=0,
196                                          is_long_timeout=0)
197
198         # verify both slaves in BondEthernet0
199         if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
200         self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
201         self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
202
203         # detach interface pg0
204         self.logger.info("detach interface pg0")
205         bond0.detach_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index)
206
207         # verify pg0 is not in BondEthernet0, but pg1 is
208         if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
209         self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
210         self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
211
212         # detach interface pg1
213         self.logger.info("detach interface pg1")
214         bond0.detach_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index)
215
216         # verify pg0 and pg1 not in BondEthernet0
217         if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
218         self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
219         self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
220
221         bond0.remove_vpp_config()
222
223     def test_bond(self):
224         """ Bond add/delete interface test """
225         self.logger.info("Bond add interfaces")
226
227         # create interface 1 (BondEthernet0)
228         bond0 = VppBondInterface(self, mode=5)
229         bond0.add_vpp_config()
230         bond0.admin_up()
231
232         # create interface 2 (BondEthernet1)
233         bond1 = VppBondInterface(self, mode=3)
234         bond1.add_vpp_config()
235         bond1.admin_up()
236
237         # verify both interfaces in the show
238         ifs = self.vapi.cli("show interface")
239         self.assertNotEqual(ifs.find('BondEthernet0'), -1)
240         self.assertNotEqual(ifs.find('BondEthernet1'), -1)
241
242         # verify they are in the dump also
243         if_dump = self.vapi.sw_interface_bond_dump()
244         self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
245         self.assertTrue(bond1.is_interface_config_in_dump(if_dump))
246
247         # delete BondEthernet1
248         self.logger.info("Deleting BondEthernet1")
249         bond1.remove_vpp_config()
250
251         self.logger.info("Verifying BondEthernet1 is deleted")
252
253         ifs = self.vapi.cli("show interface")
254         # verify BondEthernet0 still in the show
255         self.assertNotEqual(ifs.find('BondEthernet0'), -1)
256
257         # verify BondEthernet1 not in the show
258         self.assertEqual(ifs.find('BondEthernet1'), -1)
259
260         # verify BondEthernet1 is not in the dump
261         if_dump = self.vapi.sw_interface_bond_dump()
262         self.assertFalse(bond1.is_interface_config_in_dump(if_dump))
263
264         # verify BondEthernet0 is still in the dump
265         self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
266
267         # delete BondEthernet0
268         self.logger.info("Deleting BondEthernet0")
269         bond0.remove_vpp_config()
270
271         self.logger.info("Verifying BondEthernet0 is deleted")
272
273         # verify BondEthernet0 not in the show
274         ifs = self.vapi.cli("show interface")
275         self.assertEqual(ifs.find('BondEthernet0'), -1)
276
277         # verify BondEthernet0 is not in the dump
278         if_dump = self.vapi.sw_interface_bond_dump()
279         self.assertFalse(bond0.is_interface_config_in_dump(if_dump))
280
281 if __name__ == '__main__':
282     unittest.main(testRunner=VppTestRunner)