]>
Commit | Line | Data |
---|---|---|
903853f4 RG |
1 | #!/usr/bin/env python |
2 | import threading | |
3 | import time | |
4 | import dns | |
5 | from dnsdisttests import DNSDistTest | |
6 | ||
7 | class TestRoutingPoolRouting(DNSDistTest): | |
8 | ||
9 | _config_template = """ | |
10 | newServer{address="127.0.0.1:%s", pool="real"} | |
11 | addPoolRule("pool.routing.tests.powerdns.com", "real") | |
12 | addAction(makeRule("poolaction.routing.tests.powerdns.com"), PoolAction("real")) | |
13 | addQPSPoolRule("qpspool.routing.tests.powerdns.com", 10, "abuse") | |
14 | addAction(makeRule("qpspoolaction.routing.tests.powerdns.com"), QPSPoolAction(10, "abuse")) | |
15 | """ | |
16 | ||
17 | def testPolicyPool(self): | |
18 | """ | |
19 | Routing: Set pool by qname | |
20 | ||
21 | Send an A query to "pool.routing.tests.powerdns.com.", | |
22 | check that dnsdist routes the query to the "real" pool. | |
23 | """ | |
24 | name = 'pool.routing.tests.powerdns.com.' | |
25 | query = dns.message.make_query(name, 'A', 'IN') | |
26 | response = dns.message.make_response(query) | |
27 | rrset = dns.rrset.from_text(name, | |
28 | 60, | |
29 | dns.rdataclass.IN, | |
30 | dns.rdatatype.A, | |
31 | '192.0.2.1') | |
32 | response.answer.append(rrset) | |
33 | ||
34 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
35 | receivedQuery.id = query.id | |
36 | self.assertEquals(query, receivedQuery) | |
37 | self.assertEquals(response, receivedResponse) | |
38 | ||
39 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) | |
40 | receivedQuery.id = query.id | |
41 | self.assertEquals(query, receivedQuery) | |
42 | self.assertEquals(response, receivedResponse) | |
43 | ||
44 | def testPolicyPoolAction(self): | |
45 | """ | |
46 | Routing: Set pool by qname via PoolAction | |
47 | ||
48 | Send an A query to "poolaction.routing.tests.powerdns.com.", | |
49 | check that dnsdist routes the query to the "real" pool. | |
50 | """ | |
51 | name = 'pool.routing.tests.powerdns.com.' | |
52 | query = dns.message.make_query(name, 'A', 'IN') | |
53 | response = dns.message.make_response(query) | |
54 | rrset = dns.rrset.from_text(name, | |
55 | 60, | |
56 | dns.rdataclass.IN, | |
57 | dns.rdatatype.A, | |
58 | '192.0.2.1') | |
59 | response.answer.append(rrset) | |
60 | ||
61 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
62 | receivedQuery.id = query.id | |
63 | self.assertEquals(query, receivedQuery) | |
64 | self.assertEquals(response, receivedResponse) | |
65 | ||
66 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) | |
67 | receivedQuery.id = query.id | |
68 | self.assertEquals(query, receivedQuery) | |
69 | self.assertEquals(response, receivedResponse) | |
70 | ||
71 | def testDefaultPool(self): | |
72 | """ | |
73 | Routing: Set pool by qname canary | |
74 | ||
75 | Send an A query to "notpool.routing.tests.powerdns.com.", | |
76 | check that dnsdist sends no response (no servers | |
77 | in the default pool). | |
78 | """ | |
79 | name = 'notpool.routing.tests.powerdns.com.' | |
80 | query = dns.message.make_query(name, 'A', 'IN') | |
81 | ||
82 | (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) | |
83 | self.assertEquals(receivedResponse, None) | |
84 | ||
85 | (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) | |
86 | self.assertEquals(receivedResponse, None) | |
87 | ||
88 | class TestRoutingQPSPoolRouting(DNSDistTest): | |
89 | _config_template = """ | |
90 | newServer{address="127.0.0.1:%s", pool="regular"} | |
91 | addQPSPoolRule("qpspool.routing.tests.powerdns.com", 10, "regular") | |
92 | addAction(makeRule("qpspoolaction.routing.tests.powerdns.com"), QPSPoolAction(10, "regular")) | |
93 | """ | |
94 | ||
95 | def testQPSPool(self): | |
96 | """ | |
97 | Routing: Set pool by QPS | |
98 | ||
99 | Send queries to "qpspool.routing.tests.powerdns.com." | |
100 | check that dnsdist does not route the query to the "regular" pool | |
101 | when the max QPS has been reached. | |
102 | """ | |
103 | maxQPS = 10 | |
104 | name = 'qpspool.routing.tests.powerdns.com.' | |
105 | query = dns.message.make_query(name, 'A', 'IN') | |
106 | response = dns.message.make_response(query) | |
107 | rrset = dns.rrset.from_text(name, | |
108 | 60, | |
109 | dns.rdataclass.IN, | |
110 | dns.rdatatype.A, | |
111 | '192.0.2.1') | |
112 | response.answer.append(rrset) | |
113 | ||
114 | for _ in range(maxQPS): | |
115 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
116 | receivedQuery.id = query.id | |
117 | self.assertEquals(query, receivedQuery) | |
118 | self.assertEquals(response, receivedResponse) | |
119 | ||
120 | # we should now be sent to the "abuse" pool which is empty, | |
121 | # so the queries should be dropped | |
122 | (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) | |
123 | self.assertEquals(receivedResponse, None) | |
124 | ||
125 | time.sleep(1) | |
126 | ||
127 | # again, over TCP this time | |
128 | for _ in range(maxQPS): | |
129 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) | |
130 | receivedQuery.id = query.id | |
131 | self.assertEquals(query, receivedQuery) | |
132 | self.assertEquals(response, receivedResponse) | |
133 | ||
134 | ||
135 | (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) | |
136 | self.assertEquals(receivedResponse, None) | |
137 | ||
138 | def testQPSPoolAction(self): | |
139 | """ | |
140 | Routing: Set pool by QPS via action | |
141 | ||
142 | Send queries to "qpspoolaction.routing.tests.powerdns.com." | |
143 | check that dnsdist does not route the query to the "regular" pool | |
144 | when the max QPS has been reached. | |
145 | """ | |
146 | maxQPS = 10 | |
147 | name = 'qpspoolaction.routing.tests.powerdns.com.' | |
148 | query = dns.message.make_query(name, 'A', 'IN') | |
149 | response = dns.message.make_response(query) | |
150 | rrset = dns.rrset.from_text(name, | |
151 | 60, | |
152 | dns.rdataclass.IN, | |
153 | dns.rdatatype.A, | |
154 | '192.0.2.1') | |
155 | response.answer.append(rrset) | |
156 | ||
157 | for _ in range(maxQPS): | |
158 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
159 | receivedQuery.id = query.id | |
160 | self.assertEquals(query, receivedQuery) | |
161 | self.assertEquals(response, receivedResponse) | |
162 | ||
163 | # we should now be sent to the "abuse" pool which is empty, | |
164 | # so the queries should be dropped | |
165 | (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) | |
166 | self.assertEquals(receivedResponse, None) | |
167 | ||
168 | time.sleep(1) | |
169 | ||
170 | # again, over TCP this time | |
171 | for _ in range(maxQPS): | |
172 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) | |
173 | receivedQuery.id = query.id | |
174 | self.assertEquals(query, receivedQuery) | |
175 | self.assertEquals(response, receivedResponse) | |
176 | ||
177 | ||
178 | (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) | |
179 | self.assertEquals(receivedResponse, None) | |
180 | ||
181 | ||
182 | class TestRoutingRoundRobinLB(DNSDistTest): | |
183 | ||
184 | _testServer2Port = 5351 | |
185 | _config_params = ['_testServerPort', '_testServer2Port'] | |
186 | _config_template = """ | |
187 | setServerPolicy(roundrobin) | |
188 | s1 = newServer{address="127.0.0.1:%s"} | |
189 | s1:setUp() | |
190 | s2 = newServer{address="127.0.0.1:%s"} | |
191 | s2:setUp() | |
192 | """ | |
193 | ||
194 | @classmethod | |
195 | def startResponders(cls): | |
196 | print("Launching responders..") | |
197 | cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort]) | |
198 | cls._UDPResponder.setDaemon(True) | |
199 | cls._UDPResponder.start() | |
200 | cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port]) | |
201 | cls._UDPResponder2.setDaemon(True) | |
202 | cls._UDPResponder2.start() | |
203 | ||
204 | cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort]) | |
205 | cls._TCPResponder.setDaemon(True) | |
206 | cls._TCPResponder.start() | |
207 | ||
208 | cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port]) | |
209 | cls._TCPResponder2.setDaemon(True) | |
210 | cls._TCPResponder2.start() | |
211 | ||
212 | def testRR(self): | |
213 | """ | |
214 | Routing: Round Robin | |
215 | ||
216 | Send 100 A queries to "rr.routing.tests.powerdns.com.", | |
217 | check that dnsdist routes half of it to each backend. | |
218 | """ | |
219 | numberOfQueries = 10 | |
220 | name = 'rr.routing.tests.powerdns.com.' | |
221 | query = dns.message.make_query(name, 'A', 'IN') | |
222 | response = dns.message.make_response(query) | |
223 | rrset = dns.rrset.from_text(name, | |
224 | 60, | |
225 | dns.rdataclass.IN, | |
226 | dns.rdatatype.A, | |
227 | '192.0.2.1') | |
228 | response.answer.append(rrset) | |
229 | ||
230 | # the round robin counter is shared for UDP and TCP, | |
231 | # so we need to do UDP then TCP to have a clean count | |
232 | for _ in range(numberOfQueries): | |
233 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
234 | receivedQuery.id = query.id | |
235 | self.assertEquals(query, receivedQuery) | |
236 | self.assertEquals(response, receivedResponse) | |
237 | ||
238 | for _ in range(numberOfQueries): | |
239 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) | |
240 | receivedQuery.id = query.id | |
241 | self.assertEquals(query, receivedQuery) | |
242 | self.assertEquals(response, receivedResponse) | |
243 | ||
02bbf9eb RG |
244 | for key in self._responsesCounter: |
245 | value = self._responsesCounter[key] | |
903853f4 RG |
246 | self.assertEquals(value, numberOfQueries / 2) |
247 | ||
248 | class TestRoutingRoundRobinLBOneDown(DNSDistTest): | |
249 | ||
250 | _testServer2Port = 5351 | |
251 | _config_params = ['_testServerPort', '_testServer2Port'] | |
252 | _config_template = """ | |
253 | setServerPolicy(roundrobin) | |
254 | s1 = newServer{address="127.0.0.1:%s"} | |
255 | s1:setUp() | |
256 | s2 = newServer{address="127.0.0.1:%s"} | |
257 | s2:setDown() | |
258 | """ | |
259 | ||
260 | def testRRWithOneDown(self): | |
261 | """ | |
262 | Routing: Round Robin with one server down | |
263 | ||
264 | Send 100 A queries to "rr.routing.tests.powerdns.com.", | |
265 | check that dnsdist routes all of it to the only backend up. | |
266 | """ | |
267 | numberOfQueries = 10 | |
268 | name = 'rr.routing.tests.powerdns.com.' | |
269 | query = dns.message.make_query(name, 'A', 'IN') | |
270 | response = dns.message.make_response(query) | |
271 | rrset = dns.rrset.from_text(name, | |
272 | 60, | |
273 | dns.rdataclass.IN, | |
274 | dns.rdatatype.A, | |
275 | '192.0.2.1') | |
276 | response.answer.append(rrset) | |
277 | ||
278 | # the round robin counter is shared for UDP and TCP, | |
279 | # so we need to do UDP then TCP to have a clean count | |
280 | for _ in range(numberOfQueries): | |
281 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
282 | receivedQuery.id = query.id | |
283 | self.assertEquals(query, receivedQuery) | |
284 | self.assertEquals(response, receivedResponse) | |
285 | ||
286 | for _ in range(numberOfQueries): | |
287 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) | |
288 | receivedQuery.id = query.id | |
289 | self.assertEquals(query, receivedQuery) | |
290 | self.assertEquals(response, receivedResponse) | |
291 | ||
292 | total = 0 | |
02bbf9eb RG |
293 | for key in self._responsesCounter: |
294 | value = self._responsesCounter[key] | |
903853f4 RG |
295 | self.assertTrue(value == numberOfQueries or value == 0) |
296 | total += value | |
297 | ||
298 | self.assertEquals(total, numberOfQueries * 2) | |
d12cd8e9 RG |
299 | |
300 | class TestRoutingOrder(DNSDistTest): | |
301 | ||
302 | _testServer2Port = 5351 | |
303 | _config_params = ['_testServerPort', '_testServer2Port'] | |
304 | _config_template = """ | |
305 | setServerPolicy(firstAvailable) | |
306 | s1 = newServer{address="127.0.0.1:%s", order=2} | |
307 | s1:setUp() | |
308 | s2 = newServer{address="127.0.0.1:%s", order=1} | |
309 | s2:setUp() | |
310 | """ | |
311 | ||
312 | @classmethod | |
313 | def startResponders(cls): | |
314 | print("Launching responders..") | |
315 | cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort]) | |
316 | cls._UDPResponder.setDaemon(True) | |
317 | cls._UDPResponder.start() | |
318 | cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port]) | |
319 | cls._UDPResponder2.setDaemon(True) | |
320 | cls._UDPResponder2.start() | |
321 | ||
322 | cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort]) | |
323 | cls._TCPResponder.setDaemon(True) | |
324 | cls._TCPResponder.start() | |
325 | ||
326 | cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port]) | |
327 | cls._TCPResponder2.setDaemon(True) | |
328 | cls._TCPResponder2.start() | |
329 | ||
330 | def testOrder(self): | |
331 | """ | |
332 | Routing: firstAvailable policy based on 'order' | |
333 | ||
334 | Send 50 A queries to "order.routing.tests.powerdns.com.", | |
335 | check that dnsdist routes all of it to the second backend | |
336 | because it has the lower order value. | |
337 | """ | |
338 | numberOfQueries = 50 | |
339 | name = 'order.routing.tests.powerdns.com.' | |
340 | query = dns.message.make_query(name, 'A', 'IN') | |
341 | response = dns.message.make_response(query) | |
342 | rrset = dns.rrset.from_text(name, | |
343 | 60, | |
344 | dns.rdataclass.IN, | |
345 | dns.rdatatype.A, | |
346 | '192.0.2.1') | |
347 | response.answer.append(rrset) | |
348 | ||
349 | for _ in range(numberOfQueries): | |
350 | (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response) | |
351 | receivedQuery.id = query.id | |
352 | self.assertEquals(query, receivedQuery) | |
353 | self.assertEquals(response, receivedResponse) | |
354 | (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response) | |
355 | receivedQuery.id = query.id | |
356 | self.assertEquals(query, receivedQuery) | |
357 | self.assertEquals(response, receivedResponse) | |
358 | ||
359 | total = 0 | |
26a3cdb7 RG |
360 | if 'UDP Responder' in self._responsesCounter: |
361 | self.assertEquals(self._responsesCounter['UDP Responder'], 0) | |
d12cd8e9 | 362 | self.assertEquals(self._responsesCounter['UDP Responder 2'], numberOfQueries) |
26a3cdb7 RG |
363 | if 'TCP Responder' in self._responsesCounter: |
364 | self.assertEquals(self._responsesCounter['TCP Responder'], 0) | |
d12cd8e9 | 365 | self.assertEquals(self._responsesCounter['TCP Responder 2'], numberOfQueries) |
26a3cdb7 RG |
366 | |
367 | class TestRoutingNoServer(DNSDistTest): | |
368 | ||
369 | _config_template = """ | |
370 | newServer{address="127.0.0.1:%s", pool="real"} | |
371 | setServFailWhenNoServer(true) | |
372 | """ | |
373 | ||
374 | def testPolicyPoolNoServer(self): | |
375 | """ | |
376 | Routing: No server should return ServFail | |
377 | """ | |
378 | name = 'noserver.routing.tests.powerdns.com.' | |
379 | query = dns.message.make_query(name, 'A', 'IN') | |
380 | expectedResponse = dns.message.make_response(query) | |
381 | expectedResponse.set_rcode(dns.rcode.SERVFAIL) | |
382 | ||
383 | (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False) | |
384 | self.assertEquals(receivedResponse, expectedResponse) | |
385 | ||
386 | (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False) | |
387 | self.assertEquals(receivedResponse, expectedResponse) |