源码版本:H版

  以nova-api为起点开始分析!

一、在nova-api进程中进行处理

  根据对nova api的分析,当请求发过来的时候,由相应的Controller进行处理,此处如下:

nova/api/openstack/compute/servers.py

Controller类:
@wsgi.response(202)
@wsgi.serializers(xml=FullServerTemplate)
@wsgi.deserializers(xml=CreateDeserializer)
def create(self, req, body):
try:
_get_inst_type = flavors.get_flavor_by_flavor_id
inst_type = _get_inst_type(flavor_id, ctxt=context,
read_deleted="no") (instances, resv_id) = self.compute_api.create(context,
...
legacy_bdm=legacy_bdm)
...

  这里需要发现self.compute_api究竟为哪个类的对象。从Controller类的__init__函数中发现:

"""compute指nova.compute"""
self.compute_api = compute.API()

nova/compute/__init__.py

def API(*args, **kwargs):
importutils = nova.openstack.common.importutils
"""此处class_name为nova.compute.api.API"""
class_name = _get_compute_api_class_name()
"""创建nova.compute.api.API类对象并返回"""
return importutils.import_object(class_name, *args, **kwargs)

  所以self.compute_api即为nova.compute.api.API类的对象。

1、接着看self.compute_api的create函数调用

nova/compute/api.py

API类:
@hooks.add_hook("create_instance")
def create(self, context, instance_type,
...):
...
return self._create_instance(
...)
def _create_instance(self, context, instance_type,
image_href, kernel_id, ramdisk_id,
min_count, max_count,
display_name, display_description,
key_name, key_data, security_groups,
availability_zone, user_data, metadata,
injected_files, admin_password,
access_ip_v4, access_ip_v6,
requested_networks, config_drive,
block_device_mapping, auto_disk_config,
reservation_id=None, scheduler_hints=None,
legacy_bdm=True):
... self.compute_task_api.build_instances(context,
instances=instances, image=boot_meta,
filter_properties=filter_properties,
admin_password=admin_password,
injected_files=injected_files,
requested_networks=requested_networks,
security_groups=security_groups,
block_device_mapping=block_device_mapping,
legacy_bdm=False) return (instances, reservation_id)

  这里需要分析self.compute_task_api为哪个类的对象,代码分析如下:

nova/compute/api.py

API类:
@property
def compute_task_api(self):
if self._compute_task_api is None:
from nova import conductor
self._compute_task_api = conductor.ComputeTaskAPI()
return self._compute_task_api

nova/conductor/__init__.py

def ComputeTaskAPI(*args, **kwargs):
use_local = kwargs.pop('use_local', False)
  if oslo.config.cfg.CONF.conductor.use_local or use_local:
    api = conductor_api.LocalComputeTaskAPI
  else:
    """conductor_api为nova.conductor.api"""
    api = conductor_api.ComputeTaskAPI
  return api(*args, **kwargs)

  所以self.compute_task_api为nova.conductor.api.ComputeTaskAPI类的对象。

2、接着看self.compute_task_api的build_instances函数

nova/conductor/api.py

ComputeTaskAPI类:
def build_instances(self, context, instances, image, filter_properties,
admin_password, injected_files, requested_networks,
security_groups, block_device_mapping, legacy_bdm=True):
self.conductor_compute_rpcapi.build_instances(context,
instances=instances, image=image,
filter_properties=filter_properties,
admin_password=admin_password, injected_files=injected_files,
requested_networks=requested_networks,
security_groups=security_groups,
block_device_mapping=block_device_mapping,
legacy_bdm=legacy_bdm)

  其中,self.conductor_compute_rpcapi= rpcapi.ComputeTaskAPI() !

3、接着看self.conductor_compute_rpcapi的build_instances函数

nova/conductor/rpcapi.py

