render / rpms / libvirt

Forked from rpms/libvirt 9 months ago
Clone
Pablo Greco 40546a
From dbcd08b082f3e96cf09328041549c0f1dc4e1335 Mon Sep 17 00:00:00 2001
Pablo Greco 40546a
Message-Id: <dbcd08b082f3e96cf09328041549c0f1dc4e1335@dist-git>
Pablo Greco 40546a
From: Michal Privoznik <mprivozn@redhat.com>
Pablo Greco 40546a
Date: Tue, 30 Jul 2019 15:30:51 +0200
Pablo Greco 40546a
Subject: [PATCH] virNetDevOpenvswitchInterfaceStats: Optimize for speed
Pablo Greco 40546a
MIME-Version: 1.0
Pablo Greco 40546a
Content-Type: text/plain; charset=UTF-8
Pablo Greco 40546a
Content-Transfer-Encoding: 8bit
Pablo Greco 40546a
Pablo Greco 40546a
We run 'ovs-vsctl' nine times (first to find if interface is
Pablo Greco 40546a
there and then eight times = for each stats member separately).
Pablo Greco 40546a
This is very inefficient. I've found a way to run it once and
Pablo Greco 40546a
with a bit of help from virJSON module we can parse out stats
Pablo Greco 40546a
we need.
Pablo Greco 40546a
Pablo Greco 40546a
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Pablo Greco 40546a
Reviewed-by: Ján Tomko <jtomko@redhat.com>
Pablo Greco 40546a
(cherry picked from commit c297eab52599c91a4cb26b66dbdfe9d07c3142d3)
Pablo Greco 40546a
Pablo Greco 40546a
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1721434
Pablo Greco 40546a
Pablo Greco 40546a
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Pablo Greco 40546a
Message-Id: <ba02a9b373ff7014e1edfc98da4edf11abe34a9e.1564493409.git.mprivozn@redhat.com>
Pablo Greco 40546a
Reviewed-by: Ján Tomko <jtomko@redhat.com>
Pablo Greco 40546a
---
Pablo Greco 40546a
 src/util/virnetdevopenvswitch.c | 111 +++++++++++++++++++++-----------
Pablo Greco 40546a
 1 file changed, 74 insertions(+), 37 deletions(-)
Pablo Greco 40546a
Pablo Greco 40546a
diff --git a/src/util/virnetdevopenvswitch.c b/src/util/virnetdevopenvswitch.c
Pablo Greco 40546a
index cb403aaf2e..dbd832053b 100644
Pablo Greco 40546a
--- a/src/util/virnetdevopenvswitch.c
Pablo Greco 40546a
+++ b/src/util/virnetdevopenvswitch.c
Pablo Greco 40546a
@@ -35,6 +35,7 @@
Pablo Greco 40546a
 #include "virmacaddr.h"
Pablo Greco 40546a
 #include "virstring.h"
Pablo Greco 40546a
 #include "virlog.h"
Pablo Greco 40546a
+#include "virjson.h"
Pablo Greco 40546a
 
Pablo Greco 40546a
 #define VIR_FROM_THIS VIR_FROM_NONE
Pablo Greco 40546a
 
Pablo Greco 40546a
@@ -318,58 +319,94 @@ int
Pablo Greco 40546a
 virNetDevOpenvswitchInterfaceStats(const char *ifname,
Pablo Greco 40546a
                                    virDomainInterfaceStatsPtr stats)
