首页 > 网络 > 云计算 >

NovaAPI服务之创建虚拟机流程(2)

2016-11-01

NovaAPI服务之创建虚拟机流程(2)。

1、servers资源底层Controller对象的create方法调用compute API的create方法。Compute API类的create方法:

/nova/nova/compute/api.py

class API(base.Base):
    def create(self,context,instance_type,
               ...):
        #检查客户是否具有创建虚拟机的权限
        self._check_create_policies(context, availability_zone,
                requested_networks, block_device_mapping)
        #进一步处理创建虚拟机的请求
        return self._create_instance(context,intance_type,
                                     ...)

其中_check_create_policies方法的作用是检查客户端是否有创建虚拟机的权限,该方法会通过查询policy.json文

件来查看用户权限,其内部逻辑和Keystone服务的权限管理类似。由于这里只是谈虚拟机的创建过程,所以关于

Keystone就不在展开详谈。

2、接下来就是我们的重头戏了——_create_instance方法。这个方法就是执行进一步的虚拟机的创建过程,后面的

文章会花大篇幅来详细分析。

注:这里我们为了便于理解,再次打印出传入这个方法的参数实例

context =   
instance_type = {'memory_mb': 2048L, 'root_gb': 20L, 'deleted_at': None, 'name': u'm1.small',
                 'deleted': 0L, 'created_at': None, 'ephemeral_gb': 0L, 'updated_at': None,
                 'disabled': False, 'vcpus': 1L, 'extra_specs': {}, 'swap': 0L, 'rxtx_factor': 1.0,
                 'is_public': True, 'flavorid': u'2', 'vcpu_weight': None, 'id': 5L} 
image_href = 20612b24-c980-4900-b270-8e6b66e5f72f
kernel_id = None
ramdisk_id = None
min_count = 1  
max_count = 1  
display_name = test3  
display_description = test3  
key_name = oskey  
key_data = None  
security_group = ['default']  
availability_zone = None  
user_data = None  
metadata = {}  
injected_files = []  
admin_password = Piu4aSSNmSNk  
block_device_mapping = []  
access_ip_v4 = None  
access_ip_v6 = None  
requested_networks = None  
config_drive = None  
auto_disk_config = None  
scheduler_hints = {}  

3、继续分析_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 方法更新 Nova数据库中InstanceAction表的记录。InstanceAction表专门保存虚

拟机当前正在执行的操作。续集你所有可能的操作在instance_actions包中定义。

(3)调用scheduler_rpcapi.run_instance方法,向nova Scheduler服务发送RPC请求,将虚拟机创建请求交给nova

Scheduler服务处理。(后面详细介绍Nova Scheduler服务如何处理虚拟机创建请求)

4、_validate_and_provision_instance

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

。该方法的定义如下。(可能源码分析不是Liberty版本,高版本把函数进行封装)

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)
            #检查注入文件的个数和大小是否超标
            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_href)
                #获取虚拟机镜像文件信息
                image = image_service.show(context, image_id)
                #检查镜像是否可用
                if image['status'] != 'active':
                    raise exception.ImageNotActive(image_id=image_id)
            else:
                image = {}
            #检查虚拟机内存是否足够大
            if instance_type[&#39;memory_mb&#39;] < int(image.get(&#39;min_ram&#39;) or 0):
                raise exception.InstanceTypeMemoryTooSmall()
            #检查虚拟机磁盘是否足够大
            if instance_type[&#39;root_gb&#39;] < int(image.get(&#39;min_disk&#39;) or 0):
                raise exception.InstanceTypeDiskTooSmall()
            ...
            #用于创建数据库的记录
            base_options = {
                &#39;reservation_id&#39;: reservation_id,
                ...}
            #获取磁盘镜像中指定的参数
            options_from_image = self._inherit_properties_from_image(
                image, auto_disk_config)
            #将磁盘镜像中的参数合并至base_options
            base_options.update(options_from_image)
            ...
            

主要做了3件事情:

(1)验证客户端传入的参数

(2)修改租户的QUOTAS,为虚拟机预留硬件资源

(3)在数据库中创建虚拟机记录

这个方法完成了很多工作,来逐条分析吧。

(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[&#39;vcpus&#39;]  
    req_ram = max_count * instance_type[&#39;memory_mb&#39;]  
      
    #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[&#39;quotas&#39;]  
        usages = exc.kwargs[&#39;usages&#39;]  
        overs = exc.kwargs[&#39;overs&#39;]  
      
        headroom = dict((res, quotas[res] -  
            (usages[res][&#39;in_use&#39;] + usages[res][&#39;reserved&#39;]))  
            for res in quotas.keys())
        allowed = headroom[&#39;instances&#39;]
        ...     
    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:这个参数定义了配额管理默认的驱动类;  
    #参数的默认值为&#39;nova.quota.DbQuotaDriver&#39;;  
    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, &#39;sync&#39;)    #判断对象x是否包含sync的特性
        else:
            sync_filt = lambda x: not hasattr(x, &#39;sync&#39;)#判断对象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[&#39;limit&#39;]) for k, v in quotas.items())

来看get_project_quotas方法,这里的sub_resources就是从resources字典中获取

的&lsquo;instances&rsquo;、&lsquo;ram&rsquo;、&lsquo;cores&rsquo;三部分。下篇文章再继续分析get_project_quotas方法。

相关文章
最新文章
热点推荐