diff options
author | Neale Ranns <nranns@cisco.com> | 2019-11-08 12:42:31 +0000 |
---|---|---|
committer | Ole Trøan <otroan@employees.org> | 2019-11-26 09:15:11 +0000 |
commit | 9db6ada779794779158163f6293b479ae7f6ad5e (patch) | |
tree | 53dd65975958b2d87b1cd312e612b45d7225e531 /test | |
parent | 0d3d4824a46205e776ff32c82be4cb514f459a5f (diff) |
fib: Table Replace
Type: feature
from the API doc, a table replace is:
"
The use-case is that, for some unspecified reason, the control plane
has a very different set of entries it wants in the table than VPP
currently has. The CP would thus like to 'replace' VPP's current table
only by specifying what the new set of entries shall be, i.e. it is not
going to delete anything that already eixts.
the CP delcartes the start of this procedure with this begin_replace
API Call, and when it has populated all the entries it wants, it calls
the below end_replace API. From this point on it is of coursce free
to add and delete entries as usual.
The underlying mechanism by which VPP implements this replace is
purposefully left unspecified.
"
In the FIB, the algorithm is implemented using mark and sweep.
Algorithm goes:
1) replace_begin: this marks all the entries in that table as 'stale'
2) download all the entries that should be in this table
- this clears the stale flag on those entries
3) signal the table converged: ip_table_replace_end
- this removes all entries that are still stale
this procedure can be used when an agent first connects to VPP,
as an alternative to dump and diff state reconciliation.
Change-Id: I168edec10cf7670866076b129ebfe6149ea8222e
Signed-off-by: Neale Ranns <nranns@cisco.com>
Diffstat (limited to 'test')
-rw-r--r-- | test/test_ip4.py | 132 | ||||
-rw-r--r-- | test/test_ip4_vrf_multi_instance.py | 7 | ||||
-rw-r--r-- | test/test_ip6.py | 145 | ||||
-rw-r--r-- | test/test_ip6_vrf_multi_instance.py | 9 | ||||
-rw-r--r-- | test/vpp_ip.py | 17 | ||||
-rw-r--r-- | test/vpp_ip_route.py | 50 | ||||
-rw-r--r-- | test/vpp_papi_provider.py | 25 |
7 files changed, 337 insertions, 48 deletions
diff --git a/test/test_ip4.py b/test/test_ip4.py index 5c268a8be2c..7300679ff0d 100644 --- a/test/test_ip4.py +++ b/test/test_ip4.py @@ -16,7 +16,7 @@ from util import ppp from vpp_ip_route import VppIpRoute, VppRoutePath, VppIpMRoute, \ VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \ VppMplsTable, VppIpTable, FibPathType, find_route, \ - VppIpInterfaceAddress + VppIpInterfaceAddress, find_route_in_dump, find_mroute_in_dump from vpp_sub_interface import VppSubInterface, VppDot1QSubint, VppDot1ADSubint from vpp_papi import VppEnum from vpp_neighbor import VppNeighbor @@ -1961,5 +1961,135 @@ class TestIPv4Frag(VppTestCase): self.assert_equal(payload, saved_payload, "payload") +class TestIPReplace(VppTestCase): + """ IPv4 Table Replace """ + + @classmethod + def setUpClass(cls): + super(TestIPReplace, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestIPReplace, cls).tearDownClass() + + def setUp(self): + super(TestIPReplace, self).setUp() + + self.create_pg_interfaces(range(4)) + + table_id = 1 + self.tables = [] + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + i.generate_remote_hosts(2) + self.tables.append(VppIpTable(self, table_id).add_vpp_config()) + table_id += 1 + + def tearDown(self): + super(TestIPReplace, self).tearDown() + for i in self.pg_interfaces: + i.admin_down() + i.unconfig_ip4() + + def test_replace(self): + """ IP Table Replace """ + + N_ROUTES = 20 + links = [self.pg0, self.pg1, self.pg2, self.pg3] + routes = [[], [], [], []] + + # load up the tables with some routes + for ii, t in enumerate(self.tables): + for jj in range(N_ROUTES): + uni = VppIpRoute( + self, "10.0.0.%d" % jj, 32, + [VppRoutePath(links[ii].remote_hosts[0].ip4, + links[ii].sw_if_index), + VppRoutePath(links[ii].remote_hosts[1].ip4, + links[ii].sw_if_index)], + table_id=t.table_id).add_vpp_config() + multi = VppIpMRoute( + self, "0.0.0.0", + "239.0.0.%d" % jj, 32, + MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, + [VppMRoutePath(self.pg0.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT), + VppMRoutePath(self.pg1.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD), + VppMRoutePath(self.pg2.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD), + VppMRoutePath(self.pg3.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD)], + table_id=t.table_id).add_vpp_config() + routes[ii].append({'uni': uni, + 'multi': multi}) + + # + # replace the tables a few times + # + for kk in range(3): + # replace_begin each table + for t in self.tables: + t.replace_begin() + + # all the routes are still there + for ii, t in enumerate(self.tables): + dump = t.dump() + mdump = t.mdump() + for r in routes[ii]: + self.assertTrue(find_route_in_dump(dump, r['uni'], t)) + self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t)) + + # redownload the even numbered routes + for ii, t in enumerate(self.tables): + for jj in range(0, N_ROUTES, 2): + routes[ii][jj]['uni'].add_vpp_config() + routes[ii][jj]['multi'].add_vpp_config() + + # signal each table replace_end + for t in self.tables: + t.replace_end() + + # we should find the even routes, but not the odd + for ii, t in enumerate(self.tables): + dump = t.dump() + mdump = t.mdump() + for jj in range(0, N_ROUTES, 2): + self.assertTrue(find_route_in_dump( + dump, routes[ii][jj]['uni'], t)) + self.assertTrue(find_mroute_in_dump( + mdump, routes[ii][jj]['multi'], t)) + for jj in range(1, N_ROUTES - 1, 2): + self.assertFalse(find_route_in_dump( + dump, routes[ii][jj]['uni'], t)) + self.assertFalse(find_mroute_in_dump( + mdump, routes[ii][jj]['multi'], t)) + + # reload all the routes + for ii, t in enumerate(self.tables): + for r in routes[ii]: + r['uni'].add_vpp_config() + r['multi'].add_vpp_config() + + # all the routes are still there + for ii, t in enumerate(self.tables): + dump = t.dump() + mdump = t.mdump() + for r in routes[ii]: + self.assertTrue(find_route_in_dump(dump, r['uni'], t)) + self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t)) + + # + # finally flush the tables for good measure + # + for t in self.tables: + t.flush() + self.assertEqual(len(t.dump()), 5) + self.assertEqual(len(t.mdump()), 1) + + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/test_ip4_vrf_multi_instance.py b/test/test_ip4_vrf_multi_instance.py index f1ad6703604..fba00eb2ddc 100644 --- a/test/test_ip4_vrf_multi_instance.py +++ b/test/test_ip4_vrf_multi_instance.py @@ -184,7 +184,7 @@ class TestIp4VrfMultiInst(VppTestCase): pg_if = self.pg_if_by_vrf_id[vrf_id][0] dest_addr = pg_if.local_ip4n dest_addr_len = 24 - self.vapi.ip_table_add_del(is_add=1, table_id=vrf_id) + self.vapi.ip_table_add_del(is_add=1, table={'table_id': vrf_id}) self.logger.info("IPv4 VRF ID %d created" % vrf_id) if vrf_id not in self.vrf_list: self.vrf_list.append(vrf_id) @@ -210,8 +210,7 @@ class TestIp4VrfMultiInst(VppTestCase): :param int vrf_id: The FIB table / VRF ID to be reset. """ - # self.vapi.reset_vrf(vrf_id, is_ipv6=0) - self.vapi.reset_fib(vrf_id, is_ipv6=0) + self.vapi.ip_table_flush(table={'table_id': vrf_id}) if vrf_id in self.vrf_list: self.vrf_list.remove(vrf_id) if vrf_id not in self.vrf_reset_list: @@ -226,7 +225,7 @@ class TestIp4VrfMultiInst(VppTestCase): self.logger.info("IPv4 VRF ID %d reset finished" % vrf_id) self.logger.debug(self.vapi.ppcli("show ip fib")) self.logger.debug(self.vapi.ppcli("show ip arp")) - self.vapi.ip_table_add_del(is_add=0, table_id=vrf_id) + self.vapi.ip_table_add_del(is_add=0, table={'table_id': vrf_id}) def create_stream(self, src_if, packet_sizes): """ diff --git a/test/test_ip6.py b/test/test_ip6.py index 36532cedf12..c6cced9c138 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -23,8 +23,8 @@ from util import ppp, ip6_normalize, mk_ll_addr from vpp_ip import DpoProto from vpp_ip_route import VppIpRoute, VppRoutePath, find_route, VppIpMRoute, \ VppMRoutePath, MRouteItfFlags, MRouteEntryFlags, VppMplsIpBind, \ - VppMplsRoute, VppMplsTable, VppIpTable, FibPathType, \ - VppIpInterfaceAddress + VppMplsRoute, VppMplsTable, VppIpTable, FibPathType, FibPathProto, \ + VppIpInterfaceAddress, find_route_in_dump, find_mroute_in_dump from vpp_neighbor import find_nbr, VppNeighbor from vpp_pg_interface import is_ipv6_misc from vpp_sub_interface import VppSubInterface, VppDot1QSubint @@ -2370,5 +2370,146 @@ class TestIP6Input(VppTestCase): self.pg_enable_capture(self.pg_interfaces) self.pg_start() + +class TestIPReplace(VppTestCase): + """ IPv6 Table Replace """ + + @classmethod + def setUpClass(cls): + super(TestIPReplace, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(TestIPReplace, cls).tearDownClass() + + def setUp(self): + super(TestIPReplace, self).setUp() + + self.create_pg_interfaces(range(4)) + + table_id = 1 + self.tables = [] + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip6() + i.resolve_arp() + i.generate_remote_hosts(2) + self.tables.append(VppIpTable(self, table_id, + True).add_vpp_config()) + table_id += 1 + + def tearDown(self): + super(TestIPReplace, self).tearDown() + for i in self.pg_interfaces: + i.admin_down() + i.unconfig_ip4() + + def test_replace(self): + """ IP Table Replace """ + + N_ROUTES = 20 + links = [self.pg0, self.pg1, self.pg2, self.pg3] + routes = [[], [], [], []] + + # the sizes of 'empty' tables + for t in self.tables: + self.assertEqual(len(t.dump()), 2) + self.assertEqual(len(t.mdump()), 5) + + # load up the tables with some routes + for ii, t in enumerate(self.tables): + for jj in range(1, N_ROUTES): + uni = VppIpRoute( + self, "2001::%d" % jj if jj != 0 else "2001::", 128, + [VppRoutePath(links[ii].remote_hosts[0].ip6, + links[ii].sw_if_index), + VppRoutePath(links[ii].remote_hosts[1].ip6, + links[ii].sw_if_index)], + table_id=t.table_id).add_vpp_config() + multi = VppIpMRoute( + self, "::", + "ff:2001::%d" % jj, 128, + MRouteEntryFlags.MFIB_ENTRY_FLAG_NONE, + [VppMRoutePath(self.pg0.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_ACCEPT, + proto=FibPathProto.FIB_PATH_NH_PROTO_IP6), + VppMRoutePath(self.pg1.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=FibPathProto.FIB_PATH_NH_PROTO_IP6), + VppMRoutePath(self.pg2.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=FibPathProto.FIB_PATH_NH_PROTO_IP6), + VppMRoutePath(self.pg3.sw_if_index, + MRouteItfFlags.MFIB_ITF_FLAG_FORWARD, + proto=FibPathProto.FIB_PATH_NH_PROTO_IP6)], + table_id=t.table_id).add_vpp_config() + routes[ii].append({'uni': uni, + 'multi': multi}) + + # + # replace the tables a few times + # + for kk in range(3): + # replace each table + for t in self.tables: + t.replace_begin() + + # all the routes are still there + for ii, t in enumerate(self.tables): + dump = t.dump() + mdump = t.mdump() + for r in routes[ii]: + self.assertTrue(find_route_in_dump(dump, r['uni'], t)) + self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t)) + + # redownload the even numbered routes + for ii, t in enumerate(self.tables): + for jj in range(0, N_ROUTES, 2): + routes[ii][jj]['uni'].add_vpp_config() + routes[ii][jj]['multi'].add_vpp_config() + + # signal each table converged + for t in self.tables: + t.replace_end() + + # we should find the even routes, but not the odd + for ii, t in enumerate(self.tables): + dump = t.dump() + mdump = t.mdump() + for jj in range(0, N_ROUTES, 2): + self.assertTrue(find_route_in_dump( + dump, routes[ii][jj]['uni'], t)) + self.assertTrue(find_mroute_in_dump( + mdump, routes[ii][jj]['multi'], t)) + for jj in range(1, N_ROUTES - 1, 2): + self.assertFalse(find_route_in_dump( + dump, routes[ii][jj]['uni'], t)) + self.assertFalse(find_mroute_in_dump( + mdump, routes[ii][jj]['multi'], t)) + + # reload all the routes + for ii, t in enumerate(self.tables): + for r in routes[ii]: + r['uni'].add_vpp_config() + r['multi'].add_vpp_config() + + # all the routes are still there + for ii, t in enumerate(self.tables): + dump = t.dump() + mdump = t.mdump() + for r in routes[ii]: + self.assertTrue(find_route_in_dump(dump, r['uni'], t)) + self.assertTrue(find_mroute_in_dump(mdump, r['multi'], t)) + + # + # finally flush the tables for good measure + # + for t in self.tables: + t.flush() + self.assertEqual(len(t.dump()), 2) + self.assertEqual(len(t.mdump()), 5) + + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/test_ip6_vrf_multi_instance.py b/test/test_ip6_vrf_multi_instance.py index 31e00dc8e6a..c23e3015351 100644 --- a/test/test_ip6_vrf_multi_instance.py +++ b/test/test_ip6_vrf_multi_instance.py @@ -195,7 +195,8 @@ class TestIP6VrfMultiInst(VppTestCase): pg_if = self.pg_if_by_vrf_id[vrf_id][0] dest_addr = pg_if.local_ip6n dest_addr_len = 64 - self.vapi.ip_table_add_del(is_ipv6=1, is_add=1, table_id=vrf_id) + self.vapi.ip_table_add_del(is_add=1, + table={'table_id': vrf_id, 'is_ip6': 1}) self.logger.info("IPv6 VRF ID %d created" % vrf_id) if vrf_id not in self.vrf_list: self.vrf_list.append(vrf_id) @@ -222,8 +223,7 @@ class TestIP6VrfMultiInst(VppTestCase): :param int vrf_id: The FIB table / VRF ID to be reset. """ - # self.vapi.reset_vrf(vrf_id, is_ipv6=1) - self.vapi.reset_fib(vrf_id, is_ipv6=1) + self.vapi.ip_table_flush(table={'table_id': vrf_id, 'is_ip6': 1}) if vrf_id in self.vrf_list: self.vrf_list.remove(vrf_id) if vrf_id not in self.vrf_reset_list: @@ -238,7 +238,8 @@ class TestIP6VrfMultiInst(VppTestCase): self.logger.info("IPv6 VRF ID %d reset finished" % vrf_id) self.logger.debug(self.vapi.ppcli("show ip6 fib")) self.logger.debug(self.vapi.ppcli("show ip6 neighbors")) - self.vapi.ip_table_add_del(is_ipv6=1, is_add=0, table_id=vrf_id) + self.vapi.ip_table_add_del(is_add=0, + table={'table_id': vrf_id, 'is_ip6': 1}) def create_stream(self, src_if, packet_sizes): """ diff --git a/test/vpp_ip.py b/test/vpp_ip.py index 8c3bbba307a..43af5e09c07 100644 --- a/test/vpp_ip.py +++ b/test/vpp_ip.py @@ -67,14 +67,16 @@ class VppIpAddressUnion(): elif hasattr(other, "ip4") and hasattr(other, "ip6"): # vl_api_address_union_t if 4 == self.version: - return self.ip_addr.packed == other.ip4 + return self.ip_addr == other.ip4 else: - return self.ip_addr.packed == other.ip6 + return self.ip_addr == other.ip6 else: - _log.error("Comparing VppIpAddressUnions:%s" - " with incomparable type: %s", - self, other) - return NotImplemented + raise Exception("Comparing VppIpAddressUnions:%s" + " with incomparable type: %s", + self, other) + + def __ne__(self, other): + return not (self == other) def __str__(self): return str(self.ip_addr) @@ -143,7 +145,6 @@ class VppIpMPrefix(): return (self.glen == other.grp_address_length and self.gaddr == str(other.grp_address.ip6) and self.saddr == str(other.src_address.ip6)) - else: - raise Exception("Comparing VppIpPrefix:%s with unknown type: %s" % + raise Exception("Comparing VppIpMPrefix:%s with unknown type: %s" % (self, other)) return False diff --git a/test/vpp_ip_route.py b/test/vpp_ip_route.py index d6004756d34..031412cc2fa 100644 --- a/test/vpp_ip_route.py +++ b/test/vpp_ip_route.py @@ -108,6 +108,23 @@ def find_route(test, addr, len, table_id=0): return False +def find_route_in_dump(dump, route, table): + for r in dump: + if table.table_id == r.route.table_id \ + and route.prefix == r.route.prefix: + if len(route.paths) == r.route.n_paths: + return True + return False + + +def find_mroute_in_dump(dump, route, table): + for r in dump: + if table.table_id == r.route.table_id \ + and route.prefix == r.route.prefix: + return True + return False + + def find_mroute(test, grp_addr, src_addr, grp_addr_len, table_id=0): ip_mprefix = VppIpMPrefix(text_type(src_addr), @@ -168,13 +185,36 @@ class VppIpTable(VppObject): self.is_ip6 = is_ip6 def add_vpp_config(self): - self._test.vapi.ip_table_add_del(is_ipv6=self.is_ip6, is_add=1, - table_id=self.table_id) + self._test.vapi.ip_table_add_del(is_add=1, + table={'is_ip6': self.is_ip6, + 'table_id': self.table_id}) self._test.registry.register(self, self._test.logger) + return self def remove_vpp_config(self): - self._test.vapi.ip_table_add_del(is_ipv6=self.is_ip6, is_add=0, - table_id=self.table_id) + self._test.vapi.ip_table_add_del(is_add=0, + table={'is_ip6': self.is_ip6, + 'table_id': self.table_id}) + + def replace_begin(self): + self._test.vapi.ip_table_replace_begin( + table={'is_ip6': self.is_ip6, + 'table_id': self.table_id}) + + def replace_end(self): + self._test.vapi.ip_table_replace_end( + table={'is_ip6': self.is_ip6, + 'table_id': self.table_id}) + + def flush(self): + self._test.vapi.ip_table_flush(table={'is_ip6': self.is_ip6, + 'table_id': self.table_id}) + + def dump(self): + return self._test.vapi.ip_route_dump(self.table_id, self.is_ip6) + + def mdump(self): + return self._test.vapi.ip_mroute_dump(self.table_id, self.is_ip6) def query_vpp_config(self): if self.table_id == 0: @@ -468,6 +508,7 @@ class VppIpRoute(VppObject): self.stats_index = r.stats_index if self.register: self._test.registry.register(self, self._test.logger) + return self def remove_vpp_config(self): # there's no need to issue different deletes for modified routes @@ -540,6 +581,7 @@ class VppIpMRoute(VppObject): is_add=1) self.stats_index = r.stats_index self._test.registry.register(self, self._test.logger) + return self def remove_vpp_config(self): self._test.vapi.ip_mroute_add_del(self.table_id, diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index a440312d892..692d0071d28 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -65,7 +65,6 @@ defaultmapping = { 'ip_punt_police': {'is_add': 1, }, 'ip_punt_redirect': {'is_add': 1, }, 'ip_route_add_del': {'is_add': 1, }, - 'ip_table_add_del': {'is_add': 1, }, 'ip_unnumbered_dump': {'sw_if_index': 4294967295, }, 'ipsec_interface_add_del_spd': {'is_add': 1, }, 'ipsec_sad_entry_add_del': {'is_add': 1, }, @@ -460,30 +459,6 @@ class VppPapiProvider(object): return self.api(self.papi.create_loopback, {'mac_address': mac}) - def ip_table_add_del(self, - table_id, - is_add=1, - is_ipv6=0): - """ - - :param table_id - :param is_add: (Default value = 1) - :param is_ipv6: (Default value = 0) - - """ - - return self.api( - self.papi.ip_table_add_del, - {'table': - { - 'table_id': table_id, - 'is_ip6': is_ipv6 - }, - 'is_add': is_add}) - - def ip_table_dump(self): - return self.api(self.papi.ip_table_dump, {}) - def ip_route_dump(self, table_id, is_ip6=False): return self.api(self.papi.ip_route_dump, {'table': { |