1. 背景:
一直以为openstack的创建快照的操作是在线创建快照(live snapshot), 并且应该是增量的快照,即利用virsh或者qemu的live snapshot来实现的:
2. 结论:目前openstack默认的快照方式都是cold snapshot, 首先先关机,其次执行如下命令生成一个镜像文件,再次开机,最后再调用glance api将镜像上传。
3. 源代码流程分析
-
def snapshot(self, context, instance, image_id, update_task_state):
-
"""create snapshot from a running vm instance.
-
-
this command only works with qemu 0.14
-
"""
-
try:
-
virt_dom = self._get_domain(instance)
-
except exception.instancenotfound:
-
raise exception.instancenotrunning(instance_id=instance['uuid'])
-
-
base_image_ref = instance['image_ref']
-
-
base = compute_utils.get_image_metadata(
-
context, self._image_api, base_image_ref, instance)
-
-
snapshot = self._image_api.get(context, image_id)
-
-
disk_path = libvirt_utils.find_disk(virt_dom)
-
source_format = libvirt_utils.get_disk_type(disk_path)
-
-
image_format = conf.libvirt.snapshot_image_format or source_format
-
-
# note(bfilippov): save lvm and rbd as raw
-
if image_format == 'lvm' or image_format == 'rbd':
-
image_format = 'raw'
-
-
metadata = self._create_snapshot_metadata(base,
-
instance,
-
image_format,
-
snapshot['name'])
-
-
snapshot_name = uuid.uuid4().hex
-
-
state = libvirt_power_state[virt_dom.info()[0]]
-
-
# note(rmk): live snapshots require qemu 1.3 and libvirt 1.0.0.
-
# these restrictions can be relaxed as other configurations
-
# can be validated.
-
# note(dgenin): instances with lvm encrypted ephemeral storage require
-
# cold snapshots. currently, checking for encryption is
-
# redundant because lvm supports only cold snapshots.
-
# it is necessary in case this situation changes in the
-
# future.
-
#这里需要注意,解释了为啥现在是cold snapshot而不是live snapshot:
-
# 有人提过live snapshot的bug,社区认为live snapshot目前不稳定,所以默认条件下采用cold snapshot,并且是通过硬编码来实现的
-
# 看下面这个判断条件,成立的时候将live_snapshot = true,其中min_libvirt_livesnapshot_version=1.3.0, 其实现在libvirt的最新版本
-
# 才到1.2.11, 所以这个live_snapshot的条件不满足,就变成了cold_snapshot
-
if (self._host.has_min_version(min_libvirt_livesnapshot_version,
-
min_qemu_livesnapshot_version,
-
req_hypervisor_livesnapshot)
-
and source_format not in ('lvm', 'rbd')
-
and not conf.ephemeral_storage_encryption.enabled):
-
live_snapshot = true
-
# abort is an idempotent operation, so make sure any block
-
# jobs which may have failed are ended. this operation also
-
# confirms the running instance, as opposed to the system as a
-
# whole, has a new enough version of the hypervisor (bug 1193146).
-
try:
-
virt_dom.blockjobabort(disk_path, 0)
-
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
-
-
# note(rmk): we cannot perform live snapshots when a managedsave
-
# file is present, so we will use the cold/legacy method
-
# for instances which are shutdown.
-
if state == power_state.shutdown:
-
live_snapshot = false
-
-
# note(dkang): managedsave does not work for lxc
-
#注意这里,如果live_snashot目前是false,所以在做snapshot之前先要执行:
-
#(1)_detach_pci_devices, 卸载虚拟机挂载的pci设备,比如数据盘
-
#(2) self._detach_sriov_ports, 卸载虚拟机挂载的sriov设备,比如支持sriov的网卡设备
-
if conf.libvirt.virt_type != 'lxc' and not live_snapshot:
-
if state == power_state.running or state == power_state.paused:
-
self._detach_pci_devices(virt_dom,
-
pci_manager.get_instance_pci_devs(instance))
-
self._detach_sriov_ports(instance, virt_dom)
-
virt_dom.managedsave(0)
-
#判断虚拟机的后端存储是什么,不同的后端存储做snapshot是不同的,本地文件系统的化,默认qcow2
-
snapshot_backend = self.image_backend.snapshot(instance,
-
disk_path,
-
image_type=source_format)
-
-
if live_snapshot:
-
log.info(_li("beginning live snapshot process"),
-
instance=instance)
-
else:
-
log.info(_li("beginning cold snapshot process"),
-
instance=instance)
-
#更新任务的状态为:image_pending_upload, 大家都知道做完snapshot要上传
-
update_task_state(task_state=task_states.image_pending_upload)
-
#目前做快照的过程是:
-
#(1)现在../data/nova/instance/snapshots目录下生成临时目录,比如nova/instances/snapshots/tmpthr585
-
#(2)然后将快照生成到这个目录,具体参见snapshot_backend.snapshot_extract(out_path, image_format)这个函数
-
#(3)生成完成后,通过glance api上传,具体参见 self._image_api.update
-
snapshot_directory = conf.libvirt.snapshots_directory
-
fileutils.ensure_tree(snapshot_directory)
-
with utils.tempdir(dir=snapshot_directory) as tmpdir:
-
try:
-
out_path = os.path.join(tmpdir, snapshot_name)
-
if live_snapshot:
-
# note(xqueralt): libvirt needs ox in the temp directory
-
os.chmod(tmpdir, 0o701)
-
self._live_snapshot(context, instance, virt_dom, disk_path,
-
out_path, image_format, base)
-
else:
-
#这个函数实际执行了一条命令: qemu-img convert -f qcow2 -o qcow2 disk_path out_path,算是生成了快照
-
snapshot_backend.snapshot_extract(out_path, image_format)
-
finally:
-
new_dom = none
-
# note(dkang): because previous managedsave is not called
-
# for lxc, _create_domain must not be called.
-
if conf.libvirt.virt_type != 'lxc' and not live_snapshot:
-
if state == power_state.running:
-
new_dom = self._create_domain(domain=virt_dom) ##恢复做快照之前虚拟机的状态
-
elif state == power_state.paused:
-
new_dom = self._create_domain(domain=virt_dom,
-
launch_flags=libvirt.vir_domain_start_paused)
-
if new_dom is not none:
-
self._attach_pci_devices(new_dom,
-
pci_manager.get_instance_pci_devs(instance))
-
self._attach_sriov_ports(context, instance, new_dom)
-
log.info(_li("snapshot extracted, beginning image upload"),
-
instance=instance)
-
-
# upload that image to the image service
-
-
update_task_state(task_state=task_states.image_uploading,
-
expected_state=task_states.image_pending_upload)
-
with libvirt_utils.file_open(out_path) as image_file: ###将生成的快照上传到glance
-
self._image_api.update(context,
-
image_id,
-
metadata,
-
image_file)
-
log.info(_li("snapshot image upload complete"),
-
instance=instance)
------------------------------------------------------------
email: ustc.dylan@gmail.com
微博:@marshal-liu
------------------------------------------------------------