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
This commit is contained in:
armando-migliaccio 2013-09-13 11:26:18 -07:00 committed by Gerrit Code Review
parent f0bd2ddb40
commit 6a5b3f4dd6

View 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)