From: Remi Gacogne Date: Fri, 9 Feb 2024 10:16:12 +0000 (+0100) Subject: dnsdist: Fix newServerPolicy, add regression tests for custom policies X-Git-Tag: dnsdist-1.9.0~5^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c95ed79c142689baef46203d44591f7d03ed6726;p=thirdparty%2Fpdns.git dnsdist: Fix newServerPolicy, add regression tests for custom policies --- diff --git a/pdns/dnsdist-lua-bindings.cc b/pdns/dnsdist-lua-bindings.cc index e1854a280e..3f5d6e2115 100644 --- a/pdns/dnsdist-lua-bindings.cc +++ b/pdns/dnsdist-lua-bindings.cc @@ -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 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, 6> policies = { + std::make_shared("firstAvailable", firstAvailable, false), + std::make_shared("roundrobin", roundrobin, false), + std::make_shared("wrandom", wrandom, false), + std::make_shared("whashed", whashed, false), + std::make_shared("chashed", chashed, false), + std::make_shared("leastOutstanding", leastOutstanding, false) }; for (const auto& policy : policies) { - luaCtx.writeVariable(policy.d_name, &policy); + luaCtx.writeVariable(policy->d_name, policy); } #endif /* DISABLE_POLICIES_BINDINGS */ diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index 1094be77a5..f261f453d2 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -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& 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& policy, const string& pool) { setLuaSideEffect(); auto localPools = g_pools.getCopy(); - setPoolPolicy(localPools, pool, std::make_shared(std::move(policy))); + setPoolPolicy(localPools, pool, std::make_shared(*policy)); g_pools.setState(localPools); }); diff --git a/regression-tests.dnsdist/test_Routing.py b/regression-tests.dnsdist/test_Routing.py index d0ceeafd97..1dabca1a74 100644 --- a/regression-tests.dnsdist/test_Routing.py +++ b/regression-tests.dnsdist/test_Routing.py @@ -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):