From: Remi Gacogne Date: Tue, 31 May 2022 14:55:41 +0000 (+0200) Subject: dnsdist: Add 'statistics' to the general API endpoint X-Git-Tag: auth-4.8.0-alpha0~76^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F11659%2Fhead;p=thirdparty%2Fpdns.git dnsdist: Add 'statistics' to the general API endpoint 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). --- diff --git a/pdns/dnsdist-web.cc b/pdns/dnsdist-web.cc index feeab6ceea..c0f2ece9d8 100644 --- a/pdns/dnsdist-web.cc +++ b/pdns/dnsdist-web.cc @@ -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(&e.second)) { + obj.insert({e.first, (double)(*val)->load()}); + } else if (const auto& adval = boost::get*>(&e.second)) { + obj.insert({e.first, (*adval)->load()}); + } else if (const auto& dval = boost::get(&e.second)) { + obj.insert({e.first, (**dval)}); + } else { + obj.insert({e.first, (double)(*boost::get(&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(&e.second)) { - obj.insert({e.first, (double)(*val)->load()}); - } else if (const auto& adval = boost::get*>(&e.second)) { - obj.insert({e.first, (*adval)->load()}); - } else if (const auto& dval = boost::get(&e.second)) { - obj.insert({e.first, (**dval)}); - } else { - obj.insert({e.first, (double)(*boost::get(&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*>(&item.second)) { + else if (const auto& adval = boost::get*>(&item.second)) { doc.push_back(Json::object { { "type", "StatisticItem" }, { "name", item.first }, diff --git a/regression-tests.dnsdist/test_API.py b/regression-tests.dnsdist/test_API.py index 87726fb0af..35c11e0b40 100644 --- a/regression-tests.dnsdist/test_API.py +++ b/regression-tests.dnsdist/test_API.py @@ -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)