]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Fix newServerPolicy, add regression tests for custom policies
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 9 Feb 2024 10:16:12 +0000 (11:16 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 9 Feb 2024 10:16:12 +0000 (11:16 +0100)
pdns/dnsdist-lua-bindings.cc
pdns/dnsdist-lua.cc
regression-tests.dnsdist/test_Routing.py

index e1854a280edea3cd3c68d7b98fb557923e3f3540..3f5d6e21159af449016a59064b752804e2aae183 100644 (file)
@@ -78,16 +78,16 @@ void setupLuaBindings(LuaContext& luaCtx, bool client, bool configCheck)
   luaCtx.registerFunction("toString", &ServerPolicy::toString);
   luaCtx.registerFunction("__tostring", &ServerPolicy::toString);
 
-  static const std::array<ServerPolicy, 6> policies = {
-    ServerPolicy{"firstAvailable", firstAvailable, false},
-    ServerPolicy{"roundrobin", roundrobin, false},
-    ServerPolicy{"wrandom", wrandom, false},
-    ServerPolicy{"whashed", whashed, false},
-    ServerPolicy{"chashed", chashed, false},
-    ServerPolicy{"leastOutstanding", leastOutstanding, false}
+  const std::array<std::shared_ptr<ServerPolicy>, 6> policies = {
+    std::make_shared<ServerPolicy>("firstAvailable", firstAvailable, false),
+    std::make_shared<ServerPolicy>("roundrobin", roundrobin, false),
+    std::make_shared<ServerPolicy>("wrandom", wrandom, false),
+    std::make_shared<ServerPolicy>("whashed", whashed, false),
+    std::make_shared<ServerPolicy>("chashed", chashed, false),
+    std::make_shared<ServerPolicy>("leastOutstanding", leastOutstanding, false)
   };
   for (const auto& policy : policies) {
-    luaCtx.writeVariable(policy.d_name, &policy);
+    luaCtx.writeVariable(policy->d_name, policy);
   }
 
 #endif /* DISABLE_POLICIES_BINDINGS */
index 1094be77a5424bd5dfd0ab6577daac6975b1c920..f261f453d2e8d40fe7f77d43090c966579f911a5 100644 (file)
@@ -2225,12 +2225,7 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
 #endif /* HAVE_NET_SNMP */
 
 #ifndef DISABLE_POLICIES_BINDINGS
-  luaCtx.writeFunction("setServerPolicy", [](const ServerPolicy& policy) {
-    setLuaSideEffect();
-    g_policy.setState(policy);
-  });
-
-  luaCtx.writeFunction("setServerPolicy", [](const ServerPolicy* policy) {
+  luaCtx.writeFunction("setServerPolicy", [](const std::shared_ptr<ServerPolicy>& policy) {
     setLuaSideEffect();
     g_policy.setState(*policy);
   });
@@ -2257,10 +2252,10 @@ static void setupLuaConfig(LuaContext& luaCtx, bool client, bool configCheck)
     g_outputBuffer = g_policy.getLocal()->getName() + "\n";
   });
 
-  luaCtx.writeFunction("setPoolServerPolicy", [](ServerPolicy policy, const string& pool) {
+  luaCtx.writeFunction("setPoolServerPolicy", [](const std::shared_ptr<ServerPolicy>& policy, const string& pool) {
     setLuaSideEffect();
     auto localPools = g_pools.getCopy();
-    setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(std::move(policy)));
+    setPoolPolicy(localPools, pool, std::make_shared<ServerPolicy>(*policy));
     g_pools.setState(localPools);
   });
 
index d0ceeafd979c092a98da0505380307580062fda6..1dabca1a746270c1977c711dffce86b36718a460 100644 (file)
@@ -129,8 +129,44 @@ class TestRoutingQPSPoolRouting(DNSDistTest):
         (_, 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)
 
-class TestRoutingRoundRobinLB(DNSDistTest):
+        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(RoundRobinTest, DNSDistTest):
 
     _testServer2Port = pickAvailablePort()
     _config_params = ['_testServerPort', '_testServer2Port']
@@ -167,34 +203,46 @@ class TestRoutingRoundRobinLB(DNSDistTest):
         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):
 
@@ -281,7 +329,7 @@ class TestRoutingRoundRobinLBAllDown(DNSDistTest):
             (_, receivedResponse) = sender(query, response=None, useQueue=False)
             self.assertEqual(receivedResponse, None)
 
-class TestRoutingLuaFFIPerThreadRoundRobinLB(DNSDistTest):
+class TestRoutingLuaFFIPerThreadRoundRobinLB(RoundRobinTest, DNSDistTest):
 
     _testServer2Port = pickAvailablePort()
     _config_params = ['_testServerPort', '_testServer2Port']
@@ -326,39 +374,55 @@ class TestRoutingLuaFFIPerThreadRoundRobinLB(DNSDistTest):
 
     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):