mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-30 22:15:20 +00:00
Merge branch 'mnowak/pytest_rewrite_xferquota' into 'main'
Rewrite xferquota system test to pytest See merge request isc-projects/bind9!8676
This commit is contained in:
@@ -13,6 +13,7 @@ from . import check
|
||||
from . import instance
|
||||
from . import query
|
||||
from . import rndc
|
||||
from . import run
|
||||
from . import log
|
||||
|
||||
# isctest.mark module is intentionally NOT imported, because it relies on
|
||||
|
@@ -32,3 +32,10 @@ def rcode(message: dns.message.Message, expected_rcode) -> None:
|
||||
|
||||
def noerror(message: dns.message.Message) -> None:
|
||||
rcode(message, dns_rcode.NOERROR)
|
||||
|
||||
|
||||
def rrsets_equal(first_rrset: dns.rrset.RRset, second_rrset: dns.rrset.RRset) -> None:
|
||||
for rr in first_rrset:
|
||||
assert rr in second_rrset
|
||||
for rr in second_rrset:
|
||||
assert rr in first_rrset
|
||||
|
@@ -9,7 +9,7 @@
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
from typing import Iterator, Optional, TextIO, Dict, Any
|
||||
from typing import Iterator, Optional, TextIO, Dict, Any, Union, Pattern
|
||||
|
||||
import abc
|
||||
import os
|
||||
@@ -100,8 +100,9 @@ class WatchLog(abc.ABC):
|
||||
Block execution until a line containing the provided `string` appears
|
||||
in the log file. Return `None` once the line is found or raise a
|
||||
`TimeoutError` after `timeout` seconds (default: 10) if `string` does
|
||||
not appear in the log file. (Catching this exception is discouraged as
|
||||
it indicates that the test code did not behave as expected.)
|
||||
not appear in the log file (strings and regular expressions are
|
||||
supported). (Catching this exception is discouraged as it indicates
|
||||
that the test code did not behave as expected.)
|
||||
|
||||
Recommended use:
|
||||
|
||||
@@ -156,7 +157,9 @@ class WatchLog(abc.ABC):
|
||||
"""
|
||||
return self._wait_for({string: None}, timeout)
|
||||
|
||||
def wait_for_lines(self, strings: Dict[str, Any], timeout: int = 10) -> None:
|
||||
def wait_for_lines(
|
||||
self, strings: Dict[Union[str, Pattern], Any], timeout: int = 10
|
||||
) -> None:
|
||||
"""
|
||||
Block execution until a line of interest appears in the log file. This
|
||||
function is a "multi-match" variant of `wait_for_line()` which is
|
||||
@@ -165,10 +168,11 @@ class WatchLog(abc.ABC):
|
||||
|
||||
`strings` is a `dict` associating each string to look for with the
|
||||
value this function should return when that string is found in the log
|
||||
file. If none of the `strings` being looked for appear in the log file
|
||||
after `timeout` seconds, a `TimeoutError` is raised.
|
||||
(Catching this exception is discouraged as it indicates that the test
|
||||
code did not behave as expected.)
|
||||
file (strings and regular expressions are supported). If none of the
|
||||
`strings` being looked for appear in the log file after `timeout`
|
||||
seconds, a `TimeoutError` is raised. (Catching this exception is
|
||||
discouraged as it indicates that the test code did not behave as
|
||||
expected.)
|
||||
|
||||
Since `strings` is a `dict` and preserves key order (in CPython 3.6 as
|
||||
implementation detail, since 3.7 by language design), each line is
|
||||
@@ -212,7 +216,7 @@ class WatchLog(abc.ABC):
|
||||
"""
|
||||
return self._wait_for(strings, timeout)
|
||||
|
||||
def _wait_for(self, strings: Dict[str, Any], timeout: int) -> Any:
|
||||
def _wait_for(self, patterns: Dict[Union[str, Pattern], Any], timeout: int) -> Any:
|
||||
"""
|
||||
Block execution until one of the `strings` being looked for appears in
|
||||
the log file. Raise a `TimeoutError` if none of the `strings` being
|
||||
@@ -234,13 +238,15 @@ class WatchLog(abc.ABC):
|
||||
else:
|
||||
line = leftover + line
|
||||
leftover = ""
|
||||
for string, retval in strings.items():
|
||||
if string in line:
|
||||
for string, retval in patterns.items():
|
||||
if isinstance(string, Pattern) and string.search(line):
|
||||
return retval
|
||||
if isinstance(string, str) and string in line:
|
||||
return retval
|
||||
time.sleep(0.1)
|
||||
raise TimeoutError(
|
||||
"Timeout reached watching {} for {}".format(
|
||||
self._path, list(strings.keys())
|
||||
self._path, list(patterns.keys())
|
||||
)
|
||||
)
|
||||
|
||||
|
@@ -9,6 +9,15 @@
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
import time
|
||||
|
||||
def test_xferquota(run_tests_sh):
|
||||
run_tests_sh()
|
||||
|
||||
def retry_with_timeout(func, timeout, delay=1, msg=None):
|
||||
start_time = time.time()
|
||||
while time.time() < start_time + timeout:
|
||||
if func():
|
||||
return
|
||||
time.sleep(delay)
|
||||
if msg is None:
|
||||
msg = f"{func.__module__}.{func.__qualname__} timed out after {timeout} s"
|
||||
assert False, msg
|
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
#
|
||||
# Set up test data for zone transfer quota tests.
|
||||
#
|
||||
use FileHandle;
|
||||
|
||||
my $priconf = new FileHandle("ns1/zones.conf", "w") or die;
|
||||
my $secconf = new FileHandle("ns2/zones.conf", "w") or die;
|
||||
|
||||
for ($z = 0; $z < 300; $z++) {
|
||||
my $zn = sprintf("zone%06d.example", $z);
|
||||
print $priconf "zone \"$zn\" { type primary; file \"$zn.db\"; };\n";
|
||||
print $secconf "zone \"$zn\" { type secondary; file \"$zn.bk\"; masterfile-format text; primaries { 10.53.0.1; }; };\n";
|
||||
my $fn = "ns1/$zn.db";
|
||||
my $f = new FileHandle($fn, "w") or die "open: $fn: $!";
|
||||
print $f "\$TTL 300
|
||||
\@ IN SOA ns1 . 1 300 120 3600 86400
|
||||
NS ns1
|
||||
NS ns2
|
||||
ns1 A 10.53.0.1
|
||||
ns2 A 10.53.0.2
|
||||
MX 10 mail1.isp.example.
|
||||
MX 20 mail2.isp.example.
|
||||
www A 10.0.0.1
|
||||
xyzzy A 10.0.0.2
|
||||
";
|
||||
$f->close;
|
||||
}
|
46
bin/tests/system/xferquota/setup.py
Normal file
46
bin/tests/system/xferquota/setup.py
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
#
|
||||
# Set up test data for zone transfer quota tests.
|
||||
#
|
||||
|
||||
zones = 300
|
||||
|
||||
for z in range(zones):
|
||||
zn = f"zone{z:06d}.example"
|
||||
with open(f"ns1/{zn}.db", "w", encoding="utf-8") as f:
|
||||
f.write(
|
||||
"""$TTL 300
|
||||
@ IN SOA ns1 . 1 300 120 3600 86400
|
||||
NS ns1
|
||||
NS ns2
|
||||
ns1 A 10.53.0.1
|
||||
ns2 A 10.53.0.2
|
||||
MX 10 mail1.isp.example.
|
||||
MX 20 mail2.isp.example.
|
||||
www A 10.0.0.1
|
||||
xyzzy A 10.0.0.2
|
||||
"""
|
||||
)
|
||||
|
||||
with open("ns1/zones.conf", "w", encoding="utf-8") as priconf, open(
|
||||
"ns2/zones.conf", "w", encoding="utf-8"
|
||||
) as secconf:
|
||||
for z in range(zones):
|
||||
zn = f"zone{z:06d}.example"
|
||||
priconf.write(f'zone "{zn}" {{ type primary; file "{zn}.db"; }};\n')
|
||||
secconf.write(
|
||||
f'zone "{zn}" {{ type secondary; file "{zn}.bk"; '
|
||||
f"masterfile-format text; primaries {{ 10.53.0.1; }}; }};\n"
|
||||
)
|
@@ -17,7 +17,7 @@
|
||||
|
||||
. ../conf.sh
|
||||
|
||||
$PERL setup.pl
|
||||
$PYTHON setup.py
|
||||
|
||||
cp -f ns1/changing1.db ns1/changing.db
|
||||
|
||||
|
@@ -1,61 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
set -e
|
||||
|
||||
. ../conf.sh
|
||||
|
||||
DIGOPTS="+tcp +noadd +nosea +nostat +noquest +nocomm +nocmd -p ${PORT}"
|
||||
RNDCCMD="$RNDC -c ../_common/rndc.conf -p ${CONTROLPORT} -s"
|
||||
|
||||
#
|
||||
# Perform tests
|
||||
#
|
||||
|
||||
count=0
|
||||
ticks=0
|
||||
while [ $count != 300 ]; do
|
||||
if [ $ticks = 1 ]; then
|
||||
echo_i "Changing test zone..."
|
||||
cp -f ns1/changing2.db ns1/changing.db
|
||||
kill -HUP $(cat ns1/named.pid)
|
||||
fi
|
||||
sleep 1
|
||||
ticks=$((ticks + 1))
|
||||
seconds=$((ticks * 1))
|
||||
if [ $ticks = 360 ]; then
|
||||
echo_i "Took too long to load zones"
|
||||
exit 1
|
||||
fi
|
||||
count=$(cat ns2/zone*.bk | grep xyzzy | wc -l)
|
||||
echo_i "Have $count zones up in $seconds seconds"
|
||||
done
|
||||
|
||||
status=0
|
||||
|
||||
$DIG $DIGOPTS zone000099.example. @10.53.0.1 axfr >dig.out.ns1 || status=1
|
||||
|
||||
$DIG $DIGOPTS zone000099.example. @10.53.0.2 axfr >dig.out.ns2 || status=1
|
||||
|
||||
digcomp dig.out.ns1 dig.out.ns2 || status=1
|
||||
|
||||
sleep 15
|
||||
|
||||
$DIG $DIGOPTS a.changing. @10.53.0.1 a >dig.out.ns1 || status=1
|
||||
|
||||
$DIG $DIGOPTS a.changing. @10.53.0.2 a >dig.out.ns2 || status=1
|
||||
|
||||
digcomp dig.out.ns1 dig.out.ns2 || status=1
|
||||
|
||||
echo_i "exit status: $status"
|
||||
[ $status -eq 0 ] || exit 1
|
65
bin/tests/system/xferquota/tests_xferquota.py
Normal file
65
bin/tests/system/xferquota/tests_xferquota.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import signal
|
||||
|
||||
import isctest
|
||||
|
||||
import dns.message
|
||||
|
||||
|
||||
def test_xferquota(named_port, servers):
|
||||
# Changing test zone
|
||||
shutil.copyfile("ns1/changing2.db", "ns1/changing.db")
|
||||
with open("ns1/named.pid", "r", encoding="utf-8") as pidfile:
|
||||
pid = int(pidfile.read())
|
||||
os.kill(pid, signal.SIGHUP)
|
||||
with servers["ns1"].watch_log_from_start() as watcher:
|
||||
watcher.wait_for_line("received SIGHUP signal to reload zones")
|
||||
|
||||
def check_line_count():
|
||||
matching_line_count = 0
|
||||
# Iterate through zone files and count matching lines
|
||||
for file_path in glob.glob("ns2/zone000*.example.bk"):
|
||||
with open(file_path, "r", encoding="utf-8") as zonefile:
|
||||
# Count the number of lines containing the search string
|
||||
for line in zonefile:
|
||||
if "xyzzy A 10.0.0.2" in line:
|
||||
matching_line_count += 1
|
||||
return matching_line_count == 300
|
||||
|
||||
isctest.run.retry_with_timeout(check_line_count, timeout=360)
|
||||
|
||||
axfr_msg = dns.message.make_query("zone000099.example.", "AXFR")
|
||||
a_msg = dns.message.make_query("a.changing.", "A")
|
||||
|
||||
def query_and_compare(msg):
|
||||
ns1response = isctest.query.tcp(msg, "10.53.0.1")
|
||||
ns2response = isctest.query.tcp(msg, "10.53.0.2")
|
||||
isctest.check.noerror(ns1response)
|
||||
isctest.check.noerror(ns2response)
|
||||
isctest.check.rrsets_equal(ns1response.answer, ns2response.answer)
|
||||
|
||||
query_and_compare(axfr_msg)
|
||||
pattern = re.compile(
|
||||
f"transfer of 'changing/IN' from 10.53.0.1#{named_port}: "
|
||||
f"Transfer completed: .*\\(serial 2\\)"
|
||||
)
|
||||
with servers["ns2"].watch_log_from_start() as watcher:
|
||||
watcher.wait_for_line(
|
||||
pattern,
|
||||
timeout=30,
|
||||
)
|
||||
query_and_compare(a_msg)
|
Reference in New Issue
Block a user