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 = <nova.context.RequestContext object at 0x447d050>
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服务发送<span style="color:#ff0000;"><strong>RPC请求</strong></span>,创建虚拟机
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)
#<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_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)
...
主要做了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):
"""
根据<strong><span style="color:#ff0000;">配额资源</span></strong>限制所要建立实例的数目;
返回值max_count表示<strong><span style="color:#ff0000;">建立实例的最大数目</span></strong>;
返回值reservations表示<strong><span style="color:#ff0000;">建立的预定(分配)的资源的UUID的列表</span></strong>;
"""
#确定请求分配的内核数和RAM;
req_cores = max_count * instance_type['vcpus']
req_ram = max_count * instance_type['memory_mb']
#reserve:<strong><span style="color:#ff0000;">检查配额并且分配存储资源</span></strong>
#返回所建立的预定(分配)的<strong><span style="color:#ff0000;">资源的UUID的列表</span></strong>给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:这个参数定义了<span style="color:#ff0000;"><strong>配额管理默认的驱动类</strong></span>;
#参数的默认值为'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方法。
参考博文:
http://blog.csdn.net/qiuhan0314/article/details/43015683
openstack王者归来