[ovn] Honor `enable_default_route_ecmp` attribute

Only create multiple default routes when the router has the
`enable_default_route_ecmp` attribute set to True.

Partial-Bug: #2002687
Signed-off-by: Frode Nordahl <frode.nordahl@canonical.com>
Change-Id: I17512ec494661151e46282f0bd08abc55bef1df8
This commit is contained in:
Frode Nordahl 2023-03-24 14:38:16 +01:00
parent 0bc9a71387
commit 4ccc296f56
5 changed files with 108 additions and 2 deletions

View File

@ -40,6 +40,7 @@ from neutron_lib.api.definitions import flavors
from neutron_lib.api.definitions import floating_ip_port_forwarding
from neutron_lib.api.definitions import floatingip_pools
from neutron_lib.api.definitions import l3
from neutron_lib.api.definitions import l3_enable_default_route_ecmp
from neutron_lib.api.definitions import l3_ext_gw_mode
from neutron_lib.api.definitions import l3_ext_gw_multihoming
from neutron_lib.api.definitions import l3_flavors
@ -120,6 +121,7 @@ ML2_SUPPORTED_API_EXTENSIONS_OVN_L3 = [
flavors.ALIAS,
l3_flavors.ALIAS,
l3_ext_gw_multihoming.ALIAS,
l3_enable_default_route_ecmp.ALIAS,
]
ML2_SUPPORTED_API_EXTENSIONS = [
address_group.ALIAS,

View File

@ -1257,6 +1257,8 @@ class OVNClient(object):
def _add_router_ext_gw(self, context, router, networks, txn):
lrouter_name = utils.ovn_name(router['id'])
router_default_route_ecmp_enabled = router.get(
'enable_default_route_ecmp', False)
# 1. Add the external gateway router port.
admin_context = context.elevated()
@ -1267,6 +1269,11 @@ class OVNClient(object):
added_ports.append(port)
# 2. Add default route with nexthop as gateway ip
if (gw_port['id'] != router.get('gw_port_id') and
not router_default_route_ecmp_enabled):
# The `enable_default_route_ecmp` option is not enabled for
# the router, only adding routes for the first gw_port.
continue
for gw_info in self._get_gw_info(admin_context, gw_port):
if gw_info.gateway_ip is None:
continue

View File

@ -42,7 +42,8 @@ class TestRouter(base.TestOVNFunctionalBase):
self.cr_lrp_pb_event = events.WaitForCrLrpPortBindingEvent()
self.sb_api.idl.notify_handler.watch_event(self.cr_lrp_pb_event)
def _create_router(self, name, gw_info=None, az_hints=None):
def _create_router(self, name, gw_info=None, az_hints=None,
enable_ecmp=None):
router = {'router':
{'name': name,
'admin_state_up': True,
@ -51,6 +52,8 @@ class TestRouter(base.TestOVNFunctionalBase):
router['router']['availability_zone_hints'] = az_hints
if gw_info:
router['router']['external_gateway_info'] = gw_info
if enable_ecmp:
router['router']['enable_default_route_ecmp'] = enable_ecmp
return self.l3_plugin.create_router(self.context, router)
def _add_external_gateways(self, router_id, external_gateways):
@ -531,10 +534,36 @@ class TestRouter(base.TestOVNFunctionalBase):
len(lr.ports),
len(gws['router']['external_gateways']))
self.assertEqual(
len(lr.static_routes),
1)
self.l3_plugin.delete_router(self.context, id=router['id'])
self.assertRaises(idlutils.RowNotFound, self.nb_api.lookup,
'Logical_Router', ovn_utils.ovn_name(router['id']))
def test_create_router_multiple_gw_ports_ecmp(self):
ext5 = self._create_ext_network(
'ext5', 'flat', 'physnet5', None, "10.0.50.1", "10.0.50.0/24")
router = self._create_router('router5', enable_ecmp=True)
gws = self._add_external_gateways(
router['id'],
[
{'network_id': ext5['network']['id']},
{'network_id': ext5['network']['id']},
]
)
lr = self.nb_api.lookup('Logical_Router',
ovn_utils.ovn_name(router['id']))
# Check that the expected number of ports are created
self.assertEqual(
len(lr.ports),
len(gws['router']['external_gateways']))
# Check that the expected number of static routes are created
self.assertEqual(
len(lr.static_routes),
len(gws['router']['external_gateways']))
def test_gateway_chassis_rebalance(self):
def _get_result_dict():
sched_info = {}

View File

@ -71,6 +71,7 @@ class TestOVNClient(TestOVNClientBase):
self.ovn_client._get_router_gw_ports = mock.MagicMock()
gw_port = fakes.FakePort().create_one_port(
attrs={
'id': router['gw_port_id'],
'fixed_ips': [{
'subnet_id': subnet.get('id'),
'ip_address': '10.42.0.42'}]
@ -88,6 +89,66 @@ class TestOVNClient(TestOVNClientBase):
'neutron:is_ext_gw': 'true',
'neutron:subnet_id': subnet['id']})
def test__add_router_ext_gw_default_route_ecmp(self):
plugin = mock.MagicMock()
self.get_plugin.return_value = plugin
subnet1 = {
'id': 'fake-subnet-id-1',
'gateway_ip': '10.42.0.1',
'ip_version': const.IP_VERSION_4,
}
subnet2 = {
'id': 'fake-subnet-id-2',
'gateway_ip': '10.42.42.1',
'ip_version': const.IP_VERSION_4,
}
plugin.get_subnet.side_effect = [subnet1, subnet2, subnet1, subnet2]
plugin.get_subnets_by_network.return_value = [subnet1, subnet2]
router = {
'id': 'fake-router-id',
'gw_port_id': 'fake-port-id',
'enable_default_route_ecmp': True,
}
networks = mock.MagicMock()
txn = mock.MagicMock()
self.ovn_client._get_router_gw_ports = mock.MagicMock()
gw_port1 = fakes.FakePort().create_one_port(
attrs={
'id': router['gw_port_id'],
'fixed_ips': [{
'subnet_id': subnet1.get('id'),
'ip_address': '10.42.0.42'}]
})
gw_port2 = fakes.FakePort().create_one_port(
attrs={
'id': 'gw-port-id-2',
'fixed_ips': [{
'subnet_id': subnet2.get('id'),
'ip_address': '10.42.42.42'}]
})
self.ovn_client._get_router_gw_ports.return_value = [
gw_port1, gw_port2]
self.assertEqual(
[self.get_plugin().get_port(), self.get_plugin().get_port()],
self.ovn_client._add_router_ext_gw(mock.Mock(), router,
networks, txn))
self.nb_idl.add_static_route.assert_has_calls([
mock.call('neutron-' + router['id'],
ip_prefix='0.0.0.0/0',
nexthop=subnet1['gateway_ip'],
external_ids={
'neutron:is_ext_gw': 'true',
'neutron:subnet_id': subnet1['id']},
),
mock.call('neutron-' + router['id'],
ip_prefix='0.0.0.0/0',
nexthop=subnet2['gateway_ip'],
external_ids={
'neutron:is_ext_gw': 'true',
'neutron:subnet_id': subnet2['id']},
),
])
def test__add_router_ext_gw_no_default_route(self):
plugin = mock.MagicMock()
self.get_plugin.return_value = plugin
@ -112,6 +173,7 @@ class TestOVNClient(TestOVNClientBase):
self.ovn_client._get_router_gw_ports = mock.MagicMock()
gw_port = fakes.FakePort().create_one_port(
attrs={
'id': router['gw_port_id'],
'fixed_ips': [{
'subnet_id': subnet.get('id'),
'ip_address': '10.42.0.42'}]

View File

@ -835,7 +835,13 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
grps.return_value = self.fake_router_ports
fake_old_ext_gw_port = self.fake_ext_gw_port.copy()
fake_old_ext_gw_port['id'] = 'old-gw-port-id'
self._get_router_gw_ports.return_value = [fake_old_ext_gw_port]
self._get_router_gw_ports.return_value = None
self._get_router_gw_ports.side_effect = [
[fake_old_ext_gw_port],
[fake_old_ext_gw_port],
[self.fake_ext_gw_port],
[self.fake_ext_gw_port],
]
payload = self._create_payload_for_router_update(
self.get_router.return_value, self.fake_router_with_ext_gw)