NOVA架构 nova-compute启动分析

时间:2022-12-25 08:06:52

http://blog.csdn.net/sj13426074890/article/details/7915327


下面就是nova系统架构图:

                                    /- ( LDAP )
[ Auth Manager ] ---
| \- ( DB )
|
| [ scheduler ] - [ volume ] - ( ATAoE/iSCSI )
| /
[ Web Dashboard ] -> [ api ] -- < AMQP > ------ [ network ] - ( Flat/Vlan )
| \
< HTTP > [ scheduler ] - [ compute ] - ( libvirt/xen )
| |
[ objectstore ] < - retrieves images
  • DB:数据库,被所有的组件共享
  • Web Dashboard: web页面,调用所有的api接口
  • api: api组件接收http请求,转换命令通过AMQP和其他组件进行通信,或者直接通过http请求访问objectstore组件
  • Auth Manager: 组件负责用户,工程和角色,可以以数据库或者LDAP做后端
  • objectstore: 存储和检索镜像
  • scheduler: 根据所指定的scheduler类,选择最合适的host
  • volume: 卷的管理
  • network: 管理 ip forwarding, bridges, and vlans
  • compute: 管理与hypervisor和虚拟机的连接


[python] view plaincopy
  1. python启动脚本 bin/nova-compute  

[html] view plaincopy
  1. utils.default_flagfile()  #设置flag文件路径.  
  2. flags.FLAGS(sys.argv)   #把flag文件中的参数放到args中。  
  3. logging.setup()           #设置日志  
  4. utils.monkey_patch()  
  5. server = service.Service.create(binary='nova-compute') #创建服务  
  6. service.serve(server)  #启动服务  
  7. service.wait()             #等待请求  

下面我讲按6大步进行讲述:

第一步:utils.default_flagfile()

[html] view plaincopy
  1. if args is None:  
  2.      args = sys.argv  
  3.  for arg in args:  
  4.      if arg.find('flagfile') != -1:  
  5.          break  
  6.  else:  
  7.      if not os.path.isabs(filename):  
  8.          # turn relative filename into an absolute path  
  9.          script_dir = os.path.dirname(inspect.stack()[-1][1])  
  10.          filename = os.path.abspath(os.path.join(script_dir, filename))  
  11.      if not os.path.exists(filename):  
  12.          filename = "./nova.conf"  
  13.          if not os.path.exists(filename):  
  14.              filename = '/etc/nova/nova.conf'  
  15.      flagfile = '--flagfile=%s' % filename  
  16.      args.insert(1, flagfile)  
  17.   其实所完成的功能也就是把--flagfile=/etc/nova/nova.conf加到入参数列表中。  



第二步:flags.FLAGS(sys.argv)

  flags.py中有一句
  FLAGS = FlagValues(),那么直接查看FlagValues()这个类,这个类是继承于gflags.FlagValues.



第三步:logging.setup()

这个是对这个服务开启日志功能。




第四步:server = service.Service.create(binary='nova-compute')

这个函数位于/nova/service.py: 类Service中

