zoukankan      html  css  js  c++  java
  • openstack虚拟机启动过程源码分析

    源码版本:H版

      以nova-api为起点开始分析! 

    一、在nova-api进程中进行处理

      根据对nova api的分析,当请求发过来的时候,由相应的Controller进行处理,此处如下:

    nova/api/openstack/compute/servers.py

    Controller类:
    @wsgi.response(202)
    @wsgi.serializers(xml=FullServerTemplate)
    @wsgi.deserializers(xml=CreateDeserializer)
    def create(self, req, body):
        try:
            _get_inst_type = flavors.get_flavor_by_flavor_id
            inst_type = _get_inst_type(flavor_id, ctxt=context,
                                       read_deleted="no")
    
            (instances, resv_id) = self.compute_api.create(context,
                            ...
                            legacy_bdm=legacy_bdm)
            ...

      这里需要发现self.compute_api究竟为哪个类的对象。从Controller类的__init__函数中发现:

    """compute指nova.compute"""
    self.compute_api = compute.API()

    nova/compute/__init__.py

    def API(*args, **kwargs):
        importutils = nova.openstack.common.importutils
        """此处class_name为nova.compute.api.API"""
        class_name = _get_compute_api_class_name()
        """创建nova.compute.api.API类对象并返回"""
        return importutils.import_object(class_name, *args, **kwargs)

      所以self.compute_api即为nova.compute.api.API类的对象。 

    1、接着看self.compute_api的create函数调用

    nova/compute/api.py

    API类:
    @hooks.add_hook("create_instance")
    def create(self, context, instance_type,
                ...):
        ...
        return self._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):
        ...
    
        self.compute_task_api.build_instances(context,
                instances=instances, image=boot_meta,
                filter_properties=filter_properties,
                admin_password=admin_password,
                injected_files=injected_files,
                requested_networks=requested_networks,
                security_groups=security_groups,
                block_device_mapping=block_device_mapping,
                legacy_bdm=False)
    
        return (instances, reservation_id)

      这里需要分析self.compute_task_api为哪个类的对象,代码分析如下:

    nova/compute/api.py

    API类:
    @property
    def compute_task_api(self):
        if self._compute_task_api is None:
            from nova import conductor
            self._compute_task_api = conductor.ComputeTaskAPI()
        return self._compute_task_api

    nova/conductor/__init__.py

    def ComputeTaskAPI(*args, **kwargs):
        use_local = kwargs.pop('use_local', False)
      if oslo.config.cfg.CONF.conductor.use_local or use_local:
        api = conductor_api.LocalComputeTaskAPI
      else:
        """conductor_api为nova.conductor.api"""
        api = conductor_api.ComputeTaskAPI
      return api(*args, **kwargs)

      所以self.compute_task_api为nova.conductor.api.ComputeTaskAPI类的对象。 

    2、接着看self.compute_task_api的build_instances函数

    nova/conductor/api.py

    ComputeTaskAPI类:
    def build_instances(self, context, instances, image, filter_properties,
                admin_password, injected_files, requested_networks,
                security_groups, block_device_mapping, legacy_bdm=True):
        self.conductor_compute_rpcapi.build_instances(context,
                instances=instances, image=image,
                filter_properties=filter_properties,
                admin_password=admin_password, injected_files=injected_files,
                requested_networks=requested_networks,
                security_groups=security_groups,
                block_device_mapping=block_device_mapping,
                legacy_bdm=legacy_bdm)

      其中,self.conductor_compute_rpcapi= rpcapi.ComputeTaskAPI() ! 

    3、接着看self.conductor_compute_rpcapi的build_instances函数

    nova/conductor/rpcapi.py

    ComputeTaskAPI类:
    def build_instances(self, context, instances, image, filter_properties,
                admin_password, injected_files, requested_networks,
                security_groups, block_device_mapping, legacy_bdm=True):
        instances_p = [jsonutils.to_primitive(inst) for inst in instances]
        image_p = jsonutils.to_primitive(image)
        cctxt = self.client.prepare(version='1.5')
        cctxt.cast(context, 'build_instances',
                   instances=instances_p, image=image_p,
                   filter_properties=filter_properties,
                   admin_password=admin_password,
                   injected_files=injected_files,
                   requested_networks=requested_networks,
                   security_groups=security_groups,
                   block_device_mapping=block_device_mapping,
                   legacy_bdm=legacy_bdm)

      其中self.client = self.get_client(namespace=self.RPC_API_NAMESPACE),即 cctxt = self.client.prepare(version='1.5')调用后cctxt为nova.rpcclient.RPCClient,根据http://www.cnblogs.com/littlebugfish/p/4058007.html中简述的消息队列调用原理,这里将转到nova-conductor进程中进行处理。

    二、在nova-conductor进程中进行处理

    nova/conductor/manager.py

    ComputeTaskManager类:
    def build_instances(self, context, instances, image, filter_properties,
                admin_password, injected_files, requested_networks,
                security_groups, block_device_mapping, legacy_bdm=True):
        ...
        self.scheduler_rpcapi.run_instance(context, request_spec=request_spec,
                admin_password=admin_password, injected_files=injected_files,
                requested_networks=requested_networks, is_first_time=True,
                filter_properties=filter_properties,
                legacy_bdm_in_spec=legacy_bdm)

      其中,self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI(),而scheduler_rpcapi为nova.scheduler.rpcapi,则self.scheduler_rpcapi为nova.scheduler.rpcapi.SchedulerAPI类的对象,接着看代码如下:

    nova/scheduler/rpcapi.py

    SchedulerAPI类:
    def run_instance(self, ctxt, request_spec, admin_password,
                injected_files, requested_networks, is_first_time,
                filter_properties, legacy_bdm_in_spec=True):
        version = '2.0'
        msg_kwargs = {'request_spec': request_spec,
                      'admin_password': admin_password,
                      'injected_files': injected_files,
                      'requested_networks': requested_networks,
                      'is_first_time': is_first_time,
                      'filter_properties': filter_properties}
        if self.client.can_send_version('2.9'):
            version = '2.9'
            msg_kwargs['legacy_bdm_in_spec'] = legacy_bdm_in_spec
        cctxt = self.client.prepare(version=version)
        return cctxt.cast(ctxt, 'run_instance', **msg_kwargs)

      根据http://www.cnblogs.com/littlebugfish/p/4058007.html中简述的消息队列调用原理,这里将转到nova-scheduler进程中进行处理。

    三、在nova-scheduler进程中进行处理

    nova/scheduler/manager.py

    SchedulerManager类:
    def run_instance(self, context, request_spec, admin_password,
                injected_files, requested_networks, is_first_time,
                filter_properties, legacy_bdm_in_spec=True):
            
        instance_uuids = request_spec['instance_uuids']
        with compute_utils.EventReporter(context, conductor_api.LocalAPI(),
                                         'schedule', *instance_uuids):
            try:
                return self.driver.schedule_run_instance(context,
                        request_spec, admin_password, injected_files,
                        requested_networks, is_first_time, filter_properties,
                        legacy_bdm_in_spec)
    
            ...

      这里需要看self.driver究竟为哪个类的对象。在SchedulerManager类的__init__函数中,有如下代码:

    if not scheduler_driver:
      scheduler_driver = CONF.scheduler_driver
    self.driver = importutils.import_object(scheduler_driver)

      根据配置,CONF.scheduler_driver为nova.scheduler.filter_scheduler.FilterScheduler,所以self.driver为nova.scheduler.filter_scheduler.FilterScheduler类的对象,所以接下来看:

    nova.scheduler.filter_scheduler.py

    FilterScheduler类:
    def schedule_run_instance(self, context, request_spec,
                                  admin_password, injected_files,
                                  requested_networks, is_first_time,
                                  filter_properties, legacy_bdm_in_spec):
        ...
        """选用来创建虚拟机的主机"""
        weighed_hosts = self._schedule(context, request_spec,
                                       filter_properties, instance_uuids)
        ...
    
        for num, instance_uuid in enumerate(instance_uuids):
            request_spec['instance_properties']['launch_index'] = num
    
            try:
                try:
                    weighed_host = weighed_hosts.pop(0)
                    LOG.info(_("Choosing host %(weighed_host)s "
                                "for instance %(instance_uuid)s"),
                              {'weighed_host': weighed_host,
                               'instance_uuid': instance_uuid})
                except IndexError:
                    raise exception.NoValidHost(reason="")
                """创建虚拟机"""
                self._provision_resource(context, weighed_host,
                                         request_spec,
                                         filter_properties,
                                         requested_networks,
                                         injected_files, admin_password,
                                         is_first_time,
                                         instance_uuid=instance_uuid,
                                         legacy_bdm_in_spec=legacy_bdm_in_spec)
            except Exception as ex:
                driver.handle_schedule_error(context, ex, instance_uuid,
                                             request_spec)
            retry = filter_properties.get('retry', {})
            retry['hosts'] = []
        self.notifier.info(context, 'scheduler.run_instance.end', payload) 

    1、 选择用来创建虚拟机的主机,代码如下:

    weighed_hosts = self._schedule(context, request_spec,
                                   filter_properties, instance_uuids)

      接着看_schedule函数,如下:

    def _schedule(self, context, request_spec, filter_properties,
                      instance_uuids=None):
        ...
        hosts = self.host_manager.get_all_host_states(elevated)
    
        selected_hosts = []
        if instance_uuids:
            num_instances = len(instance_uuids)
        else:
            num_instances = request_spec.get('num_instances', 1)
        for num in xrange(num_instances):
            """根据规则过滤掉特定的主机。例如可以设定忽略某些主机,这样就不会选择这些主机来创建虚拟机"""
            hosts = self.host_manager.get_filtered_hosts(hosts,
                    filter_properties, index=num)
            if not hosts:
                break
    
            LOG.debug(_("Filtered %(hosts)s"), {'hosts': hosts})
            """根据规则为候选主机分配权重以便选取最佳的主机创建虚拟机,此处暂时不详述权重分配的过程"""
            weighed_hosts = self.host_manager.get_weighed_hosts(hosts,
                    filter_properties)
    
            LOG.debug(_("Weighed %(hosts)s"), {'hosts': weighed_hosts})
    
            scheduler_host_subset_size = CONF.scheduler_host_subset_size
            if scheduler_host_subset_size > len(weighed_hosts):
                scheduler_host_subset_size = len(weighed_hosts)
            if scheduler_host_subset_size < 1:
                scheduler_host_subset_size = 1
    
            chosen_host = random.choice(
                weighed_hosts[0:scheduler_host_subset_size])
            selected_hosts.append(chosen_host)
            chosen_host.obj.consume_from_instance(instance_properties)
            if update_group_hosts is True:
                filter_properties['group_hosts'].append(chosen_host.obj.host)
        return selected_hosts 

    2、开始创建虚拟机,代码如下:

    self._provision_resource(context, weighed_host,
                                     request_spec,
                                     filter_properties,
                                     requested_networks,
                                     injected_files, admin_password,
                                     is_first_time,
                                     instance_uuid=instance_uuid,
                                     legacy_bdm_in_spec=legacy_bdm_in_spec)

      接着看_provision_resource函数,如下:

    def _provision_resource(self, context, weighed_host, request_spec,
                filter_properties, requested_networks, injected_files,
                admin_password, is_first_time, instance_uuid=None,
                legacy_bdm_in_spec=True):
        ...
        try:
            """更新数据库"""
            updated_instance = driver.instance_update_db(context,
                    instance_uuid, extra_values=values)
    
        except exception.InstanceNotFound:
            LOG.warning(_("Instance disappeared during scheduling"),
                        context=context, instance_uuid=instance_uuid)
    
        else:
            scheduler_utils.populate_filter_properties(filter_properties,
                    weighed_host.obj)
    
            """这里self.compute_rpcapi为 nova.compute.rpcapi.ComputeAPI对象"""
            self.compute_rpcapi.run_instance(context,
                    instance=updated_instance,
                    host=weighed_host.obj.host,
                    request_spec=request_spec,
                    filter_properties=filter_properties,
                    requested_networks=requested_networks,
                    injected_files=injected_files,
                    admin_password=admin_password, is_first_time=is_first_time,
                    node=weighed_host.obj.nodename,
                    legacy_bdm_in_spec=legacy_bdm_in_spec)

    nova/compute/rpcapi.py

    ComputeAPI类:
    def run_instance(self, ctxt, instance, host, request_spec,
                         filter_properties, requested_networks,
                         injected_files, admin_password,
                         is_first_time, node=None, legacy_bdm_in_spec=True):
        instance_p = jsonutils.to_primitive(instance)
        msg_kwargs = {'instance': instance_p, 'request_spec': request_spec,
                      'filter_properties': filter_properties,
                      'requested_networks': requested_networks,
                      'injected_files': injected_files,
                      'admin_password': admin_password,
                      'is_first_time': is_first_time, 'node': node}
    
        if _icehouse_compat() or self.client.can_send_version('2.37'):
            version = _get_version('2.37')
            msg_kwargs['legacy_bdm_in_spec'] = legacy_bdm_in_spec
        else:
            version = '2.19'
        cctxt = self.client.prepare(server=host,
                version=version)
        cctxt.cast(ctxt, 'run_instance', **msg_kwargs)

      根据http://www.cnblogs.com/littlebugfish/p/4058007.html中简述的消息队列调用原理,这里将转到nova-compute进程中进行处理。

    四、在nova-compute进程中进行处理

    nova/compute/manager.py

    ComputeManager类:
    @wrap_exception()
    @reverts_task_state
    @wrap_instance_event
    @wrap_instance_fault
    def run_instance(self, context, instance, request_spec=None,
                     filter_properties=None, requested_networks=None,
                     injected_files=None, admin_password=None,
                     is_first_time=False, node=None, legacy_bdm_in_spec=True):
    
        if filter_properties is None:
            filter_properties = {}
    
        @utils.synchronized(instance['uuid'])
        def do_run_instance():
            self._run_instance(context, request_spec,
                    filter_properties, requested_networks, injected_files,
                    admin_password, is_first_time, node, instance,
                    legacy_bdm_in_spec)
        do_run_instance()
    def _run_instance(self, context, request_spec,
                          filter_properties, requested_networks, injected_files,
                          admin_password, is_first_time, node, instance,
                          legacy_bdm_in_spec):
        ...
    
            instance, network_info = self._build_instance(context,
                    request_spec, filter_properties, requested_networks,
                    injected_files, admin_password, is_first_time, node,
                    instance, image_meta, legacy_bdm_in_spec)
            notify("end", msg=_("Success"), network_info=network_info)
        ...
    def _build_instance(self, context, request_spec, filter_properties,
                requested_networks, injected_files, admin_password, is_first_time,
                node, instance, image_meta, legacy_bdm_in_spec):
        context = context.elevated()
    
        ...
        try:
            limits = filter_properties.get('limits', {})
            with rt.instance_claim(context, instance, limits):
                ...
    
                instance = self._spawn(context, instance, image_meta,
                                       network_info, block_device_info,
                                       injected_files, admin_password,
                                       set_access_ip=set_access_ip)
        ...
    
        # spawn success
        return instance, network_info
    def _spawn(self, context, instance, image_meta, network_info,
                   block_device_info, injected_files, admin_password,
                   set_access_ip=False):
        """Spawn an instance with error logging and update its power state."""
        instance = self._instance_update(context, instance['uuid'],
                vm_state=vm_states.BUILDING,
                task_state=task_states.SPAWNING,
                expected_task_state=task_states.BLOCK_DEVICE_MAPPING)
        try:
            self.driver.spawn(context, instance, image_meta,
                              injected_files, admin_password,
                              network_info,
                              block_device_info)
        ...
        return self._instance_update(context, instance['uuid'],
                                     **update_data)
      这里将调用相应的Driver对象进行虚拟机的创建,此处以LibvirtDriver类对象为例,后续处理过程可以查看:http://www.cnblogs.com/littlebugfish/p/4058115.html中的分析!



  • 相关阅读:
    CSS之旅——第二站 如何更深入的理解各种选择器
    CSS之旅——第一站 为什么要用CSS
    记录一些在用wcf的过程中走过的泥巴路 【第一篇】
    asp.net mvc 之旅—— 第二站 窥探Controller下的各种Result
    asp.net mvc 之旅—— 第一站 从简单的razor入手
    Sql Server之旅——终点站 nolock引发的三级事件的一些思考
    Sql Server之旅——第十四站 深入的探讨锁机制
    Sql Server之旅——第十三站 对锁的初步认识
    Sql Server之旅——第十二站 sqltext的参数化处理
    Sql Server之旅——第十一站 简单说说sqlserver的执行计划
  • 原文地址:https://www.cnblogs.com/littlebugfish/p/4090311.html
Copyright © 2011-2022 走看看