diff --git a/etc/dhcp_agent.ini b/etc/dhcp_agent.ini index 6996ed24fb4..40c6c47e502 100644 --- a/etc/dhcp_agent.ini +++ b/etc/dhcp_agent.ini @@ -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. diff --git a/etc/l3_agent.ini b/etc/l3_agent.ini index b3ac40c3382..a0c9de71e6c 100644 --- a/etc/l3_agent.ini +++ b/etc/l3_agent.ini @@ -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 diff --git a/neutron/agent/common/utils.py b/neutron/agent/common/utils.py index a6255709b24..f45ab600b93 100644 --- a/neutron/agent/common/utils.py +++ b/neutron/agent/common/utils.py @@ -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) diff --git a/neutron/common/utils.py b/neutron/common/utils.py index 1fe6ab1ce5c..47c25e6e614 100644 --- a/neutron/common/utils.py +++ b/neutron/common/utils.py @@ -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 diff --git a/neutron/manager.py b/neutron/manager.py index 51c9bf5cf48..e5ec3cb4960 100644 --- a/neutron/manager.py +++ b/neutron/manager.py @@ -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) diff --git a/neutron/tests/unit/agent/common/test_utils.py b/neutron/tests/unit/agent/common/test_utils.py index a4cf6680204..772b4e354cf 100644 --- a/neutron/tests/unit/agent/common/test_utils.py +++ b/neutron/tests/unit/agent/common/test_utils.py @@ -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) diff --git a/neutron/tests/unit/test_manager.py b/neutron/tests/unit/test_manager.py index 2020804fd4f..97c236d6f50 100644 --- a/neutron/tests/unit/test_manager.py +++ b/neutron/tests/unit/test_manager.py @@ -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') diff --git a/setup.cfg b/setup.cfg index 7cf270aae31..dd3f3b6c29a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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