Add stevedore aliases for interface_driver configuration

Changed the interface_driver configure for agents from class
imports to stevedor aliases. The loading method needed to be
updated to load as a DriverManager. Backward compatability
for configuration as class import.

DocImpact

Change-Id: Ic349691989484286cd7c60eaf3ad1454c5852d1f
Closes-Bug: #1504536
This commit is contained in:
Martin Hickey 2015-10-14 22:32:49 +01:00
parent d57c546f1f
commit c5db6050e5
8 changed files with 116 additions and 38 deletions

View File

@ -13,7 +13,7 @@
# Example of interface_driver option for OVS based plugins(OVS, Ryu, NEC, NVP,
# BigSwitch/Floodlight)
# interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver
# interface_driver = openvswitch
# Name of Open vSwitch bridge to use
# ovs_integration_bridge = br-int
@ -24,7 +24,7 @@
# ovs_use_veth = False
# Example of interface_driver option for LinuxBridge
# interface_driver = neutron.agent.linux.interface.BridgeInterfaceDriver
# interface_driver = linuxbridge
# The agent can use other DHCP drivers. Dnsmasq is the simplest and requires
# no additional setup of the DHCP server.

View File

@ -8,7 +8,7 @@
# Example of interface_driver option for OVS based plugins (OVS, Ryu, NEC)
# that supports L3 agent
# interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver
# interface_driver = openvswitch
# Use veth for an OVS interface or not.
# Support kernels with limited namespace support
@ -16,7 +16,7 @@
# ovs_use_veth = False
# Example of interface_driver option for LinuxBridge
# interface_driver = neutron.agent.linux.interface.BridgeInterfaceDriver
# interface_driver = linuxbridge
# Allow overlapping IP (Must have kernel build with CONFIG_NET_NS=y and
# iproute2 package that supports namespaces). This option is deprecated and

View File

@ -17,9 +17,9 @@ import os
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import importutils
from neutron.agent.common import config
from neutron.common import utils as neutron_utils
from neutron.i18n import _LE
@ -32,19 +32,24 @@ else:
LOG = logging.getLogger(__name__)
config.register_root_helper(cfg.CONF)
INTERFACE_NAMESPACE = 'neutron.interface_drivers'
execute = utils.execute
def load_interface_driver(conf):
if not conf.interface_driver:
LOG.error(_LE('An interface driver must be specified'))
raise SystemExit(1)
"""Load interface driver for agents like DHCP or L3 agent.
:param conf: driver configuration object
:raises SystemExit of 1 if driver cannot be loaded
"""
try:
return importutils.import_object(conf.interface_driver, conf)
except ImportError as e:
LOG.error(_LE("Error importing interface driver "
"'%(driver)s': %(inner)s"),
{'driver': conf.interface_driver,
'inner': e})
loaded_class = neutron_utils.load_class_by_alias_or_classname(
INTERFACE_NAMESPACE, conf.interface_driver)
return loaded_class(conf)
except ImportError:
LOG.error(_LE("Error loading interface driver '%s'"),
conf.interface_driver)
raise SystemExit(1)

View File

@ -30,6 +30,7 @@ import os
import random
import signal
import socket
import sys
import tempfile
import uuid
@ -38,9 +39,12 @@ from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import importutils
import six
from stevedore import driver
from neutron.common import constants as n_const
from neutron.i18n import _LE
TIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
LOG = logging.getLogger(__name__)
@ -487,3 +491,32 @@ def replace_file(file_name, data):
tmp_file.write(data)
os.chmod(tmp_file.name, 0o644)
os.rename(tmp_file.name, file_name)
def load_class_by_alias_or_classname(namespace, name):
"""Load class using stevedore alias or the class name
:param namespace: namespace where the alias is defined
:param name: alias or class name of the class to be loaded
:returns class if calls can be loaded
:raises ImportError if class cannot be loaded
"""
if not name:
LOG.error(_LE("Alias or class name is not set"))
raise ImportError(_("Class not found."))
try:
# Try to resolve class by alias
mgr = driver.DriverManager(namespace, name)
class_to_load = mgr.driver
except RuntimeError:
e1_info = sys.exc_info()
# Fallback to class name
try:
class_to_load = importutils.import_class(name)
except (ImportError, ValueError):
LOG.error(_LE("Error loading class by alias"),
exc_info=e1_info)
LOG.error(_LE("Error loading class by class name"),
exc_info=True)
raise ImportError(_("Class not found."))
return class_to_load

View File

