# If set, use this value for pool_timeout with sqlalchemy
# pool_timeout = 10
+# TODO(dougwig) - remove these lines once service repos have them
[service_providers]
# Specify service providers (drivers) for advanced services like loadbalancer, VPN, Firewall.
# Must be in form:
import webob.exc
from neutron.common import exceptions
+from neutron.common import repos
import neutron.extensions
from neutron.i18n import _LE, _LI, _LW
from neutron import manager
See tests/unit/extensions/foxinsocks.py for an example extension
implementation.
"""
+
+ # TODO(dougwig) - remove this after the service extensions move out
+ # While moving the extensions out of neutron into the service repos,
+ # don't double-load the same thing.
+ loaded = []
+
for path in self.path.split(':'):
if os.path.exists(path):
- self._load_all_extensions_from_path(path)
+ self._load_all_extensions_from_path(path, loaded)
else:
LOG.error(_LE("Extension path '%s' doesn't exist!"), path)
- def _load_all_extensions_from_path(self, path):
+ def _load_all_extensions_from_path(self, path, loaded):
# Sorting the extension list makes the order in which they
# are loaded predictable across a cluster of load-balanced
# Neutron Servers
mod_name, file_ext = os.path.splitext(os.path.split(f)[-1])
ext_path = os.path.join(path, f)
if file_ext.lower() == '.py' and not mod_name.startswith('_'):
+ if mod_name in loaded:
+ LOG.warn(_LW("Extension already loaded, skipping: %s"),
+ mod_name)
+ continue
mod = imp.load_source(mod_name, ext_path)
+ loaded.append(mod_name)
ext_name = mod_name[0].upper() + mod_name[1:]
new_ext_class = getattr(mod, ext_name, None)
if not new_ext_class:
# Returns the extension paths from a config entry and the __path__
# of neutron.extensions
def get_extensions_path():
- paths = ':'.join(neutron.extensions.__path__)
+ paths = neutron.extensions.__path__
+
+ neutron_mods = repos.NeutronModules()
+ for x in neutron_mods.installed_list():
+ paths += neutron_mods.module(x).__path__
+
if cfg.CONF.api_extensions_path:
- paths = ':'.join([cfg.CONF.api_extensions_path, paths])
+ paths.append(cfg.CONF.api_extensions_path)
+
+ LOG.debug("get_extension_paths = %s", paths)
- return paths
+ path = ':'.join(paths)
+ return path
def append_api_extensions_path(paths):
--- /dev/null
+# Copyright (c) 2015, A10 Networks
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import ConfigParser
+import importlib
+import os
+
+from neutron.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
+
+
+class NeutronModules(object):
+
+ MODULES = [
+ 'neutron_fwaas',
+ 'neutron_lbaas',
+ 'neutron_vpnaas'
+ ]
+
+ def __init__(self):
+ self.repos = {}
+ for repo in self.MODULES:
+ self.repos[repo] = {}
+ self.repos[repo]['mod'] = self._import_or_none(repo)
+ self.repos[repo]['ini'] = None
+
+ def _import_or_none(self, module):
+ try:
+ return importlib.import_module(module)
+ except ImportError:
+ return None
+
+ def installed_list(self):
+ z = filter(lambda k: self.repos[k]['mod'] is not None, self.repos)
+ LOG.debug("NeutronModules related repos installed = %s", z)
+ return z
+
+ def module(self, module):
+ return self.repos[module]['mod']
+
+ # Return an INI parser for the child module. oslo.conf is a bit too
+ # magical in its INI loading, and in one notable case, we need to merge
+ # together the [service_providers] section for across at least four
+ # repositories.
+ def ini(self, module):
+ if self.repos[module]['ini'] is None:
+ ini = ConfigParser.SafeConfigParser()
+
+ ini_path = '/etc/neutron/%s.conf' % module
+ if os.path.exists(ini_path):
+ ini.read(ini_path)
+
+ self.repos[module]['ini'] = ini
+
+ return self.repos[module]['ini']
import stevedore
from neutron.common import exceptions as n_exc
+from neutron.common import repos
from neutron.i18n import _LW
from neutron.openstack.common import log as logging
from neutron.plugins.common import constants
raise n_exc.Invalid(
_("Provider name is limited by 255 characters: %s") % name)
- svc_providers_opt = cfg.CONF.service_providers.service_provider
+ # Main neutron config file
+ try:
+ svc_providers_opt = cfg.CONF.service_providers.service_provider
+ except cfg.NoSuchOptError:
+ svc_providers_opt = []
+
+ # Add in entries from the *aas conf files
+ neutron_mods = repos.NeutronModules()
+ for x in neutron_mods.installed_list():
+ ini = neutron_mods.ini(x)
+ if ini is None:
+ continue
+
+ try:
+ sp = ini.items('service_providers')
+ for name, value in sp:
+ if name == 'service_provider':
+ svc_providers_opt.append(value)
+ except Exception:
+ continue
+
+ # TODO(dougwig) - remove this next bit after we've migrated all entries
+ # to the service repo config files. Some tests require a default driver
+ # to be present, but not two, which leads to a cross-repo breakage
+ # issue. uniq the list as a short-term workaround.
+ svc_providers_opt = list(set(svc_providers_opt))
+
+ LOG.debug("Service providers = %s", svc_providers_opt)
+
res = []
for prov_def in svc_providers_opt:
split = prov_def.split(':')
constants.LOADBALANCER +
':name2:path2:default'],
'service_providers')
- expected = {'service_type': constants.LOADBALANCER,
- 'name': 'lbaas',
- 'driver': 'driver_path',
- 'default': False}
res = provconf.parse_service_provider_opt()
- self.assertEqual(len(res), 3)
- self.assertEqual(res, [expected,
- {'service_type': constants.LOADBALANCER,
- 'name': 'name1',
- 'driver': 'path1',
- 'default': False},
- {'service_type': constants.LOADBALANCER,
- 'name': 'name2',
- 'driver': 'path2',
- 'default': True}])
+ # This parsing crosses repos if additional projects are installed,
+ # so check that at least what we expect is there; there may be more.
+ self.assertTrue(len(res) >= 3)
def test_parse_service_provider_opt_not_allowed_raises(self):
cfg.CONF.set_override('service_provider',