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

--------------- 紧接上篇 nova实例创建(2) --------------------


5. 分析create方法

在最开始的run_instances方法中可知创建实例是通过self.compute_api.create方法来实现的,该方法是在/nova/compute/api.py

# 建立所有类型的实例都要执行
# 返回一个元组(实例或者是reservation_id的元组)
# 元组里面的实例可以是"None"或者是实例字典的一个列表,这要取决于是否等待scheduler返回的信息
@hooks.add_hook("create_instance")
def create(self, context, instance_type,
           image_href, kernel_id=None, ramdisk_id=None,
           min_count=None, max_count=None,
           display_name=None, display_description=None,
           key_name=None, key_data=None, security_group=None,
           availability_zone=None, user_data=None, metadata=None,
           injected_files=None, admin_password=None,
           block_device_mapping=None, access_ip_v4=None,
           access_ip_v6=None, requested_networks=None, config_drive=None,
           auto_disk_config=None, scheduler_hints=None, legacy_bdm=True,
           shutdown_terminate=None, check_server_group_quota=False):复制代码


5.1 首先对执行create方法的资格进行验证

# 验证是否有资格执行create这个方法
# policy是nova中的一个资格验证机制
self._check_create_policies(context, availability_zone,
        requested_networks, block_device_mapping)复制代码


5.2 检测多个实例情况下IP和端口的使用情况

if requested_networks and max_count > 1:
    # 检测多个实例是否同时占用一个固定IP,如果是,引发异常
    self._check_multiple_instances_and_specified_ip(requested_networks)
    if utils.is_neutron():
        # 检测多个实例是否使用同一个端口,如果不是,引发异常
        self._check_multiple_instances_neutron_ports(
            requested_networks)
复制代码


5.3 验证所有参数并最后发送请求消息给调度器

# 验证所有的输入实例参数
# 发送要运行实例(‘run_instance’)的请求消息到远程调度器
return self._create_instance(
            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,
            scheduler_hints=scheduler_hints,
            legacy_bdm=legacy_dbm,
            shutdown_terminate=shutdown_terminate,
            check_server_group_quota=check_server_group_quota)复制代码

5.3.1 分析_create_instance方法

# 实际创建实例方法
# 不管之前创建实例前做了什么检验策略,这里还要对输入参数进行校验
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_groups,
            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,
            legacy_bdm=True, shutdown_terminate=False,
            check_server_group_quota=False):复制代码


(1)设置一些基本参数

# 设置一些基本参数
# generate_uid:随机生成一个uuid值赋值给reservation_id
if reservation_id is None:
    reservation_id = utils.generate_uid('r')
# 设置安全组或者使用默认的安全策略
security_groups = security_groups or ['default']
min_count = min_count or 1
max_count = max_count or min_count
block_device_mapping = block_device_mapping or []
if not instance_type:
    instance_type = flavors.get_default_flavor()
# 设置镜像image信息
if image_href:
    image_id, boot_meta = self._get_image(context, image_href)
else:
    image_id = None
    boot_meta = self._get_bdm_image_metedata(
        context, block_device_mapping, legacy_bdm)
复制代码


(2)设置磁盘配置和可用区

self._check_auto_disk_config(image=boot_meta,
                             auto_disk_config=auto_disk_config)


handle_az = self._handle_availability_zone
availability_zone, forced_host, forced_node = handle_az(context,
                                                    availability_zone)

if not self.skip_policy_check and (forced_host or forced_node):
    check_policy(context, 'create:forced_host', {})
复制代码


(3)对所有输入参数进行验证

# 对输入的所有参数进行验证,并建立基本的元素
base_options, max_net_count = self._validate_and_build_base_options(
        context,
        instance_type, boot_meta, image_href, image_id, kernel_id,
        ramdisk_id, display_name, display_description,
        key_name, key_data, security_groups, availability_zone,
        forced_host, user_data, metadata, access_ip_v4,
        access_ip_v6, requested_networks, config_drive,
        auto_disk_config, reservation_id, max_count)复制代码

