分享

OpenStack建立实例完整过程源码详细分析(8)

xioaxu790 发表于 2014-6-13 10:15:27 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 0 7478
问题导读:
1、建立实例完整过程是如何检测配额和储备资源的?
2、返回的配额信息有哪些资源?





  前面解析完了get_project_quotas方法,现在我们回到方法def _get_quotas(self, context, resources, keys, has_sync, project_id=None):

  1. def _get_quotas(self, context, resources, keys, has_sync, project_id=None):  
  2.         """
  3.         这是一个辅助的方法,从数据库获取特定的配额资源信息;
  4.         """  
  5.   
  6.         # 筛选资源;  
  7.         if has_sync:  
  8.             sync_filt = lambda x: hasattr(x, 'sync')#判断对象x是否包含sync的特性;  
  9.         else:  
  10.             sync_filt = lambda x: not hasattr(x, 'sync')#判断对象x是否不包含sync的特性;  
  11.         desired = set(keys)  
  12.         sub_resources = dict((k, v) for k, v in resources.items()  
  13.                              if k in desired and sync_filt(v))  
  14.   
  15.         # Make sure we accounted for all of them...  
  16.         # 确保所有磁盘配额资源都是已知的,否则引发异常,提示某些磁盘配额资源是未知的;  
  17.         if len(keys) != len(sub_resources):  
  18.             unknown = desired - set(sub_resources.keys())  
  19.             raise exception.QuotaResourceUnknown(unknown=sorted(unknown))  
  20.   
  21.         # Grab and return the quotas (without usages)  
  22.         # 获取并返回配额信息;  
  23.         quotas = self.get_project_quotas(context, sub_resources,project_id,context.quota_class, usages=False)  
  24.   
  25.         return dict((k, v['limit']) for k, v in quotas.items())  
复制代码

可见获取了配额信息之后,以字典的方式返回各种资源的配额信息limit值(应该是三种资源:instances、ram、cores):
  1. return dict((k, v['limit']) for k, v in quotas.items())  
复制代码

我们再回到方法def reserve(self, context, resources, deltas, expire=None, project_id=None):
  1. def reserve(self, context, resources, deltas, expire=None, project_id=None):  
  2.         """
  3.         @@@@检测配额和储备资源;
  4.         """  
  5.   
  6.         # 如果expire没有指定,则采用默认参数的值;  
  7.         # reservation_expire:这个参数定义了预约(资源配额)的到期时间长度;  
  8.         # 参数的默认值为86400;  
  9.         if expire is None:  
  10.             expire = CONF.reservation_expire  
  11.         if isinstance(expire, (int, long)):  
  12.             expire = datetime.timedelta(seconds=expire)  
  13.         if isinstance(expire, datetime.timedelta):  
  14.             expire = timeutils.utcnow() + expire  
  15.         if not isinstance(expire, datetime.datetime):  
  16.             raise exception.InvalidReservationExpiration(expire=expire)  
  17.   
  18.         # If project_id is None, then we use the project_id in context  
  19.         if project_id is None:  
  20.             project_id = context.project_id  
  21.   
  22.         # 获取给定对象id值,即project_id确定的对象的配额信息;         
  23.         quotas = self._get_quotas(context, resources, deltas.keys(), has_sync=True, project_id=project_id)  
  24.   
  25.         return db.quota_reserve(context, resources, quotas, deltas, expire,  
  26.                                 CONF.until_refresh, CONF.max_age,  
  27.                                 project_id=project_id)
复制代码

执行完语句quotas = self._get_quotas(context, resources, deltas.keys(), has_sync=True, project_id=project_id)之后,调试运行得到的返回结果为:
  1. quotas = {instances:10, ram:51200, cores:20}
复制代码

