深挖Openstack Glance - 源码分析(1)

v1版本API功能解析

1. 创建image

2. 更新image

3. 获取image信息(metadata和data)

4. 删除image

5. 获取image列表

6. 获取image元数据(metadata)

7. 下载image

这些功能都在/glance/api/v1/images.py里,该文件主要是提供与image相关的操作。

接下来将对这些功能逐个分析:


1. 创建image

@utils.mutating
def create(self, req, image_meta, image_data):

1.1 首先检查是否有权限进行所要的操作

# 检查各种操作的权限
# _enforce:检查是否有该操作的权限
self._enforce(req, 'add_image')
is_public = image_meta.get('is_public')
if is_public:
    self._enforce(req, 'publicize_image')
if Controller._copy_from(req):
    self._enforce(req, 'copy_from')
if image_data or Controller._copy_from(req):
    self._enforce(req, 'upload_image')


1.2 对image的数据进行其他检查

# 检查image元数据是否创建了受保护的属性
self._enforce_create_protected_props(image_meta['properties'].keys(),
                                     req)
# 检查image属性是否太大,超过了配额
self._enfore_image_property_quota(image_meta, req=req)


1.3 保存image元数据到db中

# 获取image id,设置image状态为queued
# queued状态:没有上传update,只预留了image id
image_meta = self._reserve(req, image_meta)

深入_reserve方法:

1.3.1 获取外部存储地址

location = self._external_source(image_meta, req)

1.3.2 对选择的后端store类型进行判断

# 后端存储store类型必须是glance所支持的存储store类型
scheme = image_meta.get('store')
if scheme and scheme not in store.get_known_schemes():
    msg = _("Required store %s is invalid") % scheme
    LOG.warn(msg)
    raise HTTPBadRequest(explanation=msg,
                         content_type='text/plain')

1.3.3 获取image元数据状态(active/queued)

image_meta['status'] = ('active' if image_meta.get('size') == 0)
                        else 'queued')

1.3.4 获取需要使用到的具体的存储组件

# 根据存储路径获取后端存储
backend = store.get_store_from_location(location)
# 在创建image前需要根据backend获取具体的存储组件,存储组件是哪一种类型并不关心
self.get_store_or_400(req, backend)

1.3.5 设置image元数据的size值

# 如果没有提供image大小,则从远程存储中重新获取
image_meta['size'] = self._get_size(req.context, image_meta,
                                    location)

1.3.6 将image元数据存储在db中,并将创建信息发送出去

# 将image元数据存到db中
image_meta = registry.add_image_metadata(req.contenxt, image_meta)
# 记录创建image信息
self.notifier.info("image.create", redact_loc(image_meta))


1.4 存储image_data

id = image_meta['id']
# 针对不同的image来源存储image data
image_meta = self._handle_source(req, id, image_meta, image_data)

深入_handle_source方法:

1.4.1 image的来源有三种,限制当前只能使用一种来源

# image来源方式有三种:copy_from、location、image_data
# 限制image只有一种来源方式
sources = filter(lambda x: x, (copy_from, location, image_data))
if len(source) >= 2:
    msg = _("It`s invalid to provide multiple image sources.")
    LOG.warn(msg)
    raise HTTPBadRequest(explanation=msg,
                         request=req,
                         content_type="text/plain")
if len(sources) == 0:
    return image_meta

1.4.2 处理image_data方式

# image_data: 直接传入了image_data
if image_data:
    # 确保image已被激活:所需的元数据值都是可用的
    image_meta = self._validate_image_for_activation(req,
                                                    image_id,
                                                    image_meta)
    # 上传image data并使其可用
    image_meta = self._upload_and_activate(req, image_meta)

深入_upload_and_activate方法:

(1)上传image

location_data = self._upload(req, image_meta)

分析_upload方法可以看出,

1》首先获取后端存储组件

store = self.get_store_or_400(req, scheme)

2》更新image状态为saving,表明正在上传

registry.update_image_metadata(req.context, image_id,
                                {'status': 'saving'})

3》发送image.prepare的消息

self.notifier.info("image.prepare", redact_loc(image_meta))

4》上传image data到store里

image_meta, location_data = upload_utils.upload_data_to_store(
    req, image_meta, image_data, store, self.notifier)

5》发送image.upload的消息

self.notifier.info('image.upload', redact_loc(image_meta))

 (2)使上传完成的image可用

image_meta = self._activate(req,
                            image_id,
                            location_data,
                            from_state='saving')

_activate方法主要是设置image状态为active:该镜像完全可用,并获取image data存放的地址。


1.4.3 处理copy_from方式

# copy_form:从外部存储中拷贝image数据
# 利用eventlet pool缓冲池中的数据异步执行创建操作
elif copy_from:
    msg = _LI('Triggering asynchronous copy from external source')
    LOG.info(msg)
    pool = common.get_thread_pool("copy_from_eventlet_pool")
    pool.spawn_n(self._upload_and_activate, req, image_meta)

