|
O版中镜像RESFUL请求默认是v2,与v1相比更复杂了。这里主要分析镜像创建(glance image-create)流程。
glanceclient的命令do_image_create(v2/shell.py)分别执行了image = gc.images.create(**fields)、do_image_upload(gc, args)两步,分别对应api中create、upload方法
1、glance.api.v2.images:ImageController中包含了create、index、delete等基本方法。
@utils.mutating
#创建镜像对象:这里主要分析image_factory和image_repo的获取;try内容是生成一个image对象,并存放到repo中。此时只是存储到数据库中,并没有生成相应的image文件,size大小为None
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)
try:
image = image_factory.new_image(extra_properties=extra_properties,
tags=tags, **image)
image_repo.add(image)
except (exception.DuplicateLocation,
exception.Invalid) as e:
raise webob.exc.HTTPBadRequest(explanation=e.msg)
except (exception.ReservedProperty,
exception.ReadonlyProperty) as e:
raise webob.exc.HTTPForbidden(explanation=e.msg)
except exception.Forbidden as e:
LOG.debug("User not permitted to create image")
raise webob.exc.HTTPForbidden(explanation=e.msg)
except exception.LimitExceeded as e:
LOG.warn(encodeutils.exception_to_unicode(e))
raise webob.exc.HTTPRequestEntityTooLarge(
explanation=e.msg, request=req, content_type='text/plain')
except exception.Duplicate as e:
raise webob.exc.HTTPConflict(explanation=e.msg)
except exception.NotAuthenticated as e:
raise webob.exc.HTTPUnauthorized(explanation=e.msg)
except TypeError as e:
LOG.debug(encodeutils.exception_to_unicode(e))
raise webob.exc.HTTPBadRequest(explanation=e)
return image
#依次将domain、location、quota、policy、notifier、authorization作为构造对
#像参数。返回一个FactoryProxy。
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)
#api.conf中property_protection_file默认为None
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
#获取仓库repo,用于存储image对象。
def get_repo(self, context):
image_repo = glance.db.ImageRepo(context, self.db_api)
store_image_repo = glance.location.ImageRepoProxy(
image_repo, context, self.store_api, self.store_utils)
quota_image_repo = glance.quota.ImageRepoProxy(
store_image_repo, context, self.db_api, self.store_utils)
policy_image_repo = policy.ImageRepoProxy(
quota_image_repo, context, self.policy)
notifier_image_repo = glance.notifier.ImageRepoProxy(
policy_image_repo, context, self.notifier)
if property_utils.is_property_protection_enabled():
property_rules = property_utils.PropertyRules(self.policy)
pir = property_protections.ProtectedImageRepoProxy(
notifier_image_repo, context, property_rules)
authorized_image_repo = authorization.ImageRepoProxy(
pir, context)
else:
authorized_image_repo = authorization.ImageRepoProxy(
notifier_image_repo, context)
return authorized_image_repo
2、glance.api.v2.image_data:ImageDataController中upload方法主要包括以下几步:
a、获取image_repo = self.gateway.get_repo(req.context)
b、image = image_repo.get(image_id)
image.status = 'saving'
c、image_repo.save(image, from_state='queued') save方法会将镜像image信息更新到db
image.set_data(data, size)
d、image_repo.save(image, from_state='saving') 这里data是一个eventlet.wsgi.Input对象
3、重点分析set_data方法:其分别执行了glance.notifier和glance.location模块中set_data方法。其中glance.location.ImageProxy中set_data为镜像文件的实际上传过程。
def set_data(self, data, size=None):
if size is None:
size = 0 # NOTE(markwash): zero -> unknown size
# Create the verifier for signature verification (if correct properties
# are present)
extra_props = self.image.extra_properties
if (signature_utils.should_create_verifier(extra_props)):
# NOTE(bpoulos): if creating verifier fails, exception will be
# raised
img_signature = extra_props[signature_utils.SIGNATURE]
hash_method = extra_props[signature_utils.HASH_METHOD]
key_type = extra_props[signature_utils.KEY_TYPE]
cert_uuid = extra_props[signature_utils.CERT_UUID]
verifier = signature_utils.get_verifier(
context=self.context,
img_signature_certificate_uuid=cert_uuid,
img_signature_hash_method=hash_method,
img_signature=img_signature,
img_signature_key_type=key_type
)
else:
verifier = None
#用glance_store模块完成文件上传到glance目录中,
location, size, checksum, loc_meta = self.store_api.add_to_backend(
CONF,
self.image.image_id,
utils.LimitingReader(utils.CooperativeReader(data),
CONF.image_size_cap),
size,
context=self.context,
verifier=verifier)
# NOTE(bpoulos): if verification fails, exception will be raised
if verifier:
try:
verifier.verify()
LOG.info(_LI("Successfully verified signature for image %s"),
self.image.image_id)
except crypto_exception.InvalidSignature:
raise cursive_exception.SignatureVerificationError(
_('Signature verification failed')
)
#更新镜像相关属性
self.image.locations = [{'url': location, 'metadata': loc_meta,
'status': 'active'}]
self.image.size = size
self.image.checksum = checksum
self.image.status = 'active'
默认情况下,镜像的virtual_size值为None。这里如果需要该值,则需要调用qemu-img info image-file获取virtual-size和size大小。修改1:获取virtual-size大小;修改2:设置image的virtual-size。
#glance.common.utils.py增加get_virtual_size方法
def get_virtual_size(image_id):
try:
file_path = os.path.join(CONF.glance_store.filesystem_store_datadir, image_id)
stdout, stderr = putils.trycmd('qemu-img', 'info',
'--output=json', file_path,
prlimit=utils.QEMU_IMG_PROC_LIMITS,
log_errors=putils.LOG_ALL_ERRORS)
except OSError as exc:
if exc.errno != 2:
with excutils.save_and_reraise_exception():
exc_message = encodeutils.exception_to_unicode(exc)
msg = _LE('Failed to execute introspection '
'%(task_id)s: %(exc)s')
LOG.error(msg, {'task_id': self.task_id,
'exc': exc_message})
return
if stderr:
raise RuntimeError(stderr)
metadata = json.loads(stdout)
virtual_size = metadata.get('virtual-size', 0)
return virtual_size
glance.location中set_data方法增加:self.image.virtual_size = utils.get_virtual_size(self.image.image_id) |
|