vpp/test/test_bond.py
Steven 9cd2d7a5a4 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>
2018-03-21 21:02:15 +00:00

283 lines
10 KiB
Python

#!/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)