深入其中的_validate_and_build_base_options方法进行分析:

1》验证可用的空间

# 验证是否有可用的空间
# 如果空间不可用,引发异常
if availability_zone:
    available_zones = availability_zones.\
        get_availability_zones(context.elevated(), True)
    if forced_host is None and availability_zone not in \
            available_zones:
        msg = _('The requested availability zone is not available')
        raise exception.InvalidRequest(msg)
复制代码

2》验证实例类型

# 处理实例类型为disabled的情况
if instance_type['disabled']:
    raise exception.FlavorNotFound(flavor_id=instance_type['id'])
复制代码

3》验证user_data

if user_data:
    l = len(user_data)
    if l > MAX_USERDATA_SIZE:
        # user_data会存储在一个text类型的列中
        # 如果该值过长,数据库会自动缩短长度
        raise exception.InstanceUserDataTooLarge(
            length=1, maxsize=MAX_USERDATA_SIZE)

    # 处理user_data的字符集
    try:
        base64.deeedestring(user_data)
    except base64.binascii.Error:
        raise exception.InstanceUserDataMalformed()
复制代码

4》验证安全组

# 检测所要求的安全组是否存在,并且属于指定的对象,如果不存在则会引发异常
self._check_requested_secgroups(context, security_groups)复制代码

5》验证网络

# 检测所需求的网络是否属于指定的对象(工程)
# 并且为每个网络提供的固定的IP地址是否处在同一个网段中
max_network_count = self._check_requested_networks(context,
                            requested_networks, max_count)复制代码

6》处理并获取kernel和ramdisk值

# 为实例获取合适的内核和ramdisk的值
kernel_id, ramdisk_id = self._handle_kernel_and_ramdisk(
        context, kernel_id, ramdisk_id, boot_meta)
复制代码

7》转换驱动格式

# 转换驱动drive的格式,从string到bool串
config_drive = self._check_config_drive(config_drive)复制代码

8》处理key参数

# 处理key参数有name值但没有data值的情况
if key_data is None and key_name is not None:
    key_pair = objects.KeyPair.get_by_name(context,
                                           context.user_id,
                                           key_name)
    key_data = key_pair.public_key
复制代码

9》处理其他信息

# 获取镜像image元数据
image_meta = objects.ImageMeta.from_dict(boot_meta)
# numa拓扑
numa_topology = hardware.numa_get_constraints(
        instance_type, image_meta)

system_metadata = {}

pci_request_info = pci_request.get_pci_requests_from_flavor(
    instance_type)
self.network_api.create_pci_requests_for_sriov_ports(context,
    pci_request_info, requested_networks)复制代码

10》然后构建所有的基本元素base_options

11》获取更新后的base_options属性

# 从image镜像中继承相关属性信息
# 包括os_type、architecture、vm_mode和auto_disk_config
options_from_image = self._inherit_properties_from_image(
        boot_meta, auto_disk_config)

# 相关属性更新到字典base_options当中
base_options.update(options_from_image)复制代码

12》最终返回结果

# 返回验证过的参数
# 以及根据网络配额计算的最大允许实例数
return base_options, max_network_count复制代码


(4)对最大实例数进行调整

# 根据返回的最大网络数max_net_count进行相应调整
# 如果等于0,引发异常
if max_net_count == 0:
    raise exception.PortLimitExceeded()
elif max_net_count < max_count:
    LOG.debug("max count reduced from %(max_count)d to "
              "%(max_net_count)d due to network port quota",
              {'max_count': max_count,
               'max_net_count': max_net_count})
    # 如果返回的最大网络数小于开始设置的最大实例数
    # 则应该把max_count修改为较小值,则此时的max_net_count
    max_count = max_net_count复制代码


(5)获取块设备映射