这种方式用异步的方式去调用跟image_data方式一样的方法。


1.4.4 处理location方式

# location:从location指定的backend store来获取image data
if location:

(1)比对从后端组件获取的image size值和原本元数据的size值是否相等,如果不相等,引发异常。

# 从image元数据获取的size值
image_size_meta = image_meta.get('size')
# 从后端存储组件获取的image size值
image_size_store = store.get_size_from_backend(
    location, req.context)
# 确保从后端存储组件返回的image size值和原本元数据的size值相等
# 如果从后端存储组件返回的image size值为0,通常意味着driver驱动出现了错误
# 这时应该使用client提供的image size值
if (image_size_store and
        image_size_store != image_size_meta):

(2)使上传后的image可用,设置image状态为active

location_data = {'url': location, 'metadata': {},
                'status': 'active'}
image_meta = self._activate(req, image_id, location_data)


2. 更新image

@utils.mutating
def update(self, req, id, image_meta, image_data):

2.1 首先检查是否有权限进行所要的操作

# _enforce:检查是否有该操作的权限
self._enforce(req, 'modify_image')
is_public = image_meta.get('is_public')
is is_public:
    self._enforce(req, 'publicize_image')
if Controller._copy_from(req):
    self._enforce(req, 'copy_from')
if image_data or Controller._copy_from(req):
    self._enforce(req, 'upload_image')

2.2 不能更新已被删除的image

# 获取原image的元数据
orig_image_meta = self.get_image_meta_or_404(req, id)
orig_status = orig_image_meta['status']

# 不能更新已被删除的image
if orig_status == 'deleted':
    msg = _("Forbidden to update deleted image.")
    raise HTTPForbidden(explanation=msg,
                        request=req,
                        content_type="text/plain")

2.3 规定可以修改的属性

# 当image状态为active时,只有admin才可以修改ACTIVE_IMMUTABLE中指定的元素
# ACTIVE_IMMUTABLE:size、checksum
# ACTIVE_IMMUTABLE和IMMUTABLE都在/glance/api/v1/_init_.py中设置
for key in ACTIVE_IMMUTABLE:
    if ((orig_status == 'active' or orig_status == 'deactivated')
            and image_meta.get(key) is not None
            and image_meta.get(key) != orig_image_meta.get(key)):
        msg = _("Forbidden to modify '%(key)s' of %(status)s "
                "image.") % {'key': key, 'status': orig_status}
        raise HTTPForbidden(explanation=msg,
                            request=req,
                            content_type="text/plain")

2.4 规定不可以修改的属性

# 不管image是什么状态,都不可以修改IMMUTABLE中指定的元素
# IMMUTABLE:status、id
for key in IMMUTABLE:
    if (image_meta.get(key) is not None and
            image_meta.get(key) != orig_image_meta.get(key)):
        msg = _("Forbidden to modify '%s' of image.") % key
        raise HTTPForbidden(explanation=msg,
                            request=req,
                            content_type="text/plain")

2.5 处理掉无效的image

# 处理掉image data中有数据但status不为queued的无效image
if image_data is not None and orig_status != 'queued':
    raise HTTPConflict(_("Connot upload to an unqueued image"))

2.6 处理修改状态为queued的image的情况

# 只允许使用Location|Copy-From来修改状态为queued的image
location = self._external_source(image_meta, req)
reactivating = orig_status != 'queued' and location
activating = orig_status == 'queued' and (location or image_data)

2.7 验证update/create/delete时对受保护属性的操作

# 验证是否可以更新受保护的属性
self._enforce_update_protected_props(
    orig_keys.intersection(new_keys), image_meta,
    orig_image_meta, req)
# 验证是否可以创建受保护的属性
self._enforce_create_protected_props(
    new_keys.difference(orig_keys), req)
# 如果有需要清除的属性
if purge_props:
    # 验证是否可以删除受保护的属性
    self._enforce_delete_protected_props(
        orig_keys.difference(new_keys), image_meta,
        orig_image_meta, req)

2.8 验证image属性值大小是否超过配额

self._enforce_image_property_quota(image_meta,
                                    orig_image_meta=orig_image_meta,
                                    purge_props=purge_props,
                                    req=req)

2.9 修改image元数据

image_meta = registry.update_image_metadate(req.context,
                                            id,
                                            image_meta,
                                            purge_props)

这里的update_image_metadata方法对应的是/glance/registry/client/v1/api.py:

def update_image_metadata(context, image_id, image_meta,
                        purge_props=False, from_state=None):
    LOG.debug("Updating image metadata for image %s...", image_id)
    c = get_registry_client(context)
    return c.update_image(image_id, image_meta, purge_props=purge_props,
                            from_state=from_state)

2.10 上传image data

if activatiing:
    # 上传image data值
    image_meta = self._handle_source(req, id, image_meta,
                                    image_data)



猜你喜欢

转载自blog.csdn.net/u011692924/article/details/80648429