Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1489584
  • 博文数量: 230
  • 博客积分: 474
  • 博客等级: 下士
  • 技术积分: 1955
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-19 18:40
文章分类

全部博文(230)

文章存档

2020年(3)

2019年(3)

2018年(12)

2017年(13)

2016年(11)

2015年(55)

2014年(74)

2013年(39)

2012年(2)

2011年(18)

我的朋友

分类: 云计算

2015-09-11 11:28:21

我们大家都知道部署物理机跟部署虚拟机的概念在nova来看是一样,都是nova通过创建虚拟机的方式来触发,只是底层nova-scheduler和nova-compute的驱动不一样。虚拟机的底层驱动采用的libvirt的虚拟化技术,而物理机是采用PXE+IPMItool的方式,当让PXE+ipmitool只是其中一种比较常用的技术,其他的方式也可以实现,像开发环境部比较少的话,也可以使用PXE+SSH的方式,我就是采用这种方式实践的。

采用官方的一个图,可以大致了解他的框架和流程是怎么走的,详情请见官方文档:http://docs.openstack.org/developer/ironic/deploy/user-guide.html



好了,废话少说,那我们就一起来看看相关的代码分析:

 

配置

在/etc/nova/nova.conf的配置文件中修改manager和driver

Vi /etc/nova/nova.conf

[DEFAULT]scheduler_host_manager = nova.scheduler.ironic_host_manager.IronicHostManagercompute_driver = nova.virt.ironic.driver.IronicDrivercompute_manager = ironic.nova.compute.manager.ClusteredComputeManager[ironic]admin_username = ironicadmin_password = unsetadmin_url =  = servicenova-scheduler的mamager是:scheduler_host_manager = nova.scheduler.ironic_host_manager.IronicHostManager

nova-compute的manager和driver是:

compute_driver =nova.virt.ironic.driver.IronicDriver

compute_manager = ironic.nova.compute.manager.ClusteredComputeManager

 

其中compute_manager的代码实现是在ironic项目里面。 

创建虚拟机的流程就不描述了,nova-api接收到nova boot的请求,nova-scheduler收到请求后,在scheduler_host_manager里面处理:

Nova-scheduler会使用flavor里面的额外属性extra_specs,像cpu_arch,baremetal:deploy_kernel_id,baremetal:deploy_ramdisk_id等过滤条件找到相匹配的物理节点。


Nova-compute

Nova-compute在启动的时候会初始化nova.service.py的Service类,调用launch方法将nova-compute的进程拉起来,最终调用到Service类的start方法:

  1. def start(self):        verstr = version.version_string_with_package()        LOG.audit(_('Starting %(topic)s node (version %(version)s)'),                  {'topic'self.topic, 'version': verstr})        self.basic_config_check()        self.manager.init_host()  # compute-manager的初始化        self.model_disconnected = False        ctxt = context.get_admin_context()     …….     #compute_manager的pre_start_hook方法        self.manager.pre_start_hook()        …..  


那么这个self.manager是哪里定义的呢,

nova.compute的main函数:

   server = service.Service.create(binary='nova-compute',

                                   topic=CONF.compute_topic,

                                   db_allowed=CONF.conductor.use_local)

nova.service的create方法:


  1. def create(cls, host=None, binary=None, topic=None, manager=None,               report_interval=None, periodic_enable=None,               periodic_fuzzy_delay=None, periodic_interval_max=None,               db_allowed=True):        #这里的binary就是nova-compute,manager_cls就是compute-manger,manager就是获取的compute-manager的配置项。        if not manager:            manager_cls = ('%s_manager' %                           binary.rpartition('nova-')[2])             manager = CONF.get(manager_cls, None)            ……        service_obj = cls(host, binary, topic, manager,                          report_interval=report_interval,                          periodic_enable=periodic_enable,                          periodic_fuzzy_delay=periodic_fuzzy_delay,                          periodic_interval_max=periodic_interval_max,                          db_allowed=db_allowed)  
而self.manager的初始化是在Services的初始化方法:



  1. def __init__(self, host, binary, topic, manager, report_interval=None,                 periodic_enable=None, periodic_fuzzy_delay=None,                 periodic_interval_max=None, db_allowed=True,                 *args, **kwargs):        super(Service, self).__init__()        self.host = host        self.binary = binary        self.topic = topic        self.manager_class_name = manager        self.servicegroup_api = servicegroup.API(db_allowed=db_allowed)        manager_class = importutils.import_class(self.manager_class_name)        self.manager = manager_class(host=self.host, *args, **kwargs)  


self.manager.init_host()调用的是compute-manager的init-host方法,我们配置文件里面写的是ironic.nova.compute.manager.ClusteredComputeManager, 所以调用到了compute-manager的init-host方法。


  1. class ClusteredComputeManager(manager.ComputeManager):    def init_host(self):        """Initialization for a clustered compute service."""        #调用compute_driver的init_host方法,当前没有做什么事情        self.driver.init_host(host=self.host)                 #调用父类的init_virt_events方法        self.init_virt_events()  


compute-manager的init-host方法的第一步调用self.driver.init_host(host=self.host),这里的self.dirver是什么呢? 其实就是我们在配置文件里面写的compute-driver。

 

我们来看下self.dirver是怎么定义的:

ClusteredComputeManager的父类是nova.compute.manager.ComputeManager的初始化方法__init__方法:
  1. def __init__(self, compute_driver=None, *args, **kwargs):        """Load configuration options and connect to the hypervisor."""        self.virtapi = ComputeVirtAPI(self)        self.network_api = network.API()        self.volume_api = volume.API()        self.image_api = image.API()        self._last_host_check = 0      …….        self.driver = driver.load_compute_driver(self.virtapi, compute_driver)  
