pig2 发表于 2015-3-1 19:11:28

openstack nova 基础知识——Quota(配额管理)

本帖最后由 pig2 于 2015-3-1 19:12 编辑

问题导读

1.什么是配额(quota)管理?
2.如何实现配额(quota)管理?
3.如何测试配额(quota)管理?


static/image/hrline/4.gif



一、什么是配额(quota)管理
简单的讲就是控制用户资源的数量。在openstack里,管理员为每一个工程(project)分配的资源都是有一定限制的,这些资源包括实例(instance)、cpu、内存、存储空间等等,不能让一个工程无限制的使用资源,所以配额管理针对的单位是工程(project)。先来个感性的认识,看一下dashboard里的一个工程的overview:



管理员给这个工程的资源配额是最多创建10个实例,最多使用20个vcpu,最多使用5G的内存==,只要达到某一个资源的使用上限,就会出现异常,这就是配额管理。

二、如何实现
nova里配额管理是在~/nova/quota.py中实现的,首先来看一下这个模块的静态类图:






使用时是直接调用QuotaEngine类中的方法,而真正实现功能的是DbQuotaDriver这个类,在DbQuotaDriver类中比较重要的是reserve(), commit(), rollback()这三个方法,这三个方法共同构成了配额管理一个很重要的特性:事务性,这里所说的事务性,就是当资源分配失败或者已达到配额上限等这些异常情况发生时,对已经修改过的数据库,回滚到分配之前的状态,如果不设置成这种特性,那配额管理就乱套了。
资源在配额管理中封装成了一个简单的类体系,资源被分为三类:ReservableResource, AbsoulteResource, CountableResource,至于这三种分类的区别,我现在还不是很清楚,在nova中,是这样来分类的:

resources = [
    ReservableResource('instances', _sync_instances, 'quota_instances'),
    ReservableResource('cores', _sync_instances, 'quota_cores'),
    ReservableResource('ram', _sync_instances, 'quota_ram'),
    ReservableResource('volumes', _sync_volumes, 'quota_volumes'),
    ReservableResource('gigabytes', _sync_volumes, 'quota_gigabytes'),
    ReservableResource('floating_ips', _sync_floating_ips,
                     'quota_floating_ips'),
    AbsoluteResource('metadata_items', 'quota_metadata_items'),
    AbsoluteResource('injected_files', 'quota_injected_files'),
    AbsoluteResource('injected_file_content_bytes',
                     'quota_injected_file_content_bytes'),
    AbsoluteResource('injected_file_path_bytes',
                     'quota_injected_file_path_bytes'),
    ReservableResource('security_groups', _sync_security_groups,
                     'quota_security_groups'),
    CountableResource('security_group_rules',
                      db.security_group_rule_count_by_group,
                      'quota_security_group_rules'),
    CountableResource('key_pairs', db.key_pair_count_by_user,
                      'quota_key_pairs'),
    ]
至于那几个_sync_*()函数,是作为ReservableResource类的成员变量存在的,是用来同步数据库的(下面会讲到如何同步),他们实现的功能是实时的从数据库中查询出正在使用的资源的数量。


配额管理在数据库中主要涉及到4个表:

Quotas: Represents a single quota override for a project.
mysql> show columns from quotas;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| created_at | datetime   | YES|   | NULL    |                |
| updated_at | datetime   | YES|   | NULL    |                |
| deleted_at | datetime   | YES|   | NULL    |                |
| deleted    | tinyint(1)   | YES|   | NULL    |                |
| project_id | varchar(255) | YES|   | NULL    |                |
| resource   | varchar(255) | NO   |   | NULL    |                |
| hard_limit | int(11)      | YES|   | NULL    |                |
+------------+--------------+------+-----+---------+----------------+


QuotaClass: Represents a single quota override for a quota class.
mysql> show columns from quota_classes;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| created_at | datetime   | YES|   | NULL    |                |
| updated_at | datetime   | YES|   | NULL    |                |
| deleted_at | datetime   | YES|   | NULL    |                |
| deleted    | tinyint(1)   | YES|   | NULL    |                |
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| class_name | varchar(255) | YES| MUL | NULL    |                |
| resource   | varchar(255) | YES|   | NULL    |                |
| hard_limit | int(11)      | YES|   | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

QuotaUsage: Represents the current usage for a given resource.
mysql> show columns from quota_usages;
+---------------+--------------+------+-----+---------+----------------+
| Field         | Type         | Null | Key | Default | Extra          |
+---------------+--------------+------+-----+---------+----------------+
| created_at    | datetime   | YES|   | NULL    |                |
| updated_at    | datetime   | YES|   | NULL    |                |
| deleted_at    | datetime   | YES|   | NULL    |                |
| deleted       | tinyint(1)   | YES|   | NULL    |                |
| id            | int(11)      | NO   | PRI | NULL    | auto_increment |
| project_id    | varchar(255) | YES| MUL | NULL    |                |
| resource      | varchar(255) | YES|   | NULL    |                |
| in_use      | int(11)      | NO   |   | NULL    |                |
| reserved      | int(11)      | NO   |   | NULL    |                |
| until_refresh | int(11)      | YES|   | NULL    |                |
+---------------+--------------+------+-----+---------+----------------+

Reservation: Represents a resource reservation for quotas.
usage_id is the foreign_key of quota_usages.
mysql> show columns from reservations;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| created_at | datetime   | YES|   | NULL    |                |
| updated_at | datetime   | YES|   | NULL    |                |
| deleted_at | datetime   | YES|   | NULL    |                |
| deleted    | tinyint(1)   | YES|   | NULL    |                |
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| uuid       | varchar(36)| NO   |   | NULL    |                |
| usage_id   | int(11)      | NO   | MUL | NULL    |                |
| project_id | varchar(255) | YES| MUL | NULL    |                |
| resource   | varchar(255) | YES|   | NULL    |                |
| delta      | int(11)      | NO   |   | NULL    |                |
| expire   | datetime   | YES|   | NULL    |                |
+------------+--------------+------+-----+---------+----------------+quotas表记录了分配给每个工程的各种资源的最大限额,hard_limit值就是保存最大限额的。

quota_classes表现在还不太明白有什么用,在程序里是先查quotas表,找不到相关资源的话,再去quota_classes表中查,如果还找不到的话,就使用默认值。

quota_usages表记录了每个工程当前使用的各种资源的数量,in_use值就是保存正在使用的资源数量的,程序中就是通过更新这个in_use值来实现配额管理的,至于reserv的作用,现在还不太明白。

reservations表记录的是每次分配的各种资源的变化值,即delta保存的值,它和quota_usages通过usage_id外键关联。这个表中的记录是不更新的,每次分配都会相应的增加记录。



好了,下面结合这4个表,来详细说一下构成事务性特性的那三个方法:

1. reserve()
这个函数的作用就是判断一下如果分配给请求的资源的数量给这个工程,是否会超出限额,如果超出就报异常,如果没有超出,就更新一下数据库中quota_usages表的in_use值。跟踪源码,最终实现功能的是下面的源代码:
@require_context
def quota_reserve(context, resources, quotas, deltas, expire,
                  until_refresh, max_age):
    elevated = context.elevated()
    session = get_session()
    with session.begin():
      # Get the current usages
      usages = _get_quota_usages(context, session)#从quota_usages表中获得当前工程的各种资源的使用情况

      # Handle usage refresh
      work = set(deltas.keys())
      while work:
            resource = work.pop()

            # Do we need to refresh the usage?
            refresh = False
            
            #如果当前的resource不在当前工程所使用的资源列表中,那么就把该资源添加进去,并且在数据库中增加一条相应的记录。
            #并且in_use和reserv都置为0.
            if resource not in usages:
                usages = quota_usage_create(elevated,
                                                      context.project_id,
                                                      resource,
                                                      0, 0,
                                                      until_refresh or None,
                                                      session=session)
                refresh = True
            
            #如果当前的resource在当前工程的使用列表中,并且该资源的in_use小于0,说明不同步,则refresh
            elif usages.in_use < 0:
                # Negative in_use count indicates a desync, so try to
                # heal from that...
                refresh = True
            
            #如果当前resource的until_refresh不为空,那么将其减1,若减1之后,小于0,则refresh
            elif usages.until_refresh is not None:
                usages.until_refresh -= 1
                if usages.until_refresh <= 0:
                  refresh = True
            
            #如果max_age不为空,并且该资源更新的时间减去当前的时间大于max_age,那么就执行刷新
            #max_age==0, 使用的是FLAGS中的值
            elif max_age and (usages.updated_at -
                              timeutils.utcnow()).seconds >= max_age:
                refresh = True

            # OK, refresh the usage
            if refresh:
                # Grab the sync routine
                sync = resources.sync #获得同步函数:_sync_*(),这些函数定义在quota模块中,不同的资源有不同的同步函数

                updates = sync(elevated, context.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中,那么把它添加进去
                        usages = quota_usage_create(elevated,
                                                         context.project_id,
                                                         res,
                                                         0, 0,
                                                         until_refresh or None,
                                                         session=session)

                  # Update the usage
                  usages.in_use = in_use #更新usages中该resource的in_use
                  usages.until_refresh = until_refresh or None

                  work.discard(res)

      # Check for deltas that would go negative
      #>检查in_use加上delta之后,可能小于0的情况
      unders = [resource for resource, delta in deltas.items()
                  if delta < 0 and
                  delta + usages.in_use < 0]

      #>检查这个resource的hard_limit是否小于in_use+reserved+delta之和
      overs = [resource for resource, delta in deltas.items()
               if quotas >= 0 and delta >= 0 and
               quotas < delta + usages.total]

      # Create the reservations
      #>如果没有超过的话,更新reservations表,再次更新usages
      if not overs:
            reservations = []
            for resource, delta in deltas.items():
                reservation = reservation_create(elevated,
                                                 str(utils.gen_uuid()),#uuid,唯一
                                                 usages,#当前资源在quota_usages表中的使用情况(更新过的)
                                                 context.project_id,
                                                 resource, delta, expire, #资源,变化值,超期值
                                                 session=session)
                reservations.append(reservation.uuid)

                if delta > 0:
                  usages.reserved += delta#更新usages中的reserved值,加上变化值

      # Apply updates to the usages table
      # 更新的是quota_usages表
      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这个函数执行的过程主要分为以下几步:

(1)同步
为什么要同步呢?这里同步的是什么呢?因为在为工程分配资源时,可能有各种特殊情况导致quota_usages表中记录的in_use不准确,需要得到当前实际使用的资源的情况,更新一下in_use,得到真实的资源使用情况。这里的特殊情况有一下4个:
          1)当前申请的资源没有在quota_usages表中记录
          2)当前申请的资源在quota_usages表中的in_use值小于0
          3)当前申请的资源的until_refresh值不为空,减1之后小于0
          4)当前申请的资源在quota_usages表中更新的时间减去现在的时间大于max_age
如果符合这四种情况之一,就执行同步。同步时,是调用当前资源的_sync_*()函数,去相关的表中查询实时的使用情况,比如_sync_instances()就是去instances表中查询出当前工程的instances数量,vcpu之和,ram之和,然后以字典的方式返回。然后根据这些实时的数据,更新in_use值。
(2)检查
根据各种资源的配额,和变化的情况(delta),来检查两种极端的情况:under和over。under是检查delta为负数的情况,即执行了删除等操作,使delta为负,in_use减少,导致in_use值可能小于0。over是检查delta为正数时的情况,in_use+delta就有可能大于最大的限额了。这里只对over的情况进行处理,即它只关心上限,不关心下限。如果没有over的话,就有下面的第(3)步了。如果over的话,就直接进行第(4)步。
(3)向reservations表中增加记录,记录申请资源的delta值。
(4)把in_use值写入到quota_usages表中保存。(不论under还是over都执行)

(5)如果over,即超出最大限额,则报出OverQuota异常。

2. rollback()

reserve()中如果没报异常的话,也不是万事大吉的,还可能有别的原因导致不能分配请求的资源,比如说当前申请的内存大小在配额的限额之下,但是它超出了镜像的内存,这种情况也是不能分配资源的,所以这个函数是在reserve()执行正常的情况下执行的,它的作用是回滚在reserve()函数中更改的数据。此处主要改变的数据是reserved值。因为在reserve()中,如果没有over的话,reserved值会执行reserved+=delta,而在这里,它又执行了reserved-=delta,使它回归到以前的值。主要功能代码如下:
@require_context
def reservation_rollback(context, reservations):
    session = get_session()
    with session.begin():
      usages = _get_quota_usages(context, session)

      for reservation in _quota_reservations(session, context, reservations):
            usage = usages
            if reservation.delta >= 0:
                usage.reserved -= reservation.delta

            reservation.delete(session=session)

      for usage in usages.values():
            usage.save(session=session)3. commit()
这个函数是在reserve()函数执行完之后,没有报OverQuota异常的情况下执行的,即本次请求的资源加上当前工程使用资源的数量没有超过限额,它的作用是将in_use加上变化值(delta),然后更新到quota_usages表中。主要功能源码如下:
@require_context
def reservation_commit(context, reservations):
    session = get_session()
    with session.begin():
      usages = _get_quota_usages(context, session)# 查询quota_usages表,获得当前工程中记录的资源的使用情况

      for reservation in _quota_reservations(session, context, reservations):
            usage = usages#某个资源当前记录的使用情况
            if reservation.delta >= 0:
                usage.reserved -= reservation.delta
            usage.in_use += reservation.delta

            reservation.delete(session=session)#从reservations表中删除这个reservation的记录

      for usage in usages.values():#再次更新quota_usages表
            usage.save(session=session)
注意上面reserve()中的in_use值,它只是分配资源之前,保存的是当前工程的资源使用情况,用来检查是否超过限额的,它本身并没有加上或减去delta,即并没有执行in_use+=delta,执行的是in_use+delta,因为它只是检查一下是否超额,如果超额了,in_use就不该变化的,所以in_use值只是分配本次请求资源之前的当前工程的资源使用情况,而in_use+=delta才是分配本次请求资源之后的情况。所以这个commit()函数必须是在reserve()执行之后,没报异常的情况下才执行的。

三、如何使用
通过上面的分析,怎么使用应该很清楚了,这里就举一个创建实例的过程中使用到配额管理的例子。创建实例之前,首先要检查申请的资源是否超出了该工程的限额,所以这部分功能是在向rabbitmq发出rpc call/cast请求之前来执行的。创建一个实例需要很多资源,这里创建资源使用的是flavor是m1.tiny, 即申请的资源是1个instance, 1个vcpu, 512M 内存。来看一下主要的代码:
def _create_instance(self, context, instance_type,
               image_href, kernel_id, ramdisk_id,
               min_count, max_count,
               display_name, display_description,
               key_name, key_data, security_group,
               availability_zone, user_data, metadata,
               injected_files, admin_password,
               access_ip_v4, access_ip_v6,
               requested_networks, config_drive,
               block_device_mapping, auto_disk_config,
               reservation_id=None, scheduler_hints=None):

      ………

          if not min_count:
            min_count = 1
      if not max_count:
            max_count = min_count
      ………

      # 主要就是检查instances,ram,cores是否超过配额。如果没有超过,则返回instances的数目和他们三个的reservation的uuid
      num_instances, quota_reservations = self._check_num_instances_quota(
                context, instance_type, min_count, max_count)

      ………

      if instance_type['memory_mb'] < int(image.get('min_ram') or 0):
            QUOTAS.rollback(context, quota_reservations)
            raise exception.InstanceTypeMemoryTooSmall()
      if instance_type['root_gb'] < int(image.get('min_disk') or 0):
            QUOTAS.rollback(context, quota_reservations)
            raise exception.InstanceTypeDiskTooSmall()

      ………

      instances = []
      instance_uuids = []
      try:
            for i in xrange(num_instances):
                options = base_options.copy()
                instance = self.create_db_entry_for_new_instance(
                        context, instance_type, image, options,
                        security_group, block_device_mapping)
                instances.append(instance)
                instance_uuids.append(instance['uuid'])
      except Exception:
            # Clean up as best we can.
            with excutils.save_and_reraise_exception():
                try:
                  for instance_uuid in instance_uuids:
                        self.db.instance_destroy(context,
                              instance_uuid)
                finally:
                  QUOTAS.rollback(context, quota_reservations)

      QUOTAS.commit(context, quota_reservations)

          …………

      return (instances, reservation_id)


    def _check_num_instances_quota(self, context, instance_type, min_count,
                                 max_count):

      req_cores = max_count * instance_type['vcpus'] #req_cores=1
      req_ram = max_count * instance_type['memory_mb'] #req_ram=512

      try:
            reservations = QUOTAS.reserve(context, instances=max_count,
                                          cores=req_cores, ram=req_ram)
      except exception.OverQuota as exc:
            …………
            raise exception.TooManyInstances(overs=overs,
                                             req=requested,
                                             used=used, allowed=total_allowed,
                                             resource=resource)

      return max_count, reservations
主要是_check_num_instances_quota()函数中的QUOTAS.reserv(context, instances=max_count, cores=req_cores, ram=req_ram),其中参数instances=1, cores=1, ram=512。在这里检查申请的资源是否超过限额。如果没有超出限额的话,看其他情况是否正常,如果不正常的话,则rollback(),如果一切正常的话,commit()提交。



四、测试
下面以创建实例为例,来进行一下测试,观看一下数据库中数据的变化。测试过程如下:
首先创建一个m1.tiny类型的实例,然后再创建一个m1.tiny类型的实例,然后再删除其中一个实例,注意看数据库的变化。然后再创建实例达到第11个,因为admin工程的默认实例限额为10个,所以创建第11个实例时,会报出异常。

1. 创建第一个实例及数据库数据的变化

$ nova boot --flavor 1 --image 6a5a1117-b7cb-4f14-9ab1-0fa9500542a5 --key_name pubkey-01 instance-01
$ nova list
+--------------------------------------+-------------+--------+------------------+
| ID                                 | Name      | Status | Networks         |
+--------------------------------------+-------------+--------+------------------+
| 46013b5c-d246-4544-9be1-6c33398357b8 | instance-01 | ACTIVE | private=10.0.0.2 |
+--------------------------------------+-------------+--------+------------------+

mysql> select id,uuid,project_id,hostname,vcpus,memory_mb from instances;
+----+--------------------------------------+----------------------------------+-------------+-------+-----------+
| id | uuid                                 | project_id                     | hostname    | vcpus | memory_mb |
+----+--------------------------------------+----------------------------------+-------------+-------+-----------+
|1 | 46013b5c-d246-4544-9be1-6c33398357b8 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instance-01 |   1 |       512 |
+----+--------------------------------------+----------------------------------+-------------+-------+-----------+

mysql> select id,created_at,deleted,project_id,resource,hard_limit from quotas;
Empty set (0.00 sec)

mysql> select id,created_at,deleted,class_name,resource,hard_limit from quota_classes;
Empty set (0.00 sec)

