libvirt:获取 guest 账户的IP地址

文章目录

  • libvirt 获取 guest账户的IP方案
    • 方案
      • 方案一:virsh 命令
      • 方案二:调用API接口
    • 相关错误解决方法:
    • 思考

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 
#include 
#include 
#include 
#include 
#include 

#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。只不过命令方式,只是自己封了一层调用命令。

你可能感兴趣的:(Linux)