]> git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.dnsdist/test_API.py
dnsdist: Remove BlockFilter
[thirdparty/pdns.git] / regression-tests.dnsdist / test_API.py
1 #!/usr/bin/env python
2 import os.path
3
4 import json
5 import requests
6 from dnsdisttests import DNSDistTest
7
8 class TestAPIBasics(DNSDistTest):
9
10 _webTimeout = 2.0
11 _webServerPort = 8083
12 _webServerBasicAuthPassword = 'secret'
13 _webServerAPIKey = 'apisecret'
14 # paths accessible using the API key
15 _apiPaths = ['/api/v1/servers/localhost', '/api/v1/servers/localhost/config', '/api/v1/servers/localhost/config/allow-from', '/api/v1/servers/localhost/statistics', '/jsonstat?command=stats', '/jsonstat?command=dynblocklist']
16 # paths accessible using basic auth only (list not exhaustive)
17 _basicOnlyPaths = ['/', '/index.html']
18 _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
19 _config_template = """
20 setACL({"127.0.0.1/32", "::1/128"})
21 newServer{address="127.0.0.1:%s"}
22 webserver("127.0.0.1:%s", "%s", "%s")
23 """
24
25 def testBasicAuth(self):
26 """
27 API: Basic Authentication
28 """
29 for path in self._basicOnlyPaths + self._apiPaths:
30 url = 'http://127.0.0.1:' + str(self._webServerPort) + path
31 r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
32 self.assertTrue(r)
33 self.assertEquals(r.status_code, 200)
34
35 def testXAPIKey(self):
36 """
37 API: X-Api-Key
38 """
39 headers = {'x-api-key': self._webServerAPIKey}
40 for path in self._apiPaths:
41 url = 'http://127.0.0.1:' + str(self._webServerPort) + path
42 r = requests.get(url, headers=headers, timeout=self._webTimeout)
43 self.assertTrue(r)
44 self.assertEquals(r.status_code, 200)
45
46 def testBasicAuthOnly(self):
47 """
48 API: Basic Authentication Only
49 """
50 headers = {'x-api-key': self._webServerAPIKey}
51 for path in self._basicOnlyPaths:
52 url = 'http://127.0.0.1:' + str(self._webServerPort) + path
53 r = requests.get(url, headers=headers, timeout=self._webTimeout)
54 self.assertEquals(r.status_code, 401)
55
56 def testServersLocalhost(self):
57 """
58 API: /api/v1/servers/localhost
59 """
60 headers = {'x-api-key': self._webServerAPIKey}
61 url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
62 r = requests.get(url, headers=headers, timeout=self._webTimeout)
63 self.assertTrue(r)
64 self.assertEquals(r.status_code, 200)
65 self.assertTrue(r.json())
66 content = r.json()
67
68 self.assertEquals(content['daemon_type'], 'dnsdist')
69
70 for key in ['version', 'acl', 'local', 'rules', 'response-rules', 'servers', 'frontends']:
71 self.assertIn(key, content)
72
73 for rule in content['rules']:
74 for key in ['id', 'matches', 'rule', 'action']:
75 self.assertIn(key, rule)
76 for key in ['id', 'matches']:
77 self.assertTrue(rule[key] >= 0)
78
79 for rule in content['response-rules']:
80 for key in ['id', 'matches', 'rule', 'action']:
81 self.assertIn(key, rule)
82 for key in ['id', 'matches']:
83 self.assertTrue(rule[key] >= 0)
84
85 for server in content['servers']:
86 for key in ['id', 'latency', 'name', 'weight', 'outstanding', 'qpsLimit',
87 'reuseds', 'state', 'address', 'pools', 'qps', 'queries', 'order']:
88 self.assertIn(key, server)
89
90 for key in ['id', 'latency', 'weight', 'outstanding', 'qpsLimit', 'reuseds',
91 'qps', 'queries', 'order']:
92 self.assertTrue(server[key] >= 0)
93
94 self.assertTrue(server['state'] in ['up', 'down', 'UP', 'DOWN'])
95
96 for frontend in content['frontends']:
97 for key in ['id', 'address', 'udp', 'tcp', 'queries']:
98 self.assertIn(key, frontend)
99
100 for key in ['id', 'queries']:
101 self.assertTrue(frontend[key] >= 0)
102
103 def testServersIDontExist(self):
104 """
105 API: /api/v1/servers/idontexist (should be 404)
106 """
107 headers = {'x-api-key': self._webServerAPIKey}
108 url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/idontexist'
109 r = requests.get(url, headers=headers, timeout=self._webTimeout)
110 self.assertEquals(r.status_code, 404)
111
112 def testServersLocalhostConfig(self):
113 """
114 API: /api/v1/servers/localhost/config
115 """
116 headers = {'x-api-key': self._webServerAPIKey}
117 url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/config'
118 r = requests.get(url, headers=headers, timeout=self._webTimeout)
119 self.assertTrue(r)
120 self.assertEquals(r.status_code, 200)
121 self.assertTrue(r.json())
122 content = r.json()
123 values = {}
124 for entry in content:
125 for key in ['type', 'name', 'value']:
126 self.assertIn(key, entry)
127
128 self.assertEquals(entry['type'], 'ConfigSetting')
129 values[entry['name']] = entry['value']
130
131 for key in ['acl', 'control-socket', 'ecs-override', 'ecs-source-prefix-v4',
132 'ecs-source-prefix-v6', 'fixup-case', 'max-outstanding', 'server-policy',
133 'stale-cache-entries-ttl', 'tcp-recv-timeout', 'tcp-send-timeout',
134 'truncate-tc', 'verbose', 'verbose-health-checks']:
135 self.assertIn(key, values)
136
137 for key in ['max-outstanding', 'stale-cache-entries-ttl', 'tcp-recv-timeout',
138 'tcp-send-timeout']:
139 self.assertTrue(values[key] >= 0)
140
141 self.assertTrue(values['ecs-source-prefix-v4'] >= 0 and values['ecs-source-prefix-v4'] <= 32)
142 self.assertTrue(values['ecs-source-prefix-v6'] >= 0 and values['ecs-source-prefix-v6'] <= 128)
143
144 def testServersLocalhostConfigAllowFrom(self):
145 """
146 API: /api/v1/servers/localhost/config/allow-from
147 """
148 headers = {'x-api-key': self._webServerAPIKey}
149 url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/config/allow-from'
150 r = requests.get(url, headers=headers, timeout=self._webTimeout)
151 self.assertTrue(r)
152 self.assertEquals(r.status_code, 200)
153 self.assertTrue(r.json())
154 content = r.json()
155 for key in ['type', 'name', 'value']:
156 self.assertIn(key, content)
157
158 self.assertEquals(content['name'], 'allow-from')
159 self.assertEquals(content['type'], 'ConfigSetting')
160 self.assertEquals(content['value'], ["127.0.0.1/32", "::1/128"])
161
162 def testServersLocalhostConfigAllowFromPut(self):
163 """
164 API: PUT /api/v1/servers/localhost/config/allow-from (should be refused)
165
166 The API is read-only by default, so this should be refused
167 """
168 newACL = ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"]
169 payload = json.dumps({"name": "allow-from",
170 "type": "ConfigSetting",
171 "value": newACL})
172 headers = {'x-api-key': self._webServerAPIKey}
173 url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/config/allow-from'
174 r = requests.put(url, headers=headers, timeout=self._webTimeout, data=payload)
175 self.assertFalse(r)
176 self.assertEquals(r.status_code, 405)
177
178 def testServersLocalhostStatistics(self):
179 """
180 API: /api/v1/servers/localhost/statistics
181 """
182 headers = {'x-api-key': self._webServerAPIKey}
183 url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/statistics'
184 r = requests.get(url, headers=headers, timeout=self._webTimeout)
185 self.assertTrue(r)
186 self.assertEquals(r.status_code, 200)
187 self.assertTrue(r.json())
188 content = r.json()
189 values = {}
190 for entry in content:
191 self.assertIn('type', entry)
192 self.assertIn('name', entry)
193 self.assertIn('value', entry)
194 self.assertEquals(entry['type'], 'StatisticItem')
195 values[entry['name']] = entry['value']
196
197 expected = ['responses', 'servfail-responses', 'queries', 'acl-drops',
198 'rule-drop', 'rule-nxdomain', 'rule-refused', 'self-answered', 'downstream-timeouts',
199 'downstream-send-errors', 'trunc-failures', 'no-policy', 'latency0-1',
200 'latency1-10', 'latency10-50', 'latency50-100', 'latency100-1000',
201 'latency-slow', 'latency-avg100', 'latency-avg1000', 'latency-avg10000',
202 'latency-avg1000000', 'uptime', 'real-memory-usage', 'noncompliant-queries',
203 'noncompliant-responses', 'rdqueries', 'empty-queries', 'cache-hits',
204 'cache-misses', 'cpu-user-msec', 'cpu-sys-msec', 'fd-usage', 'dyn-blocked',
205 'dyn-block-nmg-size']
206
207 for key in expected:
208 self.assertIn(key, values)
209 self.assertTrue(values[key] >= 0)
210
211 for key in values:
212 self.assertIn(key, expected)
213
214 def testJsonstatStats(self):
215 """
216 API: /jsonstat?command=stats
217 """
218 headers = {'x-api-key': self._webServerAPIKey}
219 url = 'http://127.0.0.1:' + str(self._webServerPort) + '/jsonstat?command=stats'
220 r = requests.get(url, headers=headers, timeout=self._webTimeout)
221 self.assertTrue(r)
222 self.assertEquals(r.status_code, 200)
223 self.assertTrue(r.json())
224 content = r.json()
225
226 expected = ['responses', 'servfail-responses', 'queries', 'acl-drops',
227 'rule-drop', 'rule-nxdomain', 'rule-refused', 'self-answered', 'downstream-timeouts',
228 'downstream-send-errors', 'trunc-failures', 'no-policy', 'latency0-1',
229 'latency1-10', 'latency10-50', 'latency50-100', 'latency100-1000',
230 'latency-slow', 'latency-avg100', 'latency-avg1000', 'latency-avg10000',
231 'latency-avg1000000', 'uptime', 'real-memory-usage', 'noncompliant-queries',
232 'noncompliant-responses', 'rdqueries', 'empty-queries', 'cache-hits',
233 'cache-misses', 'cpu-user-msec', 'cpu-sys-msec', 'fd-usage', 'dyn-blocked',
234 'dyn-block-nmg-size', 'packetcache-hits', 'packetcache-misses', 'over-capacity-drops',
235 'too-old-drops']
236
237 for key in expected:
238 self.assertIn(key, content)
239 self.assertTrue(content[key] >= 0)
240
241 def testJsonstatDynblocklist(self):
242 """
243 API: /jsonstat?command=dynblocklist
244 """
245 headers = {'x-api-key': self._webServerAPIKey}
246 url = 'http://127.0.0.1:' + str(self._webServerPort) + '/jsonstat?command=dynblocklist'
247 r = requests.get(url, headers=headers, timeout=self._webTimeout)
248 self.assertTrue(r)
249 self.assertEquals(r.status_code, 200)
250
251 content = r.json()
252
253 if content:
254 for key in ['reason', 'seconds', 'blocks']:
255 self.assertIn(key, content)
256
257 for key in ['blocks']:
258 self.assertTrue(content[key] >= 0)
259
260 class TestAPIServerDown(DNSDistTest):
261
262 _webTimeout = 2.0
263 _webServerPort = 8083
264 _webServerBasicAuthPassword = 'secret'
265 _webServerAPIKey = 'apisecret'
266 # paths accessible using the API key
267 _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
268 _config_template = """
269 setACL({"127.0.0.1/32", "::1/128"})
270 newServer{address="127.0.0.1:%s"}
271 getServer(0):setDown()
272 webserver("127.0.0.1:%s", "%s", "%s")
273 """
274
275 def testServerDownNoLatencyLocalhost(self):
276 """
277 API: /api/v1/servers/localhost, no latency for a down server
278 """
279 headers = {'x-api-key': self._webServerAPIKey}
280 url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost'
281 r = requests.get(url, headers=headers, timeout=self._webTimeout)
282 self.assertTrue(r)
283 self.assertEquals(r.status_code, 200)
284 self.assertTrue(r.json())
285 content = r.json()
286
287 self.assertEquals(content['servers'][0]['latency'], None)
288
289 class TestAPIWritable(DNSDistTest):
290
291 _webTimeout = 2.0
292 _webServerPort = 8083
293 _webServerBasicAuthPassword = 'secret'
294 _webServerAPIKey = 'apisecret'
295 _APIWriteDir = '/tmp'
296 _config_params = ['_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey', '_APIWriteDir']
297 _config_template = """
298 setACL({"127.0.0.1/32", "::1/128"})
299 newServer{address="127.0.0.1:%s"}
300 webserver("127.0.0.1:%s", "%s", "%s")
301 setAPIWritable(true, "%s")
302 """
303
304 def testSetACL(self):
305 """
306 API: Set ACL
307 """
308 headers = {'x-api-key': self._webServerAPIKey}
309 url = 'http://127.0.0.1:' + str(self._webServerPort) + '/api/v1/servers/localhost/config/allow-from'
310 r = requests.get(url, headers=headers, timeout=self._webTimeout)
311 self.assertTrue(r)
312 self.assertEquals(r.status_code, 200)
313 self.assertTrue(r.json())
314 content = r.json()
315 self.assertEquals(content['value'], ["127.0.0.1/32", "::1/128"])
316
317 newACL = ["192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"]
318 payload = json.dumps({"name": "allow-from",
319 "type": "ConfigSetting",
320 "value": newACL})
321 r = requests.put(url, headers=headers, timeout=self._webTimeout, data=payload)
322 self.assertTrue(r)
323 self.assertEquals(r.status_code, 200)
324 self.assertTrue(r.json())
325 content = r.json()
326 self.assertEquals(content['value'], newACL)
327
328 r = requests.get(url, headers=headers, timeout=self._webTimeout)
329 self.assertTrue(r)
330 self.assertEquals(r.status_code, 200)
331 self.assertTrue(r.json())
332 content = r.json()
333 self.assertEquals(content['value'], newACL)
334
335 configFile = self._APIWriteDir + '/' + 'acl.conf'
336 self.assertTrue(os.path.isfile(configFile))
337 fileContent = None
338 with file(configFile) as f:
339 fileContent = f.read()
340
341 self.assertEquals(fileContent, """-- Generated by the REST API, DO NOT EDIT
342 setACL({"192.0.2.0/24", "198.51.100.0/24", "203.0.113.0/24"})
343 """)