[html] view plaincopy
  1. class Service(object):  
  2.     """Service object for binaries running on hosts.  
  3.     A service takes a manager and enables rpc by listening to queues based  
  4.     on topic. It also periodically runs tasks on the manager and reports  
  5.     it state to the database services table."""  
  6.  函数定义如下:  
  7.    @classmethod   
  8.     def create(cls, host=Nonebinary=Nonetopic=Nonemanager=None,  
  9.                report_interval=Noneperiodic_interval=None):  
  10.         """Instantiates class and passes back application object.  
  11.   
  12.         :param host: defaults to FLAGS.host  
  13.         :param binary: defaults to basename of executable  
  14.         :param topic: defaults to bin_name - 'nova-' part  
  15.         :param manager: defaults to FLAGS.<topic>_manager  
  16.         :param report_interval: defaults to FLAGS.report_interval  
  17.         :param periodic_interval: defaults to FLAGS.periodic_interval  
  18.   
  19.         """  
  20.         if not host:  
  21.             host = FLAGS.host #host name  ‘nova’  
  22.         if not binary:  
  23.             binary = os.path.basename(inspect.stack()[-1][1]) # 这是因为python可以查看动栈的内容。  
  24.                                                               # 所以可以得到压入栈中的脚本的名字  这时,binary="nova-compute"  
  25.         if not topic:  
  26.             topic = binary.rpartition('nova-')[2]             #设置topic的名字:也就是把 binary的nova-去掉。  
  27.         if not manager:  
  28.             manager = FLAGS.get('%s_manager' % topic, None)   #很明显这里得到的是compute_manager.  
  29.         if not report_interval:  
  30.             report_interval = FLAGS.report_interval  
  31.         if not periodic_interval:  
  32.             periodic_interval = FLAGS.periodic_interval  
  33.         service_obj = cls(host, binary, topic, manager,  
  34.                           report_interval, periodic_interval)   #利用 Service.__init__()构造函数生成一个对象  
  35.   
  36.         return service_obj  


接下来我们看看Service.__init__()

[html] view plaincopy
  1. def __init__(self, host, binary, topic, manager, report_interval=None,  
  2.                  periodic_interval=None, *args, **kwargs):  
  3.         self.host = host  
  4.         self.binary = binary  
  5.         self.topic = topic  
  6.         self.manager_class_name = manager  
  7.         manager_class = utils.import_class(self.manager_class_name)  
  8.         self.manager = manager_class(host=self.host, *args, **kwargs)  
  9.         self.report_interval = report_interval  
  10.         self.periodic_interval = periodic_interval  
  11.         super(Service, self).__init__(*args, **kwargs)  
  12.         self.saved_args, self.saved_kwargs = args, kwargs  
  13.         self.timers = []  

1、指定host、binary、topic。这个相对简单。

        self.host = host
        self.binary = binary
        self.topic = topic


2、动态指定manager类,并动态生成实例 。

        self.manager_class_name = manager    #在create函数中指定。
        manager_class = utils.import_class(self.manager_class_name)  #动态地import此类。
        self.manager = manager_class(host=self.host, *args, **kwargs) #动态地生成这个类的实例 。

       那么这时会调用ComputeManager::__init__()函数


      我们进入ComputeManager::__init__()函数

     

[python] view plaincopy
  1. def __init__(self, compute_driver=None, *args, **kwargs):  
  2.        """Load configuration options and connect to the hypervisor."""  
  3.        # TODO(vish): sync driver creation logic with the rest of the system  
  4.        #             and re-document the module docstring  
  5.        if not compute_driver:  
  6.            compute_driver = FLAGS.compute_driver   #这部分在后面会仔细分析  
  7.                                    #cfg.StrOpt('compute_driver',default='nova.virt.connection.get_connection',  
  8.                                    #help='Driver to use for controlling virtualization')  
  9.        try:  
  10.            self.driver = utils.check_isinstance(  
  11.                                        utils.import_object(compute_driver),  
  12.                                        driver.ComputeDriver)       #动态的加载 虚拟机的控制驱动对象  
  13.        except ImportError as e:  
  14.            LOG.error(_("Unable to load the virtualization driver: %s") % (e))  
  15.            sys.exit(1)  
  16.   
  17.        self.network_api = network.API()  
  18.        self.volume_api = volume.API()  
  19.        self.network_manager = utils.import_object(FLAGS.network_manager)  
  20.        self._last_host_check = 0  
  21.        self._last_bw_usage_poll = 0  
  22.        self._last_info_cache_heal = 0  
  23.   
  24.        super(ComputeManager, self).__init__(service_name="compute",  
  25.                                             *args, **kwargs)  

 这里顺便看一下ComputeManager的类视图。

