cinder-api的启动过程

打印 上一主题 下一主题

主题 866|帖子 866|积分 2598

依赖模块

Cinder-API是OpenStack中的一个组件,它提供了一组RESTful API,用于管理和访问块存储资源。
依赖模块:
Paste:
是一个用于 Web 应用步伐的框架,它提供了许多可重用的组件和中间件,资助开辟人员更轻松地创建 Web 应用步伐。其中,Paste的主要功能是将 HTTP 请求和相应封装为 WSGI(Web Server Gateway Interface)协议,这使得开辟人员可以通过编写符合WSGI规范的Python函数来处理惩罚Web请求。
PasteDeploy
是一个用于加载、设置和管理Python Web应用步伐的框架,它可以方便地加载和设置 WSGI 应用步伐,使其可以在不同的Web服务器上运行。在 OpenStack Restful API 框架中,PasteDeploy主要负责构建WSGI服务器和WSGI应用步伐。它的主要功能是办理 WSGI 应用步伐的部署和设置标题。利用 PasteDeploy,开辟人员可以将应用步伐的设置信息分离出来,并通过设置文件或命令行参数的方式加载,从而实现应用步伐的灵活设置。
Routes
是一个 Python Web 框架,用于管理 URL 路由,使得开辟人员可以轻松地将 HTTP 请求路由到相应的处理惩罚函数。它利用简单的路由语法,例如“/users/{user_id}”,将 URL 匹配到相应的视图函数。
WebOb
是一个用于处理惩罚 HTTP 请求和相应的 Python 库。它提供了一个包装器,可以将原始 HTTP 请求和相应封装为 Python 对象,从而使开辟人员可以更方便地处理惩罚请求和相应。WebOb也提供了一些方便的函数和类,例如解析 POST 数据,处理惩罚 Cookie 和 Session 等。
WebOb 有两个重要的对象:
一个是 Webob.Request,对 WSGI Request 的 environ 参数进行封装。
一个是 webob.Response ,包罗了标准 WSGI Response 的全部元素。
此外,还有一个 webob.exc,针对 HTTP 错误代码进行封装。
WSGI
WSGI 是 Python 应用步伐或框架与 Web 服务器之间的一种接口,它定义了一套接口来实现服务器与应用端的通信规范,通过利用RESTful API使得用户可以方便地管理和访问Cinder的块存储资源。它将 web 组件分为三类:
1.web 服务器(Service):接受客户端发来的 request,并返回 app 产生的 response 发回给客户端
2.web 应用步伐(App): 每个 app 是一个 callable 对象。一个函数, 方法, 类, 大概实现了\ call 方法的实例对象都可以用来作为应用步伐对象。服务器每次收到 http 客户端发来的请求都会调用相应的应用步伐的 _call_ 方法去行止理。
3.web 中间件(Middleware):某些对象(app)可以在一些应用步伐面前是服务器, 而从另一些服务器看来却是应用步伐。
具体过程形貌如下:
1.服务器为 Applicaiton 实例化一个 WSGIService 实例,实例化过程中调用 Paste Deployment 来加载/注册各 middleware 和app。
2.服务器启动 WSGIService , 包括创建 socket,监听端口,然后等候客户端毗连。
3.当有请求来时,服务器解析客户端信息放到环境变量 environ 中,并调用绑定的 handler 来处理惩罚请求。handler 解析这个 http 请求,将请求信息例如 method,path 等放到 environ 中。wsgi handler还会将一些服务器端信息也放到 environ 中,末了服务器信息,客户端信息,本次请求信息全部都保存到了环境变量environ中
4.wsgi handler 调用注册的各 app (包括 middleware) 来处理惩罚 request,好比 middleware 来进行数据检查、整理、用户验证等, wsgi app 生成 reponse header/status/body 回传给wsgi handler。
5.response 生成以后和回传之前,一些等候 response 的 被阻塞的 middleware 的方法可以对response 进行进一步的处理惩罚,好比添加新的数据等
6.最终 handler 通过socket将response信息塞回给客户端
一.启动cinder-api入口
用户调用 cinder/bin/ 目次中的脚本来启动相关服务,好比 cinder-all 会启动cinder全部服务,cinder-api 会启动 cinder-api服务
cinder/bin/cinder-api
  1. import sys
  2. from cinder.cmd.api import main
  3. if __name__ == "_main__":
  4.     main()
