之前写了两篇从nova到ironic是如何调用的文章,只是大概的架子。这两年看openstack的源码也看了不少,看到后来觉得大同小异,之前的积累现在看起来因为没有具体项目带动也比较零散,东一点儿西一点儿,现在从ironic进来反而觉得都很清晰起来,因此想把之前写的完全搬到网上来总觉得不和谐,我决定还是从细节入手按照之前的风格,把这两年
的积累归零,换个思路来(Juno,之前是icehouse,还是很多相似的)。
这个人写的比我好:
http://bingotree.cn/?p=193
我也稍微捋一捋:
一样的,作为一个python项目,nova也有entry_points,和ironic一样,在做egg之前就写在setup.cfg中,通过stevedore在初始化时,将entry_points和类
的对应关系load进来。
在nova.cmd下面是nova启服务的地方,nova作为openstack中最初也是最复杂的一块,一切都源于nova,因此cmd包下面的启动的服务很多,不像ironic只有api,conductor,dbsync,nova有api,compute,cert,cells,console,scheduler等等,很多,但是也就是两种类型,和ironic一样,一种是WSGI的service,对外接收
rest请求,另一种是RPC service,接收RPC call进行处理或者转发,分别来看一看:
WSGI service:
在nova.cmd下以api开头的,比如api.py, api_ec2.py,api_os_comput.py以及api_metadata.py都属于WSGI的service,这里以api.py为例,
config.parse_args(sys.argv) #根据传入的参数做一些default的设置,log,debug,rpc等,初始化一个全局的CONF manager, notifier比较有意思,确定不同的message
写到不同的地方,log,test,routing等等
logging.setup("nova") #log设置,基本不会变动,包装logging
utils.monkey_patch()#运行时动态修改code,python有很多这样的黑魔法,前面提到的metaclass也是一种,其实,个人觉得不一样非要这样。。。
notify_decorator = 'nova.notifications.notify_decorator'
monkey_patch_opts = [
cfg.BoolOpt('monkey_patch',
default=False,
help='Whether to log monkey patching'),
cfg.ListOpt('monkey_patch_modules',
default=[
'nova.api.ec2.cloud:%s' % (notify_decorator),
'nova.compute.api:%s' % (notify_decorator)
],
help='List of modules/decorators to monkey patch'),
]
调用utils.monkey_patch时,我们在方法中可以看到,实际上只是给monkey_patch_modules中的方法加上了notify_decorator而已
objects.register_all()#将objects包中的类load起来
gmr.TextGuruMeditation.setup_autorun(version) #通过发送一个USER1的信号量,进程捕获后会返回对应的信息
cfg.ListOpt('enabled_apis',
default=['ec2', 'osapi_compute', 'metadata'],
help='A list of APIs to enable by default'),
针对enabled_apis启动WSGI服务,初始化self.manager的时候只有MetadataManager,没有ec2 manager或者 osapi_compute manager
self.loader = loader or wsgi.Loader()
self.app = self.loader.load_app(name)
分别解析paste-api.ini并取得接收rest的入口
接着launcher.launch_service(server, workers=server.workers or 1)就涉及到core的内容,包括协程使用等等,最后调用WSGIService的start方法,
可以看出如果前面提到的self.manager存在的话会做一些pre hook和post hook
有一段话引用的文章中说的很好,我mark一下,对于理解Openstack的service很有好处:
“
openstack中使用协程和普通的多线程模型下的HTTP server有一个很大的不同:普通的HTTP server在启动的时候只有一个线程在那里监听一个端口,来了一个请求才会fork一个线程去做独立的处理(也就是说如果请求很多的话,线程个数也会很多)。但这里由于使用了eventlet的绿化(本质就是协程,可以看这里),因此对于协程来说,一个协程只能运行在一个CPU核上,并且不存在来个请求就fork这种东西,所以这里会根据CPU的核的个数去建立对应的协程(worker)。当一个请求来了过后呢其就会交给某一个协程去处理。不管请求个数多少,协程的个数是固定的。协程在HTTP server方面的效率是很高的。
”
RPC service或者说内部service,举nova-compute service为例:
http://bingotree.cn/?p=289这一篇也不错,很细,可参考
在nova.cmd.compute.py中,首先
CONF.import_opt('compute_topic', 'nova.compute.rpcapi')
CONF.import_opt('use_local', 'nova.conductor.api', group='conductor')
分别从nova.compute.rpcapi和nova.conductor.api中引入compute_topic='compute', 'use_local'=false
if not CONF.conductor.use_local:
block_db_access() #nova.db.api.IMPL = NoDB(),在NoDB中实现__call__方法,在compute service中调用数据库会报错
objects_base.NovaObject.indirection_api = conductor_rpcapi.ConductorAPI() #在ironic中也提到indirection_api,不过ironic中并没有真正使用,而在nova中,
Objects的基类NovaObject的元数据类中有此属性,也就是创建nova中的objects时会用到该属性,而我们看到很多object的方法会加@base.remotable或者@base.remotable_classmethod,则会将方法变成rpc call(可以跟踪conductor_rpcapi.ConductorAPI()的调用过程,会发现最后走到nova.conductor.manager.py中的
_object_dispatch方法)
server = service.Service.create(binary='nova-compute',
topic=CONF.compute_topic,
db_allowed=CONF.conductor.use_local)
topic为compute,manager为nova.compute.manager.ComputeManage, db_allowed 为false
cfg.StrOpt('compute_manager',
default='nova.compute.manager.ComputeManager',
help='Full class name for the Manager for compute'),
在nova.compute.manager.ComputeManager的__init__方法中:
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._last_bw_usage_poll = 0
self._bw_usage_supported = True
self._last_bw_usage_cell_update = 0
self.compute_api = compute.API()
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
self.conductor_api = conductor.API()
self.compute_task_api = conductor.ComputeTaskAPI()
self.is_neutron_security_groups = (
openstack_driver.is_neutron_security_groups())
self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI()
self.cells_rpcapi = cells_rpcapi.CellsAPI()
self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI()
self._resource_tracker_dict = {}
self.instance_events = InstanceEvents()
self._sync_power_pool = eventlet.GreenPool()
self._syncs_in_progress = {}
建立调用的入口关系,如virtapi的入口为ComputeVirtAPI,调用其self.virtapi的方法时就知道调用的是谁了
Service.serve(server)调用service的start方法,start方法中重要的几个点为:
self.manager.init_host()#首先调用driver的init_host,默认的是LibVirt Driver,check运行环境是否为qemu或者kvm,DB和当前实际情况
的比较,比如当前host的虚机是否仍然存在等,涉及到hypervisor的信息暂时不是我的方向或者重点,不再继续追进。
self.manager.pre_start_hook()#追进去内容很多且比较底层,主要就是主动调用一个周期性的任务,将虚拟机和物理机的相关性能信息写入nova的数据库。
并且如果是新上线的物理机节点,则会注册相关信息到数据库中(内存,cpu,disk等)。
endpoints = [
self.manager,
baserpc.BaseRPCAPI(self.manager.service_name, self.backdoor_port)
]
endpoints.extend(self.manager.additional_endpoints)
serializer = objects_base.NovaObjectSerializer()
self.rpcserver = rpc.get_server(target, endpoints, serializer) #RPC调用会调用endpoints中的方法, ComputeManager中的addtional_endpoints为空,但是
ConductorManager中的不为空
还有conductor,scheduler等服务,如何启动也是上面两种情况,以后温习具体的点时再记录下来。