2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-28 21:17:54 +00:00

Use context managers as suggested by PyLint 2.8.2

PyLint 2.8.2 reports the following suggestions for two Python scripts
used in the system test suite:

    ************* Module tests_rndc_deadlock
    bin/tests/system/addzone/tests_rndc_deadlock.py:71:4: R1732: Consider using 'with' for resource-allocating operations (consider-using-with)
    ************* Module tests-shutdown
    bin/tests/system/shutdown/tests-shutdown.py:68:4: R1732: Consider using 'with' for resource-allocating operations (consider-using-with)
    bin/tests/system/shutdown/tests-shutdown.py:154:8: R1732: Consider using 'with' for resource-allocating operations (consider-using-with)

Implement the above suggestions by using
concurrent.futures.ThreadPoolExecutor() and subprocess.Popen() as
context managers.
This commit is contained in:
Michał Kępień 2021-05-18 10:53:17 +02:00
parent 71284cb496
commit a8163551ed
2 changed files with 95 additions and 103 deletions

View File

@ -68,23 +68,22 @@ def test_rndc_deadlock():
test_state = {'finished': False} test_state = {'finished': False}
# Create 4 worker threads running "rndc" commands in a loop. # Create 4 worker threads running "rndc" commands in a loop.
executor = concurrent.futures.ThreadPoolExecutor() with concurrent.futures.ThreadPoolExecutor() as executor:
for i in range(1, 5): for i in range(1, 5):
domain = 'example%d' % i domain = 'example%d' % i
executor.submit(rndc_loop, test_state, domain) executor.submit(rndc_loop, test_state, domain)
# Run "rndc status" in 1-second intervals for a maximum of 10 seconds. If # Run "rndc status" in 1-second intervals for a maximum of 10 seconds.
# any "rndc status" command fails, the loop will be interrupted. # If any "rndc status" command fails, the loop will be interrupted.
server_is_responsive = True server_is_responsive = True
attempts = 10 attempts = 10
while server_is_responsive and attempts > 0: while server_is_responsive and attempts > 0:
server_is_responsive = check_if_server_is_responsive() server_is_responsive = check_if_server_is_responsive()
attempts -= 1 attempts -= 1
time.sleep(1) time.sleep(1)
# Signal worker threads that the test is finished. # Signal worker threads that the test is finished.
test_state['finished'] = True test_state['finished'] = True
executor.shutdown()
# Check whether all "rndc status" commands succeeded. # Check whether all "rndc status" commands succeeded.
assert server_is_responsive assert server_is_responsive

View File

