您现在的位置 >> Hadoop教程 >> Hadoop实战 >> 专题  
 

如何阅读openstack源代码,源代码阅读指导

【作者:Hadoop实战专家】【关键词:源码 元数据 image 方法 】 【点击:6616次】【2014-02-1】
还有一个问题,就算我知道怎么看这些源码,我怎么去一气呵成?这个方法主要实现的是根据参数ec2_id获取指定的实例镜像元数据;3)根据元数据中的id值查找到匹配的S3格式镜像数据信息,进一步获取镜像的数据库内部的id值,更新image元数据信息,并返回;  

相关热门搜索:

大数据标签:bigdata

OpenStack 本身用 python 语言编写,虽然我一直觉得自己的 python 功底已经不错了,但在看源码的过程中,还总是觉得自己掌握的东西太少了,所以,首要的一点,如果你在看 OpenStack 源码,请一定要打牢你的 python 基础,不然有些技巧性的代码可能让你停滞不前。

看源码,如果能一气呵成最好。什么叫一气呵成呢?我先讲个每个人生活中都可能遇到的一些情况:你在做 A 事,但是突然 B 打电话,让你帮着解决 C 事,然后你就去做 C 事了,等你做完 C 事,发现家里有 D 事必须要做,然后你又去做 D 事……这样的结果就是,你把 A 事给遗忘了,即便你空闲时候想起来了,但是再去做的时候,发现没有第一次那样熟悉 A 事了,你需要重新花费一些时间来熟悉它。试想,如果一开始你就把 A 事做到底,会怎样?

看源码其实是一个很漫长的过程,特别对一个大型项目而言,如果你要看完它的源码,过程是很曲折的,这里的看完不仅仅是过目了一遍,脑子里还要能把逻辑关系理顺。你可能有疑问了,要看源码,一天两天解决不了,但是又要保证一气呵成,这根本就是无稽之谈嘛!事情也的确是这样,鱼与熊掌不可兼得!这里就有一个技巧的问题了,你不妨想象,这么大一个项目,它是怎么开发出来的?难道一开始,项目就已经策划好了?需要多少个源文件,每个文件里面的源码是什么也都做好了?有点经验的程序员都知道,这是不可能的。项目的开发是慢慢细化的,一开始只是核心,然后是骨架,然后有血肉,然后有做 A 事的工具……到这里,或许你知道我要说什么了,源码怎么一步步写出来的,我们就怎么一步步的去看它。先研究核心,再研究骨架,然后血肉,其它工具……。还有一个问题,就算我知道怎么看这些源码,我怎么去一气呵成?这就好比你要完成一件大事情,但是你发现给自己定这么宏大的目标对自己来说比登天还难,所以你就想到用小目标来不断激励自己,最终不断接近大目标。这里的一气呵成既然不能一气把所有源码呵成,那就分段吧!不要心急,不要总想着还有很多源码都还没看,保持淡定!

其实,看源码都是一样的,从架构处着手,然后慢慢扩展到细枝末叶。这里,说一些 utilities 。看源码是很枯燥的,一点都不形象不说,还要让脑子一直保持着源码中的很多东西,如果你想偷懒,如果你想让生活更简单,那就用图形吧,图形加速了整个 IT 的发展,它的强大与便利有目共睹。源码中的各个模块,类怎么耦合的,用了什么设计模式,拿张纸,画几笔,就显而易见了,当然,做个 PPT 更好。源码之间的互相交错是最让人头疼的,很多人一开始看源码,就从这个源文件的某个函数跳转到另一个源文件的某个函数,我想问一下,你以为你的大脑是电脑吗?你的大脑也可以像电脑那样按着调用顺序依次调用各个函数??如果你在看一个源文件,OK,先把这个源文件一气呵成再说,不要跳转到其它源文件,如果引用的其它源文件中的函数你不知道是干嘛的,先 pass ,以后再说,只要你知道调用它的函数是干嘛的就行,等你以后研究到另外一个源文件的时候,这个关系就很明确了。还有一个现象,很多人一接触一个项目的源码,看见那么多源文件,一下子就懵了,不知道如何下手,别人说,从 main 开始看,于是,他就从 main 开始看了,其实这个无所谓,还是那句话,不要以为你的大脑是电脑,做一些人脑力所能及的事,随便找个源文件,然后用心去看它,不要觉得这里的随便就是随便,虽然它的确是随便,但是如果你不知道我说的随便是哪个随便,那就只有随便你了。不管哪个项目,源码包中大致结构一看,基本上就知道各个东西大致是干嘛的,开发这些东西的也是人脑,不是电脑,为了方便理解,基本上文件取名都还是见名知意的。看源码是一件很有挑战的事情,对源码而言,记住,你永远都要站在它的对面,而不是将自己深埋进源码中,一旦你钻进去了,你就已经迷失了自己。

