[libvirt] [PATCH v2 0/8] Dump domain's IP addresses

It's been a while since I tried get this in. I've reworked the patches, the exposed API, and start new round of reviews. Moreover, during work I've come to point, where extending qemu guest agent seemed wise: http://lists.nongnu.org/archive/html/qemu-devel/2012-12/msg03264.html Therefore I am introducing 'flags' field, which can contain some boolean values we don't have yet, e.g. IFF_UP, IFF_PROMISC, etc. Re: 'dstaddr' field in struct _virDomainInterfaceIPAddress; It's basically a join of: union { char *dstaddr; /* for IFF_POINTOPOINT interface */ char *broadaddr; /* for IFF_BROADCAST interface */ } Since an interface cannot has both flags set (see man 3 getifaddrs) I've joined both into one 'char *dstaddr'. I know it is not mnemonic as the union, so I left it for discussion. I can change it if you want to. diff to v1: -don't return array of objects, but array of pointer to objects instead Michal Privoznik (8): Introduce virDomainInterfacesAddresses API Introduce virDomainInterfaceFree API qemu_agent: Implement 'guest-network-get-interfaces' command handling qemu: Implement qemuDomainInterfacesAddresses virsh: Expose virDomainInterfacesAddresses remote: Implement virDomainInterfacesAddresses python: Expose virDomainInterfacesAddresses python: create example for dumping domain IP addresses daemon/remote.c | 140 ++++++++++++++++++++++++++++++++ examples/python/Makefile.am | 2 +- examples/python/README | 1 + examples/python/domipaddrs.py | 50 ++++++++++++ include/libvirt/libvirt.h.in | 51 ++++++++++++ python/generator.py | 3 + python/libvirt-override-api.xml | 7 ++ python/libvirt-override.c | 120 ++++++++++++++++++++++++++++ src/driver.h | 6 ++ src/libvirt.c | 107 +++++++++++++++++++++++++ src/libvirt_public.syms | 7 ++ src/qemu/qemu_agent.c | 171 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 2 + src/qemu/qemu_driver.c | 68 ++++++++++++++++ src/remote/remote_driver.c | 94 ++++++++++++++++++++++ src/remote/remote_protocol.x | 27 ++++++- src/remote_protocol-structs | 27 +++++++ tools/virsh-domain.c | 104 ++++++++++++++++++++++++ 18 files changed, 985 insertions(+), 2 deletions(-) create mode 100755 examples/python/domipaddrs.py -- 1.8.0.2

