Merge "Mechanisms to move extensions and config into service repos"
This commit is contained in:
commit
78ca364e09
|
@ -664,6 +664,7 @@ admin_password = %SERVICE_PASSWORD%
|
||||||
# If set, use this value for pool_timeout with sqlalchemy
|
# If set, use this value for pool_timeout with sqlalchemy
|
||||||
# pool_timeout = 10
|
# pool_timeout = 10
|
||||||
|
|
||||||
|
# TODO(dougwig) - remove these lines once service repos have them
|
||||||
[service_providers]
|
[service_providers]
|
||||||
# Specify service providers (drivers) for advanced services like loadbalancer, VPN, Firewall.
|
# Specify service providers (drivers) for advanced services like loadbalancer, VPN, Firewall.
|
||||||
# Must be in form:
|
# Must be in form:
|
||||||
|
|
|
@ -26,6 +26,7 @@ import webob.dec
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
from neutron.common import exceptions
|
from neutron.common import exceptions
|
||||||
|
from neutron.common import repos
|
||||||
import neutron.extensions
|
import neutron.extensions
|
||||||
from neutron.i18n import _LE, _LI, _LW
|
from neutron.i18n import _LE, _LI, _LW
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
|
@ -518,13 +519,19 @@ class ExtensionManager(object):
|
||||||
See tests/unit/extensions/foxinsocks.py for an example extension
|
See tests/unit/extensions/foxinsocks.py for an example extension
|
||||||
implementation.
|
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(':'):
|
for path in self.path.split(':'):
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
self._load_all_extensions_from_path(path)
|
self._load_all_extensions_from_path(path, loaded)
|
||||||
else:
|
else:
|
||||||
LOG.error(_LE("Extension path '%s' doesn't exist!"), path)
|
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
|
# Sorting the extension list makes the order in which they
|
||||||
# are loaded predictable across a cluster of load-balanced
|
# are loaded predictable across a cluster of load-balanced
|
||||||
# Neutron Servers
|
# Neutron Servers
|
||||||
|
@ -534,7 +541,12 @@ class ExtensionManager(object):
|
||||||
mod_name, file_ext = os.path.splitext(os.path.split(f)[-1])
|
mod_name, file_ext = os.path.splitext(os.path.split(f)[-1])
|
||||||
ext_path = os.path.join(path, f)
|
ext_path = os.path.join(path, f)
|
||||||
if file_ext.lower() == '.py' and not mod_name.startswith('_'):
|
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)
|
mod = imp.load_source(mod_name, ext_path)
|
||||||
|
loaded.append(mod_name)
|
||||||
ext_name = mod_name[0].upper() + mod_name[1:]
|
ext_name = mod_name[0].upper() + mod_name[1:]
|
||||||
new_ext_class = getattr(mod, ext_name, None)
|
new_ext_class = getattr(mod, ext_name, None)
|
||||||
if not new_ext_class:
|
if not new_ext_class:
|
||||||
|
@ -661,11 +673,19 @@ class ResourceExtension(object):
|
||||||
# Returns the extension paths from a config entry and the __path__
|
# Returns the extension paths from a config entry and the __path__
|
||||||
# of neutron.extensions
|
# of neutron.extensions
|
||||||
def get_extensions_path():
|
def get_extensions_path():
|
||||||
paths = ':'.join(neutron.extensions.__path__)
|
paths = neutron.extensions.__path__
|
||||||
if cfg.CONF.api_extensions_path:
|
|
||||||
paths = ':'.join([cfg.CONF.api_extensions_path, paths])
|
|
||||||
|
|
||||||
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):
|
def append_api_extensions_path(paths):
|
||||||
|
|
|
@ -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']
|
|
@ -17,6 +17,7 @@ from oslo.config import cfg
|
||||||
import stevedore
|
import stevedore
|
||||||
|
|
||||||
from neutron.common import exceptions as n_exc
|
from neutron.common import exceptions as n_exc
|
||||||
|
from neutron.common import repos
|
||||||
from neutron.i18n import _LW
|
from neutron.i18n import _LW
|
||||||
from neutron.openstack.common import log as logging
|
from neutron.openstack.common import log as logging
|
||||||
from neutron.plugins.common import constants
|
from neutron.plugins.common import constants
|
||||||
|
@ -69,7 +70,35 @@ def parse_service_provider_opt():
|
||||||
raise n_exc.Invalid(
|
raise n_exc.Invalid(
|
||||||
_("Provider name is limited by 255 characters: %s") % name)
|
_("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 = []
|
res = []
|
||||||
for prov_def in svc_providers_opt:
|
for prov_def in svc_providers_opt:
|
||||||
split = prov_def.split(':')
|
split = prov_def.split(':')
|
||||||
|
|
|
@ -60,21 +60,10 @@ class ParseServiceProviderConfigurationTestCase(base.BaseTestCase):
|
||||||
constants.LOADBALANCER +
|
constants.LOADBALANCER +
|
||||||
':name2:path2:default'],
|
':name2:path2:default'],
|
||||||
'service_providers')
|
'service_providers')
|
||||||
expected = {'service_type': constants.LOADBALANCER,
|
|
||||||
'name': 'lbaas',
|
|
||||||
'driver': 'driver_path',
|
|
||||||
'default': False}
|
|
||||||
res = provconf.parse_service_provider_opt()
|
res = provconf.parse_service_provider_opt()
|
||||||
self.assertEqual(len(res), 3)
|
# This parsing crosses repos if additional projects are installed,
|
||||||
self.assertEqual(res, [expected,
|
# so check that at least what we expect is there; there may be more.
|
||||||
{'service_type': constants.LOADBALANCER,
|
self.assertTrue(len(res) >= 3)
|
||||||
'name': 'name1',
|
|
||||||
'driver': 'path1',
|
|
||||||
'default': False},
|
|
||||||
{'service_type': constants.LOADBALANCER,
|
|
||||||
'name': 'name2',
|
|
||||||
'driver': 'path2',
|
|
||||||
'default': True}])
|
|
||||||
|
|
||||||
def test_parse_service_provider_opt_not_allowed_raises(self):
|
def test_parse_service_provider_opt_not_allowed_raises(self):
|
||||||
cfg.CONF.set_override('service_provider',
|
cfg.CONF.set_override('service_provider',
|
||||||
|
|
Loading…
Reference in New Issue