From 6a5b3f4dd65e6b94c9909b5351d100a8e7b4ae35 Mon Sep 17 00:00:00 2001 From: armando-migliaccio Date: Fri, 13 Sep 2013 11:26:18 -0700 Subject: [PATCH] Avoid dhcp agent race condition on subnet and network delete 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 | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index ae7fba64848..64674352ded 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -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)