Keep HA ports info for HA router during entire lifecycle

Once HA port is set, it must remain this value no matter
what the server return. Because there is race condition
between l3-agent side sync router info for processing
and server side router deleting.

This patch adds a helper function for every ha_port set
action. If the ha_port is not None, it will always stay
with original value.

Conflicts:
	neutron/tests/unit/agent/l3/test_ha_router.py

Closes-Bug: #1826726
Change-Id: I96a088d25048be02a9c5b12c1d087df075b36fc4
(cherry picked from commit 45957f12c8)
(cherry picked from commit 13cb3cd34c)
This commit is contained in:
LIU Yulong 2019-04-25 15:25:34 +08:00 committed by Swaminathan Vasudevan
parent c4fa45d0b3
commit e106f2e16f
2 changed files with 44 additions and 2 deletions

View File

@ -113,7 +113,7 @@ class HaRouter(router.RouterInfo):
raise Exception(msg)
super(HaRouter, self).initialize(process_monitor)
self.ha_port = ha_port
self.set_ha_port()
self._init_keepalived_manager(process_monitor)
self.ha_network_added()
self.update_initial_state(self.state_change_callback)
@ -443,10 +443,23 @@ class HaRouter(router.RouterInfo):
self.ha_network_removed()
super(HaRouter, self).delete()
def set_ha_port(self):
ha_port = self.router.get(n_consts.HA_INTERFACE_KEY)
if not ha_port:
return
# NOTE: once HA port is set, it MUST remain this value no matter what
# the server return. Because there is race condition between l3-agent
# side sync router info for processing and server side router deleting.
# TODO(liuyulong): make sure router HA ports never change.
if not self.ha_port or (self.ha_port and
self.ha_port['status'] != ha_port['status']):
self.ha_port = ha_port
def process(self):
super(HaRouter, self).process()
self.ha_port = self.router.get(n_consts.HA_INTERFACE_KEY)
self.set_ha_port()
LOG.debug("Processing HA router with HA port: %s", self.ha_port)
if (self.ha_port and
self.ha_port['status'] == n_consts.PORT_STATUS_ACTIVE):
self.enable_keepalived()

View File

@ -15,6 +15,7 @@
import signal
import mock
from neutron_lib import constants as n_consts
from oslo_utils import uuidutils
from neutron.agent.l3 import ha_router
@ -117,3 +118,31 @@ class TestBasicRouterOperations(base.BaseTestCase):
calls = ["sig='str(%d)'" % signal.SIGTERM,
"sig='str(%d)'" % signal.SIGKILL]
mock_pm.disable.has_calls(calls)
def test_set_ha_port(self):
ri = self._create_router()
self.assertIsNone(ri.ha_port)
ri.router = {}
ri.set_ha_port()
self.assertIsNone(ri.ha_port)
# HA_INTERFACE_KEY from None to some value
ri.router = {n_consts.HA_INTERFACE_KEY: {"id": _uuid(),
"status": "DOWN"}}
ri.set_ha_port()
self.assertIsNotNone(ri.ha_port)
self.assertEqual('DOWN', ri.ha_port["status"])
# HA port state change
ri.router = {n_consts.HA_INTERFACE_KEY: {"id": _uuid(),
"status": "ACTIVE"}}
ri.set_ha_port()
self.assertIsNotNone(ri.ha_port)
self.assertEqual('ACTIVE', ri.ha_port["status"])
ri.router = {}
ri.set_ha_port()
# neutron server return empty HA_INTERFACE_KEY, but
# agent side router info should remain the original value.
self.assertIsNotNone(ri.ha_port)