From 478012a397029077e8444fb2872a78988d492f74 Mon Sep 17 00:00:00 2001 From: sridhargaddam Date: Thu, 30 Jul 2015 10:54:39 +0000 Subject: [PATCH] Delete HA network when last HA router is deleted Currently when the last HA router of a tenant is deleted the HA network belonging to this tenant is not removed. While running tempest aganist an OpenStack setup where tenant VLANs (with small VLAN range) is used we hit the limits are tempest tests start to fail as we cannot create new networks. This patch addresses this issue by deleting the HA network when the last HA router is deleted for the tenant. Closes-Bug: #1367157 Co-Authored-By: Ann Kamyshnikova Change-Id: I1d50b973aed4148857ac3d2bbee0d38e2e199783 (cherry picked from commit 853f7d7a74a2281b0cd34c337362e73635e501a2) --- neutron/db/l3_hamode_db.py | 33 +++++++++++ neutron/tests/unit/db/test_l3_hamode_db.py | 69 ++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/neutron/db/l3_hamode_db.py b/neutron/db/l3_hamode_db.py index 7b286869e1a..e25eea014ee 100644 --- a/neutron/db/l3_hamode_db.py +++ b/neutron/db/l3_hamode_db.py @@ -23,8 +23,11 @@ from sqlalchemy import orm from neutron.api.v2 import attributes from neutron.common import constants +from neutron.common import exceptions as n_exc from neutron.common import utils as n_utils from neutron.db import agents_db +from neutron.db import l3_attrs_db +from neutron.db import l3_db from neutron.db import l3_dvr_db from neutron.db import model_base from neutron.db import models_v2 @@ -439,6 +442,21 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin): return router_db + def _delete_ha_network(self, context, net): + admin_ctx = context.elevated() + self._core_plugin.delete_network(admin_ctx, net.network_id) + + def _ha_routers_present(self, context, tenant_id): + ha = True + routers = context.session.query(l3_db.Router).filter( + l3_db.Router.tenant_id == tenant_id).subquery() + ha_routers = context.session.query( + l3_attrs_db.RouterExtraAttributes).join( + routers, + l3_attrs_db.RouterExtraAttributes.router_id == routers.c.id + ).filter(l3_attrs_db.RouterExtraAttributes.ha == ha).first() + return ha_routers is not None + def delete_router(self, context, id): router_db = self._get_router(context, id) super(L3_HA_NAT_db_mixin, self).delete_router(context, id) @@ -450,6 +468,21 @@ 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: + 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) 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 69a6826dfe8..3daab316a93 100644 --- a/neutron/tests/unit/db/test_l3_hamode_db.py +++ b/neutron/tests/unit/db/test_l3_hamode_db.py @@ -17,6 +17,7 @@ import mock from oslo_config import cfg from oslo_utils import timeutils from oslo_utils import uuidutils +import sqlalchemy as sa from neutron.api.rpc.handlers import l3_rpc from neutron.api.v2 import attributes @@ -503,6 +504,74 @@ class L3HATestCase(L3HATestFramework): self.plugin.get_number_of_agents_for_scheduling, self.admin_ctx) + def test_ha_network_deleted_if_no_ha_router_present_two_tenants(self): + # Create two routers in different tenants. + router1 = self._create_router() + router2 = self._create_router(tenant_id='tenant2') + nets_before = [net['name'] for net in + self.core_plugin.get_networks(self.admin_ctx)] + # Check that HA networks created for each tenant + self.assertIn('HA network tenant %s' % router1['tenant_id'], + nets_before) + self.assertIn('HA network tenant %s' % router2['tenant_id'], + nets_before) + # Delete router1 + self.plugin.delete_router(self.admin_ctx, router1['id']) + nets_after = [net['name'] for net in + self.core_plugin.get_networks(self.admin_ctx)] + # Check that HA network for tenant1 is deleted and for tenant2 is not. + self.assertNotIn('HA network tenant %s' % router1['tenant_id'], + nets_after) + self.assertIn('HA network tenant %s' % router2['tenant_id'], + nets_after) + + def test_ha_network_is_not_delete_if_ha_router_is_present(self): + # Create 2 routers in one tenant and check if one is deleted, HA + # network still exists. + router1 = self._create_router() + router2 = 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) + self.plugin.delete_router(self.admin_ctx, router2['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_delete_ha_and_non_ha_router(self): + # Create HA and non-HA router. Check after deletion HA router HA + # network is deleted. + router1 = self._create_router(ha=False) + router2 = 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) + self.plugin.delete_router(self.admin_ctx, router2['id']) + nets_after = [net['name'] for net in + self.core_plugin.get_networks(self.admin_ctx)] + 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. + 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): + 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) + class L3HAModeDbTestCase(L3HATestFramework):