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
1 changed files with 17 additions and 16 deletions

View File

@ -442,11 +442,10 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
{'ip_address': ip_address, {'ip_address': ip_address,
'network_id': network_id, 'network_id': network_id,
'subnet_id': subnet_id}) 'subnet_id': subnet_id})
alloc_qry = context.session.query( context.session.query(models_v2.IPAllocation).filter_by(
models_v2.IPAllocation).with_lockmode('update') network_id=network_id,
alloc_qry.filter_by(network_id=network_id, ip_address=ip_address,
ip_address=ip_address, subnet_id=subnet_id).delete()
subnet_id=subnet_id).delete()
@staticmethod @staticmethod
def _generate_ip(context, subnets): def _generate_ip(context, subnets):
@ -990,8 +989,10 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
network = self._get_network(context, id) network = self._get_network(context, id)
filter = {'network_id': [id]} filters = {'network_id': [id]}
ports = self.get_ports(context, filters=filter) # 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 # check if there are any tenant owned ports in-use
only_auto_del = all(p['device_owner'] in AUTO_DELETE_PORT_OWNERS 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) subnet = self._get_subnet(context, id)
# Check if any tenant owned ports are using this subnet # Check if any tenant owned ports are using this subnet
allocated_qry = context.session.query(models_v2.IPAllocation) allocated_qry = context.session.query(models_v2.IPAllocation)
allocated_qry = allocated_qry.options(orm.joinedload('ports')) allocated_qry = allocated_qry.join(models_v2.Port)
allocated = allocated_qry.filter_by(subnet_id=id) allocated = allocated_qry.filter_by(
network_id=subnet.network_id).with_lockmode('update')
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)
# remove network owned ports # 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) context.session.delete(subnet)