[OVN] Handle OVN agents when "Chassis" register is deleted

If an "ovn-controller" ends not gracefully, the node "Chassis" and
"Chassis_Private" registers will remain in the OVN SB database.
Because there is no a mandatory procedure to delete the "Chassis"
and "Chassis_Private" registers, the administrator can manually
delete, from the OVN SB database, any register in any order.

If the "Chassis" register is deleted and the Neutron server restarted,
the updated "Chassis_Private" register will be read from the database.
That won't contain the "Chassis" information as this register has been
deleted. In this case, the ``NeutronAgent`` returns ``DeletedChassis``,
an empty chassis register with no information.

NOTE: the sequence of actions ("Chassis" register deletion, Neutron
server restart) must be follow to reproduce this issue. If the
"Chassis" register is deleted, the Neutron server OVN agent local cache
won't update the stored information and will keep the previous value.
It is when the Neutron server is restarted when the OVN agent local
cache is retrieved again; at this time the "Chassis_Private" register
won't have any related "Chassis" register.

Closes-Bug: #1951149

Conflicts:
  neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py

Change-Id: I17aa53cea6aba8ea83187c99102a6f25fd33cfff
(cherry picked from commit f1a5511e90)
(cherry picked from commit 29998538ce)
This commit is contained in:
Rodolfo Alonso Hernandez 2021-11-17 17:33:29 +00:00
parent 8418c3e1ce
commit 494cd8ca5d
2 changed files with 29 additions and 5 deletions

View File

@ -20,6 +20,12 @@ from neutron.common.ovn import constants as ovn_const
from neutron.common.ovn import utils as ovn_utils
class DeletedChassis(object):
external_ids = {}
hostname = '("Chassis" register deleted)'
name = '("Chassis" register deleted)'
class NeutronAgent(abc.ABC):
types = {}
@ -35,9 +41,12 @@ class NeutronAgent(abc.ABC):
def get_chassis(chassis_private):
try:
return chassis_private.chassis[0]
except (AttributeError, IndexError):
except AttributeError:
# No Chassis_Private support, just use Chassis
return chassis_private
except IndexError:
# Chassis register has been deleted but not Chassis_Private.
return DeletedChassis
@property
def updated_at(self):

View File

@ -846,12 +846,12 @@ class AgentWaitEvent(event.WaitEvent):
ONETIME = False
def __init__(self, driver, chassis_names):
def __init__(self, driver, chassis_names, events=None, timeout=None):
table = driver.agent_chassis_table
events = (self.ROW_CREATE,)
events = events or (self.ROW_CREATE,)
self.chassis_names = chassis_names
super().__init__(events, table, None)
self.event_name = 'AgentWaitEvent'
super().__init__(events, table, None, timeout=timeout)
self.event_name = "AgentWaitEvent"
def match_fn(self, event, row, old):
return row.name in self.chassis_names
@ -909,6 +909,21 @@ class TestAgentApi(base.TestOVNFunctionalBase):
self.context, filters={'host': self.host})]
self.assertCountEqual(list(self.agent_types.values()), agent_ids)
# "ovn-controller" ends without deleting "Chassis" and
# "Chassis_Private" registers. If "Chassis" register is deleted,
# then Chassis_Private.chassis = []; both metadata and controller
# agents will still be present in the agent list.
agent_event = AgentWaitEvent(self.mech_driver, [self.chassis],
events=(event.RowEvent.ROW_UPDATE,),
timeout=1)
self.sb_api.idl.notify_handler.watch_event(agent_event)
self.sb_api.chassis_del(self.chassis, if_exists=True).execute(
check_error=False)
agent_event.wait()
agent_ids = [a['id'] for a in self.plugin.get_agents(
self.context, filters={'host': self.host})]
self.assertCountEqual(list(self.agent_types.values()), agent_ids)
class ConnectionInactivityProbeSetEvent(event.WaitEvent):
"""Wait for a Connection (NB/SB) to have the inactivity probe set"""