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
@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):
# Returns the extension paths from a config entry and the __path__
# of neutron.extensions
-def get_extensions_path():
- paths = neutron.extensions.__path__
-
- 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
+def get_extensions_path(service_plugins=None):
+ paths = collections.OrderedDict()
+
+ # 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
+++ /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 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
# 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
return cls._instance
def __init__(self):
- self._load_conf()
-
- def _load_conf(self):
- self.conf = pconf.ProviderConfiguration(
- pconf.parse_service_provider_opt())
+ 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 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."""
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,
# 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__)
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()
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)
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,
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):
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):
class ServiceTypeManagerTestCase(testlib_api.SqlTestCase):
def setUp(self):
+ self.service_providers = mock.patch.object(
+ provconf.NeutronModule, 'service_providers').start()
super(ServiceTypeManagerTestCase, self).setUp()
+ 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()
- self.ctx = context.get_admin_context()
+ 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])
+ filters=dict(service_type=[constants.LOADBALANCER])
)
self.assertEqual(len(res), 1)
res = self.manager.get_service_providers(
ctx,
- filters=dict(service_type=[constants.LOADBALANCER])
+ 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)
)
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,
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,
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):
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)
# 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
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',
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',
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,
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,
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',
[{'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',
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',
'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']],
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',
'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']],