完成WSGI的接口后需要调用Controller的函数来实现功能,完成并返回结果,根据官方文档,Controller可配置多个Manager,Manager可选择Driver。Driver在配置文件keystone.conf下面可以进行配置,流程是Controller->Manager->Driver.配置文件的读取功能是由oslo.config文件来完成的,keystone.conf配置如下:
1.配置Log信息
2.配置notification,driver默认是没有notification的,keystone.openstack.common.notifier.no_op_notifier,如下所示
# notification_driver can be defined multiple times
# Do nothing driver (the default)
# notification_driver = keystone.openstack.common.notifier.no_op_notifier
# Logging driver example (not enabled by default)
# notification_driver = keystone.openstack.common.notifier.log_notifier
# RPC driver example (not enabled by default)
# notification_driver = keystone.openstack.common.notifier.rpc_notifier
3.配置rpc,默认是kombu架构的rpc通信机制,keystone.openstack.common.rpc.impl_kombu,如下所示
# For Keystone, these options apply only when the RPC notification driver is
# used.
# The messaging module to use, defaults to kombu.
# rpc_backend = keystone.openstack.common.rpc.impl_kombu
4.配置数据库
connection=xxxx
5.配置Identity
[identity]
driver = keystone.identity.backends.sql.Identity
6.配置credential
[credential]
# driver = keystone.credential.backends.sql.Credential
7.配置trust
[trust]
# driver = keystone.trust.backends.sql.Trust
8.配置os_inherit
不知到是做什么的。。。roles权限继承?
9.配置catalog
[catalog]
driver = keystone.catalog.backends.sql.Catalog
10.配置endpoint_filter
[endpoint_filter]
# extension for creating associations between project and endpoints in order to
# provide a tailored catalog for project-scoped token requests.
# driver = keystone.contrib.endpoint_filter.backends.sql.EndpointFilter
# return_all_endpoints_if_no_filter = True\
11.配置token
[token]
driver = keystone.token.backends.sql.Token
12.配置cache
[cache]
13.配置policy
[policy]
# driver = keystone.policy.backends.sql.Policy
14配置ec2
[ec2]
driver = keystone.contrib.ec2.backends.sql.Ec2
# driver = keystone.contrib.ec2.backends.kvs.Ec2
15.配置assignment
[assignment]
driver = keystone.assignment.backends.sql.Assignment
16.配置OAuth1
[oauth1]
# driver = keystone.contrib.oauth1.backends.sql.OAuth1
17.配置ssl
18配置signing
19配置ladp
20配置auth
methods = external,password,token,oauth1
#external = keystone.auth.plugins.external.ExternalDefault
password = keystone.auth.plugins.password.Password
token = keystone.auth.plugins.token.Token
oauth1 = keystone.auth.plugins.oauth1.OAuth
21.配置pastedeploy
[paste_deploy]
# Name of the paste configuration file that defines the available pipelines
config_file = /etc/keystone/keystone-paste.ini
我的keystone.conf下面就这么多配置,可见对基本的操作和driver都进行了配置,那么在实际的代码中哪里?
IOK,首先找找看Manager类,以Identity包为例,在core下实现了Identity的manager包,也就是以前说过的注册的WSGI接口的Identity.routers.Public()使用的Manager,由service.py下,wsgi.ComposingRouter进行路由分发的类。
@dependency.provider('identity_api')
@dependency.requires('assignment_api')
class Manager(manager.Manager):
"""Default pivot point for the Identity backend.
See :mod:`keystone.common.manager.Manager` for more details on how this
dynamically calls the backend.
This class also handles the support of domain specific backends, by using
the DomainConfigs class. The setup call for DomainConfigs is called
from with the @domains_configured wrapper in a lazy loading fashion
to get around the fact that we can't satisfy the assignment api it needs
from within our __init__() function since the assignment driver is not
itself yet intitalized.
Each of the identity calls are pre-processed here to choose, based on
domain, which of the drivers should be called. The non-domain-specific
driver is still in place, and is used if there is no specific driver for
the domain in question.
"""
def __init__(self):
super(Manager, self).__init__(CONF.identity.driver)
self.domain_configs = DomainConfigs()
源代码Manager实现了很多功能,这里只粘贴一小部分有关的,看到上面的@dependency装饰器了吗,Controller怎么调用Manager的秘密就在那里。这里先不管,先挖掘Manager怎么调用conf下面配置的或者默认的driver的。其实很简单,CONF.identity,driver就配置了本域下Manager用的Driver,这样也就实现了Manager和Driver的绑定,CONF是在keystone-all初始化就已经配置好了,通过oslo.config模块。而且Manager也通过DomainConfigs来实现了针对不同域的keystone认证实现(跨域认证?,需要测试)。不仅仅是Identity的core.py的Manager是这样配置其他的Manager也是类似的。下面的Manager基类实现了self.driver的赋值,通过import.import_object导入driver对象
class Manager(object):
"""Base class for intermediary request layer.
The Manager layer exists to support additional logic that applies to all
or some of the methods exposed by a service that are not specific to the
HTTP interface.
It also provides a stable entry point to dynamic backends.
An example of a probable use case is logging all the calls.
"""
def __init__(self, driver_name):
self.driver = importutils.import_object(driver_name)
assignment的Manager的assignment driver
@dependency.provider('assignment_api')
@dependency.requires('identity_api')
class Manager(manager.Manager):
"""Default pivot point for the Assignment backend.
See :mod:`keystone.common.manager.Manager` for more details on how this
dynamically calls the backend.
assignment.Manager() and identity.Manager() have a circular dependency.
The late import works around this. THe if block prevents creation of the
api object by both managers.
"""
def __init__(self):
assignment_driver = CONF.assignment.driver
if assignment_driver is None:
identity_driver = dependency.REGISTRY['identity_api'].driver
assignment_driver = identity_driver.default_assignment_driver()
super(Manager, self).__init__(assignment_driver)
根据配置文件,Identity的Driver继承自core.,py下Driver和sql.Base基类,通过openstack.common下的sql包实现了对数据库的操作。基本所有的driver针对的都是数据库,有sql的,kvs(key-value存储),ldap(目录)等等。OK,我们知道了Manager与Driver的关系,下面就是Controller和Manager的关系了,看看Manager上的dependency装饰器吧,核心的函数就是两个一个是provider,一个是requires.
def provider(name):
"""Register the wrapped dependency provider under the specified name."""
def wrapper(cls):
def wrapped(init):
def __wrapped_init__(self, *args, **kwargs):
"""Initialize the wrapped object and add it to the registry."""
init(self, *args, **kwargs)
REGISTRY[name] = self
resolve_future_dependencies(name)
return __wrapped_init__
cls.__init__ = wrapped(cls.__init__)
return cls
return wrapper
def requires(*dependencies):
"""Inject specified dependencies from the registry into the instance."""
def wrapper(self, *args, **kwargs):
"""Inject each dependency from the registry."""
self.__wrapped_init__(*args, **kwargs)
_process_dependencies(self)
def wrapped(cls):
"""Note the required dependencies on the object for later injection.
The dependencies of the parent class are combined with that of the
child class to create a new set of dependencies.
"""
existing_dependencies = getattr(cls, '_dependencies', set())
cls._dependencies = existing_dependencies.union(dependencies)
if not hasattr(cls, '__wrapped_init__'):
cls.__wrapped_init__ = cls.__init__
cls.__init__ = wrapper
return cls
return wrapped
注释说的很清楚,provider是装饰的这个类注册到REGISTER[]下面,requires是将REGISTER里面的类取出来,注入到装饰的类下面。所以为什么Manager上需要这个呢?把Manager进行注册,和提取,这样就可以方便我们的Controller调用了。在哪注册呢?service,py的全局变量。
_IDENTITY_API = identity.Manager()
DRIVERS = dict(
assignment_api=assignment.Manager(),
catalog_api=catalog.Manager(),
credentials_api=credential.Manager(),
endpoint_filter_api=endpoint_filter.Manager(),
identity_api=_IDENTITY_API,
policy_api=policy.Manager(),
token_api=token.Manager(),
trust_api=trust.Manager(),
token_provider_api=token.provider.Manager())
dependency.resolve_future_dependencies()
注册了所有的Manager,那在哪提取呢?
@dependency.requires('identity_api', 'policy_api', 'token_api',
'trust_api', 'catalog_api', 'credential_api',
'assignment_api')
class V2Controller(wsgi.Application):
"""Base controller class for Identity API v2."""
这样Controller就可以调用所有的Manager了。
bluefire1991
2013.11.2
21:14:54