Pablo Greco 40546a
 {
Pablo Greco 40546a
-    char *tmp;
Pablo Greco 40546a
-    bool gotStats = false;
Pablo Greco 40546a
     VIR_AUTOPTR(virCommand) cmd = NULL;
Pablo Greco 40546a
     VIR_AUTOFREE(char *) output = NULL;
Pablo Greco 40546a
+    VIR_AUTOPTR(virJSONValue) jsonStats = NULL;
Pablo Greco 40546a
+    virJSONValuePtr jsonMap = NULL;
Pablo Greco 40546a
+    size_t i;
Pablo Greco 40546a
 
Pablo Greco 40546a
-    /* Just ensure the interface exists in ovs */
Pablo Greco 40546a
     cmd = virCommandNew(OVSVSCTL);
Pablo Greco 40546a
     virNetDevOpenvswitchAddTimeout(cmd);
Pablo Greco 40546a
-    virCommandAddArgList(cmd, "get", "Interface", ifname, "name", NULL);
Pablo Greco 40546a
+    virCommandAddArgList(cmd, "--if-exists", "--format=list", "--data=json",
Pablo Greco 40546a
+                         "--no-headings", "--columns=statistics", "list",
Pablo Greco 40546a
+                         "Interface", ifname, NULL);
Pablo Greco 40546a
     virCommandSetOutputBuffer(cmd, &output);
Pablo Greco 40546a
 
Pablo Greco 40546a
-    if (virCommandRun(cmd, NULL) < 0) {
Pablo Greco 40546a
+    /* The above command returns either:
Pablo Greco 40546a
+     * 1) empty string if @ifname doesn't exist, or
Pablo Greco 40546a
+     * 2) a JSON array, for instance:
Pablo Greco 40546a
+     *    ["map",[["collisions",0],["rx_bytes",0],["rx_crc_err",0],["rx_dropped",0],
Pablo Greco 40546a
+     *    ["rx_errors",0],["rx_frame_err",0],["rx_over_err",0],["rx_packets",0],
Pablo Greco 40546a
+     *    ["tx_bytes",12406],["tx_dropped",0],["tx_errors",0],["tx_packets",173]]]
Pablo Greco 40546a
+     */
Pablo Greco 40546a
+
Pablo Greco 40546a
+    if (virCommandRun(cmd, NULL) < 0 ||
Pablo Greco 40546a
+        STREQ_NULLABLE(output, "")) {
Pablo Greco 40546a
         /* no ovs-vsctl or interface 'ifname' doesn't exists in ovs */
Pablo Greco 40546a
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
Pablo Greco 40546a
                        _("Interface not found"));
Pablo Greco 40546a
         return -1;
Pablo Greco 40546a
     }
Pablo Greco 40546a
 
Pablo Greco 40546a
-#define GET_STAT(name, member) \
Pablo Greco 40546a
-    do { \
Pablo Greco 40546a
-        VIR_FREE(output); \
Pablo Greco 40546a
-        virCommandFree(cmd); \
Pablo Greco 40546a
-        cmd = virCommandNew(OVSVSCTL); \
Pablo Greco 40546a
-        virNetDevOpenvswitchAddTimeout(cmd); \
Pablo Greco 40546a
-        virCommandAddArgList(cmd, "--if-exists", "get", "Interface", \
Pablo Greco 40546a
-                             ifname, "statistics:" name, NULL); \
Pablo Greco 40546a
-        virCommandSetOutputBuffer(cmd, &output); \
Pablo Greco 40546a
-        if (virCommandRun(cmd, NULL) < 0 || !output || !*output || *output == '\n') { \
Pablo Greco 40546a
-            stats->member = -1; \
Pablo Greco 40546a
-        } else { \
Pablo Greco 40546a
-            if (virStrToLong_ll(output, &tmp, 10, &stats->member) < 0 || \
Pablo Greco 40546a
-                *tmp != '\n') { \
Pablo Greco 40546a
-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s", \
Pablo Greco 40546a
-                               _("Fail to parse ovs-vsctl output")); \
Pablo Greco 40546a
-                return -1; \
Pablo Greco 40546a
-            } \
Pablo Greco 40546a
-            gotStats = true; \
Pablo Greco 40546a
-        } \
Pablo Greco 40546a
-    } while (0)
Pablo Greco 40546a
+    if (!(jsonStats = virJSONValueFromString(output)) ||
Pablo Greco 40546a
+        !virJSONValueIsArray(jsonStats) ||
Pablo Greco 40546a
+        !(jsonMap = virJSONValueArrayGet(jsonStats, 1))) {
Pablo Greco 40546a
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
Pablo Greco 40546a
+                       _("Unable to parse ovs-vsctl output"));
Pablo Greco 40546a
+        return -1;
Pablo Greco 40546a
+    }
Pablo Greco 40546a
 
Pablo Greco 40546a
-    /* The TX/RX fields appear to be swapped here
Pablo Greco 40546a
-     * because this is the host view. */
Pablo Greco 40546a
-    GET_STAT("rx_bytes", tx_bytes);
Pablo Greco 40546a
-    GET_STAT("rx_packets", tx_packets);
Pablo Greco 40546a
-    GET_STAT("rx_errors", tx_errs);
Pablo Greco 40546a
-    GET_STAT("rx_dropped", tx_drop);
Pablo Greco 40546a
-    GET_STAT("tx_bytes", rx_bytes);
Pablo Greco 40546a
-    GET_STAT("tx_packets", rx_packets);
Pablo Greco 40546a
-    GET_STAT("tx_errors", rx_errs);
Pablo Greco 40546a
-    GET_STAT("tx_dropped", rx_drop);
Pablo Greco 40546a
+    stats->rx_bytes = stats->rx_packets = stats->rx_errs = stats->rx_drop = -1;
Pablo Greco 40546a
+    stats->tx_bytes = stats->tx_packets = stats->tx_errs = stats->tx_drop = -1;
Pablo Greco 40546a
 
