From b1282b8410ca546bfa15e1174ab9bafe1c29ee43 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Wed, 18 Jun 2014 12:03:01 -0700 Subject: [PATCH] Allow reading a tenant router's external IP Adds an external IPs field to the external gateway information for a router so the external IP address of the router can be read by the tenant. DocImpact Closes-Bug: #1255142 Change-Id: If4e77c445e9b855ff77deea6c8df4a0b3cf249d4 (cherry picked from commit c7baaa068ed1d3c8b02717232edef60ba1b655f6) --- neutron/db/l3_db.py | 6 +++++- neutron/db/l3_gwmode_db.py | 8 +++++++- neutron/extensions/l3.py | 16 +++++++++++++++- neutron/extensions/l3_ext_gw_mode.py | 8 +++++++- neutron/tests/unit/test_extension_ext_gw_mode.py | 11 ++++++++--- neutron/tests/unit/test_l3_plugin.py | 10 ++++++++-- 6 files changed, 50 insertions(+), 9 deletions(-) diff --git a/neutron/db/l3_db.py b/neutron/db/l3_db.py index afc01a33bbd..709c99cb0dd 100644 --- a/neutron/db/l3_db.py +++ b/neutron/db/l3_db.py @@ -101,7 +101,11 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase): def _make_router_dict(self, router, fields=None, process_extensions=True): res = dict((key, router[key]) for key in CORE_ROUTER_ATTRS) if router['gw_port_id']: - ext_gw_info = {'network_id': router.gw_port['network_id']} + ext_gw_info = { + 'network_id': router.gw_port['network_id'], + 'external_fixed_ips': [{'subnet_id': ip["subnet_id"], + 'ip_address': ip["ip_address"]} + for ip in router.gw_port['fixed_ips']]} else: ext_gw_info = None res.update({ diff --git a/neutron/db/l3_gwmode_db.py b/neutron/db/l3_gwmode_db.py index dce6cafe90f..e9f081f6fce 100644 --- a/neutron/db/l3_gwmode_db.py +++ b/neutron/db/l3_gwmode_db.py @@ -43,7 +43,13 @@ class L3_NAT_dbonly_mixin(l3_db.L3_NAT_dbonly_mixin): nw_id = router_db.gw_port['network_id'] router_res[EXTERNAL_GW_INFO] = { 'network_id': nw_id, - 'enable_snat': router_db.enable_snat} + 'enable_snat': router_db.enable_snat, + 'external_fixed_ips': [ + {'subnet_id': ip["subnet_id"], + 'ip_address': ip["ip_address"]} + for ip in router_db.gw_port['fixed_ips'] + ] + } def _update_router_gw_info(self, context, router_id, info, router=None): # Load the router only if necessary diff --git a/neutron/extensions/l3.py b/neutron/extensions/l3.py index b02c9337b6d..1497d9fb45c 100644 --- a/neutron/extensions/l3.py +++ b/neutron/extensions/l3.py @@ -100,7 +100,20 @@ RESOURCE_ATTRIBUTE_MAP = { 'is_visible': True}, EXTERNAL_GW_INFO: {'allow_post': True, 'allow_put': True, 'is_visible': True, 'default': None, - 'enforce_policy': True} + 'enforce_policy': True, + 'validate': { + 'type:dict_or_nodata': { + 'network_id': {'type:uuid': None, + 'required': True}, + 'external_fixed_ips': { + 'convert_list_to': + attr.convert_kvp_list_to_dict, + 'type:fixed_ips': None, + 'default': None, + 'required': False, + } + } + }} }, 'floatingips': { 'id': {'allow_post': False, 'allow_put': False, @@ -174,6 +187,7 @@ class L3(extensions.ExtensionDescriptor): """Returns Ext Resources.""" plural_mappings = resource_helper.build_plural_mappings( {}, RESOURCE_ATTRIBUTE_MAP) + plural_mappings['external_fixed_ips'] = 'external_fixed_ip' attr.PLURALS.update(plural_mappings) action_map = {'router': {'add_router_interface': 'PUT', 'remove_router_interface': 'PUT'}} diff --git a/neutron/extensions/l3_ext_gw_mode.py b/neutron/extensions/l3_ext_gw_mode.py index 31c943a9c88..ae0ab1d54b9 100644 --- a/neutron/extensions/l3_ext_gw_mode.py +++ b/neutron/extensions/l3_ext_gw_mode.py @@ -29,7 +29,13 @@ EXTENDED_ATTRIBUTES_2_0 = { {'type:dict_or_nodata': {'network_id': {'type:uuid': None, 'required': True}, 'enable_snat': {'type:boolean': None, 'required': False, - 'convert_to': attrs.convert_to_boolean}} + 'convert_to': attrs.convert_to_boolean}, + 'external_fixed_ips': { + 'convert_list_to': attrs.convert_kvp_list_to_dict, + 'validate': {'type:fixed_ips': None}, + 'default': None, + 'required': False} + } }}}} diff --git a/neutron/tests/unit/test_extension_ext_gw_mode.py b/neutron/tests/unit/test_extension_ext_gw_mode.py index c119470d390..502fd1c0f9f 100644 --- a/neutron/tests/unit/test_extension_ext_gw_mode.py +++ b/neutron/tests/unit/test_extension_ext_gw_mode.py @@ -245,14 +245,16 @@ class TestL3GwModeMixin(testlib_api.SqlTestCase, def test_make_router_dict_with_ext_gw(self): router_dict = self.target_object._make_router_dict(self.router) self.assertEqual({'network_id': self.ext_net_id, - 'enable_snat': True}, + 'enable_snat': True, + 'external_fixed_ips': []}, router_dict[l3.EXTERNAL_GW_INFO]) def test_make_router_dict_with_ext_gw_snat_disabled(self): self.router.enable_snat = False router_dict = self.target_object._make_router_dict(self.router) self.assertEqual({'network_id': self.ext_net_id, - 'enable_snat': False}, + 'enable_snat': False, + 'external_fixed_ips': []}, router_dict[l3.EXTERNAL_GW_INFO]) def test_build_routers_list_no_ext_gw(self): @@ -364,7 +366,10 @@ class ExtGwModeIntTestCase(test_db_plugin.NeutronDbPluginV2TestCase, ('admin_state_up', True), ('status', 'ACTIVE'), ('external_gateway_info', {'network_id': ext_net_id, - 'enable_snat': snat_expected_value})] + 'enable_snat': snat_expected_value, + 'external_fixed_ips': [{ + 'ip_address': mock.ANY, + 'subnet_id': s['subnet']['id']}]})] with self.router( name=name, admin_state_up=True, tenant_id=tenant_id, external_gateway_info=input_value) as router: diff --git a/neutron/tests/unit/test_l3_plugin.py b/neutron/tests/unit/test_l3_plugin.py index ecb1ead448c..6b82d1759cc 100644 --- a/neutron/tests/unit/test_l3_plugin.py +++ b/neutron/tests/unit/test_l3_plugin.py @@ -1102,11 +1102,17 @@ class L3NatTestCaseBase(L3NatTestCaseMixin): 'remove', tenant_router['router']['id'], s['subnet']['id'], None, tenant_id='tenant_a') - def test_router_add_gateway_invalid_network_returns_404(self): + def test_router_add_gateway_invalid_network_returns_400(self): with self.router() as r: self._add_external_gateway_to_router( r['router']['id'], - "foobar", expected_code=exc.HTTPNotFound.code) + "foobar", expected_code=exc.HTTPBadRequest.code) + + def test_router_add_gateway_non_existent_network_returns_404(self): + with self.router() as r: + self._add_external_gateway_to_router( + r['router']['id'], + _uuid(), expected_code=exc.HTTPNotFound.code) def test_router_add_gateway_net_not_external_returns_400(self): with self.router() as r: