Nova image create流程




原文地址:http://aspirer2004.blog.163.com/blog/static/10676472013215111713232/

完整文档下载地址:nova镜像生成流程  nova镜像生成流程.docx

本文主要讨论nova/virt/libvirt/driver.py:_create_image的相关流程,只讨论file磁盘,不包括EBS盘(block设备)。

  1. Resize过程的镜像拷贝优化
  • 优化之前

首先通过libvirt的XMLDesc()方法拿到虚拟机的配置文件,然后从配置文件中读取所有file类型磁盘的信息(路径,driver,qcow2的backing file);然后如果是不同host之间resize,则qemu-img convert合并base和子镜像为qcow2(无backing file),之后通过rsync ssh方式拷贝合并后的镜像到新的host对于instance目录下,然后在if not os.path.exists(self.path) or not os.path.exists(base):则创建镜像,resize过程这个self.path是已经拷贝过来的,所以不需要创建镜像,也就是什么都不做。

  • 优化之后(仅优化了resize过程,创建过程与优化之前相同)

拷贝镜像是用rsync的daemon push模式,并且不合并base和子镜像,只拷贝子镜像部分,然后在目标host上检查base是否存在,不存在则下载,扩容,最后qemu-img rebase把子镜像rebase到新的base上;目前第二块盘(disk.local)以及swap盘(disk.swap)是不拷贝的,因为如果拷贝过去的仅仅是子镜像,会导致base找不到,为disk.local、disk.swap准备base镜像这部分代码没有实现,所以在拷贝子镜像过程中忽略了disk.local,disk.swap目前没有配置,所以代码里面没有忽略,如果开启了swap的配置,则resize过程会出现问题(base找不到导致虚拟机无法启动)。

优化后的镜像生成流程:

# nova/virt/libvirt/driver.py:LibvirtDriver._create_image()

if snapshot_optimization \

and not self._volume_in_mapping(self.default_root_device,

block_device_info):

self._create_snapshot_image(context, instance,

disk_images[‘image_id’],

basepath(‘disk’), size)

 

# nova/virt/libvirt/driver.py:LibvirtDriver

# use optimized snapshot image

# 优化后的resize过程镜像生成流程

def _create_snapshot_image(self, context, instance, image_id,

target, size):

# NOTE(hzwangpan): for resize operation, the ‘disk’ is copied from

# source node before _create_image(), so if we fetch the ‘disk’ here,

# it will cover the ‘disk’ copied from source

# 只有当’disk’不存在的时候才下载’disk’,M3从快照恢复流程优化的遗留代码

# M3的快照只有COW部分也即’disk’,所以创建虚拟机的时候要先下载’disk’,

# 然后根据其backing file的名称从glance下载它的base,这里就是下载’disk’

# 的流程,因为社区原有的下载镜像代码会转换镜像格式,而我们不需要转换,

# 所以这里新加了一个fetch_orig_image()方法。

# resize的时候instance目录下’disk’是存在的,已经从源端拷贝过来了。

if not os.path.exists(target):

libvirt_utils.fetch_orig_image(context=context, target=target,

image_id=image_id,

user_id=instance[“user_id”],

project_id=instance[“project_id”])

 

if not os.path.exists(target):

LOG.error(_(“fetch image failed, image id: %s”), image_id,

instance=instance, context=context)

raise exception.CouldNotFetchImage(image_id)

# 查询’disk’的backing file信息,也即查找其base

backing_file = libvirt_utils.get_disk_backing_file(target)

if not backing_file:

LOG.error(_(“get backing file of image %s failed”), image_id,

instance=instance, context=context)

raise exception.ImageUnacceptable(image_id=image_id,

reason=_(“%s doesn’t has backing file”) % target)

 

virtual_size = libvirt_utils.get_disk_size(target)

size = max(size, virtual_size)

 

# get base image by backing file

# 根据backing file名称下载base镜像

# 如果没有M3那种不完整的快照存在,则从backing file名称下载base镜像

# 的流程可以简化为根据image id下载镜像,因为每一个虚拟机都是从一个

# 完整的镜像/快照创建的,所以resize的时候根据虚拟机的image id下载到

# 的镜像就是’disk’的base。

base_dir = os.path.join(FLAGS.instances_path, ‘_base’)

if not os.path.exists(base_dir):

utils.ensure_tree(base_dir)

old_backing_file = os.path.join(base_dir, backing_file)

old_size = 0

if “_” in os.path.basename(old_backing_file):

base_img = old_backing_file.rsplit(“_”, 1)[0]

old_size = int(old_backing_file.rsplit(“_”, 1)[1]) * \

(1024L * 1024L * 1024L)

else:

base_img = old_backing_file

# 先检查不带大小信息的base是否存在,如果存在就不需要从glance下载了

# 如果不存在,则需要从glance下载base

if not os.path.exists(base_img):

self._get_base_image_by_backing_file(context, instance,

image_id, base_img)

 

lock_path = os.path.join(FLAGS.instances_path, ‘locks’)

 

@utils.synchronized(base_img, external=True, lock_path=lock_path)

def copy_and_extend(base_img, target_img, size):

if not os.path.exists(target_img):

libvirt_utils.copy_image(base_img, target_img)

disk.extend(target_img, size)

 

# NOTE(wangpan): qemu-img rebase ‘Safe mode’ need the old backing file,

#                refer to qemu-img manual for more details.

# 从没有大小信息的base拷贝扩容出’disk’的老的backing file,因为qemu-img

# rebase默认是采用“安全模式”的,这种模式需要COW部分的新老backing file

# 都存在才能正常执行。

if old_size:

copy_and_extend(base_img, old_backing_file, old_size)

# 从没有大小信息的base拷贝扩容出’disk’的新的backing file,也即resize之后的大小

new_backing_file = base_img

if size:

size_gb = size / (1024 * 1024 * 1024)

new_backing_file += “_%d” % size_gb

copy_and_extend(base_img, new_backing_file, size)

 

# when old_backing_file != new_backing_file, rebase is needed

# 如果新老backing file不一样,则需要对’disk’进行rebase操作

if old_backing_file != new_backing_file:

libvirt_utils.rebase_cow_image(new_backing_file, target)

 

def _get_base_image_by_backing_file(self, context, instance,

image_id, backing_file):

base_image_id_sha1 = os.path.basename(backing_file)

LOG.debug(_(“image id sha1 of backing file %(backing_file)s ”

“is: %(base_image_id_sha1)s”) % locals(),

instance=instance, context=context)

 

(image_service, image_id) = glance.get_remote_image_service(

context, image_id)

# 根据base名称,从glance查询镜像/快照信息

image_info = image_service.get_image_properties(context,

“image_id_sha1”,

base_image_id_sha1)

if not image_info:

LOG.error(_(“can’t find base image by base_image_id_sha1 ”

” %(base_image_id_sha1)s, snapshot image_id: %(image_id)s”) %

locals(), instance=instance, context=context)

raise exception.ImageNotFound(image_id=base_image_id_sha1)

base_image_id = str(image_info[0].get(“image_id”))

 

lock_path = os.path.join(FLAGS.instances_path, ‘locks’)

# 下载找到的镜像/快照

@utils.synchronized(base_image_id_sha1,

external=True, lock_path=lock_path)

def fetch_base_image(context, target, image_id, user_id, project_id):

if not os.path.exists(target):

# 使用原有的下载镜像的方法,会转换镜像格式

libvirt_utils.fetch_image(context=context,

target=target,

image_id=image_id,

user_id=user_id,

project_id=project_id)

 

fetch_base_image(context, backing_file, base_image_id,

instance[“user_id”], instance[“project_id”])

if not os.path.exists(backing_file):

LOG.error(_(“fetch base image failed, image id: %s”),

base_image_id, instance=instance, context=context)

raise exception.CouldNotFetchImage(base_image_id)

  1. 公共流程

创建和resize的公共流程:

# nova/virt/libvirt/driver.py:LibvirtDriver._create_image()

# syntactic nicety(为了语法好看,定义了三个内部方法)

def basepath(fname=, suffix=suffix):

return os.path.join(FLAGS.instances_path,

instance[‘name’],

fname + suffix)

 

def image(fname, image_type=FLAGS.libvirt_images_type):

return self.image_backend.image(instance[‘name’],

fname + suffix, image_type)

 

def raw(fname):

return image(fname, image_type=‘raw’)

 

# ensure directories exist and are writable

# 创建instance目录,用来存放镜像和libvirt.xml配置文件

utils.ensure_tree(basepath(suffix=))

# 写入libvirt.xml配置文件

libvirt_utils.write_to_file(basepath(‘libvirt.xml’), libvirt_xml)

# 写入console.log控制台输出文件

libvirt_utils.write_to_file(basepath(‘console.log’,),, 007)

 

# get image type(为了优化镜像流程而新增的代码)

image_type = None

has_base_id_sha1 = False

(image_service, image_id) = glance.get_remote_image_service(

context, disk_images[‘image_id’])

try:

image_info = image_service.show(context, image_id)

if image_info and ‘properties’ in image_info:

if image_info[‘properties’].get(‘image_type’) == “snapshot”:

image_type = “snapshot”

else: # 如果不是快照,则认为是普通镜像

image_type = “image”

# base_image_id_sha1是为了兼容M3时的快照(只上传COW部分)

if image_info[‘properties’].get(‘base_image_id_sha1’):

has_base_id_sha1 = True

except Exception:

image_type = None

has_base_id_sha1 = None

LOG.warn(_(“get image type of %s faild”) % image_id,

context=context, instance=instance)

pass

 

# 检查镜像是否有backing file,也即是否只是COW部分

backing_file = None

if os.path.exists(basepath(‘disk’)):

backing_file = libvirt_utils.get_disk_backing_file(

basepath(‘disk’))

 

# 下面的这些判断都是为了检查是否需要走我们自己修改的镜像流程

# snapshot_optimization为True则需要走修改后的流程

snapshot_optimization = False

# check use image snapshot optimization or not

use_qcow2 = ((FLAGS.libvirt_images_type == ‘default’ and

FLAGS.use_cow_images) or

FLAGS.libvirt_images_type == ‘qcow2’)

 

# only qcow2 image may be need to optimize, and images with

# ‘kernel_id’ or ‘ramdisk_id’ shouldn’t be optimized

if FLAGS.allow_image_snapshot_optimization and use_qcow2 and \

not disk_images[‘kernel_id’] and not disk_images[‘ramdisk_id’]:

# 下面的这些if语句是为了判断当前属于哪种镜像的哪个操作

# 然后就可以判断是否需要走修改后的流程,这种判断方式比较人肉,

# 以后改起来也比较麻烦,但目前没有更好的办法了。

# normal image, when create instance(普通镜像的创建虚拟机过程)

if image_type == “image” and backing_file is None and \

not has_base_id_sha1:

snapshot_optimization = False

 

# normal image, when resize(普通镜像的resize过程)

if image_type == “image” and backing_file is not None and \

not has_base_id_sha1:

snapshot_optimization = True

 

# unbroken snapshot, when create instance(完整快照的创建虚拟机过程)

if image_type == “snapshot” and backing_file is None and \

not has_base_id_sha1:

snapshot_optimization = False

 

# unbroken snapshot, when resize(完整快照的resize过程)

if image_type == “snapshot” and backing_file is not None and \

not has_base_id_sha1:

snapshot_optimization = True

 

# only cow part snapshot, when create instance

# (只有COW部分的快照(M3修改)的创建过程)

if image_type == “snapshot” and backing_file is None and \

has_base_id_sha1:

snapshot_optimization = True

 

# only cow part snapshot, when resize

# (只有COW部分的快照(M3修改)的resize过程)

if image_type == “snapshot” and backing_file is not None and \

has_base_id_sha1:

snapshot_optimization = True

 

# 生成base的文件名

root_fname = hashlib.sha1(str(disk_images[‘image_id’])).hexdigest()

  1. 创建过程
  • 概述

对于qcow2格式镜像root盘,原有流程是先下载镜像(或者说先创建base),然后qemu-img create生成子镜像(disk),对于qcow2格式的第二块临时盘和第三块swap盘,首先是通过mkfs/mkswap创建base,之后qemu-img create生成子镜像(disk.local/disk.swap)。

传入参数:

# nova/virt/libvirt/driver.py:LibvirtDriver.spawn()

self._create_image(context, instance, xml, network_info=network_info,

block_device_info=block_device_info,

files=injected_files,

                       admin_pass=admin_password)

  • Root盘

目前如果不是M3版本的快照文件,完整快照或者镜像的创建过程与社区F版本流程一致。首先根据image id下载镜像,之后转换、copy、扩容后生成并Cache base镜像,最后qemu-img create创建COW部分的disk。

# nova/virt/libvirt/driver.py:LibvirtDriver._create_image()

elif not self._volume_in_mapping(self.default_root_device,

block_device_info):

# image是上面说的三个内部方法之一,初始化为一个对象,具体的对象是

# 根据镜像的格式来确定的,FLAGS.libvirt_images_type默认是default,

# 然后会再判断FLAGS.use_cow_images是否为True,默认值为True

# 如果是True则image是Qcow2类的对象,目前这两个值都是保持默认。

# 否则则是Raw,LVM则需要配置libvirt_images_type=’lvm’。

# ‘disk’参数是root盘的文件名,也就是加上instance目录后的image.path

# cache就是image类里的一个方法,用来缓存base

# fetch_func就是如果base不存在,用来从glance下载镜像的方法

# filename是base文件的名称

image(‘disk’).cache(fetch_func=libvirt_utils.fetch_image,

context=context,

filename=root_fname,

size=size,

image_id=disk_images[‘image_id’],

user_id=instance[‘user_id’],

project_id=instance[‘project_id’])

 

# nova/virt/libvirt/imagebackend.py:Image.cache()

def cache(self, fetch_func, filename, size=None, *args, **kwargs):

“””Creates image from template.

 

Ensures that template and image not already exists.

Ensures that base directory exists.

Synchronizes on template fetching.

 

:fetch_func: Function that creates the base image

Should accept target argument.

:filename: Name of the file in the image directory

:size: Size of created image in bytes (optional)

“””

# 根据base的文件名加锁,防止两个创建过程同时下载导致的镜像损坏

@utils.synchronized(filename, external=True, lock_path=self.lock_path)

def call_if_not_exists(target, *args, **kwargs):

# 这里的判断必不可少,因为可能拿到锁的时候另外一个创建流程已经下载过了这个镜像

if not os.path.exists(target):

fetch_func(target=target, *args, **kwargs)

 

# 如果instance目录下’disk’文件已经存在,则什么都不做,否则生成’disk’

if not os.path.exists(self.path): # self.path的初始化见下面的代码

base_dir = os.path.join(FLAGS.instances_path, ‘_base’)

if not os.path.exists(base_dir):

utils.ensure_tree(base_dir)

base = os.path.join(base_dir, filename)

# 把下载镜像的方法作为参数传给创建disk的方法

self.create_image(call_if_not_exists, base, size,

*args, **kwargs)

# nova/virt/libvirt/imagebackend.py:

class Qcow2(Image):

def __init__(self, instance, name):

super(Qcow2, self).__init__(“file”, “qcow2”, is_block_dev=False)

# instance=instance[‘name’],name=’disk’

# self.path就是instance目录下的disk文件

self.path = os.path.join(FLAGS.instances_path,

instance, name)

 

def create_image(self, prepare_template, base, size, *args, **kwargs):

# 加锁,防止镜像在使用过程中中被删除或修改

@utils.synchronized(base, external=True, lock_path=self.lock_path)

def copy_qcow2_image(base, target, size):

qcow2_base = base

if size:

size_gb = size / (1024 * 1024 * 1024)

qcow2_base += ‘_%d’ % size_gb

if not os.path.exists(qcow2_base):

with utils.remove_path_on_error(qcow2_base):

# 根据flavor拷贝后扩容base

libvirt_utils.copy_image(base, qcow2_base)

disk.extend(qcow2_base, size)

# 使用qemu-img create命令行创建COW部分也即disk文件

libvirt_utils.create_cow_image(qcow2_base, target)

# 使用传入的下载镜像的方法下载镜像,也即准备base

prepare_template(target=base, *args, **kwargs)

with utils.remove_path_on_error(self.path):

copy_qcow2_image(base, self.path, size)

下载时是先把镜像保存在_base目录下,命名为root_fname.part,然后转换为raw格式,转换过程中的目标文件命名为root_fname.converted,转换完成后删除root_fname.part,并把root_fname.converted改为root_fname,扩容后的后面加上size信息例如root_fname_10。

生成的libvirt.xml配置文件中root盘的配置为:

<disk type=‘file’ device=‘disk’>

    <driver name=‘qemu’ type=‘qcow2’ cache=‘none’/>

    <source file=‘/home/openstack/nova/instances/instance-000005ac/disk’/>

    <target dev=‘vda’ bus=‘virtio’/>

</disk>

  • Ephemeral盘

首先qemu-img create创建base(mkfs.ext3格式化),之后qemu-img create创建COW部分的disk.local,配置文件与root盘相同,只是file文件的名称(disk改为disk.local)、target dev(vda改为vdb)不同而已。

# nova/virt/libvirt/driver.py:LibvirtDriver._create_image()

ephemeral_gb = instance[‘ephemeral_gb’]

if ephemeral_gb and not self._volume_in_mapping(

self.default_second_device, block_device_info):

# 如果有第二块盘’disk.local’,则swap盘作为第三块盘vdc

swap_device = self.default_third_device

# 封装创建第二块盘的方法_create_ephemeral

fn = functools.partial(self._create_ephemeral,

fs_label=‘ephemeral0’,

os_type=instance[“os_type”])

fname = “ephemeral_%s_%s_%s” % (“0”,

ephemeral_gb,

instance[“os_type”])

size = ephemeral_gb * 1024 * 1024 * 1024

# 与root盘的创建流程类似,差别只是将从glance下载镜像改为qemu-img创建base

image(‘disk.local’).cache(fetch_func=fn,

filename=fname,

size=size,

ephemeral_size=ephemeral_gb)

else:

swap_device = self.default_second_device

 

# nova/virt/libvirt/driver.py:LibvirtDriver

def _create_ephemeral(self, target, ephemeral_size, fs_label, os_type):

# 创建未格式化的空磁盘文件

self._create_local(target, ephemeral_size)

# 格式化为ext3格式

disk.mkfs(os_type, fs_label, target)

 

@staticmethod

def _create_local(target, local_size, unit=‘G’,

fs_format=None, label=None):

“””Create a blank image of specified size”””

 

if not fs_format:

fs_format = FLAGS.default_ephemeral_format # 默认为None

# qemu-img create命令创建raw格式的base

libvirt_utils.create_image(‘raw’, target,

‘%d%c’ % (local_size, unit))

if fs_format: # =None,这里不执行

libvirt_utils.mkfs(fs_format, target, label)

  • Swap盘

流程与ephemeral盘相同,只是base格式不同,首先qemu-img创建base,并用mkswap格式化,之后qemu-img create创建COW部分的disk.local。配置文件与root盘相同,只是file文件的名称(disk改为disk. swap)、target dev(vda改为vdb/vdc,根据有无Ephemeral盘而定)不同而已。

  1. Resize/冷迁移过程
  • Root盘

resize源端:

# nova/virt/libvirt/driver.py:LibvirtDriver

@exception.wrap_exception()

def migrate_disk_and_power_off(self, context, instance, dest,

instance_type, network_info,

block_device_info=None):

LOG.debug(_(“Starting migrate_disk_and_power_off”),

instance=instance)

# 获取虚拟机上所有的type=’file’类型的disk的信息

disk_info_text = self.get_instance_disk_info(instance[‘name’])

disk_info = jsonutils.loads(disk_info_text)

# 关机

self.power_off(instance)

# 块设备处理,我们目前没有使用cinder,所以这里不处理

block_device_mapping = driver.block_device_info_get_mapping(

block_device_info)

for vol in block_device_mapping:

connection_info = vol[‘connection_info’]

mount_device = vol[‘mount_device’].rpartition(“/”)[2]

self.volume_driver_method(‘disconnect_volume’,

connection_info,

mount_device)

 

# copy disks to destination

# rename instance dir to +_resize at first for using

# shared storage for instance dir (eg. NFS).

# 拷贝disk到目标host

same_host = (dest == self.get_host_ip_addr())

inst_base = “%s/%s” % (FLAGS.instances_path, instance[‘name’])

inst_base_resize = inst_base + “_resize”

clean_remote_dir = False

try:

# 先把instance目录改为instance-xxxxx_resize,备份过程

utils.execute(‘mv’, inst_base, inst_base_resize)

if same_host:

dest = None

utils.execute(‘mkdir’, ‘-p’, inst_base)

else:

# 不同宿主机之间的resize

if not FLAGS.use_rsync:

# 优化前的流程是用ssh创建目标端的instance目录

utils.execute(‘ssh’, dest, ‘mkdir’, ‘-p’, inst_base)

else:

# 新流程是用rsync创建目录

libvirt_utils.make_remote_instance_dir(inst_base_resize,

dest, instance[‘name’])

clean_remote_dir = True

# 遍历所有disk

for info in disk_info:

# assume inst_base == dirname(info[‘path’])

img_path = info[‘path’]

fname = os.path.basename(img_path)

 

# FIXME(wangpan): when resize, we ignore the ephemeral disk

# 我们在这里忽略了第二块盘’disk.local’,不拷贝到目标端

# 这里我们还应该忽略第三块盘’disk.swap’,不过暂时没用到

if fname == “disk.local”:

LOG.debug(_(“ignore disk.local when resize”),

instance=instance)

continue

 

from_path = os.path.join(inst_base_resize, fname)

remote_path = “%s/%s” % (instance[‘name’], fname)

if info[‘type’] == ‘qcow2’ and info[‘backing_file’]:

tmp_path = from_path + “_rbase”

# Note(hzzhoushaoyu): if allow optimization, just copy

# qcow2 to destination without merge.

# 优化后的流程是只拷贝COW部分,不合并COW和base

if FLAGS.allow_image_snapshot_optimization:

tmp_path = from_path

else:

# merge backing file

# 老的流程是先合并COW和base之后再拷贝

utils.execute(‘qemu-img’, ‘convert’, ‘-f’, ‘qcow2’,

‘-O’, ‘qcow2’, from_path, tmp_path)

 

if same_host and \

not FLAGS.allow_image_snapshot_optimization:

utils.execute(‘mv’, tmp_path, img_path)

elif same_host and FLAGS.allow_image_snapshot_optimization:

utils.execute(‘cp’, tmp_path, img_path)

else:

if not FLAGS.use_rsync:

# 老的流程使用rsync的ssh模式拷贝磁盘文件

libvirt_utils.copy_image(tmp_path, img_path,

host=dest)

else:

# 优化后使用rsync的daemon push模式拷贝

libvirt_utils.copy_image_to_remote(tmp_path,

remote_path, dest)

if not FLAGS.allow_image_snapshot_optimization:

utils.execute(‘rm’, ‘-f’, tmp_path)

 

else:  # raw or qcow2 with no backing file

if not FLAGS.use_rsync or same_host:

libvirt_utils.copy_image(from_path, img_path,

host=dest)

else:

libvirt_utils.copy_image_to_remote(tmp_path,

remote_path, dest)

except Exception, e:

try:

# 异常处理,清理残留文件

if os.path.exists(inst_base_resize):

utils.execute(‘rm’, ‘-rf’, inst_base)

if clean_remote_dir and FLAGS.use_rsync:

libvirt_utils.clean_remote_dir(instance[‘name’], dest)

utils.execute(‘mv’, inst_base_resize, inst_base)

if not FLAGS.use_rsync:

utils.execute(‘ssh’, dest, ‘rm’, ‘-rf’, inst_base)

except Exception:

pass

raise e

# 返回磁盘信息共目的端使用

return disk_info_text

 

resize目的端:

# nova/virt/libvirt/driver.py:LibvirtDriver

@exception.wrap_exception()

def finish_migration(self, context, migration, instance, disk_info,

network_info, image_meta, resize_instance,

block_device_info=None):

LOG.debug(_(“Starting finish_migration”), instance=instance)

# 生成libvirt.xml文件

xml = self.to_xml(instance, network_info,

block_device_info=block_device_info)

# assume _create_image do nothing if a target file exists.

# TODO(oda): injecting files is not necessary

# 这里生成镜像,但是实际上社区原有流程不会生成镜像,因为’disk’已经拷贝过来了

# 所以imagebackend.py里面的cache方法什么事情都不做

# 这里主要是创建instance目录,写入libvirt.xml和console.log文件

# 但是我们修改后的流程,会根据’disk’的backing file下载它的base,

# 还会在这里重新生成第二块盘的base和’disk.local’,

# 第三块盘’disk.swap’因为拷贝的时候没有忽略,所以这里不会重新生成,

# 所以这里可能会导致disk.swap找不到base,虚拟机启动失败。

self._create_image(context, instance, xml,

network_info=network_info,

block_device_info=None)

 

# resize disks. only “disk” and “disk.local” are necessary.

# resize磁盘,忽略了第三块盘

disk_info = jsonutils.loads(disk_info)

for info in disk_info:

fname = os.path.basename(info[‘path’])

if fname == ‘disk’:

size = instance[‘root_gb’]

elif fname == ‘disk.local’:

size = instance[‘ephemeral_gb’]

else:

size = 0

size *= 1024 * 1024 * 1024

 

# If we have a non partitioned image that we can extend

# then ensure we’re in ‘raw’ format so we can extend file system.

fmt = info[‘type’]

# 如果是qcow2格式的镜像,并且可以resize,则先把它转换为raw格式

if (size and fmt == ‘qcow2’ and

disk.can_resize_fs(info[‘path’], size, use_cow=True)):

path_raw = info[‘path’] + ‘_raw’

utils.execute(‘qemu-img’, ‘convert’, ‘-f’, ‘qcow2’,

‘-O’, ‘raw’, info[‘path’], path_raw)

utils.execute(‘mv’, path_raw, info[‘path’])

fmt = ‘raw’

# resize磁盘

if size:

disk.extend(info[‘path’], size)

 

if fmt == ‘raw’ and FLAGS.use_cow_images:

# back to qcow2 (no backing_file though) so that snapshot

# will be available

# 如果是raw格式或者刚刚被转换成raw格式,则再次转换成qcow2

path_qcow = info[‘path’] + ‘_qcow’

utils.execute(‘qemu-img’, ‘convert’, ‘-f’, ‘raw’,

‘-O’, ‘qcow2’, info[‘path’], path_qcow)

utils.execute(‘mv’, path_qcow, info[‘path’])

### 上面的两次转换过程是很耗时的,所以不建议这么做

### 还好我们目前的root_gb大小都是一样的,不会做resize动作

 

# 创建虚拟机

self._create_domain_and_network(xml, instance, network_info,

block_device_info)

# 等待虚拟机启动

timer = utils.LoopingCall(self._wait_for_running, instance)

timer.start(interval=0.5).wait()

 

  • Ephemeral盘

与root盘相同

  • Swap盘

与root盘相同

  1. 热迁移过程(带block migration情况)
  • Root盘

热迁移目的端:

# nova/virt/libvirt/driver.py:LibvirtDriver

def pre_block_migration(self, ctxt, instance, disk_info_json):

“””Preparation block migration.

 

:params ctxt: security context

:params instance:

nova.db.sqlalchemy.models.Instance object

instance object that is migrated.

:params disk_info_json:

json strings specified in get_instance_disk_info

 

“””

# 与resize相同,disk_info_json也是找到的所有type=’file’的disk

disk_info = jsonutils.loads(disk_info_json)

 

# make instance directory

instance_dir = os.path.join(FLAGS.instances_path, instance[‘name’])

if os.path.exists(instance_dir):

raise exception.DestinationDiskExists(path=instance_dir)

os.mkdir(instance_dir)

# 遍历所有file disk

for info in disk_info:

base = os.path.basename(info[‘path’])

# Get image type and create empty disk image, and

# create backing file in case of qcow2.

instance_disk = os.path.join(instance_dir, base)

# 如果disk没有backing file(raw格式、或者不带backing file的qcow2)

# 则直接用’qemu-img create’创建空盘,磁盘内容会随着热迁移流程拷贝过来

# 这就是block migration的意义。

if not info[‘backing_file’]:

libvirt_utils.create_image(info[‘type’], instance_disk,

info[‘disk_size’])

else:

# 有backing file的disk

# 与创建虚拟机相同的镜像生成流程,也就是先准备base,

# 再qemu-img create ‘disk’,这里生成的’disk’也是类似空盘

# 需要block migration拷贝过来

# 需要注意的是如果是M3的不完整快照,这里的流程会出错,

# 因为这里是根据image id下载base的,而不完整的快照的id就是它本身

# 我们需要的是根据快照的id找到并下载它的base

# M4的完整快照应该与普通镜像相同,所以没有这个问题

# Creating backing file follows same way as spawning instances.

cache_name = os.path.basename(info[‘backing_file’])

# Remove any size tags which the cache manages

cache_name = cache_name.split(‘_’)[0]

# 下面的流程与创建流程相同

image = self.image_backend.image(instance[‘name’],

instance_disk,

FLAGS.libvirt_images_type)

image.cache(fetch_func=libvirt_utils.fetch_image,

context=ctxt,

filename=cache_name,

image_id=instance[‘image_ref’],

user_id=instance[‘user_id’],

project_id=instance[‘project_id’],

size=info[‘virt_disk_size’])

  • Ephemeral盘

与root盘相同

  • Swap盘

与root盘相同