前言
本文暂不分析novaclient的认证部分,从nova-api开始。
认证部分会单独拿出来。
这一模块先叙述整个流程,再分析每个小点
代码为Rocky版本
nova-api
传入数据举例
req :
Accept: application/json
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 357
Content-Type: application/json
Host: 10.0.17.18:8774
Openstack-Api-Version: compute 2.42
Openstack-System-Scope: None
User-Agent: python-novaclient
X-Auth-Token: gAAAAABd3JbKyiNmPMJtBcxrTTIvAQjwS7rWULZt1Q_W1lnL9Zzw_NRxmJL-H01oB8Rd2VXH85wvoBsqj7vmjtVTycnPm2SZpjmo_ecetQ3UvjF1YfCrSg_Fg5xhVeS3nyDmKrsahfa4sTKqwde-ODWFKcssvFx7DLmx974BtMnAK3EUStpsyGqr8pkiLhu7RZFmgA0edoZP
X-Identity-Status: Confirmed
X-Is-Admin-Project: True
X-Openstack-Nova-Api-Version: 2.42
X-Project-Domain-Id: ee7b71848f5546b4891e42555c34cc02
X-Project-Domain-Name: Default
X-Project-Id: 0658f0902f8e4c26a07fcfa6d4e4139
X-Project-Name: admin
X-Role: admin^M
X-Roles: admin
X-Service-Catalog: [{"endpoints": [{"adminURL": "http://10.0.17.18:8774/v2.1/0658f0902f8e4c26a07fcfa6d4e41392", "region": "RegionOne", "internalURL": "http://10.0.17.18:8774/v2.1/0658f0902f8e4c26a07fcfa6d4e41392", "publicURL": "http://10.0.17.18:8774/v2.1/0658f0902f8e4c26a07fcfa6d4e41392"}], "type": "compute", "name": "nova"}, {"endpoints": [{"adminURL": "http://10.0.17.18:9292", "region": "RegionOne", "internalURL": "http://10.0.17.18:9292", "publicURL": "http://10.0.17.18:9292"}], "type": "image", "name": "glance"}, {"endpoints": [{"adminURL": "http://10.0.17.18:8000/v1", "region": "RegionOne", "internalURL": "http://10.0.17.18:8000/v1", "publicURL": "http://10.0.17.18:8000/v1"}], "type": "cloudformation", "name": "heat-cfn"}, {"endpoints": [{"adminURL": "http://10.0.17.18:8004/v1/0658f0902f8e4c26a07fcfa6d4e41392", "region": "RegionOne", "internalURL": "http://10.0.17.18:8004/v1/0658f0902f8e4c26a07fcfa6d4e41392", "publicURL": "http://10.0.17.18:8004/v1/0658f0902f8e4c26a07fcfa6d4e41392"}], "type": "orchestration", "name": "heat"}, {"endpoints": [{"adminURL": "http://10.0.17.18:8778/placement", "region": "RegionOne", "internalURL": "http://10.0.17.18:8778/placement", "publicURL": "http://10.0.17.18:8778/placement"}], "type": "placement", "name": "placement"}, {"endpoints": [{"adminURL": "http://10.0.17.18:35357/v3", "region": "RegionOne", "internalURL": "http://10.0.17.18:5000/v3", "publicURL": "http://10.0.17.18:5000/v3"}], "type": "identity", "name": "keystone"}, {"endpoints": [{"adminURL": "http://10.0.17.18:9696", "region": "RegionOne", "internalURL": "http://10.0.17.18:9696", "publicURL": "http://10.0.17.18:9696"}], "type": "network", "name": "neutron"}]
X-Tenant: admin
X-Tenant-Id: 0658f0902f8e4c26a07fcfa6d4e41392
X-Tenant-Name: admin
X-User: admin
X-User-Domain-Id: ee7b71848f5546b4891e42555c34cc02
X-User-Domain-Name: Default
X-User-Id: 2811459285f0442cb61ffe2b2c7b6038
X-User-Name: admin
{"server": {"name": "vn1", "imageRef": "eecf21c3-2e01-4a70-bf41-7aec79d7ff04", "availability_zone": "nova", "flavorRef": "96610372-0609-40c5-b123-56b801c98931", "OS-DCF:diskConfig": "AUTO", "max_count": 1, "min_count": 1, "networks": [{"uuid": "8f1e7990-e727-426c-8bbd-d234c5de58db"}], "security_groups": [{"name": "ee9628b1-8445-445f-8eaf-de3ae65b7831"}]}}
body:
{u'server': {u'name': u'vn1', u'imageRef': u'eecf21c3-2e01-4a70-bf41-7aec79d7ff04', u'availability_zone': u'nova', u'flavorRef': u'96610372-0609-40c5-b123-56b801c98931',
u'OS-DCF:diskConfig': u'AUTO', u'max_count': 1, u'min_count': 1, u'networks': [{u'uuid': u'8f1e7990-e727-426c-8bbd-d234c5de58db'}], u'security_groups': [{u'name': u'ee9628b1-8445-445f-8eaf-de3ae65b7831'}]}}
nova.api.openstack.compute.servers
class ServersController(wsgi.Controller):
def create(self, req, body):
......(数据解析)
(instances, resv_id) = self.compute_api.create(context,
inst_type, # flavor详细信息
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,
**create_kwargs)
#{user_data, key_name(密钥对名称),安全组列表,
#scheduler hint,min_count, max_count
#return_reservation_id, auto_disk_config:AUTO,
#access_ip_v4 or access_ip_v6, injected_files(personality),
#trusted_certs(trusted_image_certificates), block_device_mapping, legacy_bdm}
nova_compute API
上文中self.computa_api;当nova配置中cell_type不是api时为nova.compute.api.API
nova.compute.api.API
def create():
......
#判断所选网络,可用域是否可用
#生成filter_properties {u'ProcessorArchitecture': u'x86', u'InstructionSet': u'x86:64',
#'instance_type': flavor,'force_hosts': 可用域主机, 'force_nodes':可用域node}
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)
def _create_instance(self, context, instance_type,***)
......
# 检查镜像,磁盘,安全组,block_device_mapping,
self.compute_task_api.schedule_and_build_instances(context,
build_requests=build_requests,***)
return instances, reservation_id
conductor
nova.conductor.api.ComputeTaskAPI
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)
nova.conductor.rpcapi.ComputeTaskAPI
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}
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)
# 发cast请求
nova.conductor.manager.ComputeTaskManager
def schedule_and_build_instances(self, context, build_requests,
request_specs, image,
admin_password, injected_files,
requested_networks, block_device_mapping,
tags=None):
# 获取主机列表等
......
with obj_target_cell(instance, cell) as cctxt:
self.compute_rpcapi.build_and_run_instance(
cctxt, instance=instance, image=image,
request_spec=request_spec,
filter_properties=filter_props,
admin_password=admin_password,
injected_files=injected_files,
requested_networks=requested_networks,
security_groups=legacy_secgroups,
block_device_mapping=instance_bdms,
host=host.service_host, node=host.nodename,
limits=host.limits, host_list=host_list)
nova.compute.rpcapi.ComputeAPI
def build_and_run_instance(self, ctxt, instance, host, image, request_spec,
filter_properties, admin_password=None, injected_files=None,
requested_networks=None, security_groups=None,
block_device_mapping=None, node=None, limits=None,
host_list=None):
# NOTE(edleafe): compute nodes can only use the dict form of limits.
if isinstance(limits, objects.SchedulerLimits):
limits = limits.to_dict()
kwargs = {"instance": instance,
"image": image,
"request_spec": request_spec,
"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,
"node": node,
"limits": limits,
"host_list": host_list,
}
client = self.router.client(ctxt)
version = '5.0'
cctxt = client.prepare(server=host, version=version)
cctxt.cast(ctxt, 'build_and_run_instance', **kwargs)
nova.compute.manager.ComputeManager
def build_and_run_instance(self, context, instance, image, request_spec,
filter_properties, admin_password=None,
injected_files=None, requested_networks=None,
security_groups=None, block_device_mapping=None,
node=None, limits=None, host_list=None):
@utils.synchronized(instance.uuid)
def _locked_do_build_and_run_instance(*args, **kwargs):
with self._build_semaphore:
try:
result = self._do_build_and_run_instance(*args, **kwargs)
except Exception:
result = build_results.FAILED
raise
finally:
if result == build_results.FAILED:
self._delete_allocation_for_instance(context,
instance.uuid)
if result in (build_results.FAILED,
build_results.RESCHEDULED):
self._build_failed(node)
else:
self._build_succeeded(node)
utils.spawn_n(_locked_do_build_and_run_instance,
context, instance, image, request_spec,
filter_properties, admin_password, injected_files,
requested_networks, security_groups,
block_device_mapping, node, limits, host_list)
# 其中调用_do_build_and_run_instance
with timeutils.StopWatch() as timer:
self._build_and_run_instance(context, instance, image,
decoded_files, admin_password, requested_networks,
security_groups, block_device_mapping, node, limits,
filter_properties, request_spec)
LOG.info('Took %0.2f seconds to build instance.',
timer.elapsed(), instance=instance)
return build_results.ACTIVE
#其中_build_and_run_instance
with timeutils.StopWatch() as timer:
self.driver.spawn(context, instance, image_meta,
injected_files, admin_password,
allocs, network_info=network_info,
block_device_info=block_device_info)
其中self.driver调用driver(libvirt.LibvirtDriver)来处理
libvirt
# 调driver去实现
nova.virt.libvirt.driver.LibvirtDriver
def spawn(self, context, instance, image_meta, injected_files,
admin_password, allocations, network_info=None,
block_device_info=None):
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
instance,
image_meta,
block_device_info)
injection_info = InjectionInfo(network_info=network_info,
files=injected_files,
admin_pass=admin_password)
gen_confdrive = functools.partial(self._create_configdrive,
context, instance,
injection_info)
self._create_image(context, instance, disk_info['mapping'],
injection_info=injection_info,
block_device_info=block_device_info)
# Required by Quobyte CI
self._ensure_console_log_for_instance(instance)
# Does the guest need to be assigned some vGPU mediated devices ?
mdevs = self._allocate_mdevs(allocations)
xml = self._get_guest_xml(context, instance, network_info,
disk_info, image_meta,
block_device_info=block_device_info,
mdevs=mdevs)
self._create_domain_and_network(
context, xml, instance, network_info,
block_device_info=block_device_info,
post_xml_callback=gen_confdrive,
destroy_disks_on_failure=True)
LOG.debug("Guest created on hypervisor", instance=instance)
def _wait_for_boot():
"""Called at an interval until the VM is running."""
state = self.get_info(instance).state
if state == power_state.RUNNING:
LOG.info("Instance spawned successfully.", instance=instance)
raise loopingcall.LoopingCallDone()
timer = loopingcall.FixedIntervalLoopingCall(_wait_for_boot)
timer.start(interval=0.5).wait()
后记
代码全过程,由于较多,后续一一解读。