]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add API regression tests 3538/head
authorRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 9 Mar 2016 07:49:00 +0000 (08:49 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Wed, 9 Mar 2016 07:49:00 +0000 (08:49 +0100)
So we'll notice if we break the web server or the API.
This commit also cleans up the responses counter usage.
Because the counters are written to by the responder threads,
they are updated at the class level. We do not want to alter
them at the instance level, because that would create a new variable
at this level, disconnected from the other one, but we can still
read them from the instance level using 'self'.

regression-tests.dnsdist/requirements.txt
regression-tests.dnsdist/test_API.py [new file with mode: 0644]
regression-tests.dnsdist/test_Caching.py
regression-tests.dnsdist/test_Routing.py

index 5521abf7c59c7e399930c9472ebe40d99e405942..2063456d4fe5bc5c4864ecab582231aec0f1b47c 100644 (file)
@@ -1,3 +1,4 @@
 dnspython>=1.11
 nose>=1.3.7
 libnacl>=1.4.3
+requests>=2.1.0
diff --git a/regression-tests.dnsdist/test_API.py b/regression-tests.dnsdist/test_API.py
new file mode 100644 (file)
index 0000000..58163a6
--- /dev/null
@@ -0,0 +1,206 @@
+#!/usr/bin/env python
+
+import requests
+from dnsdisttests import DNSDistTest
+
+class TestBasics(DNSDistTest):
+
+    _webTimeout = 2.0
+    _webServerPort = 8083
+    _webServerBasicAuthPassword = 'secret'
+    _webServerAPIKey = 'apisecret'
+    # paths accessible using the API key
+    _apiPaths = ['/api/v1/servers/localhost', '/api/v1/servers/localhost/config', '/api/v1/servers/localhost/statistics', '/jsonstat?command=stats', '/jsonstat?command=dynblocklist']
+    # paths accessible using basic auth only (list not exhaustive)
+    _basicOnlyPaths = ['/', '/index.html']
+    _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
+    _config_template = """
+    newServer{address="127.0.0.1:%s"}
+    webserver("127.0.0.1:%s", "%s", "%s")
+    """
+
+    def testBasicAuth(self):
+        """
+        API: Basic Authentication
+        """
+        for path in self._basicOnlyPaths + self._apiPaths:
+            url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+            r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+            self.assertTrue(r)
+            self.assertEquals(r.status_code, 200)
+
+    def testXAPIKey(self):
+        """
+        API: X-Api-Key
+        """
+        headers = {'x-api-key': self._webServerAPIKey}
+        for path in self._apiPaths:
+            url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+            r = requests.get(url, headers=headers, timeout=self._webTimeout)
+            self.assertTrue(r)
+            self.assertEquals(r.status_code, 200)
+
+    def testBasicAuthOnly(self):
+        """
+        API: Basic Authentication Only
+        """
+        headers = {'x-api-key': self._webServerAPIKey}
+        for path in self._basicOnlyPaths:
+            url = 'http://127.0.0.1:' + str(self._webServerPort) + path
+            r = requests.get(url, headers=headers, timeout=self._webTimeout)
+            self.assertEquals(r.status_code, 401)
+
+    def testServersLocalhost(self):
+        """
+        API: /api/v1/servers/localhost
+        """
+        headers = {'x-api-key': self._webServerAPIKey}
+        url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
+        r = requests.get(url, headers=headers, timeout=self._webTimeout)
+        self.assertTrue(r)
+        self.assertEquals(r.status_code, 200)
+        self.assertTrue(r.json())
+        content = r.json()
+
+        self.assertEquals(content['daemon_type'], 'dnsdist')
+
+        for key in ['version', 'acl', 'local', 'rules', 'servers', 'frontends']:
+            self.assertIn(key, content)
+
+        for rule in content['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']:
+                self.assertIn(key, server)
+
+            for key in ['id', 'latency', 'weight', 'outstanding', 'qpsLimit', 'reuseds',
+                        'qps', 'queries', 'order']:
+                self.assertTrue(server[key] >= 0)
+
+            self.assertTrue(server['state'] in ['up', 'down', 'UP', 'DOWN'])
+
+        for frontend in content['frontends']:
+            for key in ['id', 'address', 'udp', 'tcp', 'queries']:
+                self.assertIn(key, frontend)
+
+            for key in ['id', 'queries']:
+                self.assertTrue(frontend[key] >= 0)
+
+    def testServersLocalhostConfig(self):
+        """
+        API: /api/v1/servers/localhost/config
+        """
+        headers = {'x-api-key': self._webServerAPIKey}
+        url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/config'
+        r = requests.get(url, headers=headers, timeout=self._webTimeout)
+        self.assertTrue(r)
+        self.assertEquals(r.status_code, 200)
+        self.assertTrue(r.json())
+        content = r.json()
+        values = {}
+        for entry in content:
+            for key in ['type', 'name', 'value']:
+                self.assertIn(key, entry)
+
+            self.assertEquals(entry['type'], 'ConfigSetting')
+            values[entry['name']] = entry['value']
+
+        for key in ['acl', 'control-socket', 'ecs-override', 'ecs-source-prefix-v4',
+                    'ecs-source-prefix-v6', 'fixup-case', 'max-outstanding', 'server-policy',
+                    'stale-cache-entries-ttl', 'tcp-recv-timeout', 'tcp-send-timeout',
+                    'truncate-tc', 'verbose', 'verbose-health-checks']:
+            self.assertIn(key, values)
+
+        for key in ['max-outstanding', 'stale-cache-entries-ttl', 'tcp-recv-timeout',
+                    'tcp-send-timeout']:
+            self.assertTrue(values[key] >= 0)
+
+        self.assertTrue(values['ecs-source-prefix-v4'] >= 0 and values['ecs-source-prefix-v4'] <= 32)
+        self.assertTrue(values['ecs-source-prefix-v6'] >= 0 and values['ecs-source-prefix-v6'] <= 128)
+
+    def testServersLocalhostStatistics(self):
+        """
+        API: /api/v1/servers/localhost/statistics
+        """
+        headers = {'x-api-key': self._webServerAPIKey}
+        url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/statistics'
+        r = requests.get(url, headers=headers, timeout=self._webTimeout)
+        self.assertTrue(r)
+        self.assertEquals(r.status_code, 200)
+        self.assertTrue(r.json())
+        content = r.json()
+        values = {}
+        for entry in content:
+            self.assertIn('type', entry)
+            self.assertIn('name', entry)
+            self.assertIn('value', entry)
+            self.assertEquals(entry['type'], 'StatisticItem')
+            values[entry['name']] = entry['value']
+
+        expected = ['responses', 'servfail-responses', 'queries', 'acl-drops', 'block-filter',
+                    'rule-drop', 'rule-nxdomain', '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']
+
+        for key in expected:
+            self.assertIn(key, values)
+            self.assertTrue(values[key] >= 0)
+
+    def testJsonstatStats(self):
+        """
+        API: /jsonstat?command=stats
+        """
+        headers = {'x-api-key': self._webServerAPIKey}
+        url = 'http://127.0.0.1:' + str(self._webServerPort) + '/jsonstat?command=stats'
+        r = requests.get(url, headers=headers, timeout=self._webTimeout)
+        self.assertTrue(r)
+        self.assertEquals(r.status_code, 200)
+        self.assertTrue(r.json())
+        content = r.json()
+
+        for key in ['packetcache-hits', 'packetcache-misses', 'over-capacity-drops', 'too-old-drops']:
+            self.assertIn(key, content)
+            self.assertTrue(content[key] >= 0)
+
+        expected = ['responses', 'servfail-responses', 'queries', 'acl-drops', 'block-filter',
+                    'rule-drop', 'rule-nxdomain', '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']
+
+        for key in expected:
+            self.assertIn(key, content)
+            self.assertTrue(content[key] >= 0)
+
+    def testJsonstatDynblocklist(self):
+        """
+        API: /jsonstat?command=dynblocklist
+        """
+        headers = {'x-api-key': self._webServerAPIKey}
+        url = 'http://127.0.0.1:' + str(self._webServerPort) + '/jsonstat?command=dynblocklist'
+        r = requests.get(url, headers=headers, timeout=self._webTimeout)
+        self.assertTrue(r)
+        self.assertEquals(r.status_code, 200)
+
+        content = r.json()
+
+        if content:
+            for key in ['reason', 'seconds', 'blocks']:
+                self.assertIn(key, content)
+
+            for key in ['blocks']:
+                self.assertTrue(content[key] >= 0)
index 1e1cde1be33155a3d2617acb61d5227449335218..be6d9ed999b4dd1230179d9a55b2c4812f2e171e 100644 (file)
@@ -50,8 +50,8 @@ class TestCaching(DNSDistTest):
             self.assertEquals(receivedResponse, response)
 
         total = 0
-        for key in TestCaching._responsesCounter:
-            total += TestCaching._responsesCounter[key]
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
             TestCaching._responsesCounter[key] = 0
 
         self.assertEquals(total, 1)
@@ -70,8 +70,8 @@ class TestCaching(DNSDistTest):
             self.assertEquals(receivedResponse, response)
 
         total = 0
-        for key in TestCaching._responsesCounter:
-            total += TestCaching._responsesCounter[key]
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
             TestCaching._responsesCounter[key] = 0
 
         self.assertEquals(total, 1)
@@ -109,8 +109,8 @@ class TestCaching(DNSDistTest):
             self.assertEquals(query, receivedQuery)
             self.assertEquals(receivedResponse, response)
 
-        for key in TestCaching._responsesCounter:
-            value = TestCaching._responsesCounter[key]
+        for key in self._responsesCounter:
+            value = self._responsesCounter[key]
             self.assertEquals(value, numberOfQueries)
 
     def testSkipCacheViaLua(self):
@@ -146,8 +146,8 @@ class TestCaching(DNSDistTest):
             self.assertEquals(query, receivedQuery)
             self.assertEquals(receivedResponse, response)
 
-        for key in TestCaching._responsesCounter:
-            value = TestCaching._responsesCounter[key]
+        for key in self._responsesCounter:
+            value = self._responsesCounter[key]
             self.assertEquals(value, numberOfQueries)
 
     def testCacheExpiration(self):
@@ -201,8 +201,8 @@ class TestCaching(DNSDistTest):
         self.assertEquals(receivedResponse, response)
 
         total = 0
-        for key in TestCaching._responsesCounter:
-            total += TestCaching._responsesCounter[key]
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
 
         self.assertEquals(total, misses)
 
@@ -264,8 +264,8 @@ class TestCaching(DNSDistTest):
         self.assertEquals(receivedResponse, response)
 
         total = 0
-        for key in TestCaching._responsesCounter:
-            total += TestCaching._responsesCounter[key]
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
 
         self.assertEquals(total, misses)
 
@@ -313,8 +313,8 @@ class TestCaching(DNSDistTest):
             self.assertTrue(an.ttl < ttl)
 
         total = 0
-        for key in TestCaching._responsesCounter:
-            total += TestCaching._responsesCounter[key]
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
 
         self.assertEquals(total, misses)
 
@@ -406,8 +406,8 @@ class TestCachingWithExistingEDNS(DNSDistTest):
         misses += 1
 
         total = 0
-        for key in TestCachingWithExistingEDNS._responsesCounter:
-            total += TestCachingWithExistingEDNS._responsesCounter[key]
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
 
         self.assertEquals(total, misses)
 
@@ -477,8 +477,8 @@ class TestCachingCacheFull(DNSDistTest):
         misses += 1
 
         total = 0
-        for key in TestCachingCacheFull._responsesCounter:
-            total += TestCachingCacheFull._responsesCounter[key]
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
 
         self.assertEquals(total, misses)
 
@@ -588,8 +588,8 @@ class TestCachingStale(DNSDistTest):
             self.assertEquals(an.ttl, self._staleCacheTTL)
 
         total = 0
-        for key in TestCachingCacheFull._responsesCounter:
-            total += TestCachingCacheFull._responsesCounter[key]
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
 
         self.assertEquals(total, misses)
 
@@ -652,8 +652,8 @@ class TestCacheManagement(DNSDistTest):
         self.assertEquals(receivedResponse, response)
 
         total = 0
-        for key in TestCachingCacheFull._responsesCounter:
-            total += TestCachingCacheFull._responsesCounter[key]
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
 
         self.assertEquals(total, misses)
 
@@ -734,8 +734,8 @@ class TestCacheManagement(DNSDistTest):
         self.assertEquals(receivedResponse, response2)
 
         total = 0
-        for key in TestCachingCacheFull._responsesCounter:
-            total += TestCachingCacheFull._responsesCounter[key]
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
 
         self.assertEquals(total, misses)
 
@@ -815,7 +815,6 @@ class TestCacheManagement(DNSDistTest):
         self.assertEquals(receivedResponse, response2)
 
         total = 0
-        for key in TestCachingCacheFull._responsesCounter:
-            total += TestCachingCacheFull._responsesCounter[key]
-
+        for key in self._responsesCounter:
+            total += self._responsesCounter[key]
         self.assertEquals(total, misses)
index 2ccaa264bde70851f7eb4387d850fed8dc38fac1..f51be3470c87a9ca03f94c86089c3822608c0485 100644 (file)
@@ -241,8 +241,8 @@ class TestRoutingRoundRobinLB(DNSDistTest):
             self.assertEquals(query, receivedQuery)
             self.assertEquals(response, receivedResponse)
 
-        for key in TestRoutingRoundRobinLB._responsesCounter:
-            value = TestRoutingRoundRobinLB._responsesCounter[key]
+        for key in self._responsesCounter:
+            value = self._responsesCounter[key]
             self.assertEquals(value, numberOfQueries / 2)
 
 class TestRoutingRoundRobinLBOneDown(DNSDistTest):
@@ -290,8 +290,8 @@ class TestRoutingRoundRobinLBOneDown(DNSDistTest):
             self.assertEquals(response, receivedResponse)
 
         total = 0
-        for key in TestRoutingRoundRobinLBOneDown._responsesCounter:
-            value = TestRoutingRoundRobinLBOneDown._responsesCounter[key]
+        for key in self._responsesCounter:
+            value = self._responsesCounter[key]
             self.assertTrue(value == numberOfQueries or value == 0)
             total += value