diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index dde4eda99f..aeeec93175 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -111,6 +111,30 @@ def _get_attrs(client_manager, parsed_args): parsed_args.project_domain, ).id attrs['tenant_id'] = project_id + if parsed_args.external_gateway: + gateway_info = {} + n_client = client_manager.network + network = n_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 = n_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 return attrs @@ -320,6 +344,32 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs): "repeat option to set multiple availability zones)") ) _tag.add_tag_option_to_parser_for_create(parser, _('router')) + 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 @@ -338,6 +388,12 @@ class CreateRouter(command.ShowOne, common.NeutronCommandWithExtraArgs): # tags cannot be set when created, so tags need to be set later. _tag.update_tags_for_set(client, obj, parsed_args) + 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 specify SNAT or fixed-ip values")) + raise exceptions.CommandError(msg) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) @@ -725,29 +781,6 @@ class SetRouter(common.NeutronCommandWithExtraArgs): 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 if ((parsed_args.qos_policy or parsed_args.no_qos_policy) and not parsed_args.external_gateway): diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py index 0324674817..04d9fe4836 100644 --- a/openstackclient/tests/unit/network/v2/test_router.py +++ b/openstackclient/tests/unit/network/v2/test_router.py @@ -186,6 +186,43 @@ class TestCreateRouter(TestRouter): self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) + def test_create_with_gateway(self): + _network = network_fakes.FakeNetwork.create_one_network() + _subnet = network_fakes.FakeSubnet.create_one_subnet() + self.network.find_network = mock.Mock(return_value=_network) + self.network.find_subnet = mock.Mock(return_value=_subnet) + arglist = [ + self.new_router.name, + '--external-gateway', _network.name, + '--enable-snat', + '--fixed-ip', 'ip-address=2001:db8::1' + ] + verifylist = [ + ('name', self.new_router.name), + ('enable', True), + ('distributed', False), + ('ha', False), + ('external_gateway', _network.name), + ('enable_snat', True), + ('fixed_ip', [{'ip-address': '2001:db8::1'}]), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_router.assert_called_once_with(**{ + 'admin_state_up': True, + 'name': self.new_router.name, + 'external_gateway_info': { + 'network_id': _network.id, + 'enable_snat': True, + 'external_fixed_ips': [{'ip_address': '2001:db8::1'}], + }, + }) + self.assertFalse(self.network.set_tags.called) + self.assertEqual(self.columns, columns) + self.assertCountEqual(self.data, data) + def _test_create_with_ha_options(self, option, ha): arglist = [ option, diff --git a/releasenotes/notes/options-create-router-97910a882b604652.yaml b/releasenotes/notes/options-create-router-97910a882b604652.yaml new file mode 100644 index 0000000000..f7d90b75c2 --- /dev/null +++ b/releasenotes/notes/options-create-router-97910a882b604652.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + It is now possible to add an external gateway to a router + immediately on creation. Previously it could only be done by + modifying the router after it had been created. This includes + the options to en- or disable SNAT and to specify a fixed-ip + on the external network.