Skip to content

Commit

Permalink
tests: add tests for IR controls for setting management
Browse files Browse the repository at this point in the history
closes #924

Signed-off-by: Evgeniy Zayats <[email protected]>
  • Loading branch information
Evgeniy Zayats committed Jan 17, 2025
1 parent 1b5b111 commit 14905ce
Show file tree
Hide file tree
Showing 6 changed files with 334 additions and 30 deletions.
17 changes: 17 additions & 0 deletions neofs-testlib/neofs_testlib/cli/neofs_adm/fschain.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,3 +388,20 @@ def mint_balance(
"fschain mint-balance",
**{param: param_value for param, param_value in locals().items() if param not in ["self"]},
)

def netmap_candidates(
self,
rpc_endpoint: str,
) -> CommandResult:
"""List netmap candidates nodes
Args:
rpc_endpoint: N3 RPC node endpoint
Returns:
Command's result.
"""
return self._execute(
"fschain netmap-candidates",
**{param: param_value for param, param_value in locals().items() if param not in ["self"]},
)
57 changes: 57 additions & 0 deletions neofs-testlib/neofs_testlib/cli/neofs_cli/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,60 @@ def object_status(self, address: str, endpoint: str, object: str, wallet: str) -
"control object status",
**{param: value for param, value in locals().items() if param not in ["self"]},
)

def notary_list(self, address: str, endpoint: str, wallet: str) -> CommandResult:
"""
Get list of all notary requests in network.
Args:
address: Address of wallet account
endpoint: Remote node control address (as 'multiaddr' or '<host>:<port>')
wallet: Path to the wallet
Returns:
Command's result.
"""
return self._execute(
"control notary list",
**{param: value for param, value in locals().items() if param not in ["self"]},
)

def notary_request(self, address: str, endpoint: str, wallet: str, method: str, post_data="") -> CommandResult:
"""
Create and send a notary request with one of the following methods:
- newEpoch, transaction for creating of new NeoFS epoch event in FS chain, no args
- setConfig, transaction to add/update global config value in the NeoFS network, 1 arg in the form key=val
- removeNode, transaction to move nodes to the Offline state in the candidates list, 1 arg is the public key of the node
Args:
address: Address of wallet account
endpoint: Remote node control address (as 'multiaddr' or '<host>:<port>')
wallet: Path to the wallet
method: Requested method
post_data: Requested method argument
Returns:
Command's result.
"""
return self._execute(
"control notary request",
**{param: value for param, value in locals().items() if param not in ["self"]},
)

def notary_sign(self, address: str, endpoint: str, wallet: str, hash: str) -> CommandResult:
"""
Sign notary request by its hash
Args:
address: Address of wallet account
endpoint: Remote node control address (as 'multiaddr' or '<host>:<port>')
wallet: Path to the wallet
hash: hash of the notary request
Returns:
Command's result.
"""
return self._execute(
"control notary sign",
**{param: value for param, value in locals().items() if param not in ["self"]},
)
33 changes: 19 additions & 14 deletions neofs-testlib/neofs_testlib/env/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,24 +465,29 @@ def load(cls, persisted_path: str) -> "NeoFSEnv":
with open(persisted_path, "rb") as fp:
return pickle.load(fp)

@classmethod
def _generate_default_neofs_env_config(cls) -> dict:
jinja_env = jinja2.Environment()
config_template = files("neofs_testlib.env.templates").joinpath("neofs_env_config.yaml").read_text()
jinja_template = jinja_env.from_string(config_template)
arch = platform.machine()
if arch == "x86_64":
config_arch = "linux-amd64"
warp_binary_name = "warp_Linux_x86_64.tar.gz"
elif arch == "arm64":
config_arch = "darwin-arm64"
warp_binary_name = "warp_Darwin_arm64.tar.gz"
else:
raise RuntimeError(f"Unsupported arch: {arch}")
neofs_env_config = jinja_template.render(arch=config_arch, warp_binary_name=warp_binary_name)
neofs_env_config = yaml.safe_load(str(neofs_env_config))
return neofs_env_config

