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中的分析!



  • 相关阅读:
    python之常用内置函数
    python基础之文件操作
    简洁版三级菜单
    JS 事件代理
    捕获当前事件作用的对象event.target和event.srcElement
    【javascript 技巧】谈谈setTimeout的作用域以及this的指向问题
    JSON详解
    多线程小例子
    jquery中attr和prop的区别
    django 过滤器
  • 原文地址:https://www.cnblogs.com/littlebugfish/p/4090311.html
Copyright © 2011-2022 走看看