2.4 浏览OpenStack源码
在阅读本节的内容之前,我们有必要先端正一下自己的认识:学习OpenStack并不等同于学习OpenStack的文档或书籍。OpenStack的文档确实比较完善,也有一些不错的书籍,但整日地抱着它们用功“啃”,最多只能说明你是一个很有上进心、很应该得到表彰的好青年、好同志,不过也就仅此而已了。
毫不夸张地说,学习OpenStack开发就是学习它的源码,因为源码本身就是最好的参考资料,其他任何经典或非经典的书籍都只能起到辅助作用,不能也不应该取代源码在我们学习过程中的主导地位。
但是面对OpenStack这么一个庞然大物,恐惧会在我们的心里滋生、蔓延。人类进化这么多年,面对复杂的物体和事情还是会有天生的惧怕感,体现在OpenStack开发学习上就是:那么庞大复杂的源码,让人面对起来,情何以堪啊!
有了这种恐惧感和无力感,很多人在心理上就会排斥面对和接触源码,宁愿抱着各种文档,搜集各种各样五花八门的书籍,看了又忘,忘了又看,也不大情愿去认真、细致地浏览源码。这个时候,我们应该意识到自己需要克服心理上的障碍。
在有了正确的认识之后,相信本章接下来的内容会使你在浏览、学习OpenStack源码时,不会迷失在无尽的代码海洋里。
2.4.1 浏览代码的工具
工欲善其事,必先利其器。一个功能强大,同时使用方便的代码浏览工具对于我们阅读OpenStack代码是很有帮助的,下面仅仅列举一些常用的工具。
1.Vim+各种插件
Source Insight并没有对应Linux的版本。因此对于很多Linux初学者来说,在一个完全的Linux环境下进行学习,首先要解决的一个问题就是,找到一个可以取代Source Insight的代码浏览工具。
这个工具就是Vim,各种Linux发行版都会默认安装它。虽然Vim默认的编辑界面很普通,甚至可以说丑陋,但是可以通过配置文件.vimrc添加不同的界面效果,还可以配合TagList、WinManager等很多好用的插件或工具,将Vim打造成一个不次于Source Insight的代码浏览工具。
2.Eclipse+PyDev插件
Eclipse+PyDev插件应该是Python IDE的经典搭配,不过它需要一个Java的运行时环境,对机器的配置还是有较高要求的。如果用户用来开发Python的机器内存只有256MB,还是采用其他工具为好,比如接下来提到的Spyder。
3.Spyder
Spyder相对来说比较小众,但也能称得上是一个强大的交互式Python语言开发环境,同样提供了高级的代码编辑、交互测试、调试等功能,通常各个Linux发行版都会内置它的安装包。
值得一提的是,Spyder还是一个轻量级的工具,比Eric等工具轻量得多,启动速度很快,性价比较高。Spyder使用界面如图2-3所示。
图2-3 Spyder使用界面
2.4.2 分析源码如何入手
既然要学习OpenStack源码,就要经常对代码进行分析,而OpenStack代码非常多,还源源不断地增加,这就让大部分人有种“雾里看花花不见”的无助感。不过不要怕,孔子早就留给了我们应对之策:敏于事而慎于言,就有道而正焉,可谓好学也已。这就是说,做事要踏实才是好学生、好同志,要遵循严谨的态度,去理解每一段代码的实现,多问、多想、多记。如果抱着走马观花、得过且过的态度,则结果极有可能就是一边看一边丢,没有多大的收获。
1.源码地图
我们在新到一个城市时,总是会先获取当地的地图及各种指南,有了它们在手里我们才不至于像无头苍蝇般迷惘地行走在陌生的街道上。
而我们在新接触一个稍微有点规模的项目时,面对铺天盖地袭来的无数代码,内心也总是会渴望有这样的一份地图或指南,让我们能够快速地对它有一个整体的了解并找到自己的目的地。
对于大多数项目来说,它的编译系统就承担了地图这个很重要的角色。比如,Linux内核源码中遍布各级目录的Kconfig和Makefile文件,Android源码中无处不在的Android.mk文件,这些与编译系统相关的文件,可以让我们很容易地弄清楚代码之间的脉络,从庞大的代码里定位到我们所关心的部分。
以Linux内核为例,如果某一天你希望在自己的Linux系统里浏览自己U盘里的文件,却发现一直“顺从”的Linux怎么也不能识别保存有大量珍贵视、音频资料的U盘,是U盘出问题了吗?于是你不甘心地决定去研究内核里U盘驱动的实现以寻找原因。
U盘是一个USB存储设备,你很容易地想到驱动的源码一定是在drivers/usb/storage/目录下面,但是当你进入这个目录,看到铺天盖地的数十个文件时却不由倒吸一口凉气,于是你安慰自己:我要具有强大的心理素质,这些文件和代码一定不是都与U盘的实现相关的。
也许,生活中总是充满了跌宕起伏,在认真查看Kconfig和Makefile两个文件之后,你的心情明显好了许多。从Kconfig文件来看,只有CONFIG_USB_STORAGE这个选项与U盘匹配,其他的众多选项明显不是针对U盘的,只是在混淆我们的注意力,考验我们的心理。接下来再用CONFIG_USB_STORAGE选项去Makefile文件进行过滤,就只剩下了相关的几个文件,也就是说,我们只需要研究这仅有的几个文件中的部分代码就可以完全理解Linux是如何支持U盘的,任务顿时轻松了许多。
但是作为一种解释性语言,Python源码并没有这样的一个编译系统来承担Linux内核中Kconfig与Makefile地图的角色。而面对同样纷繁复杂的文件与代码,我们需要寻找新的合适的地图。
2.setup.cfg
如果你使用Devstack部署整个开发环境,在默认情况下,OpenStack代码会被安装在/opt/stack/目录下,Nova、Cinder等各个项目的代码目录则对应了该目录下的子目录。
大致浏览一下/opt/stack/目录中下载的代码,我们可以很容易地发现每个子项目的源码根目录下都有一个setup.py文件和一个setup.cfg文件,基本上能够确定它们就是我们要寻找的地图。
如果你已经有了一定的Python开发基础,就会知道它们是与Python模块的分发相关的,如果没有的话,你可能需要去了解一下Distutils、Distutils2、Setuptools、Distribute等Python代码分发的工具,但这并不是本书的重点,我们首先以Ceilometer子项目为例来看setup.py文件的内容:
这个文件的代码很简单,仅仅只是调用了setup()函数,并且对setup()函数也只是寥寥勾勒两笔。
但事情原本不是这样的,setup()函数有大量的参数需要设置,包括项目的名称、作者、版本等。setup.cfg文件将setup()函数解脱出来,该函数使用pbr工具去读取和过滤setup.cfg文件中的数据,并将解析后的结果作为自己的参数。
setup.cfg文件的内容由很多个section组成,如global、metadata、file等,包含了这个软件包的名称、作者等有用的信息,但能够帮助及指引我们更好地理解代码的section唯有entry_points。
3.entry points
从名称来看,entry points表示入口点,根据经验,代码的入口点通常就是我们理解的突破口,只要按照这个突破口分析下去,我们总会理解一个模块或功能的实现原理与细节。
但是在OpenStack各个子项目中,setup.cfg文件里注册的entry points非常多,为了更好地理解,我们需要明白这些入口点在Python项目中的意义。
对于一个Python包来说,entry points可以被简单地理解为通过Setuptools注册的、外部可以直接调用的接口。仍然以Ceilometer子项目为例:
上述代码表示注册4个entry points,它们属于ceilometer.compute.virt组或命名空间(entry points group)。它们表示Ceilometer目前共实现了4种Inspector从Hypervisor中获取内存、磁盘等相关统计信息的方式。
在Ceilometer安装后,其他程序可以利用下面几种方式调用这些entry points。
· 使用pkg_resources:
· 仍然使用pkg_resources:
· 使用stevedore,从本质来说,stevedore只是对pkg_resources的封装:
这段代码表示,Ceilometer会根据配置选项hypervisor_inspector的设置加载相应的Inspector,比如,加载ceilometer/compute/virt/libvirt/目录下的代码来获取虚拟机的运行统计数据。
从上面的代码可以看出,entry points都是在运行时动态导入的,有点类似于一些可扩展的插件,__import__或importlib也可以实现同样的功能,但是stevedore使得这个过程更容易,更有助于我们在运行时动态导入一些扩展的代码或插件来扩展自己的应用。这种方式也正是OpenStack各个子项目所主要使用的。
到目前为止,基于对entry points的理解,我们可以相对容易地找到所需要研究的代码的突破口,比如,我们希望了解Ceilometer是如何获取虚拟机的内存磁盘等统计数据的,就可以根据ceilometer.compute.virt这个entry points组的定义研究ceilometer/compute/virt/目录下的代码,甚至可以仿照它下面的Libvirt的实现增加新的Inspector,从而对新的Hypervisor类型进行支持。
4.console_scripts
在可能的众多entry points组中,有一个比较特殊的,就是console_scripts,下面是Ceilometer的setup.cfg文件对它的定义:
这里的每一个entry points都表示有一个可执行脚本会被生成并安装,我们可以在控制台上直接执行它,因此将这些entry points理解为整个Ceilometer子项目所提供的各个服务的入口点更为准确。