一、什么是配额(quota)管理
简单的讲就是控制用户资源的数量。在openstack里,管理员为每一个工程(project)分配的资源都是有一定限制的,这些资源包括实例(instance)、cpu、内存、存储空间等等,不能让一个工程无限制的使用资源,所以配额管理针对的单位是工程(project)。先来个感性的认识,看一下dashboard里的一个工程的overview:
![](//img-my.csdn.net/uploads/201211/25/1353845820_7367.png)
管理员给这个工程的资源配额是最多创建10个实例,最多使用20个vcpu,最多使用5G的内存==,只要达到某一个资源的使用上限,就会出现异常,这就是配额管理。
二、如何实现
nova里配额管理是在~/nova/quota.py中实现的,首先来看一下这个模块的静态类图:
![](//img-my.csdn.net/uploads/201211/25/1353846203_5504.png)
使用时是直接调用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():
-
-
usages = _get_quota_usages(context, session)
-
-
-
work = set(deltas.keys())
-
while work:
-
resource = work.pop()
-
-
-
refresh = False
-
-
-
-
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
-
-
-
elif usages[resource].in_use < 0:
-
-
-
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
-
-
-
if refresh:
-
-
sync = resources[resource].sync
-
-
updates = sync(elevated, context.project_id, session)
-
for res, in_use in updates.items():
-
-
if res not in usages:
-
usages[res] = quota_usage_create(elevated,
-
context.project_id,
-
res,
-
0, 0,
-
until_refresh or None,
-
session=session)
-
-
-
usages[res].in_use = in_use
-
usages[res].until_refresh = until_refresh or None
-
-
work.discard(res)
-
-
-
-
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]
-
-
-
-
if not overs:
-
reservations = []
-
for resource, delta in deltas.items():
-
reservation = reservation_create(elevated,
-
str(utils.gen_uuid()),
-
usages[resource],
-
context.project_id,
-
resource, delta, expire,
-
session=session)
-
reservations.append(reservation.uuid)
-
-
if delta > 0:
-
usages[resource].reserved += delta
-
-
-
-
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)
-
-
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)
-
-
for usage in usages.values():
-
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
-
………
-
-
-
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:
-
-
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_ram = max_count * instance_type['memory_mb']
-
-
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
-
$ 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
-
$ 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