一、简介

libvirtLinux上的虚拟化库,是长期稳定的C语言API,支持KVM/QEMUXenLXC等主流虚拟化方案。链接:

API开发手册:

virshlibvirt对应的shell命令。

之前写了一篇使用virsh管理kvm虚拟机的博客《使用libvirt管理kvmvirsh篇)》(传送门:),这里再从编程接口API的角度介绍如何使用libvirt管理kvm虚拟机。

二、环境介绍

OSUbuntu 12.04.1 LTS

内核:Linux 3.2.0-33-generic-pae #52-Ubuntu SMP Thu Oct 18 16:39:21 UTC 2012 i686 i686 i386 GNU/Linux

libvirt0.9.8

三、准备工作

ubuntu安装,直接apt-get install

1、安装kvm/qemu

sudo apt-get install kvm qemu

2、安装libvirt

sudo apt-get install libvirt-bin libvirt-dev

3、网桥管理工具

sudo apt-get install bridge-utils

4、统一建模语言

sudo apt-get install uml-utilities

5vnc 虚拟机查看工具

sudo apt-get install vncviewer vnc4server

四、创建镜像

执行  qemu-img create -f raw template.img 3G

这样就创建了一个大小为3G的镜像(img

有人可能问镜像是什么东西。简单的说,我们在镜像上启动一个虚拟机,这个3G的镜像就相当于这个虚拟机对应的磁盘空间。

也有人执行 qemu-img create -f qcow2 template.img 3G,(备注:qcow2支持动态扩张)来获得一个动态扩张的镜像。我个人还需要对磁盘资源进行简单的统计、管理,因此没有用这个。不同情景下可能这种模式更好,有兴趣的朋友自己试一下。

五、libvirt xml配置文件

libvirt(包括virsh)使用xml文件对虚拟机进行配置,其中包括虚拟机名称、分配内存、vcpu等多种信息。定义、创建虚拟机等操作都需要xml配置文件的参与,因此这里先介绍xml配置文件。我编辑了一个名为template.xmlxml文件,其中定义了一个名为demokvm 虚拟机。

<domain type = 'kvm'>          //虚拟机类型,kvm     
    <name>demo</name>          //虚拟机名称     
    <memory>1048576</memory>   //分配内存,单位kb     
    <vcpu>1</vcpu>             //分配vcpu,单位个数     
    <os>         
        <type arch = 'x86_64' machine = 'pc'>hvm</type>         
        <boot dev = 'cdrom'/>  //cd 启动         
        <boot dev = 'hd'/>     //硬盘启动     
    </os>     
    <features>         
        <acpi/>         
        <apic/>         
        <pae/>     
    </features>     
    <clock offset = 'localtime'/>     
    <on_poweroff>destroy</on_poweroff>     
    <on_reboot>restart</on_reboot>     
    <on_crash>destroy</on_crash>     
    <devices>         
        <emulator>/usr/bin/kvm</emulator>         
        <disk type = 'file' device = 'disk'>  //对应的镜像,就是之前使用qemu-img命令新建的img文件,注意路径要正确             
            <driver name = 'qemu' type = 'raw'/>             
            <source file = '/var/lib/lynn/img/template.img'/>             
            <target dev = 'hda' bus = 'ide'/>         
        </disk>         
        <disk type = 'file' device = 'cdrom'> //可选项,iso通常是操作系统的安装光盘                  <source file = '/var/lib/lynn/img/template.iso'/>             
            <target dev = 'hdb' bus = 'ide'/>         
        </disk>         
        <interface type = 'bridge'>           //libvirt默认虚拟机的网络配置是NAT模式,就是虚拟机与宿主机的网络拓扑是NAT形式。实际中,许多开发者更希望使用网桥模式。
            <source bridge = 'br0'/>         
        </interface>         
        <input type ='tablet' bus='usb'/>         
        <input type = 'mouse' bus = 'ps2'/>         
        <graphics type = 'vnc' port = '-1' listen = '0.0.0.0' autoport = 'yes' keymap = 'en-us'/>  //vnc端口系统自动配置     
    </devices> 
</domain>

六、libvirt的使用&mdash;&mdash;头文件与编译

#include "libvirt/libvirt.h"          //libvirt主要API

#include <libvirt/virterror.h>     //libvirt 错误提示

编译时加入连接库 -lvirt,例如我要编译名为createvm.cpp的源文件,可以运行如下命令g++ createvm.cpp -o createvm -lvirt

七、主要使用的API

这里只列举一些常用的API,具体可参见libvirt API开发手册

主要功能

函数原型

创建连接

virConnectPtr virConnectOpen(const char * name)

根据xml文档创建虚拟机

virDomainPtr virDomainCreateXML(virConnectPtr conn,  const char * xmlDesc, unsigned int flags)

根据虚拟机名字获得虚拟域

virDomainPtr virDomainLookupByName(virConnectPtr conn,  const char * name)

获取虚拟域相关信息

int virDomainGetInfo(virDomainPtr domain,  virDomainInfoPtr info)

关闭虚拟域

int virDomainShutdown (virDomainPtr domain)

销毁虚拟域

int virDomainDestroy (virDomainPtr domain)

八、新建虚拟机

备注:这里只是写一个简单的例子,除print.cpp以外的代码没有写成带参数、可配置的,请见谅。

/*  createvm.cpp  */ 
/* compile with: g++ createvm.cpp -o createvm -lvirt */  
/* Wang Min @ iie           */ 
/* autumn_sky_is@163.com    */ 
/* date: 2013-02-27         */  

#include <iostream> 
#include <cstdio> 
#include <string> 
#include <fstream> 
#include <sstream>  // for stringstream 

#include "libvirt/libvirt.h" 
#include <libvirt/virterror.h>  

using namespace std; 

string vm_xml_location = "../xml/template.xml";  

int main() {     
    ifstream file(vm_xml_location.c_str());     
    if (!file) {         
        cout<<"Cannot open file "<<vm_xml_location<<endl;         
        return -1;     
    }     
    
    // read xml file 
    stringstream buffer;  
    buffer << file.rdbuf();
    string vm_xml_template = buffer.str();     
    file.close();          
    
    virConnectPtr conn = virConnectOpen("qemu:///system");     
    if (NULL==conn) {         
        fprintf(stderr, "Failed to build connection to qemu:///system.\n");         
        return -1;     
    }      
    
    virDomainPtr vm_ptr = virDomainCreateXML(conn, vm_xml_template.c_str(), 0);     
    if (!vm_ptr) {         
        virErrorPtr error = virGetLastError();         
        cout << error->message << endl;         
        return -1;     
    }     
    
    return 0; 
}

九、读取虚拟机信息

/* print.cpp  */ 
/* compile with: g++ print.cpp -o print -lvirt */  
/* Wang Min @ iie           */ 
/* autumn_sky_is@163.com    */ 
/* date: 2013-02-27         */  

#include <iostream> 
#include <cstdio> 
#include <string> 
#include <fstream> 
#include <sstream>  // for stringstream 

#include "libvirt/libvirt.h" 
#include <libvirt/virterror.h>  

using namespace std; 
//using namespace rapidxml;  

int main(int argc, char * argv[]) {     
    if (2 != argc){     
        cout << "Error: print need 1 parametre" << endl;     
        cout << "usage: ./print vm_name" << endl;     
        return -1;       
    }     
    
    virConnectPtr conn = virConnectOpen("lxc:///");     
    if (NULL == conn) {         
        fprintf(stderr, "Failed to build connection to lxc:///.\n");         
        return -1;     
    }      
    
    virDomainPtr domain = virDomainLookupByName(conn, argv[1]);    
    if (domain == NULL){         
        cout << "can not find " << argv[1] << ", regard it as success." << endl;    
        return -1;     
    }             
    
    // get lxc Info     
    virDomainInfo info;     
    if (-1 == virDomainGetInfo(domain, &info)){ 
        cout << "can not get domain info" << endl;        
        return -1; 
    }        

    printf("state:%d|maxmem:%d|memused:%d|cpunum:%d|cputime:%ld\n", info.state, info.maxMem, info.memory, info.nrVirtCpu, info.cpuTime);     
    cout << "Helloworld" << endl;     
    
    return 0; 
}

编译后运行./print virt_name,例如./print demo

十、关闭虚拟机

/* shutdownvm.cpp  */ 
/* compile with: g++ shutdownvm.cpp -o shutdownvm -lvirt */  
/* Wang Min @ iie           */ 
/* autumn_sky_is@163.com    */ 
/* date: 2013-02-27         */ 

#include <iostream> 
#include <cstdio> 
#include <string> 
#include <fstream> 
#include <sstream>  // for stringstream 

#include "libvirt/libvirt.h" 
#include <libvirt/virterror.h>  

using namespace std; 

string domname = "demo"; 

int main() {     
    virConnectPtr conn = virConnectOpen("qemu:///system");     
    if(NULL==conn) {     
        fprintf(stderr, "Failed to build connection to qemu:///system.\n");    
        return -1;     
    }      
    
    virDomainPtr domain = virDomainLookupByName(conn, domname.c_str());     
    if (domain == NULL){     
        cout << "can not find " << domname << ", regard it as success." << endl;
        return 0;     
    }     
    
    if (virDomainShutdown(domain) != 0){ 
        virErrorPtr error = virGetLastError();         
        cout << error->message << endl;         
        return -1;     
    }     
    
    return 0; 
}

编译后运行./shutdownvm

十一、销毁虚拟机

/* destroyvm.cpp  */ 
/* compile with: g++ destroyvm.cpp -o destroyvm -lvirt */  
/* Wang Min @ iie           */ 
/* autumn_sky_is@163.com    */ 
/* date: 2013-02-27         */  

#include <iostream> 
#include <cstdio> 
#include <string>
#include <fstream> 
#include <sstream>  // for stringstream  

#include "libvirt/libvirt.h" 
#include <libvirt/virterror.h>  

using namespace std; 

string domname = "demo"; 

int main() {     
    virConnectPtr conn = virConnectOpen("qemu:///system");     
    if (NULL == conn) {     
        fprintf(stderr, "Failed to build connection to qemu:///system.\n");         
        return -1;     
    }
    
    virDomainPtr domain = virDomainLookupByName(conn, domname.c_str());     
    if (domain == NULL){     
        cout << "can not find " << domname << ", regard it as success." << endl;         
        return 0;     
    }     
    
    if (virDomainDestroy(domain) != 0){
        virErrorPtr error = virGetLastError();         
        cout << error->message << endl;         
        return -1;     
    }     

    return 0; 
}

编译后运行./destroyvm