libvirt:獲取 guest 賬戶的IP地址

libvirt 獲取 guest賬戶的IP方案

方案

目前找到兩種方案。方案一:libvirt命令獲取。 方案二:調用libvirt-api獲取。

方案一:virsh 命令

相關命令如下:

virsh  #進入virsh 命令模式
list --all # 查看所有在線的虛擬機列表

虛擬機列表輸出結果如下:

 Id    Name                           State
----------------------------------------------------
 1     151                            running
 -     170                            shut off
 -     win10                          shut off

可以看出正在運行的虛擬機 爲 151

查看151支持的相關命令

 qemu-agent-command 151 '{"execute":"guest-info"}'

輸出結果如下:

{"return":{"version":"0.12.1","supported_commands":[{"enabled":true,"name":"guest-set-user-password"},{"enabled":true,"name":"guest-get-vcpus"},{"enabled":true,"name":"guest-suspend-ram"},{"enabled":true,"name":"guest-suspend-disk"},{"enabled":true,"name":"guest-fsfreeze-thaw"},{"enabled":true,"name":"guest-fsfreeze-freeze"},{"enabled":true,"name":"guest-fsfreeze-status"},{"enabled":true,"name":"guest-shutdown"},{"enabled":true,"name":"guest-info"},{"enabled":true,"name":"guest-set-time"},{"enabled":true,"name":"guest-get-time"},{"enabled":true,"name":"guest-ping"},{"enabled":true,"name":"guest-sync"},{"enabled":true,"name":"guest-sync-delimited"}]}}

獲取虛擬機IP的主要命令如下:

qemu-agent-command 151 '{"execute":"guest-network-get-interfaces"}'

輸出結果如下:

{"return":[{"name":"��̫��e","ip-addresses":[{"ip-address-type":"ipv6","ip-address":"fe80::9ded:74ad:cbc9:7ec2%5","prefix":64},{"ip-address-type":"ipv4","ip-address":"172.26.106.102","prefix":24}],"statistics":{"tx-packets":183723,"tx-errs":0,"rx-bytes":482140384,"rx-dropped":4781392,"rx-packets":329018,"rx-errs":0,"tx-bytes":12037307,"tx-dropped":0},"hardware-address":"52:54:00:89:0e:2b"},{"name":"Loopback Pseudo-Interface 1","ip-addresses":[{"ip-address-type":"ipv6","ip-address":"::1","prefix":128},{"ip-address-type":"ipv4","ip-address":"127.0.0.1","prefix":8}],"statistics":{"tx-packets":0,"tx-errs":0,"rx-bytes":0,"rx-dropped":0,"rx-packets":0,"rx-errs":0,"tx-bytes":0,"tx-dropped":0}},{"name":"Teredo Tunneling Pseudo-Interface","ip-addresses":[{"ip-address-type":"ipv6","ip-address":"2001:0:348b:fb58:28bf:26c3:2ca1:2326","prefix":64},{"ip-address-type":"ipv6","ip-address":"fe80::28bf:26c3:2ca1:2326%3","prefix":64}],"statistics":{"tx-packets":165,"tx-errs":0,"rx-bytes":18504,"rx-dropped":0,"rx-packets":191,"rx-errs":0,"tx-bytes":18780,"tx-dropped":0},"hardware-address":"00:00:00:00:00:00"}]}

所有相關的ip信息如下: ip地址類型,ip地址,硬件地址

方案二:調用API接口

官方接口地址:https://libvirt.org/html/libvirt-libvirt-domain.html#virDomainIPAddress

相關結構體如下:

struct virDomainIPAddress { //ip信息結構體
    int	type;					//virIPAddrType
    char *	addr;				//IP address
    unsigned int	prefix;		//IP address prefix
}
struct virDomainInterface { //接口信息結構體
    char *	name;						//interface name
    char *	hwaddr;						//hardware address, may be NULL
    unsigned int	naddrs;				//number of items in @addrs
    virDomainIPAddressPtr	addrs;		//array of IP addresses
}
/*
Return a pointer to the allocated array of pointers to interfaces present in given domain along with their IP and MAC addresses. Note that single interface can have multiple or even 0 IP addresses
*/
int	virDomainInterfaceAddresses	(virDomainPtr dom,
					 virDomainInterfacePtr ** ifaces,
					 unsigned int source,
					 unsigned int flags) //interface接口地址獲取方法
  
enum virDomainInterfaceAddressesSource { //ip地址的枚舉類型 
VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE	=	0 (0x0)	,//Parse DHCP lease file
VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT	=	1 (0x1)	,//Query qemu guest agent
VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP	=	2 (0x2)	,//Query ARP tables
VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LAST	=	3 (0x3)
}

目前筆者用的 類型爲 VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT ,獲取指定虛擬機IP。 VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE爲獲取 DHCP 方式的虛擬機IP。VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARPVIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LAST這兩種類型,筆者的libvirt版本不支持也就沒有測試。

官方Demo樣例如下:

virDomainInterfacePtr *ifaces = NULL; //接口指針初始化 --網卡
int ifaces_count = 0; //接口個數
size_t i, j; 
virDomainPtr dom = /*... obtain a domain here ...*/;

if ((ifaces_count = virDomainInterfaceAddresses(dom, &ifaces,
         VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE)) < 0)
    goto cleanup;
//... do something with returned values, for example:
for (i = 0; i < ifaces_count; i++) {
    printf("name: %s", ifaces[i]->name);
    if (ifaces[i]->hwaddr) //網卡個數,硬件地址 MAC
        printf(" hwaddr: %s", ifaces[i]->hwaddr);

    for (j = 0; j < ifaces[i]->naddrs; j++) {
        virDomainIPAddressPtr ip_addr = ifaces[i]->addrs + j;
        printf("[addr: %s prefix: %d type: %d]",
               ip_addr->addr, ip_addr->prefix, ip_addr->type);
    }
    printf("\n");
}

cleanup:
    if (ifaces && ifaces_count > 0)
        for (i = 0; i < ifaces_count; i++)
            virDomainInterfaceFree(ifaces[i]);
    free(ifaces);//釋放接口指針內存
/*參數說明
dom:		domain object
ifaces:		pointer to an array of pointers pointing to interface objects
source:		one of the virDomainInterfaceAddressesSource constants
flags:		currently unused, pass zero
Returns:	the number of interfaces on success, -1 in case of error.
*/

測試代碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <libvirt/libvirt.h>
#include <libvirt/virterror.h>
#include <libvirt/libvirt-domain.h>
#include <string.h>

#define MAX_LEN (1024*10)

int main(int argc, char** argv)
{
    virConnectPtr conn;
    virDomainInterfacePtr* ifaces =  NULL;
    int ret = -1;
    int  ifaces_count = 0;
    int i,j;

    conn = virConnectOpen("qemu:///system");
    if(conn == NULL){
        printf("failed to open connection to qemu\n");
	return 1;
    }
    printf("success to open connection to qemu\n");
    char* xml = malloc(MAX_LEN);
    memset(xml,0,MAX_LEN);
    FILE* fp = NULL;
    //filename 作者寫爲了固定地址。也可以利用 argv參數進行傳參
    char filename[128] = {"/etc/libvirt/qemu/151.xml"};
    fp = fopen(filename,"r");
    if(fp != NULL){
        int len = fread(xml,1,MAX_LEN,fp);
	printf("len = %d\n",len);
    }
    else{
    	printf("fp is null!\n");
    }
    if(fp != NULL){
    	fclose(fp);
    }
    virDomainPtr dom;

    dom = virDomainDefineXML(conn,xml);
    if(!dom){
        printf("domain definition failed\n");
    } 

   if((ifaces_count = virDomainInterfaceAddresses(dom,&ifaces,VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT ,0))<0)
    {
        goto cleanup;
    }
    
    for( i=0;i<ifaces_count;i++)
    {
        printf("name:%s \n",ifaces[i]->name);
        if(ifaces[i]->hwaddr) printf("hwaddr:%s\n",ifaces[i]->hwaddr);
        for( j = 0;j < ifaces[i]->naddrs;j++)
        {
            virDomainIPAddressPtr ip_addr = ifaces[i]->addrs + j;
            printf("[addr: %s prefix: %d type: %d]\n",
               ip_addr->addr, ip_addr->prefix, ip_addr->type);
        }
    }
cleanup:
    if (ifaces && ifaces_count > 0)
        for (i = 0; i < ifaces_count; i++)
            virDomainInterfaceFree(ifaces[i]);
    free(ifaces);
    return ret;
}

編譯:

gcc add.c -lvirt -o add

輸出結果如下:

success to open connection to qemu
len = 4214 
name:��̫��e 
hwaddr:52:54:00:89:0e:2b
[addr: fe80::9ded:74ad:cbc9:7ec2%5 prefix: 64 type: 1]
[addr: 172.26.106.102 prefix: 24 type: 0]
name:Loopback Pseudo-Interface 1 
[addr: ::1 prefix: 128 type: 1]
[addr: 127.0.0.1 prefix: 8 type: 0]
name:Teredo Tunneling Pseudo-Interface 
hwaddr:00:00:00:00:00:00
[addr: fe80::ffff:ffff:fffe%3 prefix: 64 type: 1]

相關錯誤解決方法:

  • 錯誤一:

    libvirt: QEMU Driver error : internal error: unable to execute QEMU agent command 'guest-network-get-interfaces': The command guest-network-get-interfaces has been disabled for this instance
    

    解決方法:

    1.查看命令是否啓用。網絡上黑白名單,但是作者是 ubuntu 系統,並沒有找到相關的json文件。查看命令是否被禁用,如果被禁用,則啓用。
    2.利用 qemu-agent-command 151 '{"execute":"guest-info"}' 命令查看libvirt支持命令,打印信息並沒有 guest-network-get-interfaces 命令。原因爲;在虛擬機,裏邊沒有安裝 qga 即 qemu-ga-x86_64.msi
    
  • 錯誤二:

    libvirt: QEMU Driver error : argument unsupported: QEMU guest agent is not configured
    

    解決方法:

    錯誤原因:在對應xml文件中,沒有增加對應的接口通道 unix 接口 傳輸信息。接口如下:
    
    <channel type='unix'>
    	<target type='virtio' name='org.qemu.guest_agent.0'/>
    	<address type='virtio-serial' controller='0' bus='0' port='2'/>
    </channel>
    

思考

其實兩個方案本質上都是調用libvirt底層API。只不過命令方式,只是自己封了一層調用命令。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章