• 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': <ID>,
    10                  'name': <NAME>,
    11                  'size': <SIZE>,
    12                  'disk_format': <DISK_FORMAT>,
    13                  'container_format': <CONTAINER_FORMAT>,
    14                  'checksum': <CHECKSUM>,
    15                  'min_disk': <MIN_DISK>,
    16                  'min_ram': <MIN_RAM>,
    17                  'store': <STORE>,
    18                  'status': <STATUS>,
    19                  'created_at': <TIMESTAMP>,
    20                  'updated_at': <TIMESTAMP>,
    21                  'deleted_at': <TIMESTAMP>|<NONE>,
    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=[image_list])
     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 = [make_image_dict(i) for i in images]
    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 [_normalize_locations(image.to_dict()) for image in query.all()]

      此方法最后将查询结果返回字典,至此,glance查询方法就此结束。

      如果有不对的地方,欢迎各位大神指出。

    PS:本博客欢迎转发,但请注明博客地址及作者~

      博客地址:http://www.cnblogs.com/voidy/

      <。)#)))≦

    评论
  • 相关阅读:
    python中元类(metaclass)的理解
    aiohttp
    async/await
    asyncio
    协程
    Bayesian Non-Exhaustive Classification A case study:online name disambiguation using temporal record streams
    技术网址
    网站
    各种网址
    OpenGL学习网址2
  • 原文地址:https://www.cnblogs.com/voidy/p/3984868.html
Copyright © 2020-2023  润新知