glance上传镜像源码分析

glance上传镜像命令

glance --debug image-create --file cirros-0.3.0-i386-disk.img --disk-format qcow2 --container-format bare --visibility public

结果

 

然后根据dedug打印出来信息去追踪代码

1.从这个打印信息可以看出请求了v2/schemas/image,从原理上我们知道glance-api接收请求最先在router开始处理,所以去glance/api/v2/router.py.API.__init__里面去找映射到schemas的那个类,在__init__初始化函数里面实例化类,定义映射,转移到glance/api/v2/schemas.py去进行实际处理


def __init__(self, mapper):

    #从/etc/glance/schema-image.json文件加载用户定义属性

    custom_image_properties = images.load_custom_properties()

    #创建glance/api/v2/schemas.py.Controller实例

    schemas_resource = schemas.create_resource(custom_image_properties)

    #定义路由映射:转到glance/api/v2/schemas.py执行
    #curl -X GET /schemas/image  -> shemas_resource.image

    mapper.connect('/schemas/image',
                       controller=schemas_resource,
                       action='image',
                       conditions={'method': ['GET']})

schemas来自于

from glance.api.v2 import schemas

所以schemas.create_resource()在schemas.py里面

返回了一个controller类的实例schemas_resource

def create_resource(custom_image_properties=None):
    controller = Controller(custom_image_properties)
    return wsgi.Resource(controller)

完了之后正式通过mapper开始映射转到glance/api/v2/schemas.py

 # curl -X GET /schemas/image  -> shemas_resource.image
        mapper.connect('/schemas/image',
                       controller=schemas_resource,
                       action='image',
                       conditions={'method': ['GET']},
                       body_reject=True)

从映射/schemas/image可以看到,转过去之后,他调用了image函数

def image(self, req):
    return self.image_schema.raw()

image函数需要返回一个self.image_schema.raw(),也就是先image_schema再调raw

self.image_schema在__init__里面被初始化

def __init__(self, custom_image_properties=None):
    self.image_schema = images.get_schema(custom_image_properties)

然后去调用get_schema函数,获取镜像的基本属性,id,name,status等的定义及描述,这里还会读取etc/glance/glance-api.conf文件决定是生成PermissiveSchema(默认)还是Schema(PermissiveSchema会设置links参数Schema不会)

def get_schema(custom_properties=None):
    
    properties = get_base_properties()
    links = _get_base_links()

    #根据配置/etc/glance/glance-api.conf决定是生成
    #PermissiveSchema(默认)还是Schema,区别是PermissiveSchema
    #多设置了links参数


    if CONF.allow_additional_image_properties:
        schema = glance.schema.PermissiveSchema('image', properties, links)
    else:
        schema = glance.schema.Schema('image', properties)

    #合并用户自定义属性
    #属性合并很简单:先得到两个属性集的交集,然后判断交集的值是否冲突
    #如果值冲突,则抛异常,否则就合并数据集
    if custom_properties:
        for property_value in custom_properties.values():
            property_value['is_base'] = False
        schema.merge_properties(custom_properties)
    return schema

self.image_schema继续调用raw方法,调用父类的Schema返回镜像属性字典,同时更新additionalProperties属性,然后将属性字典返回

#glance/api/v2/schemas.py.Controller.image类会返回一个字典

def image(self, req):
    return self.image_schema.raw()
#glance/schema.py.PermissiveSchema
 def raw(self):
     raw = super(PermissiveSchema, self).raw()
     raw['additionalProperties'] = {'type': 'string'}
     return raw

#glance/schema.py.Schema
def raw(self):
    raw = {
        'name': self.name,
        'properties': self.properties,
        'additionalProperties': False,
    }
    if self.definitions:
        raw['definitions'] = self.definitions
    if self.required:
        raw['required'] = self.required
    if self.links:
        raw['links'] = self.links
     return raw

于是我们刚看到的glance上传镜像的debug打印出来的第一条请求完成,获得了镜像属性定义,接着我们再看第二个请求

根据debug日志和路由映射,

 

