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