谁有爱奇艺会员福利0元购的福利

openstack之horizon源码分析 - 阳台 - 博客园
随笔 - 480, 文章 - 0, 评论 - 20, 引用 - 0
一、基础准备:
  Horizon是基于django webframework开发的标准的&wsgi程序,django的设计专注于代码的高度可重用,信奉DRY原则,一切面向对象,而Horizon可以说高度match了django的设计风格。网站程序基本有三部分组成,业务逻辑代码(Python),静态文件(js/css),模板(Python中的 jinja,mako,nodejs中有jade), 用户向webserver发起请求之后,server程序找到当前url对应的模板,填充模板变量(输出成字符串形式的html源码),返回给浏览器,浏览器渲染页面。一般模板语言都有继承(extend),插入(include)等特性,来提高页面的复用率。
 & Horizon做得就更彻底一些,它将页面上所有元素模块化,网页中一些常见元素,表单,表格,标签页,全部封装成Python类,每个组件有自己 对应的一小块html模板.当渲染整个页面的时候,Horizon先找到当前页面有多少组件,将各个组件分别进行渲染变成一段html片段,最后拼装成一 个完整的html页面,返回浏览器。
& & &总结Horizon的特点:
页面元素模块化
子面板可插
All in One(从部署上来说,Horizon只有它自己这一个组件)
二、分析horizon
  1、horizon设计分为三层:Dashboard-&PanelGroup-&Panel&
    a、project普通用户登录后看到的是项目面板
    b、admin管理登录看到可见左测的管理员面板
    c、settings右上角的设置面板
    d、identity不同的角色登录之后,左侧的“身份”面板,可设置项目用户
    e、交换机管理面板
  每个dashboard都是django中的一个app,django中的app可以理解成对业务逻辑模块化的一种手段,里面可以包含自己独有的url设定,模板和业务逻辑代码。每个dashboard下定义了一系列的PanelGroup,虚拟机管理对应到界面上就是一个PanelGroup(ManageCompute),里面有一系列的子panel(Overview、Instance、Volumes...). swift, heat, neutron的管理面板自都是一个PanelGroup,底下有各自的子Panel。
  2、项目结构
    Horizon项目核心的代码包有两个:openstack-dashboard和python-django-horizon。
    第一个包是控制台代码的具体实现,是一个基于Django框架的web应用,安装后主要文件在/usr/share/openstack-dashboard/路径下。第二个包是通用的一些Python类库,也包括一些静态文件,安装后在/usr/lib/python2.7/dist-packages/horizon下。定制化开发,主要是修改业务代码,基本不需要修改python-django-horizon,所以我们分析的重点放在openstack-dashboard这个包上。
  &图中的7个名词在代码中都可以对应上,代码的层级结构还是很清晰的。查看: 13343|回复: 1
