From a4cdef85479ea3befa997563aa27708cf3f76c8c Mon Sep 17 00:00:00 2001 From: Ivan Sinyagovskiy Date: Fri, 3 Apr 2026 16:18:39 +0100 Subject: [PATCH 1/2] feat: enhance show current with generator ACL and interface filtering Add -g, -i, and --acl flags to `annet show current`: - Default behaviour unchanged: shows full device config - --acl: applies all generator ACLs to scope output to managed config - -g / -i: imply --acl, further scope output to specific generators or interfaces Remove old_raw and _gen_current_items (no remaining callers) and reuse existing old_new with no_new=True, eliminating duplicated fetch logic. Co-Authored-By: Claude Sonnet 4.6 --- annet/cli.py | 73 +++++++++++++++++++++-------------------------- annet/cli_args.py | 7 +++++ annet/gen.py | 54 ++--------------------------------- 3 files changed, 43 insertions(+), 91 deletions(-) diff --git a/annet/cli.py b/annet/cli.py index aa2d85b7..ab6c4ea8 100644 --- a/annet/cli.py +++ b/annet/cli.py @@ -20,7 +20,8 @@ from annet.argparse import ArgParser, subcommand from annet.deploy import get_deployer, get_fetcher from annet.diff import gen_sort_diff -from annet.gen import Loader, old_raw +from annet.gen import Loader, format_config_blocks, old_new +from annet import patching from annet.lib import get_context_path, repair_context_file from annet.output import OutputDriver, output_driver_connector from annet.storage import Device, get_storage @@ -41,32 +42,6 @@ def list_subcommands(): return globals().copy() -def _gen_current_items( - config, - stdin, - loader: Loader, - output_driver: OutputDriver, - gen_args: cli_args.GenOptions, -) -> Iterable[Tuple[str, str, bool]]: - for device, result in old_raw( - args=gen_args, - loader=loader, - config=config, - stdin=stdin, - do_files_download=True, - use_mesh=False, - ): - if device.hw.vendor != "pc": - destname = output_driver.cfg_file_names(device)[0] - yield (destname, result, False) - else: - for entire_path, entire_data in sorted(result.items(), key=operator.itemgetter(0)): - if entire_data is None: - entire_data = "" - destname = output_driver.entire_config_dest_path(device, entire_path) - yield (destname, entire_data, False) - - @contextmanager def get_loader(gen_args: cli_args.GenOptions, args: cli_args.QueryOptionsBase): exit_stack = ExitStack() @@ -84,23 +59,41 @@ def show(): pass -@subcommand(cli_args.QueryOptions, cli_args.opt_config, cli_args.FileOutOptions, parent=show) -def show_current(args: cli_args.QueryOptions, config, arg_out: cli_args.FileOutOptions) -> None: - """Show current devices' configuration""" - gen_args = cli_args.GenOptions(args, no_acl=True) +@subcommand(cli_args.ShowCurrentOptions, parent=show) +def show_current(args: cli_args.ShowCurrentOptions) -> None: + """Show current devices' configuration, optionally filtered by generator ACL (-g) and interfaces (-i)""" + args.no_acl = not (args.acl or args.allowed_gens or args.filter_ifaces) + filterer = filtering.filterer_connector.get() output_driver = output_driver_connector.get() - with get_loader(gen_args, args) as loader: + with get_loader(args, args) as loader: if not loader.devices: get_logger().error("No devices found for %s", args.query) - items = _gen_current_items( - loader=loader, - output_driver=output_driver, - gen_args=gen_args, - stdin=args.stdin(config=config), - config=config, - ) - output_driver.write_output(arg_out, items, len(loader.devices)) + def items() -> Iterable[Tuple[str, str, bool]]: + for res in old_new( + args, + config=args.config, + loader=loader, + filterer=filterer, + no_new=True, + add_implicit=False, + do_files_download=True, + ): + if res.err: + raise res.err + device = res.device + if not device.is_pc(): + if res.old: + orderer = patching.Orderer.from_hw(device.hw) + old_text = format_config_blocks(orderer.order_config(res.old), device.hw, args.indent) + yield output_driver.cfg_file_names(device)[0], old_text, False + else: + for entire_path, entire_data in sorted(res.old_files.items()): + if entire_data is None: + entire_data = "" + yield output_driver.entire_config_dest_path(device, entire_path), entire_data, False + + output_driver.write_output(args, items(), len(loader.devices)) @subcommand(cli_args.QueryOptions, cli_args.FileOutOptions, parent=show) diff --git a/annet/cli_args.py b/annet/cli_args.py index 532c4248..e146d759 100644 --- a/annet/cli_args.py +++ b/annet/cli_args.py @@ -103,6 +103,8 @@ def opt_query_factory(**kwargs): opt_no_acl = Arg("--no-acl", default=False, help="Disable ACL for config generation") +opt_acl = Arg("--acl", default=False, help="Apply generator ACL to filter output (implied by -g and -i)") + opt_no_acl_exclusive = Arg( "--no-acl-exclusive", default=False, help="Check that ACLs of the executed generators do not intersect" ) @@ -417,6 +419,11 @@ class ShowGenOptions(GenOptions, FileOutOptions): show_hosts_progress = opt_show_hosts_progress +class ShowCurrentOptions(ShowGenOptions): + config = opt_config + acl = opt_acl + + class ShowDiffOptions(DiffOptions, FileOutOptions): indent = opt_indent show_rules = opt_show_rules diff --git a/annet/gen.py b/annet/gen.py index f0b5dbfe..018a9930 100644 --- a/annet/gen.py +++ b/annet/gen.py @@ -23,13 +23,10 @@ ) import tabulate -from contextlog import get_logger - from annet import generators, implicit, patching, rulebook, tracing -from annet.annlib import jsontools from annet.annlib.rbparser.acl import compile_acl_text -from annet.cli_args import DeployOptions, GenOptions, ShowGenOptions -from annet.deploy import get_fetcher, scrub_config +from annet.cli_args import GenOptions, ShowGenOptions +from annet.deploy import get_fetcher from annet.filtering import Filterer from annet.generators import ( BaseGenerator, @@ -46,7 +43,7 @@ from annet.tracing import tracing_connector from annet.types import OldNewResult from annet.vendors import registry_connector, tabparser - +from contextlog import get_logger # Вывод всех генераторов вместе. # Значение такое же, как для аналогичной константы в ЧК. @@ -425,51 +422,6 @@ def old_new( yield result -@tracing.function -def old_raw( - args: GenOptions, - loader: Loader, - config, - stdin=None, - do_files_download=False, - use_mesh=True, -) -> Iterable[Tuple[Device, Union[str, Dict[str, str]]]]: - device_gens = loader.resolve_gens(loader.devices) - running, failed_running = _old_resolve_running(config, loader.devices) - downloaded_files, failed_files = _old_resolve_files(config, loader.devices, device_gens, do_files_download) - if stdin is None: - stdin = args.stdin(filter_acl=args.filter_acl, config=config) - ctx = OldNewDeviceContext( - config=config, - args=args, - downloaded_files=split_downloaded_files_multi_device(downloaded_files, device_gens, loader.devices), - failed_files=failed_files, - running=running, - failed_running=failed_running, - stdin=stdin, - do_files_download=do_files_download, - device_count=len(loader.devices), - no_new=True, - add_annotations=False, - add_implicit=False, - gens=DeviceGenerators(), - fetched_packages={}, - failed_packages={}, - do_print_perf=True, - ) - for device in loader.devices: - if not device.is_pc(): - config = _old_new_get_config_cli(ctx, device) - config = scrub_config(config, device.breed) - yield device, config - else: - files = _old_new_get_config_files(ctx, device) - if files.entire_files: - yield device, files.entire_files - if files.json_fragment_files: - yield device, {path: jsontools.format_json(data) for path, data in files.json_fragment_files.items()} - - @tracing.function def worker( device_id, args: ShowGenOptions, stdin, loader: "Loader", filterer: Filterer From 8cd43c6fd5ddcca5792a294054939d681d7ff9d4 Mon Sep 17 00:00:00 2001 From: Ivan Sinyagovskiy Date: Sat, 4 Apr 2026 11:48:49 +0100 Subject: [PATCH 2/2] fix: ruff import ordering Co-Authored-By: Claude Sonnet 4.6 --- annet/cli.py | 3 +-- annet/gen.py | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/annet/cli.py b/annet/cli.py index ab6c4ea8..aab530ed 100644 --- a/annet/cli.py +++ b/annet/cli.py @@ -15,13 +15,12 @@ from contextlog import get_logger from valkit.python import valid_logging_level -from annet import api, cli_args, filtering, generators +from annet import api, cli_args, filtering, generators, patching from annet.api import Deployer, collapse_texts from annet.argparse import ArgParser, subcommand from annet.deploy import get_deployer, get_fetcher from annet.diff import gen_sort_diff from annet.gen import Loader, format_config_blocks, old_new -from annet import patching from annet.lib import get_context_path, repair_context_file from annet.output import OutputDriver, output_driver_connector from annet.storage import Device, get_storage diff --git a/annet/gen.py b/annet/gen.py index 018a9930..401a6475 100644 --- a/annet/gen.py +++ b/annet/gen.py @@ -23,6 +23,8 @@ ) import tabulate +from contextlog import get_logger + from annet import generators, implicit, patching, rulebook, tracing from annet.annlib.rbparser.acl import compile_acl_text from annet.cli_args import GenOptions, ShowGenOptions @@ -43,7 +45,7 @@ from annet.tracing import tracing_connector from annet.types import OldNewResult from annet.vendors import registry_connector, tabparser -from contextlog import get_logger + # Вывод всех генераторов вместе. # Значение такое же, как для аналогичной константы в ЧК.