复制代码
二.启动WSGI Service
  1. def main() -> None:
  2.         ...
  3.         ...
  4.     rpc.init(CONF)
  5.     launcher = service.process_launcher()
  6.     server = service.WSGIService('osapi_volume') # # 使用 Paste delopment 方式 加载 osapi_volume 这个 application,这过程中包括加载各 middleware app
  7.     launcher.launch_service(server, workers=server.workers) #真正启动service
  8.     launcher.wait()
复制代码
三.PasteDeploy生成WSGIService和WSGIApplication
cinder/service.py
  1. class WSGIService(service.ServiceBase):
  2.     """Provides ability to launch API from a 'paste' configuration."""
  3.     def __init__(self, name, loader=None):
  4.         """Initialize, but do not start the WSGI server.
  5.         :param name: The name of the WSGI server given to the loader.
  6.         :param loader: Loads the WSGI application using the given name.
  7.         :returns: None
  8.         """
  9.         self.name = name
  10.         self.manager = self._get_manager()
  11.         self.loader = loader or wsgi.Loader(CONF)
  12.         self.app = self.loader.load_app(name)
  13.         self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")
  14.         self.port = getattr(CONF, '%s_listen_port' % name, 0)
  15.         self.use_ssl = getattr(CONF, '%s_use_ssl' % name, False)
  16.         self.workers = (getattr(CONF, '%s_workers' % name, None) or
  17.                         processutils.get_worker_count())
  18.         if self.workers and self.workers < 1:
  19.             worker_name = '%s_workers' % name
  20.             msg = (_("%(worker_name)s value of %(workers)d is invalid, "
  21.                      "must be greater than 0.") %
  22.                    {'worker_name': worker_name,
  23.                     'workers': self.workers})
  24.             raise exception.InvalidConfigurationValue(msg)
  25.         setup_profiler(name, self.host)
  26.         self.server = wsgi.Server(CONF,
  27.                                   name,
  28.                                   self.app,
  29.                                   host=self.host,
  30.                                   port=self.port,
  31.                                   use_ssl=self.use_ssl)
复制代码
3.1 self.app = self.loader.load_app(name)
oslo_service/wsgi.py
  1.     def load_app(self, name):
  2.         """Return the paste URLMap wrapped WSGI application.
  3.         :param name: Name of the application to load.
  4.         :returns: Paste URLMap object wrapping the requested application.
  5.         :raises: PasteAppNotFound
  6.         """
  7.         try:
  8.             LOG.debug("Loading app %(name)s from %(path)s",
  9.                       {'name': name, 'path': self.config_path})
  10.             return deploy.loadapp("config:%s" % self.config_path, name=name)
  11.         except LookupError:
  12.             LOG.exception("Couldn't lookup app: %s", name)
  13.             raise PasteAppNotFound(name=name, path=self.config_path)
复制代码
其中config_path的定义如下:
  1. config_path = conf.api_paste_config
  2. if not os.path.isabs(config_path):
  3.     self.config_path = conf.find_file(config_path)
  4. elif os.path.exists(config_path):
  5.     self.config_path = config_path
复制代码
追根朔源:
  1. os.path.dirname(__file__), '..', '..', '..',
  2. 'etc/cinder/api-paste.ini'))
  3. CONF.api_paste_config = default_conf
复制代码
得到:
  1. config_path='etc/cinder/api-paste.ini'
