]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
recursor webhandler for prometheus metrics
authorGreg Cockroft <greg@quad9.net>
Fri, 26 Apr 2019 13:33:25 +0000 (09:33 -0400)
committerGreg Cockroft <greg@quad9.net>
Mon, 6 May 2019 16:40:19 +0000 (12:40 -0400)
A new url “/metrics” to the webserver of the precursor. Generates
prometheus format statistics. Served by a webhandler and will honor api
blacklisting of stats.

pdns/recursordist/Makefile.am
pdns/recursordist/docs/http-api/index.rst
pdns/recursordist/docs/http-api/prometheus.rst [new file with mode: 0644]
pdns/recursordist/rec_metrics.hh [new file with mode: 0644]
pdns/ws-recursor.cc
regression-tests.api/test_Servers.py
regression-tests.api/test_helper.py

index 9d1faba044ac987482530ecab1f219a206a5bf3d..794488f1fae76d2427beeba60e91a065e2120f52 100644 (file)
@@ -142,7 +142,7 @@ pdns_recursor_SOURCES = \
        rec-lua-conf.hh rec-lua-conf.cc \
        rec-protobuf.cc rec-protobuf.hh \
        rec-snmp.hh rec-snmp.cc \
-       rec_channel.cc rec_channel.hh \
+       rec_channel.cc rec_channel.hh rec_metrics.hh \
        rec_channel_rec.cc \
        recpacketcache.cc recpacketcache.hh \
        recursor_cache.cc recursor_cache.hh \
index 9fd799638717236d8a452f0e65595b2767500a48..e13b2ec21fe24a50e49127fe6376294b784a3dba 100644 (file)
@@ -55,6 +55,7 @@ All API endpoints for the PowerDNS Recursor are documented here:
 .. toctree::
   :maxdepth: 1
 
+  prometheus
   ../common/api/endpoint-api
   ../common/api/endpoint-servers
   endpoint-servers-config