而load_compute_driver做的事情就是加载相关的driver


  1. def load_compute_driver(virtapi, compute_driver=None):    if not compute_driver:        compute_driver = CONF.compute_driver    if not compute_driver:        LOG.error(_("Compute driver option required, but not specified"))        sys.exit(1)    LOG.info(_("Loading compute driver '%s'") % compute_driver)    try:        driver = importutils.import_object_ns('nova.virt',                                              compute_driver,                                              virtapi)        return utils.check_isinstance(driver, ComputeDriver)    except ImportError:        LOG.exception(_("Unable to load the virtualization driver"))        sys.exit(1)  


细心的同学应该注意到,为什么self.manager = manager_class(host=self.host, *args, **kwargs)调用的是host=self.host,到了ClusteredComputeManager的初始化方法却是compute_driver=None, 其实这个就是可选参数, 在load_compute_driver方法里面,假如compute_driver为None,则设置compute_driver= CONF.compute_driver,所以self.driver就是compute_driver的配置项设置的值。


OK,回到刚才self.manager的init_host方法,self.driver.init_host(host=self.host)调用的是nova.virt.ironic.driver.IronicDriver的init_host方法,当前Juno版本的init_host方法啥事情都没有做。父类的init_virt_events方法调用slef.driver的register_event_listener方法,但是self.driver没实现这个方法,所以需要到dirvier的父virt_driver.ComputeDriver,将self.handle_events作为self._compute_event_callback的回调函数,这个回调函数是接收从libvirtd的异步事件消息。


  1. def init_virt_events(self):        self.driver.register_event_listener(self.handle_events)  


回到Service类的start方法,往下的话就是判断nova-compute的service是否存在,这些就省略不写,跟nova-compute的流程是一样的。

接着看self.manager.pre_start_hook(), 这个Ironic的manager类ClusteredComputeManager是有实现的:

  1. def pre_start_hook(self):        try:            self.update_available_resource(nova.context.get_admin_context())        except Exception:            pass  

self.update_available_resource的是父类nova.compute.manage.ComputeManager的方法:

update_available_resource这个是个周期任务,这方法主要就是把ironic所有的node节点的信息都同步到compute_node中去,nova-scheduler在走过滤器的时候才能根据过滤条件找到适合的物理主机。


  1. @periodic_task.periodic_task    def update_available_resource(self, context):        """See driver.get_available_resource()        Periodic process that keeps that the compute host's understanding of        resource availability and usage in sync with the underlying hypervisor.        :param context: security context        """        new_resource_tracker_dict = {}        nodenames = set(self.driver.get_available_nodes())        for nodename in nodenames:            rt = self._get_resource_tracker(nodename)            rt.update_available_resource(context)            new_resource_tracker_dict[nodename] = rt        # Delete orphan compute node not reported by driver but still in db        compute_nodes_in_db = self._get_compute_nodes_in_db(context,                                                            use_slave=True)        for cn in compute_nodes_in_db:            if cn.hypervisor_hostname not in nodenames:                LOG.audit(_("Deleting orphan compute node %s") % cn.id)                cn.destroy()        self._resource_tracker_dict = new_resource_tracker_dict  


self.driver的get_available_nodes获取ironic的所有注册的node节点,方法_refresh_cache()内部维护了一个self.node_cache= {}的一个dict,从ironic的api接口ironic node-list查询出所有的node节点,放在缓存中, 如果该节点存在的话,则调用resource_tracker.ResourceTracker生成一个新的rt对象,缓存到self._resource_tracker_dict中,返回这个rt对象。然后执行rt.update_available_resource(), 在nova.compute.resource_tracker.ResourceTracker类中。

rt.update_available_resource()是先从node-cache中找到该节点信息,如果没有,则从ironic-api中调用ironic接口查询,比较关键的是最后一个动作self._node_resource(node), 这个动作会做了一些逻辑判断,CPU类型转化等。



  1. def get_available_resource(self, nodename):        …….        return self._node_resource(node)    def _node_resource(self, node):        """Helper method to create resource dict from node stats."""        vcpus = int(node.properties.get('cpus'0))        memory_mb = int(node.properties.get('memory_mb'0))        local_gb = int(node.properties.get('local_gb'0))        raw_cpu_arch = node.properties.get('cpu_arch'None)        try:            #将amd64转化为X86-64,其他的类型会转化I686,或者不存在            cpu_arch = arch.canonicalize(raw_cpu_arch)        except exception.InvalidArchitectureName:            cpu_arch = None        if not cpu_arch:            LOG.warn(_LW("cpu_arch not defined for node '%s'"), node.uuid)        nodes_extra_specs = {}        nodes_extra_specs['cpu_arch'] = raw_cpu_arch        …..        if node.instance_uuid:            # Node has an instance, report all resource as unavailable            vcpus_used = vcpus            memory_mb_used = memory_mb            local_gb_used = local_gb        elif self._node_resources_unavailable(node):        #假如单板的power-state在[ironic_states.ERROR, ironic_states.NOSTATE] 或者属于维护状态,则不上报该单板的资源。            vcpus = 0            memory_mb = 0            local_gb = 0     ……        dic.update(nodes_extra_specs)        return dic  


这里跟虚拟机的driver不同主要是在hypervisor_type 是ironic ,cpu_info是” baremetal cpu”,其他地方都是大同小异,没有什么不同。


Nova-compute走完self.manager.pre_start_hook()这个步骤,后面的步骤跟虚拟机的启动完全都是一样的了,创建rabbitmq的消息队列等等,这里就不在描述,详见nova-compute的流程,tripleo就不在这里描述了。

阅读(1862) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~