Nova image create流程

http://aspirer2004.blog.163.com/blog/#m=0

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

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

1.1   优化之前

    首先通过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是已经拷贝过来的,所以不需要创建镜像,也就是什么都不做。

1.2   优化之后(仅优化了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

    # useoptimized 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 ifwe 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 %sfailed"), 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 formore 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 bybase_image_id_sha1 "

                " %(base_image_id_sha1)s, snapshotimage_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)

2.      公共流程

创建和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_optimizationTrue则需要走修改后的流程

        snapshot_optimization = False

        # check use image snapshot optimization ornot

        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 beoptimized

        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 createinstance

            # (只有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()           

3.      创建过程

3.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)

3.2   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

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

            # 否则则是RawLVM则需要配置libvirt_images_type='lvm'

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

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

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

            # filenamebase文件的名称

            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 fromtemplate.

 

        Ensures that template and image notalready exists.

        Ensures that base directory exists.

        Synchronizes on template fetching.

 

        :fetch_func: Function that creates thebase image

                     Should accept `target`argument.

        :filename: Name of the file in theimage 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>

3.3   Ephemeral盘

首先qemu-img create创建base(mkfs.ext3格式化),之后qemu-imgcreate创建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 imageof 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)

3.4   Swap盘

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

4.      Resize/冷迁移过程

4.1   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(_("Startingmigrate_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 firstfor 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 theephemeral disk

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

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

                if fname == "disk.local":

                    LOG.debug(_("ignoredisk.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部分,不合并COWbase

                    if FLAGS.allow_image_snapshot_optimization:

                        tmp_path = from_path

                    else:

                        # merge backing file

                        # 老的流程是先合并COWbase之后再拷贝

                        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:

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

                            libvirt_utils.copy_image(tmp_path, img_path,

                                                    host=dest)

                        else:

                            # 优化后使用rsyncdaemon 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 targetfile exists.

        # TODO(oda): injecting files is not necessary

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

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

        # 这里主要是创建instance目录,写入libvirt.xmlconsole.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 wecan extend

            # then ensure we're in 'raw' format so we canextend 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) sothat 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()

 

4.2   Ephemeral盘

与root盘相同

4.3   Swap盘

与root盘相同

5.      热迁移过程(带blockmigration情况)

5.1    Root盘

热迁移目的端:

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

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

        """Preparation blockmigration.

 

        :params ctxt: security context

        :params instance:

            nova.db.sqlalchemy.models.Instanceobject

            instance object that is migrated.

        :params disk_info_json:

            json strings specified inget_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 fileraw格式、或者不带backing fileqcow2

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

            # 这就是block migration的意义。

            if not info['backing_file']:

                libvirt_utils.create_image(info['type'], instance_disk,

                                           info['disk_size'])

            else:

                # backing filedisk

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

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

                # 需要block migration拷贝过来

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

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

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

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

                # Creating backing file follows same way asspawning instances.

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

                # Remove any size tags which the cachemanages

                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'])

5.2   Ephemeral盘

与root盘相同

5.3   Swap盘

与root盘相同

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值