From 4840fe79d7d58d26cd65113f896e766ddf55f201 Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Fri, 27 Feb 2015 14:56:39 -0800 Subject: [PATCH] Don't start transaction during floating IP delete The previous floating IP code was starting a transaction to delete the floating IP object from the database and then calling the core plugin's delete_port method which could make many yielding calls, risking deadlocks. This patch updates the relationship between floating IP and the ports table to allow the port deletion to automatically clear the floating IP object via a cascade. Closes-Bug: #1426543 Closes-Bug: #1426482 Change-Id: I4b1cdc198c18d5c673a35aaf9c7e960f97f1e20b --- neutron/db/l3_db.py | 15 +++-- .../alembic_migrations/versions/HEAD | 2 +- .../f15b1fb526dd_cascade_floatingip.py | 55 +++++++++++++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) create mode 100644 neutron/db/migration/alembic_migrations/versions/f15b1fb526dd_cascade_floatingip.py diff --git a/neutron/db/l3_db.py b/neutron/db/l3_db.py index f3aa036b4..7b7629397 100644 --- a/neutron/db/l3_db.py +++ b/neutron/db/l3_db.py @@ -97,7 +97,8 @@ class FloatingIP(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant): floating_ip_address = sa.Column(sa.String(64), nullable=False) floating_network_id = sa.Column(sa.String(36), nullable=False) - floating_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id'), + floating_port_id = sa.Column(sa.String(36), + sa.ForeignKey('ports.id', ondelete="CASCADE"), nullable=False) fixed_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id')) fixed_ip_address = sa.Column(sa.String(64)) @@ -912,11 +913,13 @@ class L3_NAT_dbonly_mixin(l3.RouterPluginBase): def _delete_floatingip(self, context, id): floatingip = self._get_floatingip(context, id) router_id = floatingip['router_id'] - with context.session.begin(subtransactions=True): - context.session.delete(floatingip) - self._core_plugin.delete_port(context.elevated(), - floatingip['floating_port_id'], - l3_port_check=False) + # Foreign key cascade will take care of the removal of the + # floating IP record once the port is deleted. We can't start + # a transaction first to remove it ourselves because the delete_port + # method will yield in its post-commit activities. + self._core_plugin.delete_port(context.elevated(), + floatingip['floating_port_id'], + l3_port_check=False) return router_id def delete_floatingip(self, context, id): diff --git a/neutron/db/migration/alembic_migrations/versions/HEAD b/neutron/db/migration/alembic_migrations/versions/HEAD index 906e0b60c..b553d5259 100644 --- a/neutron/db/migration/alembic_migrations/versions/HEAD +++ b/neutron/db/migration/alembic_migrations/versions/HEAD @@ -1 +1 @@ -57dd745253a6 +f15b1fb526dd diff --git a/neutron/db/migration/alembic_migrations/versions/f15b1fb526dd_cascade_floatingip.py b/neutron/db/migration/alembic_migrations/versions/f15b1fb526dd_cascade_floatingip.py new file mode 100644 index 000000000..b351546bc --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/f15b1fb526dd_cascade_floatingip.py @@ -0,0 +1,55 @@ +# Copyright 2015 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +"""Cascade Floating IP Floating Port deletion + +Revision ID: f15b1fb526dd +Revises: 57dd745253a6 +Create Date: 2014-08-24 21:56:36.422885 + +""" + +# revision identifiers, used by Alembic. +revision = 'f15b1fb526dd' +down_revision = '57dd745253a6' + +from alembic import op +from sqlalchemy.engine import reflection + + +def _drop_constraint(): + inspector = reflection.Inspector.from_engine(op.get_bind()) + fk_name = [fk['name'] for fk in + inspector.get_foreign_keys('floatingips') + if 'floating_port_id' in fk['constrained_columns']] + op.drop_constraint(fk_name[0], 'floatingips', 'foreignkey') + + +def upgrade(): + _drop_constraint() + op.create_foreign_key( + name=None, + source='floatingips', referent='ports', + local_cols=['floating_port_id'], remote_cols=['id'], ondelete='CASCADE' + ) + + +def downgrade(): + _drop_constraint() + op.create_foreign_key( + name=None, + source='floatingips', referent='ports', + local_cols=['floating_port_id'], remote_cols=['id'] + ) -- 2.45.2