mysql> select id,deleted,project_id,resource,in_use,reserved,until_refresh from quota_usages;
+----+---------+----------------------------------+-----------+--------+----------+---------------+
| id | deleted | project_id                     | resource| in_use | reserved | until_refresh |
+----+---------+----------------------------------+-----------+--------+----------+---------------+
|1 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |      1 |      0 |          NULL |
|2 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |    512 |      0 |          NULL |
|3 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores   |      1 |      0 |          NULL |
+----+---------+----------------------------------+-----------+--------+----------+---------------+

mysql> select id,deleted,usage_id,project_id,resource,delta,expire from reservations;
+----+---------+----------+----------------------------------+-----------+-------+---------------------+
| id | deleted | usage_id | project_id                     | resource| delta | expire            |
+----+---------+----------+----------------------------------+-----------+-------+---------------------+
|1 |       1 |      1 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |   1 | 2012-11-26 08:55:26 |
|2 |       1 |      2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |   512 | 2012-11-26 08:55:26 |
|3 |       1 |      3 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores   |   1 | 2012-11-26 08:55:26 |
+----+---------+----------+----------------------------------+-----------+-------+---------------------+可以看到quota_usages表中的in_use值分别为1, 512, 1,reservations表中的delta值分别为1, 512, 1


2.创建第二个实例及数据库的变化
$ nova boot --flavor 1 --image 6a5a1117-b7cb-4f14-9ab1-0fa9500542a5 --key_name pubkey-01 instance-02
$ nova list
+--------------------------------------+-------------+--------+------------------+
| ID                                 | Name      | Status | Networks         |
+--------------------------------------+-------------+--------+------------------+
| 46013b5c-d246-4544-9be1-6c33398357b8 | instance-01 | ACTIVE | private=10.0.0.2 |
| 2a0fb8ca-9f5d-4751-b95b-43d6fe6c66d2 | instance-02 | ACTIVE | private=10.0.0.3 |
+--------------------------------------+-------------+--------+------------------+

mysql> select id,uuid,project_id,hostname,vcpus,memory_mb from instances;
+----+--------------------------------------+----------------------------------+-------------+-------+-----------+
| id | uuid                                 | project_id                     | hostname    | vcpus | memory_mb |
+----+--------------------------------------+----------------------------------+-------------+-------+-----------+
|1 | 46013b5c-d246-4544-9be1-6c33398357b8 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instance-01 |   1 |       512 |
|2 | 2a0fb8ca-9f5d-4751-b95b-43d6fe6c66d2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instance-02 |   1 |       512 |
+----+--------------------------------------+----------------------------------+-------------+-------+-----------+

mysql> select id,created_at,deleted,project_id,resource,hard_limit from quotas;
Empty set (0.00 sec)

mysql> select id,created_at,deleted,class_name,resource,hard_limit from quota_classes;
Empty set (0.00 sec)

mysql> select id,deleted,project_id,resource,in_use,reserved,until_refresh from quota_usages;
+----+---------+----------------------------------+-----------+--------+----------+---------------+
| id | deleted | project_id                     | resource| in_use | reserved | until_refresh |
+----+---------+----------------------------------+-----------+--------+----------+---------------+
|1 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |      2 |      0 |          NULL |
|2 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |   1024 |      0 |          NULL |
|3 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores   |      2 |      0 |          NULL |
+----+---------+----------------------------------+-----------+--------+----------+---------------+

mysql> select id,deleted,usage_id,project_id,resource,delta,expire from reservations;
+----+---------+----------+----------------------------------+-----------+-------+---------------------+
| id | deleted | usage_id | project_id                     | resource| delta | expire            |
+----+---------+----------+----------------------------------+-----------+-------+---------------------+
|1 |       1 |      1 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |   1 | 2012-11-26 08:55:26 |
|2 |       1 |      2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |   512 | 2012-11-26 08:55:26 |
|3 |       1 |      3 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores   |   1 | 2012-11-26 08:55:26 |
|4 |       1 |      1 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |   1 | 2012-11-26 09:09:44 |
|5 |       1 |      2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |   512 | 2012-11-26 09:09:44 |
|6 |       1 |      3 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores   |   1 | 2012-11-26 09:09:44 |
+----+---------+----------+----------------------------------+-----------+-------+---------------------+
可以看到quota_usages表中的in_use更新了,在原来的基础上,又加上了创建的第二个实例的资源数量,变为2, 1024, 2,而reservations表中的数据是不更新的,只是又增加了三条记录。