至此,方法_get_quotas解析完成,方法主要是完成了为project_id指定对象获取资源配额信息的功能;
b.2.2 解析语句return db.quota_reserve(context, resources, quotas, deltas, expire, CONF.until_refresh, CONF.max_age, project_id=project_id)
    我们来看方法quota_reserve:

  1. def quota_reserve(context, resources, quotas, deltas, expire, until_refresh, max_age, project_id=None):  
  2.   
  3.     elevated = context.elevated()  
  4.     # 获取db_session的session;  
  5.     # get_session:返回一个SQLAlchemy session,若没有定义,则新建一个SQLAlchemy session;  
  6.     session = get_session()  
  7.     with session.begin():  
  8.          
  9.         # 获取context中的project_id;  
  10.         if project_id is None:  
  11.             project_id = context.project_id  
  12.   
  13.         # 从quota_usages表中获得当前工程的各种资源的使用情况;  
  14.         usages = _get_quota_usages(context, session, project_id)  
  15.   
  16.         # Handle usage refresh  
  17.         # 注:deltas.keys() = ['instances', 'ram', 'cores']  
  18.         work = set(deltas.keys())  
  19.         while work:  
  20.             resource = work.pop()  
  21.   
  22.             # Do we need to refresh the usage?  
  23.             refresh = False  
  24.             if resource not in usages:  
  25.                 usages[resource] = _quota_usage_create(elevated,project_id,resource,0, 0,until_refresh or None,session=session)  
  26.                 refresh = True  
  27.             elif usages[resource].in_use < 0:  
  28.                 # Negative in_use count indicates a desync, so try to  
  29.                 # heal from that...  
  30.                 refresh = True  
  31.             elif usages[resource].until_refresh is not None:  
  32.                 usages[resource].until_refresh -= 1  
  33.                 if usages[resource].until_refresh <= 0:  
  34.                     refresh = True  
  35.             elif max_age and (usages[resource].updated_at - timeutils.utcnow()).seconds >= max_age:  
  36.                 refresh = True  
  37.   
  38.             # OK, refresh the usage  
  39.             if refresh:  
  40.                 # Grab the sync routine  
  41.                 sync = resources[resource].sync  
  42.   
  43.                 updates = sync(elevated, project_id, session)  
  44.                 for res, in_use in updates.items():  
  45.                     # Make sure we have a destination for the usage!  
  46.                     if res not in usages:  
  47.                         usages[res] = _quota_usage_create(elevated,project_id,res,0, 0,until_refresh or None,session=session)  
  48.   
  49.                     # Update the usage  
  50.                     usages[res].in_use = in_use  
  51.                     usages[res].until_refresh = until_refresh or None  
  52.   
  53.                     work.discard(res)  
  54.   
  55.         # Check for deltas that would go negative  
  56.         unders = [resource for resource, delta in deltas.items()  
  57.                   if delta < 0 and  
  58.                   delta + usages[resource].in_use < 0]  
  59.   
  60.         overs = [resource for resource, delta in deltas.items()  
  61.                  if quotas[resource] >= 0 and delta >= 0 and  
  62.                  quotas[resource] < delta + usages[resource].total]  
  63.   
  64.         # Create the reservations  
  65.         if not overs:  
  66.             reservations = []  
  67.             for resource, delta in deltas.items():  
  68.                 reservation = reservation_create(elevated,str(uuid.uuid4()),usages[resource],project_id,resource, delta, expire,session=session)  
  69.                 reservations.append(reservation.uuid)  
  70.   
  71.                 if delta > 0:  
  72.                     usages[resource].reserved += delta  
  73.   
  74.         # Apply updates to the usages table  
  75.         for usage_ref in usages.values():  
  76.             usage_ref.save(session=session)  
  77.   
  78.     if unders:  
  79.         LOG.warning(_("Change will make usage less than 0 for the following resources: %(unders)s") % locals())  
  80.     if overs:  
  81.         usages = dict((k, dict(in_use=v['in_use'], reserved=v['reserved'])) for k, v in usages.items())  
  82.         raise exception.OverQuota(overs=sorted(overs), quotas=quotas,usages=usages)  
  83.   
  84.     return reservations  
复制代码

这个方法中包含的内容比较多,主要完成的就是对资源进行配额检测;
    我们先来看一下传进来的参数值:

  1. # 程序运行上下文信息;  
  2. context = <nova.context.RequestContext object at 0x4656d10>  
  3. # 资源信息;  
  4. resources = {'metadata_items': <nova.quota.AbsoluteResource object at 0x276e790>, 'injected_file_content_bytes': <nova.quota.AbsoluteResource object at 0x276e810>, 'ram': <nova.quota.ReservableResource object at 0x276e6d0>, 'floating_ips': <nova.quota.ReservableResource object at 0x276e710>, 'security_group_rules': <nova.quota.CountableResource object at 0x276e8d0>, 'instances': <nova.quota.ReservableResource object at 0x276e650>, 'key_pairs': <nova.quota.CountableResource object at 0x276e910>, 'injected_files': <nova.quota.AbsoluteResource object at 0x276e7d0>, 'cores': <nova.quota.ReservableResource object at 0x276e690>, 'fixed_ips': <nova.quota.ReservableResource object at 0x276e750>, 'injected_file_path_bytes': <nova.quota.AbsoluteResource object at 0x276e850>, 'security_groups': <nova.quota.ReservableResource object at 0x276e890>}  
  5. # 已获取的规定的限制的配额信息;  
  6. quotas = {'instances': 10, 'ram': 51200, 'cores': 20}  
  7. # 建立一个实例要求的资源信息,也就是每建立一个实例或者删除一个实例,资源配额的变化量;  
  8. deltas = {'instances': 1, 'ram': 2048L, 'cores': 1L}  
  9. # reservations的有效期;  
  10. expire = 2013-06-26 15:25:45.288831  
  11.   
  12. until_refresh = 0  
  13.   
  14. max_age = 0  
  15. #对象ID值;  
  16. project_id = 0e492e86f22e4d19bd523f1e7ca64566  
