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)