源自我们已经用KVM创建了一个虚拟机,这时你想再创建一个,这当然没有问题,但问题是如果你创建很多个虚拟机,你想对其中的一个进行停止操作,你需要PS下kvm进程,然后再处理,这样我们就想到事情能不能简单一点,谁帮我管理一下这几个破虚拟机好不好。
先行者们也已经早就想到了,Libvirt就是开源的虚拟机管理程序,当然它支持qemu,xen....
1. 安装
ubuntu的世界,安装永远不是问题,apt-get install
2. 导入我们之前创建的虚拟机
virt-install --import --name demo3 --ram 512 --disk path=/media/kvm/winxp.img,format=qcow2
解释下:--import 是告诉这个工具,镜像我都做好系统了,用kvm启动他就行
--name xxx 告诉这个工具,以后这个镜像就叫这个名字了,你想对他做什么,告诉名字就可以
--raw 512 虚拟机内存啦
--disk path 镜像的地址
format 镜像的格式(libvirt默认是raw,如果不是raw必须要显示指出,否则会报boot失败)
3. 上面的操作就已经将这个虚拟机启动了,并且用virt-viewer打开了
4. 想管理他是吧,有两个工具
图形的virt-manager,这个不讲了,点点点
文本交互的virsh
virsh -c kemu:///system
virsh shutdown demo 关闭虚拟机
5. Libvirt提供了多种语言绑定开发,C/java/python
---------------------------------------------------------------------
一般而言,程序之中最最重要的是数据结构,故而我们从查找核心数据结构开始,但最初的表象切入点是libvirt的交互工具virsh。
1. 存储池的核心数据结构
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
1. typedef struct _virStoragePoolObj virStoragePoolObj;
2. typedef virStoragePoolObj *virStoragePoolObjPtr;
3. struct _virStoragePoolObj {
4. //线程异步锁
5. char *configFile; //XML文件路径
6. char *autostartLink; //软链接位置
7. int active; //是否激活
8. int autostart; //是否自动启用
9. int asyncjobs; //异步线程号
10. //当前存储池属性
11. //新存储池属性
12. //当前存储池中卷列表
13. };
其中的virStoragePoolDefPtr类型是存储池属性数据结构
[c-sharp] view plain copy
1. typedef struct _virStoragePoolDef virStoragePoolDef;
2. typedef virStoragePoolDef *virStoragePoolDefPtr;
3. struct _virStoragePoolDef {
4. /* General metadata */
5. char *name; //存储池的名字
6. char uuid[VIR_UUID_BUFLEN]; //UUID
7. int type; /* virStoragePoolType */ //类型
8. long long allocation; //使用容量大小
9. long long capacity; //全部容量大小
10. long long available; //可用容量大小
11. //原路径
12. //目的路径
13. };
找到以上两个核心数据结构,就基本上能够了解到如何描述每一个存储池,但是libvirt需要管理的是多个存储池
这时需要用到结构指针: 理解起来也简单,类似于一个数组, *p[1] 可以找到第二个结构变量
<细节>指针可以用malloc在堆上给他分配几个sizeof()的大小,存放几个结构变量,需要改数量时calloc堆上内存块的大小,根据调整变量的位置,通过memmove移动其他结构变量的位置。
实际使用中,又引入了这个结构体,便于获得到结构指针指向内存区域中结构变量的总个数
[c-sharp] view plain copy
1. typedef struct _virStoragePoolObjList virStoragePoolObjList;
2. typedef virStoragePoolObjList *virStoragePoolObjListPtr;
3. struct _virStoragePoolObjList {
4. int count;
5. virStoragePoolObjPtr *objs;
6. }
libvirt.c中conn->storageDriver->pools 就是这个类型的
2. 核心业务流程
表象 -> 细节
virsh的命令行 -> tools/virsh.c代码 -> src/libvirt.c -> src/storage/storage_dirver.c -> src/storage/storage_backend_XX.c
3. 新建存储池类操作
表象: 支持DIR,FS,NetFS,Logical,Disk,IScsi,Scsi,multipath (我们实际上只关=心DIR,FS,NetFs,Iscsi)
细节: DIR,FS,NetFS的卷的一些poolcreate,poolstart等需要实际跟操作系统打交道的功能是走的storage_backend_fs.c这个后端
同样的Logical,Disk,Iscsi,Scsi,mpath都有各自的后端
pool-define
XML描述格式,我们只关心DIR,Fs,NetFs,Iscsi几种
DIR:
[c-sharp] view plain copy
1. <pool type="dir">
2. <name>virtimages</name>
3. <target>
4. <path>/var/lib/virt/images</path>
5. </target>
6. </pool>
Fs:
[c-sharp] view plain copy
1. <pool type="fs">
2. <name>virtimages</name>
3. <source>
4. "/dev/VolGroup00/VirtImages"/>
5. </source>
6. <target>
7. <path>/var/lib/virt/images</path>
8. </target>
9. </pool>
NetFs:
[c-sharp] view plain copy
1. <pool type="netfs">
2. <name>virtimages</name>
3. <source>
4. "nfs.example.com"/>
5. "/var/lib/virt/images"/>
6. </source>
7. <target>
8. <path>/var/lib/virt/images</path>
9. </target>
10. </pool>
Iscsi: 这里path不指明一个固定的块设备,是因为物理机硬盘数的不确定性
[c-sharp] view plain copy
1. <pool type="iscsi">
2. <name>virtimages</name>
3. <source>
4. "iscsi.example.com"/>
5. "demo-target"/>
6. </source>
7. <target>
8. <path>/dev/disk/by-path</path>
9. </target>
10. </pool>
命令使用:
virsh> pool-define /tmp/default.xml
<细节> pool-define 只是把你要加入到存储池大家庭的这个存储池介绍给libvirt认识,他只看到的你的外表而已,不追究你的内在存不存在,但是你在libvirt那里已经挂上号了,pool-list --all 就能看到你,你是inactive的
代码:src/storage/storage_driver.c的storagePoolDefine函数中做以下几个事情:
* 读入xml信息 --virStoragePoolDefParseString
* 按读入的xml的name和uuid 信息判断是否在driver-pools重复存在 --virStoragePoolObjIsDuplicate
按读入的xml的type 判断类型写入dep->type --virStorageBackendForType
扩展分配结构指针的的大小 calloc +1 并且driver->pools->count +1
dirver->pools->obj[dirver->pools->count] = pool (这个是传入的xml构建的) --virStoragePoolObjAssignDef
将存储池信息保存到指定配置文件位置,如果autostart==1,做软连接存储信息均为0 -virStoragePoolDefFormat 构建xml --virStoragePoolObjSaveDef 保存xml
* 向hypervisor connection注册存储池,加入到HashTable里 --virGetStoragePool
pool-define-as
他有个重要的选项 --print-xml 可以看到你输入的参数构成的XML,对照出那个参数传入的不对应。
命令使用: virsh> pool-define-as test, 0,0,0,0,/tmp/test
pool-build
命令使用: virsh> pool-build test
pool-start
命令使用:virsh> pool-start test
<细节> 一方面修改核心数据结构中变量的active参数,另一方面做一些二道贩子的事,如
dir/fs/netfs类型 脚本执行 mount/mount.nfs
iscsi 脚本执行iscsiadm XXXXX --login
执行上面命令后,回调用refresh_pool的函数,获取pool的容量信息,并存入核心数据结构变量中
pool-create
命令使用:virsh>pool-create /tmp/example.xml
pool-create-as
他有个重要的选项 --print-xml 可以看到你输入的参数构成的XML,对照出那个参数传入的不对应。
命令使用:virsh>pool-create-as test, 0,0,0,0,/tmp/test
4. 删除/停止存储池类操作
pool-destory
命令使用: virsh>pool-destory test
<细节> 改变核心数据结构变量的active的值为0
后端处理不同 fs,netfs 分别执行 umount / umount.nfs
iscsi来说就是iscsiadm xxxxx --logout
pool-delete
命令使用:virsh>pool-delete test
<细节> 后端调用 rm -f 这个脚本命令
pool-undefine
命令使用; virsh>pool-undefine test
<细节> src/storage/storage_driver.c storagePoolUndefine函数,做了这么几件事
* 判断pool是否存在于关键数据结构列表中
* 判断是否为inactive
* 判断是否没有异步线程
unlink(ConfigureFile),删除xml文件 --virStoragePoolObjDeleteDef
不论是否为autostart,删除软连接 --unlink
释放结构指针多余内存,count -1,调整结构指针大小 --virStoragePoolObjRemove
5. 修改存储池类操作
pool-edit
命令使用: virsh>pool-edit test
6. 查询存储池类操作
<十一> virsh pool-dumpxml
命令使用: virsh>pool-dumpxml test
<细节> virStoragePoolDefFormat函数
<十二> pool-list
命令使用: virsh>pool-list --all
<细节> 先通过pools->count 得到总数量,根据条件遍历出active 数量
再遍历pools,得到name等的数组,按字母顺序排序
<十三> pool-info
激活的显示name,uuid,state,autostart,presistent和容量信息
非激活的显示 name,uuid,state,autostart,presistent信息
命令使用: virsh>pool-info test
virStoragePoolGetInfo函数得到信息
<十四> pool-refresh virsh命令之一,刷新存储池,是个异步任务,先清空当前volumes列表,后端执行refreshPool 更新volumes列表,更新pool的容量信息
命令使用: pool-refresh