分享

OpenStack建立实例完整过程源码详细分析(1)

xioaxu790 发表于 2014-6-9 09:02:12 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 1 25451
本帖最后由 pig2 于 2014-6-10 11:41 编辑
问题导读:
1、如何理解OpenStac兼容亚马逊的EC2/S3 API?

2、nova模块中建立实例的源码是怎样的?




前言
研究和学习OpenStack这么长时间了,一直想写写技术博客的,但是由于工作时间的原因,一直没有做到。现在开始从头整理一下以前看的内容,也是对研究过的内容的一个回顾了!
    从接触OpenStack开始,本人利用工作之余时间研究OpenStack的部分源码,第一遍看的之后只是看了个大概,头脑中对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. def show(self, context, image_id):  
  2.         """                     
  3.         # 调用之一传进来的参数:
  4.         # context:上下文信息;
  5.         # image_id=internal_id:实例镜像的ID值(从EC2 ID值变换了格式以后得到的);
  6.         """  
  7.         # id_to_glance_id:根据数据库内置ID值(实例或者说是镜像的ID)获取数据库中相应的UUID值,赋值给image_uuid;  
  8.         image_uuid = ec2utils.id_to_glance_id(context, image_id)  
  9.                   
  10.         # 这里的service:service = service or glance.get_default_image_service();  
  11.         # 获取service或者是GlanceImageService的对象(glance.get_default_image_service()指向的);  
  12.         # 所以这里调用的还是/nova/image/glance.py中的show方法;  
  13.         image = self.service.show(context, image_uuid)  
  14.          
  15.         # 转换镜像中的image_uuid到image_id;  
  16.         # 更新image当中的相关属性,返回更新后的image数据;  
  17.         return self._translate_uuid_to_id(context, image)  
复制代码

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;


下一篇:
OpenStack建立实例完整过程源码详细分析(2)





已有(1)人评论

跳转到指定楼层
lbwahoo 发表于 2014-7-17 21:48:47
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条