复制代码
cinder-api会加载设置文件中的参数并设置默认值,如self.host='0.0.0.0’表现cinder-api节点,self.port='8776’表现cinder-api的端口,self.workers='5’表现cinder-api的历程数等
api-paste.ini如下:
  1. #############
  2. # OpenStack #
  3. #############
  4. [composite:osapi_volume]
  5. use = call:cinder.api:root_app_factory
  6. /: apiversions
  7. /healthcheck: healthcheck
  8. /v3: openstack_volume_api_v3
  9. [composite:openstack_volume_api_v3]
  10. use = call:cinder.api.middleware.auth:pipeline_factory
  11. noauth = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler noauth apiv3
  12. noauth_include_project_id = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler noauth_include_project_id apiv3
  13. keystone = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler authtoken keystonecontext apiv3
  14. keystone_nolimit = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler authtoken keystonecontext apiv3
  15. [filter:http_proxy_to_wsgi]
  16. paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory
  17. [filter:cors]
  18. paste.filter_factory = oslo_middleware.cors:filter_factory
  19. oslo_config_project = cinder
  20. [filter:faultwrap]
  21. paste.filter_factory = cinder.api.middleware.fault:FaultWrapper.factory
  22. [filter:osprofiler]
  23. paste.filter_factory = osprofiler.web:WsgiMiddleware.factory
  24. [filter:noauth]
  25. paste.filter_factory = cinder.api.middleware.auth:NoAuthMiddleware.factory
  26. [filter:noauth_include_project_id]
  27. paste.filter_factory = cinder.api.middleware.auth:NoAuthMiddlewareIncludeProjectID.factory
  28. [filter:sizelimit]
  29. paste.filter_factory = oslo_middleware.sizelimit:RequestBodySizeLimiter.factory
  30. [app:apiv3]
  31. paste.app_factory = cinder.api.v3.router:APIRouter.factory
  32. [pipeline:apiversions]
  33. pipeline = request_id cors http_proxy_to_wsgi faultwrap osvolumeversionapp
  34. [app:osvolumeversionapp]
  35. paste.app_factory = cinder.api.versions:Versions.factory
  36. [pipeline:healthcheck]
  37. pipeline = request_id healthcheckapp
  38. [app:healthcheckapp]
  39. paste.app_factory = oslo_middleware:Healthcheck.app_factory
  40. backends = disable_by_file
  41. disable_by_file_path = /etc/cinder/healthcheck_disable
  42. ##########
  43. # Shared #
  44. ##########
  45. [filter:keystonecontext]
  46. paste.filter_factory = cinder.api.middleware.auth:CinderKeystoneContext.factory
  47. [filter:authtoken]
  48. paste.filter_factory = keystonemiddleware.auth_token:filter_factory
  49. [filter:request_id]
  50. paste.filter_factory = cinder.api.middleware.request_id:RequestId.factory
复制代码
3.2 deploy.loadapp(“config:%s” % self.config_path, name=name)
入口在文件的 [compositesapi_volume]部分:
loadapp,它可以用来从 config 设置文件大概 Python egg 文件加载 app(包括middleware/filter和app),它只要求 app 给它提供一个入口函数,该函数通过设置文件告诉Paste depoly loader。
  1. use = call:cinder.api:root_app_factory
复制代码
源码如下:
cinder/api/_init_.py
  1. def root_app_factory(loader, global_conf, **local_conf):
  2.     # To support upgrades from previous api-paste config files, we need
  3.     # to check for and remove any legacy references to the v1 or v2 API
  4.     if '/v1' in local_conf:
  5.         LOG.warning('The v1 API has been removed and is no longer '
  6.                     'available. Client applications should be '
  7.                     'using v3, which is currently the only supported '
  8.                     'version of the Block Storage API.')
  9.         del local_conf['/v1']
  10.     if '/v2' in local_conf:
  11.         LOG.warning('The v2 API has been removed and is no longer available. '
  12.                     'Client applications must now use the v3 API only. '
  13.                     'The \'enable_v2_api\' option has been removed and is '
  14.                     'ignored in the cinder.conf file.')
  15.         del local_conf['/v2']
  16.     return paste.urlmap.urlmap_factory(loader, global_conf, **local_conf)
复制代码
在该方法中,如果 cinder.conf 中 enable_v1_api = False 则不加载 V1对应的app;如果 enable_v2_api = False 则不加载V2对应的app,否则三个都会被加载
加载 openstack_volume_api_v3,它对应的composite 是:
  1. [composite:openstack_volume_api_v3]
  2. use = call:cinder.api.middleware.auth:pipeline_factory
  3. noauth = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler noauth apiv3
  4. noauth_include_project_id = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler noauth_include_project_id apiv3
  5. keystone = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler authtoken keystonecontext apiv3
  6. keystone_nolimit = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler authtoken keystonecontext apiv3
复制代码
接着,cinder-api会通过调用keystone服务验证自身的身份,确保它有权访问Cinder的API。验证完成后,cinder-api会根据设置文件中的参数,启动一个web服务器,通常是基于Python的WSGI服务器,如uWSGI或Gunicorn。
到这里,osapi_volume 的 loading 就竣事了。在此过程中,各个 filter middleware 以及 WSGI Application 都被加载/注册,并被初始化。WSGI Server 在被启动后,开始等候 HTTP request,然后进入HTTP request 处理惩罚过程。
  1. [app:apiv3]
  2. paste.app_factory = cinder.api.v3.router:APIRouter.factory
