6 from dnsdisttests
import DNSDistTest
8 class TestAPIBasics(DNSDistTest
):
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")
25 def testBasicAuth(self
):
27 API: Basic Authentication
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
)
33 self
.assertEquals(r
.status_code
, 200)
35 def testXAPIKey(self
):
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
)
44 self
.assertEquals(r
.status_code
, 200)
46 def testBasicAuthOnly(self
):
48 API: Basic Authentication Only
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)
56 def testServersLocalhost(self
):
58 API: /api/v1/servers/localhost
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
)
64 self
.assertEquals(r
.status_code
, 200)
65 self
.assertTrue(r
.json())
68 self
.assertEquals(content
['daemon_type'], 'dnsdist')
70 for key
in ['version', 'acl', 'local', 'rules', 'response-rules', 'servers', 'frontends']:
71 self
.assertIn(key
, content
)
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)
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)
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
)
90 for key
in ['id', 'latency', 'weight', 'outstanding', 'qpsLimit', 'reuseds',
91 'qps', 'queries', 'order']:
92 self
.assertTrue(server
[key
] >= 0)
94 self
.assertTrue(server
['state'] in ['up', 'down', 'UP', 'DOWN'])
96 for frontend
in content
['frontends']:
97 for key
in ['id', 'address', 'udp', 'tcp', 'queries']:
98 self
.assertIn(key
, frontend
)
100 for key
in ['id', 'queries']:
101 self
.assertTrue(frontend
[key
] >= 0)
103 def testServersIDontExist(self
):
105 API: /api/v1/servers/idontexist (should be 404)
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)
112 def testServersLocalhostConfig(self
):
114 API: /api/v1/servers/localhost/config
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
)
120 self
.assertEquals(r
.status_code
, 200)
121 self
.assertTrue(r
.json())
124 for entry
in content
:
125 for key
in ['type', 'name', 'value']:
126 self
.assertIn(key
, entry
)
128 self
.assertEquals(entry
['type'], 'ConfigSetting')
129 values
[entry
['name']] = entry
['value']
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
)
137 for key
in ['max-outstanding', 'stale-cache-entries-ttl', 'tcp-recv-timeout',
139 self
.assertTrue(values
[key
] >= 0)
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)
144 def testServersLocalhostConfigAllowFrom(self
):
146 API: /api/v1/servers/localhost/config/allow-from
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
)
152 self
.assertEquals(r
.status_code
, 200)
153 self
.assertTrue(r
.json())
155 for key
in ['type', 'name', 'value']:
156 self
.assertIn(key
, content
)
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"])
162 def testServersLocalhostConfigAllowFromPut(self
):
164 API: PUT /api/v1/servers/localhost/config/allow-from (should be refused)
166 The API is read-only by default, so this should be refused
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",
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
)
176 self
.assertEquals(r
.status_code
, 405)
178 def testServersLocalhostStatistics(self
):
180 API: /api/v1/servers/localhost/statistics
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
)
186 self
.assertEquals(r
.status_code
, 200)
187 self
.assertTrue(r
.json())
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']
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']
208 self
.assertIn(key
, values
)
209 self
.assertTrue(values
[key
] >= 0)
212 self
.assertIn(key
, expected
)
214 def testJsonstatStats(self
):
216 API: /jsonstat?command=stats
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
)
222 self
.assertEquals(r
.status_code
, 200)
223 self
.assertTrue(r
.json())
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',
238 self
.assertIn(key
, content
)
239 self
.assertTrue(content
[key
] >= 0)
241 def testJsonstatDynblocklist(self
):
243 API: /jsonstat?command=dynblocklist
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
)
249 self
.assertEquals(r
.status_code
, 200)
254 for key
in ['reason', 'seconds', 'blocks']:
255 self
.assertIn(key
, content
)
257 for key
in ['blocks']:
258 self
.assertTrue(content
[key
] >= 0)
260 class TestAPIServerDown(DNSDistTest
):
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")
275 def testServerDownNoLatencyLocalhost(self
):
277 API: /api/v1/servers/localhost, no latency for a down server
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
)
283 self
.assertEquals(r
.status_code
, 200)
284 self
.assertTrue(r
.json())
287 self
.assertEquals(content
['servers'][0]['latency'], None)
289 class TestAPIWritable(DNSDistTest
):
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")
304 def testSetACL(self
):
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
)
312 self
.assertEquals(r
.status_code
, 200)
313 self
.assertTrue(r
.json())
315 self
.assertEquals(content
['value'], ["127.0.0.1/32", "::1/128"])
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",
321 r
= requests
.put(url
, headers
=headers
, timeout
=self
._webTimeout
, data
=payload
)
323 self
.assertEquals(r
.status_code
, 200)
324 self
.assertTrue(r
.json())
326 self
.assertEquals(content
['value'], newACL
)
328 r
= requests
.get(url
, headers
=headers
, timeout
=self
._webTimeout
)
330 self
.assertEquals(r
.status_code
, 200)
331 self
.assertTrue(r
.json())
333 self
.assertEquals(content
['value'], newACL
)
335 configFile
= self
._APIWriteDir
+ '/' + 'acl.conf'
336 self
.assertTrue(os
.path
.isfile(configFile
))
338 with
file(configFile
) as f
:
339 fileContent
= f
.read()
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"})