From: Aleš Mrázek Date: Tue, 14 Nov 2023 13:04:29 +0000 (+0100) Subject: kresctl: 'cache-clear' command created X-Git-Tag: v6.0.7~23^2~15 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d9bdb85a18977a0cd1bd475fcb99e4a17ebe1fda;p=thirdparty%2Fknot-resolver.git kresctl: 'cache-clear' command created --- diff --git a/manager/knot_resolver_manager/cli/cmd/cache_clear.py b/manager/knot_resolver_manager/cli/cmd/cache_clear.py new file mode 100644 index 000000000..de5546584 --- /dev/null +++ b/manager/knot_resolver_manager/cli/cmd/cache_clear.py @@ -0,0 +1,79 @@ +import argparse +import sys +from typing import Any, Dict, List, Tuple, Type + +from knot_resolver_manager.cli.command import Command, CommandArgs, CompWords, register_command +from knot_resolver_manager.datamodel.cache_schema import CacheClearRPCSchema +from knot_resolver_manager.utils.modeling.exceptions import AggregateDataValidationError, DataValidationError +from knot_resolver_manager.utils.modeling.parsing import DataFormat +from knot_resolver_manager.utils.requests import request + + +@register_command +class CacheClearCommand(Command): + def __init__(self, namespace: argparse.Namespace) -> None: + super().__init__(namespace) + + config_dict: Dict[str, Any] = {"exact-name": namespace.exact_name} + + if hasattr(namespace, "name"): + config_dict["name"] = namespace.name + if hasattr(namespace, "rr_type"): + config_dict["rr-type"] = namespace.rr_type + if hasattr(namespace, "chunk_size"): + config_dict["chunk-size"] = namespace.chunk_size + + try: + self.config = CacheClearRPCSchema(config_dict) + except (AggregateDataValidationError, DataValidationError) as e: + print(e, file=sys.stderr) + sys.exit(1) + + @staticmethod + def register_args_subparser( + subparser: "argparse._SubParsersAction[argparse.ArgumentParser]", + ) -> Tuple[argparse.ArgumentParser, "Type[Command]"]: + cache_clear = subparser.add_parser("cache-clear", help="Purge cache records matching specified criteria.") + cache_clear.set_defaults(exact_name=False) + cache_clear.add_argument( + "--exact-name", + help="If set, only records with the same name are removed.", + action="store_true", + dest="exact_name", + ) + cache_clear.add_argument( + "--rr-type", + help="Optional, you may additionally specify the type to remove, but that is only supported with '--exact-name' flag set.", + action="store", + type=str, + ) + cache_clear.add_argument( + "--chunk-size", + help="Optional, the number of records to remove in one round; default: 100." + " The purpose is not to block the resolver for long. The resolver repeats the command after one millisecond until all matching data are cleared.", + action="store", + type=int, + default=100, + ) + cache_clear.add_argument( + "name", + type=str, + nargs="?", + help="Optional, subtree to purge; if the name isn't provided, whole cache is purged (and any other parameters are disregarded).", + default=None, + ) + + return cache_clear, CacheClearCommand + + @staticmethod + def completion(args: List[str], parser: argparse.ArgumentParser) -> CompWords: + return {} + + def run(self, args: CommandArgs) -> None: + body: str = DataFormat.JSON.dict_dump(self.config.get_unparsed_data()) + response = request(args.socket, "POST", "cache-clear", body) + + if response.status != 200: + print(response, file=sys.stderr) + sys.exit(1) + print(response.body) diff --git a/manager/knot_resolver_manager/datamodel/cache_schema.py b/manager/knot_resolver_manager/datamodel/cache_schema.py index 4608ee5dd..580a031d0 100644 --- a/manager/knot_resolver_manager/datamodel/cache_schema.py +++ b/manager/knot_resolver_manager/datamodel/cache_schema.py @@ -4,10 +4,12 @@ from typing_extensions import Literal from knot_resolver_manager.datamodel.types import ( Dir, + DNSRecordTypeEnum, DomainName, EscapedStr, File, IntNonNegative, + IntPositive, Percent, SizeUnit, TimeUnit, @@ -16,6 +18,13 @@ from knot_resolver_manager.utils.modeling import ConfigSchema from knot_resolver_manager.utils.modeling.base_schema import lazy_default +class CacheClearRPCSchema(ConfigSchema): + name: Optional[DomainName] = None + exact_name: bool = False + rr_type: Optional[DNSRecordTypeEnum] = None + chunk_size: IntPositive = IntPositive(100) + + class PrefillSchema(ConfigSchema): """ Prefill the cache periodically by importing zone data obtained over HTTP. diff --git a/manager/knot_resolver_manager/utils/modeling/base_schema.py b/manager/knot_resolver_manager/utils/modeling/base_schema.py index b9b584c3c..06a6cc22b 100644 --- a/manager/knot_resolver_manager/utils/modeling/base_schema.py +++ b/manager/knot_resolver_manager/utils/modeling/base_schema.py @@ -642,7 +642,7 @@ class ObjectMapper: try: obj._validate() except ValueError as e: - raise DataValidationError(e.args[0] if len(e.args) > 0 else "Validation error", object_path) from e + raise DataValidationError(e.args[0] if len(e.args) > 0 else "Validation error", object_path or "/") from e class BaseSchema(Serializable):