From f40111dc4a7721ab9b3487555be9e11721f21db2 Mon Sep 17 00:00:00 2001 From: Vladimir Avdeev Date: Mon, 29 Aug 2022 17:36:27 +0300 Subject: [PATCH] Implemented neofs-adm lib Signed-off-by: Vladimir Avdeev --- pytest_tests/testsuites/conftest.py | 12 +- robot/resources/lib/python_keywords/acl.py | 2 +- .../lib/python_keywords/cli_utils/__init__.py | 2 + .../python_keywords/cli_utils/adm/__init__.py | 2 + .../lib/python_keywords/cli_utils/adm/adm.py | 36 +++ .../cli_utils/adm/completion.py | 30 ++ .../cli_utils/adm/completion_type.py | 8 + .../python_keywords/cli_utils/adm/config.py | 19 ++ .../python_keywords/cli_utils/adm/gendoc.py | 34 +++ .../python_keywords/cli_utils/adm/morph.py | 272 ++++++++++++++++++ .../cli_utils/adm/storage_config.py | 20 ++ .../python_keywords/cli_utils/adm/subnet.py | 187 ++++++++++++ .../python_keywords/cli_utils/adm/version.py | 12 + .../{ => cli_utils}/cli/__init__.py | 0 .../{ => cli_utils}/cli/accounting.py | 4 +- .../{ => cli_utils}/cli/acl.py | 4 +- .../{ => cli_utils}/cli/cli.py | 13 +- .../{ => cli_utils}/cli/container.py | 4 +- .../{ => cli_utils}/cli/object.py | 4 +- .../python_keywords/cli_utils/cli/version.py | 12 + .../{cli => cli_utils}/cli_command.py | 6 +- .../lib/python_keywords/container.py | 2 +- .../lib/python_keywords/neofs_verbs.py | 2 +- robot/variables/common.py | 3 +- 24 files changed, 662 insertions(+), 28 deletions(-) create mode 100644 robot/resources/lib/python_keywords/cli_utils/__init__.py create mode 100644 robot/resources/lib/python_keywords/cli_utils/adm/__init__.py create mode 100644 robot/resources/lib/python_keywords/cli_utils/adm/adm.py create mode 100644 robot/resources/lib/python_keywords/cli_utils/adm/completion.py create mode 100644 robot/resources/lib/python_keywords/cli_utils/adm/completion_type.py create mode 100644 robot/resources/lib/python_keywords/cli_utils/adm/config.py create mode 100644 robot/resources/lib/python_keywords/cli_utils/adm/gendoc.py create mode 100644 robot/resources/lib/python_keywords/cli_utils/adm/morph.py create mode 100644 robot/resources/lib/python_keywords/cli_utils/adm/storage_config.py create mode 100644 robot/resources/lib/python_keywords/cli_utils/adm/subnet.py create mode 100644 robot/resources/lib/python_keywords/cli_utils/adm/version.py rename robot/resources/lib/python_keywords/{ => cli_utils}/cli/__init__.py (100%) rename robot/resources/lib/python_keywords/{ => cli_utils}/cli/accounting.py (89%) rename robot/resources/lib/python_keywords/{ => cli_utils}/cli/acl.py (95%) rename robot/resources/lib/python_keywords/{ => cli_utils}/cli/cli.py (78%) rename robot/resources/lib/python_keywords/{ => cli_utils}/cli/container.py (98%) rename robot/resources/lib/python_keywords/{ => cli_utils}/cli/object.py (99%) create mode 100644 robot/resources/lib/python_keywords/cli_utils/cli/version.py rename robot/resources/lib/python_keywords/{cli => cli_utils}/cli_command.py (93%) diff --git a/pytest_tests/testsuites/conftest.py b/pytest_tests/testsuites/conftest.py index 33b4a923..9ff18dca 100644 --- a/pytest_tests/testsuites/conftest.py +++ b/pytest_tests/testsuites/conftest.py @@ -6,15 +6,15 @@ from datetime import datetime import allure import pytest -from robot.api import deco - import wallet from cli_helpers import _cmd_run +from cli_utils import NeofsAdm, NeofsCli from common import (ASSETS_DIR, FREE_STORAGE, INFRASTRUCTURE_TYPE, MAINNET_WALLET_PATH, NEOFS_NETMAP_DICT) from env_properties import save_env_properties from payment_neogo import neofs_deposit, transfer_mainnet_gas from python_keywords.node_management import node_healthcheck +from robot.api import deco from service_helper import get_storage_service_helper @@ -38,9 +38,15 @@ def cloud_infrastructure_check(): @allure.title('Check binary versions') def check_binary_versions(request): # Collect versions of local binaries - binaries = ['neo-go', 'neofs-cli', 'neofs-authmate'] + binaries = ['neo-go', 'neofs-authmate'] local_binaries = _get_binaries_version_local(binaries) + try: + local_binaries['neofs-adm'] = NeofsAdm().version.get() + except RuntimeError: + logger.info(f'neofs-adm not installed') + local_binaries['neofs-cli'] = NeofsCli().version.get() + # Collect versions of remote binaries helper = get_storage_service_helper() remote_binaries = helper.get_binaries_version() diff --git a/robot/resources/lib/python_keywords/acl.py b/robot/resources/lib/python_keywords/acl.py index 3c6012e6..678401a9 100644 --- a/robot/resources/lib/python_keywords/acl.py +++ b/robot/resources/lib/python_keywords/acl.py @@ -10,8 +10,8 @@ from typing import Any, Dict, List, Optional, Union import allure import base58 -from cli import NeofsCli from cli_helpers import _cmd_run +from cli_utils import NeofsCli from common import ASSETS_DIR, NEOFS_CLI_EXEC, NEOFS_ENDPOINT, WALLET_CONFIG from data_formatters import get_wallet_public_key diff --git a/robot/resources/lib/python_keywords/cli_utils/__init__.py b/robot/resources/lib/python_keywords/cli_utils/__init__.py new file mode 100644 index 00000000..3647f358 --- /dev/null +++ b/robot/resources/lib/python_keywords/cli_utils/__init__.py @@ -0,0 +1,2 @@ +from .adm import CompletionType, NeofsAdm +from .cli import NeofsCli diff --git a/robot/resources/lib/python_keywords/cli_utils/adm/__init__.py b/robot/resources/lib/python_keywords/cli_utils/adm/__init__.py new file mode 100644 index 00000000..312516fc --- /dev/null +++ b/robot/resources/lib/python_keywords/cli_utils/adm/__init__.py @@ -0,0 +1,2 @@ +from .adm import NeofsAdm +from .completion_type import CompletionType diff --git a/robot/resources/lib/python_keywords/cli_utils/adm/adm.py b/robot/resources/lib/python_keywords/cli_utils/adm/adm.py new file mode 100644 index 00000000..a5914693 --- /dev/null +++ b/robot/resources/lib/python_keywords/cli_utils/adm/adm.py @@ -0,0 +1,36 @@ +from typing import Optional + +from common import NEOFS_ADM_EXEC + +from .completion import NeofsAdmCompletion +from .config import NeofsAdmConfig +from .gendoc import NeofsAdmGenDoc +from .morph import NeofsAdmMorph +from .subnet import NeofsAdmMorphSubnet +from .storage_config import NeofsAdmStorageConfig +from .version import NeofsAdmVersion + + +class NeofsAdm: + neofs_adm_exec_path: Optional[str] = None + config_file: Optional[str] = None + + completion: Optional[NeofsAdmCompletion] = None + config: Optional[NeofsAdmConfig] = None + gendoc: Optional[NeofsAdmGenDoc] = None + morph: Optional[NeofsAdmMorph] = None + subnet: Optional[NeofsAdmMorphSubnet] = None + storage_config: Optional[NeofsAdmStorageConfig] = None + version: Optional[NeofsAdmVersion] = None + + def __init__(self, neofs_adm_exec_path: Optional[str] = None, config_file: Optional[str] = None, timeout: int = 30): + self.config_file = config_file + self.neofs_adm_exec_path = neofs_adm_exec_path or NEOFS_ADM_EXEC + + self.completion = NeofsAdmCompletion(self.neofs_adm_exec_path, timeout=timeout, config=config_file) + self.config = NeofsAdmConfig(self.neofs_adm_exec_path, timeout=timeout, config=config_file) + self.gendoc = NeofsAdmGenDoc(self.neofs_adm_exec_path, timeout=timeout, config=config_file) + self.morph = NeofsAdmMorph(self.neofs_adm_exec_path, timeout=timeout, config=config_file) + self.subnet = NeofsAdmMorphSubnet(self.neofs_adm_exec_path, timeout=timeout, config=config_file) + self.storage_config = NeofsAdmStorageConfig(self.neofs_adm_exec_path, timeout=timeout, config=config_file) + self.version = NeofsAdmVersion(self.neofs_adm_exec_path, timeout=timeout, config=config_file) diff --git a/robot/resources/lib/python_keywords/cli_utils/adm/completion.py b/robot/resources/lib/python_keywords/cli_utils/adm/completion.py new file mode 100644 index 00000000..9bd42d8a --- /dev/null +++ b/robot/resources/lib/python_keywords/cli_utils/adm/completion.py @@ -0,0 +1,30 @@ +from cli_utils.cli_command import NeofsCliCommand + +from .completion_type import CompletionType + + +class NeofsAdmCompletion(NeofsCliCommand): + def get(self, completion_type: CompletionType = CompletionType.FISH) -> str: + """To load completions: + Bash: + $ source <(neofs-adm completion bash) + + Zsh: + If shell completion is not already enabled in your environment you will need + to enable it. You can execute the following once: + $ echo "autoload -U compinit; compinit" >> ~/.zshrc + + You will need to start a new shell for this setup to take effect. + + Fish: + $ neofs-adm completion fish | source + + Args: + completion_type (CompletionType): Select completion type (default: Fish) + + + Returns: + str: Command string + + """ + return self._execute('completion ' + completion_type.value) diff --git a/robot/resources/lib/python_keywords/cli_utils/adm/completion_type.py b/robot/resources/lib/python_keywords/cli_utils/adm/completion_type.py new file mode 100644 index 00000000..9bf2ba8b --- /dev/null +++ b/robot/resources/lib/python_keywords/cli_utils/adm/completion_type.py @@ -0,0 +1,8 @@ +from enum import Enum + + +class CompletionType(Enum): + BASH = 'bash' + ZHS = 'zsh' + FISH = 'fish' + POWERSHELL = 'powershell' diff --git a/robot/resources/lib/python_keywords/cli_utils/adm/config.py b/robot/resources/lib/python_keywords/cli_utils/adm/config.py new file mode 100644 index 00000000..3c7af171 --- /dev/null +++ b/robot/resources/lib/python_keywords/cli_utils/adm/config.py @@ -0,0 +1,19 @@ +from cli_utils.cli_command import NeofsCliCommand + + +class NeofsAdmConfig(NeofsCliCommand): + def init(self, path: str = '~/.neofs/adm/config.yml') -> str: + """Initialize basic neofs-adm configuration file. + + Args: + path (str): path to config (default ~/.neofs/adm/config.yml) + + + Returns: + str: Command string + + """ + return self._execute( + 'config init', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) diff --git a/robot/resources/lib/python_keywords/cli_utils/adm/gendoc.py b/robot/resources/lib/python_keywords/cli_utils/adm/gendoc.py new file mode 100644 index 00000000..28c52dac --- /dev/null +++ b/robot/resources/lib/python_keywords/cli_utils/adm/gendoc.py @@ -0,0 +1,34 @@ +from typing import Optional + +from cli_utils.cli_command import NeofsCliCommand + + +class NeofsAdmGenDoc(NeofsCliCommand): + def get(self, doc_file: str, depth: int = 1, doc_type: str = 'md', extension: Optional[str] = None) -> str: + """Generate documentation for this command. If the template is not provided, + builtin cobra generator is used and each subcommand is placed in + a separate file in the same directory. + + The last optional argument specifies the template to use with text/template. + In this case there is a number of helper functions which can be used: + replace STR FROM TO -- same as strings.ReplaceAll + join ARRAY SEPARATOR -- same as strings.Join + split STR SEPARATOR -- same as strings.Split + fullUse CMD -- slice of all command names starting from the parent + listFlags CMD -- list of command flags + + Args: + depth (int): if template is specified, unify all commands starting from depth in a single file. + Default = 1. + doc_file (str): file where to save generated documentation + extension (str): if the template is specified, string to append to the output file names + doc_type (str): type for the documentation ('md' or 'man') (default "md") + + Returns: + str: Command string + + """ + return self._execute( + f'gendoc {doc_file}', + **{param: param_value for param, param_value in locals().items() if param not in ['self', 'doc_file']} + ) diff --git a/robot/resources/lib/python_keywords/cli_utils/adm/morph.py b/robot/resources/lib/python_keywords/cli_utils/adm/morph.py new file mode 100644 index 00000000..458b0438 --- /dev/null +++ b/robot/resources/lib/python_keywords/cli_utils/adm/morph.py @@ -0,0 +1,272 @@ +from typing import Optional + +from cli_utils.cli_command import NeofsCliCommand + + +class NeofsAdmMorph(NeofsCliCommand): + def deposit_notary(self, rpc_endpoint: str, account: str, gas: str, storage_wallet: Optional[str] = None, + till: Optional[str] = None) -> str: + """Deposit GAS for notary service. + + Args: + account (str): wallet account address + gas (str): amount of GAS to deposit + rpc_endpoint (str): N3 RPC node endpoint + storage_wallet (str): path to storage node wallet + till (str): notary deposit duration in blocks + + + Returns: + str: Command string + + """ + return self._execute( + 'morph deposit-notary', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def dump_balances(self, rpc_endpoint: str, alphabet: Optional[str] = None, proxy: Optional[str] = None, + script_hash: Optional[str] = None, storage: Optional[str] = None) -> str: + """Dump GAS balances + + Args: + alphabet (str): dump balances of alphabet contracts + proxy (str): dump balances of the proxy contract + rpc_endpoint (str): N3 RPC node endpoint + script_hash (str): use script-hash format for addresses + storage (str): dump balances of storage nodes from the current netmap + + + Returns: + str: Command string + + """ + return self._execute( + 'morph dump-balances', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def dump_config(self, rpc_endpoint: str) -> str: + """Section for morph network configuration commands. + + Args: + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + 'morph dump-config', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def dump_containers(self, rpc_endpoint: str, cid: Optional[str] = None, container_contract: Optional[str] = None, + dump: Optional[str] = None) -> str: + """Dump NeoFS containers to file. + + Args: + cid (str): containers to dump + container_contract (str): container contract hash (for networks without NNS) + dump (str): file where to save dumped containers + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + 'morph dump-containers', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def dump_hashes(self, rpc_endpoint: str) -> str: + """Dump deployed contract hashes. + + Args: + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + 'morph dump-hashes', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def force_new_epoch(self, rpc_endpoint: Optional[str] = None, alphabet: Optional[str] = None) -> str: + """Create new NeoFS epoch event in the side chain + + Args: + alphabet (str): path to alphabet wallets dir + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + 'morph force-new-epoch', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def generate_alphabet(self, rpc_endpoint: str, alphabet_wallets: str, size: int = 7) -> str: + """Generate alphabet wallets for consensus nodes of the morph network + + Args: + alphabet_wallets (str): path to alphabet wallets dir + size (int): amount of alphabet wallets to generate (default 7) + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + 'morph generate-alphabet', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def generate_storage_wallet(self, rpc_endpoint: str, alphabet_wallets: str, storage_wallet: str, + initial_gas: Optional[str] = None) -> str: + """Generate storage node wallet for the morph network + + Args: + alphabet_wallets (str): path to alphabet wallets dir + initial_gas (str): initial amount of GAS to transfer + rpc_endpoint (str): N3 RPC node endpoint + storage_wallet (str): path to new storage node wallet + + + Returns: + str: Command string + + """ + return self._execute( + 'morph generate-storage-wallet', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def init(self, rpc_endpoint: str, alphabet_wallets: str, contracts: str, protocol: str, + container_alias_fee: int = 500, container_fee: int = 1000, epoch_duration: int = 240, + homomorphic_disabled: bool = False, local_dump: Optional[str] = None, max_object_size: int = 67108864 + ) -> str: + """Section for morph network configuration commands. + + Args: + alphabet_wallets (str): path to alphabet wallets dir + container_alias_fee (int): container alias fee (default 500) + container_fee (int): container registration fee (default 1000) + contracts (str): path to archive with compiled NeoFS contracts + (default fetched from latest github release) + epoch_duration (int): amount of side chain blocks in one NeoFS epoch (default 240) + homomorphic_disabled: (bool): disable object homomorphic hashing + local_dump (str): path to the blocks dump file + max_object_size (int): max single object size in bytes (default 67108864) + protocol (str): path to the consensus node configuration + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + 'morph init', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def refill_gas(self, rpc_endpoint: str, alphabet_wallets: str, storage_wallet: str, gas: Optional[str] = None + ) -> str: + """Refill GAS of storage node's wallet in the morph network + + Args: + alphabet_wallets (str): path to alphabet wallets dir + gas (str): additional amount of GAS to transfer + rpc_endpoint (str): N3 RPC node endpoint + storage_wallet (str): path to new storage node wallet + + + Returns: + str: Command string + + """ + return self._execute( + 'morph refill-gas', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def restore_containers(self, rpc_endpoint: str, alphabet_wallets: str, cid: str, dump: str) -> str: + """Restore NeoFS containers from file. + + Args: + alphabet_wallets (str): path to alphabet wallets dir + cid (str): containers to restore + dump (str): file to restore containers from + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + 'morph restore-containers', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def set_policy(self, rpc_endpoint: str, alphabet_wallets: str, exec_fee_factor: Optional[int] = None, + storage_price: Optional[int] = None, fee_per_byte: Optional[int] = None) -> str: + """Set global policy values + + Args: + alphabet_wallets (str): path to alphabet wallets dir + exec_fee_factor (int): ExecFeeFactor= + storage_price (int): StoragePrice= + fee_per_byte (int): FeePerByte= + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + non_param_attribute = '' + if exec_fee_factor: + non_param_attribute += f'ExecFeeFactor={exec_fee_factor} ' + if storage_price: + non_param_attribute += f'StoragePrice={storage_price} ' + if fee_per_byte: + non_param_attribute += f'FeePerByte={fee_per_byte} ' + return self._execute( + f'morph restore-containers {non_param_attribute}', + **{param: param_value for param, param_value in locals().items() if param not in [ + 'self', 'exec_fee_factor', 'storage_price', 'fee_per_byte' + ]} + ) + + def update_contracts(self, rpc_endpoint: str, alphabet_wallets: str, contracts: Optional[str] = None + ) -> str: + """Update NeoFS contracts. + + Args: + alphabet_wallets (str): path to alphabet wallets dir + contracts (str): path to archive with compiled NeoFS contracts + (default fetched from latest github release) + rpc_endpoint (str): N3 RPC node endpoint + + + Returns: + str: Command string + + """ + return self._execute( + 'morph update-contracts', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) diff --git a/robot/resources/lib/python_keywords/cli_utils/adm/storage_config.py b/robot/resources/lib/python_keywords/cli_utils/adm/storage_config.py new file mode 100644 index 00000000..502e21fe --- /dev/null +++ b/robot/resources/lib/python_keywords/cli_utils/adm/storage_config.py @@ -0,0 +1,20 @@ +from cli_utils.cli_command import NeofsCliCommand + + +class NeofsAdmStorageConfig(NeofsCliCommand): + def set(self, account: str, wallet: str) -> str: + """Initialize basic neofs-adm configuration file. + + Args: + account (str): wallet account + wallet (str): path to wallet + + + Returns: + str: Command string + + """ + return self._execute( + 'storage-config', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) diff --git a/robot/resources/lib/python_keywords/cli_utils/adm/subnet.py b/robot/resources/lib/python_keywords/cli_utils/adm/subnet.py new file mode 100644 index 00000000..bb7615ae --- /dev/null +++ b/robot/resources/lib/python_keywords/cli_utils/adm/subnet.py @@ -0,0 +1,187 @@ +from typing import Optional + +from cli_utils.cli_command import NeofsCliCommand + + +class NeofsAdmMorphSubnet(NeofsCliCommand): + def create(self, rpc_endpoint: str, address: str, wallet: str, notary: bool = False) -> str: + """Create NeoFS subnet. + + Args: + address (str): Address in the wallet, optional + notary (bool): Flag to create subnet in notary environment + rpc_endpoint (str): N3 RPC node endpoint + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + 'morph subnet create', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def get(self, rpc_endpoint: str, subnet: str) -> str: + """Read information about the NeoFS subnet. + + Args: + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + + + Returns: + str: Command string + + """ + return self._execute( + 'morph subnet get', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def remove(self, rpc_endpoint: str, wallet: str, subnet: str, address: Optional[str] = None) -> str: + """Remove NeoFS subnet. + + Args: + address (str): Address in the wallet, optional + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + 'morph subnet remove', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def admin_add(self, rpc_endpoint: str, wallet: str, admin: str, subnet: str, client: Optional[str] = None, + group: Optional[str] = None, address: Optional[str] = None) -> str: + """Add admin to the NeoFS subnet. + + Args: + address (str): Address in the wallet, optional + admin (str): Hex-encoded public key of the admin + client (str): Add client admin instead of node one + group (str): Client group ID in text format (needed with --client only) + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + 'morph subnet admin add', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def admin_remove(self, rpc_endpoint: str, wallet: str, admin: str, subnet: str, client: Optional[str] = None, + address: Optional[str] = None) -> str: + """Remove admin of the NeoFS subnet. + + Args: + address (str): Address in the wallet, optional + admin (str): Hex-encoded public key of the admin + client (str): Remove client admin instead of node one + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + 'morph subnet admin remove', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def client_add(self, rpc_endpoint: str, wallet: str, subnet: str, client: Optional[str] = None, + group: Optional[str] = None, address: Optional[str] = None) -> str: + """Add client to the NeoFS subnet. + + Args: + address (str): Address in the wallet, optional + client (str): Add client admin instead of node one + group (str): Client group ID in text format (needed with --client only) + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + 'morph subnet client add', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def client_remove(self, rpc_endpoint: str, wallet: str, client: str, group: str, subnet: str, + address: Optional[str] = None) -> str: + """Remove client of the NeoFS subnet. + + Args: + address (str): Address in the wallet, optional + client (str): Remove client admin instead of node one + group (str): ID of the client group to work with + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + 'morph subnet client remove', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def node_add(self, rpc_endpoint: str, wallet: str, node: str, subnet: str) -> str: + """Add node to the NeoFS subnet. + + Args: + node (str): Hex-encoded public key of the node + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + 'morph subnet node add', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) + + def node_remove(self, rpc_endpoint: str, wallet: str, node: str, subnet: str) -> str: + """Remove node from the NeoFS subnet. + + Args: + node (str): Hex-encoded public key of the node + rpc_endpoint (str): N3 RPC node endpoint + subnet (str): ID of the subnet to read + wallet (str): Path to file with wallet + + + Returns: + str: Command string + + """ + return self._execute( + 'morph subnet node remove', + **{param: param_value for param, param_value in locals().items() if param not in ['self']} + ) diff --git a/robot/resources/lib/python_keywords/cli_utils/adm/version.py b/robot/resources/lib/python_keywords/cli_utils/adm/version.py new file mode 100644 index 00000000..593aa1fa --- /dev/null +++ b/robot/resources/lib/python_keywords/cli_utils/adm/version.py @@ -0,0 +1,12 @@ +from cli_utils.cli_command import NeofsCliCommand + + +class NeofsAdmVersion(NeofsCliCommand): + def get(self) -> str: + """Application version + + Returns: + str: Command string + + """ + return self._execute('', version=True) diff --git a/robot/resources/lib/python_keywords/cli/__init__.py b/robot/resources/lib/python_keywords/cli_utils/cli/__init__.py similarity index 100% rename from robot/resources/lib/python_keywords/cli/__init__.py rename to robot/resources/lib/python_keywords/cli_utils/cli/__init__.py diff --git a/robot/resources/lib/python_keywords/cli/accounting.py b/robot/resources/lib/python_keywords/cli_utils/cli/accounting.py similarity index 89% rename from robot/resources/lib/python_keywords/cli/accounting.py rename to robot/resources/lib/python_keywords/cli_utils/cli/accounting.py index 84cf53bc..6fb06888 100644 --- a/robot/resources/lib/python_keywords/cli/accounting.py +++ b/robot/resources/lib/python_keywords/cli_utils/cli/accounting.py @@ -1,9 +1,9 @@ from typing import Optional -from .cli_command import NeofsCliCommandBase +from cli_utils.cli_command import NeofsCliCommand -class NeofsCliAccounting(NeofsCliCommandBase): +class NeofsCliAccounting(NeofsCliCommand): def balance(self, wallet: str, rpc_endpoint: str, address: Optional[str] = None, owner: Optional[str] = None) -> str: """Get internal balance of NeoFS account diff --git a/robot/resources/lib/python_keywords/cli/acl.py b/robot/resources/lib/python_keywords/cli_utils/cli/acl.py similarity index 95% rename from robot/resources/lib/python_keywords/cli/acl.py rename to robot/resources/lib/python_keywords/cli_utils/cli/acl.py index 887921c0..4902d62e 100644 --- a/robot/resources/lib/python_keywords/cli/acl.py +++ b/robot/resources/lib/python_keywords/cli_utils/cli/acl.py @@ -1,9 +1,9 @@ from typing import Optional -from .cli_command import NeofsCliCommandBase +from cli_utils.cli_command import NeofsCliCommand -class NeofsCliACL(NeofsCliCommandBase): +class NeofsCliACL(NeofsCliCommand): def extended_create(self, cid: str, out: str, file: Optional[str] = None, rule: Optional[list] = None) -> str: """Create extended ACL from the text representation. diff --git a/robot/resources/lib/python_keywords/cli/cli.py b/robot/resources/lib/python_keywords/cli_utils/cli/cli.py similarity index 78% rename from robot/resources/lib/python_keywords/cli/cli.py rename to robot/resources/lib/python_keywords/cli_utils/cli/cli.py index 2074f749..2bd7eb0e 100644 --- a/robot/resources/lib/python_keywords/cli/cli.py +++ b/robot/resources/lib/python_keywords/cli_utils/cli/cli.py @@ -4,9 +4,9 @@ from common import NEOFS_CLI_EXEC from .accounting import NeofsCliAccounting from .acl import NeofsCliACL -from .cli_command import NeofsCliCommandBase from .container import NeofsCliContainer from .object import NeofsCliObject +from .version import NeofsCliVersion class NeofsCli: @@ -16,6 +16,7 @@ class NeofsCli: acl: Optional[NeofsCliACL] = None container: Optional[NeofsCliContainer] = None object: Optional[NeofsCliObject] = None + version: Optional[NeofsCliVersion] = None def __init__(self, neofs_cli_exec_path: Optional[str] = None, config: Optional[str] = None, timeout: int = 30): self.config = config # config(str): config file (default is $HOME/.config/neofs-cli/config.yaml) @@ -24,12 +25,4 @@ class NeofsCli: self.acl = NeofsCliACL(self.neofs_cli_exec_path, timeout=timeout, config=config) self.container = NeofsCliContainer(self.neofs_cli_exec_path, timeout=timeout, config=config) self.object = NeofsCliObject(self.neofs_cli_exec_path, timeout=timeout, config=config) - - def version(self) -> str: - """Application version and NeoFS API compatibility - - Returns: - str: Command string - - """ - return NeofsCliCommandBase(self.neofs_cli_exec_path, config=self.config)._execute(command=None, version=True) + self.version = NeofsCliVersion(self.neofs_cli_exec_path, timeout=timeout, config=config) diff --git a/robot/resources/lib/python_keywords/cli/container.py b/robot/resources/lib/python_keywords/cli_utils/cli/container.py similarity index 98% rename from robot/resources/lib/python_keywords/cli/container.py rename to robot/resources/lib/python_keywords/cli_utils/cli/container.py index 7b7e9241..292f51c4 100644 --- a/robot/resources/lib/python_keywords/cli/container.py +++ b/robot/resources/lib/python_keywords/cli_utils/cli/container.py @@ -1,9 +1,9 @@ from typing import Optional -from .cli_command import NeofsCliCommandBase +from cli_utils.cli_command import NeofsCliCommand -class NeofsCliContainer(NeofsCliCommandBase): +class NeofsCliContainer(NeofsCliCommand): def create(self, rpc_endpoint: str, wallet: str, address: Optional[str] = None, attributes: Optional[dict] = None, basic_acl: Optional[str] = None, await_mode: bool = False, disable_timestamp: bool = False, name: Optional[str] = None, nonce: Optional[str] = None, policy: Optional[str] = None, diff --git a/robot/resources/lib/python_keywords/cli/object.py b/robot/resources/lib/python_keywords/cli_utils/cli/object.py similarity index 99% rename from robot/resources/lib/python_keywords/cli/object.py rename to robot/resources/lib/python_keywords/cli_utils/cli/object.py index ce9b65e3..085f722e 100644 --- a/robot/resources/lib/python_keywords/cli/object.py +++ b/robot/resources/lib/python_keywords/cli_utils/cli/object.py @@ -1,9 +1,9 @@ from typing import Optional -from .cli_command import NeofsCliCommandBase +from cli_utils.cli_command import NeofsCliCommand -class NeofsCliObject(NeofsCliCommandBase): +class NeofsCliObject(NeofsCliCommand): def delete(self, rpc_endpoint: str, wallet: str, cid: str, oid: str, address: Optional[str] = None, bearer: Optional[str] = None, session: Optional[str] = None, ttl: Optional[int] = None, xhdr: Optional[dict] = None) -> str: diff --git a/robot/resources/lib/python_keywords/cli_utils/cli/version.py b/robot/resources/lib/python_keywords/cli_utils/cli/version.py new file mode 100644 index 00000000..4889449d --- /dev/null +++ b/robot/resources/lib/python_keywords/cli_utils/cli/version.py @@ -0,0 +1,12 @@ +from cli_utils.cli_command import NeofsCliCommand + + +class NeofsCliVersion(NeofsCliCommand): + def get(self) -> str: + """Application version and NeoFS API compatibility + + Returns: + str: Command string + + """ + return self._execute('', version=True) diff --git a/robot/resources/lib/python_keywords/cli/cli_command.py b/robot/resources/lib/python_keywords/cli_utils/cli_command.py similarity index 93% rename from robot/resources/lib/python_keywords/cli/cli_command.py rename to robot/resources/lib/python_keywords/cli_utils/cli_command.py index 1d2afa1c..21c7f095 100644 --- a/robot/resources/lib/python_keywords/cli/cli_command.py +++ b/robot/resources/lib/python_keywords/cli_utils/cli_command.py @@ -3,13 +3,13 @@ from typing import Optional from cli_helpers import _cmd_run -class NeofsCliCommandBase: +class NeofsCliCommand: neofs_cli_exec: Optional[str] = None timeout: Optional[int] = None __base_params: Optional[str] = None - map_params = {'json_mode': 'json', 'await_mode': 'await', 'hash_type': 'hash'} + map_params = {'json_mode': 'json', 'await_mode': 'await', 'hash_type': 'hash', 'doc_type': 'type'} - def __init__(self, neofs_cli_exec: str, timeout: int = 30, **base_params): + def __init__(self, neofs_cli_exec: str, timeout: int, **base_params): self.neofs_cli_exec = neofs_cli_exec self.timeout = timeout self.__base_params = ' '.join([f'--{param} {value}' for param, value in base_params.items() if value]) diff --git a/robot/resources/lib/python_keywords/container.py b/robot/resources/lib/python_keywords/container.py index 54b6e93e..cf9850fe 100644 --- a/robot/resources/lib/python_keywords/container.py +++ b/robot/resources/lib/python_keywords/container.py @@ -9,7 +9,7 @@ from time import sleep from typing import Optional, Union import json_transformers -from cli import NeofsCli +from cli_utils import NeofsCli from common import NEOFS_ENDPOINT, WALLET_CONFIG from robot.api import logger from robot.api.deco import keyword diff --git a/robot/resources/lib/python_keywords/neofs_verbs.py b/robot/resources/lib/python_keywords/neofs_verbs.py index 315c55fc..d81f75ee 100644 --- a/robot/resources/lib/python_keywords/neofs_verbs.py +++ b/robot/resources/lib/python_keywords/neofs_verbs.py @@ -11,7 +11,7 @@ import uuid from typing import Optional import json_transformers -from cli import NeofsCli +from cli_utils import NeofsCli from common import ASSETS_DIR, NEOFS_ENDPOINT, NEOFS_NETMAP, WALLET_CONFIG from robot.api import logger from robot.api.deco import keyword diff --git a/robot/variables/common.py b/robot/variables/common.py index 862c1e5b..bc6bb420 100644 --- a/robot/variables/common.py +++ b/robot/variables/common.py @@ -107,7 +107,8 @@ STORAGE_NODE_SSH_PRIVATE_KEY_PATH = os.getenv("STORAGE_NODE_SSH_PRIVATE_KEY_PATH STORAGE_NODE_BIN_PATH = os.getenv("STORAGE_NODE_BIN_PATH", f"{DEVENV_PATH}/vendor") # Path and config for neofs-adm utility. Optional if tests are running against devenv -NEOFS_ADM_EXEC = os.getenv("NEOFS_ADM_EXEC") +NEOFS_ADM_EXEC = os.getenv('NEOFS_ADM_EXEC', 'neofs-adm') + NEOFS_ADM_CONFIG_PATH = os.getenv("NEOFS_ADM_CONFIG_PATH") INFRASTRUCTURE_TYPE = os.getenv("INFRASTRUCTURE_TYPE", "LOCAL_DEVENV")