当前位置: 首页 > 工具软件 > Cinder > 使用案例 >

cinder-api的启动过程

闻法
2023-12-01

依赖模块

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

import sys
from cinder.cmd.api import main

if __name__ == "_main__":
    main()

二.启动WSGI Service

def main() -> None:
	...
	...

    rpc.init(CONF)
    launcher = service.process_launcher()
    server = service.WSGIService('osapi_volume') # # 使用 Paste delopment 方式 加载 osapi_volume 这个 application,这过程中包括加载各 middleware app
    launcher.launch_service(server, workers=server.workers) #真正启动service
    launcher.wait()

三.PasteDeploy生成WSGIService和WSGIApplication
cinder/service.py

class WSGIService(service.ServiceBase):
    """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(CONF)
        self.app = self.loader.load_app(name)
        self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")
        self.port = getattr(CONF, '%s_listen_port' % name, 0)
        self.use_ssl = getattr(CONF, '%s_use_ssl' % name, False)
        self.workers = (getattr(CONF, '%s_workers' % name, None) or
                        processutils.get_worker_count())
        if self.workers and self.workers < 1:
            worker_name = '%s_workers' % name
            msg = (_("%(worker_name)s value of %(workers)d is invalid, "
                     "must be greater than 0.") %
                   {'worker_name': worker_name,
                    'workers': self.workers})
            raise exception.InvalidConfigurationValue(msg)
        setup_profiler(name, self.host)

        self.server = wsgi.Server(CONF,
                                  name,
                                  self.app,
                                  host=self.host,
                                  port=self.port,
                                  use_ssl=self.use_ssl)

3.1 self.app = self.loader.load_app(name)
oslo_service/wsgi.py

    def load_app(self, name):
        """Return the paste URLMap wrapped WSGI application.

        :param name: Name of the application to load.
        :returns: Paste URLMap object wrapping the requested application.
        :raises: PasteAppNotFound

        """
        try:
            LOG.debug("Loading app %(name)s from %(path)s",
                      {'name': name, 'path': self.config_path})
            return deploy.loadapp("config:%s" % self.config_path, name=name)
        except LookupError:
            LOG.exception("Couldn't lookup app: %s", name)
            raise PasteAppNotFound(name=name, path=self.config_path)

其中config_path的定义如下:

config_path = conf.api_paste_config
if not os.path.isabs(config_path):
    self.config_path = conf.find_file(config_path)
elif os.path.exists(config_path):
    self.config_path = config_path

追根朔源:

os.path.dirname(__file__), '..', '..', '..',
'etc/cinder/api-paste.ini'))
CONF.api_paste_config = default_conf

得到:

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如下:

#############
# OpenStack #
#############

[composite:osapi_volume]
use = call:cinder.api:root_app_factory
/: apiversions
/healthcheck: healthcheck
/v3: openstack_volume_api_v3

[composite:openstack_volume_api_v3]
use = call:cinder.api.middleware.auth:pipeline_factory
noauth = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler noauth apiv3
noauth_include_project_id = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler noauth_include_project_id apiv3
keystone = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler authtoken keystonecontext apiv3
keystone_nolimit = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler authtoken keystonecontext apiv3

[filter:http_proxy_to_wsgi]
paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory

[filter:cors]
paste.filter_factory = oslo_middleware.cors:filter_factory
oslo_config_project = cinder

[filter:faultwrap]
paste.filter_factory = cinder.api.middleware.fault:FaultWrapper.factory

[filter:osprofiler]
paste.filter_factory = osprofiler.web:WsgiMiddleware.factory

[filter:noauth]
paste.filter_factory = cinder.api.middleware.auth:NoAuthMiddleware.factory

[filter:noauth_include_project_id]
paste.filter_factory = cinder.api.middleware.auth:NoAuthMiddlewareIncludeProjectID.factory

[filter:sizelimit]
paste.filter_factory = oslo_middleware.sizelimit:RequestBodySizeLimiter.factory

[app:apiv3]
paste.app_factory = cinder.api.v3.router:APIRouter.factory

[pipeline:apiversions]
pipeline = request_id cors http_proxy_to_wsgi faultwrap osvolumeversionapp

[app:osvolumeversionapp]
paste.app_factory = cinder.api.versions:Versions.factory

[pipeline:healthcheck]
pipeline = request_id healthcheckapp

[app:healthcheckapp]
paste.app_factory = oslo_middleware:Healthcheck.app_factory
backends = disable_by_file
disable_by_file_path = /etc/cinder/healthcheck_disable

##########
# Shared #
##########

[filter:keystonecontext]
paste.filter_factory = cinder.api.middleware.auth:CinderKeystoneContext.factory

[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory

[filter:request_id]
paste.filter_factory = cinder.api.middleware.request_id:RequestId.factory

3.2 deploy.loadapp(“config:%s” % self.config_path, name=name)
入口在文件的 [composite:osapi_volume]部分:
loadapp,它可以用来从 config 配置文件或者 Python egg 文件加载 app(包括middleware/filter和app),它只要求 app 给它提供一个入口函数,该函数通过配置文件告诉Paste depoly loader。

use = call:cinder.api:root_app_factory

源码如下:
cinder/api/_init_.py

def root_app_factory(loader, global_conf, **local_conf):
    # To support upgrades from previous api-paste config files, we need
    # to check for and remove any legacy references to the v1 or v2 API
    if '/v1' in local_conf:
        LOG.warning('The v1 API has been removed and is no longer '
                    'available. Client applications should be '
                    'using v3, which is currently the only supported '
                    'version of the Block Storage API.')
        del local_conf['/v1']

    if '/v2' in local_conf:
        LOG.warning('The v2 API has been removed and is no longer available. '
                    'Client applications must now use the v3 API only. '
                    'The \'enable_v2_api\' option has been removed and is '
                    'ignored in the cinder.conf file.')
        del local_conf['/v2']

    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 是:

[composite:openstack_volume_api_v3]
use = call:cinder.api.middleware.auth:pipeline_factory
noauth = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler noauth apiv3
noauth_include_project_id = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler noauth_include_project_id apiv3
keystone = request_id cors http_proxy_to_wsgi faultwrap sizelimit osprofiler authtoken keystonecontext apiv3
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 处理过程。

[app:apiv3]
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 去做消息分发。

class APIRouter(base_wsgi.Router):
    """Routes requests on the API to the appropriate controller and method."""
    ExtensionManager = None  # override in subclasses

    @classmethod
    def factory(cls, global_config, **local_config):
        """Simple paste factory, :class:`cinder.wsgi.Router` doesn't have."""
        return cls()

    def __init__(self, ext_mgr=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)
        self._setup_ext_routes(mapper, ext_mgr)
        self._setup_extensions(ext_mgr)
        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:

@wsgi.action('os-migrate_volume_completion')  #该方法的 alias 是 os-migrate_volume_completion            
def _migrate_volume_completion(self, req, id, body) 

使用:使用 HTTP Post method,在 URL 中使用其 Core resource 的 alias 比如 ’volumes‘,‘action’ method,在Rquest body 中包含 action 的 alias 和 参数。比如:

POST /{tenant-id}/volumes/{volume-id}/action
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 方法为例,其使用方法如下:

POST http://9.123.245.88:8776/v2/2f07ad0f1beb4b629e42e1113196c04b/volumes
 
body:

{
"volume": {
"availability_zone": null,
"source_volid": null,
"description": null,
"snapshot_id": null,
"size": 1,
"name": "hintvolume3",
"imageRef": null,
"volume_type": null,
"metadata": {}
},

"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,其过程如下:

  1. 首先读取 cinder.conf 中有配置项 osapi_volume_extension, 其默认值为 cinder.api.contrib.standard_extensions。该配置项可以设置多个value。

  2. 使用 importutils.import_class(‘cinder.api.contrib.standard_extensions’),然后调用方法其入口方法 load_standard_extensions,这方法又会调用 extensions.load_standard_extensions(ext_mgr, LOG, _path_, _package_)

  3. 该方法会对 /cinder/api/contrib 目录下所有的 py 文件中的类调用 load_extension 来加载。

  4. load 每个extension时,会执行每个 extension 类的 init 方法,并将其实例指针存放在 ExtensionManager 的 extensions 内,供以后调用。

六.注册路由

6.1 _setup_routes 为核心资源建立 routes
为每个Core Resource 类建立 URL 到其 Controller 类的 method 的映射规则
以 volumes Resource 为例,其 mapper 规则为:

1      self.resources['volumes'] = volumes.create_resource(ext_mgr)
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


class APIRouter(cinder.api.openstack.APIRouter):
    """Routes requests on the API to the appropriate controller and method."""
    ExtensionManager = extensions.ExtensionManager

    def _setup_routes(self, mapper, ext_mgr):
        self.resources['versions'] = versions.create_resource()
        mapper.connect("versions", "/",
                       controller=self.resources['versions'],
                       action='index')

        mapper.redirect("", "/")

        self.resources['volumes'] = volumes.create_resource(ext_mgr)
        mapper.resource("volume", "volumes",
                        controller=self.resources['volumes'],
                        collection={'detail': 'GET', 'summary': 'GET'},
                        member={'action': 'POST'})

self.resources[‘volumes’] = volumes.create_resource(ext_mgr)

6.2 _setup_ext_routes为扩展资源建立 route

APIRourter 调用类 ExtensionManager 的 init 方法, 它再调用其 _load_extensions 方法获取所有的 extensions,其过程如下:

  1. 首先读取 cinder.conf 中有配置项 osapi_volume_extension, 其默认值为 cinder.api.contrib.standard_extensions。该配置项可以设置多个value。

  2. 使用 importutils.import_class(‘cinder.api.contrib.standard_extensions’),然后调用方法其入口方法 load_standard_extensions,这方法又会调用 extensions.load_standard_extensions(ext_mgr, LOG, path, package)

  3. 该方法会对 /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 属性中。

    cfg.MultiStrOpt('osapi_volume_extension',
                    default=['cinder.api.contrib.standard_extensions'],
                    help='osapi volume extension to load')
    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)

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 为例,

{'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 方法为例,它有两个扩展方法:

[<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 到该方法上得到其输出了。

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。

    def _setup_extensions(self, ext_mgr):
        for extension in ext_mgr.get_controller_extensions():
            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',
                            {'ext_name': extension.extension.name,
                             'collection': collection})
                continue

            LOG.debug('Extension %(ext_name)s extending resource: '
                      '%(collection)s',
                      {'ext_name': extension.extension.name,
                       'collection': collection})

            resource = self.resources[collection]
            resource.register_actions(controller)
            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

 类似资料: