diff --git a/neutron/db/l3_dvr_db.py b/neutron/db/l3_dvr_db.py index fef38b64dc4..99533df61b4 100644 --- a/neutron/db/l3_dvr_db.py +++ b/neutron/db/l3_dvr_db.py @@ -156,6 +156,19 @@ class DVRResourceOperationHandler(object): payload.context, payload.desired_state, 'distributed', migrating_to_distributed) + @registry.receives(resources.ROUTER, [events.AFTER_UPDATE], + priority_group.PRIORITY_ROUTER_EXTENDED_ATTRIBUTE) + def _delete_distributed_port_bindings_after_change(self, resource, event, + trigger, context, + router_id, router, + request_attrs, + router_db, **kwargs): + old_router = kwargs['old_router'] + if (old_router and old_router['distributed'] and not + router['distributed']): + self._core_plugin.delete_distributed_port_bindings_by_router_id( + context.elevated(), router_db['id']) + @registry.receives(resources.ROUTER, [events.AFTER_UPDATE], priority_group.PRIORITY_ROUTER_EXTENDED_ATTRIBUTE) def _delete_snat_interfaces_after_change(self, resource, event, trigger, diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index fc4286b4f72..6630eaa5438 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -1573,6 +1573,13 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, # merge into session to reflect changes binding.persist_state_to_session(plugin_context.session) + def delete_distributed_port_bindings_by_router_id(self, context, + router_id): + for binding in (context.session.query(models.DistributedPortBinding). + filter_by(router_id=router_id)): + db.clear_binding_levels(context, binding.port_id, binding.host) + context.session.delete(binding) + @utils.transaction_guard @db_api.retry_if_session_inactive() def update_distributed_port_binding(self, context, id, port): diff --git a/neutron/tests/unit/db/test_l3_dvr_db.py b/neutron/tests/unit/db/test_l3_dvr_db.py index a1f854932d5..0f65f6a4319 100644 --- a/neutron/tests/unit/db/test_l3_dvr_db.py +++ b/neutron/tests/unit/db/test_l3_dvr_db.py @@ -130,6 +130,32 @@ class L3DvrTestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): self.assertEqual(1, self.mixin._migrate_router_ports.call_count) + def test_update_router_db_distributed_to_centralized(self): + router = {'name': 'foo_router', 'admin_state_up': True, + 'distributed': True} + agent = {'id': _uuid(), 'host': 'xyz'} + router_db = self._create_router(router) + router_id = router_db['id'] + self.assertTrue(router_db.extra_attributes.distributed) + self.mixin._get_router = mock.Mock(return_value=router_db) + self.mixin._validate_router_migration = mock.Mock() + self.mixin._migrate_router_ports = mock.Mock() + self.mixin._core_plugin.\ + delete_distributed_port_bindings_by_router_id = mock.Mock() + self.mixin.list_l3_agents_hosting_router = mock.Mock( + return_value={'agents': [agent]}) + self.mixin._unbind_router = mock.Mock() + updated_router = self.mixin.update_router(self.ctx, router_id, + {'router': {'distributed': False}}) + # Assert that the DB value has changed + self.assertFalse(updated_router['distributed']) + self.assertEqual(1, + self.mixin._migrate_router_ports.call_count) + self.assertEqual( + 1, + self.mixin._core_plugin. + delete_distributed_port_bindings_by_router_id.call_count) + def _test_get_device_owner(self, is_distributed=False, expected=const.DEVICE_OWNER_ROUTER_INTF, pass_router_id=True):