Remove locking from network and subnet delete op

delete_subnet in Ml2 plugin instead of using SELECT FOR
UPDATE deletes the IPAllocations that can be auto-deleted
straight away.
An exception is raised if there are ports that cannot be
autodeleted.

delete_network in ML2 plugin tries to delete all ports
and subnets before performing the network deletion.
No lock is needed here - if some other process modifies
the Port or Subnet table, adding new items, the network
deletion will fail because of a violation of a foreign
key contraint.
In that case the operation will be retried.

Change-Id: Ib4e9441a95d6c80b92a43d55fdeb18d7b51a1cf3
Closes-bug: #1332917
This commit is contained in:
rossella 2014-08-19 19:41:16 +02:00 committed by Rossella Sblendido
parent 99534786ab
commit 89c24012d0

View File

@ -576,11 +576,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
session = context.session session = context.session
while True: while True:
try: try:
# REVISIT(rkukura): Its not clear that
# with_lockmode('update') is really needed in this
# transaction, and if not, the semaphore can also be
# removed.
#
# REVISIT: Serialize this operation with a semaphore # REVISIT: Serialize this operation with a semaphore
# to prevent deadlock waiting to acquire a DB lock # to prevent deadlock waiting to acquire a DB lock
# held by another thread in the same process, leading # held by another thread in the same process, leading
@ -592,13 +587,14 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
# Additionally, a rollback may not be enough to undo the # Additionally, a rollback may not be enough to undo the
# deletion of a floating IP with certain L3 backends. # deletion of a floating IP with certain L3 backends.
self._process_l3_delete(context, id) self._process_l3_delete(context, id)
# Using query().with_lockmode isn't necessary. Foreign-key
# constraints prevent deletion if concurrent creation happens.
with contextlib.nested(lockutils.lock('db-access'), with contextlib.nested(lockutils.lock('db-access'),
session.begin(subtransactions=True)): session.begin(subtransactions=True)):
# Get ports to auto-delete. # Get ports to auto-delete.
ports = (session.query(models_v2.Port). ports = (session.query(models_v2.Port).
enable_eagerloads(False). enable_eagerloads(False).
filter_by(network_id=id). filter_by(network_id=id).all())
with_lockmode('update').all())
LOG.debug("Ports to auto-delete: %s", ports) LOG.debug("Ports to auto-delete: %s", ports)
only_auto_del = all(p.device_owner only_auto_del = all(p.device_owner
in db_base_plugin_v2. in db_base_plugin_v2.
@ -611,8 +607,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
# Get subnets to auto-delete. # Get subnets to auto-delete.
subnets = (session.query(models_v2.Subnet). subnets = (session.query(models_v2.Subnet).
enable_eagerloads(False). enable_eagerloads(False).
filter_by(network_id=id). filter_by(network_id=id).all())
with_lockmode('update').all())
LOG.debug("Subnets to auto-delete: %s", subnets) LOG.debug("Subnets to auto-delete: %s", subnets)
if not (ports or subnets): if not (ports or subnets):
@ -722,20 +717,33 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
session.begin(subtransactions=True)): session.begin(subtransactions=True)):
record = self._get_subnet(context, id) record = self._get_subnet(context, id)
subnet = self._make_subnet_dict(record, None) subnet = self._make_subnet_dict(record, None)
# Get ports to auto-deallocate qry_allocated = (session.query(models_v2.IPAllocation).
allocated = (session.query(models_v2.IPAllocation). filter_by(subnet_id=id).
filter_by(subnet_id=id). join(models_v2.Port))
join(models_v2.Port). is_ipv6_slaac_subnet = ipv6_utils.is_slaac_subnet(subnet)
filter_by(network_id=subnet['network_id']). # Remove network owned ports, and delete IP allocations
with_lockmode('update').all()) # for IPv6 addresses which were automatically generated
# via SLAAC
if not is_ipv6_slaac_subnet:
qry_allocated = (
qry_allocated.filter(models_v2.Port.device_owner.
in_(db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS)))
allocated = qry_allocated.all()
# Delete all the IPAllocation that can be auto-deleted
if allocated:
map(session.delete, allocated)
LOG.debug("Ports to auto-deallocate: %s", allocated) LOG.debug("Ports to auto-deallocate: %s", allocated)
only_auto_del = ipv6_utils.is_slaac_subnet(subnet) or all( # Check if there are tenant owned ports
not a.port_id or a.ports.device_owner in db_base_plugin_v2. tenant_port = (session.query(models_v2.IPAllocation).
AUTO_DELETE_PORT_OWNERS for a in allocated) filter_by(subnet_id=id).
if not only_auto_del: join(models_v2.Port).
first())
if tenant_port:
LOG.debug("Tenant-owned ports exist") LOG.debug("Tenant-owned ports exist")
raise exc.SubnetInUse(subnet_id=id) raise exc.SubnetInUse(subnet_id=id)
# If allocated is None, then all the IPAllocation were
# correctly deleted during the previous pass.
if not allocated: if not allocated:
mech_context = driver_context.SubnetContext(self, context, mech_context = driver_context.SubnetContext(self, context,
subnet) subnet)
@ -763,7 +771,6 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
LOG.exception(_LE("Exception deleting fixed_ip " LOG.exception(_LE("Exception deleting fixed_ip "
"from port %s"), a.port_id) "from port %s"), a.port_id)
session.delete(a)
try: try:
self.mechanism_manager.delete_subnet_postcommit(mech_context) self.mechanism_manager.delete_subnet_postcommit(mech_context)