ComputeTaskAPI类:
def build_instances(self, context, instances, image, filter_properties,
admin_password, injected_files, requested_networks,
security_groups, block_device_mapping, legacy_bdm=True):
instances_p = [jsonutils.to_primitive(inst) for inst in instances]
image_p = jsonutils.to_primitive(image)
cctxt = self.client.prepare(version='1.5')
cctxt.cast(context, 'build_instances',
instances=instances_p, image=image_p,
filter_properties=filter_properties,
admin_password=admin_password,
injected_files=injected_files,
requested_networks=requested_networks,
security_groups=security_groups,
block_device_mapping=block_device_mapping,
legacy_bdm=legacy_bdm)

  其中self.client = self.get_client(namespace=self.RPC_API_NAMESPACE),即 cctxt = self.client.prepare(version='1.5')调用后cctxt为nova.rpcclient.RPCClient,根据http://www.cnblogs.com/littlebugfish/p/4058007.html中简述的消息队列调用原理,这里将转到nova-conductor进程中进行处理。

二、在nova-conductor进程中进行处理

nova/conductor/manager.py

ComputeTaskManager类:
def build_instances(self, context, instances, image, filter_properties,
admin_password, injected_files, requested_networks,
security_groups, block_device_mapping, legacy_bdm=True):
...
self.scheduler_rpcapi.run_instance(context, request_spec=request_spec,
admin_password=admin_password, injected_files=injected_files,
requested_networks=requested_networks, is_first_time=True,
filter_properties=filter_properties,
legacy_bdm_in_spec=legacy_bdm)

  其中,self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI(),而scheduler_rpcapi为nova.scheduler.rpcapi,则self.scheduler_rpcapi为nova.scheduler.rpcapi.SchedulerAPI类的对象,接着看代码如下:

nova/scheduler/rpcapi.py

SchedulerAPI类:
def run_instance(self, ctxt, request_spec, admin_password,
injected_files, requested_networks, is_first_time,
filter_properties, legacy_bdm_in_spec=True):
version = '2.0'
msg_kwargs = {'request_spec': request_spec,
'admin_password': admin_password,
'injected_files': injected_files,
'requested_networks': requested_networks,
'is_first_time': is_first_time,
'filter_properties': filter_properties}
if self.client.can_send_version('2.9'):
version = '2.9'
msg_kwargs['legacy_bdm_in_spec'] = legacy_bdm_in_spec
cctxt = self.client.prepare(version=version)
return cctxt.cast(ctxt, 'run_instance', **msg_kwargs)

  根据http://www.cnblogs.com/littlebugfish/p/4058007.html中简述的消息队列调用原理,这里将转到nova-scheduler进程中进行处理。

三、在nova-scheduler进程中进行处理

nova/scheduler/manager.py

SchedulerManager类:
def run_instance(self, context, request_spec, admin_password,
injected_files, requested_networks, is_first_time,
filter_properties, legacy_bdm_in_spec=True): instance_uuids = request_spec['instance_uuids']
with compute_utils.EventReporter(context, conductor_api.LocalAPI(),
'schedule', *instance_uuids):
try:
return self.driver.schedule_run_instance(context,
request_spec, admin_password, injected_files,
requested_networks, is_first_time, filter_properties,
legacy_bdm_in_spec) ...

  这里需要看self.driver究竟为哪个类的对象。在SchedulerManager类的__init__函数中,有如下代码:

if not scheduler_driver:
  scheduler_driver = CONF.scheduler_driver
self.driver = importutils.import_object(scheduler_driver)

  根据配置,CONF.scheduler_driver为nova.scheduler.filter_scheduler.FilterScheduler,所以self.driver为nova.scheduler.filter_scheduler.FilterScheduler类的对象,所以接下来看:

nova.scheduler.filter_scheduler.py

FilterScheduler类:
def schedule_run_instance(self, context, request_spec,
admin_password, injected_files,
requested_networks, is_first_time,
filter_properties, legacy_bdm_in_spec):
...
"""选用来创建虚拟机的主机"""
weighed_hosts = self._schedule(context, request_spec,
filter_properties, instance_uuids)
... for num, instance_uuid in enumerate(instance_uuids):
request_spec['instance_properties']['launch_index'] = num try:
try:
weighed_host = weighed_hosts.pop(0)
LOG.info(_("Choosing host %(weighed_host)s "
"for instance %(instance_uuid)s"),
{'weighed_host': weighed_host,
'instance_uuid': instance_uuid})
except IndexError:
raise exception.NoValidHost(reason="")
"""创建虚拟机"""
self._provision_resource(context, weighed_host,
request_spec,
filter_properties,
requested_networks,
injected_files, admin_password,
is_first_time,
instance_uuid=instance_uuid,
legacy_bdm_in_spec=legacy_bdm_in_spec)
except Exception as ex:
driver.handle_schedule_error(context, ex, instance_uuid,
request_spec)
retry = filter_properties.get('retry', {})
retry['hosts'] = []
self.notifier.info(context, 'scheduler.run_instance.end', payload) 

1、 选择用来创建虚拟机的主机,代码如下:

weighed_hosts = self._schedule(context, request_spec,
filter_properties, instance_uuids)

  接着看_schedule函数,如下:

def _schedule(self, context, request_spec, filter_properties,
instance_uuids=None):
...
hosts = self.host_manager.get_all_host_states(elevated) selected_hosts = []
if instance_uuids:
num_instances = len(instance_uuids)
else:
num_instances = request_spec.get('num_instances', 1)
for num in xrange(num_instances):
"""根据规则过滤掉特定的主机。例如可以设定忽略某些主机,这样就不会选择这些主机来创建虚拟机"""
hosts = self.host_manager.get_filtered_hosts(hosts,
filter_properties, index=num)
if not hosts:
break LOG.debug(_("Filtered %(hosts)s"), {'hosts': hosts})
"""根据规则为候选主机分配权重以便选取最佳的主机创建虚拟机,此处暂时不详述权重分配的过程"""
weighed_hosts = self.host_manager.get_weighed_hosts(hosts,
filter_properties) LOG.debug(_("Weighed %(hosts)s"), {'hosts': weighed_hosts}) scheduler_host_subset_size = CONF.scheduler_host_subset_size
if scheduler_host_subset_size > len(weighed_hosts):
scheduler_host_subset_size = len(weighed_hosts)
if scheduler_host_subset_size < 1:
scheduler_host_subset_size = 1 chosen_host = random.choice(
weighed_hosts[0:scheduler_host_subset_size])
selected_hosts.append(chosen_host)
chosen_host.obj.consume_from_instance(instance_properties)
if update_group_hosts is True:
filter_properties['group_hosts'].append(chosen_host.obj.host)
return selected_hosts 

2、开始创建虚拟机,代码如下:

self._provision_resource(context, weighed_host,
request_spec,
filter_properties,
requested_networks,
injected_files, admin_password,
is_first_time,
instance_uuid=instance_uuid,
legacy_bdm_in_spec=legacy_bdm_in_spec)

  接着看_provision_resource函数,如下:

def _provision_resource(self, context, weighed_host, request_spec,
filter_properties, requested_networks, injected_files,
admin_password, is_first_time, instance_uuid=None,
legacy_bdm_in_spec=True):
...
try:
"""更新数据库"""
updated_instance = driver.instance_update_db(context,
instance_uuid, extra_values=values) except exception.InstanceNotFound:
LOG.warning(_("Instance disappeared during scheduling"),
context=context, instance_uuid=instance_uuid) else:
scheduler_utils.populate_filter_properties(filter_properties,
weighed_host.obj) """这里self.compute_rpcapi为 nova.compute.rpcapi.ComputeAPI对象"""
self.compute_rpcapi.run_instance(context,
instance=updated_instance,
host=weighed_host.obj.host,
request_spec=request_spec,
filter_properties=filter_properties,
requested_networks=requested_networks,
injected_files=injected_files,
admin_password=admin_password, is_first_time=is_first_time,
node=weighed_host.obj.nodename,
legacy_bdm_in_spec=legacy_bdm_in_spec)