NOVA架构 nova-compute启动分析

  3、设置参数:应该是服务间隔时间之类的。

       self.report_interval = report_interval
        self.periodic_interval = periodic_interval


  4、 设置多出来的一些参数。      

        super(Service, self).__init__(*args, **kwargs)
        self.saved_args, self.saved_kwargs = args, kwargs
        self.timers = []



 第五步:service.serve(server)开启服务

[python] view plaincopy
  1. def serve(*servers):  
  2.     global _launcher  # class Launcher 一个全局对象  
  3.     if not _launcher:  
  4.         _launcher = Launcher()  
  5.     for server in servers:  
  6.         _launcher.launch_server(server)  

  类Launcher 主要作用是为每个服务开启一个线程

 

[python] view plaincopy
  1. def launch_server(self, server):  
  2.       """Load and start the given server. 
  3.  
  4.       :param server: The server you would like to start. 
  5.       :returns: None 
  6.  
  7.       """  
  8.       gt = eventlet.spawn(self.run_server, server)   #启动线程运行 run_server  
  9.       self._services.append(gt)   #保存到 _services里面  
  10.  
  11.    @staticmethod  
  12.   def run_server(server):  
  13.       """Start and wait for a server to finish. 
  14.  
  15.       :param service: Server to run and wait for. 
  16.       :returns: None 
  17.  
  18.       """  
  19.       server.start()  
  20.       server.wait()  

其中server.start() #启动服务 我们来查看下 service.py中 Service类中start方法   [python] view plaincopy
  1. <strong> </strong><span style="font-size:14px;">def start(self):  
  2.         vcs_string = version.version_string_with_vcs()  #获取版本号  
  3.         LOG.audit(_('Starting %(topic)s node (version %(vcs_string)s)'),  
  4.                   {'topic'self.topic, 'vcs_string': vcs_string})  
  5.         utils.cleanup_file_locks()  #清除锁文件  
  6.         self.manager.init_host()    
  7.         self.model_disconnected = False  
  8.         ctxt = context.get_admin_context()  
  9.         try:  
  10.             service_ref = db.service_get_by_args(ctxt,  
  11.                                                  self.host,  
  12.                                                  self.binary)  
  13.             self.service_id = service_ref['id']  
  14.         except exception.NotFound:  
  15.             self._create_service_ref(ctxt)  
  16.   
  17.         if 'nova-compute' == self.binary:  
  18.             self.manager.update_available_resource(ctxt)  
  19.   
  20.         self.conn = rpc.create_connection(new=True)  
  21.         LOG.debug(_("Creating Consumer connection for Service %s") %  
  22.                   self.topic)  
  23.   
  24.         # Share this same connection for these Consumers  
  25.         self.conn.create_consumer(self.topic, self, fanout=False)  
  26.   
  27.         node_topic = '%s.%s' % (self.topic, self.host)  
  28.         self.conn.create_consumer(node_topic, self, fanout=False)  
  29.   
  30.         self.conn.create_consumer(self.topic, self, fanout=True)  
  31.   
  32.         # Consume from all consumers in a thread  
  33.         self.conn.consume_in_thread()  
  34.   
  35.         if self.report_interval:  
  36.             pulse = utils.LoopingCall(self.report_state)  
  37.             pulse.start(interval=self.report_interval, now=False)  
  38.             self.timers.append(pulse)  
  39.   
  40.         if self.periodic_interval:  
  41.             periodic = utils.LoopingCall(self.periodic_tasks)  
  42.             periodic.start(interval=self.periodic_interval, now=False)  
  43.             self.timers.append(periodic)</span>  

下面我们对start方法进行分析:

 1、设置版本

        vcs_string = version.version_string_with_vcs()
        logging.audit(_('Starting %(topic)s node (version %(vcs_string)s)'),
                      {'topic': self.topic, 'vcs_string': vcs_string})


 2、初始化init_host(self): nova.compute.ComputeManager


    其中 self.driver.init_host(host=self.host)

    这里需要查看一下在__init__函数中driver的设置。

        if not compute_driver:
                   compute_driver = FLAGS.compute_driver
     FLAGS.compute_driver的值也是在/nova/compute/manager.py中设置:

    

    flags.DEFINE_string('compute_driver', 'nova.virt.connection.get_connection',
                    'Driver to use for controlling virtualization')


    关于get_connection下篇blog继续分析



