Add router default route BFD/ECMP options

Add the `--enable-default-route-bfd`, `--disable-default-route-bfd`
`--enable-default-route-ecmp` and `--disable-default-route-ecmp`
options for `router create` and `router set` commands.

Related-Bug: #2002687
Signed-off-by: Frode Nordahl <frode.nordahl@canonical.com>
Change-Id: Ia5a196daa87d29445dc5514dcb91544f9d470795
This commit is contained in:
Frode Nordahl 2023-10-26 11:50:55 +02:00
parent 16c695045c
commit 7184e876a5
No known key found for this signature in database
GPG Key ID: 6A5D59A3BA48373F
2 changed files with 137 additions and 0 deletions

View File

@ -231,9 +231,72 @@ def _get_attrs(client_manager, parsed_args):
if 'flavor_id' in parsed_args and parsed_args.flavor_id is not None:
attrs['flavor_id'] = parsed_args.flavor_id
for attr in ('enable_default_route_bfd', 'enable_default_route_ecmp'):
value = getattr(parsed_args, attr, None)
if value is not None:
attrs[attr] = value
return attrs
def _parser_add_bfd_ecmp_arguments(parser):
"""Helper to add BFD and ECMP args for CreateRouter and SetRouter."""
parser.add_argument(
'--enable-default-route-bfd',
dest='enable_default_route_bfd',
default=None,
action='store_true',
help=_(
"Enable BFD sessions for default routes inferred from "
"the external gateway port subnets for this router."
),
)
parser.add_argument(
'--disable-default-route-bfd',
dest='enable_default_route_bfd',
default=None,
action='store_false',
help=_(
"Disable BFD sessions for default routes inferred from "
"the external gateway port subnets for this router."
),
)
parser.add_argument(
'--enable-default-route-ecmp',
dest='enable_default_route_ecmp',
default=None,
action='store_true',
help=_(
"Add ECMP default routes if multiple are available via "
"different gateway ports."
),
)
parser.add_argument(
'--disable-default-route-ecmp',
dest='enable_default_route_ecmp',
default=None,
action='store_false',
help=_("Add default route only for first gateway port."),
)
def _command_check_bfd_ecmp_supported(attrs, client):
"""Helper to check for server side support when bfd/ecmp attrs provided.
:raises: exceptions.CommandError
"""
if (
'enable_default_route_bfd' in attrs
or 'enable_default_route_ecmp' in attrs
) and not is_multiple_gateways_supported(client):
msg = _(
'The external-gateway-multihoming extension is not enabled at '
'the Neutron side, cannot use --enable-default-route-bfd or '
'--enable-default-route-ecmp arguments.'
)
raise exceptions.CommandError(msg)
class AddPortToRouter(command.Command):
_description = _("Add a port to a router")
@ -502,6 +565,7 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs):
metavar='<flavor-id>',
help=_("Associate the router to a flavor by ID"),
)
_parser_add_bfd_ecmp_arguments(parser)
return parser
@ -527,6 +591,8 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs):
if parsed_args.enable_ndp_proxy is not None:
attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy
_command_check_bfd_ecmp_supported(attrs, client)
external_gateways = attrs.pop('external_gateways', None)
obj = client.create_router(**attrs)
# tags cannot be set when created, so tags need to be set later.
@ -943,6 +1009,7 @@ class SetRouter(common.NeutronCommandWithExtraArgs):
help=_("Remove QoS policy from router gateway IPs"),
)
_tag.add_tag_option_to_parser_for_set(parser, _('router'))
_parser_add_bfd_ecmp_arguments(parser)
return parser
def take_action(self, parsed_args):
@ -1015,6 +1082,8 @@ class SetRouter(common.NeutronCommandWithExtraArgs):
if parsed_args.enable_ndp_proxy is not None:
attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy
_command_check_bfd_ecmp_supported(attrs, client)
if attrs:
external_gateways = attrs.pop('external_gateways', None)
client.update_router(obj, **attrs)

View File

@ -410,6 +410,74 @@ class TestCreateRouter(TestRouter):
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
def test_create_with_enable_default_route_bfd(self):
self.network_client.find_extension = mock.Mock(
return_value=network_fakes.create_one_extension(
attrs={'name': 'external-gateway-multihoming'}
)
)
arglist = [self.new_router.name, '--enable-default-route-bfd']
verifylist = [
('name', self.new_router.name),
('enable_default_route_bfd', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.network_client.create_router.assert_called_once_with(
name=self.new_router.name,
admin_state_up=True,
enable_default_route_bfd=True,
)
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
def test_create_with_enable_default_route_bfd_no_extension(self):
arglist = [self.new_router.name, '--enable-default-route-bfd']
verifylist = [
('name', self.new_router.name),
('enable_default_route_bfd', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args,
)
def test_create_with_enable_default_route_ecmp(self):
self.network_client.find_extension = mock.Mock(
return_value=network_fakes.create_one_extension(
attrs={'name': 'external-gateway-multihoming'}
)
)
arglist = [self.new_router.name, '--enable-default-route-ecmp']
verifylist = [
('name', self.new_router.name),
('enable_default_route_ecmp', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.network_client.create_router.assert_called_once_with(
name=self.new_router.name,
admin_state_up=True,
enable_default_route_ecmp=True,
)
self.assertEqual(self.columns, columns)
self.assertCountEqual(self.data, data)
def test_create_with_enable_default_route_ecmp_no_extension(self):
arglist = [self.new_router.name, '--enable-default-route-ecmp']
verifylist = [
('name', self.new_router.name),
('enable_default_route_ecmp', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args,
)
class TestDeleteRouter(TestRouter):
# The routers to delete.