]> granicus.if.org Git - pdns/commitdiff
API: Expose ResponseStats and Ringbuffers
authorChris Hofstaedtler <chris.hofstaedtler@deduktiva.com>
Wed, 3 Jan 2018 12:15:15 +0000 (13:15 +0100)
committerChris Hofstaedtler <chris.hofstaedtler@deduktiva.com>
Wed, 3 Jan 2018 17:36:49 +0000 (18:36 +0100)
docs/http-api/swagger/authoritative-api-swagger.yaml
pdns/ws-api.cc
regression-tests.api/runtests.py
regression-tests.api/test_Servers.py

index cfb1cc893e95a5b18a5fc7cb61924f15c3358b19..f849b9feb41cf3cd056ae0fec4b38c7338534c5e 100644 (file)
@@ -352,7 +352,7 @@ paths:
   '/servers/{server_id}/statistics':
     get:
       summary: 'Query statistics.'
-      description: 'Query PowerDNS internal statistics. Returns a list of StatisticItem elements.'
+      description: 'Query PowerDNS internal statistics. Returns a list of BaseStatisticItem derived elements.'
       operationId: getStats
       tags:
         - stats
@@ -369,6 +369,8 @@ paths:
             type: array
             items:
               $ref: '#/definitions/StatisticItem'
+              $ref: '#/definitions/MapStatisticItem'
+              $ref: '#/definitions/RingStatisticItem'
 
   '/servers/{server_id}/search-log':
     get:
@@ -879,18 +881,69 @@ definitions:
         type: string
         description: 'The value of setting name'
 
-  StatisticItem:
-    title: StatisticItem
+  BaseStatisticItem:
+    title: BaseStatisticItem
     properties:
       name:
-        type: string
-        description: 'set to “StatisticItem”'
-      type:
         type: string
         description: 'The name of this item (e.g. ‘uptime’)'
-      value:
-        type: string
-        description: 'The value of item'
+
+  StatisticItem:
+    title: StatisticItem
+    allOf:
+      $ref: "#/definitions/BaseStatisticItem"
+      properties:
+        type:
+          enum: [StatisticItem]
+          description: 'set to "StatisticItem"'
+        value:
+          type: string
+          description: 'The value of item'
+
+  MapStatisticItem:
+    title: MapStatisticItem
+    allOf:
+      $ref: "#/definitions/BaseStatisticItem"
+      properties:
+        type:
+          enum: [MapStatisticItem]
+          description: 'set to "MapStatisticItem"'
+        value:
+          type: array
+          description: 'named statistic values'
+          items:
+            type: array
+            properties:
+              name:
+                type: string
+                description: 'item name'
+              value:
+                type: string
+                description: 'item value'
+
+  RingStatisticItem:
+    title: RingStatisticItem
+    allOf:
+      $ref: "#/definitions/BaseStatisticItem"
+      properties:
+        type:
+          enum: [RingStatisticItem]
+          description: 'set to "RingStatisticItem"'
+        size:
+          type: integer
+          description: 'for RingStatisticItem objects, the size of the ring'
+        value:
+          type: array
+          description: 'named ring statistic values'
+          items:
+            type: array
+            properties:
+              name:
+                type: string
+                description: 'item name'
+              value:
+                type: string
+                description: 'item value'
 
   SearchResultZone:
     title: SearchResultZone
index 4258f3d0b4bc7692a6d809f1120d9ac7a6ed89c9..dd390380f030e9eb9e6b2340747b41acc0c74c4e 100644 (file)
 #include "json.hh"
 #include "version.hh"
 #include "arguments.hh"
+#include "dnsparser.hh"
+#include "responsestats.hh"
+#include "statbag.hh"
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
 #include <sys/types.h>
 #include <iomanip>
 
-extern string s_programname;
 using json11::Json;
 
