Created
June 21, 2013 05:13
-
-
Save nehaljwani/5829005 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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