Skip to content

Commit 51f173c

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "Add router default route BFD/ECMP options"
2 parents 7997466 + 7184e87 commit 51f173c

2 files changed

Lines changed: 137 additions & 0 deletions

File tree

openstackclient/network/v2/router.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,72 @@ def _get_attrs(client_manager, parsed_args):
231231
if 'flavor_id' in parsed_args and parsed_args.flavor_id is not None:
232232
attrs['flavor_id'] = parsed_args.flavor_id
233233

234+
for attr in ('enable_default_route_bfd', 'enable_default_route_ecmp'):
235+
value = getattr(parsed_args, attr, None)
236+
if value is not None:
237+
attrs[attr] = value
238+
234239
return attrs
235240

236241

242+
def _parser_add_bfd_ecmp_arguments(parser):
243+
"""Helper to add BFD and ECMP args for CreateRouter and SetRouter."""
244+
parser.add_argument(
245+
'--enable-default-route-bfd',
246+
dest='enable_default_route_bfd',
247+
default=None,
248+
action='store_true',
249+
help=_(
250+
"Enable BFD sessions for default routes inferred from "
251+
"the external gateway port subnets for this router."
252+
),
253+
)
254+
parser.add_argument(
255+
'--disable-default-route-bfd',
256+
dest='enable_default_route_bfd',
257+
default=None,
258+
action='store_false',
259+
help=_(
260+
"Disable BFD sessions for default routes inferred from "
261+
"the external gateway port subnets for this router."
262+
),
263+
)
264+
parser.add_argument(
265+
'--enable-default-route-ecmp',
266+
dest='enable_default_route_ecmp',
267+
default=None,
268+
action='store_true',
269+
help=_(
270+
"Add ECMP default routes if multiple are available via "
271+
"different gateway ports."
272+
),
273+
)
274+
parser.add_argument(
275+
'--disable-default-route-ecmp',
276+
dest='enable_default_route_ecmp',
277+
default=None,
278+
action='store_false',
279+
help=_("Add default route only for first gateway port."),
280+
)
281+
282+
283+
def _command_check_bfd_ecmp_supported(attrs, client):
284+
"""Helper to check for server side support when bfd/ecmp attrs provided.
285+
286+
:raises: exceptions.CommandError
287+
"""
288+
if (
289+
'enable_default_route_bfd' in attrs
290+
or 'enable_default_route_ecmp' in attrs
291+
) and not is_multiple_gateways_supported(client):
292+
msg = _(
293+
'The external-gateway-multihoming extension is not enabled at '
294+
'the Neutron side, cannot use --enable-default-route-bfd or '
295+
'--enable-default-route-ecmp arguments.'
296+
)
297+
raise exceptions.CommandError(msg)
298+
299+
237300
class AddPortToRouter(command.Command):
238301
_description = _("Add a port to a router")
239302

@@ -502,6 +565,7 @@ def get_parser(self, prog_name):
502565
metavar='<flavor-id>',
503566
help=_("Associate the router to a flavor by ID"),
504567
)
568+
_parser_add_bfd_ecmp_arguments(parser)
505569

506570
return parser
507571

@@ -527,6 +591,8 @@ def take_action(self, parsed_args):
527591
if parsed_args.enable_ndp_proxy is not None:
528592
attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy
529593

594+
_command_check_bfd_ecmp_supported(attrs, client)
595+
530596
external_gateways = attrs.pop('external_gateways', None)
531597
obj = client.create_router(**attrs)
532598
# tags cannot be set when created, so tags need to be set later.
@@ -943,6 +1009,7 @@ def get_parser(self, prog_name):
9431009
help=_("Remove QoS policy from router gateway IPs"),
9441010
)
9451011
_tag.add_tag_option_to_parser_for_set(parser, _('router'))
1012+
_parser_add_bfd_ecmp_arguments(parser)
9461013
return parser
9471014

9481015
def take_action(self, parsed_args):
@@ -1015,6 +1082,8 @@ def take_action(self, parsed_args):
10151082
if parsed_args.enable_ndp_proxy is not None:
10161083
attrs['enable_ndp_proxy'] = parsed_args.enable_ndp_proxy
10171084

1085+
_command_check_bfd_ecmp_supported(attrs, client)
1086+
10181087
if attrs:
10191088
external_gateways = attrs.pop('external_gateways', None)
10201089
client.update_router(obj, **attrs)

openstackclient/tests/unit/network/v2/test_router.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,74 @@ def test_create_with_flavor_id(self):
410410
self.assertEqual(self.columns, columns)
411411
self.assertCountEqual(self.data, data)
412412

413+
def test_create_with_enable_default_route_bfd(self):
414+
self.network_client.find_extension = mock.Mock(
415+
return_value=network_fakes.create_one_extension(
416+
attrs={'name': 'external-gateway-multihoming'}
417+
)
418+
)
419+
arglist = [self.new_router.name, '--enable-default-route-bfd']
420+
verifylist = [
421+
('name', self.new_router.name),
422+
('enable_default_route_bfd', True),
423+
]
424+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
425+
columns, data = self.cmd.take_action(parsed_args)
426+
self.network_client.create_router.assert_called_once_with(
427+
name=self.new_router.name,
428+
admin_state_up=True,
429+
enable_default_route_bfd=True,
430+
)
431+
self.assertEqual(self.columns, columns)
432+
self.assertCountEqual(self.data, data)
433+
434+
def test_create_with_enable_default_route_bfd_no_extension(self):
435+
arglist = [self.new_router.name, '--enable-default-route-bfd']
436+
verifylist = [
437+
('name', self.new_router.name),
438+
('enable_default_route_bfd', True),
439+
]
440+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
441+
self.assertRaises(
442+
exceptions.CommandError,
443+
self.cmd.take_action,
444+
parsed_args,
445+
)
446+
447+
def test_create_with_enable_default_route_ecmp(self):
448+
self.network_client.find_extension = mock.Mock(
449+
return_value=network_fakes.create_one_extension(
450+
attrs={'name': 'external-gateway-multihoming'}
451+
)
452+
)
453+
arglist = [self.new_router.name, '--enable-default-route-ecmp']
454+
verifylist = [
455+
('name', self.new_router.name),
456+
('enable_default_route_ecmp', True),
457+
]
458+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
459+
columns, data = self.cmd.take_action(parsed_args)
460+
self.network_client.create_router.assert_called_once_with(
461+
name=self.new_router.name,
462+
admin_state_up=True,
463+
enable_default_route_ecmp=True,
464+
)
465+
self.assertEqual(self.columns, columns)
466+
self.assertCountEqual(self.data, data)
467+
468+
def test_create_with_enable_default_route_ecmp_no_extension(self):
469+
arglist = [self.new_router.name, '--enable-default-route-ecmp']
470+
verifylist = [
471+
('name', self.new_router.name),
472+
('enable_default_route_ecmp', True),
473+
]
474+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
475+
self.assertRaises(
476+
exceptions.CommandError,
477+
self.cmd.take_action,
478+
parsed_args,
479+
)
480+
413481

414482
class TestDeleteRouter(TestRouter):
415483
# The routers to delete.

0 commit comments

Comments
 (0)