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):