@classmethod
@allure.step("Deploy simple neofs env")
def simple(cls, neofs_env_config: dict = None, with_main_chain=False) -> "NeoFSEnv":
if not neofs_env_config:
jinja_env = jinja2.Environment()
config_template = files("neofs_testlib.env.templates").joinpath("neofs_env_config.yaml").read_text()
jinja_template = jinja_env.from_string(config_template)
arch = platform.machine()
if arch == "x86_64":
config_arch = "linux-amd64"
warp_binary_name = "warp_Linux_x86_64.tar.gz"
elif arch == "arm64":
config_arch = "darwin-arm64"
warp_binary_name = "warp_Darwin_arm64.tar.gz"
else:
raise RuntimeError(f"Unsupported arch: {arch}")
neofs_env_config = jinja_template.render(arch=config_arch, warp_binary_name=warp_binary_name)
neofs_env_config = yaml.safe_load(str(neofs_env_config))
neofs_env_config = cls._generate_default_neofs_env_config()

neofs_env = NeoFSEnv(neofs_env_config=neofs_env_config)
neofs_env.download_binaries()
Expand Down
14 changes: 14 additions & 0 deletions pytest_tests/lib/helpers/neofs_verbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@
# https://github.com/nspcc-dev/neofs-api/blob/5c3535423c564fe63991812cb84d8334db1c553a/object/service.proto#L319
NEOFS_API_HEADER_LIMIT = 16384

CONFIG_KEYS_MAPPING = {
"MaxObjectSize": "maximum_object_size",
"BasicIncomeRate": "storage_price",
"AuditFee": "audit_fee",
"EpochDuration": "epoch_duration",
"ContainerFee": "container_fee",
"EigenTrustIterations": "number_of_eigentrust_iterations",
"EigenTrustAlpha": "eigentrust_alpha",
"InnerRingCandidateFee": "inner_ring_candidate_fee",
"WithdrawFee": "withdrawal_fee",
"HomomorphicHashingDisabled": "homomorphic_hashing_disabled",
"MaintenanceModeAllowed": "maintenance_mode_allowed",
}