复制代码
四.APIRouter
cinder/api/v3/router.py的APIRouter继承自:
cinder/api/openstack/_init_.py
APIRouters 利用一个 mapper 属性来保存为全部resource 建立的 mapping,该 mapper 是个ProjectManager 的实例。其集成关系是 ProjectMapper -> APIMapper -> routes.Mapper,它提供一个重要的方法 def match(self, url=None, environ=None) 来根据其 routes 来获取映射关系。
1)它被deploy loadapp调用, 然后它调用 ExtensionManager 的方法去获取各个Resource;保存 mapper,router,resource 等全部数据
2)它接受Middleware filters 处理惩罚过的 Request,交给 router (RoutesMiddleware) 去做 URL 匹配,然后交给匹配得到的 Resource 去做消息分发。
  1. class APIRouter(base_wsgi.Router):
  2.     """Routes requests on the API to the appropriate controller and method."""
  3.     ExtensionManager = None  # override in subclasses
  4.     @classmethod
  5.     def factory(cls, global_config, **local_config):
  6.         """Simple paste factory, :class:`cinder.wsgi.Router` doesn't have."""
  7.         return cls()
  8.     def __init__(self, ext_mgr=None):
  9.         if ext_mgr is None:
  10.             if self.ExtensionManager:
  11.                 ext_mgr = self.ExtensionManager()
  12.             else:
  13.                 raise Exception(_("Must specify an ExtensionManager class"))
  14.         mapper = ProjectMapper()
  15.         self.resources = {}
  16.         self._setup_routes(mapper, ext_mgr)
  17.         self._setup_ext_routes(mapper, ext_mgr)
  18.         self._setup_extensions(ext_mgr)
  19.         super(APIRouter, self).__init__(mapper)
复制代码
APIRouter 是 Cinder 中的核心类之一,它负责分发 HTTP Request 到其管理的某个 Resource:
它的几个重要属性
属性 resources 是个数组,用来保存全部的 Resource 的 Controller 类的instance;每个Resource 拥有一个该数组的数组项,好比 self.resources[‘versions’] = versions.create_resource() 会为 versions 核心资源创建一个 Resource 并保存到 resources 中。
属性 mapper 用来保存保存全部 resource, extension resource,resource extension 的 routes 供 RoutesMiddleware 利用。它实在是一张路由表。 每个表项表现一个 URL 和 controller 以及 action 的映射关系,每个 controller 就是 Resource 的一个实例。好比:
{‘action’: u’detail’, ‘controller’: <cinder.api.openstack.wsgi.Resource object at 0x7fa137be8950>, ‘project_id’: u’fa2046aaead44a698de8268f94759fc1’}
属性 routes 是 routes.middleware.RoutesMiddleware 的实例,它实在是一个 WSGI app,它利用 mapper 和 _dispatch_进行初始化。功能是根据 URL 得到 controller 和它的 action。
五,cinder资源类型
OpenStack 定义了两种类型的资源:
Core resource (Resource): 核心资源。核心资源的定义文件在 /cinder/api/v2/ 目次下面。Cinder 的核心资源包括:volumes,types,snapshots,limits等。
Extension resource: 扩展资源也是资源,利用同核心资源,只是它们的地位稍低。在 ./cinder/api/contrib/ 目次下面有许多文件,这些文件定义的都是扩展资源,例如 quotas.py 等。扩展资源又分为两种环境:
一种扩展资源本身也是一种资源,只是没那么核心,好比 os-quota-sets。对扩展资源的访问方法同核心资源,好比 PUT /v2/2f07ad0f1beb4b629e42e1113196c04b/os-quota-sets/2f07ad0f1beb4b629e42e1113196c04b
另一种扩展资源是对核心资源的扩展(Resource extension),包括对 action 的扩展和根本操纵的扩展,如今的 Cinder 中只有对 Resource 根本操纵的扩展,例如 SchedulerHints 是对 volumes 提供了扩展方法。一些扩展资源同时具备这两种功能。
1)根本操纵
根本操纵: 即核心资源和扩展资源拥有的最根本的操纵,利用 HTTP Method 好比 GET, PUT, POST,DELETE 等方法访问这些资源。
以 volumes 为例,其 Controller 类 VolumeController 定义了对 volumes 的 index,create,delete,show, update等。好比 GET /v2/{tenant-id}/volumes/detail。
2)action
Action: 资源扩展拥有的利用 @wsgi.action(alias) 装饰的方法,这些方法是Core Resource 的 CRUD 根本操纵不能满意的对资源的操纵。它们本身是看做 Core resource 的访问方法,只是访问方法和 CRUD 的访问方法不同。
好比: volumes 的扩展资源的 Controller 类 VolumeAdminController 定义的 os-migrate_volume_completion action:
  1. @wsgi.action('os-migrate_volume_completion')  #该方法的 alias 是 os-migrate_volume_completion            
  2. def _migrate_volume_completion(self, req, id, body)