3 . 接着从service.start()接着init_host()之后,

    得到context.然后再更新当前机器上可用的资源。 

[python] view plaincopy
  1. self.model_disconnected = False  
  2. ctxt = context.get_admin_context()  
  3. try:  
  4.     service_ref = db.service_get_by_args(ctxt,  
  5.                                          self.host,  
  6.                                          self.binary)  
  7.     self.service_id = service_ref['id']  
  8. except exception.NotFound:  
  9.     self._create_service_ref(ctxt)  
  10.   
  11. if 'nova-compute' == self.binary:  
  12.     self.manager.update_available_resource(ctxt)  


4. 更新RPC链接

[python] view plaincopy
  1. <span style="font-size:18px;"self.conn = rpc.create_connection(new=True)  
  2.         LOG.debug(_("Creating Consumer connection for Service %s") %  
  3.                   self.topic)  
  4.   
  5.            # Share this same connection for these Consumers  
  6.         self.conn.create_consumer(self.topic, self, fanout=False)  
  7.   
  8.         node_topic = '%s.%s' % (self.topic, self.host)  
  9.         self.conn.create_consumer(node_topic, self, fanout=False)  
  10.   
  11.         self.conn.create_consumer(self.topic, self, fanout=True)  
  12.   
  13.         # Consume from all consumers in a thread  
  14.         self.conn.consume_in_thread()  
  15.   
  16.        
  17.              if self.report_interval:  
  18.             pulse = utils.LoopingCall(self.report_state)        #循环调用report_state  
  19.             pulse.start(interval=self.report_interval, now=False)  
  20.             self.timers.append(pulse)  
  21.   
  22.         if self.periodic_interval:  
  23.             periodic = utils.LoopingCall(self.periodic_tasks) #循环调用 periodic_tasks 下面详细说明  
  24.             periodic.start(interval=self.periodic_interval, now=False)  
  25.             self.timers.append(periodic)</span>  

 Nova-Compute启动的主要工作是把环境配置好。让RqbbitMQ的消息队列建立起来。最后服务的启动则主要是让rabbitmq的consumer运行。并且进入wait状态。消息的响应则主要是找到相应的函数地址,并执行之。



六. Wait() 等待

一般服务启动之后,都会有wait()。那么这里也需要看一下服务的wait()。从而可以知道是什么东西在后台真正地运行。

[python] view plaincopy
  1. /usr/bin/nova-compute  
  2. service.wait()  
  3.   
  4. /nova/service.py:Class:Service::wait()  

上篇blog对bin/nova-compute按步进行了分析,这篇主要对其中ComputeManager进行分析

[python] view plaincopy
  1. <span style="font-size:18px;">class ComputeManager(manager.SchedulerDependentManager):  
  2.     """Manages the running instances from creation to destruction."""  
  3.   
  4.     def __init__(self, compute_driver=None, *args, **kwargs):  
  5.         """Load configuration options and connect to the hypervisor."""  
  6.         # TODO(vish): sync driver creation logic with the rest of the system  
  7.         #             and re-document the module docstring  
  8.         if not compute_driver:  
  9.             compute_driver = FLAGS.compute_driver  
  10.         try:  
  11.             self.driver = utils.check_isinstance(  
  12.                                         utils.import_object(compute_driver),  
  13.                                         driver.ComputeDriver)  
  14.         except ImportError as e:  
  15.             LOG.error(_("Unable to load the virtualization driver: %s") % (e))  
  16.             sys.exit(1)  
  17.   
  18.         self.network_api = network.API()  
  19.         self.volume_api = volume.API()  
  20.         self.network_manager = utils.import_object(FLAGS.network_manager)  
  21.         self._last_host_check = 0  
  22.         self._last_bw_usage_poll = 0  
  23.         self._last_info_cache_heal = 0  
  24.   
  25.         super(ComputeManager, self).__init__(service_name="compute",  
  26.                                              *args, **kwargs)</span>  