nova/compute/rpcapi.py

ComputeAPI类:
def run_instance(self, ctxt, instance, host, request_spec,
filter_properties, requested_networks,
injected_files, admin_password,
is_first_time, node=None, legacy_bdm_in_spec=True):
instance_p = jsonutils.to_primitive(instance)
msg_kwargs = {'instance': instance_p, 'request_spec': request_spec,
'filter_properties': filter_properties,
'requested_networks': requested_networks,
'injected_files': injected_files,
'admin_password': admin_password,
'is_first_time': is_first_time, 'node': node} if _icehouse_compat() or self.client.can_send_version('2.37'):
version = _get_version('2.37')
msg_kwargs['legacy_bdm_in_spec'] = legacy_bdm_in_spec
else:
version = '2.19'
cctxt = self.client.prepare(server=host,
version=version)
cctxt.cast(ctxt, 'run_instance', **msg_kwargs)

  根据http://www.cnblogs.com/littlebugfish/p/4058007.html中简述的消息队列调用原理,这里将转到nova-compute进程中进行处理。

四、在nova-compute进程中进行处理

nova/compute/manager.py

ComputeManager类:
@wrap_exception()
@reverts_task_state
@wrap_instance_event
@wrap_instance_fault
def run_instance(self, context, instance, request_spec=None,
filter_properties=None, requested_networks=None,
injected_files=None, admin_password=None,
is_first_time=False, node=None, legacy_bdm_in_spec=True): if filter_properties is None:
filter_properties = {} @utils.synchronized(instance['uuid'])
def do_run_instance():
self._run_instance(context, request_spec,
filter_properties, requested_networks, injected_files,
admin_password, is_first_time, node, instance,
legacy_bdm_in_spec)
do_run_instance()
def _run_instance(self, context, request_spec,
filter_properties, requested_networks, injected_files,
admin_password, is_first_time, node, instance,
legacy_bdm_in_spec):
... 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)
notify("end", msg=_("Success"), network_info=network_info)
...
def _build_instance(self, context, request_spec, filter_properties,
requested_networks, injected_files, admin_password, is_first_time,
node, instance, image_meta, legacy_bdm_in_spec):
context = context.elevated() ...
try:
limits = filter_properties.get('limits', {})
with rt.instance_claim(context, instance, limits):
... instance = self._spawn(context, instance, image_meta,
network_info, block_device_info,
injected_files, admin_password,
set_access_ip=set_access_ip)
... # spawn success
return instance, network_info
def _spawn(self, context, instance, image_meta, network_info,
block_device_info, injected_files, admin_password,
set_access_ip=False):
"""Spawn an instance with error logging and update its power state."""
instance = self._instance_update(context, instance['uuid'],
vm_state=vm_states.BUILDING,
task_state=task_states.SPAWNING,
expected_task_state=task_states.BLOCK_DEVICE_MAPPING)
try:
self.driver.spawn(context, instance, image_meta,
injected_files, admin_password,
network_info,
block_device_info)
...
return self._instance_update(context, instance['uuid'],
**update_data)
  这里将调用相应的Driver对象进行虚拟机的创建,此处以LibvirtDriver类对象为例,后续处理过程可以查看:http://www.cnblogs.com/littlebugfish/p/4058115.html中的分析!