@ -65,65 +65,63 @@ def do_work(named_proc, resolver, rndc_cmd, kill_method, n_workers, n_queries):
# We're going to execute queries in parallel by means of a thread pool. # We're going to execute queries in parallel by means of a thread pool.
# dnspython functions block, so we need to circunvent that. # dnspython functions block, so we need to circunvent that.
executor = ThreadPoolExecutor(n_workers + 1) with ThreadPoolExecutor(n_workers + 1) as executor:
# Helper dict, where keys=Future objects and values are tags used # Helper dict, where keys=Future objects and values are tags used
# to process results later. # to process results later.
futures = {} futures = {}
# 50% of work will be A queries.
# 1 work will be rndc stop.
# Remaining work will be rndc status (so we test parallel control
# connections that were crashing named).
shutdown = True
for i in range(n_queries):
if i < (n_queries // 2):
# Half work will be standard A queries.
# Among those we split 50% queries relname='www',
# 50% queries relname=random characters
if random.randrange(2) == 1:
tag = "good"
relname = "www"
else:
tag = "bad"
length = random.randint(4, 10)
relname = "".join(letters[
random.randrange(len(letters))] for i in range(length))
qname = relname + ".test"
futures[executor.submit(resolver.query, qname, 'A')] = tag
elif shutdown: # We attempt to stop named in the middle
shutdown = False
if kill_method == "rndc":
futures[executor.submit(launch_rndc, ['stop'])] = 'stop'
else:
futures[executor.submit(named_proc.terminate)] = 'kill'
# 50% of work will be A queries.
# 1 work will be rndc stop.
# Remaining work will be rndc status (so we test parallel control
# connections that were crashing named).
shutdown = True
for i in range(n_queries):
if i < (n_queries // 2):
# Half work will be standard A queries.
# Among those we split 50% queries relname='www',
# 50% queries relname=random characters
if random.randrange(2) == 1:
tag = "good"
relname = "www"
else: else:
tag = "bad" # We attempt to send couple rndc commands while named is
length = random.randint(4, 10) # being shutdown
relname = "".join(letters[ futures[executor.submit(launch_rndc, ['status'])] = 'status'
random.randrange(len(letters))] for i in range(length))
qname = relname + ".test" ret_code = -1
futures[executor.submit(resolver.query, qname, 'A')] = tag for future in as_completed(futures):
elif shutdown: # We attempt to stop named in the middle try:
shutdown = False result = future.result()
if kill_method == "rndc": # If tag is "stop", result is an instance of
futures[executor.submit(launch_rndc, ['stop'])] = 'stop' # subprocess.CompletedProcess, then we check returncode
else: # attribute to know if rncd stop command finished successfully.
futures[executor.submit(named_proc.terminate)] = 'kill' #
# if tag is "kill" then the main function will check if
# named process exited gracefully after SIGTERM signal.
if futures[future] == "stop":
ret_code = result
else: except (dns.resolver.NXDOMAIN, dns.exception.Timeout):
# We attempt to send couple rndc commands while named is pass
# being shutdown
futures[executor.submit(launch_rndc, ['status'])] = 'status'
ret_code = -1 if kill_method == "rndc":
for future in as_completed(futures): assert ret_code == 0
try:
result = future.result()
# If tag is "stop", result is an instance of
# subprocess.CompletedProcess, then we check returncode
# attribute to know if rncd stop command finished successfully.
#
# if tag is "kill" then the main function will check if
# named process exited gracefully after SIGTERM signal.
if futures[future] == "stop":
ret_code = result
except (dns.resolver.NXDOMAIN, dns.exception.Timeout):
pass
if kill_method == "rndc":
assert ret_code == 0
executor.shutdown()
@pytest.mark.dnspython @pytest.mark.dnspython
@ -149,14 +147,6 @@ def test_named_shutdown(named_port, control_port):
rndc_cmd = [rndc, "-c", rndc_cfg, "-p", str(control_port), rndc_cmd = [rndc, "-c", rndc_cfg, "-p", str(control_port),
"-s", "10.53.0.3"] "-s", "10.53.0.3"]
# Helper function, launch named without blocking.
def launch_named():
proc = subprocess.Popen([named, "-c", cfg_file, "-f"], cwd=cfg_dir)
# Ensure named is running
assert proc.poll() is None
return proc
# We create a resolver instance that will be used to send queries. # We create a resolver instance that will be used to send queries.
resolver = dns.resolver.Resolver() resolver = dns.resolver.Resolver()
resolver.nameservers = ['10.53.0.3'] resolver.nameservers = ['10.53.0.3']
@ -167,38 +157,41 @@ def test_named_shutdown(named_port, control_port):
# Method 2: killing with SIGTERM # Method 2: killing with SIGTERM
# In both methods named should exit gracefully. # In both methods named should exit gracefully.
for kill_method in ("rndc", "sigterm"): for kill_method in ("rndc", "sigterm"):
named_proc = launch_named() named_cmdline = [named, "-c", cfg_file, "-f"]
# wait for named to finish loading with subprocess.Popen(named_cmdline, cwd=cfg_dir) as named_proc:
for _ in range(10): # Ensure named is running
try: assert named_proc.poll() is None
resolver.query('version.bind', 'TXT', 'CH') # wait for named to finish loading
break for _ in range(10):
except (dns.resolver.NoNameservers, dns.exception.Timeout): try:
time.sleep(1) resolver.query('version.bind', 'TXT', 'CH')
break
except (dns.resolver.NoNameservers, dns.exception.Timeout):
time.sleep(1)
do_work(named_proc, resolver, rndc_cmd, do_work(named_proc, resolver, rndc_cmd,
kill_method, n_workers=12, n_queries=16) kill_method, n_workers=12, n_queries=16)
# Wait named to exit for a maximum of MAX_TIMEOUT seconds. # Wait named to exit for a maximum of MAX_TIMEOUT seconds.
MAX_TIMEOUT = 10 MAX_TIMEOUT = 10
is_dead = False is_dead = False
for _ in range(MAX_TIMEOUT):
if named_proc.poll() is not None:
is_dead = True
break
time.sleep(1)
if not is_dead:
named_proc.send_signal(signal.SIGABRT)
for _ in range(MAX_TIMEOUT): for _ in range(MAX_TIMEOUT):
if named_proc.poll() is not None: if named_proc.poll() is not None:
is_dead = True is_dead = True
break break
time.sleep(1) time.sleep(1)
if not is_dead:
named_proc.kill()
assert is_dead if not is_dead:
# Ensures that named exited gracefully. named_proc.send_signal(signal.SIGABRT)
# If it crashed (abort()) exitcode will be non zero. for _ in range(MAX_TIMEOUT):
assert named_proc.returncode == 0 if named_proc.poll() is not None:
is_dead = True
break
time.sleep(1)
if not is_dead:
named_proc.kill()
assert is_dead
# Ensures that named exited gracefully.
# If it crashed (abort()) exitcode will be non zero.
assert named_proc.returncode == 0