xuyangus 发表于 2015-4-11 15:30:30

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]
查看完整版本: OpenStack之Glance源码简析