接上一篇文章继续分析文件nova/api/openstack/compute/servers.py中类ServersController中的create()函数。
self._create_by_func_list(server_dict, create_kwargs, body)
availability_zone = create_kwargs.pop("availability_zone", None)
if api_version_request.is_supported(req, min_version='2.52'):
create_kwargs['tags'] = server_dict.get('tags')
helpers.translate_attributes(helpers.CREATE,
server_dict, create_kwargs)
target = {
'project_id': context.project_id,
'user_id': context.user_id,
'availability_zone': availability_zone}
context.can(server_policies.SERVERS % 'create', target)
# TODO(Shao He, Feng) move this policy check to os-availability-zone
# extension after refactor it.
parse_az = self.compute_api.parse_availability_zone
try:
availability_zone, host, node = parse_az(context,
availability_zone)
except exception.InvalidInput as err:
raise exc.HTTPBadRequest(explanation=six.text_type(err))
if host or node:
context.can(server_policies.SERVERS % 'create:forced_host', {})
min_compute_version = service_obj.get_minimum_version_all_cells(
nova_context.get_admin_context(), ['nova-compute'])
supports_device_tagging = (min_compute_version >=
DEVICE_TAGGING_MIN_COMPUTE_VERSION)
从create_kwargs中获取availability_zone,在本实例中,availability_zone位空。如果api的版本大于2.52,则从server中获取tags,在本实例中,也为空。
helpers.translate_attributes()函数继续从用户请求中提取变量,如access_ip_v4,函数在文件nova/api/openstack/compute/helpers.py中定义,在本实例中,改函数中提取的变量全都为空。
接下来定义变量target,其中包含project_id,user_id和availability_zone,在本实例中project_id的值为5043d96c15f541ba9deb29e3850bb458,user_id的值为0fdce610fa2544998cd340e90c2eda96,availability_zone为空。
context.can(server_policies.SERVERS % 'create', target),根据网上查阅的资料说是对用户的执行权限进行验证,server_policies.SERVERS的的定义在nova/policies/servers.py文件中,policies目录中的函数与policy策略相关,通过/etc/nova/policy.json中定义的规则来执行各种操作策略,即执行某个操作的用户需要具有什么样的身份,以下为文件policy.json中定义的两条规则,规定请求需求启动或关闭的用户必须是admin或是虚拟机的属主。
"os_compute_api:servers:start": "rule:admin_or_owner",
"os_compute_api:servers:stop": "rule:admin_or_owner",
接下来调用nova/compute/api.py文件中的parse_availability_zone()函数获取availability_zone, host, node,该函数的定义如下:
@staticmethod
def parse_availability_zone(context, availability_zone):
# NOTE(vish): We have a legacy hack to allow admins to specify hosts
# via az using az:host:node. It might be nice to expose an
# api to specify specific hosts to force onto, but for
# now it just supports this legacy hack.
# NOTE(deva): It is also possible to specify az::node, in which case
# the host manager will determine the correct host.
forced_host = None
forced_node = None
if availability_zone and ':' in availability_zone:
c = availability_zone.count(':')
if c == 1:
availability_zone, forced_host = availability_zone.split(':')
elif c == 2:
if '::' in availability_zone:
availability_zone, forced_node = \
availability_zone.split('::')
else:
availability_zone, forced_host, forced_node = \
availability_zone.split(':')
else:
raise exception.InvalidInput(
reason="Unable to parse availability_zone")
if not availability_zone:
availability_zone = CONF.default_schedule_zone
return availability_zone, forced_host, forced_node
在本实例中,availability_zone将被设置在配置文件中定义的默认值,host和node为空。
继续分析类ServersController中的create()函数,接下来的两行设置变量supports_device_tagging的值,暂时还不知道这个变量的作用,create()函数的后续代码如下:
block_device_mapping = create_kwargs.get("block_device_mapping")
# TODO(Shao He, Feng) move this policy check to os-block-device-mapping
# extension after refactor it.
if block_device_mapping:
context.can(server_policies.SERVERS % 'create:attach_volume',
target)
for bdm in block_device_mapping:
if bdm.get('tag', None) and not supports_device_tagging:
msg = _('Block device tags are not yet supported.')
raise exc.HTTPBadRequest(explanation=msg)
image_uuid = self._image_from_req_data(server_dict, create_kwargs)
从用户传递过来的参数中获取block_device_mapping的值,在本实例中为空,代码image_uuid = self._image_from_req_data(server_dict, create_kwargs)为获取image_uuid,在本实例中,该值为af711ee1-dacd-435b-9968-798bf7244bb0。继续分析ServersController中的create()函数中的代码。
# NOTE(cyeoh): Although upper layer can set the value of
# return_reservation_id in order to request that a reservation
# id be returned to the client instead of the newly created
# instance information we do not want to pass this parameter
# to the compute create call which always returns both. We use
# this flag after the instance create call to determine what
# to return to the client
return_reservation_id = create_kwargs.pop('return_reservation_id',
False)
requested_networks = server_dict.get('networks', None)
if requested_networks is not None:
requested_networks = self._get_requested_networks(
requested_networks, supports_device_tagging)
# Skip policy check for 'create:attach_network' if there is no
# network allocation request.
if requested_networks and len(requested_networks) and \
not requested_networks.no_allocate:
context.can(server_policies.SERVERS % 'create:attach_network',
target)
获取变量return_reservation_id和requested_networks的值,如果requested_networks不为空,则需要做权限验证,本实例中,这连个变量均为空。继续分析ServersController中的create()函数中的代码。
flavor_id = self._flavor_id_from_req_data(body)
try:
inst_type = flavors.get_flavor_by_flavor_id(
flavor_id, ctxt=context, read_deleted="no")
(instances, resv_id) = self.compute_api.create(context,
inst_type,
image_uuid,
display_name=name,
display_description=description,
availability_zone=availability_zone,
forced_host=host, forced_node=node,
metadata=server_dict.get('metadata', {}),
admin_password=password,
requested_networks=requested_networks,
check_server_group_quota=True,
**create_kwargs)
except (exception.QuotaError,
exception.PortLimitExceeded) as error:
raise exc.HTTPForbidden(
explanation=error.format_message())
except exception.ImageNotFound:
msg = _("Can not find requested image")
raise exc.HTTPBadRequest(explanation=msg)
except exception.KeypairNotFound:
msg = _("Invalid key_name provided.")
raise exc.HTTPBadRequest(explanation=msg)
except exception.ConfigDriveInvalidValue:
msg = _("Invalid config_drive provided.")
raise exc.HTTPBadRequest(explanation=msg)
except (exception.BootFromVolumeRequiredForZeroDiskFlavor,
exception.ExternalNetworkAttachForbidden) as error:
raise exc.HTTPForbidden(explanation=error.format_message())
except messaging.RemoteError as err:
msg = "%(err_type)s: %(err_msg)s" % {'err_type': err.exc_type,
'err_msg': err.value}
raise exc.HTTPBadRequest(explanation=msg)
except UnicodeDecodeError as error:
msg = "UnicodeError: %s" % error
raise exc.HTTPBadRequest(explanation=msg)
except (exception.CPUThreadPolicyConfigurationInvalid,
exception.ImageNotActive,
exception.ImageBadRequest,
exception.ImageNotAuthorized,
这段代码首先通过调用本类中的_flavor_id_from_req_data()获取变量flavor_id的值,在本实例中为2,并通过flavor_id获取flavor的内容,flavors.get_flavor_by_flavor_id在nova/compute/flavors.py文件中定义。flavor指定了虚拟机的配置,即配置几个虚拟CPU,多少内存容量,多少硬盘容量。
接下来调用nova/compute/api.py文件中的类API中的create()函数。在后续的文章中,将继续分析类API中的create()函数。