From ff021e7e4c7cc28bf4524313d85f521d6ed4c3eb 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 (cherry picked from commit f54cba053556a43d51ccd895cdf8232c51210299) --- neutron/db/l3_hamode_db.py | 36 ++++++++++++------- neutron/tests/unit/db/test_l3_hamode_db.py | 42 +++++++++++++++++++--- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/neutron/db/l3_hamode_db.py b/neutron/db/l3_hamode_db.py index 076ccc1f0e1..85a4f93e73b 100644 --- a/neutron/db/l3_hamode_db.py +++ b/neutron/db/l3_hamode_db.py @@ -468,21 +468,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 get_ha_router_port_bindings(self, context, router_ids, host=None): if not router_ids: diff --git a/neutron/tests/unit/db/test_l3_hamode_db.py b/neutron/tests/unit/db/test_l3_hamode_db.py index af05a0c8e11..659dbcc3314 100644 --- a/neutron/tests/unit/db/test_l3_hamode_db.py +++ b/neutron/tests/unit/db/test_l3_hamode_db.py @@ -18,10 +18,12 @@ from oslo_config import cfg from oslo_utils import timeutils 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 from neutron.common import constants +from neutron.common import exceptions as n_exc from neutron import context from neutron.db import agents_db from neutron.db import common_db_mixin @@ -574,23 +576,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):