源码版本: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

nova-virt与libvirt的更多相关文章

  1. KVM 介绍(7):使用 libvirt 做 QEMU/KVM 快照和 Nova 实例的快照 (Nova Instances Snapshot Libvirt)

    学习 KVM 的系列文章: (1)介绍和安装 (2)CPU 和 内存虚拟化 (3)I/O QEMU 全虚拟化和准虚拟化(Para-virtulizaiton) (4)I/O PCI/PCIe设备直接分 ...

  2. KVM 介绍(6):Nova 通过 libvirt 管理 QEMU/KVM 虚机 [Nova Libvirt QEMU/KVM Domain]

    学习 KVM 的系列文章: (1)介绍和安装 (2)CPU 和 内存虚拟化 (3)I/O QEMU 全虚拟化和准虚拟化(Para-virtulizaiton) (4)I/O PCI/PCIe设备直接分 ...

  3. KVM(六)Nova 通过 libvirt 管理 QEMU/KVM 虚机

    1. Libvirt 在 OpenStack 架构中的位置 在 Nova Compute 节点上运行的 nova-compute 服务调用 Hypervisor API 去管理运行在该 Hypervi ...

  4. nova libvirt event

    nova中利用libvirt 事件来更新vm的DB中的power状态 https://wiki.openstack.org/wiki/ComputeDriverEvents Nova compute ...

  5. KVM 介绍(8):使用 libvirt 迁移 QEMU/KVM 虚机和 Nova 虚机 [Nova Libvirt QEMU/KVM Live Migration]

    学习 KVM 的系列文章: (1)介绍和安装 (2)CPU 和 内存虚拟化 (3)I/O QEMU 全虚拟化和准虚拟化(Para-virtulizaiton) (4)I/O PCI/PCIe设备直接分 ...

  6. KVM(八)使用 libvirt 迁移 QEMU/KVM 虚机和 Nova 虚机

    1. QEMU/KVM 迁移的概念 迁移(migration)包括系统整体的迁移和某个工作负载的迁移.系统整理迁移,是将系统上所有软件包括操作系统完全复制到另一个物理机硬件机器上.虚拟化环境中的迁移, ...

  7. KVM(七)使用 libvirt 做 QEMU/KVM 快照和 Nova 实例的快照

    本文将梳理 QEMU/KVM 快照相关的知识,以及在 OpenStack Nova 中使用 libvirt 来对 QEMU/KVM 虚机做快照的过程. 1. QEMU/KVM 快照 1.1 概念 QE ...

  8. Neutron 理解 (9): OpenStack 是如何实现 Neutron 网络 和 Nova虚机 防火墙的 [How Nova Implements Security Group and How Neutron Implements Virtual Firewall]

    学习 Neutron 系列文章: (1)Neutron 所实现的虚拟化网络 (2)Neutron OpenvSwitch + VLAN 虚拟网络 (3)Neutron OpenvSwitch + GR ...

  9. openstack-lanch an instance and nova compute log analysis

    1. how to launch an instance: [root@localhost ~(keystone_admin)]# nova flavor-list+----+-----------+ ...

  10. Nova: 虚机的块设备总结 [Nova Instance Block Device]

    和物理机一样,虚拟机包括几个重要的部分:CPU.内存.磁盘设备.网络设备等.本文将简要总结虚机磁盘设备有关知识. 1. Nova boot CLI 中有关虚机块设备的几个参数 nova boot CL ...

随机推荐

  1. (转)一篇写的简明易懂的logging模块

    转:http://kenby.iteye.com/blog/1162698 一.从一个使用场景开始 开发一个日志系统, 既要把日志输出到控制台, 还要写入日志文件 import logging # 创 ...

  2. 172322 2018-2019-1 《Java软件结构与数据结构》实验一报告

    172322 2018-2019-1 <Java软件结构与数据结构>实验一报告 课程:<程序设计与数据结构> 班级: 1723 姓名: 张昊然 学号:20172322 实验教师 ...

  3. SVN版本合并技巧

    公司使用了bug管理系统,项目添加新功能时,建一个主工单,再分成多个子工单,将子工单分给多个程序员来开发. 开发人员完成一部分就提交一部分,多个小功能模块就分多次提交到测试主干,然后用测试主干项目发布 ...

  4. App接口如何保证安全

    微信开发或者高德地图,百度地图什么的api要使用,使用之前都需要注册一个账号,然后系统会给你一个key,然后调用api的时候把key传给服务器. 平常公司内部开发项目时,直接用mvc为app客户端提供 ...

  5. spring复杂数据类型传递

    1.VO对象与PO对象的区别: PO(persistant object) 持久对象:通常对应数据模型(数据库),本身还有部分业务逻辑的处理.可以看成是与数据库中的表相映射的java对象.最简单的PO ...

  6. 团队项目-BUG挖掘

    测试硬件: 华为畅享5 测试平台: 安卓5.1 测试项目Git地址: https://github.com/RABITBABY/We-have-bing 测试Apk来源地址: http://www.a ...

  7. nexus在linux上搭建

    Maven 仓库的分类:(maven的仓库只有两大类) 1.本地仓库 2.远程仓库,在远程仓库中又分成了3种: 2.1 中央仓库 2.2 私服 2.3 其它公共库 有个maven私服可以很方便地管理我 ...

  8. window redis php(必须版本>=5.4) 安装

    1.下载redis的win版客户端 下载地址: http://code.google.com/p/servicestack/wiki/RedisWindowsDownload 2.选择32bit,64 ...

  9. CERC2013(C)_Magical GCD

    题意是这样的,给你一个序列a[i],需要你选一段连续的序列a[i]到a[j],使得长度乘以这个段的gcd最大. 一开始总是以为是各种神奇的数据结构,诶,后来才发现,机智才是王道啊. 可以这样考虑,每次 ...

  10. 个人博客开发-01-nodeJs项目搭建

    // window系统下 1.nodeJs 安装 nodeJs 安装 看  这里 , 先下载再下一步下一步就OK了,我的是在C盘里安装的. 安装完以后 按 win + R ,在弹出的小框框里输入 CM ...