]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Avoid dhcp agent race condition on subnet and network delete
authorarmando-migliaccio <amigliaccio@nicira.com>
Fri, 13 Sep 2013 18:26:18 +0000 (11:26 -0700)
committerGerrit Code Review <review@openstack.org>
Fri, 22 Nov 2013 21:51:45 +0000 (21:51 +0000)
Ensure that ports that are about to be deleted are 'selected
for update'. By doing so, we avoid a race condition between
subnet and network delete operations carried out by two
separate server instances.

A race caused by the dhcp agent deleting the DHCP port
(caused by a subnet-delete event notification) can
still occur and will be addressed in a subsequent patch.

delete_subnet's way to delete ports has been tweaked to
ensure that postgres db can handle the SELECT FOR UPDATE
correctly.

Partial-Bug:1197627

Change-Id: I5bd75a758395a2faeff9db35a03c42dfa8ae0eab

neutron/db/db_base_plugin_v2.py

index ae7fba648485d5ba7808807abf70b9a060b77e90..64674352ded75dc8f885df3bbf89ae4ecef0bbfa 100644 (file)
@@ -442,11 +442,10 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
                   {'ip_address': ip_address,
                    'network_id': network_id,
                    'subnet_id': subnet_id})
-        alloc_qry = context.session.query(
-            models_v2.IPAllocation).with_lockmode('update')
-        alloc_qry.filter_by(network_id=network_id,
-                            ip_address=ip_address,
-                            subnet_id=subnet_id).delete()
+        context.session.query(models_v2.IPAllocation).filter_by(
+            network_id=network_id,
+            ip_address=ip_address,
+            subnet_id=subnet_id).delete()
 
     @staticmethod
     def _generate_ip(context, subnets):
@@ -990,8 +989,10 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
         with context.session.begin(subtransactions=True):
             network = self._get_network(context, id)
 
-            filter = {'network_id': [id]}
-            ports = self.get_ports(context, filters=filter)
+            filters = {'network_id': [id]}
+            # NOTE(armando-migliaccio): stick with base plugin
+            ports = self._get_ports_query(
+                context, filters=filters).with_lockmode('update')
 
             # check if there are any tenant owned ports in-use
             only_auto_del = all(p['device_owner'] in AUTO_DELETE_PORT_OWNERS
@@ -1266,17 +1267,17 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
             subnet = self._get_subnet(context, id)
             # Check if any tenant owned ports are using this subnet
             allocated_qry = context.session.query(models_v2.IPAllocation)
-            allocated_qry = allocated_qry.options(orm.joinedload('ports'))
-            allocated = allocated_qry.filter_by(subnet_id=id)
-
-            only_auto_del = all(not a.port_id or
-                                a.ports.device_owner in AUTO_DELETE_PORT_OWNERS
-                                for a in allocated)
-            if not only_auto_del:
-                raise q_exc.SubnetInUse(subnet_id=id)
+            allocated_qry = allocated_qry.join(models_v2.Port)
+            allocated = allocated_qry.filter_by(
+                network_id=subnet.network_id).with_lockmode('update')
 
             # remove network owned ports
-            allocated.delete()
+            for a in allocated:
+                if a.ports.device_owner in AUTO_DELETE_PORT_OWNERS:
+                    NeutronDbPluginV2._delete_ip_allocation(
+                        context, subnet.network_id, id, a.ip_address)
+                else:
+                    raise q_exc.SubnetInUse(subnet_id=id)
 
             context.session.delete(subnet)