OpenStack现在已成为开源云平台中的明星项目,获得普遍关注。OpenStack的优秀出众依赖于众多开发者的努力,在享受其带来的便利与快捷的同时,为其作一份贡献也是一个开发者的义务。 在前段时间的OpenStack的测试过程当中,我发现Nova项目中的一个Bug,因而向社区提交了Bug报告,并提交代码修复了该Bug,从提交报告到代码入库经历近一月,下面重现整个过程。python
Nova中的虚拟机软删除(soft-delete)功能,是指在一段时间内,仅将数据库中的某虚拟机记录作一个标记(status='SOFT-DELETE'),而后将虚拟化平台(kvm等)中对应的虚拟机实例置为关机状态,当超过某一时间段后才将虚拟机实例真正删除;该功能为云平台用户提供了“后悔时间”,能够在必定程度上挽回误操做。默认状况下,软删除功能是关闭的,其开启方式是在nova配置文件中添加"reclaim_instance_interval"选项,并将其值设置为"后悔时间"的毫秒数。git
在描述具体Bug前,须要对openstack中的用户管理方面的基本概念简单介绍一下。github
上图是openstack用户模型的简化版本,为了便于理解将不属于keystone管理的quota也拿了过来。数据库
Bug就与软删除相关。具体场景是这样的:假设OpenStack中有两个项目和两个用户:普通项目A其用户a,管理员项目Admin其用户为admin(用户管理相关概念能够查阅keystone文档),用户a不慎将本身的一台虚拟机删除了,这时求助系统管理员看看有没有办法恢复,好在系统开启的软删除功能,并且被删除的虚拟机还在可回收的时间范围内,这时管理员便以admin的身份登陆系统,为用户a恢复了虚拟机,可是细心的管理员却发现了一些不对:其Admin项目下并无任何虚拟机,可是其配额却被使用了,难道这和刚才的操做有关?再来重试一下:普通用户删除虚拟机,admin用户来为其恢复,这时配额又发生了变化,果真如此:被恢复的虚拟机的配额错误的添加到了Admin项目下。该Bug在最新的kilo版本中仍然存在,感兴趣的同窗能够实验一下。api
发现了Bug的存在,那就更进一步,到代码中找一下缘由吧。网络
如何肯定问题代码的位置呢?这须要对Nova的项目结构有大致的了解,咱们来简单了解一下:架构
上图是nova架构的极简版本,与本问题无关的组件都没有画上去,恢复虚拟机的操做过程大体是这样:单元测试
既然软删除的功能层面没有任何问题,虚拟机的删除和恢复过程都很顺利,可见不会是驱动的问题,顺着API层的代码调用往下找,很快就能够定位了。直接看出问题的代码片断:测试
def restore(self, context, instance): # 该代码作了删减 flavor = instance.get_flavor() # 获取quotas对象 num_instances, quotas = self._check_num_instances_quota( context, flavor, 1, 1) self._record_action_start(context, instance, instance_actions.RESTORE) try: if instance.host: instance.task_state = task_states.RESTORING instance.deleted_at = None instance.save(expected_task_state=[None]) self.compute_rpcapi.restore_instance(context, instance) else: instance.vm_state = vm_states.ACTIVE instance.task_state = None instance.deleted_at = None instance.save(expected_task_state=[None]) # 更新quotas quotas.commit()
上面的这段代码就是API层面上进行虚拟机回收的主要方法,能够看到其中有明显的配额操做(quotas),在解读这段代码前有必要先对nova中"context"的概念作个简介。不只是nova,在openstack其余项目中都随处可见这个"context",它是一个包装了用户请求信息的对象,包含用户的项目和认证信息等,经过它能够简便的进行各项目之间的API调用和用户信息的查询,API服务接收到用户的每一次HTTP请求,都会建立一个新的context。.net
回到这段代码,咱们重点关注对quotas所做的操做:在方法的第二行,经过了一个方法获取了quotas,有在方法的结尾执行了quotas.commit(),可以获取到的信息很少,咱们再看一下获取quotas的方法:_check_num_instances_quota
# 这里只截取一部分 def _check_num_instances_quota(self, context, instance_type, min_count, max_count): req_cores = max_count * instance_type['vcpus'] vram_mb = int(instance_type.get('extra_specs', {}).get(VIDEO_RAM, 0)) req_ram = max_count * (instance_type['memory_mb'] + vram_mb) try: quotas = objects.Quotas(context) quotas.reserve(context, instances=max_count, cores=req_cores, ram=req_ram) ... return max_count, quotas
这里能够看到获取quotas的过程:经过当前的context建立quotas对象,而且执行了reserve操做; 咱们知道context是由HTTP请求而来,里面保存的是发请求的用户的信息,因此这里的quotas对象的“全部者”也就是context中的用户。
结合Bug发生的场景来看:管理员还原用户a的虚拟机,发请求的是管理员,当前context中记录的是管理员的信息,这里的quotas理所固然的就是管理员的,而后操做了用户a的虚拟机,更新的倒是管理员的quotas。嗯,真相大白!
Bug的缘由是获取的quotas并不属于指望的用户,可是直接修改context显然不合适(会影响后续的操做),先了解一下quotas对象自身吧:
class Quotas(base.NovaObject): # 部分代码 def __init__(self, *args, **kwargs): super(Quotas, self).__init__(*args, **kwargs) # Set up defaults. self.reservations = [] self.project_id = None self.user_id = None self.obj_reset_changes() ... def reserve(self, context, expire=None, project_id=None, user_id=None, **deltas): reservations = quota.QUOTAS.reserve(context, expire=expire, project_id=project_id, user_id=user_id, **deltas) self.reservations = reservations self.project_id = project_id self.user_id = user_id self.obj_reset_changes() def commit(self, context=None): if not self.reservations: return if context is None: context = self._context quota.QUOTAS.commit(context, self.reservations, project_id=self.project_id, user_id=self.user_id) self.reservations = None self.obj_reset_changes()
注意看reserve方法的参数,默认为None的project_id和user_id,这正是改变quotas属主的方便入口!
修改后的代码这里就不贴了,感兴趣的同窗能够到此次提交中看:Code Review
openstack社区有着整套项目管理流程,这里有一张图可以较详细的描述工做流程:
由图可见bugfix是其中最简单的流程。
关于如何提交代码,这篇文章有详细的介绍: 向 OpenStack 贡献您的代码。另外须要注意一点,在国内向社区提交代码,常常会由于网络问题致使没法提交,幸亏找到了大牛的博客介绍了该类问题的解决办法。 修改完代码的单元测试和pep8本地测试固然不能少,早就知道社区对单元测试要求很严格,此次才真正领教了,三行代码的修改,单元测试却写了30行,review期间屡次由于单元测试的问题重提代码(哭)。社区里面的开发者,尤为是项目的core,对待项目有着像对本身孩子般的认真与细致:他们会在一个本身根本不会在乎的地方提醒你、面对当前的问题他们会延伸的考虑相似的问题。他们的态度让我首先感觉到的吃惊,而后是敬佩!
经历八次review、历时近一个月,个人代码总算是入库了!但愿个人这篇记录能对你有帮助。
感谢休伦公司技术总监 孙琦 提供的英文支持,社区大牛Alex Xu给出的修改建议。
Launchpad上面的bug提交: Abnormal changes of quota usage after instance restored by admin
代码审查过程: Fix abnormal quota usage after restore by admin
Git@OSC中的代码: Fix abnormal quota usage after restore by admin