On 03/11/2015 07:09 AM, Daniel P. Berrange wrote: > From: Nehal J Wani <nehaljw.kkd1@xxxxxxxxx> > > By querying the qemu guest agent with the QMP command > "guest-network-get-interfaces" and converting the received JSON > output to structured objects. > > Although "ifconfig" is deprecated, IP aliases created by "ifconfig" > are supported by this API. The legacy syntax of an IP alias is: > "<ifname>:<alias-name>". Since we want all aliases to be clubbed > under parent interface, simply stripping ":<alias-name>" suffices. > Note that IP aliases formed by "ip" aren't visible to "ifconfig", > and aliases created by "ip" do not have any specific name. But > we are lucky, as qemu guest agent detects aliases created by both. > > src/qemu/qemu_agent.h: > * Define qemuAgentGetInterfaces > > src/qemu/qemu_agent.c: > * Implement qemuAgentGetInterface > > src/qemu/qemu_driver.c: > * New function qemuGetDHCPInterfaces > * New function qemuDomainInterfaceAddresses > > src/remote_protocol-sructs: > * Define new structs > > tests/qemuagenttest.c: > * Add new test: testQemuAgentGetInterfaces > Test cases for IP aliases, 0 or multiple ipv4/ipv6 address(es) > > Signed-off-by: Nehal J Wani <nehaljw.kkd1@xxxxxxxxx> > --- > src/qemu/qemu_agent.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++ > src/qemu/qemu_agent.h | 4 + > src/qemu/qemu_driver.c | 175 ++++++++++++++++++++++++++++++++++++++++++ > tests/qemuagenttest.c | 188 +++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 571 insertions(+) > ACK - there's a NIT and a comment that follows, I'll let you decide how to handle. John > diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c > index 5fcc40f..8155006 100644 > --- a/src/qemu/qemu_agent.c > +++ b/src/qemu/qemu_agent.c > @@ -1953,3 +1953,207 @@ qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr **info, > virJSONValueFree(reply); > return ret; > } > + > +/* > + * qemuAgentGetInterfaces: > + * @mon: Agent monitor > + * @ifaces: pointer to an array of pointers pointing to interface objects > + * > + * Issue guest-network-get-interfaces to guest agent, which returns a > + * list of interfaces of a running domain along with their IP and MAC > + * addresses. > + * > + * Returns: number of interfaces on success, -1 on error. > + */ > +int > +qemuAgentGetInterfaces(qemuAgentPtr mon, > + virDomainInterfacePtr **ifaces) > +{ > + int ret = -1; > + size_t i, j; > + int size = -1; > + virJSONValuePtr cmd = NULL; > + virJSONValuePtr reply = NULL; > + virJSONValuePtr ret_array = NULL; > + size_t ifaces_count = 0; > + size_t addrs_count = 0; > + virDomainInterfacePtr *ifaces_ret = NULL; > + virHashTablePtr ifaces_store = NULL; > + char **ifname = NULL; > + > + /* Hash table to handle the interface alias */ > + if (!(ifaces_store = virHashCreate(ifaces_count, NULL))) { > + virHashFree(ifaces_store); > + return -1; > + } > + > + if (!(cmd = qemuAgentMakeCommand("guest-network-get-interfaces", NULL))) > + goto cleanup; > + > + if (qemuAgentCommand(mon, cmd, &reply, false, VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0 || > + qemuAgentCheckError(cmd, reply) < 0) { > + goto cleanup; > + } > + > + if (!(ret_array = virJSONValueObjectGet(reply, "return"))) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + _("qemu agent didn't provide 'return' field")); > + goto cleanup; > + } > + > + if ((size = virJSONValueArraySize(ret_array)) < 0) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + _("qemu agent didn't return an array of interfaces")); > + goto cleanup; > + } > + > + for (i = 0; i < size; i++) { > + virJSONValuePtr tmp_iface = virJSONValueArrayGet(ret_array, i); > + virJSONValuePtr ip_addr_arr = NULL; > + const char *hwaddr, *ifname_s, *name = NULL; > + int ip_addr_arr_size; > + virDomainInterfacePtr iface = NULL; > + > + /* Shouldn't happen but doesn't hurt to check neither */ > + if (!tmp_iface) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + _("something has went really wrong")); > + goto error; > + } > + > + /* interface name is required to be presented */ > + name = virJSONValueObjectGetString(tmp_iface, "name"); > + if (!name) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + _("qemu agent didn't provide 'name' field")); > + goto error; > + } > + > + /* Handle interface alias (<ifname>:<alias>) */ > + ifname = virStringSplit(name, ":", 2); > + ifname_s = ifname[0]; > + > + iface = virHashLookup(ifaces_store, ifname_s); > + > + /* If the hash table doesn't contain this iface, add it */ > + if (!iface) { > + if (VIR_EXPAND_N(ifaces_ret, ifaces_count, 1) < 0) > + goto error; > + > + if (VIR_ALLOC(ifaces_ret[ifaces_count - 1]) < 0) > + goto error; > + > + if (virHashAddEntry(ifaces_store, ifname_s, > + ifaces_ret[ifaces_count - 1]) < 0) > + goto error; > + > + iface = ifaces_ret[ifaces_count - 1]; > + iface->naddrs = 0; > + > + if (VIR_STRDUP(iface->name, ifname_s) < 0) > + goto error; > + > + hwaddr = virJSONValueObjectGetString(tmp_iface, "hardware-address"); > + if (!hwaddr) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + _("qemu agent didn't provide" > + " 'hardware-address' field")); > + goto error; > + } > + > + if (VIR_STRDUP(iface->hwaddr, hwaddr) < 0) > + goto error; > + } > + > + /* Has to be freed for each interface. */ > + virStringFreeList(ifname); > + > + /* as well as IP address which - moreover - > + * can be presented multiple times */ > + ip_addr_arr = virJSONValueObjectGet(tmp_iface, "ip-addresses"); > + if (!ip_addr_arr) > + continue; > + NIT: Extra line here > + > + if ((ip_addr_arr_size = virJSONValueArraySize(ip_addr_arr)) < 0) > + /* Mmm, empty 'ip-address'? */ > + goto error; > + > + /* If current iface already exists, continue with the count */ > + addrs_count = iface->naddrs; > + > + for (j = 0; j < ip_addr_arr_size; j++) { > + const char *type, *addr; > + virJSONValuePtr ip_addr_obj = virJSONValueArrayGet(ip_addr_arr, j); > + virDomainIPAddressPtr ip_addr; > + > + if (VIR_EXPAND_N(iface->addrs, addrs_count, 1) < 0) > + goto error; > + > + ip_addr = &iface->addrs[addrs_count - 1]; > + > + /* Shouldn't happen but doesn't hurt to check neither */ > + if (!ip_addr_obj) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + _("something has went really wrong")); Cannot remember if this text caught my attention the first time, but it is humorous... > + goto error; > + } > + > + type = virJSONValueObjectGetString(ip_addr_obj, "ip-address-type"); > + if (!type) { > + virReportError(VIR_ERR_INTERNAL_ERROR, > + _("qemu agent didn't provide 'ip-address-type'" > + " field for interface '%s'"), name); > + goto error; > + } else if (STREQ(type, "ipv4")) { > + ip_addr->type = VIR_IP_ADDR_TYPE_IPV4; > + } else if (STREQ(type, "ipv6")) { > + ip_addr->type = VIR_IP_ADDR_TYPE_IPV6; > + } else { > + virReportError(VIR_ERR_INTERNAL_ERROR, > + _("unknown ip address type '%s'"), > + type); > + goto error; > + } > + > + addr = virJSONValueObjectGetString(ip_addr_obj, "ip-address"); > + if (!addr) { > + virReportError(VIR_ERR_INTERNAL_ERROR, > + _("qemu agent didn't provide 'ip-address'" > + " field for interface '%s'"), name); > + goto error; > + } > + if (VIR_STRDUP(ip_addr->addr, addr) < 0) > + goto error; > + > + if (virJSONValueObjectGetNumberUint(ip_addr_obj, "prefix", > + &ip_addr->prefix) < 0) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + _("malformed 'prefix' field")); > + goto error; > + } > + } > + > + iface->naddrs = addrs_count; > + } > + > + *ifaces = ifaces_ret; > + ifaces_ret = NULL; > + ret = ifaces_count; > + > + cleanup: > + virJSONValueFree(cmd); > + virJSONValueFree(reply); > + virHashFree(ifaces_store); > + return ret; > + > + error: > + if (ifaces_ret) { > + for (i = 0; i < ifaces_count; i++) > + virDomainInterfaceFree(ifaces_ret[i]); > + } > + VIR_FREE(ifaces_ret); > + virStringFreeList(ifname); > + > + goto cleanup; > +} > diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h > index c983828..1cd5749 100644 > --- a/src/qemu/qemu_agent.h > +++ b/src/qemu/qemu_agent.h > @@ -108,4 +108,8 @@ int qemuAgentSetTime(qemuAgentPtr mon, > long long seconds, > unsigned int nseconds, > bool sync); > + > +int qemuAgentGetInterfaces(qemuAgentPtr mon, > + virDomainInterfacePtr **ifaces); > + > #endif /* __QEMU_AGENT_H__ */ > diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c > index ceceafa..5f21c22 100644 > --- a/src/qemu/qemu_driver.c > +++ b/src/qemu/qemu_driver.c > @@ -171,6 +171,9 @@ static int qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, > const char *path, int oflags, > bool *needUnlink, bool *bypassSecurityDriver); > > +static int qemuGetDHCPInterfaces(virDomainPtr dom, > + virDomainObjPtr vm, > + virDomainInterfacePtr **ifaces); > > virQEMUDriverPtr qemu_driver = NULL; > > @@ -19246,6 +19249,177 @@ qemuDomainGetFSInfo(virDomainPtr dom, > return ret; > } > > +static int > +qemuDomainInterfaceAddresses(virDomainPtr dom, > + virDomainInterfacePtr **ifaces, > + unsigned int source, > + unsigned int flags) > +{ > + virQEMUDriverPtr driver = dom->conn->privateData; > + qemuDomainObjPrivatePtr priv = NULL; > + virDomainObjPtr vm = NULL; > + int ret = -1; > + > + virCheckFlags(0, -1); > + > + if (!(vm = qemuDomObjFromDomain(dom))) > + goto cleanup; > + > + priv = vm->privateData; > + > + if (virDomainInterfaceAddressesEnsureACL(dom->conn, vm->def) < 0) > + goto cleanup; > + > + if (!virDomainObjIsActive(vm)) { > + virReportError(VIR_ERR_OPERATION_INVALID, "%s", > + _("domain is not running")); > + goto cleanup; > + } > + > + switch (source) { > + case VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE: > + ret = qemuGetDHCPInterfaces(dom, vm, ifaces); > + break; > + > + case VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT: > + if (priv->agentError) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + _("QEMU guest agent is not " > + "available due to an error")); > + goto cleanup; > + } > + > + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) > + goto cleanup; > + > + if (!virDomainObjIsActive(vm)) { > + virReportError(VIR_ERR_OPERATION_INVALID, "%s", > + _("domain is not running")); > + goto endjob; > + } > + > + if (!priv->agent) { > + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", > + _("QEMU guest agent is not configured")); > + goto endjob; > + } > + > + qemuDomainObjEnterAgent(vm); > + ret = qemuAgentGetInterfaces(priv->agent, ifaces); > + qemuDomainObjExitAgent(vm); > + > + endjob: > + qemuDomainObjEndJob(driver, vm); > + > + break; > + > + default: > + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, > + _("Unknown IP address data source %d"), > + source); > + break; > + } > + > + cleanup: > + if (vm) > + virObjectUnlock(vm); > + return ret; > +} > + > +static int > +qemuGetDHCPInterfaces(virDomainPtr dom, > + virDomainObjPtr vm, > + virDomainInterfacePtr **ifaces) > +{ > + int rv = -1; > + int n_leases = 0; > + size_t i, j; > + size_t ifaces_count = 0; > + virNetworkPtr network; > + char macaddr[VIR_MAC_STRING_BUFLEN]; > + virDomainInterfacePtr iface = NULL; > + virNetworkDHCPLeasePtr *leases = NULL; > + virDomainInterfacePtr *ifaces_ret = NULL; > + > + if (!dom->conn->networkDriver || > + !dom->conn->networkDriver->networkGetDHCPLeases) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + _("Network driver does not support DHCP lease query")); > + return -1; > + } > + > + for (i = 0; i < vm->def->nnets; i++) { > + if (vm->def->nets[i]->type != VIR_DOMAIN_NET_TYPE_NETWORK) > + continue; > + > + virMacAddrFormat(&(vm->def->nets[i]->mac), macaddr); > + network = virNetworkLookupByName(dom->conn, > + vm->def->nets[i]->data.network.name); > + > + if ((n_leases = virNetworkGetDHCPLeases(network, macaddr, > + &leases, 0)) < 0) > + goto error; > + > + if (n_leases) { > + if (VIR_EXPAND_N(ifaces_ret, ifaces_count, 1) < 0) > + goto error; > + > + if (VIR_ALLOC(ifaces_ret[ifaces_count - 1]) < 0) > + goto error; > + > + iface = ifaces_ret[ifaces_count - 1]; > + /* Assuming each lease corresponds to a separate IP */ > + iface->naddrs = n_leases; > + > + if (VIR_ALLOC_N(iface->addrs, iface->naddrs) < 0) > + goto error; > + > + if (VIR_STRDUP(iface->name, vm->def->nets[i]->ifname) < 0) > + goto cleanup; > + > + if (VIR_STRDUP(iface->hwaddr, macaddr) < 0) > + goto cleanup; > + } > + > + for (j = 0; j < n_leases; j++) { > + virNetworkDHCPLeasePtr lease = leases[j]; > + virDomainIPAddressPtr ip_addr = &iface->addrs[j]; > + > + if (VIR_STRDUP(ip_addr->addr, lease->ipaddr) < 0) > + goto cleanup; > + > + ip_addr->type = lease->type; > + ip_addr->prefix = lease->prefix; > + } > + > + for (j = 0; j < n_leases; j++) > + virNetworkDHCPLeaseFree(leases[j]); > + > + VIR_FREE(leases); > + } > + > + *ifaces = ifaces_ret; > + ifaces_ret = NULL; > + rv = ifaces_count; > + > + cleanup: > + if (leases) { > + for (i = 0; i < n_leases; i++) > + virNetworkDHCPLeaseFree(leases[i]); > + } > + VIR_FREE(leases); > + > + return rv; > + > + error: > + if (ifaces_ret) { > + for (i = 0; i < ifaces_count; i++) > + virDomainInterfaceFree(ifaces_ret[i]); > + } > + VIR_FREE(ifaces_ret); > + > + goto cleanup; > +} > > static virHypervisorDriver qemuHypervisorDriver = { > .name = QEMU_DRIVER_NAME, > @@ -19449,6 +19623,7 @@ static virHypervisorDriver qemuHypervisorDriver = { > .connectGetAllDomainStats = qemuConnectGetAllDomainStats, /* 1.2.8 */ > .nodeAllocPages = qemuNodeAllocPages, /* 1.2.9 */ > .domainGetFSInfo = qemuDomainGetFSInfo, /* 1.2.11 */ > + .domainInterfaceAddresses = qemuDomainInterfaceAddresses, /* 1.2.14 */ > }; > > > diff --git a/tests/qemuagenttest.c b/tests/qemuagenttest.c > index 54a45df..d8d1e41 100644 > --- a/tests/qemuagenttest.c > +++ b/tests/qemuagenttest.c > @@ -723,6 +723,193 @@ testQemuAgentTimeout(const void *data) > return ret; > } > > +static const char testQemuAgentGetInterfacesResponse[] = > + "{\"return\": " > + " [" > + " {\"name\":\"eth2\"," > + " \"hardware-address\":\"52:54:00:36:2a:e5\"" > + " }," > + " {\"name\":\"eth1:0\"," > + " \"ip-addresses\":" > + " [" > + " {\"ip-address-type\":\"ipv4\"," > + " \"ip-address\":\"192.168.10.91\"," > + " \"prefix\":24" > + " }," > + " {\"ip-address-type\":\"ipv6\"," > + " \"ip-address\":\"fe80::fc54:ff:fefe:4c4f\"," > + " \"prefix\":64" > + " }" > + " ]," > + " \"hardware-address\":\"52:54:00:d3:39:ee\"" > + " }," > + " {\"name\":\"eth0\"," > + " \"ip-addresses\":" > + " [" > + " {\"ip-address-type\":\"ipv6\"," > + " \"ip-address\":\"fe80::5054:ff:fe89:ad35\"," > + " \"prefix\":64" > + " }," > + " {\"ip-address-type\":\"ipv4\"," > + " \"ip-address\":\"192.168.102.142\"," > + " \"prefix\":24" > + " }," > + " {\"ip-address-type\":\"ipv4\"," > + " \"ip-address\":\"192.168.234.152\"," > + " \"prefix\":16" > + " }," > + " {\"ip-address-type\":\"ipv6\"," > + " \"ip-address\":\"fe80::5054:ff:fec3:68bb\"," > + " \"prefix\":64" > + " }" > + " ]," > + " \"hardware-address\":\"52:54:00:89:ad:35\"" > + " }," > + " {\"name\":\"eth1\"," > + " \"ip-addresses\":" > + " [" > + " {\"ip-address-type\":\"ipv4\"," > + " \"ip-address\":\"192.168.103.83\"," > + " \"prefix\":32" > + " }," > + " {\"ip-address-type\":\"ipv6\"," > + " \"ip-address\":\"fe80::5054:ff:fed3:39ee\"," > + " \"prefix\":64" > + " }" > + " ]," > + " \"hardware-address\":\"52:54:00:d3:39:ee\"" > + " }," > + " {\"name\":\"lo\"," > + " \"ip-addresses\":" > + " [" > + " {\"ip-address-type\":\"ipv4\"," > + " \"ip-address\":\"127.0.0.1\"," > + " \"prefix\":8" > + " }," > + " {\"ip-address-type\":\"ipv6\"," > + " \"ip-address\":\"::1\"," > + " \"prefix\":128" > + " }" > + " ]," > + " \"hardware-address\":\"00:00:00:00:00:00\"" > + " }" > + " ]" > + "}"; > + > +static int > +testQemuAgentGetInterfaces(const void *data) > +{ > + virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data; > + qemuMonitorTestPtr test = qemuMonitorTestNewAgent(xmlopt); > + size_t i; > + int ret = -1; > + int ifaces_count = 0; > + virDomainInterfacePtr *ifaces = NULL; > + > + if (!test) > + return -1; > + > + if (qemuMonitorTestAddAgentSyncResponse(test) < 0) > + goto cleanup; > + > + if (qemuMonitorTestAddItem(test, "guest-network-get-interfaces", > + testQemuAgentGetInterfacesResponse) < 0) > + goto cleanup; > + > + if ((ifaces_count = qemuAgentGetInterfaces(qemuMonitorTestGetAgent(test), > + &ifaces)) < 0) > + goto cleanup; > + > + if (ifaces_count != 4) { > + virReportError(VIR_ERR_INTERNAL_ERROR, > + "expected 4 interfaces, got %d", ret); > + goto cleanup; > + } > + > + if (STRNEQ(ifaces[0]->name, "eth2") || > + STRNEQ(ifaces[1]->name, "eth1") || > + STRNEQ(ifaces[2]->name, "eth0") || > + STRNEQ(ifaces[3]->name, "lo")) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + "unexpected return values for interface names"); > + goto cleanup; > + } > + > + if (STRNEQ(ifaces[0]->hwaddr, "52:54:00:36:2a:e5") || > + STRNEQ(ifaces[1]->hwaddr, "52:54:00:d3:39:ee") || > + STRNEQ(ifaces[2]->hwaddr, "52:54:00:89:ad:35") || > + STRNEQ(ifaces[3]->hwaddr, "00:00:00:00:00:00")) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + "unexpected return values for MAC addresses"); > + goto cleanup; > + } > + > + if (ifaces[0]->naddrs != 0 || > + ifaces[1]->naddrs != 4 || > + ifaces[2]->naddrs != 4 || > + ifaces[3]->naddrs != 2) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + "unexpected return values for number of IP addresses"); > + goto cleanup; > + } > + > + if (ifaces[1]->addrs[0].type != VIR_IP_ADDR_TYPE_IPV4 || > + ifaces[1]->addrs[1].type != VIR_IP_ADDR_TYPE_IPV6 || > + ifaces[1]->addrs[2].type != VIR_IP_ADDR_TYPE_IPV4 || > + ifaces[1]->addrs[3].type != VIR_IP_ADDR_TYPE_IPV6 || > + ifaces[2]->addrs[0].type != VIR_IP_ADDR_TYPE_IPV6 || > + ifaces[2]->addrs[1].type != VIR_IP_ADDR_TYPE_IPV4 || > + ifaces[2]->addrs[2].type != VIR_IP_ADDR_TYPE_IPV4 || > + ifaces[2]->addrs[3].type != VIR_IP_ADDR_TYPE_IPV6 || > + ifaces[3]->addrs[0].type != VIR_IP_ADDR_TYPE_IPV4 || > + ifaces[3]->addrs[1].type != VIR_IP_ADDR_TYPE_IPV6) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + "unexpected return values for IP address types"); > + goto cleanup; > + } > + > + if (ifaces[1]->addrs[0].prefix != 24 || > + ifaces[1]->addrs[1].prefix != 64 || > + ifaces[1]->addrs[2].prefix != 32 || > + ifaces[1]->addrs[3].prefix != 64 || > + ifaces[2]->addrs[0].prefix != 64 || > + ifaces[2]->addrs[1].prefix != 24 || > + ifaces[2]->addrs[2].prefix != 16 || > + ifaces[2]->addrs[3].prefix != 64 || > + ifaces[3]->addrs[0].prefix != 8 || > + ifaces[3]->addrs[1].prefix != 128) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + "unexpected return values for IP address prefix"); > + goto cleanup; > + } > + > + if (STRNEQ(ifaces[1]->addrs[0].addr, "192.168.10.91") || > + STRNEQ(ifaces[1]->addrs[1].addr, "fe80::fc54:ff:fefe:4c4f") || > + STRNEQ(ifaces[1]->addrs[2].addr, "192.168.103.83") || > + STRNEQ(ifaces[1]->addrs[3].addr, "fe80::5054:ff:fed3:39ee") || > + STRNEQ(ifaces[2]->addrs[0].addr, "fe80::5054:ff:fe89:ad35") || > + STRNEQ(ifaces[2]->addrs[1].addr, "192.168.102.142") || > + STRNEQ(ifaces[2]->addrs[2].addr, "192.168.234.152") || > + STRNEQ(ifaces[2]->addrs[3].addr, "fe80::5054:ff:fec3:68bb") || > + STRNEQ(ifaces[3]->addrs[0].addr, "127.0.0.1") || > + STRNEQ(ifaces[3]->addrs[1].addr, "::1")) { > + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", > + "unexpected return values for IP address values"); > + goto cleanup; > + } > + > + ret = 0; > + > + cleanup: > + qemuMonitorTestFree(test); > + if (ifaces) { > + for (i = 0; i < ifaces_count; i++) > + virDomainInterfaceFree(ifaces[i]); > + } > + VIR_FREE(ifaces); > + > + return ret; > +} > > static int > mymain(void) > @@ -753,6 +940,7 @@ mymain(void) > DO_TEST(Shutdown); > DO_TEST(CPU); > DO_TEST(ArbitraryCommand); > + DO_TEST(GetInterfaces); > > DO_TEST(Timeout); /* Timeout should always be called last */ > > -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list