zoukankan      html  css  js  c++  java
  • nova-api源码分析(APP的创建)

    目录结构如下:

      

      上面介绍了nova-api发布所用到的一些lib库,有了上面的基础知识,再来分析nova-api的发布流程,就比较轻松了。nova-api可以提供多种api服务:ec2, osapi_compute, osapi_volume, metadata。可以通过配置项enabled_apis来设置启动哪些服务,默认情况下,四种服务都是启动的。从nova-api的可执行脚本中,可以看出每个nova-api服务都是通过nova.service.WSGIService来管理的:

    class WSGIService(object):
     
      def __init__(self, name, loader=None):
        self.name = name
        self.manager = self._get_manager()
        self.loader = loader or wsgi.Loader()
        self.app = self.loader.load_app(name)
        self.host = getattr(FLAGS, '%s_listen' % name, "0.0.0.0")
        self.port = getattr(FLAGS, '%s_listen_port' % name, 0)
        self.workers = getattr(FLAGS, '%s_workers' % name, None)
        self.server = wsgi.Server(name,                               #这里通过eventlet来启动服务
                       self.app,
                       host=self.host,
                       port=self.port)
    
      def start(self):
        if self.manager:
        self.manager.init_host()
        self.server.start()
      .......

          从上可知,WSGIService使用self.app = self.loader.load_app(name)来加载wsgi app,app加载完成后,使用nova.wsgi.Server来发布服务。Server首先用指定ip和port实例化一个监听socket,并使用wsgi.server以协程的方式来发布socket,并将监听到的http请求交给app处理。下面我们主要来分析处理HTTP请求的wsgi app是如何构建的,对于每一个请求,它是如何根据url和请求方法将请求分发到具体的具体函数处理的。

    1、 composite:osapi_compute

      上个语句self.loader.load_app(name)中的loader是nova.wsgi.Loader的实例。Loader.load_app(name)执行下面指令,使用deploy来加载wsgi app:

    deploy.loadapp("config:%s" % self.config_path, name=name)

          self.config_path为api-paste.ini文件路径,一般为/etc/nova/api-paste.ini。name为ec2, osapi_compute, osapi_volume, metadata之一,根据指定的name不同来加载不同的wsgi app。下面以name=“osapi_compute”时,加载提供openstack compute API服务的wsgi app作为具体分析。osapi_compute的配置如下

    [composite:osapi_compute]                                                      
    use = call:nova.api.openstack.urlmap:urlmap_factory                            
    /: oscomputeversions                                               
    /v2: openstack_compute_api_v2

          osapi_compute是调用urlmap_factory函数返回的一个nova.api.openstack.urlmap.URLMap实例,nova.api.openstack.urlmap.URLMap继承paste.urlmap.URLMap,它提供了wsgi调用接口,所以该实例为wsgi app。但是函数nova.api.openstack.urlmap.urlmap_factory与paste.urlmap.urlmap_factory定义完全一样,不过由于它们所在的module不同,使得它们所用的URLMap分别为与它处于同一module的URLMap。paste.urlmap.URLMap实现的功能很简单:根据配置将url映射到特定wsgi app,并根据url的长短作一个优先级排序,url较长的将优先进行匹配。所以/v2将先于/进行匹配。URLMap在调用下层的wsgi app前,会更新SCRIPT_NAME和PATH_INFO。nova.api.openstack.urlmap.URLMap继承了paste.urlmap.URLMap,并写了一堆代码,其实只是为了实现对请求类型的判断,并设置environ['nova.best_content_type']:如果url的后缀名为json(如/xxxx.json),那么environ['nova.best_content_type']=“application/json”。如果url没有后缀名,那么将通过HTTP headers的content_type字段中mimetype判断。否则默认environ['nova.best_content_type']=“application/json”。

    2、composite:openstack_compute_api_v2

          经上面配置加载的osapi_compute为一个URLMap实例,wsgi server接受的HTTP请求将直接交给该实例处理。它将url为'/v2'的请求将交给openstack_compute_api_v2,url为'/'的请求交给oscomputerversions处理(它直接返回系统版本号)。其它的url请求,则返回NotFound。下面继续分析openstack_compute_api_v2,其配置如下:

    [composite:openstack_compute_api_v2]                                           
    use = call:nova.api.auth:pipeline_factory                                      
    noauth = faultwrap sizelimit noauth ratelimit osapi_compute_app_v2             
    keystone = faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2
    keystone_nolimit = faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v2

          openstack_compute_api_v2是调用nova.api.auth.pipeline_factory()返回的wsgi app。pipeline_factory()根据配置项auth_strategy来加载不同的filter和最终的osapi_compute_app_v2。filter的大概配置如下:

    [filter:faultwrap]                                                             
    paste.filter_factory = nova.api.openstack:FaultWrapper.factory

         filter在nova中对应的是nova.wsgi.Middleware,它的定义如下:

    class Middleware(Application):                                                                      
    
      @classmethod                                                               
      def factory(cls, global_config, **local_config):                                                                  
        def _factory(app):                                                     
          return cls(app, **local_config)                                    
        return _factory                                                        
       
      def __init__(self, application):                                           
        self.application = application                                         
      
      def process_request(self, req):                                            
        return None                                                            
      
      def process_response(self, response):                          
        return response                                                        
      
      @webob.dec.wsgify(RequestClass=Request)                                    
      def __call__(self, req):                                                   
        response = self.process_request(req)                                   
        if response:                                                           
          return response                                                    
        response = req.get_response(self.application)                          
        return self.process_response(response)

           Middleware初始化接收一个wsgi app,在调用wsgi app之前,执行process_request()对请求进行预处理,判断请求是否交给传入的wsgi app,还是直接返回,或者修改些req再给传入的wsgi app处理。wsgi app返回的response再交给process_response()处理。例如,对于进行验证的逻辑,可以放在process_request中,如果验证通过则继续交给app处理,否则返回“Authentication required”。不过查看nova所有Mddlerware的编写,似乎都不用这种定义好的结构,而是把处理逻辑都放到__call__中,这样导致__call__变得复杂,代码不够整洁。当auth_strategy=“keystone”时,openstack_compute_api_v2=FaultWrapper-> RequestBodySizeLimiter-> auth_token-> NovaKeystoneContext-> RateLimitingMiddleware-> osapi_compute_app_v2。所以HTTP请求需要经过五个Middleware的处理,才能到达osapi_compute_app_v2。这五个Middleware分别完成:

      1)异常捕获,防止服务内部处理异常导致wsgi server挂掉;

      2)限制HTTP请求body大小,对于太大的body,将直接返回BadRequest;

      3)对请求keystone对header中token id进行验证;

      4)利用headers初始化一个nova.context.RequestContext实例,并赋给req.environ['nova.context'];

      5)限制用户的访问速度。

      注意以上filter是正序调用,逆序返回。即正序处理请求,逆序处理结果!

    3、app:osapi_compute_app_v2

         当HTTP请求经过上面五个Middlerware处理后,最终交给osapi_compute_app_v2,它是怎么继续处理呢?它的配置如下:

    [app:osapi_compute_app_v2]                                                     
    paste.app_factory = nova.api.openstack.compute:APIRouter.factory 

         osapi_compute_app_v2是调用nova.api.openstack.compute.APIRouter.factory()返回的一个APIRouter实例。具体的请求会调用APIRouter的__call__函数。大致为,app加载主要是构造APIRouter,调用__init__函数,然后发散开去构造其他的Resource(或者Controller),走这一条线索;而请求的处理主要是调用APIRouter的__call__函数,然后匹配到相应的Controller,调用Controller的函数,走这一条线索。可以参考:

    http://www.choudan.net/2013/07/30/OpenStack-API%E5%88%86%E6%9E%90(%E4%B8%80).html

         先看看这部分的类关系,如下图所示:

    3.1 首先分析APIRouter的构造

    nova/api/openstack/__init__.py

    APIRouter类:
    def __init__(self, ext_mgr=None, init_only=None):
        if ext_mgr is None:
            if self.ExtensionManager:
                ext_mgr = self.ExtensionManager()
            else:
                raise Exception(_("Must specify an ExtensionManager class"))
    
        mapper = ProjectMapper()
        self.resources = {}
        self._setup_routes(mapper, ext_mgr, init_only)
        self._setup_ext_routes(mapper, ext_mgr, init_only)
        self._setup_extensions(ext_mgr)
        super(APIRouter, self).__init__(mapper)

    nova/wsgi.py

    Router类:
    def __init__(self, mapper):
        self.map = mapper
        self._router = routes.middleware.RoutesMiddleware(self._dispatch,
                                                          self.map)

          APIRouter通过它的成员变量self.map(mapper)来建立和维护url与controller之间的映射,该mapper是nova.api.openstack.ProjectMapper的实例,它继承nova.api.openstack.APIMapper(routes.Mapper)。APIMapper将每个url的format限制为json或xml,对于其它扩展名的url将返回NotFound。ProjectMapper在每个请求url前面加上一个project_id,这样每个请求的url都需要带上用户所属的project id,所以一般请求的url为/v2/project_id/resources。

          APIRouter通过self._setup_routes,self._setup_ext_routes,self._setup_extensions三个函数对mapper进行设置,下面会进行详细分析。其中使用到的ExtensionManager实例,该实例的构造可以参见:http://www.cnblogs.com/littlebugfish/p/4049853.html,此处不再详述,ExtensionManager中会有属性索引到扩展模块中对应的扩展类的实例。最后调用父类Router的__init__函数,其中self._router是routes.middleware.RoutesMiddleware的实例,使用self._dispatch和self.map来初始化的。 

    3.1.1 _setup_routes分析(核心Resource)

    class APIRouter(nova.api.openstack.APIRouter):                                                                   
      ExtensionManager = extensions.ExtensionManager                             
      
      def _setup_routes(self, mapper, ext_mgr):
        self.resources['servers'] = servers.create_resource(ext_mgr)           
        mapper.resource("server", "servers",                                   
                  controller=self.resources['servers'])                  
       
        self.resources['ips'] = ips.create_resource()                          
        mapper.resource("ip", "ips", controller=self.resources['ips'],         
                parent_resource=dict(member_name='server',             
                             collection_name='servers'))
        .......

       APIRouter通过调用routes.Mapper.resource()函数建立RESTFUL API,也可以通过routes.Mapper.connect()来建立url与controller的映射。如上所示,servers相关请求的controller设为servers.create_resource(ext_mgr),该函数返回的是一个用nova.api.openstack.compute.servers.Controller()作为初始化参数的nova.api.openstack.wsgi.Resource实例,ips相关请求的controller设为由nova.api.openstack.ips.Controller()初始化的nova.api.openstack.wsgi.Resource实例。因为调用mapper.resource建立ips的url映射时,添加了一个parent_resource参数,使得请求ips相关api的url形式为/v2/project_id/servers/server_id/ips。对于limits、flavors、metadata等请求情况类似。 

    3.1.2  _setup_ext_routes分析(扩展Resource)     

    def _setup_ext_routes(self, mapper, ext_mgr, init_only):
        for resource in ext_mgr.get_resources():
            LOG.debug(_('Extended resource: %s'),
                      resource.collection)
    
            if init_only is not None and resource.collection not in init_only:
                continue
    
            inherits = None
            if resource.inherits:
                inherits = self.resources.get(resource.inherits)
                if not resource.controller:
                    resource.controller = inherits.controller
            wsgi_resource = wsgi.Resource(resource.controller,
                                          inherits=inherits)
            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)

      首先会调用ExtensionManager的get_resources函数,该函数会遍历调用注册在ExtensionManager中的扩展类实例的get_resources函数(即nova/api/openstack/compute/contrib中每个文件里面对应文件名的类的get_resources函数),从而获取一个nova.api.openstack.ResourceExtension对象列表。接着将这些ResourceExtension对象的controller封装成nova.api.openstack.wsgi.Resource对象,最后使用mapper.resource函数构建URL到这些Resource的映射。 

    3.1.3  _setup_extensions分析(扩展Controller)     

    def _setup_extensions(self, ext_mgr):
        for extension in ext_mgr.get_controller_extensions():
            collection = extension.collection
            controller = extension.controller
    
            msg_format_dict = {'collection': collection,
                               'ext_name': extension.extension.name}
            if collection not in self.resources:
                LOG.warning(_('Extension %(ext_name)s: Cannot extend '
                              'resource %(collection)s: No such resource'),
                            msg_format_dict)
                continue
    
            LOG.debug(_('Extension %(ext_name)s extending resource: '
                        '%(collection)s'),
                      msg_format_dict)
    
            resource = self.resources[collection]
            resource.register_actions(controller)
            resource.register_extensions(controller)

      首先会调用ExtensionManager的get_controller_extensions函数,该函数会遍历调用注册在ExtensionManager中的扩展类实例的get_controller_extensions函数(即nova/api/openstack/compute/contrib中每个文件里面对应文件名的类的get_controller_extensions函数),从而获取一个nova.api.openstack. ControllerExtension对象列表。接着获取每个ControllerExtension中collection对应的nova.api.openstack.wsgi.Resource资源,向该资源注册ControllerExtension中controller的各个函数。 

  • 相关阅读:
    触发事件trigger
    淘宝天猫关键词SEO优化
    Linux下升级python
    python3多线程趣味详解
    python之selenium
    1kkk
    python之lxml(xpath)
    python中时间日期格式化符号
    唯品会数据采集-异步瀑布流
    python数据库操作pymysql
  • 原文地址:https://www.cnblogs.com/littlebugfish/p/4660586.html
Copyright © 2011-2022 走看看