问题导读:
1、建立实例完整过程是如何检测配额和储备资源的?
2、返回的配额信息有哪些资源?
前面解析完了get_project_quotas方法,现在我们回到方法def _get_quotas(self, context, resources, keys, has_sync, project_id=None):
- def _get_quotas(self, context, resources, keys, has_sync, project_id=None):
- """
- 这是一个辅助的方法,从数据库获取特定的配额资源信息;
- """
-
- # 筛选资源;
- if has_sync:
- sync_filt = lambda x: hasattr(x, 'sync')#判断对象x是否包含sync的特性;
- else:
- sync_filt = lambda x: not hasattr(x, 'sync')#判断对象x是否不包含sync的特性;
- desired = set(keys)
- sub_resources = dict((k, v) for k, v in resources.items()
- if k in desired and sync_filt(v))
-
- # Make sure we accounted for all of them...
- # 确保所有磁盘配额资源都是已知的,否则引发异常,提示某些磁盘配额资源是未知的;
- if len(keys) != len(sub_resources):
- unknown = desired - set(sub_resources.keys())
- raise exception.QuotaResourceUnknown(unknown=sorted(unknown))
-
- # Grab and return the quotas (without usages)
- # 获取并返回配额信息;
- quotas = self.get_project_quotas(context, sub_resources,project_id,context.quota_class, usages=False)
-
- return dict((k, v['limit']) for k, v in quotas.items())
复制代码
可见获取了配额信息之后,以字典的方式返回各种资源的配额信息limit值(应该是三种资源:instances、ram、cores):
- return dict((k, v['limit']) for k, v in quotas.items())
复制代码
我们再回到方法def reserve(self, context, resources, deltas, expire=None, project_id=None):
- def reserve(self, context, resources, deltas, expire=None, project_id=None):
- """
- @@@@检测配额和储备资源;
- """
-
- # 如果expire没有指定,则采用默认参数的值;
- # reservation_expire:这个参数定义了预约(资源配额)的到期时间长度;
- # 参数的默认值为86400;
- if expire is None:
- expire = CONF.reservation_expire
- if isinstance(expire, (int, long)):
- expire = datetime.timedelta(seconds=expire)
- if isinstance(expire, datetime.timedelta):
- expire = timeutils.utcnow() + expire
- if not isinstance(expire, datetime.datetime):
- raise exception.InvalidReservationExpiration(expire=expire)
-
- # If project_id is None, then we use the project_id in context
- if project_id is None:
- project_id = context.project_id
-
- # 获取给定对象id值,即project_id确定的对象的配额信息;
- quotas = self._get_quotas(context, resources, deltas.keys(), has_sync=True, project_id=project_id)
-
- return db.quota_reserve(context, resources, quotas, deltas, expire,
- CONF.until_refresh, CONF.max_age,
- project_id=project_id)
复制代码
执行完语句quotas = self._get_quotas(context, resources, deltas.keys(), has_sync=True, project_id=project_id)之后,调试运行得到的返回结果为:
- 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:
- def quota_reserve(context, resources, quotas, deltas, expire, until_refresh, max_age, project_id=None):
-
- elevated = context.elevated()
- # 获取db_session的session;
- # get_session:返回一个SQLAlchemy session,若没有定义,则新建一个SQLAlchemy session;
- session = get_session()
- with session.begin():
-
- # 获取context中的project_id;
- if project_id is None:
- project_id = context.project_id
-
- # 从quota_usages表中获得当前工程的各种资源的使用情况;
- usages = _get_quota_usages(context, session, project_id)
-
- # Handle usage refresh
- # 注:deltas.keys() = ['instances', 'ram', 'cores']
- work = set(deltas.keys())
- while work:
- resource = work.pop()
-
- # Do we need to refresh the usage?
- refresh = False
- if resource not in usages:
- usages[resource] = _quota_usage_create(elevated,project_id,resource,0, 0,until_refresh or None,session=session)
- refresh = True
- elif usages[resource].in_use < 0:
- # Negative in_use count indicates a desync, so try to
- # heal from that...
- refresh = True
- elif usages[resource].until_refresh is not None:
- usages[resource].until_refresh -= 1
- if usages[resource].until_refresh <= 0:
- refresh = True
- elif max_age and (usages[resource].updated_at - timeutils.utcnow()).seconds >= max_age:
- refresh = True
-
- # OK, refresh the usage
- if refresh:
- # Grab the sync routine
- sync = resources[resource].sync
-
- updates = sync(elevated, project_id, session)
- for res, in_use in updates.items():
- # Make sure we have a destination for the usage!
- if res not in usages:
- usages[res] = _quota_usage_create(elevated,project_id,res,0, 0,until_refresh or None,session=session)
-
- # Update the usage
- usages[res].in_use = in_use
- usages[res].until_refresh = until_refresh or None
-
- work.discard(res)
-
- # Check for deltas that would go negative
- unders = [resource for resource, delta in deltas.items()
- if delta < 0 and
- delta + usages[resource].in_use < 0]
-
- overs = [resource for resource, delta in deltas.items()
- if quotas[resource] >= 0 and delta >= 0 and
- quotas[resource] < delta + usages[resource].total]
-
- # Create the reservations
- if not overs:
- reservations = []
- for resource, delta in deltas.items():
- reservation = reservation_create(elevated,str(uuid.uuid4()),usages[resource],project_id,resource, delta, expire,session=session)
- reservations.append(reservation.uuid)
-
- if delta > 0:
- usages[resource].reserved += delta
-
- # Apply updates to the usages table
- for usage_ref in usages.values():
- usage_ref.save(session=session)
-
- if unders:
- LOG.warning(_("Change will make usage less than 0 for the following resources: %(unders)s") % locals())
- if overs:
- usages = dict((k, dict(in_use=v['in_use'], reserved=v['reserved'])) for k, v in usages.items())
- raise exception.OverQuota(overs=sorted(overs), quotas=quotas,usages=usages)
-
- return reservations
复制代码
这个方法中包含的内容比较多,主要完成的就是对资源进行配额检测;
我们先来看一下传进来的参数值:
- # 程序运行上下文信息;
- context = <nova.context.RequestContext object at 0x4656d10>
- # 资源信息;
- 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>}
- # 已获取的规定的限制的配额信息;
- quotas = {'instances': 10, 'ram': 51200, 'cores': 20}
- # 建立一个实例要求的资源信息,也就是每建立一个实例或者删除一个实例,资源配额的变化量;
- deltas = {'instances': 1, 'ram': 2048L, 'cores': 1L}
- # reservations的有效期;
- expire = 2013-06-26 15:25:45.288831
-
- until_refresh = 0
-
- max_age = 0
- #对象ID值;
- project_id = 0e492e86f22e4d19bd523f1e7ca64566
复制代码
这里解释一下:
until_refresh是从配置信息CONF.until_refresh赋值的,它的解释是直到usage刷新,reservations的数目,默认值为0;
max_age是从配置信息CONF.max_age赋值的,它的解释是刷新usage之间停留的秒数,默认值为0;
比较容易理解的语句直接就写在代码的注释中。
下面来看语句:
- usages = _get_quota_usages(context, session, project_id)
复制代码
这条语句完成了从quota_usages表中获得当前工程的各种资源的使用情况;
来看方法_get_quota_usages:
- def _get_quota_usages(context, session, project_id):
- # 获取资源配额使用信息,以字典的形式返回;
- rows = model_query(context, models.QuotaUsage,
- read_deleted="no",
- session=session).\
- filter_by(project_id=project_id).\
- with_lockmode('update').\
- all()
- return dict((row.resource, row) for row in rows)
复制代码
类QuotaUsage定义了当前资源的使用情况;
- class QuotaUsage(BASE, NovaBase):
- """
- Represents the current usage for a given resource.
- 表示一个给定资源当前的使用情况;
- """
-
- __tablename__ = 'quota_usages'
- id = Column(Integer, primary_key=True)
-
- project_id = Column(String(255), index=True)
- resource = Column(String(255))
-
- in_use = Column(Integer)
- reserved = Column(Integer)
-
- @property
- def total(self):
- return self.in_use + self.reserved
-
- until_refresh = Column(Integer, nullable=True)
复制代码
来看下面一段代码:
- if resource not in usages:
- usages[resource] = _quota_usage_create(elevated,project_id,resource,0, 0,until_refresh or None,session=session)
- refresh = True
复制代码
如果当前的resource不在当前工程所使用的资源列表中,那么就把该资源添加进去,并且在数据库中增加一条相应的记录;为后续查询当前工程当前各种资源的使用情况做准备;
具体来看一下方法_quota_usage_create:
- def _quota_usage_create(context, project_id, resource, in_use, reserved,
- until_refresh, session=None):
- quota_usage_ref = models.QuotaUsage()
- quota_usage_ref.project_id = project_id
- quota_usage_ref.resource = resource
- quota_usage_ref.in_use = in_use
- quota_usage_ref.reserved = reserved
- quota_usage_ref.until_refresh = until_refresh
-
- quota_usage_ref.save(session=session)
-
- return quota_usage_ref
复制代码
再看一下类QuotaUsage:
- class QuotaUsage(BASE, NovaBase):
- """
- Represents the current usage for a given resource.
- 表示一个给定资源当前的使用情况;
- """
-
- __tablename__ = 'quota_usages'
- id = Column(Integer, primary_key=True)
-
- project_id = Column(String(255), index=True)
- resource = Column(String(255))
-
- in_use = Column(Integer)
- reserved = Column(Integer)
-
- @property
- def total(self):
- return self.in_use + self.reserved
-
- until_refresh = Column(Integer, nullable=True)
复制代码
应该比较好理解了;
看下一段代码:
- elif usages[resource].in_use < 0:
- # Negative in_use count indicates a desync, so try to
- # heal from that...
- refresh = True
-
- elif usages[resource].until_refresh is not None:
- usages[resource].until_refresh -= 1
- if usages[resource].until_refresh <= 0:
- refresh = True
-
- elif max_age and (usages[resource].updated_at - timeutils.utcnow()).seconds >= max_age:
- refresh = True
复制代码
这段代码的含义是:
如果当前的resource在当前工程的使用列表中,并且该资源的in_use小于0,说明资源数据信息不同步,则执行refresh(刷新);
如果当前的until_refresh不为空,那么将其减1,如果减1之后小于等于0,则执行refresh(刷新);(目前还不是太明白)
如果max_age不为空,并且该资源更新的时间减去当前的时间大于max_age,说明需要执行refresh(刷新);
下一篇博文将会继续分析这个方法,接下来将会介绍如何执行refresh(刷新)同步操作的。
上一篇:
OpenStack建立实例完整过程源码详细分析(7)
下一篇:
OpenStack建立实例完整过程源码详细分析(9)
|