mirror of
https://github.com/isc-projects/bind9.git
synced 2026-06-13 19:19:59 -04:00
To unify the command handling, utilize EnvCmd() to handle rndc commands:
1. Remove isctest.rndc abstractions. They were intended for an upcoming
python-only implementation. A couple of years later, it doesn't seem
to be coming any time soon, so let's stick with the interface that
makes sense today, i.e. use the same command handling interface
everywhere.
2. Remove the specialized rndc.log in favor of the generic logging
already implemented by isctest.run.cmd(). I believe the cause of the
many rndc(log=False) invocations was that nobody wanted this extra
file. Yet, logging everything by default makes sense for debugging,
unless there's a good reason not to. In almost all cases, logging was
switched to the default (enabled).
3. With the NamedInstance.rndc() call now returning CmdResult rather
than combined stdout+stderr string, adjust all the invocations to use
`.out` or `.err` as necessary.
4. Replace some manual rndc invocation and its base argument
construction with the standardized nsX.rndc() call.
5. In cases where rndc is expected to fail, utilize
raise_on_exception=False and check the `.rc` from the result, rather
than handling an exception.
6. In addzone/tests_rndc_deadlock.py, refactor the test slightly to
avoid using EnvCmd() entirely to avoid spamming the logs. This test
calls rndc in a loop from multiple threads and such test case is an
exception which doesn't warrant changing the `isctest.run.cmd()`
implementation.
(cherry picked from commit f33e2b6d87)
111 lines
3.6 KiB
Python
111 lines
3.6 KiB
Python
#!/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.
|
|
|
|
from typing import NamedTuple
|
|
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
|
|
from .log import WatchLogFromStart, WatchLogFromHere
|
|
from .run import CmdResult, EnvCmd
|
|
from .text import TextFile
|
|
|
|
|
|
class NamedPorts(NamedTuple):
|
|
dns: int = 53
|
|
rndc: int = 953
|
|
|
|
|
|
class NamedInstance:
|
|
"""
|
|
A class representing a `named` instance used in a system test.
|
|
|
|
This class is expected to be instantiated as part of the `servers` fixture:
|
|
|
|
```python
|
|
def test_foo(servers):
|
|
servers["ns1"].rndc("status")
|
|
```
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
identifier: str,
|
|
ports: NamedPorts = NamedPorts(),
|
|
) -> None:
|
|
"""
|
|
`identifier` must be an `ns<X>` string, where `<X>` is an integer
|
|
identifier of the `named` instance this object should represent.
|
|
|
|
`ports` is the `NamedPorts` instance listing the UDP/TCP ports on which
|
|
this `named` instance is listening for various types of traffic (both
|
|
DNS traffic and RNDC commands).
|
|
"""
|
|
self.ip = self._identifier_to_ip(identifier)
|
|
self.ports = ports
|
|
self.log = TextFile(os.path.join(identifier, "named.run"))
|
|
|
|
self._rndc_conf = Path("../_common/rndc.conf").absolute()
|
|
self._rndc = EnvCmd("RNDC", self.rndc_args)
|
|
|
|
@property
|
|
def rndc_args(self) -> str:
|
|
"""Base arguments for calling RNDC to control the instance."""
|
|
return f"-c {self._rndc_conf} -s {self.ip} -p {self.ports.rndc}"
|
|
|
|
@staticmethod
|
|
def _identifier_to_ip(identifier: str) -> str:
|
|
regex_match = re.match(r"^ns(?P<index>[0-9]{1,2})$", identifier)
|
|
if not regex_match:
|
|
raise ValueError("Invalid named instance identifier" + identifier)
|
|
return "10.53.0." + regex_match.group("index")
|
|
|
|
def rndc(self, command: str, timeout=10, **kwargs) -> CmdResult:
|
|
"""
|
|
Send `command` to this named instance using RNDC. Return the server's
|
|
response.
|
|
|
|
To suppress exceptions, redirect outputs, control logging change
|
|
timeout etc. use keyword arguments which are passed to
|
|
isctest.cmd.run().
|
|
"""
|
|
return self._rndc(command, timeout=timeout, **kwargs)
|
|
|
|
def watch_log_from_start(
|
|
self, timeout: float = WatchLogFromStart.DEFAULT_TIMEOUT
|
|
) -> WatchLogFromStart:
|
|
"""
|
|
Return an instance of the `WatchLogFromStart` context manager for this
|
|
`named` instance's log file.
|
|
"""
|
|
return WatchLogFromStart(self.log.path, timeout)
|
|
|
|
def watch_log_from_here(
|
|
self, timeout: float = WatchLogFromHere.DEFAULT_TIMEOUT
|
|
) -> WatchLogFromHere:
|
|
"""
|
|
Return an instance of the `WatchLogFromHere` context manager for this
|
|
`named` instance's log file.
|
|
"""
|
|
return WatchLogFromHere(self.log.path, timeout)
|
|
|
|
def reconfigure(self, **kwargs) -> CmdResult:
|
|
"""
|
|
Reconfigure this named `instance` and wait until reconfiguration is
|
|
finished.
|
|
"""
|
|
with self.watch_log_from_here() as watcher:
|
|
cmd = self.rndc("reconfig", **kwargs)
|
|
watcher.wait_for_line("any newly configured zones are now loaded")
|
|
return cmd
|