复制代码
利用:利用 HTTP Post method,在 URL 中利用其 Core resource 的 alias 好比 ’volumes‘,‘action’ method,在Rquest body 中包罗 action 的 alias 和 参数。好比:
  1. POST /{tenant-id}/volumes/{volume-id}/action
  2. body: {"os-force_delete": null}
复制代码
3)extends
extends: 资源扩展利用 @wsgi.extends 装饰了的函数。extends 是对某个 Core Resource 的某个 CURD 方法好比 create, index,show,detail 等的扩展,你在利用标准 HTTP Method 访问 Core resource 时,可以附加 extension 信息,在 response 中你可以得到这些方法的output。尚不清楚实际应用场景。
例如: volumes 的资源扩展 SchedulerHints 的 Controller 类 SchedulerHintsController 定义了 volumes.create 方法的 extends 方法:
@wsgi.extends
def create(self, req, body)
以OS-SCH-HNT/SchedulerHints 扩展的 volumes 的 create 方法为例,其利用方法如下:
  1. POST http://9.123.245.88:8776/v2/2f07ad0f1beb4b629e42e1113196c04b/volumes
  2. body:
  3. {
  4. "volume": {
  5. "availability_zone": null,
  6. "source_volid": null,
  7. "description": null,
  8. "snapshot_id": null,
  9. "size": 1,
  10. "name": "hintvolume3",
  11. "imageRef": null,
  12. "volume_type": null,
  13. "metadata": {}
  14. },
  15. "OS-SCH-HNT:scheduler_hints": {"near": "2b7c42eb-7736-4a0f-afab-f23969a35ada"}
复制代码
Cinder 在接收到核心资源的 CRUD 访问 Request 时,在调用其根本功能的前面大概后面(根据extend 方法的具体实现决定是前面照旧后面),它会找到该核心资源的全部扩展了该方法的全部资源扩展,再分别调用这些资源扩展中的方法,这些方法的 output 会被插入到 volumes 的 create 方法产生的 response 内。
资源加载:
load extension
APIRourter 调用类 ExtensionManager 的 _init_ 方法, 它再调用其 _load_extensions 方法获取全部的 extensions,其过程如下:

  • 首先读取 cinder.conf 中有设置项 osapi_volume_extension, 其默认值为 cinder.api.contrib.standard_extensions。该设置项可以设置多个value。
  • 利用 importutils.import_class(‘cinder.api.contrib.standard_extensions’),然后调用方法其入口方法 load_standard_extensions,这方法又会调用 extensions.load_standard_extensions(ext_mgr, LOG, _path_, _package_)
  • 该方法会对 /cinder/api/contrib 目次下全部的 py 文件中的类调用 load_extension 来加载。
  • load 每个extension时,会执行每个 extension 类的 init 方法,并将实在例指针存放在 ExtensionManager 的 extensions 内,供以后调用。
六.注册路由
6.1 _setup_routes 为核心资源建立 routes
为每个Core Resource 类建立 URL 到其 Controller 类的 method 的映射规则
以 volumes Resource 为例,其 mapper 规则为:
  1. 1      self.resources['volumes'] = volumes.create_resource(ext_mgr)
  2. 2      mapper.resource("volume", "volumes", controller=self.resources['volumes'], collection={'detail': 'GET'}, member={'action': 'POST'})
复制代码
第1行:会该 Resource 创建一个 Resource 实例,初始化其controller 为VolumeController,调用 Resource 的 init 方法去获取 Controller 的 wsgi_actions 并保存(实在都是空的),Resource 实例会被保存到 APIRouter 的 resources 数组中以’volume‘ 为键值的一个数组项。实在 Resource 是个 wsgi Application,未来会调用它的 call 方法去做消息分发。
第2行:定义了一个将 URL 映射到 VolumeController method 的规则:
如果 URL 是 /volumes/{volume ID} 并且 HTTP method 是 POST,那么该 URL 会被映射到 action 方法,该 action 会被转化为 VolumeController的具体方法。
如果 URL 是 /volumes 并且HTTP Method 是 GET,那么映射到 detail 方法,其对应的是 VolumeConroller 的 detail 方法。
留意这里 Core resource 的 mapper 的处理惩罚单个Resource 的 action (member={‘action’: ‘POST’})和处理惩罚Resource聚集的 action (collection={‘detail’: ‘GET’})都是hard coded 的。 全部的映射规则都会被保存到 APIRouter 类的 mapper 属性中。
cinder/api/v3/router.py
  1. class APIRouter(cinder.api.openstack.APIRouter):
  2.     """Routes requests on the API to the appropriate controller and method."""
  3.     ExtensionManager = extensions.ExtensionManager
  4.     def _setup_routes(self, mapper, ext_mgr):
  5.         self.resources['versions'] = versions.create_resource()
  6.         mapper.connect("versions", "/",
  7.                        controller=self.resources['versions'],
  8.                        action='index')
  9.         mapper.redirect("", "/")
  10.         self.resources['volumes'] = volumes.create_resource(ext_mgr)
  11.         mapper.resource("volume", "volumes",
  12.                         controller=self.resources['volumes'],
  13.                         collection={'detail': 'GET', 'summary': 'GET'},
  14.                         member={'action': 'POST'})
复制代码
self.resources[‘volumes’] = volumes.create_resource(ext_mgr)
6.2 _setup_ext_routes为扩展资源建立 route
APIRourter 调用类 ExtensionManager 的 init 方法, 它再调用其 _load_extensions 方法获取全部的 extensions,其过程如下:

  • 首先读取 cinder.conf 中有设置项 osapi_volume_extension, 其默认值为 cinder.api.contrib.standard_extensions。该设置项可以设置多个value。
  • 利用 importutils.import_class(‘cinder.api.contrib.standard_extensions’),然后调用方法其入口方法 load_standard_extensions,这方法又会调用 extensions.load_standard_extensions(ext_mgr, LOG, path, package)
  • 该方法会对 /cinder/api/contrib 目次下全部的 py 文件中的类调用 load_extension 来加载。
4.ExtensionManager 类会遍历 contrib 目次下每个 py 文件,检查其中的 class 定义,判定它是哪一种资源,为每个 extension resource 对应的 Controller 类的每个 Member 和 Collection 方法建立映射规则 (mapper.resource(resource.collection, resource.collection, **kargs)),该规则同样保存在 APIRouter 的 mapper 属性中。
  1.     cfg.MultiStrOpt('osapi_volume_extension',
  2.                     default=['cinder.api.contrib.standard_extensions'],
  3.                     help='osapi volume extension to load')
复制代码
  1.     def _setup_ext_routes(self, mapper, ext_mgr):
  2.         for resource in ext_mgr.get_resources():
  3.             LOG.debug('Extended resource: %s',
  4.                       resource.collection)
  5.             wsgi_resource = wsgi.Resource(resource.controller)
  6.             self.resources[resource.collection] = wsgi_resource
  7.             kargs = dict(
  8.                 controller=wsgi_resource,
  9.                 collection=resource.collection_actions,
  10.                 member=resource.member_actions)
  11.             if resource.parent:
  12.                 kargs['parent_resource'] = resource.parent
  13.             mapper.resource(resource.collection, resource.collection, **kargs)
  14.             if resource.custom_routes_fn:
  15.                 resource.custom_routes_fn(mapper, wsgi_resource)
复制代码
6.3 _setup_extensions
APIRouter 类的 _setup_extensions 方法遍历每个extension,找到每个 extension 的 wsgi actions 和 wsgi extends,保存到其 resources 数组的该 extension 对应的 resource 数组项中。
通过以上几个步调,APIRouter 为全部的 Resouce 和 Extension resource 建立了URL 到 Resource 的 Controller 类的方法的映射规则并保存到 mapper 属性中,还保存了Resource extension 的方法到 Resource 中。
我们来看看 APIRouter 到底是怎么保存和利用这些信息的:
(0)以 os-extend 为例,URL 中利用 ‘action’, Request body 中带有具体的action: {“os-extend”: {“new_size”: 2}}
(1)APIRoutes 有个数组属性 resources,每一个数组项是每一个资源拥有的 class Resource(wsgi.Application) 实例。该实例的 wsgi_actions 属性保存该Resource 支持的全部 actions,每个 action 的数据是个 pair (alias,action 对应的 method 的Controller 类实例的地址)。以 volumes 为例,
  1. {'os-migrate_volume_completion': <bound method VolumeAdminController._migrate_volume_completion of <cinder.api.contrib.admin_actions.VolumeAdminController object at 0x7f459d256b90>>, 'os-reserve': <bound method VolumeActionsController._reserve of <cinder.api.contrib.volume_actions.VolumeActionsController object at 0x7f459d254d10>>, 'os-promote-replica': <bound method VolumeReplicationController.promote of <cinder.api.contrib.volume_replication.VolumeReplicationController object at 0x7f459d237d10>>, ...... 'os-attach': <bound method VolumeActionsController._attach of <cinder.api.contrib.volume_actions.VolumeActionsController object at 0x7f459d254d10>>}
复制代码
(2)Resource 还有个 wsgi_extensions 数组属性,它为每一个 Resource 的根本方法保存扩展的方法。以 volumes 的 detail 方法为例,它有两个扩展方法:
  1. [<bound method VolumeTenantAttributeController.detail of <cinder.api.contrib.volume_tenant_attribute.VolumeTenantAttributeController object at 0x7f459d3a5c90>>, <bound method VolumeReplicationController.detail of <cinder.api.contrib.volume_replication.VolumeReplicationController object at 0x7f459d237d10>>]
复制代码
(3)收到一个 action 后,RoutesMiddleware 根据 URL 找到 Resource 的地址,然后 Resource 从 request body 中取出 action 的 alias,然后查找该 pairs,得到真正的method。
(4)首先会查找wsgi_extensions,获取该 method 对应的全部扩展方法的Controller 类,分别调用其method; 然后再把 request dispatch 到该方法上得到其输出了。
  1. wsgi_actions['os-extend']: <bound method VolumeActionsController._extend of <cinder.api.contrib.volume_actions.VolumeActionsController object at 0x7f459d254d10>>
复制代码
(5)该 Resource 实例同样还利用属性 wsgi_action_extensions 保存其全部的 action extensions。
  1.     def _setup_extensions(self, ext_mgr):
  2.         for extension in ext_mgr.get_controller_extensions():
  3.             collection = extension.collection
  4.             controller = extension.controller
  5.             if collection not in self.resources:
  6.                 LOG.warning('Extension %(ext_name)s: Cannot extend '
  7.                             'resource %(collection)s: No such resource',
  8.                             {'ext_name': extension.extension.name,
  9.                              'collection': collection})
  10.                 continue
  11.             LOG.debug('Extension %(ext_name)s extending resource: '
  12.                       '%(collection)s',
  13.                       {'ext_name': extension.extension.name,
  14.                        'collection': collection})
  15.             resource = self.resources[collection]
  16.             resource.register_actions(controller)
  17.             resource.register_extensions(controller)
复制代码
末了,cinder-api将等候来自客户端的请求,当cinder-api接收到请求后,它会根据请求的方法(GET、POST、PUT、DELETE等)和URL路由确定请求要调用的具体处理惩罚步伐。
处理惩罚步伐会利用Cinder的API,与Cinder服务进行交互,以执行请求的操纵,如创建、删除、修改卷等。
处理惩罚步伐将根据请求的操纵类型和结果生成相应,以及相应的HTTP状态码和任何其他须要的相应头信息。
cinder-api将相应发送回客户端,并在需要时设置缓存头信息,以减少后续对相同资源的请求。
如果发生任何错误或异常,cinder-api将返回相应的HTTP错误码,并在相应主体中包罗错误信息。
当cinder-api收到终止信号时(例如,系统关机或重启),它将关闭web服务器,释放资源,并停止服务。
总的来说,Cinder-API的启动过程可以概括为加载设置文件,验证身份,启动web服务器,注册路由,处理惩罚请求和返回相应。
其他组件启动过程:
Cinder-scheduler
启动cinder-scheduler 会启动一个名为 cinder-scheduler 的 Service。与 cinder-api 的WSGI Service 不同的是,它需要创建 RPC 毗连,启动消耗者线程,然后等候队列消息,它的启动过程可以分为以下几个步调:
1.设置文件加载:Cinder-scheduler的设置文件通常位于/etc/cinder/cinder.conf,该文件包罗了全部设置选项的值,如数据库毗连、日记级别、调度策略等。
2.日记记录:Cinder-scheduler启动时会初始化日记记录功能,通常日记文件位于/var/log/cinder/scheduler.log。在日记中,您可以检察调度器的具体信息,包括启动时间、加载的设置文件以及利用的驱动步伐等。
3.RPC通信初始化:Cinder-scheduler通过远程过程调用(RPC)协议与其他Cinder组件进行通信,如Cinder-api和Cinder-volume。在启动时,Cinder-scheduler将初始化RPC通信所需的全部组件,如消息队列和RPC服务。
4.调度器驱动初始化:Cinder-scheduler会初始化所选的调度器驱动步伐,该驱动步伐决定了如何进行存储卷的调度和分配。Cinder提供了多种调度器驱动步伐,如过滤器和重量级调度器。
5.调度步伐启动:末了,Cinder-scheduler会启动调度步伐,并开始等候调度请求。调度步伐将监听来自Cinder-api的请求,并利用选定的调度器驱动步伐对请求进行处理惩罚。
在Cinder-scheduler启动后,它将不停监听来自Cinder-api和Cinder-volume的请求,并根据所选的调度器驱动步伐对这些请求进行处理惩罚和相应。
Cinder-volume
它的启动过程可以分为以下几个步调:
1.设置文件加载:Cinder-volume的设置文件通常位于/etc/cinder/cinder.conf,该文件包罗了全部设置选项的值,如数据库毗连、日记级别、存储驱动等。
2.日记记录:Cinder-volume启动时会初始化日记记录功能,通常日记文件位于/var/log/cinder/volume.log。在日记中,您可以检察卷管理器的具体信息,包括启动时间、加载的设置文件以及利用的驱动步伐等。
3.存储驱动初始化:Cinder-volume会初始化所选的存储驱动步伐,该驱动步伐决定了如何管理存储卷。Cinder提供了多种存储驱动步伐,如LVM、Ceph和NFS等。
4.与Cinder-api和Cinder-scheduler进行通信:Cinder-volume通过远程过程调用(RPC)协议与其他Cinder组件进行通信,如Cinder-api和Cinder-scheduler。在启动时,Cinder-volume将初始化RPC通信所需的全部组件,如消息队列和RPC服务。
5.卷管理器启动:末了,Cinder-volume会启动卷管理器,并开始等候来自Cinder-api的请求。卷管理器将监听来自Cinder-api的请求,并利用选定的存储驱动步伐对请求进行处理惩罚。
在Cinder-volume启动后,它将不停监听来自Cinder-api的请求,并根据所选的存储驱动步伐对这些请求进行处理惩罚和相应。它可以处理惩罚创建、删除、扩容、快照等操纵,从而使存储卷得到有效的管理和维护。
Cinder-volume执行底层存储的具体技术取决于所利用的存储驱动步伐,Cinder支持多种存储后端,每种存储后端可能利用不同的底层存储技术。以下是Cinder支持的常见存储后端以及它们所支持的底层存储技术:
LVM(Logical Volume Manager):LVM是一种逻辑卷管理器,可以在物理磁盘上创建逻辑卷。Cinder通过LVM存储驱动步伐支持利用LVM作为底层存储。
Ceph:Ceph是一种分布式存储系统,支持对象存储、块存储和文件存储。Cinder通过Ceph存储驱动步伐支持利用Ceph作为底层存储。
NFS(Network File System):NFS是一种网络文件系统协议,可以在不同的盘算机之间共享文件。Cinder通过NFS存储驱动步伐支持利用NFS作为底层存储。
iSCSI(Internet Small Computer System Interface):iSCSI是一种网络存储协议,可以将存储设备映射到远程盘算机上。Cinder通过iSCSI存储驱动步伐支持利用iSCSI作为底层存储。
GlusterFS:GlusterFS是一种分布式文件系统,支持对象存储、块存储和文件存储。Cinder通过GlusterFS存储驱动步伐支持利用GlusterFS作为底层存储。
总之,Cinder-volume支持多种不同的存储后端和存储驱动步伐,每种存储驱动步伐可能利用不同的底层存储技术。
参考链接:https://www.cnblogs.com/sammyliu/p/4272611.html

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

不到断气不罢休

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表