Openstack liberty源码分析 之 云主机的启动过程1
2016-05-24 11:34
501 查看
接触Openstack也有一段时间了,因为工作需要着重阅读了Glance、Nova、Cinder模块源码并通过搭建的devstack测试环境调试学习相关操作的执行流程。现准备陆续将相关的学习成果和心得在博客中分享出来,希望对读者有所帮助。这是第一篇 - 云主机启动过程源码分析 - 介绍云主机启动时,接口调用在nova-api、nova-conductor、nova-sechduler及nova-compute等组件上的流转过程。
既可以通过Dashboard启动云主机,也可以通过Nova CLI命令行实现,下面的命令启动了一台类型为2(
根据
我们知道启动云主机的请求经过
来看看body参数的内容:
body参数是该次请求的请求体,显示的是待启动云主机的配置参数,包含有:云主机名、
验证输入参数
获取镜像元信息
生成主机配置并创建主机实例对象
在数据库生成主机信息
下面具体来看看该方法的实现(说明见红色注释部分):
接着上面的分析,
至此,
既可以通过Dashboard启动云主机,也可以通过Nova CLI命令行实现,下面的命令启动了一台类型为2(
--flavor 2)名为vm2的云主机,使用的镜像是:
226bc6e5-60d7-4a2c-bf0d-a568a1e26e00,
--debug参数用打开调试信息:
nova --debug boot --flavor 2 --image 226bc6e5-60d7-4a2c-bf0d-a568a1e26e00 vm2
nova-api
启动云主机过程中,nova-api主要完成输入参数的验证和装配,之后根据输入参数创建云主机实例对象、记录数据库条目,最后将请求转发给
nova-conductor进行后续处理。简单分析如下:
根据
nova-api启动过程中建立的路由映射以及调试信息的佐证,如下:
我们知道启动云主机的请求经过
novaclient的处理后,发送给了nova-api,并由
nova/api/openstack/servers.py.ServersController.create处理,从代码上看该方法的处理过程比较简单,主要是完成:用户参数的转换、policy验证,方法声明如下:
@wsgi.response(202) @extensions.expected_errors((400, 403, 409, 413)) @validation.schema(schema_server_create_v20, '2.0', '2.0') @validation.schema(schema_server_create, '2.1') def create(self,req,body):
来看看body参数的内容:
body参数是该次请求的请求体,显示的是待启动云主机的配置参数,包含有:云主机名、
image uuid,云主机类型等,很明显这就是之前在
nova boot命令行中指定的参数,之后根据
extension扩展方法转换用户参数,完成参数解析,policy认证等,之后将请求转发给
nova/compute/api.py.API.create。
API.create方法虽然参数很多,但启动云主机必须的参数只有
instance_type及
image_href,分别指定主机类型及image uuid,处理逻辑也非常的简单:执行网络及块设备policy检查(如果有),接着将参数原封不动的传递给
_create_instance方法,该方法主要完成如下工作:
验证输入参数
获取镜像元信息
生成主机配置并创建主机实例对象
在数据库生成主机信息
下面具体来看看该方法的实现(说明见红色注释部分):
//nova/compute/api.py.API 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, shutdown_terminate=False, check_server_group_quota=False): """Verify all the input parameters regardless of the provisioning strategy being performed and schedule the instance(s) for creation. """ # Normalize and setup some parameters if reservation_id is None: reservation_id = utils.generate_uid('r') security_groups = security_groups or ['default'] min_count = min_count or 1 max_count = max_count or min_count block_device_mapping = block_device_mapping or [] if not instance_type: instance_type = flavors.get_default_flavor() #根据uuid获取image的metadata信息 if image_href: image_id, boot_meta = self._get_image(context, image_href) else: image_id = None boot_meta = self._get_bdm_image_metadata( context, block_device_mapping, legacy_bdm) self._check_auto_disk_config(image=boot_meta, auto_disk_config=auto_disk_config) handle_az = self._handle_availability_zone availability_zone, forced_host, forced_node = handle_az(context, availability_zone) if not self.skip_policy_check and (forced_host or forced_node): check_policy(context, 'create:forced_host', {}) #根据输入参数,生成主机配置 base_options, max_net_count = self._validate_and_build_base_options( context, instance_type, boot_meta, image_href, image_id, kernel_id, ramdisk_id, display_name, display_description, key_name, key_data, security_groups, availability_zone, forced_host, user_data, metadata, access_ip_v4, access_ip_v6, requested_networks, config_drive, auto_disk_config, reservation_id, max_count) # max_net_count is the maximum number of instances requested by the # user adjusted for any network quota constraints, including # consideration of connections to each requested network if max_net_count == 0: raise exception.PortLimitExceeded() elif max_net_count < max_count: LOG.debug("max count reduced from %(max_count)d to " "%(max_net_count)d due to network port quota", {'max_count': max_count, 'max_net_count': max_net_count}) max_count = max_net_count #归总用户输入(--block-device-mapping参数)、镜像属性中指定、flavor配置中指定的块设备映射 block_device_mapping = self._check_and_transform_bdm(context, base_options, instance_type, boot_meta, min_count, max_count, block_device_mapping, legacy_bdm) # We can't do this check earlier because we need bdms from all sources # to have been merged in order to get the root bdm. self._checks_for_create_and_rebuild(context, image_id, boot_meta, instance_type, metadata, injected_files, block_device_mapping.root_bdm()) instance_group = self._get_requested_instance_group(context, scheduler_hints, check_server_group_quota) #创建云主机实例对象,并生成数据库条目 instances = self._provision_instances(context, instance_type, min_count, max_count, base_options, boot_meta, security_groups, block_device_mapping, shutdown_terminate, instance_group, check_server_group_quota) #scheduler需要用的过滤选项 filter_properties = self._build_filter_properties(context, scheduler_hints, forced_host, forced_node, instance_type) #更新数据库中实例的启动状态 for instance in instances: self._record_action_start(context, instance, instance_actions.CREATE) #调用conductor api,通过conductor rpc将请求转发给conductor manager 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)
接着上面的分析,
nova/conductor/api.py.ComputeTaskAPI.build_instance直接将请求转发给
nova/conductor/rpcapi.py.ComputeTaskAPI.build_instance,来看看该函数的实现(请根据注解阅读):
//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): ''' 该函数的逻辑清晰简单:根据rpc client的版本,调整云主机参数:主机过滤参数,网络参数,块设备映射参数,最后通过一个异步rpc调用,将请求转发给conductor-manager ''' image_p = jsonutils.to_primitive(image) version = '1.10' if not self.client.can_send_version(version): version = '1.9' if 'instance_type' in filter_properties: flavor = filter_properties['instance_type'] flavor_p = objects_base.obj_to_primitive(flavor) filter_properties = dict(filter_properties, instance_type=flavor_p) kw = {'instances': instances, 'image': image_p, 'filter_properties': filter_properties, 'admin_password': admin_password, 'injected_files': injected_files, 'requested_networks': requested_networks, 'security_groups': security_groups} if not self.client.can_send_version(version): version = '1.8' kw['requested_networks'] = kw['requested_networks'].as_tuples() if not self.client.can_send_version('1.7'): version = '1.5' bdm_p = objects_base.obj_to_primitive(block_device_mapping) kw.update({'block_device_mapping': bdm_p, 'legacy_bdm': legacy_bdm}) #返回_CallContext对象 cctxt = self.client.prepare(version=version) #发送异步rpc消息到消息队列,conductor-manager会收到该消息 cctxt.cast(context, 'build_instances', **kw)
至此,
nova-api的任务就完成了,下一篇文章分析conductor-manage的处理过程。
相关文章推荐
- 从源码安装Mysql/Percona 5.5
- openstack kilo-with-dokcer
- 什么是OpenStack 开源的云计算管理平台项目
- 浅析Ruby的源代码布局及其编程风格
- asp.net 抓取网页源码三种实现方法
- JS小游戏之仙剑翻牌源码详解
- JS小游戏之宇宙战机源码详解
- jQuery源码分析之jQuery中的循环技巧详解
- 本人自用的global.js库源码分享
- java中原码、反码与补码的问题分析
- ASP.NET使用HttpWebRequest读取远程网页源代码
- PHP网页游戏学习之Xnova(ogame)源码解读(六)
- C#获取网页HTML源码实例
- PHP网页游戏学习之Xnova(ogame)源码解读(八)
- PHP网页游戏学习之Xnova(ogame)源码解读(四)
- JS小游戏之极速快跑源码详解
- JS小游戏之象棋暗棋源码详解
- android源码探索之定制android关机界面的方法
- 基于Android设计模式之--SDK源码之策略模式的详解
- Android游戏源码分享之2048