Browse Source

Fix migration from the HA to non-HA routers

In case if during switching HA router to be down, there will be any
failure, router_info will be stored in L3 agent's cache as HaRouter.
In case when next update on the router is migration to non-HA router
this is wrong class and it causes other issues, e.g. with
remove_vip_by_ip_address() which is correct only for HA routers.

This patch fixes that issue by adding check of the router's ha and
distributed flags and update local cache with new router_info class
in case if at least one of those flags don't match.

Change-Id: Ib0d3a501f88c149baea7d715c7cfe5811bc85e4f
Closes-Bug: #1892846
(cherry picked from commit 489e0ead72)
changes/47/763347/1
Slawek Kaplonski 9 months ago
parent
commit
512e5fa8ed
2 changed files with 77 additions and 0 deletions
  1. +17
    -0
      neutron/agent/l3/agent.py
  2. +60
    -0
      neutron/tests/unit/agent/l3/test_agent.py

+ 17
- 0
neutron/agent/l3/agent.py View File

@ -549,6 +549,23 @@ class L3NATAgent(ha.AgentMixin,
def _process_updated_router(self, router):
ri = self.router_info[router['id']]
router_ha = router.get('ha')
router_distributed = router.get('distributed')
if ((router_ha is not None and ri.router.get('ha') != router_ha) or
(router_distributed is not None and
ri.router.get('distributed') != router_distributed)):
LOG.warning('Type of the router %(id)s changed. '
'Old type: ha=%(old_ha)s; distributed=%(old_dvr)s; '
'New type: ha=%(new_ha)s; distributed=%(new_dvr)s',
{'id': router['id'],
'old_ha': ri.router.get('ha'),
'old_dvr': ri.router.get('distributed'),
'new_ha': router.get('ha'),
'new_dvr': router.get('distributed')})
ri = self._create_router(router['id'], router)
self.router_info[router['id']] = ri
is_dvr_snat_agent = (self.conf.agent_mode ==
lib_const.L3_AGENT_MODE_DVR_SNAT)
is_dvr_only_agent = (self.conf.agent_mode in


+ 60
- 0
neutron/tests/unit/agent/l3/test_agent.py View File

@ -2798,6 +2798,16 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
router.distributed = True
router.ha = True
router_info = mock.MagicMock()
def mock_get(name):
if name == 'ha':
return router.ha
if name == 'distributed':
return router.distributed
return mock.Mock()
router_info.router.get.side_effect = mock_get
agent.router_info[router.id] = router_info
updated_router = {'id': '1234',
'distributed': True,
@ -2830,6 +2840,16 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
router._ha_interface = True
router.ha = True
router_info = mock.MagicMock()
def mock_get(name):
if name == 'ha':
return router.ha
if name == 'distributed':
return router.distributed
return mock.Mock()
router_info.router.get.side_effect = mock_get
agent.router_info[router.id] = router_info
updated_router = {'id': '1234',
'distributed': True, 'ha': True,
@ -2908,6 +2928,46 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
agent._process_router_if_compatible(router)
self.assertIn(router['id'], agent.router_info)
def test_process_router_if_compatible_type_match(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = {'id': _uuid(),
'routes': [],
'admin_state_up': True,
'ha': False, 'distributed': False,
'external_gateway_info': {'network_id': 'aaa'}}
ri = mock.Mock(router=router)
agent.router_info[router['id']] = ri
with mock.patch.object(agent, "_create_router") as create_router_mock:
agent._process_router_if_compatible(router)
create_router_mock.assert_not_called()
self.assertIn(router['id'], agent.router_info)
self.assertFalse(agent.router_info[router['id']].router['ha'])
self.assertFalse(agent.router_info[router['id']].router['distributed'])
def test_process_router_if_compatible_type_changed(self):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
router = {'id': _uuid(),
'routes': [],
'admin_state_up': True,
'revision_number': 1,
'ha': True, 'distributed': False,
'external_gateway_info': {'network_id': 'aaa'}}
ri = mock.Mock(router=router)
agent.router_info[router['id']] = ri
new_router = copy.deepcopy(router)
new_router['ha'] = False
with mock.patch.object(agent, "_create_router") as create_router_mock:
agent._process_router_if_compatible(new_router)
create_router_mock.assert_called_once_with(
new_router['id'], new_router)
self.assertIn(router['id'], agent.router_info)
self.assertFalse(agent.router_info[router['id']].router['ha'])
self.assertFalse(agent.router_info[router['id']].router['distributed'])
def test_nonexistent_interface_driver(self):
self.conf.set_override('interface_driver', None)
self.assertRaises(SystemExit, l3_agent.L3NATAgent,


Loading…
Cancel
Save