OpenStack源码分析之cinder-api服务启动

版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处!


cat /usr/bin/cinder-api

#!/usr/bin/python
# EASY-INSTALL-SCRIPT: 'cinder==2013.1.g3','cinder-api'
__requires__ = 'cinder==2013.1.g3'
import pkg_resources
pkg_resources.run_script('cinder==2013.1.g3', 'cinder-api')

run_script第一参数指向了site-packages/cinder-2013.1.g3-py2.6.egg,第二个参数为要运行的脚本名cinder-api,查看该目录下的EGG-INFO/scripts目录,找到cinder-api

cat cinder-api
import eventlet
eventlet.monkey_patch()

import os
import sys

possible_topdir = os.path.normpath(os.path.join(os.path.abspath(
        sys.argv[0]), os.pardir, os.pardir))
if os.path.exists(os.path.join(possible_topdir, "cinder", "__init__.py")):
    sys.path.insert(0, possible_topdir)

from cinder import flags
from cinder.openstack.common import log as logging
from cinder import service
from cinder import utils

if __name__ == '__main__':
    flags.parse_args(sys.argv)
    logging.setup("cinder")
    utils.monkey_patch()
    server = service.WSGIService('osapi_volume')
    service.serve(server)
    service.wait()

启动了一个WSGIService服务,下面具体分析启动过程。

cinder/service.py
class WSGIService(object):
    """Provides ability to launch API from a 'paste' configuration."""

    def __init__(self, name, loader=None):
        """Initialize, but do not start the WSGI server.

        :param name: The name of the WSGI server given to the loader.
        :param loader: Loads the WSGI application using the given name.
        :returns: None

        """
        self.name = name
        self.manager = self._get_manager()          //初始化服务管理对象
        self.loader = loader or wsgi.Loader()       //指定apploader
        self.app = self.loader.load_app(name)  //加载app,这个过程相对复杂,涉及到paste模块-配置文件解析、模块加载
        self.host = getattr(FLAGS, '%s_listen' % name, "0.0.0.0")  //从flages.py或配置文件中获取osapi_volume_listen配置参数
        self.port = getattr(FLAGS, '%s_listen_port' % name, 0)
        self.server = wsgi.Server(name,
                                  self.app,
                                  host=self.host,
                                  port=self.port)   //初始化一个wsgi的Server

    def _get_manager(self):
        fl = '%s_manager' % self.name     //在flages.py中无osapi_volume_manager配置项,因此返回None
        if fl not in FLAGS:
            return None

        manager_class_name = FLAGS.get(fl, None)
        if not manager_class_name:
            return None

        manager_class = importutils.import_class(manager_class_name)
        return manager_class()

    def start(self):
        if self.manager:
            self.manager.init_host()
        self.server.start()
        self.port = self.server.port

def serve(*servers):
    global _launcher
    if not _launcher:
        _launcher = Launcher()
    for server in servers:
        _launcher.launch_server(server)

self.loader = loader or wsgi.Loader()
self.app = self.loader.load_app(name)  
既加载osapi_volume程序

cinder/wsgi.py
class Loader(object):
    def __init__(self, config_path=None):
         config_path = config_path or FLAGS.api_paste_config   //此处指向 /etc/cinder/api-paste.ini
        self.config_path = utils.find_config(config_path)

     def load_app(self, name):
        try:
            return  deploy.loadapp("config:%s" % self.config_path, name=name)
        except LookupError as err:
            LOG.error(err)
            raise exception.PasteAppNotFound(name=name, path=self.config_path)


deploy.loadapp("config:%s" % self.config_path, name=name)
deploy源自PasteDeploy-1.5.0-py2.6.egg/paste/deploy,加载了loadwsgi.py

PasteDeploy-xxx/pasete/deploy/loadwsgi.py
class _App(_ObjectType):

    name = 'application'
    egg_protocols = ['paste.app_factory', 'paste.composite_factory',
                     'paste.composit_factory']
    config_prefixes = [['app', 'application'], ['composite', 'composit'],
                       'pipeline', 'filter-app']

    def  invoke(self, context):
        if context.protocol in ('paste.composit_factory',
                                'paste.composite_factory'):
            return fix_call(context.object, context.loader, context.global_conf,**context.local_conf)                             
        elif context.protocol == 'paste.app_factory':
            return  fix_call(context.object, context.global_conf, **context.local_conf)
        else:
            assert 0, "Protocol %r unknown" % context.protocol

APP = _App()

def loadapp(uri, name=None, **kw):
    return  loadobj( APP, uri, name=name, **kw)

def loadobj(object_type, uri, name=None, relative_to=None,global_conf=None):       
    context =  loadcontext(object_type, uri, name=name, relative_to=relative_to, global_conf=global_conf)     
    return  context.create()

