]>
git.ipfire.org Git - thirdparty/pdns.git/blob - regression-tests.dnsdist/test_Routing.py
6 from dnsdisttests
import DNSDistTest
8 class TestRoutingPoolRouting(DNSDistTest
):
10 _config_template
= """
11 newServer{address="127.0.0.1:%s", pool="real"}
12 addAction(makeRule("poolaction.routing.tests.powerdns.com"), PoolAction("real"))
15 def testPolicyPoolAction(self
):
17 Routing: Set pool by qname via PoolAction
19 Send an A query to "poolaction.routing.tests.powerdns.com.",
20 check that dnsdist routes the query to the "real" pool.
22 name
= 'poolaction.routing.tests.powerdns.com.'
23 query
= dns
.message
.make_query(name
, 'A', 'IN')
24 response
= dns
.message
.make_response(query
)
25 rrset
= dns
.rrset
.from_text(name
,
30 response
.answer
.append(rrset
)
32 for method
in ("sendUDPQuery", "sendTCPQuery"):
33 sender
= getattr(self
, method
)
34 (receivedQuery
, receivedResponse
) = sender(query
, response
)
35 receivedQuery
.id = query
.id
36 self
.assertEquals(query
, receivedQuery
)
37 self
.assertEquals(response
, receivedResponse
)
39 def testDefaultPool(self
):
41 Routing: Set pool by qname canary
43 Send an A query to "notpool.routing.tests.powerdns.com.",
44 check that dnsdist sends no response (no servers
47 name
= 'notpool.routing.tests.powerdns.com.'
48 query
= dns
.message
.make_query(name
, 'A', 'IN')
50 for method
in ("sendUDPQuery", "sendTCPQuery"):
51 sender
= getattr(self
, method
)
52 (_
, receivedResponse
) = sender(query
, response
=None, useQueue
=False)
53 self
.assertEquals(receivedResponse
, None)
55 class TestRoutingQPSPoolRouting(DNSDistTest
):
56 _config_template
= """
57 newServer{address="127.0.0.1:%s", pool="regular"}
58 addAction(makeRule("qpspoolaction.routing.tests.powerdns.com"), QPSPoolAction(10, "regular"))
61 def testQPSPoolAction(self
):
63 Routing: Set pool by QPS via action
65 Send queries to "qpspoolaction.routing.tests.powerdns.com."
66 check that dnsdist does not route the query to the "regular" pool
67 when the max QPS has been reached.
70 name
= 'qpspoolaction.routing.tests.powerdns.com.'
71 query
= dns
.message
.make_query(name
, 'A', 'IN')
72 response
= dns
.message
.make_response(query
)
73 rrset
= dns
.rrset
.from_text(name
,
78 response
.answer
.append(rrset
)
80 for _
in range(maxQPS
):
81 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
82 receivedQuery
.id = query
.id
83 self
.assertEquals(query
, receivedQuery
)
84 self
.assertEquals(response
, receivedResponse
)
86 # we should now be sent to the "abuse" pool which is empty,
87 # so the queries should be dropped
88 (_
, receivedResponse
) = self
.sendUDPQuery(query
, response
=None, useQueue
=False)
89 self
.assertEquals(receivedResponse
, None)
93 # again, over TCP this time
94 for _
in range(maxQPS
):
95 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
96 receivedQuery
.id = query
.id
97 self
.assertEquals(query
, receivedQuery
)
98 self
.assertEquals(response
, receivedResponse
)
101 (_
, receivedResponse
) = self
.sendTCPQuery(query
, response
=None, useQueue
=False)
102 self
.assertEquals(receivedResponse
, None)
105 class TestRoutingRoundRobinLB(DNSDistTest
):
107 _testServer2Port
= 5351
108 _config_params
= ['_testServerPort', '_testServer2Port']
109 _config_template
= """
110 setServerPolicy(roundrobin)
111 s1 = newServer{address="127.0.0.1:%s"}
113 s2 = newServer{address="127.0.0.1:%s"}
118 def startResponders(cls
):
119 print("Launching responders..")
120 cls
._UDPResponder
= threading
.Thread(name
='UDP Responder', target
=cls
.UDPResponder
, args
=[cls
._testServerPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
121 cls
._UDPResponder
.setDaemon(True)
122 cls
._UDPResponder
.start()
123 cls
._UDPResponder
2 = threading
.Thread(name
='UDP Responder 2', target
=cls
.UDPResponder
, args
=[cls
._testServer
2Port
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
124 cls
._UDPResponder
2.setDaemon(True)
125 cls
._UDPResponder
2.start()
127 cls
._TCPResponder
= threading
.Thread(name
='TCP Responder', target
=cls
.TCPResponder
, args
=[cls
._testServerPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
128 cls
._TCPResponder
.setDaemon(True)
129 cls
._TCPResponder
.start()
131 cls
._TCPResponder
2 = threading
.Thread(name
='TCP Responder 2', target
=cls
.TCPResponder
, args
=[cls
._testServer
2Port
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
132 cls
._TCPResponder
2.setDaemon(True)
133 cls
._TCPResponder
2.start()
139 Send 10 A queries to "rr.routing.tests.powerdns.com.",
140 check that dnsdist routes half of it to each backend.
143 name
= 'rr.routing.tests.powerdns.com.'
144 query
= dns
.message
.make_query(name
, 'A', 'IN')
145 response
= dns
.message
.make_response(query
)
146 rrset
= dns
.rrset
.from_text(name
,
151 response
.answer
.append(rrset
)
153 # the round robin counter is shared for UDP and TCP,
154 # so we need to do UDP then TCP to have a clean count
155 for _
in range(numberOfQueries
):
156 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
157 receivedQuery
.id = query
.id
158 self
.assertEquals(query
, receivedQuery
)
159 self
.assertEquals(response
, receivedResponse
)
161 for _
in range(numberOfQueries
):
162 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
163 receivedQuery
.id = query
.id
164 self
.assertEquals(query
, receivedQuery
)
165 self
.assertEquals(response
, receivedResponse
)
167 for key
in self
._responsesCounter
:
168 value
= self
._responsesCounter
[key
]
169 self
.assertEquals(value
, numberOfQueries
/ 2)
171 class TestRoutingRoundRobinLBOneDown(DNSDistTest
):
173 _testServer2Port
= 5351
174 _config_params
= ['_testServerPort', '_testServer2Port']
175 _config_template
= """
176 setServerPolicy(roundrobin)
177 s1 = newServer{address="127.0.0.1:%s"}
179 s2 = newServer{address="127.0.0.1:%s"}
183 def testRRWithOneDown(self
):
185 Routing: Round Robin with one server down
187 Send 100 A queries to "rr.routing.tests.powerdns.com.",
188 check that dnsdist routes all of it to the only backend up.
191 name
= 'rr.routing.tests.powerdns.com.'
192 query
= dns
.message
.make_query(name
, 'A', 'IN')
193 response
= dns
.message
.make_response(query
)
194 rrset
= dns
.rrset
.from_text(name
,
199 response
.answer
.append(rrset
)
201 # the round robin counter is shared for UDP and TCP,
202 # so we need to do UDP then TCP to have a clean count
203 for _
in range(numberOfQueries
):
204 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
205 receivedQuery
.id = query
.id
206 self
.assertEquals(query
, receivedQuery
)
207 self
.assertEquals(response
, receivedResponse
)
209 for _
in range(numberOfQueries
):
210 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
211 receivedQuery
.id = query
.id
212 self
.assertEquals(query
, receivedQuery
)
213 self
.assertEquals(response
, receivedResponse
)
216 for key
in self
._responsesCounter
:
217 value
= self
._responsesCounter
[key
]
218 self
.assertTrue(value
== numberOfQueries
or value
== 0)
221 self
.assertEquals(total
, numberOfQueries
* 2)
223 class TestRoutingOrder(DNSDistTest
):
225 _testServer2Port
= 5351
226 _config_params
= ['_testServerPort', '_testServer2Port']
227 _config_template
= """
228 setServerPolicy(firstAvailable)
229 s1 = newServer{address="127.0.0.1:%s", order=2}
231 s2 = newServer{address="127.0.0.1:%s", order=1}
236 def startResponders(cls
):
237 print("Launching responders..")
238 cls
._UDPResponder
= threading
.Thread(name
='UDP Responder', target
=cls
.UDPResponder
, args
=[cls
._testServerPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
239 cls
._UDPResponder
.setDaemon(True)
240 cls
._UDPResponder
.start()
241 cls
._UDPResponder
2 = threading
.Thread(name
='UDP Responder 2', target
=cls
.UDPResponder
, args
=[cls
._testServer
2Port
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
242 cls
._UDPResponder
2.setDaemon(True)
243 cls
._UDPResponder
2.start()
245 cls
._TCPResponder
= threading
.Thread(name
='TCP Responder', target
=cls
.TCPResponder
, args
=[cls
._testServerPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
246 cls
._TCPResponder
.setDaemon(True)
247 cls
._TCPResponder
.start()
249 cls
._TCPResponder
2 = threading
.Thread(name
='TCP Responder 2', target
=cls
.TCPResponder
, args
=[cls
._testServer
2Port
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
250 cls
._TCPResponder
2.setDaemon(True)
251 cls
._TCPResponder
2.start()
255 Routing: firstAvailable policy based on 'order'
257 Send 50 A queries to "order.routing.tests.powerdns.com.",
258 check that dnsdist routes all of it to the second backend
259 because it has the lower order value.
262 name
= 'order.routing.tests.powerdns.com.'
263 query
= dns
.message
.make_query(name
, 'A', 'IN')
264 response
= dns
.message
.make_response(query
)
265 rrset
= dns
.rrset
.from_text(name
,
270 response
.answer
.append(rrset
)
272 for _
in range(numberOfQueries
):
273 for method
in ("sendUDPQuery", "sendTCPQuery"):
274 sender
= getattr(self
, method
)
275 (receivedQuery
, receivedResponse
) = sender(query
, response
)
276 receivedQuery
.id = query
.id
277 self
.assertEquals(query
, receivedQuery
)
278 self
.assertEquals(response
, receivedResponse
)
281 if 'UDP Responder' in self
._responsesCounter
:
282 self
.assertEquals(self
._responsesCounter
['UDP Responder'], 0)
283 self
.assertEquals(self
._responsesCounter
['UDP Responder 2'], numberOfQueries
)
284 if 'TCP Responder' in self
._responsesCounter
:
285 self
.assertEquals(self
._responsesCounter
['TCP Responder'], 0)
286 self
.assertEquals(self
._responsesCounter
['TCP Responder 2'], numberOfQueries
)
288 class TestRoutingNoServer(DNSDistTest
):
290 _config_template
= """
291 newServer{address="127.0.0.1:%s", pool="real"}
292 setServFailWhenNoServer(true)
295 def testPolicyPoolNoServer(self
):
297 Routing: No server should return ServFail
299 name
= 'noserver.routing.tests.powerdns.com.'
300 query
= dns
.message
.make_query(name
, 'A', 'IN')
301 expectedResponse
= dns
.message
.make_response(query
)
302 expectedResponse
.set_rcode(dns
.rcode
.SERVFAIL
)
304 for method
in ("sendUDPQuery", "sendTCPQuery"):
305 sender
= getattr(self
, method
)
306 (_
, receivedResponse
) = sender(query
, response
=None, useQueue
=False)
307 self
.assertEquals(receivedResponse
, expectedResponse
)
309 class TestRoutingWRandom(DNSDistTest
):
311 _testServer2Port
= 5351
312 _config_params
= ['_testServerPort', '_testServer2Port']
313 _config_template
= """
314 setServerPolicy(wrandom)
315 s1 = newServer{address="127.0.0.1:%s", weight=1}
317 s2 = newServer{address="127.0.0.1:%s", weight=2}
322 def startResponders(cls
):
323 print("Launching responders..")
324 cls
._UDPResponder
= threading
.Thread(name
='UDP Responder', target
=cls
.UDPResponder
, args
=[cls
._testServerPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
325 cls
._UDPResponder
.setDaemon(True)
326 cls
._UDPResponder
.start()
327 cls
._UDPResponder
2 = threading
.Thread(name
='UDP Responder 2', target
=cls
.UDPResponder
, args
=[cls
._testServer
2Port
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
328 cls
._UDPResponder
2.setDaemon(True)
329 cls
._UDPResponder
2.start()
331 cls
._TCPResponder
= threading
.Thread(name
='TCP Responder', target
=cls
.TCPResponder
, args
=[cls
._testServerPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
332 cls
._TCPResponder
.setDaemon(True)
333 cls
._TCPResponder
.start()
335 cls
._TCPResponder
2 = threading
.Thread(name
='TCP Responder 2', target
=cls
.TCPResponder
, args
=[cls
._testServer
2Port
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
336 cls
._TCPResponder
2.setDaemon(True)
337 cls
._TCPResponder
2.start()
339 def testWRandom(self
):
343 Send 100 A queries to "wrandom.routing.tests.powerdns.com.",
344 check that dnsdist routes less than half to one, more to the other.
346 numberOfQueries
= 100
347 name
= 'wrandom.routing.tests.powerdns.com.'
348 query
= dns
.message
.make_query(name
, 'A', 'IN')
349 response
= dns
.message
.make_response(query
)
350 rrset
= dns
.rrset
.from_text(name
,
355 response
.answer
.append(rrset
)
357 # the counter is shared for UDP and TCP,
358 # so we need to do UDP then TCP to have a clean count
359 for _
in range(numberOfQueries
):
360 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
361 receivedQuery
.id = query
.id
362 self
.assertEquals(query
, receivedQuery
)
363 self
.assertEquals(response
, receivedResponse
)
365 for _
in range(numberOfQueries
):
366 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
367 receivedQuery
.id = query
.id
368 self
.assertEquals(query
, receivedQuery
)
369 self
.assertEquals(response
, receivedResponse
)
371 # The lower weight downstream should receive less than half the queries
372 self
.assertLess(self
._responsesCounter
['UDP Responder'], numberOfQueries
* 0.50)
373 self
.assertLess(self
._responsesCounter
['TCP Responder'], numberOfQueries
* 0.50)
375 # The higher weight downstream should receive more than half the queries
376 self
.assertGreater(self
._responsesCounter
['UDP Responder 2'], numberOfQueries
* 0.50)
377 self
.assertGreater(self
._responsesCounter
['TCP Responder 2'], numberOfQueries
* 0.50)
380 class TestRoutingHighValueWRandom(DNSDistTest
):
382 _testServer2Port
= 5351
383 _consoleKey
= DNSDistTest
.generateConsoleKey()
384 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
385 _config_params
= ['_consoleKeyB64', '_consolePort', '_testServerPort', '_testServer2Port']
386 _config_template
= """
388 controlSocket("127.0.0.1:%s")
389 setServerPolicy(wrandom)
390 s1 = newServer{address="127.0.0.1:%s", weight=2000000000}
392 s2 = newServer{address="127.0.0.1:%s", weight=2000000000}
397 def startResponders(cls
):
398 print("Launching responders..")
399 cls
._UDPResponder
= threading
.Thread(name
='UDP Responder', target
=cls
.UDPResponder
, args
=[cls
._testServerPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
400 cls
._UDPResponder
.setDaemon(True)
401 cls
._UDPResponder
.start()
402 cls
._UDPResponder
2 = threading
.Thread(name
='UDP Responder 2', target
=cls
.UDPResponder
, args
=[cls
._testServer
2Port
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
403 cls
._UDPResponder
2.setDaemon(True)
404 cls
._UDPResponder
2.start()
406 cls
._TCPResponder
= threading
.Thread(name
='TCP Responder', target
=cls
.TCPResponder
, args
=[cls
._testServerPort
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
407 cls
._TCPResponder
.setDaemon(True)
408 cls
._TCPResponder
.start()
410 cls
._TCPResponder
2 = threading
.Thread(name
='TCP Responder 2', target
=cls
.TCPResponder
, args
=[cls
._testServer
2Port
, cls
._toResponderQueue
, cls
._fromResponderQueue
])
411 cls
._TCPResponder
2.setDaemon(True)
412 cls
._TCPResponder
2.start()
414 def testHighValueWRandom(self
):
416 Routing: WRandom (overflow)
418 Send 100 A queries to "wrandom-overflow.routing.tests.powerdns.com.",
419 check that dnsdist routes to each downstream, rather than failing with
422 numberOfQueries
= 100
423 name
= 'wrandom-overflow.routing.tests.powerdns.com.'
424 query
= dns
.message
.make_query(name
, 'A', 'IN')
425 response
= dns
.message
.make_response(query
)
426 rrset
= dns
.rrset
.from_text(name
,
431 response
.answer
.append(rrset
)
433 # the counter is shared for UDP and TCP,
434 # so we need to do UDP then TCP to have a clean count
435 for _
in range(numberOfQueries
):
436 (receivedQuery
, receivedResponse
) = self
.sendUDPQuery(query
, response
)
437 receivedQuery
.id = query
.id
438 self
.assertEquals(query
, receivedQuery
)
439 self
.assertEquals(response
, receivedResponse
)
441 for _
in range(numberOfQueries
):
442 (receivedQuery
, receivedResponse
) = self
.sendTCPQuery(query
, response
)
443 receivedQuery
.id = query
.id
444 self
.assertEquals(query
, receivedQuery
)
445 self
.assertEquals(response
, receivedResponse
)
447 stats
= self
.sendConsoleCommand("dumpStats()").split()
450 # Map to a dict with every other element being the value to the previous one
451 for i
, x
in enumerate(stats
):
453 stats_dict
[x
] = stats
[i
+1]
455 # There should be no queries getting "no-policy" responses
456 self
.assertEquals(stats_dict
['no-policy'], '0')
458 # Each downstream should receive some queries, but it will be unbalanced
459 # because the sum of the weights is higher than INT_MAX.
460 # The first downstream will receive more than half the queries
461 self
.assertGreater(self
._responsesCounter
['UDP Responder'], numberOfQueries
/ 2)
462 self
.assertGreater(self
._responsesCounter
['TCP Responder'], numberOfQueries
/ 2)
464 # The second downstream will receive the remainder of the queries, but it might very well be 0
465 if 'UDP Responder 2' in self
._responsesCounter
:
466 self
.assertEquals(self
._responsesCounter
['UDP Responder 2'], numberOfQueries
- self
._responsesCounter
['UDP Responder'])
467 if 'TCP Responder 2' in self
._responsesCounter
:
468 self
.assertEquals(self
._responsesCounter
['TCP Responder 2'], numberOfQueries
- self
._responsesCounter
['TCP Responder'])
470 class TestRoutingBadWeightWRandom(DNSDistTest
):
472 _testServer2Port
= 5351
473 _consoleKey
= DNSDistTest
.generateConsoleKey()
474 _consoleKeyB64
= base64
.b64encode(_consoleKey
).decode('ascii')
475 _config_params
= ['_consoleKeyB64', '_consolePort', '_testServerPort', '_testServer2Port']
476 _config_template
= """
478 controlSocket("127.0.0.1:%s")
479 setServerPolicy(wrandom)
480 s1 = newServer{address="127.0.0.1:%s", weight=-1}
481 s2 = newServer{address="127.0.0.1:%s", weight=2147483648}
484 def testBadWeightWRandom(self
):
488 Test that downstreams cannot be added with invalid weights.
490 # There should be no downstreams
491 self
.assertTrue(self
.sendConsoleCommand("getServer(0)").startswith("Error"))