[ovn] Add support for enable_default_route_bfd attribute

When ``enable_default_route_bfd`` is set, maintain BFD records
along with default route records.  Default route records will
also be fitted with the `output_port` key, which is a requirement
for the OVN BFD support.

Partial-Bug: #2002687
Signed-off-by: Frode Nordahl <frode.nordahl@canonical.com>
Change-Id: I34e2453ab206c13c3ca40c4181970c320bdd8e67
This commit is contained in:
Frode Nordahl 2023-03-24 17:07:36 +01:00
parent 743bd1ccef
commit cc1ff09b9e
No known key found for this signature in database
GPG Key ID: 6A5D59A3BA48373F
11 changed files with 364 additions and 14 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_bfd
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
@ -122,6 +123,7 @@ ML2_SUPPORTED_API_EXTENSIONS_OVN_L3 = [
flavors.ALIAS,
l3_flavors.ALIAS,
l3_ext_gw_multihoming.ALIAS,
l3_enable_default_route_bfd.ALIAS,
l3_enable_default_route_ecmp.ALIAS,
]
ML2_SUPPORTED_API_EXTENSIONS = [

View File

@ -261,11 +261,13 @@ class API(api.API, metaclass=abc.ABCMeta):
"""
@abc.abstractmethod
def add_static_route(self, lrouter, **columns):
def add_static_route(self, lrouter, maintain_bfd=False, **columns):
"""Add static route to logical router.
:param lrouter: The unique name of the lrouter
:type lrouter: string
:param maintain_bfd: Ensure a BFD record exists for the static route.
:type maintain_bfd: bool
:param columns: Dictionary of static columns
Supported columns: prefix, nexthop, valid
:type columns: dictionary
@ -533,11 +535,13 @@ class API(api.API, metaclass=abc.ABCMeta):
"""
@abc.abstractmethod
def delete_lrouter_ext_gw(self, lrouter_name):
def delete_lrouter_ext_gw(self, lrouter_name, maintain_bfd=True):
"""Delete Logical Router external gateway.
:param lrouter_name: The name of the logical router
:type lrouter_name: string
:param maintain_bfd: Ensure any existing BFD record is removed.
:type maintain_bfd: bool
:returns: :class:`Command` with no result
"""

View File

@ -15,6 +15,7 @@
from oslo_utils import timeutils
from ovsdbapp.backend.ovs_idl import command
from ovsdbapp.backend.ovs_idl import idlutils
from ovsdbapp.schema.ovn_northbound import commands as ovn_nb_commands
from ovsdbapp import utils as ovsdbapp_utils
from neutron._i18n import _
@ -624,9 +625,10 @@ class DelACLCommand(command.BaseCommand):
class AddStaticRouteCommand(command.BaseCommand):
def __init__(self, api, lrouter, **columns):
def __init__(self, api, lrouter, maintain_bfd=False, **columns):
super(AddStaticRouteCommand, self).__init__(api)
self.lrouter = lrouter
self.maintain_bfd = maintain_bfd
self.columns = columns
def run_idl(self, txn):
@ -637,9 +639,29 @@ class AddStaticRouteCommand(command.BaseCommand):
msg = _("Logical Router %s does not exist") % self.lrouter
raise RuntimeError(msg)
bfd_uuid = None
if (self.maintain_bfd and
'nexthop' in self.columns and
'output_port' in self.columns):
cmd = ovn_nb_commands.BFDAddCommand(self.api,
self.columns['output_port'],
self.columns['nexthop'],
may_exist=True)
cmd.run_idl(txn)
try:
bfd_uuid = cmd.result.uuid
except AttributeError:
# When the BFD record is created in the same transaction the
# post commit code that would resolve the real UUID and look up
# the bfd record has not run yet, and consequently the object
# returned by BFDAddCommand() is an UUID object.
bfd_uuid = cmd.result
row = txn.insert(self.api._tables['Logical_Router_Static_Route'])
for col, val in self.columns.items():
setattr(row, col, val)
if bfd_uuid:
setattr(row, 'bfd', bfd_uuid)
_addvalue_to_list(lrouter, 'static_routes', row.uuid)
@ -915,10 +937,11 @@ class CheckRevisionNumberCommand(command.BaseCommand):
class DeleteLRouterExtGwCommand(command.BaseCommand):
def __init__(self, api, lrouter, if_exists):
def __init__(self, api, lrouter, if_exists, maintain_bfd=True):
super(DeleteLRouterExtGwCommand, self).__init__(api)
self.lrouter = lrouter
self.if_exists = if_exists
self.maintain_bfd = maintain_bfd
def run_idl(self, txn):
try:
@ -930,9 +953,20 @@ class DeleteLRouterExtGwCommand(command.BaseCommand):
msg = _("Logical Router %s does not exist") % self.lrouter
raise RuntimeError(msg)
if self.maintain_bfd:
lrp_names = set()
for lrp in getattr(lrouter, 'ports', []):
lrp_names.add(lrp.name)
for route in lrouter.static_routes:
external_ids = getattr(route, 'external_ids', {})
if ovn_const.OVN_ROUTER_IS_EXT_GW in external_ids:
bfd = getattr(route, 'bfd', [])
if bfd and self.maintain_bfd:
for bfd_rec in bfd:
bfd_logical_port = getattr(bfd_rec, 'logical_port', '')
if bfd_logical_port in lrp_names:
route.delvalue('bfd', bfd_rec)
bfd_rec.delete()
lrouter.delvalue('static_routes', route)
route.delete()

View File

@ -464,8 +464,9 @@ class OvsdbNbOvnIdl(nb_impl_idl.OvnNbApiIdlImpl, Backend):
def delete_acl(self, lswitch, lport, if_exists=True):
return cmd.DelACLCommand(self, lswitch, lport, if_exists)
def add_static_route(self, lrouter, **columns):
return cmd.AddStaticRouteCommand(self, lrouter, **columns)
def add_static_route(self, lrouter, maintain_bfd=False, **columns):
return cmd.AddStaticRouteCommand(self, lrouter, maintain_bfd,
**columns)
def delete_static_route(self, lrouter, ip_prefix, nexthop, if_exists=True):
return cmd.DelStaticRouteCommand(self, lrouter, ip_prefix, nexthop,
@ -815,8 +816,10 @@ class OvsdbNbOvnIdl(nb_impl_idl.OvnNbApiIdlImpl, Backend):
return lr
return None
def delete_lrouter_ext_gw(self, lrouter_name, if_exists=True):
return cmd.DeleteLRouterExtGwCommand(self, lrouter_name, if_exists)
def delete_lrouter_ext_gw(self, lrouter_name, if_exists=True,
maintain_bfd=True):
return cmd.DeleteLRouterExtGwCommand(self, lrouter_name, if_exists,
maintain_bfd)
def get_port_group(self, pg_name):
if uuidutils.is_uuid_like(pg_name):

View File

@ -1264,6 +1264,8 @@ class OVNClient(object):
lrouter_name = utils.ovn_name(router['id'])
router_default_route_ecmp_enabled = router.get(
'enable_default_route_ecmp', False)
router_default_route_bfd_enabled = router.get(
'enable_default_route_bfd', False)
# 1. Add the external gateway router port.
admin_context = context.elevated()
@ -1285,9 +1287,16 @@ class OVNClient(object):
columns = {'external_ids': {
ovn_const.OVN_ROUTER_IS_EXT_GW: 'true',
ovn_const.OVN_SUBNET_EXT_ID_KEY: gw_info.subnet_id}}
if router_default_route_bfd_enabled:
columns.update({
'output_port': utils.ovn_lrouter_port_name(
gw_port['id']),
})
txn.add(self._nb_idl.add_static_route(
lrouter_name, ip_prefix=gw_info.ip_prefix,
nexthop=gw_info.gateway_ip, **columns))
nexthop=gw_info.gateway_ip,
maintain_bfd=router_default_route_bfd_enabled,
**columns))
# 3. Add snat rules for tenant networks in lrouter if snat is enabled
if utils.is_snat_enabled(router) and networks:
@ -1328,6 +1337,22 @@ class OVNClient(object):
if snat.external_ip != gw_info.router_ip:
return True
router_default_route_bfd = router.get(
'enable_default_route_bfd',
False
)
for route in ovn_static_routes:
# If gateway in OVN DB has static routes, the ovn_static_route
# parameter contains data from
# `utils.get_lrouter_ext_gw_static_route`, otherwise it will
# contain a Dict ref `update_router` method.
route_bfd = getattr(route, 'bfd', [])
if router_default_route_bfd and not route_bfd:
return True
elif route_bfd and not router_default_route_bfd:
return True
return False
def update_router_routes(self, context, router_id, add, remove,
@ -1528,6 +1553,9 @@ class OVNClient(object):
"""Delete a logical router."""
lrouter_name = utils.ovn_name(router_id)
with self._nb_idl.transaction(check_error=True) as txn:
# This will ensure any BFD records are removed
txn.add(self._nb_idl.delete_lrouter_ext_gw(lrouter_name,
if_exists=True))
txn.add(self._nb_idl.lr_del(lrouter_name, if_exists=True))
db_rev.delete_revision(context, router_id, ovn_const.TYPE_ROUTERS)

View File

@ -21,6 +21,7 @@ from neutron_lib import constants
from oslo_utils import netutils
from oslo_utils import uuidutils
from ovsdbapp.backend.ovs_idl import connection
from ovsdbapp.backend.ovs_idl import idlutils
from ovsdbapp import constants as const
from ovsdbapp import event as ovsdb_event
from ovsdbapp.tests.functional import base
@ -417,6 +418,178 @@ class TestNbApi(BaseOvnIdlTest):
self.assertEqual(r.options[ovn_const.LR_OPTIONS_MAC_AGE_LIMIT],
ovn_conf.get_ovn_mac_binding_age_threshold())
def _add_static_route(self, txn, lr_name, lrp_name, **columns):
r = txn.add(self.nbapi.lr_add(lr_name))
if lrp_name:
txn.add(self.nbapi.add_lrouter_port(lrp_name, lr_name))
columns.update({'output_port': lrp_name})
txn.add(self.nbapi.add_static_route(lr_name, **columns))
return r
def test_add_static_route(self):
name = 'router_with_static_routes'
columns = {
'bfd': [],
'external_ids': {'fake_eid_key': 'fake_eid_value'},
'ip_prefix': '0.0.0.0/0',
'nexthop': '192.0.2.1',
'options': {'fake_option_key': 'fake_option_value'},
'output_port': [],
'policy': ['dst-ip'],
'route_table': '',
}
with self.nbapi.transaction(check_error=True) as txn:
r = self._add_static_route(txn, name, '', **columns)
for route in r.result.static_routes:
for k, v in columns.items():
self.assertEqual(
getattr(route, k),
v)
def _add_static_route_bfd_assert(self, r, lr_name, lrp_name, ip_prefix,
nexthop):
for route in r.result.static_routes:
self.assertEqual(
route.ip_prefix,
ip_prefix)
self.assertEqual(
route.nexthop,
nexthop)
self.assertEqual(
route.output_port[0],
lrp_name)
self.assertEqual(
route.bfd[0].logical_port,
lrp_name)
self.assertEqual(
route.bfd[0].dst_ip,
nexthop)
def test_add_static_route_bfd(self):
lr_name = 'router_with_static_routes_and_bfd'
lrp_name = 'lrp-' + lr_name
ip_prefix = '0.0.0.0/0'
nexthop = '192.0.2.1'
with self.nbapi.transaction(check_error=True) as txn:
r = self._add_static_route(txn, lr_name, lrp_name,
ip_prefix=ip_prefix,
nexthop=nexthop,
maintain_bfd=True)
self._add_static_route_bfd_assert(r, lr_name, lrp_name, ip_prefix,
nexthop)
def test_add_static_route_bfd_record_exists(self):
lr_name = 'router_with_static_routes_and_preexisting_bfd_record'
lrp_name = 'lrp-' + lr_name
ip_prefix = '0.0.0.0/0'
nexthop = '192.0.2.1'
with self.nbapi.transaction(check_error=True) as txn:
bfd = txn.add(self.nbapi.bfd_add(lrp_name, nexthop))
r = self._add_static_route(txn, lr_name, lrp_name,
ip_prefix=ip_prefix,
nexthop=nexthop,
maintain_bfd=True)
for route in r.result.static_routes:
self.assertEqual(
bfd.result,
route.bfd[0],
)
self._add_static_route_bfd_assert(r, lr_name, lrp_name, ip_prefix,
nexthop)
def test_add_static_route_bfd_record_exists_multiple_txn(self):
lr_name = 'router_with_static_routes_and_preexisting_bfd_record_txn'
lrp_name = 'lrp-' + lr_name
ip_prefix = '0.0.0.0/0'
nexthop = '192.0.2.1'
with self.nbapi.transaction(check_error=True) as txn:
bfd = txn.add(self.nbapi.bfd_add(lrp_name, nexthop))
with self.nbapi.transaction(check_error=True) as txn:
r = self._add_static_route(txn, lr_name, lrp_name,
ip_prefix=ip_prefix,
nexthop=nexthop,
maintain_bfd=True)
for route in r.result.static_routes:
self.assertEqual(
bfd.result,
route.bfd[0],
)
self._add_static_route_bfd_assert(r, lr_name, lrp_name, ip_prefix,
nexthop)
def test_delete_lrouter_ext_gw(self):
lr_name = 'router_with_ext_gw'
ip_prefix = '0.0.0.0/0'
nexthop = '192.0.2.1'
external_ids = {ovn_const.OVN_ROUTER_IS_EXT_GW: 'True'}
with self.nbapi.transaction(check_error=True) as txn:
r = self._add_static_route(txn, lr_name, '',
ip_prefix=ip_prefix,
nexthop=nexthop,
external_ids=external_ids)
uuids = []
for route in r.result.static_routes:
lkp = self.nbapi.lookup("Logical_Router_Static_Route", route.uuid)
uuids.append(lkp.uuid)
self.assertTrue(len(uuids))
with self.nbapi.transaction(check_error=True) as txn:
txn.add(self.nbapi.delete_lrouter_ext_gw(lr_name))
for route_uuid in uuids:
self.assertRaises(
idlutils.RowNotFound,
self.nbapi.lookup,
"Logical_Router_Static_Route",
route_uuid)
def test_delete_lrouter_ext_gw_bfd(self):
lr_name = 'router_with_ext_gw_bfd'
lrp_name = 'lrp-' + lr_name
ip_prefix = '0.0.0.0/0'
nexthop = '192.0.2.1'
external_ids = {ovn_const.OVN_ROUTER_IS_EXT_GW: 'True'}
with self.nbapi.transaction(check_error=True) as txn:
r = self._add_static_route(txn, lr_name, lrp_name,
ip_prefix=ip_prefix,
nexthop=nexthop,
external_ids=external_ids,
maintain_bfd=True)
uuids = []
bfd_uuids = []
for route in r.result.static_routes:
lkp = self.nbapi.lookup("Logical_Router_Static_Route", route.uuid)
uuids.append(lkp.uuid)
self.assertTrue(len(lkp.bfd))
for bfd_rec in lkp.bfd:
bfd_uuids.append(bfd_rec.uuid)
self.assertTrue(len(uuids))
self.assertTrue(len(bfd_uuids))
with self.nbapi.transaction(check_error=True) as txn:
txn.add(self.nbapi.delete_lrouter_ext_gw(lr_name))
for route_uuid in uuids:
self.assertRaises(
idlutils.RowNotFound,
self.nbapi.lookup,
"Logical_Router_Static_Route",
route_uuid)
for bfd_uuid in bfd_uuids:
self.assertRaises(
idlutils.RowNotFound,
self.nbapi.lookup,
"BFD",
bfd_uuid)
class TestIgnoreConnectionTimeout(BaseOvnIdlTest):
@classmethod

View File

@ -45,7 +45,7 @@ class TestRouter(base.TestOVNFunctionalBase):
self.sb_api.idl.notify_handler.watch_event(self.cr_lrp_pb_event)
def _create_router(self, name, gw_info=None, az_hints=None,
enable_ecmp=None):
enable_ecmp=None, enable_bfd=None):
router = {'router':
{'name': name,
'admin_state_up': True,
@ -54,6 +54,8 @@ class TestRouter(base.TestOVNFunctionalBase):
router['router']['availability_zone_hints'] = az_hints
if gw_info:
router['router']['external_gateway_info'] = gw_info
if enable_bfd:
router['router']['enable_default_route_bfd'] = enable_bfd
if enable_ecmp:
router['router']['enable_default_route_ecmp'] = enable_ecmp
return self.l3_plugin.create_router(self.context, router)
@ -658,6 +660,101 @@ class TestRouter(base.TestOVNFunctionalBase):
len(lr.static_routes),
len(gws['router']['external_gateways']))
def test_create_delete_router_multiple_gw_ports_ecmp_and_bfd(self):
default_gw = "10.0.60.1"
ext6 = self._create_ext_network(
'ext6', 'flat', 'physnet6', None, default_gw, "10.0.60.0/24")
router = self._create_router('router6', gw_info=None,
enable_bfd=True, enable_ecmp=True)
gws = self._add_external_gateways(
router['id'],
[
{'network_id': ext6['network']['id']},
{'network_id': ext6['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']))
# Check that static_route bfd and output_port attributes is set to the
# expected values
for static_route in lr.static_routes:
self.assertNotEqual(
[],
static_route.bfd)
self.assertNotEqual(
[],
static_route.output_port)
self.assertIn(static_route.output_port[0],
[lrp.name
for lrp in lr.ports])
self.assertIn(static_route.bfd[0].logical_port,
[lrp.name
for lrp in lr.ports])
self.assertEqual(static_route.bfd[0].logical_port,
static_route.output_port[0])
router_ips = set()
for ext_gws in gws['router']['external_gateways']:
for ext_fip in ext_gws['external_fixed_ips']:
router_ips.add(ext_fip['ip_address'])
lrps = set()
for lrp in lr.ports:
for network in lrp.networks:
self.assertIn(
network.split('/')[0],
router_ips)
lrps.add(lrp.name)
bfd_rows = self.nb_api.bfd_find(
lrp.name, default_gw).execute(check_error=True)
if not bfd_rows:
raise AssertionError('None of the expected BFD rows found.')
for bfd_row in bfd_rows:
self.assertEqual(
bfd_row.logical_port,
lrp.name)
self.assertEqual(
bfd_row.dst_ip,
default_gw)
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']))
for lrp_name in lrps:
if self.nb_api.bfd_find(
lrp.name, default_gw).execute(check_error=True):
raise AssertionError('Unexpectedly found BFD rows.')
def test_update_router_single_gw_bfd(self):
ext1 = self._create_ext_network(
'ext7', 'flat', 'physnet1', None, "10.0.70.1", "10.0.70.0/24")
gw_info = {'network_id': ext1['network']['id']}
router = self._create_router('router7', gw_info=gw_info)
self.assertFalse(router['enable_default_route_bfd'])
lr = self.nb_api.lr_get(ovn_utils.ovn_name(router['id'])).execute()
for route in ovn_utils.get_lrouter_ext_gw_static_route(lr):
self.assertEqual(
[],
route.bfd)
router = self.l3_plugin.update_router(
self.context, router['id'],
{'router': {'enable_default_route_bfd': True}})
self.assertTrue(router['enable_default_route_bfd'])
lr = self.nb_api.lr_get(ovn_utils.ovn_name(router['id'])).execute()
for route in ovn_utils.get_lrouter_ext_gw_static_route(lr):
self.assertNotEqual(
[],
route.bfd)
def test_gateway_chassis_rebalance(self):
ovn_client = self.l3_plugin._ovn_client
chassis4 = self.add_fake_chassis(

View File

@ -797,7 +797,8 @@ class FakeOVNPort(object):
FakeStaticRoute = collections.namedtuple(
'Static_Routes', ['ip_prefix', 'nexthop', 'external_ids'])
'Static_Routes', ['ip_prefix', 'nexthop', 'external_ids', 'bfd'],
defaults=([],))
class FakeOVNRouterPort(object):

View File

@ -85,6 +85,7 @@ class TestOVNClient(TestOVNClientBase):
'neutron-' + router['id'],
ip_prefix='0.0.0.0/0',
nexthop='10.42.0.1',
maintain_bfd=False,
external_ids={
'neutron:is_ext_gw': 'true',
'neutron:subnet_id': subnet['id']})
@ -136,6 +137,7 @@ class TestOVNClient(TestOVNClientBase):
mock.call('neutron-' + router['id'],
ip_prefix='0.0.0.0/0',
nexthop=subnet1['gateway_ip'],
maintain_bfd=False,
external_ids={
'neutron:is_ext_gw': 'true',
'neutron:subnet_id': subnet1['id']},
@ -143,6 +145,7 @@ class TestOVNClient(TestOVNClientBase):
mock.call('neutron-' + router['id'],
ip_prefix='0.0.0.0/0',
nexthop=subnet2['gateway_ip'],
maintain_bfd=False,
external_ids={
'neutron:is_ext_gw': 'true',
'neutron:subnet_id': subnet2['id']},

View File

@ -627,6 +627,7 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
expected_calls = [
mock.call('neutron-router-id', ip_prefix='0.0.0.0/0',
nexthop='192.168.1.254',
maintain_bfd=False,
external_ids={
ovn_const.OVN_ROUTER_IS_EXT_GW: 'true',
ovn_const.OVN_SUBNET_EXT_ID_KEY: 'ext-subnet-id'})]
@ -798,6 +799,7 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
lsp_address=ovn_const.DEFAULT_ADDR_FOR_LSP_WITH_PEER)
self.l3_inst._nb_ovn.add_static_route.assert_called_once_with(
'neutron-router-id', ip_prefix='0.0.0.0/0',
maintain_bfd=False,
external_ids={ovn_const.OVN_ROUTER_IS_EXT_GW: 'true',
ovn_const.OVN_SUBNET_EXT_ID_KEY: 'ext-subnet-id'},
nexthop='192.168.1.254')
@ -868,6 +870,7 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
self.l3_inst._nb_ovn.add_static_route.assert_called_once_with(
'neutron-router-id', ip_prefix='0.0.0.0/0',
nexthop='192.168.1.254',
maintain_bfd=False,
external_ids={ovn_const.OVN_ROUTER_IS_EXT_GW: 'true',
ovn_const.OVN_SUBNET_EXT_ID_KEY: 'ext-subnet-id'})
self.l3_inst._nb_ovn.add_nat_rule_in_lrouter.assert_called_once_with(
@ -920,6 +923,7 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
self.l3_inst._nb_ovn.add_static_route.assert_called_once_with(
'neutron-router-id', ip_prefix='0.0.0.0/0',
nexthop='192.168.1.254',
maintain_bfd=False,
external_ids={ovn_const.OVN_ROUTER_IS_EXT_GW: 'true',
ovn_const.OVN_SUBNET_EXT_ID_KEY: 'ext-subnet-id'})
self.l3_inst._nb_ovn.add_nat_rule_in_lrouter.assert_called_once_with(
@ -971,6 +975,7 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase):
# Need not check lsp and lrp here, it has been tested in other cases
self.l3_inst._nb_ovn.add_static_route.assert_called_once_with(
'neutron-router-id', ip_prefix='0.0.0.0/0',
maintain_bfd=False,
external_ids={ovn_const.OVN_ROUTER_IS_EXT_GW: 'true',
ovn_const.OVN_SUBNET_EXT_ID_KEY: 'ext-subnet-id'},
nexthop='192.168.1.254')
@ -2131,8 +2136,8 @@ class OVNL3ExtrarouteTests(test_l3_gw.ExtGwModeIntTestCase,
ovn_const.OVN_SUBNET_EXT_ID_KEY: mock.ANY}
add_static_route_calls = [
mock.call(mock.ANY, ip_prefix='0.0.0.0/0', nexthop='10.0.0.1',
external_ids=expected_ext_ids),
maintain_bfd=False, external_ids=expected_ext_ids),
mock.call(mock.ANY, ip_prefix='::/0', nexthop='2001:db8::',
external_ids=expected_ext_ids)]
maintain_bfd=False, external_ids=expected_ext_ids)]
self.l3_inst._nb_ovn.add_static_route.assert_has_calls(
add_static_route_calls, any_order=True)

View File

@ -46,7 +46,7 @@ osprofiler>=2.3.0 # Apache-2.0
os-ken>=2.2.0 # Apache-2.0
os-resource-classes>=1.1.0 # Apache-2.0
ovs>=2.10.0 # Apache-2.0
ovsdbapp>=2.2.1 # Apache-2.0
ovsdbapp>=2.3.0 # Apache-2.0
psutil>=5.3.0 # BSD
pyroute2>=0.7.3;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2)
pyOpenSSL>=17.1.0 # Apache-2.0