[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
|
return False
|
||||||
|
|
||||||
|
|
||||||
class ChassisCreateEvent(row_event.RowEvent):
|
class ChassisCreateEventBase(row_event.RowEvent):
|
||||||
"""Row create event - Chassis name == our_chassis.
|
"""Row create event - Chassis name == our_chassis.
|
||||||
|
|
||||||
On connection, we get a dump of all chassis so if we catch a creation
|
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
|
to do a full sync to make sure that we capture all changes while the
|
||||||
connection to OVSDB was down.
|
connection to OVSDB was down.
|
||||||
"""
|
"""
|
||||||
|
table = None
|
||||||
|
|
||||||
def __init__(self, metadata_agent):
|
def __init__(self, metadata_agent):
|
||||||
self.agent = metadata_agent
|
self.agent = metadata_agent
|
||||||
self.first_time = True
|
self.first_time = True
|
||||||
table = 'Chassis'
|
|
||||||
events = (self.ROW_CREATE,)
|
events = (self.ROW_CREATE,)
|
||||||
super(ChassisCreateEvent, self).__init__(
|
super(ChassisCreateEventBase, self).__init__(
|
||||||
events, table, (('name', '=', self.agent.chassis),))
|
events, self.table, (('name', '=', self.agent.chassis),))
|
||||||
self.event_name = self.__class__.__name__
|
self.event_name = self.__class__.__name__
|
||||||
|
|
||||||
def run(self, event, row, old):
|
def run(self, event, row, old):
|
||||||
@ -159,6 +159,14 @@ class ChassisCreateEvent(row_event.RowEvent):
|
|||||||
self.agent.sync()
|
self.agent.sync()
|
||||||
|
|
||||||
|
|
||||||
|
class ChassisCreateEvent(ChassisCreateEventBase):
|
||||||
|
table = 'Chassis'
|
||||||
|
|
||||||
|
|
||||||
|
class ChassisPrivateCreateEvent(ChassisCreateEventBase):
|
||||||
|
table = 'Chassis_Private'
|
||||||
|
|
||||||
|
|
||||||
class SbGlobalUpdateEvent(row_event.RowEvent):
|
class SbGlobalUpdateEvent(row_event.RowEvent):
|
||||||
"""Row update event on SB_Global table."""
|
"""Row update event on SB_Global table."""
|
||||||
|
|
||||||
@ -170,8 +178,12 @@ class SbGlobalUpdateEvent(row_event.RowEvent):
|
|||||||
self.event_name = self.__class__.__name__
|
self.event_name = self.__class__.__name__
|
||||||
|
|
||||||
def run(self, event, row, old):
|
def run(self, event, row, old):
|
||||||
self.agent.sb_idl.update_metadata_health_status(
|
table = ('Chassis_Private' if self.agent.has_chassis_private
|
||||||
self.agent.chassis, row.nb_cfg).execute()
|
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):
|
class MetadataAgent(object):
|
||||||
@ -207,13 +219,26 @@ class MetadataAgent(object):
|
|||||||
proxy = metadata_server.UnixDomainMetadataProxy(self.conf)
|
proxy = metadata_server.UnixDomainMetadataProxy(self.conf)
|
||||||
proxy.run()
|
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.
|
# Open the connection to OVN SB database.
|
||||||
self.sb_idl = ovsdb.MetadataAgentOvnSbIdl(
|
self.has_chassis_private = False
|
||||||
chassis=self.chassis,
|
try:
|
||||||
events=[PortBindingChassisCreatedEvent(self),
|
self.sb_idl = ovsdb.MetadataAgentOvnSbIdl(
|
||||||
PortBindingChassisDeletedEvent(self),
|
chassis=self.chassis, tables=tables + ('Chassis_Private', ),
|
||||||
ChassisCreateEvent(self),
|
events=events + (ChassisPrivateCreateEvent(self), )).start()
|
||||||
SbGlobalUpdateEvent(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.
|
# Do the initial sync.
|
||||||
self.sync()
|
self.sync()
|
||||||
@ -226,9 +251,10 @@ class MetadataAgent(object):
|
|||||||
def register_metadata_agent(self):
|
def register_metadata_agent(self):
|
||||||
# NOTE(lucasagomes): db_add() will not overwrite the UUID if
|
# NOTE(lucasagomes): db_add() will not overwrite the UUID if
|
||||||
# it's already set.
|
# it's already set.
|
||||||
|
table = ('Chassis_Private' if self.has_chassis_private else 'Chassis')
|
||||||
ext_ids = {
|
ext_ids = {
|
||||||
ovn_const.OVN_AGENT_METADATA_ID_KEY: uuidutils.generate_uuid()}
|
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)
|
ext_ids).execute(check_error=True)
|
||||||
|
|
||||||
def _get_own_chassis_name(self):
|
def _get_own_chassis_name(self):
|
||||||
|
@ -38,8 +38,10 @@ class MetadataAgentOvnSbIdl(ovsdb_monitor.OvnIdl):
|
|||||||
helper.register_table(table)
|
helper.register_table(table)
|
||||||
super(MetadataAgentOvnSbIdl, self).__init__(
|
super(MetadataAgentOvnSbIdl, self).__init__(
|
||||||
None, connection_string, helper)
|
None, connection_string, helper)
|
||||||
if chassis and 'Chassis' in tables:
|
if chassis:
|
||||||
self.tables['Chassis'].condition = [['name', '==', chassis]]
|
table = ('Chassis_Private' if 'Chassis_Private' in tables
|
||||||
|
else 'Chassis')
|
||||||
|
self.tables[table].condition = [['name', '==', chassis]]
|
||||||
if events:
|
if events:
|
||||||
self.notify_handler.watch_events(events)
|
self.notify_handler.watch_events(events)
|
||||||
|
|
||||||
|
@ -28,8 +28,13 @@ class NeutronAgent(abc.ABC):
|
|||||||
for _type in cls.types:
|
for _type in cls.types:
|
||||||
NeutronAgent.types[_type] = cls
|
NeutronAgent.types[_type] = cls
|
||||||
|
|
||||||
def __init__(self, chassis):
|
def __init__(self, chassis_private):
|
||||||
self.chassis = chassis
|
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
|
@property
|
||||||
def updated_at(self):
|
def updated_at(self):
|
||||||
@ -58,8 +63,8 @@ class NeutronAgent(abc.ABC):
|
|||||||
'admin_state_up': True}
|
'admin_state_up': True}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_type(cls, _type, chassis):
|
def from_type(cls, _type, chassis_private):
|
||||||
return cls.types[_type](chassis)
|
return cls.types[_type](chassis_private)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def agent_types():
|
def agent_types():
|
||||||
@ -85,15 +90,15 @@ class ControllerAgent(NeutronAgent):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def nb_cfg(self):
|
def nb_cfg(self):
|
||||||
return self.chassis.nb_cfg
|
return self.chassis_private.nb_cfg
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def agent_id(self):
|
def agent_id(self):
|
||||||
return self.chassis.name
|
return self.chassis_private.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self):
|
def description(self):
|
||||||
return self.chassis.external_ids.get(
|
return self.chassis_private.external_ids.get(
|
||||||
ovn_const.OVN_AGENT_DESC_KEY, '')
|
ovn_const.OVN_AGENT_DESC_KEY, '')
|
||||||
|
|
||||||
|
|
||||||
@ -105,15 +110,15 @@ class MetadataAgent(NeutronAgent):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def nb_cfg(self):
|
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))
|
ovn_const.OVN_AGENT_METADATA_SB_CFG_KEY, 0))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def agent_id(self):
|
def agent_id(self):
|
||||||
return self.chassis.external_ids.get(
|
return self.chassis_private.external_ids.get(
|
||||||
ovn_const.OVN_AGENT_METADATA_ID_KEY)
|
ovn_const.OVN_AGENT_METADATA_ID_KEY)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def description(self):
|
def description(self):
|
||||||
return self.chassis.external_ids.get(
|
return self.chassis_private.external_ids.get(
|
||||||
ovn_const.OVN_AGENT_METADATA_DESC_KEY, '')
|
ovn_const.OVN_AGENT_METADATA_DESC_KEY, '')
|
||||||
|
@ -120,6 +120,9 @@ class OVNMechanismDriver(api.MechanismDriver):
|
|||||||
self.subscribe()
|
self.subscribe()
|
||||||
self.qos_driver = qos_driver.OVNQosDriver.create(self)
|
self.qos_driver = qos_driver.OVNQosDriver.create(self)
|
||||||
self.trunk_driver = trunk_driver.OVNTrunkDriver.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
|
@property
|
||||||
def _plugin(self):
|
def _plugin(self):
|
||||||
@ -262,6 +265,9 @@ class OVNMechanismDriver(api.MechanismDriver):
|
|||||||
self._nb_ovn, self._sb_ovn = impl_idl_ovn.get_ovn_idls(
|
self._nb_ovn, self._sb_ovn = impl_idl_ovn.get_ovn_idls(
|
||||||
self, trigger, binding_events=not is_maintenance)
|
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
|
# AGENTS must be populated after fork so if ovn-controller is stopped
|
||||||
# before a worker handles a get_agents request, we still show agents
|
# before a worker handles a get_agents request, we still show agents
|
||||||
populate_agents(self)
|
populate_agents(self)
|
||||||
@ -1105,14 +1111,15 @@ class OVNMechanismDriver(api.MechanismDriver):
|
|||||||
def mark_agent_alive(self, agent):
|
def mark_agent_alive(self, agent):
|
||||||
# Update the time of our successful check
|
# Update the time of our successful check
|
||||||
value = timeutils.utcnow(with_timezone=True).isoformat()
|
value = timeutils.utcnow(with_timezone=True).isoformat()
|
||||||
self._sb_ovn.db_set('Chassis', agent.chassis.uuid,
|
self._sb_ovn.db_set(
|
||||||
('external_ids', {agent.key: value})).execute(
|
self.agent_chassis_table, agent.chassis_private.uuid,
|
||||||
check_error=True)
|
('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 = {}
|
agent_dict = {}
|
||||||
# Iterate over each unique Agent subclass
|
# 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:
|
if not agent.agent_id:
|
||||||
continue
|
continue
|
||||||
alive = self.agent_alive(agent, update_db)
|
alive = self.agent_alive(agent, update_db)
|
||||||
@ -1186,7 +1193,7 @@ class OVNMechanismDriver(api.MechanismDriver):
|
|||||||
|
|
||||||
|
|
||||||
def populate_agents(driver):
|
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
|
# update the cache, rows are hashed on uuid but it is the name that
|
||||||
# stays consistent across ovn-controller restarts
|
# stays consistent across ovn-controller restarts
|
||||||
AGENTS.update({ch.name: ch})
|
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):
|
def get_agent(self, context, id, fields=None, _driver=None):
|
||||||
chassis = None
|
chassis = None
|
||||||
try:
|
try:
|
||||||
# look up Chassis by *name*, which the id attribte is
|
# look up Chassis by *name*, which the id attribute is
|
||||||
chassis = _driver._sb_ovn.lookup('Chassis', id)
|
chassis = _driver._sb_ovn.lookup(_driver.agent_chassis_table, id)
|
||||||
except idlutils.RowNotFound:
|
except idlutils.RowNotFound:
|
||||||
# If the UUID is not found, check for the metadata agent ID
|
# 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(
|
metadata_agent_id = ch.external_ids.get(
|
||||||
ovn_const.OVN_AGENT_METADATA_ID_KEY)
|
ovn_const.OVN_AGENT_METADATA_ID_KEY)
|
||||||
if id == metadata_agent_id:
|
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
|
if (r.mac and str(r.datapath.uuid) == network) and
|
||||||
ip_address in r.mac[0].split(' ')]
|
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):
|
def set_port_cidrs(self, name, cidrs):
|
||||||
# TODO(twilson) add if_exists to db commands
|
# TODO(twilson) add if_exists to db commands
|
||||||
return self.db_set('Port_Binding', name, 'external_ids',
|
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):
|
def from_server(cls, connection_string, schema_name, driver):
|
||||||
_check_and_set_ssl_files(schema_name)
|
_check_and_set_ssl_files(schema_name)
|
||||||
helper = idlutils.get_schema_helper(connection_string, 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('Chassis')
|
||||||
helper.register_table('Encap')
|
helper.register_table('Encap')
|
||||||
helper.register_table('Port_Binding')
|
helper.register_table('Port_Binding')
|
||||||
|
@ -163,6 +163,8 @@ class FakeOvsdbSbOvnIdl(object):
|
|||||||
self.db_set = mock.Mock()
|
self.db_set = mock.Mock()
|
||||||
self.lookup = mock.MagicMock()
|
self.lookup = mock.MagicMock()
|
||||||
self.chassis_list = mock.MagicMock()
|
self.chassis_list = mock.MagicMock()
|
||||||
|
self.is_table_present = mock.Mock()
|
||||||
|
self.is_table_present.return_value = False
|
||||||
|
|
||||||
|
|
||||||
class FakeOvsdbTransaction(object):
|
class FakeOvsdbTransaction(object):
|
||||||
|
@ -1615,18 +1615,21 @@ class TestOVNMechanismDriver(test_plugin.Ml2PluginV2TestCase):
|
|||||||
|
|
||||||
def _add_chassis_agent(self, nb_cfg, agent_type, updated_at=None):
|
def _add_chassis_agent(self, nb_cfg, agent_type, updated_at=None):
|
||||||
updated_at = updated_at or datetime.datetime.utcnow()
|
updated_at = updated_at or datetime.datetime.utcnow()
|
||||||
chassis = mock.Mock()
|
chassis_private = mock.Mock()
|
||||||
chassis.nb_cfg = nb_cfg
|
chassis_private.nb_cfg = nb_cfg
|
||||||
chassis.uuid = uuid.uuid4()
|
chassis_private.uuid = uuid.uuid4()
|
||||||
chassis.external_ids = {ovn_const.OVN_LIVENESS_CHECK_EXT_ID_KEY:
|
chassis_private.external_ids = {
|
||||||
datetime.datetime.isoformat(updated_at)}
|
ovn_const.OVN_LIVENESS_CHECK_EXT_ID_KEY:
|
||||||
|
datetime.datetime.isoformat(updated_at)}
|
||||||
if agent_type == ovn_const.OVN_METADATA_AGENT:
|
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.OVN_AGENT_METADATA_SB_CFG_KEY: nb_cfg,
|
||||||
ovn_const.METADATA_LIVENESS_CHECK_EXT_ID_KEY:
|
ovn_const.METADATA_LIVENESS_CHECK_EXT_ID_KEY:
|
||||||
datetime.datetime.isoformat(updated_at)})
|
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):
|
def test_agent_alive_true(self):
|
||||||
for agent_type in (ovn_const.OVN_CONTROLLER_AGENT,
|
for agent_type in (ovn_const.OVN_CONTROLLER_AGENT,
|
||||||
|
Loading…
Reference in New Issue
Block a user