本小节,将以虚拟机创建请求为例,分析底层Controller类的HTTP请求处理方法。
1、处理HTTP请求的核心工作都在底层Controller对象中定义。Resource对象在底层Controller对象的基础上,实现
数据的转化工作。
2、Resource对象首相将传入的对象反序列化。然后将数据交给底层Controller对象中相应的处理方法处理。最后将
底层Controller对象处理方法返回的结果序列化。
1、以创建虚拟机请求为例,分析底层controller对象的定义。底层controller类中,处理虚拟机创建请求的方法是
create方法。
2、servers.Controller类的create方法
每个资源都对应一个底层的Controller类。servers资源的底层Controller类定义在servers包中。处理虚拟机创建
请求的方法为create方法。定义如下:
class Controller(wsgi.Controller):
_view_builder_class = views_servers.ViewBuilder
@wsgi.response(202)
@wsgi.serialozers(xml=FullServerTemplate
@wsgi.deserialozers(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']
...
#获取和检查虚拟机名
name = server_dict['name']
self._validate_server_name(name)
name = name.strip()
#获取虚拟机的磁盘镜像uuid
image_uuid = self._image_from_req_data(body)
....
# 获取客户端需求的网络
requested_networks = self._determine_requested_networks(server_dict)
#获取和验证客户端指定的虚拟机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
flavor_id = self._flavor_id_from_req_data(body)
# optional openstack extensions:
key_name = self._extract(server_dict, 'os-keypairs', 'key_name')
availability_zone = self._extract(server_dict, 'os-availability-zone',
'availability_zone')
user_data = self._extract(server_dict, 'os-user-data', 'user_data')
self._validate_user_data(user_data)
image_uuid_specified = bool(image_uuid)
legacy_bdm, block_device_mapping = self._extract_bdm(server_dict,
image_uuid_specified)
ret_resv_id = False
# min_count and max_count are optional. If they exist, they may come
# in as strings. Verify that they are valid integers and > 0.
# Also, we want to default 'min_count' to 1, and default
# 'max_count' to be 'min_count'.
min_count = 1
max_count = 1
if self.ext_mgr.is_loaded('os-multiple-create'):
ret_resv_id = server_dict.get('return_reservation_id', False)
min_count = server_dict.get('min_count', 1)
max_count = server_dict.get('max_count', min_count)
try:
min_count = utils.validate_integer(
min_count, "min_count", min_value=1)
max_count = utils.validate_integer(
max_count, "max_count", min_value=1)
except exception.InvalidInput as e:
raise exc.HTTPBadRequest(explanation=e.format_message())
if min_count > max_count:
msg = _('min_count must be <= max_count')
raise exc.HTTPBadRequest(explanation=msg)
auto_disk_config = False
if self.ext_mgr.is_loaded('OS-DCF'):
auto_disk_config = server_dict.get('auto_disk_config')
scheduler_hints = {}
if self.ext_mgr.is_loaded('OS-SCH-HNT'):
scheduler_hints = server_dict.get('scheduler_hints', {})
check_server_group_quota = self.ext_mgr.is_loaded(
'os-server-group-quotas')
try:
#获取虚拟机规格信息
_get_inst_type = flavors.get_flavor_by_flavor_id
inst_type = _get_inst_type(flavor_id, ctxt=context,
read_deleted="no")
#调用compute API创建虚拟机
(instances, resv_id) = self.compute_api.create(context,
...)
...
except UnicodeDecodeError as error:
...
#将虚拟机信息转化为字典
server = self._view_builder.create(req, instances[0])
if CONF.enable_instance_password:
server['server']['adminPass'] = password
#将虚拟机信息封装成ResponseObject对象
robj = wsgi.ResponseObject(server)
#添加访问当前虚拟机资源的url
return self._add_location(robj)
(1)、这个类继承自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
该方法检查客户端传入的消息体是否合法,这个方法位于Controller类的基类wsgi.Controller中。is_valid_body方
法首先检查消息体中是否含有server字段,然后检查server字段的内容是否为字典。
Nova中,所有的消息(包括客户端传给Nova API服务器的消息,以及服务器返回给客户端的消息)都是以资源名作为
键值的{key:value}对。
接下来的一系列方法都是获取并验证客户端传入的虚拟机参数。其中,server_dict保存了虚拟机参数,包括虚拟机
名、镜像uuid、需要的网络、固定ip、虚拟机规格等等。
(3) _validate_server_name方法
该方法检查虚拟机名的长度是否越界。
(4) _validate_access_ipv4&_validate_access_ipv6方法
该方法验证传入的固定ip格式是否合法。
(5) _favor_id_from_req_data方法
该方法对HTTP请求中包含的虚拟机规格id进行过滤解析。所谓规格id就是url包含的id或者uuid的值,如
http://www.foo.com/bar/123?q=4,该方法则返回123。
(6) 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,从数据库中查找对应规格信息后返回。
(7) 调用Nova Compute的API处理虚拟机创建请求。
__init__方法:self.compute_api = compute.API()
Compute API的create方法会返回一个创建虚拟机信息列表。列表中的每个元素都是数据库Model对象。数据库
Model对象是Nova与数据库交互的数据格式,不可序列化。
为了便于显示以及序列化,self._view_builder.create对象的create方法将Model对象的数据转换为字典。
(8) Nova中,每个底层的Controller对象的_view_builder对象类型都是由_view_builder_class类成员变量指定。
对于server资源的底层Controller对象,其_view_builder_class类成员变量的值为views_servers.ViewBuilder:
_view_builder_class = views_servers.ViewBuilder.
class ViewBuilder(common.ViewBuilder):
def create(self, request, instance):
"""View that should be returned when an instance is created."""
return {
"server": {
"id": instance["uuid"],
"links": self._get_links(request,
instance["uuid"],
self._collection_name),
},
}
Nova API中底层Controller对象定义的HTTP请求的处理方法的小结。
1、检查客户端传入的参数是否合法有效
2、调用nova其他子服务的API处理客户端的HTTP请求。
3、将Nova其他子服务的API返回结果转化为可视化的字典。(反序列化)