]> git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.dnsdist/test_Routing.py
Merge pull request #5883 from pieterlexis/issue-5853-pdnsutil-clobber-metadata
[thirdparty/pdns.git] / regression-tests.dnsdist / test_Routing.py
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, cls._toResponderQueue, cls._fromResponderQueue])
198 cls._UDPResponder.setDaemon(True)
199 cls._UDPResponder.start()
200 cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
201 cls._UDPResponder2.setDaemon(True)
202 cls._UDPResponder2.start()
203
204 cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
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, cls._toResponderQueue, cls._fromResponderQueue])
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
244 for key in self._responsesCounter:
245 value = self._responsesCounter[key]
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
293 for key in self._responsesCounter:
294 value = self._responsesCounter[key]
295 self.assertTrue(value == numberOfQueries or value == 0)
296 total += value
297
298 self.assertEquals(total, numberOfQueries * 2)
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, cls._toResponderQueue, cls._fromResponderQueue])
316 cls._UDPResponder.setDaemon(True)
317 cls._UDPResponder.start()
318 cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
319 cls._UDPResponder2.setDaemon(True)
320 cls._UDPResponder2.start()
321
322 cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
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, cls._toResponderQueue, cls._fromResponderQueue])
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
360 if 'UDP Responder' in self._responsesCounter:
361 self.assertEquals(self._responsesCounter['UDP Responder'], 0)
362 self.assertEquals(self._responsesCounter['UDP Responder 2'], numberOfQueries)
363 if 'TCP Responder' in self._responsesCounter:
364 self.assertEquals(self._responsesCounter['TCP Responder'], 0)
365 self.assertEquals(self._responsesCounter['TCP Responder 2'], numberOfQueries)
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)