上篇文章主要介绍了Nova API的启动流程,这篇文章我们来分析一下一个虚拟机的创建产生过程是怎样的。
由前一篇文章的分析,我们知道,处理HTTP请求的核心工作实际上都在底层的Controller对象中定义完成。Resource对象只是在Controller的基础上完成的封装,用来实现数据的序列化和反序列化。Controller类中,处理虚拟机创建请求的方法是create,该方法最终调用了Compute API类的create方法。来看代码,先来看Controller类,这个类位于nova/nova/api/openstack/compute/servers.py中。
class Controller(wsgi.Controller):
_view_builder_class = views_servers.ViewBuilder
...
@wsgi.response(202)
@wsgi.serializers(xml=FullServerTemplate)
@wsgi.deserializers(xml=CreateDeserializer)
def create(self,req,body):
#检查HTTP消息是否合法
if not self.is_valid_body(body, 'server'):
raise exc.HTTPUnprocessableEntity()
#获取客户端传入的虚拟机参数
context = req.environ['nova.context']
server_dict = body['server']
password = self._get_server_admin_password(server_dict)
#获取和检查虚拟机名
if not 'name' in server_dict:
msg = _("Server name is not defined")
raise exc.HTTPBadRequest(explanation=msg)
name = server_dict['name']
self._validate_server_name(name)
name = name.strip()
#获取虚拟机磁盘镜像uuid
image_uuid = self._image_from_req_data(body)
...
#获取客户端需求的网络
requested_networks = None
if (self.ext_mgr.is_loaded('os-networks') or self._is_quantum_v2()):
requested_networks = server_dict.get('networks')
if requested_networks is not None:
requested_networks = self._get_requested_networks(
requested_networks)
#获取和验证客户端指定的虚拟机IP
(access_ip_v4, ) = server_dict.get('accessIPv4'),
if access_ip_v4 is not None:
self._validate_access_ipv4(access_ip_v4)
(access_ip_v6, ) = server_dict.get('accessIPv6'),
if access_ip_v6 is not None:
self._validate_access_ipv6(access_ip_v6)
#获取虚拟机规格id
try:
flavor_id = self._flavor_id_from_req_data(body)
except ValueError as error:
...
#获取虚拟机规格信息
try:
_get_inst_type = instance_types.get_instance_type_by_flavor_id
inst_type = _get_inst_type(flavor_id, read_deleted="no")
#调用compute API创建虚拟机
(instances, resv_id) = self.compute_api.create(context,
...)
except exception.QuotaError as error:
...
#将虚拟机信息转化为字典
server = self._view_builder.create(req,instances[0])
#将虚拟机信息封装成ResponseObject对象
robj = wsgi.ResponseObject(server)
#添加访问当前虚拟机资源的url
return self._add_location(robj)
这个类继承自wsgi.Controller,该类位于nova/nova/api/openstack/wsgi.py。首先为create方法指定了序列化对象、反序列化对象和默认的HTTP Code。接着我们进入create方法,来分析代码。
先来看看context = req.eviron['nova.context']究竟拿到了什么信息,这个上下文信息对象是由类nova.context.RequestContext实例化而来。我们来把获取到的context打印出来,这样方便大家理解。
user_id = afc380206e2549ad930396d9050d20cf
project_id = 0e492e86f22e4d19bd523f1e7ca64566
roles = [u'admin', u'KeystoneAdmin', u'KeystoneServiceAdmin']
read_deleted = no
remote_address = 172.21.6.145
timestamp = 2013-06-23 16:36:37.399405
request_id = req-f0255b14-833d-4fff-b973-23c35f70ddda
auth_token = 7e7bb3cf84ab43269010bb55410064b3
service_catalog = [{u'endpoints': [{u'adminURL': u'http://172.21.5.161:8776/v1/0e492e86f22e4d19bd523f1e7ca64566',
u'region': u'RegionOne', u'id': u'753a1ad55e91469794e2eb7ac4c3df92',
u'internalURL': u'http://172.21.5.161:8776/v1/0e492e86f22e4d19bd523f1e7ca64566',
u'publicURL': u'http://172.21.6.145:8776/v1/0e492e86f22e4d19bd523f1e7ca64566'}],
u'endpoints_links': [], u'type': u'volume', u'name': u'cinder'}]
instance_lock_checked = False
quota_class = None
user_name = admin
project_name = admin
is_admin = True
后面我们来逐个分析其中的方法。
(1) is_valid_body方法
该方法检查客户端传入的消息体是否合法,这个方法位于Controller类的基类wsgi.Controller中。is_valid_body方法首先检查消息体中是否含有server字段,然后检查server字段的内容是否为字典。
接下来的一系列方法都是获取并验证客户端传入的虚拟机参数。其中,server_dict保存了虚拟机参数,包括虚拟机名、镜像uuid、需要的网络、固定ip、虚拟机规格等等。
(2) _validate_server_name方法
该方法检查虚拟机名的长度是否越界。
(3) _validate_access_ipv4&_validate_access_ipv6方法
该方法验证传入的固定ip格式是否合法。
(4) _favor_id_from_req_data方法
该方法对HTTP请求中包含的虚拟机规格id进行过滤解析。所谓规格id就是url包含的id或者uuid的值,如http://www.foo.com/bar/123?q=4,该方法则返回123。
(5) get_instance_type_get_by_flavor_id方法
该方法位于nova/nova/compute/instance_types.py中,该方法调用了nova/nova/db/api.py下的instance_type_get_by_flavor_id方法,其功能是根据虚拟机规格id,从数据库中查找对应信息后返回。在openstack中,与数据库的交互用的是sqlalchemy这个东东,我目前也不是很熟悉,先放一放吧,后面我再专门分析。
再接下来调用了Nova Compute的API处理虚拟机的创建请求。Compute API的create方法会返回一个创建虚拟机的列表。重点分析一下这个方法。
(6) compute_api.create方法
class API(base.Base):
def create(self,context,instance_type,
...):
#检查客户端是否具有创建虚拟机的权限
self._check_create_policies(context, availability_zone,
requested_networks, block_device_mapping)
#进一步处理创建虚拟机的请求
return self._create_instance(context,intance_type,
...)
其中_check_create_policies方法的作用是检查客户端是否有创建虚拟机的权限,该方法会通过查询policy.json文件来查看用户权限,其内部逻辑和Keystone服务的权限管理类似。由于这里只是谈虚拟机的创建过程,所以关于Keystone就不在展开详谈。
接下来就是我们的重头戏了——_create_instance方法。这个方法就是执行进一步的虚拟机的创建过程,后面的文章我会花大篇幅来详细分析。
注:这里我们为了便于理解,再次打印出传入这个方法的参数实例
context = <nova.context.RequestContext object at 0x447d050>
instance_type = {'memory_mb': 2048L, 'root_gb': 20L, 'deleted_at': None, 'name': u'm1.small',
'deleted': 0L, 'created_at': None, 'ephemeral_gb': 0L, 'updated_at': None,
'disabled': False, 'vcpus': 1L, 'extra_specs': {}, 'swap': 0L, 'rxtx_factor': 1.0,
'is_public': True, 'flavorid': u'2', 'vcpu_weight': None, 'id': 5L}
image_href = 20612b24-c980-4900-b270-8e6b66e5f72f
kernel_id = None
ramdisk_id = None
min_count = 1
max_count = 1
display_name = test3
display_description = test3
key_name = oskey
key_data = None
security_group = ['default']
availability_zone = None
user_data = None
metadata = {}
injected_files = []
admin_password = Piu4aSSNmSNk
block_device_mapping = []
access_ip_v4 = None
access_ip_v6 = None
requested_networks = None
config_drive = None
auto_disk_config = None
scheduler_hints = {}