summaryrefslogtreecommitdiffstats
path: root/test/test_bond.py
diff options
context:
space:
mode:
authorSteven <sluong@cisco.com>2017-12-20 12:43:01 -0800
committerDamjan Marion <dmarion.lists@gmail.com>2018-03-21 21:02:15 +0000
commit9cd2d7a5a4fafadb65d772c48109d55d1e19d425 (patch)
tree4a9e0665be0096ee6bfc2235388f90b276b23814 /test/test_bond.py
parent43ebe29b6ea1107c30311cfb3dbd8190282903d0 (diff)
bond: Add bonding driver and LACP protocol
Add bonding driver to support creation of bond interface which composes of multiple slave interfaces. The slave interfaces could be physical interfaces, or just any virtual interfaces. For example, memif interfaces. The syntax to create a bond interface is create bond mode <lacp | xor | acitve-backup | broadcast | round-robin> To enslave an interface to the bond interface, enslave interface TenGigabitEthernet6/0/0 to BondEthernet0 Please see src/plugins/lacp/lacp_doc.md for more examples and additional options. LACP is a control plane protocol which manages and monitors the status of the slave interfaces. The protocol is part of 802.3ad standard. This patch implements LACPv1. LACPv2 is not supported. To enable LACP on the bond interface, specify "mode lacp" when the bond interface is created. The syntax to enslave a slave interface is the same as other bonding modes. Change-Id: I06581d3b87635972f9f0e1ec50b67560fc13e26c Signed-off-by: Steven <sluong@cisco.com>
Diffstat (limited to 'test/test_bond.py')
-rw-r--r--test/test_bond.py282
1 files changed, 282 insertions, 0 deletions
diff --git a/test/test_bond.py b/test/test_bond.py
new file mode 100644
index 00000000000..b54a1f1deb5
--- /dev/null
+++ b/test/test_bond.py
@@ -0,0 +1,282 @@
+#!/usr/bin/env python
+
+import socket
+import unittest
+
+from framework import VppTestCase, VppTestRunner
+from scapy.packet import Raw
+from scapy.layers.l2 import Ether
+from scapy.layers.inet import IP, UDP
+from util import mactobinary
+from vpp_bond_interface import VppBondInterface
+
+
+class TestBondInterface(VppTestCase):
+ """Bond Test Case
+
+ """
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestBondInterface, cls).setUpClass()
+ # Test variables
+ cls.pkts_per_burst = 257 # Number of packets per burst
+ # create 3 pg interfaces
+ cls.create_pg_interfaces(range(4))
+
+ # packet sizes
+ cls.pg_if_packet_sizes = [64, 512, 1518] # , 9018]
+
+ # setup all interfaces
+ for i in cls.pg_interfaces:
+ i.admin_up()
+
+ def setUp(self):
+ super(TestBondInterface, self).setUp()
+
+ def tearDown(self):
+ super(TestBondInterface, self).tearDown()
+ if not self.vpp_dead:
+ self.logger.info(self.vapi.ppcli("show interface"))
+
+ def test_bond_traffic(self):
+ """ Bond traffic test """
+
+ # topology
+ #
+ # RX-> TX->
+ #
+ # pg2 ------+ +------pg0 (slave)
+ # | |
+ # BondEthernet0 (10.10.10.1)
+ # | |
+ # pg3 ------+ +------pg1 (slave)
+ #
+
+ # create interface (BondEthernet0)
+ # self.logger.info("create bond")
+ bond0_mac = "02:fe:38:30:59:3c"
+ mac = mactobinary(bond0_mac)
+ bond0 = VppBondInterface(self,
+ mode=3,
+ lb=1,
+ use_custom_mac=1,
+ mac_address=mac)
+ bond0.add_vpp_config()
+ bond0.admin_up()
+ bond0_addr = socket.inet_pton(socket.AF_INET, "10.10.10.1")
+ self.vapi.sw_interface_add_del_address(bond0.sw_if_index,
+ bond0_addr,
+ 24)
+
+ self.pg2.config_ip4()
+ self.pg2.resolve_arp()
+ self.pg3.config_ip4()
+ self.pg3.resolve_arp()
+
+ self.logger.info(self.vapi.cli("show interface"))
+ self.logger.info(self.vapi.cli("show interface address"))
+ self.logger.info(self.vapi.cli("show ip arp"))
+
+ # enslave pg0 and pg1 to BondEthernet0
+ self.logger.info("bond enslave interface pg0 to BondEthernet0")
+ bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index,
+ is_passive=0,
+ is_long_timeout=0)
+ self.logger.info("bond enslave interface pg1 to BondEthernet0")
+ bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index,
+ is_passive=0,
+ is_long_timeout=0)
+
+ # verify both slaves in BondEthernet0
+ if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
+ self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
+ self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
+
+ # generate a packet from pg2 -> BondEthernet0 -> pg1
+ # BondEthernet0 TX hashes this packet to pg1
+ p2 = (Ether(src=bond0_mac, dst=self.pg2.local_mac) /
+ IP(src=self.pg2.local_ip4, dst="10.10.10.12") /
+ UDP(sport=1235, dport=1235) /
+ Raw('\xa5' * 100))
+ self.pg2.add_stream(p2)
+
+ # generate a packet from pg3 -> BondEthernet0 -> pg0
+ # BondEthernet0 TX hashes this packet to pg0
+ # notice the ip address and ports are different than p2 packet
+ p3 = (Ether(src=bond0_mac, dst=self.pg3.local_mac) /
+ IP(src=self.pg3.local_ip4, dst="10.10.10.11") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))
+ self.pg3.add_stream(p3)
+
+ self.pg_enable_capture(self.pg_interfaces)
+
+ # set up the static arp entries pointing to the BondEthernet0 interface
+ # so that it does not try to resolve the ip address
+ self.logger.info(self.vapi.cli(
+ "set ip arp static BondEthernet0 10.10.10.12 abcd.abcd.0002"))
+ self.logger.info(self.vapi.cli(
+ "set ip arp static BondEthernet0 10.10.10.11 abcd.abcd.0004"))
+
+ # clear the interface counters
+ self.logger.info(self.vapi.cli("clear interfaces"))
+
+ self.pg_start()
+
+ self.logger.info("check the interface counters")
+
+ # verify counters
+
+ # BondEthernet0 tx bytes = 284
+ intfs = self.vapi.cli("show interface BondEthernet0").split("\n")
+ found = 0
+ for intf in intfs:
+ if "tx bytes" in intf and "284" in intf:
+ found = 1
+ self.assertEqual(found, 1)
+
+ # pg0 tx bytes = 142
+ intfs = self.vapi.cli("show interface pg0").split("\n")
+ found = 0
+ for intf in intfs:
+ if "tx bytes" in intf and "142" in intf:
+ found = 1
+ self.assertEqual(found, 1)
+
+ # pg0 tx bytes = 142
+ intfs = self.vapi.cli("show interface pg1").split("\n")
+ found = 0
+ for intf in intfs:
+ if "tx bytes" in intf and "142" in intf:
+ found = 1
+ self.assertEqual(found, 1)
+
+ # pg2 rx bytes = 142
+ intfs = self.vapi.cli("show interface pg2").split("\n")
+ found = 0
+ for intf in intfs:
+ if "rx bytes" in intf and "142" in intf:
+ found = 1
+ self.assertEqual(found, 1)
+
+ # pg3 rx bytes = 142
+ intfs = self.vapi.cli("show interface pg3").split("\n")
+ found = 0
+ for intf in intfs:
+ if "rx bytes" in intf and "142" in intf:
+ found = 1
+ self.assertEqual(found, 1)
+
+ bond0.remove_vpp_config()
+
+ def test_bond_enslave(self):
+ """ Bond enslave/detach slave test """
+
+ # create interface (BondEthernet0)
+ self.logger.info("create bond")
+ bond0 = VppBondInterface(self, mode=3)
+ bond0.add_vpp_config()
+ bond0.admin_up()
+
+ # verify pg0 and pg1 not in BondEthernet0
+ if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
+ self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
+ self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
+
+ # enslave pg0 and pg1 to BondEthernet0
+ self.logger.info("bond enslave interface pg0 to BondEthernet0")
+ bond0.enslave_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index,
+ is_passive=0,
+ is_long_timeout=0)
+
+ self.logger.info("bond enslave interface pg1 to BondEthernet0")
+ bond0.enslave_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index,
+ is_passive=0,
+ is_long_timeout=0)
+
+ # verify both slaves in BondEthernet0
+ if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
+ self.assertTrue(self.pg0.is_interface_config_in_dump(if_dump))
+ self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
+
+ # detach interface pg0
+ self.logger.info("detach interface pg0")
+ bond0.detach_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index)
+
+ # verify pg0 is not in BondEthernet0, but pg1 is
+ if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
+ self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
+ self.assertTrue(self.pg1.is_interface_config_in_dump(if_dump))
+
+ # detach interface pg1
+ self.logger.info("detach interface pg1")
+ bond0.detach_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index)
+
+ # verify pg0 and pg1 not in BondEthernet0
+ if_dump = self.vapi.sw_interface_slave_dump(bond0.sw_if_index)
+ self.assertFalse(self.pg0.is_interface_config_in_dump(if_dump))
+ self.assertFalse(self.pg1.is_interface_config_in_dump(if_dump))
+
+ bond0.remove_vpp_config()
+
+ def test_bond(self):
+ """ Bond add/delete interface test """
+ self.logger.info("Bond add interfaces")
+
+ # create interface 1 (BondEthernet0)
+ bond0 = VppBondInterface(self, mode=5)
+ bond0.add_vpp_config()
+ bond0.admin_up()
+
+ # create interface 2 (BondEthernet1)
+ bond1 = VppBondInterface(self, mode=3)
+ bond1.add_vpp_config()
+ bond1.admin_up()
+
+ # verify both interfaces in the show
+ ifs = self.vapi.cli("show interface")
+ self.assertNotEqual(ifs.find('BondEthernet0'), -1)
+ self.assertNotEqual(ifs.find('BondEthernet1'), -1)
+
+ # verify they are in the dump also
+ if_dump = self.vapi.sw_interface_bond_dump()
+ self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
+ self.assertTrue(bond1.is_interface_config_in_dump(if_dump))
+
+ # delete BondEthernet1
+ self.logger.info("Deleting BondEthernet1")
+ bond1.remove_vpp_config()
+
+ self.logger.info("Verifying BondEthernet1 is deleted")
+
+ ifs = self.vapi.cli("show interface")
+ # verify BondEthernet0 still in the show
+ self.assertNotEqual(ifs.find('BondEthernet0'), -1)
+
+ # verify BondEthernet1 not in the show
+ self.assertEqual(ifs.find('BondEthernet1'), -1)
+
+ # verify BondEthernet1 is not in the dump
+ if_dump = self.vapi.sw_interface_bond_dump()
+ self.assertFalse(bond1.is_interface_config_in_dump(if_dump))
+
+ # verify BondEthernet0 is still in the dump
+ self.assertTrue(bond0.is_interface_config_in_dump(if_dump))
+
+ # delete BondEthernet0
+ self.logger.info("Deleting BondEthernet0")
+ bond0.remove_vpp_config()
+
+ self.logger.info("Verifying BondEthernet0 is deleted")
+
+ # verify BondEthernet0 not in the show
+ ifs = self.vapi.cli("show interface")
+ self.assertEqual(ifs.find('BondEthernet0'), -1)
+
+ # verify BondEthernet0 is not in the dump
+ if_dump = self.vapi.sw_interface_bond_dump()
+ self.assertFalse(bond0.is_interface_config_in_dump(if_dump))
+
+if __name__ == '__main__':
+ unittest.main(testRunner=VppTestRunner)