xref: /netbsd-src/external/mpl/bind/dist/bin/tests/system/addzone/tests_rndc_deadlock.py (revision 9689912e6b171cbda866ec33f15ae94a04e2c02d)
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