上面说了那么多,都没有谈到 OpenStack ,其实这个是相辅相成的,上面的你知道了,看 OpenStack 你也应该没有问题了,OpenStack 的核心项目是 nova, glance, swift ,最核心的就是 nova 了,所以,从 nova 开始看吧。nova 源码包中有很多子包,源文件。除了版权版本以及和其它组件交互的东西,随便找一个开始看吧。切记,在开始看之前,最好能把你知道的 nova 架构图烂熟于心。 这个很重要,因为你之后随时有可能沉迷进源码大军中。

貌似没有给冲着 OpenStack 源码来的读者一个很好的建议,其实,任何事都没有一蹴而就的方法,想做成它,最好的方法就是,保持淡定的心态,一步步,走下去!作为过来人,还是给个建议,从 虚拟化开始看,因为这个里面用到了适配器设计模式,你稍微看一点就知道了这个包是干嘛的了,而且,可以提升你继续看源码的信心。
下面进一步给大家研究一下nova模块中的建立实例的实现过程的源码。肯定会有错误和不严谨的地方,需要大家给予指正。
代码中是本人之前看源码时候写的注释,可能不是太规范,但是贴上来希望能帮助大家更好的理解源码;

之前的命令行和配置文件解析这里暂时不详解。因为OpenStack的服务能兼容亚马逊的EC2/S3 API,所以可以应用EC2 API来建立一个新的实例,将会调用/nova/api/ec2/cloud.py中的run_instances方法:

1. def run_instances(self, context, **kwargs):

2. """

3. 准备实例,并且发送实例的信息和要运行实例的请求消息到远程调度器scheduler;

4. 实现实例的简历和运行,由调度器完成;

5. """

6.

7. # 设置最小建立实例的数目;

8. min_count = int(kwargs.get('min_count', 1))

9.

10. # 获取kwargs['kernel_id']指定的镜像image数据返回给kernel;

11. # 获取更新的kwargs['kernel_id'];

12. # 注:kernel_id为虚拟机内核ID值;

13. if kwargs.get('kernel_id'):

14. # _get_image:

15. # context:上下文信息;

16. # kwargs['kernel_id']:从参数信息中获取'kernel_id'值;

17. kernel = self._get_image(context, kwargs['kernel_id'])

18. # 根据kernel['id']查询数据库中匹配的S3镜像数据,获取它的uuid属性,并返回;

19. # 返回匹配的db.models.S3Image.uuid给kwargs['kernel_id'];

20. kwargs['kernel_id'] = ec2utils.id_to_glance_id(context, kernel['id'])

21.

22. # 获取kwargs['ramdisk_id']指定的镜像image数据返回给ramdisk;

23. # 获取更新的kwargs['ramdisk_id'];

24. if kwargs.get('ramdisk_id'):

25. ramdisk = self._get_image(context, kwargs['ramdisk_id'])

26. # 根据ramdisk['id']查询数据库中匹配的S3镜像数据,获取它的uuid属性,并返回;

27. # 返回匹配的db.models.S3Image.uuid给kwargs['ramdisk_id'];

28. kwargs['ramdisk_id'] = ec2utils.id_to_glance_id(context, ramdisk['id'])

29.

30. # 循环获取每一个块设备映射;

31. # 解析块设备映射bdm;

32. for bdm in kwargs.get('block_device_mapping', []):

33. _parse_block_device_mapping(bdm)

34.

35. # 获取kwargs['image_id']指定的镜像image数据;

36. image = self._get_image(context, kwargs['image_id'])

37. # 根据image['id']查询数据库中匹配的S3镜像数据,获取它的uuid属性,并返回;

38. # 返回匹配的db.models.S3Image.uuid给image_uuid;

39. image_uuid = ec2utils.id_to_glance_id(context, image['id'])

40.

41. # 获取镜像image的状态;

42. if image:

43. image_state = self._get_image_state(image)

44. else:

45. raise exception.ImageNotFoundEC2(image_id=kwargs['image_id'])

46.

47. if image_state != 'available':

48. raise exception.EC2APIError(_('Image must be available'))

49.

50.

51. # create:准备实例,并且发送实例的信息和要运行实例的请求消息到远程调度器scheduler;

52. # 实现实例的简历和运行,由调度器完成,这部分代码实际上只是实现请求消息的发送;

53. (instances, resv_id) = self.compute_api.create(context,

54. # get_instance_type_by_name:通过给定的name检索单个实例类型信息;

55. # 以字典的形式返回查询结果;

56. instance_type=instance_types.get_instance_type_by_name(kwargs.get('instance_type', None)),

57. image_href=image_uuid,

58. max_count=int(kwargs.get('max_count', min_count)),

59. min_count=min_count,

60. kernel_id=kwargs.get('kernel_id'),

61. ramdisk_id=kwargs.get('ramdisk_id'),

62. key_name=kwargs.get('key_name'),

63. user_data=kwargs.get('user_data'),

64. security_group=kwargs.get('security_group'),

65. availability_zone=kwargs.get('placement', {}).get('availability_zone'),

66. block_device_mapping=kwargs.get('block_device_mapping', {}))

67. return self._format_run_instances(context, resv_id)

复制代码
1.方法_get_image的源码分析:

可以看到程序中三次调用_get_image方法,来获取相应的镜像元数据,首先来分析这个方法:

1. def _get_image(self, context, ec2_id):

2. """

3. 获取ec2_id指定的镜像image元数据;

4.

5. # 调用之一传进来的参数:

6. # context:上下文信息;

7. # ec2_id=kwargs['kernel_id']:从参数信息中获取'kernel_id'值,kernel_id为虚拟机内核ID值 ;

8. """

9. try:

10. # ec2_id_to_id:转换一个EC2的ID为一个实例(镜像)的ID(INT格式);(主要是格式变换的问题)

11. # 转换之后赋值给internal_id,也就是镜像image的内置ID;

12. internal_id = ec2utils.ec2_id_to_id(ec2_id)

13.

14. # show:这个方法完成了以下的工作:

15. # 转换镜像image的ID值internal_id到image_uuid;

16. # 根据给定的image_uuid从glance下载image元数据,并且转换为字典格式;

17. # 转换镜像image中的image_uuid到新的image_id;

18. # 更新image当中的相关属性,返回更新后的image数据;

19.

20. # context:上下文信息;

21. # internal_id:实例镜像的ID值(从EC2 ID值变换了格式以后得到的);

22. # 注:S3是EC2的存储平台,所以这里调用/nova/image/s3.py中的show方法;

23. image = self.image_service.show(context, internal_id)

24. except (exception.InvalidEc2Id, exception.ImageNotFound):

25. filters = {'name': ec2_id}

26. images = self.image_service.detail(context, filters=filters)

27. try:

28. return images[0]

29. except IndexError:

30. raise exception.ImageNotFound(image_id=ec2_id)

31.

32. # 通过ec2_id获取image_type;

33. image_type = ec2_id.split('-')[0]

34.

