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)

 

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