diff --git a/neutron/agent/ovn/metadata/agent.py b/neutron/agent/ovn/metadata/agent.py index 0e2e52557b2..a5b4bcc69dc 100644 --- a/neutron/agent/ovn/metadata/agent.py +++ b/neutron/agent/ovn/metadata/agent.py @@ -111,7 +111,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 @@ -119,14 +119,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): @@ -141,6 +141,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.""" @@ -152,8 +160,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): @@ -189,13 +201,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() @@ -208,9 +233,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): diff --git a/neutron/agent/ovn/metadata/ovsdb.py b/neutron/agent/ovn/metadata/ovsdb.py index eb5311d4d87..de736033db6 100644 --- a/neutron/agent/ovn/metadata/ovsdb.py +++ b/neutron/agent/ovn/metadata/ovsdb.py @@ -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) diff --git a/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py b/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py index 18adc2e2655..d2e33f6c42b 100644 --- a/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py +++ b/neutron/plugins/ml2/drivers/ovn/agent/neutron_agent.py @@ -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, '') diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py index f06fa7f007c..eb8e0d5829d 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/mech_driver.py @@ -119,6 +119,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): @@ -233,6 +236,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) @@ -1059,14 +1065,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) @@ -1140,7 +1147,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}) @@ -1161,11 +1168,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: diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py index 066eb19f0d2..830baa139d2 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py @@ -813,12 +813,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', diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py index 5bd2928309c..f7c3f1221f4 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovsdb_monitor.py @@ -516,6 +516,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') diff --git a/neutron/tests/unit/fake_resources.py b/neutron/tests/unit/fake_resources.py index 7157045721d..52b22f81564 100644 --- a/neutron/tests/unit/fake_resources.py +++ b/neutron/tests/unit/fake_resources.py @@ -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): diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py index e4884305723..0065c377aba 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py @@ -1558,18 +1558,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,