[libvirt] [PATCH 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. 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 | 138 +++++++++++++++++++++++++++++++++ examples/python/Makefile.am | 2 +- examples/python/README | 1 + examples/python/domipaddrs.py | 50 ++++++++++++ include/libvirt/libvirt.h.in | 52 +++++++++++++ python/generator.py | 3 + python/libvirt-override-api.xml | 7 ++ python/libvirt-override.c | 120 +++++++++++++++++++++++++++++ src/driver.h | 6 ++ src/libvirt.c | 102 +++++++++++++++++++++++++ src/libvirt_public.syms | 7 ++ src/qemu/qemu_agent.c | 164 ++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 2 + src/qemu/qemu_driver.c | 68 +++++++++++++++++ src/remote/remote_driver.c | 86 +++++++++++++++++++++ src/remote/remote_protocol.x | 27 ++++++- src/remote_protocol-structs | 27 +++++++ tools/virsh-domain.c | 102 +++++++++++++++++++++++++ 18 files changed, 962 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 c6739d7..3eb7ced 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -4564,6 +4564,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 e9b9270..e2f3134 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..94bea18 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..ad0f78c 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 Fri, Dec 21, 2012 at 04:49:36PM +0100, 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 c6739d7..3eb7ced 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -4564,6 +4564,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);
I tend to prefer it if we use "virDomainInterfacePtr **ifaces" as we did with the vir*ListAll() methods. eg an array of pointers to objects, rather than array of objects. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

This is just a free function for array of virDomainInterfacePtr as returned by virDomainInterfacesAddresses API. --- include/libvirt/libvirt.h.in | 3 +++ python/generator.py | 1 + src/libvirt.c | 31 +++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 36 insertions(+) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 3eb7ced..d40c4f8 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -4613,6 +4613,9 @@ int virDomainInterfacesAddresses(virDomainPtr domain, unsigned int method, unsigned int flags); +void virDomainInterfaceFree(virDomainInterfacePtr ifaces, + int count); + /** * virSchedParameterType: * diff --git a/python/generator.py b/python/generator.py index e2f3134..1cc1e6e 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 ad0f78c..3188257 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -20495,3 +20495,34 @@ error: virDispatchError(domain->conn); return -1; } + +/** + * virDomainInterfaceFree: + * @ifaces: array of interfaces + * @count: count of @ifaces items + * + * Free the array of interfaces as returned by virDomainInterfacesAddresses. + * The @ifaces pointer is freed and should not be used thereafter. + */ +void +virDomainInterfaceFree(virDomainInterfacePtr ifaces, + int count) +{ + int i, j; + + VIR_DEBUG("ifaces=%p, count=%d", ifaces, count); + + for (i = 0; i < count; i++) { + virDomainInterface tmp = ifaces[i]; + + VIR_FREE(tmp.name); + VIR_FREE(tmp.hwaddr); + for (j = 0; j < tmp.ip_addrs_count; j++) { + VIR_FREE(tmp.ip_addrs[j].addr); + VIR_FREE(tmp.ip_addrs[j].dstaddr); + } + VIR_FREE(tmp.ip_addrs); + } + + VIR_FREE(ifaces); +} 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

