From 30dab936e602cba7e35806e8a558b53eb8936f48 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Thu, 7 Jan 2016 14:28:24 -0800 Subject: [PATCH] Only restrict gateway_ip change for router ports The subnet update code was restricting gateway_ip changes if the existing gateway IP belonged to a Neutron port. This was implemented because changing the gateway will break all floating IP addresses if the gateway is a Neutron router. However, this restriction makes it possible to get a subnet stuck to an IP address that belongs to another port (e.g. a compute port) so the user has to either delete the port or change it's IP, both of which are disruptive. This patch just changes the restriction so it only prevents gateway IP changes if the current gateway IP belongs to a router. This preserves the intent of the original change while allowing the subnet to be updated off of IP addresses that belong to normal ports. Change-Id: I4691505ef2fad6019e0d2fd80ff1b9e157662a29 Closes-bug: #1532004 --- neutron/db/db_base_plugin_v2.py | 10 +++--- .../tests/unit/db/test_db_base_plugin_v2.py | 35 +++++++++++++------ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index bf55ba3d5e7..a0e77f09bd9 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -466,10 +466,12 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon, # a subnet-update and a router-interface-add operation are # executed concurrently if cur_subnet and not ipv6_utils.is_ipv6_pd_enabled(s): - alloc_qry = context.session.query(models_v2.IPAllocation) - allocated = alloc_qry.filter_by( - ip_address=cur_subnet['gateway_ip'], - subnet_id=cur_subnet['id']).first() + ipal = models_v2.IPAllocation + alloc_qry = context.session.query(ipal) + alloc_qry = alloc_qry.join("port", "routerport") + allocated = alloc_qry.filter( + ipal.ip_address == cur_subnet['gateway_ip'], + ipal.subnet_id == cur_subnet['id']).first() if allocated and allocated['port_id']: raise n_exc.GatewayIpInUse( ip_address=cur_subnet['gateway_ip'], diff --git a/neutron/tests/unit/db/test_db_base_plugin_v2.py b/neutron/tests/unit/db/test_db_base_plugin_v2.py index 0f2fca62f67..bc6acb15dce 100644 --- a/neutron/tests/unit/db/test_db_base_plugin_v2.py +++ b/neutron/tests/unit/db/test_db_base_plugin_v2.py @@ -4177,22 +4177,37 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) - def test_update_subnet_gw_ip_in_use_returns_409(self): + def test_update_subnet_gw_ip_in_use_by_router_returns_409(self): with self.network() as network: - with self.subnet( - network=network, - allocation_pools=[{'start': '10.0.0.100', - 'end': '10.0.0.253'}]) as subnet: - subnet_data = subnet['subnet'] + with self.subnet(network=network, + allocation_pools=[{'start': '10.0.0.2', + 'end': '10.0.0.8'}]) as subnet: + s = subnet['subnet'] with self.port( - subnet=subnet, - fixed_ips=[{'subnet_id': subnet_data['id'], - 'ip_address': subnet_data['gateway_ip']}]): + subnet=subnet, fixed_ips=[{'subnet_id': s['id'], + 'ip_address': s['gateway_ip']}] + ) as port: + # this protection only applies to router ports so we need + # to make this port belong to a router + ctx = context.get_admin_context() + with ctx.session.begin(): + router = l3_db.Router() + ctx.session.add(router) + with ctx.session.begin(): + rp = l3_db.RouterPort(router_id=router.id, + port_id=port['port']['id']) + ctx.session.add(rp) data = {'subnet': {'gateway_ip': '10.0.0.99'}} req = self.new_update_request('subnets', data, - subnet_data['id']) + s['id']) res = req.get_response(self.api) self.assertEqual(409, res.status_int) + # should work fine if it's not a router port + with ctx.session.begin(): + ctx.session.delete(rp) + ctx.session.delete(router) + res = req.get_response(self.api) + self.assertEqual(res.status_int, 200) def test_update_subnet_inconsistent_ipv4_gatewayv6(self): with self.network() as network: