一、什么是配额(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。一些资源需要预占,因为使用情况随时在变化,可能牵扯到并发,这些被划分为ReservableResource;而有些资源(比如 injected_files)在一次请求中的最大限额是固定的,这些就是AbsoluteResource;而CountableResource是继 承自AbsoluteResource的。如下,是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 | |
+------------+--------------+------+-----+---------+----------------+ |
1) quotas表记录了分配给每个工程的各种资源的最大限额,hard_limit值就是保存最大限额的。
2) quota_classes表现在还不太明白有什么用,在程序里是先查quotas表,找不到相关资源的话,再去quota_classes表中查,如果还找不到的话,就使用默认值。
3) quota_usages表记录了每个工程当前使用的各种资源的数量,in_use值就是保存正在使用的资源数量的,程序中就是通过更新这个in_use值来实现配额管理的,至于reserved的作用,是在事务回滚的时候要用到。
4) 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[resource] = 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[resource].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[resource].until_refresh is not None:
usages[resource].until_refresh -= 1
if usages[resource].until_refresh = max_age:
refresh = True
# OK, refresh the usage
if refresh:
# Grab the sync routine
sync = resources[resource].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[res] = quota_usage_create(elevated,
context.project_id,
res,
0, 0,
until_refresh or None,
session=session)
# Update the usage
usages[res].in_use = in_use #更新usages中该resource的in_use
usages[res].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[resource].in_use < 0] #>检查这个resource的hard_limit是否小于in_use+reserved+delta之和
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 #>如果没有超过的话,更新reservations表,再次更新usages
if not overs:
reservations = []
for resource, delta in deltas.items():
reservation = reservation_create(elevated,
str(utils.gen_uuid()),#uuid,唯一
usages[resource],#当前资源在quota_usages表中的使用情况(更新过的)
context.project_id,
resource, delta, expire, #资源,变化值,超期值
session=session)
reservations.append(reservation.uuid)
if delta > 0:
usages[resource].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[reservation.resource]
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[reservation.resource]#某个资源当前记录的使用情况
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[resource],
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 |
+----+---------+----------+----------------------------------+-----------+-------+---------------------+ |
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 |
这也从另外一个方面说明了配额管理只是管配额的而已