From 4ace9fe85de0564f3b9734dbbcb366474747665e Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Thu, 30 Nov 2017 10:47:34 +0100 Subject: [PATCH] dnsdist: Add Pools, cacheHitResponseRules to the API --- pdns/dnsdist-web.cc | 70 +++++++++++++++++----- pdns/dnsdistdist/docs/guides/webserver.rst | 29 ++++++++- regression-tests.dnsdist/test_API.py | 17 +++++- 3 files changed, 96 insertions(+), 20 deletions(-) diff --git a/pdns/dnsdist-web.cc b/pdns/dnsdist-web.cc index feb0ff6a6..3d5186156 100644 --- a/pdns/dnsdist-web.cc +++ b/pdns/dnsdist-web.cc @@ -375,6 +375,7 @@ static void connectionThread(int sock, ComboAddress remote, string password, str status = "DOWN"; else status = (a->upStatus ? "up" : "down"); + Json::array pools; for(const auto& p: a->pools) pools.push_back(p); @@ -392,7 +393,9 @@ static void connectionThread(int sock, ComboAddress remote, string password, str {"order", (int)a->order}, {"pools", pools}, {"latency", (int)(a->latencyUsec/1000.0)}, - {"queries", (double)a->queries}}; + {"queries", (double)a->queries}, + {"sendErrors", (int)a->sendErrors} + }; /* sending a latency for a DOWN server doesn't make sense */ if (a->availability == DownstreamState::Availability::Down) { @@ -417,16 +420,38 @@ static void connectionThread(int sock, ComboAddress remote, string password, str frontends.push_back(frontend); } + Json::array pools; + auto localPools = g_pools.getCopy(); + num=0; + for(const auto& pool :localPools) { + const auto& cache = pool.second->packetCache; + Json::object entry { + { "id", num++ }, + { "name", pool.first }, + { "serversCount", (int) pool.second->servers.size() }, + { "cacheSize", (double) (cache ? cache->getMaxEntries() : 0) }, + { "cacheEntries", (double) (cache ? cache->getEntriesCount() : 0) }, + { "cacheHits", (double) (cache ? cache->getHits() : 0) }, + { "cacheMisses", (double) (cache ? cache->getMisses() : 0) }, + { "cacheDeferredInserts", (double) (cache ? cache->getDeferredInserts() : 0) }, + { "cacheDeferredLookups", (double) (cache ? cache->getDeferredLookups() : 0) }, + { "cacheLookupCollisions", (double) (cache ? cache->getLookupCollisions() : 0) }, + { "cacheInsertCollisions", (double) (cache ? cache->getInsertCollisions() : 0) }, + { "cacheTTLTooShorts", (double) (cache ? cache->getTTLTooShorts() : 0) } + }; + pools.push_back(entry); + } + Json::array rules; auto localRules = g_rulactions.getCopy(); num=0; for(const auto& a : localRules) { Json::object rule{ - {"id", num++}, - {"matches", (double)a.first->d_matches}, - {"rule", a.first->toString()}, - {"action", a.second->toString()}, - {"action-stats", a.second->getStats()} + {"id", num++}, + {"matches", (double)a.first->d_matches}, + {"rule", a.first->toString()}, + {"action", a.second->toString()}, + {"action-stats", a.second->getStats()} }; rules.push_back(rule); } @@ -444,6 +469,19 @@ static void connectionThread(int sock, ComboAddress remote, string password, str responseRules.push_back(rule); } + Json::array cacheHitResponseRules; + num=0; + auto localCacheHitResponseRules = g_cachehitresprulactions.getCopy(); + for(const auto& a : localCacheHitResponseRules) { + Json::object rule{ + {"id", num++}, + {"matches", (double)a.first->d_matches}, + {"rule", a.first->toString()}, + {"action", a.second->toString()}, + }; + cacheHitResponseRules.push_back(rule); + } + string acl; vector vec; @@ -451,7 +489,7 @@ static void connectionThread(int sock, ComboAddress remote, string password, str for(const auto& s : vec) { if(!acl.empty()) acl += ", "; - acl+=s; + acl+=s; } string localaddresses; for(const auto& loc : g_locals) { @@ -460,14 +498,16 @@ static void connectionThread(int sock, ComboAddress remote, string password, str } Json my_json = Json::object { - { "daemon_type", "dnsdist" }, - { "version", VERSION}, - { "servers", servers}, - { "frontends", frontends }, - { "rules", rules}, - { "response-rules", responseRules}, - { "acl", acl}, - { "local", localaddresses} + { "daemon_type", "dnsdist" }, + { "version", VERSION}, + { "servers", servers}, + { "frontends", frontends }, + { "pools", pools }, + { "rules", rules}, + { "response-rules", responseRules}, + { "cache-hit-response-rules", cacheHitResponseRules}, + { "acl", acl}, + { "local", localaddresses} }; resp.headers["Content-Type"] = "application/json"; resp.body=my_json.dump(); diff --git a/pdns/dnsdistdist/docs/guides/webserver.rst b/pdns/dnsdistdist/docs/guides/webserver.rst index 0b57abf78..6f3767a37 100644 --- a/pdns/dnsdistdist/docs/guides/webserver.rst +++ b/pdns/dnsdistdist/docs/guides/webserver.rst @@ -102,8 +102,10 @@ URL Endpoints Get a quick overview of several parameters. :>json string acl: A string of comma-separated netmasks currently allowed by the :ref:`ACL `. + :>json list cache-hit-response-rules: A list of :json:object:`ResponseRule` objects applied on cache hits :>json string daemon_type: The type of daemon, always "dnsdist" :>json list frontends: A list of :json:object:`Frontend` objects + :>json list pools: A list of :json:object:`Pool` objects :>json list response-rules: A list of :json:object:`ResponseRule` objects :>json list rules: A list of :json:object:`Rule` objects :>json list servers: A list of :json:object:`Server` objects @@ -162,12 +164,29 @@ JSON Objects :property boolean udp: true if this is a UDP bind :property boolean tcp: true if this is a TCP bind +.. json:object:: Pool + + A description of a pool of backend servers. + + :property integer id: Internal identifier + :property integer cacheDeferredInserts: The number of times an entry could not be inserted in the associated cache, if any, because of a lock + :property integer cacheDeferredLookups: The number of times an entry could not be looked up from the associated cache, if any, because of a lock + :property integer cacheEntries: The current number of entries in the associated cache, if any + :property integer cacheHits: The number of cache hits for the associated cache, if any + :property integer cacheLookupCollisions: The number of times an entry retrieved from the cache based on the query hash did not match the actual query + :property integer cacheInsertCollisions: The number of times an entry could not be inserted into the cache because a different entry with the same hash already existed + :property integer cacheMisses: The number of cache misses for the associated cache, if any + :property integer cacheSize: The maximum number of entries in the associated cache, if any + :property integer cacheTTLTooShorts: The number of times an entry could not be inserted into the cache because its TTL was set below the minimum threshold + :property string name: Name of the pool + :property integer serversCount: Number of backends in this pool + .. json:object:: Rule This represents a policy that is applied to queries :property string action: The action taken when the rule matches (e.g. "to pool abuse") - :property dict action-stats: TODO + :property dict action-stats: A list of statistics whose content varies depending on the kind of rule :property integer id: The identifier (or order) of this rule :property integer matches: How many times this rule was hit :property string rule: The matchers for the packet (e.g. "qname==bad-domain1.example., bad-domain2.example.") @@ -176,7 +195,10 @@ JSON Objects This represents a policy that is applied to responses - TODO + :property string action: The action taken when the rule matches (e.g. "drop") + :property integer id: The identifier (or order) of this rule + :property integer matches: How many times this rule was hit + :property string rule: The matchers for the packet (e.g. "qname==bad-domain1.example., bad-domain2.example.") .. json:object:: Server @@ -192,7 +214,8 @@ JSON Objects :property integer qps: The current number of queries per second to this server :property integer qpsLimit: The configured maximum number of queries per second :property integer queries: Total number of queries sent to this backend - :property integer reuseds: TODO + :property integer reuseds: Number of queries for which a response was not received in time + :property integer sendErrors: Number of network errors while sending a query to this server :property string state: The state of the server (e.g. "DOWN" or "up") :property integer weight: The weight assigned to this server diff --git a/regression-tests.dnsdist/test_API.py b/regression-tests.dnsdist/test_API.py index 2fd53d8d4..c1d43b076 100644 --- a/regression-tests.dnsdist/test_API.py +++ b/regression-tests.dnsdist/test_API.py @@ -78,7 +78,7 @@ class TestAPIBasics(DNSDistTest): self.assertEquals(content['daemon_type'], 'dnsdist') - for key in ['version', 'acl', 'local', 'rules', 'response-rules', 'servers', 'frontends']: + for key in ['version', 'acl', 'local', 'rules', 'response-rules', 'cache-hit-response-rules', 'servers', 'frontends', 'pools']: self.assertIn(key, content) for rule in content['rules']: @@ -93,9 +93,15 @@ class TestAPIBasics(DNSDistTest): for key in ['id', 'matches']: self.assertTrue(rule[key] >= 0) + for rule in content['cache-hit-response-rules']: + for key in ['id', 'matches', 'rule', 'action']: + self.assertIn(key, rule) + for key in ['id', 'matches']: + self.assertTrue(rule[key] >= 0) + for server in content['servers']: for key in ['id', 'latency', 'name', 'weight', 'outstanding', 'qpsLimit', - 'reuseds', 'state', 'address', 'pools', 'qps', 'queries', 'order']: + 'reuseds', 'state', 'address', 'pools', 'qps', 'queries', 'order', 'sendErrors']: self.assertIn(key, server) for key in ['id', 'latency', 'weight', 'outstanding', 'qpsLimit', 'reuseds', @@ -111,6 +117,13 @@ class TestAPIBasics(DNSDistTest): for key in ['id', 'queries']: self.assertTrue(frontend[key] >= 0) + for pool in content['pools']: + for key in ['id', 'name', 'cacheSize', 'cacheEntries', 'cacheHits', 'cacheMisses', 'cacheDeferredInserts', 'cacheDeferredLookups', 'cacheLookupCollisions', 'cacheInsertCollisions', 'cacheTTLTooShorts']: + self.assertIn(key, pool) + + for key in ['id', 'cacheSize', 'cacheEntries', 'cacheHits', 'cacheMisses', 'cacheDeferredInserts', 'cacheDeferredLookups', 'cacheLookupCollisions', 'cacheInsertCollisions', 'cacheTTLTooShorts']: + self.assertTrue(pool[key] >= 0) + def testServersIDontExist(self): """ API: /api/v1/servers/idontexist (should be 404) -- 2.40.0