Merge "Mechanisms to move extensions and config into service repos"

This commit is contained in:
Jenkins 2015-01-31 00:23:07 +00:00 committed by Gerrit Code Review
commit 78ca364e09
5 changed files with 128 additions and 21 deletions

View File

@ -664,6 +664,7 @@ admin_password = %SERVICE_PASSWORD%
# 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:

View File

@ -26,6 +26,7 @@ import webob.dec
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
@ -518,13 +519,19 @@ class ExtensionManager(object):
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
@ -534,7 +541,12 @@ class ExtensionManager(object):
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:
@ -661,11 +673,19 @@ class ResourceExtension(object):
# Returns the extension paths from a config entry and the __path__
# of neutron.extensions
def get_extensions_path():
paths = ':'.join(neutron.extensions.__path__)
if cfg.CONF.api_extensions_path:
paths = ':'.join([cfg.CONF.api_extensions_path, paths])
paths = neutron.extensions.__path__
return paths
neutron_mods = repos.NeutronModules()
for x in neutron_mods.installed_list():
paths += neutron_mods.module(x).__path__
if cfg.CONF.api_extensions_path:
paths.append(cfg.CONF.api_extensions_path)
LOG.debug("get_extension_paths = %s", paths)
path = ':'.join(paths)
return path
def append_api_extensions_path(paths):

68
neutron/common/repos.py Normal file
View File

@ -0,0 +1,68 @@
# 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']

View File

@ -17,6 +17,7 @@ from oslo.config import cfg
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
@ -69,7 +70,35 @@ def parse_service_provider_opt():
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(':')

View File

@ -60,21 +60,10 @@ class ParseServiceProviderConfigurationTestCase(base.BaseTestCase):
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',