• nova-virt与libvirt


    源码版本:H版

      nova通过nova/virt/driver.py中的ComputeDriver对底层虚拟化技术进行抽象,不同的虚拟化技术在nova/virt下有不同的目录,里面均有driver.py文件,通过继承ComputeDriver类来实现自己的Driver类。nova可以通过对Driver类进行统一接口的调用实现底层虚拟技术的管理。下面具体谈谈nova对libvirt的使用:

    一、libvirt基础

    参考:http://www.cnblogs.com/littlebugfish/p/4231996.html

     

    二、nova对libvirt的使用架构

      说明:其中libvirt-python仅仅是对libvirt的Python绑定,并没有改变功能,对API的改动也不大。

    三、nova创建虚拟机时对libvirt的调度

    nova/virt/libvirt/driver.py

    LibvirtDriver类:
    def spawn(self, context, instance, image_meta, injected_files,
                  admin_password, network_info=None, block_device_info=None):
        """获取disk配置信息"""
        disk_info = blockinfo.get_disk_info(CONF.libvirt_type,
                                            instance,
                                            block_device_info,
                                            image_meta)
        """处理镜像,见下文第1节"""
        self._create_image(context, instance,
                           disk_info['mapping'],
                           network_info=network_info,
                           block_device_info=block_device_info,
                           files=injected_files,
                           admin_pass=admin_password)
        """将当前的参数配置转成创建虚拟机需要用到的xml文件"""
        xml = self.to_xml(context, instance, network_info,
                          disk_info, image_meta,
                          block_device_info=block_device_info,
                          write_to_disk=True)
        """调用libvirt创建并启动虚拟机,见下文第2节"""
        self._create_domain_and_network(context, xml, instance, network_info,
                                        block_device_info)
        ...

    1、处理镜像

    def _create_image(self, context, instance,
                      disk_mapping, suffix='',
                      disk_images=None, network_info=None,
                      block_device_info=None, files=None,
                      admin_pass=None, inject_files=True):
        ...
        def image(fname, image_type=CONF.libvirt_images_type):
            return self.image_backend.image(instance,
                                            fname + suffix, image_type)
        ...
    """初始化镜像文件引用""" if not disk_images: disk_images = {'image_id': instance['image_ref'], 'kernel_id': instance['kernel_id'], 'ramdisk_id': instance['ramdisk_id']}
    """从kernel创建磁盘镜像,一般不采用""" if disk_images['kernel_id']: fname = imagecache.get_cache_fname(disk_images, 'kernel_id') ... inst_type = flavors.extract_flavor(instance) """从image创建启动磁盘镜像""" if not booted_from_volume: root_fname = imagecache.get_cache_fname(disk_images, 'image_id') size = instance['root_gb'] * 1024 * 1024 * 1024 if size == 0 or suffix == '.rescue': size = None 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']) """创建临时磁盘镜像和swap磁盘镜像,过程基本同创建启动磁盘镜像类似""" ... """配置驱动""" if configdrive.required_by(instance): LOG.info(_('Using config drive'), instance=instance) extra_md = {} ... """文件注入""" elif inject_files and CONF.libvirt_inject_partition != -2: if booted_from_volume: LOG.warn(_('File injection into a boot from volume ' 'instance is not supported'), instance=instance) ... if CONF.libvirt_type == 'uml': libvirt_utils.chown(image('disk').path, 'root')

      这里重点分析从image创建启动磁盘镜像的过程 。接着分析代码如下:

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

      首先来看image函数,它本身在_create_image函数中进行定义,如下:

    def image(fname, image_type=CONF.libvirt_images_type):
        return self.image_backend.image(instance,
                                        fname + suffix, image_type)

      主要的作用是根据配置文件在nova.virt.libvirt.imagebackend模块中选择适当对象进行构造并返回,通常为Qcow2对象。

      接着来看cache函数。Qcow2类没有定义该函数,但是由于在nova.virt.libvirt.imagebackend模块中Qcow2类继承自Image类,所以自然调用Image类中定义的该函数。两个类的关系如下图所示:

      具体代码如下:

    nova/virt/libvirt/imagebackend.py 

    Image类:
    def
    cache(self, fetch_func, filename, size=None, *args, **kwargs): """构造获取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) elif CONF.libvirt_images_type == "lvm" and 'ephemeral_size' in kwargs: fetch_func(target=target, *args, **kwargs)
    base_dir
    = os.path.join(CONF.instances_path, CONF.base_dir_name) if not os.path.exists(base_dir): fileutils.ensure_tree(base_dir) base = os.path.join(base_dir, filename)   """如果不存在镜像或不存在base镜像的话""" if not self.check_image_exists() or not os.path.exists(base): self.create_image(call_if_not_exists, base, size, *args, **kwargs)
       ...

      由于创建虚拟机时建立的启动磁盘镜像为qcow2格式,所以需要base镜像,镜像格式可参考:http://docs.openstack.org/image-guide/content/ch_introduction.html。但此时还没有启动磁盘镜像,所以需要调用Qcow2类的create_image函数进行创建,该函数代码如下:

    nova/virt/libvirt/imagebackend.py

    Qcow2类:
    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):
            libvirt_utils.create_cow_image(base, target)
            if size:
                disk.extend(target, size, use_cow=True)
    
        """没有base镜像的话先下载base镜像"""
        if not os.path.exists(base):
            prepare_template(target=base, max_size=size, *args, **kwargs) 
        else:
            self.verify_base_size(base, size)
        ...
    """有base镜像后创建qcow2镜像""" if not os.path.exists(self.path): with fileutils.remove_path_on_error(self.path): copy_qcow2_image(base, self.path, size)

      假设此时连base镜像也没有,则需要先下载base镜像。从上面可以看出,prepare_template 函数即为cache 函数中传入的call_if_not_exists 函数,call_if_not_exists 函数里面接着调用fetch_func 函数,而fetch_func在_create_image 函数中已经表明为fetch_func=libvirt_utils.fetch_image,所以继续如下分析:

    nova/virt/libvirt/utils.py

    def fetch_image(context, target, image_id, user_id, project_id, max_size=0):
        """images为nova/virt/images.py"""
        images.fetch_to_raw(context, image_id, target, user_id, project_id,
                            max_size=max_size)

    nova/virt/images.py

    def fetch_to_raw(context, image_href, path, user_id, project_id, max_size=0):
        path_tmp = "%s.part" % path
        fetch(context, image_href, path_tmp, user_id, project_id,
              max_size=max_size)
        ...
    
    def fetch(context, image_href, path, _user_id, _project_id, max_size=0):
        """其中glance为nova/image/glance.py模块"""
        (image_service, image_id) = glance.get_remote_image_service(context,
                                                                    image_href)
        with fileutils.remove_path_on_error(path):
            image_service.download(context, image_id, dst_path=path)

    nova/image/glance.py

    GlanceImageService类:
    def download(self, context, image_id, data=None, dst_path=None):
        ...
        try:
            """使用glanceclient获取镜像数据"""
            image_chunks = self._client.call(context, 1, 'data', image_id)
        except Exception:
            _reraise_translated_image_exception(image_id)
        ...

      接着来看当有了base镜像后,但此时仍然没有启动磁盘镜像,所以需要接着创建启动磁盘镜像,回到create_image函数中进行分析,涉及的代码如下:

    nova/virt/libvirt/imagebackend.py

    Qcow2类:
    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):        
            libvirt_utils.create_cow_image(base, target)
            if size:
                disk.extend(target, size, use_cow=True)
        ...
        """有base镜像后创建qcow2镜像"""
        if not os.path.exists(self.path):
            with fileutils.remove_path_on_error(self.path):
                copy_qcow2_image(base, self.path, size)

      其中主要调用copy_qcow2_image函数,而copy_qcow2_image函数又调用create_cow_image函数,接着分析代码:

    nova/virt/libvirt/utils.py

    def create_cow_image(backing_file, path, size=None):
        """构建命令创建qcow2镜像,其中设置base镜像为_base目录下相应的镜像,即backing_file"""
        base_cmd = ['qemu-img', 'create', '-f', 'qcow2']
        cow_opts = []
        if backing_file:
            cow_opts += ['backing_file=%s' % backing_file]
            base_details = images.qemu_img_info(backing_file)
        else:
            base_details = None
        if base_details and base_details.cluster_size is not None:
            cow_opts += ['cluster_size=%s' % base_details.cluster_size]
        if base_details and base_details.encryption:
            cow_opts += ['encryption=%s' % base_details.encryption]
        if size is not None:
            cow_opts += ['size=%s' % size]
        if cow_opts:
            csv_opts = ",".join(cow_opts)
            cow_opts = ['-o', csv_opts]
        cmd = base_cmd + cow_opts + [path]
        execute(*cmd) 

    2、创建并启动虚拟机

      继续分析spawn函数中_create_domain_and_network函数的调用,具体代码如下:

    nova/virt/libvirt/driver.py

    LibvirtDriver类:
    def _create_domain_and_network(self, xml, instance, network_info,
                                       block_device_info=None, power_on=True,
                                       context=None, reboot=False):
        ...
        self.plug_vifs(instance, network_info)
        self.firewall_driver.setup_basic_filtering(instance, network_info)
        self.firewall_driver.prepare_instance_filter(instance, network_info)
        domain = self._create_domain(xml, instance=instance, power_on=power_on)
        self.firewall_driver.apply_instance_filter(instance, network_info)
        return domain
    
        
    def _create_domain(self, xml=None, domain=None,
                           instance=None, launch_flags=0, power_on=True):
        ...
        if xml:
            try:
                """self._conn为libvirt.virConnect,所以这里实际上是调用libvirt定义虚拟机"""
                domain = self._conn.defineXML(xml)
            except Exception as e:
                LOG.error(_("An error occurred while trying to define a domain"
                            " with xml: %s") % xml)
                raise e
    
        if power_on:
            try:
                """调用libvirt启动虚拟机"""
                domain.createWithFlags(launch_flags)
            except Exception as e:
                with excutils.save_and_reraise_exception():
                    LOG.error(_("An error occurred while trying to launch a "
                                "defined domain with xml: %s") %
                              domain.XMLDesc(0))
        ...
        return domain
  • 相关阅读:
    (Problem 3)Largest prime factor
    (Problem 2)Even Fibonacci numbers
    (Problem 33)Digit canceling fractions
    (Problem 28)Number spiral diagonals
    多线程与多进程
    安装非英语版本的 Microsoft Visual Studio 2008 Service Pack 1 后,智能提示变为英文的临时补丁发布
    SCOPE_IDENTITY、IDENT_CURRENT 和 @@IDENTITY的区别比较
    MSIL Instruction Table
    使用updatePanel报错 “不允许进行筛选”!
    SQLServer2008中恼人的"阻止保存要求重新创建表的更改"
  • 原文地址:https://www.cnblogs.com/littlebugfish/p/4058115.html
Copyright © 2020-2023  润新知