def  loadcontext(object_type, uri, name=None, relative_to=None, global_conf=None):          
    if '#' in uri:
        if name is None:                      //此处第一次调用时name=osapi_volume , uri=config:/etc/cinder/api-paste.ini
            uri, name = uri.split('#', 1)
        else:
            # @@: Ignore fragment or error?
            uri = uri.split('#', 1)[0]
    if name is None:
        name = 'main'
    if ':' not in uri:
        raise LookupError("URI has no scheme: %r" % uri)
    scheme, path = uri.split(':', 1)      //此处第一次得到的scheme 是config,path是/etc/cinder/api-paste.ini  因此reuturn 返回的是 _loacer['config'] ,具体指向看后面的部分
    scheme = scheme.lower()
    if scheme not in _loaders:
        raise LookupError(
            "URI scheme not known: %r (from %s)"
            % (scheme, ', '.join(_loaders.keys())))
    return _loaders[scheme](object_type, uri, path, name=name, relative_to=relative_to,global_conf=global_conf)

其中
     
def _loadegg(object_type, uri, spec, name, relative_to, global_conf):             
    loader = EggLoader(spec)
    return loader.get_context(object_type, name, global_conf)

def _loadfunc(object_type, uri, spec, name, relative_to, global_conf):        
    loader =  FuncLoader(spec)
    return loader. get_context(object_type, name, global_conf)  //返回了一个LoaderContext,包含了一个ConfigLoader(_Loader)对象

_loaders['config'] = _loadconfig
_loaders['egg'] = _loadegg
_loaders['call'] = _loadfunc

class  FuncLoader(_Loader):   //继承自_Loader类
     def __init__(self, spec):
            self.spec = spec
     def  get_context(...):
            obj =  lookup_object(self.spec)  //该方法来自paste/deploy/util.py
            return  LoaderContext(obj,object_type,None, global_conf or {},{},self,)  
class  LoaderContext(object):
     def create(self):
        return self. object_type. invoke(self)   //此处的object_type为APP,所以调用的是class _APP的invoke方法,从上面的代码可以看到,该方法返回的是一个 fix_call的调用结果,下面具体分析

fix_call方法来自于paste/deploy/util.py
def  fix_call(callable, *args, **kw):
    try:
        val =  callable(*args, **kw)   
//此处的callable指向api的一些功能入口api/__init__.py:root_app_factory    api/middleware/auth.py:pipeline_factory 可以看出此处进行了接口的实际调用,其中root_app_factory实际出发了Paste/paste/urlmap.py:urlmap_factory的调用,后者调用了loader.get_app() ,laoder对象是一个PasteDeploy/paste/deploy/loadwsgi.py中的ConfigLoader(_Loader)类对象。urlmap_factory中的local_conf.items() 为
/: apiversions
/v1: openstack_volume_api_v1
/v2: openstack_volume_api_v2
参看/etc/cinder/api-paste.ini
因此loader.get_app()的解析是逐个加载下面几个字段,直到所有字段加载完成,后面的其它模块加载流程基本相同
[composite:openstack_volume_api_v2]
[composite:openstack_volume_api_v1]
[pipeline:apiversions]
参看/etc/cinder/api-paste.ini


    except TypeError:
        exc_info = fix_type_error(None, callable, args, kw)
        reraise(*exc_info)
    return val

def  lookup_object(spec):    //此处的spec值类似于 cinder.api:root_app_factory、cinder.api.middleware.auth:pipeline_factory
    parts, target = spec.split(':') if ':' in spec else (spec, None)
    module = __import__(parts)
    for part in parts.split('.')[1:] + ([target] if target else []):
        module = getattr(module, part)
     return module

Paste/paste/urlmap.py
def  urlmap_factory(loader, global_conf, **local_conf):
    if 'not_found_app' in local_conf:
        not_found_app = local_conf.pop('not_found_app')
    else:
        not_found_app = global_conf.get('not_found_app')
    if not_found_app:
        not_found_app = loader.get_app(not_found_app, global_conf=global_conf)
    urlmap = URLMap(not_found_app=not_found_app)
    for path, app_name in local_conf.items():
        path = parse_path_expression(path)
        app =  loader.get_app(app_name, global_conf=global_conf)
        urlmap[path] = app
    return urlmap


