Skip to content

Commit 669d7cc

Browse files
committed
[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 Change-Id: I17aa53cea6aba8ea83187c99102a6f25fd33cfff (cherry picked from commit f1a5511)
1 parent 0f84be3 commit 669d7cc

File tree

2 files changed

+46
-7
lines changed

2 files changed

+46
-7
lines changed

neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
from neutron.common import utils
2424

2525

26+
class DeletedChassis(object):
27+
external_ids = {}
28+
hostname = '("Chassis" register deleted)'
29+
name = '("Chassis" register deleted)'
30+
31+
2632
class NeutronAgent(abc.ABC):
2733
types = {}
2834

@@ -45,9 +51,12 @@ def update(self, chassis_private, updated_at=None, clear_down=False):
4551
def chassis_from_private(chassis_private):
4652
try:
4753
return chassis_private.chassis[0]
48-
except (AttributeError, IndexError):
54+
except AttributeError:
4955
# No Chassis_Private support, just use Chassis
5056
return chassis_private
57+
except IndexError:
58+
# Chassis register has been deleted but not Chassis_Private.
59+
return DeletedChassis
5160

5261
@property
5362
def chassis(self):

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

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -997,9 +997,9 @@ class AgentWaitEvent(event.WaitEvent):
997997

998998
ONETIME = False
999999

1000-
def __init__(self, driver, chassis_names):
1000+
def __init__(self, driver, chassis_names, events=None):
10011001
table = driver.agent_chassis_table
1002-
events = (self.ROW_CREATE,)
1002+
events = events or (self.ROW_CREATE,)
10031003
self.chassis_names = chassis_names
10041004
super().__init__(events, table, None)
10051005
self.event_name = "AgentWaitEvent"
@@ -1059,11 +1059,41 @@ def test_agent_list(self):
10591059
self.context, filters={'host': self.host})]
10601060
self.assertCountEqual(list(self.agent_types.values()), agent_ids)
10611061

1062+
# "ovn-controller" ends without deleting "Chassis" and
1063+
# "Chassis_Private" registers. If "Chassis" register is deleted,
1064+
# then Chassis_Private.chassis = []; both metadata and controller
1065+
# agents will still be present in the agent list.
1066+
agent_event = AgentWaitEvent(self.mech_driver, [self.chassis],
1067+
events=(event.RowEvent.ROW_UPDATE,))
1068+
self.sb_api.idl.notify_handler.watch_event(agent_event)
1069+
self.sb_api.chassis_del(self.chassis).execute(check_error=True)
1070+
self.assertTrue(agent_event.wait())
1071+
agent_ids = [a['id'] for a in self.plugin.get_agents(
1072+
self.context, filters={'host': self.host})]
1073+
self.assertCountEqual(list(self.agent_types.values()), agent_ids)
1074+
10621075
def test_agent_delete(self):
1063-
for agent_id in self.agent_types.values():
1064-
self.plugin.delete_agent(self.context, agent_id)
1065-
self.assertRaises(agent_exc.AgentNotFound, self.plugin.get_agent,
1066-
self.context, agent_id)
1076+
# Non OVN agent deletion.
1077+
agent_id = self.agent_types[self.TEST_AGENT]
1078+
self.plugin.delete_agent(self.context, agent_id)
1079+
self.assertRaises(agent_exc.AgentNotFound, self.plugin.get_agent,
1080+
self.context, agent_id)
1081+
1082+
# OVN controller agent deletion, that triggers the "Chassis" register
1083+
# deletion. The "Chassis" register deletion triggers the host OVN
1084+
# agents deletion, both controller and metadata if present.
1085+
controller_id = self.agent_types[ovn_const.OVN_CONTROLLER_AGENT]
1086+
metadata_id = self.agent_types[ovn_const.OVN_METADATA_AGENT]
1087+
self.plugin.delete_agent(self.context, controller_id)
1088+
self.assertRaises(agent_exc.AgentNotFound, self.plugin.get_agent,
1089+
self.context, controller_id)
1090+
self.assertEqual(
1091+
metadata_id,
1092+
self.plugin.get_agent(self.context, metadata_id)['id'])
1093+
1094+
self.plugin.delete_agent(self.context, metadata_id)
1095+
self.assertRaises(agent_exc.AgentNotFound, self.plugin.get_agent,
1096+
self.context, metadata_id)
10671097

10681098

10691099
class ConnectionInactivityProbeSetEvent(event.WaitEvent):

0 commit comments

Comments
 (0)