Merge "Support migrating of legacy routers to HA and back"
This commit is contained in:
commit
7e2d05a394
|
@ -23,6 +23,7 @@ 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_dvr_db
|
||||
|
@ -405,28 +406,47 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin):
|
|||
|
||||
def _update_router_db(self, context, router_id, data, gw_info):
|
||||
router_db = self._get_router(context, router_id)
|
||||
|
||||
original_distributed_state = router_db.extra_attributes.distributed
|
||||
original_ha_state = router_db.extra_attributes.ha
|
||||
if original_ha_state and data.get('distributed'):
|
||||
|
||||
requested_ha_state = data.pop('ha', None)
|
||||
requested_distributed_state = data.get('distributed', None)
|
||||
|
||||
if ((original_ha_state and requested_distributed_state) or
|
||||
(requested_ha_state and original_distributed_state) or
|
||||
(requested_ha_state and requested_distributed_state)):
|
||||
raise l3_ha.DistributedHARouterNotSupported()
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
router_db = super(L3_HA_NAT_db_mixin, self)._update_router_db(
|
||||
context, router_id, data, gw_info)
|
||||
|
||||
ha = data.pop('ha', None)
|
||||
ha_not_changed = ha is None or ha == original_ha_state
|
||||
ha_not_changed = (requested_ha_state is None or
|
||||
requested_ha_state == original_ha_state)
|
||||
if ha_not_changed:
|
||||
return router_db
|
||||
|
||||
if router_db.admin_state_up:
|
||||
msg = _('Cannot change HA attribute of active routers. Please '
|
||||
'set router admin_state_up to False prior to upgrade.')
|
||||
raise n_exc.BadRequest(resource='router', msg=msg)
|
||||
|
||||
ha_network = self.get_ha_network(context,
|
||||
router_db.tenant_id)
|
||||
router_db.extra_attributes.ha = ha
|
||||
if not ha:
|
||||
router_db.extra_attributes.ha = requested_ha_state
|
||||
if not requested_ha_state:
|
||||
self._delete_vr_id_allocation(
|
||||
context, ha_network, router_db.extra_attributes.ha_vr_id)
|
||||
router_db.extra_attributes.ha_vr_id = None
|
||||
|
||||
if ha:
|
||||
# The HA attribute has changed. First unbind the router from agents
|
||||
# to force a proper re-scheduling to agents.
|
||||
# TODO(jschwarz): This will have to be more selective to get HA + DVR
|
||||
# working (Only unbind from dvr_snat nodes).
|
||||
self._unbind_ha_router(context, router_id)
|
||||
|
||||
if requested_ha_state:
|
||||
if not ha_network:
|
||||
ha_network = self._create_ha_network(context,
|
||||
router_db.tenant_id)
|
||||
|
@ -452,6 +472,10 @@ class L3_HA_NAT_db_mixin(l3_dvr_db.L3_NAT_with_dvr_db_mixin):
|
|||
context, ha_network, router_db.extra_attributes.ha_vr_id)
|
||||
self._delete_ha_interfaces(context, router_db.id)
|
||||
|
||||
def _unbind_ha_router(self, context, router_id):
|
||||
for agent in self.get_l3_agents_hosting_routers(context, [router_id]):
|
||||
self.remove_router_from_l3_agent(context, agent['id'], router_id)
|
||||
|
||||
def get_ha_router_port_bindings(self, context, router_ids, host=None):
|
||||
if not router_ids:
|
||||
return []
|
||||
|
|
|
@ -21,7 +21,7 @@ from neutron.common import exceptions
|
|||
HA_INFO = 'ha'
|
||||
EXTENDED_ATTRIBUTES_2_0 = {
|
||||
'routers': {
|
||||
HA_INFO: {'allow_post': True, 'allow_put': False,
|
||||
HA_INFO: {'allow_post': True, 'allow_put': True,
|
||||
'default': attributes.ATTR_NOT_SPECIFIED, 'is_visible': True,
|
||||
'enforce_policy': True,
|
||||
'convert_to': attributes.convert_to_boolean_if_not_none}
|
||||
|
|
|
@ -19,6 +19,7 @@ from oslo_utils import uuidutils
|
|||
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
|
||||
|
@ -72,12 +73,20 @@ class L3HATestFramework(testlib_api.SqlTestCase):
|
|||
router['distributed'] = distributed
|
||||
return self.plugin.create_router(ctx, {'router': router})
|
||||
|
||||
def _update_router(self, router_id, ha=None, distributed=None, ctx=None):
|
||||
def _migrate_router(self, router_id, ha):
|
||||
self._update_router(router_id, admin_state=False)
|
||||
self._update_router(router_id, ha=ha)
|
||||
return self._update_router(router_id, admin_state=True)
|
||||
|
||||
def _update_router(self, router_id, ha=None, distributed=None, ctx=None,
|
||||
admin_state=None):
|
||||
if ctx is None:
|
||||
ctx = self.admin_ctx
|
||||
data = {'ha': ha} if ha is not None else {}
|
||||
if distributed is not None:
|
||||
data['distributed'] = distributed
|
||||
if admin_state is not None:
|
||||
data['admin_state_up'] = admin_state
|
||||
return self.plugin._update_router_db(ctx, router_id,
|
||||
data, None)
|
||||
|
||||
|
@ -190,7 +199,7 @@ class L3HATestCase(L3HATestFramework):
|
|||
router = self._create_router()
|
||||
self.assertTrue(router['ha'])
|
||||
|
||||
router = self._update_router(router['id'], ha=False)
|
||||
router = self._migrate_router(router['id'], False)
|
||||
self.assertFalse(router.extra_attributes['ha'])
|
||||
self.assertIsNone(router.extra_attributes['ha_vr_id'])
|
||||
|
||||
|
@ -198,10 +207,17 @@ class L3HATestCase(L3HATestFramework):
|
|||
router = self._create_router(ha=False)
|
||||
self.assertFalse(router['ha'])
|
||||
|
||||
router = self._update_router(router['id'], ha=True)
|
||||
router = self._migrate_router(router['id'], True)
|
||||
self.assertTrue(router.extra_attributes['ha'])
|
||||
self.assertIsNotNone(router.extra_attributes['ha_vr_id'])
|
||||
|
||||
def test_migration_requires_admin_state_down(self):
|
||||
router = self._create_router(ha=False)
|
||||
self.assertRaises(n_exc.BadRequest,
|
||||
self._update_router,
|
||||
router['id'],
|
||||
ha=True)
|
||||
|
||||
def test_migrate_ha_router_to_distributed(self):
|
||||
router = self._create_router()
|
||||
self.assertTrue(router['ha'])
|
||||
|
@ -211,6 +227,44 @@ class L3HATestCase(L3HATestFramework):
|
|||
router['id'],
|
||||
distributed=True)
|
||||
|
||||
def test_migrate_distributed_router_to_ha(self):
|
||||
router = self._create_router(ha=False, distributed=True)
|
||||
self.assertFalse(router['ha'])
|
||||
self.assertTrue(router['distributed'])
|
||||
|
||||
self.assertRaises(l3_ext_ha_mode.DistributedHARouterNotSupported,
|
||||
self._update_router,
|
||||
router['id'],
|
||||
ha=True)
|
||||
|
||||
def test_migrate_legacy_router_to_distributed_and_ha(self):
|
||||
router = self._create_router(ha=False, distributed=False)
|
||||
self.assertFalse(router['ha'])
|
||||
self.assertFalse(router['distributed'])
|
||||
|
||||
self.assertRaises(l3_ext_ha_mode.DistributedHARouterNotSupported,
|
||||
self._update_router,
|
||||
router['id'],
|
||||
ha=True,
|
||||
distributed=True)
|
||||
|
||||
def test_unbind_ha_router(self):
|
||||
router = self._create_router()
|
||||
self._bind_router(router['id'])
|
||||
|
||||
bound_agents = self.plugin.get_l3_agents_hosting_routers(
|
||||
self.admin_ctx, [router['id']])
|
||||
self.assertEqual(2, len(bound_agents))
|
||||
|
||||
with mock.patch.object(manager.NeutronManager,
|
||||
'get_service_plugins') as mock_manager:
|
||||
self.plugin._unbind_ha_router(self.admin_ctx, router['id'])
|
||||
|
||||
bound_agents = self.plugin.get_l3_agents_hosting_routers(
|
||||
self.admin_ctx, [router['id']])
|
||||
self.assertEqual(0, len(bound_agents))
|
||||
self.assertEqual(2, mock_manager.call_count)
|
||||
|
||||
def test_l3_agent_routers_query_interface(self):
|
||||
router = self._create_router()
|
||||
self._bind_router(router['id'])
|
||||
|
@ -251,7 +305,7 @@ class L3HATestCase(L3HATestFramework):
|
|||
else:
|
||||
self.assertIsNotNone(interface)
|
||||
|
||||
self._update_router(router['id'], to_ha)
|
||||
self._migrate_router(router['id'], to_ha)
|
||||
routers = self.plugin.get_ha_sync_data_for_host(self.admin_ctx)
|
||||
router = routers[0]
|
||||
interface = router.get(constants.HA_INTERFACE_KEY)
|
||||
|
@ -273,7 +327,7 @@ class L3HATestCase(L3HATestFramework):
|
|||
def test_update_router_to_ha_notifies_agent(self):
|
||||
router = self._create_router(ha=False)
|
||||
self.notif_m.reset_mock()
|
||||
self._update_router(router['id'], ha=True)
|
||||
self._migrate_router(router['id'], True)
|
||||
self.assertTrue(self.notif_m.called)
|
||||
|
||||
def test_unique_vr_id_between_routers(self):
|
||||
|
@ -333,7 +387,7 @@ class L3HATestCase(L3HATestFramework):
|
|||
allocs_before = self.plugin._get_allocated_vr_id(self.admin_ctx,
|
||||
network.network_id)
|
||||
router = self._create_router()
|
||||
self._update_router(router['id'], ha=False)
|
||||
self._migrate_router(router['id'], False)
|
||||
allocs_after = self.plugin._get_allocated_vr_id(self.admin_ctx,
|
||||
network.network_id)
|
||||
self.assertEqual(allocs_before, allocs_after)
|
||||
|
@ -617,7 +671,7 @@ class L3HAUserTestCase(L3HATestFramework):
|
|||
|
||||
def test_update_router(self):
|
||||
router = self._create_router(ctx=self.user_ctx)
|
||||
self._update_router(router['id'], ha=False, ctx=self.user_ctx)
|
||||
self._update_router(router['id'], ctx=self.user_ctx)
|
||||
|
||||
def test_delete_router(self):
|
||||
router = self._create_router(ctx=self.user_ctx)
|
||||
|
|
Loading…
Reference in New Issue