From 36d9f97da981d9dae7d8b969a4dc22b447eb769e Mon Sep 17 00:00:00 2001 From: Dmitriy Zayakin Date: Mon, 28 Apr 2025 16:50:01 +0300 Subject: [PATCH] [#375] Added ape manager group command to grpc client Signed-off-by: Dmitriy Zayakin --- src/frostfs_testlib/cli/frostfs_cli/cli.py | 1 + .../grpc_operations/client_wrappers.py | 1 + .../implementations/__init__.py | 1 + .../implementations/ape_manager.py | 79 +++++++++++++++++++ .../implementations/container.py | 11 +++ .../grpc_operations/interfaces/__init__.py | 1 + .../grpc_operations/interfaces/ape_manager.py | 48 +++++++++++ .../grpc_operations/interfaces/container.py | 4 + .../grpc_operations/interfaces_wrapper.py | 4 + 9 files changed, 150 insertions(+) create mode 100644 src/frostfs_testlib/storage/grpc_operations/implementations/ape_manager.py create mode 100644 src/frostfs_testlib/storage/grpc_operations/interfaces/ape_manager.py diff --git a/src/frostfs_testlib/cli/frostfs_cli/cli.py b/src/frostfs_testlib/cli/frostfs_cli/cli.py index d83b7ae..7874f18 100644 --- a/src/frostfs_testlib/cli/frostfs_cli/cli.py +++ b/src/frostfs_testlib/cli/frostfs_cli/cli.py @@ -29,6 +29,7 @@ class FrostfsCli: util: FrostfsCliUtil version: FrostfsCliVersion control: FrostfsCliControl + ape_manager: FrostfsCliApeManager def __init__(self, shell: Shell, frostfs_cli_exec_path: str, config_file: Optional[str] = None): self.accounting = FrostfsCliAccounting(shell, frostfs_cli_exec_path, config=config_file) diff --git a/src/frostfs_testlib/storage/grpc_operations/client_wrappers.py b/src/frostfs_testlib/storage/grpc_operations/client_wrappers.py index c1e3a31..d9f94b2 100644 --- a/src/frostfs_testlib/storage/grpc_operations/client_wrappers.py +++ b/src/frostfs_testlib/storage/grpc_operations/client_wrappers.py @@ -8,6 +8,7 @@ class CliClientWrapper(interfaces_wrapper.GrpcClientWrapper): self.object: interfaces.ObjectInterface = implementations.ObjectOperations(self.cli) self.container: interfaces.ContainerInterface = implementations.ContainerOperations(self.cli) self.netmap: interfaces.NetmapInterface = implementations.NetmapOperations(self.cli) + self.ape_manager: interfaces.ApeManagerInterface = implementations.ApeManagerOperations(self.cli) class RpcClientWrapper(interfaces_wrapper.GrpcClientWrapper): diff --git a/src/frostfs_testlib/storage/grpc_operations/implementations/__init__.py b/src/frostfs_testlib/storage/grpc_operations/implementations/__init__.py index 18e8ae5..df820fa 100644 --- a/src/frostfs_testlib/storage/grpc_operations/implementations/__init__.py +++ b/src/frostfs_testlib/storage/grpc_operations/implementations/__init__.py @@ -1,3 +1,4 @@ +from .ape_manager import ApeManagerOperations from .chunks import ChunksOperations from .container import ContainerOperations from .netmap import NetmapOperations diff --git a/src/frostfs_testlib/storage/grpc_operations/implementations/ape_manager.py b/src/frostfs_testlib/storage/grpc_operations/implementations/ape_manager.py new file mode 100644 index 0000000..070d8a6 --- /dev/null +++ b/src/frostfs_testlib/storage/grpc_operations/implementations/ape_manager.py @@ -0,0 +1,79 @@ +from typing import Optional + +from frostfs_testlib import reporter +from frostfs_testlib.cli.frostfs_cli.cli import FrostfsCli +from frostfs_testlib.resources.cli import CLI_DEFAULT_TIMEOUT + + +class ApeManagerOperations: + def __init__(self, cli: FrostfsCli): + self.cli = cli + + @reporter.step("Add ape rule") + def add( + self, + rpc_endpoint: str, + chain_id: Optional[str] = None, + chain_id_hex: Optional[str] = None, + path: Optional[str] = None, + rule: Optional[str] | Optional[list[str]] = None, + target_name: Optional[str] = None, + target_type: Optional[str] = None, + wallet: Optional[str] = None, + address: Optional[str] = None, + timeout: Optional[str] = CLI_DEFAULT_TIMEOUT, + ): + return self.cli.ape_manager.add( + rpc_endpoint=rpc_endpoint, + chain_id=chain_id, + chain_id_hex=chain_id_hex, + path=path, + rule=rule, + target_name=target_name, + target_type=target_type, + wallet=wallet, + address=address, + timeout=timeout, + ) + + @reporter.step("Get list APE rules") + def list( + self, + rpc_endpoint: str, + target_name: Optional[str] = None, + target_type: Optional[str] = None, + wallet: Optional[str] = None, + address: Optional[str] = None, + timeout: Optional[str] = CLI_DEFAULT_TIMEOUT, + ): + return self.cli.ape_manager.list( + rpc_endpoint=rpc_endpoint, + target_name=target_name, + target_type=target_type, + wallet=wallet, + address=address, + timeout=timeout, + ) + + @reporter.step("Remove APE rule") + def remove( + self, + rpc_endpoint: str, + chain_id: Optional[str] = None, + chain_id_hex: Optional[str] = None, + target_name: Optional[str] = None, + target_type: Optional[str] = None, + wallet: Optional[str] = None, + address: Optional[str] = None, + timeout: Optional[str] = CLI_DEFAULT_TIMEOUT, + ): + return self.cli.ape_manager.remove( + rpc_endpoint=rpc_endpoint, + chain_id=chain_id, + chain_id_hex=chain_id_hex, + target_name=target_name, + target_type=target_type, + wallet=wallet, + address=address, + timeout=timeout, + ) diff --git a/src/frostfs_testlib/storage/grpc_operations/implementations/container.py b/src/frostfs_testlib/storage/grpc_operations/implementations/container.py index 75af00c..afdf6cb 100644 --- a/src/frostfs_testlib/storage/grpc_operations/implementations/container.py +++ b/src/frostfs_testlib/storage/grpc_operations/implementations/container.py @@ -1,6 +1,7 @@ import json import logging import re +from time import sleep from typing import List, Optional, Union from frostfs_testlib import reporter @@ -301,6 +302,16 @@ class ContainerOperations(interfaces.ContainerInterface): resolver: BucketContainerResolver = resolver_cls() return resolver.resolve(node, name) + @reporter.step("Wait create container, with list") + def wait_creation(self, cid: str, endpoint: str, attempts: int = 15, sleep_interval: int = 1): + for _ in range(attempts): + containers = self.list(endpoint) + if cid in containers: + return + logger.info(f"There is no {cid} in {containers} yet; sleep {sleep_interval} and continue") + sleep(sleep_interval) + raise RuntimeError(f"After {attempts * sleep_interval} seconds container {cid} hasn't been persisted; exiting") + def _parse_cid(self, output: str) -> str: """ Parses container ID from a given CLI output. The input string we expect: diff --git a/src/frostfs_testlib/storage/grpc_operations/interfaces/__init__.py b/src/frostfs_testlib/storage/grpc_operations/interfaces/__init__.py index 17b3e9c..379bbe0 100644 --- a/src/frostfs_testlib/storage/grpc_operations/interfaces/__init__.py +++ b/src/frostfs_testlib/storage/grpc_operations/interfaces/__init__.py @@ -1,3 +1,4 @@ +from .ape_manager import ApeManagerInterface from .chunks import ChunksInterface from .container import ContainerInterface from .netmap import NetmapInterface diff --git a/src/frostfs_testlib/storage/grpc_operations/interfaces/ape_manager.py b/src/frostfs_testlib/storage/grpc_operations/interfaces/ape_manager.py new file mode 100644 index 0000000..5b198bc --- /dev/null +++ b/src/frostfs_testlib/storage/grpc_operations/interfaces/ape_manager.py @@ -0,0 +1,48 @@ +from abc import ABC, abstractmethod +from typing import Optional + +from frostfs_testlib.shell.interfaces import CommandResult + + +class ApeManagerInterface(ABC): + @abstractmethod + def add( + self, + rpc_endpoint: str, + chain_id: Optional[str] = None, + chain_id_hex: Optional[str] = None, + path: Optional[str] = None, + rule: Optional[str] | Optional[list[str]] = None, + target_name: Optional[str] = None, + target_type: Optional[str] = None, + wallet: Optional[str] = None, + address: Optional[str] = None, + timeout: Optional[str] = None, + ) -> CommandResult: + pass + + @abstractmethod + def list( + self, + rpc_endpoint: str, + target_name: Optional[str] = None, + target_type: Optional[str] = None, + wallet: Optional[str] = None, + address: Optional[str] = None, + timeout: Optional[str] = None, + ) -> CommandResult: + pass + + @abstractmethod + def remove( + self, + rpc_endpoint: str, + chain_id: Optional[str] = None, + chain_id_hex: Optional[str] = None, + target_name: Optional[str] = None, + target_type: Optional[str] = None, + wallet: Optional[str] = None, + address: Optional[str] = None, + timeout: Optional[str] = None, + ) -> CommandResult: + pass diff --git a/src/frostfs_testlib/storage/grpc_operations/interfaces/container.py b/src/frostfs_testlib/storage/grpc_operations/interfaces/container.py index d5e3eeb..397f7b2 100644 --- a/src/frostfs_testlib/storage/grpc_operations/interfaces/container.py +++ b/src/frostfs_testlib/storage/grpc_operations/interfaces/container.py @@ -123,3 +123,7 @@ class ContainerInterface(ABC): ) -> List[ClusterNode]: """Show the nodes participating in the container in the current epoch.""" raise NotImplementedError("No implemethed method nodes") + + @abstractmethod + def wait_creation(self, cid: str, endpoint: str, attempts: Optional[str], sleep_interval: Optional[int]) -> None: + raise NotImplementedError("No implemented method wait_creation") diff --git a/src/frostfs_testlib/storage/grpc_operations/interfaces_wrapper.py b/src/frostfs_testlib/storage/grpc_operations/interfaces_wrapper.py index 6574012..5edc99f 100644 --- a/src/frostfs_testlib/storage/grpc_operations/interfaces_wrapper.py +++ b/src/frostfs_testlib/storage/grpc_operations/interfaces_wrapper.py @@ -1,10 +1,14 @@ from abc import ABC +from frostfs_testlib.cli.frostfs_cli.cli import FrostfsCli + from . import interfaces class GrpcClientWrapper(ABC): def __init__(self) -> None: + self.cli: FrostfsCli self.object: interfaces.ObjectInterface self.container: interfaces.ContainerInterface self.netmap: interfaces.NetmapInterface + self.ape_manager: interfaces.ApeManagerInterface