nova-compute启动分析-1

时间:2022-12-25 08:16:21

python启动脚本 bin/nova-compute

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

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

第一步:utils.default_flagfile()

   if args is None:
args = sys.argv
for arg in args:
if arg.find('flagfile') != -1:
break
else:
if not os.path.isabs(filename):
# turn relative filename into an absolute path
script_dir = os.path.dirname(inspect.stack()[-1][1])
filename = os.path.abspath(os.path.join(script_dir, filename))
if not os.path.exists(filename):
filename = "./nova.conf"
if not os.path.exists(filename):
filename = '/etc/nova/nova.conf'
flagfile = '--flagfile=%s' % filename
args.insert(1, flagfile)
其实所完成的功能也就是把--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中

class Service(object):
"""Service object for binaries running on hosts.
A service takes a manager and enables rpc by listening to queues based
on topic. It also periodically runs tasks on the manager and reports
it state to the database services table."""
函数定义如下:
@classmethod
def create(cls, host=None, binary=None, topic=None, manager=None,
report_interval=None, periodic_interval=None):
"""Instantiates class and passes back application object.

:param host: defaults to FLAGS.host
:param binary: defaults to basename of executable
:param topic: defaults to bin_name - 'nova-' part
:param manager: defaults to FLAGS.<topic>_manager
:param report_interval: defaults to FLAGS.report_interval
:param periodic_interval: defaults to FLAGS.periodic_interval

"""
if not host:
host = FLAGS.host #host name ‘nova’
if not binary:
binary = os.path.basename(inspect.stack()[-1][1]) # 这是因为python可以查看动栈的内容。
# 所以可以得到压入栈中的脚本的名字 这时,binary="nova-compute"
if not topic:
topic = binary.rpartition('nova-')[2] #设置topic的名字:也就是把 binary的nova-去掉。
if not manager:
manager = FLAGS.get('%s_manager' % topic, None) #很明显这里得到的是compute_manager.
if not report_interval:
report_interval = FLAGS.report_interval
if not periodic_interval:
periodic_interval = FLAGS.periodic_interval
service_obj = cls(host, binary, topic, manager,
report_interval, periodic_interval) #利用 Service.__init__()构造函数生成一个对象

return service_obj


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

def __init__(self, host, binary, topic, manager, report_interval=None,
periodic_interval=None, *args, **kwargs):
self.host = host
self.binary = binary
self.topic = topic
self.manager_class_name = manager
manager_class = utils.import_class(self.manager_class_name)
self.manager = manager_class(host=self.host, *args, **kwargs)
self.report_interval = report_interval
self.periodic_interval = periodic_interval
super(Service, self).__init__(*args, **kwargs)
self.saved_args, self.saved_kwargs = args, kwargs
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__()函数

     

 def __init__(self, compute_driver=None, *args, **kwargs):
"""Load configuration options and connect to the hypervisor."""
# TODO(vish): sync driver creation logic with the rest of the system
# and re-document the module docstring
if not compute_driver:
compute_driver = FLAGS.compute_driver #这部分在后面会仔细分析
#cfg.StrOpt('compute_driver',default='nova.virt.connection.get_connection',
#help='Driver to use for controlling virtualization')
try:
self.driver = utils.check_isinstance(
utils.import_object(compute_driver),
driver.ComputeDriver) #动态的加载 虚拟机的控制驱动对象
except ImportError as e:
LOG.error(_("Unable to load the virtualization driver: %s") % (e))
sys.exit(1)

self.network_api = network.API()
self.volume_api = volume.API()
self.network_manager = utils.import_object(FLAGS.network_manager)
self._last_host_check = 0
self._last_bw_usage_poll = 0
self._last_info_cache_heal = 0

super(ComputeManager, self).__init__(service_name="compute",
*args, **kwargs)

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

nova-compute启动分析-1

  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)开启服务

def serve(*servers):
global _launcher # class Launcher 一个全局对象
if not _launcher:
_launcher = Launcher()
for server in servers:
_launcher.launch_server(server)

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

 

  def launch_server(self, server):
"""Load and start the given server.

:param server: The server you would like to start.
:returns: None

"""
gt = eventlet.spawn(self.run_server, server) #启动线程运行 run_server
self._services.append(gt) #保存到 _services里面

@staticmethod
def run_server(server):
"""Start and wait for a server to finish.

:param service: Server to run and wait for.
:returns: None

"""
server.start()
server.wait()

其中server.start() #启动服务 我们来查看下 service.py中 Service类中start方法  
 def start(self):
vcs_string = version.version_string_with_vcs() #获取版本号
LOG.audit(_('Starting %(topic)s node (version %(vcs_string)s)'),
{'topic': self.topic, 'vcs_string': vcs_string})
utils.cleanup_file_locks() #清除锁文件
self.manager.init_host()
self.model_disconnected = False
ctxt = context.get_admin_context()
try:
service_ref = db.service_get_by_args(ctxt,
self.host,
self.binary)
self.service_id = service_ref['id']
except exception.NotFound:
self._create_service_ref(ctxt)

if 'nova-compute' == self.binary:
self.manager.update_available_resource(ctxt)

self.conn = rpc.create_connection(new=True)
LOG.debug(_("Creating Consumer connection for Service %s") %
self.topic)

# Share this same connection for these Consumers
self.conn.create_consumer(self.topic, self, fanout=False)

node_topic = '%s.%s' % (self.topic, self.host)
self.conn.create_consumer(node_topic, self, fanout=False)

self.conn.create_consumer(self.topic, self, fanout=True)

# Consume from all consumers in a thread
self.conn.consume_in_thread()

if self.report_interval:
pulse = utils.LoopingCall(self.report_state)
pulse.start(interval=self.report_interval, now=False)
self.timers.append(pulse)

if self.periodic_interval:
periodic = utils.LoopingCall(self.periodic_tasks)
periodic.start(interval=self.periodic_interval, now=False)
self.timers.append(periodic)

下面我们对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.然后再更新当前机器上可用的资源。 

        self.model_disconnected = False
ctxt = context.get_admin_context()
try:
service_ref = db.service_get_by_args(ctxt,
self.host,
self.binary)
self.service_id = service_ref['id']
except exception.NotFound:
self._create_service_ref(ctxt)

if 'nova-compute' == self.binary:
self.manager.update_available_resource(ctxt)


4. 更新RPC链接

 self.conn = rpc.create_connection(new=True)
LOG.debug(_("Creating Consumer connection for Service %s") %
self.topic)

# Share this same connection for these Consumers
self.conn.create_consumer(self.topic, self, fanout=False)

node_topic = '%s.%s' % (self.topic, self.host)
self.conn.create_consumer(node_topic, self, fanout=False)

self.conn.create_consumer(self.topic, self, fanout=True)

# Consume from all consumers in a thread
self.conn.consume_in_thread()


if self.report_interval:
pulse = utils.LoopingCall(self.report_state) #循环调用report_state
pulse.start(interval=self.report_interval, now=False)
self.timers.append(pulse)

if self.periodic_interval:
periodic = utils.LoopingCall(self.periodic_tasks) #循环调用 periodic_tasks 下面详细说明
periodic.start(interval=self.periodic_interval, now=False)
self.timers.append(periodic)

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



六. Wait() 等待

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

/usr/bin/nova-compute
service.wait()

/nova/service.py:Class:Service::wait()
def wait(self):
for x in self.timers:
try:
x.wait()
except Exception:
pass