Pablo Greco 40546a
-    if (!gotStats) {
Pablo Greco 40546a
+    for (i = 0; i < virJSONValueArraySize(jsonMap); i++) {
Pablo Greco 40546a
+        virJSONValuePtr item = virJSONValueArrayGet(jsonMap, i);
Pablo Greco 40546a
+        virJSONValuePtr jsonKey;
Pablo Greco 40546a
+        virJSONValuePtr jsonVal;
Pablo Greco 40546a
+        const char *key;
Pablo Greco 40546a
+        long long val;
Pablo Greco 40546a
+
Pablo Greco 40546a
+        if (!item ||
Pablo Greco 40546a
+            (!(jsonKey = virJSONValueArrayGet(item, 0))) ||
Pablo Greco 40546a
+            (!(jsonVal = virJSONValueArrayGet(item, 1))) ||
Pablo Greco 40546a
+            (!(key = virJSONValueGetString(jsonKey))) ||
Pablo Greco 40546a
+            (virJSONValueGetNumberLong(jsonVal, &val) < 0)) {
Pablo Greco 40546a
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
Pablo Greco 40546a
+                           _("Malformed ovs-vsctl output"));
Pablo Greco 40546a
+            return -1;
Pablo Greco 40546a
+        }
Pablo Greco 40546a
+
Pablo Greco 40546a
+        /* The TX/RX fields appear to be swapped here
Pablo Greco 40546a
+         * because this is the host view. */
Pablo Greco 40546a
+        if (STREQ(key, "rx_bytes")) {
Pablo Greco 40546a
+            stats->tx_bytes = val;
Pablo Greco 40546a
+        } else if (STREQ(key, "rx_packets")) {
Pablo Greco 40546a
+            stats->tx_packets = val;
Pablo Greco 40546a
+        } else if (STREQ(key, "rx_errors")) {
Pablo Greco 40546a
+            stats->tx_errs = val;
Pablo Greco 40546a
+        } else if (STREQ(key, "rx_dropped")) {
Pablo Greco 40546a
+            stats->tx_drop = val;
Pablo Greco 40546a
+        } else if (STREQ(key, "tx_bytes")) {
Pablo Greco 40546a
+            stats->rx_bytes = val;
Pablo Greco 40546a
+        } else if (STREQ(key, "tx_packets")) {
Pablo Greco 40546a
+            stats->rx_packets = val;
Pablo Greco 40546a
+        } else if (STREQ(key, "tx_errors")) {
Pablo Greco 40546a
+            stats->rx_errs = val;
Pablo Greco 40546a
+        } else if (STREQ(key, "tx_dropped")) {
Pablo Greco 40546a
+            stats->rx_drop = val;
Pablo Greco 40546a
+        } else {
Pablo Greco 40546a
+            VIR_DEBUG("Unused ovs-vsctl stat key=%s val=%lld", key, val);
Pablo Greco 40546a
+        }
Pablo Greco 40546a
+    }
Pablo Greco 40546a
+
Pablo Greco 40546a
+    if (stats->rx_bytes == -1 &&
Pablo Greco 40546a
+        stats->rx_packets == -1 &&
Pablo Greco 40546a
+        stats->rx_errs == -1 &&
Pablo Greco 40546a
+        stats->rx_drop == -1 &&
Pablo Greco 40546a
+        stats->tx_bytes == -1 &&
Pablo Greco 40546a
+        stats->tx_packets == -1 &&
Pablo Greco 40546a
+        stats->tx_errs == -1 &&
Pablo Greco 40546a
+        stats->tx_drop == -1) {
Pablo Greco 40546a
         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
Pablo Greco 40546a
                        _("Interface doesn't have any statistics"));
Pablo Greco 40546a
         return -1;
Pablo Greco 40546a
-- 
Pablo Greco 40546a
2.22.0
Pablo Greco 40546a