Create a gist now

Instantly share code, notes, and snippets.

diff --git a/daemon/remote.c b/daemon/remote.c
index 47267c2..4f186b3 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -2220,6 +2220,129 @@ no_memory:
}
static int
+remoteSerializeDomainInterfacePtr(virDomainInterfacePtr ifaces,
+ unsigned int ifaces_count,
+ remote_domain_interfaces_addresses_ret *ret)
+{
+ int i, j;
+
+ if (VIR_ALLOC_N(ret->ifaces.ifaces_val, ifaces_count) < 0)
+ goto no_memory;
+
+ ret->ifaces.ifaces_len = ifaces_count;
+
+ for (i = 0; i < ifaces_count; i++) {
+ virDomainInterfacePtr iface = &(ifaces[i]);
+ remote_domain_interface *iface_ret = &(ret->ifaces.ifaces_val[i]);
+
+ if (!(iface_ret->name = strdup(iface->name)))
+ goto no_memory;
+
+ if (iface->hwaddr) {
+ char **hwaddr_p = NULL;
+ if (VIR_ALLOC(hwaddr_p) < 0)
+ goto no_memory;
+ *hwaddr_p = strdup(iface->hwaddr);
+ if (!hwaddr_p)
+ goto no_memory;
+
+ iface_ret->hwaddr = hwaddr_p;
+ }
+
+ if (VIR_ALLOC_N(iface_ret->ip_addrs.ip_addrs_val,
+ iface->ip_addrs_count) < 0)
+ goto no_memory;
+
+ iface_ret->ip_addrs.ip_addrs_len = iface->ip_addrs_count;
+
+ for (j = 0; j < iface->ip_addrs_count; j++) {
+ virDomainIPAddressPtr ip_addr = &(iface->ip_addrs[j]);
+ remote_domain_ip_addr *ip_addr_ret =
+ &(iface_ret->ip_addrs.ip_addrs_val[j]);
+
+ if (!(ip_addr_ret->addr = strdup(ip_addr->addr)))
+ goto no_memory;
+
+ ip_addr_ret->prefix = ip_addr->prefix;
+ ip_addr_ret->type = ip_addr->type;
+ }
+ }
+
+ return 0;
+
+no_memory:
+ if (ret->ifaces.ifaces_val) {
+ for (i = 0; i < ifaces_count; i++) {
+ remote_domain_interface *iface_ret = &(ret->ifaces.ifaces_val[i]);
+ VIR_FREE(iface_ret->name);
+ VIR_FREE(iface_ret->hwaddr);
+ for (j = 0; j < iface_ret->ip_addrs.ip_addrs_len; j++) {
+ remote_domain_ip_addr *ip_addr =
+ &(iface_ret->ip_addrs.ip_addrs_val[j]);
+ VIR_FREE(ip_addr->addr);
+ }
+ }
+ VIR_FREE(ret->ifaces.ifaces_val);
+ }
+ virReportOOMError();
+ 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;
+ unsigned int ifaces_count = 0;
+ struct daemonClientPrivate *priv =
+ virNetServerClientGetPrivateData(client);
+
+ if (!priv->conn) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open"));
+ goto cleanup;
+ }
+
+ if (!(dom = get_nonnull_domain(priv->conn, args->dom)))
+ goto cleanup;
+
+ if (virDomainInterfacesAddresses(dom, &ifaces,
+ &ifaces_count, args->flags) < 0)
+ goto cleanup;
+
+ if (remoteSerializeDomainInterfacePtr(ifaces, ifaces_count, ret) < 0)
+ goto cleanup;
+
+ rv = 0;
+
+cleanup:
+ if (rv < 0)
+ virNetMessageSaveError(rerr);
+ if (dom)
+ virDomainFree(dom);
+ if (ifaces) {
+ unsigned int i, j;
+
+ for (i = 0; i < ifaces_count; i++) {
+ 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);
+ }
+ VIR_FREE(ifaces);
+ }
+ return rv;
+}
+
+
+
+static int
remoteDispatchDomainGetBlockJobInfo(virNetServerPtr server ATTRIBUTE_UNUSED,
virNetServerClientPtr client ATTRIBUTE_UNUSED,
virNetMessagePtr msg ATTRIBUTE_UNUSED,
diff --git a/examples/python/Makefile.am b/examples/python/Makefile.am
index 2cacfa1..d33ee17 100644
--- a/examples/python/Makefile.am
+++ b/examples/python/Makefile.am
@@ -17,4 +17,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..e58438f 100644
--- a/examples/python/README
+++ b/examples/python/README
@@ -10,6 +10,8 @@ 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/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index acf3218..11bb8e3 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -1946,6 +1946,38 @@ int virDomainGetInterfaceParameters (virDomainPtr dom,
virTypedParameterPtr params,
int *nparams, unsigned int flags);
+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 */
+};
+
+typedef struct _virDomainInterface virDomainInterface;
+typedef virDomainInterface *virDomainInterfacePtr;
+struct _virDomainInterface {
+ char *name; /* interface name */
+ char *hwaddr; /* hardware address */
+ unsigned int ip_addrs_count; /* number of items in @ip_addr */
+ virDomainIPAddressPtr ip_addrs; /* array of IP addresses */
+};
+
+int virDomainInterfacesAddresses (virDomainPtr dom,
+ virDomainInterfacePtr *ifaces,
+ unsigned int *ifaces_count,
+ unsigned int flags);
+
/* Management of domain block devices */
int virDomainBlockPeek (virDomainPtr dom,
diff --git a/python/generator.py b/python/generator.py
index 8c380bb..7d2acc6 100755
--- a/python/generator.py
+++ b/python/generator.py
@@ -458,6 +458,7 @@ skip_impl = (
'virNodeGetMemoryParameters',
'virNodeSetMemoryParameters',
'virNodeGetCPUMap',
+ 'virDomainInterfacesAddresses',
)
lxc_skip_impl = (
diff --git a/python/libvirt-override-api.xml b/python/libvirt-override-api.xml
index 155ab36..655a3cf 100644
--- a/python/libvirt-override-api.xml
+++ b/python/libvirt-override-api.xml
@@ -584,5 +584,11 @@
<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>returns array of IP addresses for all domain interfaces</info>
+ <arg name='dom' type='virDomainPtr' info='pointer to the domain'/>
+ <arg name='flags' type='unsigned int' info='extra flags; not used yet, so callers should always pass 0'/>
+ <return type='virDomainInterfacePtr' info="dictionary of domain interfaces among with their HW and IP addresses"/>
+ </function>
</symbols>
</api>
diff --git a/python/libvirt-override.c b/python/libvirt-override.c
index b6462c2..ea2723e 100644
--- a/python/libvirt-override.c
+++ b/python/libvirt-override.c
@@ -4557,6 +4557,121 @@ cleanup:
return py_retval;
}
+
+static PyObject *
+libvirt_virDomainInterfacesAddresses(PyObject *self ATTRIBUTE_UNUSED,
+ PyObject *args)
+{
+ PyObject *py_retval = VIR_PY_NONE;
+ virDomainPtr domain;
+ PyObject *pyobj_domain;
+ unsigned int flags;
+ virDomainInterfacePtr ifaces = NULL;
+ unsigned int ifaces_count = 0;
+ unsigned int i, j;
+ int ret;
+ bool full_free = true;
+
+ if (!PyArg_ParseTuple(args, (char *) "Oi:virDomainInterfacePtr",
+ &pyobj_domain, &flags))
+ return NULL;
+
+ domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain);
+
+ LIBVIRT_BEGIN_ALLOW_THREADS;
+ ret = virDomainInterfacesAddresses(domain, &ifaces, &ifaces_count, flags);
+ LIBVIRT_END_ALLOW_THREADS;
+ if (ret < 0)
+ goto cleanup;
+
+ if (!(py_retval = PyDict_New()))
+ goto no_memory;
+
+ for (i = 0; i < ifaces_count; i++) {
+ virDomainInterfacePtr iface = &(ifaces[i]);
+ PyObject *py_ip_addrs = NULL;
+ PyObject *py_iface = NULL;
+
+ if (!(py_iface = PyDict_New()))
+ goto no_memory;
+
+ if (iface->ip_addrs_count) {
+ if (!(py_ip_addrs = PyList_New(iface->ip_addrs_count))) {
+ { Py_DECREF(py_iface); }
+ goto no_memory;
+ }
+ } else {
+ py_ip_addrs = VIR_PY_NONE;
+ }
+
+ for (j = 0; j < iface->ip_addrs_count; j++) {
+ virDomainIPAddressPtr ip_addr = &(iface->ip_addrs[j]);
+ PyObject *py_addr = PyDict_New();
+ const char *type;
+
+ if (!py_addr) {
+ { Py_DECREF(py_iface); }
+ { Py_DECREF(py_ip_addrs); }
+ goto no_memory;
+ }
+
+ switch (ip_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(py_addr, libvirt_constcharPtrWrap("addr"),
+ PyString_FromString(ip_addr->addr));
+ PyDict_SetItem(py_addr, libvirt_constcharPtrWrap("prefix"),
+ libvirt_intWrap(ip_addr->prefix));
+ PyDict_SetItem(py_addr, libvirt_constcharPtrWrap("type"),
+ PyString_FromString(type));
+
+ PyList_SetItem(py_ip_addrs, j, py_addr);
+ }
+
+ PyDict_SetItem(py_iface, libvirt_constcharPtrWrap("ip_addrs"),
+ py_ip_addrs);
+ PyDict_SetItem(py_iface, libvirt_constcharPtrWrap("hwaddr"),
+ libvirt_charPtrWrap(iface->hwaddr));
+
+ PyDict_SetItem(py_retval, libvirt_charPtrWrap(iface->name),
+ py_iface);
+ }
+
+ full_free = false;
+
+cleanup:
+ for (i = 0; i < ifaces_count; i++) {
+ /* We don't want to free values we've just
+ * shared with python variables unless
+ * there was an error and hence we are
+ * retyrning 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);
+ }
+ VIR_FREE(ifaces);
+
+ return py_retval;
+
+no_memory:
+ Py_XDECREF(py_retval);
+ py_retval = PyErr_NoMemory();
+ goto cleanup;
+}
+
/*******************************************
* Helper functions to avoid importing modules
* for every callback
@@ -6879,6 +6994,7 @@ static PyMethodDef libvirtMethods[] = {
{(char *) "virDomainBlockPeek", libvirt_virDomainBlockPeek, METH_VARARGS, NULL},
{(char *) "virDomainMemoryPeek", libvirt_virDomainMemoryPeek, METH_VARARGS, NULL},
{(char *) "virDomainGetDiskErrors", libvirt_virDomainGetDiskErrors, METH_VARARGS, NULL},
+ {(char *) "virDomainInterfacesAddresses", libvirt_virDomainInterfacesAddresses, METH_VARARGS, NULL},
{(char *) "virNodeGetMemoryParameters", libvirt_virNodeGetMemoryParameters, METH_VARARGS, NULL},
{(char *) "virNodeSetMemoryParameters", libvirt_virNodeSetMemoryParameters, METH_VARARGS, NULL},
{(char *) "virNodeGetCPUMap", libvirt_virNodeGetCPUMap, METH_VARARGS, NULL},
diff --git a/src/driver.h b/src/driver.h
index ec5fc53..b0cec05 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -500,6 +500,12 @@ typedef int
virTypedParameterPtr params,
int *nparams,
unsigned int flags);
+typedef int
+(*virDrvDomainInterfacesAddresses) (virDomainPtr dom,
+ virDomainInterfacePtr *ifaces,
+ unsigned int *ifaces_count,
+ unsigned int flags);
+
typedef int
(*virDrvDomainMemoryStats)(virDomainPtr domain,
@@ -1158,6 +1164,7 @@ struct _virDriver {
virDrvDomainInterfaceStats domainInterfaceStats;
virDrvDomainSetInterfaceParameters domainSetInterfaceParameters;
virDrvDomainGetInterfaceParameters domainGetInterfaceParameters;
+ virDrvDomainInterfacesAddresses domainInterfacesAddresses;
virDrvDomainMemoryStats domainMemoryStats;
virDrvDomainBlockPeek domainBlockPeek;
virDrvDomainMemoryPeek domainMemoryPeek;
diff --git a/src/libvirt.c b/src/libvirt.c
index db120b7..ce08ad4 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -7683,6 +7683,108 @@ error:
return -1;
}
+ /**
+ * virDomainInterfacesAddresses:
+ * @dom: domain object
+ * @ifaces: array of @dom interfaces
+ * @ifaces_count: number of items in @ifaces
+ * @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. Note, that single
+ * interface can have multiple or even none IP address.
+ *
+ * This API dynamically allocates the virDomainInterfacePtr
+ * struct based on how much interfaces domain @dom has,
+ * usually there's 1:1 correlation. The count of elements
+ * allocated is stored in @ifaces_count.
+ *
+ * Note that for some hypervisors a configured guest agent
+ * is needed for successful return from this API.
+ * Moreover, if guest agent is used then the interface name
+ * is the one seen by guest OS. To match such interface with
+ * the one from @dom XML use HW address or IP range.
+ *
+ * @ifaces->name is never NULL, @ifaces->hwaddr might be NULL,
+ *
+ * The caller *must* free @ifaces when no longer needed.
+ * Usual use case looks like this:
+ *
+ * virDomainInterfacePtr ifaces = NULL;
+ * unsigned int ifaces_count = 0;
+ * unsigned int i, j;
+ * virDomainPtr dom = ... obtain a domain here ...;
+ *
+ * if (virDomainInterfacesAddresses(dom, &ifaces, &ifaces_count, 0) < 0)
+ * goto cleanup;
+ *
+ * ... do something with returned values, for example:
+ * for (i = 0; i < ifaces_count; i++) {
+ * printf("name: %s", ifaces[i].name);
+ * if (ifaces[i].hwaddr)
+ * printf(" hwaddr: %s", ifaces[i].hwaddr);
+ *
+ * for (j = 0; j < ifaces[i].ip_addrs_count; j++) {
+ * virDomainIPAddressPtr ip_addr = ifaces[i].ip_addrs + j;
+ * printf("[addr: %s prefix: %d type: %d]",
+ * ip_addr->addr, ip_addr->prefix, ip_addr->type);
+ * }
+ * printf("\n");
+ * }
+ *
+ * cleanup:
+ * for (i = 0; i < ifaces_count; i++) {
+ * free(ifaces[i].name);
+ * free(ifaces[i].hwaddr);
+ * for (j = 0; j < ifaces[i].ip_addrs_count; j++)
+ * free(ifaces[i].ip_addrs[j].addr);
+ * free(ifaces[i].ip_addrs);
+ * }
+ * free(ifaces);
+ *
+ * Returns 0 on success,
+ * -1 in case of error
+ */
+int
+virDomainInterfacesAddresses (virDomainPtr dom,
+ virDomainInterfacePtr *ifaces,
+ unsigned int *ifaces_count,
+ unsigned int flags)
+{
+ virConnectPtr conn;
+
+ VIR_DOMAIN_DEBUG(dom, "ifaces=%p, ifaces_count=%p, flags=%x",
+ ifaces, ifaces_count, flags);
+
+ virResetLastError();
+
+ if (!VIR_IS_CONNECTED_DOMAIN(dom)) {
+ virLibDomainError(VIR_ERR_INVALID_DOMAIN, __FUNCTION__);
+ goto error;
+ }
+
+ conn = dom->conn;
+
+ virCheckNonNullArgGoto(ifaces, error);
+ virCheckNonNullArgGoto(ifaces_count, error);
+
+ if (conn->driver->domainInterfacesAddresses) {
+ int ret;
+ ret = conn->driver->domainInterfacesAddresses(dom, ifaces,
+ ifaces_count, flags);
+ if (ret < 0)
+ goto error;
+ return ret;
+ }
+
+ virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+ virDispatchError(dom ? dom->conn : NULL);
+ return -1;
+}
+
+
/**
* virDomainMemoryStats:
* @dom: pointer to the domain object
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index 4ee2d27..0719566 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -542,6 +542,7 @@ LIBVIRT_0.9.13 {
virDomainSnapshotIsCurrent;
virDomainSnapshotListAllChildren;
virDomainSnapshotRef;
+ virDomainInterfacesAddresses;
} LIBVIRT_0.9.11;
LIBVIRT_0.10.0 {
diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c
index 9914521..2d1a89e 100644
--- a/src/qemu/qemu_agent.c
+++ b/src/qemu/qemu_agent.c
@@ -1330,6 +1330,165 @@ cleanup:
return ret;
}
+
+int
+qemuAgentGetInterfaces(qemuAgentPtr mon,
+ virDomainInterfacePtr *ifaces,
+ unsigned int *ifaces_count)
+{
+ int ret = -1;
+ int i, j, size = -1;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr ret_array = NULL;
+
+ cmd = qemuAgentMakeCommand("guest-network-get-interfaces", NULL);
+
+ if (!cmd)
+ return -1;
+
+ if (qemuAgentCommand(mon, cmd, &reply, 1) < 0 ||
+ qemuAgentCheckError(cmd, reply) < 0)
+ goto cleanup;
+
+ if (!(ret_array = virJSONValueObjectGet(reply, "return"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("qemu agent didn't provide 'return' field"));
+ goto cleanup;
+ }
+
+ if ((size = virJSONValueArraySize(ret_array)) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("qemu agent didn't return an array of interfaces"));
+ goto cleanup;
+ }
+
+ *ifaces_count = (unsigned int) size;
+
+ if (VIR_ALLOC_N(*ifaces, size) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ for (i = 0; i < size; i++) {
+ virJSONValuePtr tmp_iface = virJSONValueArrayGet(ret_array, i);
+ virJSONValuePtr ip_addr_arr = NULL;
+ const char *name, *hwaddr;
+ int ip_addr_arr_size;
+
+ /* Shouldn't happen but doesn't hurt to check neither */
+ if (!tmp_iface) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("something has went really wrong"));
+ goto cleanup;
+ }
+
+ /* interface name is required to be presented */
+ name = virJSONValueObjectGetString(tmp_iface, "name");
+ if (!name) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("qemu agent didn't provide 'name' field"));
+ goto cleanup;
+ }
+
+ if (!((*ifaces)[i].name = strdup(name))) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ /* hwaddr might be omitted */
+ hwaddr = virJSONValueObjectGetString(tmp_iface, "hardware-address");
+ if (hwaddr && !((*ifaces)[i].hwaddr = strdup(hwaddr))) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ /* as well as IP address which - moreover -
+ * can be presented multiple times */
+ ip_addr_arr = virJSONValueObjectGet(tmp_iface, "ip-addresses");
+ if (!ip_addr_arr)
+ continue;
+
+ if ((ip_addr_arr_size = virJSONValueArraySize(ip_addr_arr)) < 0) {
+ /* Mmm, empty 'ip-address'? */
+ continue;
+ }
+
+ (*ifaces)[i].ip_addrs_count = (unsigned int) ip_addr_arr_size;
+
+ if (VIR_ALLOC_N((*ifaces)[i].ip_addrs, ip_addr_arr_size) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ for (j = 0; j < ip_addr_arr_size; j++) {
+ virJSONValuePtr ip_addr_obj = virJSONValueArrayGet(ip_addr_arr, j);
+ virDomainIPAddressPtr ip_addr = &(*ifaces)[i].ip_addrs[j];
+ const char *type, *addr;
+
+ /* Shouldn't happen but doesn't hurt to check neither */
+ if (!ip_addr_obj) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("something has went really wrong"));
+ goto cleanup;
+ }
+
+ type = virJSONValueObjectGetString(ip_addr_obj, "ip-address-type");
+ if (!type) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("qemu agent didn't provide 'ip-address-type'"
+ " field for interface '%s'"), name);
+ goto cleanup;
+ } else if (STREQ(type, "ipv4")) {
+ ip_addr->type = VIR_IP_ADDR_TYPE_IPV4;
+ } else if (STREQ(type, "ipv6")) {
+ ip_addr->type = VIR_IP_ADDR_TYPE_IPV6;
+ } else {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown ip address type '%s'"),
+ type);
+ goto cleanup;
+ }
+
+ addr = virJSONValueObjectGetString(ip_addr_obj, "ip-address");
+ if (!addr) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("qemu agent didn't provide 'ip-address'"
+ " field for interface '%s'"), name);
+ goto cleanup;
+ }
+ if (!(ip_addr->addr = strdup(addr))) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (virJSONValueObjectGetNumberInt(ip_addr_obj, "prefix",
+ &ip_addr->prefix) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("malformed 'prefix' field"));
+ goto cleanup;
+ }
+ }
+ }
+
+ ret = 0;
+
+cleanup:
+ if (ret < 0 && *ifaces && size> 0) {
+ for (i = 0; i < size; i++) {
+ 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);
+ }
+ VIR_FREE(*ifaces);
+ }
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
/*
* qemuAgentFSThaw:
* @mon: Agent
diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h
index cf70653..5cf93a0 100644
--- a/src/qemu/qemu_agent.h
+++ b/src/qemu/qemu_agent.h
@@ -76,6 +76,9 @@ int qemuAgentFSThaw(qemuAgentPtr mon);
int qemuAgentSuspend(qemuAgentPtr mon,
unsigned int target);
+int qemuAgentGetInterfaces(qemuAgentPtr mon,
+ virDomainInterfacePtr *ifaces,
+ unsigned int *ifaces_count);
int qemuAgentArbitraryCommand(qemuAgentPtr mon,
const char *cmd,
char **result,
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 3d9457f..0972163 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -15207,6 +15207,72 @@ qemuNodeSuspendForDuration(virConnectPtr conn ATTRIBUTE_UNUSED,
return nodeSuspendForDuration(target, duration, flags);
}
+static int
+qemuDomainInterfacesAddresses(virDomainPtr dom,
+ virDomainInterfacePtr *ifaces,
+ unsigned int *ifaces_count,
+ unsigned int flags)
+{
+ virQEMUDriverPtr driver = dom->conn->privateData;
+ qemuDomainObjPrivatePtr priv = NULL;
+ virDomainObjPtr vm = NULL;
+ unsigned long long ret = -1;
+
+ virCheckFlags(0, -1);
+
+ if (!ifaces || !ifaces_count) {
+ virReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("ifaces and ifaces_count cannot be NULL"));
+ return -1;
+ }
+
+ if (!(vm = qemuDomObjFromDomain(dom)))
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_OPERATION_INVALID, "%s",
+ _("domain is not running"));
+ goto cleanup;
+ }
+
+ priv = vm->privateData;
+
+ if (priv->agentError) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("QEMU guest agent is not "
+ "available due to an error"));
+ goto cleanup;
+ }
+
+ if (!priv->agent) {
+ virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+ _("QEMU guest agent is not configured"));
+ 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(vm);
+ ret = qemuAgentGetInterfaces(priv->agent, ifaces, ifaces_count);
+ qemuDomainObjExitAgent(vm);
+
+endjob:
+ if (qemuDomainObjEndJob(driver, vm) == 0)
+ vm = NULL;
+
+cleanup:
+ if (vm)
+ virObjectUnlock(vm);
+ return ret;
+}
+
static virDriver qemuDriver = {
.no = VIR_DRV_QEMU,
@@ -15301,6 +15367,7 @@ static virDriver qemuDriver = {
.domainMemoryPeek = qemuDomainMemoryPeek, /* 0.4.4 */
.domainGetBlockInfo = qemuDomainGetBlockInfo, /* 0.8.1 */
.nodeGetCPUStats = qemuNodeGetCPUStats, /* 0.9.3 */
+ .domainInterfacesAddresses = qemuDomainInterfacesAddresses, /* 0.9.13 */
.nodeGetMemoryStats = qemuNodeGetMemoryStats, /* 0.9.3 */
.nodeGetCellsFreeMemory = qemuNodeGetCellsFreeMemory, /* 0.4.4 */
.nodeGetFreeMemory = qemuNodeGetFreeMemory, /* 0.4.4 */
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 87c61f4..11b746f 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -6041,6 +6041,99 @@ done:
return rv;
}
+static int
+remoteDomainInterfacesAddresses(virDomainPtr dom,
+ virDomainInterfacePtr *ifaces,
+ unsigned int *ifaces_count,
+ 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));
+ args.flags = flags;
+ make_nonnull_domain(&args.dom, dom);
+
+ 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 done;
+ }
+
+ if (ret.ifaces.ifaces_len &&
+ VIR_ALLOC_N(*ifaces, ret.ifaces.ifaces_len) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ *ifaces_count = ret.ifaces.ifaces_len;
+
+ for (i = 0; i < *ifaces_count; i++) {
+ virDomainInterfacePtr iface = &((*ifaces)[i]);
+ remote_domain_interface *iface_ret = &(ret.ifaces.ifaces_val[i]);
+
+ if (!(iface->name = strdup(iface_ret->name))) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (iface_ret->hwaddr &&
+ !(iface->hwaddr = strdup(*iface_ret->hwaddr))) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ iface->ip_addrs_count = iface_ret->ip_addrs.ip_addrs_len;
+
+ if (iface->ip_addrs_count) {
+ if (VIR_ALLOC_N(iface->ip_addrs, iface->ip_addrs_count) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ for (j = 0; j < iface->ip_addrs_count; j++) {
+ virDomainIPAddressPtr ip_addr = &(iface->ip_addrs[j]);
+ remote_domain_ip_addr *ip_addr_ret =
+&(iface_ret->ip_addrs.ip_addrs_val[j]);
+
+ if (!(ip_addr->addr = strdup(ip_addr_ret->addr))) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ ip_addr->prefix = ip_addr_ret->prefix;
+ ip_addr->type = ip_addr_ret->type;
+ }
+ }
+ }
+
+ rv = 0;
+
+cleanup:
+ if (rv < 0) {
+ for (i = 0; i < *ifaces_count; i++) {
+ 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);
+ }
+ VIR_FREE(*ifaces);
+ }
+ xdr_free((xdrproc_t)xdr_remote_domain_interfaces_addresses_ret,
+ (char *) &ret);
+done:
+ remoteDriverUnlock(priv);
+ return rv;
+}
+
+
static void
remoteDomainEventQueue(struct private_data *priv, virDomainEventPtr event)
@@ -6361,6 +6454,7 @@ static virDriver remote_driver = {
.domainGetDiskErrors = remoteDomainGetDiskErrors, /* 0.9.10 */
.domainSetMetadata = remoteDomainSetMetadata, /* 0.9.10 */
.domainGetMetadata = remoteDomainGetMetadata, /* 0.9.10 */
+ .domainInterfacesAddresses = remoteDomainInterfacesAddresses, /* 0.9.13 */
.domainGetHostname = remoteDomainGetHostname, /* 0.10.0 */
.nodeSetMemoryParameters = remoteNodeSetMemoryParameters, /* 0.10.2 */
.nodeGetMemoryParameters = remoteNodeGetMemoryParameters, /* 0.10.2 */
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 9723377..3be3902 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -663,6 +663,29 @@ struct remote_domain_get_interface_parameters_ret {
int nparams;
};
+struct remote_domain_ip_addr {
+ int type;
+ remote_nonnull_string addr;
+ int prefix;
+};
+
+struct remote_domain_interface {
+ remote_nonnull_string name;
+ remote_string hwaddr;
+ remote_domain_ip_addr ip_addrs<>;
+};
+
+struct remote_domain_interfaces_addresses_args {
+ remote_nonnull_domain dom;
+ unsigned int flags;
+};
+
+struct remote_domain_interfaces_addresses_ret {
+ remote_domain_interface ifaces<>;
+};
+
+
+
struct remote_domain_memory_stats_args {
remote_nonnull_domain dom;
unsigned int maxStats;
@@ -4434,6 +4457,11 @@ enum remote_procedure {
/**
* @generate: server
*/
- REMOTE_PROC_NODE_DEVICE_DETACH_FLAGS = 301
+ REMOTE_PROC_NODE_DEVICE_DETACH_FLAGS = 301,
+
+ /**
+ * @generate: none
+ */
+ REMOTE_PROC_DOMAIN_INTERFACES_ADDRESSES = 302
};
diff --git a/tools/virsh-domain-monitor.c b/tools/virsh-domain-monitor.c
index 3ba829c..5c404a5 100644
--- a/tools/virsh-domain-monitor.c
+++ b/tools/virsh-domain-monitor.c
@@ -350,6 +350,97 @@ cmdDomMemStat(vshControl *ctl, const vshCmd *cmd)
return true;
}
+
+/* "domifaddr" command
+ */
+static const vshCmdInfo info_domifaddr[] = {
+ {"help", N_("get network interfaces addresses for a domain")},
+ {"desc", N_("Get network interfaces addresses for a running domain")},
+ {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_domifaddr[] = {
+ {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, N_("domain name, id or uuid")},
+ {"interface", VSH_OT_DATA, VSH_OFLAG_NONE, N_("network interface name")},
+ {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdDomIfAddr(vshControl *ctl, const vshCmd *cmd)
+{
+ virDomainPtr dom = NULL;
+ const char *device = NULL;
+ virDomainInterfacePtr ifaces = NULL;
+ unsigned int i, j, ifaces_count = 0;
+ unsigned int flags = 0;
+ bool ret = false;
+
+ if (!(dom = vshCommandOptDomain(ctl, cmd, NULL)))
+ return false;
+
+ if (vshCommandOptString(cmd, "interface", &device) < 0) {
+ goto cleanup;
+ }
+
+ if (virDomainInterfacesAddresses(dom, &ifaces, &ifaces_count, flags) < 0) {
+ vshError(ctl, _("Failed to query for interfaces addresses"));
+ goto cleanup;
+ }
+
+ vshPrintExtra(ctl, " %-10s %-17s %s\n%s\n",
+ _("Name"), _("HW address"), _("IP address"),
+ "---------------------------------------------------");
+
+ for (i = 0; i < ifaces_count; i++) {
+ virDomainInterfacePtr iface = &(ifaces[i]);
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ const char *hwaddr = "";
+ const char *ip_addr_str = NULL;
+
+ if (device && STRNEQ(device, iface->name))
+ continue;
+
+ if (iface->hwaddr)
+ hwaddr = iface->hwaddr;
+
+ for (j = 0; j < iface->ip_addrs_count; j++) {
+ if (j)
+ virBufferAddChar(&buf, ' ');
+ virBufferAsprintf(&buf, "%s/%d",
+ iface->ip_addrs[j].addr,
+ iface->ip_addrs[j].prefix);
+ }
+
+ ip_addr_str = virBufferContentAndReset(&buf);
+
+ if (!ip_addr_str)
+ ip_addr_str = "";
+
+ vshPrintExtra(ctl, " %-10s %-17s %s\n",
+ iface->name, hwaddr, ip_addr_str);
+
+ virBufferFreeAndReset(&buf);
+ }
+
+ ret = true;
+
+cleanup:
+ for (i = 0; i < ifaces_count; i++) {
+ 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);
+ }
+ VIR_FREE(ifaces);
+
+ if (dom)
+ virDomainFree(dom);
+ return ret;
+}
+
+
+
/*
* "domblkinfo" command
*/
@@ -1843,6 +1934,12 @@ const vshCmdDef domMonitoringCmds[] = {
.info = info_domif_getlink,
.flags = 0
},
+ {.name = "domifaddr",
+ .handler = cmdDomIfAddr,
+ .opts = opts_domifaddr,
+ .info = info_domifaddr,
+ .flags = 0
+ },
{.name = "domiflist",
.handler = cmdDomiflist,
.opts = opts_domiflist,
diff --git a/tools/virsh.pod b/tools/virsh.pod
index f4746cc..ed0c9b3 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -627,6 +627,17 @@ B<Explanation of fields> (fields appear in the following order):
flush_total_times - total time flush operations took (ns)
<-- other fields provided by hypervisor -->
+=item B<domifaddr> I<domain> [I<interface-device>]
+
+Get a list of interfaces of domain among with their IP and hardware
+addresses, or if I<interface-device> is specified limit output just
+for that one interface. Note, that interface name can be driver
+dependent meaning it can be name within guest OS or the name you would
+see in domain XML. Moreover, the whole command may require a guest
+agent to be configured for the queried domain under some drivers,
+notably qemu.
+
+
=item B<domifstat> I<domain> I<interface-device>
Get network interface stats for a running domain.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment