完成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