[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()
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):
def __init__(self, conf):
@@ -139,7 +165,9 @@ class OVNNeutronAgent(service.Service):
return ovsdb.MonitorAgentOvnNbIdl(tables, events).start()
def _load_sb_idl(self):
events = [SbGlobalUpdateEvent]
events = [SbGlobalUpdateEvent,
ChassisPrivateCreateEvent,
]
tables = ['SB_Global', 'Chassis_Private']
for extension in self.ext_manager:
events += extension.obj.sb_idl_events

View File

@@ -19,6 +19,7 @@ import threading
from neutron_lib.agent import extension
from neutron_lib import exceptions
from oslo_log import log as logging
from oslo_service import service
from neutron._i18n import _
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.')
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):
def __init__(self, conf):

View File

@@ -115,8 +115,6 @@ class MetadataExtension(extension_manager.OVNAgentExtension,
def sb_idl_events(self):
return [metadata_agent.PortBindingUpdatedEvent,
metadata_agent.PortBindingDeletedEvent,
metadata_agent.SbGlobalUpdateEvent,
metadata_agent.ChassisPrivateCreateEvent,
]
# 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._update_chassis_private_config()
self.agent_api.update_neutron_sb_cfg_key()
self.sync()
def start(self):

View File

@@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import abc
import collections
import functools
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 ip_lib
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 ovsdb
from neutron.agent.ovn.metadata import server_socket as metadata_server
@@ -87,36 +86,8 @@ class ConfigException(Exception):
"""
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, 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):
class PortBindingEvent(extension_manager.OVNExtensionEvent,
row_event.RowEvent):
def __init__(self, agent):
table = 'Port_Binding'
super().__init__((self.__class__.EVENT,), table, None)
@@ -301,7 +272,7 @@ class PortBindingDeletedEvent(PortBindingEvent):
return True
class ChassisPrivateCreateEvent(_OVNExtensionEvent, row_event.RowEvent):
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
@@ -314,9 +285,7 @@ class ChassisPrivateCreateEvent(_OVNExtensionEvent, row_event.RowEvent):
self.first_time = True
events = (self.ROW_CREATE,)
super().__init__(events, 'Chassis_Private', None)
# NOTE(ralonsoh): ``self._agent`` needs to be assigned before being
# used in the property ``self.agent``.
self._agent = agent
self.agent = agent
self.conditions = (('name', '=', self.agent.chassis),)
self.event_name = self.__class__.__name__
@@ -332,14 +301,14 @@ class ChassisPrivateCreateEvent(_OVNExtensionEvent, row_event.RowEvent):
self.agent.sync()
class SbGlobalUpdateEvent(_OVNExtensionEvent, row_event.RowEvent):
class SbGlobalUpdateEvent(row_event.RowEvent):
"""Row update event on SB_Global table."""
def __init__(self, agent):
table = 'SB_Global'
events = (self.ROW_UPDATE,)
super().__init__(events, table, None)
self._agent = agent
self.agent = agent
self.event_name = self.__class__.__name__
self.first_run = True

View File

@@ -18,6 +18,7 @@ import uuid
from oslo_config import fixture as fixture_config
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 ovsdb as agent_ovsdb
@@ -94,6 +95,29 @@ class TestOVNNeutronAgentBase(base.TestOVNFunctionalBase):
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):
def setUp(self, **kwargs):