openstack代码层面剖析虚拟机创建完整流程
前言
使用python-novaclient创建虚拟机,结合github上python-novaclient、nova源码分析创建虚拟机的整个流程。
由于代码涉及功能非常多,因此文章着重介绍整个链路,而不会去解释每个节点做了什么。
环境
openstack S版本
正文
调用novaclient创建虚拟机时,novaclient最后会去请求nova处理任务。因此这里将novaclient和nova分开介绍。
novaclient 部分:
python-novaclient代码仓库:
https://github.com/openstack/python-novaclient
SDK 创建虚拟机参考:
https://docs.openstack.org/ocata/user-guide/sdk-compute-apis.html#create-server-api-v2
如图,创建虚拟机调用了 nova_client.servers.create()
。
1. 首先寻找nova_client
from novaclient.client import Client
通过导包,可以看到 nova_client
是此处的 novaclient.client.Client
2. 寻找 novaclient.client.Client
novaclient.client.Client代码位置:
https://github.com/openstack/python-novaclient/blob/stable/stein/novaclient/client.py
已然找到了Client,在此Client方法中返回了client_class。对client_class定义的代码都在当前页面中,分析后可以发现Client导向 novaclient.v2.client.Client
3. 寻找 novaclient.v2.client.Client
novaclient.v2.client.Client代码位置:
https://github.com/openstack/python-novaclient/blob/stable/stein/novaclient/v2/client.py
如图,第一层nova_client至此结束,实际指向
novaclient.v2.client.Client
类。接下来分析
nova_client.servers.create()
中的servers。在当前Client类中 可以发现 servers指向
servers.ServerManager(self)
,结合导包得出这个servers 实际指向 novaclient.v2.servers.ServerManager
4. 寻找 novaclient.v2.servers.ServerManager
novaclient.v2.servers.ServerManager代码位置:
https://github.com/openstack/python-novaclient/blob/stable/stein/novaclient/v2/servers.py
如图,
nova_client.servers.create()
中的servers也已找到。开始分析create()方法。
- 当前类的
create()
指向self._boot()
- 当前类的
self._boot()
指向self._create()
- 而
self._create()
并没有在当前类中定义,这里可以通过基类寻找。
5. 寻找 self._create()
- 父类 BootingManagerWithFind 中并没有定义
- 再往上,ManagerWithFind 类中也没有定义
- 再往上, Manager 类中定义了
_create()
方法
self._create() 代码位置:
https://github.com/openstack/python-novaclient/blob/stable/stein/novaclient/base.py
在 _create() 中,可以看到调用了一个post方法。这里就是novaclient sdk的最底层。
分析可得知,novaclient 向nova 发送http post请求完成创建任务。
因此nova 势必实现了一个web server,接下来分析nova源码时,可以从此处切入。
nova 部分
nova 代码仓库:
https://github.com/openstack/nova
1. 寻找webserver的入口
根据webserver 编程经验,首先寻找路由部分。
路由代码:
https://github.com/openstack/nova/blob/stable/stein/nova/api/openstack/compute/routes.py
如图,结合 novalicent的url 以及post方式,可以看出:创建虚拟机通过 server_controller 的create方法实现。
通过当前页面的代码分析,可以得出 server_controller 指向 ServersController。
下一步寻找 ServersController
2. 寻找 ServersController
ServersController 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/api/openstack/compute/servers.py
在当前类中,找到了create方法,如下图:
image.png
create方法最后调用了 self.compute_api.create()
在类初始化定义当中 compute_api 指向 compute.API()
接下来寻找 compute.API()
3. 寻找 compute.API()
compute.API() 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/compute/init.py
当前页面分析代码,可得知 API() 指向 nova.compute.api.API
4. 寻找 nova.compute.api.API
nova.compute.api.API 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/compute/api.py
self.compute_api.create()
中的compute_api已然找到,现在寻找对应的create()
- 当前类中实现了
create()
,create()
指向self._create_instance()
- 当前类实现了
self._create_instance()
,由于参数的配置不同,本人环境self._create_instance()
指向self.compute_task_api.schedule_and_build_instances()
。这里指向的方法虽有不同,但是大体实现原理都是一致的。 -
self.compute_task_api
又指向conductor.ComputeTaskAPI()
接下来找到conductor.ComputeTaskAPI()
5. 寻找 conductor.ComputeTaskAPI()
conductor.ComputeTaskAPI() 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/conductor/init.py
如图,根据导包,conductor.ComputeTaskAPI() 又指向
nova.conductor.api.ComputeTaskAPI
6. 寻找 nova.conductor.api.ComputeTaskAPI
nova.conductor.api.ComputeTaskAPI 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/conductor/api.py
至此,self.compute_task_api.schedule_and_build_instances()
中的 compute_task_api 已然找到。接下来寻找 schedule_and_build_instances()
。
- 当前类实现了
schedule_and_build_instances()
-
schedule_and_build_instances()
又指向self.conductor_compute_rpcapi.schedule_and_build_instances()
-
self.conductor_compute_rpcapi
指向rpcapi.ComputeTaskAPI()
接下来寻找 rpcapi.ComputeTaskAPI()
7. 寻找 rpcapi.ComputeTaskAPI()
rpcapi.ComputeTaskAPI() 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/conductor/rpcapi.py
如图
self.conductor_compute_rpcapi.schedule_and_build_instances()
中的 self.conductor_compute_rpcapi
已然找到,接下来就是寻找 schedule_and_build_instances()
。
当前类实现了 schedule_and_build_instances()
,这个方法调用了 消息队列的 异步方法,代码如下:
cctxt.cast(context, 'schedule_and_build_instances', **kw)
这里说明,nova哪里还有一个消息的消费者,也就是实现 schedule_and_build_instances
的服务端。
接下来去寻找这个 schedule_and_build_instances
8. 寻找消息队列的消费者 schedule_and_build_instances
schedule_and_build_instances 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/conductor/manager.py
通过方法所在类的装饰器可以看出,这是消息队列的消费者。
当前方法又指向 self.compute_rpcapi.build_and_run_instance()
self.compute_rpcapi
指向 compute_rpcapi.ComputeAPI()
结合导包,最终指向为:nova.compute.rpcapi.ComputeAPI()
现在去寻找 nova.compute.rpcapi.ComputeAPI()
9. 寻找 nova.compute.rpcapi.ComputeAPI()
nova.compute.rpcapi.ComputeAPI() 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/compute/rpcapi.py
self.compute_rpcapi.build_and_run_instance()
中的 self.compute_rpcapi
已然找到,接下来寻找当前类的 build_and_run_instance()
当前类实现了
build_and_run_instance()
build_and_run_instance()
调用了消息队列的异步方法,代码如下:
cctxt.cast(ctxt, 'build_and_run_instance', **kwargs)
接下来,就去寻找这个 build_and_run_instance
10. 寻找 build_and_run_instance
build_and_run_instance 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/compute/manager.py
- 当前方法最后调用了一个协程,指向
_locked_do_build_and_run_instance
- 当前方法中定义了
_locked_do_build_and_run_instance
-
_locked_do_build_and_run_instance
指向self._do_build_and_run_instance
- 当前类定义了
self._do_build_and_run_instance
-
self._do_build_and_run_instance
指向self._build_and_run_instance()
- 当前类定义了
self._build_and_run_instance()
-
self._build_and_run_instance()
数据库更新、调度都在这里有体现。指向self.driver.spawn()
- 结合导包,
self.driver
指向nova.virt.driver.load_compute_driver
11. 寻找 nova.virt.driver.load_compute_driver
nova.virt.driver.load_compute_driver 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/virt/driver.py
本环境的 driver是
libvirt.LibvirtDriver
当前方法返回
nova.virt.libvirt.LibvirtDriver
接下来寻找
nova.virt.libvirt.LibvirtDriver
12. 寻找 nova.virt.libvirt.LibvirtDriver
nova.virt.libvirt.LibvirtDriver 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/virt/libvirt/init.py
这里又指向
nova.virt.libvirt.driver.LibvirtDriver
接下来寻找
nova.virt.libvirt.driver.LibvirtDriver
13. 寻找 nova.virt.libvirt.driver.LibvirtDriver
nova.virt.libvirt.driver.LibvirtDriver 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/virt/libvirt/driver.py
-
self.driver.spawn()
中的driver 已然找到,在当前类中寻找spawn()
- 当前类实现了
spawn()
-
spawn()
中实现了生成xml等,指向self._create_domain_and_network()
- 当前类实现了
self._create_domain_and_network()
-
self._create_domain_and_network()
指向self._create_domain()
- 当前类实现了
host.write_instance_config()
,这里需要注意传入了一个host参数,host取值于当前类的属性。 - 结合导包,
self._create_domain()
指向nova.virt.libvirt.guest.Guest.create()
接下来寻找nova.virt.libvirt.guest.Guest.create()
14. 寻找 nova.virt.libvirt.guest.Guest.create()
nova.virt.libvirt.guest.Guest.create() 代码位置:https://github.com/openstack/nova/blob/stable/stein/nova/virt/libvirt/guest.py
指向
host.write_instance_config()
接下来寻找
host.write_instance_config()
。这里的host 是步骤【13】里的 host.write_instance_config()
传入的。
15. 寻找 host.write_instance_config()
host.write_instance_config() 代码位置:
https://github.com/openstack/nova/blob/stable/stein/nova/virt/libvirt/host.py
- 当前方法指向
self.get_connection().defineXML()
-
self.get_connection()
指向self._get_connection()
-
self._get_connection()
指向self._connect()
-
self._connect()
指向tpool.proxy_call()
, 大致意思是返回一个libvirt 连接。 -
defineXML()
就是创建一个虚拟机,但是还未启动。 - 回到步骤【13】中的
_create_domain()
,这里实现了虚拟机是否开启
至此,openstack部分已全部走完。