有了前面的一些基础之后,我们再来看一下,大家可能非常关系的一个问题,就是如何通过OpenStack创建一个虚拟机实例。本文注重结构性分析,细节性问题,请小伙伴们自己多思考撒,因为我也是正在边学Python,边看nova源码的,我的理解肯定会有错误的地方,也劳烦大神们,看到错误的时候,和小弟说一下,先谢了。
1、nova-api <文件位于/nova/api/openstackcompute/server.py>
nova-api起到了一个Cloud Controller的作用,主要为所有的API查询提供了一个接口(比如Openstack API ,EC2 API),引发多数业务流程的活动(如运行一个实例),并实施一些政策(主要是配额检查)。
因为今天我们主题是虚拟机实例的启动过程,所以,重点关注/nova/api/openstackcompute/server.py。找到类class Controller(wsgi.Controller):create()代码段,我们知道nova-api的作用就是对外提供标准REST接口的服务。下面的代码段,已经标注了很多内容,大家看一下即可。
- #对外提供创建虚拟机实例的外部接口,nova-api.create()
- #其中req是整个http报文内容,body就是REST中传递过来的参数
- #整个方法的作用是将REST接口参数映射到内部接口compute-api.create()
- #比如非常重要的环节,将image-flavor的id转换成虚拟机具体配置信息instanc_type
- def create(self, req, body):
- """Creates a new server for a given user."""
- if not self.is_valid_body(body, 'server'):
- raise exc.HTTPUnprocessableEntity()
- #A mapping object representing the string environment.
- #For example, environ['HOME'] is the pathname of your
- #home directory (on some platforms), and is equivalent to getenv("HOME") in C
- #对下面两个参数的讨论,设计到webob等概念,需要在以后做专门的讲述
- #*****************经常会用到的两个参数***************
- context = req.environ['nova.context']
- server_dict = body['server']
- #*****************经常会用到的两个参数***************
- (instances, resv_id) = self.compute_api.create(context,
- inst_type, ###*****已经转换的flavor
- image_uuid,
- display_name=name,
- display_description=name,
- key_name=key_name,
- metadata=server_dict.get('metadata', {}),
- access_ip_v4=access_ip_v4,
- access_ip_v6=access_ip_v6,
- injected_files=injected_files,
- admin_password=password,
- min_count=min_count,
- max_count=max_count,
- requested_networks=requested_networks,
- security_group=sg_names,
- user_data=user_data,
- availability_zone=availability_zone,
- config_drive=config_drive,
- block_device_mapping=block_device_mapping,
- auto_disk_config=auto_disk_config,
- scheduler_hints=scheduler_hints,
- legacy_bdm=legacy_bdm)
2、compute-api 的处理过程
compute-api的作用是对外提供了管理compute的api接口,外部模块通过这些接口完成对计算资源的操作。
- #*********************************#compute-api下面的create()函数*******************************#
- @hooks.add_hook("create_instance")
- def create(self, context, instance_type, #由/nova/api/compute/service.py的套餐id转换到这个type
- image_href, kernel_id=None, ramdisk_id=None,
- min_count=None, max_count=None,
- display_name=None, display_description=None,
- key_name=None, key_data=None, security_group=None,
- availability_zone=None, user_data=None, metadata=None,
- injected_files=None, admin_password=None,
- block_device_mapping=None, access_ip_v4=None,
- access_ip_v6=None, requested_networks=None, config_drive=None,
- auto_disk_config=None, scheduler_hints=None, legacy_bdm=True):
- """
- Provision instances, sending instance information to the
- scheduler. The scheduler will determine where the instance(s)
- go and will handle creating the DB entries.
- Returns a tuple of (instances, reservation_id)
- """
- #policy是nova中一个资格验证机制
- self._check_create_policies(context, availability_zone,
- requested_networks, block_device_mapping)
- #创建一个实例的函数,
- return self._create_instance(
- context, instance_type,
- image_href, kernel_id, ramdisk_id,
- min_count, max_count,
- display_name, display_description,
- key_name, key_data, security_group,
- 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,
- scheduler_hints=scheduler_hints,
- legacy_bdm=legacy_bdm)
我们看下上面的代码:a) 首先进行了能否创建实例的资格验证 b)再调用了_create_instance()方法
3、在_create_instance()方法中,做的一些操作有,验证各种参数,如意套餐类型instance_type是否存在,租户配额限制检查等,没问题后,commit一下,确定资源的占用。为了简化问题,我大概描述一下该方法做的事情,具体小伙伴们看一下代码就知道了。
完成上面的操作后,在_create_instance() 最后调用了 self.compute_task_api.build_instances(context,***)方法。
4、我们通过寻找compute_task_api,找到了这个其实conductor.ComputeTaskAPI()的一个对象。
注:关于conductor的话,我们单独还会再讲,G版中开始添加了这个conductor,以前的版本是没有这个东西的。主要作用是隔离compute对数据库的直接操作。
我们转到/nova/conductor/api.py文件下
- #创建实例的调用
- 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)
通过上述代码,我们看到,compute调用了conductor的API接口,再调用了conductor的RpcAPi接口,转到 /nova/conductor/rpcapi.py看如下代码:
- #创建虚拟机实例
- 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%2