[OVN] Implement OVN agent metadata extension
This patch is implementing the OVN agent metadata extension, by reusing the OVN metadata class. The class ``MetadataAgent`` is inherited in the ``MetadataExtension`` class. The goal is to use the same code in both implementations (until the OVN metadata agent is deprecated). The OVN agent metadata extension has a different initialization process. The OVN and OVS IDL connections are created during the extension initialization but are not accessible. The ``start`` method is used to load the configuration, execute the sync process and register the metadata extension. This extension will replace the need of the OVN metadata agent. The deprecation of this agent will imply the refactor of the existing code that now is shared between both agents. This new OVN agent will be tested in the "neutron-tempest-plugin-ovn" CI job, after the change done in the following patch. Needed-By: https://review.opendev.org/c/openstack/neutron-tempest-plugin/+/909860 Partial-Bug: #2017871 Change-Id: I4381a67648a9b6198a8d936db784964d74dc87a1
This commit is contained in:
parent
5c187e8dab
commit
fe31f4fe02
@ -54,9 +54,9 @@ class OVNNeutronAgent(service.Service):
|
||||
def __init__(self, conf):
|
||||
super().__init__()
|
||||
self._conf = conf
|
||||
self.chassis = None
|
||||
self.chassis_id = None
|
||||
self.ovn_bridge = None
|
||||
self._chassis = None
|
||||
self._chassis_id = None
|
||||
self._ovn_bridge = None
|
||||
self.ext_manager_api = ext_mgr.OVNAgentExtensionAPI()
|
||||
self.ext_manager = ext_mgr.OVNAgentExtensionManager(self._conf)
|
||||
self.ext_manager.initialize(None, 'ovn', self)
|
||||
@ -65,6 +65,10 @@ class OVNNeutronAgent(service.Service):
|
||||
"""Return the named extension objet from ``self.ext_manager``"""
|
||||
return self.ext_manager[name].obj
|
||||
|
||||
@property
|
||||
def conf(self):
|
||||
return self._conf
|
||||
|
||||
@property
|
||||
def ovs_idl(self):
|
||||
if not self.ext_manager_api.ovs_idl:
|
||||
@ -87,15 +91,27 @@ class OVNNeutronAgent(service.Service):
|
||||
def sb_post_fork_event(self):
|
||||
return self.ext_manager_api.sb_post_fork_event
|
||||
|
||||
@property
|
||||
def chassis(self):
|
||||
return self._chassis
|
||||
|
||||
@property
|
||||
def chassis_id(self):
|
||||
return self._chassis_id
|
||||
|
||||
@property
|
||||
def ovn_bridge(self):
|
||||
return self._ovn_bridge
|
||||
|
||||
def load_config(self):
|
||||
self.chassis = ovsdb.get_own_chassis_name(self.ovs_idl)
|
||||
self._chassis = ovsdb.get_own_chassis_name(self.ovs_idl)
|
||||
try:
|
||||
self.chassis_id = uuid.UUID(self.chassis)
|
||||
self._chassis_id = uuid.UUID(self.chassis)
|
||||
except ValueError:
|
||||
# OVS system-id could be a non UUID formatted string.
|
||||
self.chassis_id = uuid.uuid5(OVN_MONITOR_UUID_NAMESPACE,
|
||||
self.chassis)
|
||||
self.ovn_bridge = ovsdb.get_ovn_bridge(self.ovs_idl)
|
||||
self._chassis_id = uuid.uuid5(OVN_MONITOR_UUID_NAMESPACE,
|
||||
self._chassis)
|
||||
self._ovn_bridge = ovsdb.get_ovn_bridge(self.ovs_idl)
|
||||
LOG.info("Loaded chassis name %s (UUID: %s) and ovn bridge %s.",
|
||||
self.chassis, self.chassis_id, self.ovn_bridge)
|
||||
|
||||
|
@ -18,11 +18,13 @@ import threading
|
||||
|
||||
from neutron_lib.agent import extension
|
||||
from neutron_lib import exceptions
|
||||
from oslo_log import log as logging
|
||||
|
||||
from neutron._i18n import _
|
||||
from neutron.agent import agent_extensions_manager as agent_ext_mgr
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
OVN_AGENT_EXT_MANAGER_NAMESPACE = 'neutron.agent.ovn.extensions'
|
||||
|
||||
|
||||
@ -45,13 +47,15 @@ class OVNAgentExtensionManager(agent_ext_mgr.AgentExtensionsManager):
|
||||
"""Start the extensions, once the OVN agent has been initialized."""
|
||||
for ext in self:
|
||||
ext.obj.start()
|
||||
LOG.info('Extension manager: %s started', ext.obj.name)
|
||||
|
||||
|
||||
class OVNAgentExtension(extension.AgentExtension, metaclass=abc.ABCMeta):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.agent_api = None
|
||||
self._is_started = False
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
@ -77,6 +81,11 @@ class OVNAgentExtension(extension.AgentExtension, metaclass=abc.ABCMeta):
|
||||
OVN agent and the extension manager API. It is executed at the end of
|
||||
the OVN agent ``start`` method.
|
||||
"""
|
||||
self._is_started = True
|
||||
|
||||
@property
|
||||
def is_started(self):
|
||||
return self._is_started
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
|
174
neutron/agent/ovn/extensions/metadata.py
Normal file
174
neutron/agent/ovn/extensions/metadata.py
Normal file
@ -0,0 +1,174 @@
|
||||
# Copyright 2024 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import collections
|
||||
import functools
|
||||
import re
|
||||
|
||||
from oslo_concurrency import lockutils
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log
|
||||
from ovsdbapp.backend.ovs_idl import vlog
|
||||
|
||||
from neutron.agent.linux import external_process
|
||||
from neutron.agent.ovn.extensions import extension_manager
|
||||
from neutron.agent.ovn.metadata import agent as metadata_agent
|
||||
from neutron.agent.ovn.metadata import server as metadata_server
|
||||
from neutron.common.ovn import constants as ovn_const
|
||||
from neutron.conf.agent.database import agents_db
|
||||
from neutron.conf.agent.metadata import config as meta_conf
|
||||
from neutron.conf.agent.ovn.metadata import config as ovn_meta
|
||||
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as config
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
EXT_NAME = 'metadata'
|
||||
agents_db.register_db_agents_opts()
|
||||
_SYNC_STATE_LOCK = lockutils.ReaderWriterLock()
|
||||
CHASSIS_METADATA_LOCK = 'chassis_metadata_lock'
|
||||
|
||||
SB_IDL_TABLES = ['Encap',
|
||||
'Port_Binding',
|
||||
'Datapath_Binding',
|
||||
'SB_Global',
|
||||
'Chassis',
|
||||
'Chassis_Private',
|
||||
]
|
||||
|
||||
NS_PREFIX = ovn_const.OVN_METADATA_PREFIX
|
||||
MAC_PATTERN = re.compile(r'([0-9A-F]{2}[:-]){5}([0-9A-F]{2})', re.I)
|
||||
OVN_VIF_PORT_TYPES = (
|
||||
"", ovn_const.LSP_TYPE_EXTERNAL, ovn_const.LSP_TYPE_LOCALPORT)
|
||||
|
||||
MetadataPortInfo = collections.namedtuple('MetadataPortInfo', ['mac',
|
||||
'ip_addresses',
|
||||
'logical_port'])
|
||||
|
||||
|
||||
def _sync_lock(f):
|
||||
"""Decorator to block all operations for a global sync call."""
|
||||
@functools.wraps(f)
|
||||
def wrapped(*args, **kwargs):
|
||||
with _SYNC_STATE_LOCK.write_lock():
|
||||
return f(*args, **kwargs)
|
||||
return wrapped
|
||||
|
||||
|
||||
class MetadataExtension(extension_manager.OVNAgentExtension,
|
||||
metadata_agent.MetadataAgent):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(conf=cfg.CONF)
|
||||
vlog.use_python_logger(max_level=config.get_ovn_ovsdb_log_level())
|
||||
self._process_monitor = None
|
||||
self._proxy = None
|
||||
# We'll restart all haproxy instances upon start so that they honor
|
||||
# any potential changes in their configuration.
|
||||
self.restarted_metadata_proxy_set = set()
|
||||
|
||||
@staticmethod
|
||||
def _register_config_options():
|
||||
ovn_meta.register_meta_conf_opts(meta_conf.SHARED_OPTS)
|
||||
ovn_meta.register_meta_conf_opts(
|
||||
meta_conf.UNIX_DOMAIN_METADATA_PROXY_OPTS)
|
||||
ovn_meta.register_meta_conf_opts(meta_conf.METADATA_PROXY_HANDLER_OPTS)
|
||||
ovn_meta.register_meta_conf_opts(meta_conf.METADATA_RATE_LIMITING_OPTS,
|
||||
group=meta_conf.RATE_LIMITING_GROUP)
|
||||
|
||||
def initialize(self, *args):
|
||||
self._register_config_options()
|
||||
self._process_monitor = external_process.ProcessMonitor(
|
||||
config=self.agent_api.conf, resource_type='metadata')
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return 'Metadata OVN agent extension'
|
||||
|
||||
@property
|
||||
def ovs_idl_events(self):
|
||||
return []
|
||||
|
||||
@property
|
||||
def nb_idl_tables(self):
|
||||
return []
|
||||
|
||||
@property
|
||||
def nb_idl_events(self):
|
||||
return []
|
||||
|
||||
@property
|
||||
def sb_idl_tables(self):
|
||||
return SB_IDL_TABLES
|
||||
|
||||
@property
|
||||
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
|
||||
# to the Metadata agent to the OVN agent, while sharing the code with
|
||||
# ``metadata_agent.MetadataAgent``
|
||||
@property
|
||||
def nb_idl(self):
|
||||
return self.agent_api.nb_idl
|
||||
|
||||
@property
|
||||
def sb_idl(self):
|
||||
return self.agent_api.sb_idl
|
||||
|
||||
@property
|
||||
def ovs_idl(self):
|
||||
return self.agent_api.ovs_idl
|
||||
|
||||
@property
|
||||
def conf(self):
|
||||
return self.agent_api.conf
|
||||
|
||||
@property
|
||||
def chassis(self):
|
||||
return self.agent_api.chassis
|
||||
|
||||
@property
|
||||
def ovn_bridge(self):
|
||||
return self.agent_api.ovn_bridge
|
||||
|
||||
@_sync_lock
|
||||
def resync(self):
|
||||
"""Resync the Metadata OVN agent extension.
|
||||
|
||||
Reload the configuration and sync the agent again.
|
||||
"""
|
||||
self.agent_api.load_config()
|
||||
self.sync()
|
||||
|
||||
def start(self):
|
||||
self._load_config()
|
||||
|
||||
# Launch the server that will act as a proxy between the VM's and Nova.
|
||||
self._proxy = metadata_server.UnixDomainMetadataProxy(
|
||||
self.agent_api.conf, self.agent_api.chassis,
|
||||
sb_idl=self.agent_api.sb_idl)
|
||||
self._proxy.run()
|
||||
|
||||
# Do the initial sync.
|
||||
self.sync()
|
||||
|
||||
# Register the agent with its corresponding Chassis
|
||||
self.register_metadata_agent()
|
||||
|
||||
# Raise the "is_started" flag.
|
||||
self._is_started = True
|
@ -12,6 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import abc
|
||||
import collections
|
||||
import functools
|
||||
from random import randint
|
||||
@ -31,6 +32,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.metadata import driver as metadata_driver
|
||||
from neutron.agent.ovn.metadata import ovsdb
|
||||
from neutron.agent.ovn.metadata import server as metadata_server
|
||||
@ -84,11 +86,40 @@ class ConfigException(Exception):
|
||||
"""
|
||||
|
||||
|
||||
class PortBindingEvent(row_event.RowEvent):
|
||||
def __init__(self, metadata_agent):
|
||||
self.agent = metadata_agent
|
||||
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):
|
||||
def __init__(self, agent):
|
||||
table = 'Port_Binding'
|
||||
super().__init__((self.__class__.EVENT,), table, None)
|
||||
self._agent = agent
|
||||
self.event_name = self.__class__.__name__
|
||||
self._log_msg = (
|
||||
"PortBindingEvent matched for logical port %s and network %s")
|
||||
@ -269,7 +300,7 @@ class PortBindingDeletedEvent(PortBindingEvent):
|
||||
return True
|
||||
|
||||
|
||||
class ChassisPrivateCreateEvent(row_event.RowEvent):
|
||||
class ChassisPrivateCreateEvent(_OVNExtensionEvent, 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
|
||||
@ -277,12 +308,15 @@ class ChassisPrivateCreateEvent(row_event.RowEvent):
|
||||
to do a full sync to make sure that we capture all changes while the
|
||||
connection to OVSDB was down.
|
||||
"""
|
||||
def __init__(self, metadata_agent):
|
||||
self.agent = metadata_agent
|
||||
def __init__(self, agent):
|
||||
self._extension = None
|
||||
self.first_time = True
|
||||
events = (self.ROW_CREATE,)
|
||||
super(ChassisPrivateCreateEvent, self).__init__(
|
||||
events, 'Chassis_Private', (('name', '=', self.agent.chassis),))
|
||||
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.conditions = (('name', '=', self.agent.chassis),)
|
||||
self.event_name = self.__class__.__name__
|
||||
|
||||
def run(self, event, row, old):
|
||||
@ -297,14 +331,14 @@ class ChassisPrivateCreateEvent(row_event.RowEvent):
|
||||
self.agent.sync()
|
||||
|
||||
|
||||
class SbGlobalUpdateEvent(row_event.RowEvent):
|
||||
class SbGlobalUpdateEvent(_OVNExtensionEvent, row_event.RowEvent):
|
||||
"""Row update event on SB_Global table."""
|
||||
|
||||
def __init__(self, metadata_agent):
|
||||
self.agent = metadata_agent
|
||||
def __init__(self, agent):
|
||||
table = 'SB_Global'
|
||||
events = (self.ROW_UPDATE,)
|
||||
super(SbGlobalUpdateEvent, self).__init__(events, table, None)
|
||||
self._agent = agent
|
||||
self.event_name = self.__class__.__name__
|
||||
self.first_run = True
|
||||
|
||||
@ -337,16 +371,21 @@ class SbGlobalUpdateEvent(row_event.RowEvent):
|
||||
class MetadataAgent(object):
|
||||
|
||||
def __init__(self, conf):
|
||||
self.conf = conf
|
||||
self._conf = conf
|
||||
vlog.use_python_logger(max_level=config.get_ovn_ovsdb_log_level())
|
||||
self._process_monitor = external_process.ProcessMonitor(
|
||||
config=self.conf,
|
||||
config=self._conf,
|
||||
resource_type='metadata')
|
||||
self._sb_idl = None
|
||||
self._post_fork_event = threading.Event()
|
||||
# We'll restart all haproxy instances upon start so that they honor
|
||||
# any potential changes in their configuration.
|
||||
self.restarted_metadata_proxy_set = set()
|
||||
self._chassis = None
|
||||
|
||||
@property
|
||||
def conf(self):
|
||||
return self._conf
|
||||
|
||||
@property
|
||||
def sb_idl(self):
|
||||
@ -358,15 +397,27 @@ class MetadataAgent(object):
|
||||
def sb_idl(self, val):
|
||||
self._sb_idl = val
|
||||
|
||||
@property
|
||||
def chassis(self):
|
||||
return self._chassis
|
||||
|
||||
@property
|
||||
def chassis_id(self):
|
||||
return self._chassis_id
|
||||
|
||||
@property
|
||||
def ovn_bridge(self):
|
||||
return self._ovn_bridge
|
||||
|
||||
def _load_config(self):
|
||||
self.chassis = self._get_own_chassis_name()
|
||||
self._chassis = self._get_own_chassis_name()
|
||||
try:
|
||||
self.chassis_id = uuid.UUID(self.chassis)
|
||||
self._chassis_id = uuid.UUID(self._chassis)
|
||||
except ValueError:
|
||||
# OVS system-id could be a non UUID formatted string.
|
||||
self.chassis_id = uuid.uuid5(OVN_METADATA_UUID_NAMESPACE,
|
||||
self.chassis)
|
||||
self.ovn_bridge = self._get_ovn_bridge()
|
||||
self._chassis_id = uuid.uuid5(OVN_METADATA_UUID_NAMESPACE,
|
||||
self._chassis)
|
||||
self._ovn_bridge = self._get_ovn_bridge()
|
||||
LOG.debug("Loaded chassis name %s (UUID: %s) and ovn bridge %s.",
|
||||
self.chassis, self.chassis_id, self.ovn_bridge)
|
||||
|
||||
@ -408,14 +459,14 @@ class MetadataAgent(object):
|
||||
|
||||
self._post_fork_event.clear()
|
||||
self.sb_idl = ovsdb.MetadataAgentOvnSbIdl(
|
||||
chassis=self.chassis, tables=tables, events=events).start()
|
||||
chassis=self._chassis, tables=tables, events=events).start()
|
||||
|
||||
# Now IDL connections can be safely used.
|
||||
self._post_fork_event.set()
|
||||
|
||||
# Launch the server that will act as a proxy between the VM's and Nova.
|
||||
self._proxy = metadata_server.UnixDomainMetadataProxy(
|
||||
self.conf, self.chassis, sb_idl=self.sb_idl)
|
||||
self.conf, self._chassis, sb_idl=self.sb_idl)
|
||||
self._proxy.run()
|
||||
|
||||
# Do the initial sync.
|
||||
@ -661,7 +712,7 @@ class MetadataAgent(object):
|
||||
metadata_port.logical_port)
|
||||
|
||||
chassis_ports = self.sb_idl.get_ports_on_chassis(
|
||||
self.chassis, include_additional_chassis=True)
|
||||
self._chassis, include_additional_chassis=True)
|
||||
datapath_ports_ips = []
|
||||
for chassis_port in self._vif_ports(chassis_ports):
|
||||
if str(chassis_port.datapath.uuid) == datapath_uuid:
|
||||
|
@ -79,4 +79,4 @@ class FakeOVNAgentExtension(ext_mgr.OVNAgentExtension):
|
||||
return [OVNSBChassisEvent]
|
||||
|
||||
def start(self):
|
||||
self._is_ext_started = True
|
||||
self._is_started = True
|
||||
|
@ -14,47 +14,57 @@
|
||||
# under the License.
|
||||
|
||||
from unittest import mock
|
||||
import uuid
|
||||
|
||||
from oslo_config import fixture as fixture_config
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron.agent.ovn.agent import ovn_neutron_agent
|
||||
from neutron.agent.ovn.agent import ovsdb as agent_ovsdb
|
||||
from neutron.agent.ovn.metadata import agent as metadata_agent
|
||||
from neutron.common.ovn import constants as ovn_const
|
||||
from neutron.common import utils as n_utils
|
||||
from neutron.tests.common import net_helpers
|
||||
from neutron.tests.functional import base
|
||||
|
||||
|
||||
TEST_EXTENSION = 'testing'
|
||||
METADATA_EXTENSION = 'metadata'
|
||||
EXTENSION_NAMES = {TEST_EXTENSION: 'Fake OVN agent extension',
|
||||
METADATA_EXTENSION: 'Metadata OVN agent extension',
|
||||
}
|
||||
|
||||
|
||||
class TestOVNNeutronAgent(base.TestOVNFunctionalBase):
|
||||
class TestOVNNeutronAgentBase(base.TestOVNFunctionalBase):
|
||||
|
||||
OVN_BRIDGE = 'br-int'
|
||||
FAKE_CHASSIS_HOST = 'ovn-host-fake'
|
||||
|
||||
def setUp(self, **kwargs):
|
||||
def setUp(self, extensions, **kwargs):
|
||||
super().setUp(**kwargs)
|
||||
self.host_name = 'host-' + uuidutils.generate_uuid()[:5]
|
||||
self.chassis_name = self.add_fake_chassis(self.host_name)
|
||||
self.extensions = extensions
|
||||
self.mock_chassis_name = mock.patch.object(
|
||||
agent_ovsdb, 'get_own_chassis_name').start()
|
||||
self.ovn_agent = self._start_ovn_neutron_agent()
|
||||
agent_ovsdb, 'get_own_chassis_name',
|
||||
return_value=self.chassis_name).start()
|
||||
with mock.patch.object(metadata_agent.MetadataAgent,
|
||||
'_get_own_chassis_name',
|
||||
return_value=self.chassis_name):
|
||||
self.ovn_agent = self._start_ovn_neutron_agent()
|
||||
|
||||
def _check_loaded_and_started_extensions(self, ovn_agent):
|
||||
loaded_ext = ovn_agent[TEST_EXTENSION]
|
||||
self.assertEqual('Fake OVN agent extension', loaded_ext.name)
|
||||
self.assertTrue(loaded_ext._is_ext_started)
|
||||
for _ext in self.extensions:
|
||||
loaded_ext = ovn_agent[_ext]
|
||||
self.assertEqual(EXTENSION_NAMES.get(_ext), loaded_ext.name)
|
||||
self.assertTrue(loaded_ext.is_started)
|
||||
|
||||
def _start_ovn_neutron_agent(self):
|
||||
conf = self.useFixture(fixture_config.Config()).conf
|
||||
conf.set_override('extensions', TEST_EXTENSION, group='agent')
|
||||
conf.set_override('extensions', ','.join(self.extensions),
|
||||
group='agent')
|
||||
ovn_nb_db = self.ovsdb_server_mgr.get_ovsdb_connection_path('nb')
|
||||
conf.set_override('ovn_nb_connection', ovn_nb_db, group='ovn')
|
||||
ovn_sb_db = self.ovsdb_server_mgr.get_ovsdb_connection_path('sb')
|
||||
conf.set_override('ovn_sb_connection', ovn_sb_db, group='ovn')
|
||||
|
||||
self.chassis_name = uuidutils.generate_uuid()
|
||||
self.mock_chassis_name.return_value = self.chassis_name
|
||||
|
||||
agt = ovn_neutron_agent.OVNNeutronAgent(conf)
|
||||
agt.test_ovs_idl = []
|
||||
agt.test_ovn_sb_idl = []
|
||||
@ -62,8 +72,6 @@ class TestOVNNeutronAgent(base.TestOVNFunctionalBase):
|
||||
agt.start()
|
||||
self._check_loaded_and_started_extensions(agt)
|
||||
|
||||
self.add_fake_chassis(self.FAKE_CHASSIS_HOST, name=self.chassis_name)
|
||||
|
||||
self.addCleanup(agt.ext_manager_api.ovs_idl.ovsdb_connection.stop)
|
||||
if agt.ext_manager_api.sb_idl:
|
||||
self.addCleanup(agt.ext_manager_api.sb_idl.ovsdb_connection.stop)
|
||||
@ -71,6 +79,12 @@ class TestOVNNeutronAgent(base.TestOVNFunctionalBase):
|
||||
self.addCleanup(agt.ext_manager_api.nb_idl.ovsdb_connection.stop)
|
||||
return agt
|
||||
|
||||
|
||||
class TestOVNNeutronAgentFakeAgent(TestOVNNeutronAgentBase):
|
||||
|
||||
def setUp(self, **kwargs):
|
||||
super().setUp(extensions=[TEST_EXTENSION], **kwargs)
|
||||
|
||||
def test_ovs_and_ovs_events(self):
|
||||
# Test the OVS IDL is attending the provided events.
|
||||
bridge = self.useFixture(net_helpers.OVSBridgeFixture()).bridge
|
||||
@ -95,4 +109,22 @@ class TestOVNNeutronAgent(base.TestOVNFunctionalBase):
|
||||
exc = Exception('Logical Switch %s not added or not detected by ')
|
||||
n_utils.wait_until_true(
|
||||
lambda: lswitch_name in self.ovn_agent.test_ovn_nb_idl,
|
||||
timeout=10)
|
||||
timeout=10, exception=exc)
|
||||
|
||||
|
||||
class TestOVNNeutronAgentMetadataExtension(TestOVNNeutronAgentBase):
|
||||
|
||||
def setUp(self, **kwargs):
|
||||
super().setUp(extensions=[METADATA_EXTENSION], **kwargs)
|
||||
|
||||
def test_check_metadata_started(self):
|
||||
# Check the metadata extension is registered.
|
||||
chassis_id = uuid.UUID(self.chassis_name)
|
||||
agent_id = uuid.uuid5(chassis_id, 'metadata_agent')
|
||||
ext_ids = {ovn_const.OVN_AGENT_METADATA_ID_KEY: str(agent_id)}
|
||||
ch_private = self.sb_api.lookup('Chassis_Private', self.chassis_name)
|
||||
self.assertEqual(ext_ids, ch_private.external_ids)
|
||||
|
||||
# Check Unix proxy is running.
|
||||
metadata_extension = self.ovn_agent[METADATA_EXTENSION]
|
||||
self.assertIsNotNone(metadata_extension._proxy.server)
|
||||
|
@ -80,8 +80,8 @@ class TestMetadataAgent(base.BaseTestCase):
|
||||
self.agent.sb_idl = mock.Mock()
|
||||
self.agent.ovs_idl = mock.Mock()
|
||||
self.agent.ovs_idl.transaction = mock.MagicMock()
|
||||
self.agent.chassis = 'chassis'
|
||||
self.agent.ovn_bridge = 'br-int'
|
||||
self.agent._chassis = 'chassis'
|
||||
self.agent._ovn_bridge = 'br-int'
|
||||
|
||||
self.ports = []
|
||||
for i in range(0, 3):
|
||||
|
@ -146,6 +146,7 @@ neutron.agent.l3.extensions =
|
||||
conntrack_helper = neutron.agent.l3.extensions.conntrack_helper:ConntrackHelperAgentExtension
|
||||
ndp_proxy = neutron.agent.l3.extensions.ndp_proxy:NDPProxyAgentExtension
|
||||
neutron.agent.ovn.extensions =
|
||||
metadata = neutron.agent.ovn.extensions.metadata:MetadataExtension
|
||||
qos_hwol = neutron.agent.ovn.extensions.qos_hwol:QoSHardwareOffloadExtension
|
||||
noop = neutron.agent.ovn.extensions.noop:NoopOVNAgentExtension
|
||||
testing = neutron.tests.functional.agent.ovn.agent.fake_ovn_agent_extension:FakeOVNAgentExtension
|
||||
|
Loading…
Reference in New Issue
Block a user