Add the ability to load a set of service plugins on startup
Service plugins are a great way of adding functionality in a cohesive way. Some plugins (e.g. network ip availability or auto_allocate) extend the capabilities of the Neutron server by being completely orthogonal to the core plugin, and yet may be considered an integral part of functionality available in any Neutron deployment. For this reason, it makes sense to include them seamlessly in the service plugin loading process. This patch, in particular, introduces the 'auto_allocate' service plugin for default loading, as we'd want this feature to be enabled for Nova to use irrespective of the chosen underlying core plugin. The feature requires subnetpools, external_net and router, while the first is part of the core, the others can be plugin specific and they must be explicitly advertised. That said, they all are features that any deployment can hardly live without. DocImpact: The "get-me-a-network" feature simplifies the process for launching an instance with basic network connectivity (via an externally connected private tenant network). Once leveraged by Nova, a tenant/admin is no longer required to provision networking resources ahead of boot process in order to successfully launch an instance. Implements: blueprint get-me-a-network Change-Id: Ia35e8a946bf0ac0bb085cde46b675d17b0bb2f51
This commit is contained in:
parent
5888f79baf
commit
aadf2f30f8
|
@ -30,6 +30,7 @@ from neutron._i18n import _, _LE, _LI, _LW
|
|||
from neutron.common import exceptions
|
||||
import neutron.extensions
|
||||
from neutron import manager
|
||||
from neutron.plugins.common import constants as const
|
||||
from neutron.services import provider_configuration
|
||||
from neutron import wsgi
|
||||
|
||||
|
@ -440,11 +441,18 @@ class ExtensionManager(object):
|
|||
# Exit loop as no progress was made
|
||||
break
|
||||
if exts_to_process:
|
||||
LOG.error(_LE("It was impossible to process the following "
|
||||
"extensions: %s because of missing requirements."),
|
||||
','.join(exts_to_process.keys()))
|
||||
raise exceptions.ExtensionsNotFound(
|
||||
extensions=list(exts_to_process.keys()))
|
||||
unloadable_extensions = set(exts_to_process.keys())
|
||||
LOG.error(_LE("Unable to process extensions (%s) because "
|
||||
"the configured plugins do not satisfy "
|
||||
"their requirements. Some features will not "
|
||||
"work as expected."),
|
||||
', '.join(unloadable_extensions))
|
||||
# Fail gracefully for default extensions, just in case some out
|
||||
# of tree plugins are not entirely up to speed
|
||||
default_extensions = set(const.DEFAULT_SERVICE_PLUGINS.values())
|
||||
if not unloadable_extensions <= default_extensions:
|
||||
raise exceptions.ExtensionsNotFound(
|
||||
extensions=list(unloadable_extensions))
|
||||
|
||||
# Extending extensions' attributes map.
|
||||
for ext in processed_exts.values():
|
||||
|
|
|
@ -161,6 +161,10 @@ class NeutronManager(object):
|
|||
LOG.info(_LI("Service %s is supported by the core plugin"),
|
||||
service_type)
|
||||
|
||||
def _get_default_service_plugins(self):
|
||||
"""Get default service plugins to be loaded."""
|
||||
return constants.DEFAULT_SERVICE_PLUGINS.keys()
|
||||
|
||||
def _load_service_plugins(self):
|
||||
"""Loads service plugins.
|
||||
|
||||
|
@ -171,6 +175,7 @@ class NeutronManager(object):
|
|||
self._load_services_from_core_plugin()
|
||||
|
||||
plugin_providers = cfg.CONF.service_plugins
|
||||
plugin_providers.extend(self._get_default_service_plugins())
|
||||
LOG.debug("Loading service plugins: %s", plugin_providers)
|
||||
for provider in plugin_providers:
|
||||
if provider == '':
|
||||
|
|
|
@ -38,6 +38,11 @@ EXT_TO_SERVICE_MAPPING = {
|
|||
'qos': QOS,
|
||||
}
|
||||
|
||||
# Maps default service plugins entry points to their extension aliases
|
||||
DEFAULT_SERVICE_PLUGINS = {
|
||||
'auto_allocate': 'auto-allocated-topology',
|
||||
}
|
||||
|
||||
# Service operation status constants
|
||||
ACTIVE = "ACTIVE"
|
||||
DOWN = "DOWN"
|
||||
|
|
|
@ -350,6 +350,7 @@ class BaseTestCase(DietTestCase):
|
|||
cp = PluginFixture(core_plugin)
|
||||
self.useFixture(cp)
|
||||
self.patched_dhcp_periodic = cp.patched_dhcp_periodic
|
||||
self.patched_default_svc_plugins = cp.patched_default_svc_plugins
|
||||
|
||||
def setup_notification_driver(self, notification_driver=None):
|
||||
self.addCleanup(fake_notifier.reset)
|
||||
|
@ -365,6 +366,11 @@ class PluginFixture(fixtures.Fixture):
|
|||
self.core_plugin = core_plugin
|
||||
|
||||
def _setUp(self):
|
||||
# Do not load default service plugins in the testing framework
|
||||
# as all the mocking involved can cause havoc.
|
||||
self.default_svc_plugins_p = mock.patch(
|
||||
'neutron.manager.NeutronManager._get_default_service_plugins')
|
||||
self.patched_default_svc_plugins = self.default_svc_plugins_p.start()
|
||||
self.dhcp_periodic_p = mock.patch(
|
||||
'neutron.db.agentschedulers_db.DhcpAgentSchedulerDbMixin.'
|
||||
'start_periodic_dhcp_agent_status_check')
|
||||
|
|
|
@ -502,13 +502,21 @@ class RequestExtensionTest(base.BaseTestCase):
|
|||
|
||||
class ExtensionManagerTest(base.BaseTestCase):
|
||||
|
||||
def test_missing_required_extensions(self):
|
||||
def test_missing_required_extensions_raise_error(self):
|
||||
ext_mgr = extensions.ExtensionManager('')
|
||||
attr_map = {}
|
||||
ext_mgr.add_extension(ext_stubs.StubExtensionWithReqs('foo_alias'))
|
||||
self.assertRaises(exceptions.ExtensionsNotFound,
|
||||
ext_mgr.extend_resources, "2.0", attr_map)
|
||||
|
||||
def test_missing_required_extensions_gracefully_error(self):
|
||||
ext_mgr = extensions.ExtensionManager('')
|
||||
attr_map = {}
|
||||
default_ext = list(constants.DEFAULT_SERVICE_PLUGINS.values())[0]
|
||||
ext_mgr.add_extension(ext_stubs.StubExtensionWithReqs(default_ext))
|
||||
ext_mgr.extend_resources("2.0", attr_map)
|
||||
self.assertIn(default_ext, ext_mgr.extensions)
|
||||
|
||||
def test_invalid_extensions_are_not_registered(self):
|
||||
|
||||
class InvalidExtension(object):
|
||||
|
|
|
@ -111,6 +111,15 @@ class NeutronManagerTestCase(base.BaseTestCase):
|
|||
self.assertIn(constants.LOADBALANCER, svc_plugins.keys())
|
||||
self.assertIn(constants.DUMMY, svc_plugins.keys())
|
||||
|
||||
def test_load_default_service_plugins(self):
|
||||
self.patched_default_svc_plugins.return_value = {
|
||||
'neutron.tests.unit.dummy_plugin.DummyServicePlugin': 'DUMMY'
|
||||
}
|
||||
cfg.CONF.set_override("core_plugin", DB_PLUGIN_KLASS)
|
||||
mgr = manager.NeutronManager.get_instance()
|
||||
svc_plugins = mgr.get_service_plugins()
|
||||
self.assertIn('DUMMY', svc_plugins)
|
||||
|
||||
def test_post_plugin_validation(self):
|
||||
cfg.CONF.import_opt('dhcp_agents_per_network',
|
||||
'neutron.db.agentschedulers_db')
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
---
|
||||
prelude: >
|
||||
The "get-me-a-network" feature simplifies the process
|
||||
for launching an instance with basic network connectivity
|
||||
(via an externally connected private tenant network).
|
||||
features:
|
||||
- Once Nova takes advantage of this feature, a user can
|
||||
launch an instance without explicitly provisioning
|
||||
network resources.
|
|
@ -76,6 +76,7 @@ neutron.service_plugins =
|
|||
neutron.services.vpn.plugin.VPNDriverPlugin = neutron_vpnaas.services.vpn.plugin:VPNDriverPlugin
|
||||
qos = neutron.services.qos.qos_plugin:QoSPlugin
|
||||
flavors = neutron.services.flavors.flavors_plugin:FlavorsPlugin
|
||||
auto_allocate = neutron.services.auto_allocate.plugin:Plugin
|
||||
neutron.qos.notification_drivers =
|
||||
message_queue = neutron.services.qos.notification_drivers.message_queue:RpcQosServiceNotificationDriver
|
||||
neutron.ml2.type_drivers =
|
||||
|
|
Loading…
Reference in New Issue