博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
centos下C编程调用libvirt的API访问KVM虚拟机
阅读量:4178 次
发布时间:2019-05-26

本文共 18677 字,大约阅读时间需要 62 分钟。

一、简介

        libvirt是一套免费、开源的支持Linux下主流虚拟化工具的C函数库,其旨在为包括Xen在内的各种虚拟化工具提供一套方便、可靠的编程接口,支持与C,C++,Ruby,Python等多种主流开发语言的绑定。当前主流Linux平台上默认的虚拟化管理工具virt-manager(图形化),virt-install(命令行模式)等均基于libvirt开发而成。

       本文基于libvirt,使用它的C函数库进行虚拟机的相应操作。

二、详解

1、创建虚拟机

(1)C语言代码

/***************************************************************************      *  create_kvm.c       *  create kvm machine(domain) based on conf.xml      *  compile command: 'gcc -g -Wall create_kvm.c -o create -lvirt'      *  run shell command:'qemu-img create -f qcow2 newlinux.img 15G'     *  running command: './create ./create_kvm.xml' ***************************************************************************/  #include 
#include
#include
#include
#include
#include
static int create_kvm();static int get_status();static int open_file(char *file_name);static virConnectPtr conn = NULL;static virDomainPtr dom = NULL;char *buff = NULL;int main(int argc, char *argv[]){ if (argc != 2) { fprintf(stderr, "parametes are wrong,please checkout!\n"); return -1; } if (open_file(argv[1]) != 0) { fprintf(stderr, "open_file failed!\n"); return -1; } conn = virConnectOpen("qemu:///system"); if (conn == NULL) { fprintf(stderr, "Failed to open connection to qemu:///system\n"); return -1; } if (create_kvm() != 0) { fprintf(stderr, "create_kvm failed!\n"); virConnectClose(conn); return -1; } if (get_status() != 0) { fprintf(stderr, "create_kvm failed!\n"); virDomainFree(dom); virConnectClose(conn); return -1; } if (dom != NULL) virDomainFree(dom); if (conn != NULL) virConnectClose(conn); return 0;}int open_file(char *file_name){ FILE *fp = fopen(file_name, "r+"); assert(fp); int flag = fseek(fp, 0, SEEK_END); assert(flag == 0); int len = ftell(fp); buff = (char *)malloc(sizeof(char) * (len + 1)); flag = fseek(fp, 0, SEEK_SET); assert(flag == 0); int num = fread(buff, 1, len + 1, fp); assert(num == len); return 0;}int create_kvm(){ dom = virDomainDefineXML(conn, buff); if (!dom) { virErrorPtr error = virGetLastError(); fprintf(stderr, "virDomainDefineXML failed:%s!\n", error->message); virFreeError(error); return -1; } if (virDomainCreate(dom) < 0) { virErrorPtr error = virGetLastError(); fprintf(stderr, "virDomainCreate failed:%s!\n", error->message); virDomainUndefine(dom); virFreeError(error); //virDomainFree(dom); return -1; } return 0;}int get_status(){ char *status = NULL; virErrorPtr error = NULL; int vcpus = 0; unsigned long long node_free_memory = 0; int id = 0; const char *name = NULL; virNodeInfo nodeinfo; virDomainInfo info; // 获取虚拟机状态 fprintf(stdout, "****************************************************\n"); /* get the capabilities of conn*/ status = virConnectGetCapabilities(conn); if (status == NULL) { error = virGetLastError(); fprintf(stderr, "virConnectGetCapabilities failed: %s\n", error->message); virFreeError(error); return -1; } fprintf(stdout, "Capabilities of connection:\t%s\n", status); free(status); status = NULL; /* get the hostname reported from conn*/ status = virConnectGetHostname(conn); if (status == NULL) { error = virGetLastError(); fprintf(stderr, "virConnectGetHostname failed: %s\n", error->message); virFreeError(error); return -1; } fprintf(stdout, "Connection hostname:\t%s\n", status); free(status); status = NULL; /* get the maximum number of vcpus supported by conn3 */ vcpus = virConnectGetMaxVcpus(conn, NULL); if (vcpus < 0) { error = virGetLastError(); fprintf(stderr, "virConnectGetMaxVcpus failed: %s\n", error->message); virFreeError(error); return -1; } fprintf(stdout, "Maximum number of cpus supported on connection:\t%d\n", vcpus); /* get the amount of free memory available on the node from conn4 */ node_free_memory = virNodeGetFreeMemory(conn); if (node_free_memory == 0) { error = virGetLastError(); fprintf(stderr, "virNodeGetFreeMemory failed: %s\n", error->message); virFreeError(error); return -1; } fprintf(stdout, "Node free memory:\t%llu\n", node_free_memory); /* get the node information from conn*/ if (virNodeGetInfo(conn, &nodeinfo) < 0) { error = virGetLastError(); fprintf(stderr, "virNodeGetInfo failed: %s\n", error->message); virFreeError(error); return -1; } fprintf(stdout, "Node information from connection\n"); fprintf(stdout, "Model:\t%s\n", nodeinfo.model); fprintf(stdout, "Memory size:\t%lukb\n", nodeinfo.memory); fprintf(stdout, "Number of CPUs:\t%u\n", nodeinfo.cpus); fprintf(stdout, "MHz of CPUs:\t%u\n", nodeinfo.mhz); fprintf(stdout, "Number of NUMA nodes:\t%u\n", nodeinfo.nodes); fprintf(stdout, "Number of CPU sockets:\t%u\n", nodeinfo.sockets); fprintf(stdout, "Number of CPU cores per socket:\t%u\n", nodeinfo.cores); fprintf(stdout, "Number of CPU threads per core:\t%u\n", nodeinfo.threads); fprintf(stdout, "****************************************************\n"); fprintf(stdout, "id\t名称\t\t状态\n"); fprintf(stdout, "------------------------------------------\n"); id = virDomainGetID(dom); name = virDomainGetName(dom); if (virDomainGetInfo(dom, &info) < 0) { error = virGetLastError(); fprintf(stderr, "virDomainGetInfo failed: %s\n", error->message); virFreeError(error); return -1; } fprintf(stdout, "%d\t%s\t\t%d\n", id, name, info.state); fprintf(stdout, "****************************************************\n"); return 0;}
(2)创建所需要的启动配置xml文件

       首先需要准备好centos6.3.iso系统镜像文件,在xml中<source file = '/home/taiyang/centos6.3.iso'/>设置其全路径。
       然后创建img虚拟机镜像文件#qemu-img create -f qcow2 newlinux.img 15G,在 <source file = '/var/lib/libvirt/images/newlinux.img'/>设置其路径。
      最后#vim create_kvm.xml,输入一下内容:

//虚拟机类型,kvm
newlinux
//虚拟机名称
1048576
    //分配内存,单位kb
1
    //分配vcpu,单位个数
hvm
//cd 启动
//硬盘启动
destroy
restart
destroy
/usr/libexec/qemu-kvm
//对应的镜像,就是之前使用qemu-img命令新建的img文件,注意路径要正确
//可选项,iso通常是操作系统的安装光盘
//libvirt默认虚拟机的网络配置是NAT模式,就是虚拟机与宿主机的网络拓扑是NAT形式。实际中,许多开发者更希望使用网桥模式。
//vnc端口系统自动配置
注意:<memory>1048576</memory>
单位kb,分配内存过大无法启动;文件路径不正确无法启动。

(3)编译运行

#gcc -g -Wall create_kvm.c -o create -lvirt

#./create create_kvm.xml

可以通过virsh查看创建的虚拟机:

也可以通过virt-manager查看创建的虚拟机:

2、操作虚拟机(开机、关机、查询状态)

(1)C语言代码

/*   gcc -g -Wall virtctl.c -o virtctl -lvirt      *//*   virtctl  guest-name  start/shutdown/status    */#include 
#include
#include
#include
#include
static int set_start(char *guestname);static int set_shutdown(char *guestname);static int get_status(char *guestname);static virConnectPtr conn = NULL;static virDomainPtr dom = NULL;int main(int argc, char *argv[]){ if (argc != 3) { fprintf(stderr, "Usage:./virtctl guest-name start/shutdown/status\n"); return -1; } conn = virConnectOpen("qemu:///system"); if (conn == NULL) { fprintf(stderr, "Failed to open connection to qemu:///system\n"); return -1; } dom = virDomainLookupByName(conn, argv[1]); if (dom == NULL) { fprintf(stderr, "virDomainLookupByName failed!\n"); virConnectClose(conn); return -1; } if (strcmp(argv[2], "start") == 0 || strcmp(argv[2], "START") == 0) { if (set_start(argv[1]) != 0) { fprintf(stderr, "start failed!\n"); virDomainFree(dom); //virConnectClose(conn); return -1; } } if (strcmp(argv[2], "shutdown") == 0 || strcmp(argv[2], "SHUTDOWN") == 0) { if (set_shutdown(argv[1]) != 0) { fprintf(stderr, "shutdown failed!\n"); virConnectClose(conn); virDomainFree(dom); return -1; } } if (strcmp(argv[2], "status") == 0 || strcmp(argv[2], "STATUS") == 0) { if (get_status(argv[1]) != 0) { fprintf(stderr, "get status failed!\n"); virDomainFree(dom); virConnectClose(conn); return -1; } } if (dom != NULL) virDomainFree(dom); if (conn != NULL) virConnectClose(conn); return 0;}int set_start(char *guestname){ int flag = -1; // 启动已定义虚拟机 flag = virDomainCreate(dom); if (flag != 0) { virErrorPtr error = virGetLastError(); fprintf(stderr, "virDomainCreate failed:%s!\n", error->message); virFreeError(error); return -1; } return 0;}int set_shutdown(char *guestname){ int flag = -1; // 关闭已定义虚拟机 flag = virDomainShutdown(dom); if(flag != 0) { virErrorPtr error = virGetLastError(); fprintf(stderr, "virDomainShutdown failed:%s!\n", error->message); virFreeError(error); return -1; } return 0;}int get_status(char *guestname){ char *status = NULL; virErrorPtr error = NULL; int vcpus = 0; unsigned long long node_free_memory = 0; int id = 0; const char *name = NULL; virNodeInfo nodeinfo; virDomainInfo info; // 获取虚拟机状态 fprintf(stdout, "****************************************************\n"); /* get the capabilities of conn*/ status = virConnectGetCapabilities(conn); if (status == NULL) { error = virGetLastError(); fprintf(stderr, "virConnectGetCapabilities failed: %s\n", error->message); virFreeError(error); return -1; } //fprintf(stdout, "Capabilities of connection:\n%s\n", status); free(status); status = NULL; /* get the hostname reported from conn*/ status = virConnectGetHostname(conn); if (status == NULL) { error = virGetLastError(); fprintf(stderr, "virConnectGetHostname failed: %s\n", error->message); virFreeError(error); return -1; } //fprintf(stdout, "------------------------------------------\n"); fprintf(stdout, "Connection hostname:\t%s\n", status); free(status); status = NULL; /* get the maximum number of vcpus supported by conn3 */ vcpus = virConnectGetMaxVcpus(conn, NULL); if (vcpus < 0) { error = virGetLastError(); fprintf(stderr, "virConnectGetMaxVcpus failed: %s\n", error->message); virFreeError(error); return -1; } fprintf(stdout, "Maximum number of cpus supported on connection:\t%d\n", vcpus); /* get the amount of free memory available on the node from conn4 */ node_free_memory = virNodeGetFreeMemory(conn); if (node_free_memory == 0) { error = virGetLastError(); fprintf(stderr, "virNodeGetFreeMemory failed: %s\n", error->message); virFreeError(error); return -1; } fprintf(stdout, "Node free memory:\t%llu\n", node_free_memory); /* get the node information from conn*/ if (virNodeGetInfo(conn, &nodeinfo) < 0) { error = virGetLastError(); fprintf(stderr, "virNodeGetInfo failed: %s\n", error->message); virFreeError(error); return -1; } fprintf(stdout, "------------------------------------------\n"); fprintf(stdout, "Node information from connection\n"); fprintf(stdout, "Model:\t%s\n", nodeinfo.model); fprintf(stdout, "Memory size:\t%lukb\n", nodeinfo.memory); fprintf(stdout, "Number of CPUs:\t%u\n", nodeinfo.cpus); fprintf(stdout, "MHz of CPUs:\t%u\n", nodeinfo.mhz); fprintf(stdout, "Number of NUMA nodes:\t%u\n", nodeinfo.nodes); fprintf(stdout, "Number of CPU sockets:\t%u\n", nodeinfo.sockets); fprintf(stdout, "Number of CPU cores per socket:\t%u\n", nodeinfo.cores); fprintf(stdout, "Number of CPU threads per core:\t%u\n", nodeinfo.threads); fprintf(stdout, "****************************************************\n"); fprintf(stdout, "id\t名称\t\t状态\n"); fprintf(stdout, "------------------------------------------\n"); id = virDomainGetID(dom); name = virDomainGetName(dom); if (virDomainGetInfo(dom, &info) < 0) { error = virGetLastError(); fprintf(stderr, "virDomainGetInfo failed: %s\n", error->message); virFreeError(error); return -1; } fprintf(stdout, "%d\t%s\t\t%d\n", id, name, info.state); fprintf(stdout, "****************************************************\n"); return 0;}

(2)编译运行

#gcc -g -Wall virtctl.c -o virtctl -lvirt

#./virtctl newlinux status

****************************************************Connection hostname:	localhost.localdomainMaximum number of cpus supported on connection:	16Node free memory:	1381175296------------------------------------------Node information from connectionModel:	x86_64Memory size:	3823996kbNumber of CPUs:	4MHz of CPUs:	3200Number of NUMA nodes:	1Number of CPU sockets:	1Number of CPU cores per socket:	2Number of CPU threads per core:	2****************************************************id	名称		状态------------------------------------------8	newlinux		1****************************************************

3、virsh命令行管理工具

Libvirt有两种控制方式,命令行和图形界面

(1)图形界面:通过执行名virt-manager,启动libvirt的图形界面,在图形界面下可以一步一步的创建虚拟机,管理虚拟机,还可以直接控制虚拟机的桌面。

(2)virsh所有命令行:

    help            打印帮助

    attach-device   从一个XML文件附加装置
    attach-disk     附加磁盘设备
    attach-interface 获得网络界面
    autostart       自动开始一个域
    capabilities    性能
    cd              change the current directory
    connect         连接(重新连接)到 hypervisor
    console         连接到客户会话
    cpu-baseline    compute baseline CPU
    cpu-compare     compare host CPU with a CPU described by an XML file
    create          从一个 XML 文件创建一个域
    start           开始一个(以前定义的)非活跃的域
    destroy         删除一个域
    detach-device   从一个 XML 文件分离设备
    detach-disk     分离磁盘设备
    detach-interface 分离网络界面
    define          从一个 XML 文件定义(但不开始)一个域
    domid           把一个域名或 UUID 转换为域 id
    domuuid         把一个域名或 id 转换为域 UUID
    dominfo         域信息
    domjobinfo      domain job information
    domjobabort     abort active domain job
    domname         将域 id 或 UUID 转换为域名
    domstate        域状态
    domblkstat      获得域设备块状态
    domifstat       获得域网络接口状态
    dommemstat      get memory statistics for a domain
    domblkinfo      domain block device size information
    domxml-from-native Convert native config to domain XML
    domxml-to-native Convert domain XML to native config
    dumpxml         XML 中的域信息
    edit            编辑某个域的 XML 配置
    find-storage-pool-sources 发现潜在存储池源
    find-storage-pool-sources-as 找到潜在存储池源
    freecell        NUMA可用内存
    hostname        打印管理程序主机名
    list            列出域
    migrate         将域迁移到另一个主机中
    migrate-setmaxdowntime set maximum tolerable downtime
    net-autostart   自动开始网络
    net-create      从一个 XML 文件创建一个网络
    net-define      从一个 XML 文件定义(但不开始)一个网络
    net-destroy     删除一个网络
    net-dumpxml     XML 中的网络信息
    net-edit        为网络编辑 XML 配置
    net-list        列出网络
    net-name        把一个网络UUID 转换为网络名
    net-start       开始一个(以前定义的)不活跃的网络
    net-undefine    取消定义一个非活跃的网络
    net-uuid        把一个网络名转换为网络UUID
    iface-list      list physical host interfaces
    iface-name      convert an interface MAC address to interface name
    iface-mac       convert an interface name to interface MAC address
    iface-dumpxml   interface information in XML
    iface-define    define (but don't start) a physical host interface from an XML file
    iface-undefine  undefine a physical host interface (remove it from configuration)
    iface-edit      edit XML configuration for a physical host interface
    iface-start     start a physical host interface (enable it / "if-up")
    iface-destroy   destroy a physical host interface (disable it / "if-down")
    managedsave     managed save of a domain state
    nodeinfo        节点信息
    nodedev-list    这台主机中中的枚举设备
    nodedev-dumpxml XML 中的节点设备详情
    nodedev-dettach dettach node device from its device driver
    nodedev-reattach reattach node device to its device driver
    nodedev-reset   重置节点设备
    nodedev-create  create a device defined by an XML file on the node
    nodedev-destroy destroy a device on the node
    nwfilter-define define or update a network filter from an XML file
    nwfilter-undefine undefine a network filter
    nwfilter-dumpxml network filter information in XML
    nwfilter-list   list network filters
    nwfilter-edit   edit XML configuration for a network filter
    pool-autostart  自动启动某个池
    pool-build      建立池
    pool-create     从一个 XML 文件中创建一个池
    pool-create-as  从一组变量中创建一个池
    pool-define     在一个 XML 文件中定义(但不启动)一个池
    pool-define-as  在一组变量中定义池
    pool-destroy    销毁池
    pool-delete     删除池
    pool-dumpxml    XML 中的池信息
    pool-edit       为存储池编辑 XML 配置
    pool-info       存储池信息
    pool-list       列出池
    pool-name       将池 UUID 转换为池名称
    pool-refresh    刷新池
    pool-start      启动一个(以前定义的)非活跃的池
    pool-undefine   取消定义一个不活跃的池
    pool-uuid       把一个池名称转换为池 UUID
    secret-define   define or modify a secret from an XML file
    secret-dumpxml  secret attributes in XML
    secret-set-value set a secret value
    secret-get-value Output a secret value
    secret-undefine undefine a secret
    secret-list     list secrets
    pwd             print the current directory
    quit            退出这个非交互式终端
    exit            退出这个非交互式终端
    reboot          重新启动一个域
    restore         从一个存在一个文件中的状态恢复一个域
    resume          重新恢复一个域
    save            把一个域的状态保存到一个文件
    schedinfo       显示/设置日程安排变量
    dump            把一个域的内核 dump 到一个文件中以方便分析
    shutdown        关闭一个域
    setmem          改变内存的分配
    setmaxmem       改变最大内存限制值
    setvcpus        改变虚拟 CPU 的号
    suspend         挂起一个域
    ttyconsole      tty 控制台
    undefine        取消定义一个非活跃的域
    update-device   update device from an XML file
    uri             打印管理程序典型的URI
    vol-create      从一个 XML 文件创建一个卷
    vol-create-from create a vol, using another volume as input
    vol-create-as   从一组变量中创建卷
    vol-clone       clone a volume.
    vol-delete      删除卷
    vol-wipe        wipe a vol
    vol-dumpxml     XML 中的卷信息
    vol-info        存储卷信息
    vol-list        列出卷
    vol-pool        returns the storage pool for a given volume key or path
    vol-path        returns the volume path for a given volume name or key
    vol-name        returns the volume name for a given volume key or path
    vol-key         returns the volume key for a given volume name or path
    vcpuinfo        域 vcpu 的信息
    vcpupin         控制域 vcpu affinity
    version         显示版本
    vncdisplay      vnc 显示
    snapshot-create Create a snapshot
    snapshot-current Get the current snapshot
    snapshot-delete Delete a domain snapshot
    snapshot-dumpxml Dump XML for a domain snapshot
    snapshot-list   List snapshots for a domain
    snapshot-revert Revert a domain to a snapshot

其中总结常用的命令:

virsh -c qemu:///system                       #连接本机

virsh -c qemu+ssh://root@192.168.40.125   #连接远程libvirt,也可通过connect命令

virsh list                                                 #显示本地活动虚拟机

virsh list --all                                         #显示本地所有的虚拟机(活动的+不活动的)

virsh define create_kvm.xml              #通过配置文件定义一个虚拟机(这个虚拟机还不是活动的)

virsh start newlinux                             #启动名字为virtual的非活动虚拟机

virsh create create_kvm.xml              # 创建虚拟机(创建后,虚拟机立即执行,成为活动主机)

virsh suspend newlinux                     # 暂停虚拟机

virsh resume newlinux                       # 启动暂停的虚拟机

virsh shutdown newlinux                   # 正常关闭虚拟机

virsh destroy newlinux                       # 强制关闭虚拟机

virsh dominfo newlinux                     #显示虚拟机的基本信息

virsh domname 2                                # 显示id号为2的虚拟机名

virsh domid newlinux                        # 显示虚拟机id号

virsh domuuid newlinux                   # 显示虚拟机的uuid

virsh domstate newlinux                   # 显示虚拟机的当前状态

virsh dumpxml newlinux                   # 显示虚拟机的当前配置文件(可能和定义虚拟机时的配置不同,因为当虚拟机启动时,需要给虚拟机分配id号、uuid、vnc端口号等等)

virsh setmem newlinux 512000      #给不活动虚拟机设置内存大小

virsh setvcpus newlinux 4                # 给不活动虚拟机设置cpu个数

virsh edit newlinux                            # 编辑配置文件(一般是在刚定义完虚拟机之后)

例1:virsh创建kvm虚拟机
#qemu-img create -f qcow2 newlinux.qcow2 15G    //制作虚拟机镜像#vim create_kvm.xml                        //创建配置文件#virsh define create_kvm.xml          //创建虚拟机#virsh start newlinux                        //启动虚拟机#virsh vncdisplay newlinux            //查看虚拟机的vnc端口,然后就可以通过vnc登录 #vncviewer :0来完成虚拟机的安装
例2:Add CDROM

#virsh attach-disk guest01 /root/disc1.iso hdc --driver file --type cdrom  --mode readonly

例3:Change CDROM

#virsh attach-disk guest01 /root/disc2.iso hdc --driver file --type cdrom --mode readonly

例4:Remove CDROM

#virsh attach-disk guest01 " " hdc --driver file --type cdrom --mode readonly

三、参考

(1)libvirt官方的APIs列表:

(2)网络博客:

(3)libvirt架构及源码分析:

四、总结

(1)Libvirt涉及内容众多,可以先了解qemu-img、virsh、virt-manager、virt-install等工具。

(2)有时间的可以下载开源的Libvirt源码分析,这样对自己的帮助更大。

你可能感兴趣的文章
Mybatis 缓存实现原理
查看>>
怎么提升SQL查询效率
查看>>
预编译防止sql注入
查看>>
覆盖equals方法时总是要覆盖hashCode
查看>>
clone详解
查看>>
【Java并发编程实战】——AbstractQueuedSynchronizer源码分析(一)
查看>>
【Java并发编程实战】——并发编程基础
查看>>
【Java并发编程实战】——Java内存模型与线程
查看>>
【Java并发编程实战】——AbstractQueuedSynchronizer源码分析(二)
查看>>
【Java并发编程实战】——Semaphore源码分析
查看>>
【Java并发编程实战】——CyclicBarrier源码分析
查看>>
【Java并发编程实战】——ReentrantReadWriteLock源码分析
查看>>
【Java并发编程实战】——BlockingQueue阻塞队列
查看>>
怎样设置电脑里不显示Win10软件记录的最近打开的项目
查看>>
Java类中的native方法变红的问题
查看>>
Win10设置电脑每天定时关机
查看>>
AndroidX使用问题记录
查看>>
AS 出现:<item> inner element must either be a resource reference or empty
查看>>
AS中 Anonymous new xxx can be replaced with lambda
查看>>
RecyclerView notifyItemRangeInserted()方法报数组越界
查看>>