Merge "[OVN] Use the Chassis_Private table for agents healthcheck"

This commit is contained in:
Zuul 2020-08-22 01:26:33 +00:00 committed by Gerrit Code Review
commit 661e3c192d
8 changed files with 89 additions and 47 deletions

View File

@ -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()
# Open the connection to OVN SB database.
self.sb_idl = ovsdb.MetadataAgentOvnSbIdl(
chassis=self.chassis,
events=[PortBindingChassisCreatedEvent(self),
tables = ('Encap', 'Port_Binding', 'Datapath_Binding', 'SB_Global',
'Chassis')
events = (PortBindingChassisCreatedEvent(self),
PortBindingChassisDeletedEvent(self),
ChassisCreateEvent(self),
SbGlobalUpdateEvent(self)]).start()
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.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):

View File

@ -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)

View File

@ -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, '')

View File

@ -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:

View File

@ -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',

View File

@ -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')

View File

@ -169,6 +169,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):

View File

@ -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:
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,