Gathering vhostuser interface stats with ovs

When vhostuser interfaces are used, the interface statistics
are not available in /proc/net/dev.

This change looks at the openvswitch interfaces statistics
tables to provide this information for vhostuser interface.

Note that in openvswitch world drop/error doesn't always make sense
for some interface type. When these informations are not available we
set them to 0 on the virDomainInterfaceStats.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
Mehdi Abaakouk 2016-11-18 23:51:13 +01:00 committed by Michal Privoznik
parent 03876cf506
commit 013df874db
4 changed files with 139 additions and 9 deletions

View File

@ -2047,6 +2047,7 @@ virNetDevMidonetUnbindPort;
# util/virnetdevopenvswitch.h
virNetDevOpenvswitchAddPort;
virNetDevOpenvswitchGetMigrateData;
virNetDevOpenvswitchInterfaceStats;
virNetDevOpenvswitchRemovePort;
virNetDevOpenvswitchSetMigrateData;

View File

@ -66,6 +66,7 @@
#include "virhostcpu.h"
#include "virhostmem.h"
#include "virstats.h"
#include "virnetdevopenvswitch.h"
#include "capabilities.h"
#include "viralloc.h"
#include "viruuid.h"
@ -10974,6 +10975,7 @@ qemuDomainInterfaceStats(virDomainPtr dom,
virDomainInterfaceStatsPtr stats)
{
virDomainObjPtr vm;
virDomainNetDefPtr net = NULL;
size_t i;
int ret = -1;
@ -10991,19 +10993,27 @@ qemuDomainInterfaceStats(virDomainPtr dom,
/* Check the path is one of the domain's network interfaces. */
for (i = 0; i < vm->def->nnets; i++) {
if (vm->def->nets[i]->ifname &&
STREQ(vm->def->nets[i]->ifname, path)) {
ret = 0;
if (STREQ_NULLABLE(vm->def->nets[i]->ifname, path)) {
net = vm->def->nets[i];
break;
}
}
if (ret == 0)
ret = virNetInterfaceStats(path, stats);
else
if (!net) {
virReportError(VIR_ERR_INVALID_ARG,
_("invalid path, '%s' is not a known interface"), path);
goto cleanup;
}
if (net->type == VIR_DOMAIN_NET_TYPE_VHOSTUSER) {
if (virNetDevOpenvswitchInterfaceStats(path, stats) < 0)
goto cleanup;
} else {
if (virNetInterfaceStats(path, stats) < 0)
goto cleanup;
}
ret = 0;
cleanup:
virDomainObjEndAPI(&vm);
return ret;
@ -19187,9 +19197,17 @@ qemuDomainGetStatsInterface(virQEMUDriverPtr driver ATTRIBUTE_UNUSED,
QEMU_ADD_NAME_PARAM(record, maxparams,
"net", "name", i, dom->def->nets[i]->ifname);
if (virNetInterfaceStats(dom->def->nets[i]->ifname, &tmp) < 0) {
virResetLastError();
continue;
if (dom->def->nets[i]->type == VIR_DOMAIN_NET_TYPE_VHOSTUSER) {
if (virNetDevOpenvswitchInterfaceStats(dom->def->nets[i]->ifname,
&tmp) < 0) {
virResetLastError();
continue;
}
} else {
if (virNetInterfaceStats(dom->def->nets[i]->ifname, &tmp) < 0) {
virResetLastError();
continue;
}
}
QEMU_ADD_NET_PARAM(record, maxparams, i,

View File

@ -24,6 +24,8 @@
#include <config.h>
#include <stdio.h>
#include "virnetdevopenvswitch.h"
#include "vircommand.h"
#include "viralloc.h"
@ -270,3 +272,108 @@ int virNetDevOpenvswitchSetMigrateData(char *migrate, const char *ifname)
virCommandFree(cmd);
return ret;
}
/**
* virNetDevOpenvswitchInterfaceStats:
* @ifname: the name of the interface
* @stats: the retreived domain interface stat
*
* Retrieves the OVS interfaces stats
*
* Returns 0 in case of success or -1 in case of failure
*/
int
virNetDevOpenvswitchInterfaceStats(const char *ifname,
virDomainInterfaceStatsPtr stats)
{
virCommandPtr cmd = NULL;
char *output;
long long rx_bytes;
long long rx_packets;
long long tx_bytes;
long long tx_packets;
long long rx_errs;
long long rx_drop;
long long tx_errs;
long long tx_drop;
int ret = -1;
/* Just ensure the interface exists in ovs */
cmd = virCommandNewArgList(OVSVSCTL, "--timeout=5",
"get", "Interface", ifname,
"name", NULL);
virCommandSetOutputBuffer(cmd, &output);
if (virCommandRun(cmd, NULL) < 0) {
/* no ovs-vsctl or interface 'ifname' doesn't exists in ovs */
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Interface not found"));
goto cleanup;
}
VIR_FREE(output);
virCommandFree(cmd);
cmd = virCommandNewArgList(OVSVSCTL, "--timeout=5",
"get", "Interface", ifname,
"statistics:rx_bytes",
"statistics:rx_packets",
"statistics:tx_bytes",
"statistics:tx_packets", NULL);
virCommandSetOutputBuffer(cmd, &output);
if (virCommandRun(cmd, NULL) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Interface doesn't have statistics"));
goto cleanup;
}
/* The TX/RX fields appear to be swapped here
* because this is the host view. */
if (sscanf(output, "%lld\n%lld\n%lld\n%lld\n",
&tx_bytes, &tx_packets, &rx_bytes, &rx_packets) != 4) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Fail to parse ovs-vsctl output"));
goto cleanup;
}
stats->rx_bytes = rx_bytes;
stats->rx_packets = rx_packets;
stats->tx_bytes = tx_bytes;
stats->tx_packets = tx_packets;
VIR_FREE(output);
virCommandFree(cmd);
cmd = virCommandNewArgList(OVSVSCTL, "--timeout=5",
"get", "Interface", ifname,
"statistics:rx_errors",
"statistics:rx_dropped",
"statistics:tx_errors",
"statistics:tx_dropped", NULL);
virCommandSetOutputBuffer(cmd, &output);
if (virCommandRun(cmd, NULL) < 0) {
/* This interface don't have errors or dropped, so set them to 0 */
stats->rx_errs = 0;
stats->rx_drop = 0;
stats->tx_errs = 0;
stats->tx_drop = 0;
} else if (sscanf(output, "%lld\n%lld\n%lld\n%lld\n",
&tx_errs, &tx_drop, &rx_errs, &rx_drop) == 4) {
stats->rx_errs = rx_errs;
stats->rx_drop = rx_drop;
stats->tx_errs = tx_errs;
stats->tx_drop = tx_drop;
ret = 0;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Fail to parse ovs-vsctl output"));
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(output);
virCommandFree(cmd);
return ret;
}

View File

@ -48,4 +48,8 @@ int virNetDevOpenvswitchGetMigrateData(char **migrate, const char *ifname)
int virNetDevOpenvswitchSetMigrateData(char *migrate, const char *ifname)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
int virNetDevOpenvswitchInterfaceStats(const char *ifname,
virDomainInterfaceStatsPtr stats)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
#endif /* __VIR_NETDEV_OPENVSWITCH_H__ */