vmware-nsx/neutron/tests/unit/db/test_l3_dvr_db.py
Sylvain Afchain e4d5dad6a1 Add L3 VRRP HA base classes
Add L3 HA base classes on the plugin side. A new admin-only ha
attribute is added to the API router resource. Conversion from
or to HA router is possible. Each tenant gets a single network
used for HA traffic. The tenant_id for that network is set to
'' so that it isn't visible via the CLI or GUI. A new table
is added to map a tenant to its HA network. Specific HA
attributes are added to the extra router attributes table.
Finally, each HA router gets a port on the HA network, per
l3 agent it is scheduled on. A new table is added to track
these bindings. A new table is added in order to track
VRID allocations.

DVR integration is not expected to work. Any issues will
be reported as bugs and handled after the feature merges.
Migrating a router to HA or from HA works server side
but is not expected to work (Yet) agent side. This will be
dealt with as a bug in the future.

DocImpact
Partially-implements: blueprint l3-high-availability
Change-Id: I9d935cf5e0c231e8cb7af5f61b9a9fc552c3521e
Co-Authored-By: Assaf Muller <amuller@redhat.com>
2014-09-10 12:06:13 +00:00

234 lines
9.3 KiB
Python

# Copyright (c) 2014 OpenStack Foundation, all rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import contextlib
import mock
from neutron.common import constants as l3_const
from neutron import context
from neutron.db import l3_dvr_db
from neutron import manager
from neutron.openstack.common import uuidutils
from neutron.tests.unit import testlib_api
_uuid = uuidutils.generate_uuid
class L3DvrTestCase(testlib_api.SqlTestCase):
def setUp(self):
super(L3DvrTestCase, self).setUp()
self.ctx = context.get_admin_context()
self.mixin = l3_dvr_db.L3_NAT_with_dvr_db_mixin()
def _create_router(self, router):
with self.ctx.session.begin(subtransactions=True):
return self.mixin._create_router_db(self.ctx, router, 'foo_tenant')
def _test__create_router_db(self, expected=False, distributed=None):
router = {'name': 'foo_router', 'admin_state_up': True}
if distributed is not None:
router['distributed'] = distributed
result = self._create_router(router)
self.assertEqual(expected, result.extra_attributes['distributed'])
def test_create_router_db_default(self):
self._test__create_router_db(expected=False)
def test_create_router_db_centralized(self):
self._test__create_router_db(expected=False, distributed=False)
def test_create_router_db_distributed(self):
self._test__create_router_db(expected=True, distributed=True)
def test__validate_router_migration_on_router_update(self):
router = {
'name': 'foo_router',
'admin_state_up': True,
'distributed': True
}
router_db = self._create_router(router)
self.assertIsNone(self.mixin._validate_router_migration(
router_db, {'name': 'foo_router_2'}))
def test__validate_router_migration_raise_error(self):
router = {
'name': 'foo_router',
'admin_state_up': True,
'distributed': True
}
router_db = self._create_router(router)
self.assertRaises(NotImplementedError,
self.mixin._validate_router_migration,
router_db, {'distributed': False})
def test_update_router_db_centralized_to_distributed(self):
router = {'name': 'foo_router', 'admin_state_up': True}
distributed = {'distributed': True}
router_db = self._create_router(router)
router_id = router_db['id']
self.assertFalse(router_db.extra_attributes.distributed)
with mock.patch.object(self.mixin, '_update_distributed_attr') as f:
with mock.patch.object(self.mixin, '_get_router') as g:
g.return_value = router_db
router_db = self.mixin._update_router_db(
self.ctx, router_id, distributed, mock.ANY)
# Assert that the DB value has changed
self.assertTrue(router_db.extra_attributes.distributed)
self.assertEqual(1, f.call_count)
def _test_get_device_owner(self, is_distributed=False,
expected=l3_const.DEVICE_OWNER_ROUTER_INTF,
pass_router_id=True):
router = {
'name': 'foo_router',
'admin_state_up': True,
'distributed': is_distributed
}
router_db = self._create_router(router)
router_pass = router_db['id'] if pass_router_id else router_db
with mock.patch.object(self.mixin, '_get_router') as f:
f.return_value = router_db
result = self.mixin._get_device_owner(self.ctx, router_pass)
self.assertEqual(expected, result)
def test_get_device_owner_by_router_id(self):
self._test_get_device_owner()
def test__get_device_owner_centralized(self):
self._test_get_device_owner(pass_router_id=False)
def test__get_device_owner_distributed(self):
self._test_get_device_owner(
is_distributed=True,
expected=l3_dvr_db.DEVICE_OWNER_DVR_INTERFACE,
pass_router_id=False)
def _test__is_distributed_router(self, router, expected):
result = l3_dvr_db.is_distributed_router(router)
self.assertEqual(expected, result)
def test__is_distributed_router_by_db_object(self):
router = {'name': 'foo_router', 'admin_state_up': True}
router_db = self._create_router(router)
self.mixin._get_device_owner(mock.ANY, router_db)
def test__is_distributed_router_default(self):
router = {'id': 'foo_router_id'}
self._test__is_distributed_router(router, False)
def test__is_distributed_router_centralized(self):
router = {'id': 'foo_router_id', 'distributed': False}
self._test__is_distributed_router(router, False)
def test__is_distributed_router_distributed(self):
router = {'id': 'foo_router_id', 'distributed': True}
self._test__is_distributed_router(router, True)
def test_get_agent_gw_ports_exist_for_network(self):
with mock.patch.object(manager.NeutronManager, 'get_plugin') as gp:
plugin = mock.Mock()
gp.return_value = plugin
plugin.get_ports.return_value = []
self.mixin.get_agent_gw_ports_exist_for_network(
self.ctx, 'network_id', 'host', 'agent_id')
plugin.get_ports.assert_called_with(self.ctx, {
'network_id': ['network_id'],
'device_id': ['agent_id'],
'device_owner': [l3_const.DEVICE_OWNER_AGENT_GW]})
def test__create_gw_port_with_no_gateway(self):
router = {
'name': 'foo_router',
'admin_state_up': True,
'distributed': True,
}
router_db = self._create_router(router)
router_id = router_db['id']
self.assertTrue(router_db.extra_attributes.distributed)
with contextlib.nested(
mock.patch.object(l3_dvr_db.l3_db.L3_NAT_db_mixin,
'_create_gw_port'),
mock.patch.object(self.mixin,
'create_snat_intf_ports_if_not_exists')
) as (cw, cs):
self.mixin._create_gw_port(
self.ctx, router_id, router_db, mock.ANY)
self.assertFalse(cs.call_count)
def test_build_routers_list_with_gw_port_mismatch(self):
routers = [{'gw_port_id': 'foo_gw_port_id', 'id': 'foo_router_id'}]
gw_ports = {}
routers = self.mixin._build_routers_list(self.ctx, routers, gw_ports)
self.assertIsNone(routers[0].get('gw_port'))
def test_clear_unused_fip_agent_gw_port(self):
floatingip = {
'id': _uuid(),
'fixed_port_id': _uuid(),
'floating_network_id': _uuid()
}
fip_id = floatingip['id']
with contextlib.nested(
mock.patch.object(l3_dvr_db.l3_db.L3_NAT_db_mixin,
'_get_floatingip'),
mock.patch.object(self.mixin,
'get_vm_port_hostid'),
mock.patch.object(self.mixin,
'check_fips_availability_on_host'),
mock.patch.object(self.mixin,
'delete_floatingip_agent_gateway_port')
) as (gfips, gvm, cfips, dfips):
gfips.return_value = floatingip
gvm.return_value = 'my-host'
cfips.return_value = True
self.mixin.clear_unused_fip_agent_gw_port(
self.ctx, floatingip, fip_id)
self.assertTrue(dfips.called)
self.assertTrue(cfips.called)
self.assertTrue(gvm.called)
def _delete_floatingip_test_setup(self, floatingip):
fip_id = floatingip['id']
with contextlib.nested(
mock.patch.object(l3_dvr_db.l3_db.L3_NAT_db_mixin,
'_get_floatingip'),
mock.patch.object(self.mixin,
'clear_unused_fip_agent_gw_port'),
mock.patch.object(l3_dvr_db.l3_db.L3_NAT_db_mixin,
'delete_floatingip')) as (gf, vf, df):
gf.return_value = floatingip
self.mixin.delete_floatingip(self.ctx, fip_id)
return vf
def test_delete_floatingip_without_internal_port(self):
floatingip = {
'id': _uuid(),
'fixed_port_id': None,
'floating_network_id': _uuid()
}
mock_fip_clear = self._delete_floatingip_test_setup(floatingip)
self.assertFalse(mock_fip_clear.call_count)
def test_delete_floatingip_with_internal_port(self):
floatingip = {
'id': _uuid(),
'fixed_port_id': _uuid(),
'floating_network_id': _uuid()
}
mock_fip_clear = self._delete_floatingip_test_setup(floatingip)
self.assertTrue(mock_fip_clear.called)