整个load_app加载了多个模块,最终返回的就是一个urlmap的列表,看下面的调用流程
****** PasteDeploy:paste:deploy:loadwsgi.py:loadcontext: uri:config:/etc/cinder/api-paste.ini  name:osapi_volume  scheme:config: 
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  osapi_volume
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  call:cinder.api:root_app_factory
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name:call:cinder.api:root_app_factory have a sechema
****** PasteDeploy:paste:deploy:loadwsgi.py:loadcontext: uri:call:cinder.api:root_app_factory  name:main  scheme:call: 
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/__init__.pyc'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <function root_app_factory at 0x2529e60>
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <function root_app_factory at 0x2529e60>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  openstack_volume_api_v2
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  call:cinder.api.middleware.auth:pipeline_factory
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name:call:cinder.api.middleware.auth:pipeline_factory have a sechema
****** PasteDeploy:paste:deploy:loadwsgi.py:loadcontext: uri:call:cinder.api.middleware.auth:pipeline_factory  name:main  scheme:call: 
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/__init__.pyc'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api.middleware' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/middleware/__init__.pyc'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api.middleware.auth' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/middleware/auth.pyc'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <function pipeline_factory at 0x2538e60>
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <function pipeline_factory at 0x2538e60>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  faultwrap
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.fault.FaultWrapper'>>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  sizelimit
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.sizelimit.RequestBodySizeLimiter'>>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  authtoken
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <function filter_factory at 0x26d2668>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  keystonecontext
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.auth.CinderKeystoneContext'>>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  apiv2
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.v2.router.APIRouter'>>
2013-02-03 02:59:41.149 4294 AUDIT cinder.api.extensions [-] Initializing extension manager.
2013-02-03 02:59:41.151 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-vol-image-meta
2013-02-03 02:59:41.152 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-hosts
2013-02-03 02:59:41.153 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-types-manage
2013-02-03 02:59:41.154 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-volume-actions
2013-02-03 02:59:41.155 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-vol-host-attr
2013-02-03 02:59:41.156 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-admin-actions
2013-02-03 02:59:41.157 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-extended-snapshot-attributes
2013-02-03 02:59:41.157 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-services
2013-02-03 02:59:41.158 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-image-create
2013-02-03 02:59:41.159 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-vol-tenant-attr
2013-02-03 02:59:41.159 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-quota-class-sets
2013-02-03 02:59:41.160 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-types-extra-specs
2013-02-03 02:59:41.272 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-quota-sets
2013-02-03 02:59:41.273 4294 AUDIT cinder.api.extensions [-] Loaded extension: backups
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  openstack_volume_api_v1
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  call:cinder.api.middleware.auth:pipeline_factory
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name:call:cinder.api.middleware.auth:pipeline_factory have a sechema
****** PasteDeploy:paste:deploy:loadwsgi.py:loadcontext: uri:call:cinder.api.middleware.auth:pipeline_factory  name:main  scheme:call: 
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/__init__.pyc'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api.middleware' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/middleware/__init__.pyc'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api.middleware.auth' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/middleware/auth.pyc'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <function pipeline_factory at 0x2538e60>
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <function pipeline_factory at 0x2538e60>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  faultwrap
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.fault.FaultWrapper'>>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  sizelimit
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.sizelimit.RequestBodySizeLimiter'>>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  authtoken
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <function filter_factory at 0x26d2668>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  keystonecontext
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.auth.CinderKeystoneContext'>>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  apiv1
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.v1.router.APIRouter'>>
2013-02-03 02:59:41.295 4294 AUDIT cinder.api.extensions [-] Initializing extension manager.
2013-02-03 02:59:41.296 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-vol-image-meta
2013-02-03 02:59:41.297 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-hosts
2013-02-03 02:59:41.297 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-types-manage
2013-02-03 02:59:41.297 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-volume-actions
2013-02-03 02:59:41.298 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-vol-host-attr
2013-02-03 02:59:41.298 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-admin-actions
2013-02-03 02:59:41.299 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-extended-snapshot-attributes
2013-02-03 02:59:41.299 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-services
2013-02-03 02:59:41.300 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-image-create
2013-02-03 02:59:41.300 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-vol-tenant-attr
2013-02-03 02:59:41.300 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-quota-class-sets
2013-02-03 02:59:41.301 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-types-extra-specs
2013-02-03 02:59:41.301 4294 AUDIT cinder.api.extensions [-] Loaded extension: os-quota-sets
2013-02-03 02:59:41.301 4294 AUDIT cinder.api.extensions [-] Loaded extension: backups
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  apiversions
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  osvolumeversionapp
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  faultwrap
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.versions.Versions'>>
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.fault.FaultWrapper'>>
****** Paste:paste:urlmap.py:urlmap_factory: app:  <cinder.api.middleware.fault.FaultWrapper object at 0x3480fd0>


其中,在加载cinder.api.v1.router.APIRouter模块时,会有一个加载extension manager的过程,这个部分加载的模块是api服务的关键,完成实际服务的app来自于这些扩展,manager来进行管理,在有web请求时进过路由到对应的app


下面分析cinder.api.v2.router.APIRouter 模块的加载过程:
cinder/api/v2/router.py
class APIRouter(cinder.api.openstack.APIRouter):
     ExtensionManager =  extensions.ExtensionManager
     def  _setup_routes(self, mapper, ext_mgr):
         self.resources['versions'] = versions.create_resource()
         mapper.connect("versions", "/", controller=self.resources['versions'], action='show') 

