Merge "[OVN] Implement OVN agent metadata extension"
This commit is contained in:
commit
c0f113073d
@ -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