This API returns dynamically allocated array of IP addresses for all domain interfaces. --- include/libvirt/libvirt.h.in | 49 ++++++++++++++++++++++++++++++ python/generator.py | 2 ++ src/driver.h | 6 ++++ src/libvirt.c | 71 ++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 6 ++++ 5 files changed, 134 insertions(+) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index cc3fe13..3ee6688 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -4563,6 +4563,55 @@ int virDomainFSTrim(virDomainPtr dom, unsigned long long minimum, unsigned int flags); +typedef enum { + VIR_INTERFACE_BROADCAST = (1 << 0), /* Broadcast address valid. */ + VIR_INTERFACE_PPP = (1 << 1), /* Interface is point-to-point. */ + /* we can add new flags here */ +} virDomainInterfaceType; + +typedef enum { + VIR_IP_ADDR_TYPE_IPV4, + VIR_IP_ADDR_TYPE_IPV6, + +#ifdef VIR_ENUM_SENTINELS + VIR_IP_ADDR_TYPE_LAST +#endif +} virIPAddrType; + + +typedef struct _virDomainInterfaceIPAddress virDomainIPAddress; +typedef virDomainIPAddress *virDomainIPAddressPtr; +struct _virDomainInterfaceIPAddress { + int type; /* virIPAddrType */ + char *addr; /* IP address */ + int prefix; /* IP address prefix */ + char *dstaddr; /* Broadcast address (if @flags & VIR_INTERFACE_BROADCAST), + PPP destination address (if @flags & VIR_INTERFACE_PPP) */ +}; + +typedef struct _virDomainInterface virDomainInterface; +typedef virDomainInterface *virDomainInterfacePtr; +struct _virDomainInterface { + char *name; /* interface name */ + unsigned int flags; /* virDomainInterfaceType */ + char *hwaddr; /* hardware address */ + unsigned int ip_addrs_count; /* number of items in @ip_addr */ + virDomainIPAddressPtr ip_addrs; /* array of IP addresses */ +}; + +typedef enum { + VIR_DOMAIN_INTERFACE_ADDRS_DEFAULT = 0, /* hypervisor choice */ + VIR_DOMAIN_INTERFACE_ADDRS_GUEST_AGENT = 1, /* use guest agent */ + VIR_DOMAIN_INTERFACE_ADDRS_NWFILTER = 2, /* use nwfilter learning code */ + /* etc ... */ +} virDomainInterfacesAddressesMethod; + + +int virDomainInterfacesAddresses(virDomainPtr domain, + virDomainInterfacePtr **ifaces, + unsigned int method, + unsigned int flags); + /** * virSchedParameterType: * diff --git a/python/generator.py b/python/generator.py index bae4edc..ee39e51 100755 --- a/python/generator.py +++ b/python/generator.py @@ -243,6 +243,7 @@ skipped_types = { 'virEventHandleCallback': "No function types in python", 'virEventTimeoutCallback': "No function types in python", 'virDomainBlockJobInfoPtr': "Not implemented yet", + 'virDomainInterfacePtr': "Not implemented yet", } ####################################################################### @@ -428,6 +429,7 @@ skip_impl = ( 'virNodeGetMemoryParameters', 'virNodeSetMemoryParameters', 'virNodeGetCPUMap', + 'virDomainInterfacesAddresses', ) qemu_skip_impl = ( diff --git a/src/driver.h b/src/driver.h index 64d652f..dc93ffb 100644 --- a/src/driver.h +++ b/src/driver.h @@ -914,6 +914,11 @@ typedef int const char *mountPoint, unsigned long long minimum, unsigned int flags); +typedef int + (*virDrvDomainInterfacesAddresses)(virDomainPtr domain, + virDomainInterfacePtr **ifaces, + unsigned int method, + unsigned int flags); /** * _virDriver: @@ -1107,6 +1112,7 @@ struct _virDriver { virDrvNodeGetCPUMap nodeGetCPUMap; virDrvDomainFSTrim domainFSTrim; virDrvDomainSendProcessSignal domainSendProcessSignal; + virDrvDomainInterfacesAddresses domainInterfacesAddresses; }; typedef int diff --git a/src/libvirt.c b/src/libvirt.c index bf674d1..6f0de36 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -20424,3 +20424,74 @@ error: virDispatchError(dom->conn); return -1; } + +/** + * virDomainInterfacesAddresses: + * @domain: domain object + * @ifaces: array of @domain interfaces + * @method: which method use, one of virDomainInterfacesAddressesMethod + * @flags: extra flags, not used yet, so callers should always pass 0 + * + * Return an array of interfaces presented in given @domain among with + * their IP and HW addresses. Single interface can have multiple or even + * none IP address. + * + * This API dynamically allocates the virDomainInterfacePtr struct based on + * how much interfaces domain has, usually there's 1:1 correlation between + * interfaces seen from the host and interfaces seen from the guest. The + * count of elements allocated is then returned. + * + * Depending on selected @method, guest agent or NWFilter can be used. The + * guest agent method communicates with a running agent within guest and + * dumps all interfaces within their addresses. This, obviously requires + * guest agent to be configured and running and will fail otherwise. + * However, this gives interfaces from guest point of view, that is, names + * and HW addresses are those as seen from the guest. When the NWFilter + * method is used, libvirt starts snooping on guests interfaces and try to + * gather as much info as possible. This returns interface name and HW + * address as seen from the host perspective. + * + * @ifaces->name is never NULL, other pointers MIGHT be NULL. + * + * The caller *must* free @ifaces when no longer needed. + * + * Returns -1 on failure + * otherwise the count of items stored into @ifaces. + */ +int +virDomainInterfacesAddresses(virDomainPtr domain, + virDomainInterfacePtr **ifaces, + unsigned int method, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "ifaces=%p, methd=%d, flags=%x", + ifaces, method, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + virCheckNonNullArgGoto(ifaces, error); + + if (conn->driver->domainInterfacesAddresses) { + int ret; + ret = conn->driver->domainInterfacesAddresses(domain, ifaces, + method, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index e3d63d3..8e7c5d2 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -580,4 +580,10 @@ LIBVIRT_1.0.1 { virDomainSendProcessSignal; } LIBVIRT_1.0.0; +LIBVIRT_1.0.2 { + global: + virDomainInterfacesAddresses; +} LIBVIRT_1.0.1; + + # .... define new API here using predicted next version number .... -- 1.8.0.2

On 01/03/2013 08:46 AM, Michal Privoznik wrote:
This API returns dynamically allocated array of IP addresses for all domain interfaces. --- include/libvirt/libvirt.h.in | 49 ++++++++++++++++++++++++++++++ python/generator.py | 2 ++ src/driver.h | 6 ++++ src/libvirt.c | 71 ++++++++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 6 ++++ 5 files changed, 134 insertions(+)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index cc3fe13..3ee6688 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -4563,6 +4563,55 @@ int virDomainFSTrim(virDomainPtr dom, unsigned long long minimum, unsigned int flags);
+typedef enum { + VIR_INTERFACE_BROADCAST = (1 << 0), /* Broadcast address valid. */ + VIR_INTERFACE_PPP = (1 << 1), /* Interface is point-to-point. */
I think you mean VIR_INTERFACE_PTP, not _PPP.
+ /* we can add new flags here */ +} virDomainInterfaceType; + +typedef enum { + VIR_IP_ADDR_TYPE_IPV4, + VIR_IP_ADDR_TYPE_IPV6, + +#ifdef VIR_ENUM_SENTINELS + VIR_IP_ADDR_TYPE_LAST +#endif +} virIPAddrType; + + +typedef struct _virDomainInterfaceIPAddress virDomainIPAddress; +typedef virDomainIPAddress *virDomainIPAddressPtr; +struct _virDomainInterfaceIPAddress { + int type; /* virIPAddrType */ + char *addr; /* IP address */ + int prefix; /* IP address prefix */ + char *dstaddr; /* Broadcast address (if @flags & VIR_INTERFACE_BROADCAST), + PPP destination address (if @flags & VIR_INTERFACE_PPP) */ +};
I dislike creating yet another struct to represent IP address info when we already can do that internally with virSocketAddr, but virSocketAddr is too complicated (and has platform-specific bits to boot) to put it in the public API, and every other IP address in the public API is represented with XML, so I guess this is okay.
+ +typedef struct _virDomainInterface virDomainInterface; +typedef virDomainInterface *virDomainInterfacePtr; +struct _virDomainInterface { + char *name; /* interface name */ + unsigned int flags; /* virDomainInterfaceType */ + char *hwaddr; /* hardware address */ + unsigned int ip_addrs_count; /* number of items in @ip_addr */ + virDomainIPAddressPtr ip_addrs; /* array of IP addresses */ +}; + +typedef enum { + VIR_DOMAIN_INTERFACE_ADDRS_DEFAULT = 0, /* hypervisor choice */ + VIR_DOMAIN_INTERFACE_ADDRS_GUEST_AGENT = 1, /* use guest agent */ + VIR_DOMAIN_INTERFACE_ADDRS_NWFILTER = 2, /* use nwfilter learning code */ + /* etc ... */
You should remove the "etc" line.
+} virDomainInterfacesAddressesMethod; + + +int virDomainInterfacesAddresses(virDomainPtr domain, + virDomainInterfacePtr **ifaces, + unsigned int method, + unsigned int flags); + /** * virSchedParameterType: * diff --git a/python/generator.py b/python/generator.py index bae4edc..ee39e51 100755 --- a/python/generator.py +++ b/python/generator.py @@ -243,6 +243,7 @@ skipped_types = { 'virEventHandleCallback': "No function types in python", 'virEventTimeoutCallback': "No function types in python", 'virDomainBlockJobInfoPtr': "Not implemented yet", + 'virDomainInterfacePtr': "Not implemented yet", }
####################################################################### @@ -428,6 +429,7 @@ skip_impl = ( 'virNodeGetMemoryParameters', 'virNodeSetMemoryParameters', 'virNodeGetCPUMap', + 'virDomainInterfacesAddresses', )
qemu_skip_impl = ( diff --git a/src/driver.h b/src/driver.h index 64d652f..dc93ffb 100644 --- a/src/driver.h +++ b/src/driver.h @@ -914,6 +914,11 @@ typedef int const char *mountPoint, unsigned long long minimum, unsigned int flags); +typedef int + (*virDrvDomainInterfacesAddresses)(virDomainPtr domain, + virDomainInterfacePtr **ifaces, + unsigned int method, + unsigned int flags);
/** * _virDriver: @@ -1107,6 +1112,7 @@ struct _virDriver { virDrvNodeGetCPUMap nodeGetCPUMap; virDrvDomainFSTrim domainFSTrim; virDrvDomainSendProcessSignal domainSendProcessSignal; + virDrvDomainInterfacesAddresses domainInterfacesAddresses; };
typedef int diff --git a/src/libvirt.c b/src/libvirt.c index bf674d1..6f0de36 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -20424,3 +20424,74 @@ error: virDispatchError(dom->conn); return -1; } + +/** + * virDomainInterfacesAddresses:
The repeated plurality sounds a bit clumsy, but it *is* the most correct I guess (since virDomainInterfaceAddresses would imply that it's only returning the addresses of a single interface). However, most (unfortunately not all) libvirt APIs that retrieve information about an entity include a verb in the name, usually "Get". So how about naming it "virDomainGetInterfacesAddresses" (following all the other virDomainGet* function names). (BTW, it seems a bit bothersome to me that we will now have virDomainGetInterfaceParameters() (which merely returns duplicates of bandwidth information already present in the domain XML for a single interface), virDomainInterfaceStats() (which returns traffic stats for a single interface), and this new function, which returns addresses for all interfaces. It's true that the source of each piece of information is different (the first is from the domain's configuration, the 2nd is from ioctls run on the host, and the third is from either querying the guest agent, or from asking nwfilter for the value it's learned via snooping the traffic), but that's an implementation detail that shouldn't matter to the user of the libvirt API. It would be nice if this was somehow consolidated, although I'm not expecting you to do it :-))
+ * @domain: domain object + * @ifaces: array of @domain interfaces + * @method: which method use, one of virDomainInterfacesAddressesMethod + * @flags: extra flags, not used yet, so callers should always pass 0 + * + * Return an array of interfaces presented in given @domain among with s/presented in given/present in the given/ s/among/along/
+ * their IP and HW addresses. Single interface can have multiple or even + * none IP address.
s/Single/A single/ s/none/no/
+ * + * This API dynamically allocates the virDomainInterfacePtr struct based on
s/struct/object/
+ * how much interfaces domain has, usually there's 1:1 correlation between
s/much interfaces domain/many interfaces the domain/
+ * interfaces seen from the host and interfaces seen from the guest. The + * count of elements allocated is then returned. + * + * Depending on selected @method, guest agent or NWFilter can be used. The + * guest agent method communicates with a running agent within guest and + * dumps all interfaces within their addresses. This, obviously requires + * guest agent to be configured and running and will fail otherwise. + * However, this gives interfaces from guest point of view, that is, names + * and HW addresses are those as seen from the guest. When the NWFilter + * method is used, libvirt starts snooping on guests interfaces and try to + * gather as much info as possible. This returns interface name and HW + * address as seen from the host perspective.
Depending on the selected @method, the address information will be retrieved from a different source: the guest agent method requests the information from an agent running in the guest. This obviously requires a guest agent to be configured and running, and will otherwise fail. However, the guest agent method provides the information from the guest's point of view - the interface names and hardware addresses are those seen by the guest OS. When the NWFilter method is used, the information is requested from libvirt's NWFilter driver, which "snoops" traffic on the guest's network connections and deduces IP and MAC address information from examining that traffic; in this case, the interface name will be the name of the tap device created by libvirt to connect the device to the host's network, *not* the name of the device as seen by the guest OS.
+ * + * @ifaces->name is never NULL, other pointers MIGHT be NULL. + * + * The caller *must* free @ifaces when no longer needed. + * + * Returns -1 on failure + * otherwise the count of items stored into @ifaces. + */ +int +virDomainInterfacesAddresses(virDomainPtr domain, + virDomainInterfacePtr **ifaces, + unsigned int method, + unsigned int flags) +{ + virConnectPtr conn; + + VIR_DOMAIN_DEBUG(domain, "ifaces=%p, methd=%d, flags=%x", + ifaces, method, flags); + + virResetLastError(); + + if (!VIR_IS_CONNECTED_DOMAIN(domain)) { + virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__); + goto error; + } + + conn = domain->conn; + + virCheckNonNullArgGoto(ifaces, error); + + if (conn->driver->domainInterfacesAddresses) { + int ret; + ret = conn->driver->domainInterfacesAddresses(domain, ifaces, + method, flags); + if (ret < 0) + goto error; + return ret; + } + + virLibDomainError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(domain->conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index e3d63d3..8e7c5d2 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -580,4 +580,10 @@ LIBVIRT_1.0.1 { virDomainSendProcessSignal; } LIBVIRT_1.0.0;
+LIBVIRT_1.0.2 { + global: + virDomainInterfacesAddresses; +} LIBVIRT_1.0.1; + + # .... define new API here using predicted next version number ....

This is just a free function for array of virDomainInterfacePtr as returned by virDomainInterfacesAddresses API. --- include/libvirt/libvirt.h.in | 2 ++ python/generator.py | 1 + src/libvirt.c | 36 ++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 40 insertions(+) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 3ee6688..4199cd3 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -4612,6 +4612,8 @@ int virDomainInterfacesAddresses(virDomainPtr domain, unsigned int method, unsigned int flags); +void virDomainInterfaceFree(virDomainInterfacePtr iface); + /** * virSchedParameterType: * diff --git a/python/generator.py b/python/generator.py index ee39e51..e0a6f10 100755 --- a/python/generator.py +++ b/python/generator.py @@ -430,6 +430,7 @@ skip_impl = ( 'virNodeSetMemoryParameters', 'virNodeGetCPUMap', 'virDomainInterfacesAddresses', + 'virDomainInterfaceFree', ) qemu_skip_impl = ( diff --git a/src/libvirt.c b/src/libvirt.c index 6f0de36..6f4ce60 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -20495,3 +20495,39 @@ error: virDispatchError(domain->conn); return -1; } + +/** + * virDomainInterfaceFree: + * @iface: array of interfaces + * + * Free an interface as returned by virDomainInterfacesAddresses. + * The @ifaces pointer is freed and should not be used thereafter. + * Basic usage is: + * + * virDomainInterfacePtr *iface = NULL; + * int i, j; + * + * i = virDomainInterfacesAddresses(dom, &iface, method, flags); + * + * Do something here ...; + * + * for (j = 0; j < i; j ++) + * virDomainInterfaceFree(iface[j]); + * free(iface); + */ +void +virDomainInterfaceFree(virDomainInterfacePtr iface) +{ + int i; + VIR_DEBUG("iface=%p", iface); + + + VIR_FREE(iface->name); + VIR_FREE(iface->hwaddr); + for (i = 0; i < iface->ip_addrs_count; i++) { + VIR_FREE(iface->ip_addrs[i].addr); + VIR_FREE(iface->ip_addrs[i].dstaddr); + } + VIR_FREE(iface->ip_addrs); + VIR_FREE(iface); +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 8e7c5d2..a401363 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -583,6 +583,7 @@ LIBVIRT_1.0.1 { LIBVIRT_1.0.2 { global: virDomainInterfacesAddresses; + virDomainInterfaceFree; } LIBVIRT_1.0.1; -- 1.8.0.2

This command returns an array of all guest interfaces among with their IP and HW addresses. --- src/qemu/qemu_agent.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 2 + 2 files changed, 173 insertions(+) diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index bb421bd..409ba04 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -1474,3 +1474,174 @@ qemuAgentFSTrim(qemuAgentPtr mon, virJSONValueFree(reply); return ret; } + +static int +getInterfaces(virJSONValuePtr reply, + virDomainInterfacePtr **ifaces) +{ + int ret = -1; + int i, size = 0; + virJSONValuePtr replyArray = NULL; + + if (!(replyArray = virJSONValueObjectGet(reply, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent did not provide 'return' object")); + goto cleanup; + } + + if ((size = virJSONValueArraySize(replyArray)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent did not provide any interface")); + goto cleanup; + } + + if (size && VIR_ALLOC_N(*ifaces, size * sizeof(virDomainInterfacePtr)) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < size; i++) { + virJSONValuePtr jsonIface = virJSONValueArrayGet(replyArray, i); + virDomainInterfacePtr tmpIface = NULL; + virJSONValuePtr jsonIpAddrArr = NULL; + int j, jsonIpAddrArrSize = 0; + const char *name, *hwaddr; + + if (VIR_ALLOC(tmpIface) < 0) { + virReportOOMError(); + goto cleanup; + } + + (*ifaces)[i] = tmpIface; + /* should not happen, but doesn't hurt to check */ + if (!jsonIface) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Something went really wrong while processing " + "guest agent reply")); + goto cleanup; + } + + name = virJSONValueObjectGetString(jsonIface, "name"); + if (!name) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent did not provide 'name' object")); + goto cleanup; + } + + if (!(tmpIface->name = strdup(name))) { + virReportOOMError(); + goto cleanup; + } + + /* hwaddr might be omitted */ + hwaddr = virJSONValueObjectGetString(jsonIface, "hardware-address"); + if (hwaddr && !(tmpIface->hwaddr = strdup(hwaddr))) { + virReportOOMError(); + goto cleanup; + } + + /* as well as ip-addresses */ + jsonIpAddrArr = virJSONValueObjectGet(jsonIface, "ip-addresses"); + if (!jsonIpAddrArr) + continue; + + if ((jsonIpAddrArrSize = virJSONValueArraySize(jsonIpAddrArr)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent provided malformed " + "ip-addresses field")); + goto cleanup; + } + + if (VIR_ALLOC_N(tmpIface->ip_addrs, jsonIpAddrArrSize) < 0) { + virReportOOMError(); + goto cleanup; + } + tmpIface->ip_addrs_count = jsonIpAddrArrSize; + + for (j = 0; j < jsonIpAddrArrSize; j++) { + virJSONValuePtr jsonIpAddr = virJSONValueArrayGet(jsonIpAddrArr, j); + virDomainIPAddressPtr tmpIpAddr = &(tmpIface->ip_addrs[j]); + const char *ipAddr, *ipAddrType; + + if (!(ipAddr = virJSONValueObjectGetString(jsonIpAddr, + "ip-address"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu-agent didn't provided " + "an ip-address field")); + goto cleanup; + } + + if (!(tmpIpAddr->addr = strdup(ipAddr))) { + virReportOOMError(); + goto cleanup; + } + + if (!(ipAddrType = virJSONValueObjectGetString(jsonIpAddr, + "ip-address-type"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu-agent didn't provided " + "an ip-address-type field")); + goto cleanup; + } + + if (STREQ(ipAddrType, "ipv4")) + tmpIpAddr->type = VIR_IP_ADDR_TYPE_IPV4; + else if (STREQ(ipAddrType, "ipv6")) + tmpIpAddr->type = VIR_IP_ADDR_TYPE_IPV6; + else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("qemu agent provided unknown " + "ip-address-type '%s'"), + ipAddrType); + goto cleanup; + } + + if (virJSONValueObjectGetNumberInt(jsonIpAddr, "prefix", + &(tmpIpAddr->prefix)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent provided " + "malformed prefix field")); + goto cleanup; + } + + /* Nor broadcast address is reported */ + } + } + + ret = size; + +cleanup: + if (ret < 0) { + for (i = 0; i < size; i++) + virDomainInterfaceFree((*ifaces)[i]); + VIR_FREE(*ifaces); + } + return ret; +} + +int +qemuAgentGetInterfaces(qemuAgentPtr mon, + virDomainInterfacePtr **ifaces) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + cmd = qemuAgentMakeCommand("guest-network-get-interfaces", NULL); + + if (!cmd) + return ret; + + ret = qemuAgentCommand(mon, cmd, &reply, + VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK); + + if (reply && ret == 0) + ret = qemuAgentCheckError(cmd, reply); + + if (ret == 0) + ret = getInterfaces(reply, ifaces); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h index dad068b..0c22920 100644 --- a/src/qemu/qemu_agent.h +++ b/src/qemu/qemu_agent.h @@ -85,4 +85,6 @@ int qemuAgentArbitraryCommand(qemuAgentPtr mon, int timeout); int qemuAgentFSTrim(qemuAgentPtr mon, unsigned long long minimum); +int qemuAgentGetInterfaces(qemuAgentPtr mon, + virDomainInterfacePtr **ifaces); #endif /* __QEMU_AGENT_H__ */ -- 1.8.0.2

On 01/03/13 14:46, Michal Privoznik wrote:
This command returns an array of all guest interfaces among with their IP and HW addresses. --- src/qemu/qemu_agent.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 2 + 2 files changed, 173 insertions(+)
diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index bb421bd..409ba04 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -1474,3 +1474,174 @@ qemuAgentFSTrim(qemuAgentPtr mon, virJSONValueFree(reply); return ret; } + +static int +getInterfaces(virJSONValuePtr reply, + virDomainInterfacePtr **ifaces) +{ + int ret = -1; + int i, size = 0; + virJSONValuePtr replyArray = NULL; + + if (!(replyArray = virJSONValueObjectGet(reply, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent did not provide 'return' object")); + goto cleanup; + } + + if ((size = virJSONValueArraySize(replyArray)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent did not provide any interface")); + goto cleanup; + } + + if (size && VIR_ALLOC_N(*ifaces, size * sizeof(virDomainInterfacePtr)) < 0) { + virReportOOMError();
size is > 0 now and *ifaces = NULL, if you jump to cleanup you'll dereference it. I'd suggest just returning -1 here and above.
+ goto cleanup; + } + + for (i = 0; i < size; i++) { + virJSONValuePtr jsonIface = virJSONValueArrayGet(replyArray, i); + virDomainInterfacePtr tmpIface = NULL; + virJSONValuePtr jsonIpAddrArr = NULL; + int j, jsonIpAddrArrSize = 0; + const char *name, *hwaddr; + + if (VIR_ALLOC(tmpIface) < 0) { + virReportOOMError(); + goto cleanup; + } + + (*ifaces)[i] = tmpIface; + /* should not happen, but doesn't hurt to check */ + if (!jsonIface) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Something went really wrong while processing " + "guest agent reply")); + goto cleanup; + } + + name = virJSONValueObjectGetString(jsonIface, "name"); + if (!name) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent did not provide 'name' object")); + goto cleanup; + } + + if (!(tmpIface->name = strdup(name))) { + virReportOOMError(); + goto cleanup; + } + + /* hwaddr might be omitted */ + hwaddr = virJSONValueObjectGetString(jsonIface, "hardware-address"); + if (hwaddr && !(tmpIface->hwaddr = strdup(hwaddr))) { + virReportOOMError(); + goto cleanup; + } + + /* as well as ip-addresses */ + jsonIpAddrArr = virJSONValueObjectGet(jsonIface, "ip-addresses"); + if (!jsonIpAddrArr) + continue; + + if ((jsonIpAddrArrSize = virJSONValueArraySize(jsonIpAddrArr)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent provided malformed " + "ip-addresses field")); + goto cleanup; + } + + if (VIR_ALLOC_N(tmpIface->ip_addrs, jsonIpAddrArrSize) < 0) { + virReportOOMError(); + goto cleanup; + } + tmpIface->ip_addrs_count = jsonIpAddrArrSize; + + for (j = 0; j < jsonIpAddrArrSize; j++) {
extra space
+ virJSONValuePtr jsonIpAddr = virJSONValueArrayGet(jsonIpAddrArr, j); + virDomainIPAddressPtr tmpIpAddr = &(tmpIface->ip_addrs[j]); + const char *ipAddr, *ipAddrType; + + if (!(ipAddr = virJSONValueObjectGetString(jsonIpAddr, + "ip-address"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu-agent didn't provided " + "an ip-address field"));
didn't provide
+ goto cleanup; + } + + if (!(tmpIpAddr->addr = strdup(ipAddr))) { + virReportOOMError(); + goto cleanup; + } + + if (!(ipAddrType = virJSONValueObjectGetString(jsonIpAddr, + "ip-address-type"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu-agent didn't provided " + "an ip-address-type field"));
here too
+ goto cleanup; + } + + if (STREQ(ipAddrType, "ipv4")) + tmpIpAddr->type = VIR_IP_ADDR_TYPE_IPV4; + else if (STREQ(ipAddrType, "ipv6")) + tmpIpAddr->type = VIR_IP_ADDR_TYPE_IPV6; + else { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("qemu agent provided unknown " + "ip-address-type '%s'"), + ipAddrType); + goto cleanup; + } + + if (virJSONValueObjectGetNumberInt(jsonIpAddr, "prefix", + &(tmpIpAddr->prefix)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("qemu agent provided " + "malformed prefix field")); + goto cleanup; + } + + /* Nor broadcast address is reported */ + } + } + + ret = size; + +cleanup: + if (ret < 0) { + for (i = 0; i < size; i++) + virDomainInterfaceFree((*ifaces)[i]);
(*ifaces)[i] might be NULL here, but virDomainInterfaceFree doesn't check for that.
+ VIR_FREE(*ifaces); + } + return ret; +} +

--- src/qemu/qemu_driver.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b8bb36b..662d956 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14520,6 +14520,73 @@ cleanup: return ret; } +static int +qemuDomainInterfacesAddresses(virDomainPtr dom, + virDomainInterfacePtr **ifaces, + unsigned int method, + unsigned int flags) +{ + virQEMUDriverPtr driver = dom->conn->privateData; + virDomainObjPtr vm; + int ret = -1; + qemuDomainObjPrivatePtr priv; + + virCheckFlags(0, -1); + + if (method != VIR_DOMAIN_INTERFACE_ADDRS_DEFAULT && + method != VIR_DOMAIN_INTERFACE_ADDRS_GUEST_AGENT) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("Only guest agent method is supported for now")); + return -1; + } + + if (!(vm = qemuDomObjFromDomain(dom))) + goto cleanup; + + priv = vm->privateData; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("domain is not running")); + goto cleanup; + } + + if (!priv->agent) { + virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s", + _("QEMU guest agent is not configured")); + goto cleanup; + } + + if (priv->agentError) { + virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%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; + } + + qemuDomainObjEnterAgent(driver, vm); + ret = qemuAgentGetInterfaces(priv->agent, ifaces); + qemuDomainObjExitAgent(driver, vm); + +endjob: + if (qemuDomainObjEndJob(driver, vm) == 0) + vm = NULL; + +cleanup: + if (vm) + virDomainObjUnlock(vm); + return ret; +} + static virDriver qemuDriver = { .no = VIR_DRV_QEMU, .name = QEMU_DRIVER_NAME, @@ -14694,6 +14761,7 @@ static virDriver qemuDriver = { .nodeSetMemoryParameters = nodeSetMemoryParameters, /* 0.10.2 */ .nodeGetCPUMap = nodeGetCPUMap, /* 1.0.0 */ .domainFSTrim = qemuDomainFSTrim, /* 1.0.1 */ + .domainInterfacesAddresses = qemuDomainInterfacesAddresses, /* 1.0.2 */ }; -- 1.8.0.2

--- tools/virsh-domain.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index f3da1d5..e6c09f0 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8491,6 +8491,109 @@ cleanup: return ret; } +static const vshCmdInfo info_domifaddrs[] = { + {"help", N_("Dump domain's IP addresses and other interfaces info")}, + {"desc", N_("Dump domain's IP addresses and other interfaces info")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_domifaddrs[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")}, + {"interface", VSH_OT_STRING, 0, N_("Limit selection just to one interface")}, + {"method", VSH_OT_STRING, 0, N_("Use one method: default, agent, nwfilter")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdDomIfAddrs(vshControl *ctl, const vshCmd *cmd) +{ + virDomainPtr dom = NULL; + bool ret = false; + unsigned int flags = 0; + const char *device = NULL; + const char *methodStr = NULL; + int method = VIR_DOMAIN_INTERFACE_ADDRS_DEFAULT; + virDomainInterfacePtr *ifaces = NULL; + int i, j, ifacesCount = 0; + + if (!(dom = vshCommandOptDomain(ctl, cmd, NULL))) + goto cleanup; + + if (vshCommandOptString(cmd, "interface", &device) < 0) { + vshError(ctl, _("Unable to parse interface parameter")); + goto cleanup; + } + + if (vshCommandOptString(cmd, "method", &methodStr) < 0) { + vshError(ctl, _("Unable to parse method parameter")); + goto cleanup; + } + + if (STREQ_NULLABLE(methodStr, "default")) + method = VIR_DOMAIN_INTERFACE_ADDRS_DEFAULT; + else if (STREQ_NULLABLE(methodStr, "agent")) + method = VIR_DOMAIN_INTERFACE_ADDRS_GUEST_AGENT; + else if (STREQ_NULLABLE(methodStr, "nwfilter")) + method = VIR_DOMAIN_INTERFACE_ADDRS_NWFILTER; + else if (methodStr) { + vshError(ctl, _("Unknown method: %s"), methodStr); + goto cleanup; + } + + ifacesCount = virDomainInterfacesAddresses(dom, &ifaces, method, flags); + if (ifacesCount < 0) + goto cleanup; + + vshPrintExtra(ctl, " %-10s %-17s %s\n%s\n", + _("Name"), _("HW address"), _("IP address"), + "---------------------------------------------------"); + for (i = 0; i < ifacesCount; i++) { + virDomainInterfacePtr iface = ifaces[i]; + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *hwaddr = ""; + const char *ipAddrStr = NULL; + + if (device && STRNEQ(device, iface->name)) + continue; + + if (iface->hwaddr) + hwaddr = iface->hwaddr; + + for (j = 0; j < iface->ip_addrs_count; j++) { + const char *type = ""; + if (iface->ip_addrs[j].type == VIR_IP_ADDR_TYPE_IPV4) + type = "inet "; + else if (iface->ip_addrs[j].type == VIR_IP_ADDR_TYPE_IPV6) + type = "inet6 "; + if (j) + virBufferAddChar(&buf, ' '); + virBufferAsprintf(&buf, "%s%s/%d", + type, + iface->ip_addrs[j].addr, + iface->ip_addrs[j].prefix); + } + + ipAddrStr = virBufferContentAndReset(&buf); + + if (!ipAddrStr) + ipAddrStr = vshStrdup(ctl,""); + + vshPrintExtra(ctl, " %-10s %-17s %s\n", + iface->name, hwaddr, ipAddrStr); + + VIR_FREE(ipAddrStr); + } + + ret = true; +cleanup: + for (i = 0; i < ifacesCount; i++) + virDomainInterfaceFree(ifaces[i]); + VIR_FREE(ifaces); + if (dom) + virDomainFree(dom); + return ret; +} + const vshCmdDef domManagementCmds[] = { {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device, 0}, @@ -8527,6 +8630,7 @@ const vshCmdDef domManagementCmds[] = { {"domhostname", cmdDomHostname, opts_domhostname, info_domhostname, 0}, {"domid", cmdDomid, opts_domid, info_domid, 0}, {"domif-setlink", cmdDomIfSetLink, opts_domif_setlink, info_domif_setlink, 0}, + {"domifaddrs", cmdDomIfAddrs, opts_domifaddrs, info_domifaddrs, 0}, {"domiftune", cmdDomIftune, opts_domiftune, info_domiftune, 0}, {"domjobabort", cmdDomjobabort, opts_domjobabort, info_domjobabort, 0}, {"domjobinfo", cmdDomjobinfo, opts_domjobinfo, info_domjobinfo, 0}, -- 1.8.0.2

--- daemon/remote.c | 140 +++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 94 +++++++++++++++++++++++++++++ src/remote/remote_protocol.x | 27 ++++++++- src/remote_protocol-structs | 27 +++++++++ 4 files changed, 287 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 8767c18..7363e2a 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4783,3 +4783,143 @@ no_memory: virReportOOMError(); return -1; } + +static int +remoteSerializeDomainInterfacesPtr(virDomainInterfacePtr *ifaces, + int ifacesCount, + remote_domain_interfaces_addresses_ret *ret) +{ + int i, j; + + if (!ifacesCount) + return 0; + + if (VIR_ALLOC_N(ret->ifaces.ifaces_val, ifacesCount) < 0) { + virReportOOMError(); + return -1; + } + + ret->ifaces.ifaces_len = ifacesCount; + + for (i = 0; i < ifacesCount; i++) { + virDomainInterfacePtr tmp = ifaces[i]; + remote_domain_interface *tmp_ret = &(ret->ifaces.ifaces_val[i]); + + if (!(tmp_ret->name = strdup(tmp->name))) + goto no_memory; + + if (tmp->hwaddr) { + char **hwaddr_p = NULL; + if (VIR_ALLOC(hwaddr_p) < 0) + goto no_memory; + *hwaddr_p = strdup(tmp->hwaddr); + if (!*hwaddr_p) { + VIR_FREE(hwaddr_p); + goto no_memory; + } + + tmp_ret->hwaddr = hwaddr_p; + } + + tmp_ret->flags = tmp->flags; + + if (!tmp->ip_addrs_count) + continue; + + if (VIR_ALLOC_N(tmp_ret->ip_addrs.ip_addrs_val, + tmp->ip_addrs_count) < 0) + goto no_memory; + + tmp_ret->ip_addrs.ip_addrs_len = tmp->ip_addrs_count; + + for (j = 0; j < tmp->ip_addrs_count; j++) { + virDomainIPAddress addr = tmp->ip_addrs[j]; + remote_domain_ip_addrs *addr_ret = &(tmp_ret->ip_addrs.ip_addrs_val[j]); + + addr_ret->prefix = addr.prefix; + addr_ret->type = addr.type; + + if (addr.addr) { + char **addr_p = NULL; + if (VIR_ALLOC(addr_p) < 0) + goto no_memory; + *addr_p = strdup(addr.addr); + if (!*addr_p) { + VIR_FREE(addr_p); + goto no_memory; + } + addr_ret->addr = addr_p; + } + + if (addr.dstaddr) { + char **addr_p = NULL; + if (VIR_ALLOC(addr_p) < 0) + goto no_memory; + *addr_p = strdup(addr.dstaddr); + if (!*addr_p) { + VIR_FREE(addr_p); + goto no_memory; + } + addr_ret->dstaddr = addr_p; + } + } + } + + return 0; + +no_memory: + virReportOOMError(); + for (i = 0; i < ret->ifaces.ifaces_len; i++) { + remote_domain_interface tmp_ret = ret->ifaces.ifaces_val[i]; + + VIR_FREE(tmp_ret.name); + VIR_FREE(tmp_ret.hwaddr); + for (j = 0; j < tmp_ret.ip_addrs.ip_addrs_len; j++) { + VIR_FREE(tmp_ret.ip_addrs.ip_addrs_val[j].addr); + VIR_FREE(tmp_ret.ip_addrs.ip_addrs_val[j].dstaddr); + } + } + VIR_FREE(ret->ifaces.ifaces_val); + + return -1; +} + +static int +remoteDispatchDomainInterfacesAddresses(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr, + remote_domain_interfaces_addresses_args *args, + remote_domain_interfaces_addresses_ret *ret) +{ + int rv = -1; + virDomainPtr dom = NULL; + virDomainInterfacePtr *ifaces = NULL; + int ifacesCount = 0; + struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client); + + if (!(dom = get_nonnull_domain(priv->conn, args->domain))) + goto cleanup; + + ifacesCount = virDomainInterfacesAddresses(dom, &ifaces, + args->method, + args->flags); + if (ifacesCount < 0) + goto cleanup; + + if (remoteSerializeDomainInterfacesPtr(ifaces, ifacesCount, ret) < 0) + goto cleanup; + + rv = 0; + +cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + if (dom) + virDomainFree(dom); + + while (ifacesCount > 0) + virDomainInterfaceFree(ifaces[--ifacesCount]); + VIR_FREE(ifaces); + return rv; +} diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index ae861cc..7290f30 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -5834,6 +5834,99 @@ done: return rv; } +static int +remoteDomainInterfacesAddresses(virDomainPtr dom, + virDomainInterfacePtr **ifaces, + unsigned int method, + unsigned int flags) +{ + int rv = -1; + remote_domain_interfaces_addresses_args args; + remote_domain_interfaces_addresses_ret ret; + struct private_data *priv = dom->conn->privateData; + int i, j; + + *ifaces = NULL; + + memset(&ret, 0, sizeof(ret)); + make_nonnull_domain(&args.domain, dom); + args.method = method; + args.flags = flags; + + remoteDriverLock(priv); + + if (call(dom->conn, priv, 0, REMOTE_PROC_DOMAIN_INTERFACES_ADDRESSES, + (xdrproc_t)xdr_remote_domain_interfaces_addresses_args, + (char *)&args, + (xdrproc_t)xdr_remote_domain_interfaces_addresses_ret, + (char *)&ret) == -1) + goto cleanup; + + if (!ret.ifaces.ifaces_len) { + rv = 0; + goto cleanup; + } + + if (VIR_ALLOC_N(*ifaces, ret.ifaces.ifaces_len * sizeof(virDomainInterfacePtr)) < 0) + goto no_memory; + + for (i = 0; i < ret.ifaces.ifaces_len; i++) { + virDomainInterfacePtr tmp = NULL; + remote_domain_interface tmp_ret = ret.ifaces.ifaces_val[i]; + + if (VIR_ALLOC(tmp) < 0) + goto no_memory; + + (*ifaces)[i] = tmp; + + if (!(tmp->name = strdup(tmp_ret.name))) + goto no_memory; + + if (tmp_ret.hwaddr && + !(tmp->hwaddr = strdup(*tmp_ret.hwaddr))) + goto no_memory; + + tmp->flags = tmp_ret.flags; + + if (!tmp_ret.ip_addrs.ip_addrs_len) + continue; + + if (VIR_ALLOC_N(tmp->ip_addrs, tmp_ret.ip_addrs.ip_addrs_len) < 0) + goto no_memory; + + tmp->ip_addrs_count = tmp_ret.ip_addrs.ip_addrs_len; + for (j = 0; j < tmp->ip_addrs_count; j++) { + virDomainIPAddressPtr addr = &(tmp->ip_addrs[j]); + remote_domain_ip_addrs addr_ret = tmp_ret.ip_addrs.ip_addrs_val[j]; + + addr->prefix = addr_ret.prefix; + addr->type = addr_ret.type; + if (addr_ret.addr && + !(addr->addr = strdup(*addr_ret.addr))) + goto no_memory; + + if (addr_ret.dstaddr && + !(addr->dstaddr = strdup(*addr_ret.dstaddr))) + goto no_memory; + } + } + + rv = ret.ifaces.ifaces_len; + +cleanup: + remoteDriverUnlock(priv); + xdr_free((xdrproc_t)xdr_remote_domain_interfaces_addresses_ret, + (char *) &ret); + return rv; + +no_memory: + virReportOOMError(); + for (i = 0; i < ret.ifaces.ifaces_len; i++) + virDomainInterfaceFree((*ifaces)[i]); + VIR_FREE(*ifaces); + goto cleanup; +} + static void remoteDomainEventQueue(struct private_data *priv, virDomainEventPtr event) { @@ -6153,6 +6246,7 @@ static virDriver remote_driver = { .nodeGetMemoryParameters = remoteNodeGetMemoryParameters, /* 0.10.2 */ .nodeGetCPUMap = remoteNodeGetCPUMap, /* 1.0.0 */ .domainFSTrim = remoteDomainFSTrim, /* 1.0.1 */ + .domainInterfacesAddresses = remoteDomainInterfacesAddresses, /* 1.0.2 */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index bdad9f0..bd15107 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -2696,6 +2696,30 @@ struct remote_domain_fstrim_args { unsigned int flags; }; +struct remote_domain_ip_addrs { + int type; + remote_string addr; + int prefix; + remote_string dstaddr; +}; + +struct remote_domain_interface { + remote_nonnull_string name; + unsigned int flags; + remote_string hwaddr; + remote_domain_ip_addrs ip_addrs<>; +}; + +struct remote_domain_interfaces_addresses_args { + remote_nonnull_domain domain; + unsigned int method; + unsigned int flags; +}; + +struct remote_domain_interfaces_addresses_ret { + remote_domain_interface ifaces<>; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -3042,7 +3066,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENT_PMSUSPEND_DISK = 292, /* autogen autogen */ REMOTE_PROC_NODE_GET_CPU_MAP = 293, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_FSTRIM = 294, /* autogen autogen */ - REMOTE_PROC_DOMAIN_SEND_PROCESS_SIGNAL = 295 /* autogen autogen */ + REMOTE_PROC_DOMAIN_SEND_PROCESS_SIGNAL = 295, /* autogen autogen */ + REMOTE_PROC_DOMAIN_INTERFACES_ADDRESSES = 296 /* skipgen skipgen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index e7d05b8..b481f4f 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2151,6 +2151,32 @@ struct remote_domain_fstrim_args { uint64_t minimum; u_int flags; }; +struct remote_domain_ip_addrs { + int type; + remote_string addr; + int prefix; + remote_string dstaddr; +}; +struct remote_domain_interface { + remote_nonnull_string name; + u_int flags; + remote_string hwaddr; + struct { + u_int ip_addrs_len; + remote_domain_ip_addrs * ip_addrs_val; + } ip_addrs; +}; +struct remote_domain_interfaces_addresses_args { + remote_nonnull_domain domain; + u_int method; + u_int flags; +}; +struct remote_domain_interfaces_addresses_ret { + struct { + u_int ifaces_len; + remote_domain_interface * ifaces_val; + } ifaces; +}; enum remote_procedure { REMOTE_PROC_OPEN = 1, REMOTE_PROC_CLOSE = 2, @@ -2447,4 +2473,5 @@ enum remote_procedure { REMOTE_PROC_NODE_GET_CPU_MAP = 293, REMOTE_PROC_DOMAIN_FSTRIM = 294, REMOTE_PROC_DOMAIN_SEND_PROCESS_SIGNAL = 295, + REMOTE_PROC_DOMAIN_INTERFACES_ADDRESSES = 296, }; -- 1.8.0.2

--- python/libvirt-override-api.xml | 7 +++ python/libvirt-override.c | 120 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml index a0e0496..bea1a59 100644 --- a/python/libvirt-override-api.xml +++ b/python/libvirt-override-api.xml @@ -548,5 +548,12 @@ <arg name='conn' type='virConnectPtr' info='pointer to the hypervisor connection'/> <arg name='flags' type='int' info='unused, pass 0'/> </function> + <function name='virDomainInterfacesAddresses' file='python'> + <info>Get domain's interfaces among with their IP and HW addresses</info> + <return type='virDomainInterfacePtr' info='array of @domain interfaces'/> + <arg name='domain' type='virDomainPtr' info='domain object'/> + <arg name='method' type='unsigned int' info='which method use, one of virDomainInterfacesAddressesMethod'/> + <arg name='flags' type='unsigned int' info='extra flags, not used yet, so callers should always pass 0'/> + </function> </symbols> </api> diff --git a/python/libvirt-override.c b/python/libvirt-override.c index 91e82c6..c8365d4 100644 --- a/python/libvirt-override.c +++ b/python/libvirt-override.c @@ -6588,6 +6588,125 @@ error: goto cleanup; } +static PyObject * +libvirt_virDomainInterfacesAddresses(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *py_retval; + virDomainPtr domain; + PyObject *pyobj_domain; + unsigned int method, flags; + virDomainInterfacePtr *ifaces = NULL; + int j, i, i_retval = 0; + bool full_free = true; + + if (!PyArg_ParseTuple(args, (char *) "Oii:virDomainInterfacesAddresses", + &pyobj_domain, &method, &flags)) + return NULL; + + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + LIBVIRT_BEGIN_ALLOW_THREADS; + i_retval = virDomainInterfacesAddresses(domain, &ifaces, method, flags); + LIBVIRT_END_ALLOW_THREADS; + + if (i_retval < 0) { + py_retval = VIR_PY_NONE; + goto cleanup; + } + + if (!(py_retval = PyDict_New())) + goto no_memory; + + for (i = 0; i < i_retval; i++) { + virDomainInterfacePtr iface = ifaces[i]; + PyObject *pyIPAddrs = NULL; + PyObject *pyIface = NULL; + + if (!(pyIface = PyDict_New())) + goto no_memory; + + if (iface->ip_addrs_count) { + if (!(pyIPAddrs = PyList_New(iface->ip_addrs_count))) { + Py_DECREF(pyIface); + goto no_memory; + } + } else { + pyIPAddrs = VIR_PY_NONE; + } + + for (j = 0; j < iface->ip_addrs_count; j++) { + virDomainIPAddress addr = iface->ip_addrs[j]; + PyObject *pyAddr = PyDict_New(); + const char *type = NULL; + + if (!pyAddr) { + { Py_DECREF(pyIface); } + { Py_DECREF(pyIPAddrs); } + goto no_memory; + } + + switch (addr.type) { + case VIR_IP_ADDR_TYPE_IPV4: + type = "ipv4"; + break; + case VIR_IP_ADDR_TYPE_IPV6: + type = "ipv6"; + break; + default: + type = "unknown"; + break; + } + + PyDict_SetItem(pyAddr, libvirt_constcharPtrWrap("addr"), + PyString_FromString(addr.addr)); + PyDict_SetItem(pyAddr, libvirt_constcharPtrWrap("prefix"), + libvirt_intWrap(addr.prefix)); + PyDict_SetItem(pyAddr, libvirt_constcharPtrWrap("type"), + PyString_FromString(type)); + PyDict_SetItem(pyAddr, libvirt_constcharPtrWrap("dstaddr"), + libvirt_charPtrWrap(addr.dstaddr)); + PyList_SetItem(pyIPAddrs, j, pyAddr); + } + + PyDict_SetItem(pyIface, libvirt_constcharPtrWrap("ip_addrs"), + pyIPAddrs); + PyDict_SetItem(pyIface, libvirt_constcharPtrWrap("hwaddr"), + libvirt_charPtrWrap(iface->hwaddr)); + PyDict_SetItem(pyIface, libvirt_constcharPtrWrap("flags"), + libvirt_intWrap(iface->flags)); + + PyDict_SetItem(py_retval, libvirt_constcharPtrWrap(iface->name), + pyIface); + } + + full_free = false; + + +cleanup: + for (i = 0; i < i_retval; i++) { + /* We don't want to free values we've just + * shared with python variables unless + * there was an error and hence we are + * returning PY_NONE or equivalent */ + if (full_free) { + VIR_FREE(ifaces[i]->name); + VIR_FREE(ifaces[i]->hwaddr); + for (j = 0; j < ifaces[i]->ip_addrs_count; j++) { + VIR_FREE(ifaces[i]->ip_addrs[j].addr); + VIR_FREE(ifaces[i]->ip_addrs[j].dstaddr); + } + } + VIR_FREE(ifaces[i]->ip_addrs); + } + VIR_FREE(ifaces); + + return py_retval; +no_memory: + Py_XDECREF(py_retval); + py_retval = PyErr_NoMemory(); + goto cleanup; +} /************************************************************************ * * @@ -6708,6 +6827,7 @@ static PyMethodDef libvirtMethods[] = { {(char *) "virNodeGetMemoryParameters", libvirt_virNodeGetMemoryParameters, METH_VARARGS, NULL}, {(char *) "virNodeSetMemoryParameters", libvirt_virNodeSetMemoryParameters, METH_VARARGS, NULL}, {(char *) "virNodeGetCPUMap", libvirt_virNodeGetCPUMap, METH_VARARGS, NULL}, + {(char *) "virDomainInterfacesAddresses", libvirt_virDomainInterfacesAddresses, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} }; -- 1.8.0.2

--- examples/python/Makefile.am | 2 +- examples/python/README | 1 + examples/python/domipaddrs.py | 50 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) create mode 100755 examples/python/domipaddrs.py diff --git a/examples/python/Makefile.am b/examples/python/Makefile.am index b04d126..b51f729 100644 --- a/examples/python/Makefile.am +++ b/examples/python/Makefile.am @@ -4,4 +4,4 @@ EXTRA_DIST= \ README \ consolecallback.py \ - dominfo.py domrestore.py domsave.py domstart.py esxlist.py + dominfo.py domrestore.py domsave.py domstart.py esxlist.py domipaddrs.py diff --git a/examples/python/README b/examples/python/README index f4db76c..d895740 100644 --- a/examples/python/README +++ b/examples/python/README @@ -10,6 +10,7 @@ domsave.py - save all running domU's into a directory domrestore.py - restore domU's from their saved files in a directory esxlist.py - list active domains of an VMware ESX host and print some info. also demonstrates how to use the libvirt.openAuth() method +domipaddrs.py - print domain interfaces among with their HW and IP addresses The XML files in this directory are examples of the XML format that libvirt expects, and will have to be adapted for your setup. They are only needed diff --git a/examples/python/domipaddrs.py b/examples/python/domipaddrs.py new file mode 100755 index 0000000..82c5774 --- /dev/null +++ b/examples/python/domipaddrs.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# domipaddrds - print IP addresses for running domain + +import libvirt +import sys + +def usage(): + print "Usage: %s [URI] DOMAIN" % sys.argv[0] + print " Print IP addresses assigned to domain interfaces" + +uri = None +name = None +args = len(sys.argv) + +if args == 2: + name = sys.argv[1] +elif args == 3: + uri = sys.argv[1] + name = sys.argv[2] +else: + usage() + sys.exit(2) + +conn = libvirt.openReadOnly(uri) +if conn == None: + print "Unable to open connection to libvirt" + sys.exit(1) + +try: + dom = conn.lookupByName(name) +except libvirt.libvirtError: + print "Domain %s not found" % name + sys.exit(0) + +ifaces = dom.interfacesAddresses(libvirt.VIR_DOMAIN_INTERFACE_ADDRS_DEFAULT, 0) +if (ifaces == None): + print "Failed to get domain interfaces" + sys.exit(0) + +print " {0:10} {1:17} {2}".format("Interface", "HW address", "IP Address") + +for (name, val) in ifaces.iteritems(): + print " {0:10} {1:17}".format(name, val['hwaddr']), + + if (val['ip_addrs'] <> None): + print " ", + for addr in val['ip_addrs']: + print "{0}/{1} ".format(addr['addr'], addr['prefix']), + + print -- 1.8.0.2
participants (3)
-
Ján Tomko
-
Laine Stump
-
Michal Privoznik