一、nova组件
nova是OpenStack中的计算组织控制器,Nova主要负责Computer模块,就是调度管理虚拟机的创建、启动、删除等。
- nova-api:接收和响应来自最终用户的计算API请求。检查客户端传入的参数是否合法有效、调用 nova 其他子服务的处理客户端 HTTP 请求、格式化 nova 其他子服务返回的结果并返回给客户端。
- nova-computer:nova组件中最核心的服务,通过调用 Hypervisor API 实现虚机生命周期管理。
- nova-scheduler:请求的调度器,决定在哪台计算服务主机运行实例。
- nova-conductor:提供数据库查询功能,隔离了nova直接访问数据库。
- nova-network: 网络控制器处理主机的网络配置,例如IP地址分配,配置项目VLAN,设定安全群组以及为计算节点配置网络。
- Rabbit MQ:OpenStack 默认的消息队列,nova对请求应答进行异步调用,接收请求后立即响应,不会让用户处于等待状态。
二、OpenStack创建虚拟机的流程
1.客户端调用nova-api发起创建虚拟机的请求,携带用户登录后返回的token信息。
2.nova-api接受请求后向keystone发送认证请求,查看token是否为有效用户和token。
3.验证成功后才可以发送创建虚拟机的请求。
4.然后nova-api和数据库连接。
5.nova-api把创建虚拟机的请求放到 消息队列 里边。
6.消息队列 通知nova-scheduler选择可供创建虚拟机的host(主机)
7.nova-scheduler 提供可创建虚拟机的host,把消息放置到 消息队列 里边。
8. 消息队列 把创建虚拟机的消息给nova-compute。
9.nova-compute从 消息队列 中获取创建虚拟机请求的信息。
10.nova-compute把查询虚拟机信息的请求放到 消息队列 里边去。
11.nova-conductor从 消息队列 里边拿到nova-compute的请求信息。
12. nova-conductor根据消息查询虚拟机对应的信息。
13. nova-conductor从数据库中获取虚拟机对应的信息。
14. nova-conductor把虚拟机信息 放到 消息队列 里边。
15. nova-compute 从 消息队列 里边获取虚拟机信息的信息。
16. nova-compute请求glance-api获取创建虚拟机所需要的镜像。glance-api 会验证token信息。
17.nova-compute请求neutron-server获取创建虚拟机所需要的网络信息。neutron-server会验证token信息。
18.nova-compute请求cinder-api获取创建虚拟机所需要的持久化存储信息。cinder-api会验证token信息。
19. nova-compute 根据instance信息,对虚拟机进行创建。
Instance信息是nova-scheduler调度nova-compute的run_instance方法,对虚拟机的信息配置。
虚拟机的创建源码解读
# nova/api/openstack/computer/servers.py
class ServersController(wsgi.Controller):
/****...../
# 创建的方法
def create(self, req, body):
"""Creates a new server for a given user."""
# 首先是参数的提取,然后进行检查
context = req.environ['nova.context']
# server_dict保存要创建的虚拟机的信息,包括镜像,网络等
server_dict = body['server']
password = self._get_server_admin_password(server_dict)
name = common.normalize_name(server_dict['name'])
description = name
if api_version_request.is_supported(req, min_version='2.19'):
description = server_dict.get('description')
..../
try:
# 通过flavor_id获取到flavor信息
inst_type = flavors.get_flavor_by_flavor_id(
flavor_id, ctxt=context, read_deleted="no")
supports_multiattach = common.supports_multiattach_volume(req)
supports_port_resource_request = \
common.supports_port_resource_request(req)
#调用compute_api 传入参数 创建虚机
(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,
supports_multiattach=supports_multiattach,
supports_port_resource_request=supports_port_resource_request,
**create_kwargs)
except (exception.QuotaError,exception.PortLimitExceeded) as error:
raise exc.HTTPForbidden(explanation=error.format_message())
# 对异常的处理
.../
compute_api.create(...) 创建虚拟机
# nova/computer/api.py
@profiler.trace_cls("compute_api")
class API(base.Base):
"""API for interacting with the compute manager."""
# 把创建虚拟机所用的各个api 进行实例化
def __init__(self, image_api=None, network_api=None, volume_api=None,
security_group_api=None, **kwargs):
self.image_api = image_api or image.API()
self.network_api = network_api or network.API()
self.volume_api = volume_api or cinder.API()
self._placementclient = None # Lazy-load on first access.
self.security_group_api = (security_group_api or
openstack_driver.get_openstack_security_group_driver())
self.consoleauth_rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
self.compute_task_api = conductor.ComputeTaskAPI()
self.servicegroup_api = servicegroup.API()
self.notifier = rpc.get_notifier('compute', CONF.host)
if CONF.ephemeral_storage_encryption.enabled:
self.key_manager = key_manager.API()
# Help us to record host in EventReporter
self.host = CONF.host
super(API, self).__init__(**kwargs)
# 创建虚拟机前对参数的验证
@hooks.add_hook("create_instance")
def create(self, context, instance_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_groups=None,
availability_zone=None, forced_host=None, forced_node=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, shutdown_terminate=False,
check_server_group_quota=False, tags=None,
supports_multiattach=False, trusted_certs=None,
supports_port_resource_request=False):
# 参数的验证
/****...../
# 调用方法_create_instance(...)传入创建虚拟机的参数
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_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,
filter_properties=filter_properties,
legacy_bdm=legacy_bdm,
shutdown_terminate=shutdown_terminate,
check_server_group_quota=check_server_group_quota,
tags=tags, supports_multiattach=supports_multiattach,
trusted_certs=trusted_certs,
supports_port_resource_request=supports_port_resource_request)
# 创建虚机的方法
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, filter_properties,
reservation_id=None, legacy_bdm=True, shutdown_terminate=False,
check_server_group_quota=False, tags=None,
supports_multiattach=False, trusted_certs=None,
supports_port_resource_request=False):
# 参数的验证检查
/**...../
# 获取到镜像image信息
if image_href:
image_id, boot_meta = self._get_image(context, image_href)
else:
# This is similar to the logic in _retrieve_trusted_certs_object.
if (trusted_certs or
(CONF.glance.verify_glance_signatures and
CONF.glance.enable_certificate_validation and
CONF.glance.default_trusted_certificate_ids)):
msg = _("Image certificate validation is not supported "
"when booting from volume")
raise exception.CertificateValidationFailed(message=msg)
image_id = None
boot_meta = self._get_bdm_image_metadata(
context, block_device_mapping, legacy_bdm)
# 参数的验证检查
/****...... /
if CONF.cells.enable:
for instance in instances:
instance.create()
# 检查实例的配额
if CONF.quota.recheck_quota:
try:
# 当前实例数量是否已到最大值
compute_utils.check_num_instances_quota(
context, instance_type, 0, 0,
orig_num_req=len(instances))
except exception.TooManyInstances:
with excutils.save_and_reraise_exception():
# Need to clean up all the instances we created
# along with the build requests, request specs,
# and instance mappings.
self._cleanup_build_artifacts(instances,
instances_to_build)
# 把消息放到队列中
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)
else:
self.compute_task_api.schedule_and_build_instances(
context,
build_requests=build_requests,
request_spec=request_specs,
image=boot_meta,
admin_password=admin_password,
injected_files=injected_files,
requested_networks=requested_networks,
block_device_mapping=block_device_mapping,
tags=tags)
return instances, reservation_id
compute_task_api.build_instances(...)/compute_task_api.schedule_and_build_instances(...)消息放入队列中
# 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,
request_spec=None, host_lists=None):
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, request_spec=request_spec,
host_lists=host_lists)
def schedule_and_build_instances(self, context, build_requests,
request_spec, image,
admin_password, injected_files,
requested_networks, block_device_mapping,
tags=None):
self.conductor_compute_rpcapi.schedule_and_build_instances(
context, build_requests, request_spec, image,
admin_password, injected_files, requested_networks,
block_device_mapping, tags)
使用rpc api以消息队列通信
# nova/conductor/rpc.api
def build_instances(self, context, instances, image, filter_properties,
admin_password, injected_files, requested_networks,
security_groups, block_device_mapping, legacy_bdm=True,
request_spec=None, host_lists=None):
image_p = jsonutils.to_primitive(image)
kwargs = {"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,
"request_spec": request_spec,
"host_lists": host_lists}
# api版本的检查
version = '1.19'
if not self.client.can_send_version(version):
version = '1.18'
kwargs.pop("host_lists")
..../
cctxt = self.client.prepare(version=version)
cctxt.cast(context, 'build_instances', **kwargs)
def schedule_and_build_instances(self, context, build_requests,
request_specs,
image, admin_password, injected_files,
requested_networks,
block_device_mapping,
tags=None):
version = '1.17'
kw = {'build_requests': build_requests,
'request_specs': request_specs,
'image': jsonutils.to_primitive(image),
'admin_password': admin_password,
'injected_files': injected_files,
'requested_networks': requested_networks,
'block_device_mapping': block_device_mapping,
'tags': tags}
# api版本的检查
if not self.client.can_send_version(version):
version = '1.16'
del kw['tags']
# 发送消息到消息队列
cctxt = self.client.prepare(version=version)
cctxt.cast(context, 'schedule_and_build_instances', **kw)