diff --git a/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py b/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py index ea4290c701a..ad3aac4abc2 100644 --- a/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py +++ b/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py @@ -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): diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py index a66cfb2034c..0642504d55b 100644 --- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py @@ -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"""