首页 > 代码库 > nova底层虚拟管理与libvirt

nova底层虚拟管理与libvirt

源码版本:H版

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

一、libvirt基础

1、libvirt基本知识:

http://www.ibm.com/developerworks/cn/linux/l-libvirt/

http://libvirt.org/

2、libvirt配置

http://smilejay.com/2013/03/libvirt-configuration-and-usage/

3、libvirt日志信息

http://www.chenyudong.com/archives/qemu-vm-log-and-libvirtd-log.html

4、libvirt创建instance的xml配置

http://libvirt.org/formatdomain.html

5、libvirt的简单使用

http://blog.163.com/soloman_hao/blog/static/209653155201291652337198/

6、libvirt的Python绑定编程

http://libvirt.org/python.html

http://www.ibm.com/developerworks/cn/opensource/os-python-kvm-scripting1/

 

二、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):        ...        """此处返回的对象为Qcow2对象"""        def image(fname, image_type=CONF.libvirt_images_type):            return self.image_backend.image(instance,                                            fname + suffix, image_type)        ...        """        首先试图获取存储在_base目录中的base镜像文件,由于创建虚拟机时建立的镜像为qcow2格式,所以需要base镜像,        镜像格式参考:        http://docs.openstack.org/image-guide/content/ch_introduction.html        """        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])           ...        """配置驱动"""        if configdrive.required_by(instance):            LOG.info(_(Using config drive), instance=instance)            extra_md = {}            ...        """文件注入"""        elif inject_files and CONF.libvirt_inject_partition != -2:            target_partition = None            ...

nova/virt/libvirt/imagebackend.py

Image类:def cache(self, fetch_func, filename, size=None, *args, **kwargs):    @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)    ...    if not self.check_image_exists() or not os.path.exists(base):        self.create_image(call_if_not_exists, base, size,                          *args, **kwargs)    ...
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镜像,见1.1""" if not os.path.exists(base): prepare_template(target=base, max_size=size, *args, **kwargs) else: self.verify_base_size(base, size) ... """ 有base镜像后创建qcow2镜像,从上面的代码可以看出copy_qcow2_image函数的核心是create_cow_image,见1.2 """ if not os.path.exists(self.path): with fileutils.remove_path_on_error(self.path): copy_qcow2_image(base, self.path, size)

1.1、下载base镜像(即分析prepare_template

  从上面可以看出,prepare_template函数即为cache函数中传入的call_if_not_exists函数,call_if_not_exists函数里面接着调用fetch_func(target=target, *args, **kwargs),而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=http://www.mamicode.com/None, dst_path=None):    ...    try:        """使用glanceclient获取镜像数据"""        image_chunks = self._client.call(context, 1, ‘data‘, image_id)    except Exception:        _reraise_translated_image_exception(image_id)    ...

1.2、创建qcow2镜像(即分析create_cow_image)

  注意这里创建的qcow2镜像是直接供虚拟机所用的。分析如下:

nova/virt/libvirt/utils.py

def create_cow_image(backing_file, path, size=None):    """构建命令创建cow镜像"""    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函数的内容,如下:

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定义XML"""            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

 

nova底层虚拟管理与libvirt