Refactoring old functions for FrostfsCli #145

Closed
mkadilov wants to merge 3 commits from (deleted):bug/cli_methods_moving into master
4 changed files with 108 additions and 62 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

@ -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,
@ -137,3 +137,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

@ -13,8 +13,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.hosting import Host
logger = logging.getLogger("NeoLogger") logger = logging.getLogger("NeoLogger")
@ -36,7 +36,7 @@ class HealthStatus:
@reporter.step("Get Locode from random storage node") @reporter.step("Get Locode from random storage node")
def get_locode_from_random_node(cluster: Cluster) -> str: def get_locode_from_random_node( cluster: Cluster) -> str:
node = random.choice(cluster.services(StorageNode)) node = random.choice(cluster.services(StorageNode))
locode = node.get_un_locode() locode = node.get_un_locode()
logger.info(f"Chosen '{locode}' locode from node {node}") logger.info(f"Chosen '{locode}' locode from node {node}")
@ -44,7 +44,7 @@ def get_locode_from_random_node(cluster: Cluster) -> str:
@reporter.step("Healthcheck for storage node {node}") @reporter.step("Healthcheck for storage node {node}")
def storage_node_healthcheck(node: StorageNode) -> HealthStatus: def storage_node_healthcheck(self, node: StorageNode) -> HealthStatus:
""" """
The function returns storage node's health status. The function returns storage node's health status.
Args: Args:
@ -52,9 +52,23 @@ 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"]
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)
endpoint = node.get_control_endpoint()
output = cli.control.healthcheck(endpoint)
return HealthStatus.from_stdout(output.stdout)
@reporter.step("Set status for {node}") @reporter.step("Set status for {node}")
@ -66,8 +80,19 @@ 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) shell = host.get_shell()
cli_config = host.get_cli_config("frostfs-cli")
wallet_config_path = f"/tmp/{node.name}-config.yaml"
service_config = host.get_service_config(node.name)
wallet_path = service_config.attributes["wallet_path"]
wallet_password = service_config.attributes["wallet_password"]
wallet_config = f'wallet: {wallet_path}\npassword: "{wallet_password}"'
shell.exec(f"echo '{wallet_config}' > {wallet_config_path}")
cli = FrostfsCli(shell, cli_config.exec_path, wallet_config_path)
endpoint = node.get_control_endpoint()
cli.control.set_status(endpoint, status)
@reporter.step("Get netmap snapshot") @reporter.step("Get netmap snapshot")
@ -91,7 +116,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,20 +124,44 @@ 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) shell = host.get_shell()
cli_config = host.get_cli_config("frostfs-cli")
wallet_config_path = f"/tmp/{node.name}-config.yaml"
wallet_password = host.get_service_config(node.name).attributes["wallet_password"]
wallet_config = f'password: "{wallet_password}"'
service_config = host.get_service_config(node.name)
wallet_path = service_config.attributes["wallet_path"]
wallet_password = service_config.attributes["wallet_password"]
wallet_config = f'wallet: {wallet_path}\npassword: "{wallet_password}"'
shell.exec(f"echo '{wallet_config}' > {wallet_config_path}")
cli = FrostfsCli(shell, cli_config.exec_path, wallet_config_path)
endpoint = node.get_control_endpoint()
output = cli.shards.list(endpoint=endpoint, json_mode=json)
return re.findall(r"Shard (.*):", output) return re.findall(r"Shard (.*):", output)
@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) -> str:
""" """
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) shell = host.get_shell()
cli_config = host.get_cli_config("frostfs-cli")
wallet_config_path = f"/tmp/{node.name}-config.yaml"
service_config = host.get_service_config(node.name)
wallet_path = service_config.attributes["wallet_path"]
wallet_password = service_config.attributes["wallet_password"]
wallet_config = f'wallet: {wallet_path}\npassword: "{wallet_password}"'
shell.exec(f"echo '{wallet_config}' > {wallet_config_path}")
cli = FrostfsCli(shell, cli_config.exec_path, wallet_config_path)
endpoint = node.get_control_endpoint()
return cli.shards.set_mode(endpoint=endpoint, id=shard, mode=mode)
@reporter.step("Drop object from {node}") @reporter.step("Drop object from {node}")
@ -120,10 +169,22 @@ def drop_object(node: StorageNode, cid: str, oid: str) -> str:
""" """
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) shell = host.get_shell()
cli_config = host.get_cli_config("frostfs-cli")
wallet_config_path = f"/tmp/{node.name}-config.yaml"
service_config = host.get_service_config(node.name)
wallet_path = service_config.attributes["wallet_path"]
wallet_password = service_config.attributes["wallet_password"]
wallet_config = f'wallet: {wallet_path}\npassword: "{wallet_password}"'
shell.exec(f"echo '{wallet_config}' > {wallet_config_path}")
cli = FrostfsCli(shell, cli_config.exec_path, wallet_config_path)
endpoint = node.get_control_endpoint()
objects = f"{cid}/{oid}"
return cli.control.drop_objects(endpoint, objects)
@reporter.step("Delete data from host for node {node}") @reporter.step("Delete data from host for node {node}")
@ -238,38 +299,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,