Merge "Implement extension: extraroute-atomic"
This commit is contained in:
commit
10c5f451ce
@ -151,6 +151,88 @@ class ExtraRoute_dbonly_mixin(l3_db.L3_NAT_dbonly_mixin):
|
||||
raise xroute_exc.RouterInterfaceInUseByRoute(
|
||||
router_id=router_id, subnet_id=subnet_id)
|
||||
|
||||
@staticmethod
|
||||
def _add_extra_routes(old, add):
|
||||
"""Add two lists of extra routes.
|
||||
|
||||
Exact duplicates (both destination and nexthop) in old and add are
|
||||
merged into one item.
|
||||
Same destinations with different nexthops are accepted and all of
|
||||
them are returned.
|
||||
Overlapping destinations are accepted and all of them are returned.
|
||||
"""
|
||||
routes_dict = {} # its values are sets of nexthops
|
||||
for r in old + add:
|
||||
dst = r['destination']
|
||||
nexthop = r['nexthop']
|
||||
if dst not in routes_dict:
|
||||
routes_dict[dst] = set()
|
||||
routes_dict[dst].add(nexthop)
|
||||
routes_list = []
|
||||
for dst, nexthops in routes_dict.items():
|
||||
for nexthop in nexthops:
|
||||
routes_list.append({'destination': dst, 'nexthop': nexthop})
|
||||
return routes_list
|
||||
|
||||
@staticmethod
|
||||
def _remove_extra_routes(old, remove):
|
||||
"""Remove the 2nd list of extra routes from the first.
|
||||
|
||||
Since we care about the end state if an extra route to be removed
|
||||
is already missing from old, that's not an error, but accepted.
|
||||
"""
|
||||
routes_dict = {} # its values are sets of nexthops
|
||||
for r in old:
|
||||
dst = r['destination']
|
||||
nexthop = r['nexthop']
|
||||
if dst not in routes_dict:
|
||||
routes_dict[dst] = set()
|
||||
routes_dict[dst].add(nexthop)
|
||||
for r in remove:
|
||||
dst = r['destination']
|
||||
nexthop = r['nexthop']
|
||||
if dst in routes_dict:
|
||||
routes_dict[dst].discard(nexthop)
|
||||
routes_list = []
|
||||
for dst, nexthops in routes_dict.items():
|
||||
for nexthop in nexthops:
|
||||
routes_list.append({'destination': dst, 'nexthop': nexthop})
|
||||
return routes_list
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def add_extraroutes(self, context, router_id, body=None):
|
||||
# NOTE(bence romsics): The input validation is delayed until
|
||||
# update_router() validates the whole set of routes. Until then
|
||||
# do not trust 'routes'.
|
||||
routes = body['router']['routes']
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
old_routes = self._get_extra_routes_by_router_id(
|
||||
context, router_id)
|
||||
router = self.update_router(
|
||||
context,
|
||||
router_id,
|
||||
{'router':
|
||||
{'routes':
|
||||
self._add_extra_routes(old_routes, routes)}})
|
||||
return {'router': router}
|
||||
|
||||
@db_api.retry_if_session_inactive()
|
||||
def remove_extraroutes(self, context, router_id, body=None):
|
||||
# NOTE(bence romsics): The input validation is delayed until
|
||||
# update_router() validates the whole set of routes. Until then
|
||||
# do not trust 'routes'.
|
||||
routes = body['router']['routes']
|
||||
with db_api.CONTEXT_WRITER.using(context):
|
||||
old_routes = self._get_extra_routes_by_router_id(
|
||||
context, router_id)
|
||||
router = self.update_router(
|
||||
context,
|
||||
router_id,
|
||||
{'router':
|
||||
{'routes':
|
||||
self._remove_extra_routes(old_routes, routes)}})
|
||||
return {'router': router}
|
||||
|
||||
|
||||
class ExtraRoute_db_mixin(ExtraRoute_dbonly_mixin, l3_db.L3_NAT_db_mixin):
|
||||
"""Mixin class to support extra route configuration on router with rpc."""
|
||||
|
@ -16,6 +16,7 @@
|
||||
from neutron_lib.agent import topics
|
||||
from neutron_lib.api.definitions import dvr
|
||||
from neutron_lib.api.definitions import extraroute
|
||||
from neutron_lib.api.definitions import extraroute_atomic
|
||||
from neutron_lib.api.definitions import fip_port_details
|
||||
from neutron_lib.api.definitions import floatingip_pools
|
||||
from neutron_lib.api.definitions import l3 as l3_apidef
|
||||
@ -94,6 +95,7 @@ class L3RouterPlugin(service_base.ServicePluginBase,
|
||||
_supported_extension_aliases = [dvr.ALIAS, l3_apidef.ALIAS,
|
||||
l3_ext_gw_mode.ALIAS,
|
||||
extraroute.ALIAS,
|
||||
extraroute_atomic.ALIAS,
|
||||
n_const.L3_AGENT_SCHEDULER_EXT_ALIAS,
|
||||
l3_ext_ha_mode.ALIAS,
|
||||
router_availability_zone.ALIAS,
|
||||
|
@ -73,3 +73,85 @@ class TestExtraRouteDb(testlib_api.SqlTestCase):
|
||||
self.assertItemsEqual(updated_router['routes'], routes)
|
||||
got_router = self._plugin.get_router(ctx, router_id)
|
||||
self.assertItemsEqual(got_router['routes'], routes)
|
||||
|
||||
def assertEqualRoutes(self, a, b):
|
||||
"""Compare a list of routes without caring for the list order."""
|
||||
return self.assertSetEqual(
|
||||
set(frozenset(r.items()) for r in a),
|
||||
set(frozenset(r.items()) for r in b))
|
||||
|
||||
def test_add_extra_routes(self):
|
||||
self.assertEqual(
|
||||
[],
|
||||
self._plugin._add_extra_routes([], []),
|
||||
)
|
||||
|
||||
old = [{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"}]
|
||||
add = []
|
||||
self.assertEqual(old, self._plugin._add_extra_routes(old, add))
|
||||
|
||||
old = []
|
||||
add = [{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"}]
|
||||
self.assertEqual(add, self._plugin._add_extra_routes(old, add))
|
||||
|
||||
old = [{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"}]
|
||||
add = [{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"}]
|
||||
self.assertEqual(old, self._plugin._add_extra_routes(old, add))
|
||||
|
||||
old = [{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"}]
|
||||
add = [{"destination": "10.0.10.0/24", "nexthop": "10.0.0.11"}]
|
||||
self.assertEqualRoutes(
|
||||
old + add, self._plugin._add_extra_routes(old, add))
|
||||
|
||||
def test_remove_extra_routes(self):
|
||||
old = []
|
||||
remove = []
|
||||
self.assertEqual(old, self._plugin._remove_extra_routes(old, remove))
|
||||
|
||||
old = [{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"}]
|
||||
remove = []
|
||||
self.assertEqual(old, self._plugin._remove_extra_routes(old, remove))
|
||||
|
||||
old = []
|
||||
remove = [{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"}]
|
||||
self.assertEqual(old, self._plugin._remove_extra_routes(old, remove))
|
||||
|
||||
old = [{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"}]
|
||||
remove = [{"destination": "10.0.10.0/24", "nexthop": "10.0.0.11"}]
|
||||
self.assertEqual(old, self._plugin._remove_extra_routes(old, remove))
|
||||
|
||||
old = [{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"}]
|
||||
remove = old
|
||||
self.assertEqual([], self._plugin._remove_extra_routes(old, remove))
|
||||
|
||||
old = [
|
||||
{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"},
|
||||
{"destination": "10.0.11.0/24", "nexthop": "10.0.0.11"},
|
||||
]
|
||||
remove = old[1:]
|
||||
self.assertEqual(
|
||||
old[:1], self._plugin._remove_extra_routes(old, remove))
|
||||
|
||||
old = [
|
||||
{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"},
|
||||
{"destination": "10.0.10.0/24", "nexthop": "10.0.0.11"},
|
||||
]
|
||||
remove = old[1:]
|
||||
self.assertEqual(
|
||||
old[:1], self._plugin._remove_extra_routes(old, remove))
|
||||
|
||||
old = []
|
||||
remove = [
|
||||
{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"},
|
||||
{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"},
|
||||
]
|
||||
self.assertEqual([], self._plugin._remove_extra_routes(old, remove))
|
||||
|
||||
old = [
|
||||
{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"},
|
||||
]
|
||||
remove = [
|
||||
{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"},
|
||||
{"destination": "10.0.10.0/24", "nexthop": "10.0.0.10"},
|
||||
]
|
||||
self.assertEqual([], self._plugin._remove_extra_routes(old, remove))
|
||||
|
Loading…
x
Reference in New Issue
Block a user