[OVN] Use the Chassis_Private table for agents healthcheck
The core OVN team has introduced a new table called Chassis_Private to avoid nb_cfg flooding when checking for the Chassis' status. The OVN driver does rely on that mechanism for the agent liveness mechanism. This patch makes use of this new table but it's also backward compatible. For more information, check the core OVN changes at: https://patchwork.ozlabs.org/patch/1254394. Closes-Bug: #1892477 Change-Id: Iea4263b852d1e3f81eb2557918ea3cbb7adb8016 Signed-off-by: Lucas Alvares Gomes <lucasagomes@gmail.com>
This commit is contained in:
parent
b4dad5bb3d
commit
1dddbbfc92
@ -129,7 +129,7 @@ class PortBindingChassisDeletedEvent(PortBindingChassisEvent):
|
||||
return False
|
||||
|
||||
|
||||
class ChassisCreateEvent(row_event.RowEvent):
|
||||
class ChassisCreateEventBase(row_event.RowEvent):
|
||||
"""Row create event - Chassis name == our_chassis.
|
||||
|
||||
On connection, we get a dump of all chassis so if we catch a creation
|
||||
@ -137,14 +137,14 @@ class ChassisCreateEvent(row_event.RowEvent):
|
||||
to do a full sync to make sure that we capture all changes while the
|
||||
connection to OVSDB was down.
|
||||
"""
|
||||
table = None
|
||||
|
||||
def __init__(self, metadata_agent):
|
||||
self.agent = metadata_agent
|
||||
self.first_time = True
|
||||
table = 'Chassis'
|
||||
events = (self.ROW_CREATE,)
|
||||
super(ChassisCreateEvent, self).__init__(
|
||||
events, table, (('name', '=', self.agent.chassis),))
|
||||
super(ChassisCreateEventBase, self).__init__(
|
||||
events, self.table, (('name', '=', self.agent.chassis),))
|
||||
self.event_name = self.__class__.__name__
|
||||
|
||||
def run(self, event, row, old):
|
||||
@ -159,6 +159,14 @@ class ChassisCreateEvent(row_event.RowEvent):
|
||||
self.agent.sync()
|
||||
|
||||
|
||||
class ChassisCreateEvent(ChassisCreateEventBase):
|
||||
table = 'Chassis'
|
||||
|
||||
|
||||
class ChassisPrivateCreateEvent(ChassisCreateEventBase):
|
||||
table = 'Chassis_Private'
|
||||
|
||||
|
||||
class SbGlobalUpdateEvent(row_event.RowEvent):
|
||||
"""Row update event on SB_Global table."""
|
||||
|
||||
@ -170,8 +178,12 @@ class SbGlobalUpdateEvent(row_event.RowEvent):
|
||||
self.event_name = self.__class__.__name__
|
||||
|
||||
def run(self, event, row, old):
|
||||
self.agent.sb_idl.update_metadata_health_status(
|
||||
self.agent.chassis, row.nb_cfg).execute()
|
||||
table = ('Chassis_Private' if self.agent.has_chassis_private
|
||||
else 'Chassis')
|
||||
self.agent.sb_idl.db_set(
|
||||
table, self.agent.chassis, ('external_ids', {
|
||||
ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY:
|
||||
str(row.nb_cfg)})).execute()
|
||||
|
||||
|
||||
class MetadataAgent(object):
|
||||
@ -207,13 +219,26 @@ class MetadataAgent(object):
|
||||
proxy = metadata_server.UnixDomainMetadataProxy(self.conf)
|
||||
proxy.run()
|
||||
|
||||
tables = ('Encap', 'Port_Binding', 'Datapath_Binding', 'SB_Global',
|
||||
'Chassis')
|
||||
events = (PortBindingChassisCreatedEvent(self),
|
||||
PortBindingChassisDeletedEvent(self),
|
||||
SbGlobalUpdateEvent(self))
|
||||
|
||||
# TODO(lucasagomes): Remove this in the future. Try to register
|
||||
# the Chassis_Private table, if not present, fallback to the normal
|
||||
# Chassis table.
|
||||
# Open the connection to OVN SB database.
|
||||
self.sb_idl = ovsdb.MetadataAgentOvnSbIdl(
|
||||
chassis=self.chassis,
|
||||
events=[PortBindingChassisCreatedEvent(self),
|
||||
PortBindingChassisDeletedEvent(self),
|
||||
ChassisCreateEvent(self),
|
||||
SbGlobalUpdateEvent(self)]).start()
|
||||
self.has_chassis_private = False
|
||||
try:
|
||||
self.sb_idl = ovsdb.MetadataAgentOvnSbIdl(
|
||||
chassis=self.chassis, tables=tables + ('Chassis_Private', ),
|
||||
events=events + (ChassisPrivateCreateEvent(self), )).start()
|
||||
self.has_chassis_private = True
|
||||
except AssertionError:
|
||||
self.sb_idl = ovsdb.MetadataAgentOvnSbIdl(
|
||||
chassis=self.chassis, tables=tables,
|
||||
events=events + (ChassisCreateEvent(self), )).start()
|
||||
|
||||
# Do the initial sync.
|
||||
self.sync()
|
||||
@ -226,9 +251,10 @@ class MetadataAgent(object):
|
||||
def register_metadata_agent(self):
|
||||
# NOTE(lucasagomes): db_add() will not overwrite the UUID if
|
||||
# it's already set.
|
||||
table = ('Chassis_Private' if self.has_chassis_private else 'Chassis')
|
||||
ext_ids = {
|
||||
ovn_const.OVN_AGENT_METADATA_ID_KEY: uuidutils.generate_uuid()}
|
||||
self.sb_idl.db_add('Chassis', self.chassis, 'external_ids',
|
||||
self.sb_idl.db_add(table, self.chassis, 'external_ids',
|
||||
ext_ids).execute(check_error=True)
|
||||
|
||||
def _get_own_chassis_name(self):
|
||||
|
@ -38,8 +38,10 @@ class MetadataAgentOvnSbIdl(ovsdb_monitor.OvnIdl):
|
||||
helper.register_table(table)
|
||||
super(MetadataAgentOvnSbIdl, self).__init__(
|
||||
None, connection_string, helper)
|
||||
if chassis and 'Chassis' in tables:
|
||||
self.tables['Chassis'].condition = [['name', '==', chassis]]
|
||||
if chassis:
|
||||
table = ('Chassis_Private' if 'Chassis_Private' in tables
|
||||
else 'Chassis')
|
||||
self.tables[table].condition = [['name', '==', chassis]]
|
||||
if events:
|
||||
self.notify_handler.watch_events(events)
|
||||
|
||||
|
@ -28,8 +28,13 @@ class NeutronAgent(abc.ABC):
|
||||
for _type in cls.types:
|
||||
NeutronAgent.types[_type] = cls
|
||||
|
||||
def __init__(self, chassis):
|
||||
self.chassis = chassis
|
||||
def __init__(self, chassis_private):
|
||||
self.chassis_private = chassis_private
|
||||
try:
|
||||
self.chassis = self.chassis_private.chassis[0]
|
||||
except (AttributeError, IndexError):
|
||||
# No Chassis_Private support, just use Chassis
|
||||
self.chassis = self.chassis_private
|
||||
|
||||
@property
|
||||
def updated_at(self):
|
||||
@ -58,8 +63,8 @@ class NeutronAgent(abc.ABC):
|
||||
'admin_state_up': True}
|
||||
|
||||
@classmethod
|
||||
def from_type(cls, _type, chassis):
|
||||
return cls.types[_type](chassis)
|
||||
def from_type(cls, _type, chassis_private):
|
||||
return cls.types[_type](chassis_private)
|
||||
|
||||
@staticmethod
|
||||
def agent_types():
|
||||
@ -85,15 +90,15 @@ class ControllerAgent(NeutronAgent):
|
||||
|
||||
@property
|
||||
def nb_cfg(self):
|
||||
return self.chassis.nb_cfg
|
||||
return self.chassis_private.nb_cfg
|
||||
|
||||
@property
|
||||
def agent_id(self):
|
||||
return self.chassis.name
|
||||
return self.chassis_private.name
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
return self.chassis.external_ids.get(
|
||||
return self.chassis_private.external_ids.get(
|
||||
ovn_const.OVN_AGENT_DESC_KEY, '')
|
||||
|
||||
|
||||
@ -105,15 +110,15 @@ class MetadataAgent(NeutronAgent):
|
||||
|
||||
@property
|
||||
def nb_cfg(self):
|
||||
return int(self.chassis.external_ids.get(
|
||||
return int(self.chassis_private.external_ids.get(
|
||||
ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY, 0))
|
||||
|
||||
@property
|
||||
def agent_id(self):
|
||||
return self.chassis.external_ids.get(
|
||||
return self.chassis_private.external_ids.get(
|
||||
ovn_const.OVN_AGENT_METADATA_ID_KEY)
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
return self.chassis.external_ids.get(
|
||||
return self.chassis_private.external_ids.get(
|
||||
ovn_const.OVN_AGENT_METADATA_DESC_KEY, '')
|
||||
|
@ -120,6 +120,9 @@ class OVNMechanismDriver(api.MechanismDriver):
|
||||
self.subscribe()
|
||||
self.qos_driver = qos_driver.OVNQosDriver.create(self)
|
||||
self.trunk_driver = trunk_driver.OVNTrunkDriver.create(self)
|
||||
# The agent_chassis_table will be changed to Chassis_Private if it
|
||||
# exists, we need to have a connection in order to check that.
|
||||
self.agent_chassis_table = 'Chassis'
|
||||
|
||||
@property
|
||||
def _plugin(self):
|
||||
@ -262,6 +265,9 @@ class OVNMechanismDriver(api.MechanismDriver):
|
||||
self._nb_ovn, self._sb_ovn = impl_idl_ovn.get_ovn_idls(
|
||||
self, trigger, binding_events=not is_maintenance)
|
||||
|
||||
if self._sb_ovn.is_table_present('Chassis_Private'):
|
||||
self.agent_chassis_table = 'Chassis_Private'
|
||||
|
||||
# AGENTS must be populated after fork so if ovn-controller is stopped
|
||||
# before a worker handles a get_agents request, we still show agents
|
||||
populate_agents(self)
|
||||
@ -1105,14 +1111,15 @@ class OVNMechanismDriver(api.MechanismDriver):
|
||||
def mark_agent_alive(self, agent):
|
||||
# Update the time of our successful check
|
||||
value = timeutils.utcnow(with_timezone=True).isoformat()
|
||||
self._sb_ovn.db_set('Chassis', agent.chassis.uuid,
|
||||
('external_ids', {agent.key: value})).execute(
|
||||
check_error=True)
|
||||
self._sb_ovn.db_set(
|
||||
self.agent_chassis_table, agent.chassis_private.uuid,
|
||||
('external_ids', {agent.key: value})).execute(check_error=True)
|
||||
|
||||
def agents_from_chassis(self, chassis, update_db=True):
|
||||
def agents_from_chassis(self, chassis_private, update_db=True):
|
||||
agent_dict = {}
|
||||
# Iterate over each unique Agent subclass
|
||||
for agent in [a(chassis) for a in n_agent.NeutronAgent.agent_types()]:
|
||||
for agent in [a(chassis_private)
|
||||
for a in n_agent.NeutronAgent.agent_types()]:
|
||||
if not agent.agent_id:
|
||||
continue
|
||||
alive = self.agent_alive(agent, update_db)
|
||||
@ -1186,7 +1193,7 @@ class OVNMechanismDriver(api.MechanismDriver):
|
||||
|
||||
|
||||
def populate_agents(driver):
|
||||
for ch in driver._sb_ovn.tables['Chassis'].rows.values():
|
||||
for ch in driver._sb_ovn.tables[driver.agent_chassis_table].rows.values():
|
||||
# update the cache, rows are hashed on uuid but it is the name that
|
||||
# stays consistent across ovn-controller restarts
|
||||
AGENTS.update({ch.name: ch})
|
||||
@ -1207,11 +1214,12 @@ def get_agents(self, context, filters=None, fields=None, _driver=None):
|
||||
def get_agent(self, context, id, fields=None, _driver=None):
|
||||
chassis = None
|
||||
try:
|
||||
# look up Chassis by *name*, which the id attribte is
|
||||
chassis = _driver._sb_ovn.lookup('Chassis', id)
|
||||
# look up Chassis by *name*, which the id attribute is
|
||||
chassis = _driver._sb_ovn.lookup(_driver.agent_chassis_table, id)
|
||||
except idlutils.RowNotFound:
|
||||
# If the UUID is not found, check for the metadata agent ID
|
||||
for ch in _driver._sb_ovn.tables['Chassis'].rows.values():
|
||||
for ch in _driver._sb_ovn.tables[
|
||||
_driver.agent_chassis_table].rows.values():
|
||||
metadata_agent_id = ch.external_ids.get(
|
||||
ovn_const.OVN_AGENT_METADATA_ID_KEY)
|
||||
if id == metadata_agent_id:
|
||||
|
@ -836,12 +836,6 @@ class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend):
|
||||
if (r.mac and str(r.datapath.uuid) == network) and
|
||||
ip_address in r.mac[0].split(' ')]
|
||||
|
||||
def update_metadata_health_status(self, chassis, nb_cfg):
|
||||
return cmd.UpdateChassisExtIdsCommand(
|
||||
self, chassis,
|
||||
{ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY: str(nb_cfg)},
|
||||
if_exists=True)
|
||||
|
||||
def set_port_cidrs(self, name, cidrs):
|
||||
# TODO(twilson) add if_exists to db commands
|
||||
return self.db_set('Port_Binding', name, 'external_ids',
|
||||
|
@ -528,6 +528,8 @@ class OvnSbIdl(OvnIdlDistributedLock):
|
||||
def from_server(cls, connection_string, schema_name, driver):
|
||||
_check_and_set_ssl_files(schema_name)
|
||||
helper = idlutils.get_schema_helper(connection_string, schema_name)
|
||||
if 'Chassis_Private' in helper.schema_json['tables']:
|
||||
helper.register_table('Chassis_Private')
|
||||
helper.register_table('Chassis')
|
||||
helper.register_table('Encap')
|
||||
helper.register_table('Port_Binding')
|
||||
|
@ -163,6 +163,8 @@ class FakeOvsdbSbOvnIdl(object):
|
||||
self.db_set = mock.Mock()
|
||||
self.lookup = mock.MagicMock()
|
||||
self.chassis_list = mock.MagicMock()
|
||||
self.is_table_present = mock.Mock()
|
||||
self.is_table_present.return_value = False
|
||||
|
||||
|
||||
class FakeOvsdbTransaction(object):
|
||||
|
@ -1615,18 +1615,21 @@ class TestOVNMechanismDriver(test_plugin.Ml2PluginV2TestCase):
|
||||
|
||||
def _add_chassis_agent(self, nb_cfg, agent_type, updated_at=None):
|
||||
updated_at = updated_at or datetime.datetime.utcnow()
|
||||
chassis = mock.Mock()
|
||||
chassis.nb_cfg = nb_cfg
|
||||
chassis.uuid = uuid.uuid4()
|
||||
chassis.external_ids = {ovn_const.OVN_LIVENESS_CHECK_EXT_ID_KEY:
|
||||
datetime.datetime.isoformat(updated_at)}
|
||||
chassis_private = mock.Mock()
|
||||
chassis_private.nb_cfg = nb_cfg
|
||||
chassis_private.uuid = uuid.uuid4()
|
||||
chassis_private.external_ids = {
|
||||
ovn_const.OVN_LIVENESS_CHECK_EXT_ID_KEY:
|
||||
datetime.datetime.isoformat(updated_at)}
|
||||
if agent_type == ovn_const.OVN_METADATA_AGENT:
|
||||
chassis.external_ids.update({
|
||||
chassis_private.external_ids.update({
|
||||
ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY: nb_cfg,
|
||||
ovn_const.METADATA_LIVENESS_CHECK_EXT_ID_KEY:
|
||||
datetime.datetime.isoformat(updated_at)})
|
||||
chassis_private.chassis = [chassis_private]
|
||||
|
||||
return neutron_agent.NeutronAgent.from_type(agent_type, chassis)
|
||||
return neutron_agent.NeutronAgent.from_type(
|
||||
agent_type, chassis_private)
|
||||
|
||||
def test_agent_alive_true(self):
|
||||
for agent_type in (ovn_const.OVN_CONTROLLER_AGENT,
|
||||
|
Loading…
Reference in New Issue
Block a user