]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add 'statistics' to the general API endpoint 11659/head
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 31 May 2022 14:55:41 +0000 (16:55 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 7 Jun 2022 09:59:47 +0000 (11:59 +0200)
Making it possible to retrieve all metrics by querying one endpoint
(/api/v1/servers/localhost) instead of two (/api/v1/servers/localhost
and /api/v1/servers/localhost/statistics).

pdns/dnsdist-web.cc
regression-tests.dnsdist/test_API.py

index feeab6ceea919d8b96e193c379eead21bd8d4f7d..c0f2ece9d82bf05d2ebb7d6bc9dc9042ed229128 100644 (file)
@@ -837,6 +837,24 @@ static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp)
 
 using namespace json11;
 
+static void addStatsToJSONObject(Json::object& obj)
+{
+  for (const auto& e : g_stats.entries) {
+    if (e.first == "special-memory-usage") {
+      continue; // Too expensive for get-all
+    }
+    if (const auto& val = boost::get<pdns::stat_t*>(&e.second)) {
+      obj.insert({e.first, (double)(*val)->load()});
+    } else if (const auto& adval = boost::get<pdns::stat_t_trait<double>*>(&e.second)) {
+      obj.insert({e.first, (*adval)->load()});
+    } else if (const auto& dval = boost::get<double*>(&e.second)) {
+      obj.insert({e.first, (**dval)});
+    } else {
+      obj.insert({e.first, (double)(*boost::get<DNSDistStats::statfunction_t>(&e.second))(e.first)});
+    }
+  }
+}
+
 #ifndef DISABLE_BUILTIN_HTML
 static void handleJSONStats(const YaHTTP::Request& req, YaHTTP::Response& resp)
 {
@@ -859,19 +877,8 @@ static void handleJSONStats(const YaHTTP::Request& req, YaHTTP::Response& resp)
       { "server-policy", g_policy.getLocal()->getName()}
     };
 
-    for (const auto& e : g_stats.entries) {
-      if (e.first == "special-memory-usage")
-        continue; // Too expensive for get-all
-      if (const auto& val = boost::get<pdns::stat_t*>(&e.second)) {
-        obj.insert({e.first, (double)(*val)->load()});
-      } else if (const auto& adval = boost::get<pdns::stat_t_trait<double>*>(&e.second)) {
-        obj.insert({e.first, (*adval)->load()});
-      } else if (const auto& dval = boost::get<double*>(&e.second)) {
-        obj.insert({e.first, (**dval)});
-      } else {
-        obj.insert({e.first, (double)(*boost::get<DNSDistStats::statfunction_t>(&e.second))(e.first)});
-      }
-    }
+    addStatsToJSONObject(obj);
+
     Json my_json = obj;
     resp.body = my_json.dump();
     resp.headers["Content-Type"] = "application/json";
@@ -1165,6 +1172,9 @@ static void handleStats(const YaHTTP::Request& req, YaHTTP::Response& resp)
     localaddressesStr += addr;
   }
 
+  Json::object stats;
+  addStatsToJSONObject(stats);
+
   Json my_json = Json::object {
     { "daemon_type", "dnsdist" },
     { "version", VERSION},
@@ -1177,7 +1187,8 @@ static void handleStats(const YaHTTP::Request& req, YaHTTP::Response& resp)
     { "self-answered-response-rules", selfAnsweredResponseRules},
     { "acl", acl},
     { "local", localaddressesStr},
-    { "dohFrontends", dohs }
+    { "dohFrontends", dohs },
+    { "statistics", stats }
   };
   resp.headers["Content-Type"] = "application/json";
   resp.body = my_json.dump();
@@ -1252,7 +1263,7 @@ static void handleStatsOnly(const YaHTTP::Request& req, YaHTTP::Response& resp)
           { "value", (double)(*val)->load() }
         });
     }
