Use neofs-testlib

Signed-off-by: Vladimir Avdeev <v.avdeev@yadro.com>
This commit is contained in:
Vladimir Avdeev 2022-10-13 21:53:44 +03:00 committed by Vladimir Avdeev
parent 31d43fbba9
commit e63db788c5
46 changed files with 889 additions and 1992 deletions

View file

@ -35,6 +35,7 @@ neo-mamba==0.10.0
neo3crypto==0.2.1
neo3vm==0.9.0
neo3vm-stubs==0.9.0
neofs-testlib==0.1.0
netaddr==0.8.0
orjson==3.6.8
packaging==21.3

View file

@ -63,10 +63,10 @@ def file_path():
@pytest.fixture(scope="function")
def eacl_container_with_objects(wallets, file_path):
def eacl_container_with_objects(wallets, client_shell, file_path):
user_wallet = wallets.get_wallet()
with allure.step("Create eACL public container"):
cid = create_container(user_wallet.wallet_path, basic_acl=PUBLIC_ACL)
cid = create_container(user_wallet.wallet_path, basic_acl=PUBLIC_ACL, shell=client_shell)
with allure.step("Add test objects to container"):
objects_oids = [
@ -75,6 +75,7 @@ def eacl_container_with_objects(wallets, file_path):
file_path,
cid,
attributes={"key1": "val1", "key": val, "key2": "abc"},
shell=client_shell,
)
for val in range(OBJECT_COUNT)
]

View file

@ -57,10 +57,10 @@ class TestStorageGroup:
neofs_deposit(self.other_wallet, 30)
@allure.title("Test Storage Group in Private Container")
def test_storagegroup_basic_private_container(self, object_size):
cid = create_container(self.main_wallet)
def test_storagegroup_basic_private_container(self, client_shell, object_size):
cid = create_container(self.main_wallet, shell=client_shell)
file_path = generate_file(object_size)
oid = put_object(self.main_wallet, file_path, cid)
oid = put_object(self.main_wallet, file_path, cid, shell=client_shell)
objects = [oid]
storage_group = put_storagegroup(self.main_wallet, cid, objects)
@ -73,10 +73,10 @@ class TestStorageGroup:
)
@allure.title("Test Storage Group in Public Container")
def test_storagegroup_basic_public_container(self, object_size):
cid = create_container(self.main_wallet, basic_acl="public-read-write")
def test_storagegroup_basic_public_container(self, client_shell, object_size):
cid = create_container(self.main_wallet, basic_acl="public-read-write", shell=client_shell)
file_path = generate_file(object_size)
oid = put_object(self.main_wallet, file_path, cid)
oid = put_object(self.main_wallet, file_path, cid, shell=client_shell)
objects = [oid]
self.expect_success_for_storagegroup_operations(self.main_wallet, cid, objects, object_size)
self.expect_success_for_storagegroup_operations(
@ -87,10 +87,10 @@ class TestStorageGroup:
)
@allure.title("Test Storage Group in Read-Only Container")
def test_storagegroup_basic_ro_container(self, object_size):
cid = create_container(self.main_wallet, basic_acl="public-read")
def test_storagegroup_basic_ro_container(self, client_shell, object_size):
cid = create_container(self.main_wallet, basic_acl="public-read", shell=client_shell)
file_path = generate_file(object_size)
oid = put_object(self.main_wallet, file_path, cid)
oid = put_object(self.main_wallet, file_path, cid, shell=client_shell)
objects = [oid]
self.expect_success_for_storagegroup_operations(self.main_wallet, cid, objects, object_size)
self.storagegroup_operations_by_other_ro_container(
@ -101,10 +101,12 @@ class TestStorageGroup:
)
@allure.title("Test Storage Group with Bearer Allow")
def test_storagegroup_bearer_allow(self, object_size):
cid = create_container(self.main_wallet, basic_acl="eacl-public-read-write")
def test_storagegroup_bearer_allow(self, client_shell, object_size):
cid = create_container(
self.main_wallet, basic_acl="eacl-public-read-write", shell=client_shell
)
file_path = generate_file(object_size)
oid = put_object(self.main_wallet, file_path, cid)
oid = put_object(self.main_wallet, file_path, cid, shell=client_shell)
objects = [oid]
self.expect_success_for_storagegroup_operations(self.main_wallet, cid, objects, object_size)
storage_group = put_storagegroup(self.main_wallet, cid, objects)
@ -130,10 +132,10 @@ class TestStorageGroup:
)
@allure.title("Test to check Storage Group lifetime")
def test_storagegroup_lifetime(self, object_size):
cid = create_container(self.main_wallet)
def test_storagegroup_lifetime(self, client_shell, object_size):
cid = create_container(self.main_wallet, shell=client_shell)
file_path = generate_file(object_size)
oid = put_object(self.main_wallet, file_path, cid)
oid = put_object(self.main_wallet, file_path, cid, shell=client_shell)
objects = [oid]
storage_group = put_storagegroup(self.main_wallet, cid, objects, lifetime="1")
tick_epoch()

View file

@ -1,5 +1,6 @@
import allure
import pytest
from python_keywords.acl import EACLRole
from python_keywords.container import create_container
from python_keywords.container_access import (
@ -16,10 +17,12 @@ from wellknown_acl import PRIVATE_ACL_F, PUBLIC_ACL_F, READONLY_ACL_F
@pytest.mark.acl_basic
class TestACLBasic:
@pytest.fixture(scope="function")
def public_container(self, wallets):
def public_container(self, client_shell, wallets):
user_wallet = wallets.get_wallet()
with allure.step("Create public container"):
cid_public = create_container(user_wallet.wallet_path, basic_acl=PUBLIC_ACL_F)
cid_public = create_container(
user_wallet.wallet_path, basic_acl=PUBLIC_ACL_F, shell=client_shell
)
yield cid_public
@ -27,10 +30,12 @@ class TestACLBasic:
# delete_container(user_wallet.wallet_path, cid_public)
@pytest.fixture(scope="function")
def private_container(self, wallets):
def private_container(self, client_shell, wallets):
user_wallet = wallets.get_wallet()
with allure.step("Create private container"):
cid_private = create_container(user_wallet.wallet_path, basic_acl=PRIVATE_ACL_F)
cid_private = create_container(
user_wallet.wallet_path, basic_acl=PRIVATE_ACL_F, shell=client_shell
)
yield cid_private
@ -38,10 +43,12 @@ class TestACLBasic:
# delete_container(user_wallet.wallet_path, cid_private)
@pytest.fixture(scope="function")
def read_only_container(self, wallets):
def read_only_container(self, client_shell, wallets):
user_wallet = wallets.get_wallet()
with allure.step("Create public readonly container"):
cid_read_only = create_container(user_wallet.wallet_path, basic_acl=READONLY_ACL_F)
cid_read_only = create_container(
user_wallet.wallet_path, basic_acl=READONLY_ACL_F, shell=client_shell
)
yield cid_read_only
@ -49,7 +56,7 @@ class TestACLBasic:
# delete_container(user_wallet.wallet_path, cid_read_only)
@allure.title("Test basic ACL on public container")
def test_basic_acl_public(self, wallets, public_container, file_path):
def test_basic_acl_public(self, wallets, client_shell, public_container, file_path):
"""
Test basic ACL set during public container creation.
"""
@ -64,20 +71,26 @@ class TestACLBasic:
user_wallet.wallet_path,
file_path,
cid,
shell=client_shell,
attributes={"created": "owner"},
)
other_object_oid = put_object(
other_wallet.wallet_path,
file_path,
cid,
shell=client_shell,
attributes={"created": "other"},
)
with allure.step(f"Check {desc} has full access to public container"):
check_full_access_to_container(wallet.wallet_path, cid, owner_object_oid, file_path)
check_full_access_to_container(wallet.wallet_path, cid, other_object_oid, file_path)
check_full_access_to_container(
wallet.wallet_path, cid, owner_object_oid, file_path, shell=client_shell
)
check_full_access_to_container(
wallet.wallet_path, cid, other_object_oid, file_path, shell=client_shell
)
@allure.title("Test basic ACL on private container")
def test_basic_acl_private(self, wallets, private_container, file_path):
def test_basic_acl_private(self, wallets, client_shell, private_container, file_path):
"""
Test basic ACL set during private container creation.
"""
@ -85,21 +98,23 @@ class TestACLBasic:
other_wallet = wallets.get_wallet(role=EACLRole.OTHERS)
cid = private_container
with allure.step("Add test objects to container"):
owner_object_oid = put_object(user_wallet.wallet_path, file_path, cid)
owner_object_oid = put_object(
user_wallet.wallet_path, file_path, cid, shell=client_shell
)
with allure.step("Check only owner has full access to private container"):
with allure.step("Check no one except owner has access to operations with container"):
check_no_access_to_container(
other_wallet.wallet_path, cid, owner_object_oid, file_path
other_wallet.wallet_path, cid, owner_object_oid, file_path, shell=client_shell
)
with allure.step("Check owner has full access to private container"):
check_full_access_to_container(
user_wallet.wallet_path, cid, owner_object_oid, file_path
user_wallet.wallet_path, cid, owner_object_oid, file_path, shell=client_shell
)
@allure.title("Test basic ACL on readonly container")
def test_basic_acl_readonly(self, wallets, read_only_container, file_path):
def test_basic_acl_readonly(self, wallets, client_shell, read_only_container, file_path):
"""
Test basic ACL Operations for Read-Only Container.
"""
@ -108,10 +123,14 @@ class TestACLBasic:
cid = read_only_container
with allure.step("Add test objects to container"):
object_oid = put_object(user_wallet.wallet_path, file_path, cid)
object_oid = put_object(user_wallet.wallet_path, file_path, cid, shell=client_shell)
with allure.step("Check other has read-only access to operations with container"):
check_read_only_container(other_wallet.wallet_path, cid, object_oid, file_path)
check_read_only_container(
other_wallet.wallet_path, cid, object_oid, file_path, shell=client_shell
)
with allure.step("Check owner has full access to public container"):
check_full_access_to_container(user_wallet.wallet_path, cid, object_oid, file_path)
check_full_access_to_container(
user_wallet.wallet_path, cid, object_oid, file_path, shell=client_shell
)

View file

@ -22,7 +22,9 @@ from python_keywords.container_access import (
@pytest.mark.acl_bearer
class TestACLBearer:
@pytest.mark.parametrize("role", [EACLRole.USER, EACLRole.OTHERS])
def test_bearer_token_operations(self, wallets, eacl_container_with_objects, role):
def test_bearer_token_operations(
self, wallets, client_shell, eacl_container_with_objects, role
):
allure.dynamic.title(f"Testcase to validate NeoFS operations with {role.value} BearerToken")
cid, objects_oids, file_path = eacl_container_with_objects
user_wallet = wallets.get_wallet()
@ -35,14 +37,15 @@ class TestACLBearer:
objects_oids.pop(),
file_path,
wallet_config=deny_wallet.config_path,
shell=client_shell,
)
with allure.step(f"Set deny all operations for {role.value} via eACL"):
eacl = [
EACLRule(access=EACLAccess.DENY, role=role, operation=op) for op in EACLOperation
]
eacl_file = create_eacl(cid, eacl)
set_eacl(user_wallet.wallet_path, cid, eacl_file)
eacl_file = create_eacl(cid, eacl, shell=client_shell)
set_eacl(user_wallet.wallet_path, cid, eacl_file, shell=client_shell)
wait_for_cache_expired()
with allure.step(f"Create bearer token for {role.value} with all operations allowed"):
@ -53,6 +56,7 @@ class TestACLBearer:
EACLRule(operation=op, access=EACLAccess.ALLOW, role=role)
for op in EACLOperation
],
shell=client_shell,
)
with allure.step(
@ -64,6 +68,7 @@ class TestACLBearer:
objects_oids.pop(),
file_path,
wallet_config=deny_wallet.config_path,
shell=client_shell,
)
with allure.step(
@ -76,14 +81,15 @@ class TestACLBearer:
file_path,
bearer=bearer_token,
wallet_config=deny_wallet.config_path,
shell=client_shell,
)
with allure.step(f"Set allow all operations for {role.value} via eACL"):
eacl = [
EACLRule(access=EACLAccess.ALLOW, role=role, operation=op) for op in EACLOperation
]
eacl_file = create_eacl(cid, eacl)
set_eacl(user_wallet.wallet_path, cid, eacl_file)
eacl_file = create_eacl(cid, eacl, shell=client_shell)
set_eacl(user_wallet.wallet_path, cid, eacl_file, shell=client_shell)
wait_for_cache_expired()
with allure.step(
@ -95,10 +101,13 @@ class TestACLBearer:
objects_oids.pop(),
file_path,
wallet_config=deny_wallet.config_path,
shell=client_shell,
)
@allure.title("BearerToken Operations for compound Operations")
def test_bearer_token_compound_operations(self, wallets, eacl_container_with_objects):
def test_bearer_token_compound_operations(
self, wallets, client_shell, eacl_container_with_objects
):
cid, objects_oids, file_path = eacl_container_with_objects
user_wallet = wallets.get_wallet()
other_wallet = wallets.get_wallet(role=EACLRole.OTHERS)
@ -141,7 +150,12 @@ class TestACLBearer:
eacl_deny += [
EACLRule(access=EACLAccess.DENY, role=role, operation=op) for op in operations
]
set_eacl(user_wallet.wallet_path, cid, eacl_table_path=create_eacl(cid, eacl_deny))
set_eacl(
user_wallet.wallet_path,
cid,
eacl_table_path=create_eacl(cid, eacl_deny, shell=client_shell),
shell=client_shell,
)
wait_for_cache_expired()
with allure.step("Check rule consistency without bearer"):
@ -152,6 +166,7 @@ class TestACLBearer:
file_path,
deny_operations=deny_map[EACLRole.USER],
wallet_config=user_wallet.config_path,
shell=client_shell,
)
check_custom_access_to_container(
other_wallet.wallet_path,
@ -160,6 +175,7 @@ class TestACLBearer:
file_path,
deny_operations=deny_map[EACLRole.OTHERS],
wallet_config=other_wallet.config_path,
shell=client_shell,
)
with allure.step("Check rule consistency using bearer token"):
@ -170,6 +186,7 @@ class TestACLBearer:
EACLRule(operation=op, access=EACLAccess.ALLOW, role=EACLRole.USER)
for op in bearer_map[EACLRole.USER]
],
shell=client_shell,
)
bearer_token_other = form_bearertoken_file(
@ -179,6 +196,7 @@ class TestACLBearer:
EACLRule(operation=op, access=EACLAccess.ALLOW, role=EACLRole.OTHERS)
for op in bearer_map[EACLRole.OTHERS]
],
shell=client_shell,
)
check_custom_access_to_container(
@ -189,6 +207,7 @@ class TestACLBearer:
deny_operations=deny_map_with_bearer[EACLRole.USER],
bearer=bearer_token_user,
wallet_config=user_wallet.config_path,
shell=client_shell,
)
check_custom_access_to_container(
other_wallet.wallet_path,
@ -198,4 +217,5 @@ class TestACLBearer:
deny_operations=deny_map_with_bearer[EACLRole.OTHERS],
bearer=bearer_token_other,
wallet_config=other_wallet.config_path,
shell=client_shell,
)

View file

