openstack虚拟机创建流程
- novaclient<->keystone
novaclient
通过API
向keystone
获取认证信息keystone
通过用户请求认证信息,并生成auth-token
返回给对应的认证请求
- novaclient<->nova-api
novaclient
通过API
向nova-api
发送http post create servers
请求
- nova-api<->keystone
nova-api
接受请求后向keystone
发送认证请求,查看token是否有效keystone
验证token是否有效,将结果返回给nova-api
- nova-api<->db
nova-api
和db通信,初始化新建虚拟机的数据库记录
- nova-api<->nova-scheduler
nova-api
调用rpc
,向nova-scheduler
请求是否创建虚拟机资源nova-scheduler
通过rpc
,获取nova-api
请求
- nova-scheduler<->db
nova-scheduler
查询db
中计算资源的情况,通过filter和weight调度算法决定hostnova-scheduler
更新数据库中虚拟机对应的物理主机信息
- nova-scheduler<->nova-compute
nova-scheduler
通过rpc
调用向nova-compute
发送对应的创建虚拟机请求的消息nova-compute
通过rpc
获取nova-scheduler
创建虚拟机请求的消息
- nova-compute<->nova-conductor
nova-compute
通过rpc
调用向nova-conductor
请求获取虚拟机消息nova-conductor
通过rpc
获取nova-compute
消息
- nova-conductor<->db
nova-conductor
查询db
中虚拟机信息
- nova-conductor<->nova-compute
nova-conductor
调用rpc
发送虚拟机信息nova-compute
通过rpc
获取nova-conductor
信息
- nova-compute<->glance
nova-compute
请求glance-api
获取镜像imageglance-api
向keystone
获取token认证keystone
返回验证nova-compute
获取镜像信息(URL)
- nova-compute<->neutron
nova-compute
请求neutron-server
获取网络信息neutron-server
向keystone
获取token认证keystone
返回验证nova-compute
获得虚拟机网络信息
- nova-compute<->cinder
nova-compute
请求cinder-api
获取卷信息cinder-api
向keystone
获取token认证keystone
返回验证nova-compute
获得虚拟机持久化卷存储信息
- nova-compute
nova-compute
调用虚拟化驱动创建虚拟机- build instance
Create server code listing example
#!/usr/bin/env python
import time
from credentials import get_nova_credentials_v2
from novaclient.client import Client
try:
credentials = get_nova_credentials_v2()
nova_client = Client(**credentials)
image = nova_client.images.find(name="cirros")
flavor = nova_client.flavors.find(name="m1.tiny")
net = nova_client.networks.find(label="private")
nics = [{'net-id': net.id}]
instance = nova_client.servers.create(name="vm2", image=image,
flavor=flavor, key_name="keypair-1", nics=nics)
print("Sleeping for 5s after create command")
time.sleep(5)
print("List of VMs")
print(nova_client.servers.list())
finally:
print("Execution Completed")
novaclient
- novaclient
- v2
- client.py
- service.py
- base.py
- client.py
- shell.py
- v2
创建虚拟机一般是从界面或命令行发出请求开始的
两种方式都会借助novaclient向nova-api发出HTTP请求
novaclient会向Keystone发送用户的用户名、密码、域名信息来申请一个有效的token
然后向nova-api发送带有token信息的请求
/shell.py
# 实际调用client
self.cs = client.Client()
args.func(self.cs, args)
client
/client.py
# HTTP服务和keystone验证
# keystone的keystoneauth1:通过keystone验证token
class SessionClient()
def _construct_http_client()
# 基于version,初始化client
# version:1.1、2、2.x
def Client(version, username=None, password=None, project_id=None,
auth_url=None, **kwargs):
#数据验证
api_version, client_class = _get_client_class_and_version(version)
return client_class(api_version=api_version,
auth_url=auth_url,
direct_use=False,
username=username,
**kwargs)
client调用novaclient.v2.client.Client
/client.py
def _get_client_class_and_version(version):
return version, importutils.import_class(
"novaclient.v%s.client.Client" % version.ver_major)
/v2/client.py
class Client(object):
def __init__():
self.services = services.ServiceManager(self)
#调用client._construct_http_client
#返回SessionClient类
client.server.create
/v2/servers.py
class ServerManager(base.BootingManagerWithFind):
def _boot():
return self._create('/servers', body, response_key,
return_raw=return_raw, **kwargs)
def create():
return self._boot(response_key, *boot_args, **boot_kwargs)
/base.py
通过父类寻找create方法
class Manager(HookableMixin):
def _create(self, url, body, response_key, return_raw=False,
obj_class=None, **kwargs):
resp, body = self.api.client.post(url, body=body)
总结:novaclient实例化Client调用create最终nova-api调用post方法
nova
官方文档:https://docs.openstack.org/api-ref/compute/?expanded=create-server-detail#create-server
POST
/servers
Create Server
- nova
- api
- openstack
- compute
- routes.py
- servers.py
- compute
- openstack
- compute
- __init__.py
- api.py
- rpcapi.py
- conductor
- __init__.py
- api.py
- manager.py
- rpcapi.py
- virt
- libvirt
- __init__.py
- driver.py
- driver.py
- libvirt
- api
nova-api
调用nova-api的post方法
nova-api收到请求后会向Keystone验证token的有效性,token验证通过后,nova-api会向数据库写入虚拟机的初始数据
nova.api.openstack.compute.servers.ServersController::create()
/nova/api/openstack/compute/routes.py
server_controller = functools.partial(_create_controller,
servers.ServersController,
[,]
)
ROUTE_LIST = (
('/servers', {
'GET': [server_controller, 'index'],
'POST': [server_controller, 'create']
}),
)
/nova/api/openstack/compute/servers.py
class ServersController(wsgi.Controller):
def __init__(self, **kwargs):
self.compute_api = compute.API()
def create(self, req, body):
(instances, resv_id) = self.compute_api.create()
nova.compute.api.API::create()
/nova/compute/__init__.py
#通过配置文件导入nova.compute.api.API
CELL_TYPE_TO_CLS_NAME = {'api': 'nova.compute.cells_api.ComputeCellsAPI',
'compute': 'nova.compute.api.API',
None: 'nova.compute.api.API',
}
def API(*args, **kwargs)
/nova/compute/api.py
@profiler.trace_cls("compute_api")
class API(base.Base):
#API for interacting with the compute manager
def __init__():
self.compute_task_api = conductor.ComputeTaskAPI()
#检查信息
#发送实例信息给scheduler
#scheduler决定host和db
@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):
#返回:(instances, reservation_id)
return self._create_instance()
def _create_instance():
#根据配置文件选择不同创建实例方法
self.compute_task_api.build_instances()
self.compute_task_api.schedule_and_build_instances()
调用conductor API
/nova/conductor/__init__.py
from nova.conductor import api as conductor_api
API = conductor_api.API
ComputeTaskAPI = conductor_api.ComputeTaskAPI
/nova/conductor/api.py
class ComputeTaskAPI(object):
"""ComputeTask API that queues up compute tasks for nova-conductor."""
def __init__(self):
self.conductor_compute_rpcapi = rpcapi.ComputeTaskAPI()
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)
通过rpc调用nova-conduct·or
nova-conductor是nova-compute和数据库之间的桥梁,它可以防止nova-compute直接访问数据库,从而提高对数据库访问的安全性
topic publisher在消息队列中发布topic
RPC_TOPIC = ‘conductor’
method = build_instances
等待topic consumer接收并调用nova-conductor manager中的build_instance方法
/nova/conductor/rpcapi.py
@profiler.trace_cls("rpc")
class ComputeTaskAPI(object):
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):
#异步消息队列
#RPC_TOPIC = 'conductor'
cctxt = self.client.prepare(version=version)
cctxt.cast(context, 'build_instances', **kwargs)
/nova/conductor/manager.py
@profiler.trace_cls("rpc")
class ComputeTaskManager(base.Base):
def __init__(self):
#nova-scheduler
self.query_client = query.SchedulerQueryClient()
self.report_client = report.SchedulerReportClient()
#nova-compute
self.compute_rpcapi = compute_rpcapi.ComputeAPI()
def build_instances(self, context, instances, image, filter_properties,
admin_password, injected_files, requested_networks,
security_groups, block_device_mapping=None, legacy_bdm=True,
request_spec=None, host_lists=None):
host_lists = self._schedule_instances(context, spec_obj,
instance_uuids, return_alternates=True)
self.compute_rpcapi.build_and_run_instance(context,
instance=instance, host=host.service_host, image=image,
request_spec=local_reqspec,
filter_properties=local_filter_props,
admin_password=admin_password,
injected_files=injected_files,
requested_networks=requested_networks,
security_groups=security_groups,
block_device_mapping=bdms, node=host.nodename,
limits=host.limits, host_list=host_list)
def _schedule_instances(self, context, request_spec,
instance_uuids=None, return_alternates=False):
host_lists = self.query_client.select_destinations(
context, request_spec, instance_uuids, return_objects=True,
return_alternates=return_alternates)
通过rpc调用nova-scheduler
nova-conductor收到请求后首先以rpc.call的方式请求nova-scheduler完成虚拟机创建的调度工作
nova-scheduler使用过滤和权重计算的方法来选定创建虚拟机的主机,过滤器和权重计算方法可以在nova.conf中配置
nova-scheduler自己会维护一份计算节点数据,并在数据库中对比
调度完成后将选定的host返回给nova-conductor,由nova-conductor向nova-compute发起rpc.cast的创建请求
/nova/scheduler/client/query.py
class SchedulerQueryClient(object):
def select_destinations():
return self.scheduler_rpcapi.select_destinations()
/nova/scheduler/rpcapi.py
@profiler.trace_cls("rpc")
class SchedulerAPI(object):
def select_destinations():
# rpc call
return cctxt.call(ctxt, 'select_destinations', **msg_args)
/nova/scheduler/manager.py
@messaging.expected_exceptions(exception.NoValidHost)
def select_destinations(self, ctxt, request_spec=None,
filter_properties=None, spec_obj=_sentinel, instance_uuids=None,
return_objects=False, return_alternates=False):
#filter过滤
#过滤通用接口返回list,queens版本前后不同
return selections
通过rpc调用nova-compute
topic publisher在消息队列中发布topic
RPC_TOPIC = ‘compute’
method = build_and_run_instance
等待topic consumer接收并调用nova-compute manager中的build_and_run_instance方法
/nova/compute/rpcapi.py
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):
#异步消息队列
#RPC_TOPIC = "compute"
cctxt = client.prepare(server=host, version=version)
cctxt.cast(ctxt, 'build_and_run_instance', **kwargs)
/nova/compute/manager.py
class ComputeManager(manager.Manager):
"""Manages the running instances from creation to destruction."""
def __init__():
self.driver = driver.load_compute_driver(self.virtapi, compute_driver)
def build_and_run_instance()
def _do_build_and_run_instance()
#vm_states.BUILDING
def _build_and_run_instance(self, context, instance, image, injected_files,
admin_password, requested_networks, security_groups,
block_device_mapping, node, limits, filter_properties,
request_spec=None):
self._build_resources():
#调用neutronclient,通过neutron-server发起创建网络请求
#network_info:neutronclient
#标记task_states
#task_states.BLOCK_DEVICE_MAPPING
# --Hypervisor--
self.driver.spawn(context, instance, image_meta,
injected_files, admin_password,
allocs, network_info=network_info,
block_device_info=block_device_info)
#task_states.SPAWNING
#block_device_info/network_info/allocs
此时虚拟机的vm_state为Building,虚拟机还只存在于数据库中,最后调用Hypervisor来完成创建
Hypervisor
OpenStack支持的Hypervisor包括Libvirt、hyperv、xen、vmware等,其中对libvirt管理的KVM的支持性最好
OpenStack默认的hypervisor:libvirt
寻找libvirt.LibvirtDriver
/nova/virt/driver.py
#nova.virt.%s' % compute_driver
#compute_driver = CONF.compute_driver
nova/virt/libvirt/__init__.py
from nova.virt.libvirt import driver
LibvirtDriver = driver.LibvirtDriver
driver.LibvirtDriver.spawn
/nova/virt/libvirt/driver.py
class LibvirtDriver(driver.ComputeDriver):
def spawn(self, context, instance, image_meta, injected_files,
admin_password, allocations, network_info=None,
block_device_info=None):
#disk_info/injection_info/gen_confdrive
#下载image
self._create_image()
#生成xml文件
self._get_guest_xml()
#写入_create_domain_and_network
#传入host
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)
#间隔检测虚拟机状态:state == power_state.RUNNING
#spawn调用
def _create_domain_and_network(self, context, xml, instance, network_info,
block_device_info=None, power_on=True,
vifs_already_plugged=False,
post_xml_callback=None,
destroy_disks_on_failure=False,
external_events=None):
self.plug_vifs(instance, network_info)
guest = self._create_domain(
xml, pause=pause, power_on=power_on,
post_xml_callback=post_xml_callback)
#_create_domain_and_network调用
def plug_vifs(self, instance, network_info):
#根据network_info中的vif添加网络
#_create_domain_and_network调用
def _create_domain(self, xml=None, domain=None,
power_on=True, pause=False, post_xml_callback=None):
#传入domain和xml时,xml覆盖domain
libvirt_guest.Guest.create(xml, self._host)
guest = libvirt_guest.Guest(domain)
#guest控制虚拟机状态power_on/pause
libvirt_guest.Guest.create
/nova/virt/libvirt/guest.py
/nova/virt/libvirt/guest.py
#create_domain调用
class Guest(object):
@classmethod
def create(cls, xml, host):
# xml写入
guest = host.write_instance_config(xml)
/nova/virt/libvirt/host.py
/nova/virt/libvirt/host.py
class Host(object):
def write_instance_config(self, xml):
#创建一个虚拟机,但未启动
domain = self.get_connection().defineXML(xml)
return libvirt_guest.Guest(domain)
def get_connection()
def _get_connection()
def _get_new_connection()
def _connect(uri, read_only):
#返回一个libvirt连接
return tpool.proxy_call(
(libvirt.virDomain, libvirt.virConnect),
libvirt.openAuth, uri, auth, flags)
总结:调用libvirt执行创建请求,下载image,生成xml文件,并根据xml创建网络、domain,直到虚拟机状态为running则创建成功