本帖最后由 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方法:
- 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) = self.compute_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)
复制代码
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
复制代码
这个方法主要实现的是根据参数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方法,来看这个方法:
- def show(self, context, image_id):
- """
- # 调用之一传进来的参数:
- # context:上下文信息;
- # image_id=internal_id:实例镜像的ID值(从EC2 ID值变换了格式以后得到的);
- """
- # id_to_glance_id:根据数据库内置ID值(实例或者说是镜像的ID)获取数据库中相应的UUID值,赋值给image_uuid;
- image_uuid = ec2utils.id_to_glance_id(context, image_id)
-
- # 这里的service:service = service or glance.get_default_image_service();
- # 获取service或者是GlanceImageService的对象(glance.get_default_image_service()指向的);
- # 所以这里调用的还是/nova/image/glance.py中的show方法;
- image = self.service.show(context, image_uuid)
-
- # 转换镜像中的image_uuid到image_id;
- # 更新image当中的相关属性,返回更新后的image数据;
- 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可见:
- 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)
复制代码
这个类从NovaBase类继承,这也是model_query方法所要求的。看看这个类,我们可以理解为,它创建了一个数据库中的表s3_images,并且定义数据表的结构,有两个属性id和uuid;
所以我们就能够知道id_to_glance_id(context, image_id)实现的是根据给定的image_id值,查询数据库,找到匹配的表信息,获取它的S3Image.uuid并返回,赋值给:
image_uuid = S3Image.uuid;
下一篇:
OpenStack建立实例完整过程源码详细分析(2)
|