OpenStack之Glance源码简析
Glance简介OpenStack镜像服务器是一套虚拟机镜像发现、注册、检索。
glance架构图:
Glance源码结构:
glance/api:主要负责接收响应镜像管理命令的Restful请求,分析消息请求信息并分发其所带的命令(如新增,删除,更新等)。默认绑定端口是9292。
glance/registry:主要负责接收响应镜像元数据命令的Restful请求。分析消息请求信息并分发其所带的命令(如获取元数据,更新元数据等)。默认绑定的端口是9191。
glance/db:主要负责与数据库Mysql的交互
glance/store:主要负责存储适配
本次主要从简单的查询来简析glance源码,看一下glance代码是如何执行的。
查询的API:/v1/images/detail,method:GET,有兴趣的朋友可以用火狐浏览器自带的RESTClient来进行REST服务的测试。
好了,废话不多说,源码走起。
源码分析
图为glance/api/v1中的源码目录树。
此次就从glance的查询来分析glance的源码,从horizon的传过来的HTTP请求首先来到glance/api/v1/router.py中,查找匹配的HTTP请求:
代码如下:
1 mapper.connect("/images/detail",
2 controller=images_resource,
3 action='detail',
4 conditions={'method': ['GET']})
可以看到,这与Glance的API文档是相符的,GET请求,然后是/v1/images/detail。
而后,找到匹配的请求之后,就会进入glance/api/v1/images.py文件中,下面是有关的代码:
1 def detail(self, req):
2 """
3 Returns detailed information for all available images
4
5 :param req: The WSGI/Webob Request object
6 :retval The response body is a mapping of the following form::
7
8 {'images': [
9 {'id': ,
10 'name': ,
11 'size': ,
12 'disk_format': ,
13 'container_format': ,
14 'checksum': ,
15 'min_disk': ,
16 'min_ram': ,
17 'store': ,
18 'status': ,
19 'created_at': ,
20 'updated_at': ,
21 'deleted_at': |,
22 'properties': {'distro': 'Ubuntu 10.04 LTS', ...}}, ...
23 ]}
24 """
25 self._enforce(req, 'get_images')
26 params = self._get_query_params(req)
27 try:
28 images = registry.get_images_detail(req.context, **params)
29 # Strip out the Location attribute. Temporary fix for
30 # LP Bug #755916. This information is still coming back
31 # from the registry, since the API server still needs access
32 # to it, however we do not return this potential security
33 # information to the API end user...
34 for image in images:
35 redact_loc(image, copy_dict=False)
36 self._enforce_read_protected_props(image, req)
37 except exception.Invalid as e:
38 raise HTTPBadRequest(explanation="%s" % e)
39 return dict(images=images)
26行是根据获取查询条件,在此不深入谈,想了解的朋友可以留言,主要查询语句在try里面,也就是第28行:
1 images = registry.get_images_detail(req.context, **params)
从这里,查询就跳入了glance/registry/client/v1/api.py文件中。
图为glance/registry的目录树,glance/registry/client/v1/api.py中相关代码如下:
1 def get_images_detail(context, **kwargs):
2 c = get_registry_client(context)
3 return c.get_images_detailed(**kwargs)
首先,会在glance/registry/client/v1/api.py文件进行registry端口的认证连接,
1 def get_registry_client(cxt):
2 global _CLIENT_CREDS, _CLIENT_KWARGS, _CLIENT_HOST, _CLIENT_PORT
3 global _METADATA_ENCRYPTION_KEY
4 kwargs = _CLIENT_KWARGS.copy()
5 if CONF.use_user_token:
6 kwargs['auth_tok'] = cxt.auth_tok
7 if _CLIENT_CREDS:
8 kwargs['creds'] = _CLIENT_CREDS
9
10 if CONF.send_identity_headers:
11 identity_headers = {
12 'X-User-Id': cxt.user,
13 'X-Tenant-Id': cxt.tenant,
14 'X-Roles': ','.join(cxt.roles),
15 'X-Identity-Status': 'Confirmed',
16 'X-Service-Catalog': jsonutils.dumps(cxt.service_catalog),
17 }
18 kwargs['identity_headers'] = identity_headers
19 return client.RegistryClient(_CLIENT_HOST, _CLIENT_PORT,
20 _METADATA_ENCRYPTION_KEY, **kwargs)
然后第三行返回查询,进入glance/registry/client/v1/client.py中:
1 def get_images_detailed(self, **kwargs):
2 """
3 Returns a list of detailed image data mappings from Registry
4
5 :param filters: dict of keys & expected values to filter results
6 :param marker: image id after which to start page
7 :param limit: max number of images to return
8 :param sort_key: results will be ordered by this image attribute
9 :param sort_dir: direction in which to to order results (asc, desc)
10 """
11 params = self._extract_params(kwargs, images.SUPPORTED_PARAMS)
12 res = self.do_request("GET", "/images/detail", params=params)
13 image_list = jsonutils.loads(res.read())['images']
14 for image in image_list:
15 image = self.decrypt_metadata(image)
16 return image_list
在12行再次发出请求,然后在glance/registry/api/v1/__init__.py中查找匹配的请求:
1 mapper.connect("/images/detail",
2 controller=images_resource,
3 action="detail",
4 conditions={'method': ['GET']})
在glance/registry/api/v1/images.py中找到相应的方法进行查询:
1 def detail(self, req):
2 """Return a filtered list of public, non-deleted images in detail
3
4 :param req: the Request object coming from the wsgi layer
5 :retval a mapping of the following form::
6
7 dict(images=)
8
9 Where image_list is a sequence of mappings containing
10 all image model fields.
11 """
12 params = self._get_query_params(req)
13
14 images = self._get_images(req.context, **params)
15 image_dicts =
16 LOG.info(_("Returning detailed image list"))
17 return dict(images=image_dicts)
第14行又调用glance/registry/api/v1/images.py的_get_images(req.context, **params)方法:
1 def _get_images(self, context, filters, **params):
2 """Get images, wrapping in exception if necessary."""
3 # NOTE(markwash): for backwards compatibility, is_public=True for
4 # admins actually means "treat me as if I'm not an admin and show me
5 # all my images"
6 if context.is_admin and params.get('is_public') is True:
7 params['admin_as_user'] = True
8 del params['is_public']
9 try:
10 return self.db_api.image_get_all(context, filters=filters,
11 **params)
12 except exception.NotFound:
13 LOG.info(_("Invalid marker. Image %(id)s could not be "
14 "found.") % {'id': params.get('marker')})
15 msg = _("Invalid marker. Image could not be found.")
16 raise exc.HTTPBadRequest(explanation=msg)
17 except exception.Forbidden:
18 LOG.info(_("Access denied to image %(id)s but returning "
19 "'not found'") % {'id': params.get('marker')})
20 msg = _("Invalid marker. Image could not be found.")
21 raise exc.HTTPBadRequest(explanation=msg)
22 except Exception:
23 LOG.exception(_("Unable to get images"))
24 raise
在第10行中,进入glance/db/sqlalchemy/api.py中:
图为glance/db的目录树。下面是调用的glance/db/sqlalchemy/api.py中的image_get_all方法,
1 def image_get_all(context, filters=None, marker=None, limit=None,
2 sort_key='created_at', sort_dir='desc',
3 member_status='accepted', is_public=None,
4 admin_as_user=False):
5 """
6 Get all images that match zero or more filters.
7
8 :param filters: dict of filter keys and values. If a 'properties'
9 key is present, it is treated as a dict of key/value
10 filters on the image properties attribute
11 :param marker: image id after which to start page
12 :param limit: maximum number of images to return
13 :param sort_key: image attribute by which results should be sorted
14 :param sort_dir: direction in which results should be sorted (asc, desc)
15 :param member_status: only return shared images that have this membership
16 status
17 :param is_public: If true, return only public images. If false, return
18 only private and shared images.
19 :param admin_as_user: For backwards compatibility. If true, then return to
20 an admin the equivalent set of images which it would see
21 if it were a regular user
22 """
23 filters = filters or {}
24
25 visibility = filters.pop('visibility', None)
26 showing_deleted = 'changes-since' in filters or filters.get('deleted',
27 False)
28
29 img_conditions, prop_conditions, tag_conditions = \
30 _make_conditions_from_filters(filters, is_public)
31
32 query = _select_images_query(context,
33 img_conditions,
34 admin_as_user,
35 member_status,
36 visibility)
37
38 if visibility is not None:
39 if visibility == 'public':
40 query = query.filter(models.Image.is_public == True)
41 elif visibility == 'private':
42 query = query.filter(models.Image.is_public == False)
43
44 if prop_conditions:
45 for prop_condition in prop_conditions:
46 query = query.join(models.ImageProperty, aliased=True)\
47 .filter(sa_sql.and_(*prop_condition))
48
49 if tag_conditions:
50 for tag_condition in tag_conditions:
51 query = query.join(models.ImageTag, aliased=True)\
52 .filter(sa_sql.and_(*tag_condition))
53
54 marker_image = None
55 if marker is not None:
56 marker_image = _image_get(context,
57 marker,
58 force_show_deleted=showing_deleted)
59
60 sort_keys = ['created_at', 'id']
61 sort_keys.insert(0, sort_key) if sort_key not in sort_keys else sort_keys
62
63 query = _paginate_query(query, models.Image, limit,
64 sort_keys,
65 marker=marker_image,
66 sort_dir=sort_dir)
67
68 query = query.options(sa_orm.joinedload(models.Image.properties))\
69 .options(sa_orm.joinedload(models.Image.locations))
70
71 return
此方法最后将查询结果返回字典,至此,glance查询方法就此结束。
如果有不对的地方,欢迎各位大神指出。
PS:本博客欢迎转发,但请注明博客地址及作者~
博客地址:http://www.iyunv.com/voidy/
页:
[1]