0 本文概述
本文将对通过终端cli在Ceph集群中创建image的流程进行解析
1 流程时序图
2 代码走读
2.1 命令行处理:/src/tools/
2.1.1 rbd.cc
rbd.cc提供main函数入口,作为主程序的入口,调用shell.cc中的execute()函数,对指令进行解析。
2.1.2 shell.cc
shell.cc解析命令行,并通过封装的Action类调用对应的文件,对应当前目录下的Action文件。对于create而言,这里调用create.cc文件。
2.1.3 Action/create.cc
在execute()函数中调用librados和IoContext并初始化,之后调用do_create().
2.2 librbd层:src/librbd/
2.2.1 librbd.cc
librbd提供相应的接口,具体实现在interval.cc中,本文中create image操作使用create4()实现对create()接口的封装。
通过create()接口,调用interval.cc中create()函数,interval.cc提供这些接口的实现(line 820 to line 877).
在create()函数中,librbd获取image id和相应的参数,并根据format的新旧使用调用不同的接口。v1现在已被废弃。使用v2来完成image create。
对于新format信息,调用当前目录中image目录中的CreateRequest.cc中send()函数进行调用。
2.2.2 /src/librbd/image/CreateRequest.cc
librbd中,将每个操作的具体实现都封装成了<operation>Request.cc/h文件,operation对应每个具体的操作,如当前的create操作的具体实现就是createRequest.cc/h中。
如上,头文件给出了类的定义,send实现在.cc文件中。头文件还给出了CreateRequest.cc实现image create的函数调用流程图。
函数声明方面,函数和回调函数成对声明,回调函数以handle_开头区分。
上面的函数实现中可以看出,send()函数调用validate_pool()函数,在validate_pool()函数中,封装并调用回调函数handle_validate_pool()函数,校验rbd_directory对象是否存在,下一步的操作在回调函数中调用。其中aio_operate()实际执行回调函数。
validate_overwrite()作用是校验rbd_info的内容。
在handle_validate_overwrite()函数中,如果rbd_info存在并且内容为“overwrite validated”, 直接进入下一状态,调用create_id_object(),创建rbd_id.<image_name>对象。
create_id_object()函数中,先调用了cls_client::set_id()创建rbd_id,之后又使用回调函数,这里个人观点为cls的函数需要回调函数触发。在回调函数中,调用add_image_to_directory()函数进入下一状态。
add_image_to_directory()作用为在rbd_directory对象中加入该image的id和name。
negotiate_features()中,会首先获取所有的feature,之后触发回调函数,将返回的feature decode到all_features中,之后调用create_image(),进入下一状态。
在create_image()中,通过cls注册的函数创建rbd_header对象,并设置omap中的值,调用回调函数完成创建。
之后代码遵循类似的流程,都是通过cls_client注册的函数 + 回调函数完成该状态功能,并调用函数,进入下一状态。调用的函数顺序为:
set_strip_unit_count() -> handle_strip_unit_count() -> object_map_resize() -> handle_object_map_resize() -> fetch_mirror_mode() -> handle_fetch_mirror_mode() -> journal_create() -> handle_journal_create() -> mirror_image_enable() -> handle_mirror_image_enable()
在一系列的调用完成后,调用complete()函数,传入的参数为0。
在complete()函数中,释放数据对象上下文,调用CreateRequest回调函数,完成步骤。
2.2.3 cls/rbd/cls_rbd_client.cc
在前文的Request调用中,使用回调函数进入下一状态之前,会先调用cls模块进行注册,cls模块针对rbd的实现在cls_rbd_client.cc文件中。
cls_client模块负责注册部分元数据操作。从调用的代码可以看出,cls_client下函数不会直接执行,而通过librados::ObjectOperation的exec()执行。在下一节可以看到,cls_client调用的操作在最终的OSDC层完成函数注册,但并不会实际调用。
需要注意的是,具体到image的创建,实际上只会记录一些image的基本信息。比如创建元数据对象rbd_id.foo和rbd_header.foo,对于image的真正数据对象rbd_data*,根本不会创建,这是因为ceph选择thin-provisioning这种凡事,可以做到秒级创建快设备,后段也可以超额分配容量。
2.3 librados层:src/librados/
librbd中cls_client的注册和回调函数的触发都在librados中实现。
2.3.1 librados.cc / librados.h
librados组件提供了cls_client注册函数需要的ObjectOperation的定义和接口实现,和IoctxImpl相关函数接口的封装。
cls_client调用的exec()函数的实现如下,函数中初始化ObjectOperationImpl实例,并调用其成员变量o的call()函数。
代码中成员变量o的类型ObjectOperation来自雨OSDC层的Objecter组件,具体实现将在OSDC层中进行进一步讨论。
librbd中,真正触发回调函数是通过aio_operate()完成的,librados中提供了该接口,具体实现是封装了IoctxImpl的aio_operate()接口,最终实现是在IoctxImpl中完成。
2.3.2 IoctxImpl.cc
IoctxImpl中,::ObjectOperation op实际上是引用的src/osdc/Objecter.cc里面定义的类,初始化之后,通过op将消息发送给osd。真正使用osdc中的Objecter.cc中的组件的地方是operator()和aio_operate()函数。
2.4 OSDC 层:src/osdc
osdc层直接与osd进行通信,调用objecter.cc/objecter.h组件来实现消息的发送。
2.4.1 objecter.cc
objecter.cc提供对上层消息的封装,并将这些消息发送给osd,完成各项操作。
在librados层,cls_client调用的ObjectOperation的定义在objector.h中。
代码中各项成员变量的含义为
out_bl: 用于存放ops每个操作的输出内容
out_handler: 存放ops每个操作完成后执行的回调函数
out_rval: 存放ops每个操作的返回值
librados层中注册函数时调用的call函数的定义如下
add_op()为其核心操作,作用是将ops数组中增加一个op,但不执行操作。
最终的执行操作是通过aio_operate()函数在osdc层调用的op_submit()函数完成的。
从上述代码可以看出,在Objecter类中,遵循op_submit() -> _op_submit() -> _send_op()的调用顺序。
3 备注
[注] 参考链接ceph-librbd-create-image.html
[注] 从相关资料描述来看,RadosClient是其核心管理类,处理Rados层和Pool层管理,但是在当前的解析中并未体现RadosClient的作用,需要进一步的研究和分析。