@allure.step("Get object from random node")
def get_object_from_random_node(
Expand Down
17 changes: 1 addition & 16 deletions pytest_tests/tests/network/test_config_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,9 @@

import allure
import pytest
from helpers.neofs_verbs import get_netmap_netinfo
from helpers.neofs_verbs import CONFIG_KEYS_MAPPING, get_netmap_netinfo
from neofs_env.neofs_env_test_base import NeofsEnvTestBase

CONFIG_KEYS_MAPPING = {
"MaxObjectSize": "maximum_object_size",
"BasicIncomeRate": "storage_price",
"AuditFee": "audit_fee",
"EpochDuration": "epoch_duration",
"ContainerFee": "container_fee",
# "ContainerAliasFee": "container_alias_fee",
"EigenTrustIterations": "number_of_eigentrust_iterations",
"EigenTrustAlpha": "eigentrust_alpha",
"InnerRingCandidateFee": "inner_ring_candidate_fee",
"WithdrawFee": "withdrawal_fee",
"HomomorphicHashingDisabled": "homomorphic_hashing_disabled",
"MaintenanceModeAllowed": "maintenance_mode_allowed",
}


@allure.title("Network configuration changes via neofs-adm")
class TestNetworkConfigChange(NeofsEnvTestBase):
Expand Down
226 changes: 226 additions & 0 deletions pytest_tests/tests/network/test_ir_controls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import time
from typing import Union

import allure
import neofs_env.neofs_epoch as neofs_epoch
import pytest
from helpers.neofs_verbs import CONFIG_KEYS_MAPPING, get_netmap_netinfo
from helpers.test_control import wait_for_success
from neofs_testlib.env.env import NeoFSEnv, StorageNode
from neofs_testlib.utils import wallet as wallet_utils


@allure.step("Wait until new config value arrives")
@wait_for_success(60, 5)
def wait_until_new_config_value(neofs_env: NeoFSEnv, config_key: str, config_value: int) -> int:
net_info = get_netmap_netinfo(
wallet=neofs_env.storage_nodes[0].wallet.path,
wallet_config=neofs_env.storage_nodes[0].cli_config,
endpoint=neofs_env.storage_nodes[0].endpoint,
shell=neofs_env.shell,
)

assert net_info[config_key] == config_value, f"Invalid config value: {net_info[config_key]}"


@allure.step("Wait until node disappears from netmap snapshot")
@wait_for_success(60, 5)
def wait_until_node_disappears_from_netmap_snapshot(
neofs_env: NeoFSEnv, alive_node: StorageNode, offline_node_addr: str
) -> int:
netmap_snapshot = (
neofs_env.neofs_cli(alive_node.cli_config)
.netmap.snapshot(
rpc_endpoint=alive_node.endpoint,
wallet=alive_node.wallet.path,
)
.stdout
)
assert offline_node_addr not in netmap_snapshot, f"Node {offline_node_addr} is still in network map"


@pytest.fixture(scope="module")
def multi_ir_neofs_env():
neofs_env = NeoFSEnv(neofs_env_config=NeoFSEnv._generate_default_neofs_env_config())
with allure.step("Deploy neofs with 4 ir nodes"):
neofs_env.download_binaries()
neofs_env.deploy_inner_ring_nodes(count=4)
neofs_env.deploy_storage_nodes(
count=4,
node_attrs={
0: ["UN-LOCODE:RU MOW", "Price:22"],
1: ["UN-LOCODE:RU LED", "Price:33"],
2: ["UN-LOCODE:SE STO", "Price:11"],
3: ["UN-LOCODE:FI HEL", "Price:44"],
},
)
neofs_env.log_env_details_to_file()
neofs_env.log_versions_to_allure()

neofs_env.neofs_adm().fschain.set_config(
rpc_endpoint=f"http://{neofs_env.fschain_rpc}",
alphabet_wallets=neofs_env.alphabet_wallets_dir,
post_data="ContainerFee=0 ContainerAliasFee=0 MaxObjectSize=524288",
)
time.sleep(30)
yield neofs_env
neofs_env.kill()


def test_control_notary_request_new_epoch(multi_ir_neofs_env: NeoFSEnv):
if multi_ir_neofs_env.storage_nodes[0]._get_version() <= "0.44.2":
pytest.skip("Test requires fresh node version")
neofs_env = multi_ir_neofs_env

with allure.step("Create notary request to tick epoch"):
current_epoch = neofs_epoch.ensure_fresh_epoch(neofs_env)

ir_node = neofs_env.inner_ring_nodes[0]

tx_hash = (
neofs_env.neofs_cli(ir_node.cli_config)
.control.notary_request(
address=ir_node.alphabet_wallet.address,
endpoint=ir_node.grpc_address,
wallet=ir_node.alphabet_wallet.path,
method="newEpoch",
)
.stdout.strip()
.split("Transaction Hash:")[1]
.strip()
)

notary_list = (
neofs_env.neofs_cli(ir_node.cli_config)
.control.notary_list(
address=ir_node.alphabet_wallet.address,
endpoint=ir_node.grpc_address,
wallet=ir_node.alphabet_wallet.path,
)
.stdout.strip()
)
assert tx_hash in notary_list, f"Transaction hash {tx_hash} not found in notary list {notary_list}"

with allure.step("Sign notary request by 2 more IR nodes"):
for ir_node in neofs_env.inner_ring_nodes[1:-1]:
neofs_env.neofs_cli(ir_node.cli_config).control.notary_sign(
address=ir_node.alphabet_wallet.address,
endpoint=ir_node.grpc_address,
wallet=ir_node.alphabet_wallet.path,
hash=tx_hash,
)

with allure.step("Wait until new epoch arrives"):
neofs_epoch.wait_until_new_epoch(neofs_env, current_epoch)


@pytest.mark.parametrize(
"key, value",
[
("MaxObjectSize", 1048576),
("BasicIncomeRate", 50000000),
("AuditFee", 5000),
("EpochDuration", 480),
("ContainerFee", 2000),
("EigenTrustIterations", 8),
("EigenTrustAlpha", 0.2),
("InnerRingCandidateFee", 5000000000),
("WithdrawFee", 200000000),
("HomomorphicHashingDisabled", True),
("MaintenanceModeAllowed", True),
],
)
def test_control_notary_request_new_config_value(multi_ir_neofs_env: NeoFSEnv, key: str, value: Union[str, int, bool]):
if multi_ir_neofs_env.storage_nodes[0]._get_version() <= "0.44.2":
pytest.skip("Test requires fresh node version")
neofs_env = multi_ir_neofs_env

with allure.step("Create notary request to update config value"):
ir_node = neofs_env.inner_ring_nodes[0]

tx_hash = (
neofs_env.neofs_cli(ir_node.cli_config)
.control.notary_request(
address=ir_node.alphabet_wallet.address,
endpoint=ir_node.grpc_address,
wallet=ir_node.alphabet_wallet.path,
method="setConfig",
post_data=f"{key}={value}",
)
.stdout.strip()
.split("Transaction Hash:")[1]
.strip()
)

notary_list = (
neofs_env.neofs_cli(ir_node.cli_config)
.control.notary_list(
address=ir_node.alphabet_wallet.address,
endpoint=ir_node.grpc_address,
wallet=ir_node.alphabet_wallet.path,
)
.stdout.strip()
)
assert tx_hash in notary_list, f"Transaction hash {tx_hash} not found in notary list {notary_list}"

with allure.step("Sign notary request by 2 more IR nodes"):
for ir_node in neofs_env.inner_ring_nodes[1:-1]:
neofs_env.neofs_cli(ir_node.cli_config).control.notary_sign(
address=ir_node.alphabet_wallet.address,
endpoint=ir_node.grpc_address,
wallet=ir_node.alphabet_wallet.path,
hash=tx_hash,
)

wait_until_new_config_value(neofs_env, CONFIG_KEYS_MAPPING[key], value)


def test_control_notary_request_node_removal(multi_ir_neofs_env: NeoFSEnv):
if multi_ir_neofs_env.storage_nodes[0]._get_version() <= "0.44.2":
pytest.skip("Test requires fresh node version")
neofs_env = multi_ir_neofs_env

with allure.step("Create notary request to remove node"):
ir_node = neofs_env.inner_ring_nodes[0]
sn_offline_node = neofs_env.storage_nodes[1]
sn_offline_node_addr = str(
wallet_utils.get_last_public_key_from_wallet(sn_offline_node.wallet.path, sn_offline_node.wallet.password)
)

tx_hash = (
neofs_env.neofs_cli(ir_node.cli_config)
.control.notary_request(
address=ir_node.alphabet_wallet.address,
endpoint=ir_node.grpc_address,
wallet=ir_node.alphabet_wallet.path,
method="removeNode",
post_data=sn_offline_node_addr,
)
.stdout.strip()
.split("Transaction Hash:")[1]
.strip()
)

notary_list = (
neofs_env.neofs_cli(ir_node.cli_config)
.control.notary_list(
address=ir_node.alphabet_wallet.address,
endpoint=ir_node.grpc_address,
wallet=ir_node.alphabet_wallet.path,
)
.stdout.strip()
)
assert tx_hash in notary_list, f"Transaction hash {tx_hash} not found in notary list {notary_list}"

with allure.step("Sign notary request by 2 more IR nodes"):
for ir_node in neofs_env.inner_ring_nodes[1:-1]:
neofs_env.neofs_cli(ir_node.cli_config).control.notary_sign(
address=ir_node.alphabet_wallet.address,
endpoint=ir_node.grpc_address,
wallet=ir_node.alphabet_wallet.path,
hash=tx_hash,
)

with allure.step("Node should disappear in the next epoch"):
neofs_epoch.ensure_fresh_epoch(neofs_env)
wait_until_node_disappears_from_netmap_snapshot(neofs_env, neofs_env.storage_nodes[0], sn_offline_node_addr)

0 comments on commit 14905ce

Please sign in to comment.