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等基本方法。
1 @utils.mutating
2 #创建镜像对象:这里主要分析image_factory和image_repo的获取;try内容是生成一个image对象,并存放到repo中。此时只是存储到数据库中,并没有生成相应的image文件,size大小为None
3 def create(self, req, image, extra_properties, tags):
4 image_factory = self.gateway.get_image_factory(req.context)
5 image_repo = self.gateway.get_repo(req.context)
6 try:
7 image = image_factory.new_image(extra_properties=extra_properties,
8 tags=tags, **image)
9 image_repo.add(image)
10 except (exception.DuplicateLocation,
11 exception.Invalid) as e:
12 raise webob.exc.HTTPBadRequest(explanation=e.msg)
13 except (exception.ReservedProperty,
14 exception.ReadonlyProperty) as e:
15 raise webob.exc.HTTPForbidden(explanation=e.msg)
16 except exception.Forbidden as e:
17 LOG.debug("User not permitted to create image")
18 raise webob.exc.HTTPForbidden(explanation=e.msg)
19 except exception.LimitExceeded as e:
20 LOG.warn(encodeutils.exception_to_unicode(e))
21 raise webob.exc.HTTPRequestEntityTooLarge(
22 explanation=e.msg, request=req, content_type='text/plain')
23 except exception.Duplicate as e:
24 raise webob.exc.HTTPConflict(explanation=e.msg)
25 except exception.NotAuthenticated as e:
26 raise webob.exc.HTTPUnauthorized(explanation=e.msg)
27 except TypeError as e:
28 LOG.debug(encodeutils.exception_to_unicode(e))
29 raise webob.exc.HTTPBadRequest(explanation=e)
30
31 return image
1 #依次将domain、location、quota、policy、notifier、authorization作为构造对
2 #像参数。返回一个FactoryProxy。
3 def get_image_factory(self, context):
4 image_factory = glance.domain.ImageFactory()
5 store_image_factory = glance.location.ImageFactoryProxy(
6 image_factory, context, self.store_api, self.store_utils)
7 quota_image_factory = glance.quota.ImageFactoryProxy(
8 store_image_factory, context, self.db_api, self.store_utils)
9 policy_image_factory = policy.ImageFactoryProxy(
10 quota_image_factory, context, self.policy)
11 notifier_image_factory = glance.notifier.ImageFactoryProxy(
12 policy_image_factory, context, self.notifier)
13 #api.conf中property_protection_file默认为None
14 if property_utils.is_property_protection_enabled():
15 property_rules = property_utils.PropertyRules(self.policy)
16 pif = property_protections.ProtectedImageFactoryProxy(
17 notifier_image_factory, context, property_rules)
18 authorized_image_factory = authorization.ImageFactoryProxy(
19 pif, context)
20 else:
21 #执行如下流程
22 authorized_image_factory = authorization.ImageFactoryProxy(
23 notifier_image_factory, context)
24 return authorized_image_factory
1 #获取仓库repo,用于存储image对象。
2 def get_repo(self, context):
3 image_repo = glance.db.ImageRepo(context, self.db_api)
4 store_image_repo = glance.location.ImageRepoProxy(
5 image_repo, context, self.store_api, self.store_utils)
6 quota_image_repo = glance.quota.ImageRepoProxy(
7 store_image_repo, context, self.db_api, self.store_utils)
8 policy_image_repo = policy.ImageRepoProxy(
9 quota_image_repo, context, self.policy)
10 notifier_image_repo = glance.notifier.ImageRepoProxy(
11 policy_image_repo, context, self.notifier)
12 if property_utils.is_property_protection_enabled():
13 property_rules = property_utils.PropertyRules(self.policy)
14 pir = property_protections.ProtectedImageRepoProxy(
15 notifier_image_repo, context, property_rules)
16 authorized_image_repo = authorization.ImageRepoProxy(
17 pir, context)
18 else:
19 authorized_image_repo = authorization.ImageRepoProxy(
20 notifier_image_repo, context)
21
22 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为镜像文件的实际上传过程。
1 def set_data(self, data, size=None):
2 if size is None:
3 size = 0 # NOTE(markwash): zero -> unknown size
4
5 # Create the verifier for signature verification (if correct properties
6 # are present)
7 extra_props = self.image.extra_properties
8 if (signature_utils.should_create_verifier(extra_props)):
9 # NOTE(bpoulos): if creating verifier fails, exception will be
10 # raised
11 img_signature = extra_props[signature_utils.SIGNATURE]
12 hash_method = extra_props[signature_utils.HASH_METHOD]
13 key_type = extra_props[signature_utils.KEY_TYPE]
14 cert_uuid = extra_props[signature_utils.CERT_UUID]
15 verifier = signature_utils.get_verifier(
16 context=self.context,
17 img_signature_certificate_uuid=cert_uuid,
18 img_signature_hash_method=hash_method,
19 img_signature=img_signature,
20 img_signature_key_type=key_type
21 )
22 else:
23 verifier = None
24 #用glance_store模块完成文件上传到glance目录中,
25 location, size, checksum, loc_meta = self.store_api.add_to_backend(
26 CONF,
27 self.image.image_id,
28 utils.LimitingReader(utils.CooperativeReader(data),
29 CONF.image_size_cap),
30 size,
31 context=self.context,
32 verifier=verifier)
33
34 # NOTE(bpoulos): if verification fails, exception will be raised
35 if verifier:
36 try:
37 verifier.verify()
38 LOG.info(_LI("Successfully verified signature for image %s"),
39 self.image.image_id)
40 except crypto_exception.InvalidSignature:
41 raise cursive_exception.SignatureVerificationError(
42 _('Signature verification failed')
43 )
44 #更新镜像相关属性
45 self.image.locations = [{'url': location, 'metadata': loc_meta,
46 'status': 'active'}]
47 self.image.size = size
48 self.image.checksum = checksum
49 self.image.status = 'active'
默认情况下,镜像的virtual_size值为None。这里如果需要该值,则需要调用qemu-img info image-file获取virtual-size和size大小。修改1:获取virtual-size大小;修改2:设置image的virtual-size。
1 #glance.common.utils.py增加get_virtual_size方法
2 def get_virtual_size(image_id):
3 try:
4 file_path = os.path.join(CONF.glance_store.filesystem_store_datadir, image_id)
5 stdout, stderr = putils.trycmd('qemu-img', 'info',
6 '--output=json', file_path,
7 prlimit=utils.QEMU_IMG_PROC_LIMITS,
8 log_errors=putils.LOG_ALL_ERRORS)
9 except OSError as exc:
10 if exc.errno != 2:
11 with excutils.save_and_reraise_exception():
12 exc_message = encodeutils.exception_to_unicode(exc)
13 msg = _LE('Failed to execute introspection '
14 '%(task_id)s: %(exc)s')
15 LOG.error(msg, {'task_id': self.task_id,
16 'exc': exc_message})
17 return
18
19 if stderr:
20 raise RuntimeError(stderr)
21 metadata = json.loads(stdout)
22 virtual_size = metadata.get('virtual-size', 0)
23 return virtual_size
glance.location中set_data方法增加:self.image.virtual_size = utils.get_virtual_size(self.image.image_id)