深挖Openstack Nova - 实例创建(4)

本文深入剖析OpenStack Nova在实例创建过程中如何进行配额管理,包括获取实例数目、确定内核和RAM、分配配额等步骤。同时,详细解释了循环创建实例的流程,从实例创建的开端到处理多实例情况。
摘要由CSDN通过智能技术生成
--------------- 紧接上篇 nova实例创建(3) --------------------


(8)完成配额管理

instances = self._provision_instances(context, instance_type,
        min_count, max_count, base_options, boot_meta, security_groups, 
        block_device_mapping, shutdown_terminate,
        instance_group, check_server_group_quota)

深入_provision_instances方法:

1》获取建立实例数目

# _check_num_instances_quota:根据资源配额限制确定所要建立实例的数目
num_instances, quotas = self._check_num_instances_quota(
        context, instance_type, min_count, max_count)
LOG.debug("Going to run %s instances..." % num_instances)

查看其中的_check_num_instances_quota方法:

1.1》确定内核数和RAM

req_cores = max_count * instance_type['vcpus']
vram_mb = int(instance_type.get('extra_specs', {}).get(VIDEO_RAM, 0))
req_ram = max_count * (instance_type['memory_mb'] + vram_mb)


1.2》分配配额

#  检查配额并且分配存储资源
try:
    quotas = objects.Quotas(context=context)
    # reserve:实现了对资源配额的检测、管理和分配
    quotas.reserve(instances=max_count,
                    cores=req_cores, ram=req_ram, 
                    project_id=project_id, user_id=user_id)

其中reserve方法对应在/nova/objects/quotas.py文件里

1.2.1》获取文件配额的到期时间

# 如果expire没有指定,则采用默认参数的值
# reservation_expire:这个参数定义了预约(资源配额)的到期时间长度
# 参数的默认值为86400
if expire in None:
    expire = CONF.reservation_expire
if isinstance(expire, six.integer_types):
    expire = datetime.timedelta(seconds=expire)
# 把当前时间加上预约时间长度,得到到期时间expire
if isinstance(expire, datetime.timedelta):
    expire = timeutils.utcnow() + expire
if not isinstance(expire, datetime.datetime):
    raise exception.InvalidReservationExpiration(expire=expire)

1.2.2》获取project_id和user_id

# 如果没有定义project_id,则应用context中的project_id值
if project_id is None:
    project_id = context.project_id
    LOG.debug('Reserving resources using context.project_id: %s',
                project_id)
# 如果没有定义user_id,则应用context中的user_id值
if user_id is None:
    user_id = context.user_id
    LOG.debug('Reserving resources using context.user_id: %s', 
                user_id)

1.2.3》获取对象的配额信息

# 获取user_id和project_id确定的用户对象的配额信息
user_quotas = self._get_quotas(context, resources, deltas.keys(),
                                has_sync=True, project_id=project_id,
                                user_id=user_id,
                                project_quotas=project_quotas)
# 获取project_id确定的对象的配额信息
quotas = self._get_quotas(context, resources, deltas.keys(),
                            has_sync=True, project_id=project_id,
                            project_quotas=project_quotas)
# 对于一个给定的项目,检索它的所有的磁盘配额
# 根据project_id查询数据库中相应项目的数据库信息
# 获取其中的hard_limit值,也就是获取规定的资源最大限额值
project_quotas = db.quota_get_all_by_project(context, project_id)

其中分析_get_quotas方法,这是个辅助方法,从数据库获取特定的配额资源信息:

1.2.3.1》筛选资源

if has_sync:
    sync_filt = lambda x: hasattr(x, 'sync') # 判断对象x是否包含sync的特性
else:
    sync_filt = lambda x: not hasattr(x, 'sync') # 判断对象x是否不包含sync的特性
desired = set(keys)
sub_resources = {k: v for k, v in resources.items()
                if k in desired and sync_filt(v)}

1.2.3.2》检测磁盘配额资源

# 确保所有磁盘配额资源都是已知的,否则引发异常,提示某些磁盘配额资源是未知的
if len(keys) != len(sub_resources):
    unknown = desired - set(sub_resoures.keys())
    raise exception.QuotaResourceUnknown(unknown=sorted(unknown))

1.2.3.3》根据user_id或project_id获取配额信息

有user_id,执行:

# 获取并返回未使用的磁盘配额
quotas = self.get_user_quotas(context, sub_resources,
                                project_id, user_id,
                                context.quota_class, usages=False,
                                project_quotas=project_quotas)

如果没有user_id,则执行:

quotas = self.get_project_quotas(context, sub_resources,
                                project_id,
                                context.quota_class,
                                usages=False,
                                project_quotas=project_quotas)

1.2.3.4》返回资源的limit值

# 以字典的形式返回各种资源的配额信息limit值
# 三种资源:instances、ram、cores
return {k: v['limit'] for k, v in quotas.items()}


1.2.4》最后获取资源配额

return db.quota_reserve(context, resources, quotas, user_quotas,
                        deltas, expire,
                        CONF.until_refresh, CONF.max_age,
                        project_id=project_id, user_id=user_id)

这里quota_reserve方法调用的是/nova/db/api.py文件的相应方法,该方法返回的是IMPL的quota_reserve方法:

return IMPL.quota_reserve(context, resources, quotas, user_quotas, deltas,
                            expire, until_refresh, max_age,
                            project_id=project_id, user_id=user_id)

_BACKEND_MAPPING设置中可看出,方法最终是调用了/nova/db/sqlalchemy.pyquota_reserve方法。

# context:程序运行上下文信息
# deltas:建立一个实例要求的资源信息,也就是每建立或者删除一个实例,资源配额的变化量
# expires:reservations的有限期
# until_refresh:是从配置信息CONF.until_refresh赋值的
# until_refresh作用:直到usage刷新,reservations的数目,默认值为0
# max_age:是从配置信息CONF.max_age赋值的
# max_age作用:刷新usage之间停留的秒数,默认值为0
@require_context
@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
def quota_reserve(context, resources, project_quotas, user_quotas, deltas,
                 expire, until_refresh, max_age, project_id=None,
                 user_id=None):

1.2.4.1》获取session

# 获取db_session的session
# get_session:返回一个SQLAlchemy session,若没有定义,则新建一个SQLAlchemy session
session = get_session()
with session.begin():

1.2.4.2》通过project_id和user_id获取资源使用情况

# 获取context中的project_id和user_id
if project_id is None:
    project_id = context.project_id
if user_id is None:
    user_id = context.user_id

# 从quota_usages表中获得当前工程的各种资源的使用情况
project_usages, user_usages = _get_project_user_quota_usages(
        context, session, project_id, )

1.2.4.3》取出resource元素判断是否需要进行refresh操作

# 处理usage的refresh操作
# 这里deltas.keys() = ['instances', 'ram', 'cores']
work = set(deltas.keys())
while work:
    # 任意地从集合work中取出一个元素
    resource = work.pop()

1.2.4.4》判断条件

# 判断是否需要重新刷新usage
created = _create_quota_usage_if_missing(user_usages, resource,
                                        until_refresh, project_id,
                                        user_id, session)
refresh = created or _is_quota_refresh_needed(
                            user_usages[resource], max_age)

1.2.4.5》获取不同资源的同步方法

# 执行更新(同步)usage
if refresh:
    # 获取同步方法,_sync_*(),这些方法定义在quota模块中
    # 在不同的资源有不同的同步方法,也即三种资源:instances、ram、cores
    # sync方法能够实时查询到工程当前所使用资源的情况
    # 也就能够用于刷新(同步)资源使用信息的操作
    sync = QUOTA_SYNC_FUNCTIONS[resources[resource], sync]

1.2.4.6》查询并更新实时数据

# 查询当前正在使用的实时的资源的数据信息
# 同步方法实现从数据库中查询到匹配的数据表‘instances’
# 进而获取其id、vcpus和memory_mb三种资源的实时使用情况
# 分别赋值给'instances'、'cores'和'ram',以字典的形式返回给updates
updates = sync(elevated, project_id, user_id, session)
for res, in_use in updates.items():
    # 如果实时使用的资源没有在usages中,那么把它添加进去
    _create_quota_usage_if_missing(user_usages, res,
                                    until_refresh, project_id,
                                    user_id, session)
    # 更新usage中的until_refresh数据信息
    _refresh_quota_usages(user_usages[res], until_refresh,
                          in_use)

1.2.4.7》处理in_use小于0的情况

# 检测资源数据中in_use加上delta之后,可能小于0的情况
# unders是检查delta为负数的情况,即执行了删除等操作,使delta为负,in_use减少
# 导致in_use值可能小于0
unders = [res for res, delta in deltas.items()
          if delta < 0 and
          delta + user_usages[res].in_use < 0]

1.2.4.8》检测是否有超额

# 检测这个resource的hard_limit是否小于in_use+resourced+delta之和
# 如果overs为真,说明in_use+resourced+delta的值已经大于系统限定的资源配额的数值
overs = _calculate_overquota(project_quotas, user_quotas, deltas,
                            project_usages, user_usages)

1.2.4.9》其余的是做其他信息的更新操作,不一一列举


2》循环创建实例

for i in range(num_instances):
    # 创建instance实例对象
    instance = objects.Instance(context=context)
    instance.update(base_options)
    # 为每一个新的实例在数据库中建立新的条目,包括任何更新的表(如安全组等等)
    instance = self.create_db_entry_for_new_instance(
            context, instance_type, boot_meta, instance,
            security_groups, block_device_mapping,
            num_instances, i, shutdown_terminate)
    # 实例instance添加到instances中
    instances.append(instance)

深入create_db_entry_for_new_instance方法:

2.1》建立新实例的开端

# _populate_instance_for_create:建立一个新的实例的开销
# 首先执行instance = base_options
# 然后补充一些实例的相关信息到instance这个字典中
# 返回设置好信息的实例字典
# 另外,还做了:
# 存储image镜像的属性信息,以便后面我们能够用到它们
# 对目前这个image镜像实例的来源,即这个基础镜像的记录信息进行保存
self._populate_instance_for_create(context, instance, image, index,
                                    security_group, instance_type)

2.2》确定基本信息

# 确定实例的显示名称和主机名称(display_name和hostname)
self._populate_instance_names(instance, num_instances)

# 确定实例的关机和终止状态信息(shutdown_terminate)
instance.shutdown_terminate = shutdown_terminate

# ensure_default:确保contenxt有一个安全组,如果没有就建立一个
self.security_group_api.ensure_default(context)

2.3》创建实例

# 创建一个新的实例,并记录在数据库中
instance.create()

2.4》处理建立多个实例的情况

# 如果要建立实例的最大数目大于1
# 当一个请求创建多个实例时,这时实例的命名遵循名称模板来进行
if num_instances > 1:
    instance = self._apply_instance_name_template(context, instance,
                                                  index)


(未完待续...)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值