openstack nova 基础知识——Quota(配额管理)
一、什么是配额(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[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 <= 0:
- refresh = True
- #如果max_age不为空,并且该资源更新的时间减去当前的时间大于max_age,那么就执行刷新
- #max_age==0, 使用的是FLAGS中的值
- elif max_age and (usages[resource].updated_at -
- timeutils.utcnow()).seconds >= max_age:
- refresh = True
- # OK, refresh the usage
- if refresh:
- # Grab the sync routine
- sync = resources[resource].sync #获得同步函数:_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 |
- +----+---------+----------+----------------------------------+-----------+-------+---------------------+
可以看到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
http://blog.csdn.net/hackerain/article/details/8223125
openstack nova 基础知识——Quota(配额管理)的更多相关文章
- openstack学习笔记(一)-openstack的基础知识
一.OpenStack的基础知识 openstack是一个由NASA(美国国家航空航天局)和Rackspace合作研发并发起的,以Apache2.0许可证(兼容GPLv3以及DFSG)授权的自由软件和 ...
- MySQL基础知识:启动管理和账号管理
整理.记录常用的MySQL基础知识:时间久了,很多就忘记了. 操作系统环境为MacOS Catalina, MySQL版本为: 8.0.13 MySQL Community Server - GPL. ...
- OpenStack之基础知识
一.云计算 云计算(cloud computing)是基于互联网的相关服务的增加.使用和交付模式,通常涉及通过互联网来提供动态易扩展且经常是虚拟化的资源.云是网络.互联网的一种比喻说法.过去在图中往往 ...
- 事务基础知识-->Spring事务管理
Spring虽然提供了灵活方便的事务管理功能,但这些功能都是基于底层数据库本身的事务处理机制工作的.要深入了解Spring的事务管理和配置,有必要先对数据库事务的基础知识进行学习. 何为数据库事务 “ ...
- linux 基础10-磁盘配额管理
1. 基本概念 1.1 概念: 在linux系统中,由于是多人多任务的使用环境,所以会有多人共同使用一个硬盘空间的情况,如果其中少数几个人大量使用了硬盘空间的话,势必会压缩其他使用者的使用空间,因此管 ...
- OpenStack 云计算基础知识
OpenStack Docs: Currenthttp://docs.openstack.org/ OpenStack云计算快速入门教程 - OpenStack及其构成简介_服务器应用_Linux公社 ...
- django基础知识之后台管理Admin站点:
Admin站点 通过使用startproject创建的项目模版中,默认Admin被启用 1.创建管理员的用户名和密码 python manage.py createsuperuser 然后按提示填写用 ...
- Nginx基础知识之————日志管理
一.Server段,可以看到如下类似信息 #access_log logs/host.access.log main; 说明该server, 它的访问日志的文件是 logs/host.access. ...
- 学习Spring必学的Java基础知识
[1] Java反射知识-->Spring IoC :http://www.iteye.com/topic/1123081 [2] Java动态代理-->Spring AOP :http: ...
随机推荐
- EasyNVR现场部署搭配EasyNVS云端集中控制应用于幼儿园直播场景的最佳方案!
在之前的介绍中,我们已经介绍了很多EasyNVR成功应用于幼儿园类教育直播的场景,例如<EasyDarwin幼教云视频平台在幼教平台领域大放异彩!>.<基于EasyDarwin云视频 ...
- pycharm 调试django 服务端断点调试
django runserver 服务端断电调试 D:\model\gitlab\eebo.ehr.analysis\venv\Scripts\python.exe "C:\Program ...
- 查找杀死指定进程delphi
//需要引用tlhelp32单元//查找进程function findProcessId(pname:string):Cardinal; var hsnapshot:THandle; lpe:TPro ...
- Python迭代器包itertools(转)
原文:http://www.cnblogs.com/vamei/p/3174796.html 作者:Vamei 在循环对象和函数对象中,我们了解了循环器(iterator)的功能.循环器是对象的容器, ...
- LinuxCentos系统安装Nginx过程记录
网站服务 想必我们大多数人都是通过访问网站而开始接触互联网的吧.我们平时访问的网站服务就是Web网络服务,一般是指允许用户通过浏览器访问到互联网中各种资源的服务. Web网络服务是一种被动访问的服务程 ...
- python基础知识回顾[1]
1.声明变量 # 声明一个变量name用来存储一个字符串'apollo' name = 'apollo' # 声明一个变量age用来存储一个数字20 age = 20 # 在控制台打印变量name中存 ...
- Slyce,这家硅谷创业公司的来头你知道吗
Slyce,也许你没听过,一家硅谷创业公司,旨在帮助运动员和其他社会名流组织.优化社交媒体,过滤粉丝的声音,让明星更好的在社交媒体上和他们互动.但是如果如果说库里,那你应该就知道了,拿到了上届NBA总 ...
- leetcode第一刷_Rotate Image
这个题该怎么说呢.旋转又要求inplace.一般就是要找到某种规律了.这个还是非常明显的,画一下原来的.再画一下旋转之后的.看看原来的跑到什么位置了. 牵扯到四个位置按顺时针方向互换一下位置,发现仅仅 ...
- 0102-使用 API 网关构建微服务
一.移动客户端如何访问这些服务 1.1.客户端与微服务直接通信[很少使用] 从理论上讲,客户端可以直接向每个微服务发送请求.每个微服务都有一个公开的端点(https ://.api.company.n ...
- requirejs源码分析: requirejs 方法–2. context.require(deps, callback, errback);
上一篇 requirejs源码分析: requirejs 方法–1. 主入口 中的return context.require(deps, callback, errback); 调用的是make ...