@ -27,6 +27,7 @@ from python_keywords.object_access import (
can_put_object,
can_search_object,
)
from neofs_testlib.shell import Shell
from wellknown_acl import PUBLIC_ACL
@ -37,14 +38,17 @@ class TestEACLContainer:
NODE_COUNT = len(NEOFS_NETMAP_DICT.keys())
@pytest.fixture(scope="function")
def eacl_full_placement_container_with_object(self, wallets, file_path):
def eacl_full_placement_container_with_object(self, wallets, file_path, shell: Shell):
user_wallet = wallets.get_wallet()
with allure.step("Create eACL public container with full placement rule"):
full_placement_rule = (
f"REP {self.NODE_COUNT} IN X CBF 1 SELECT {self.NODE_COUNT} FROM * AS X"
)
cid = create_container(
user_wallet.wallet_path, full_placement_rule, basic_acl=PUBLIC_ACL
user_wallet.wallet_path,
full_placement_rule,
basic_acl=PUBLIC_ACL,
shell=shell,
)
with allure.step("Add test object to container"):
@ -55,7 +59,7 @@ class TestEACLContainer:
@pytest.mark.parametrize("deny_role", [EACLRole.USER, EACLRole.OTHERS])
def test_extended_acl_deny_all_operations(
self, wallets, eacl_container_with_objects, deny_role
self, wallets, client_shell, eacl_container_with_objects, deny_role
):
user_wallet = wallets.get_wallet()
other_wallet = wallets.get_wallet(EACLRole.OTHERS)
@ -71,7 +75,12 @@ class TestEACLContainer:
EACLRule(access=EACLAccess.DENY, role=deny_role, operation=op)
for op in EACLOperation
]
set_eacl(user_wallet.wallet_path, cid, create_eacl(cid, eacl_deny))
set_eacl(
user_wallet.wallet_path,
cid,
create_eacl(cid, eacl_deny, shell=client_shell),
shell=client_shell,
)
wait_for_cache_expired()
with allure.step(f"Check only {not_deny_role_str} has full access to container"):
@ -79,14 +88,22 @@ class TestEACLContainer:
f"Check {deny_role_str} has not access to any operations with container"
):
check_no_access_to_container(
deny_role_wallet.wallet_path, cid, object_oids[0], file_path
deny_role_wallet.wallet_path,
cid,
object_oids[0],
file_path,
shell=client_shell,
)
with allure.step(
f"Check {not_deny_role_wallet} has full access to eACL public container"
):
check_full_access_to_container(
not_deny_role_wallet.wallet_path, cid, object_oids.pop(), file_path
not_deny_role_wallet.wallet_path,
cid,
object_oids.pop(),
file_path,
shell=client_shell,
)
with allure.step(f"Allow all operations for {deny_role_str} via eACL"):
@ -94,20 +111,33 @@ class TestEACLContainer:
EACLRule(access=EACLAccess.ALLOW, role=deny_role, operation=op)
for op in EACLOperation
]
set_eacl(user_wallet.wallet_path, cid, create_eacl(cid, eacl_deny))
set_eacl(
user_wallet.wallet_path,
cid,
create_eacl(cid, eacl_deny, shell=client_shell),
shell=client_shell,
)
wait_for_cache_expired()
with allure.step(f"Check all have full access to eACL public container"):
check_full_access_to_container(
user_wallet.wallet_path, cid, object_oids.pop(), file_path
user_wallet.wallet_path,
cid,
object_oids.pop(),
file_path,
shell=client_shell,
)
check_full_access_to_container(
other_wallet.wallet_path, cid, object_oids.pop(), file_path
other_wallet.wallet_path,
cid,
object_oids.pop(),
file_path,
shell=client_shell,
)
@allure.title("Testcase to allow NeoFS operations for only one other pubkey.")
def test_extended_acl_deny_all_operations_exclude_pubkey(
self, wallets, eacl_container_with_objects
self, wallets, client_shell, eacl_container_with_objects
):
user_wallet = wallets.get_wallet()
other_wallet, other_wallet_allow = wallets.get_wallets_list(EACLRole.OTHERS)[0:2]
@ -126,28 +156,45 @@ class TestEACLContainer:
EACLRule(access=EACLAccess.DENY, role=EACLRole.OTHERS, operation=op)
for op in EACLOperation
]
set_eacl(user_wallet.wallet_path, cid, create_eacl(cid, eacl))
set_eacl(
user_wallet.wallet_path,
cid,
create_eacl(cid, eacl, shell=client_shell),
shell=client_shell,
)
wait_for_cache_expired()
with allure.step("Check only owner and allowed other have full access to public container"):
with allure.step("Check other has not access to operations with container"):
check_no_access_to_container(
other_wallet.wallet_path, cid, object_oids[0], file_path
other_wallet.wallet_path,
cid,
object_oids[0],
file_path,
shell=client_shell,
)
with allure.step("Check owner has full access to public container"):
check_full_access_to_container(
user_wallet.wallet_path, cid, object_oids.pop(), file_path
user_wallet.wallet_path,
cid,
object_oids.pop(),
file_path,
shell=client_shell,
)
with allure.step("Check allowed other has full access to public container"):
check_full_access_to_container(
other_wallet_allow.wallet_path, cid, object_oids.pop(), file_path
other_wallet_allow.wallet_path,
cid,
object_oids.pop(),
file_path,
shell=client_shell,
)
@allure.title("Testcase to validate NeoFS replication with eACL deny rules.")
def test_extended_acl_deny_replication(
self, wallets, eacl_full_placement_container_with_object, file_path
self, wallets, client_shell, eacl_full_placement_container_with_object, file_path
):
user_wallet = wallets.get_wallet()
cid, oid, file_path = eacl_full_placement_container_with_object
@ -161,7 +208,12 @@ class TestEACLContainer:
EACLRule(access=EACLAccess.DENY, role=EACLRole.OTHERS, operation=op)
for op in EACLOperation
]
set_eacl(user_wallet.wallet_path, cid, create_eacl(cid, eacl_deny))
set_eacl(
user_wallet.wallet_path,
cid,
create_eacl(cid, eacl_deny, shell=client_shell),
shell=client_shell,
)
wait_for_cache_expired()
with allure.step("Drop object to check replication"):
@ -172,7 +224,7 @@ class TestEACLContainer:
wait_object_replication_on_nodes(storage_wallet_path, cid, oid, self.NODE_COUNT)
@allure.title("Testcase to validate NeoFS system operations with extended ACL")
def test_extended_actions_system(self, wallets, eacl_container_with_objects):
def test_extended_actions_system(self, wallets, client_shell, eacl_container_with_objects):
user_wallet = wallets.get_wallet()
ir_wallet, storage_wallet = wallets.get_wallets_list(role=EACLRole.SYSTEM)[:2]
@ -180,10 +232,18 @@ class TestEACLContainer:
with allure.step("Check IR and STORAGE rules compliance"):
assert not can_put_object(
ir_wallet.wallet_path, cid, file_path, wallet_config=ir_wallet.config_path
ir_wallet.wallet_path,
cid,
file_path,
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
assert can_put_object(
storage_wallet.wallet_path, cid, file_path, wallet_config=storage_wallet.config_path
storage_wallet.wallet_path,
cid,
file_path,
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
assert can_get_object(
@ -191,6 +251,7 @@ class TestEACLContainer:
cid,
object_oids[0],
file_path,
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
assert can_get_object(
@ -198,62 +259,88 @@ class TestEACLContainer:
cid,
object_oids[0],
file_path,
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
assert can_get_head_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
ir_wallet.wallet_path,
cid,
object_oids[0],
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
assert can_get_head_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
assert can_search_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
ir_wallet.wallet_path,
cid,
shell=client_shell,
oid=object_oids[0],
wallet_config=ir_wallet.config_path,
)
assert can_search_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
shell=client_shell,
oid=object_oids[0],
wallet_config=storage_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_range_of_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_range_of_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_range_hash_of_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_range_hash_of_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_delete_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_delete_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
@ -262,97 +349,134 @@ class TestEACLContainer:
user_wallet.wallet_path,
cid,
create_eacl(
cid,
[
cid=cid,
rules_list=[
EACLRule(access=EACLAccess.DENY, role=EACLRole.SYSTEM, operation=op)
for op in EACLOperation
],
shell=client_shell,
),
shell=client_shell,
)
wait_for_cache_expired()
with allure.step("Check IR and STORAGE rules compliance with deny eACL"):
assert not can_put_object(
ir_wallet.wallet_path, cid, file_path, wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
file_name=file_path,
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
assert not can_put_object(
storage_wallet.wallet_path, cid, file_path, wallet_config=storage_wallet.config_path
wallet=storage_wallet.wallet_path,
cid=cid,
file_name=file_path,
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_object(
ir_wallet.wallet_path,
cid,
object_oids[0],
file_path,
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
file_name=file_path,
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
file_path,
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
file_name=file_path,
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_head_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_head_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_search_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
shell=client_shell,
oid=object_oids[0],
wallet_config=ir_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_search_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
shell=client_shell,
oid=object_oids[0],
wallet_config=storage_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_range_of_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_range_of_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_range_hash_of_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_range_hash_of_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_delete_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_delete_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
@ -361,90 +485,127 @@ class TestEACLContainer:
user_wallet.wallet_path,
cid,
create_eacl(
cid,
[
cid=cid,
rules_list=[
EACLRule(access=EACLAccess.ALLOW, role=EACLRole.SYSTEM, operation=op)
for op in EACLOperation
],
shell=client_shell,
),
shell=client_shell,
)
wait_for_cache_expired()
with allure.step("Check IR and STORAGE rules compliance with allow eACL"):
assert not can_put_object(
ir_wallet.wallet_path, cid, file_path, wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
file_name=file_path,
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
assert can_put_object(
storage_wallet.wallet_path, cid, file_path, wallet_config=storage_wallet.config_path
wallet=storage_wallet.wallet_path,
cid=cid,
file_name=file_path,
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
assert can_get_object(
ir_wallet.wallet_path,
cid,
object_oids[0],
file_path,
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
file_name=file_path,
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
assert can_get_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
file_path,
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
file_name=file_path,
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
assert can_get_head_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
assert can_get_head_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
assert can_search_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
shell=client_shell,
oid=object_oids[0],
wallet_config=ir_wallet.config_path,
)
assert can_search_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
shell=client_shell,
oid=object_oids[0],
wallet_config=storage_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_range_of_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_range_of_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_range_hash_of_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_get_range_hash_of_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=storage_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_delete_object(
ir_wallet.wallet_path, cid, object_oids[0], wallet_config=ir_wallet.config_path
wallet=ir_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=ir_wallet.config_path,
)
with pytest.raises(AssertionError):
assert can_delete_object(
storage_wallet.wallet_path,
cid,
object_oids[0],
wallet=storage_wallet.wallet_path,
cid=cid,
oid=object_oids[0],
shell=client_shell,
wallet_config=storage_wallet.config_path,
)

View file

@ -68,10 +68,12 @@ class TestEACLFilters:
]
@pytest.fixture(scope="function")
def eacl_container_with_objects(self, wallets, file_path):
def eacl_container_with_objects(self, wallets, client_shell, file_path):
user_wallet = wallets.get_wallet()
with allure.step("Create eACL public container"):
cid = create_container(user_wallet.wallet_path, basic_acl=PUBLIC_ACL)
cid = create_container(
user_wallet.wallet_path, basic_acl=PUBLIC_ACL, shell=client_shell
)
with allure.step("Add test objects to container"):
objects_with_header = [
@ -79,6 +81,7 @@ class TestEACLFilters:
user_wallet.wallet_path,
file_path,
cid,
shell=client_shell,
attributes={**self.SET_HEADERS, "key": val},
)
for val in range(self.OBJECT_COUNT)
@ -89,25 +92,28 @@ class TestEACLFilters:
user_wallet.wallet_path,
file_path,
cid,
shell=client_shell,
attributes={**self.OTHER_HEADERS, "key": val},
)
for val in range(self.OBJECT_COUNT)
]
objects_without_header = [
put_object(user_wallet.wallet_path, file_path, cid)
put_object(user_wallet.wallet_path, file_path, cid, shell=client_shell)
for _ in range(self.OBJECT_COUNT)
]
yield cid, objects_with_header, objects_with_other_header, objects_without_header, file_path
with allure.step("Delete eACL public container"):
delete_container(user_wallet.wallet_path, cid)
delete_container(user_wallet.wallet_path, cid, shell=client_shell)
@pytest.mark.parametrize(
"match_type", [EACLMatchType.STRING_EQUAL, EACLMatchType.STRING_NOT_EQUAL]
)
def test_extended_acl_filters_request(self, wallets, eacl_container_with_objects, match_type):
def test_extended_acl_filters_request(
self, wallets, client_shell, eacl_container_with_objects, match_type
):
allure.dynamic.title(f"Validate NeoFS operations with request filter: {match_type.name}")
user_wallet = wallets.get_wallet()
other_wallet = wallets.get_wallet(EACLRole.OTHERS)
@ -131,7 +137,12 @@ class TestEACLFilters:
)
for op in EACLOperation
]
set_eacl(user_wallet.wallet_path, cid, create_eacl(cid, eacl_deny))
set_eacl(
user_wallet.wallet_path,
cid,
create_eacl(cid, eacl_deny, shell=client_shell),
shell=client_shell,
)
wait_for_cache_expired()
# Filter denies requests where "check_key {match_type} ATTRIBUTE", so when match_type
@ -152,7 +163,9 @@ class TestEACLFilters:
objects_without_header,
):
with allure.step("Check other has full access when sending request without headers"):
check_full_access_to_container(other_wallet.wallet_path, cid, oid.pop(), file_path)
check_full_access_to_container(
other_wallet.wallet_path, cid, oid.pop(), file_path, shell=client_shell
)
with allure.step(
"Check other has full access when sending request with allowed headers"
@ -162,6 +175,7 @@ class TestEACLFilters:
cid,
oid.pop(),
file_path,
shell=client_shell,
xhdr=allow_headers,
)
@ -171,6 +185,7 @@ class TestEACLFilters:
cid,
oid.pop(),
file_path,
shell=client_shell,
xhdr=deny_headers,
)
@ -185,12 +200,14 @@ class TestEACLFilters:
EACLRule(operation=op, access=EACLAccess.ALLOW, role=EACLRole.OTHERS)
for op in EACLOperation
],
shell=client_shell,
)
check_full_access_to_container(
other_wallet.wallet_path,
cid,
oid.pop(),
file_path,
shell=client_shell,
xhdr=deny_headers,
bearer=bearer_token_other,
)
@ -199,7 +216,7 @@ class TestEACLFilters:
"match_type", [EACLMatchType.STRING_EQUAL, EACLMatchType.STRING_NOT_EQUAL]
)
def test_extended_acl_deny_filters_object(
self, wallets, eacl_container_with_objects, match_type
self, wallets, client_shell, eacl_container_with_objects, match_type
):
allure.dynamic.title(
f"Validate NeoFS operations with deny user headers filter: {match_type.name}"
@ -226,7 +243,12 @@ class TestEACLFilters:
)
for op in self.OBJECT_ATTRIBUTES_FILTER_SUPPORTED_OPERATIONS
]
set_eacl(user_wallet.wallet_path, cid, create_eacl(cid, eacl_deny))
set_eacl(
user_wallet.wallet_path,
cid,
create_eacl(cid, eacl_deny, shell=client_shell),
shell=client_shell,
)
wait_for_cache_expired()
allow_objects = (
@ -250,6 +272,7 @@ class TestEACLFilters:
cid,
objs_without_header.pop(),
file_path,
shell=client_shell,
xhdr=xhdr,
)
@ -259,13 +282,18 @@ class TestEACLFilters:
cid,
allow_objects.pop(),
file_path,
shell=client_shell,
xhdr=xhdr,
)
with allure.step("Check other have no access to objects with deny attribute"):
with pytest.raises(AssertionError):
assert can_get_head_object(
other_wallet.wallet_path, cid, deny_objects[0], xhdr=xhdr
other_wallet.wallet_path,
cid,
deny_objects[0],
shell=client_shell,
xhdr=xhdr,
)
with pytest.raises(AssertionError):
assert can_get_object(
@ -273,6 +301,7 @@ class TestEACLFilters:
cid,
deny_objects[0],
file_path,
shell=client_shell,
xhdr=xhdr,
)
@ -290,12 +319,14 @@ class TestEACLFilters:
)
for op in EACLOperation
],
shell=client_shell,
)
check_full_access_to_container(
other_wallet.wallet_path,
cid,
deny_objects.pop(),
file_path,
shell=client_shell,
xhdr=xhdr,
bearer=bearer_token_other,
)
@ -305,9 +336,13 @@ class TestEACLFilters:
)
with allure.step("Check other can PUT objects without denied attribute"):
assert can_put_object(
other_wallet.wallet_path, cid, file_path, attributes=allow_attribute
other_wallet.wallet_path,
cid,
file_path,
shell=client_shell,
attributes=allow_attribute,
)
assert can_put_object(other_wallet.wallet_path, cid, file_path)
assert can_put_object(other_wallet.wallet_path, cid, file_path, shell=client_shell)
deny_attribute = (
self.ATTRIBUTE if match_type == EACLMatchType.STRING_EQUAL else self.OTHER_ATTRIBUTE
@ -315,7 +350,11 @@ class TestEACLFilters:
with allure.step("Check other can not PUT objects with denied attribute"):
with pytest.raises(AssertionError):
assert can_put_object(
other_wallet.wallet_path, cid, file_path, attributes=deny_attribute
other_wallet.wallet_path,
cid,
file_path,
shell=client_shell,
attributes=deny_attribute,
)
with allure.step(
@ -331,11 +370,13 @@ class TestEACLFilters:
role=EACLRole.OTHERS,
)
],
shell=client_shell,
)
assert can_put_object(
other_wallet.wallet_path,
cid,
file_path,
shell=client_shell,
attributes=deny_attribute,
bearer=bearer_token_other_for_put,
)
@ -344,7 +385,7 @@ class TestEACLFilters:
"match_type", [EACLMatchType.STRING_EQUAL, EACLMatchType.STRING_NOT_EQUAL]
)
def test_extended_acl_allow_filters_object(
self, wallets, eacl_container_with_objects, match_type
self, wallets, client_shell, eacl_container_with_objects, match_type
):
allure.dynamic.title(
"Testcase to validate NeoFS operation with allow eACL user headers filters:"
@ -377,7 +418,12 @@ class TestEACLFilters:
EACLRule(access=EACLAccess.DENY, role=EACLRole.OTHERS, operation=op)
for op in self.OBJECT_ATTRIBUTES_FILTER_SUPPORTED_OPERATIONS
]
set_eacl(user_wallet.wallet_path, cid, create_eacl(cid, eacl))
set_eacl(
user_wallet.wallet_path,
cid,
create_eacl(cid, eacl, shell=client_shell),
shell=client_shell,
)
wait_for_cache_expired()
if match_type == EACLMatchType.STRING_EQUAL:
@ -394,11 +440,13 @@ class TestEACLFilters:
with allure.step(f"Check other cannot get and put objects without attributes"):
oid = objects_without_header.pop()
with pytest.raises(AssertionError):
assert can_get_head_object(other_wallet.wallet_path, cid, oid)
assert can_get_head_object(other_wallet.wallet_path, cid, oid, shell=client_shell)
with pytest.raises(AssertionError):
assert can_get_object(other_wallet.wallet_path, cid, oid, file_path)
assert can_get_object(
other_wallet.wallet_path, cid, oid, file_path, shell=client_shell
)
with pytest.raises(AssertionError):
assert can_put_object(other_wallet.wallet_path, cid, file_path)
assert can_put_object(other_wallet.wallet_path, cid, file_path, shell=client_shell)
with allure.step(
"Check other can get and put objects without attributes and using bearer token"
@ -414,11 +462,13 @@ class TestEACLFilters:
)
for op in EACLOperation
],
shell=client_shell,
)
assert can_get_head_object(
other_wallet.wallet_path,
cid,
objects_without_header[0],
shell=client_shell,
bearer=bearer_token_other,
)
assert can_get_object(
@ -426,28 +476,45 @@ class TestEACLFilters:
cid,
objects_without_header[0],
file_path,
shell=client_shell,
bearer=bearer_token_other,
)
assert can_put_object(
other_wallet.wallet_path, cid, file_path, bearer=bearer_token_other
other_wallet.wallet_path,
cid,
file_path,
shell=client_shell,
bearer=bearer_token_other,
)
with allure.step(f"Check other can get objects with attributes matching the filter"):
oid = allow_objects.pop()
assert can_get_head_object(other_wallet.wallet_path, cid, oid)
assert can_get_object(other_wallet.wallet_path, cid, oid, file_path)
assert can_get_head_object(other_wallet.wallet_path, cid, oid, shell=client_shell)
assert can_get_object(other_wallet.wallet_path, cid, oid, file_path, shell=client_shell)
assert can_put_object(
other_wallet.wallet_path, cid, file_path, attributes=allow_attribute
other_wallet.wallet_path,
cid,
file_path,
shell=client_shell,
attributes=allow_attribute,
)
with allure.step("Check other cannot get objects without attributes matching the filter"):
with pytest.raises(AssertionError):
assert can_get_head_object(other_wallet.wallet_path, cid, deny_objects[0])
assert can_get_head_object(
other_wallet.wallet_path, cid, deny_objects[0], shell=client_shell
)
with pytest.raises(AssertionError):
assert can_get_object(other_wallet.wallet_path, cid, deny_objects[0], file_path)
assert can_get_object(
other_wallet.wallet_path, cid, deny_objects[0], file_path, shell=client_shell
)
with pytest.raises(AssertionError):
assert can_put_object(
other_wallet.wallet_path, cid, file_path, attributes=deny_attribute
other_wallet.wallet_path,
cid,
file_path,
attributes=deny_attribute,
shell=client_shell,
)
with allure.step(
@ -456,15 +523,21 @@ class TestEACLFilters:
):
oid = deny_objects.pop()
assert can_get_head_object(
other_wallet.wallet_path, cid, oid, bearer=bearer_token_other
other_wallet.wallet_path, cid, oid, shell=client_shell, bearer=bearer_token_other
)
assert can_get_object(
other_wallet.wallet_path, cid, oid, file_path, bearer=bearer_token_other
other_wallet.wallet_path,
cid,
oid,
file_path,
shell=client_shell,
bearer=bearer_token_other,
)
assert can_put_object(
other_wallet.wallet_path,
cid,
file_path,
shell=client_shell,
attributes=deny_attribute,
bearer=bearer_token_other,
)

View file

@ -8,15 +8,20 @@ import allure
import pytest
import wallet
from cli_helpers import _cmd_run
from cli_utils import NeofsAdm, NeofsCli
from common import (
ASSETS_DIR,
FREE_STORAGE,
INFRASTRUCTURE_TYPE,
MAINNET_WALLET_PATH,
NEOFS_ADM_EXEC,
NEOFS_CLI_EXEC,
NEOFS_NETMAP_DICT,
WALLET_CONFIG,
)
from neofs_testlib.cli import NeofsAdm, NeofsCli
from env_properties import save_env_properties
from neofs_testlib.shell import LocalShell, Shell
from payment_neogo import neofs_deposit, transfer_mainnet_gas
from python_keywords.node_management import node_healthcheck
from service_helper import get_storage_service_helper
@ -24,6 +29,11 @@ from service_helper import get_storage_service_helper
logger = logging.getLogger("NeoLogger")
@pytest.fixture(scope="session")
def client_shell() -> Shell:
yield LocalShell()
@pytest.fixture(scope="session")
def cloud_infrastructure_check():
if INFRASTRUCTURE_TYPE != "CLOUD_VM":
@ -33,18 +43,20 @@ def cloud_infrastructure_check():
@pytest.fixture(scope="session", autouse=True)
@allure.title("Check binary versions")
def check_binary_versions(request):
def check_binary_versions(request, client_shell):
# Collect versions of local binaries
binaries = ["neo-go", "neofs-authmate"]
local_binaries = _get_binaries_version_local(binaries)
try:
local_binaries["neofs-adm"] = NeofsAdm().version.get()
local_binaries["neofs-adm"] = NeofsAdm(client_shell, NEOFS_ADM_EXEC).version.get()
except RuntimeError:
logger.info(f"neofs-adm not installed")
local_binaries["neofs-cli"] = NeofsCli().version.get()
local_binaries["neofs-cli"] = NeofsCli(
client_shell, NEOFS_CLI_EXEC, WALLET_CONFIG
).version.get()
# Collect versions of remote binaries
helper = get_storage_service_helper()
remote_binaries = helper.get_binaries_version()
all_binaries = {**local_binaries, **remote_binaries}

View file