3.删除第二个实例及数据库数据的变化
$ nova delete 2a0fb8ca-9f5d-4751-b95b-43d6fe6c66d2
$ nova list
+--------------------------------------+-------------+--------+------------------+
| ID                                 | Name      | Status | Networks         |
+--------------------------------------+-------------+--------+------------------+
| 46013b5c-d246-4544-9be1-6c33398357b8 | instance-01 | ACTIVE | private=10.0.0.2 |
+--------------------------------------+-------------+--------+------------------+

mysql> select id,deleted,uuid,project_id,hostname,vcpus,memory_mb from instances;
+----+---------+--------------------------------------+----------------------------------+-------------+-------+-----------+
| id | deleted | uuid                                 | project_id                     | hostname    | vcpus | memory_mb |
+----+---------+--------------------------------------+----------------------------------+-------------+-------+-----------+
|1 |       0 | 46013b5c-d246-4544-9be1-6c33398357b8 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instance-01 |   1 |       512 |
|2 |       1 | 2a0fb8ca-9f5d-4751-b95b-43d6fe6c66d2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instance-02 |   1 |       512 |
+----+---------+--------------------------------------+----------------------------------+-------------+-------+-----------+

mysql> select id,created_at,deleted,project_id,resource,hard_limit from quotas;
Empty set (0.00 sec)

mysql> select id,created_at,deleted,class_name,resource,hard_limit from quota_classes;
Empty set (0.00 sec)

mysql> select id,deleted,project_id,resource,in_use,reserved,until_refresh from quota_usages;
+----+---------+----------------------------------+-----------+--------+----------+---------------+
| id | deleted | project_id                     | resource| in_use | reserved | until_refresh |
+----+---------+----------------------------------+-----------+--------+----------+---------------+
|1 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |      1 |      0 |          NULL |
|2 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |    512 |      0 |          NULL |
|3 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores   |      1 |      0 |          NULL |
+----+---------+----------------------------------+-----------+--------+----------+---------------+

mysql> select id,deleted,usage_id,project_id,resource,delta,expire from reservations;
+----+---------+----------+----------------------------------+-----------+-------+---------------------+
| id | deleted | usage_id | project_id                     | resource| delta | expire            |
+----+---------+----------+----------------------------------+-----------+-------+---------------------+
|1 |       1 |      1 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |   1 | 2012-11-26 08:55:26 |
|2 |       1 |      2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |   512 | 2012-11-26 08:55:26 |
|3 |       1 |      3 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores   |   1 | 2012-11-26 08:55:26 |
|4 |       1 |      1 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |   1 | 2012-11-26 09:09:44 |
|5 |       1 |      2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |   512 | 2012-11-26 09:09:44 |
|6 |       1 |      3 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores   |   1 | 2012-11-26 09:09:44 |
|7 |       1 |      1 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |    -1 | 2012-11-26 09:17:30 |
|8 |       1 |      2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |-512 | 2012-11-26 09:17:30 |
|9 |       1 |      3 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores   |    -1 | 2012-11-26 09:17:30 |
+----+---------+----------+----------------------------------+-----------+-------+---------------------+可以看到quota_usages表中的in_use值又变化了,分别减去了1, 512, 1 ,变成了1, 512, 1,而reservations表中又增加了三条记录,而delta值为-1, -512, -1,因为是删除实例嘛,所以delta值为负。


