From 841d9d8dbd313b3dcf604466786d6b9a5c918156 Mon Sep 17 00:00:00 2001 From: Reedip Date: Fri, 19 Aug 2016 21:02:35 +0530 Subject: [PATCH] Add support for setting router gateway This patch adds the support to set the gateway information for a router. Implements: blueprint neutron-client-advanced-router Partially-Implements: blueprint network-commands-options Change-Id: Ifb5a4d1965cd7e75c0c8cf2cfb677e0628b699dc Depends-On: I2bda0dd40afd64b6cecca5f64ef2326bda4fac92 --- doc/source/command-objects/router.rst | 19 +++ openstackclient/network/v2/router.py | 58 ++++++++- .../tests/unit/network/v2/test_router.py | 112 +++++++++++++++++- .../router-gateway-set-01d9c7ffe4461daf.yaml | 7 ++ 4 files changed, 189 insertions(+), 7 deletions(-) create mode 100644 releasenotes/notes/router-gateway-set-01d9c7ffe4461daf.yaml diff --git a/doc/source/command-objects/router.rst b/doc/source/command-objects/router.rst index 56b95ffa68..22c4149032 100644 --- a/doc/source/command-objects/router.rst +++ b/doc/source/command-objects/router.rst @@ -213,6 +213,7 @@ Set router properties [--description ] [--route destination=,gateway= | --no-route] [--ha | --no-ha] + [--external-gateway [--enable-snat|--disable-snat] [--fixed-ip subnet=,ip-address=]] .. option:: --name @@ -258,6 +259,24 @@ Set router properties Clear high availablability attribute of the router (disabled router only) +.. option:: --external-gateway + + External Network used as router's gateway (name or ID) + +.. option:: --enable-snat + + Enable Source NAT on external gateway + +.. option:: --disable-snat + + Disable Source NAT on external gateway + +.. option:: --fixed-ip subnet=,ip-address= + + Desired IP and/or subnet (name or ID) on external gateway: + subnet=,ip-address= + (repeat option to set multiple fixed IP addresses) + .. _router_set-router: .. describe:: diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index cbd412b585..bf60cf6fa9 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -473,9 +473,32 @@ class SetRouter(command.Command): help=_("Clear high availablability attribute of the router " "(disabled router only)") ) - # TODO(tangchen): Support setting 'external_gateway_info' property in - # 'router set' command. - + parser.add_argument( + '--external-gateway', + metavar="", + help=_("External Network used as router's gateway (name or ID)") + ) + parser.add_argument( + '--fixed-ip', + metavar='subnet=,ip-address=', + action=parseractions.MultiKeyValueAction, + optional_keys=['subnet', 'ip-address'], + help=_("Desired IP and/or subnet (name or ID)" + "on external gateway: " + "subnet=,ip-address= " + "(repeat option to set multiple fixed IP addresses)") + ) + snat_group = parser.add_mutually_exclusive_group() + snat_group.add_argument( + '--enable-snat', + action='store_true', + help=_("Enable Source NAT on external gateway") + ) + snat_group.add_argument( + '--disable-snat', + action='store_true', + help=_("Disable Source NAT on external gateway") + ) return parser def take_action(self, parsed_args): @@ -504,7 +527,34 @@ class SetRouter(command.Command): for route in parsed_args.routes: route['nexthop'] = route.pop('gateway') attrs['routes'] = obj.routes + parsed_args.routes - + if (parsed_args.disable_snat or parsed_args.enable_snat or + parsed_args.fixed_ip) and not parsed_args.external_gateway: + msg = (_("You must specify '--external-gateway' in order" + "to update the SNAT or fixed-ip values")) + raise exceptions.CommandError(msg) + if parsed_args.external_gateway: + gateway_info = {} + network = client.find_network( + parsed_args.external_gateway, ignore_missing=False) + gateway_info['network_id'] = network.id + if parsed_args.disable_snat: + gateway_info['enable_snat'] = False + if parsed_args.enable_snat: + gateway_info['enable_snat'] = True + if parsed_args.fixed_ip: + ips = [] + for ip_spec in parsed_args.fixed_ip: + if ip_spec.get('subnet', False): + subnet_name_id = ip_spec.pop('subnet') + if subnet_name_id: + subnet = client.find_subnet(subnet_name_id, + ignore_missing=False) + ip_spec['subnet_id'] = subnet.id + if ip_spec.get('ip-address', False): + ip_spec['ip_address'] = ip_spec.pop('ip-address') + ips.append(ip_spec) + gateway_info['external_fixed_ips'] = ips + attrs['external_gateway_info'] = gateway_info client.update_router(obj, **attrs) diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 24984e47b0..f5f2c3255b 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -561,17 +561,19 @@ class TestSetRouter(TestRouter): # The router to set. _default_route = {'destination': '10.20.20.0/24', 'nexthop': '10.20.30.1'} + _network = network_fakes.FakeNetwork.create_one_network() + _subnet = network_fakes.FakeSubnet.create_one_subnet() _router = network_fakes.FakeRouter.create_one_router( attrs={'routes': [_default_route]} ) def setUp(self): super(TestSetRouter, self).setUp() - + self.network.router_add_gateway = mock.Mock() self.network.update_router = mock.Mock(return_value=None) - self.network.find_router = mock.Mock(return_value=self._router) - + self.network.find_network = mock.Mock(return_value=self._network) + self.network.find_subnet = mock.Mock(return_value=self._subnet) # Get the command object to test self.cmd = router.SetRouter(self.app, self.namespace) @@ -758,6 +760,110 @@ class TestSetRouter(TestRouter): self._router, **attrs) self.assertIsNone(result) + def test_wrong_gateway_params(self): + arglist = [ + "--fixed-ip", "subnet='abc'", + self._router.id, + ] + verifylist = [ + ('fixed_ip', [{'subnet': "'abc'"}]), + ('router', self._router.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, parsed_args) + + def test_set_gateway_network_only(self): + arglist = [ + "--external-gateway", self._network.id, + self._router.id, + ] + verifylist = [ + ('external_gateway', self._network.id), + ('router', self._router.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.update_router.assert_called_with( + self._router, **{'external_gateway_info': { + 'network_id': self._network.id}}) + self.assertIsNone(result) + + def test_set_gateway_options_subnet_only(self): + arglist = [ + "--external-gateway", self._network.id, + "--fixed-ip", "subnet='abc'", + self._router.id, + '--enable-snat', + ] + verifylist = [ + ('router', self._router.id), + ('external_gateway', self._network.id), + ('fixed_ip', [{'subnet': "'abc'"}]), + ('enable_snat', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.update_router.assert_called_with( + self._router, **{'external_gateway_info': { + 'network_id': self._network.id, + 'external_fixed_ips': [{ + 'subnet_id': self._subnet.id, }], + 'enable_snat': True, }}) + self.assertIsNone(result) + + def test_set_gateway_option_ipaddress_only(self): + arglist = [ + "--external-gateway", self._network.id, + "--fixed-ip", "ip-address=10.0.1.1", + self._router.id, + '--enable-snat', + ] + verifylist = [ + ('router', self._router.id), + ('external_gateway', self._network.id), + ('fixed_ip', [{'ip-address': "10.0.1.1"}]), + ('enable_snat', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.update_router.assert_called_with( + self._router, **{'external_gateway_info': { + 'network_id': self._network.id, + 'external_fixed_ips': [{ + 'ip_address': "10.0.1.1", }], + 'enable_snat': True, }}) + self.assertIsNone(result) + + def test_set_gateway_options_subnet_ipaddress(self): + arglist = [ + "--external-gateway", self._network.id, + "--fixed-ip", "subnet='abc',ip-address=10.0.1.1", + self._router.id, + '--enable-snat', + ] + verifylist = [ + ('router', self._router.id), + ('external_gateway', self._network.id), + ('fixed_ip', [{'subnet': "'abc'", + 'ip-address': "10.0.1.1"}]), + ('enable_snat', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.network.update_router.assert_called_with( + self._router, **{'external_gateway_info': { + 'network_id': self._network.id, + 'external_fixed_ips': [{ + 'subnet_id': self._subnet.id, + 'ip_address': "10.0.1.1", }], + 'enable_snat': True, }}) + self.assertIsNone(result) + class TestShowRouter(TestRouter): diff --git a/releasenotes/notes/router-gateway-set-01d9c7ffe4461daf.yaml b/releasenotes/notes/router-gateway-set-01d9c7ffe4461daf.yaml new file mode 100644 index 0000000000..dec9d09663 --- /dev/null +++ b/releasenotes/notes/router-gateway-set-01d9c7ffe4461daf.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Add support for setting the gateway information in a router, + by introducing the new option ``--external-gateway`` in + ``router set`` CLI. + [ Blueprint `neutron-client-advanced-router `_]