Decentralize the managemement of service providers
After the service split, some of the configuration, parsing and validation was kept in the neutron core; ultimately this needs to get closer to the services where it belongs. This patch starts from ProviderConfiguration and ServiceTypeManager classes, and aims at removing the hard-coded elements, like the list of known advanced services, so that in the long run we can make Neutron easier to plug with external services. Partial-bug: #1473110 Depends-on: I44edcceba37ac58efcc0a53c9d1f835d9530344a Depends-on: I8924234aadf786801ffc100d7daa27acc145a195 Change-Id: Ia4cad678e6c722ca05821dbdbf05d61523246a86
This commit is contained in:
parent
43c00a9f65
commit
61121c5f2a
|
@ -27,10 +27,10 @@ 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
|
||||
from neutron.services import provider_configuration
|
||||
from neutron import wsgi
|
||||
|
||||
|
||||
|
@ -579,8 +579,9 @@ class PluginAwareExtensionManager(ExtensionManager):
|
|||
@classmethod
|
||||
def get_instance(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = cls(get_extensions_path(),
|
||||
manager.NeutronManager.get_service_plugins())
|
||||
service_plugins = manager.NeutronManager.get_service_plugins()
|
||||
cls._instance = cls(get_extensions_path(service_plugins),
|
||||
service_plugins)
|
||||
return cls._instance
|
||||
|
||||
def get_supported_extension_aliases(self):
|
||||
|
@ -647,31 +648,30 @@ class ResourceExtension(object):
|
|||
|
||||
# Returns the extension paths from a config entry and the __path__
|
||||
# of neutron.extensions
|
||||
def get_extensions_path():
|
||||
paths = neutron.extensions.__path__
|
||||
def get_extensions_path(service_plugins=None):
|
||||
paths = collections.OrderedDict()
|
||||
|
||||
neutron_mods = repos.NeutronModules()
|
||||
for x in neutron_mods.installed_list():
|
||||
try:
|
||||
paths += neutron_mods.module(x).extensions.__path__
|
||||
except AttributeError:
|
||||
# Occurs normally if module has no extensions sub-module
|
||||
pass
|
||||
# Add Neutron core extensions
|
||||
paths[neutron.extensions.__path__[0]] = 1
|
||||
if service_plugins:
|
||||
# Add Neutron *-aas extensions
|
||||
for plugin in service_plugins.values():
|
||||
neutron_mod = provider_configuration.NeutronModule(
|
||||
plugin.__module__.split('.')[0])
|
||||
try:
|
||||
paths[neutron_mod.module().extensions.__path__[0]] = 1
|
||||
except AttributeError:
|
||||
# Occurs normally if module has no extensions sub-module
|
||||
pass
|
||||
|
||||
# Add external/other plugins extensions
|
||||
if cfg.CONF.api_extensions_path:
|
||||
paths.append(cfg.CONF.api_extensions_path)
|
||||
|
||||
# If the path has dups in it, from discovery + conf file, the duplicate
|
||||
# import of the same module and super() do not play nicely, so weed
|
||||
# out the duplicates, preserving search order.
|
||||
|
||||
z = collections.OrderedDict()
|
||||
for x in paths:
|
||||
z[x] = 1
|
||||
paths = z.keys()
|
||||
for path in cfg.CONF.api_extensions_path.split(":"):
|
||||
paths[path] = 1
|
||||
|
||||
LOG.debug("get_extension_paths = %s", paths)
|
||||
|
||||
# Re-build the extension string
|
||||
path = ':'.join(paths)
|
||||
return path
|
||||
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
# 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 importlib
|
||||
import os
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from six.moves import configparser as ConfigParser
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NeutronModules(object):
|
||||
|
||||
MODULES = {
|
||||
'neutron_fwaas': {
|
||||
'alembic-name': 'fwaas',
|
||||
},
|
||||
'neutron_lbaas': {
|
||||
'alembic-name': 'lbaas',
|
||||
},
|
||||
'neutron_vpnaas': {
|
||||
'alembic-name': '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']
|
||||
|
||||
def alembic_name(self, module):
|
||||
return self.MODULES[module]['alembic-name']
|
||||
|
||||
# Return an INI parser for the child module. oslo.config is a bit too
|
||||
# magical in its INI loading, and in one notable case, we need to merge
|
||||
# together the [service_providers] section across at least four
|
||||
# repositories.
|
||||
def ini(self, module):
|
||||
if self.repos[module]['ini'] is None:
|
||||
neutron_dir = None
|
||||
try:
|
||||
neutron_dir = cfg.CONF.config_dir
|
||||
except cfg.NoSuchOptError:
|
||||
pass
|
||||
|
||||
if neutron_dir is None:
|
||||
neutron_dir = '/etc/neutron'
|
||||
|
||||
ini = ConfigParser.SafeConfigParser()
|
||||
ini_path = os.path.join(neutron_dir, '%s.conf' % module)
|
||||
if os.path.exists(ini_path):
|
||||
ini.read(ini_path)
|
||||
|
||||
self.repos[module]['ini'] = ini
|
||||
|
||||
return self.repos[module]['ini']
|
||||
|
||||
def service_providers(self, module):
|
||||
ini = self.ini(module)
|
||||
|
||||
sp = []
|
||||
try:
|
||||
for name, value in ini.items('service_providers'):
|
||||
if name == 'service_provider':
|
||||
sp.append(value)
|
||||
except ConfigParser.NoSectionError:
|
||||
pass
|
||||
|
||||
return sp
|
|
@ -13,6 +13,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from itertools import chain
|
||||
|
||||
from oslo_log import log as logging
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
@ -42,14 +44,36 @@ class ServiceTypeManager(object):
|
|||
return cls._instance
|
||||
|
||||
def __init__(self):
|
||||
self._load_conf()
|
||||
self.config = {}
|
||||
# TODO(armax): remove these as soon as *-aaS start using
|
||||
# the newly introduced add_provider_configuration API
|
||||
self.config['LOADBALANCER'] = (
|
||||
pconf.ProviderConfiguration('neutron_lbaas'))
|
||||
self.config['LOADBALANCERV2'] = (
|
||||
pconf.ProviderConfiguration('neutron_lbaas'))
|
||||
self.config['FIREWALL'] = (
|
||||
pconf.ProviderConfiguration('neutron_fwaas'))
|
||||
self.config['VPN'] = (
|
||||
pconf.ProviderConfiguration('neutron_vpnaas'))
|
||||
|
||||
def _load_conf(self):
|
||||
self.conf = pconf.ProviderConfiguration(
|
||||
pconf.parse_service_provider_opt())
|
||||
def add_provider_configuration(self, service_type, configuration):
|
||||
"""Add or update the provider configuration for the service type."""
|
||||
LOG.debug('Adding provider configuration for service %s', service_type)
|
||||
self.config.update({service_type: configuration})
|
||||
|
||||
def get_service_providers(self, context, filters=None, fields=None):
|
||||
return self.conf.get_service_providers(filters, fields)
|
||||
if filters and 'service_type' in filters:
|
||||
return list(
|
||||
chain.from_iterable(self.config[svc_type].
|
||||
get_service_providers(filters, fields)
|
||||
for svc_type in filters['service_type']
|
||||
if svc_type in self.config)
|
||||
)
|
||||
return list(
|
||||
chain.from_iterable(
|
||||
self.config[p].get_service_providers(filters, fields)
|
||||
for p in self.config)
|
||||
)
|
||||
|
||||
def get_default_service_provider(self, context, service_type):
|
||||
"""Return the default provider for a given service type."""
|
||||
|
@ -65,7 +89,7 @@ class ServiceTypeManager(object):
|
|||
|
||||
def add_resource_association(self, context, service_type, provider_name,
|
||||
resource_id):
|
||||
r = self.conf.get_service_providers(
|
||||
r = self.get_service_providers(context,
|
||||
filters={'service_type': [service_type], 'name': [provider_name]})
|
||||
if not r:
|
||||
raise pconf.ServiceProviderNotFound(provider=provider_name,
|
||||
|
|
|
@ -13,14 +13,16 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import importlib
|
||||
import os
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from six.moves import configparser as ConfigParser
|
||||
import stevedore
|
||||
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.common import repos
|
||||
from neutron.i18n import _LW
|
||||
from neutron.plugins.common import constants
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
@ -36,6 +38,67 @@ serviceprovider_opts = [
|
|||
cfg.CONF.register_opts(serviceprovider_opts, 'service_providers')
|
||||
|
||||
|
||||
class NeutronModule(object):
|
||||
"""A Neutron extension module."""
|
||||
|
||||
def __init__(self, service_module):
|
||||
self.module_name = service_module
|
||||
self.repo = {
|
||||
'mod': self._import_or_none(),
|
||||
'ini': None
|
||||
}
|
||||
|
||||
def _import_or_none(self):
|
||||
try:
|
||||
return importlib.import_module(self.module_name)
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
def installed(self):
|
||||
LOG.debug("NeutronModule installed = %s", self.module_name)
|
||||
return self.module_name
|
||||
|
||||
def module(self):
|
||||
return self.repo['mod']
|
||||
|
||||
# Return an INI parser for the child module. oslo.config is a bit too
|
||||
# magical in its INI loading, and in one notable case, we need to merge
|
||||
# together the [service_providers] section across at least four
|
||||
# repositories.
|
||||
def ini(self):
|
||||
if self.repo['ini'] is None:
|
||||
neutron_dir = None
|
||||
try:
|
||||
neutron_dir = cfg.CONF.config_dir
|
||||
except cfg.NoSuchOptError:
|
||||
pass
|
||||
|
||||
if neutron_dir is None:
|
||||
neutron_dir = '/etc/neutron'
|
||||
|
||||
ini = ConfigParser.SafeConfigParser()
|
||||
ini_path = os.path.join(neutron_dir, '%s.conf' % self.module_name)
|
||||
if os.path.exists(ini_path):
|
||||
ini.read(ini_path)
|
||||
|
||||
self.repo['ini'] = ini
|
||||
|
||||
return self.repo['ini']
|
||||
|
||||
def service_providers(self):
|
||||
ini = self.ini()
|
||||
|
||||
sp = []
|
||||
try:
|
||||
for name, value in ini.items('service_providers'):
|
||||
if name == 'service_provider':
|
||||
sp.append(value)
|
||||
except ConfigParser.NoSectionError:
|
||||
pass
|
||||
|
||||
return sp
|
||||
|
||||
|
||||
#global scope function that should be used in service APIs
|
||||
def normalize_provider_name(name):
|
||||
return name.lower()
|
||||
|
@ -65,32 +128,16 @@ def get_provider_driver_class(driver, namespace=SERVICE_PROVIDERS):
|
|||
return new_driver
|
||||
|
||||
|
||||
def parse_service_provider_opt():
|
||||
def parse_service_provider_opt(service_module='neutron'):
|
||||
|
||||
"""Parse service definition opts and returns result."""
|
||||
def validate_name(name):
|
||||
if len(name) > 255:
|
||||
raise n_exc.Invalid(
|
||||
_("Provider name is limited by 255 characters: %s") % name)
|
||||
|
||||
# TODO(dougwig) - phase out the neutron.conf location for service
|
||||
# providers a cycle or two after Kilo.
|
||||
|
||||
# Look in neutron.conf for service providers first (legacy mode)
|
||||
try:
|
||||
svc_providers_opt = cfg.CONF.service_providers.service_provider
|
||||
except cfg.NoSuchOptError:
|
||||
svc_providers_opt = []
|
||||
|
||||
# Look in neutron-*aas.conf files for service provider configs
|
||||
if svc_providers_opt:
|
||||
LOG.warning(_LW("Reading service_providers from legacy location in "
|
||||
"neutron.conf, and ignoring values in "
|
||||
"neutron_*aas.conf files; this override will be "
|
||||
"going away soon."))
|
||||
else:
|
||||
neutron_mods = repos.NeutronModules()
|
||||
for x in neutron_mods.installed_list():
|
||||
svc_providers_opt += neutron_mods.service_providers(x)
|
||||
neutron_mod = NeutronModule(service_module)
|
||||
svc_providers_opt = neutron_mod.service_providers()
|
||||
|
||||
LOG.debug("Service providers = %s", svc_providers_opt)
|
||||
|
||||
|
@ -113,14 +160,7 @@ def parse_service_provider_opt():
|
|||
prov_def)
|
||||
LOG.error(msg)
|
||||
raise n_exc.Invalid(msg)
|
||||
ALLOWED_SERVICES = constants.EXT_TO_SERVICE_MAPPING.values()
|
||||
if svc_type not in ALLOWED_SERVICES:
|
||||
msg = (_("Service type '%(svc_type)s' is not allowed, "
|
||||
"allowed types: %(allowed)s") %
|
||||
{'svc_type': svc_type,
|
||||
'allowed': ALLOWED_SERVICES})
|
||||
LOG.error(msg)
|
||||
raise n_exc.Invalid(msg)
|
||||
|
||||
driver = get_provider_driver_class(driver)
|
||||
res.append({'service_type': svc_type,
|
||||
'name': name,
|
||||
|
@ -145,9 +185,10 @@ class ServiceProviderAlreadyAssociated(n_exc.Conflict):
|
|||
|
||||
|
||||
class ProviderConfiguration(object):
|
||||
def __init__(self, prov_data):
|
||||
|
||||
def __init__(self, svc_module='neutron'):
|
||||
self.providers = {}
|
||||
for prov in prov_data:
|
||||
for prov in parse_service_provider_opt(svc_module):
|
||||
self.add_provider(prov)
|
||||
|
||||
def _ensure_driver_unique(self, driver):
|
||||
|
|
|
@ -67,6 +67,40 @@ class FakePluginWithExtension(object):
|
|||
self._log("method_to_support_foxnsox_extension", context)
|
||||
|
||||
|
||||
class ExtensionPathTest(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.base_path = extensions.get_extensions_path()
|
||||
super(ExtensionPathTest, self).setUp()
|
||||
|
||||
def test_get_extensions_path_with_plugins(self):
|
||||
path = extensions.get_extensions_path(
|
||||
{constants.CORE: FakePluginWithExtension()})
|
||||
self.assertEqual(path,
|
||||
'%s:neutron/tests/unit/extensions' % self.base_path)
|
||||
|
||||
def test_get_extensions_path_no_extensions(self):
|
||||
# Reset to default value, as it's overriden by base class
|
||||
cfg.CONF.set_override('api_extensions_path', '')
|
||||
path = extensions.get_extensions_path()
|
||||
self.assertEqual(path, self.base_path)
|
||||
|
||||
def test_get_extensions_path_single_extension(self):
|
||||
cfg.CONF.set_override('api_extensions_path', 'path1')
|
||||
path = extensions.get_extensions_path()
|
||||
self.assertEqual(path, '%s:path1' % self.base_path)
|
||||
|
||||
def test_get_extensions_path_multiple_extensions(self):
|
||||
cfg.CONF.set_override('api_extensions_path', 'path1:path2')
|
||||
path = extensions.get_extensions_path()
|
||||
self.assertEqual(path, '%s:path1:path2' % self.base_path)
|
||||
|
||||
def test_get_extensions_path_duplicate_extensions(self):
|
||||
cfg.CONF.set_override('api_extensions_path', 'path1:path1')
|
||||
path = extensions.get_extensions_path()
|
||||
self.assertEqual(path, '%s:path1' % self.base_path)
|
||||
|
||||
|
||||
class PluginInterfaceTest(base.BaseTestCase):
|
||||
def test_issubclass_hook(self):
|
||||
class A(object):
|
||||
|
|
|
@ -38,65 +38,63 @@ _get_path = test_base._get_path
|
|||
|
||||
class ServiceTypeManagerTestCase(testlib_api.SqlTestCase):
|
||||
def setUp(self):
|
||||
self.service_providers = mock.patch.object(
|
||||
provconf.NeutronModule, 'service_providers').start()
|
||||
super(ServiceTypeManagerTestCase, self).setUp()
|
||||
st_db.ServiceTypeManager._instance = None
|
||||
self.manager = st_db.ServiceTypeManager.get_instance()
|
||||
self.ctx = context.get_admin_context()
|
||||
|
||||
def _set_override(self, service_providers):
|
||||
self.service_providers.return_value = service_providers
|
||||
st_db.ServiceTypeManager._instance = None
|
||||
self.manager = st_db.ServiceTypeManager.get_instance()
|
||||
for provider in service_providers:
|
||||
self.manager.add_provider_configuration(
|
||||
provider.split(':')[0], provconf.ProviderConfiguration())
|
||||
|
||||
def test_service_provider_driver_not_unique(self):
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas:driver'],
|
||||
'service_providers')
|
||||
self._set_override([constants.LOADBALANCER + ':lbaas:driver'])
|
||||
prov = {'service_type': constants.LOADBALANCER,
|
||||
'name': 'name2',
|
||||
'driver': 'driver',
|
||||
'default': False}
|
||||
self.manager._load_conf()
|
||||
self.assertRaises(
|
||||
n_exc.Invalid, self.manager.conf.add_provider, prov)
|
||||
n_exc.Invalid,
|
||||
self.manager.config['LOADBALANCER'].add_provider, prov)
|
||||
|
||||
def test_get_service_providers(self):
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas:driver_path',
|
||||
constants.DUMMY + ':dummy:dummy_dr'],
|
||||
'service_providers')
|
||||
"""Test that get_service_providers filters correctly."""
|
||||
self._set_override(
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas:driver_path1',
|
||||
constants.FIREWALL +
|
||||
':fwaas:driver_path2'])
|
||||
ctx = context.get_admin_context()
|
||||
provconf.parse_service_provider_opt()
|
||||
self.manager._load_conf()
|
||||
res = self.manager.get_service_providers(ctx)
|
||||
self.assertEqual(len(res), 2)
|
||||
|
||||
res = self.manager.get_service_providers(
|
||||
ctx,
|
||||
filters=dict(service_type=[constants.DUMMY])
|
||||
)
|
||||
self.assertEqual(len(res), 1)
|
||||
|
||||
res = self.manager.get_service_providers(
|
||||
ctx,
|
||||
filters=dict(service_type=[constants.LOADBALANCER])
|
||||
)
|
||||
self.assertEqual(len(res), 1)
|
||||
|
||||
res = self.manager.get_service_providers(
|
||||
ctx,
|
||||
filters=dict(service_type=[constants.FIREWALL])
|
||||
)
|
||||
self.assertEqual(len(res), 1)
|
||||
|
||||
def test_multiple_default_providers_specified_for_service(self):
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas1:driver_path:default',
|
||||
constants.LOADBALANCER +
|
||||
':lbaas2:driver_path:default'],
|
||||
'service_providers')
|
||||
self.assertRaises(n_exc.Invalid, self.manager._load_conf)
|
||||
self.assertRaises(
|
||||
n_exc.Invalid,
|
||||
self._set_override,
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas1:driver_path:default',
|
||||
constants.LOADBALANCER +
|
||||
':lbaas2:driver_path:default'])
|
||||
|
||||
def test_get_default_provider(self):
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas1:driver_path:default',
|
||||
constants.DUMMY +
|
||||
':lbaas2:driver_path2'],
|
||||
'service_providers')
|
||||
self.manager._load_conf()
|
||||
self._set_override([constants.LOADBALANCER +
|
||||
':lbaas1:driver_path:default',
|
||||
constants.DUMMY +
|
||||
':lbaas2:driver_path2'])
|
||||
# can pass None as a context
|
||||
p = self.manager.get_default_service_provider(None,
|
||||
constants.LOADBALANCER)
|
||||
|
@ -112,13 +110,10 @@ class ServiceTypeManagerTestCase(testlib_api.SqlTestCase):
|
|||
)
|
||||
|
||||
def test_add_resource_association(self):
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas1:driver_path:default',
|
||||
constants.DUMMY +
|
||||
':lbaas2:driver_path2'],
|
||||
'service_providers')
|
||||
self.manager._load_conf()
|
||||
self._set_override([constants.LOADBALANCER +
|
||||
':lbaas1:driver_path:default',
|
||||
constants.DUMMY +
|
||||
':lbaas2:driver_path2'])
|
||||
ctx = context.get_admin_context()
|
||||
self.manager.add_resource_association(ctx,
|
||||
constants.LOADBALANCER,
|
||||
|
@ -130,13 +125,10 @@ class ServiceTypeManagerTestCase(testlib_api.SqlTestCase):
|
|||
ctx.session.delete(assoc)
|
||||
|
||||
def test_invalid_resource_association(self):
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas1:driver_path:default',
|
||||
constants.DUMMY +
|
||||
':lbaas2:driver_path2'],
|
||||
'service_providers')
|
||||
self.manager._load_conf()
|
||||
self._set_override([constants.LOADBALANCER +
|
||||
':lbaas1:driver_path:default',
|
||||
constants.DUMMY +
|
||||
':lbaas2:driver_path2'])
|
||||
ctx = context.get_admin_context()
|
||||
self.assertRaises(provconf.ServiceProviderNotFound,
|
||||
self.manager.add_resource_association,
|
||||
|
@ -200,13 +192,19 @@ class ServiceTypeExtensionTestCase(ServiceTypeExtensionTestCaseBase):
|
|||
class ServiceTypeManagerExtTestCase(ServiceTypeExtensionTestCaseBase):
|
||||
"""Tests ServiceTypemanager as a public API."""
|
||||
def setUp(self):
|
||||
self.service_providers = mock.patch.object(
|
||||
provconf.NeutronModule, 'service_providers').start()
|
||||
service_providers = [
|
||||
constants.LOADBALANCER + ':lbaas:driver_path',
|
||||
constants.DUMMY + ':dummy:dummy_dr'
|
||||
]
|
||||
self.service_providers.return_value = service_providers
|
||||
# Blank out service type manager instance
|
||||
st_db.ServiceTypeManager._instance = None
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas:driver_path',
|
||||
constants.DUMMY + ':dummy:dummy_dr'],
|
||||
'service_providers')
|
||||
self.manager = st_db.ServiceTypeManager.get_instance()
|
||||
for provider in service_providers:
|
||||
self.manager.add_provider_configuration(
|
||||
provider.split(':')[0], provconf.ProviderConfiguration())
|
||||
super(ServiceTypeManagerExtTestCase, self).setUp()
|
||||
|
||||
def _list_service_providers(self):
|
||||
|
@ -217,4 +215,4 @@ class ServiceTypeManagerExtTestCase(ServiceTypeExtensionTestCaseBase):
|
|||
self.assertEqual(res.status_int, webexc.HTTPOk.code)
|
||||
data = self.deserialize(res)
|
||||
self.assertIn('service_providers', data)
|
||||
self.assertEqual(len(data['service_providers']), 2)
|
||||
self.assertGreaterEqual(len(data['service_providers']), 2)
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
|
||||
from neutron.common import exceptions as n_exc
|
||||
|
@ -22,15 +24,22 @@ from neutron.tests import base
|
|||
|
||||
|
||||
class ParseServiceProviderConfigurationTestCase(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ParseServiceProviderConfigurationTestCase, self).setUp()
|
||||
self.service_providers = mock.patch.object(
|
||||
provconf.NeutronModule, 'service_providers').start()
|
||||
|
||||
def _set_override(self, service_providers):
|
||||
self.service_providers.return_value = service_providers
|
||||
|
||||
def test_default_service_provider_configuration(self):
|
||||
providers = cfg.CONF.service_providers.service_provider
|
||||
self.assertEqual(providers, [])
|
||||
|
||||
def test_parse_single_service_provider_opt(self):
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas:driver_path'],
|
||||
'service_providers')
|
||||
self._set_override([constants.LOADBALANCER +
|
||||
':lbaas:driver_path'])
|
||||
expected = {'service_type': constants.LOADBALANCER,
|
||||
'name': 'lbaas',
|
||||
'driver': 'driver_path',
|
||||
|
@ -40,10 +49,8 @@ class ParseServiceProviderConfigurationTestCase(base.BaseTestCase):
|
|||
self.assertEqual(res, [expected])
|
||||
|
||||
def test_parse_single_default_service_provider_opt(self):
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas:driver_path:default'],
|
||||
'service_providers')
|
||||
self._set_override([constants.LOADBALANCER +
|
||||
':lbaas:driver_path:default'])
|
||||
expected = {'service_type': constants.LOADBALANCER,
|
||||
'name': 'lbaas',
|
||||
'driver': 'driver_path',
|
||||
|
@ -53,56 +60,46 @@ class ParseServiceProviderConfigurationTestCase(base.BaseTestCase):
|
|||
self.assertEqual(res, [expected])
|
||||
|
||||
def test_parse_multi_service_provider_opt(self):
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas:driver_path',
|
||||
constants.LOADBALANCER + ':name1:path1',
|
||||
constants.LOADBALANCER +
|
||||
':name2:path2:default'],
|
||||
'service_providers')
|
||||
self._set_override([constants.LOADBALANCER +
|
||||
':lbaas:driver_path',
|
||||
constants.LOADBALANCER + ':name1:path1',
|
||||
constants.LOADBALANCER +
|
||||
':name2:path2:default'])
|
||||
res = provconf.parse_service_provider_opt()
|
||||
# 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',
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas:driver_path',
|
||||
'svc_type:name1:path1'],
|
||||
'service_providers')
|
||||
self.assertRaises(n_exc.Invalid, provconf.parse_service_provider_opt)
|
||||
|
||||
def test_parse_service_provider_invalid_format(self):
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':lbaas:driver_path',
|
||||
'svc_type:name1:path1:def'],
|
||||
'service_providers')
|
||||
self._set_override([constants.LOADBALANCER +
|
||||
':lbaas:driver_path',
|
||||
'svc_type:name1:path1:def'])
|
||||
self.assertRaises(n_exc.Invalid, provconf.parse_service_provider_opt)
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':',
|
||||
'svc_type:name1:path1:def'],
|
||||
'service_providers')
|
||||
self._set_override([constants.LOADBALANCER +
|
||||
':',
|
||||
'svc_type:name1:path1:def'])
|
||||
self.assertRaises(n_exc.Invalid, provconf.parse_service_provider_opt)
|
||||
|
||||
def test_parse_service_provider_name_too_long(self):
|
||||
name = 'a' * 256
|
||||
cfg.CONF.set_override('service_provider',
|
||||
[constants.LOADBALANCER +
|
||||
':' + name + ':driver_path',
|
||||
'svc_type:name1:path1:def'],
|
||||
'service_providers')
|
||||
self._set_override([constants.LOADBALANCER +
|
||||
':' + name + ':driver_path',
|
||||
'svc_type:name1:path1:def'])
|
||||
self.assertRaises(n_exc.Invalid, provconf.parse_service_provider_opt)
|
||||
|
||||
|
||||
class ProviderConfigurationTestCase(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(ProviderConfigurationTestCase, self).setUp()
|
||||
self.service_providers = mock.patch.object(
|
||||
provconf.NeutronModule, 'service_providers').start()
|
||||
|
||||
def _set_override(self, service_providers):
|
||||
self.service_providers.return_value = service_providers
|
||||
|
||||
def test_ensure_driver_unique(self):
|
||||
pconf = provconf.ProviderConfiguration([])
|
||||
pconf = provconf.ProviderConfiguration()
|
||||
pconf.providers[('svctype', 'name')] = {'driver': 'driver',
|
||||
'default': True}
|
||||
self.assertRaises(n_exc.Invalid,
|
||||
|
@ -110,7 +107,7 @@ class ProviderConfigurationTestCase(base.BaseTestCase):
|
|||
self.assertIsNone(pconf._ensure_driver_unique('another_driver1'))
|
||||
|
||||
def test_ensure_default_unique(self):
|
||||
pconf = provconf.ProviderConfiguration([])
|
||||
pconf = provconf.ProviderConfiguration()
|
||||
pconf.providers[('svctype', 'name')] = {'driver': 'driver',
|
||||
'default': True}
|
||||
self.assertRaises(n_exc.Invalid,
|
||||
|
@ -121,7 +118,7 @@ class ProviderConfigurationTestCase(base.BaseTestCase):
|
|||
self.assertIsNone(pconf._ensure_default_unique('svctype1', False))
|
||||
|
||||
def test_add_provider(self):
|
||||
pconf = provconf.ProviderConfiguration([])
|
||||
pconf = provconf.ProviderConfiguration()
|
||||
prov = {'service_type': constants.LOADBALANCER,
|
||||
'name': 'name',
|
||||
'driver': 'path',
|
||||
|
@ -134,7 +131,7 @@ class ProviderConfigurationTestCase(base.BaseTestCase):
|
|||
[{'driver': 'path', 'default': False}])
|
||||
|
||||
def test_add_duplicate_provider(self):
|
||||
pconf = provconf.ProviderConfiguration([])
|
||||
pconf = provconf.ProviderConfiguration()
|
||||
prov = {'service_type': constants.LOADBALANCER,
|
||||
'name': 'name',
|
||||
'driver': 'path',
|
||||
|
@ -144,6 +141,10 @@ class ProviderConfigurationTestCase(base.BaseTestCase):
|
|||
self.assertEqual(len(pconf.providers), 1)
|
||||
|
||||
def test_get_service_providers(self):
|
||||
self._set_override([constants.LOADBALANCER + ':name:path',
|
||||
constants.LOADBALANCER + ':name2:path2',
|
||||
'st2:name:driver:default',
|
||||
'st3:name2:driver2:default'])
|
||||
provs = [{'service_type': constants.LOADBALANCER,
|
||||
'name': 'name',
|
||||
'driver': 'path',
|
||||
|
@ -161,7 +162,7 @@ class ProviderConfigurationTestCase(base.BaseTestCase):
|
|||
'name': 'name2',
|
||||
'driver': 'driver2',
|
||||
'default': True}]
|
||||
pconf = provconf.ProviderConfiguration(provs)
|
||||
pconf = provconf.ProviderConfiguration()
|
||||
for prov in provs:
|
||||
p = pconf.get_service_providers(
|
||||
filters={'name': [prov['name']],
|
||||
|
@ -170,6 +171,8 @@ class ProviderConfigurationTestCase(base.BaseTestCase):
|
|||
self.assertEqual(p, [prov])
|
||||
|
||||
def test_get_service_providers_with_fields(self):
|
||||
self._set_override([constants.LOADBALANCER + ":name:path",
|
||||
constants.LOADBALANCER + ":name2:path2"])
|
||||
provs = [{'service_type': constants.LOADBALANCER,
|
||||
'name': 'name',
|
||||
'driver': 'path',
|
||||
|
@ -178,7 +181,7 @@ class ProviderConfigurationTestCase(base.BaseTestCase):
|
|||
'name': 'name2',
|
||||
'driver': 'path2',
|
||||
'default': False}]
|
||||
pconf = provconf.ProviderConfiguration(provs)
|
||||
pconf = provconf.ProviderConfiguration()
|
||||
for prov in provs:
|
||||
p = pconf.get_service_providers(
|
||||
filters={'name': [prov['name']],
|
||||
|
|
Loading…
Reference in New Issue