openstack虚拟机启动过程源码分析的更多相关文章

  1. Android系统默认Home应用程序(Launcher)的启动过程源码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还须要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  2. Android Content Provider的启动过程源码分析

    本文參考Android应用程序组件Content Provider的启动过程源码分析http://blog.csdn.net/luoshengyang/article/details/6963418和 ...

  3. Flume-NG启动过程源码分析(二)(原创)

    在上一节中讲解了——Flume-NG启动过程源码分析(一)(原创)  本节分析配置文件的解析,即PollingPropertiesFileConfigurationProvider.FileWatch ...

  4. 10.4 android输入系统_框架、编写一个万能模拟输入驱动程序、reader/dispatcher线程启动过程源码分析

    1. 输入系统框架 android输入系统官方文档 // 需FQhttp://source.android.com/devices/input/index.html <深入理解Android 卷 ...

  5. Spark(五十一):Spark On YARN(Yarn-Cluster模式)启动流程源码分析(二)

    上篇<Spark(四十九):Spark On YARN启动流程源码分析(一)>我们讲到启动SparkContext初始化,ApplicationMaster启动资源中,讲解的内容明显不完整 ...

  6. Spark(四十九):Spark On YARN启动流程源码分析(一)

    引导: 该篇章主要讲解执行spark-submit.sh提交到将任务提交给Yarn阶段代码分析. spark-submit的入口函数 一般提交一个spark作业的方式采用spark-submit来提交 ...

  7. Activity启动过程源码分析(Android 8.0)

    Activity启动过程源码分析 本文来Activity的启动流程,一般我们都是通过startActivity或startActivityForResult来启动目标activity,那么我们就由此出 ...

  8. Netty入门一:服务端应用搭建 & 启动过程源码分析

    最近周末也没啥事就学学Netty,同时打算写一些博客记录一下(写的过程理解更加深刻了) 本文主要从三个方法来呈现:Netty核心组件简介.Netty服务端创建.Netty启动过程源码分析 如果你对Ne ...

  9. Spring启动过程源码分析基本概念

    Spring启动过程源码分析基本概念 本文是通过AnnotationConfigApplicationContext读取配置类来一步一步去了解Spring的启动过程. 在看源码之前,我们要知道某些类的 ...

随机推荐

  1. 实验一linux 系统简介和实验二基本概念及操作

    作业 zy e

  2. java 第一次实验报告

    北京电子科技学院(BESTI) 实     验    报     告 课程:Java程序设计 班级:1353  姓名:黎静  学号:20135338 成绩:             指导教师:娄嘉鹏  ...

  3. Sprint10

    进展:设置事件提醒部分已经完成,接下来是实现完成后在添加主界面显示已添加的事件及时间,并可设置可用与不可用.

  4. spring冲刺第四天

    昨天进行了地图的初步编写,但是存在BUG. 今天上网查找了错误的原因,改进了源代码,使程序可以执行. 遇到的问题;感觉地图界面太简单,需要作出更多的场景,这就需要不断的完善.

  5. Do~Hamburger~

    在上一次的结对编程中,我的结对队友是 方俊杰 ,大家都称他为“JJ师兄”. 我们两个彼此在合作中发现错误并在合作中一起进步. First(汉堡上层面包):     JJ他的JAVA功底比我扎实很多,所 ...

  6. 第三章 深入Servlet技术

    3.1 配置Servlet <servlet-name>,<servlet-class>是必须配置的,以便于web容器知道浏览器具体访问的是哪个servlet. <ini ...

  7. [转帖]认识固态:SSD硬盘内外结构解析

    认识固态:SSD硬盘内外结构解析 来自: 中关村在线 收藏 分享 邀请 固态硬盘(Solid State Drive),简称固态盘(SSD),是用固态电子存储芯片阵列而制成的硬盘,由控制单元和存储单元 ...

  8. 在MFC中显示图片(opencv Mat类型)

    1,在MFC窗体中添加picture control控件,并添加对应的变量名 2,在窗体的初始化窗口中添加: namedWindow(); HWND hWnd = (HWND)cvGetWindowH ...

  9. 【大数据】MapTask工作机制

    1.MapTask工作机制 整个map阶段流程大体如上图所示.简单概述:input File通过getSplits被逻辑切分为多个split文件,通通过RecordReader(默认使用lineRec ...

  10. [bzoj1875][SDOI2009] HH去散步 [dp+矩阵快速幂]

    题面 传送门 正文 其实就是让你求有多少条长度为t的路径,但是有一个特殊条件:不能走过一条边以后又立刻反着走一次(如果两次经过同意条边中间隔了别的边是可以的) 如果没有这个特殊条件,我们很容易想到dp ...