@ -13,23 +13,19 @@
# License for the specific language governing permissions and limitations
# under the License.
import sys
import weakref
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
from oslo_service import periodic_task
from oslo_utils import importutils
import six
from neutron.common import utils
from neutron.db import flavors_db
from neutron.i18n import _LE, _LI
from neutron.i18n import _LI
from neutron.plugins.common import constants
from stevedore import driver
LOG = logging.getLogger(__name__)
@ -136,25 +132,18 @@ class NeutronManager(object):
@staticmethod
def load_class_for_provider(namespace, plugin_provider):
if not plugin_provider:
LOG.error(_LE("Error, plugin is not set"))
raise ImportError(_("Plugin not found."))
"""Loads plugin using alias or class name
:param namespace: namespace where alias is defined
:param plugin_provider: plugin alias or class name
:returns plugin that is loaded
:raises ImportError if fails to load plugin
"""
try:
# Try to resolve plugin by name
mgr = driver.DriverManager(namespace, plugin_provider)
plugin_class = mgr.driver
except RuntimeError:
e1_info = sys.exc_info()
# fallback to class name
try:
plugin_class = importutils.import_class(plugin_provider)
except ImportError:
LOG.error(_LE("Error loading plugin by name"),
exc_info=e1_info)
LOG.error(_LE("Error loading plugin by class"),
exc_info=True)
raise ImportError(_("Plugin not found."))
return plugin_class
return utils.load_class_by_alias_or_classname(namespace,
plugin_provider)
except ImportError:
raise ImportError(_("Plugin '%s' not found.") % plugin_provider)
def _get_plugin_instance(self, namespace, plugin_provider):
plugin_class = self.load_class_for_provider(namespace, plugin_provider)

View File

@ -42,7 +42,7 @@ class TestLoadInterfaceDriver(base.BaseTestCase):
def test_load_interface_driver_does_not_consume_irrelevant_errors(self):
self.conf.set_override('interface_driver',
'neutron.agent.linux.interface.NullDriver')
with mock.patch('oslo_utils.importutils.import_object',
with mock.patch('oslo_utils.importutils.import_class',
side_effect=RuntimeError()):
with testlib_api.ExpectedException(RuntimeError):
utils.load_interface_driver(self.conf)
@ -52,3 +52,38 @@ class TestLoadInterfaceDriver(base.BaseTestCase):
'neutron.agent.linux.interface.NullDriver')
self.assertIsInstance(utils.load_interface_driver(self.conf),
interface.NullDriver)
def test_load_null_interface_driver_success(self):
self.conf.set_override('interface_driver',
'null')
self.assertIsInstance(utils.load_interface_driver(self.conf),
interface.NullDriver)
def test_load_ivs_interface_driver_success(self):
self.conf.set_override('interface_driver',
'ivs')
self.assertIsInstance(utils.load_interface_driver(self.conf),
interface.IVSInterfaceDriver)
def test_load_linuxbridge_interface_driver_success(self):
self.conf.set_override('interface_driver',
'linuxbridge')
self.assertIsInstance(utils.load_interface_driver(self.conf),
interface.BridgeInterfaceDriver)
def test_load_midonet_interface_driver_success(self):
self.conf.set_override('interface_driver',
'midonet')
self.assertIsInstance(utils.load_interface_driver(self.conf),
interface.MidonetInterfaceDriver)
def test_load_ovs_interface_driver_success(self):
self.conf.set_override('interface_driver',
'openvswitch')
self.assertIsInstance(utils.load_interface_driver(self.conf),
interface.OVSInterfaceDriver)
def test_load_interface_driver_as_alias_wrong_driver(self):
self.conf.set_override('interface_driver', 'openvswitchXX')
with testlib_api.ExpectedException(SystemExit):
utils.load_interface_driver(self.conf)

View File

@ -21,6 +21,7 @@ from neutron import manager
from neutron.plugins.common import constants
from neutron.tests import base
from neutron.tests.unit import dummy_plugin
from neutron.tests.unit import testlib_api
LOG = logging.getLogger(__name__)
@ -139,3 +140,12 @@ class NeutronManagerTestCase(base.BaseTestCase):
'dummy': 'dummy_agent_notifier'}
core_plugin = manager.NeutronManager.get_plugin()
self.assertEqual(expected, core_plugin.agent_notifiers)
def test_load_class_for_provider(self):
manager.NeutronManager.load_class_for_provider(
'neutron.core_plugins', 'ml2')
def test_load_class_for_provider_wrong_plugin(self):
with testlib_api.ExpectedException(ImportError):
manager.NeutronManager.load_class_for_provider(
'neutron.core_plugins', 'ml2XXXXXX')

View File

@ -173,6 +173,12 @@ oslo.messaging.notify.drivers =
neutron.openstack.common.notifier.test_notifier = oslo_messaging.notify._impl_test:TestDriver
neutron.db.alembic_migrations =
neutron = neutron.db.migration:alembic_migrations
neutron.interface_drivers =
ivs = neutron.agent.linux.interface:IVSInterfaceDriver
linuxbridge = neutron.agent.linux.interface:BridgeInterfaceDriver
midonet = neutron.agent.linux.interface:MidonetInterfaceDriver
null = neutron.agent.linux.interface:NullDriver
openvswitch = neutron.agent.linux.interface:OVSInterfaceDriver
[build_sphinx]
all_files = 1