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)