]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Enable to update external network subnet's gateway-ip
authorHirofumi Ichihara <ichihara.hirofumi@lab.ntt.co.jp>
Tue, 25 Aug 2015 00:10:00 +0000 (09:10 +0900)
committerArmando Migliaccio <armamig@gmail.com>
Wed, 2 Sep 2015 16:46:34 +0000 (16:46 +0000)
This patch enables users to update gateway_ip of a subnet even if
the subnet is in use for an external network of a router.

Change-Id: I78d2b024c99b1af0001bd454465d2fc02692cbf2
Closes-Bug: #1317363

neutron/callbacks/resources.py
neutron/db/db_base_plugin_v2.py
neutron/db/l3_db.py
neutron/tests/unit/extensions/test_l3.py

index 1544fe5a4b30460d898d8b2394f15bd157946c10..029df4305abddf6a2f037d95e0b732aa02868fc5 100644 (file)
@@ -18,3 +18,4 @@ ROUTER_INTERFACE = 'router_interface'
 SECURITY_GROUP = 'security_group'
 SECURITY_GROUP_RULE = 'security_group_rule'
 SUBNET = 'subnet'
+SUBNET_GATEWAY = 'subnet_gateway'
index ca0c73015fe8f6bbececbfc2dc81fb95dc28c2d0..8fef9b36452d763683414b35e9ced8664f1c8283 100644 (file)
@@ -715,13 +715,22 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
             s['allocation_pools'] = range_pools
 
         # If either gateway_ip or allocation_pools were specified
-        gateway_ip = s.get('gateway_ip')
-        if gateway_ip is not None or s.get('allocation_pools') is not None:
-            if gateway_ip is None:
-                gateway_ip = db_subnet.gateway_ip
+        new_gateway_ip = s.get('gateway_ip')
+        gateway_ip_changed = (new_gateway_ip and
+                              new_gateway_ip != db_subnet.gateway_ip)
+        if gateway_ip_changed or s.get('allocation_pools') is not None:
+            gateway_ip = new_gateway_ip or db_subnet.gateway_ip
             pools = range_pools if range_pools is not None else db_pools
             self.ipam.validate_gw_out_of_pools(gateway_ip, pools)
 
+        if gateway_ip_changed:
+            # Provide pre-update notification not to break plugins that don't
+            # support gateway ip change
+            kwargs = {'context': context, 'subnet_id': id,
+                      'network_id': db_subnet.network_id}
+            registry.notify(resources.SUBNET_GATEWAY, events.BEFORE_UPDATE,
+                            self, **kwargs)
+
         with context.session.begin(subtransactions=True):
             subnet, changes = self.ipam.update_db_subnet(context, id, s,
                                                          db_pools)
@@ -753,6 +762,12 @@ class NeutronDbPluginV2(db_base_plugin_common.DbBasePluginCommon,
                 l3_rpc_notifier = l3_rpc_agent_api.L3AgentNotifyAPI()
                 l3_rpc_notifier.routers_updated(context, routers)
 
+        if gateway_ip_changed:
+            kwargs = {'context': context, 'subnet_id': id,
+                      'network_id': db_subnet.network_id}
+            registry.notify(resources.SUBNET_GATEWAY, events.AFTER_UPDATE,
+                            self, **kwargs)
+
         return result
 
     def _subnet_check_ip_allocations(self, context, subnet_id):
index a803dcb6143c4b4d7d3790bb5b113d0e6b7248b4..23d128cf15c2d784454b6d03fcbe1bb17e8ff5be 100644 (file)
@@ -1417,11 +1417,32 @@ def _notify_routers_callback(resource, event, trigger, **kwargs):
     l3plugin.notify_routers_updated(context, router_ids)
 
 
+def _notify_subnet_gateway_ip_update(resource, event, trigger, **kwargs):
+    l3plugin = manager.NeutronManager.get_service_plugins().get(
+            constants.L3_ROUTER_NAT)
+    if not l3plugin:
+        return
+    context = kwargs['context']
+    network_id = kwargs['network_id']
+    subnet_id = kwargs['subnet_id']
+    query = context.session.query(models_v2.Port).filter_by(
+                network_id=network_id,
+                device_owner=l3_constants.DEVICE_OWNER_ROUTER_GW)
+    query = query.join(models_v2.Port.fixed_ips).filter(
+                models_v2.IPAllocation.subnet_id == subnet_id)
+    router_ids = set(port['device_id'] for port in query)
+    for router_id in router_ids:
+        l3plugin.notify_router_updated(context, router_id)
+
+
 def subscribe():
     registry.subscribe(
         _prevent_l3_port_delete_callback, resources.PORT, events.BEFORE_DELETE)
     registry.subscribe(
         _notify_routers_callback, resources.PORT, events.AFTER_DELETE)
+    registry.subscribe(
+        _notify_subnet_gateway_ip_update, resources.SUBNET_GATEWAY,
+        events.AFTER_UPDATE)
 
 # NOTE(armax): multiple l3 service plugins (potentially out of tree) inherit
 # from l3_db and may need the callbacks to be processed. Having an implicit
index 7c6f47cad38e733c812f64540bc41c60abb0d4ec..7cf89f7b96f864db743688fa51668cfd5ca107b9 100644 (file)
@@ -28,8 +28,10 @@ from webob import exc
 from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
 from neutron.api.rpc.handlers import l3_rpc
 from neutron.api.v2 import attributes
+from neutron.callbacks import events
 from neutron.callbacks import exceptions
 from neutron.callbacks import registry
+from neutron.callbacks import resources
 from neutron.common import constants as l3_constants
 from neutron.common import exceptions as n_exc
 from neutron import context
@@ -2494,6 +2496,41 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
         routers = plugin.get_routers(ctx)
         self.assertEqual(0, len(routers))
 
+    def test_update_subnet_gateway_for_external_net(self):
+        """Test to make sure notification to routers occurs when the gateway
+            ip address of a subnet of the external network is changed.
+        """
+        plugin = manager.NeutronManager.get_service_plugins()[
+            service_constants.L3_ROUTER_NAT]
+        if not hasattr(plugin, 'l3_rpc_notifier'):
+            self.skipTest("Plugin does not support l3_rpc_notifier")
+        # make sure the callback is registered.
+        registry.subscribe(
+            l3_db._notify_subnet_gateway_ip_update, resources.SUBNET_GATEWAY,
+            events.AFTER_UPDATE)
+        with mock.patch.object(plugin.l3_rpc_notifier,
+                               'routers_updated') as chk_method:
+            with self.network() as network:
+                allocation_pools = [{'start': '120.0.0.3',
+                                     'end': '120.0.0.254'}]
+                with self.subnet(network=network,
+                                 gateway_ip='120.0.0.1',
+                                 allocation_pools=allocation_pools,
+                                 cidr='120.0.0.0/24') as subnet:
+                    kwargs = {
+                        'device_owner': l3_constants.DEVICE_OWNER_ROUTER_GW,
+                        'device_id': 'fake_device'}
+                    with self.port(subnet=subnet, **kwargs):
+                        data = {'subnet': {'gateway_ip': '120.0.0.2'}}
+                        req = self.new_update_request('subnets', data,
+                                                      subnet['subnet']['id'])
+                        res = self.deserialize(self.fmt,
+                                               req.get_response(self.api))
+                        self.assertEqual(res['subnet']['gateway_ip'],
+                                         data['subnet']['gateway_ip'])
+                        chk_method.assert_called_with(mock.ANY,
+                                                      ['fake_device'], None)
+
 
 class L3AgentDbTestCaseBase(L3NatTestCaseMixin):