@ -18,7 +18,7 @@ from wellknown_acl import PRIVATE_ACL_F
@pytest.mark.parametrize("name", ["", "test-container"], ids=["No name", "Set particular name"])
@pytest.mark.sanity
@pytest.mark.container
def test_container_creation(prepare_wallet_and_deposit, name):
def test_container_creation(client_shell, prepare_wallet_and_deposit, name):
scenario_title = f"with name {name}" if name else "without name"
allure.dynamic.title(f"User can create container {scenario_title}")
@ -27,12 +27,12 @@ def test_container_creation(prepare_wallet_and_deposit, name):
json_wallet = json.load(file)
placement_rule = "REP 2 IN X CBF 1 SELECT 2 FROM * AS X"
cid = create_container(wallet, rule=placement_rule, name=name)
cid = create_container(wallet, rule=placement_rule, name=name, shell=client_shell)
containers = list_containers(wallet)
containers = list_containers(wallet, shell=client_shell)
assert cid in containers, f"Expected container {cid} in containers: {containers}"
container_info: str = get_container(wallet, cid, json_mode=False)
container_info: str = get_container(wallet, cid, json_mode=False, shell=client_shell)
container_info = container_info.casefold() # To ignore case when comparing with expected values
info_to_check = {
@ -57,15 +57,15 @@ def test_container_creation(prepare_wallet_and_deposit, name):
), f"Expected {expected_info} in container info:\n{container_info}"
with allure.step("Delete container and check it was deleted"):
delete_container(wallet, cid)
delete_container(wallet, cid, shell=client_shell)
tick_epoch()
wait_for_container_deletion(wallet, cid)
wait_for_container_deletion(wallet, cid, shell=client_shell)
@allure.title("Parallel container creation and deletion")
@pytest.mark.sanity
@pytest.mark.container
def test_container_creation_deletion_parallel(prepare_wallet_and_deposit):
def test_container_creation_deletion_parallel(client_shell, prepare_wallet_and_deposit):
containers_count = 3
wallet = prepare_wallet_and_deposit
placement_rule = "REP 2 IN X CBF 1 SELECT 2 FROM * AS X"
@ -75,16 +75,22 @@ def test_container_creation_deletion_parallel(prepare_wallet_and_deposit):
for _ in range(containers_count):
cids.append(
create_container(
wallet, rule=placement_rule, await_mode=False, wait_for_creation=False
wallet,
rule=placement_rule,
await_mode=False,
shell=client_shell,
wait_for_creation=False,
)
)
with allure.step(f"Wait for containers occur in container list"):
for cid in cids:
wait_for_container_creation(wallet, cid, sleep_interval=containers_count)
wait_for_container_creation(
wallet, cid, sleep_interval=containers_count, shell=client_shell
)
with allure.step("Delete containers and check they were deleted"):
for cid in cids:
delete_container(wallet, cid)
delete_container(wallet, cid, shell=client_shell)
tick_epoch()
wait_for_container_deletion(wallet, cid)
wait_for_container_deletion(wallet, cid, shell=client_shell)

View file

@ -48,7 +48,7 @@ def restore_network():
@allure.title("Block Storage node traffic")
@pytest.mark.failover
@pytest.mark.failover_net
def test_block_storage_node_traffic(prepare_wallet_and_deposit, cloud_infrastructure_check):
def test_block_storage_node_traffic(prepare_wallet_and_deposit, client_shell, cloud_infrastructure_check):
"""
Block storage nodes traffic using iptables and wait for replication for objects.
"""
@ -59,9 +59,9 @@ def test_block_storage_node_traffic(prepare_wallet_and_deposit, cloud_infrastruc
nodes_to_block_count = 2
source_file_path = generate_file()
cid = create_container(wallet, rule=placement_rule, basic_acl=PUBLIC_ACL)
oid = put_object(wallet, source_file_path, cid)
nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2)
cid = create_container(wallet, shell=client_shell, rule=placement_rule, basic_acl=PUBLIC_ACL)
oid = put_object(wallet, source_file_path, cid, shell=client_shell)
nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2, shell=client_shell)
logger.info(f"Nodes are {nodes}")
random_nodes = [(node, node.split(":")[0]) for node in nodes]
@ -89,7 +89,7 @@ def test_block_storage_node_traffic(prepare_wallet_and_deposit, cloud_infrastruc
assert random_node not in new_nodes
got_file_path = get_object(wallet, cid, oid, endpoint=new_nodes[0])
got_file_path = get_object(wallet, cid, oid, shell=client_shell, endpoint=new_nodes[0])
assert get_file_hash(source_file_path) == get_file_hash(got_file_path)
for random_node, random_node_ip in random_nodes:
@ -108,7 +108,7 @@ def test_block_storage_node_traffic(prepare_wallet_and_deposit, cloud_infrastruc
blocked_hosts.remove(random_node_ip)
sleep(wakeup_node_timeout)
new_nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2)
new_nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2, shell=client_shell)
got_file_path = get_object(wallet, cid, oid, endpoint=new_nodes[0])
got_file_path = get_object(wallet, cid, oid, shell=client_shell, endpoint=new_nodes[0])
assert get_file_hash(source_file_path) == get_file_hash(got_file_path)

View file

@ -64,6 +64,7 @@ def return_all_storage_nodes(sbercloud_client: SberCloud) -> None:
@pytest.mark.failover
def test_lost_storage_node(
prepare_wallet_and_deposit,
client_shell,
sbercloud_client: SberCloud,
cloud_infrastructure_check,
hard_reboot: bool,
@ -71,27 +72,29 @@ def test_lost_storage_node(
wallet = prepare_wallet_and_deposit
placement_rule = "REP 2 IN X CBF 2 SELECT 2 FROM * AS X"
source_file_path = generate_file()
cid = create_container(wallet, rule=placement_rule, basic_acl=PUBLIC_ACL)
oid = put_object(wallet, source_file_path, cid)
nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2)
cid = create_container(wallet, shell=client_shell, rule=placement_rule, basic_acl=PUBLIC_ACL)
oid = put_object(wallet, source_file_path, cid, shell=client_shell)
nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2, shell=client_shell)
new_nodes = []
for node in nodes:
stopped_hosts.append(node)
with allure.step(f"Stop storage node {node}"):
sbercloud_client.stop_node(node_ip=node.split(":")[-2], hard=hard_reboot)
new_nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2, excluded_nodes=[node])
new_nodes = wait_object_replication_on_nodes(
wallet, cid, oid, 2, shell=client_shell, excluded_nodes=[node]
)
assert not [node for node in nodes if node in new_nodes]
got_file_path = get_object(wallet, cid, oid, endpoint=new_nodes[0])
got_file_path = get_object(wallet, cid, oid, shell=client_shell, endpoint=new_nodes[0])
assert get_file_hash(source_file_path) == get_file_hash(got_file_path)
with allure.step(f"Return storage nodes"):
return_all_storage_nodes(sbercloud_client)
new_nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2)
new_nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2, shell=client_shell)
got_file_path = get_object(wallet, cid, oid, endpoint=new_nodes[0])
got_file_path = get_object(wallet, cid, oid, shell=client_shell, endpoint=new_nodes[0])
assert get_file_hash(source_file_path) == get_file_hash(got_file_path)
@ -99,14 +102,16 @@ def test_lost_storage_node(
@pytest.mark.parametrize("sequence", [True, False])
@pytest.mark.failover_panic
@pytest.mark.failover
def test_panic_storage_node(prepare_wallet_and_deposit, cloud_infrastructure_check, sequence: bool):
def test_panic_storage_node(
prepare_wallet_and_deposit, client_shell, cloud_infrastructure_check, sequence: bool
):
wallet = prepare_wallet_and_deposit
placement_rule = "REP 2 IN X CBF 2 SELECT 2 FROM * AS X"
source_file_path = generate_file()
cid = create_container(wallet, rule=placement_rule, basic_acl=PUBLIC_ACL)
oid = put_object(wallet, source_file_path, cid)
cid = create_container(wallet, shell=client_shell, rule=placement_rule, basic_acl=PUBLIC_ACL)
oid = put_object(wallet, source_file_path, cid, shell=client_shell)
nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2)
nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2, shell=client_shell)
new_nodes: list[str] = []
allure.attach("\n".join(nodes), "Current nodes with object", allure.attachment_type.TEXT)
for node in nodes:
@ -115,10 +120,12 @@ def test_panic_storage_node(prepare_wallet_and_deposit, cloud_infrastructure_che
if sequence:
try:
new_nodes = wait_object_replication_on_nodes(
wallet, cid, oid, 2, excluded_nodes=[node]
wallet, cid, oid, 2, shell=client_shell, excluded_nodes=[node]
)
except AssertionError:
new_nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2)
new_nodes = wait_object_replication_on_nodes(
wallet, cid, oid, 2, shell=client_shell
)
allure.attach(
"\n".join(new_nodes),
@ -127,10 +134,10 @@ def test_panic_storage_node(prepare_wallet_and_deposit, cloud_infrastructure_che
)
if not sequence:
new_nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2)
new_nodes = wait_object_replication_on_nodes(wallet, cid, oid, 2, shell=client_shell)
allure.attach(
"\n".join(new_nodes), "Nodes with object after nodes fail", allure.attachment_type.TEXT
)
got_file_path = get_object(wallet, cid, oid, endpoint=new_nodes[0])
got_file_path = get_object(wallet, cid, oid, shell=client_shell, endpoint=new_nodes[0])
assert get_file_hash(source_file_path) == get_file_hash(got_file_path)

View file