4. 创建实例达到11个,观看异常:
$ nova boot --flavor 1 --image 6a5a1117-b7cb-4f14-9ab1-0fa9500542a5 --key_name pubkey-01 instance-11
ERROR: Quota exceeded for instances: Requested 1, but already used 10 of 10 instances (HTTP 413) (Request-ID: req-2337fb00-d109-46e1-b381-1f4d63df0fd5)
这个异常就是超出限额,_check_num_instances_quota()函数中报的TooManyInstance异常:
class TooManyInstances(QuotaError):
    message = _("Quota exceeded for %(overs)s: Requested %(req)s,"
                " but already used %(used)d of %(allowed)d %(resource)s")
其实创建实例是可以达到最多10个,但是创建成功的就不一定有10个了,比如我的数据:
$ nova list
+--------------------------------------+-------------+--------+------------------+
| ID                                 | Name      | Status | Networks         |
+--------------------------------------+-------------+--------+------------------+
| 46013b5c-d246-4544-9be1-6c33398357b8 | instance-01 | ACTIVE | private=10.0.0.2 |
| 18b4a216-dad3-4e30-8f63-e4fd04ed5cd2 | instance-02 | ACTIVE | private=10.0.0.3 |
| 76e545b5-fa43-4ff1-b7f1-f3f1e0ee4ff2 | instance-03 | ACTIVE | private=10.0.0.4 |
| a279f645-f733-4f7d-b95a-42c186582b39 | instance-04 | ACTIVE | private=10.0.0.5 |
| 3e2c491f-3f3b-48d2-8fbf-c4459956639b | instance-05 | ERROR|                  |
| 8ff24877-b4d1-4b3d-be20-f7fae03113e7 | instance-06 | ERROR|                  |
| 3177a8ba-4c01-46a4-a1c1-904aac71fcd2 | instance-07 | ERROR|                  |
| d4adb08b-c199-48fd-a2f9-024e19ba8bbd | instance-08 | ERROR|                  |
| ada1c610-2a93-4ee5-bd06-f58f18be04dd | instance-09 | ERROR|                  |
| fdd9c770-3bae-48cb-987c-00425e917574 | instance-10 | ERROR|                  |
+--------------------------------------+-------------+--------+------------------+只成功创建了4个实例,因为我的2G内存已经不够了:
$ free -m
             total       used       free   shared    buffers   cached
Mem:          1974       1824      150          0         20      404
-/+ buffers/cache:       1400      574
Swap:         2382      355       2027

这也从另外一个方面说明了配额管理只是管配额的而已。


五、问题
写blog之前本来有很多疑问的,但是在写的过程中思考,竟然把一些疑问想明白了,但是还是有一些:
1. quotas和quota_classes表为空,我想这可能是因为没有配置的原因吧,因为程序中如果没有从这两个表中得到配额(hard_limit),那么就使用默认的值,所以我的测试里这两个表为空,但是程序正常执行,是因为使用了默认的值。
2. 关于那几个资源分类的,为什么要那样分,他们之间有什么区别,我还不明白。
3. dashboard界面的问题,它的quota界面的限额值是写死了的,还是会根据配置的值来变化,这个我还没有验证过。

4. 关于expire的,我看程序中参数的传递,expire始终为空,但是不知道怎么回事,到数据库中就变成有值的了,而且这个值很奇怪,即使删除了实例,它还是有,为什么呢?
5. 关于quota_usages表中的reserved值,它究竟是干什么用的呢?它的值,在一个事务之外,始终是为0,不为0的情况只在一个事务当中,这种情况只有调试才会看到,难道它就是只起一个回滚的作用?
6. quota_classes表不知道是用来做什么的,难道用一个quotas表还不够吗?难道说这个表保存的是每个资源的整体情况?
7. 还有就是一些资源不知道是什么资源,比如injected_file






嘴角微扬 发表于 2015-7-2 10:53:21

知识是需要一步一步累积的,学习了
页: [1]
查看完整版本: openstack nova 基础知识——Quota(配额管理)