From 23f41d789c54bd8d9b87db8cf43d0235c5699da4 Mon Sep 17 00:00:00 2001 From: Dave Wallace Date: Mon, 4 Jan 2021 18:55:12 -0500 Subject: tests: move bond tests to src/vnet/bonding/test - Refactor make test code to be co-located with the vpp feature source code. Type: test Signed-off-by: Dave Wallace Change-Id: I056717261553f6449f5fcd3611b6ae3895a00ba6 --- src/vnet/bonding/test/test_bond.py | 321 ++++++++++++++++++++++++++++ src/vnet/bonding/test/vpp_bond_interface.py | 52 +++++ 2 files changed, 373 insertions(+) create mode 100644 src/vnet/bonding/test/test_bond.py create mode 100644 src/vnet/bonding/test/vpp_bond_interface.py (limited to 'src') diff --git a/src/vnet/bonding/test/test_bond.py b/src/vnet/bonding/test/test_bond.py new file mode 100644 index 00000000000..5df86ae5b0f --- /dev/null +++ b/src/vnet/bonding/test/test_bond.py @@ -0,0 +1,321 @@ +#!/usr/bin/env python3 + +import socket +import unittest + +from scapy.packet import Raw +from scapy.layers.l2 import Ether +from scapy.layers.inet import IP, UDP + +from framework import VppTestCase, VppTestRunner +from vpp_bond_interface import VppBondInterface +from vpp_papi import MACAddress, VppEnum + + +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() + + @classmethod + def tearDownClass(cls): + super(TestBondInterface, cls).tearDownClass() + + def setUp(self): + super(TestBondInterface, self).setUp() + + def tearDown(self): + super(TestBondInterface, self).tearDown() + + def show_commands_at_teardown(self): + self.logger.info(self.vapi.ppcli("show interface")) + + def test_bond_traffic(self): + """ Bond traffic test """ + + # topology + # + # RX-> TX-> + # + # pg2 ------+ +------pg0 (member) + # | | + # BondEthernet0 (10.10.10.1) + # | | + # pg3 ------+ +------pg1 (memberx) + # + + # create interface (BondEthernet0) + # self.logger.info("create bond") + bond0_mac = "02:fe:38:30:59:3c" + mac = MACAddress(bond0_mac).packed + bond0 = VppBondInterface( + self, + mode=VppEnum.vl_api_bond_mode_t.BOND_API_MODE_XOR, + lb=VppEnum.vl_api_bond_lb_algo_t.BOND_API_LB_ALGO_L34, + numa_only=0, + use_custom_mac=1, + mac_address=mac) + bond0.add_vpp_config() + bond0.admin_up() + self.vapi.sw_interface_add_del_address( + sw_if_index=bond0.sw_if_index, + prefix="10.10.10.1/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 neighbors")) + + # add member pg0 and pg1 to BondEthernet0 + self.logger.info("bond add member interface pg0 to BondEthernet0") + bond0.add_member_vpp_bond_interface(sw_if_index=self.pg0.sw_if_index) + self.logger.info("bond add_member interface pg1 to BondEthernet0") + bond0.add_member_vpp_bond_interface(sw_if_index=self.pg1.sw_if_index) + + # verify both members in BondEthernet0 + if_dump = self.vapi.sw_member_interface_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(b'\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(b'\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 neighbor static BondEthernet0 10.10.10.12 abcd.abcd.0002")) + self.logger.info(self.vapi.cli( + "set ip neighbor 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) + + # 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) + + # 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_add_member(self): + """ Bond add_member/detach member test """ + + # create interface (BondEthernet0) and set bond mode to LACP + self.logger.info("create bond") + bond0 = VppBondInterface( + self, + mode=VppEnum.vl_api_bond_mode_t.BOND_API_MODE_LACP, + enable_gso=0) + bond0.add_vpp_config() + bond0.admin_up() + + # verify that interfaces can be added as_member and detached two times + for i in range(2): + # verify pg0 and pg1 not in BondEthernet0 + if_dump = self.vapi.sw_member_interface_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)) + + # add_member pg0 and pg1 to BondEthernet0 + self.logger.info("bond add_member interface pg0 to BondEthernet0") + bond0.add_member_vpp_bond_interface( + sw_if_index=self.pg0.sw_if_index, + is_passive=0, + is_long_timeout=0) + + self.logger.info("bond add_member interface pg1 to BondEthernet0") + bond0.add_member_vpp_bond_interface( + sw_if_index=self.pg1.sw_if_index, + is_passive=0, + is_long_timeout=0) + # verify both members in BondEthernet0 + if_dump = self.vapi.sw_member_interface_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_member_interface_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_member_interface_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=VppEnum.vl_api_bond_mode_t.BOND_API_MODE_LACP) + bond0.add_vpp_config() + bond0.admin_up() + + # create interface 2 (BondEthernet1) + bond1 = VppBondInterface( + self, mode=VppEnum.vl_api_bond_mode_t.BOND_API_MODE_XOR) + bond1.add_vpp_config() + bond1.admin_up() + + # verify both interfaces in the show + ifs = self.vapi.cli("show interface") + self.assertIn('BondEthernet0', ifs) + self.assertIn('BondEthernet1', ifs) + + # verify they are in the dump also + if_dump = self.vapi.sw_bond_interface_dump(sw_if_index=0xFFFFFFFF) + 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.assertIn('BondEthernet0', ifs) + + # verify BondEthernet1 not in the show + self.assertNotIn('BondEthernet1', ifs) + + # verify BondEthernet1 is not in the dump + if_dump = self.vapi.sw_bond_interface_dump(sw_if_index=0xFFFFFFFF) + 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.assertNotIn('BondEthernet0', ifs) + + # verify BondEthernet0 is not in the dump + if_dump = self.vapi.sw_bond_interface_dump( + sw_if_index=bond0.sw_if_index) + self.assertFalse(bond0.is_interface_config_in_dump(if_dump)) + + def test_bond_link(self): + """ Bond hw interface link state test """ + + # for convenience + bond_modes = VppEnum.vl_api_bond_mode_t + intf_flags = VppEnum.vl_api_if_status_flags_t + + # create interface 1 (BondEthernet0) + self.logger.info("Create bond interface") + # use round-robin mode to avoid negotiation required by LACP + bond0 = VppBondInterface(self, + mode=bond_modes.BOND_API_MODE_ROUND_ROBIN) + bond0.add_vpp_config() + + # set bond admin up. + self.logger.info("set interface BondEthernet0 admin up") + bond0.admin_up() + # confirm link up + bond0.assert_interface_state(intf_flags.IF_STATUS_API_FLAG_ADMIN_UP, + intf_flags.IF_STATUS_API_FLAG_LINK_UP) + + # toggle bond admin state + self.logger.info("toggle interface BondEthernet0") + bond0.admin_down() + bond0.admin_up() + + # confirm link is still up + bond0.assert_interface_state(intf_flags.IF_STATUS_API_FLAG_ADMIN_UP, + intf_flags.IF_STATUS_API_FLAG_LINK_UP) + + # delete BondEthernet0 + self.logger.info("Deleting BondEthernet0") + bond0.remove_vpp_config() + + +if __name__ == '__main__': + unittest.main(testRunner=VppTestRunner) diff --git a/src/vnet/bonding/test/vpp_bond_interface.py b/src/vnet/bonding/test/vpp_bond_interface.py new file mode 100644 index 00000000000..60c1ac1557b --- /dev/null +++ b/src/vnet/bonding/test/vpp_bond_interface.py @@ -0,0 +1,52 @@ +from vpp_object import VppObject +from vpp_interface import VppInterface + + +class VppBondInterface(VppInterface): + """VPP bond interface.""" + + def __init__(self, test, mode, lb=0, numa_only=0, enable_gso=0, + use_custom_mac=0, mac_address='', id=0xFFFFFFFF): + + """ Create VPP Bond interface """ + super(VppBondInterface, self).__init__(test) + self.mode = mode + self.lb = lb + self.numa_only = numa_only + self.enable_gso = enable_gso + self.use_custom_mac = use_custom_mac + self.mac_address = mac_address + self.id = id + + def add_vpp_config(self): + r = self.test.vapi.bond_create2(self.mode, + self.lb, + self.numa_only, + self.enable_gso, + self.use_custom_mac, + self.mac_address, + self.id) + self.set_sw_if_index(r.sw_if_index) + + def remove_vpp_config(self): + self.test.vapi.bond_delete(self.sw_if_index) + + def add_member_vpp_bond_interface(self, + sw_if_index, + is_passive=0, + is_long_timeout=0): + self.test.vapi.bond_add_member(sw_if_index, + self.sw_if_index, + is_passive, + is_long_timeout) + + def detach_vpp_bond_interface(self, + sw_if_index): + self.test.vapi.bond_detach_member(sw_if_index) + + def is_interface_config_in_dump(self, dump): + for i in dump: + if i.sw_if_index == self.sw_if_index: + return True + else: + return False -- cgit 1.2.3-korg