复制代码

这里解释一下:
    until_refresh是从配置信息CONF.until_refresh赋值的,它的解释是直到usage刷新,reservations的数目,默认值为0;
    max_age是从配置信息CONF.max_age赋值的,它的解释是刷新usage之间停留的秒数,默认值为0;
    比较容易理解的语句直接就写在代码的注释中。
    下面来看语句:

  1. usages = _get_quota_usages(context, session, project_id)  
复制代码

这条语句完成了从quota_usages表中获得当前工程的各种资源的使用情况;
    来看方法_get_quota_usages:

  1. def _get_quota_usages(context, session, project_id):  
  2.     # 获取资源配额使用信息,以字典的形式返回;  
  3.     rows = model_query(context, models.QuotaUsage,  
  4.                        read_deleted="no",  
  5.                        session=session).\  
  6.                    filter_by(project_id=project_id).\  
  7.                    with_lockmode('update').\  
  8.                    all()  
  9.     return dict((row.resource, row) for row in rows)  
复制代码

类QuotaUsage定义了当前资源的使用情况;
  1. class QuotaUsage(BASE, NovaBase):  
  2.     """
  3.     Represents the current usage for a given resource.
  4.     表示一个给定资源当前的使用情况;
  5.     """  
  6.   
  7.     __tablename__ = 'quota_usages'  
  8.     id = Column(Integer, primary_key=True)  
  9.   
  10.     project_id = Column(String(255), index=True)  
  11.     resource = Column(String(255))  
  12.   
  13.     in_use = Column(Integer)  
  14.     reserved = Column(Integer)  
  15.     @property  
  16.     def total(self):  
  17.         return self.in_use + self.reserved  
  18.   
  19.     until_refresh = Column(Integer, nullable=True)
复制代码

   来看下面一段代码:
  1. if resource not in usages:  
  2.     usages[resource] = _quota_usage_create(elevated,project_id,resource,0, 0,until_refresh or None,session=session)  
  3. refresh = True  
复制代码

   如果当前的resource不在当前工程所使用的资源列表中,那么就把该资源添加进去,并且在数据库中增加一条相应的记录;为后续查询当前工程当前各种资源的使用情况做准备;
    具体来看一下方法_quota_usage_create:
  1. def _quota_usage_create(context, project_id, resource, in_use, reserved,  
  2.                        until_refresh, session=None):  
  3.     quota_usage_ref = models.QuotaUsage()  
  4.     quota_usage_ref.project_id = project_id  
  5.     quota_usage_ref.resource = resource  
  6.     quota_usage_ref.in_use = in_use  
  7.     quota_usage_ref.reserved = reserved  
  8.     quota_usage_ref.until_refresh = until_refresh  
  9.   
  10.     quota_usage_ref.save(session=session)  
  11.   
  12.     return quota_usage_ref  
复制代码



再看一下类QuotaUsage:
  1. class QuotaUsage(BASE, NovaBase):  
  2.     """
  3.     Represents the current usage for a given resource.
  4.     表示一个给定资源当前的使用情况;
  5.     """  
  6.   
  7.     __tablename__ = 'quota_usages'  
  8.     id = Column(Integer, primary_key=True)  
  9.   
  10.     project_id = Column(String(255), index=True)  
  11.     resource = Column(String(255))  
  12.   
  13.     in_use = Column(Integer)  
  14.     reserved = Column(Integer)  
  15.     @property  
  16.     def total(self):  
  17.         return self.in_use + self.reserved  
  18.   
  19.     until_refresh = Column(Integer, nullable=True)  
复制代码

应该比较好理解了;
    看下一段代码:
  1. elif usages[resource].in_use < 0:  
  2.     # Negative in_use count indicates a desync, so try to  
  3.     # heal from that...  
  4.     refresh = True  
  5.   
  6. elif usages[resource].until_refresh is not None:  
  7.     usages[resource].until_refresh -= 1  
  8.     if usages[resource].until_refresh <= 0:  
  9.         refresh = True  
  10.   
  11. elif max_age and (usages[resource].updated_at - timeutils.utcnow()).seconds >= max_age:  
  12.     refresh = True  
复制代码


    这段代码的含义是:
    如果当前的resource在当前工程的使用列表中,并且该资源的in_use小于0,说明资源数据信息不同步,则执行refresh(刷新);
    如果当前的until_refresh不为空,那么将其减1,如果减1之后小于等于0,则执行refresh(刷新);(目前还不是太明白)
    如果max_age不为空,并且该资源更新的时间减去当前的时间大于max_age,说明需要执行refresh(刷新);
    下一篇博文将会继续分析这个方法,接下来将会介绍如何执行refresh(刷新)同步操作的。

上一篇:
OpenStack建立实例完整过程源码详细分析(7)


下一篇:
OpenStack建立实例完整过程源码详细分析(9)



没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条