如何阅读openstack源代码,源代码阅读指导
主题帖子积分
本帖最后由 pig2 于
01:49 编辑
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方法:
def run_instances(self, context, **kwargs):
&&&
准备实例,并且发送实例的信息和要运行实例的请求消息到远程调度器scheduler;
实现实例的简历和运行,由调度器完成;
&&&
# 设置最小建立实例的数目;
min_count = int(kwargs.get('min_count', 1))
# 获取kwargs['kernel_id']指定的镜像image数据返回给kernel;
# 获取更新的kwargs['kernel_id'];
# 注:kernel_id为虚拟机内核ID值;
if kwargs.get('kernel_id'):
# _get_image:
# context:上下文信息;
# kwargs['kernel_id']:从参数信息中获取'kernel_id'值;
kernel = self._get_image(context, kwargs['kernel_id'])
# 根据kernel['id']查询数据库中匹配的S3镜像数据,获取它的uuid属性,并返回;
# 返回匹配的db.models.S3Image.uuid给kwargs['kernel_id'];
kwargs['kernel_id'] = ec2utils.id_to_glance_id(context, kernel['id'])
# 获取kwargs['ramdisk_id']指定的镜像image数据返回给ramdisk;
# 获取更新的kwargs['ramdisk_id'];
if kwargs.get('ramdisk_id'):
ramdisk = self._get_image(context, kwargs['ramdisk_id'])
# 根据ramdisk['id']查询数据库中匹配的S3镜像数据,获取它的uuid属性,并返回;
# 返回匹配的db.models.S3Image.uuid给kwargs['ramdisk_id'];
kwargs['ramdisk_id'] = ec2utils.id_to_glance_id(context, ramdisk['id'])
# 循环获取每一个块设备映射;
# 解析块设备映射bdm;
for bdm in kwargs.get('block_device_mapping', []):
_parse_block_device_mapping(bdm)
# 获取kwargs['image_id']指定的镜像image数据;
image = self._get_image(context, kwargs['image_id'])
# 根据image['id']查询数据库中匹配的S3镜像数据,获取它的uuid属性,并返回;
# 返回匹配的db.models.S3Image.uuid给image_uuid;
image_uuid = ec2utils.id_to_glance_id(context, image['id'])
# 获取镜像image的状态;
if image:
image_state = self._get_image_state(image)
else:
raise exception.ImageNotFoundEC2(image_id=kwargs['image_id'])
if image_state != 'available':
raise exception.EC2APIError(_('Image must be available'))
# create:准备实例,并且发送实例的信息和要运行实例的请求消息到远程调度器scheduler;
# 实现实例的简历和运行,由调度器完成,这部分代码实际上只是实现请求消息的发送;
(instances, resv_id) = pute_api.create(context,
# get_instance_type_by_name:通过给定的name检索单个实例类型信息;
# 以字典的形式返回查询结果;
instance_type=instance_types.get_instance_type_by_name(kwargs.get('instance_type', None)),
image_href=image_uuid,
max_count=int(kwargs.get('max_count', min_count)),
min_count=min_count,
kernel_id=kwargs.get('kernel_id'),
ramdisk_id=kwargs.get('ramdisk_id'),
key_name=kwargs.get('key_name'),
user_data=kwargs.get('user_data'),
security_group=kwargs.get('security_group'),
availability_zone=kwargs.get('placement', {}).get('availability_zone'),
block_device_mapping=kwargs.get('block_device_mapping', {}))
return self._format_run_instances(context, resv_id)&/FONT&复制代码1.方法_get_image的源码分析:
可以看到程序中三次调用_get_image方法,来获取相应的镜像元数据,首先来分析这个方法: def _get_image(self, context, ec2_id):
&&&
获取ec2_id指定的镜像image元数据;
# 调用之一传进来的参数:
# context:上下文信息;
# ec2_id=kwargs['kernel_id']:从参数信息中获取'kernel_id'值,kernel_id为虚拟机内核ID值 ;
&&&
try:
# ec2_id_to_id:转换一个EC2的ID为一个实例(镜像)的ID(INT格式);(主要是格式变换的问题)
# 转换之后赋值给internal_id,也就是镜像image的内置ID;
internal_id = ec2utils.ec2_id_to_id(ec2_id)
# show:这个方法完成了以下的工作:
# 转换镜像image的ID值internal_id到image_uuid;
# 根据给定的image_uuid从glance下载image元数据,并且转换为字典格式;
# 转换镜像image中的image_uuid到新的image_id;
# 更新image当中的相关属性,返回更新后的image数据;
# context:上下文信息;
# internal_id:实例镜像的ID值(从EC2 ID值变换了格式以后得到的);
# 注:S3是EC2的存储平台,所以这里调用/nova/image/s3.py中的show方法;
image = self.image_service.show(context, internal_id)
except (exception.InvalidEc2Id, exception.ImageNotFound):
filters = {'name': ec2_id}
images = self.image_service.detail(context, filters=filters)
try:
return images[0]
except IndexError:
raise exception.ImageNotFound(image_id=ec2_id)
# 通过ec2_id获取image_type;
image_type = ec2_id.split('-')[0]
# 通过image_type验证找到的镜像image是否是所要求找的镜像;
# 如果通过ec2_id获取的image_type和通过获取的镜像image得到的image_type不一致;
# 则引发异常,提示所要求找的镜像找不到;
if ec2utils.image_type(image.get('container_format')) != image_type:
raise exception.ImageNotFound(image_id=ec2_id)
# 返回获取的镜像数据;
return image&/FONT&复制代码这个方法主要实现的是根据参数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可见: def id_to_glance_id(context, image_id):
return db.s3_image_get(context, image_id)['uuid']复制代码def s3_image_get(context, image_id):
&&&
通过给定的image_id查找数据库中的S3格式的镜像;
&&&
result = model_query(context, models.S3Image, read_deleted=&yes&).\
filter_by(id=image_id).\
first()
if not result:
raise exception.ImageNotFound(image_id=image_id)
return result复制代码model_query是SQLAlchemy的一个方法;SQLAlchemy是一个Python的SQL工具包以及数据库对象映射框架,SQLAlchemy 的一个目标是提供能兼容众多数据库(如 SQLite、MySQL、Postgres、Oracle、MS-SQL、SQLServer 和 Firebird)的企业级持久性模型;后续我会对这个框架进行一个总结;(见后续链接)
model_query方法实现的是对数据库按照一定的规则的查询操作并返回结果;
我们看到,在调用model_query方法的时候传入了一个models.S3Image参数,原来S3Image是个类: class S3Image(BASE, NovaBase):
__tablename__ = 's3_images'
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
uuid = Column(String(36), nullable=False)&/FONT&复制代码这个类从NovaBase类继承,这也是model_query方法所要求的。看看这个类,我们可以理解为,它创建了一个数据库中的表s3_images,并且定义数据表的结构,有两个属性id和uuid;
所以我们就能够知道id_to_glance_id(context, image_id)实现的是根据给定的image_id值,查询数据库,找到匹配的表信息,获取它的S3Image.uuid并返回,赋值给:
image_uuid = S3Image.uuid;
出自about云总结,暂时到此,谢谢大家!
欢迎加入about云群 、 ,云计算爱好者群,关注
主题帖子积分
中级会员, 积分 566, 距离下一级还需 434 积分
中级会员, 积分 566, 距离下一级还需 434 积分
感谢分享,暂时还看不懂。
经常参与各类话题的讨论,发帖内容较有主见
经常帮助其他会员答疑
活跃且尽责职守的版主
为论坛做出突出贡献的会员
站长推荐 /4
云计算hadoop视频大全(新增 yarn、flume|storm、hadoop一套视频
等待验证会员请验证邮箱
新手获取积分方法
技术类问答,解决学习openstack,hadoop生态系统中遇到的问题
Powered by

我要回帖

更多关于 爱奇艺会员福利0元购 的文章

 

随机推荐