-    else if(const auto& adval = boost::get<pdns::stat_t_trait<double>*>(&item.second)) {
+    else if (const auto& adval = boost::get<pdns::stat_t_trait<double>*>(&item.second)) {
       doc.push_back(Json::object {
           { "type", "StatisticItem" },
           { "name", item.first },
index 87726fb0af23140317362c4342a10b4f07ed6170..35c11e0b401aad8d7ba87faa5e001c6c5c33c6a5 100644 (file)
@@ -23,6 +23,21 @@ class APITestsBase(DNSDistTest):
     webserver("127.0.0.1:%s")
     setWebserverConfig({password="%s", apiKey="%s"})
     """
+    _expectedMetrics = ['responses', 'servfail-responses', 'queries', 'acl-drops',
+                        'frontend-noerror', 'frontend-nxdomain', 'frontend-servfail',
+                        'rule-drop', 'rule-nxdomain', 'rule-refused', 'self-answered', 'downstream-timeouts',
+                        'downstream-send-errors', 'trunc-failures', 'no-policy', 'latency0-1',
+                        'latency1-10', 'latency10-50', 'latency50-100', 'latency100-1000',
+                        'latency-slow', 'latency-sum', 'latency-count', 'latency-avg100', 'latency-avg1000',
+                        'latency-avg10000', 'latency-avg1000000', 'uptime', 'real-memory-usage', 'noncompliant-queries',
+                        'noncompliant-responses', 'rdqueries', 'empty-queries', 'cache-hits',
+                        'cache-misses', 'cpu-iowait', 'cpu-steal', 'cpu-sys-msec', 'cpu-user-msec', 'fd-usage', 'dyn-blocked',
+                        'dyn-block-nmg-size', 'rule-servfail', 'rule-truncated', 'security-status',
+                        'udp-in-csum-errors', 'udp-in-errors', 'udp-noport-errors', 'udp-recvbuf-errors', 'udp-sndbuf-errors',
+                        'udp6-in-errors', 'udp6-recvbuf-errors', 'udp6-sndbuf-errors', 'udp6-noport-errors', 'udp6-in-csum-errors',
+                        'doh-query-pipe-full', 'doh-response-pipe-full', 'proxy-protocol-invalid', 'tcp-listen-overflows',
+                        'outgoing-doh-query-pipe-full', 'tcp-query-pipe-full', 'tcp-cross-protocol-query-pipe-full',
+                        'tcp-cross-protocol-response-pipe-full']
 
 class TestAPIBasics(APITestsBase):
 
@@ -140,6 +155,13 @@ class TestAPIBasics(APITestsBase):
             for key in ['id', 'cacheSize', 'cacheEntries', 'cacheHits', 'cacheMisses', 'cacheDeferredInserts', 'cacheDeferredLookups', 'cacheLookupCollisions', 'cacheInsertCollisions', 'cacheTTLTooShorts', 'cacheCleanupCount']:
                 self.assertTrue(pool[key] >= 0)
 
+        stats = content['statistics']
+        for key in self._expectedMetrics:
+            self.assertIn(key, stats)
+            self.assertTrue(stats[key] >= 0)
+        for key in stats:
+            self.assertIn(key, self._expectedMetrics)
+
     def testServersLocalhostPool(self):
         """
         API: /api/v1/servers/localhost/pool?name=mypool
@@ -274,28 +296,12 @@ class TestAPIBasics(APITestsBase):
             self.assertEqual(entry['type'], 'StatisticItem')
             values[entry['name']] = entry['value']
 
-        expected = ['responses', 'servfail-responses', 'queries', 'acl-drops',
-                    'frontend-noerror', 'frontend-nxdomain', 'frontend-servfail',
-                    'rule-drop', 'rule-nxdomain', 'rule-refused', 'self-answered', 'downstream-timeouts',
-                    'downstream-send-errors', 'trunc-failures', 'no-policy', 'latency0-1',
-                    'latency1-10', 'latency10-50', 'latency50-100', 'latency100-1000',
-                    'latency-slow', 'latency-sum', 'latency-count', 'latency-avg100', 'latency-avg1000',
-                    'latency-avg10000', 'latency-avg1000000', 'uptime', 'real-memory-usage', 'noncompliant-queries',
-                    'noncompliant-responses', 'rdqueries', 'empty-queries', 'cache-hits',
-                    'cache-misses', 'cpu-iowait', 'cpu-steal', 'cpu-sys-msec', 'cpu-user-msec', 'fd-usage', 'dyn-blocked',
-                    'dyn-block-nmg-size', 'rule-servfail', 'rule-truncated', 'security-status',
-                    'udp-in-csum-errors', 'udp-in-errors', 'udp-noport-errors', 'udp-recvbuf-errors', 'udp-sndbuf-errors',
-                    'udp6-in-errors', 'udp6-recvbuf-errors', 'udp6-sndbuf-errors', 'udp6-noport-errors', 'udp6-in-csum-errors',
-                    'doh-query-pipe-full', 'doh-response-pipe-full', 'proxy-protocol-invalid', 'tcp-listen-overflows',
-                    'outgoing-doh-query-pipe-full', 'tcp-query-pipe-full', 'tcp-cross-protocol-query-pipe-full',
-                    'tcp-cross-protocol-response-pipe-full']
-
-        for key in expected:
+        for key in self._expectedMetrics:
             self.assertIn(key, values)
             self.assertTrue(values[key] >= 0)
 
         for key in values:
-            self.assertIn(key, expected)
+            self.assertIn(key, self._expectedMetrics)
 
     def testJsonstatStats(self):
         """
@@ -309,19 +315,7 @@ class TestAPIBasics(APITestsBase):
         self.assertTrue(r.json())
         content = r.json()
 
-        expected = ['responses', 'servfail-responses', 'queries', 'acl-drops',
-                    'frontend-noerror', 'frontend-nxdomain', 'frontend-servfail',
-                    'rule-drop', 'rule-nxdomain', 'rule-refused', 'rule-truncated', 'self-answered', 'downstream-timeouts',
-                    'downstream-send-errors', 'trunc-failures', 'no-policy', 'latency0-1',
-                    'latency1-10', 'latency10-50', 'latency50-100', 'latency100-1000',
-                    'latency-slow', 'latency-avg100', 'latency-avg1000', 'latency-avg10000',
-                    'latency-avg1000000', 'uptime', 'real-memory-usage', 'noncompliant-queries',
-                    'noncompliant-responses', 'rdqueries', 'empty-queries', 'cache-hits',
-                    'cache-misses', 'cpu-user-msec', 'cpu-sys-msec', 'fd-usage', 'dyn-blocked',
-                    'dyn-block-nmg-size', 'packetcache-hits', 'packetcache-misses', 'over-capacity-drops',
-                    'too-old-drops', 'proxy-protocol-invalid', 'doh-query-pipe-full', 'doh-response-pipe-full']
-
-        for key in expected:
+        for key in self._expectedMetrics:
             self.assertIn(key, content)
             self.assertTrue(content[key] >= 0)