首先设置 self.driver

     FLAGS.compute_driver的值也是在/nova/compute/manager.py中设置:

     flags.DEFINE_string('compute_driver', 'nova.virt.connection.get_connection',
                    'Driver to use for controlling virtualization')

  

     从而可知comute_dirver = nova.virt.connection.get_connection

     那么需要查看一下virt/connection.get_connection中的init_host

     那么应该是调用了/nova/virt/connection.py:get_connection函数。

    

    get_connection()获得与hypervisor的连接

   首先来说,get_connection的入口点位于/nova/virt/connection.py:get_connection()中。

     [python] view plaincopy
  1. <span style="font-size:14px;">t = FLAGS.connection_type  
  2.     if t == 'fake':  
  3.         conn = fake.get_connection(read_only)  
  4.     elif t == 'libvirt':  
  5.         conn = libvirt_conn.get_connection(read_only)  
  6.     elif t == 'xenapi':  
  7.         conn = xenapi_conn.get_connection(read_only)  
  8.     elif t == 'hyperv':  
  9.         conn = hyperv.get_connection(read_only)  
  10.     elif t == 'vmwareapi':  
  11.         conn = vmwareapi_conn.get_connection(read_only)  
  12.     else:  
  13.         raise Exception('Unknown connection type "%s"' % t)  
  14.   
  15.     if conn is None:  
  16.         LOG.error(_('Failed to open connection to the hypervisor'))  
  17.         sys.exit(1)  
  18.     return utils.check_isinstance(conn, driver.ComputeDriver)</span>  

 

 由于一般我们使用的是libvirt.所以这里特别关注一下libvirt.  

  /nova/virt/connection.py的开头,我们会发现这么一句:

 from nova.virt.libvirt import connection as libvirt_conn

 说明libvirt_conn指向/nova/virt/libvirt/connection.py

那么就直接查看./libvirt/connection.py中的:get_connection函数。

/nova/virt/libvirt/connection.py:get_connection()

[python] view plaincopy
  1. def get_connection(read_only):  
  2.     # These are loaded late so that there's no need to install these  
  3.     # libraries when not using libvirt.  
  4.     # Cheetah is separate because the unit tests want to load Cheetah,  
  5.     # but not libvirt.  
  6.     global libvirt  
  7.     if libvirt is None:  
  8.         libvirt = __import__('libvirt')  
  9.     _late_load_cheetah()  
  10.     return LibvirtConnection(read_only)  
首先看一下类视图:

NOVA架构 nova-compute启动分析


现在接下来看一下LibvirtConnection类。位于/nova/virt/libvirt/connection.py文件中


[python] view plaincopy
  1. def __init__(self, read_only):  
  2.        super(LibvirtConnection, self).__init__()  
  3.   
  4.        self._host_state = None  
  5.        self._initiator = None  
  6.        self._wrapped_conn = None  
  7.        self.container = None  
  8.        self.read_only = read_only  
  9.        if FLAGS.firewall_driver not in firewall.drivers:  
  10.            FLAGS.set_default('firewall_driver', firewall.drivers[0])  
  11.        fw_class = utils.import_class(FLAGS.firewall_driver)  
  12.        self.firewall_driver = fw_class(get_connection=self._get_connection)  
  13.        self.vif_driver = utils.import_object(FLAGS.libvirt_vif_driver)  
  14.        self.volume_drivers = {}  
  15.        for driver_str in FLAGS.libvirt_volume_drivers:  
  16.            driver_type, _sep, driver = driver_str.partition('=')  
  17.            driver_class = utils.import_class(driver)  
  18.            self.volume_drivers[driver_type] = driver_class(self)  
  19.        self._host_state = None  
  20.   
  21.        disk_prefix_map = {"lxc": "", "uml": "ubd", "xen": "sd"}  
  22.        if FLAGS.libvirt_disk_prefix:  
  23.            self._disk_prefix = FLAGS.libvirt_disk_prefix  
  24.        else:  
  25.            self._disk_prefix = disk_prefix_map.get(FLAGS.libvirt_type, 'vd')  
  26.        self.default_root_device = self._disk_prefix + 'a'  
  27.        self.default_second_device = self._disk_prefix + 'b'  
  28.        self.default_third_device = self._disk_prefix + 'c'  
  29.   
  30.        self._disk_cachemode = None  
  31.        self.image_cache_manager = imagecache.ImageCacheManager()  

