一、什么是配额(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中,是这样来分类的:

  1. resources = [
  2. ReservableResource('instances', _sync_instances, 'quota_instances'),
  3. ReservableResource('cores', _sync_instances, 'quota_cores'),
  4. ReservableResource('ram', _sync_instances, 'quota_ram'),
  5. ReservableResource('volumes', _sync_volumes, 'quota_volumes'),
  6. ReservableResource('gigabytes', _sync_volumes, 'quota_gigabytes'),
  7. ReservableResource('floating_ips', _sync_floating_ips,
  8. 'quota_floating_ips'),
  9. AbsoluteResource('metadata_items', 'quota_metadata_items'),
  10. AbsoluteResource('injected_files', 'quota_injected_files'),
  11. AbsoluteResource('injected_file_content_bytes',
  12. 'quota_injected_file_content_bytes'),
  13. AbsoluteResource('injected_file_path_bytes',
  14. 'quota_injected_file_path_bytes'),
  15. ReservableResource('security_groups', _sync_security_groups,
  16. 'quota_security_groups'),
  17. CountableResource('security_group_rules',
  18. db.security_group_rule_count_by_group,
  19. 'quota_security_group_rules'),
  20. CountableResource('key_pairs', db.key_pair_count_by_user,
  21. 'quota_key_pairs'),
  22. ]

至于那几个_sync_*()函数,是作为ReservableResource类的成员变量存在的,是用来同步数据库的(下面会讲到如何同步),他们实现的功能是实时的从数据库中查询出正在使用的资源的数量。

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

  1. Quotas: Represents a single quota override for a project.
  2. mysql> show columns from quotas;
  3. +------------+--------------+------+-----+---------+----------------+
  4. | Field      | Type         | Null | Key | Default | Extra          |
  5. +------------+--------------+------+-----+---------+----------------+
  6. | id         | int(11)      | NO   | PRI | NULL    | auto_increment |
  7. | created_at | datetime     | YES  |     | NULL    |                |
  8. | updated_at | datetime     | YES  |     | NULL    |                |
  9. | deleted_at | datetime     | YES  |     | NULL    |                |
  10. | deleted    | tinyint(1)   | YES  |     | NULL    |                |
  11. | project_id | varchar(255) | YES  |     | NULL    |                |
  12. | resource   | varchar(255) | NO   |     | NULL    |                |
  13. | hard_limit | int(11)      | YES  |     | NULL    |                |
  14. +------------+--------------+------+-----+---------+----------------+
  15. QuotaClass: Represents a single quota override for a quota class.
  16. mysql> show columns from quota_classes;
  17. +------------+--------------+------+-----+---------+----------------+
  18. | Field      | Type         | Null | Key | Default | Extra          |
  19. +------------+--------------+------+-----+---------+----------------+
  20. | created_at | datetime     | YES  |     | NULL    |                |
  21. | updated_at | datetime     | YES  |     | NULL    |                |
  22. | deleted_at | datetime     | YES  |     | NULL    |                |
  23. | deleted    | tinyint(1)   | YES  |     | NULL    |                |
  24. | id         | int(11)      | NO   | PRI | NULL    | auto_increment |
  25. | class_name | varchar(255) | YES  | MUL | NULL    |                |
  26. | resource   | varchar(255) | YES  |     | NULL    |                |
  27. | hard_limit | int(11)      | YES  |     | NULL    |                |
  28. +------------+--------------+------+-----+---------+----------------+
  29. QuotaUsage: Represents the current usage for a given resource.
  30. mysql> show columns from quota_usages;
  31. +---------------+--------------+------+-----+---------+----------------+
  32. | Field         | Type         | Null | Key | Default | Extra          |
  33. +---------------+--------------+------+-----+---------+----------------+
  34. | created_at    | datetime     | YES  |     | NULL    |                |
  35. | updated_at    | datetime     | YES  |     | NULL    |                |
  36. | deleted_at    | datetime     | YES  |     | NULL    |                |
  37. | deleted       | tinyint(1)   | YES  |     | NULL    |                |
  38. | id            | int(11)      | NO   | PRI | NULL    | auto_increment |
  39. | project_id    | varchar(255) | YES  | MUL | NULL    |                |
  40. | resource      | varchar(255) | YES  |     | NULL    |                |
  41. | in_use        | int(11)      | NO   |     | NULL    |                |
  42. | reserved      | int(11)      | NO   |     | NULL    |                |
  43. | until_refresh | int(11)      | YES  |     | NULL    |                |
  44. +---------------+--------------+------+-----+---------+----------------+
  45. Reservation: Represents a resource reservation for quotas.
  46. usage_id is the foreign_key of quota_usages.
  47. mysql> show columns from reservations;
  48. +------------+--------------+------+-----+---------+----------------+
  49. | Field      | Type         | Null | Key | Default | Extra          |
  50. +------------+--------------+------+-----+---------+----------------+
  51. | created_at | datetime     | YES  |     | NULL    |                |
  52. | updated_at | datetime     | YES  |     | NULL    |                |
  53. | deleted_at | datetime     | YES  |     | NULL    |                |
  54. | deleted    | tinyint(1)   | YES  |     | NULL    |                |
  55. | id         | int(11)      | NO   | PRI | NULL    | auto_increment |
  56. | uuid       | varchar(36)  | NO   |     | NULL    |                |
  57. | usage_id   | int(11)      | NO   | MUL | NULL    |                |
  58. | project_id | varchar(255) | YES  | MUL | NULL    |                |
  59. | resource   | varchar(255) | YES  |     | NULL    |                |
  60. | delta      | int(11)      | NO   |     | NULL    |                |
  61. | expire     | datetime     | YES  |     | NULL    |                |
  62. +------------+--------------+------+-----+---------+----------------+

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值。跟踪源码,最终实现功能的是下面的源代码:

  1. @require_context
  2. def quota_reserve(context, resources, quotas, deltas, expire,
  3. until_refresh, max_age):
  4. elevated = context.elevated()
  5. session = get_session()
  6. with session.begin():
  7. # Get the current usages
  8. usages = _get_quota_usages(context, session)#从quota_usages表中获得当前工程的各种资源的使用情况
  9. # Handle usage refresh
  10. work = set(deltas.keys())
  11. while work:
  12. resource = work.pop()
  13. # Do we need to refresh the usage?
  14. refresh = False
  15. #如果当前的resource不在当前工程所使用的资源列表中,那么就把该资源添加进去,并且在数据库中增加一条相应的记录。
  16. #并且in_use和reserv都置为0.
  17. if resource not in usages:
  18. usages[resource] = quota_usage_create(elevated,
  19. context.project_id,
  20. resource,
  21. 0, 0,
  22. until_refresh or None,
  23. session=session)
  24. refresh = True
  25. #如果当前的resource在当前工程的使用列表中,并且该资源的in_use小于0,说明不同步,则refresh
  26. elif usages[resource].in_use < 0:
  27. # Negative in_use count indicates a desync, so try to
  28. # heal from that...
  29. refresh = True
  30. #如果当前resource的until_refresh不为空,那么将其减1,若减1之后,小于0,则refresh
  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. #如果max_age不为空,并且该资源更新的时间减去当前的时间大于max_age,那么就执行刷新
  36. #max_age==0, 使用的是FLAGS中的值
  37. elif max_age and (usages[resource].updated_at -
  38. timeutils.utcnow()).seconds >= max_age:
  39. refresh = True
  40. # OK, refresh the usage
  41. if refresh:
  42. # Grab the sync routine
  43. sync = resources[resource].sync #获得同步函数:_sync_*(),这些函数定义在quota模块中,不同的资源有不同的同步函数
  44. updates = sync(elevated, context.project_id, session) #查询出当前正在使用的资源的一些情况,是实时的情况
  45. for res, in_use in updates.items():
  46. # Make sure we have a destination for the usage!
  47. if res not in usages:#如果实时的使用的资源没有在usages中,那么把它添加进去
  48. usages[res] = quota_usage_create(elevated,
  49. context.project_id,
  50. res,
  51. 0, 0,
  52. until_refresh or None,
  53. session=session)
  54. # Update the usage
  55. usages[res].in_use = in_use #更新usages中该resource的in_use
  56. usages[res].until_refresh = until_refresh or None
  57. work.discard(res)
  58. # Check for deltas that would go negative
  59. #>检查in_use加上delta之后,可能小于0的情况
  60. unders = [resource for resource, delta in deltas.items()
  61. if delta < 0 and
  62. delta + usages[resource].in_use < 0]
  63. #>检查这个resource的hard_limit是否小于in_use+reserved+delta之和
  64. overs = [resource for resource, delta in deltas.items()
  65. if quotas[resource] >= 0 and delta >= 0 and
  66. quotas[resource] < delta + usages[resource].total]
  67. # Create the reservations
  68. #>如果没有超过的话,更新reservations表,再次更新usages
  69. if not overs:
  70. reservations = []
  71. for resource, delta in deltas.items():
  72. reservation = reservation_create(elevated,
  73. str(utils.gen_uuid()),#uuid,唯一
  74. usages[resource],#当前资源在quota_usages表中的使用情况(更新过的)
  75. context.project_id,
  76. resource, delta, expire, #资源,变化值,超期值
  77. session=session)
  78. reservations.append(reservation.uuid)
  79. if delta > 0:
  80. usages[resource].reserved += delta  #更新usages中的reserved值,加上变化值
  81. # Apply updates to the usages table
  82. # 更新的是quota_usages表
  83. for usage_ref in usages.values():
  84. usage_ref.save(session=session)
  85. if unders:
  86. LOG.warning(_("Change will make usage less than 0 for the following "
  87. "resources: %(unders)s") % locals())
  88. if overs:
  89. usages = dict((k, dict(in_use=v['in_use'], reserved=v['reserved']))
  90. for k, v in usages.items())
  91. raise exception.OverQuota(overs=sorted(overs), quotas=quotas,
  92. usages=usages)
  93. 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,使它回归到以前的值。主要功能代码如下:

  1. @require_context
  2. def reservation_rollback(context, reservations):
  3. session = get_session()
  4. with session.begin():
  5. usages = _get_quota_usages(context, session)
  6. for reservation in _quota_reservations(session, context, reservations):
  7. usage = usages[reservation.resource]
  8. if reservation.delta >= 0:
  9. usage.reserved -= reservation.delta
  10. reservation.delete(session=session)
  11. for usage in usages.values():
  12. usage.save(session=session)

3. commit()

这个函数是在reserve()函数执行完之后,没有报OverQuota异常的情况下执行的,即本次请求的资源加上当前工程使用资源的数量没有超过限额,它的作用是将in_use加上变化值(delta),然后更新到quota_usages表中。主要功能源码如下:

  1. @require_context
  2. def reservation_commit(context, reservations):
  3. session = get_session()
  4. with session.begin():
  5. usages = _get_quota_usages(context, session)# 查询quota_usages表,获得当前工程中记录的资源的使用情况
  6. for reservation in _quota_reservations(session, context, reservations):
  7. usage = usages[reservation.resource]#某个资源当前记录的使用情况
  8. if reservation.delta >= 0:
  9. usage.reserved -= reservation.delta
  10. usage.in_use += reservation.delta
  11. reservation.delete(session=session)#从reservations表中删除这个reservation的记录
  12. for usage in usages.values():#再次更新quota_usages表
  13. 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 内存。来看一下主要的代码:

  1. def _create_instance(self, context, instance_type,
  2. image_href, kernel_id, ramdisk_id,
  3. min_count, max_count,
  4. display_name, display_description,
  5. key_name, key_data, security_group,
  6. availability_zone, user_data, metadata,
  7. injected_files, admin_password,
  8. access_ip_v4, access_ip_v6,
  9. requested_networks, config_drive,
  10. block_device_mapping, auto_disk_config,
  11. reservation_id=None, scheduler_hints=None):
  12. ………
  13. if not min_count:
  14. min_count = 1
  15. if not max_count:
  16. max_count = min_count
  17. ………
  18. # 主要就是检查instances,ram,cores是否超过配额。如果没有超过,则返回instances的数目和他们三个的reservation的uuid
  19. num_instances, quota_reservations = self._check_num_instances_quota(
  20. context, instance_type, min_count, max_count)
  21. ………
  22. if instance_type['memory_mb'] < int(image.get('min_ram') or 0):
  23. QUOTAS.rollback(context, quota_reservations)
  24. raise exception.InstanceTypeMemoryTooSmall()
  25. if instance_type['root_gb'] < int(image.get('min_disk') or 0):
  26. QUOTAS.rollback(context, quota_reservations)
  27. raise exception.InstanceTypeDiskTooSmall()
  28. ………
  29. instances = []
  30. instance_uuids = []
  31. try:
  32. for i in xrange(num_instances):
  33. options = base_options.copy()
  34. instance = self.create_db_entry_for_new_instance(
  35. context, instance_type, image, options,
  36. security_group, block_device_mapping)
  37. instances.append(instance)
  38. instance_uuids.append(instance['uuid'])
  39. except Exception:
  40. # Clean up as best we can.
  41. with excutils.save_and_reraise_exception():
  42. try:
  43. for instance_uuid in instance_uuids:
  44. self.db.instance_destroy(context,
  45. instance_uuid)
  46. finally:
  47. QUOTAS.rollback(context, quota_reservations)
  48. QUOTAS.commit(context, quota_reservations)
  49. …………
  50. return (instances, reservation_id)
  51. def _check_num_instances_quota(self, context, instance_type, min_count,
  52. max_count):
  53. req_cores = max_count * instance_type['vcpus'] #req_cores=1
  54. req_ram = max_count * instance_type['memory_mb'] #req_ram=512
  55. try:
  56. reservations = QUOTAS.reserve(context, instances=max_count,
  57. cores=req_cores, ram=req_ram)
  58. except exception.OverQuota as exc:
  59. …………
  60. raise exception.TooManyInstances(overs=overs,
  61. req=requested[resource],
  62. used=used, allowed=total_allowed,
  63. resource=resource)
  64. 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. 创建第一个实例及数据库数据的变化

  1. $ nova boot --flavor 1 --image 6a5a1117-b7cb-4f14-9ab1-0fa9500542a5 --key_name pubkey-01 instance-01
  2. $ nova list
  3. +--------------------------------------+-------------+--------+------------------+
  4. | ID                                   | Name        | Status | Networks         |
  5. +--------------------------------------+-------------+--------+------------------+
  6. | 46013b5c-d246-4544-9be1-6c33398357b8 | instance-01 | ACTIVE | private=10.0.0.2 |
  7. +--------------------------------------+-------------+--------+------------------+
  8. mysql> select id,uuid,project_id,hostname,vcpus,memory_mb from instances;
  9. +----+--------------------------------------+----------------------------------+-------------+-------+-----------+
  10. | id | uuid                                 | project_id                       | hostname    | vcpus | memory_mb |
  11. +----+--------------------------------------+----------------------------------+-------------+-------+-----------+
  12. |  1 | 46013b5c-d246-4544-9be1-6c33398357b8 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instance-01 |     1 |       512 |
  13. +----+--------------------------------------+----------------------------------+-------------+-------+-----------+
  14. mysql> select id,created_at,deleted,project_id,resource,hard_limit from quotas;
  15. Empty set (0.00 sec)
  16. mysql> select id,created_at,deleted,class_name,resource,hard_limit from quota_classes;
  17. Empty set (0.00 sec)
  18. mysql> select id,deleted,project_id,resource,in_use,reserved,until_refresh from quota_usages;
  19. +----+---------+----------------------------------+-----------+--------+----------+---------------+
  20. | id | deleted | project_id                       | resource  | in_use | reserved | until_refresh |
  21. +----+---------+----------------------------------+-----------+--------+----------+---------------+
  22. |  1 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |      1 |        0 |          NULL |
  23. |  2 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |    512 |        0 |          NULL |
  24. |  3 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores     |      1 |        0 |          NULL |
  25. +----+---------+----------------------------------+-----------+--------+----------+---------------+
  26. mysql> select id,deleted,usage_id,project_id,resource,delta,expire from reservations;
  27. +----+---------+----------+----------------------------------+-----------+-------+---------------------+
  28. | id | deleted | usage_id | project_id                       | resource  | delta | expire              |
  29. +----+---------+----------+----------------------------------+-----------+-------+---------------------+
  30. |  1 |       1 |        1 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |     1 | 2012-11-26 08:55:26 |
  31. |  2 |       1 |        2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |   512 | 2012-11-26 08:55:26 |
  32. |  3 |       1 |        3 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores     |     1 | 2012-11-26 08:55:26 |
  33. +----+---------+----------+----------------------------------+-----------+-------+---------------------+

可以看到quota_usages表中的in_use值分别为1, 512, 1,  reservations表中的delta值分别为1, 512, 1

2.  创建第二个实例及数据库的变化

  1. $ nova boot --flavor 1 --image 6a5a1117-b7cb-4f14-9ab1-0fa9500542a5 --key_name pubkey-01 instance-02
  2. $ nova list
  3. +--------------------------------------+-------------+--------+------------------+
  4. | ID                                   | Name        | Status | Networks         |
  5. +--------------------------------------+-------------+--------+------------------+
  6. | 46013b5c-d246-4544-9be1-6c33398357b8 | instance-01 | ACTIVE | private=10.0.0.2 |
  7. | 2a0fb8ca-9f5d-4751-b95b-43d6fe6c66d2 | instance-02 | ACTIVE | private=10.0.0.3 |
  8. +--------------------------------------+-------------+--------+------------------+
  9. mysql> select id,uuid,project_id,hostname,vcpus,memory_mb from instances;
  10. +----+--------------------------------------+----------------------------------+-------------+-------+-----------+
  11. | id | uuid                                 | project_id                       | hostname    | vcpus | memory_mb |
  12. +----+--------------------------------------+----------------------------------+-------------+-------+-----------+
  13. |  1 | 46013b5c-d246-4544-9be1-6c33398357b8 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instance-01 |     1 |       512 |
  14. |  2 | 2a0fb8ca-9f5d-4751-b95b-43d6fe6c66d2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instance-02 |     1 |       512 |
  15. +----+--------------------------------------+----------------------------------+-------------+-------+-----------+
  16. mysql> select id,created_at,deleted,project_id,resource,hard_limit from quotas;
  17. Empty set (0.00 sec)
  18. mysql> select id,created_at,deleted,class_name,resource,hard_limit from quota_classes;
  19. Empty set (0.00 sec)
  20. mysql> select id,deleted,project_id,resource,in_use,reserved,until_refresh from quota_usages;
  21. +----+---------+----------------------------------+-----------+--------+----------+---------------+
  22. | id | deleted | project_id                       | resource  | in_use | reserved | until_refresh |
  23. +----+---------+----------------------------------+-----------+--------+----------+---------------+
  24. |  1 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |      2 |        0 |          NULL |
  25. |  2 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |   1024 |        0 |          NULL |
  26. |  3 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores     |      2 |        0 |          NULL |
  27. +----+---------+----------------------------------+-----------+--------+----------+---------------+
  28. mysql> select id,deleted,usage_id,project_id,resource,delta,expire from reservations;
  29. +----+---------+----------+----------------------------------+-----------+-------+---------------------+
  30. | id | deleted | usage_id | project_id                       | resource  | delta | expire              |
  31. +----+---------+----------+----------------------------------+-----------+-------+---------------------+
  32. |  1 |       1 |        1 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |     1 | 2012-11-26 08:55:26 |
  33. |  2 |       1 |        2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |   512 | 2012-11-26 08:55:26 |
  34. |  3 |       1 |        3 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores     |     1 | 2012-11-26 08:55:26 |
  35. |  4 |       1 |        1 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |     1 | 2012-11-26 09:09:44 |
  36. |  5 |       1 |        2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |   512 | 2012-11-26 09:09:44 |
  37. |  6 |       1 |        3 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores     |     1 | 2012-11-26 09:09:44 |
  38. +----+---------+----------+----------------------------------+-----------+-------+---------------------+

可以看到quota_usages表中的in_use更新了,在原来的基础上,又加上了创建的第二个实例的资源数量,变为2, 1024, 2,而reservations表中的数据是不更新的,只是又增加了三条记录。

3.  删除第二个实例及数据库数据的变化

  1. $ nova delete 2a0fb8ca-9f5d-4751-b95b-43d6fe6c66d2
  2. $ nova list
  3. +--------------------------------------+-------------+--------+------------------+
  4. | ID                                   | Name        | Status | Networks         |
  5. +--------------------------------------+-------------+--------+------------------+
  6. | 46013b5c-d246-4544-9be1-6c33398357b8 | instance-01 | ACTIVE | private=10.0.0.2 |
  7. +--------------------------------------+-------------+--------+------------------+
  8. mysql> select id,deleted,uuid,project_id,hostname,vcpus,memory_mb from instances;
  9. +----+---------+--------------------------------------+----------------------------------+-------------+-------+-----------+
  10. | id | deleted | uuid                                 | project_id                       | hostname    | vcpus | memory_mb |
  11. +----+---------+--------------------------------------+----------------------------------+-------------+-------+-----------+
  12. |  1 |       0 | 46013b5c-d246-4544-9be1-6c33398357b8 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instance-01 |     1 |       512 |
  13. |  2 |       1 | 2a0fb8ca-9f5d-4751-b95b-43d6fe6c66d2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instance-02 |     1 |       512 |
  14. +----+---------+--------------------------------------+----------------------------------+-------------+-------+-----------+
  15. mysql> select id,created_at,deleted,project_id,resource,hard_limit from quotas;
  16. Empty set (0.00 sec)
  17. mysql> select id,created_at,deleted,class_name,resource,hard_limit from quota_classes;
  18. Empty set (0.00 sec)
  19. mysql> select id,deleted,project_id,resource,in_use,reserved,until_refresh from quota_usages;
  20. +----+---------+----------------------------------+-----------+--------+----------+---------------+
  21. | id | deleted | project_id                       | resource  | in_use | reserved | until_refresh |
  22. +----+---------+----------------------------------+-----------+--------+----------+---------------+
  23. |  1 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |      1 |        0 |          NULL |
  24. |  2 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |    512 |        0 |          NULL |
  25. |  3 |       0 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores     |      1 |        0 |          NULL |
  26. +----+---------+----------------------------------+-----------+--------+----------+---------------+
  27. mysql> select id,deleted,usage_id,project_id,resource,delta,expire from reservations;
  28. +----+---------+----------+----------------------------------+-----------+-------+---------------------+
  29. | id | deleted | usage_id | project_id                       | resource  | delta | expire              |
  30. +----+---------+----------+----------------------------------+-----------+-------+---------------------+
  31. |  1 |       1 |        1 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |     1 | 2012-11-26 08:55:26 |
  32. |  2 |       1 |        2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |   512 | 2012-11-26 08:55:26 |
  33. |  3 |       1 |        3 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores     |     1 | 2012-11-26 08:55:26 |
  34. |  4 |       1 |        1 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |     1 | 2012-11-26 09:09:44 |
  35. |  5 |       1 |        2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |   512 | 2012-11-26 09:09:44 |
  36. |  6 |       1 |        3 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores     |     1 | 2012-11-26 09:09:44 |
  37. |  7 |       1 |        1 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | instances |    -1 | 2012-11-26 09:17:30 |
  38. |  8 |       1 |        2 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | ram       |  -512 | 2012-11-26 09:17:30 |
  39. |  9 |       1 |        3 | aefecee7a7ca4cb3bcfac8e3c9fd27d0 | cores     |    -1 | 2012-11-26 09:17:30 |
  40. +----+---------+----------+----------------------------------+-----------+-------+---------------------+

可以看到quota_usages表中的in_use值又变化了,分别减去了1, 512, 1 ,变成了1, 512, 1,而reservations表中又增加了三条记录,而delta值为-1, -512, -1,因为是删除实例嘛,所以delta值为负。

4. 创建实例达到11个,观看异常:

  1. $ nova boot --flavor 1 --image 6a5a1117-b7cb-4f14-9ab1-0fa9500542a5 --key_name pubkey-01 instance-11
  2. 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异常:

  1. class TooManyInstances(QuotaError):
  2. message = _("Quota exceeded for %(overs)s: Requested %(req)s,"
  3. " but already used %(used)d of %(allowed)d %(resource)s")

其实创建实例是可以达到最多10个,但是创建成功的就不一定有10个了,比如我的数据:

  1. $ nova list
  2. +--------------------------------------+-------------+--------+------------------+
  3. | ID                                   | Name        | Status | Networks         |
  4. +--------------------------------------+-------------+--------+------------------+
  5. | 46013b5c-d246-4544-9be1-6c33398357b8 | instance-01 | ACTIVE | private=10.0.0.2 |
  6. | 18b4a216-dad3-4e30-8f63-e4fd04ed5cd2 | instance-02 | ACTIVE | private=10.0.0.3 |
  7. | 76e545b5-fa43-4ff1-b7f1-f3f1e0ee4ff2 | instance-03 | ACTIVE | private=10.0.0.4 |
  8. | a279f645-f733-4f7d-b95a-42c186582b39 | instance-04 | ACTIVE | private=10.0.0.5 |
  9. | 3e2c491f-3f3b-48d2-8fbf-c4459956639b | instance-05 | ERROR  |                  |
  10. | 8ff24877-b4d1-4b3d-be20-f7fae03113e7 | instance-06 | ERROR  |                  |
  11. | 3177a8ba-4c01-46a4-a1c1-904aac71fcd2 | instance-07 | ERROR  |                  |
  12. | d4adb08b-c199-48fd-a2f9-024e19ba8bbd | instance-08 | ERROR  |                  |
  13. | ada1c610-2a93-4ee5-bd06-f58f18be04dd | instance-09 | ERROR  |                  |
  14. | fdd9c770-3bae-48cb-987c-00425e917574 | instance-10 | ERROR  |                  |
  15. +--------------------------------------+-------------+--------+------------------+

只成功创建了4个实例,因为我的2G内存已经不够了:

  1. $ free -m
  2. total       used       free     shared    buffers     cached
  3. Mem:          1974       1824        150          0         20        404
  4. -/+ buffers/cache:       1400        574
  5. 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

http://blog.csdn.net/hackerain/article/details/8223125

openstack nova 基础知识——Quota(配额管理)的更多相关文章

  1. openstack学习笔记(一)-openstack的基础知识

    一.OpenStack的基础知识 openstack是一个由NASA(美国国家航空航天局)和Rackspace合作研发并发起的,以Apache2.0许可证(兼容GPLv3以及DFSG)授权的自由软件和 ...

  2. MySQL基础知识:启动管理和账号管理

    整理.记录常用的MySQL基础知识:时间久了,很多就忘记了. 操作系统环境为MacOS Catalina, MySQL版本为: 8.0.13 MySQL Community Server - GPL. ...

  3. OpenStack之基础知识

    一.云计算 云计算(cloud computing)是基于互联网的相关服务的增加.使用和交付模式,通常涉及通过互联网来提供动态易扩展且经常是虚拟化的资源.云是网络.互联网的一种比喻说法.过去在图中往往 ...

  4. 事务基础知识-->Spring事务管理

    Spring虽然提供了灵活方便的事务管理功能,但这些功能都是基于底层数据库本身的事务处理机制工作的.要深入了解Spring的事务管理和配置,有必要先对数据库事务的基础知识进行学习. 何为数据库事务 “ ...

  5. linux 基础10-磁盘配额管理

    1. 基本概念 1.1 概念: 在linux系统中,由于是多人多任务的使用环境,所以会有多人共同使用一个硬盘空间的情况,如果其中少数几个人大量使用了硬盘空间的话,势必会压缩其他使用者的使用空间,因此管 ...

  6. OpenStack 云计算基础知识

    OpenStack Docs: Currenthttp://docs.openstack.org/ OpenStack云计算快速入门教程 - OpenStack及其构成简介_服务器应用_Linux公社 ...

  7. django基础知识之后台管理Admin站点:

    Admin站点 通过使用startproject创建的项目模版中,默认Admin被启用 1.创建管理员的用户名和密码 python manage.py createsuperuser 然后按提示填写用 ...

  8. Nginx基础知识之————日志管理

    一.Server段,可以看到如下类似信息 #access_log logs/host.access.log main; 说明该server, 它的访问日志的文件是  logs/host.access. ...

  9. 学习Spring必学的Java基础知识

    [1] Java反射知识-->Spring IoC :http://www.iteye.com/topic/1123081 [2] Java动态代理-->Spring AOP :http: ...

随机推荐

  1. 获取html元素所在页面的坐标

    function findPosition(oElement) { var x2 = 0; var y2 = 0; var width = oElement.offsetWidth; var heig ...

  2. 母版页改变被嵌套的页面中的控件ID的解决方法

    使用过模板页的朋友都会很纳闷,怎么页面的用js通过getElementById(“id”):找不到对象.查看了页面源代码才发现,原来控件的ID变了,这是母版页导致的.因为母版页怕母版页本身页面中的控件 ...

  3. Xamarin.Forms学习之位图(二)

    上篇文章分享了如何加载网络图片和PCL中的图片,所以今天继续分享关于如何加载平台的内嵌图片,在这之前说一下上篇文章开头的一个问题:为什么不能加载UWP项目,这是因为我升级了UWP的SDK,而Xamar ...

  4. <2013 07 06> Future and Near Future

    试图了解     量子力学 近现代基础物理学理论 量子计算机   脑科学 近现代生物学 遗传变异与进化   复杂工程学 系统工程 管理科学   人工智能 智能算法 机器学习 深度学习 大数据 云计算 ...

  5. python多线程安全local()

    import time import threading from threading import local class Foo(local): pass # 实例化(创建)对象 foo = Fo ...

  6. Python3.6全栈开发实例[002]

    2.判断用户传入的对象(字符串.列表.元组)长度是否大于5. li = [11,22,33,44,55,66,77,88,99,000,111,222] def func2(lst): if len( ...

  7. linux安装jdk_1.8

    转载自http://blog.csdn.net/ldl22847/article/details/7605650 1.下载jdk的rpm安装包,这里以jdk-8u141-linux-x64.rpm为例 ...

  8. Springboot入门2-配置druid

    Druid是Java语言中最好的数据库连接池,在连接池之外,还提供了非常优秀的监控功能. 下面来说明如何在 Spring Boot 中配置使用Druid 1.添加Maven依赖 (或jar包) < ...

  9. BigDecimal类型、Long类型判断值是否相等,以及BigDecimal加减乘除

    //Long是需要比较精度的,所以要用longValueif(project.getFriendId().longValue() != friendId.longValue()) { return t ...

  10. 17南宁区域赛 I - Rake It In 【DFS】

    题目链接 https://nanti.jisuanke.com/t/19975 题意 Alice 和 Bob 玩游戏 在一个4x4 的方格上 每个人 每次选择2x2的区域 将里面的四个值求和加到最后的 ...