Add relationship between port and floating ip

We need to have the relationship between port and floating ip, because updating
quota will happen when the event "after_delete" occurs. And current cascade
removal of the floating ip does not cause the event "after_delete" for floating
ip. The cascade on the ORM-level "delete" must be added.

Closes-bug: #1533687
Change-Id: Id414cf20069563085737fee5ddce94b1bf2f912d
This commit is contained in:
Sergey Belous 2016-01-18 16:37:06 +03:00
parent f40538e629
commit c82f0021d5
2 changed files with 45 additions and 2 deletions

View File

@ -111,6 +111,15 @@ class FloatingIP(model_base.HasStandardAttributes, model_base.BASEV2,
floating_port_id = sa.Column(sa.String(36),
sa.ForeignKey('ports.id', ondelete="CASCADE"),
nullable=False)
# The ORM-level "delete" cascade relationship between port and floating_ip
# is required for causing the in-Python event "after_delete" that needs for
# proper quota management in case when cascade removal of the floating_ip
# happens after removal of the floating_port
port = orm.relationship(models_v2.Port,
backref=orm.backref('floating_ips',
cascade='all,delete-orphan'),
foreign_keys='FloatingIP.floating_port_id')
fixed_port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id'))
fixed_ip_address = sa.Column(sa.String(64))
router_id = sa.Column(sa.String(36), sa.ForeignKey('routers.id'))

View File

@ -15,7 +15,10 @@ from oslo_utils import uuidutils
from neutron import context
from neutron.db.quota import api as quota_db_api
from neutron.tests.unit.api import test_extensions
from neutron.tests.unit.extensions import test_l3
from neutron.tests.unit.extensions import test_securitygroup
from neutron.tests.unit.plugins.ml2 import base as ml2_base
from neutron.tests.unit.plugins.ml2 import test_plugin
@ -50,13 +53,13 @@ class BaseTestTrackedResources(test_plugin.Ml2PluginV2TestCase,
self.ctx, resource_name, self._tenant_id)
class TestTrackedResourcesEventHandler(BaseTestTrackedResources):
class BaseTestEventHandler(object):
def setUp(self):
handler_patch = mock.patch(
'neutron.quota.resource.TrackedResource._db_event_handler')
self.handler_mock = handler_patch.start()
super(TestTrackedResourcesEventHandler, self).setUp()
super(BaseTestEventHandler, self).setUp()
def _verify_event_handler_calls(self, data, expected_call_count=1):
if not hasattr(data, '__iter__') or isinstance(data, dict):
@ -70,6 +73,10 @@ class TestTrackedResourcesEventHandler(BaseTestTrackedResources):
self.assertEqual(model['tenant_id'], item['tenant_id'])
call_idx = call_idx - 1
class TestTrackedResourcesEventHandler(BaseTestEventHandler,
BaseTestTrackedResources):
def test_create_delete_network_triggers_event(self):
self._test_init('network')
net = self._make_network('json', 'meh', True)['network']
@ -148,6 +155,33 @@ class TestTrackedResourcesEventHandler(BaseTestTrackedResources):
self._verify_event_handler_calls(sec_group_rule, expected_call_count=5)
class TestL3ResourcesEventHandler(BaseTestEventHandler,
ml2_base.ML2TestFramework,
test_l3.L3NatTestCaseMixin):
def setUp(self):
super(TestL3ResourcesEventHandler, self).setUp()
ext_mgr = test_l3.L3TestExtensionManager()
self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr)
def test_create_delete_floating_ip_triggers_event(self):
net = self._make_network('json', 'meh', True)
subnet = self._make_subnet('json', net, '14.0.0.1',
'14.0.0.0/24')['subnet']
self._set_net_external(subnet['network_id'])
floatingip = self._make_floatingip('json', subnet['network_id'])
internal_port = self._show(
'ports', floatingip['floatingip']['port_id'])['ports'][0]
# When a floatingip is created it also creates port, therefore
# there will be four calls in total to the event handler
self._verify_event_handler_calls(floatingip['floatingip'],
expected_call_count=4)
self._delete('floatingips', floatingip['floatingip']['id'])
# Expecting 2 more calls - 1 for the port, 1 for the floatingip
self._verify_event_handler_calls(
[internal_port, floatingip['floatingip']], expected_call_count=6)
class TestTrackedResources(BaseTestTrackedResources):
def _verify_dirty_bit(self, resource_name, expected_value=True):