[#145] Refactoring old functions for FrostfsCli

Refactoring old functions for FrostfsCli

Signed-off-by: Mikhail Kadilov m.kadilov@yadro.com
This commit is contained in:
Mikhail Kadilov 2024-02-19 11:49:09 +03:00
parent 751381cd60
commit 37b30822a5
5 changed files with 123 additions and 63 deletions

View file

@ -39,14 +39,12 @@ class FrostfsCliControl(CliCommand):
address: Optional[str] = None, address: Optional[str] = None,
timeout: Optional[str] = None, timeout: Optional[str] = None,
) -> CommandResult: ) -> CommandResult:
"""Set status of the storage node in FrostFS network map """Health check for FrostFS storage nodes
Args: Args:
wallet: Path to the wallet or binary key wallet: Path to the wallet or binary key
address: Address of wallet account address: Address of wallet account
endpoint: Remote node control address (as 'multiaddr' or '<host>:<port>') endpoint: Remote node control address (as 'multiaddr' or '<host>:<port>')
force: Force turning to local maintenance
status: New netmap status keyword ('online', 'offline', 'maintenance')
timeout: Timeout for an operation (default 15s) timeout: Timeout for an operation (default 15s)
Returns: Returns:
@ -56,3 +54,28 @@ class FrostfsCliControl(CliCommand):
"control healthcheck", "control healthcheck",
**{param: value for param, value in locals().items() if param not in ["self"]}, **{param: value for param, value in locals().items() if param not in ["self"]},
) )
def drop_objects(
self,
endpoint: str,
objects: str,
wallet: Optional[str] = None,
address: Optional[str] = None,
timeout: Optional[str] = None,
) -> CommandResult:
"""Drop objects from the node's local storage
Args:
wallet: Path to the wallet or binary key
address: Address of wallet account
endpoint: Remote node control address (as 'multiaddr' or '<host>:<port>')
objects: List of object addresses to be removed in string format
timeout: Timeout for an operation (default 15s)
Returns:
Command`s result.
"""
return self._execute(
"control drop-objects",
**{param: value for param, value in locals().items() if param not in ["self"]},
)

View file

@ -357,7 +357,7 @@ class FrostfsCliObject(CliCommand):
wallet: Optional[str] = None, wallet: Optional[str] = None,
address: Optional[str] = None, address: Optional[str] = None,
bearer: Optional[str] = None, bearer: Optional[str] = None,
generate_key: Optional = None, generate_key: Optional[bool] = None,
oid: Optional[str] = None, oid: Optional[str] = None,
trace: bool = False, trace: bool = False,
root: bool = False, root: bool = False,

View file

@ -39,10 +39,10 @@ class FrostfsCliShards(CliCommand):
def set_mode( def set_mode(
self, self,
endpoint: str, endpoint: str,
wallet: str,
wallet_password: str,
mode: str, mode: str,
id: Optional[list[str]], id: Optional[list[str]],
wallet: Optional[str] = None,
wallet_password: Optional[str] = None,
address: Optional[str] = None, address: Optional[str] = None,
all: bool = False, all: bool = False,
clear_errors: bool = False, clear_errors: bool = False,
@ -65,6 +65,11 @@ class FrostfsCliShards(CliCommand):
Returns: Returns:
Command's result. Command's result.
""" """
if not wallet_password:
return self._execute(
"control shards set-mode",
**{param: value for param, value in locals().items() if param not in ["self"]},
)
return self._execute_with_password( return self._execute_with_password(
"control shards set-mode", "control shards set-mode",
wallet_password, wallet_password,
@ -137,3 +142,4 @@ class FrostfsCliShards(CliCommand):
wallet_password, wallet_password,
**{param: value for param, value in locals().items() if param not in ["self", "wallet_password"]}, **{param: value for param, value in locals().items() if param not in ["self", "wallet_password"]},
) )

View file

@ -2,6 +2,7 @@ import logging
import random import random
import re import re
import time import time
import pytest
from dataclasses import dataclass from dataclasses import dataclass
from time import sleep from time import sleep
from typing import Optional from typing import Optional
@ -13,8 +14,8 @@ from frostfs_testlib.resources.common import MORPH_BLOCK_TIME
from frostfs_testlib.shell import Shell from frostfs_testlib.shell import Shell
from frostfs_testlib.steps.epoch import tick_epoch, wait_for_epochs_align from frostfs_testlib.steps.epoch import tick_epoch, wait_for_epochs_align
from frostfs_testlib.storage.cluster import Cluster, StorageNode from frostfs_testlib.storage.cluster import Cluster, StorageNode
from frostfs_testlib.storage.dataclasses.frostfs_services import S3Gate
from frostfs_testlib.utils import datetime_utils from frostfs_testlib.utils import datetime_utils
from frostfs_testlib.testing.parallel import parallel
logger = logging.getLogger("NeoLogger") logger = logging.getLogger("NeoLogger")
@ -52,9 +53,24 @@ def storage_node_healthcheck(node: StorageNode) -> HealthStatus:
Returns: Returns:
health status as HealthStatus object. health status as HealthStatus object.
""" """
command = "control healthcheck"
output = _run_control_command_with_retries(node, command) host = node.host
return HealthStatus.from_stdout(output) service_config = host.get_service_config(node.name)
wallet_path = service_config.attributes["wallet_path"]
wallet_password = service_config.attributes["wallet_password"]
control_endpoint = service_config.attributes["control_endpoint"]
shell = host.get_shell()
wallet_config_path = f"/tmp/{node.name}-config.yaml"
wallet_config = f'wallet: {wallet_path}\npassword: "{wallet_password}"'
shell.exec(f"echo '{wallet_config}' > {wallet_config_path}")
cli_config = host.get_cli_config("frostfs-cli")
cli = FrostfsCli(shell, cli_config.exec_path, wallet_config_path)
result = cli.control.healthcheck(control_endpoint)
return HealthStatus.from_stdout(result.stdout)
@reporter.step("Set status for {node}") @reporter.step("Set status for {node}")
@ -66,8 +82,21 @@ def storage_node_set_status(node: StorageNode, status: str, retries: int = 0) ->
status: online or offline. status: online or offline.
retries (optional, int): number of retry attempts if it didn't work from the first time retries (optional, int): number of retry attempts if it didn't work from the first time
""" """
command = f"control set-status --status {status}" host = node.host
_run_control_command_with_retries(node, command, retries) service_config = host.get_service_config(node.name)
wallet_path = service_config.attributes["wallet_path"]
wallet_password = service_config.attributes["wallet_password"]
control_endpoint = service_config.attributes["control_endpoint"]
shell = host.get_shell()
wallet_config_path = f"/tmp/{node.name}-config.yaml"
wallet_config = f'wallet: {wallet_path}\npassword: "{wallet_password}"'
shell.exec(f"echo '{wallet_config}' > {wallet_config_path}")
cli_config = host.get_cli_config("frostfs-cli")
cli = FrostfsCli(shell, cli_config.exec_path, wallet_config_path)
cli.control.set_status(control_endpoint, status)
@reporter.step("Get netmap snapshot") @reporter.step("Get netmap snapshot")
@ -91,7 +120,7 @@ def get_netmap_snapshot(node: StorageNode, shell: Shell) -> str:
@reporter.step("Get shard list for {node}") @reporter.step("Get shard list for {node}")
def node_shard_list(node: StorageNode) -> list[str]: def node_shard_list(node: StorageNode, json: Optional[bool] = None) -> list[str]:
""" """
The function returns list of shards for specified storage node. The function returns list of shards for specified storage node.
Args: Args:
@ -99,31 +128,72 @@ def node_shard_list(node: StorageNode) -> list[str]:
Returns: Returns:
list of shards. list of shards.
""" """
command = "control shards list" host = node.host
output = _run_control_command_with_retries(node, command) service_config = host.get_service_config(node.name)
return re.findall(r"Shard (.*):", output) wallet_path = service_config.attributes["wallet_path"]
wallet_password = service_config.attributes["wallet_password"]
control_endpoint = service_config.attributes["control_endpoint"]
shell = host.get_shell()
wallet_config_path = f"/tmp/{node.name}-config.yaml"
wallet_config = f'wallet: {wallet_path}\npassword: "{wallet_password}"'
shell.exec(f"echo '{wallet_config}' > {wallet_config_path}")
cli_config = host.get_cli_config("frostfs-cli")
cli = FrostfsCli(shell, cli_config.exec_path, wallet_config_path)
result = cli.shards.list(endpoint=control_endpoint, json_mode=json)
return re.findall(r"Shard (.*):", result.stdout)
@reporter.step("Shard set for {node}") @reporter.step("Shard set for {node}")
def node_shard_set_mode(node: StorageNode, shard: str, mode: str) -> str: def node_shard_set_mode(node: StorageNode, shard: list[str], mode: str) -> None:
""" """
The function sets mode for specified shard. The function sets mode for specified shard.
Args: Args:
node: node on which shard mode should be set. node: node on which shard mode should be set.
""" """
command = f"control shards set-mode --id {shard} --mode {mode}" host = node.host
return _run_control_command_with_retries(node, command) service_config = host.get_service_config(node.name)
wallet_path = service_config.attributes["wallet_path"]
wallet_password = service_config.attributes["wallet_password"]
control_endpoint = service_config.attributes["control_endpoint"]
shell = host.get_shell()
wallet_config_path = f"/tmp/{node.name}-config.yaml"
wallet_config = f'wallet: {wallet_path}\npassword: "{wallet_password}"'
shell.exec(f"echo '{wallet_config}' > {wallet_config_path}")
cli_config = host.get_cli_config("frostfs-cli")
cli = FrostfsCli(shell, cli_config.exec_path, wallet_config_path)
cli.shards.set_mode(endpoint=control_endpoint, mode=mode, id=shard)
@reporter.step("Drop object from {node}") @reporter.step("Drop object from {node}")
def drop_object(node: StorageNode, cid: str, oid: str) -> str: def drop_object(node: StorageNode, cid: str, oid: str) -> None:
""" """
The function drops object from specified node. The function drops object from specified node.
Args: Args:
node_id str: node from which object should be dropped. node: node from which object should be dropped.
""" """
command = f"control drop-objects -o {cid}/{oid}" host = node.host
return _run_control_command_with_retries(node, command) service_config = host.get_service_config(node.name)
wallet_path = service_config.attributes["wallet_path"]
wallet_password = service_config.attributes["wallet_password"]
control_endpoint = service_config.attributes["control_endpoint"]
shell = host.get_shell()
wallet_config_path = f"/tmp/{node.name}-config.yaml"
wallet_config = f'wallet: {wallet_path}\npassword: "{wallet_password}"'
shell.exec(f"echo '{wallet_config}' > {wallet_config_path}")
cli_config = host.get_cli_config("frostfs-cli")
cli = FrostfsCli(shell, cli_config.exec_path, wallet_config_path)
objects = f"{cid}/{oid}"
cli.control.drop_objects(control_endpoint, objects)
@reporter.step("Delete data from host for node {node}") @reporter.step("Delete data from host for node {node}")
@ -238,38 +308,3 @@ def remove_nodes_from_map_morph(
config_file=FROSTFS_ADM_CONFIG_PATH, config_file=FROSTFS_ADM_CONFIG_PATH,
) )
frostfsadm.morph.remove_nodes(node_netmap_keys) frostfsadm.morph.remove_nodes(node_netmap_keys)
def _run_control_command_with_retries(node: StorageNode, command: str, retries: int = 0) -> str:
for attempt in range(1 + retries): # original attempt + specified retries
try:
return _run_control_command(node, command)
except AssertionError as err:
if attempt < retries:
logger.warning(f"Command {command} failed with error {err} and will be retried")
continue
raise AssertionError(f"Command {command} failed with error {err}") from err
def _run_control_command(node: StorageNode, command: str) -> None:
host = node.host
service_config = host.get_service_config(node.name)
wallet_path = service_config.attributes["wallet_path"]
wallet_password = service_config.attributes["wallet_password"]
control_endpoint = service_config.attributes["control_endpoint"]
shell = host.get_shell()
wallet_config_path = f"/tmp/{node.name}-config.yaml"
wallet_config = f'password: "{wallet_password}"'
shell.exec(f"echo '{wallet_config}' > {wallet_config_path}")
cli_config = host.get_cli_config("frostfs-cli")
# TODO: implement cli.control
# cli = FrostfsCli(shell, cli_config.exec_path, wallet_config_path)
result = shell.exec(
f"{cli_config.exec_path} {command} --endpoint {control_endpoint} "
f"--wallet {wallet_path} --config {wallet_config_path}"
)
return result.stdout

View file

@ -97,8 +97,6 @@ class ShardsWatcher:
response = shards_cli.list( response = shards_cli.list(
endpoint=self.storage_node.get_control_endpoint(), endpoint=self.storage_node.get_control_endpoint(),
wallet=self.storage_node.get_remote_wallet_path(),
wallet_password=self.storage_node.get_wallet_password(),
json_mode=True, json_mode=True,
) )
@ -110,9 +108,7 @@ class ShardsWatcher:
self.storage_node.host.get_cli_config("frostfs-cli").exec_path, self.storage_node.host.get_cli_config("frostfs-cli").exec_path,
) )
return shards_cli.set_mode( return shards_cli.set_mode(
self.storage_node.get_control_endpoint(), endpoint=self.storage_node.get_control_endpoint(),
self.storage_node.get_remote_wallet_path(),
self.storage_node.get_wallet_password(),
mode=mode, mode=mode,
id=[shard_id], id=[shard_id],
clear_errors=clear_errors, clear_errors=clear_errors,