centos下C程式設計呼叫libvirt的API訪問KVM虛擬機器
一、簡介
libvirt是一套免費、開源的支援Linux下主流虛擬化工具的C函式庫,其旨在為包括Xen在內的各種虛擬化工具提供一套方便、可靠的程式設計介面,支援與C,C++,Ruby,Python等多種主流開發語言的繫結。當前主流Linux平臺上預設的虛擬化管理工具virt-manager(圖形化),virt-install(命令列模式)等均基於libvirt開發而成。
本文基於libvirt,使用它的C函式庫進行虛擬機器的相應操作。
二、詳解
1、建立虛擬機器
(1)C語言程式碼
(2)建立所需要的啟動配置xml檔案 首先需要準備好centos6.3.iso系統映象檔案,在xml中<source file = '/home/taiyang/centos6.3.iso'/>設定其全路徑。/*************************************************************************** * 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 <stdio.h> #include <stdlib.h> #include <string.h> #include<assert.h> #include <libvirt/libvirt.h> #include <libvirt/virterror.h> 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; }
然後建立img虛擬機器映象檔案#qemu-img create -f qcow2 newlinux.img 15G,在 <source file = '/var/lib/libvirt/images/newlinux.img'/>設定其路徑。
最後#vim create_kvm.xml,輸入一下內容:
注意:<memory>1048576</memory>單位kb,分配記憶體過大無法啟動;檔案路徑不正確無法啟動。<domain type = 'kvm'> //虛擬機器型別,kvm <name>newlinux</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/libexec/qemu-kvm</emulator> <disk type = 'file' device = 'disk'> //對應的映象,就是之前使用qemu-img命令新建的img檔案,注意路徑要正確 <driver name = 'qemu' type = 'qcow2'/> <source file = '/var/lib/libvirt/images/newlinux.img'/> <target dev = 'hda' bus = 'ide'/> </disk> <disk type = 'file' device = 'cdrom'> //可選項,iso通常是作業系統的安裝光碟 <source file = '/home/taiyang/centos6.3.iso'/> <target dev = 'hdb' bus = 'ide'/> </disk> <interface type = 'bridge'> //libvirt預設虛擬機器的網路配置是NAT模式,就是虛擬機器與宿主機的網路拓撲是NAT形式。實際中,許多開發者更希望使用網橋模式。 <source bridge = 'virbr0'/> </interface> <input type ='tablet' bus='usb'/> <input type = 'mouse' bus = 'ps2'/> <graphics type = 'vnc' port = '-1' listen = '0.0.0.0' keymap = 'en-us'/> //vnc埠系統自動配置 </devices> </domain>
(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 <stdio.h> #include <stdlib.h> #include <string.h> #include <libvirt/libvirt.h> #include <libvirt/virterror.h> 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.localdomain
Maximum number of cpus supported on connection: 16
Node free memory: 1381175296
------------------------------------------
Node information from connection
Model: x86_64
Memory size: 3823996kb
Number of CPUs: 4
MHz of CPUs: 3200
Number of NUMA nodes: 1
Number of CPU sockets: 1
Number of CPU cores per socket: 2
Number 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://[email protected] #連線遠端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涉及內容眾多,可以先了解qemu-img、virsh、virt-manager、virt-install等工具。
(2)有時間的可以下載開源的Libvirt原始碼分析,這樣對自己的幫助更大。