OpenStack之Nova分析——创建虚拟机(二)

接着上篇文章,我们来继续分析_create_instance方法。这个方法定义在nova/nova/compute/api.py中。

class API(base.Base):
    def _create_instance(self,context,instance_type,
                         ...):
        ...
        #验证客户端传入参数,做虚拟机创建前的准备工作
        (instances,request_spec,filter_properties) = self._validate_and_provision_instance(context,
                                                                                           instance_type,
                                                                                           ...)
        for instance in instances:
            #更新InstanceAction表记录
            self._record_action_start(context,instance,instance_actions.CREATE)
        #向Nova Scheduler服务发送RPC请求,创建虚拟机
        self.scheduler_rpcapi.run_instance(context,...)
        return (instances,reservation_id)
可以看到_create_instance方法包括三个步骤:1.调用_validate_and_provision_instance方法完成虚拟机创建的准备工作。2.调用_record_action_start方法更新数据库中InstanceAction表的记录,将虚拟机状态改为“开始创建”。3.调用scheduler_rpcapi.run_instance方法,向Nova Scheduler服务发送RPC请求,将虚拟机创建请求交给Nova Scheduler服务处理。

1. _validate_and_provision_instance

这个方法非常重要,它主要完成的是对输入的各个参数的检验,其中完成了一个非常重要的任务,就是资源的配额管理。该方法的定义如下。

class API(base.Base):
    def _validate_and_provision_instance(self,context,instance_type,
                                         ...):
        #一些参数的验证和初始化
        if not metadata:
            metadata = {}
        if not security_group:
            security_group = 'default'
        ...
        #如果客户端没有指定虚拟机规格,则使用默认的格式
        if not instance_type:
            instance_type = instance_type.get_default_instance_type()
        ...
        #根据配额资源限制计算所要建立实例的数目,并获取了分配好的资源(块存储)的UUID的列表
        num_instances, quota_reservations = self._check_num_instances_quota(
                context, instance_type,...)
        try:
            instances = []
            instance_uuids = []
            #检查metadata项数是否超标
            self._check_metadata_properties_quota(context, metadata)
            #<span><span class="comment">检查注入文件的个数和大小是否超标</span></span>
            self._check_injected_file_quota(context, injected_files)
            #检查需求网络是否合法
            self._check_requested_networks(context, requested_networks)
            #获取、检查虚拟机镜像文件
            if image_href:
                #获取虚拟机镜像文件的uuid
                (image_service, image_id) = glance.get_remote_image_service(context,
                                                                    image_href)
                #获取虚拟机镜像文件信息
                image = image_service.show(context, image_id)
                #检查镜像是否可用
                if image['status'] != 'active':
                    raise exception.ImageNotActive(image_id=image_id)
            else:
                image = {}
            #检查虚拟机内存是否足够大
            if instance_type['memory_mb'] < int(image.get('min_ram') or 0):
                raise exception.InstanceTypeMemoryTooSmall()
            #检查虚拟机磁盘是否足够大
            if instance_type['root_gb'] < int(image.get('min_disk') or 0):
                raise exception.InstanceTypeDiskTooSmall()
            ...
            #用于创建数据库的记录
            base_options = {
                'reservation_id': reservation_id,
                ...}
            #获取镜像中指定的参数
            options_from_image = self._inherit_properties_from_image(
                image, auto_disk_config)
            #将镜像中的参数合并至base_options
            base_options.update(options_from_image)
            ...
            
这个方法完成了很多工作,来逐条分析吧。

(1) num_instances, quota_reservations = self._check_num_instances_quota(context, instance_type, min_count, max_count)

这个方法主要实现的是资源配额管理,根据磁盘配额限制确定所要创建的实例数目,并获取分配好的资源(块存储)uuid的列表。

def _check_num_instances_quota(self, context, instance_type, min_count,max_count):  
    """ 
    根据配额资源限制所要建立实例的数目; 
    返回值max_count表示建立实例的最大数目; 
    返回值reservations表示建立的预定(分配)的资源的UUID的列表; 
    """  
      
    #确定请求分配的内核数和RAM;  
    req_cores = max_count * instance_type['vcpus']  
    req_ram = max_count * instance_type['memory_mb']  
      
    #reserve:检查配额并且分配存储资源
    #返回所建立的预定(分配)的资源的UUID的列表给reservations;  
    try:  
        reservations = QUOTAS.reserve(context, instances=max_count,cores=req_cores, ram=req_ram)  
    except exception.OverQuota as exc:
        #查找超出配额限制的原因;  
        quotas = exc.kwargs['quotas']  
        usages = exc.kwargs['usages']  
        overs = exc.kwargs['overs']  
      
        headroom = dict((res, quotas[res] -  
            (usages[res]['in_use'] + usages[res]['reserved']))  
            for res in quotas.keys())
        allowed = headroom['instances']
        ...     
    return max_count, reservations  

重点分析这句: reservations = QUOTAS.reserve(context, instances=max_count,cores=req_cores, ram=req_ram)

这条语句实现了对资源配额的检测、管理和分配,如果没有错误,则返回所建立的资源的uuid列表这里调用了QUOTAS类的reserve方法,这个类位于nova/nova/quotas.py中。

def reserve(self, context, expire=None, project_id=None, **deltas):  
        """ 
        检查配额并且分配存储资源; 
        如果没有错误,方法返回所建立的预定(分配)的资源的UUID的列表; 
        """
        reservations = self._driver.reserve(context, self._resources, deltas,expire=expire,project_id=project_id)  
        LOG.debug(_("Created reservations %(reservations)s") % locals())
        return reservations
我们再来看看方法_driver的定义。

def _driver(self):  
    if self.__driver:  
        return self.__driver  
    #quota_driver:这个参数定义了配额管理默认的驱动类;  
    #参数的默认值为'nova.quota.DbQuotaDriver';  
    if not self._driver_cls:  
        self._driver_cls = CONF.quota_driver  
    if isinstance(self._driver_cls, basestring):  
        self._driver_cls = importutils.import_object(self._driver_cls)  
    self.__driver = self._driver_cls  
    return self.__driver
可以看到返回的self._driver的值默认为配置文件中的“quota_driver",这里是“nova.quota.DbQuotaDriver”,所以上一个方法调用的就是这个类的reserve方法。

def reserve(self, context, resources, deltas, expire=None, project_id=None):
        #如果expire没有指定,则采用默认参数的值
        #reservation_expire:这个参数定义了预约(资源配额)的到期时间长度
        #参数的默认值为86400
        if expire is None:
            expire = CONF.reservation_expire
        if isinstance(expire, (int, long)):
            expire = datetime.timedelta(seconds=expire)
        if isinstance(expire, datetime.timedelta):
            expire = timeutils.utcnow() + expire
        if not isinstance(expire, datetime.datetime):
            raise exception.InvalidReservationExpiration(expire=expire)  
  
        #如果没有定义project_id,则应用context中的project_id值;  
        if project_id is None:
            project_id = context.project_id
  
        #获取适用的配额信息  
        quotas = self._get_quotas(context, resources, deltas.keys(), has_sync=True, project_id=project_id)
  
        return db.quota_reserve(context, resources, quotas, deltas, expire,
                                CONF.until_refresh, CONF.max_age,
                                project_id=project_id)
这里先定义了预约资源的到期时间,接下来调用_get_quotas方法来获取project_id对应对象的配额信息。在分析_get_quotas方法之前,先来看看这个方法的参数。主要是参数resources,通过这个参数我们可以从数据库中获取实例相应的资源的信息,另外detals是 (instances=max_count,cores=req_cores, ram=req_ram)组成的字典。下面来分析_get_quotas方法。

def _get_quotas(self, context, resources, keys, has_sync, project_id=None):
        #筛选资源
        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 = dict((k, v) for k, v in resources.items()
                             if k in desired and sync_filt(v))
  
        #确保所有磁盘配额资源都是已知的,否则引发异常,提示某些磁盘配额资源是未知的 
        if len(keys) != len(sub_resources):  
            unknown = desired - set(sub_resources.keys())  
            raise exception.QuotaResourceUnknown(unknown=sorted(unknown))  
  
        #获取并返回磁盘配额
        quotas = self.get_project_quotas(context, sub_resources,project_id,context.quota_class, usages=False)
        return dict((k, v['limit']) for k, v in quotas.items())
来看get_project_quotas方法,这里的sub_resources就是从resources字典中获取的‘instances’、‘ram’、‘cores’三部分。

篇幅有点长了~~下篇文章再继续分析get_project_quotas方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值