云计算国产化之路 vnc登录 文件系统直通(virtio-9p) 扩展qemu接口 gpg WARNING 珍藏博客 虚拟内存情况dommemstat分析 免密码自动登录脚本 Linux网桥 测试网段IP占用情况 Linux 进程状态 systemc强制依赖 调试openstack ut uefi p2v 重做ubuntu内核 virsh创建虚拟机简介 virtio IO路径 虚拟化层升级后磁盘无法卸载卷 vmtouch使用 Taint flags 主机和虚拟机文件共享处理的几种方法 kvm分析工具 kvm中对磁盘的io cache 虚拟化不同导致的guestos中软件安装差异(未解决) 设备直通(PCI Assignment)到底是咋整的 virtio到底是咋整的 内核启动参数 虚拟化实时性提升(零)之配置步骤 中断虚拟化(pic)到底是咋整的 中断虚拟化(apic)到底是咋整的 Raid卡配置丢失导致服务器无法启动 tainted kernels cpu stuck for 23s问题分析 虚拟化实时性提升(一)之hostOS切换为强实时系统 内存虚拟化到底是咋整的 qemu-kvm中vcpu虚拟化到底是咋整的 风河虚拟化技术点分析 使用qga的好处 qemu迁移代码分析 虚拟机串口配置及其导出到主机pts和console.log System-based I/O vs. Raw I/O 虚拟机使用Hugepage(大页) 硬件辅助分页(hardware assisted paging) 修改centos7默认启动项目 virtio的工作流程——kernel中virtio-pci初始化(2) virtio的工作流程——qemu中virtio-backend初始化(1) qmp ceilometer取不到memory.usage指标 Virtio-Balloon超详细分析 slabtop输出 虚拟机磁盘cache导致的host os kernel崩溃 虚拟机cpu和memory性能优化测评 PCI配置空间(PCI Configuration Space) centos下网卡设备直通(VT-dpci passthrough)遇到的问题及其解决思路 libguestfs详解 yum卸载软件包及其依赖 通过原始Centos ISO来定制自己的ISO centos下网卡设备直通(VT-d,pci passthrough) (占位符)window虚拟机中拔盘如何通知到libvirt和qemu后端的 cirrus漏洞分析CVE-2017-2615 XSA-208 qcow2随笔 控制寄存器概览 ceilometer对接postgresql 解压initrd和vmlinuz qemu guest agent验证 QEMU升级指南(待续) ubuntu中kdump的配置 qemu(2.3)接口梳理 热迁移导致的FC存储场景下的multipath卷残留问题分析 virsh命令(4)secret,snapshot,pool,volume部分 virsh命令(3)之interface,filter,network virsh命令(2)monitor,host,nodedev部分 virsh命令(1)之domain部分 QEMU内存管理之FlatView模型(QEMU2.0.0) ovirt基于sanock的高可用(主机粒度HA) Sanlock防脑裂场景功能测试用例 gnocchi配置及与ceilometer对接指南 make patch for libvirt in centos centos使用sanlock指导 高可用nfs资料 ubuntu14中使用sanlock指导 LVM操作指南 sanlock相关功能验证流程汇总 make patch for libvirt in ubuntu libvirt.so.0-version `LIBVIRT_PRIVATE_1.2.7' not found gdb debug libvirt 基于ubuntu社区源码包编译libvirt compile libvirt(centos) No PCI buses available nfs lead to Linux halt nfs install and config anti-virus for cloud platform nova fetch image from glance(something about _base) token auth process ovs入门指南 virt software anti-virus something about ceilometer disk sample context interview questions openstack vm injection openstack Restful and RPC murano 概览 创建虚拟机流程(compute节点)之网络创建 创建虚拟机流程之compute_api之虚拟机实例填充之配额检测 创建虚拟机流程之compute_api之基本参数生成 创建虚拟机流程之compute_api 创建虚拟机流程(主) 创建虚拟机之image 创建虚拟机流程之准备网桥 创建虚拟机流程之virt 创建虚拟机流程之compute节点 CI/CD研发流程之工程创建 CI/CD研发流程之代码合入 CI/CD研发流程之UT(单元测试) 向openstack社区合入代码记 openstack/ceilometer/gnocchi杂谈 影子页表原理 mem_add(exec.c) qemu编译安装调试 openstack/ceilometer/gnocchi之Grafana简介 openstack wiki etcd openstack计量ceilometer openstack计费cloudKitty enventlet backdoor USB 安装VMWARE ESX pycharm设置指南 无法执行modprobe -a kvm-intel解决办法 QEMU配置项 网络不通小记 libvirt之XML(虚拟机定义文件) openstack-horizon 证书认证 ceilometer与ceph对接 openstack定时任务剖析(TODO) 服务器重启后mongodb进程无法启动 ubuntu14下新增openstack服务到service的导引 ERROR 1045(28000)-数据库连不上 Python两个内置函数—locals和globals unknown exit, hardware reason 31

block_device_mapping

2015年02月03日

在代码中,文档中,接口中经常看见bdm或者block_device_mapping,直译就是块设备映射?
但它到底是什么东东?有没有一直心存困惑?
让我们来一探究竟。

块设备

指对其信息的存取以“块”为单位,如通常的光盘、硬磁盘、软磁盘、磁带等
本人机器上块设备信息如下:

Nova服务创建的虚拟机,如何为其指派块设备?
猜的没错,可以通过块设备映射。
把原有的一些东东(比如cinder的卷,glance的镜像)映射给虚拟机,作为块设备使用。

命令行(CLI)

在nova help boot可以看到参数如下:

mapping 的格式是 =::<size(GB)>: 其中:

dev-name

A device name where the volume is attached in the system at /dev/dev_name .
在虚拟机实例中的挂载点

id

The ID of the volume to boot from, as shown in the output of nova volume-list.
卷id,就是nova volume-list

type

Either snap, which means that the volume was created from a snapshot,
or anything other than snap (a blank string is valid). In the example above,
the volume was not created from a snapshot, so we leave this field blank in our example below.
类型,一般留空。

size (GB)

The size of the volume in gigabytes.
It is safe to leave this blank and have the Compute Service infer the size.
卷大小(G),可留空

delete-on-terminate

A boolean to indicate whether the volume should be deleted when the instance is terminated.
True can be specified as True or 1. False can be specified as False or 0.
实例挂卷之后卷是否删除此设备

这里我们用镜像启动一个虚拟机,同时挂载磁盘818f15ec-3c5d-4791-b72e-528f82e97584到虚拟机vdc(type和size留空):

root@controller1:~# nova boot --image 6eacf3fe-7fe2-4262-b42a-98f624e688c4 --flavor 189aed07-77e3-43ce-a687-56abd5990974 --block-device-mapping vdc=818f15ec-3c5d-4791-b72e-528f82e97584:::0 --nic net-id=47921b66-3e9d-4939-a260-db913d0550f4  test_bdm_win7

虚拟机创建完成后,查看创建的虚拟机详情
会发现818f15ec-3c5d-4791-b72e-528f82e97584(名为block_device_mapping)的块被挂载

仔细一瞅,nova boot里怎么还有block-device这么个参数,看看说明,好像也是块映射

Block Device Mapping
看到这句注释一阵阵想抽过去,到底和上面–block_device_mapping啥关系啊?
后面还有一大堆东西。
先不管,太乱了,不能忍,东西整理如下:

id=UUID(image_id,snapshot_id,volume_id)
souce=源类型(image,snapshot,volume,blank)
dest=目的类型(volume,local)
bus=总线类型(uml,lxc,virtio等,常用virtio)
type=设备类型(disk,cdrom,Floppy,Flash,默认是disk)
device=设备名称(vda,xda)
size=块大小
format=格式(ext4,ISO,swap,ntfs)
bootindex=定义启动盘,是启动盘的话需要是0
shutdown=关机对应动作(prserve,remove)

优雅的打开代码,找到nova-client,找到创建虚拟机流程部分(cli在novaclient数据流分析点击这里)
代码在novaclient/v2/shell.py的_boot方法
可见从block_device_mapping取出值放入block_device_mapping
从_parse_block_device_mapping_v2取出值放入block_device_mapping_v2
两者不能兼容,也就是指定了–block_device_mapping,就不能指定参数–boot_volume、–snapshot、–block_device

为什么整出两套不兼容的东东?
因为EC2就是按照–block_device_mapping这种方式搞的,openstack照抄不误。
后来openstack 兼容多种虚拟化,有的虚拟化不支持直接指定虚拟机的挂载点。
因此推出了加强版块设备映射(block_device_mapping_v2)
同时,为了向上兼容block_device_mapping没有干掉,就导致了这种局面。下面让我们看看–block_device参数
由于源类型(image,snapshot,volume,blank)有这么几种可选,目的类型(volume,local)有这两种可选
上面参数可以组合出几种形态,依据不同的形态有不同的含义:

API接口中

创建虚拟机实例的API有block_device_mapping_v2字段。(为什么是v2?我们上面已揭晓)
在nova/api/openstack/compute/servers.py的create方法中只有这么一段代码

然后通过handler调用到同目录下的block_device_mapping.py中的servier_create
(不明白这么调用到的,猜测应该是通过某种统一的方式,调用到各个扩展部分,回头再看吧,有知道的同学也请告知)
具体原因如下:依次会调用到setup.cfg的nova.api.v3.extensions.server.create的各个插件的server_create

block_device_mapping.py文件的开头声明了几个常量字符串,对,就有我们此节的主角block_device_mapping_v2

代码逻辑如下图,从入参server_dict中根据上述常量值,取出对应的参数bdm,或者legacy_bdm(legacy遗产的意思)
然后调用block_device.BlockDeviceDict.from_api将取出的bdm转化为BlockDeviceDict对象
最后将参数放入create_kwargs[‘block_device_mapping’],传入底层去创建虚拟了。
(这里需要注意block_device_mapping_v2和block_device_mapping互斥
因为block_device_mapping_v2是升级版,为了解决老版问题引入的,详细情况容后表)

数据表

在nova的数据表中有block_device_mapping
查询上面一个创建的虚拟机
可见有个/dev/hda 其source是image dest是local 对应到上表发现是镜像启动,和我们的启动方式相符。

代码流程

对象转换

一般情况会从数据库block_device_mapping表中查出磁盘信息

然后将数据库类型的bdms封装成对于的blockdevice类

也就是

这里我们重点关注下convert_all_volumes这个方法
就是依次判断每个磁盘的类型,然后返回对应的对象

这里以convert_volumes为例

就是说,使用_convert_block_devices方法判断block_device_mapping是不是对于的类型
出错就跳过

下面上几个类的类图

所有的类初始化,都会调用DriveBlockDevice的init方法
init方法最后会调用一下_transform方法

总结一下

需要注意 这里不会转换下面这种情况
也就是说这种磁盘类型会过滤掉

磁盘操作

磁盘操作,以磁盘挂载为例,是调用的各个不同类型磁盘的attach方法,见上面的类图。

给虚拟机挂载磁盘

实际上也是这段代码,注意参数do_driver_attach为true
因为创建虚拟机的时候挂在磁盘也会调用这里
如果do_driver_attach会在虚拟机启动的时候挂载

但是构造bdm对象的时候,指定source_type和destination_type都是volume

也就是说bdm.attach的时候,bdm是 DriverVolumeBlockDevice
调用的也是DriverVolumeBlockDevice.attach方法

  1. 从cinder中获取磁盘的连接信息

    通过断电调试发现connection_info如下图(我们使用的ceph)

    注意:这里调用的cinder的接口/v2/​{tenant_id}​/types/​{volume_type}​/action
    接口文档中说没有返回,其实是有返回的

  2. 调用hypervisor的attach_volume方法

  3. 如果需要的话,在cinder上建立instance和volume的对应关系
    cinder数据库中记录

这里深入看下第2步hypervisor的attach_volume方法
也就是libvirt的driver.py中的attach_volume方法
实际就是获取到虚拟机的domain然后调用attachDeviceFlags方法
入参是根据磁盘信息生成的xml,整理后如下

另外,如果需要的话,会把盘和当前计算节点进行connect
我们这里用的ceph,什么也不做
也就是connection_info.get(‘driver_volume_type’)为rbd
从而rbd=nova.virt.libvirt.volume.LibvirtNetVolumeDriver
中connect_volume

参考文档:
http://docs.openstack.org/developer/nova/block_device_mapping.html#id3
https://wiki.openstack.org/wiki/BlockDeviceConfig