[OVN] The OVN agent should handle its own tags and status

The OVN agent should handle its own tags, written in the
"Chassis_Private.external_ids" dictionary. These are:
* neutron:ovn-neutron-agent-sb-cfg
* neutron:description-neutron-agent
* neutron:ovn-neutron-agent-id

The "metadata" extension should not create nor update these tags.
This patch removes from the "metadata" extension the
``SbGlobalUpdateEvent`` and ``ChassisPrivateCreateEvent`` events.

Partial-Bug: #2119097
Signed-off-by: Rodolfo Alonso Hernandez <ralonsoh@redhat.com>
Change-Id: I7f0ecf8f8727e363d98e981948c999305ae33426
This commit is contained in:
Rodolfo Alonso Hernandez
2025-07-30 09:01:43 +00:00
parent c68aa4cab3
commit c4fdba2582
5 changed files with 90 additions and 42 deletions

View File

@@ -49,6 +49,32 @@ class SbGlobalUpdateEvent(row_event.RowEvent):
('external_ids', ext_ids)).execute() ('external_ids', ext_ids)).execute()
class ChassisPrivateCreateEvent(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
of our own chassis it has to be a reconnection. In this case, we need
to do a full sync to make sure that we capture all changes while the
connection to OVSDB was down.
"""
def __init__(self, ovn_agent):
self._first_time = True
self.ovn_agent = ovn_agent
events = (self.ROW_CREATE,)
super().__init__(events, 'Chassis_Private', None)
self.conditions = (('name', '=', self.ovn_agent.chassis),)
self.event_name = self.__class__.__name__
def run(self, event, row, old):
if self._first_time:
self._first_time = False
return
# Re-register the OVN agent with the local chassis in case its
# entry was re-created (happens when restarting the ovn-controller)
self.ovn_agent.register_ovn_agent()
class OVNNeutronAgent(service.Service): class OVNNeutronAgent(service.Service):
def __init__(self, conf): def __init__(self, conf):
@@ -139,7 +165,9 @@ class OVNNeutronAgent(service.Service):
return ovsdb.MonitorAgentOvnNbIdl(tables, events).start() return ovsdb.MonitorAgentOvnNbIdl(tables, events).start()
def _load_sb_idl(self): def _load_sb_idl(self):
events = [SbGlobalUpdateEvent] events = [SbGlobalUpdateEvent,
ChassisPrivateCreateEvent,
]
tables = ['SB_Global', 'Chassis_Private'] tables = ['SB_Global', 'Chassis_Private']
for extension in self.ext_manager: for extension in self.ext_manager:
events += extension.obj.sb_idl_events events += extension.obj.sb_idl_events

View File

@@ -19,6 +19,7 @@ import threading
from neutron_lib.agent import extension from neutron_lib.agent import extension
from neutron_lib import exceptions from neutron_lib import exceptions
from oslo_log import log as logging from oslo_log import log as logging
from oslo_service import service
from neutron._i18n import _ from neutron._i18n import _
from neutron.agent import agent_extensions_manager as agent_ext_mgr from neutron.agent import agent_extensions_manager as agent_ext_mgr
@@ -33,6 +34,35 @@ class ConfigException(exceptions.NeutronException):
message = _('Error configuring the OVN Neutron Agent: %(description)s.') message = _('Error configuring the OVN Neutron Agent: %(description)s.')
class OVNExtensionEvent(metaclass=abc.ABCMeta):
"""Implements a method to retrieve the correct caller agent
The events inheriting from this class could be called from the OVN metadata
agent or as part of an extension of the OVN agent ("metadata" extension,
for example). In future releases, the OVN metadata agent will be superseded
by the OVN agent (with the "metadata" extension) and this class removed,
keeping only the compatibility with the OVN agent (to be removed in C+2).
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._agent_or_extension = None
self._agent = None
@property
def agent(self):
"""This method provide support for the OVN agent
This event can be used in the OVN metadata agent and in the OVN
agent metadata extension.
"""
if not self._agent_or_extension:
if isinstance(self._agent, service.Service):
self._agent_or_extension = self._agent['metadata']
else:
self._agent_or_extension = self._agent
return self._agent_or_extension
class OVNAgentExtensionManager(agent_ext_mgr.AgentExtensionsManager): class OVNAgentExtensionManager(agent_ext_mgr.AgentExtensionsManager):
def __init__(self, conf): def __init__(self, conf):

View File

@@ -115,8 +115,6 @@ class MetadataExtension(extension_manager.OVNAgentExtension,
def sb_idl_events(self): def sb_idl_events(self):
return [metadata_agent.PortBindingUpdatedEvent, return [metadata_agent.PortBindingUpdatedEvent,
metadata_agent.PortBindingDeletedEvent, metadata_agent.PortBindingDeletedEvent,
metadata_agent.SbGlobalUpdateEvent,
metadata_agent.ChassisPrivateCreateEvent,
] ]
# NOTE(ralonsoh): the following properties are needed during the migration # NOTE(ralonsoh): the following properties are needed during the migration
@@ -162,7 +160,6 @@ class MetadataExtension(extension_manager.OVNAgentExtension,
""" """
self.agent_api.load_config() self.agent_api.load_config()
self._update_chassis_private_config() self._update_chassis_private_config()
self.agent_api.update_neutron_sb_cfg_key()
self.sync() self.sync()
def start(self): def start(self):

View File

@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import abc
import collections import collections
import functools import functools
import re import re
@@ -31,7 +30,7 @@ from ovsdbapp.backend.ovs_idl import vlog
from neutron.agent.linux import external_process from neutron.agent.linux import external_process
from neutron.agent.linux import ip_lib from neutron.agent.linux import ip_lib
from neutron.agent.linux import iptables_manager from neutron.agent.linux import iptables_manager
from neutron.agent.ovn.agent import ovn_neutron_agent from neutron.agent.ovn.extensions import extension_manager
from neutron.agent.ovn.metadata import driver as metadata_driver from neutron.agent.ovn.metadata import driver as metadata_driver
from neutron.agent.ovn.metadata import ovsdb from neutron.agent.ovn.metadata import ovsdb
from neutron.agent.ovn.metadata import server_socket as metadata_server from neutron.agent.ovn.metadata import server_socket as metadata_server
@@ -87,36 +86,8 @@ class ConfigException(Exception):
""" """
class _OVNExtensionEvent(metaclass=abc.ABCMeta): class PortBindingEvent(extension_manager.OVNExtensionEvent,
"""Implements a method to retrieve the correct caller agent row_event.RowEvent):
The events inheriting from this class could be called from the OVN metadata
agent or as part of an extension of the OVN agent ("metadata" extension,
for example). In future releases, the OVN metadata agent will be superseded
by the OVN agent (with the "metadata" extension) and this class removed,
keeping only the compatibility with the OVN agent (to be removed in C+2).
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._agent_or_extension = None
self._agent = None
@property
def agent(self):
"""This method provide support for the OVN agent
This event can be used in the OVN metadata agent and in the OVN
agent metadata extension.
"""
if not self._agent_or_extension:
if isinstance(self._agent, ovn_neutron_agent.OVNNeutronAgent):
self._agent_or_extension = self._agent['metadata']
else:
self._agent_or_extension = self._agent
return self._agent_or_extension
class PortBindingEvent(_OVNExtensionEvent, row_event.RowEvent):
def __init__(self, agent): def __init__(self, agent):
table = 'Port_Binding' table = 'Port_Binding'
super().__init__((self.__class__.EVENT,), table, None) super().__init__((self.__class__.EVENT,), table, None)
@@ -301,7 +272,7 @@ class PortBindingDeletedEvent(PortBindingEvent):
return True return True
class ChassisPrivateCreateEvent(_OVNExtensionEvent, row_event.RowEvent): class ChassisPrivateCreateEvent(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
@@ -314,9 +285,7 @@ class ChassisPrivateCreateEvent(_OVNExtensionEvent, row_event.RowEvent):
self.first_time = True self.first_time = True
events = (self.ROW_CREATE,) events = (self.ROW_CREATE,)
super().__init__(events, 'Chassis_Private', None) super().__init__(events, 'Chassis_Private', None)
# NOTE(ralonsoh): ``self._agent`` needs to be assigned before being self.agent = agent
# used in the property ``self.agent``.
self._agent = agent
self.conditions = (('name', '=', self.agent.chassis),) self.conditions = (('name', '=', self.agent.chassis),)
self.event_name = self.__class__.__name__ self.event_name = self.__class__.__name__
@@ -332,14 +301,14 @@ class ChassisPrivateCreateEvent(_OVNExtensionEvent, row_event.RowEvent):
self.agent.sync() self.agent.sync()
class SbGlobalUpdateEvent(_OVNExtensionEvent, row_event.RowEvent): class SbGlobalUpdateEvent(row_event.RowEvent):
"""Row update event on SB_Global table.""" """Row update event on SB_Global table."""
def __init__(self, agent): def __init__(self, agent):
table = 'SB_Global' table = 'SB_Global'
events = (self.ROW_UPDATE,) events = (self.ROW_UPDATE,)
super().__init__(events, table, None) super().__init__(events, table, None)
self._agent = agent self.agent = agent
self.event_name = self.__class__.__name__ self.event_name = self.__class__.__name__
self.first_run = True self.first_run = True

View File

@@ -18,6 +18,7 @@ import uuid
from oslo_config import fixture as fixture_config from oslo_config import fixture as fixture_config
from oslo_utils import uuidutils from oslo_utils import uuidutils
from ovsdbapp.backend.ovs_idl import idlutils
from neutron.agent.ovn.agent import ovn_neutron_agent from neutron.agent.ovn.agent import ovn_neutron_agent
from neutron.agent.ovn.agent import ovsdb as agent_ovsdb from neutron.agent.ovn.agent import ovsdb as agent_ovsdb
@@ -94,6 +95,29 @@ class TestOVNNeutronAgentBase(base.TestOVNFunctionalBase):
return agt return agt
class TestOVNNeutronAgent(TestOVNNeutronAgentBase):
def setUp(self, **kwargs):
super().setUp(extensions=[METADATA_EXTENSION], **kwargs)
def test_chassis_private_create_event(self):
def _check_chassis_private():
try:
ext_ids = self.ovn_agent.sb_idl.db_get(
'Chassis_Private', self.chassis_name,
'external_ids').execute(check_error=True)
return (ext_ids.get(ovn_const.OVN_AGENT_NEUTRON_ID_KEY)
is not None)
except idlutils.RowNotFound:
return False
# If the "Chassis_Private" register is deleted and created again,
# the agent should be able to re-register itself.
self.ovn_agent.sb_idl.chassis_del(self.chassis_name).execute(
check_error=True)
self.add_fake_chassis(self.host_name, name=self.chassis_name)
n_utils.wait_until_true(_check_chassis_private, timeout=10)
class TestOVNNeutronAgentFakeAgent(TestOVNNeutronAgentBase): class TestOVNNeutronAgentFakeAgent(TestOVNNeutronAgentBase):
def setUp(self, **kwargs): def setUp(self, **kwargs):