当用户通过身份验证后,需要一个客户端发送创建请求,openstack现在提供两种客户端:
(1) 网页交互界面horizon,通过勾选等方式选择参数后,点击启动按钮进行创建:
(2) 通过CLI命令行指令nova,以及添加一些自定义参数,进行启动:
nova boot vm_name --image image_id --flavor flavor_id
其实不论用什么方法,都会转化符合RESTful 的请求api进行请求。
首先,需要客户端需要先构造一个包含username +password的body,然后通过 HTTP请求(url为http://localhost:5000/v2.0/tokens)到keystone中去验证身份。身份验证通过后,返回给客户端一个token_id 和serviceCatalog。至此,客户端才可以向nova-api发起创建请求。当然,客户端向nova-api发送满足REST API 的HTTP创建请求,需要有url请求地址,这个url地址需要通过keystone验证后,通过serviceCatalog 中返回给客户端。
其次,如果nova-pai想要接收发送过来的满足REST的HTTP 的请求,肯定需要有一个HTTP服务器的,那么这个服务器是怎么实现的呢?接下来我们来看一下openstack是如何实现的服务器创建并对外发布服务的呢?我们看下:
首先程序会找到nova.cmd下的api.py的main()并执行启动。在启动HTTP服务前,会先创建一个WSGIService对象。
defmain():
... ...
for apiin CONF.enabled_apis:
should_use_ssl = api in CONF.enabled_ssl_apis
if api =='ec2':
server =service.WSGIService(api, use_ssl=should_use_ssl,
max_url_len=16384)
else:
server =service.WSGIService(api, use_ssl=should_use_ssl)
......
launcher.wait()
它是基于WSGI标准的服务器类,在启动服务时,启动服务时,需要会将这个WSGIService对象当做参数传过去。
launcher.launch_service(server, workers=server.workers or 1)
那么这个WSGIService中又引用了那些类,实现了那些功能的呢?
WSGIService在构造函数中,通过paste.deploy解析paste.ini配置文件并根据参数name(例如osapi_compute),经过一系列的middleware(fliter)中间件进行处理,最终走到一个application,并将这个加载的app以及一些host、port作为创建 nova.wsgi.py的Server类的参数,最终nova就是通过这个server类以协程方式来对外提供服务。
那么这个application是怎么来的呢?nova.api.openstack.compute.APIRouter.factory()返回 的一个APIRouter实例,这个实例调用其父类的父类nova.wsgi.APIRouter中的成员变量mapper来实现url与controller的映射。根据http请求中的地址找到对应的controller,并实现继承了这个Controller类的子类实例,这个子类实例就是我们要找的具体的application。
到这里我们已经准备好了由server接收HTTP 请求后, 交给这个application去处理。根据请求url我们能找到具体的controller中的具体处理方法,例如我们要实现创建虚拟机,那么就会根据接收过来的请求命令,http://localhost:8774/v2/project_id/servers 。根据/v2 以及paste.ini 配置文件:
[composite:osapi_compute]
use = call:nova.api.openstack.urlmap:urlmap_factory
/: oscomputeversions
/v1.1: openstack_compute_api_v2
/v2: openstack_compute_api_v2
/v3: openstack_compute_api_v3
找到对应的composite:
[composite:openstack_compute_api_v2] use = call:nova.api.auth:pipeline_factory noauth = faultwrap sizelimit noauth ratelimit osapi_compute_app_v2 keystone = faultwrap sizelimit authtoken keystonecontext ratelimit osapi_compute_app_v2 keystone_nolimit = faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v2
这里是根据nova.api.auth.pipeline_factory() 并经过一系列的filter(middleware)进行处理后,最后找到wsgi app :osapi_compute_app_v2。
[app:osapi_compute_app_v2] paste.app_factory = nova.api.openstack.compute:APIRouter.factory
根据配置返回一个APIRouter实例,这个实例会通过其父类(nova.api.openstack.APIRouter)的父类(nova,wsgi.APIRouter)的成员变量mapper来维护url到controller的映射。最终找到会根据url找到nova.api.openstack.compute.servers.
Controller()类的create()方法。到这里开始正式对创建虚拟机进行处理。
nova-api处理模块:
1 nova.api.openstack.compute.servers.py:Controller.create()
主要任务是从传递过的req中获取各种创建虚拟机所需要的参数信息,并做验证,然后将获取的一系列参数(image_uuid,name,insts_type等)作为nova/compute/api.py中的API类的create()方法参数,进行下一步处理。
2 nova.compute.api.py:API.create():
主要任务是对check policies、check quota、创建db记录,创建下一步中schedule所需要的调度规则信息filter_properties,将部分参数整合,然后将创建请求发送到nova/conductor/api.py中,进行下一步处理。
3 nova.conductor.api.py:ComputeTaskAPI.bulid_instance()
接收有上一步的命令,转发的rpcapi,进行下一步:
nova.conductor.rpcapi.py:ComputeTaskAPI. ComputeTaskAPI()
创建rabbitmq消息队列的client对象,以cast方式发送rpc消息到conductor.manager(如何实现发送需细化)进行下一步:
cctxt = self.client.prepare(version='1.5') cctxt.cast(context, 'build_instances', instances=instances, image=image_p ...)
nova.conductor.manager.ComputeTaskManager. build_instances()
1) 创建request_spec,供下一步schedule中使用。(request_spec包含虚拟机的类型、数量、uuid等信息)。
2) 调用nova/scheduler/rpcapi.py的run_instance()方法,将请求发送至scheduler中。
nova-schduler处理模块:
1 nova.scheduler.rpcapi.py:SchedulerAPI.run_instance()
接收到请求后,创建rabbitmq消息队列的client对象,以cast方式发送rpc消息到scheduler.manager,进行进一步处理:
cctxt = self.client.prepare(version=version) return cctxt.cast(ctxt, 'run_instance', **msg_kwargs)
2 nova.schduler.manager.py : SchedulerManager.run_instance()
收到当SchedulerManager实例化的时候,会根据取得的配置文件取得相应的调度器,默认是FilterScheduler调度器。这个调度器是nova.scheduler.filter_scheduler.
FilterScheduler类的实例。
3 nova.scheduler.filter_scheduler.py:FilterScheduler._scheduler()
1) 通过self.schedule(),经过过滤和权重筛选,最终得到满足条件可以创建出虚拟机的所有host集合--weighted_hosts
weighed_hosts = self._schedule(context, request_spec, filter_properties, instance_uuids)
2) 通过self._provision_resource()来进行数据库的更新操作,并通过调用nova/compute/rpcapi.py的run_instance()进行进一步处理
nova-compute处理模块:
1 nova.compute.rpcapi:ComputeAPI.run_instance():
创建rabbitmq消息队列的client对象,以cast方式发送rpc消息到compute.manager进行下一步处理。
2 nova.compute.manager.py:ComputeManager.run_instance()
根据context中的想信息找到_prebuild_instance()方法中得到image信息。
image_meta =self._prebuild_instance(context,instance)
_prebuild_instance中的
image_meta= _get_image_meta(context,instance['image_ref'])
_get_image_meta():
def_get_image_meta(context,image_ref):
image_service, image_id = glance.get_remote_image_service(context,
image_ref)
return image_service.show(context,image_id)
创建glanceClient对象,通过http call到glance中请求镜像并下载到本地。
获取网络信息:(这里是通过nova-network方式获取网络信息,到neutron请求类似于glance)
通过调用self._run_instance()中的_build_instance()获取instance和网络信息network_info。
instance, network_info = self._build_instance(context, request_spec, filter_properties, requested_networks, injected_files, admin_password, is_first_time, node, instance, image_meta, legacy_bdm_in_spec)
在build_instance()通过调用_allocate_network()获得网络信息network_info
network_info = self._allocate_network(context, instance, requested_networks, macs, security_groups, dhcp_options)
通过调用self._spawn()并将network_info当做参数,进行虚拟机创建
下面看下如何在_spawn()方法中创建instance:
self.driver = driver.load_compute_driver(self.virtapi, compute_driver)
首先会根据虚拟技术(KVM docker vmware等),先获得对应的driver,这里我们是选择kvm,所以driver会找到nova/virt/libvirt/dirver.py
self.driver.spawn(context, instance, image_meta, injected_files, admin_password, … …)
nova-driver处理模块:
3 nova.virt.libvirt.driver.py:LibvirtDriver.spawn()
1) 获取磁盘相关信息
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type, instance,...
2) 创建实例镜像
self._create_image(context, instance,...
3) 为新建的实例参数获取配置数据conf,并把conf 的配置信息文件转化成所需xml格式的文件。
xml = self.to_xml(context, instance, network_info ,...
4) 获取网络和域等相关信息
elf._create_domain_and_network(context, xml, instance, network_info,...
5)_wait_for_boot函数等待libvirt进行虚拟机启动操作。
参考文档:
http://blog.csdn.net/xuriwuyun/article/details/16845601?utm_source=tuicool&utm_medium=referral
http://blog.csdn.net/xuriwuyun/article/details/37497619
http://www.cnblogs.com/popsuper1982/p/3927390.html
http://blog.csdn.net/xuriwuyun/article/details/16845601
http://www.cnblogs.com/juandx/p/4953191.html
http://blog.csdn.net/bingxx11/article/details/19110101
附官方流程:
1. dashboard 或是 CLI 从用户处获得认证信息(用户名及密码),然后转成REST 的API 发送给 Keystone,并等待返回结果。Keystone会返回一个token, 然后用于后续的操作。
2. dashboard 或是 CLI 使用上步的token发送基于REST的创建Instance的命令给nova-api。
3. nova-api 使用token对操作的API进行权限认证。
4. nova-api 根据当前参数和数据据进行比较,检查冲突项。并且会在数据库中 建立一条记录
5. nova-api 发送RPC请求到queue, 交给调度器(Scheduler)处理
6. 调度器从queue里把消息取出来
7. 调度器根据各种Filter来进行筛选,从有效的主机中选出一个可用的主机。并且发送RPC请求到queue中。
8. 本地的主机从queue中取出RCP请求消息
9. nova-computer发送RPC请求给nova-conductor获取host相关的信息,并且更新数据库中instance的状态
10. nova-conductor取出消息
11. nova-conductor从数据库中取出信息并且返回给nova-computer
12. nova-computer发送REST请求给Glance, 用以获取Image的信息并且从Image的存储设备上下载对应的Image
13. Glance通过token去Kenstone进行认证,并且下载Image到Host
14. nova-computer发送REST请求给Neutron获取网络相关信息
15. Neutron通过token去Kenstone进行认证,并返回相关信息
16. nova-computer发送REST请求Cinder获取硬盘相关的信息
17. Cinder通过token去Kenstone进行认证,并返回相关信息
18. nova-computer生成相关的信息,然后调用Hyervisor去启动Instance