1、父类初始化

      super(LibvirtConnection, self).__init__()  


2、设置firewallDriver

     fw_class = utils.import_class(FLAGS.firewall_driver)

     在此文件的上方有:这里还只是获得类名,并不是得到一个实例。

     flags.DEFINE_string('firewall_driver',
                    'nova.virt.libvirt.firewall.IptablesFirewallDriver',
                    'Firewall driver (defaults to iptables)')

    应该是引入防火墙规则之类的。

3、实例化防火墙类。

    self.firewall_driver = fw_class(get_connection=self._get_connection)
    那么这里应该会调用_get_connection()函数。


4、获得链接调用_get_connection()

[python] view plaincopy
  1. def _get_connection(self):  
  2.        if not self._wrapped_conn or not self._test_connection():  
  3.            LOG.debug(_('Connecting to libvirt: %s'), self.uri)  
  4.            if not FLAGS.libvirt_nonblocking:  
  5.                self._wrapped_conn = self._connect(self.uri,  
  6.                                               self.read_only)  
  7.            else:  
  8.                self._wrapped_conn = tpool.proxy_call(  
  9.                    (libvirt.virDomain, libvirt.virConnect),  
  10.                    self._connect, self.uri, self.read_only)  
  11.   
  12.        return self._wrapped_conn  
  13.   
  14.    _conn = property(_get_connection)  


5、再查看_connect()

[python] view plaincopy
  1. @staticmethod  
  2. def _connect(uri, read_only):  
  3.     auth = [[libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_NOECHOPROMPT],  
  4.             'root',  
  5.             None]  
  6.   
  7.     if read_only:  
  8.         return libvirt.openReadOnly(uri)  
  9.     else:  
  10.         return libvirt.openAuth(uri, auth, 0)  

这个函数是从底层libvirt库中拿到链接。


在nova/service.py中Service类中start方法中

[python] view plaincopy
  1. if self.periodic_interval:  
  2.            periodic = utils.LoopingCall(self.periodic_tasks) #循环调用 periodic_tasks 下面详细说明  
  3.            periodic.start(interval=self.periodic_interval, now=False)  
  4.            self.timers.append(periodic)  

此出循环调用 self.periodic_tasks

[python] view plaincopy
  1. def periodic_tasks(self, raise_on_error=False):  
  2.         """Tasks to be run at a periodic interval."""  
  3.         ctxt = context.get_admin_context()  
  4.         self.manager.periodic_tasks(ctxt, raise_on_error=raise_on_error)  

调用self.manager. periodic_tasks 函数。 

其中manager 就是nova/compute/manager.py 中 ComputeManager

ComputeManager的父类SchedulerDependentManager

SchedulerDependentManager父类Manager(nova/manager.py)(可以参考nova-compute启动分析-1中manager类图)


