From f54cba053556a43d51ccd895cdf8232c51210299 Mon Sep 17 00:00:00 2001 From: LIU Yulong Date: Tue, 8 Dec 2015 14:13:44 +0800 Subject: [PATCH] Catch known exceptions during deleting last HA router In some scenarios, for instance rally test create_and_delete_routers, it will get some exceptions, such as the network in use exception, during the router deleting api call, but actually the router has been deleted. There has race between HA router create and delete, if set more api and rpc worker race raises exception more frequently. Because the inconsistent error message was not useful for user, this patch will catch those know exceptions ObjectDeletedError, NetworkInUse when user delete last HA router. At the same time, when user create the first HA router, but because of the failure of HA network creation, the router will be deleted, then the deleting HA network will raise AttributeError, this patch also move HA network deleting procedure under ha_network exist check block. Change-Id: I8cda00c1e7caffc4dfb20a817a11c60736855bb5 Closes-Bug: #1523780 Related-Bug: #1367157 --- neutron/db/l3_hamode_db.py | 36 ++++++++++++------- neutron/tests/unit/db/test_l3_hamode_db.py | 41 +++++++++++++++++++--- 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/neutron/db/l3_hamode_db.py b/neutron/db/l3_hamode_db.py index 43a0be74ec1..01e9791514e 100644 --- a/neutron/db/l3_hamode_db.py +++ b/neutron/db/l3_hamode_db.py @@ -493,21 +493,31 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin, self._delete_vr_id_allocation( context, ha_network, router_db.extra_attributes.ha_vr_id) self._delete_ha_interfaces(context, router_db.id) - try: + + # In case that create HA router failed because of the failure + # in HA network creation. So here put this deleting HA network + # procedure under 'if ha_network' block. if not self._ha_routers_present(context, router_db.tenant_id): - self._delete_ha_network(context, ha_network) - LOG.info(_LI("HA network %(network)s was deleted as " - "no HA routers are present in tenant " - "%(tenant)s."), - {'network': ha_network.network_id, - 'tenant': router_db.tenant_id}) - except n_exc.NetworkNotFound: - LOG.debug("HA network %s was already deleted.", - ha_network.network_id) - except sa.exc.InvalidRequestError: - LOG.info(_LI("HA network %s can not be deleted."), - ha_network.network_id) + try: + self._delete_ha_network(context, ha_network) + except (n_exc.NetworkNotFound, + orm.exc.ObjectDeletedError): + LOG.debug( + "HA network for tenant %s was already deleted.", + router_db.tenant_id) + except sa.exc.InvalidRequestError: + LOG.info(_LI("HA network %s can not be deleted."), + ha_network.network_id) + except n_exc.NetworkInUse: + LOG.debug("HA network %s is still in use.", + ha_network.network_id) + else: + LOG.info(_LI("HA network %(network)s was deleted as " + "no HA routers are present in tenant " + "%(tenant)s."), + {'network': ha_network.network_id, + 'tenant': router_db.tenant_id}) def _unbind_ha_router(self, context, router_id): for agent in self.get_l3_agents_hosting_routers(context, [router_id]): diff --git a/neutron/tests/unit/db/test_l3_hamode_db.py b/neutron/tests/unit/db/test_l3_hamode_db.py index 9a1133651e4..8e22257eeb2 100644 --- a/neutron/tests/unit/db/test_l3_hamode_db.py +++ b/neutron/tests/unit/db/test_l3_hamode_db.py @@ -16,6 +16,7 @@ import mock from oslo_config import cfg from oslo_utils import uuidutils import sqlalchemy as sa +from sqlalchemy import orm from neutron.api.rpc.handlers import l3_rpc from neutron.api.v2 import attributes @@ -617,23 +618,53 @@ class L3HATestCase(L3HATestFramework): self.assertNotIn('HA network tenant %s' % router1['tenant_id'], nets_after) - def test_ha_network_is_not_deleted_if_another_ha_router_is_created(self): - # If another router was created during deletion of current router, - # _delete_ha_network will fail with InvalidRequestError. Check that HA - # network won't be deleted. + def _test_ha_network_is_not_deleted_raise_exception(self, exception): router1 = self._create_router() nets_before = [net['name'] for net in self.core_plugin.get_networks(self.admin_ctx)] self.assertIn('HA network tenant %s' % router1['tenant_id'], nets_before) with mock.patch.object(self.plugin, '_delete_ha_network', - side_effect=sa.exc.InvalidRequestError): + side_effect=exception): self.plugin.delete_router(self.admin_ctx, router1['id']) nets_after = [net['name'] for net in self.core_plugin.get_networks(self.admin_ctx)] self.assertIn('HA network tenant %s' % router1['tenant_id'], nets_after) + def test_ha_network_is_not_deleted_if_another_ha_router_is_created(self): + # If another router was created during deletion of current router, + # _delete_ha_network will fail with InvalidRequestError. Check that HA + # network won't be deleted. + self._test_ha_network_is_not_deleted_raise_exception( + sa.exc.InvalidRequestError) + + def test_ha_network_is_not_deleted_if_network_in_use(self): + self._test_ha_network_is_not_deleted_raise_exception( + n_exc.NetworkInUse(net_id="foo_net_id")) + + def test_ha_network_is_not_deleted_if_db_deleted_error(self): + self._test_ha_network_is_not_deleted_raise_exception( + orm.exc.ObjectDeletedError(None)) + + def test_ha_router_create_failed_no_ha_network_delete(self): + tenant_id = "foo_tenant_id" + nets_before = self.core_plugin.get_networks(self.admin_ctx) + self.assertNotIn('HA network tenant %s' % tenant_id, + nets_before) + + # Unable to create HA network + with mock.patch.object(self.core_plugin, 'create_network', + side_effect=n_exc.NoNetworkAvailable): + self.assertRaises(n_exc.NoNetworkAvailable, + self._create_router, + True, + tenant_id) + nets_after = self.core_plugin.get_networks(self.admin_ctx) + self.assertEqual(nets_before, nets_after) + self.assertNotIn('HA network tenant %s' % tenant_id, + nets_after) + class L3HAModeDbTestCase(L3HATestFramework):