+extern string s_programname;
+extern ResponseStats g_rs;
+#ifndef RECURSOR
+extern StatBag S;
+#endif
+
 #ifndef HAVE_STRCASESTR
 
 /*
@@ -196,12 +204,15 @@ void apiServerStatistics(HttpRequest* req, HttpResponse* resp) {
   if(req->method != "GET")
     throw HttpMethodNotAllowedException();
 
-  map<string,string> items;
-  productServerStatisticsFetch(items);
+  typedef map<string, string> stat_items_t;
+  stat_items_t general_stats;
+  productServerStatisticsFetch(general_stats);
+
+  auto resp_qtype_stats = g_rs.getQTypeResponseCounts();
+  auto resp_size_stats = g_rs.getSizeResponseCounts();
 
   Json::array doc;
-  typedef map<string, string> items_t;
-  for(const items_t::value_type& item : items) {
+  for(const auto& item : general_stats) {
     doc.push_back(Json::object {
       { "type", "StatisticItem" },
       { "name", item.first },
@@ -209,6 +220,66 @@ void apiServerStatistics(HttpRequest* req, HttpResponse* resp) {
     });
   }
 
+  {
+    Json::array values;
+    for(const auto& item : resp_qtype_stats) {
+      if (item.second == 0)
+        continue;
+      values.push_back(Json::object {
+        { "name", DNSRecordContent::NumberToType(item.first) },
+        { "value", std::to_string(item.second) },
+      });
+    }
+
+    doc.push_back(Json::object {
+      { "type", "MapStatisticItem" },
+      { "name", "queries-by-qtype" },
+      { "value", values },
+    });
+  }
+
+  {
+    Json::array values;
+    for(const auto& item : resp_size_stats) {
+      if (item.second == 0)
+        continue;
+
+      values.push_back(Json::object {
+        { "name", std::to_string(item.first) },
+        { "value", std::to_string(item.second) },
+      });
+    }
+
+    doc.push_back(Json::object {
+      { "type", "MapStatisticItem" },
+      { "name", "response-sizes" },
+      { "value", values },
+    });
+  }
+
+#ifndef RECURSOR
+  for(const auto& ringName : S.listRings()) {
+    Json::array values;
+    const auto& ring = S.getRing(ringName);
+    for(const auto& item : ring) {
+      if (item.second == 0)
+        continue;
+
+      values.push_back(Json::object {
+        { "name", item.first },
+        { "value", std::to_string(item.second) },
+      });
+    }
+
+    doc.push_back(Json::object {
+      { "type", "RingStatisticItem" },
+      { "name", ringName },
+      { "size", std::to_string(S.getRingSize(ringName)) },
+      { "value", values },
+    });
+  }
+#endif
+
   resp->setBody(doc);
 }
 
index b6270241d61b5ac4d14575ee4118985d2c1c6f37..7f17a81831a5c74ade737ab5748faa22f0839b6c 100755 (executable)
@@ -100,6 +100,8 @@ common_args = [
     "--api-key="+APIKEY
 ]
 
+run_check_call(["make", "-C", "../pdns", "sdig"])
+
 if daemon == 'authoritative':
 
     # Prepare sqlite DB with some zones.
@@ -162,6 +164,9 @@ if not available:
     serverproc.wait()
     sys.exit(2)
 
+print "Query for example.com/A to create statistic data..."
+run_check_call(["../pdns/sdig", "127.0.0.1", str(DNSPORT), "example.com", "A"])
+
 print "Running tests..."
 returncode = 0
 test_env = {}
index 7b08d763ab620633dff890842789f9094c7d98b0..10bd99d8804bd437f0a94b21159ade7030ddab33 100644 (file)
@@ -39,5 +39,18 @@ class Servers(ApiTestCase):
     def test_read_statistics(self):
         r = self.session.get(self.url("/api/v1/servers/localhost/statistics"))
         self.assert_success_json(r)
-        data = dict([(r['name'], r['value']) for r in r.json()])
-        self.assertIn('uptime', data)
+        data = r.json()
+        self.assertIn('uptime', [e['name'] for e in data])
+        if is_auth():
+            print data
+            qtype_stats, respsize_stats, queries_stats = None, None, None
+            for elem in data:
+                if elem['type'] == 'MapStatisticItem' and elem['name'] == 'queries-by-qtype':
+                    qtype_stats = elem['value']
+                elif elem['type'] == 'MapStatisticItem' and elem['name'] == 'response-sizes':
+                    respsize_stats = elem['value']
+                elif elem['type'] == 'RingStatisticItem' and elem['name'] == 'queries':
+                    queries_stats = elem['value']
+            self.assertIn('A', [e['name'] for e in qtype_stats])
+            self.assertIn('60', [e['name'] for e in respsize_stats])
+            self.assertIn('example.com/A', [e['name'] for e in queries_stats])