api/openstack/__init__.py 
class  APIRouter(base_wsgi.Router):
     def  __init__(self, ext_mgr=None):                                                         
        if ext_mgr is None:                                                                   
            if self.ExtensionManager:                                                         
                ext_mgr =  self.ExtensionManager() 此处触发一个 standard api extension 程序的加载过程                                         
            else:                                                                             
                raise Exception(_("Must specify an ExtensionManager class"))                  
                                                                                              
        mapper = ProjectMapper()                                                              
        self.resources = {}                                                                   
        self._setup_routes(mapper, ext_mgr)                                                   
        self._setup_ext_routes(mapper, ext_mgr)                                               
        self._setup_extensions(ext_mgr)                                                       
        super(APIRouter, self).__init__(mapper)

    def  _setup_ext_routes(self, mapper, ext_mgr):                                             
        for resource in ext_mgr.get_resources():
            LOG.debug(_('Extended resource: %s'),
                      resource.collection)

            wsgi_resource = wsgi.Resource(resource.controller)
            self.resources[resource.collection] = wsgi_resource
            kargs = dict(
                controller=wsgi_resource,
                collection=resource.collection_actions,
                member=resource.member_actions)

            if resource.parent:
                kargs['parent_resource'] = resource.parent

            mapper.resource(resource.collection, resource.collection, **kargs)

            if resource.custom_routes_fn:
                resource.custom_routes_fn(mapper, wsgi_resource)

    def  _setup_extensions(self, ext_mgr):
        for extension in ext_mgr.get_controller_extensions():
            ext_name = extension.extension.name
            collection = extension.collection
            controller = extension.controller
            if collection not in self.resources:
                LOG.warning(_('Extension %(ext_name)s: Cannot extend '
                              'resource %(collection)s: No such resource') %
                            locals())
                continue

            LOG.debug(_('Extension %(ext_name)s extending resource: '
                        '%(collection)s') % locals())

            resource = self.resources[collection]
            resource.register_actions(controller)
            resource.register_extensions(controller)

    def  _setup_routes(self, mapper, ext_mgr):
        raise NotImplementedError

cinder/api/extensions.py
class ExtensionManager(object):
    def  __init__(self):
        LOG.audit(_('Initializing extension manager.'))

        self.cls_list = FLAGS.osapi_volume_extension    在flages.py配置文件中,osapi_volume_extension的配置值为cinder.api.contrib.standard_extensions
        self.extensions = {}
        self. _load_extensions()     开始加载standard api extension类

     def _load_extensions(self):
        """Load extensions specified on the command line."""
        extensions = list(self.cls_list)
        for ext_factory in extensions:
            try:
                self. load_extension(ext_factory)    此处ext_factory 为cinder.api.contrib.standard_extensions

     def load_extension(self, ext_factory):
        factory = importutils.import_class(ext_factory)  此处ext_factory 为cinder.api.contrib. standard_extensions,此处调用了cinder/api/contrib/__init__.py: standard_extensions(ext_mgr):
        factory(self) 

cinder/api/contrib/__init__.py
def  standard_extensions(ext_mgr):
    extensions. load_standard_extensions(ext_mgr, LOG, __path__, __package__)   调用的是extensions.py中的load_standard_extensions 方法.


cinder/api/extensions.py
def load_standard_extensions(ext_mgr, logger, path, package, ext_list=None):
    """Registers all standard API extensions."""

    # Walk through all the modules in our directory...
    our_dir = path[0]
    for dirpath, dirnames, filenames in os.walk(our_dir):
        # Compute the relative package name from the dirpath
        relpath = os.path.relpath(dirpath, our_dir)
        if relpath == '.':
            relpkg = ''
        else:
            relpkg = '.%s' % '.'.join(relpath.split(os.sep))

        # Now, consider each file in turn, only considering .py files
        //下面的这个循环根据文件名遍历了standard API extension的.py文件
        for fname in filenames:
            root, ext = os.path.splitext(fname)

            # Skip __init__ and anything that's not .py
            if ext != '.py' or root == '__init__':
                continue

            # Try loading it
            classname = "%s%s" % (root[0].upper(), root[1:])     //root第一个字符变为大写后作为类名
            classpath = ("%s%s.%s.%s" % (package, relpkg, root, classname))   
            //根据文件名及路径信息构造类的路径信息,类似于cinder.api.contrib.volume_image_metadata.Volume_image_metadata
                         

            if ext_list is not None and classname not in ext_list:
                logger.debug("Skipping extension: %s" % classpath)
                continue

            try:
                ext_mgr. load_extension(classpath)    //根据给定的路径加载类
            except Exception as exc:
                logger.warn(_('Failed to load extension %(classpath)s: '
                              '%(exc)s') % locals())

        # Now, let's consider any subdirectories we may have...
        subdirs = []
        for dname in dirnames:                         //子目录同样的加载过程
            # Skip it if it does not have __init__.py
            if not os.path.exists(os.path.join(dirpath, dname,
                                               '__init__.py')):
                continue

            # If it has extension(), delegate...
            ext_name = ("%s%s.%s.extension" %
                        (package, relpkg, dname))
            try:
                ext = importutils.import_class(ext_name)
            except common_exception.NotFound:
                # extension() doesn't exist on it, so we'll explore
                # the directory for ourselves
                subdirs.append(dname)
            else:
                try:
                    ext(ext_mgr)
                except Exception as exc:
                    logger.warn(_('Failed to load extension %(ext_name)s: '
                                  '%(exc)s') % locals())

        # Update the list of directories we'll explore...
        dirnames[:] = subdirs

在基本的standard api extension加载完成后,APIRouter的初始化并没有结束,还有如下的一个过程
        mapper = ProjectMapper()                                                              
        self.resources = {}                                                                   
         self._setup_routes(mapper, ext_mgr)                                                   
        self._setup_ext_routes(mapper, ext_mgr)                                               
        self._setup_extensions(ext_mgr)                                                       
        super(APIRouter, self).__init__(mapper)


cinder/api/v2/router.py
class APIRouter(cinder.api.openstack.APIRouter):
     def _setup_routes(self, mapper, ext_mgr):
        self.resources['versions'] = versions.create_resource()                               
        mapper.connect("versions", "/",
                       controller=self.resources['versions'],                                 
                       action='show')                                                         
        
        mapper.redirect("", "/")                                                              
        
        self.resources['volumes'] = volumes.create_resource(ext_mgr)                          
        mapper.resource("volume", "volumes",
                        controller=self.resources['volumes'],                                 
                        collection={'detail': 'GET'},                                         
                        member={'action': 'POST'})

                                         
以volumes.create_resource(ext_mgr) 为例创建一个resource

cinder/api/v2/volume.py
def create_resource(ext_mgr):
    return  wsgi.Resource( VolumeController(ext_mgr))
 
class  VolumeController(wsgi.Controller):        
    """The Volumes API controller for the OpenStack API."""    
    _view_builder_class = volume_views.ViewBuilder
    def __init__(self, ext_mgr):
         self.volume_api = volume.API()       volume驱动API接口
        self.ext_mgr = ext_mgr
        super(VolumeController, self).__init__()
    @wsgi.serializers(xml=VolumeTemplate)
    def show(self, req, id):
        """Return data about the given volume."""
    def delete(self, req, id):
        """Delete a volume."""
    def index(self, req):
        """Returns a summary list of volumes."""
    @wsgi.serializers(xml=VolumesTemplate)
    def detail(self, req):
        """Returns a detailed list of volumes."""
可以看出该类包含了对卷的操作方法、show、delete等,最终都是调用的self.volume_api来进行操作的。

cinder/volume.py
API = cinder.openstack.common.importutils.import_class(cinder.flags.FLAGS.volume_api_class)
在flages.py中volume_api_class默认值为 cinder.volume.api.API

