Nova数据库模块的使用方法和开发
Nova DB简介
nova.db在F、G、H版本的差异不大,但是从G版开始加入了conductor,不允许compute直接访问数据库,所以在compute的代码里调用数据库需要通过conductor。(PS:现在可以在计算节点配置conductor session的use_local选项来决定是否由compute服务直接访问数据库)
如果要增加一个新的功能,而且这个功能需要操作数据库,在操作数据库这个方面一般分为两个步骤:
一、db模块中的内容编写,主要包括数据表的创建、功能及api的编写;
二、compute模块中,对db提供的api调用方法的编写。
推荐优先学习sqlalchemy模块
openstack环境版本:H版(G版类似,F、E版的区别会有说明)
DB模块中的内容编写
描述数据表
nova数据库的创建:
第一次同步nova数据库时的操作:
1 | nova-manage db sync |
这个指定的代码在 nova.db.sqlalchemy.migration.py 中:
1 | def db_sync(version=None): |
因为 version=None ,所以走到 upgrade 里,它最后会找到 nova.db.sqlalchemy.migrate_repo.versions.133_folsom.py 这个文件里面的 upgrade 方法,代码很长,这里仅截取创建数据表相关的部分:
首先,写一个数据表的结构(以下这个就是 nova.instances 的表结构):
1 | instances = Table('instances', meta, |
创建表:
1 | tables = [……, instances, ……] |
也就是说,想在nova-manage db sync
的时候创建一个新的表,就需要现在 upgrade 方法中加入一个表的结构描述,再把这个表的名称加入tables这个list元素中。
model的建立
假设已经创建了这个表,现在需要的是为这个表编写 api 。
打开 nova.db.sqlalchemy.models.py ,这个文件中是用来定义数据表模型的,把一个数据表转化成一个类。
以 snapshot 的 model 为例:
1 | class Snapshot(BASE, NovaBase): |
实际上就是把数据库里的字段一个个定义出来,主要有两个部分:__tablename__ 和字段的定义。这个类是继承了BASE和NovaBase这两个类,所以编写起来会很方便,因为NovaBase类中已经提供了一些基本的方法和实用的字段(可以在nova.openstack.common.db.sqlalchemy.models.py文件中查看NovaBase所继承的三个类),比如:
1 | class TimestampMixin(object): |
这个类提供了创建时间和更新时间(注意,NovaBase所提供的字段也需要写入133_folsom.py中表结构的定义里)。
api的编写
拥有model之后,就需要提供操作这个 model 的 api ,进入 nova.db.sqlalchemy.api.py 。
api 一般分为创建、查询、更新、删除,以 flavor 的一些操作为例:
创建
1 |
|
@require_admin_context 这是一个修饰函数,相当于一个函数加工,这里的这个修饰作用是检查 context ,看是否为 admin 权限。这个可以自定义,一般使用 @require_context ,意味着需要传入 context 才可以执行。
1 | instance_type_ref = models.InstanceTypes() |
这里创建了一个 models中InstanceTypes 类的实例,通过 instance_type_ref.update(values) 为实例添加一个新的记录( update 这个方法就是由 NovaBase 这个类提供的,非常方便)。然后通过 instance_type_ref.save() 把这条记录存入数据表当中( save 也是由 NovaBase 这个类提供的,可以参考 nova.openstack.common.db.sqlalchemy.models.py 中 ModelBase 这个类)。
创建的流程总结如下:
需要传入 context 和 values (这个是一个包含字段与字段值的字典元素)
创建一个数据表 model 的实例( somemodel_ref = models.SomeModel() )
将 values 更新到实例中( somemodel_ref.update(values) )
存入数据表中( somemodel_ref.save() )
查询
1 |
|
model_query 是实际执行查询的函数,它的作用是先获取一个数据库的 session (这是 sqlalchemy 定义的一个实例,可以在 nova.openstack.common.db.sqlalchemy.session.py 中查看 get_session 这个函数),返回一个 query (这是通过 session 实例的 query 方法,根据传入的参数查询数据表后获取的返回数据)。
在编写查询 api 的时候可以不用关心这个。主要的是这个函数的编写:
1 | def _instance_type_get_query(context, session=None, read_deleted=None): |
model_query 需要传入 context 、 model (模型的名字)、 session ,还有一些参数如 read_deleted ( read_deleted 值为”yes”或者”no”,用于是选择否获取 deleted 为 true 的记录,因为 openstack 几乎不删除记录,只是把记录的 deleted 值从0改成1)
一些完整的实例:
获取 SomeModel 中 id 字段值为 some_id 的第一个没有被删除的数据:
1 | result = model_query(context, models.SomeModel, session=session, |
获取 SomeModel 中 size 字段值为 some_size 的第一个没有被删除的数据:
1 | result = model_query(context, models.SomeModel, session=session, |
获取 SomeModel 中 id 字段值为 some_id 的所有没有被删除的数据:
1 | result = model_query(context, models.SomeModel, session=session, |
大概就是这个样子。
查询的流程总结如下:
根据 model_query 编写一个正确的查询语句
创建 filter 或者不创建
选择 all 或者 first (其实也是列表和字符串的区别)
更新
1 |
|
更新这个比较简单,和创建很像,区别是创建方法中的 some_ref 是 models.py 中数据模型的实例,而更新方法中的 some_ref 是通过查询得到的一个 model_query 实例。
这个方法就不总结了。
删除
1 |
|
删除这个也比较容易理解,查询到需要删除的那个记录,把 deleted 更新为 True (或者1等等布尔值为 true 的值)。
这个也不总结了。
把api封装一下
进入 nova.db.api.py,将在 nova.db.sqlalchemy.api.py 中编写的 api 加入这个文件,示例:
1 | def some_create(context, values): |
到这里,在 nova.db 中的编程就基本结束了
compute模块中的调用方法编写
先说E、F版的 nova.compute.manager.py 中调用数据库的方法:
通过 Manager 类就可以导入 db ,只用使用 self.db.some_api 就能直接掉 nova.db.api 里的方法,非常方便T_T
再说G、H版中通过 conductor 来调用 nova.db :
当你要调用 nova.db.api 中的方法时,需要在以下几个文件中添加相应的方法。
示例,现在 nova.db.api 中有这么一个方法: some_create(context, values) 用于创建某条记录,在 nova.compute.manager.py 中一个功能会使用到它,这个功能里就必须有这么一条语句:
1 | self.conductor_api.some_create(ontext, values) #(当然在这里无论是函数名还是参数都可以随意,除了context) |
它会调入 nova.conductor.api.py 中 LocalAPI 类的 some_create 这个方法( LocalAPI 代表这个功能的操作在本计算节点执行) LocalAPI 类中 some_create 方法可以这样编写:
1 | def some_create(self, context, values): |
接着调入 nova.conductor.rpcapi.py 中 ConductorAPI 类的 some_create 方法,可以这么编写:
1 | def some_create(self, context, values): |
这里 conductor 还调用了 rpc ,也就是说这个创建数据表中记录的动作通过消息发回了控制节点,被控制节点指配给 nova.conductor.manager.py 中 ConductorManager 类的 some_create 方法来执行。
在这个方法里,就可以做和E、F版 nova.compute.manager.py 中一样的操作了,就是直接调用 nova.db.api.py 中的方法。
1 | def some_create(self, context, values): |
这样基本就完成了一个功能在操作 nova 数据库方面的编程。