diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 826efbd293..500419c177 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2047,6 +2047,7 @@ virNetDevMidonetUnbindPort; # util/virnetdevopenvswitch.h virNetDevOpenvswitchAddPort; virNetDevOpenvswitchGetMigrateData; +virNetDevOpenvswitchInterfaceStats; virNetDevOpenvswitchRemovePort; virNetDevOpenvswitchSetMigrateData; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2997f1f95d..d86d301a91 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -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, diff --git a/src/util/virnetdevopenvswitch.c b/src/util/virnetdevopenvswitch.c index 9283bbb6d9..f003b3b134 100644 --- a/src/util/virnetdevopenvswitch.c +++ b/src/util/virnetdevopenvswitch.c @@ -24,6 +24,8 @@ #include +#include + #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; +} diff --git a/src/util/virnetdevopenvswitch.h b/src/util/virnetdevopenvswitch.h index 131be73243..0f9e1dfa66 100644 --- a/src/util/virnetdevopenvswitch.h +++ b/src/util/virnetdevopenvswitch.h @@ -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__ */