感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
如果转载,请保留作者信息。
博客地址:http://blog.csdn.net/gaoxingnengjisuan
邮箱地址:[email protected]
研究和学习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)
进入方法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)
所以我们就能够知道id_to_glance_id(context, image_id)实现的是根据给定的image_id值,查询数据库,找到匹配的表信息,获取它的S3Image.uuid并返回,赋值给:
image_uuid = S3Image.uuid;
1.2.2 image = self.service.show(context, image_uuid)
(见下一篇博文)
1.2.3 return self._translate_uuid_to_id(context, image)
(见后续链接)