forked from TrueCloudLab/frostfs-testcases
Use neofs-testlib
Signed-off-by: Vladimir Avdeev <v.avdeev@yadro.com>
This commit is contained in:
parent
31d43fbba9
commit
e63db788c5
46 changed files with 889 additions and 1992 deletions
|
@ -11,9 +11,10 @@ from typing import Any, Dict, List, Optional, Union
|
|||
import allure
|
||||
import base58
|
||||
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
|
||||
from neofs_testlib.cli import NeofsCli
|
||||
from neofs_testlib.shell import Shell
|
||||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
EACL_LIFETIME = 100500
|
||||
|
@ -76,7 +77,8 @@ class EACLFilters:
|
|||
return (
|
||||
",".join(
|
||||
[
|
||||
f"{filter.header_type.value}:{filter.key}{filter.match_type.value}{filter.value}"
|
||||
f"{filter.header_type.value}:"
|
||||
f"{filter.key}{filter.match_type.value}{filter.value}"
|
||||
for filter in self.filters
|
||||
]
|
||||
)
|
||||
|
@ -115,22 +117,22 @@ class EACLRule:
|
|||
|
||||
|
||||
@allure.title("Get extended ACL")
|
||||
def get_eacl(wallet_path: str, cid: str) -> Optional[str]:
|
||||
cli = NeofsCli(config=WALLET_CONFIG)
|
||||
def get_eacl(wallet_path: str, cid: str, shell: Shell) -> Optional[str]:
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
|
||||
try:
|
||||
output = cli.container.get_eacl(wallet=wallet_path, rpc_endpoint=NEOFS_ENDPOINT, cid=cid)
|
||||
except RuntimeError as exc:
|
||||
logger.info("Extended ACL table is not set for this container")
|
||||
logger.info(f"Got exception while getting eacl: {exc}")
|
||||
return None
|
||||
if "extended ACL table is not set for this container" in output:
|
||||
if "extended ACL table is not set for this container" in output.stdout:
|
||||
return None
|
||||
return output
|
||||
return output.stdout
|
||||
|
||||
|
||||
@allure.title("Set extended ACL")
|
||||
def set_eacl(wallet_path: str, cid: str, eacl_table_path: str) -> None:
|
||||
cli = NeofsCli(config=WALLET_CONFIG, timeout=60)
|
||||
def set_eacl(wallet_path: str, cid: str, eacl_table_path: str, shell: Shell) -> None:
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
|
||||
cli.container.set_eacl(
|
||||
wallet=wallet_path,
|
||||
rpc_endpoint=NEOFS_ENDPOINT,
|
||||
|
@ -145,9 +147,10 @@ def _encode_cid_for_eacl(cid: str) -> str:
|
|||
return base64.b64encode(cid_base58).decode("utf-8")
|
||||
|
||||
|
||||
def create_eacl(cid: str, rules_list: List[EACLRule]) -> str:
|
||||
def create_eacl(cid: str, rules_list: List[EACLRule], shell: Shell) -> str:
|
||||
table_file_path = f"{os.getcwd()}/{ASSETS_DIR}/eacl_table_{str(uuid.uuid4())}.json"
|
||||
NeofsCli().acl.extended_create(cid=cid, out=table_file_path, rule=rules_list)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
|
||||
cli.acl.extended_create(cid=cid, out=table_file_path, rule=rules_list)
|
||||
|
||||
with open(table_file_path, "r") as file:
|
||||
table_data = file.read()
|
||||
|
@ -157,7 +160,7 @@ def create_eacl(cid: str, rules_list: List[EACLRule]) -> str:
|
|||
|
||||
|
||||
def form_bearertoken_file(
|
||||
wif: str, cid: str, eacl_rule_list: List[Union[EACLRule, EACLPubKey]]
|
||||
wif: str, cid: str, eacl_rule_list: List[Union[EACLRule, EACLPubKey]], shell: Shell
|
||||
) -> str:
|
||||
"""
|
||||
This function fetches eACL for given <cid> on behalf of <wif>,
|
||||
|
@ -167,7 +170,7 @@ def form_bearertoken_file(
|
|||
enc_cid = _encode_cid_for_eacl(cid)
|
||||
file_path = f"{os.getcwd()}/{ASSETS_DIR}/{str(uuid.uuid4())}"
|
||||
|
||||
eacl = get_eacl(wif, cid)
|
||||
eacl = get_eacl(wif, cid, shell=shell)
|
||||
json_eacl = dict()
|
||||
if eacl:
|
||||
eacl = eacl.replace("eACL: ", "").split("Signature")[0]
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
from .adm import CompletionType, NeofsAdm
|
||||
from .cli import NeofsCli
|
|
@ -1,2 +0,0 @@
|
|||
from .adm import NeofsAdm
|
||||
from .completion_type import CompletionType
|
|
@ -1,49 +0,0 @@
|
|||
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 .storage_config import NeofsAdmStorageConfig
|
||||
from .subnet import NeofsAdmMorphSubnet
|
||||
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
|
||||
)
|
|
@ -1,30 +0,0 @@
|
|||
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)
|
|
@ -1,8 +0,0 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class CompletionType(Enum):
|
||||
BASH = "bash"
|
||||
ZHS = "zsh"
|
||||
FISH = "fish"
|
||||
POWERSHELL = "powershell"
|
|
@ -1,19 +0,0 @@
|
|||
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: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
|
@ -1,40 +0,0 @@
|
|||
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: value
|
||||
for param, value in locals().items()
|
||||
if param not in ["self", "doc_file"]
|
||||
},
|
||||
)
|
|
@ -1,308 +0,0 @@
|
|||
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: value for 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: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def dump_config(self, rpc_endpoint: str) -> str:
|
||||
"""
|
||||
Dump NeoFS network config.
|
||||
|
||||
Args:
|
||||
rpc_endpoint (str): N3 RPC node endpoint
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"morph dump-config",
|
||||
**{param: value for 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: value for 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: value for 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: value for 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: value for 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: value for 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:
|
||||
"""
|
||||
Initialize side chain network with smart-contracts and network settings.
|
||||
|
||||
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: value for 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: value for 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: value for 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=<n1>
|
||||
storage_price (int): StoragePrice=<n2>
|
||||
fee_per_byte (int): FeePerByte=<n3>
|
||||
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: value
|
||||
for 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: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
|
@ -1,20 +0,0 @@
|
|||
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: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
|
@ -1,210 +0,0 @@
|
|||
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: value for 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: value for 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: value for 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: value for 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: value for 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: value for 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: value for 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: value for 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: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
|
@ -1,12 +0,0 @@
|
|||
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)
|
|
@ -1 +0,0 @@
|
|||
from .cli import NeofsCli
|
|
@ -1,29 +0,0 @@
|
|||
from typing import Optional
|
||||
|
||||
from cli_utils.cli_command import NeofsCliCommand
|
||||
|
||||
|
||||
class NeofsCliAccounting(NeofsCliCommand):
|
||||
def balance(
|
||||
self,
|
||||
wallet: Optional[str] = None,
|
||||
rpc_endpoint: Optional[str] = None,
|
||||
address: Optional[str] = None,
|
||||
owner: Optional[str] = None,
|
||||
) -> str:
|
||||
"""Get internal balance of NeoFS account
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
owner: owner of balance account (omit to use owner from private key)
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
|
||||
"""
|
||||
return self._execute(
|
||||
"accounting balance",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
|
@ -1,49 +0,0 @@
|
|||
from typing import Optional
|
||||
|
||||
from cli_utils.cli_command import NeofsCliCommand
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Rule consist of these blocks: <action> <operation> [<filter1> ...] [<target1> ...]
|
||||
Action is 'allow' or 'deny'.
|
||||
Operation is an object service verb: 'get', 'head', 'put', 'search', 'delete', 'getrange', or 'getrangehash'.
|
||||
|
||||
Filter consists of <typ>:<key><match><value>
|
||||
Typ is 'obj' for object applied filter or 'req' for request applied filter.
|
||||
Key is a valid unicode string corresponding to object or request header key.
|
||||
Well-known system object headers start with '$Object:' prefix.
|
||||
User defined headers start without prefix.
|
||||
Read more about filter keys at:
|
||||
http://github.com/nspcc-dev/neofs-api/blob/master/proto-docs/acl.md#message-eaclrecordfilter
|
||||
Match is '=' for matching and '!=' for non-matching filter.
|
||||
Value is a valid unicode string corresponding to object or request header value.
|
||||
|
||||
Target is
|
||||
'user' for container owner,
|
||||
'system' for Storage nodes in container and Inner Ring nodes,
|
||||
'others' for all other request senders,
|
||||
'pubkey:<key1>,<key2>,...' for exact request sender, where <key> is a hex-encoded 33-byte public key.
|
||||
|
||||
When both '--rule' and '--file' arguments are used, '--rule' records will be placed higher in resulting
|
||||
extended ACL table.
|
||||
|
||||
Args:
|
||||
cid: Container ID
|
||||
file: Read list of extended ACL table records from from text file
|
||||
out: Save JSON formatted extended ACL table in file
|
||||
rule: Extended ACL table record to apply
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
|
||||
"""
|
||||
return self._execute(
|
||||
"acl extended create",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
|
@ -1,33 +0,0 @@
|
|||
from typing import Optional
|
||||
|
||||
from cli_utils.cli.netmap import NeofsCliNetmap
|
||||
from common import NEOFS_CLI_EXEC
|
||||
|
||||
from .accounting import NeofsCliAccounting
|
||||
from .acl import NeofsCliACL
|
||||
from .container import NeofsCliContainer
|
||||
from .object import NeofsCliObject
|
||||
from .version import NeofsCliVersion
|
||||
|
||||
|
||||
class NeofsCli:
|
||||
accounting: NeofsCliAccounting
|
||||
acl: NeofsCliACL
|
||||
container: NeofsCliContainer
|
||||
netmap: NeofsCliNetmap
|
||||
object: NeofsCliObject
|
||||
version: NeofsCliVersion
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
neofs_cli_exec_path: Optional[str] = None,
|
||||
config: Optional[str] = None,
|
||||
timeout: int = 30,
|
||||
):
|
||||
neofs_cli_exec_path = neofs_cli_exec_path or NEOFS_CLI_EXEC
|
||||
self.accounting = NeofsCliAccounting(neofs_cli_exec_path, timeout=timeout, config=config)
|
||||
self.acl = NeofsCliACL(neofs_cli_exec_path, timeout=timeout, config=config)
|
||||
self.container = NeofsCliContainer(neofs_cli_exec_path, timeout=timeout, config=config)
|
||||
self.netmap = NeofsCliNetmap(neofs_cli_exec_path, timeout=timeout, config=config)
|
||||
self.object = NeofsCliObject(neofs_cli_exec_path, timeout=timeout, config=config)
|
||||
self.version = NeofsCliVersion(neofs_cli_exec_path, timeout=timeout, config=config)
|
|
@ -1,248 +0,0 @@
|
|||
from typing import Optional
|
||||
|
||||
from cli_utils.cli_command import NeofsCliCommand
|
||||
|
||||
|
||||
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,
|
||||
session: Optional[str] = None,
|
||||
subnet: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Create a new container and register it in the NeoFS.
|
||||
It will be stored in the sidechain when the Inner Ring accepts it.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
attributes: comma separated pairs of container attributes in form of Key1=Value1,Key2=Value2
|
||||
await_mode: block execution until container is persisted
|
||||
basic_acl: hex encoded basic ACL value or keywords like 'public-read-write', 'private',
|
||||
'eacl-public-read' (default "private")
|
||||
disable_timestamp: disable timestamp container attribute
|
||||
name: container name attribute
|
||||
nonce: UUIDv4 nonce value for container
|
||||
policy: QL-encoded or JSON-encoded placement policy or path to file with it
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
session: path to a JSON-encoded container session token
|
||||
subnet: string representation of container subnetwork
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"container create",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def delete(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
cid: str,
|
||||
address: Optional[str] = None,
|
||||
await_mode: bool = False,
|
||||
session: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
force: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
Delete an existing container.
|
||||
Only the owner of the container has permission to remove the container.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
await_mode: block execution until container is removed
|
||||
cid: container ID
|
||||
force: do not check whether container contains locks and remove immediately
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
session: path to a JSON-encoded container session token
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
|
||||
return self._execute(
|
||||
"container delete",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def get(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
cid: str,
|
||||
address: Optional[str] = None,
|
||||
await_mode: bool = False,
|
||||
to: Optional[str] = None,
|
||||
json_mode: bool = False,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get container field info.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
await_mode: block execution until container is removed
|
||||
cid: container ID
|
||||
json_mode: print or dump container in JSON format
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
to: path to dump encoded container
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"container get",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def get_eacl(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
cid: str,
|
||||
address: Optional[str] = None,
|
||||
await_mode: bool = False,
|
||||
to: Optional[str] = None,
|
||||
session: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get extended ACL table of container.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
await_mode: block execution until container is removed
|
||||
cid: container ID
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
to: path to dump encoded container
|
||||
session: path to a JSON-encoded container session token
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
|
||||
"""
|
||||
return self._execute(
|
||||
"container get-eacl",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def list(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
address: Optional[str] = None,
|
||||
owner: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
**params,
|
||||
) -> str:
|
||||
"""
|
||||
List all created containers.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
owner: owner of containers (omit to use owner from private key)
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"container list",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def list_objects(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
cid: str,
|
||||
address: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
List existing objects in container.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
cid: container ID
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"container list-objects",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def set_eacl(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
cid: str,
|
||||
address: Optional[str] = None,
|
||||
await_mode: bool = False,
|
||||
table: Optional[str] = None,
|
||||
session: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Set a new extended ACL table for the container.
|
||||
Container ID in the EACL table will be substituted with the ID from the CLI.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
await_mode: block execution until container is removed
|
||||
cid: container ID
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
session: path to a JSON-encoded container session token
|
||||
table: path to file with JSON or binary encoded EACL table
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"container set-eacl",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
|
@ -1,119 +0,0 @@
|
|||
from typing import Optional
|
||||
|
||||
from cli_utils.cli_command import NeofsCliCommand
|
||||
|
||||
|
||||
class NeofsCliNetmap(NeofsCliCommand):
|
||||
def epoch(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
address: Optional[str] = None,
|
||||
generate_key: bool = False,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get current epoch number.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
generate_key: generate new private key
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: path to the wallet or binary key
|
||||
xhdr: request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Raw command output
|
||||
"""
|
||||
return self._execute(
|
||||
"netmap epoch",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def netinfo(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
address: Optional[str] = None,
|
||||
generate_key: bool = False,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get information about NeoFS network.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
generate_key: generate new private key
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: path to the wallet or binary key
|
||||
xhdr: request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Raw command output
|
||||
"""
|
||||
return self._execute(
|
||||
"netmap netinfo",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def nodeinfo(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
address: Optional[str] = None,
|
||||
generate_key: bool = False,
|
||||
json: bool = False,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get target node info.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
generate_key: generate new private key
|
||||
json: print node info in JSON format
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: path to the wallet or binary key
|
||||
xhdr: request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Raw command output
|
||||
"""
|
||||
return self._execute(
|
||||
"netmap nodeinfo",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def snapshot(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
address: Optional[str] = None,
|
||||
generate_key: bool = False,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Request current local snapshot of the network map.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
generate_key: generate new private key
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: path to the wallet or binary key
|
||||
xhdr: request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Raw command output
|
||||
"""
|
||||
return self._execute(
|
||||
"netmap snapshot",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
|
@ -1,329 +0,0 @@
|
|||
from typing import Optional
|
||||
|
||||
from cli_utils.cli_command import NeofsCliCommand
|
||||
|
||||
|
||||
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:
|
||||
"""
|
||||
Delete object from NeoFS.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
bearer: File with signed JSON or binary encoded bearer token
|
||||
cid: Container ID
|
||||
oid: Object ID
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
session: path to a JSON-encoded container session token
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"object delete",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def get(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
address: Optional[str] = None,
|
||||
bearer: Optional[str] = None,
|
||||
file: Optional[str] = None,
|
||||
header: Optional[str] = None,
|
||||
no_progress: bool = False,
|
||||
raw: bool = False,
|
||||
session: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get object from NeoFS.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
bearer: File with signed JSON or binary encoded bearer token
|
||||
cid: Container ID
|
||||
file: File to write object payload to. Default: stdout.
|
||||
header: File to write header to. Default: stdout.
|
||||
no_progress: Do not show progress bar
|
||||
oid: Object ID
|
||||
raw: Set raw request option
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
session: path to a JSON-encoded container session token
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"object get",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def hash(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
address: Optional[str] = None,
|
||||
bearer: Optional[str] = None,
|
||||
range: Optional[str] = None,
|
||||
salt: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
hash_type: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get object hash.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
bearer: File with signed JSON or binary encoded bearer token
|
||||
cid: Container ID
|
||||
oid: Object ID
|
||||
range: Range to take hash from in the form offset1:length1,...
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
salt: Salt in hex format
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
hash_type: Hash type. Either 'sha256' or 'tz' (default "sha256")
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"object hash",
|
||||
**{
|
||||
param: value for param, value in locals().items() if param not in ["self", "params"]
|
||||
},
|
||||
)
|
||||
|
||||
def head(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
address: Optional[str] = None,
|
||||
bearer: Optional[str] = None,
|
||||
file: Optional[str] = None,
|
||||
json_mode: bool = False,
|
||||
main_only: bool = False,
|
||||
proto: bool = False,
|
||||
raw: bool = False,
|
||||
session: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get object header.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
bearer: File with signed JSON or binary encoded bearer token
|
||||
cid: Container ID
|
||||
file: File to write object payload to. Default: stdout.
|
||||
json_mode: Marshal output in JSON
|
||||
main_only: Return only main fields
|
||||
oid: Object ID
|
||||
proto: Marshal output in Protobuf
|
||||
raw: Set raw request option
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
session: path to a JSON-encoded container session token
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"object head",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def lock(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
lifetime: int,
|
||||
address: Optional[str] = None,
|
||||
bearer: Optional[str] = None,
|
||||
session: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Lock object in container.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
bearer: File with signed JSON or binary encoded bearer token
|
||||
cid: Container ID
|
||||
oid: Object ID
|
||||
lifetime: Object lifetime
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
session: path to a JSON-encoded container session token
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"object lock",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def put(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
cid: str,
|
||||
file: str,
|
||||
address: Optional[str] = None,
|
||||
attributes: Optional[dict] = None,
|
||||
bearer: Optional[str] = None,
|
||||
disable_filename: bool = False,
|
||||
disable_timestamp: bool = False,
|
||||
expire_at: Optional[int] = None,
|
||||
no_progress: bool = False,
|
||||
notify: Optional[str] = None,
|
||||
session: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Put object to NeoFS.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
attributes: User attributes in form of Key1=Value1,Key2=Value2
|
||||
bearer: File with signed JSON or binary encoded bearer token
|
||||
cid: Container ID
|
||||
disable_filename: Do not set well-known filename attribute
|
||||
disable_timestamp: Do not set well-known timestamp attribute
|
||||
expire_at: Last epoch in the life of the object
|
||||
file: File with object payload
|
||||
no_progress: Do not show progress bar
|
||||
notify: Object notification in the form of *epoch*:*topic*; '-' topic means using default
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
session: path to a JSON-encoded container session token
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"object put",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def range(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
range: str,
|
||||
address: Optional[str] = None,
|
||||
bearer: Optional[str] = None,
|
||||
file: Optional[str] = None,
|
||||
json_mode: bool = False,
|
||||
raw: bool = False,
|
||||
session: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Get payload range data of an object.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
bearer: File with signed JSON or binary encoded bearer token
|
||||
cid: Container ID
|
||||
file: File to write object payload to. Default: stdout.
|
||||
json_mode: Marshal output in JSON
|
||||
oid: Object ID
|
||||
range: Range to take data from in the form offset:length
|
||||
raw: Set raw request option
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
session: path to a JSON-encoded container session token
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"object range",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def search(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
wallet: str,
|
||||
cid: str,
|
||||
address: Optional[str] = None,
|
||||
bearer: Optional[str] = None,
|
||||
filters: Optional[list] = None,
|
||||
oid: Optional[str] = None,
|
||||
phy: bool = False,
|
||||
root: bool = False,
|
||||
session: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Search object.
|
||||
|
||||
Args:
|
||||
address: address of wallet account
|
||||
bearer: File with signed JSON or binary encoded bearer token
|
||||
cid: Container ID
|
||||
filters: Repeated filter expressions or files with protobuf JSON
|
||||
oid: Object ID
|
||||
phy: Search physically stored objects
|
||||
root: Search for user objects
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
session: path to a JSON-encoded container session token
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
"""
|
||||
return self._execute(
|
||||
"object search",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
|
@ -1,12 +0,0 @@
|
|||
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)
|
|
@ -1,56 +0,0 @@
|
|||
from typing import Optional
|
||||
|
||||
from cli_helpers import _cmd_run
|
||||
|
||||
|
||||
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",
|
||||
"doc_type": "type",
|
||||
}
|
||||
|
||||
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]
|
||||
)
|
||||
|
||||
def _format_command(self, command: str, **params) -> str:
|
||||
param_str = []
|
||||
for param, value in params.items():
|
||||
if param in self.map_params.keys():
|
||||
param = self.map_params[param]
|
||||
param = param.replace("_", "-")
|
||||
if not value:
|
||||
continue
|
||||
if isinstance(value, bool):
|
||||
param_str.append(f"--{param}")
|
||||
elif isinstance(value, int):
|
||||
param_str.append(f"--{param} {value}")
|
||||
elif isinstance(value, list):
|
||||
for value_item in value:
|
||||
val_str = str(value_item).replace("'", "\\'")
|
||||
param_str.append(f"--{param} '{val_str}'")
|
||||
elif isinstance(value, dict):
|
||||
param_str.append(
|
||||
f"--{param} '{','.join(f'{key}={val}' for key, val in value.items())}'"
|
||||
)
|
||||
else:
|
||||
if "'" in str(value):
|
||||
value_str = str(value).replace('"', '\\"')
|
||||
param_str.append(f'--{param} "{value_str}"')
|
||||
else:
|
||||
param_str.append(f"--{param} '{value}'")
|
||||
|
||||
param_str = " ".join(param_str)
|
||||
|
||||
return f'{self.neofs_cli_exec} {self.__base_params} {command or ""} {param_str}'
|
||||
|
||||
def _execute(self, command: Optional[str], **params) -> str:
|
||||
return _cmd_run(self._format_command(command, **params), timeout=self.timeout)
|
|
@ -11,17 +11,24 @@
|
|||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
import allure
|
||||
import neofs_verbs
|
||||
from common import NEOFS_NETMAP, WALLET_CONFIG
|
||||
from neofs_testlib.shell import Shell
|
||||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
|
||||
|
||||
@allure.step("Get Link Object")
|
||||
def get_link_object(
|
||||
wallet: str, cid: str, oid: str, bearer_token: str = "", wallet_config: str = WALLET_CONFIG
|
||||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
shell: Shell,
|
||||
bearer_token: str = "",
|
||||
wallet_config: str = WALLET_CONFIG,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
|
@ -29,6 +36,7 @@ def get_link_object(
|
|||
are requested
|
||||
cid (str): Container ID which stores the Large Object
|
||||
oid (str): Large Object ID
|
||||
shell: executor for cli command
|
||||
bearer_token (optional, str): path to Bearer token file
|
||||
wallet_config (optional, str): path to the neofs-cli config file
|
||||
Returns:
|
||||
|
@ -42,6 +50,7 @@ def get_link_object(
|
|||
wallet,
|
||||
cid,
|
||||
oid,
|
||||
shell=shell,
|
||||
endpoint=node,
|
||||
is_raw=True,
|
||||
is_direct=True,
|
||||
|
@ -57,13 +66,14 @@ def get_link_object(
|
|||
|
||||
|
||||
@allure.step("Get Last Object")
|
||||
def get_last_object(wallet: str, cid: str, oid: str):
|
||||
def get_last_object(wallet: str, cid: str, oid: str, shell: Shell) -> Optional[str]:
|
||||
"""
|
||||
Args:
|
||||
wallet (str): path to the wallet on whose behalf the Storage Nodes
|
||||
are requested
|
||||
cid (str): Container ID which stores the Large Object
|
||||
oid (str): Large Object ID
|
||||
shell: executor for cli command
|
||||
Returns:
|
||||
(str): Last Object ID
|
||||
When no Last Object ID is found after all Storage Nodes polling,
|
||||
|
@ -72,7 +82,7 @@ def get_last_object(wallet: str, cid: str, oid: str):
|
|||
for node in NEOFS_NETMAP:
|
||||
try:
|
||||
resp = neofs_verbs.head_object(
|
||||
wallet, cid, oid, endpoint=node, is_raw=True, is_direct=True
|
||||
wallet, cid, oid, shell=shell, endpoint=node, is_raw=True, is_direct=True
|
||||
)
|
||||
if resp["lastPart"]:
|
||||
return resp["lastPart"]
|
||||
|
|
|
@ -11,8 +11,9 @@ from typing import Optional, Union
|
|||
|
||||
import allure
|
||||
import json_transformers
|
||||
from cli_utils import NeofsCli
|
||||
from common import NEOFS_ENDPOINT, WALLET_CONFIG
|
||||
from common import NEOFS_CLI_EXEC, NEOFS_ENDPOINT, WALLET_CONFIG
|
||||
from neofs_testlib.cli import NeofsCli
|
||||
from neofs_testlib.shell import Shell
|
||||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
|
||||
|
@ -22,6 +23,7 @@ DEFAULT_PLACEMENT_RULE = "REP 2 IN X CBF 1 SELECT 4 FROM * AS X"
|
|||
@allure.step("Create Container")
|
||||
def create_container(
|
||||
wallet: str,
|
||||
shell: Shell,
|
||||
rule: str = DEFAULT_PLACEMENT_RULE,
|
||||
basic_acl: str = "",
|
||||
attributes: Optional[dict] = None,
|
||||
|
@ -46,6 +48,7 @@ def create_container(
|
|||
session_wallet(optional, str): a path to the wallet which signed
|
||||
the session token; this parameter makes sense
|
||||
when paired with `session_token`
|
||||
shell: executor for cli command
|
||||
options (optional, dict): any other options to pass to the call
|
||||
name (optional, str): container name attribute
|
||||
await_mode (bool): block execution until container is persisted
|
||||
|
@ -55,7 +58,7 @@ def create_container(
|
|||
(str): CID of the created container
|
||||
"""
|
||||
|
||||
cli = NeofsCli(config=WALLET_CONFIG, timeout=60)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
|
||||
output = cli.container.create(
|
||||
rpc_endpoint=NEOFS_ENDPOINT,
|
||||
wallet=session_wallet if session_wallet else wallet,
|
||||
|
@ -68,19 +71,21 @@ def create_container(
|
|||
**options or {},
|
||||
)
|
||||
|
||||
cid = _parse_cid(output)
|
||||
cid = _parse_cid(output.stdout)
|
||||
|
||||
logger.info("Container created; waiting until it is persisted in the sidechain")
|
||||
|
||||
if wait_for_creation:
|
||||
wait_for_container_creation(wallet, cid)
|
||||
wait_for_container_creation(wallet, cid, shell=shell)
|
||||
|
||||
return cid
|
||||
|
||||
|
||||
def wait_for_container_creation(wallet: str, cid: str, attempts: int = 15, sleep_interval: int = 1):
|
||||
def wait_for_container_creation(
|
||||
wallet: str, cid: str, shell: Shell, attempts: int = 15, sleep_interval: int = 1
|
||||
):
|
||||
for _ in range(attempts):
|
||||
containers = list_containers(wallet)
|
||||
containers = list_containers(wallet, shell=shell)
|
||||
if cid in containers:
|
||||
return
|
||||
logger.info(f"There is no {cid} in {containers} yet; sleep {sleep_interval} and continue")
|
||||
|
@ -90,10 +95,12 @@ def wait_for_container_creation(wallet: str, cid: str, attempts: int = 15, sleep
|
|||
)
|
||||
|
||||
|
||||
def wait_for_container_deletion(wallet: str, cid: str, attempts: int = 30, sleep_interval: int = 1):
|
||||
def wait_for_container_deletion(
|
||||
wallet: str, cid: str, shell: Shell, attempts: int = 30, sleep_interval: int = 1
|
||||
):
|
||||
for _ in range(attempts):
|
||||
try:
|
||||
get_container(wallet, cid)
|
||||
get_container(wallet, cid, shell=shell)
|
||||
sleep(sleep_interval)
|
||||
continue
|
||||
except Exception as err:
|
||||
|
@ -104,42 +111,46 @@ def wait_for_container_deletion(wallet: str, cid: str, attempts: int = 30, sleep
|
|||
|
||||
|
||||
@allure.step("List Containers")
|
||||
def list_containers(wallet: str) -> list[str]:
|
||||
def list_containers(wallet: str, shell: Shell) -> list[str]:
|
||||
"""
|
||||
A wrapper for `neofs-cli container list` call. It returns all the
|
||||
available containers for the given wallet.
|
||||
Args:
|
||||
wallet (str): a wallet on whose behalf we list the containers
|
||||
shell: executor for cli command
|
||||
Returns:
|
||||
(list): list of containers
|
||||
"""
|
||||
cli = NeofsCli(config=WALLET_CONFIG)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
|
||||
output = cli.container.list(rpc_endpoint=NEOFS_ENDPOINT, wallet=wallet)
|
||||
logger.info(f"Containers: \n{output}")
|
||||
return output.split()
|
||||
return output.stdout.split()
|
||||
|
||||
|
||||
@allure.step("Get Container")
|
||||
def get_container(wallet: str, cid: str, json_mode: bool = True) -> Union[dict, str]:
|
||||
def get_container(
|
||||
wallet: str, cid: str, shell: Shell, json_mode: bool = True,
|
||||
) -> Union[dict, str]:
|
||||
"""
|
||||
A wrapper for `neofs-cli container get` call. It extracts container's
|
||||
attributes and rearranges them into a more compact view.
|
||||
Args:
|
||||
wallet (str): path to a wallet on whose behalf we get the container
|
||||
cid (str): ID of the container to get
|
||||
shell: executor for cli command
|
||||
json_mode (bool): return container in JSON format
|
||||
Returns:
|
||||
(dict, str): dict of container attributes
|
||||
"""
|
||||
cli = NeofsCli(config=WALLET_CONFIG)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
|
||||
output = cli.container.get(
|
||||
rpc_endpoint=NEOFS_ENDPOINT, wallet=wallet, cid=cid, json_mode=json_mode
|
||||
)
|
||||
|
||||
if not json_mode:
|
||||
return output
|
||||
return output.stdout
|
||||
|
||||
container_info = json.loads(output)
|
||||
container_info = json.loads(output.stdout)
|
||||
attributes = dict()
|
||||
for attr in container_info["attributes"]:
|
||||
attributes[attr["key"]] = attr["value"]
|
||||
|
@ -151,17 +162,20 @@ def get_container(wallet: str, cid: str, json_mode: bool = True) -> Union[dict,
|
|||
@allure.step("Delete Container")
|
||||
# TODO: make the error message about a non-found container more user-friendly
|
||||
# https://github.com/nspcc-dev/neofs-contract/issues/121
|
||||
def delete_container(wallet: str, cid: str, force: bool = False) -> None:
|
||||
def delete_container(
|
||||
wallet: str, cid: str, shell: Shell, force: bool = False
|
||||
) -> None:
|
||||
"""
|
||||
A wrapper for `neofs-cli container delete` call.
|
||||
Args:
|
||||
wallet (str): path to a wallet on whose behalf we delete the container
|
||||
cid (str): ID of the container to delete
|
||||
shell: executor for cli command
|
||||
force (bool): do not check whether container contains locks and remove immediately
|
||||
This function doesn't return anything.
|
||||
"""
|
||||
|
||||
cli = NeofsCli(config=WALLET_CONFIG)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
|
||||
cli.container.delete(wallet=wallet, cid=cid, rpc_endpoint=NEOFS_ENDPOINT, force=force)
|
||||
|
||||
|
||||
|
@ -183,6 +197,7 @@ def _parse_cid(output: str) -> str:
|
|||
# taking first line from command's output
|
||||
first_line = output.split("\n")[0]
|
||||
except Exception:
|
||||
first_line = ""
|
||||
logger.error(f"Got empty output: {output}")
|
||||
splitted = first_line.split(": ")
|
||||
if len(splitted) != 2:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from acl import EACLOperation
|
||||
from neofs_testlib.shell import Shell
|
||||
from python_keywords.object_access import (
|
||||
can_delete_object,
|
||||
can_get_head_object,
|
||||
|
@ -17,17 +18,18 @@ def check_full_access_to_container(
|
|||
cid: str,
|
||||
oid: str,
|
||||
file_name: str,
|
||||
shell: Shell,
|
||||
bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
):
|
||||
assert can_put_object(wallet, cid, file_name, bearer, wallet_config, xhdr)
|
||||
assert can_get_head_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert can_get_range_of_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert can_get_range_hash_of_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert can_search_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert can_get_object(wallet, cid, oid, file_name, bearer, wallet_config, xhdr)
|
||||
assert can_delete_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert can_put_object(wallet, cid, file_name, shell, bearer, wallet_config, xhdr)
|
||||
assert can_get_head_object(wallet, cid, oid, shell, bearer, wallet_config, xhdr)
|
||||
assert can_get_range_of_object(wallet, cid, oid, shell, bearer, wallet_config, xhdr)
|
||||
assert can_get_range_hash_of_object(wallet, cid, oid, shell, bearer, wallet_config, xhdr)
|
||||
assert can_search_object(wallet, cid, shell, oid, bearer, wallet_config, xhdr)
|
||||
assert can_get_object(wallet, cid, oid, file_name, shell, bearer, wallet_config, xhdr)
|
||||
assert can_delete_object(wallet, cid, oid, shell, bearer, wallet_config, xhdr)
|
||||
|
||||
|
||||
def check_no_access_to_container(
|
||||
|
@ -35,17 +37,18 @@ def check_no_access_to_container(
|
|||
cid: str,
|
||||
oid: str,
|
||||
file_name: str,
|
||||
shell: Shell,
|
||||
bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
):
|
||||
assert not can_put_object(wallet, cid, file_name, bearer, wallet_config, xhdr)
|
||||
assert not can_get_head_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert not can_get_range_of_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert not can_get_range_hash_of_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert not can_search_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert not can_get_object(wallet, cid, oid, file_name, bearer, wallet_config, xhdr)
|
||||
assert not can_delete_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert not can_put_object(wallet, cid, file_name, shell, bearer, wallet_config, xhdr)
|
||||
assert not can_get_head_object(wallet, cid, oid, shell, bearer, wallet_config, xhdr)
|
||||
assert not can_get_range_of_object(wallet, cid, oid, shell, bearer, wallet_config, xhdr)
|
||||
assert not can_get_range_hash_of_object(wallet, cid, oid, shell, bearer, wallet_config, xhdr)
|
||||
assert not can_search_object(wallet, cid, shell, oid, bearer, wallet_config, xhdr)
|
||||
assert not can_get_object(wallet, cid, oid, file_name, shell, bearer, wallet_config, xhdr)
|
||||
assert not can_delete_object(wallet, cid, oid, shell, bearer, wallet_config, xhdr)
|
||||
|
||||
|
||||
def check_custom_access_to_container(
|
||||
|
@ -53,6 +56,7 @@ def check_custom_access_to_container(
|
|||
cid: str,
|
||||
oid: str,
|
||||
file_name: str,
|
||||
shell: Shell,
|
||||
deny_operations: Optional[List[EACLOperation]] = None,
|
||||
ignore_operations: Optional[List[EACLOperation]] = None,
|
||||
bearer: Optional[str] = None,
|
||||
|
@ -64,31 +68,31 @@ def check_custom_access_to_container(
|
|||
checks: dict = {}
|
||||
if EACLOperation.PUT.value not in ignore_operations:
|
||||
checks[EACLOperation.PUT.value] = can_put_object(
|
||||
wallet, cid, file_name, bearer, wallet_config, xhdr
|
||||
wallet, cid, file_name, shell, bearer, wallet_config, xhdr
|
||||
)
|
||||
if EACLOperation.HEAD.value not in ignore_operations:
|
||||
checks[EACLOperation.HEAD.value] = can_get_head_object(
|
||||
wallet, cid, oid, bearer, wallet_config, xhdr
|
||||
wallet, cid, oid, shell, bearer, wallet_config, xhdr
|
||||
)
|
||||
if EACLOperation.GET_RANGE.value not in ignore_operations:
|
||||
checks[EACLOperation.GET_RANGE.value] = can_get_range_of_object(
|
||||
wallet, cid, oid, bearer, wallet_config, xhdr
|
||||
wallet, cid, oid, shell, bearer, wallet_config, xhdr
|
||||
)
|
||||
if EACLOperation.GET_RANGE_HASH.value not in ignore_operations:
|
||||
checks[EACLOperation.GET_RANGE_HASH.value] = can_get_range_hash_of_object(
|
||||
wallet, cid, oid, bearer, wallet_config, xhdr
|
||||
wallet, cid, oid, shell, bearer, wallet_config, xhdr
|
||||
)
|
||||
if EACLOperation.SEARCH.value not in ignore_operations:
|
||||
checks[EACLOperation.SEARCH.value] = can_search_object(
|
||||
wallet, cid, oid, bearer, wallet_config, xhdr
|
||||
wallet, cid, shell, oid, bearer, wallet_config, xhdr
|
||||
)
|
||||
if EACLOperation.GET.value not in ignore_operations:
|
||||
checks[EACLOperation.GET.value] = can_get_object(
|
||||
wallet, cid, oid, file_name, bearer, wallet_config, xhdr
|
||||
wallet, cid, oid, file_name, shell, bearer, wallet_config, xhdr
|
||||
)
|
||||
if EACLOperation.DELETE.value not in ignore_operations:
|
||||
checks[EACLOperation.DELETE.value] = can_delete_object(
|
||||
wallet, cid, oid, bearer, wallet_config, xhdr
|
||||
wallet, cid, oid, shell, bearer, wallet_config, xhdr
|
||||
)
|
||||
|
||||
failed_checks = [
|
||||
|
@ -109,6 +113,7 @@ def check_read_only_container(
|
|||
cid: str,
|
||||
oid: str,
|
||||
file_name: str,
|
||||
shell: Shell,
|
||||
bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
|
@ -122,4 +127,5 @@ def check_read_only_container(
|
|||
bearer=bearer,
|
||||
wallet_config=wallet_config,
|
||||
xhdr=xhdr,
|
||||
shell=shell,
|
||||
)
|
||||
|
|
|
@ -4,6 +4,7 @@ from typing import Optional
|
|||
|
||||
import allure
|
||||
from common import NEOFS_NETMAP_DICT
|
||||
from neofs_testlib.shell import Shell
|
||||
from python_keywords.node_management import node_healthcheck
|
||||
from storage_policy import get_nodes_with_object
|
||||
|
||||
|
@ -16,13 +17,14 @@ def wait_object_replication_on_nodes(
|
|||
cid: str,
|
||||
oid: str,
|
||||
expected_copies: int,
|
||||
shell: Shell,
|
||||
excluded_nodes: Optional[list[str]] = None,
|
||||
) -> list[str]:
|
||||
excluded_nodes = excluded_nodes or []
|
||||
sleep_interval, attempts = 10, 18
|
||||
nodes = []
|
||||
for __attempt in range(attempts):
|
||||
nodes = get_nodes_with_object(wallet, cid, oid, skip_nodes=excluded_nodes)
|
||||
nodes = get_nodes_with_object(wallet, cid, oid, shell=shell, skip_nodes=excluded_nodes)
|
||||
if len(nodes) == expected_copies:
|
||||
return nodes
|
||||
sleep(sleep_interval)
|
||||
|
|
|
@ -13,8 +13,9 @@ from typing import Optional
|
|||
|
||||
import allure
|
||||
import json_transformers
|
||||
from cli_utils import NeofsCli
|
||||
from common import ASSETS_DIR, NEOFS_ENDPOINT, NEOFS_NETMAP, WALLET_CONFIG
|
||||
from common import ASSETS_DIR, NEOFS_CLI_EXEC, NEOFS_ENDPOINT, NEOFS_NETMAP, WALLET_CONFIG
|
||||
from neofs_testlib.cli import NeofsCli
|
||||
from neofs_testlib.shell import Shell
|
||||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
|
||||
|
@ -24,6 +25,7 @@ def get_object(
|
|||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
shell: Shell,
|
||||
bearer_token: Optional[str] = None,
|
||||
write_object: str = "",
|
||||
endpoint: str = "",
|
||||
|
@ -39,6 +41,7 @@ def get_object(
|
|||
wallet (str): wallet on whose behalf GET is done
|
||||
cid (str): ID of Container where we get the Object from
|
||||
oid (str): Object ID
|
||||
shell: executor for cli command
|
||||
bearer_token (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
write_object (optional, str): path to downloaded file, appends to `--file` key
|
||||
endpoint (optional, str): NeoFS endpoint to send request to, appends to `--rpc-endpoint` key
|
||||
|
@ -50,7 +53,6 @@ def get_object(
|
|||
(str): path to downloaded file
|
||||
"""
|
||||
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
if not write_object:
|
||||
write_object = str(uuid.uuid4())
|
||||
file_path = f"{ASSETS_DIR}/{write_object}"
|
||||
|
@ -58,7 +60,7 @@ def get_object(
|
|||
if not endpoint:
|
||||
endpoint = random.sample(NEOFS_NETMAP, 1)[0]
|
||||
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, wallet_config or WALLET_CONFIG)
|
||||
cli.object.get(
|
||||
rpc_endpoint=endpoint or NEOFS_ENDPOINT,
|
||||
wallet=wallet,
|
||||
|
@ -74,14 +76,14 @@ def get_object(
|
|||
return file_path
|
||||
|
||||
|
||||
# TODO: make `bearer_token` optional
|
||||
@allure.step("Get Range Hash")
|
||||
def get_range_hash(
|
||||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
bearer_token: str,
|
||||
range_cut: str,
|
||||
shell: Shell,
|
||||
bearer_token: Optional[str] = None,
|
||||
endpoint: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
|
@ -93,6 +95,7 @@ def get_range_hash(
|
|||
wallet (str): wallet on whose behalf GETRANGEHASH is done
|
||||
cid (str): ID of Container where we get the Object from
|
||||
oid (str): Object ID
|
||||
shell: executor for cli command
|
||||
bearer_token (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
range_cut (str): Range to take hash from in the form offset1:length1,...,
|
||||
value to pass to the `--range` parameter
|
||||
|
@ -103,8 +106,7 @@ def get_range_hash(
|
|||
None
|
||||
"""
|
||||
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, wallet_config or WALLET_CONFIG)
|
||||
output = cli.object.hash(
|
||||
rpc_endpoint=endpoint or NEOFS_ENDPOINT,
|
||||
wallet=wallet,
|
||||
|
@ -116,7 +118,7 @@ def get_range_hash(
|
|||
)
|
||||
|
||||
# cutting off output about range offset and length
|
||||
return output.split(":")[1].strip()
|
||||
return output.stdout.split(":")[1].strip()
|
||||
|
||||
|
||||
@allure.step("Put object")
|
||||
|
@ -124,7 +126,8 @@ def put_object(
|
|||
wallet: str,
|
||||
path: str,
|
||||
cid: str,
|
||||
bearer: str = "",
|
||||
shell: Shell,
|
||||
bearer: Optional[str] = None,
|
||||
attributes: Optional[dict] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
endpoint: Optional[str] = None,
|
||||
|
@ -140,6 +143,7 @@ def put_object(
|
|||
wallet (str): wallet on whose behalf PUT is done
|
||||
path (str): path to file to be PUT
|
||||
cid (str): ID of Container where we get the Object from
|
||||
shell: executor for cli command
|
||||
bearer (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
attributes (optional, str): User attributes in form of Key1=Value1,Key2=Value2
|
||||
endpoint(optional, str): NeoFS endpoint to send request to
|
||||
|
@ -151,13 +155,12 @@ def put_object(
|
|||
Returns:
|
||||
(str): ID of uploaded Object
|
||||
"""
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
if not endpoint:
|
||||
endpoint = random.sample(NEOFS_NETMAP, 1)[0]
|
||||
if not endpoint:
|
||||
logger.info(f"---DEB:\n{NEOFS_NETMAP}")
|
||||
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, wallet_config or WALLET_CONFIG)
|
||||
output = cli.object.put(
|
||||
rpc_endpoint=endpoint,
|
||||
wallet=wallet,
|
||||
|
@ -172,7 +175,7 @@ def put_object(
|
|||
)
|
||||
|
||||
# splitting CLI output to lines and taking the penultimate line
|
||||
id_str = output.strip().split("\n")[-2]
|
||||
id_str = output.stdout.strip().split("\n")[-2]
|
||||
oid = id_str.split(":")[1]
|
||||
return oid.strip()
|
||||
|
||||
|
@ -182,6 +185,7 @@ def delete_object(
|
|||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
shell: Shell,
|
||||
endpoint: Optional[str] = None,
|
||||
bearer: str = "",
|
||||
wallet_config: Optional[str] = None,
|
||||
|
@ -195,6 +199,7 @@ def delete_object(
|
|||
wallet (str): wallet on whose behalf DELETE is done
|
||||
cid (str): ID of Container where we get the Object from
|
||||
oid (str): ID of Object we are going to delete
|
||||
shell: executor for cli command
|
||||
bearer (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
endpoint (optional, str): NeoFS endpoint to send request to, appends to `--rpc-endpoint` key
|
||||
wallet_config(optional, str): path to the wallet config
|
||||
|
@ -203,8 +208,7 @@ def delete_object(
|
|||
Returns:
|
||||
(str): Tombstone ID
|
||||
"""
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, wallet_config or WALLET_CONFIG)
|
||||
output = cli.object.delete(
|
||||
rpc_endpoint=endpoint or NEOFS_ENDPOINT,
|
||||
wallet=wallet,
|
||||
|
@ -215,7 +219,7 @@ def delete_object(
|
|||
session=session,
|
||||
)
|
||||
|
||||
id_str = output.split("\n")[1]
|
||||
id_str = output.stdout.split("\n")[1]
|
||||
tombstone = id_str.split(":")[1]
|
||||
return tombstone.strip()
|
||||
|
||||
|
@ -226,6 +230,7 @@ def get_range(
|
|||
cid: str,
|
||||
oid: str,
|
||||
range_cut: str,
|
||||
shell: Shell,
|
||||
endpoint: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None,
|
||||
bearer: str = "",
|
||||
|
@ -240,6 +245,7 @@ def get_range(
|
|||
cid (str): ID of Container where we get the Object from
|
||||
oid (str): ID of Object we are going to request
|
||||
range_cut (str): range to take data from in the form offset:length
|
||||
shell: executor for cli command
|
||||
endpoint (optional, str): NeoFS endpoint to send request to, appends to `--rpc-endpoint` key
|
||||
bearer (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
wallet_config(optional, str): path to the wallet config
|
||||
|
@ -248,10 +254,9 @@ def get_range(
|
|||
Returns:
|
||||
(str, bytes) - path to the file with range content and content of this file as bytes
|
||||
"""
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
range_file = f"{ASSETS_DIR}/{uuid.uuid4()}"
|
||||
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, wallet_config or WALLET_CONFIG)
|
||||
cli.object.range(
|
||||
rpc_endpoint=endpoint or NEOFS_ENDPOINT,
|
||||
wallet=wallet,
|
||||
|
@ -273,6 +278,7 @@ def get_range(
|
|||
def search_object(
|
||||
wallet: str,
|
||||
cid: str,
|
||||
shell: Shell,
|
||||
bearer: str = "",
|
||||
endpoint: Optional[str] = None,
|
||||
filters: Optional[dict] = None,
|
||||
|
@ -287,6 +293,7 @@ def search_object(
|
|||
Args:
|
||||
wallet (str): wallet on whose behalf SEARCH is done
|
||||
cid (str): ID of Container where we get the Object from
|
||||
shell: executor for cli command
|
||||
bearer (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
endpoint (optional, str): NeoFS endpoint to send request to, appends to `--rpc-endpoint` key
|
||||
filters (optional, dict): key=value pairs to filter Objects
|
||||
|
@ -298,8 +305,7 @@ def search_object(
|
|||
(list): list of found ObjectIDs
|
||||
"""
|
||||
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, wallet_config or WALLET_CONFIG)
|
||||
output = cli.object.search(
|
||||
rpc_endpoint=endpoint or NEOFS_ENDPOINT,
|
||||
wallet=wallet,
|
||||
|
@ -312,7 +318,7 @@ def search_object(
|
|||
session=session,
|
||||
)
|
||||
|
||||
found_objects = re.findall(r"(\w{43,44})", output)
|
||||
found_objects = re.findall(r"(\w{43,44})", output.stdout)
|
||||
|
||||
if expected_objects_list:
|
||||
if sorted(found_objects) == sorted(expected_objects_list):
|
||||
|
@ -334,7 +340,8 @@ def head_object(
|
|||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
bearer_token: str = "",
|
||||
shell: Shell,
|
||||
bearer: str = "",
|
||||
xhdr: Optional[dict] = None,
|
||||
endpoint: Optional[str] = None,
|
||||
json_output: bool = True,
|
||||
|
@ -350,7 +357,8 @@ def head_object(
|
|||
wallet (str): wallet on whose behalf HEAD is done
|
||||
cid (str): ID of Container where we get the Object from
|
||||
oid (str): ObjectID to HEAD
|
||||
bearer_token (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
shell: executor for cli command
|
||||
bearer (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
endpoint(optional, str): NeoFS endpoint to send request to
|
||||
json_output(optional, bool): return reponse in JSON format or not; this flag
|
||||
turns into `--json` key
|
||||
|
@ -368,14 +376,13 @@ def head_object(
|
|||
(str): HEAD response as a plain text
|
||||
"""
|
||||
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, wallet_config or WALLET_CONFIG)
|
||||
output = cli.object.head(
|
||||
rpc_endpoint=endpoint or NEOFS_ENDPOINT,
|
||||
wallet=wallet,
|
||||
cid=cid,
|
||||
oid=oid,
|
||||
bearer=bearer_token,
|
||||
bearer=bearer,
|
||||
json_mode=json_output,
|
||||
raw=is_raw,
|
||||
ttl=1 if is_direct else None,
|
||||
|
@ -387,15 +394,15 @@ def head_object(
|
|||
return output
|
||||
|
||||
try:
|
||||
decoded = json.loads(output)
|
||||
decoded = json.loads(output.stdout)
|
||||
except Exception as exc:
|
||||
# If we failed to parse output as JSON, the cause might be
|
||||
# the plain text string in the beginning of the output.
|
||||
# Here we cut off first string and try to parse again.
|
||||
logger.info(f"failed to parse output: {exc}")
|
||||
logger.info("parsing output in another way")
|
||||
fst_line_idx = output.find("\n")
|
||||
decoded = json.loads(output[fst_line_idx:])
|
||||
fst_line_idx = output.stdout.find("\n")
|
||||
decoded = json.loads(output.stdout[fst_line_idx:])
|
||||
|
||||
# If response is Complex Object header, it has `splitId` key
|
||||
if "splitId" in decoded.keys():
|
||||
|
|
|
@ -8,10 +8,17 @@ from dataclasses import dataclass
|
|||
from typing import Optional
|
||||
|
||||
import allure
|
||||
from cli_utils.cli.cli import NeofsCli
|
||||
from common import MORPH_BLOCK_TIME, NEOFS_NETMAP_DICT, STORAGE_WALLET_CONFIG, STORAGE_WALLET_PASS
|
||||
from common import (
|
||||
MORPH_BLOCK_TIME,
|
||||
NEOFS_CLI_EXEC,
|
||||
NEOFS_NETMAP_DICT,
|
||||
STORAGE_WALLET_CONFIG,
|
||||
STORAGE_WALLET_PASS,
|
||||
)
|
||||
from data_formatters import get_wallet_public_key
|
||||
from epoch import tick_epoch
|
||||
from neofs_testlib.cli import NeofsCli
|
||||
from neofs_testlib.shell import Shell
|
||||
from service_helper import get_storage_service_helper
|
||||
from utility import parse_time
|
||||
|
||||
|
@ -90,8 +97,8 @@ def node_set_status(node_name: str, status: str, retries: int = 0) -> None:
|
|||
"""
|
||||
The function sets particular status for given node.
|
||||
Args:
|
||||
node_name str: node name for which status should be set.
|
||||
status str: online or offline.
|
||||
node_name: node name for which status should be set.
|
||||
status: online or offline.
|
||||
retries (optional, int): number of retry attempts if it didn't work from the first time
|
||||
"""
|
||||
command = f"control set-status --status {status}"
|
||||
|
@ -99,7 +106,7 @@ def node_set_status(node_name: str, status: str, retries: int = 0) -> None:
|
|||
|
||||
|
||||
@allure.step("Get netmap snapshot")
|
||||
def get_netmap_snapshot(node_name: str) -> str:
|
||||
def get_netmap_snapshot(node_name: str, shell: Shell) -> str:
|
||||
"""
|
||||
The function returns string representation of netmap snapshot.
|
||||
Args:
|
||||
|
@ -108,11 +115,11 @@ def get_netmap_snapshot(node_name: str) -> str:
|
|||
string representation of netmap
|
||||
"""
|
||||
node_info = NEOFS_NETMAP_DICT[node_name]
|
||||
cli = NeofsCli(config=STORAGE_WALLET_CONFIG)
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, config_file=STORAGE_WALLET_CONFIG)
|
||||
return cli.netmap.snapshot(
|
||||
rpc_endpoint=node_info["rpc"],
|
||||
wallet=node_info["wallet_path"],
|
||||
)
|
||||
).stdout
|
||||
|
||||
|
||||
@allure.step("Get shard list for node {node_name}")
|
||||
|
@ -160,7 +167,7 @@ def delete_node_data(node_name: str) -> None:
|
|||
|
||||
|
||||
@allure.step("Exclude node {node_to_exclude} from network map")
|
||||
def exclude_node_from_network_map(node_to_exclude: str, alive_node: str) -> None:
|
||||
def exclude_node_from_network_map(node_to_exclude: str, alive_node: str, shell: Shell) -> None:
|
||||
node_wallet_path = NEOFS_NETMAP_DICT[node_to_exclude]["wallet_path"]
|
||||
node_netmap_key = get_wallet_public_key(node_wallet_path, STORAGE_WALLET_PASS)
|
||||
|
||||
|
@ -169,31 +176,31 @@ def exclude_node_from_network_map(node_to_exclude: str, alive_node: str) -> None
|
|||
time.sleep(parse_time(MORPH_BLOCK_TIME))
|
||||
tick_epoch()
|
||||
|
||||
snapshot = get_netmap_snapshot(node_name=alive_node)
|
||||
snapshot = get_netmap_snapshot(node_name=alive_node, shell=shell)
|
||||
assert (
|
||||
node_netmap_key not in snapshot
|
||||
), f"Expected node with key {node_netmap_key} not in network map"
|
||||
|
||||
|
||||
@allure.step("Include node {node_to_include} into network map")
|
||||
def include_node_to_network_map(node_to_include: str, alive_node: str) -> None:
|
||||
def include_node_to_network_map(node_to_include: str, alive_node: str, shell: Shell) -> None:
|
||||
node_set_status(node_to_include, status="online")
|
||||
|
||||
time.sleep(parse_time(MORPH_BLOCK_TIME))
|
||||
tick_epoch()
|
||||
|
||||
check_node_in_map(node_to_include, alive_node)
|
||||
check_node_in_map(node_to_include, alive_node, shell=shell)
|
||||
|
||||
|
||||
@allure.step("Check node {node_name} in network map")
|
||||
def check_node_in_map(node_name: str, alive_node: Optional[str] = None) -> None:
|
||||
def check_node_in_map(node_name: str, shell: Shell, alive_node: Optional[str] = None) -> None:
|
||||
alive_node = alive_node or node_name
|
||||
node_wallet_path = NEOFS_NETMAP_DICT[node_name]["wallet_path"]
|
||||
node_netmap_key = get_wallet_public_key(node_wallet_path, STORAGE_WALLET_PASS)
|
||||
|
||||
logger.info(f"Node {node_name} netmap key: {node_netmap_key}")
|
||||
|
||||
snapshot = get_netmap_snapshot(node_name=alive_node)
|
||||
snapshot = get_netmap_snapshot(node_name=alive_node, shell=shell)
|
||||
assert node_netmap_key in snapshot, f"Expected node with key {node_netmap_key} in network map"
|
||||
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ from typing import Optional
|
|||
import allure
|
||||
from file_helper import get_file_hash
|
||||
from grpc_responses import OBJECT_ACCESS_DENIED, error_matches_status
|
||||
from neofs_testlib.shell import Shell
|
||||
from python_keywords.neofs_verbs import (
|
||||
delete_object,
|
||||
get_object,
|
||||
|
@ -21,6 +22,7 @@ def can_get_object(
|
|||
cid: str,
|
||||
oid: str,
|
||||
file_name: str,
|
||||
shell: Shell,
|
||||
bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
|
@ -28,7 +30,7 @@ def can_get_object(
|
|||
with allure.step("Try get object from container"):
|
||||
try:
|
||||
got_file_path = get_object(
|
||||
wallet, cid, oid, bearer_token=bearer, wallet_config=wallet_config, xhdr=xhdr
|
||||
wallet, cid, oid, bearer_token=bearer, wallet_config=wallet_config, xhdr=xhdr, shell=shell,
|
||||
)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(
|
||||
|
@ -43,6 +45,7 @@ def can_put_object(
|
|||
wallet: str,
|
||||
cid: str,
|
||||
file_name: str,
|
||||
shell: Shell,
|
||||
bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
|
@ -58,6 +61,7 @@ def can_put_object(
|
|||
wallet_config=wallet_config,
|
||||
xhdr=xhdr,
|
||||
attributes=attributes,
|
||||
shell=shell,
|
||||
)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(
|
||||
|
@ -71,13 +75,16 @@ def can_delete_object(
|
|||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
shell: Shell,
|
||||
bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> bool:
|
||||
with allure.step("Try delete object from container"):
|
||||
try:
|
||||
delete_object(wallet, cid, oid, bearer=bearer, wallet_config=wallet_config, xhdr=xhdr)
|
||||
delete_object(
|
||||
wallet, cid, oid, bearer=bearer, wallet_config=wallet_config, xhdr=xhdr, shell=shell
|
||||
)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(
|
||||
err, OBJECT_ACCESS_DENIED
|
||||
|
@ -90,6 +97,7 @@ def can_get_head_object(
|
|||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
shell: Shell,
|
||||
bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
|
@ -97,7 +105,13 @@ def can_get_head_object(
|
|||
with allure.step("Try get head of object"):
|
||||
try:
|
||||
head_object(
|
||||
wallet, cid, oid, bearer_token=bearer, wallet_config=wallet_config, xhdr=xhdr
|
||||
wallet,
|
||||
cid,
|
||||
oid,
|
||||
bearer=bearer,
|
||||
wallet_config=wallet_config,
|
||||
xhdr=xhdr,
|
||||
shell=shell,
|
||||
)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(
|
||||
|
@ -111,6 +125,7 @@ def can_get_range_of_object(
|
|||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
shell: Shell,
|
||||
bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
|
@ -125,6 +140,7 @@ def can_get_range_of_object(
|
|||
range_cut="0:10",
|
||||
wallet_config=wallet_config,
|
||||
xhdr=xhdr,
|
||||
shell=shell,
|
||||
)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(
|
||||
|
@ -138,6 +154,7 @@ def can_get_range_hash_of_object(
|
|||
wallet: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
shell: Shell,
|
||||
bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
|
@ -152,6 +169,7 @@ def can_get_range_hash_of_object(
|
|||
range_cut="0:10",
|
||||
wallet_config=wallet_config,
|
||||
xhdr=xhdr,
|
||||
shell=shell,
|
||||
)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(
|
||||
|
@ -164,6 +182,7 @@ def can_get_range_hash_of_object(
|
|||
def can_search_object(
|
||||
wallet: str,
|
||||
cid: str,
|
||||
shell: Shell,
|
||||
oid: Optional[str] = None,
|
||||
bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None,
|
||||
|
@ -171,7 +190,9 @@ def can_search_object(
|
|||
) -> bool:
|
||||
with allure.step("Try search object in container"):
|
||||
try:
|
||||
oids = search_object(wallet, cid, bearer=bearer, wallet_config=wallet_config, xhdr=xhdr)
|
||||
oids = search_object(
|
||||
wallet, cid, bearer=bearer, wallet_config=wallet_config, xhdr=xhdr, shell=shell
|
||||
)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(
|
||||
err, OBJECT_ACCESS_DENIED
|
||||
|
|
|
@ -6,19 +6,20 @@
|
|||
"""
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
from typing import List, Optional
|
||||
|
||||
import allure
|
||||
import complex_object_actions
|
||||
import neofs_verbs
|
||||
from common import NEOFS_NETMAP
|
||||
from grpc_responses import OBJECT_NOT_FOUND, error_matches_status
|
||||
from neofs_testlib.shell import Shell
|
||||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
|
||||
|
||||
@allure.step("Get Object Copies")
|
||||
def get_object_copies(complexity: str, wallet: str, cid: str, oid: str):
|
||||
def get_object_copies(complexity: str, wallet: str, cid: str, oid: str, shell: Shell) -> int:
|
||||
"""
|
||||
The function performs requests to all nodes of the container and
|
||||
finds out if they store a copy of the object. The procedure is
|
||||
|
@ -31,18 +32,19 @@ def get_object_copies(complexity: str, wallet: str, cid: str, oid: str):
|
|||
copies are got
|
||||
cid (str): ID of the container
|
||||
oid (str): ID of the Object
|
||||
shell: executor for cli command
|
||||
Returns:
|
||||
(int): the number of object copies in the container
|
||||
"""
|
||||
return (
|
||||
get_simple_object_copies(wallet, cid, oid)
|
||||
get_simple_object_copies(wallet, cid, oid, shell)
|
||||
if complexity == "Simple"
|
||||
else get_complex_object_copies(wallet, cid, oid)
|
||||
else get_complex_object_copies(wallet, cid, oid, shell)
|
||||
)
|
||||
|
||||
|
||||
@allure.step("Get Simple Object Copies")
|
||||
def get_simple_object_copies(wallet: str, cid: str, oid: str):
|
||||
def get_simple_object_copies(wallet: str, cid: str, oid: str, shell: Shell) -> int:
|
||||
"""
|
||||
To figure out the number of a simple object copies, only direct
|
||||
HEAD requests should be made to the every node of the container.
|
||||
|
@ -52,13 +54,16 @@ def get_simple_object_copies(wallet: str, cid: str, oid: str):
|
|||
copies are got
|
||||
cid (str): ID of the container
|
||||
oid (str): ID of the Object
|
||||
shell: executor for cli command
|
||||
Returns:
|
||||
(int): the number of object copies in the container
|
||||
"""
|
||||
copies = 0
|
||||
for node in NEOFS_NETMAP:
|
||||
try:
|
||||
response = neofs_verbs.head_object(wallet, cid, oid, endpoint=node, is_direct=True)
|
||||
response = neofs_verbs.head_object(
|
||||
wallet, cid, oid, shell=shell, endpoint=node, is_direct=True
|
||||
)
|
||||
if response:
|
||||
logger.info(f"Found object {oid} on node {node}")
|
||||
copies += 1
|
||||
|
@ -69,7 +74,7 @@ def get_simple_object_copies(wallet: str, cid: str, oid: str):
|
|||
|
||||
|
||||
@allure.step("Get Complex Object Copies")
|
||||
def get_complex_object_copies(wallet: str, cid: str, oid: str):
|
||||
def get_complex_object_copies(wallet: str, cid: str, oid: str, shell: Shell) -> int:
|
||||
"""
|
||||
To figure out the number of a complex object copies, we firstly
|
||||
need to retrieve its Last object. We consider that the number of
|
||||
|
@ -81,16 +86,18 @@ def get_complex_object_copies(wallet: str, cid: str, oid: str):
|
|||
copies are got
|
||||
cid (str): ID of the container
|
||||
oid (str): ID of the Object
|
||||
shell: executor for cli command
|
||||
Returns:
|
||||
(int): the number of object copies in the container
|
||||
"""
|
||||
last_oid = complex_object_actions.get_last_object(wallet, cid, oid)
|
||||
return get_simple_object_copies(wallet, cid, last_oid)
|
||||
last_oid = complex_object_actions.get_last_object(wallet, cid, oid, shell)
|
||||
assert last_oid, f"No Last Object for {cid}/{oid} found among all Storage Nodes"
|
||||
return get_simple_object_copies(wallet, cid, last_oid, shell)
|
||||
|
||||
|
||||
@allure.step("Get Nodes With Object")
|
||||
def get_nodes_with_object(
|
||||
wallet: str, cid: str, oid: str, skip_nodes: Optional[list[str]] = None
|
||||
wallet: str, cid: str, oid: str, shell: Shell, skip_nodes: Optional[list[str]] = None
|
||||
) -> list[str]:
|
||||
"""
|
||||
The function returns list of nodes which store
|
||||
|
@ -100,6 +107,7 @@ def get_nodes_with_object(
|
|||
we request the nodes
|
||||
cid (str): ID of the container which store the object
|
||||
oid (str): object ID
|
||||
shell: executor for cli command
|
||||
skip_nodes (list): list of nodes that should be excluded from check
|
||||
Returns:
|
||||
(list): nodes which store the object
|
||||
|
@ -111,7 +119,9 @@ def get_nodes_with_object(
|
|||
nodes_list = []
|
||||
for node in nodes_to_search:
|
||||
try:
|
||||
res = neofs_verbs.head_object(wallet, cid, oid, endpoint=node, is_direct=True)
|
||||
res = neofs_verbs.head_object(
|
||||
wallet, cid, oid, shell=shell, endpoint=node, is_direct=True
|
||||
)
|
||||
if res is not None:
|
||||
logger.info(f"Found object {oid} on node {node}")
|
||||
nodes_list.append(node)
|
||||
|
@ -122,7 +132,7 @@ def get_nodes_with_object(
|
|||
|
||||
|
||||
@allure.step("Get Nodes Without Object")
|
||||
def get_nodes_without_object(wallet: str, cid: str, oid: str):
|
||||
def get_nodes_without_object(wallet: str, cid: str, oid: str, shell: Shell) -> List[str]:
|
||||
"""
|
||||
The function returns list of nodes which do not store
|
||||
the given object.
|
||||
|
@ -131,13 +141,16 @@ def get_nodes_without_object(wallet: str, cid: str, oid: str):
|
|||
we request the nodes
|
||||
cid (str): ID of the container which store the object
|
||||
oid (str): object ID
|
||||
shell: executor for cli command
|
||||
Returns:
|
||||
(list): nodes which do not store the object
|
||||
"""
|
||||
nodes_list = []
|
||||
for node in NEOFS_NETMAP:
|
||||
try:
|
||||
res = neofs_verbs.head_object(wallet, cid, oid, endpoint=node, is_direct=True)
|
||||
res = neofs_verbs.head_object(
|
||||
wallet, cid, oid, shell=shell, endpoint=node, is_direct=True
|
||||
)
|
||||
if res is None:
|
||||
nodes_list.append(node)
|
||||
except Exception as err:
|
||||
|
|
|
@ -1,18 +1,16 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
import json
|
||||
|
||||
import allure
|
||||
import neofs_verbs
|
||||
from neo3 import wallet
|
||||
from neofs_testlib.shell import Shell
|
||||
from neofs_verbs import head_object
|
||||
|
||||
|
||||
@allure.step("Verify Head Tombstone")
|
||||
def verify_head_tombstone(wallet_path: str, cid: str, oid_ts: str, oid: str):
|
||||
header = neofs_verbs.head_object(wallet_path, cid, oid_ts)
|
||||
header = header["header"]
|
||||
def verify_head_tombstone(wallet_path: str, cid: str, oid_ts: str, oid: str, shell: Shell):
|
||||
header = head_object(wallet_path, cid, oid_ts, shell=shell)["header"]
|
||||
assert header["containerID"] == cid, "Tombstone Header CID is wrong"
|
||||
|
||||
wlt_data = dict()
|
||||
with open(wallet_path, "r") as fout:
|
||||
wlt_data = json.loads(fout.read())
|
||||
wlt = wallet.Wallet.from_json(wlt_data, password="")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue