#!/usr/bin/env python
+import base64
import threading
import time
import dns
"""
Routing: Round Robin
- Send 100 A queries to "rr.routing.tests.powerdns.com.",
+ Send 10 A queries to "rr.routing.tests.powerdns.com.",
check that dnsdist routes half of it to each backend.
"""
numberOfQueries = 10
(_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
self.assertEquals(receivedResponse, expectedResponse)
+
+
+class TestRoutingWRandom(DNSDistTest):
+
+ _testServer2Port = 5351
+ _config_params = ['_testServerPort', '_testServer2Port']
+ _config_template = """
+ setServerPolicy(wrandom)
+ s1 = newServer{address="127.0.0.1:%s", weight=1}
+ s1:setUp()
+ s2 = newServer{address="127.0.0.1:%s", weight=2}
+ 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.setDaemon(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.start()
+
+ cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder.setDaemon(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.start()
+
+ def testWRandom(self):
+ """
+ Routing: WRandom
+
+ Send 100 A queries to "rr.routing.tests.powerdns.com.",
+ check that dnsdist routes less than half to one, more to the other.
+ """
+ numberOfQueries = 100
+ 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)
+
+ # the 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.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+
+ for _ in range(numberOfQueries):
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+
+ # The lower weight downstream should receive less than half the queries
+ self.assertTrue(self._responsesCounter['UDP Responder'] < numberOfQueries * 0.50)
+ self.assertTrue(self._responsesCounter['TCP Responder'] < numberOfQueries * 0.50)
+
+ # The higher weight downstream should receive more than half the queries
+ self.assertTrue(self._responsesCounter['UDP Responder 2'] > numberOfQueries * 0.50)
+ self.assertTrue(self._responsesCounter['TCP Responder 2'] > numberOfQueries * 0.50)
+
+
+class TestRoutingHighValueWRandom(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=2000000000}
+ s1:setUp()
+ s2 = newServer{address="127.0.0.1:%s", weight=2000000000}
+ 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.setDaemon(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.start()
+
+ cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, cls._toResponderQueue, cls._fromResponderQueue])
+ cls._TCPResponder.setDaemon(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.start()
+
+ def testHighValueWRandom(self):
+ """
+ Routing: WRandom
+
+ Send 100 A queries to "rr.routing.tests.powerdns.com.",
+ check that dnsdist routes to each downstream, rather than failing with
+ no-policy.
+ """
+ numberOfQueries = 100
+ 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)
+
+ # the 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.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+
+ for _ in range(numberOfQueries):
+ (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+ receivedQuery.id = query.id
+ self.assertEquals(query, receivedQuery)
+ self.assertEquals(response, receivedResponse)
+
+ stats = self.sendConsoleCommand("dumpStats()").split()
+ stats_dict = {}
+
+ # Map to a dict with every other element being the value to the previous one
+ for i, x in enumerate(stats):
+ if not i % 2:
+ stats_dict[x] = stats[i+1]
+
+ # There should be no queries getting "no-policy" responses
+ self.assertEquals(stats_dict['no-policy'], '0')
+
+ # Each downstream should receive some queries, but it will be unbalanced
+ # The first downstream will receive more than half the queries
+ self.assertTrue(self._responsesCounter['UDP Responder'] > numberOfQueries / 2)
+ self.assertTrue(self._responsesCounter['TCP Responder'] > numberOfQueries / 2)
+
+ # The second downstream will receive the remainder of the queries, more than 0
+ self.assertTrue(self._responsesCounter['UDP Responder 2'] > 0)
+ self.assertTrue(self._responsesCounter['TCP Responder 2'] > 0)
+ self.assertEquals(self._responsesCounter['UDP Responder 2'], numberOfQueries - self._responsesCounter['UDP Responder'])
+ self.assertEquals(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}
+ """
+
+ 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"))