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 f54cba0535)
This commit is contained in:
LIU Yulong 2015-12-08 14:13:44 +08:00 committed by Assaf Muller
parent 4270a6f5a1
commit ff021e7e4c
2 changed files with 60 additions and 18 deletions

View File

@ -468,21 +468,31 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin):
self._delete_vr_id_allocation( self._delete_vr_id_allocation(
context, ha_network, router_db.extra_attributes.ha_vr_id) context, ha_network, router_db.extra_attributes.ha_vr_id)
self._delete_ha_interfaces(context, router_db.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, if not self._ha_routers_present(context,
router_db.tenant_id): router_db.tenant_id):
self._delete_ha_network(context, ha_network) try:
LOG.info(_LI("HA network %(network)s was deleted as " self._delete_ha_network(context, ha_network)
"no HA routers are present in tenant " except (n_exc.NetworkNotFound,
"%(tenant)s."), orm.exc.ObjectDeletedError):
{'network': ha_network.network_id, LOG.debug(
'tenant': router_db.tenant_id}) "HA network for tenant %s was already deleted.",
except n_exc.NetworkNotFound: router_db.tenant_id)
LOG.debug("HA network %s was already deleted.", except sa.exc.InvalidRequestError:
ha_network.network_id) LOG.info(_LI("HA network %s can not be deleted."),
except sa.exc.InvalidRequestError: ha_network.network_id)
LOG.info(_LI("HA network %s can not be deleted."), except n_exc.NetworkInUse:
ha_network.network_id) 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): def get_ha_router_port_bindings(self, context, router_ids, host=None):
if not router_ids: if not router_ids:

View File

@ -18,10 +18,12 @@ from oslo_config import cfg
from oslo_utils import timeutils from oslo_utils import timeutils
from oslo_utils import uuidutils from oslo_utils import uuidutils
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import orm
from neutron.api.rpc.handlers import l3_rpc from neutron.api.rpc.handlers import l3_rpc
from neutron.api.v2 import attributes from neutron.api.v2 import attributes
from neutron.common import constants from neutron.common import constants
from neutron.common import exceptions as n_exc
from neutron import context from neutron import context
from neutron.db import agents_db from neutron.db import agents_db
from neutron.db import common_db_mixin from neutron.db import common_db_mixin
@ -574,23 +576,53 @@ class L3HATestCase(L3HATestFramework):
self.assertNotIn('HA network tenant %s' % router1['tenant_id'], self.assertNotIn('HA network tenant %s' % router1['tenant_id'],
nets_after) nets_after)
def test_ha_network_is_not_deleted_if_another_ha_router_is_created(self): def _test_ha_network_is_not_deleted_raise_exception(self, exception):
# 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.
router1 = self._create_router() router1 = self._create_router()
nets_before = [net['name'] for net in nets_before = [net['name'] for net in
self.core_plugin.get_networks(self.admin_ctx)] self.core_plugin.get_networks(self.admin_ctx)]
self.assertIn('HA network tenant %s' % router1['tenant_id'], self.assertIn('HA network tenant %s' % router1['tenant_id'],
nets_before) nets_before)
with mock.patch.object(self.plugin, '_delete_ha_network', 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']) self.plugin.delete_router(self.admin_ctx, router1['id'])
nets_after = [net['name'] for net in nets_after = [net['name'] for net in
self.core_plugin.get_networks(self.admin_ctx)] self.core_plugin.get_networks(self.admin_ctx)]
self.assertIn('HA network tenant %s' % router1['tenant_id'], self.assertIn('HA network tenant %s' % router1['tenant_id'],
nets_after) 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): class L3HAModeDbTestCase(L3HATestFramework):