35. # 通过image_type验证找到的镜像image是否是所要求找的镜像;

36. # 如果通过ec2_id获取的image_type和通过获取的镜像image得到的image_type不一致;

37. # 则引发异常,提示所要求找的镜像找不到;

38. if ec2utils.image_type(image.get('container_format')) != image_type:

39. raise exception.ImageNotFound(image_id=ec2_id)

40.

41. # 返回获取的镜像数据;

42. return image

复制代码
这个方法主要实现的是根据参数ec2_id获取指定的实例镜像元数据;
1.1 internal_id = ec2utils.ec2_id_to_id(ec2_id)
转换一个EC2的ID为一个实例(镜像)的ID(INT格式)(主要是格式变换的问题);
1.2 image = self.image_service.show(context, internal_id)
这是比较重要的方法,完成了获取镜像元数据的任务,这个方法主要完成了以下工作流程:
internal_id分别针对'kernel_id'、'ramdisk_id'和'image_id':

1)转换镜像ID值internal_id到image_uuid;
2)根据给定的image_uuid从glance下载image元数据,并且转换为字典格式;
3)根据元数据中的id值查找到匹配的S3格式镜像数据信息,进一步获取镜像的数据库内部的id值,更新image元数据信息,并返回;
4)更新image当中的相关属性,返回更新后的image数据;
后面对这个方法进行详细的跟踪分析;
1.3 image_type = ec2_id.split('-')[0]
通过ec2_id获取image_type;
通过image_type验证找到的镜像image是否是所要求找的镜像;
1.4 return image
返回获取的镜像元数据;

1.2 image = self.image_service.show(context, internal_id)跟踪分析:
因为S3是EC2的存储平台,所以这里调用/nova/image/s3.py中----show方法
1.2.1 image_uuid = ec2utils.id_to_glance_id(context, image_id)

进入方法id_to_glance_id可见:

1. def id_to_glance_id(context, image_id):

2. return db.s3_image_get(context, image_id)['uuid']

复制代码

1. def s3_image_get(context, image_id):

2. """

3. 通过给定的image_id查找数据库中的S3格式的镜像;

4. """

5. result = model_query(context, models.S3Image, read_deleted="yes").\

6. filter_by(id=image_id).\

7. first()

8. if not result:

9. raise exception.ImageNotFound(image_id=image_id)

10. return result

复制代码
model_query是SQLAlchemy的一个方法;SQLAlchemy是一个Python的SQL工具包以及数据库对象映射框架,SQLAlchemy 的一个目标是提供能兼容众多数据库(如 SQLite、MySQL、Postgres、Oracle、MS-SQL、SQLServer 和 Firebird)的企业级持久性模型;后续我会对这个框架进行一个总结;(见后续链接)

model_query方法实现的是对数据库按照一定的规则的查询操作并返回结果;
我们看到,在调用model_query方法的时候传入了一个models.S3Image参数,原来S3Image是个类:

1. class S3Image(BASE, NovaBase):

2. __tablename__ = 's3_images'

3. id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)

4. uuid = Column(String(36), nullable=False)

复制代码
这个类从NovaBase类继承,这也是model_query方法所要求的。看看这个类,我们可以理解为,它创建了一个数据库中的表s3_images,并且定义数据表的结构,有两个属性id和uuid;

所以我们就能够知道id_to_glance_id(context, image_id)实现的是根据给定的image_id值,查询数据库,找到匹配的表信息,获取它的S3Image.uuid并返回,赋值给:
image_uuid = S3Image.uuid;
出自about云总结,暂时到此,谢谢大家!

大数据系列相关文章:

最新评论
风雨同舟2014-09-09 08:26:31
[图片]
曼珠沙华2014-09-09 08:13:34
尖越科技2014-09-09 03:30:44
今天有讲课么?
紫梦依然2014-09-09 01:49:59
谁看过书的能不能教教我?
2014-09-08 06:27:03
电子档 还是 纸质的
 
  • Hadoop生态系统资料推荐