On Fri, Dec 21, 2012 at 04:49:37PM +0100, Michal Privoznik wrote:
This is just a free function for array of virDomainInterfacePtr as returned by virDomainInterfacesAddresses API. --- include/libvirt/libvirt.h.in | 3 +++ python/generator.py | 1 + src/libvirt.c | 31 +++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 36 insertions(+)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 3eb7ced..d40c4f8 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -4613,6 +4613,9 @@ int virDomainInterfacesAddresses(virDomainPtr domain, unsigned int method, unsigned int flags);
+void virDomainInterfaceFree(virDomainInterfacePtr ifaces, + int count);
Ewww, no. Please just have void virDomainInterfaceFree(virDomainInterfacePtr iface); Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 21.12.2012 16:52, Daniel P. Berrange wrote:
On Fri, Dec 21, 2012 at 04:49:37PM +0100, Michal Privoznik wrote:
This is just a free function for array of virDomainInterfacePtr as returned by virDomainInterfacesAddresses API. --- include/libvirt/libvirt.h.in | 3 +++ python/generator.py | 1 + src/libvirt.c | 31 +++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 36 insertions(+)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 3eb7ced..d40c4f8 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -4613,6 +4613,9 @@ int virDomainInterfacesAddresses(virDomainPtr domain, unsigned int method, unsigned int flags);
+void virDomainInterfaceFree(virDomainInterfacePtr ifaces, + int count);
Ewww, no. Please just have
void virDomainInterfaceFree(virDomainInterfacePtr iface);
Daniel
You mean to free just one item within an array? So users should do: for (i = 0; i < ifaces_count; i++) virDomainInterfaceFree(ifaces[i]); VIR_FREE(ifaces) then? Why would we want to enforce them to do something like that? (I am not saying you approach is bad, I just don't understand it) Michal

On 21.12.2012 17:01, Michal Privoznik wrote:
On 21.12.2012 16:52, Daniel P. Berrange wrote:
On Fri, Dec 21, 2012 at 04:49:37PM +0100, Michal Privoznik wrote:
This is just a free function for array of virDomainInterfacePtr as returned by virDomainInterfacesAddresses API. --- include/libvirt/libvirt.h.in | 3 +++ python/generator.py | 1 + src/libvirt.c | 31 +++++++++++++++++++++++++++++++ src/libvirt_public.syms | 1 + 4 files changed, 36 insertions(+)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 3eb7ced..d40c4f8 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -4613,6 +4613,9 @@ int virDomainInterfacesAddresses(virDomainPtr domain, unsigned int method, unsigned int flags);
+void virDomainInterfaceFree(virDomainInterfacePtr ifaces, + int count);
Ewww, no. Please just have
void virDomainInterfaceFree(virDomainInterfacePtr iface);
Daniel
You mean to free just one item within an array? So users should do:
for (i = 0; i < ifaces_count; i++) virDomainInterfaceFree(ifaces[i]); VIR_FREE(ifaces)
then? Why would we want to enforce them to do something like that? (I am not saying you approach is bad, I just don't understand it)
Aah. Now, after I read your reply to 1/8 I understand. Okay, I think I can do that.
Michal
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

This command returns an array of all guest interfaces among with their IP and HW addresses. --- src/qemu/qemu_agent.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_agent.h | 2 + 2 files changed, 166 insertions(+) diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c index bb421bd..ba17a26 100644 --- a/src/qemu/qemu_agent.c +++ b/src/qemu/qemu_agent.c @@ -1474,3 +1474,167 @@ 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) < 0) { + virReportOOMError(); + goto cleanup; + } + + for (i = 0; i < size; i++) { + virJSONValuePtr jsonIface = virJSONValueArrayGet(replyArray, i); + virDomainInterfacePtr tmpIface = &((*ifaces)[i]); + virJSONValuePtr jsonIpAddrArr = NULL; + int j, jsonIpAddrArrSize = 0; + const char *name, *hwaddr; + + /* 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) { + virDomainInterfaceFree(*ifaces, size); + *ifaces = NULL; + } + 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..43c78ff 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

--- 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 0058c8d..841c980 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14887,6 +14887,73 @@ cleanup: return ret; } +static int +qemuDomainInterfacesAddresses(virDomainPtr dom, + virDomainInterfacePtr *iface, + 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, iface); + 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, @@ -15061,6 +15128,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 | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index f3da1d5..c72b078 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -8491,6 +8491,107 @@ 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 = ""; + + vshPrintExtra(ctl, " %-10s %-17s %s\n", + iface->name, hwaddr, ipAddrStr); + + VIR_FREE(ipAddrStr); + } + + ret = true; +cleanup: + virDomainInterfaceFree(ifaces, ifacesCount); + if (dom) + virDomainFree(dom); + return ret; +} + const vshCmdDef domManagementCmds[] = { {"attach-device", cmdAttachDevice, opts_attach_device, info_attach_device, 0}, @@ -8527,6 +8628,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 | 138 +++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 86 +++++++++++++++++++++++++++ src/remote/remote_protocol.x | 27 ++++++++- src/remote_protocol-structs | 27 +++++++++ 4 files changed, 277 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 8767c18..449f94e 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -4783,3 +4783,141 @@ 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++) { + virDomainInterface 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); + + virDomainInterfaceFree(ifaces, ifacesCount); + return rv; +} diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index ae861cc..f5f9c05 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -5834,6 +5834,91 @@ 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; + + 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) < 0) + goto no_memory; + + for (i = 0; i < ret.ifaces.ifaces_len; i++) { + virDomainInterfacePtr tmp = &((*ifaces)[i]); + remote_domain_interface tmp_ret = ret.ifaces.ifaces_val[i]; + + 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(); + virDomainInterfaceFree(*ifaces, ret.ifaces.ifaces_len); + *ifaces = NULL; + goto cleanup; +} + static void remoteDomainEventQueue(struct private_data *priv, virDomainEventPtr event) { @@ -6153,6 +6238,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..ac21c66 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++) { + virDomainInterface 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 (2)
-
Daniel P. Berrange
-
Michal Privoznik