注: 这是本人看cinder代码时候的一些脉络整理记录。不做具体详细的逐行分析。
参考文档: http://blog.csdn.net/gaoxingnengjisuan/article/category/1853287
Cinder服务起来以后,会有三个服务,api,schedule,volume。这次主要是通过volume创建一个卷的操作来理解cinder代码的跳转运作。
大致流程:
APIRouter【cinder.api.v2.router】
→ VolumeController:create 【cinder.api.v2.volumes】
→ volume_api:create【config里面的volume_api_class配置,默认是cinder.volume.api.API】
→ create_volume : get_flow 【cinder.volume.flows.api.create_volume】
→ 构建flow,把相应的task放到flow里面执行
→ 执行flow,VolumeCastTask
→ VolumeCastTask : execute → _cast_create_volume
→ _cast_create_volume : 如果不存存在可以用的主机,则使用调度器进行主机的选择①,否则直接使用volume的主机创建②。
①→ scheduler_rpcapi.create_volume 【cinder.scheduler.rpcapi】发送rpc消息cast
→ manager接受rpc cast消息【cinder.scheduler.manager】,相同的方法名 create_volume
→ create_volume.get_flow 【cinder.scheduler.flows】,调用数据库【cinder.db】,调用driver【config里scheduler_driver=cinder.scheduler.filter_scheduler.FilterScheduler】。创建flow,给flow加入task
→ 执行flow,ScheduleCreateVolumeTask:execute
→ 调用driver的具体实现来进行过滤:schedule_create_volume【cinder.scheduler.filter_scheduler.FilterScheduler】,如果没有合适主机则抛出异常。
→ 调用 volume_rpcapi.create_volume 发送rpc cast消息,转到②
②→ volume_rpcapi.create_volume【cinder.volume.rpcapi】发送rpc消息cast
→ manager接受rpc cast消息【cinder.volume.manager】,通过create_volume接收
→ create_volume.get_flow【cinder.volume.flows.manager.create_volume】,这里就是实际创建一个卷的代码了,CreateVolumeFromSpecTask。
→ CreateVolumeFromSpecTask中可以根据volume_type 来选择创建卷的方式(raw,snapshot,image,源卷)
APIRouter
在CLI中输入cinder create
命令后,可以创建一个卷。首先,命令通过RESTful API会在 cinder.api.v2.router.APIRouter
中路由到所对应的controller方法捕获。create命令对应的就是里面的create方法。
#cinder/api/v2/router.py
self.resources['volumes'] = volumes.create_resource(ext_mgr)
mapper.resource("volume", "volumes",
controller=self.resources['volumes'],
collection={'detail': 'GET'},
member={'action': 'POST'})
wsgi router 的 Resource
router的Resource会找到相应的cinder.api.v1.volumes.VolumeController里面对应的create方法:VolumeController:create
- 在这个类里面的create方法中,主要是通过req和body解析出建立卷所需要的参数。
#cinder/api/v1/volumes.py
@wsgi.response(202)
@wsgi.serializers(xml=VolumeTemplate)
@wsgi.deserializers(xml=CreateDeserializer)
def create(self, req, body):
# ......
# 前面都是处理req请求的,主要是解析建立卷所需要的参数
# 最后通过volume_api的create方法创建
# volume_api是通过cinder.conf配置的
# 默认是cinder.volume.api.APIvolume_api_class
new_volume = self.volume_api.create(context,
size,
volume.get('display_name'),
volume.get('display_description'),
**kwargs)
Cinder/volume/api.API
跳到这一步,分析构建卷的参数,构建taskflow模式。
这里通过调用taskflow模式,通过get_flow跳转到cinder.volume.flows.api.create_volum中
#Cinder/volume/api.py
try:
flow_engine = create_volume.get_flow(self.scheduler_rpcapi,
self.volume_rpcapi,
self.db,
self.image_service,
availability_zones,
create_what)
except Exception:
msg = _('Failed to create api volume flow.')
LOG.exception(msg)
raise exception.CinderException(msg)
Taskflow模式
- 构建flow,把相应的task放到flow里面执行
- 执行flow,VolumeCastTask
#cinder.volume.flows.api.create_volum.py
def get_flow(scheduler_rpcapi, volume_rpcapi, db_api,
image_service_api, availability_zones,
create_what):
"""Constructs and returns the api entrypoint flow.
This flow will do the following:
1. Inject keys & values for dependent tasks.
2. Extracts and validates the input keys & values.
3. Reserves the quota (reverts quota on any failures).
4. Creates the database entry.
5. Commits the quota.
6. Casts to volume manager or scheduler for further processing.
"""
flow_name = ACTION.replace(":", "_") + "_api"
api_flow = linear_flow.Flow(flow_name)
api_flow.add(ExtractVolumeRequestTask(
image_service_api,
availability_zones,
rebind={'size': 'raw_size',
'availability_zone': 'raw_availability_zone',
'volume_type': 'raw_volume_type'}))
api_flow.add(QuotaReserveTask(),
EntryCreateTask(db_api),
QuotaCommitTask())
# This will cast it out to either the scheduler or volume manager via
# the rpc apis provided.
api_flow.add(VolumeCastTask(scheduler_rpcapi, volume_rpcapi, db_api))
# Now load (but do not run) the flow using the provided initial data.
return taskflow.engines.load(api_flow, store=create_what)
这里实际做到创建卷这个动作的就是在VolumeCastTask 这个task中,通过里面的execute调用到_cast_create_volume方法,这里的主要作用就是会判断,如果已经确定了不存在可以用的主机,就使用调度器调度的方法找到主机后再通过volume的driver来做具体的创建操作①,如果已经存在可用主机,就直接调用driver来创建卷②。
① 通过scheduler模块找到合适主机
- 上一步就会跳转到代码:cinder.scheduler.rpcapi.py来发送消息
- scheduler_rpcapi.create_volume 发送rpc cast消息。
- 一般来说,一个模块的rpc消息,都是通过这个模块的manager.py里面的同名方法,create_volume来接收的。这里就是cinder.scheduler.manager.py
#cinder.scheduler.manager.py
def create_volume(self, context, topic, volume_id, snapshot_id=None,
image_id=None, request_spec=None,
filter_properties=None):
self._wait_for_scheduler()
try:
#这里的create_volume是cinder.scheduler.flows里面的create_volume.py
flow_engine = create_volume.get_flow(context,
db, self.driver,
request_spec,
filter_properties,
volume_id,
snapshot_id,
image_id)
except Exception:
msg = _("Failed to create scheduler manager volume flow")
LOG.exception(msg)
raise exception.CinderException(msg)
with flow_utils.DynamicLogListener(flow_engine, logger=LOG):
flow_engine.run()
接着在上面的get_flow继续进入taskflow模式,构建flow。db就是cinder的数据库: cinder.db;self.driver是通过cinder.conf配置的scheduler_driver确定的,默认是cinder.scheduler.filter_scheduler.FilterScheduler
其中在get_flow中, 主要用于调度作用的是ScheduleCreateVolumeTask这个task,它的execute方法中,就会通过调用schedule_driver里面的schedule_create_volume来执行调度的方法找到适用的主机。
找到主机以后再通过volume_rpcapi来发送cast信息给volume来执行创建操作②
② 通过volume模块来创建卷
cinder.volume.manager 接收rpc消息,create_volume
创建taskflow来执行具体的创建操作。主要就是cinder.volume.flows.manager.create_volume 里面的
CreateVolumeFromSpecTask方法。CreateVolumeFromSpecTask可以根据volume_type来选择创建卷的方式,一共有四种raw,snapshot,image,源卷。