openstack nova源码分析--compute创建虚拟机(1)

博客作为学习笔记记录,若有理解或表述错误,欢迎指出

在OpenStack nova创建虚拟机过程中,nova-api收到虚拟机创建请求,然后nova-scheduler完成选择合适计算节点的任务,nova-conductor则开始调用build_instance()来创建虚机。

在conductor.manager.ComputeTaskManager.build_instance()中,通过rpc调用:self.compute_rpcapi.build_and_run_instance(),来调用compute节点上的build_and_run_instance()方法。而build_and_run_instance()方法就完成VM在计算节点上的创建和启动任务。

下面我们来看看Nova compute上的build_and_run_instance()的具体实现。

def build_and_run_instance(self, context, instance, image, request_spec,
                     filter_properties, admin_password=None,
                     injected_files=None, requested_networks=None,
                     security_groups=None, block_device_mapping=None,
                     node=None, limits=None, host_list=None):

        @utils.synchronized(instance.uuid)
        def _locked_do_build_and_run_instance(*args, **kwargs):
            # NOTE(danms): We grab the semaphore with the instance uuid
            # locked because we could wait in line to build this instance
            # for a while and we want to make sure that nothing else tries
            # to do anything with this instance while we wait.
            with self._build_semaphore:
                try:
                    result = self._do_build_and_run_instance(*args, **kwargs)
                except Exception:
                    # NOTE(mriedem): This should really only happen if
                    # _decode_files in _do_build_and_run_instance fails, and
                    # that's before a guest is spawned so it's OK to remove
                    # allocations for the instance for this node from Placement
                    # below as there is no guest consuming resources anyway.
                    # The _decode_files case could be handled more specifically
                    # but that's left for another day.
                    result = build_results.FAILED
                    raise
                finally:
                    if result == build_results.FAILED:
                        # Remove the allocation records from Placement for the
                        # instance if the build failed. The instance.host is
                        # likely set to None in _do_build_and_run_instance
                        # which means if the user deletes the instance, it
                        # will be deleted in the API, not the compute service.
                        # Setting the instance.host to None in
                        # _do_build_and_run_instance means that the
                        # ResourceTracker will no longer consider this instance
                        # to be claiming resources against it, so we want to
                        # reflect that same thing in Placement.  No need to
                        # call this for a reschedule, as the allocations will
                        # have already been removed in
                        # self._do_build_and_run_instance().
                        self.reportclient.delete_allocation_for_instance(
                            context, instance.uuid)

                    if result in (build_results.FAILED,
                                  build_results.RESCHEDULED):
                        self._build_failed(node)
                    else:
                        self._build_succeeded(node)

        # NOTE(danms): We spawn here to return the RPC worker thread back to
        # the pool. Since what follows could take a really long time, we don't
        # want to tie up RPC workers.
        utils.spawn_n(_locked_do_build_and_run_instance,
                      context, instance, image, request_spec,
                      filter_properties, admin_password, injected_files,
                      requested_networks, security_groups,
                      block_device_mapping, node, limits, host_list)

compute.manager.ComputeManager.build_and_run_instance()方法很简单,里面定义了一个_locked_do_build_and_run_instance()方法,这里真正实现instance的创建和启动的,还有一个spawn方法调用,这是因为nova compute创建VM的时间会比较长,因为其中包括了下载VM镜像、创建和配置VM网卡信息、文件注入等任务,因此调用utils.spawn_n()方法返回RPC线程而不会造成阻塞。

在_locked_do_build_and_run_instance()方法直接调用self._do_build_and_run_instance()。

    @hooks.add_hook('build_instance')
    @wrap_exception()
    @reverts_task_state
    @wrap_instance_event(prefix='compute')
    @wrap_instance_fault
    def _do_build_and_run_instance(self, context, instance, image,
            request_spec, filter_properties, admin_password, injected_files,
            requested_networks, security_groups, block_device_mapping,
            node=None, limits=None, host_list=None):

        try:
            LOG.debug('Starting instance...', instance=instance)
            instance.vm_state = vm_states.BUILDING
            instance.task_state = None
            instance.save(expected_task_state=
                    (task_states.SCHEDULING, None))
        except exception.InstanceNotFound:
            msg = 'Instance disappeared before build.'
            LOG.debug(msg, instance=instance)
            return build_results.FAILED
        except exception.UnexpectedTaskStateError as e:
            LOG.debug(e.format_message(), instance=instance)
            return build_results.FAILED

        # b64 decode the files to inject:
        decoded_files = self._decode_files(injected_files)

        if limits is None:
            limits = {}

        if node is None:
            node = self._get_nodename(instance, refresh=True)

        try:
            with timeutils.StopWatch() as timer:
                self._build_and_run_instance(context, instance, image,
                        decoded_files, admin_password, requested_networks,
                        security_groups, block_device_mapping, node, limits,
                        filter_properties, request_spec)
            LOG.info('Took %0.2f seconds to build instance.',
                     timer.elapsed(), instance=instance)
            return build_results.ACTIVE
        except exception.RescheduledException as e:
            retry = filter_properties.get('retry')
            if not retry:
                # no retry information, do not reschedule.
                LOG.debug("Retry info not present, will not reschedule",
                    instance=instance)
                self._cleanup_allocated_networks(context, instance,
                    requested_networks)
                self._cleanup_volumes(context, instance,
                    block_device_mapping, raise_exc=False)
                compute_utils.add_instance_fault_from_exc(context,
                        instance, e, sys.exc_info(),
                        fault_message=e.kwargs['reason'])
                self._nil_out_instance_obj_host_and_node(instance)
                self._set_instance_obj_error_state(context, instance,
                                                   clean_task_state=True)
                return build_results.FAILED
            LOG.debug(e.format_message(), instance=instance)
            # This will be used for logging the exception
            retry['exc'] = traceback.format_exception(*sys.exc_info())
            # This will be used for setting the instance fault message
            retry['exc_reason'] = e.kwargs['reason']
            # NOTE(comstud): Deallocate networks if the driver wants
            # us to do so.
            # NOTE(mriedem): Always deallocate networking when using Neutron.
            # This is to unbind any ports that the user supplied in the server
            # create request, or delete any ports that nova created which were
            # meant to be bound to this host. This check intentionally bypasses
            # the result of deallocate_networks_on_reschedule because the
            # default value in the driver is False, but that method was really
            # only meant for Ironic and should be removed when nova-network is
            # removed (since is_neutron() will then always be True).
            # NOTE(vladikr): SR-IOV ports should be deallocated to
            # allow new sriov pci devices to be allocated on a new host.
            # Otherwise, if devices with pci addresses are already allocated
            # on the destination host, the instance will fail to spawn.
            # info_cache.network_info should be present at this stage.
            if (self.driver.deallocate_networks_on_reschedule(instance) or
                utils.is_neutron() or
                self.deallocate_sriov_ports_on_reschedule(instance)):
                self._cleanup_allocated_networks(context, instance,
                        requested_networks)
            else:
                # NOTE(alex_xu): Network already allocated and we don't
                # want to deallocate them before rescheduling. But we need
                # to cleanup those network resources setup on this host before
                # rescheduling.
                self.network_api.cleanup_instance_network_on_host(
                    context, instance, self.host)

            self._nil_out_instance_obj_host_and_node(instance)
            instance.task_state = task_states.SCHEDULING
            instance.save()
            # The instance will have already claimed resources from this host
            # before this build was attempted. Now that it has failed, we need
            # to unclaim those resources before casting to the conductor, so
            # that if there are alternate hosts available for a retry, it can
            # claim resources on that new host for the instance.
            self.reportclient.delete_allocation_for_instance(context,
                                                             instance.uuid)

            self.compute_task_api.build_instances(context, [instance],
                    image, filter_properties, admin_password,
                    injected_files, requested_networks, security_groups,
                    block_device_mapping, request_spec=request_spec,
                    host_lists=[host_list])
            return build_results.RESCHEDULED
        except (exception.InstanceNotFound,
                exception.UnexpectedDeletingTaskStateError):
            msg = 'Instance disappeared during build.'
            LOG.debug(msg, instance=instance)
            self._cleanup_allocated_networks(context, instance,
                    requested_networks)
            return build_results.FAILED
        except Exception as e:
            if isinstance(e, exception.BuildAbortException):
                LOG.error(e.format_message(), instance=instance)
            else:
                # Should not reach here.
                LOG.exception('Unexpected build failure, not rescheduling '
                              'build.', instance=instance)
            self._cleanup_allocated_networks(context, instance,
                    requested_networks)
            self._cleanup_volumes(context, instance,
                    block_device_mapping, raise_exc=False)
            compute_utils.add_instance_fault_from_exc(context, instance,
                    e, sys.exc_info())
            self._nil_out_instance_obj_host_and_node(instance)
            self._set_instance_obj_error_state(context, instance,
                                               clean_task_state=True)
            return build_results.FAILED

在_do_build_and_run_instance()中完成了几件事情:
在这里插入图片描述

  1. 首先在函数_do_build_and_run_instance()中先设置任务状态:修改vm_state为building,task_state为None,然后调用instance.save()将状态存入数据库中。
  2. 如果起虚机时有文件注入,将会调用nova.compute.manager.ComputeManager._decode_files()完成文件注入处理。
# b64 decode the files to inject:
        decoded_files = self._decode_files(injected_files)
  1. 接下来是虚机创建的主要任务:
    3.1 开始创建VM并启动,调用nova.compute.manager.ComputeManager._build_and_run_instance()开始创建VM,此时vm_stat为building,task_state为spawning,然后调用instance.save()方法将状态存入数据库中。
    3.2 VM创建过程中,包括了为虚机申请disk、ram、申请网卡、以及网卡上的IP、MAC信息等。计算节点上创建虚机任务的大头也就是这个地方,这里先简单介绍功能和流程,后面再具体展开各类资源申请过程。
    3.3 等虚机各种资源都申请成功后,更新数据库中的状态:vm_state为building, task_state为spawning
    3.4. 最后,万事俱备只欠东风,通过调用driver.spawn来完成VM孵化。OpenStack底层调用的是libvirt,因此这里的driver就是libvirt提供的接口。
    with timeutils.StopWatch() as timer:
                         self.driver.spawn(context, instance, image_meta,
                                           injected_files, admin_password,
                                           allocs, network_info=network_info,
                                           block_device_info=block_device_info)
    

现在,这里介绍了OpenStack compute节点上,虚机创建的关键步骤和主要流程,后续将会对每个环节具体展开介绍。

猜你喜欢

转载自blog.csdn.net/ksj367043706/article/details/89287020