1# Copyright (C) Internet Systems Consortium, Inc. ("ISC") 2# 3# SPDX-License-Identifier: MPL-2.0 4# 5# This Source Code Form is subject to the terms of the Mozilla Public 6# License, v. 2.0. If a copy of the MPL was not distributed with this 7# file, you can obtain one at https://mozilla.org/MPL/2.0/. 8# 9# See the COPYRIGHT file distributed with this work for additional 10# information regarding copyright ownership. 11 12import concurrent.futures 13import time 14 15import pytest 16 17import isctest 18 19pytestmark = pytest.mark.extra_artifacts( 20 [ 21 "ns*/*.nzf*", 22 "ns*/*.nzd*", 23 "ns1/redirect.db", 24 "ns2/new-zones", 25 "ns2/redirect.db", 26 "ns3/redirect.db", 27 ] 28) 29 30 31def rndc_loop(test_state, domain, ns3): 32 """ 33 Run "rndc addzone", "rndc modzone", and "rndc delzone" in a tight loop 34 until the test is considered finished, ignoring errors 35 """ 36 rndc_commands = [ 37 ["addzone", domain, '{ type primary; file "example.db"; };'], 38 [ 39 "modzone", 40 domain, 41 '{ type primary; file "example.db"; allow-transfer { any; }; };', 42 ], 43 ["delzone", domain], 44 ] 45 46 while not test_state["finished"]: 47 for command in rndc_commands: 48 ns3.rndc(" ".join(command), ignore_errors=True, log=False) 49 50 51def check_if_server_is_responsive(ns3): 52 """ 53 Check if server status can be successfully retrieved using "rndc status" 54 """ 55 try: 56 ns3.rndc("status", log=False) 57 return True 58 except isctest.rndc.RNDCException: 59 return False 60 61 62def test_rndc_deadlock(servers): 63 """ 64 Test whether running "rndc addzone", "rndc modzone", and "rndc delzone" 65 commands concurrently does not trigger a deadlock 66 """ 67 test_state = {"finished": False} 68 ns3 = servers["ns3"] 69 70 # Create 4 worker threads running "rndc" commands in a loop. 71 with concurrent.futures.ThreadPoolExecutor() as executor: 72 for i in range(1, 5): 73 domain = "example%d" % i 74 executor.submit(rndc_loop, test_state, domain, ns3) 75 76 # Run "rndc status" 10 times, with 1-second pauses between attempts. 77 # Each "rndc status" invocation has a timeout of 10 seconds. If any of 78 # them fails, the loop will be interrupted. 79 server_is_responsive = True 80 attempts = 10 81 while server_is_responsive and attempts > 0: 82 server_is_responsive = check_if_server_is_responsive(ns3) 83 attempts -= 1 84 time.sleep(1) 85 86 # Signal worker threads that the test is finished. 87 test_state["finished"] = True 88 89 # Check whether all "rndc status" commands succeeded. 90 assert server_is_responsive 91