cinder/volume/api.py
from cinder.scheduler import rpcapi as scheduler_rpcapi
from cinder.volume import rpcapi as volume_rpcapi
class API(base.Base):
    """API for interacting with the volume manager."""

    def __init__(self, db_driver=None, image_service=None):
        self.image_service = (image_service or glance.get_default_image_service())                            
        self.scheduler_rpcapi =  scheduler_rpcapi. SchedulerAPI()
        self.volume_rpcapi =  volume_rpcapi. VolumeAPI()
        super(API, self).__init__(db_driver)

    def create(self, context, size, name, description, snapshot=None,
               image_id=None, volume_type=None, metadata=None,
               availability_zone=None, source_volume=None):
        .....................................................
        self._check_metadata_properties(context, metadata)
        options = {'size': size,
                   'user_id': context.user_id,
                   'project_id': context.project_id,
                   'snapshot_id': snapshot_id,
                   'availability_zone': availability_zone,
                   'status': "creating",
                   'attach_status': "detached",
                   'display_name': name,
                   'display_description': description,
                   'volume_type_id': volume_type_id,
                   'metadata': metadata,
                   'source_volid': source_volid}
        try:
            volume = self.db.volume_create(context, options)
            QUOTAS.commit(context, reservations)
        request_spec = {'volume_properties': options,
                        'volume_type': volume_type,
                        'volume_id': volume['id'],
                        'snapshot_id': volume['snapshot_id'],
                        'image_id': image_id,
                        'source_volid': volume['source_volid']}

        filter_properties = {}
        self. _cast_create_volume(context, request_spec, filter_properties)
        return volume

    def  _cast_create_volume(self, context, request_spec, filter_properties):
             source_volume_ref = self.db.volume_get(context,source_volid)                                           
            now = timeutils.utcnow()
            values = {'host': source_volume_ref['host'], 'scheduled_at': now}
            volume_ref = self.db.volume_update(context, volume_id, values)

            # bypass scheduler and send request directly to volume
            self. volume_rpcapi.create_volume(context,............

cinder/volume/rpcapi.py
class VolumeAPI( cinder.openstack.common.rpc.proxy.RpcProxy):
    BASE_RPC_API_VERSION = '1.0'

    def __init__(self, topic=None):
        super(VolumeAPI, self).__init__(topic=topic or FLAGS.volume_topic,default_version=self.BASE_RPC_API_VERSION)
            
    def create_volume(self, ctxt, volume, host, request_spec, filter_properties,allow_reschedule=True, snapshot_id=None, image_id=None, source_volid=None):                    
        self. cast(ctxt,
                  self.make_msg('create_volume',volume_id=volume['id'], request_spec=request_spec,filter_properties=filter_properties,
                                allow_reschedule=allow_reschedule,snapshot_id=snapshot_id,image_id=image_id,source_volid=source_volid),
                  topic=rpc.queue_get_for(ctxt,self.topic,host),
                  version='1.4')
    使用cast方法发送了一个methord:create_volume的消息到AMQP服务器,另外本类还提供了其它针对volume操作的方法。


再回过来看 wsgi.Resource
cinder/api/openstack/wsgi.py
class Resource(wsgi.Application):
    def __init__(self, controller, action_peek=None, **deserializers):
         self.controller = controller                             从上面的调用wsgi.Resource( VolumeController(ext_mgr))可以看出controller是一个 VolumeController控制对象
        default_deserializers = dict(xml=XMLDeserializer,
        .......

经过上面的这段代码流程的分析,可以看出,self._setup_routes(mapper, ext_mgr)  是对resource和controller进行初始化操作

下面继续
api/openstack/__init__.py 
class  APIRouter(base_wsgi.Router):__init__初始化中的  self._setup_ext_routes(mapper, ext_mgr) 过程分析
    
     //此方法主要是生成controller字典
    def _setup_ext_routes(self, mapper, ext_mgr):       
        for resource in  ext_mgr.get_resources():
            LOG.debug(_('Extended resource: %s'),
                      resource.collection)

            wsgi_resource = wsgi.Resource(resource.controller)
            self.resources[resource.collection] = wsgi_resource
            kargs = dict(
                controller=wsgi_resource,
                collection=resource.collection_actions,
                member=resource.member_actions)

            if resource.parent:
                kargs['parent_resource'] = resource.parent

            mapper.resource(resource.collection, resource.collection, **kargs)

            if resource.custom_routes_fn:
                resource.custom_routes_fn(mapper, wsgi_resource)    

    //本方法主要是注册controller
    def _setup_extensions(self, ext_mgr):
        for extension in ext_mgr.get_controller_extensions():
            ext_name = extension.extension.name
            collection = extension.collection
            controller = extension.controller

            if collection not in self.resources:
                LOG.warning(_('Extension %(ext_name)s: Cannot extend '
                              'resource %(collection)s: No such resource') %
                            locals())
                continue

            LOG.debug(_('Extension %(ext_name)s extending resource: '
                        '%(collection)s') % locals())

            resource = self.resources[collection]
            resource.register_actions(controller)
            resource.register_extensions(controller)

   最后一步:
cinder/wsgi.py

class Router(object):
       def __init__(self, mapper):                                                               
        """Create a router for the given routes.Mapper.                                       
        self.map = mapper                                                                     
        self._router = routes.middleware.RoutesMiddleware(self._dispatch,  self.map)                     
                                                         
     @webob.dec.wsgify(RequestClass=Request)     此处使用了“装饰者”模式,将__call__方法的返回值作为参数传给webob.dec.wsgify的同名方法__call__
    def __call__(self, req):
        """Route the incoming request to a controller based on self.map.

        If no match, return a 404.

        """
        return self._router

    @staticmethod
    @webob.dec.wsgify(RequestClass=Request)
    def _dispatch(req):
        """Dispatch the request to the appropriate controller.

        Called by self._router after matching the incoming request to a route
        and putting the information into req.environ.  Either returns 404
        or the routed WSGI app's response.

        """
        match = req.environ['wsgiorg.routing_args'][1]
        if not match:
            return webob.exc.HTTPNotFound()
        app = match['controller']
        return app    

当有web请求时,请求的入口就是__call__方法

最后在看WSGIService启动的最后一个步骤serve函数
cinder/service.py
def serve(*servers): 
    global _launcher
    if not _launcher:
        _launcher =  Launcher()
    for server in servers:
        _launcher. launch_server(server)

class Launcher(object):
    @staticmethod
    def run_server(server):
        """Start and wait for a server to finish.

        :param service: Server to run and wait for.
        :returns: None

        """   
         server.start()
        server.wait()

     def launch_server(self, server):
        """Load and start the given server.
    
        :param server: The server you would like to start.
        :returns: None
        
        """
        gt = eventlet.spawn(self.run_server, server)
        self._services.append(gt)

可以看出最终是调用了wsgi. Server的start方法
cinder/wsgi.py
    def _start(self):
        eventlet.wsgi.server(self._socket,
                             self.app,                   这里的app就是经过多层包装的 APIRouter
                             protocol=self._protocol,
                             custom_pool=self._pool,
                             log=self._wsgi_logger)

    def start(self, backlog=128):
        """Start serving a WSGI application.
        """
        if backlog < 1:
            raise exception.InvalidInput(
                reason='The backlog must be more than 1')
        self._socket = self._get_socket(self._host,
                                        self._port,
                                        backlog=backlog)
        self._server = eventlet.spawn(self._start)
        (self._host, self._port) = self._socket.getsockname()[0:2]
        LOG.info(_("Started %(name)s on %(_host)s:%(_port)s") % self.__dict__)


补充一些程序的调用流程输出,也许可以帮助我们理解:

****** PasteDeploy:paste:deploy:loadwsgi.py:loadcontext: uri:config:/etc/cinder/api-paste.ini  name:osapi_volume  scheme:config: 
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  osapi_volume
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    composite:osapi_volume
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_use ******
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  call:cinder.api:root_app_factory
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name:call:cinder.api:root_app_factory have a sechema
****** PasteDeploy:paste:deploy:loadwsgi.py:loadcontext: uri:call:cinder.api:root_app_factory  name:main  scheme:call: 
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/__init__.pyc'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <function root_app_factory at 0x20b0e60>
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <function root_app_factory at 0x20b0e60>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  openstack_volume_api_v2
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    composite:openstack_volume_api_v2
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_use ******
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  call:cinder.api.middleware.auth:pipeline_factory
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name:call:cinder.api.middleware.auth:pipeline_factory have a sechema
****** PasteDeploy:paste:deploy:loadwsgi.py:loadcontext: uri:call:cinder.api.middleware.auth:pipeline_factory  name:main  scheme:call: 
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/__init__.pyc'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api.middleware' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/middleware/__init__.pyc'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api.middleware.auth' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/middleware/auth.py'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <function pipeline_factory at 0x20bbed8>
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <function pipeline_factory at 0x20bbed8>
****** api.middleware.auth.pipeline_factory: call get_context method of loader for N times in pipeline: ['faultwrap', 'sizelimit', 'authtoken', 'keystonecontext', 'apiv2']
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  faultwrap
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    filter:faultwrap
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_filter ******
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.fault.FaultWrapper'>>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  sizelimit
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    filter:sizelimit
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_filter ******
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.sizelimit.RequestBodySizeLimiter'>>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  authtoken
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    filter:authtoken
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_filter ******
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <function filter_factory at 0x22536e0>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  keystonecontext
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    filter:keystonecontext
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_filter ******
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.auth.CinderKeystoneContext'>>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  apiv2
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    app:apiv2
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_filter ******
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.v2.router.APIRouter'>>
2013-02-08 05:28:49.000 39038 AUDIT cinder.api.extensions [-] Initializing extension manager.
2013-02-08 05:28:49.002 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-vol-image-meta
2013-02-08 05:28:49.003 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-hosts
2013-02-08 05:28:49.004 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-types-manage
2013-02-08 05:28:49.005 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-volume-actions
2013-02-08 05:28:49.006 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-vol-host-attr
2013-02-08 05:28:49.007 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-admin-actions
2013-02-08 05:28:49.007 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-extended-snapshot-attributes
2013-02-08 05:28:49.008 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-services
2013-02-08 05:28:49.008 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-image-create
2013-02-08 05:28:49.009 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-vol-tenant-attr
2013-02-08 05:28:49.010 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-quota-class-sets
2013-02-08 05:28:49.011 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-types-extra-specs
2013-02-08 05:28:49.122 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-quota-sets
2013-02-08 05:28:49.123 39038 AUDIT cinder.api.extensions [-] Loaded extension: backups
****** api.middleware.auth.pipeline_factory: filter(app) : <cinder.api.middleware.auth.CinderKeystoneContext object at 0x2db0950>
****** api.middleware.auth.pipeline_factory: filter(app) : <keystoneclient.middleware.auth_token.AuthProtocol object at 0x2ed2810>
****** api.middleware.auth.pipeline_factory: filter(app) : <cinder.api.middleware.sizelimit.RequestBodySizeLimiter object at 0x2ed2c50>
****** api.middleware.auth.pipeline_factory: filter(app) : <cinder.api.middleware.fault.FaultWrapper object at 0x2ed2e50>
****** Paste:paste:urlmap.py:urlmap_factory: local_conf, app:  {'/v2': 'openstack_volume_api_v2', '/v1': 'openstack_volume_api_v1', '/': 'apiversions'} <cinder.api.middleware.fault.FaultWrapper object at 0x2ed2e50>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  openstack_volume_api_v1
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    composite:openstack_volume_api_v1
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_use ******
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  call:cinder.api.middleware.auth:pipeline_factory
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name:call:cinder.api.middleware.auth:pipeline_factory have a sechema
****** PasteDeploy:paste:deploy:loadwsgi.py:loadcontext: uri:call:cinder.api.middleware.auth:pipeline_factory  name:main  scheme:call: 
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/__init__.pyc'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api.middleware' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/middleware/__init__.pyc'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <module 'cinder.api.middleware.auth' from '/usr/lib/python2.6/site-packages/cinder-2013.1.g3-py2.6.egg/cinder/api/middleware/auth.py'>
****** PasteDeploy:paste:deploy:util.py:lookup_object: module <function pipeline_factory at 0x20bbed8>
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <function pipeline_factory at 0x20bbed8>
****** api.middleware.auth.pipeline_factory: call get_context method of loader for N times in pipeline: ['faultwrap', 'sizelimit', 'authtoken', 'keystonecontext', 'apiv1']
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  faultwrap
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    filter:faultwrap
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_filter ******
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.fault.FaultWrapper'>>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  sizelimit
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    filter:sizelimit
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_filter ******
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.sizelimit.RequestBodySizeLimiter'>>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  authtoken
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    filter:authtoken
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_filter ******
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <function filter_factory at 0x22536e0>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  keystonecontext
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    filter:keystonecontext
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_filter ******
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.auth.CinderKeystoneContext'>>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  apiv1
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    app:apiv1
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_filter ******
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.v1.router.APIRouter'>>
2013-02-08 05:28:49.145 39038 AUDIT cinder.api.extensions [-] Initializing extension manager.
2013-02-08 05:28:49.146 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-vol-image-meta
2013-02-08 05:28:49.146 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-hosts
2013-02-08 05:28:49.147 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-types-manage
2013-02-08 05:28:49.147 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-volume-actions
2013-02-08 05:28:49.148 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-vol-host-attr
2013-02-08 05:28:49.148 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-admin-actions
2013-02-08 05:28:49.148 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-extended-snapshot-attributes
2013-02-08 05:28:49.149 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-services
2013-02-08 05:28:49.149 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-image-create
2013-02-08 05:28:49.150 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-vol-tenant-attr
2013-02-08 05:28:49.150 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-quota-class-sets
2013-02-08 05:28:49.150 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-types-extra-specs
2013-02-08 05:28:49.151 39038 AUDIT cinder.api.extensions [-] Loaded extension: os-quota-sets
2013-02-08 05:28:49.151 39038 AUDIT cinder.api.extensions [-] Loaded extension: backups
****** api.middleware.auth.pipeline_factory: filter(app) : <cinder.api.middleware.auth.CinderKeystoneContext object at 0x2fd3810>
****** api.middleware.auth.pipeline_factory: filter(app) : <keystoneclient.middleware.auth_token.AuthProtocol object at 0x30016d0>
****** api.middleware.auth.pipeline_factory: filter(app) : <cinder.api.middleware.sizelimit.RequestBodySizeLimiter object at 0x3001950>
****** api.middleware.auth.pipeline_factory: filter(app) : <cinder.api.middleware.fault.FaultWrapper object at 0x3001b10>
****** Paste:paste:urlmap.py:urlmap_factory: local_conf, app:  {'/v2': 'openstack_volume_api_v2', '/v1': 'openstack_volume_api_v1', '/': 'apiversions'} <cinder.api.middleware.fault.FaultWrapper object at 0x3001b10>
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  apiversions
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    pipeline:apiversions
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_pipeline ******
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  osvolumeversionapp
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    app:osvolumeversionapp
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_filter ******
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: name  faultwrap
****** PasteDeploy:paste:deploy:loadwsgi.py:ConfigLoader:get_context: section:    filter:faultwrap
****** PasteDeploy.paste.deploy.loadwsgi.get_context: section_start_with_filter ******
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.versions.Versions'>>
****** PasteDeploy:paste:deploy:util.py:fix_call: callable:  <bound method type.factory of <class 'cinder.api.middleware.fault.FaultWrapper'>>
****** Paste:paste:urlmap.py:urlmap_factory: local_conf, app:  {'/v2': 'openstack_volume_api_v2', '/v1': 'openstack_volume_api_v1', '/': 'apiversions'} <cinder.api.middleware.fault.FaultWrapper object at 0x30090d0>

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值