可以知道该请求由glance/api/v2/images.py.ImagesController.create方法处理,调用gateway的方法分别创建image_factory和image_repo,几乎所有glance关于数据库的操作都会先创建一个image_factory,由这个image_factory去完成具体的数据库操作

 @utils.mutating
 def create(self, req, image, extra_properties, tags):

     image_factory = self.gateway.get_image_factory(req.context)
     image_repo = self.gateway.get_repo(req.context)

     #实现用户认证,策略检查,参数检查等,具体请看下面的分析
     image = image_factory.new_image(extra_properties=extra_properties,
                                            tags=tags, **image)
     #进一步实现相关的检查,发送消息通知并记录数据
     image_repo.add(image)

     return image

创建image_factory函数get_image_factory() #创建image_factory里面延展下去很深,就没有追下去,暂时只关注咱们的主线以及这个image_factory的作用

def get_image_factory(self, context):

    image_factory = glance.domain.ImageFactory()

    store_image_factory = glance.location.ImageFactoryProxy(
                image_factory, context, self.store_api, 
                                        self.store_utils)
    quota_image_factory = glance.quota.ImageFactoryProxy(
                store_image_factory, context, self.db_api, 
                                        self.store_utils)
    policy_image_factory = policy.ImageFactoryProxy(
                quota_image_factory, context, self.policy)
    notifier_image_factory = glance.notifier.ImageFactoryProxy(
                policy_image_factory, context, self.notifier)

    #用户可以通过`/etc/glance/glance-api.conf中的
    #property_protection_file`选项配置属性策略,默认为disabled

    if property_utils.is_property_protection_enabled():
        property_rules = 
                    property_utils.PropertyRules(self.policy)
        pif = property_protections.ProtectedImageFactoryProxy(
                               notifier_image_factory, context, 
                                                property_rules)
        authorized_image_factory = 
                            authorization.ImageFactoryProxy(
                                                pif, context)
    else:
        authorized_image_factory = 
                            authorization.ImageFactoryProxy(
                               notifier_image_factory, context)
   return authorized_image_factory

image_factory镜像工厂,基本用来创建封装镜像对象的;各个子类也分别实现:权限检查、消息通知、策略检查、配额检查等

在create中可以看到成功获得image_factory之后又用image_factory去调用了new_image()进行真正的检查操作,super(ImageFactoryProxy, self).new_image(owner=owner, **kwargs)

    def new_image(self, **kwargs):
        owner = kwargs.pop('owner', self.context.owner)

        if not self.context.is_admin:
            if owner is None or owner != self.context.owner:
                message = _("You are not permitted to create images "
                            "owned by '%s'.")
                raise exception.Forbidden(message % owner)

        return super(ImageFactoryProxy, self).new_image(owner=owner, **kwargs)

这里面下去也很深,我在网上找到了new_image的调用的序列图

所以暂时也没有进去细究,但是可以明确他的目的,先把流程跑通,下次再研究这些分支,第二个请求主线就看完了,他主要是检查各种包括数据库条目,状态等等,保证上传操作前提无误

到第三个请求

debug信息如下,一个PUT请求,进行真正的上传操作

在映射里面可以看到

由此转到glance\api\v2\image_data.py查看upload函数,

    @utils.mutating
    def upload(self, req, image_id, data, size):
        image_repo = self.gateway.get_repo(req.context)
        image = None
        refresher = None
        cxt = req.context
        try:
            self.policy.enforce(cxt, 'upload_image', {})

            # get方法从数据库取出image_id指向的条目,封装成`domain/__init__.py/Image`对象,
            #然后经过层层封装返回authorization/ImageProxy对象,同时将镜像状态更新为saving
            image = image_repo.get(image_id)
            image.status = 'saving'

  save方法与上面的get方法一样,逐层调用`ImageRepoProxy`完成相关的 检查,通知,最后更新数据库条目状态(这个时候可以在Dashboard上看到状态为'保存中')


            # save方法与上面的get方法一样,逐层调用`ImageRepoProxy`完成相关的
            # 检查,通知,最后更新数据库条目状态(这个时候可以在Dashboard上看
            # 到状态为'保存中')
            image_repo.save(image, from_state='queued')

镜像上传成功后(在`location.py/set_data方法中上传文件成功后, 修改状态为active),更新数据库状态为active(这个时候可以在 Dashboard上看到状态为'运行中'),最终的信息是这样的:
 

            # 镜像上传成功后(在`location.py/set_data方法中上传文件成功后,
            # 修改状态为active),更新数据库状态为active(这个时候可以在
            # Dashboard上看到状态为'运行中'),最终的信息是这样的:
            image.set_data(data, size)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章