import threading
import time
import dns
-from dnsdisttests import DNSDistTest
+from dnsdisttests import DNSDistTest, pickAvailablePort
class TestRoutingPoolRouting(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%s", pool="real"}
- addAction(makeRule("poolaction.routing.tests.powerdns.com"), PoolAction("real"))
+ addAction(SuffixMatchNodeRule("poolaction.routing.tests.powerdns.com"), PoolAction("real"))
+ -- by default PoolAction stops the processing so the second rule should not be executed
+ addAction(SuffixMatchNodeRule("poolaction.routing.tests.powerdns.com"), PoolAction("not-real"))
+
+ -- this time we configure PoolAction to not stop the processing
+ addAction(SuffixMatchNodeRule("poolaction-nostop.routing.tests.powerdns.com"), PoolAction("no-real", false))
+ -- so the second rule should be executed
+ addAction(SuffixMatchNodeRule("poolaction-nostop.routing.tests.powerdns.com"), PoolAction("real"))
"""
def testPolicyPoolAction(self):
self.assertEqual(query, receivedQuery)
self.assertEqual(response, receivedResponse)
+ def testPolicyPoolActionNoStop(self):
+ """
+ Routing: Set pool by qname via PoolAction (no stop)
+ """
+ name = 'poolaction-nostop.routing.tests.powerdns.com.'
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ response.answer.append(rrset)
+
+ for method in ("sendUDPQuery", "sendTCPQuery"):
+ sender = getattr(self, method)
+ (receivedQuery, receivedResponse) = sender(query, response)
+ receivedQuery.id = query.id
+ self.assertEqual(query, receivedQuery)
+ self.assertEqual(response, receivedResponse)
+
def testDefaultPool(self):
"""
Routing: Set pool by qname canary
class TestRoutingQPSPoolRouting(DNSDistTest):
_config_template = """
newServer{address="127.0.0.1:%s", pool="regular"}
- addAction(makeRule("qpspoolaction.routing.tests.powerdns.com"), QPSPoolAction(10, "regular"))
+ addAction(SuffixMatchNodeRule("qpspoolaction.routing.tests.powerdns.com"), QPSPoolAction(10, "regular"))
"""
def testQPSPoolAction(self):
(_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
+class RoundRobinTest(object):
+ def doTestRR(self, name):
+ """
+ Routing: Round Robin
+
+ Send 10 A queries to the requested name,
+ check that dnsdist routes half of it to each backend.
+ """
+ numberOfQueries = 10
+ name = name
+ query = dns.message.make_query(name, 'A', 'IN')
+ response = dns.message.make_response(query)
+ rrset = dns.rrset.from_text(name,
+ 60,
+ dns.rdataclass.IN,
+ dns.rdatatype.A,
+ '192.0.2.1')
+ response.answer.append(rrset)
+
+ # the round robin counter is shared for UDP and TCP,
+ # so we need to do UDP then TCP to have a clean count
+ for _ in range(numberOfQueries):
+ (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+ receivedQuery.id = query.id
+ self.assertEqual(query, receivedQuery)
+ self.assertEqual(response, receivedResponse)
+
+ for _ in range(numberOfQueries):
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ receivedQuery.id = query.id
+ self.assertEqual(query, receivedQuery)
+ self.assertEqual(response, receivedResponse)
+
+ for key in self._responsesCounter:
+ value = self._responsesCounter[key]
+ self.assertEqual(value, numberOfQueries / 2)
-class TestRoutingRoundRobinLB(DNSDistTest):
+class TestRoutingRoundRobinLB(RoundRobinTest, DNSDistTest):
- _testServer2Port = 5351
+ _testServer2Port = pickAvailablePort()
_config_params = ['_testServerPort', '_testServer2Port']
_config_template = """
setServerPolicy(roundrobin)
def startResponders(cls):
print("Launching responders..")
cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
- cls._UDPResponder.setDaemon(True)
+ cls._UDPResponder.daemon = True
cls._UDPResponder.start()
cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
- cls._UDPResponder2.setDaemon(True)
+ cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
- cls._TCPResponder.setDaemon(True)
+ cls._TCPResponder.daemon = True
cls._TCPResponder.start()
cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
- cls._TCPResponder2.setDaemon(True)
+ cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
def testRR(self):
Send 10 A queries to "rr.routing.tests.powerdns.com.",
check that dnsdist routes half of it to each backend.
"""
- numberOfQueries = 10
- name = 'rr.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
- response.answer.append(rrset)
+ self.doTestRR('rr.routing.tests.powerdns.com.')
- # the round robin counter is shared for UDP and TCP,
- # so we need to do UDP then TCP to have a clean count
- for _ in range(numberOfQueries):
- (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
- receivedQuery.id = query.id
- self.assertEqual(query, receivedQuery)
- self.assertEqual(response, receivedResponse)
+class TestRoutingRoundRobinLBViaPool(RoundRobinTest, DNSDistTest):
- for _ in range(numberOfQueries):
- (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
- receivedQuery.id = query.id
- self.assertEqual(query, receivedQuery)
- self.assertEqual(response, receivedResponse)
+ _testServer2Port = pickAvailablePort()
+ _config_params = ['_testServerPort', '_testServer2Port']
+ _config_template = """
+ s1 = newServer{address="127.0.0.1:%d"}
+ s1:setUp()
+ s2 = newServer{address="127.0.0.1:%d"}
+ s2:setUp()
+ setPoolServerPolicy(roundrobin, '')
+ """
- for key in self._responsesCounter:
- value = self._responsesCounter[key]
- self.assertEqual(value, numberOfQueries / 2)
+ @classmethod
+ def startResponders(cls):
+ print("Launching responders..")
+ cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder.daemon = True
+ cls._UDPResponder.start()
+ cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2.daemon = True
+ cls._UDPResponder2.start()
+
+ cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder.daemon = True
+ cls._TCPResponder.start()
+
+ cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2.daemon = True
+ cls._TCPResponder2.start()
+
+ def testRR(self):
+ """
+ Routing: Round Robin (pool)
+
+ Send 10 A queries to "rr-pool.routing.tests.powerdns.com.",
+ check that dnsdist routes half of it to each backend.
+ """
+ self.doTestRR('rr-pool.routing.tests.powerdns.com.')
class TestRoutingRoundRobinLBOneDown(DNSDistTest):
- _testServer2Port = 5351
+ _testServer2Port = pickAvailablePort()
_config_params = ['_testServerPort', '_testServer2Port']
_config_template = """
setServerPolicy(roundrobin)
class TestRoutingRoundRobinLBAllDown(DNSDistTest):
- _testServer2Port = 5351
+ _testServer2Port = pickAvailablePort()
_config_params = ['_testServerPort', '_testServer2Port']
_config_template = """
setServerPolicy(roundrobin)
(_, receivedResponse) = sender(query, response=None, useQueue=False)
self.assertEqual(receivedResponse, None)
-class TestRoutingLuaFFIPerThreadRoundRobinLB(DNSDistTest):
+class TestRoutingLuaFFIPerThreadRoundRobinLB(RoundRobinTest, DNSDistTest):
- _testServer2Port = 5351
+ _testServer2Port = pickAvailablePort()
_config_params = ['_testServerPort', '_testServer2Port']
_config_template = """
-- otherwise we start too many TCP workers, and as each thread
def startResponders(cls):
print("Launching responders..")
cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
- cls._UDPResponder.setDaemon(True)
+ cls._UDPResponder.daemon = True
cls._UDPResponder.start()
cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
- cls._UDPResponder2.setDaemon(True)
+ cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
- cls._TCPResponder.setDaemon(True)
+ cls._TCPResponder.daemon = True
cls._TCPResponder.start()
cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
- cls._TCPResponder2.setDaemon(True)
+ cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
def testRR(self):
"""
- Routing: Round Robin
-
- Send 10 A queries to "rr.routing.tests.powerdns.com.",
- check that dnsdist routes half of it to each backend.
+ Routing: Round Robin (LuaFFI)
"""
- numberOfQueries = 10
- name = 'rr.routing.tests.powerdns.com.'
- query = dns.message.make_query(name, 'A', 'IN')
- response = dns.message.make_response(query)
- rrset = dns.rrset.from_text(name,
- 60,
- dns.rdataclass.IN,
- dns.rdatatype.A,
- '192.0.2.1')
- response.answer.append(rrset)
+ self.doTestRR('rr-luaffi.routing.tests.powerdns.com.')
- # the round robin counter is shared for UDP and TCP,
- # so we need to do UDP then TCP to have a clean count
- for _ in range(numberOfQueries):
- (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
- receivedQuery.id = query.id
- self.assertEqual(query, receivedQuery)
- self.assertEqual(response, receivedResponse)
+class TestRoutingCustomLuaRoundRobinLB(RoundRobinTest, DNSDistTest):
- for _ in range(numberOfQueries):
- (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
- receivedQuery.id = query.id
- self.assertEqual(query, receivedQuery)
- self.assertEqual(response, receivedResponse)
+ _testServer2Port = pickAvailablePort()
+ _config_params = ['_testServerPort', '_testServer2Port']
+ _config_template = """
+ -- otherwise we start too many TCP workers, and as each thread
+ -- uses it own counter this makes the TCP queries distribution hard to predict
+ setMaxTCPClientThreads(1)
- for key in self._responsesCounter:
- value = self._responsesCounter[key]
- self.assertEqual(value, numberOfQueries / 2)
+ local counter = 0
+ function luaroundrobin(servers_list, dq)
+ counter = counter + 1
+ return servers_list[(counter %% #servers_list)+1]
+ end
+ setServerPolicy(newServerPolicy("custom lua round robin policy", luaroundrobin))
+
+ s1 = newServer{address="127.0.0.1:%s"}
+ s1:setUp()
+ s2 = newServer{address="127.0.0.1:%s"}
+ s2:setUp()
+ """
+
+ @classmethod
+ def startResponders(cls):
+ print("Launching responders..")
+ cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder.daemon = True
+ cls._UDPResponder.start()
+ cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._UDPResponder2.daemon = True
+ cls._UDPResponder2.start()
+
+ cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder.daemon = True
+ cls._TCPResponder.start()
+
+ cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder2.daemon = True
+ cls._TCPResponder2.start()
+
+ def testRR(self):
+ """
+ Routing: Round Robin (Lua)
+ """
+ self.doTestRR('rr-lua.routing.tests.powerdns.com.')
class TestRoutingOrder(DNSDistTest):
- _testServer2Port = 5351
+ _testServer2Port = pickAvailablePort()
_config_params = ['_testServerPort', '_testServer2Port']
_config_template = """
setServerPolicy(firstAvailable)
def startResponders(cls):
print("Launching responders..")
cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
- cls._UDPResponder.setDaemon(True)
+ cls._UDPResponder.daemon = True
cls._UDPResponder.start()
cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
- cls._UDPResponder2.setDaemon(True)
+ cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
- cls._TCPResponder.setDaemon(True)
+ cls._TCPResponder.daemon = True
cls._TCPResponder.start()
cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
- cls._TCPResponder2.setDaemon(True)
+ cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
def testOrder(self):
class TestFirstAvailableQPSPacketCacheHits(DNSDistTest):
_verboseMode = True
- _testServer2Port = 5351
+ _testServer2Port = pickAvailablePort()
_config_params = ['_testServerPort', '_testServer2Port']
_config_template = """
setServerPolicy(firstAvailable)
def startResponders(cls):
print("Launching responders..")
cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
- cls._UDPResponder.setDaemon(True)
+ cls._UDPResponder.daemon = True
cls._UDPResponder.start()
cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
- cls._UDPResponder2.setDaemon(True)
+ cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
- cls._TCPResponder.setDaemon(True)
+ cls._TCPResponder.daemon = True
cls._TCPResponder.start()
cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
- cls._TCPResponder2.setDaemon(True)
+ cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
def testOrderQPSCacheHits(self):
class TestRoutingWRandom(DNSDistTest):
- _testServer2Port = 5351
+ _testServer2Port = pickAvailablePort()
_config_params = ['_testServerPort', '_testServer2Port']
_config_template = """
setServerPolicy(wrandom)
def startResponders(cls):
print("Launching responders..")
cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
- cls._UDPResponder.setDaemon(True)
+ cls._UDPResponder.daemon = True
cls._UDPResponder.start()
cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
- cls._UDPResponder2.setDaemon(True)
+ cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
- cls._TCPResponder.setDaemon(True)
+ cls._TCPResponder.daemon = True
cls._TCPResponder.start()
cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
- cls._TCPResponder2.setDaemon(True)
+ cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
def testWRandom(self):
class TestRoutingHighValueWRandom(DNSDistTest):
- _testServer2Port = 5351
+ _testServer2Port = pickAvailablePort()
_consoleKey = DNSDistTest.generateConsoleKey()
_consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
_config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_testServer2Port']
def startResponders(cls):
print("Launching responders..")
cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
- cls._UDPResponder.setDaemon(True)
+ cls._UDPResponder.daemon = True
cls._UDPResponder.start()
cls._UDPResponder2 = threading.Thread(name='UDP Responder 2', target=cls.UDPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
- cls._UDPResponder2.setDaemon(True)
+ cls._UDPResponder2.daemon = True
cls._UDPResponder2.start()
cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
- cls._TCPResponder.setDaemon(True)
+ cls._TCPResponder.daemon = True
cls._TCPResponder.start()
cls._TCPResponder2 = threading.Thread(name='TCP Responder 2', target=cls.TCPResponder, args=[cls._testServer2Port, cls._toResponderQueue, cls._fromResponderQueue])
- cls._TCPResponder2.setDaemon(True)
+ cls._TCPResponder2.daemon = True
cls._TCPResponder2.start()
def testHighValueWRandom(self):
self.assertEqual(self._responsesCounter['UDP Responder 2'], numberOfQueries - self._responsesCounter['UDP Responder'])
if 'TCP Responder 2' in self._responsesCounter:
self.assertEqual(self._responsesCounter['TCP Responder 2'], numberOfQueries - self._responsesCounter['TCP Responder'])
-
-class TestRoutingBadWeightWRandom(DNSDistTest):
-
- _testServer2Port = 5351
- _consoleKey = DNSDistTest.generateConsoleKey()
- _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
- _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_testServer2Port']
- _config_template = """
- setKey("%s")
- controlSocket("127.0.0.1:%s")
- setServerPolicy(wrandom)
- s1 = newServer{address="127.0.0.1:%s", weight=-1}
- s2 = newServer{address="127.0.0.1:%s", weight=2147483648}
- """
- _checkConfigExpectedOutput = b"""Error creating new server: downstream weight value must be greater than 0.
-Error creating new server: downstream weight value must be between 1 and 2147483647
-Configuration 'configs/dnsdist_TestRoutingBadWeightWRandom.conf' OK!
-"""
-
- def testBadWeightWRandom(self):
- """
- Routing: WRandom
-
- Test that downstreams cannot be added with invalid weights.
- """
- # There should be no downstreams
- self.assertTrue(self.sendConsoleCommand("getServer(0)").startswith("Error"))