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的类视图。
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