@ -4,9 +4,9 @@ from time import sleep
import allure
import pytest
from neofs_testlib.shell import Shell
from common import (
COMPLEX_OBJ_SIZE,
MAINNET_BLOCK_TIME,
MORPH_BLOCK_TIME,
NEOFS_CONTRACT_CACHE_TIMEOUT,
NEOFS_NETMAP_DICT,
@ -46,15 +46,15 @@ check_nodes = []
@pytest.fixture
@allure.title("Create container and pick the node with data")
def create_container_and_pick_node(prepare_wallet_and_deposit):
def create_container_and_pick_node(prepare_wallet_and_deposit, client_shell):
wallet = prepare_wallet_and_deposit
file_path = generate_file()
placement_rule = "REP 1 IN X CBF 1 SELECT 1 FROM * AS X"
cid = create_container(wallet, rule=placement_rule, basic_acl=PUBLIC_ACL)
oid = put_object(wallet, file_path, cid)
cid = create_container(wallet, shell=client_shell, rule=placement_rule, basic_acl=PUBLIC_ACL)
oid = put_object(wallet, file_path, cid, shell=client_shell)
nodes = get_nodes_with_object(wallet, cid, oid)
nodes = get_nodes_with_object(wallet, cid, oid, shell=client_shell)
assert len(nodes) == 1
node = nodes[0]
@ -83,13 +83,13 @@ def after_run_start_all_nodes():
@pytest.fixture
def return_nodes_after_test_run():
def return_nodes_after_test_run(client_shell):
yield
return_nodes()
return_nodes(shell=client_shell)
@allure.step("Return node to cluster")
def return_nodes(alive_node: str = None):
def return_nodes(shell: Shell, alive_node: str = None):
helper = get_storage_service_helper()
for node in list(check_nodes):
with allure.step(f"Start node {node}"):
@ -113,13 +113,15 @@ def return_nodes(alive_node: str = None):
except RuntimeError:
sleep(3)
check_node_in_map(node, alive_node)
check_node_in_map(node, shell=shell, alive_node=alive_node)
@allure.title("Add one node to cluster")
@pytest.mark.add_nodes
@pytest.mark.node_mgmt
def test_add_nodes(prepare_tmp_dir, prepare_wallet_and_deposit, return_nodes_after_test_run):
def test_add_nodes(
prepare_tmp_dir, client_shell, prepare_wallet_and_deposit, return_nodes_after_test_run
):
wallet = prepare_wallet_and_deposit
placement_rule_3 = "REP 3 IN X CBF 1 SELECT 3 FROM * AS X"
placement_rule_4 = "REP 4 IN X CBF 1 SELECT 4 FROM * AS X"
@ -134,7 +136,7 @@ def test_add_nodes(prepare_tmp_dir, prepare_wallet_and_deposit, return_nodes_aft
)
alive_node = choice([node for node in NEOFS_NETMAP_DICT if node != additional_node])
check_node_in_map(additional_node, alive_node)
check_node_in_map(additional_node, shell=client_shell, alive_node=alive_node)
# Add node to recovery list before messing with it
check_nodes.append(additional_node)
@ -147,7 +149,7 @@ def test_add_nodes(prepare_tmp_dir, prepare_wallet_and_deposit, return_nodes_aft
)
wait_object_replication_on_nodes(wallet, cid, oid, 3)
return_nodes(alive_node)
return_nodes(shell=client_shell, alive_node=alive_node)
with allure.step("Check data could be replicated to new node"):
random_node = choice(
@ -156,7 +158,7 @@ def test_add_nodes(prepare_tmp_dir, prepare_wallet_and_deposit, return_nodes_aft
exclude_node_from_network_map(random_node, alive_node)
wait_object_replication_on_nodes(wallet, cid, oid, 3, excluded_nodes=[random_node])
include_node_to_network_map(random_node, alive_node)
include_node_to_network_map(random_node, alive_node, shell=client_shell)
wait_object_replication_on_nodes(wallet, cid, oid, 3)
with allure.step("Check container could be created with new node"):
@ -164,12 +166,12 @@ def test_add_nodes(prepare_tmp_dir, prepare_wallet_and_deposit, return_nodes_aft
oid = put_object(
wallet, source_file_path, cid, endpoint=NEOFS_NETMAP_DICT[alive_node].get("rpc")
)
wait_object_replication_on_nodes(wallet, cid, oid, 4)
wait_object_replication_on_nodes(wallet, cid, oid, 4, shell=client_shell)
@allure.title("Control Operations with storage nodes")
@pytest.mark.node_mgmt
def test_nodes_management(prepare_tmp_dir):
def test_nodes_management(prepare_tmp_dir, client_shell):
"""
This test checks base control operations with storage nodes (healthcheck, netmap-snapshot, set-status).
"""
@ -181,7 +183,7 @@ def test_nodes_management(prepare_tmp_dir):
random_node_netmap_key = get_wallet_public_key(random_node_wallet_path, STORAGE_WALLET_PASS)
with allure.step("Check node {random_node} is in netmap"):
snapshot = get_netmap_snapshot(node_name=alive_node)
snapshot = get_netmap_snapshot(node_name=alive_node, shell=client_shell)
assert random_node_netmap_key in snapshot, f"Expected node {random_node} in netmap"
with allure.step("Run health check for all storage nodes"):
@ -198,7 +200,7 @@ def test_nodes_management(prepare_tmp_dir):
with allure.step(f"Check node {random_node} went to offline"):
health_check = node_healthcheck(random_node)
assert health_check.health_status == "READY" and health_check.network_status == "OFFLINE"
snapshot = get_netmap_snapshot(node_name=alive_node)
snapshot = get_netmap_snapshot(node_name=alive_node, shell=client_shell)
assert random_node_netmap_key not in snapshot, f"Expected node {random_node} not in netmap"
with allure.step(f"Check node {random_node} went to online"):
@ -210,7 +212,7 @@ def test_nodes_management(prepare_tmp_dir):
with allure.step(f"Check node {random_node} went to online"):
health_check = node_healthcheck(random_node)
assert health_check.health_status == "READY" and health_check.network_status == "ONLINE"
snapshot = get_netmap_snapshot(node_name=alive_node)
snapshot = get_netmap_snapshot(node_name=alive_node, shell=client_shell)
assert random_node_netmap_key in snapshot, f"Expected node {random_node} in netmap"

View file

@ -18,6 +18,7 @@ from python_keywords.neofs_verbs import (
search_object,
)
from python_keywords.storage_policy import get_complex_object_copies, get_simple_object_copies
from neofs_testlib.shell import Shell
from tombstone import verify_head_tombstone
from utility import wait_for_gc_pass_on_storage_nodes
@ -32,14 +33,14 @@ CLEANUP_TIMEOUT = 10
@pytest.mark.parametrize(
"object_size", [SIMPLE_OBJ_SIZE, COMPLEX_OBJ_SIZE], ids=["simple object", "complex object"]
)
def test_object_api(prepare_wallet_and_deposit, request, object_size):
def test_object_api(prepare_wallet_and_deposit, client_shell, request, object_size):
"""
Test common gRPC API for object (put/get/head/get_range_hash/get_range/search/delete).
"""
allure.dynamic.title(f"Test native object API for {request.node.callspec.id}")
wallet = prepare_wallet_and_deposit
cid = create_container(wallet)
cid = create_container(wallet, shell=client_shell)
wallet_cid = {"wallet": wallet, "cid": cid}
file_usr_header = {"key1": 1, "key2": "abc", "common_key": "common_value"}
file_usr_header_oth = {"key1": 2, "common_key": "common_value"}
@ -50,70 +51,114 @@ def test_object_api(prepare_wallet_and_deposit, request, object_size):
file_hash = get_file_hash(file_path)
with allure.step("Check container is empty"):
search_object(**wallet_cid, expected_objects_list=[])
search_object(**wallet_cid, expected_objects_list=[], shell=client_shell)
oids = []
with allure.step("Put objects"):
oids.append(put_object(wallet=wallet, path=file_path, cid=cid))
oids.append(put_object(wallet=wallet, path=file_path, cid=cid, attributes=file_usr_header))
oids.append(put_object(wallet=wallet, path=file_path, cid=cid, shell=client_shell))
oids.append(
put_object(wallet=wallet, path=file_path, cid=cid, attributes=file_usr_header_oth)
put_object(
wallet=wallet,
path=file_path,
cid=cid,
shell=client_shell,
attributes=file_usr_header,
)
)
oids.append(
put_object(
wallet=wallet,
path=file_path,
cid=cid,
shell=client_shell,
attributes=file_usr_header_oth,
)
)
with allure.step("Validate storage policy for objects"):
for oid_to_check in oids:
if object_size == SIMPLE_OBJ_SIZE:
copies = get_simple_object_copies(wallet=wallet, cid=cid, oid=oid_to_check)
copies = get_simple_object_copies(
wallet=wallet, cid=cid, oid=oid_to_check, shell=client_shell
)
else:
copies = get_complex_object_copies(wallet=wallet, cid=cid, oid=oid_to_check)
copies = get_complex_object_copies(
wallet=wallet, cid=cid, oid=oid_to_check, shell=client_shell
)
assert copies == 2, "Expected 2 copies"
with allure.step("Get objects and compare hashes"):
for oid_to_check in oids:
got_file_path = get_object(wallet=wallet, cid=cid, oid=oid_to_check)
got_file_path = get_object(wallet=wallet, cid=cid, oid=oid_to_check, shell=client_shell)
got_file_hash = get_file_hash(got_file_path)
assert file_hash == got_file_hash
with allure.step("Get range/range hash"):
range_hash = get_range_hash(**wallet_cid, oid=oids[0], bearer_token="", range_cut=range_cut)
range_hash = get_range_hash(
**wallet_cid, oid=oids[0], shell=client_shell, range_cut=range_cut
)
assert (
get_file_hash(file_path, range_len) == range_hash
), f"Expected range hash to match {range_cut} slice of file payload"
range_hash = get_range_hash(**wallet_cid, oid=oids[1], bearer_token="", range_cut=range_cut)
range_hash = get_range_hash(
**wallet_cid, oid=oids[1], shell=client_shell, range_cut=range_cut
)
assert (
get_file_hash(file_path, range_len) == range_hash
), f"Expected range hash to match {range_cut} slice of file payload"
_, range_content = get_range(**wallet_cid, oid=oids[1], bearer="", range_cut=range_cut)
_, range_content = get_range(
**wallet_cid, oid=oids[1], shell=client_shell, range_cut=range_cut
)
assert (
get_file_content(file_path, content_len=range_len, mode="rb") == range_content
), f"Expected range content to match {range_cut} slice of file payload"
with allure.step("Search objects"):
search_object(**wallet_cid, expected_objects_list=oids)
search_object(**wallet_cid, filters=file_usr_header, expected_objects_list=oids[1:2])
search_object(**wallet_cid, filters=file_usr_header_oth, expected_objects_list=oids[2:3])
search_object(**wallet_cid, filters=common_header, expected_objects_list=oids[1:3])
search_object(**wallet_cid, shell=client_shell, expected_objects_list=oids)
search_object(
**wallet_cid,
shell=client_shell,
filters=file_usr_header,
expected_objects_list=oids[1:2],
)
search_object(
**wallet_cid,
shell=client_shell,
filters=file_usr_header_oth,
expected_objects_list=oids[2:3],
)
search_object(
**wallet_cid, shell=client_shell, filters=common_header, expected_objects_list=oids[1:3]
)
with allure.step("Head object and validate"):
head_object(**wallet_cid, oid=oids[0])
head_info = head_object(**wallet_cid, oid=oids[1])
head_object(**wallet_cid, oid=oids[0], shell=client_shell)
head_info = head_object(**wallet_cid, oid=oids[1], shell=client_shell)
check_header_is_presented(head_info, file_usr_header)
with allure.step("Delete objects"):
tombstone_s = delete_object(**wallet_cid, oid=oids[0])
tombstone_h = delete_object(**wallet_cid, oid=oids[1])
tombstone_s = delete_object(**wallet_cid, oid=oids[0], shell=client_shell)
tombstone_h = delete_object(**wallet_cid, oid=oids[1], shell=client_shell)
verify_head_tombstone(wallet_path=wallet, cid=cid, oid_ts=tombstone_s, oid=oids[0])
verify_head_tombstone(wallet_path=wallet, cid=cid, oid_ts=tombstone_h, oid=oids[1])
verify_head_tombstone(
wallet_path=wallet, cid=cid, oid_ts=tombstone_s, oid=oids[0], shell=client_shell
)
verify_head_tombstone(
wallet_path=wallet, cid=cid, oid_ts=tombstone_h, oid=oids[1], shell=client_shell
)
tick_epoch()
sleep(CLEANUP_TIMEOUT)
with allure.step("Get objects and check errors"):
get_object_and_check_error(**wallet_cid, oid=oids[0], error_pattern=OBJECT_ALREADY_REMOVED)
get_object_and_check_error(**wallet_cid, oid=oids[1], error_pattern=OBJECT_ALREADY_REMOVED)
get_object_and_check_error(
**wallet_cid, oid=oids[0], error_pattern=OBJECT_ALREADY_REMOVED, shell=client_shell
)
get_object_and_check_error(
**wallet_cid, oid=oids[1], error_pattern=OBJECT_ALREADY_REMOVED, shell=client_shell
)
@allure.title("Test object life time")
@ -122,12 +167,12 @@ def test_object_api(prepare_wallet_and_deposit, request, object_size):
@pytest.mark.parametrize(
"object_size", [SIMPLE_OBJ_SIZE, COMPLEX_OBJ_SIZE], ids=["simple object", "complex object"]
)
def test_object_api_lifetime(prepare_wallet_and_deposit, request, object_size):
def test_object_api_lifetime(prepare_wallet_and_deposit, client_shell, request, object_size):
"""
Test object deleted after expiration epoch.
"""
wallet = prepare_wallet_and_deposit
cid = create_container(wallet)
cid = create_container(wallet, shell=client_shell)
allure.dynamic.title(f"Test object life time for {request.node.callspec.id}")
@ -135,8 +180,8 @@ def test_object_api_lifetime(prepare_wallet_and_deposit, request, object_size):
file_hash = get_file_hash(file_path)
epoch = get_epoch()
oid = put_object(wallet, file_path, cid, expire_at=epoch + 1)
got_file = get_object(wallet, cid, oid)
oid = put_object(wallet, file_path, cid, shell=client_shell, expire_at=epoch + 1)
got_file = get_object(wallet, cid, oid, shell=client_shell)
assert get_file_hash(got_file) == file_hash
with allure.step("Tick two epochs"):
@ -148,12 +193,14 @@ def test_object_api_lifetime(prepare_wallet_and_deposit, request, object_size):
with allure.step("Check object deleted because it expires-on epoch"):
with pytest.raises(Exception, match=OBJECT_NOT_FOUND):
get_object(wallet, cid, oid)
get_object(wallet, cid, oid, shell=client_shell)
def get_object_and_check_error(wallet: str, cid: str, oid: str, error_pattern: str) -> None:
def get_object_and_check_error(
wallet: str, cid: str, oid: str, error_pattern: str, shell: Shell
) -> None:
try:
get_object(wallet=wallet, cid=cid, oid=oid)
get_object(wallet=wallet, cid=cid, oid=oid, shell=shell)
raise AssertionError(f"Expected object {oid} removed, but it is not")
except Exception as err:
logger.info(f"Error is {err}")

View file

@ -3,8 +3,8 @@ import logging
import allure
import pytest
import yaml
from cli_utils import NeofsCli
from common import ASSETS_DIR, FREE_STORAGE, NEOFS_ENDPOINT, WALLET_CONFIG
from common import ASSETS_DIR, FREE_STORAGE, NEOFS_CLI_EXEC, NEOFS_ENDPOINT, WALLET_CONFIG
from neofs_testlib.cli import NeofsCli
from python_keywords.payment_neogo import _address_from_wallet
from wallet import init_wallet
@ -22,27 +22,27 @@ class TestBalanceAccounting:
_, self.another_address, _ = init_wallet(ASSETS_DIR)
@allure.title("Test balance request with wallet and address")
def test_balance_wallet_address(self):
cli = NeofsCli(config=WALLET_CONFIG)
def test_balance_wallet_address(self, client_shell):
cli = NeofsCli(client_shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
output = cli.accounting.balance(
wallet=self.user_wallet,
rpc_endpoint=NEOFS_ENDPOINT,
address=self.address,
)
logger.info(f"Out wallet+address: {output}")
assert int(output.rstrip()) == DEPOSIT_AMOUNT
assert int(output.stdout.rstrip()) == DEPOSIT_AMOUNT
@allure.title("Test balance request with wallet only")
def test_balance_wallet(self):
cli = NeofsCli(config=WALLET_CONFIG)
def test_balance_wallet(self, client_shell):
cli = NeofsCli(client_shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
output = cli.accounting.balance(wallet=self.user_wallet, rpc_endpoint=NEOFS_ENDPOINT)
logger.info(f"Out wallet: {output}")
assert int(output.rstrip()) == DEPOSIT_AMOUNT
assert int(output.stdout.rstrip()) == DEPOSIT_AMOUNT
@allure.title("Test balance request with wallet and wrong address")
def test_balance_wrong_address(self):
def test_balance_wrong_address(self, client_shell):
with pytest.raises(Exception, match="address option must be specified and valid"):
cli = NeofsCli(config=WALLET_CONFIG)
cli = NeofsCli(client_shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
cli.accounting.balance(
wallet=self.user_wallet,
rpc_endpoint=NEOFS_ENDPOINT,
@ -50,13 +50,13 @@ class TestBalanceAccounting:
)
@allure.title("Test balance request with config file")
def test_balance_api(self):
def test_balance_api(self, client_shell):
config_file = self.write_api_config(endpoint=NEOFS_ENDPOINT, wallet=self.user_wallet)
logger.info(f"YAML: {config_file}")
cli = NeofsCli(config=config_file)
cli = NeofsCli(client_shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
output = cli.accounting.balance()
logger.info(f"Out api: {output}")
assert int(output.rstrip()) == DEPOSIT_AMOUNT
logger.info(f"Out api: {output.stdout}")
assert int(output.stdout.rstrip()) == DEPOSIT_AMOUNT
@staticmethod
@allure.step("Write YAML config")

View file

@ -17,6 +17,7 @@ from python_keywords.http_gate import (
upload_via_http_gate,
upload_via_http_gate_curl,
)
from neofs_testlib.shell import Shell
from python_keywords.neofs_verbs import get_object, put_object
from python_keywords.storage_policy import get_nodes_without_object
from utility import wait_for_gc_pass_on_storage_nodes
@ -46,7 +47,7 @@ class TestHttpGate:
TestHttpGate.wallet = prepare_wallet_and_deposit
@allure.title("Test Put over gRPC, Get over HTTP")
def test_put_grpc_get_http(self):
def test_put_grpc_get_http(self, client_shell):
"""
Test that object can be put using gRPC interface and get using HTTP.
@ -61,21 +62,27 @@ class TestHttpGate:
Expected result:
Hashes must be the same.
"""
cid = create_container(self.wallet, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL)
cid = create_container(
self.wallet, shell=client_shell, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL
)
file_path_simple, file_path_large = generate_file(), generate_file(COMPLEX_OBJ_SIZE)
with allure.step("Put objects using gRPC"):
oid_simple = put_object(wallet=self.wallet, path=file_path_simple, cid=cid)
oid_large = put_object(wallet=self.wallet, path=file_path_large, cid=cid)
oid_simple = put_object(
wallet=self.wallet, path=file_path_simple, cid=cid, shell=client_shell
)
oid_large = put_object(
wallet=self.wallet, path=file_path_large, cid=cid, shell=client_shell
)
for oid, file_path in ((oid_simple, file_path_simple), (oid_large, file_path_large)):
self.get_object_and_verify_hashes(oid, file_path, self.wallet, cid)
self.get_object_and_verify_hashes(oid, file_path, self.wallet, cid, shell=client_shell)
@allure.link("https://github.com/nspcc-dev/neofs-http-gw#uploading", name="uploading")
@allure.link("https://github.com/nspcc-dev/neofs-http-gw#downloading", name="downloading")
@pytest.mark.sanity
@allure.title("Test Put over HTTP, Get over HTTP")
def test_put_http_get_http(self):
def test_put_http_get_http(self, client_shell):
"""
Test that object can be put and get using HTTP interface.
@ -88,7 +95,9 @@ class TestHttpGate:
Expected result:
Hashes must be the same.
"""
cid = create_container(self.wallet, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL)
cid = create_container(
self.wallet, shell=client_shell, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL
)
file_path_simple, file_path_large = generate_file(), generate_file(COMPLEX_OBJ_SIZE)
with allure.step("Put objects using HTTP"):
@ -96,7 +105,7 @@ class TestHttpGate:
oid_large = upload_via_http_gate(cid=cid, path=file_path_large)
for oid, file_path in ((oid_simple, file_path_simple), (oid_large, file_path_large)):
self.get_object_and_verify_hashes(oid, file_path, self.wallet, cid)
self.get_object_and_verify_hashes(oid, file_path, self.wallet, cid, shell=client_shell)
@allure.link(
"https://github.com/nspcc-dev/neofs-http-gw#by-attributes", name="download by attributes"
@ -111,7 +120,7 @@ class TestHttpGate:
],
ids=["simple", "hyphen", "percent"],
)
def test_put_http_get_http_with_headers(self, attributes: dict):
def test_put_http_get_http_with_headers(self, client_shell, attributes: dict):
"""
Test that object can be downloaded using different attributes in HTTP header.
@ -124,7 +133,9 @@ class TestHttpGate:
Expected result:
Hashes must be the same.
"""
cid = create_container(self.wallet, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL)
cid = create_container(
self.wallet, shell=client_shell, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL
)
file_path = generate_file()
with allure.step("Put objects using HTTP with attribute"):
@ -136,8 +147,10 @@ class TestHttpGate:
self.get_object_by_attr_and_verify_hashes(oid, file_path, cid, attributes)
@allure.title("Test Expiration-Epoch in HTTP header")
def test_expiration_epoch_in_http(self):
cid = create_container(self.wallet, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL)
def test_expiration_epoch_in_http(self, client_shell):
cid = create_container(
self.wallet, shell=client_shell, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL
)
file_path = generate_file()
oids = []
@ -172,8 +185,10 @@ class TestHttpGate:
get_via_http_gate(cid=cid, oid=oid)
@allure.title("Test Zip in HTTP header")
def test_zip_in_http(self):
cid = create_container(self.wallet, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL)
def test_zip_in_http(self, client_shell):
cid = create_container(
self.wallet, shell=client_shell, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL
)
file_path_simple, file_path_large = generate_file(), generate_file(COMPLEX_OBJ_SIZE)
common_prefix = "my_files"
@ -194,11 +209,13 @@ class TestHttpGate:
@pytest.mark.curl
@pytest.mark.long
@allure.title("Test Put over HTTP/Curl, Get over HTTP/Curl for large object")
def test_put_http_get_http_large_file(self):
def test_put_http_get_http_large_file(self, client_shell):
"""
This test checks upload and download using curl with 'large' object. Large is object with size up to 20Mb.
"""
cid = create_container(self.wallet, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL)
cid = create_container(
self.wallet, shell=client_shell, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL
)
obj_size = int(os.getenv("BIG_OBJ_SIZE", COMPLEX_OBJ_SIZE))
file_path = generate_file(obj_size)
@ -207,16 +224,18 @@ class TestHttpGate:
oid_gate = upload_via_http_gate(cid=cid, path=file_path)
oid_curl = upload_via_http_gate_curl(cid=cid, filepath=file_path, large_object=True)
self.get_object_and_verify_hashes(oid_gate, file_path, self.wallet, cid)
self.get_object_and_verify_hashes(oid_curl, file_path, self.wallet, cid, get_via_http_curl)
self.get_object_and_verify_hashes(oid_gate, file_path, self.wallet, cid, shell=client_shell)
self.get_object_and_verify_hashes(oid_curl, file_path, self.wallet, cid, shell=client_shell, object_getter=get_via_http_curl)
@pytest.mark.curl
@allure.title("Test Put/Get over HTTP using Curl utility")
def test_put_http_get_http_curl(self):
def test_put_http_get_http_curl(self, client_shell):
"""
Test checks upload and download over HTTP using curl utility.
"""
cid = create_container(self.wallet, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL)
cid = create_container(
self.wallet, shell=client_shell, rule=self.PLACEMENT_RULE, basic_acl=PUBLIC_ACL
)
file_path_simple, file_path_large = generate_file(), generate_file(COMPLEX_OBJ_SIZE)
with allure.step("Put objects using curl utility"):
@ -224,7 +243,7 @@ class TestHttpGate:
oid_large = upload_via_http_gate_curl(cid=cid, filepath=file_path_large)
for oid, file_path in ((oid_simple, file_path_simple), (oid_large, file_path_large)):
self.get_object_and_verify_hashes(oid, file_path, self.wallet, cid, get_via_http_curl)
self.get_object_and_verify_hashes(oid, file_path, self.wallet, cid, shell=client_shell, object_getter=get_via_http_curl)
@staticmethod
@allure.step("Try to get object and expect error")
@ -251,13 +270,15 @@ class TestHttpGate:
@staticmethod
@allure.step("Verify object can be get using HTTP")
def get_object_and_verify_hashes(
oid: str, file_name: str, wallet: str, cid: str, object_getter=None
oid: str, file_name: str, wallet: str, cid: str, shell: Shell, object_getter=None
) -> None:
nodes = get_nodes_without_object(wallet=wallet, cid=cid, oid=oid)
nodes = get_nodes_without_object(wallet=wallet, cid=cid, oid=oid, shell=shell)
random_node = choice(nodes)
object_getter = object_getter or get_via_http_gate
got_file_path = get_object(wallet=wallet, cid=cid, oid=oid, endpoint=random_node)
got_file_path = get_object(
wallet=wallet, cid=cid, oid=oid, shell=shell, endpoint=random_node
)
got_file_path_http = object_getter(cid=cid, oid=oid)
TestHttpGate._assert_hashes_are_equal(file_name, got_file_path, got_file_path_http)

View file

@ -2,6 +2,7 @@ import random
import allure
import pytest
from neofs_testlib.shell import Shell
from common import COMPLEX_OBJ_SIZE, NEOFS_NETMAP_DICT, SIMPLE_OBJ_SIZE
from file_helper import generate_file
from grpc_responses import SESSION_NOT_FOUND
@ -25,7 +26,7 @@ from python_keywords.session_token import create_session_token
[SIMPLE_OBJ_SIZE, COMPLEX_OBJ_SIZE],
ids=["simple object", "complex object"],
)
def test_object_session_token(prepare_wallet_and_deposit, object_size):
def test_object_session_token(prepare_wallet_and_deposit, client_shell: Shell, object_size):
"""
Test how operations over objects are executed with a session token
@ -61,18 +62,19 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
f'AS LOC_{locode}_PLACE FILTER "UN-LOCODE" '
f'EQ "{un_locode}" AS LOC_{locode}'
)
cid = create_container(wallet, rule=placement_policy)
cid = create_container(wallet, shell=client_shell, rule=placement_policy)
with allure.step("Put Objects"):
file_path = generate_file(object_size)
oid = put_object(wallet=wallet, path=file_path, cid=cid)
oid_delete = put_object(wallet=wallet, path=file_path, cid=cid)
oid = put_object(wallet=wallet, path=file_path, cid=cid, shell=client_shell)
oid_delete = put_object(wallet=wallet, path=file_path, cid=cid, shell=client_shell)
with allure.step("Node not in container but granted a session token"):
put_object(
wallet=wallet,
path=file_path,
cid=cid,
shell=client_shell,
endpoint=session_token_node,
session=session_token,
)
@ -80,12 +82,14 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
wallet=wallet,
cid=cid,
oid=oid,
shell=client_shell,
endpoint=session_token_node,
session=session_token,
)
search_object(
wallet=wallet,
cid=cid,
shell=client_shell,
endpoint=session_token_node,
expected_objects_list=[oid],
session=session_token,
@ -94,6 +98,7 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
wallet=wallet,
cid=cid,
oid=oid,
shell=client_shell,
endpoint=session_token_node,
session=session_token,
)
@ -101,6 +106,7 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
wallet=wallet,
cid=cid,
oid=oid,
shell=client_shell,
range_cut="0:256",
endpoint=session_token_node,
session=session_token,
@ -109,6 +115,7 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
wallet=wallet,
cid=cid,
oid=oid_delete,
shell=client_shell,
endpoint=session_token_node,
session=session_token,
)
@ -119,6 +126,7 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
wallet=wallet,
path=file_path,
cid=cid,
shell=client_shell,
endpoint=container_node,
session=session_token,
)
@ -126,12 +134,14 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
wallet=wallet,
cid=cid,
oid=oid,
shell=client_shell,
endpoint=container_node,
session=session_token,
)
search_object(
wallet=wallet,
cid=cid,
shell=client_shell,
endpoint=container_node,
expected_objects_list=[oid],
session=session_token,
@ -140,6 +150,7 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
wallet=wallet,
cid=cid,
oid=oid,
shell=client_shell,
endpoint=container_node,
session=session_token,
)
@ -148,6 +159,7 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
cid=cid,
oid=oid,
range_cut="0:256",
shell=client_shell,
endpoint=container_node,
session=session_token,
)
@ -156,6 +168,7 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
wallet=wallet,
cid=cid,
oid=oid,
shell=client_shell,
endpoint=container_node,
session=session_token,
)
@ -166,6 +179,7 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
wallet=wallet,
path=file_path,
cid=cid,
shell=client_shell,
endpoint=noncontainer_node,
session=session_token,
)
@ -173,12 +187,14 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
wallet=wallet,
cid=cid,
oid=oid,
shell=client_shell,
endpoint=noncontainer_node,
session=session_token,
)
search_object(
wallet=wallet,
cid=cid,
shell=client_shell,
endpoint=noncontainer_node,
expected_objects_list=[oid],
session=session_token,
@ -187,6 +203,7 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
wallet=wallet,
cid=cid,
oid=oid,
shell=client_shell,
endpoint=noncontainer_node,
session=session_token,
)
@ -195,6 +212,7 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
cid=cid,
oid=oid,
range_cut="0:256",
shell=client_shell,
endpoint=noncontainer_node,
session=session_token,
)
@ -203,6 +221,7 @@ def test_object_session_token(prepare_wallet_and_deposit, object_size):
wallet=wallet,
cid=cid,
oid=oid,
shell=client_shell,
endpoint=noncontainer_node,
session=session_token,
)

View file

@ -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]

View file

@ -1,2 +0,0 @@
from .adm import CompletionType, NeofsAdm
from .cli import NeofsCli

View file

@ -1,2 +0,0 @@
from .adm import NeofsAdm
from .completion_type import CompletionType

View file

@ -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
)

View 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)

View file

@ -1,8 +0,0 @@
from enum import Enum
class CompletionType(Enum):
BASH = "bash"
ZHS = "zsh"
FISH = "fish"
POWERSHELL = "powershell"

View file

@ -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"]},
)

View file

@ -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"]
},
)

View 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"]},
)

View file

@ -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"]},
)

View file

@ -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"]},
)

View file

@ -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)

View file

@ -1 +0,0 @@
from .cli import NeofsCli

View file

@ -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"]},
)

View file

@ -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"]},
)

View file

@ -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)

View file

@ -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"]},
)

View file

@ -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"]},
)

View file

@ -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"]},
)

View file

@ -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)

View file

@ -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)

View file

@ -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"]

View file

@ -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:

View file

@ -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,
)

View file

@ -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)

View file

@ -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():

View file

@ -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"

View file

@ -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

View file

@ -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:

View file

@ -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="")