From: Greg Cockroft Date: Fri, 26 Apr 2019 13:33:25 +0000 (-0400) Subject: recursor webhandler for prometheus metrics X-Git-Tag: dnsdist-1.4.0-rc3~31^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8b4030fb89c7c38c1ff9983dd84b54d225886456;p=thirdparty%2Fpdns.git recursor webhandler for prometheus metrics 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. --- diff --git a/pdns/recursordist/Makefile.am b/pdns/recursordist/Makefile.am index 9d1faba044..794488f1fa 100644 --- a/pdns/recursordist/Makefile.am +++ b/pdns/recursordist/Makefile.am @@ -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 \ diff --git a/pdns/recursordist/docs/http-api/index.rst b/pdns/recursordist/docs/http-api/index.rst index 9fd7996387..e13b2ec21f 100644 --- a/pdns/recursordist/docs/http-api/index.rst +++ b/pdns/recursordist/docs/http-api/index.rst @@ -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 index 0000000000..0a11b01af9 --- /dev/null +++ b/pdns/recursordist/docs/http-api/prometheus.rst @@ -0,0 +1,37 @@ +Prometheus Data Endpoint +======================== + +.. versionadded:: 4.2.x + +.. http:get:: /metrics + + Get statistics from Recursor in `Prometheus `_ 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 index 0000000000..ec1580fa5f --- /dev/null +++ b/pdns/recursordist/rec_metrics.hh @@ -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 +#include +#include +#include +#include +#include + +// 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 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; diff --git a/pdns/ws-recursor.cc b/pdns/ws-recursor.cc index bf4038a2a5..fd7b4676d6 100644 --- a/pdns/ws-recursor.cc +++ b/pdns/ws-recursor.cc @@ -30,6 +30,7 @@ #include #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 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(); } diff --git a/regression-tests.api/test_Servers.py b/regression-tests.api/test_Servers.py index bc24d08c5b..ba64df3a48 100644 --- a/regression-tests.api/test_Servers.py +++ b/regression-tests.api/test_Servers.py @@ -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") diff --git a/regression-tests.api/test_helper.py b/regression-tests.api/test_helper.py index 9ef00b919c..5df46745ef 100644 --- a/regression-tests.api/test_helper.py +++ b/regression-tests.api/test_helper.py @@ -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()