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:
armando-migliaccio 2016-01-28 01:39:00 -08:00 committed by Kyle Mestery
parent 5888f79baf
commit aadf2f30f8
8 changed files with 57 additions and 6 deletions

View File

@ -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():

View File

@ -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 == '':

View File

@ -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"

View File

@ -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')

View File

@ -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):

View File

@ -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')

View File

@ -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.

View File

@ -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 =