bonding: add support for numa-only in lacp mode
[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 vpp_bond_interface import VppBondInterface
11 from vpp_papi import MACAddress
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     @classmethod
35     def tearDownClass(cls):
36         super(TestBondInterface, cls).tearDownClass()
37
38     def setUp(self):
39         super(TestBondInterface, self).setUp()
40
41     def tearDown(self):
42         super(TestBondInterface, self).tearDown()
43
44     def show_commands_at_teardown(self):
45         self.logger.info(self.vapi.ppcli("show interface"))
46
47     def test_bond_traffic(self):
48         """ Bond traffic test """
49
50         # topology
51         #
52         # RX->              TX->
53         #
54         # pg2 ------+        +------pg0 (slave)
55         #           |        |
56         #          BondEthernet0 (10.10.10.1)
57         #           |        |
58         # pg3 ------+        +------pg1 (slave)
59         #
60
61         # create interface (BondEthernet0)
62         #        self.logger.info("create bond")
63         bond0_mac = "02:fe:38:30:59:3c"
64         mac = MACAddress(bond0_mac).packed
65         bond0 = VppBondInterface(self,
66                                  mode=3,
67                                  lb=1,
68                                  numa_only=0,
69                                  use_custom_mac=1,
70                                  mac_address=mac)
71         bond0.add_vpp_config()
72         bond0.admin_up()
73         bond0_addr = socket.inet_pton(socket.AF_INET, "10.10.10.1")
74         self.vapi.sw_interface_add_del_address(sw_if_index=bond0.sw_if_index,
75                                                address=bond0_addr,
76                                                address_length=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 arp"))
86
87         # enslave pg0 and pg1 to BondEthernet0
88         self.logger.info("bond enslave interface pg0 to BondEthernet0")
89         bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index,
90                                          is_passive=0,
91                                          is_long_timeout=0)
92         self.logger.info("bond enslave interface pg1 to BondEthernet0")
93         bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index,
94                                          is_passive=0,
95                                          is_long_timeout=0)
96
97         # verify both slaves in BondEthernet0
98         if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
99         self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
100         self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
101
102         # generate a packet from pg2 -> BondEthernet0 -> pg1
103         # BondEthernet0 TX hashes this packet to pg1
104         p2 = (Ether(src=bond0_mac, dst=self.pg2.local_mac) /
105               IP(src=self.pg2.local_ip4, dst="10.10.10.12") /
106               UDP(sport=1235, dport=1235) /
107               Raw('\xa5' * 100))
108         self.pg2.add_stream(p2)
109
110         # generate a packet from pg3 -> BondEthernet0 -> pg0
111         # BondEthernet0 TX hashes this packet to pg0
112         # notice the ip address and ports are different than p2 packet
113         p3 = (Ether(src=bond0_mac, dst=self.pg3.local_mac) /
114               IP(src=self.pg3.local_ip4, dst="10.10.10.11") /
115               UDP(sport=1234, dport=1234) /
116               Raw('\xa5' * 100))
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(self.vapi.cli(
124             "set ip arp static BondEthernet0 10.10.10.12 abcd.abcd.0002"))
125         self.logger.info(self.vapi.cli(
126             "set ip arp static BondEthernet0 10.10.10.11 abcd.abcd.0004"))
127
128         # clear the interface counters
129         self.logger.info(self.vapi.cli("clear interfaces"))
130
131         self.pg_start()
132
133         self.logger.info("check the interface counters")
134
135         # verify counters
136
137         # BondEthernet0 tx bytes = 284
138         intfs = self.vapi.cli("show interface BondEthernet0").split("\n")
139         found = 0
140         for intf in intfs:
141             if "tx bytes" in intf and "284" in intf:
142                 found = 1
143         self.assertEqual(found, 1)
144
145         # BondEthernet0 tx bytes = 284
146         intfs = self.vapi.cli("show interface BondEthernet0").split("\n")
147         found = 0
148         for intf in intfs:
149             if "tx bytes" in intf and "284" in intf:
150                 found = 1
151         self.assertEqual(found, 1)
152
153         # pg2 rx bytes = 142
154         intfs = self.vapi.cli("show interface pg2").split("\n")
155         found = 0
156         for intf in intfs:
157             if "rx bytes" in intf and "142" in intf:
158                 found = 1
159         self.assertEqual(found, 1)
160
161         # pg3 rx bytes = 142
162         intfs = self.vapi.cli("show interface pg3").split("\n")
163         found = 0
164         for intf in intfs:
165             if "rx bytes" in intf and "142" in intf:
166                 found = 1
167         self.assertEqual(found, 1)
168
169         bond0.remove_vpp_config()
170
171     def test_bond_enslave(self):
172         """ Bond enslave/detach slave test """
173
174         # create interface (BondEthernet0)
175         self.logger.info("create bond")
176         bond0 = VppBondInterface(self, mode=3)
177         bond0.add_vpp_config()
178         bond0.admin_up()
179
180         # verify pg0 and pg1 not in BondEthernet0
181         if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
182         self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
183         self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
184
185         # enslave pg0 and pg1 to BondEthernet0
186         self.logger.info("bond enslave interface pg0 to BondEthernet0")
187         bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index,
188                                          is_passive=0,
189                                          is_long_timeout=0)
190
191         self.logger.info("bond enslave interface pg1 to BondEthernet0")
192         bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index,
193                                          is_passive=0,
194                                          is_long_timeout=0)
195
196         # verify both slaves in BondEthernet0
197         if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
198         self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
199         self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
200
201         # detach interface pg0
202         self.logger.info("detach interface pg0")
203         bond0.detach_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index)
204
205         # verify pg0 is not in BondEthernet0, but pg1 is
206         if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
207         self.assertFalse(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 pg1
211         self.logger.info("detach interface pg1")
212         bond0.detach_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index)
213
214         # verify pg0 and pg1 not in BondEthernet0
215         if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
216         self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
217         self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
218
219         bond0.remove_vpp_config()
220
221     def test_bond(self):
222         """ Bond add/delete interface test """
223         self.logger.info("Bond add interfaces")
224
225         # create interface 1 (BondEthernet0)
226         bond0 = VppBondInterface(self, mode=5)
227         bond0.add_vpp_config()
228         bond0.admin_up()
229
230         # create interface 2 (BondEthernet1)
231         bond1 = VppBondInterface(self, mode=3)
232         bond1.add_vpp_config()
233         bond1.admin_up()
234
235         # verify both interfaces in the show
236         ifs = self.vapi.cli("show interface")
237         self.assertIn('BondEthernet0', ifs)
238         self.assertIn('BondEthernet1', ifs)
239
240         # verify they are in the dump also
241         if_dump = self.vapi.sw_interface_bond_dump()
242         self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
243         self.assertTrue(bond1.is_interface_config_in_dump(if_dump))
244
245         # delete BondEthernet1
246         self.logger.info("Deleting BondEthernet1")
247         bond1.remove_vpp_config()
248
249         self.logger.info("Verifying BondEthernet1 is deleted")
250
251         ifs = self.vapi.cli("show interface")
252         # verify BondEthernet0 still in the show
253         self.assertIn('BondEthernet0', ifs)
254
255         # verify BondEthernet1 not in the show
256         self.assertNotIn('BondEthernet1', ifs)
257
258         # verify BondEthernet1 is not in the dump
259         if_dump = self.vapi.sw_interface_bond_dump()
260         self.assertFalse(bond1.is_interface_config_in_dump(if_dump))
261
262         # verify BondEthernet0 is still in the dump
263         self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
264
265         # delete BondEthernet0
266         self.logger.info("Deleting BondEthernet0")
267         bond0.remove_vpp_config()
268
269         self.logger.info("Verifying BondEthernet0 is deleted")
270
271         # verify BondEthernet0 not in the show
272         ifs = self.vapi.cli("show interface")
273         self.assertNotIn('BondEthernet0', ifs)
274
275         # verify BondEthernet0 is not in the dump
276         if_dump = self.vapi.sw_interface_bond_dump()
277         self.assertFalse(bond0.is_interface_config_in_dump(if_dump))
278
279 if __name__ == '__main__':
280     unittest.main(testRunner=VppTestRunner)