diff --git a/etc/neutron.conf b/etc/neutron.conf index 6086d1a659..1e2226fc3e 100644 --- a/etc/neutron.conf +++ b/etc/neutron.conf @@ -44,11 +44,23 @@ lock_path = $state_path/lock # extensions are in there you don't need to specify them here # api_extensions_path = -# Neutron plugin provider module +# (StrOpt) Neutron core plugin entrypoint to be loaded from the +# neutron.core_plugins namespace. See setup.cfg for the entrypoint names of the +# plugins included in the neutron source distribution. For compatibility with +# previous versions, the class name of a plugin can be specified instead of its +# entrypoint name. +# # core_plugin = +# Example: core_plugin = ml2 -# Advanced service modules +# (ListOpt) List of service plugin entrypoints to be loaded from the +# neutron.service_plugins namespace. See setup.cfg for the entrypoint names of +# the plugins included in the neutron source distribution. For compatibility +# with previous versions, the class name of a plugin can be specified instead +# of its entrypoint name. +# # service_plugins = +# Example: service_plugins = router,firewall,lbaas,vpnaas,metering # Paste configuration file # api_paste_config = api-paste.ini diff --git a/neutron/manager.py b/neutron/manager.py index bc71464191..eda835d3ca 100644 --- a/neutron/manager.py +++ b/neutron/manager.py @@ -25,6 +25,8 @@ from neutron.openstack.common import log as logging from neutron.openstack.common import periodic_task from neutron.plugins.common import constants +from stevedore import driver + LOG = logging.getLogger(__name__) @@ -108,16 +110,10 @@ class NeutronManager(object): # intentianally to allow v2 plugins to be monitored # for performance metrics. plugin_provider = cfg.CONF.core_plugin - LOG.debug(_("Plugin location: %s"), plugin_provider) - # If the plugin can't be found let them know gracefully - try: - LOG.info(_("Loading Plugin: %s"), plugin_provider) - plugin_klass = importutils.import_class(plugin_provider) - except ImportError: - LOG.exception(_("Error loading plugin")) - raise Exception(_("Plugin not found. ")) + LOG.info(_("Loading core plugin: %s"), plugin_provider) + self.plugin = self._get_plugin_instance('neutron.core_plugins', + plugin_provider) legacy.modernize_quantum_config(cfg.CONF) - self.plugin = plugin_klass() msg = validate_post_plugin_load() if msg: @@ -131,6 +127,21 @@ class NeutronManager(object): self.service_plugins = {constants.CORE: self.plugin} self._load_service_plugins() + def _get_plugin_instance(self, namespace, plugin_provider): + try: + # Try to resolve plugin by name + mgr = driver.DriverManager(namespace, plugin_provider) + plugin_class = mgr.driver + except RuntimeError as e1: + # fallback to class name + try: + plugin_class = importutils.import_class(plugin_provider) + except ImportError as e2: + LOG.exception(_("Error loading plugin by name, %s"), e1) + LOG.exception(_("Error loading plugin by class, %s"), e2) + raise ImportError(_("Plugin not found.")) + return plugin_class() + def _load_services_from_core_plugin(self): """Puts core plugin in service_plugins for supported services.""" LOG.debug(_("Loading services supported by the core plugin")) @@ -159,13 +170,10 @@ class NeutronManager(object): for provider in plugin_providers: if provider == '': continue - try: - LOG.info(_("Loading Plugin: %s"), provider) - plugin_class = importutils.import_class(provider) - except ImportError: - LOG.exception(_("Error loading plugin")) - raise ImportError(_("Plugin not found.")) - plugin_inst = plugin_class() + + LOG.info(_("Loading Plugin: %s"), provider) + plugin_inst = self._get_plugin_instance('neutron.service_plugins', + provider) # only one implementation of svc_type allowed # specifying more than one plugin diff --git a/neutron/tests/unit/test_neutron_manager.py b/neutron/tests/unit/test_neutron_manager.py index e8e5fde33f..86db9f9440 100644 --- a/neutron/tests/unit/test_neutron_manager.py +++ b/neutron/tests/unit/test_neutron_manager.py @@ -76,6 +76,17 @@ class NeutronManagerTestCase(base.BaseTestCase): (dummy_plugin.DummyServicePlugin, types.ClassType)), "loaded plugin should be of type neutronDummyPlugin") + def test_service_plugin_by_name_is_loaded(self): + cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) + cfg.CONF.set_override("service_plugins", ["dummy"]) + mgr = NeutronManager.get_instance() + plugin = mgr.get_service_plugins()[constants.DUMMY] + + self.assertTrue( + isinstance(plugin, + (dummy_plugin.DummyServicePlugin, types.ClassType)), + "loaded plugin should be of type neutronDummyPlugin") + def test_multiple_plugins_specified_for_service_type(self): cfg.CONF.set_override("service_plugins", ["neutron.tests.unit.dummy_plugin." @@ -85,6 +96,18 @@ class NeutronManagerTestCase(base.BaseTestCase): cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) self.assertRaises(ValueError, NeutronManager.get_instance) + def test_multiple_plugins_by_name_specified_for_service_type(self): + cfg.CONF.set_override("service_plugins", ["dummy", "dummy"]) + cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) + self.assertRaises(ValueError, NeutronManager.get_instance) + + def test_multiple_plugins_mixed_specified_for_service_type(self): + cfg.CONF.set_override("service_plugins", + ["neutron.tests.unit.dummy_plugin." + "DummyServicePlugin", "dummy"]) + cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS) + self.assertRaises(ValueError, NeutronManager.get_instance) + def test_service_plugin_conflicts_with_core_plugin(self): cfg.CONF.set_override("service_plugins", ["neutron.tests.unit.dummy_plugin." diff --git a/setup.cfg b/setup.cfg index 5306300420..fc1eb6e05d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -119,6 +119,28 @@ console_scripts = quantum-rootwrap = neutron.openstack.common.rootwrap.cmd:main quantum-usage-audit = neutron.cmd.usage_audit:main neutron-metering-agent = neutron.services.metering.agents.metering_agent:main +neutron.core_plugins = + bigswitch = neutron.plugins.bigswitch.plugin:NeutronRestProxyV2 + brocade = neutron.plugins.brocade.NeutronPlugin:BrocadePluginV2 + cisco = neutron.plugins.cisco.network_plugin:PluginV2 + embrane = neutron.plugins.embrane.plugins.embrane_ovs_plugin:EmbraneOvsPlugin + hyperv = neutron.plugins.hyperv.hyperv_neutron_plugin:HyperVNeutronPlugin + linuxbridge = neutron.plugins.linuxbridge.lb_neutron_plugin:LinuxBridgePluginV2 + midonet = neutron.plugins.midonet.plugin:MidonetPluginV2 + ml2 = neutron.plugins.ml2.plugin:Ml2Plugin + mlnx = neutron.plugins.mlnx.mlnx_plugin:MellanoxEswitchPlugin + nec = neutron.plugins.nec.nec_plugin:NECPluginV2 + nicira = neutron.plugins.nicira.NeutronPlugin:NvpPluginV2 + openvswitch = neutron.plugins.openvswitch.ovs_neutron_plugin:OVSNeutronPluginV2 + plumgrid = neutron.plugins.plumgrid.plumgrid_plugin.plumgrid_plugin:NeutronPluginPLUMgridV2 + ryu = neutron.plugins.ryu.ryu_neutron_plugin:RyuNeutronPluginV2 +neutron.service_plugins = + dummy = neutron.tests.unit.dummy_plugin:DummyServicePlugin + router = neutron.services.l3_router.l3_router_plugin:L3RouterPlugin + firewall = neutron.services.firewall.fwaas_plugin:FirewallPlugin + lbaas = neutron.services.loadbalancer.plugin:LoadBalancerPlugin + vpnaas = neutron.services.vpn.plugin:VPNDriverPlugin + metering = neutron.services.metering.metering_plugin:MeteringPlugin neutron.ml2.type_drivers = flat = neutron.plugins.ml2.drivers.type_flat:FlatTypeDriver local = neutron.plugins.ml2.drivers.type_local:LocalTypeDriver