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

感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
如果转载,请保留作者信息。
博客地址: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)

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;


1.2.2  image = self.service.show(context, image_uuid)

(见下一篇博文)

1.2.3 return self._translate_uuid_to_id(context, image)

(见后续链接)




你可能感兴趣的:(OpenStack源码分析)