这是openStack请求应答模型(源自could man)
一.nova-api接收到外部请求,并处理转发请求
代码路径 :nova-stable-queens ——> nova ——> compute——> api.py
然后 snapshot 函数开始接收请求,并进行处理(函数很长,分步解释)
1.准备处理请求
#创建快照
def snapshot(self, context, instance, name, extra_properties=None):
"""Snapshot the given instance.
2.先创建新的镜像。这个新的镜像将保留给compute manager用来上载快照或备份
image_meta = self._create_image(context, instance, name,
'snapshot',
extra_properties=extra_properties)
3.将instance.task_state的状态设置为penddging状态(这个状态码后面要用到,它标志着操作进行到什么程度,完成各个子操作状态码会变更,方便判断以及异常处理)
instance.task_state = task_states.IMAGE_SNAPSHOT_PENDING
4.调用 nova-stable-queens ——> nova ——> compute ——> rpcapi.py 里的snapshot_instance函数,(并且将刚才创建的那个新镜像的id,也就是 image_meta['id'] 作为参数传入)
self.compute_rpcapi.snapshot_instance(context, instance,
image_meta['id'])
5.打开 rpcapi.py找到 snapshot_instance 函数,snapshot_instance 函数被api.py的snapshot函数调用,用于创建快照
def snapshot_instance(self, ctxt, instance, image_id):
version = self._ver(ctxt, '4.0')
6.实例化一个router用来传递消息(正如文首的模型图所示,nova-api是通过消息队列来异步发消息的),向compute发出具体需要执行的操作
cctxt = self.router.client(ctxt).prepare(
server=_compute_host(None, instance), version=version)
7.通过名为cctxt的router,调用cast,发送message
cctxt.cast(ctxt, 'snapshot_instance',
instance=instance,
image_id=image_id)
8.cast函数调用proxy_rpc_to_manager函数,通知manager
def cast(self, ctxt, method, **kwargs):
msg = self._make_msg(method, **kwargs)
topic = self._get_topic()
self.cells_rpcapi.proxy_rpc_to_manager(ctxt, msg, topic)
9.proxy_rpc_to_manager函数
def proxy_rpc_to_manager(self, message, host_name, rpc_message,
topic, timeout):
"""Proxy RPC to the given compute topic."""
# Check that the host exists.
objects.Service.get_by_compute_host(message.ctxt, host_name)
topic, _sep, server = topic.partition('.')
cctxt = rpc.get_client(messaging.Target(topic=topic,
server=server or None))
补充:
1.真正的snapshot操作是需要在nova-compute节点上执行的,所以nova-api需要向nova-compute发送message,由于OpenStack环境中会有很多个nova-compute,所以需要通过server=_compute_host(None, instance)来获取虚拟机所在的host,并向其发送message。
2.proxy_rpc_to_manager函数会通知manager,所以之后应该去该目录下的manager.py去找。由于前面都是函数调用的关系去串联的,刚开始学习看源码的时候就在这里断掉了不明白为什么就直接到driver.py了,实际上就是这样来的。
3.进行函数追踪的时候,点进去会有很多函数都是同名的不知道是哪一个,可以通过(1.对参数 2.看实例化它的类是那个,选择该类里面的那个方法)去辅助找
怎么通知的需要深究rpc原理,有兴趣可以单独看
10. 打开nova-stable-queens ——> nova ——> compute ——>manager.py 里面_snapshot_instance函数,可以看到调用dirver的代码
def _snapshot_instance(self, context, image_id, instance,
expected_task_state):
……此处省略一堆堆,下面是调用dirver的代码,将在snapshot函数中真正执行创建
self.driver.snapshot(context, instance, image_id,update_task_state)
二.向compute节点执行
11.打开nova-stable-queens ——> nova——> virt ——> libvirt ——> driver.py,找到 snapshot函数,从这里才真的开始创建快照
def snapshot(self, context, instance, image_id, update_task_state):
"""Create snapshot from a running VM instance.
12. 检测libvirt版本,看使用live snapshot 热快照 还是cold snapshot 冷快照(取决于最后得出的 live_snapshot 是否等于 False,社区目前认为live snapshot不稳定,libvirt版本小于1.3.0的都只能用cold snapshot )
if (self._host.has_min_version(hv_type=host.HV_DRIVER_QEMU)
and source_type not in ('lvm')
and not CONF.ephemeral_storage_encryption.enabled
and not CONF.workarounds.disable_libvirt_livesnapshot
try:
guest.get_block_device(disk_path).abort_job()
except libvirt.libvirtError as ex:
error_code = ex.get_error_code()
if error_code == libvirt.VIR_ERR_CONFIG_UNSUPPORTED:
live_snapshot = False
else:
pass
else:
live_snapshot = False
13.调用image_backend函数判断后端存储
root_disk = self.image_backend.by_libvirt_path(
instance, disk_path, image_type=source_type)
14.这些判断都搞完之后更新一下task状态码
update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD)
update_task_state(task_state=task_states.IMAGE_UPLOADING,expected_state=task_states.IMAGE_PENDING_UPLOAD)
15.挂起客户机
self._prepare_domain_for_snapshot(context, live_snapshot, state, instance)
16.然后执行创建快照(到底执行哪一种,取决于live_snapshot 的值)
16.1 冷创建
#冷创建快照
root_disk.snapshot_extract(out_path, image_format)
16.2 热创建
self._live_snapshot(context, instance, guest,disk_path, out_path, source_format,image_format, instance.image_meta)
17.创建完快照,调用_snapshot_domain函数,恢复虚拟机状态
self._snapshot_domain(context, live_snapshot, virt_dom, state, instance)
_snapshot_domain会调用_attach_pci_devices和_attach_direct_passthrough_ports
if guest is not None:
self._attach_pci_devices(guest, pci_manager.get_instance_pci_devs(instance))
self._attach_direct_passthrough_ports(context, instance, guest)
_attach_pci_devices又会调用attach_device函数,最后在attach_device中将客户机连接到设备
def attach_device(self, conf, persistent=False, live=False):
"""Attaches device to the guest.
18.调用update函数,上传快照到glance
self._image_api.update(context,image_id,metadata,image_file)
结束