# 获取块设备映射
block_device_mapping = self._check_and_transform_bdm(context,
    base_options, instance_type, boot_meta, min_count, max_count,
    block_device_mapping, legary_bdm)复制代码


(6)对基本元素进行检测然后创建或重建

# 这个检测需要等所有来源的bdm都合并在一块去获取到root bdm后去执行
self._checks_for_create_and_rebuild(context, image_id, boot_meta,
        instance_type, metadata, injected_files,
        block_device_mapping.root_bdm())复制代码

深入_checks_for_create_and_rebuild方法,可以发现它是依次调用三个检测方法:

# 对磁盘配额的元数据属性进行检测
self._check_metadata_properties_quota(context, metadata)
# 对磁盘配额注入文件的限制进行检测
self._check_injected_file_quota(context, files_to_inject)
# 对所要求的镜像image进行检测
self._check_requested_image(context, image_id, image,
                            instance_type, root_bdm)复制代码

1》_check_metadata_properties_quota

1.1》metadata格式预处理

if not metadata:
    metadata = {}
# 确保metadata为字典
if not isinstance(metadata, dict):
    msg = (_("Metadata type should be dict."))
    raise exception.InvalidMetadata(reason=msg)
复制代码

1.2》metadata配额处理

# limit_check:简单的磁盘配额限制检测,并构造一个资源列表
# 这个资源列表将会被values.items所限制,会据此来检测磁盘资源配额是否符合要求
# 如果资源超出配额限制,则会引发异常
try:
    objects.Quotas.limit_check(context, metadata_items=num_metadata)
except exception.OverQuota as exc:
    quota_metadata = exc.kwargs['quotas']['metadata_items']
    raise exception.MetadataLimitExceeded(allowed=quota_metadata)
复制代码

1.3》检测metadata的长度

for k, v in six.iteritems(metadata):
    try:
        utils.check_string_length(v)
        utils.check_string_length(k, min_length=1)
    except exception.InvalidInput as e:
        raise exception.InvalidMetadata(reason=e.format_message())

    # 确保metadata中数据的长度在0-255之间
    if len(k) > 255:
        msg = _("Metadata property key greater than 255 characters")
        raise exception.InvalidMetadataSize(reason=msg)
    if len(v) > 255:
        msg = _("Metadata property value greater than 255 characters")
        raise exception.InvalidMetadataSize(reason=msg)

复制代码

2》_check_injected_file_quota

2.1》限制注入文件数目

try:
    objects.Quotas.limit_check(context,
                                injected_files=len(injected_files))
except exception.OverQuota:
    raise exception.OnsetFileLimitExceeded()
复制代码

2.2》限制注入文件路径长度和注入内容长度

max_path = 0
max_context = 0
for path, content in injected_files:
    max_path = max(max_path, len(path))
    max_content = max(max_content, len(content))

try:
    objects.Quotas.limit_check(context,
                                injected_file_path_bytes=max_path,
                                injected_file_content_bytes=max_content)
复制代码

3》_check_requested_image

3.1》检测镜像状态

# 镜像状态应该为active
if image['status'] != 'active':
    raise exception.ImageNotActive(image_id=image_id)
复制代码

3.2》检测drive配置选项

# drive配置选项应该为“可选”或“强制”
image_properties = image.get('properties', {})
config_drive_option = image_properties.get(
    'img_config_drive', 'optional')
if config_drive_option not in ['optional', 'mandatory']:
    raise exception.InvalidImageConfigDrive(
        config_drive=config_drive_option)
复制代码

3.3》检测内存空间

# 内存空间应该足够大
if instance_type['memory_mb'] < int(image.get('min_ram') or 0):
    raise exception.FlavorMemoryTooSmall()
复制代码

3.4》(还有一些检测,有待研究)


(7)获取实例组

instance_group = self._get_requested_instance_group(context,
                            scheduler_hints, check_server_group_quota)复制代码


(未完待续...)


转载于:https://juejin.im/post/5b10e44a5188257d831e26dd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值