nova-api源碼分析(APP的建立)

來源:互聯網
上載者:User

標籤:

 

二、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_v2keystone_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的各個函數。 

 

 

nova-api源碼分析(APP的建立)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.