diff --git a/pdns/recursordist/docs/http-api/prometheus.rst b/pdns/recursordist/docs/http-api/prometheus.rst
new file mode 100644 (file)
index 0000000..0a11b01
--- /dev/null
@@ -0,0 +1,37 @@
+Prometheus Data Endpoint
+========================
+
+.. versionadded:: 4.2.x
+
+.. http:get:: /metrics
+
+    Get statistics from Recursor in `Prometheus <https://prometheus.io>`_ format. Uses :ref:`setting-webserver-password` and returned list can be controlled with :ref:`setting-stats-api-blacklist`
+
+  **Example request**:
+   .. sourcecode:: bash
+
+    curl -i -u=#:webpassword http://127.0.0.1:8081/metrics
+
+  **Example response**:
+   .. sourcecode:: http
+
+    HTTP/1.1 200 OK
+    Connection: close
+    Content-Length: 19203
+    Content-Type: text/plain
+    Server: PowerDNS/0.0.16480.0.g876dd46192
+
+    # HELP pdnsrecursor_all_outqueries Number of outgoing UDP queries since starting
+    # TYPE pdnsrecursor_all_outqueries counter
+    pdnsrecursor_all_outqueries 20
+    # HELP pdnsrecursor_answers_slow Number of queries answered after 1 second
+    # TYPE pdnsrecursor_answers_slow counter
+    pdnsrecursor_answers_slow 0
+    # HELP pdnsrecursor_answers0_1 Number of queries answered within 1 millisecond
+    # TYPE pdnsrecursor_answers0_1 counter
+    pdnsrecursor_answers0_1 0
+    # HELP pdnsrecursor_answers1_10 Number of queries answered within 10 milliseconds
+    # TYPE pdnsrecursor_answers1_10 counter
+
+    ...
+
diff --git a/pdns/recursordist/rec_metrics.hh b/pdns/recursordist/rec_metrics.hh
new file mode 100644 (file)
index 0000000..ec1580f
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#pragma once
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include <inttypes.h>
+#include <unistd.h>
+#include <atomic>
+
+// Metric types for Prometheus
+enum class PrometheusMetricType: int {
+    counter = 1,
+    gauge = 2
+};
+
+// Keeps additional information about metrics
+struct MetricDefinition {
+    MetricDefinition(PrometheusMetricType prometheusType, const std::string& description) {
+      this->prometheusType = prometheusType;
+      this->description = description;
+    }
+
+    MetricDefinition() = default;
+
+    // Metric description
+    std::string description;
+    // Metric type for Prometheus
+    PrometheusMetricType prometheusType;
+};
+
+struct MetricDefinitionStorage {
+    // Return metric definition by name
+    bool getMetricDetails(std::string metricName, MetricDefinition& metric) {
+      auto metricDetailsIter = metrics.find(metricName);
+
+      if (metricDetailsIter == metrics.end()) {
+        return false;
+      }
+
+      metric = metricDetailsIter->second;
+      return true;
+    };
+
+    // Return string representation of Prometheus metric type
+    std::string getPrometheusStringMetricType(PrometheusMetricType metricType) {
+      switch (metricType) {
+        case PrometheusMetricType::counter:
+          return "counter";
+              break;
+        case PrometheusMetricType::gauge:
+          return "gauge";
+              break;
+        default:
+          return "";
+              break;
+      }
+    };
+
+    // Description and types for prometheus output of stats
+    std::map<std::string, MetricDefinition> metrics = {
+            { "all-outqueries",              MetricDefinition(PrometheusMetricType::counter, "Number of outgoing UDP queries since starting") },
+
+            { "answers-slow",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered after 1 second") },
+            { "answers0-1",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered within 1 millisecond") },
+            { "answers1-10",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered within 10 milliseconds") },
+            { "answers10-100",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered within 100 milliseconds") },
+            { "answers100-1000",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered within 1 second") },
+
+            { "auth4-answers-slow",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered by auth4s after 1 second") },
+            { "auth4-answers0-1",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered by auth4s within 1 millisecond") },
+            { "auth4-answers1-10",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered by auth4s within 10 milliseconds") },
+            { "auth4-answers10-100",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered by auth4s within 100 milliseconds") },
+            { "auth4-answers100-1000",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered by auth4s within 1 second") },
+
+            { "auth6-answers-slow",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered by auth6s after 1 second") },
+            { "auth6-answers0-1",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered by auth6s within 1 millisecond") },
+            { "auth6-answers1-10",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered by auth6s within 10 milliseconds") },
+            { "auth6-answers10-100",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered by auth6s within 100 milliseconds") },
+            { "auth6-answers100-1000",              MetricDefinition(PrometheusMetricType::counter, "Number of queries answered by auth6s within 1 second") },
+
+
+
+            { "auth-zone-queries",              MetricDefinition(PrometheusMetricType::counter, "Number of queries to locally hosted authoritative zones (`setting-auth-zones`) since starting") },
+            { "cache-bytes",              MetricDefinition(PrometheusMetricType::gauge, "Size of the cache in bytes") },
+            { "cache-entries",              MetricDefinition(PrometheusMetricType::gauge, "Number of entries in the cache") },
+            { "cache-hits",              MetricDefinition(PrometheusMetricType::counter, "Number of of cache hits since starting, this does **not** include hits that got answered from the packet-cache") },
+            { "cache-misses",              MetricDefinition(PrometheusMetricType::counter, "Number of cache misses since starting") },
+            { "case-mismatches",              MetricDefinition(PrometheusMetricType::counter, "Number of mismatches in character case since starting") },
+            { "chain-resends",              MetricDefinition(PrometheusMetricType::gauge, "Number of queries chained to existing outstanding") },
+            { "client-parse-errors",              MetricDefinition(PrometheusMetricType::counter, "Number of client packets that could not be parsed") },
+            { "concurrent-queries",              MetricDefinition(PrometheusMetricType::gauge, "Number of MThreads currently running") },
+            { "cpu-msec-thread-0",              MetricDefinition(PrometheusMetricType::counter, "Number of milliseconds spent in thread n") },
+            { "dlg-only-drops",              MetricDefinition(PrometheusMetricType::counter, "Number of records dropped because of `setting-delegation-only` setting") },
+
+            { "dnssec-authentic-data-queries",              MetricDefinition(PrometheusMetricType::counter, "Number of queries received with the AD bit set") },
+            { "dnssec-check-disabled-queries",              MetricDefinition(PrometheusMetricType::counter, "Number of queries received with the CD bit set") },
+            { "dnssec-queries",              MetricDefinition(PrometheusMetricType::counter, "Number of queries received with the DO bit set") },
+            { "dnssec-result-bogus",              MetricDefinition(PrometheusMetricType::counter, "Number of DNSSEC validations that had the Bogus state") },
+            { "dnssec-result-indeterminate",              MetricDefinition(PrometheusMetricType::counter, "Number of DNSSEC validations that had the Indeterminate state") },
+            { "dnssec-result-insecure",              MetricDefinition(PrometheusMetricType::counter, "Number of DNSSEC validations that had the Insecure state") },
+            { "dnssec-result-nta",              MetricDefinition(PrometheusMetricType::counter, "Number of DNSSEC validations that had the (negative trust anchor) state") },
+            { "dnssec-result-secure",              MetricDefinition(PrometheusMetricType::counter, "Number of DNSSEC validations that had the Secure state") },
+
+            { "dnssec-validations",              MetricDefinition(PrometheusMetricType::counter, "Number of DNSSEC validations performed") },
+            { "dont-outqueries",              MetricDefinition(PrometheusMetricType::counter, "Number of outgoing queries dropped because of `setting-dont-query` setting") },
+            { "ecs-queries",              MetricDefinition(PrometheusMetricType::counter, "Number of outgoing queries adorned with an EDNS Client Subnet option") },
+            { "ecs-responses",              MetricDefinition(PrometheusMetricType::counter, "Number of responses received from authoritative servers with an EDNS Client Subnet option we used") },
+            { "edns-ping-matches",              MetricDefinition(PrometheusMetricType::counter, "Number of servers that sent a valid EDNS PING response") },
+            { "edns-ping-mismatches",              MetricDefinition(PrometheusMetricType::counter, "Number of servers that sent an invalid EDN PING response") },
+            { "failed-host-entries",              MetricDefinition(PrometheusMetricType::counter, "Number of servers that failed to resolve") },
+            { "ignored-packets",              MetricDefinition(PrometheusMetricType::counter, "Number of non-query packets received on server sockets that should only get query packets") },
+            { "ipv6-outqueries",              MetricDefinition(PrometheusMetricType::counter, "Number of outgoing queries over IPv6") },
+            { "ipv6-questions",              MetricDefinition(PrometheusMetricType::counter, "Number of end-user initiated queries with the RD bit set, received over IPv6 UDP") },
+            { "malloc-bytes",              MetricDefinition(PrometheusMetricType::counter, "Number of bytes allocated by the process (broken, always returns 0)") },
+            { "max-cache-entries",              MetricDefinition(PrometheusMetricType::gauge, "Currently configured maximum number of cache entries") },
+            { "max-packetcache-entries",              MetricDefinition(PrometheusMetricType::counter, "Currently configured maximum number of packet cache entries") },
+            { "max-mthread-stack",              MetricDefinition(PrometheusMetricType::counter, "Maximum amount of thread stack ever used") },
+
+
+            { "negcache-entries",              MetricDefinition(PrometheusMetricType::gauge, "Number of entries in the negative answer cache") },
+            { "no-packet-error",              MetricDefinition(PrometheusMetricType::counter, "Number of erroneous received packets") },
+            { "noedns-outqueries",              MetricDefinition(PrometheusMetricType::counter, "Number of queries sent out without EDNS") },
+            { "noerror-answers",              MetricDefinition(PrometheusMetricType::counter, "Number of NOERROR answers since starting") },
+            { "noping-outqueries",              MetricDefinition(PrometheusMetricType::counter, "Number of queries sent out without ENDS PING") },
+            { "nsset-invalidations",              MetricDefinition(PrometheusMetricType::counter, "Number of times an nsset was dropped because it no longer worked") },
+            { "nsspeeds-entries",              MetricDefinition(PrometheusMetricType::gauge, "Number of entries in the NS speeds map") },
+            { "nxdomain-answers",              MetricDefinition(PrometheusMetricType::counter, "Number of NXDOMAIN answers since starting") },
+            { "outgoing-timeouts",              MetricDefinition(PrometheusMetricType::counter, "Number of timeouts on outgoing UDP queries since starting") },
+            { "outgoing4-timeouts",              MetricDefinition(PrometheusMetricType::counter, "Number of timeouts on outgoing UDP IPv4 queries since starting") },
+            { "outgoing6-timeouts",              MetricDefinition(PrometheusMetricType::counter, "Number of timeouts on outgoing UDP IPv6 queries since starting") },
+            { "over-capacity-drops",              MetricDefinition(PrometheusMetricType::counter, "Number of questions dropped because over maximum concurrent query limit") },
+            { "packetcache-bytes",              MetricDefinition(PrometheusMetricType::gauge, "Size of the packet cache in bytes") },
+            { "packetcache-entries",              MetricDefinition(PrometheusMetricType::gauge, "Number of packet cache entries") },
+            { "packetcache-hits",              MetricDefinition(PrometheusMetricType::counter, "Number of packet cache hits") },
+            { "packetcache-misses",              MetricDefinition(PrometheusMetricType::counter, "Number of packet cache misses") },
+
+            { "policy-drops",              MetricDefinition(PrometheusMetricType::counter, "Number of packets dropped because of (Lua) policy decision") },
+            { "policy-result-noaction",              MetricDefinition(PrometheusMetricType::counter, "Number of packets that were not actioned upon by the RPZ/filter engine") },
+            { "policy-result-drop",              MetricDefinition(PrometheusMetricType::counter, "Number of packets that were dropped by the RPZ/filter engine") },
+            { "policy-result-nxdomain",              MetricDefinition(PrometheusMetricType::counter, "Number of packets that were replied to with NXDOMAIN by the RPZ/filter engine") },
+            { "policy-result-nodata",              MetricDefinition(PrometheusMetricType::counter, "Number of packets that were replied to with no data by the RPZ/filter engine") },
+            { "policy-result-truncate",              MetricDefinition(PrometheusMetricType::counter, "Number of packets that were were forced to TCP by the RPZ/filter engine") },
+            { "policy-result-custom",              MetricDefinition(PrometheusMetricType::counter, "Number of packets that were sent a custom answer by the RPZ/filter engine") },
+
+            { "qa-latency",              MetricDefinition(PrometheusMetricType::gauge, "Shows the current latency average, in microseconds, exponentially weighted over past 'latency-statistic-size' packets") },
+            { "query-pipe-full-drops",              MetricDefinition(PrometheusMetricType::counter, "Number of questions dropped because the query distribution pipe was full") },
+            { "questions",              MetricDefinition(PrometheusMetricType::counter, "Counts all end-user initiated queries with the RD bit set") },
+            { "rebalanced-queries",              MetricDefinition(PrometheusMetricType::counter, "Number of queries balanced to a different worker thread because the first selected one was above the target load configured with 'distribution-load-factor'") },
+            { "resource-limits",              MetricDefinition(PrometheusMetricType::counter, "Number of queries that could not be performed because of resource limits") },
+            { "security-status",              MetricDefinition(PrometheusMetricType::counter, "security status based on `securitypolling`") },
+            { "server-parse-errors",              MetricDefinition(PrometheusMetricType::counter, "Number of server replied packets that could not be parsed") },
+            { "servfail-answers",              MetricDefinition(PrometheusMetricType::counter, "Number of SERVFAIL answers since starting") },
+            { "spoof-prevents",              MetricDefinition(PrometheusMetricType::counter, "Number of times PowerDNS considered itself spoofed, and dropped the data") },
+            { "sys-msec",              MetricDefinition(PrometheusMetricType::counter, "Number of CPU milliseconds spent in 'system' mode") },
+            { "tcp-client-overflow",              MetricDefinition(PrometheusMetricType::counter, "Number of times an IP address was denied TCP access because it already had too many connections") },
+            { "tcp-clients",              MetricDefinition(PrometheusMetricType::gauge, "Number of currently active TCP/IP clients") },
+            { "tcp-outqueries",              MetricDefinition(PrometheusMetricType::counter, "Number of outgoing TCP queries since starting") },
+            { "tcp-questions",              MetricDefinition(PrometheusMetricType::counter, "Number of all incoming TCP queries since starting") },
+            { "throttle-entries",              MetricDefinition(PrometheusMetricType::gauge, "Number of of entries in the throttle map") },
+            { "throttled-out",              MetricDefinition(PrometheusMetricType::counter, "Number of throttled outgoing UDP queries since starting") },
+            { "throttled-outqueries",              MetricDefinition(PrometheusMetricType::counter, "Number of throttled outgoing UDP queries since starting") },
+            { "too-old-drops",              MetricDefinition(PrometheusMetricType::counter, "Number of questions dropped that were too old") },
+            { "truncated-drops",              MetricDefinition(PrometheusMetricType::counter, "Number of questions dropped because they were larger than 512 bytes") },
+            { "empty-queries",              MetricDefinition(PrometheusMetricType::counter, "Questions dropped because they had a QD count of 0") },
+            { "unauthorized-tcp",              MetricDefinition(PrometheusMetricType::counter, "Number of TCP questions denied because of allow-from restrictions") },
+            { "unauthorized-udp",              MetricDefinition(PrometheusMetricType::counter, "Number of UDP questions denied because of allow-from restrictions") },
+            { "unexpected-packets",              MetricDefinition(PrometheusMetricType::counter, "Number of answers from remote servers that were unexpected (might point to spoofing)") },
+            { "unreachables",              MetricDefinition(PrometheusMetricType::counter, "Number of times nameservers were unreachable since starting") },
+            { "uptime",              MetricDefinition(PrometheusMetricType::counter, "Number of seconds process has been running") },
+            { "user-msec",              MetricDefinition(PrometheusMetricType::counter, "Number of CPU milliseconds spent in 'user' mode") },
+            { "variable-responses",              MetricDefinition(PrometheusMetricType::counter, "Number of responses that were marked as 'variable'") },
+
+            { "x-our-latency",              MetricDefinition(PrometheusMetricType::counter, "How much time was spent within PowerDNS in microseconds") },
+            { "x-ourtime0-1",              MetricDefinition(PrometheusMetricType::counter, "Counts responses where between 0 and 1 milliseconds was spent within the Recursor") },
+            { "x-ourtime1-2",              MetricDefinition(PrometheusMetricType::counter, "Counts responses where between 1 and 2 milliseconds was spent within the Recursor") },
+            { "x-ourtime2-4",              MetricDefinition(PrometheusMetricType::counter, "Counts responses where between 2 and 4 milliseconds was spent within the Recursor") },
+            { "x-ourtime4-8",              MetricDefinition(PrometheusMetricType::counter, "Counts responses where between 4 and 8 milliseconds was spent within the Recursor") },
+            { "x-ourtime8-16",              MetricDefinition(PrometheusMetricType::counter, "Counts responses where between 8 and 16 milliseconds was spent within the Recursor") },
+            { "x-ourtime16-32",              MetricDefinition(PrometheusMetricType::counter, "Counts responses where between 16 and 32 milliseconds was spent within the Recursor") },
+            { "x-ourtime-slow",              MetricDefinition(PrometheusMetricType::counter, "Counts responses where more than 32 milliseconds was spent within the Recursor") },
+
+            { "fd-usage",              MetricDefinition(PrometheusMetricType::gauge, "Number of open file descriptors") },
+            { "real-memory-usage",              MetricDefinition(PrometheusMetricType::counter, "Number of bytes real process memory usage") },
+            { "udp-in-errors",              MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp InErrors") },
+            { "udp-noport-errors",              MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp NoPorts") },
+            { "udp-recvbuf-errors",              MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp RcvbufErrors") },
+            { "udp-sndbuf-errors",              MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp SndbufErrors") },
+    };
+};
+
+extern MetricDefinitionStorage g_metricDefinitions;
index bf4038a2a51a92fbd10aa9489134dd855368e34e..fd7b4676d6942ae13dadec7c28f81a7b7a3ce322 100644 (file)
@@ -30,6 +30,7 @@
 #include <iostream>
 #include "iputils.hh"
 #include "rec_channel.hh"
+#include "rec_metrics.hh"
 #include "arguments.hh"
 #include "misc.hh"
 #include "syncres.hh"
@@ -420,6 +421,50 @@ static void apiServerRPZStats(HttpRequest* req, HttpResponse* resp) {
   resp->setBody(ret);
 }
 
+
+static void prometheusMetrics(HttpRequest *req, HttpResponse *resp) {
+    static MetricDefinitionStorage g_metricDefinitions;
+
+    if (req->method != "GET")
+        throw HttpMethodNotAllowedException();
+
+    registerAllStats();
+
+
+    std::ostringstream output;
+    typedef map <string, string> varmap_t;
+    varmap_t varmap = getAllStatsMap(
+            StatComponent::API); // Argument controls blacklisting of any stats. So stats-api-blacklist will be used to block returned stats.
+    for (const varmap_t::value_type &tup :  varmap) {
+        std::string metricName = tup.first;
+
+        // Prometheus suggest using '_' instead of '-'
+        std::string prometheusMetricName = "pdnsrecursor_" + boost::replace_all_copy(metricName, "-", "_");
+
+        MetricDefinition metricDetails;
+
+        if (g_metricDefinitions.getMetricDetails(metricName, metricDetails)) {
+          std::string prometheusTypeName = g_metricDefinitions.getPrometheusStringMetricType(
+                  metricDetails.prometheusType);
+
+          if (prometheusTypeName == "") {
+            continue;
+          }
+
+          // for these we have the help and types encoded in the sources:
+          output << "# HELP " << prometheusMetricName << " " << metricDetails.description << "\n";
+          output << "# TYPE " << prometheusMetricName << " " << prometheusTypeName << "\n";
+        }
+        output << prometheusMetricName << " " << tup.second << "\n";
+    }
+
+    resp->body = output.str();
+    resp->headers["Content-Type"] = "text/plain";
+    resp->status = 200;
+
+}
+
+
 #include "htmlfiles.h"
 
 static void serveStuff(HttpRequest* req, HttpResponse* resp)
@@ -483,6 +528,7 @@ RecursorWebServer::RecursorWebServer(FDMultiplexer* fdm)
   for(const auto& u : g_urlmap) 
     d_ws->registerWebHandler("/"+u.first, serveStuff);
   d_ws->registerWebHandler("/", serveStuff);
+  d_ws->registerWebHandler("/metrics", prometheusMetrics);
   d_ws->go();
 }
 
index bc24d08c5b8f5a04295b87546ab64e19000bf9c7..ba64df3a4855c1780b6a1328ee32d20353a3f6fc 100644 (file)
@@ -68,3 +68,17 @@ class Servers(ApiTestCase):
         r = self.session.get(self.url("/api/v1/servers/localhost/statistics?statistic=uptimeAAAA"))
         self.assertEquals(r.status_code, 422)
         self.assertIn("Unknown statistic name", r.json()['error'])
+
+
+    def test_read_metrics(self):
+        if is_recursor():
+            res = self.session.get(self.url("/metrics"), auth=('whatever', self.webServerBasicAuthPassword), timeout=2.0)
+            self.assertEqual(res.status_code, 200)
+            # print(res.text)
+            found = False
+            for line in res.text.splitlines():
+                if line[0] == "#":
+                    continue
+                if line.split(" ")[0] == "pdnsrecursor_uptime":
+                    found = True
+            self.assertTrue(found,"pdnsrecursor_uptime is missing")
index 9ef00b919c76defa3bf82b843c0140858a3f3d2c..5df46745efc5108c479010670d32564e0d1acd6b 100644 (file)
@@ -23,6 +23,7 @@ class ApiTestCase(unittest.TestCase):
     def setUp(self):
         # TODO: config
         self.server_address = '127.0.0.1'
+        self.webServerBasicAuthPassword = 'something'
         self.server_port = int(os.environ.get('WEBPORT', '5580'))
         self.server_url = 'http://%s:%s/' % (self.server_address, self.server_port)
         self.session = requests.Session()