[python] view plaincopy
  1. def periodic_tasks(self, context, raise_on_error=False):  
  2.       """Tasks to be run at a periodic interval."""  
  3.       for task_name, task in self._periodic_tasks:  
  4.           full_task_name = '.'.join([self.__class__.__name__, task_name])  
  5.   
  6.           ticks_to_skip = self._ticks_to_skip[task_name]  
  7.           if ticks_to_skip > 0:  
  8.               LOG.debug(_("Skipping %(full_task_name)s, %(ticks_to_skip)s"  
  9.                           " ticks left until next run"), locals())  
  10.               self._ticks_to_skip[task_name] -= 1  
  11.               continue  
  12.   
  13.           self._ticks_to_skip[task_name] = task._ticks_between_runs  
  14.           LOG.debug(_("Running periodic task %(full_task_name)s"), locals())  
  15.   
  16.           try:  
  17.               task(self, context)  
  18.           except Exception as e:  
  19.               if raise_on_error:  
  20.                   raise  
  21.               LOG.exception(_("Error during %(full_task_name)s: %(e)s"),  
  22.                             locals())  


这个方法其实就是隔_ticks_to_skip次运行保存在_periodic_tasks中的方法

其中属性  _periodic_tasks 来自  __metaclass__ = ManagerMeta

 

这个时候来看下 ManagerMeta 类(nova/manager.py)

[python] view plaincopy
  1. class ManagerMeta(type):  
  2.     def __init__(cls, names, bases, dict_):  
  3.         """Metaclass that allows us to collect decorated periodic tasks."""  
  4.         super(ManagerMeta, cls).__init__(names, bases, dict_)  
  5.   
  6.         # NOTE(sirp): if the attribute is not present then we must be the base  
  7.         # class, so, go ahead an initialize it. If the attribute is present,  
  8.         # then we're a subclass so make a copy of it so we don't step on our  
  9.         # parent's toes.  
  10.         try:  
  11.             cls._periodic_tasks = cls._periodic_tasks[:]  
  12.         except AttributeError:  
  13.             cls._periodic_tasks = []  
  14.   
  15.         try:  
  16.             cls._ticks_to_skip = cls._ticks_to_skip.copy()  
  17.         except AttributeError:  
  18.             cls._ticks_to_skip = {}  
  19.   
  20.         for value in cls.__dict__.values():  
  21.             if getattr(value, '_periodic_task'False):  
  22.                 task = value  
  23.                 name = task.__name__  
  24.                 cls._periodic_tasks.append((name, task))  
  25.                 cls._ticks_to_skip[name] = task._ticks_between_runs  

  其实很简单,  if getattr(value, '_periodic_task', False):

  如果某个  cls.__dict__.values() 的属性存_periodic_task

  就加入到 cls._periodic_tasks 中 ,

 其中这个属性的设置 是通过装饰函数进行完成的 def periodic_task(nova/manager.py)

[python] view plaincopy
  1. def periodic_task(*args, **kwargs):  
  2.     """Decorator to indicate that a method is a periodic task. 
  3.  
  4.     This decorator can be used in two ways: 
  5.  
  6.         1. Without arguments '@periodic_task', this will be run on every tick 
  7.            of the periodic scheduler. 
  8.  
  9.         2. With arguments, @periodic_task(ticks_between_runs=N), this will be 
  10.            run on every N ticks of the periodic scheduler. 
  11.     """  
  12.     def decorator(f):  
  13.         f._periodic_task = True  
  14.         f._ticks_between_runs = kwargs.pop('ticks_between_runs'0)  
  15.         return f  
  16.   
  17.     # NOTE(sirp): The `if` is necessary to allow the decorator to be used with  
  18.     # and without parens.  
  19.     #  
  20.     # In the 'with-parens' case (with kwargs present), this function needs to  
  21.     # return a decorator function since the interpreter will invoke it like:  
  22.     #  
  23.     #   periodic_task(*args, **kwargs)(f)  
  24.     #  
  25.     # In the 'without-parens' case, the original function will be passed  
  26.     # in as the first argument, like:  
  27.     #  
  28.     #   periodic_task(f)  
  29.     if kwargs:  
  30.         return decorator  
  31.     else:  
  32.         return decorator(args[0])