Added create container grpc method
Signed-off-by: Ilyas Niyazov <i.niyazov@yadro.com>
This commit is contained in:
parent
9a1b5d778b
commit
f8465e5b99
34 changed files with 532 additions and 53 deletions
|
@ -1,23 +0,0 @@
|
||||||
# Create channel and Stubs
|
|
||||||
import grpc
|
|
||||||
from frostfs_api.client.services.container import ContainerClient
|
|
||||||
|
|
||||||
|
|
||||||
class FrostfsClient:
|
|
||||||
def __init__(self, address):
|
|
||||||
self.channel = grpc.insecure_channel(address)
|
|
||||||
self.container = ContainerClient(self.channel)
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.channel.close()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
client = FrostfsClient("localhost:50051")
|
|
||||||
create_response = client.container.create_container("my_container")
|
|
||||||
print("Container created:", create_response)
|
|
||||||
|
|
||||||
get_response = client.container.get_container(create_response)
|
|
||||||
print("Container details:", get_response)
|
|
||||||
|
|
||||||
client.close()
|
|
|
@ -1,20 +0,0 @@
|
||||||
# implementation Conainer methods
|
|
||||||
import grpc
|
|
||||||
import protos.models.container.service_pb2_grpc as service_pb2_grpc_container
|
|
||||||
import protos.models.container.service_pb2 as service_pb2_container
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ContainerClient:
|
|
||||||
def __init__(self, channel):
|
|
||||||
self.container_stub = service_pb2_grpc_container.ContainerServiceStub(channel)
|
|
||||||
|
|
||||||
def create_container(self, container_name) -> bytes:
|
|
||||||
request = service_pb2_container.PutRequest(name=container_name)
|
|
||||||
response: service_pb2_container.PutResponse = self.container_stub.Put(request)
|
|
||||||
return response.body.container_id
|
|
||||||
|
|
||||||
def get_container(self, container_id) -> service_pb2_container.GetResponse:
|
|
||||||
request = service_pb2_container.GetRequest(id=container_id)
|
|
||||||
response: service_pb2_container.GetResponse = self.container_stub.Get(request)
|
|
||||||
return response
|
|
30
frostfs_sdk/client/frostfs_client.py
Normal file
30
frostfs_sdk/client/frostfs_client.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Create channel and Stubs
|
||||||
|
import grpc
|
||||||
|
from frostfs_sdk.client.models.client_environment import ClientEnvironment
|
||||||
|
from frostfs_sdk.client.models.client_settings import ClientSettings
|
||||||
|
from frostfs_sdk.client.models.ecdsa import ECDSA
|
||||||
|
from frostfs_sdk.client.services.container import ContainerClient
|
||||||
|
|
||||||
|
|
||||||
|
class FrostfsClient:
|
||||||
|
def __init__(self, client_settings: ClientSettings):
|
||||||
|
self.channel = grpc.insecure_channel(f"{client_settings.host}:{client_settings.port}")
|
||||||
|
self.ecdsa: ECDSA = ECDSA(wif=client_settings.wif)
|
||||||
|
|
||||||
|
client_environment = ClientEnvironment(self.ecdsa, self.channel)
|
||||||
|
self.container = ContainerClient(client_environment)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
self.channel.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
import frostfs_sdk
|
||||||
|
|
||||||
|
WIF = "L5XNVUzPnma6m4mPrWEN6CcTscJERcfX3yvb1cdffdxe1iriAshU"
|
||||||
|
address = "10.78.128.25:8080"
|
||||||
|
client = frostfs_sdk.FrostfsClient(ClientSettings(WIF, address))
|
||||||
|
params = frostfs_sdk.models.PrmsCreateContainer(name="1234")
|
||||||
|
client.container.create(params)
|
||||||
|
"""
|
8
frostfs_sdk/client/models/client_environment.py
Normal file
8
frostfs_sdk/client/models/client_environment.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import grpc
|
||||||
|
from frostfs_sdk.client.models.ecdsa import ECDSA
|
||||||
|
|
||||||
|
|
||||||
|
class ClientEnvironment:
|
||||||
|
def __init__(self, ecdsa: ECDSA, channel: grpc.Channel):
|
||||||
|
self.ecdsa = ecdsa
|
||||||
|
self.channel = channel
|
19
frostfs_sdk/client/models/client_settings.py
Normal file
19
frostfs_sdk/client/models/client_settings.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
class ClientSettings:
|
||||||
|
def __init__(self, wif: str = None, address: str = None):
|
||||||
|
"""
|
||||||
|
Initializes client settings with validation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
wif: Wallet import format string
|
||||||
|
address: FrostFS node host address
|
||||||
|
"""
|
||||||
|
self.wif = wif
|
||||||
|
self.address = address
|
||||||
|
|
||||||
|
# Perform validation after initialization
|
||||||
|
self.validate()
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
"""Performs runtime validation of the settings"""
|
||||||
|
if not (self.address and self.wif):
|
||||||
|
raise ValueError("The value must be specified ADDRESS and WIF")
|
8
frostfs_sdk/client/models/ecdsa.py
Normal file
8
frostfs_sdk/client/models/ecdsa.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from frostfs_sdk.cryptography.key_extension import KeyExtension
|
||||||
|
|
||||||
|
|
||||||
|
class ECDSA:
|
||||||
|
def __init__(self, wif: str):
|
||||||
|
self.wif = wif
|
||||||
|
self.private_key: bytes = KeyExtension().get_private_key_from_wif(wif)
|
||||||
|
self.public_key: bytes = KeyExtension().get_public_key(self.private_key)
|
18
frostfs_sdk/client/parameters/container_create.py
Normal file
18
frostfs_sdk/client/parameters/container_create.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Optional, Dict
|
||||||
|
|
||||||
|
from frostfs_sdk.models.dto.container import Container
|
||||||
|
from frostfs_sdk.models.dto.session_token import SessionToken
|
||||||
|
from frostfs_sdk.client.parameters.wait import PrmWait
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class PrmContainerCreate:
|
||||||
|
container: Container
|
||||||
|
wait_params: Optional[PrmWait] = None
|
||||||
|
session_token: Optional[SessionToken] = None
|
||||||
|
x_headers: Dict[str, str] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
if self.wait_params is None:
|
||||||
|
object.__setattr__(self, 'wait_params', PrmWait())
|
21
frostfs_sdk/client/parameters/wait.py
Normal file
21
frostfs_sdk/client/parameters/wait.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class PrmWait:
|
||||||
|
DEFAULT_TIMEOUT: timedelta = field(default=timedelta(seconds=120), init=False)
|
||||||
|
DEFAULT_POLL_INTERVAL: timedelta = field(default=timedelta(seconds=5), init=False)
|
||||||
|
|
||||||
|
timeout: timedelta = DEFAULT_TIMEOUT
|
||||||
|
poll_interval: timedelta = DEFAULT_POLL_INTERVAL
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
if self.timeout is None:
|
||||||
|
object.__setattr__(self, 'timeout', self.DEFAULT_TIMEOUT)
|
||||||
|
if self.poll_interval is None:
|
||||||
|
object.__setattr__(self, 'poll_interval', self.DEFAULT_POLL_INTERVAL)
|
||||||
|
|
||||||
|
def get_deadline(self) -> datetime:
|
||||||
|
return datetime.now() + self.timeout
|
32
frostfs_sdk/client/services/container.py
Normal file
32
frostfs_sdk/client/services/container.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# implementation Conainer methods
|
||||||
|
from frostfs_sdk.client.models.client_environment import ClientEnvironment
|
||||||
|
from frostfs_sdk.cryptography.signer import Signer
|
||||||
|
from frostfs_sdk.models.dto.container import ContainerId
|
||||||
|
import protos.models.container.service_pb2_grpc as service_pb2_grpc_container
|
||||||
|
import protos.models.container.service_pb2 as service_pb2_container
|
||||||
|
|
||||||
|
from frostfs_sdk.client.parameters.container_create import PrmContainerCreate
|
||||||
|
from frostfs_sdk.models.mappers.container_mapper import ContainerMapper
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerClient:
|
||||||
|
def __init__(self, client_environment: ClientEnvironment):
|
||||||
|
self.container_stub = service_pb2_grpc_container.ContainerServiceStub(client_environment.channel)
|
||||||
|
self.ecdsa = client_environment.ecdsa
|
||||||
|
|
||||||
|
def create(self, prm_container_create: PrmContainerCreate) -> ContainerId:
|
||||||
|
request = self.create_put_request(prm_container_create)
|
||||||
|
response: service_pb2_container.PutResponse = self.container_stub.Put(request)
|
||||||
|
return ContainerId(value=response.body.container_id)
|
||||||
|
|
||||||
|
def create_put_request(self, prm: PrmContainerCreate):
|
||||||
|
grpc_container=ContainerMapper().to_grpc_message(prm.container)
|
||||||
|
body = service_pb2_container.PutRequest.Body(
|
||||||
|
container=grpc_container,
|
||||||
|
signature=Signer.sign_rfc6979(self.ecdsa.private_key, grpc_container)
|
||||||
|
)
|
||||||
|
|
||||||
|
request = service_pb2_container.PutRequest(body=body)
|
||||||
|
signed_request = Signer.sign(self.ecdsa.private_key, request)
|
||||||
|
return signed_request
|
|
@ -3,7 +3,8 @@ from hashlib import sha256, sha512
|
||||||
|
|
||||||
|
|
||||||
class Signer:
|
class Signer:
|
||||||
def sign_rfc6979(self, private_key: bytes, message: bytes) -> bytes:
|
@staticmethod
|
||||||
|
def sign_rfc6979(private_key: bytes, message: bytes) -> bytes:
|
||||||
if len(private_key) == 0 or private_key is None:
|
if len(private_key) == 0 or private_key is None:
|
||||||
raise ValueError(f"Incorrect private_key: {private_key}")
|
raise ValueError(f"Incorrect private_key: {private_key}")
|
||||||
|
|
||||||
|
@ -13,7 +14,8 @@ class Signer:
|
||||||
|
|
||||||
return signature
|
return signature
|
||||||
|
|
||||||
def sign(self, private_key: bytes, message: bytes) -> bytes:
|
@staticmethod
|
||||||
|
def sign(private_key: bytes, message: bytes) -> bytes:
|
||||||
if len(private_key) == 0 or private_key is None:
|
if len(private_key) == 0 or private_key is None:
|
||||||
raise ValueError(f"Incorrect private key: {private_key}")
|
raise ValueError(f"Incorrect private key: {private_key}")
|
||||||
|
|
25
frostfs_sdk/models/dto/container.py
Normal file
25
frostfs_sdk/models/dto/container.py
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Dict, Optional
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from frostfs_sdk.models.enums.basic_acl import BasicAcl
|
||||||
|
from frostfs_sdk.models.dto.placement_policy import PlacementPolicy
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Container:
|
||||||
|
basicAcl: BasicAcl
|
||||||
|
placementPolicy: PlacementPolicy
|
||||||
|
nonce: uuid.UUID = field(default_factory=uuid.uuid4)
|
||||||
|
version: Optional[str] = None
|
||||||
|
attributes: Dict[str, str] = field(default_factory=dict)
|
||||||
|
|
||||||
|
def __init__(self, basicAcl: BasicAcl, placementPolicy: PlacementPolicy):
|
||||||
|
self.basicAcl = basicAcl
|
||||||
|
self.placementPolicy = placementPolicy
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ContainerId:
|
||||||
|
value: str
|
14
frostfs_sdk/models/dto/filter.py
Normal file
14
frostfs_sdk/models/dto/filter.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from frostfs_sdk.models.enums.filter_operation import FilterOperation
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Filter:
|
||||||
|
"""
|
||||||
|
Data Transfer Object for Filter configuration
|
||||||
|
"""
|
||||||
|
name: str
|
||||||
|
key: str
|
||||||
|
operation: FilterOperation
|
||||||
|
value: str
|
9
frostfs_sdk/models/dto/placement_policy.py
Normal file
9
frostfs_sdk/models/dto/placement_policy.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from frostfs_sdk.models.dto.replica import Replica
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PlacementPolicy:
|
||||||
|
replicas: List[Replica]
|
||||||
|
unique: bool
|
12
frostfs_sdk/models/dto/replica.py
Normal file
12
frostfs_sdk/models/dto/replica.py
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
|
||||||
|
|
||||||
|
EMPTY_STRING = ""
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Replica:
|
||||||
|
count: int
|
||||||
|
selector: str = field(default=EMPTY_STRING)
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
self.selector = self.selector if self.selector else EMPTY_STRING
|
15
frostfs_sdk/models/dto/selector.py
Normal file
15
frostfs_sdk/models/dto/selector.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from frostfs_sdk.models.enums.selector_clause import SelectorClause
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Selector:
|
||||||
|
"""
|
||||||
|
Data Transfer Object for Selector configuration
|
||||||
|
"""
|
||||||
|
name: str
|
||||||
|
count: int
|
||||||
|
clause: SelectorClause
|
||||||
|
attribute: str
|
||||||
|
filter: str
|
5
frostfs_sdk/models/dto/session_token.py
Normal file
5
frostfs_sdk/models/dto/session_token.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class SessionToken:
|
||||||
|
token: bytes
|
7
frostfs_sdk/models/enums/basic_acl.py
Normal file
7
frostfs_sdk/models/enums/basic_acl.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class BasicAcl(Enum):
|
||||||
|
PRIVATE = 0x1C8C8CCC
|
||||||
|
PUBLIC_RO = 0x1FBF8CFF
|
||||||
|
PUBLIC_RW = 0x1FBFBFFF
|
||||||
|
PUBLIC_APPEND = 0x1FBF9FFF
|
42
frostfs_sdk/models/enums/filter_operation.py
Normal file
42
frostfs_sdk/models/enums/filter_operation.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
class FilterOperation:
|
||||||
|
"""
|
||||||
|
Enum for filter operations with integer value mapping
|
||||||
|
"""
|
||||||
|
OPERATION_UNSPECIFIED = 0
|
||||||
|
EQ = 1
|
||||||
|
NE = 2
|
||||||
|
GT = 3
|
||||||
|
GE = 4
|
||||||
|
LT = 5
|
||||||
|
LE = 6
|
||||||
|
OR = 7
|
||||||
|
AND = 8
|
||||||
|
NOT = 9
|
||||||
|
LIKE = 10
|
||||||
|
|
||||||
|
_value_map = {
|
||||||
|
0: OPERATION_UNSPECIFIED,
|
||||||
|
1: EQ,
|
||||||
|
2: NE,
|
||||||
|
3: GT,
|
||||||
|
4: GE,
|
||||||
|
5: LT,
|
||||||
|
6: LE,
|
||||||
|
7: OR,
|
||||||
|
8: AND,
|
||||||
|
9: NOT,
|
||||||
|
10: LIKE
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, value: int) -> 'FilterOperation':
|
||||||
|
"""
|
||||||
|
Get enum instance by integer value
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: Integer value of the operation
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Corresponding FilterOperation instance
|
||||||
|
"""
|
||||||
|
return cls._value_map.get(value)
|
26
frostfs_sdk/models/enums/selector_clause.py
Normal file
26
frostfs_sdk/models/enums/selector_clause.py
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
class SelectorClause:
|
||||||
|
"""
|
||||||
|
Enum for selector clauses with integer value mapping
|
||||||
|
"""
|
||||||
|
CLAUSE_UNSPECIFIED = 0
|
||||||
|
SAME = 1
|
||||||
|
DISTINCT = 2
|
||||||
|
|
||||||
|
_value_map = {
|
||||||
|
0: CLAUSE_UNSPECIFIED,
|
||||||
|
1: SAME,
|
||||||
|
2: DISTINCT
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, value: int) -> 'SelectorClause':
|
||||||
|
"""
|
||||||
|
Get enum instance by integer value
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: Integer value of the clause
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Corresponding SelectorClause instance
|
||||||
|
"""
|
||||||
|
return cls._value_map.get(value)
|
65
frostfs_sdk/models/mappers/container_mapper.py
Normal file
65
frostfs_sdk/models/mappers/container_mapper.py
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
from typing import ByteString, Optional
|
||||||
|
|
||||||
|
from frostfs_sdk.models.mappers.placement_policy_mapper import PlacementPolicyMapper
|
||||||
|
import protos.models.container.types_pb2 as types_pb2_container
|
||||||
|
from frostfs_sdk.models.dto.container import Container
|
||||||
|
|
||||||
|
|
||||||
|
class ContainerMapper:
|
||||||
|
@staticmethod
|
||||||
|
def to_grpc_message(container: Container) -> Optional[types_pb2_container.Container]:
|
||||||
|
"""
|
||||||
|
Converts Container DTO to gRPC message
|
||||||
|
|
||||||
|
Args:
|
||||||
|
container: Container DTO object
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
gRPC Container message builder
|
||||||
|
"""
|
||||||
|
if not container:
|
||||||
|
return None
|
||||||
|
|
||||||
|
attributes = [
|
||||||
|
types_pb2_container.Container.Attribute(key=k, value=v)
|
||||||
|
for k, v in container.attributes.items()
|
||||||
|
]
|
||||||
|
|
||||||
|
grpc_container = types_pb2_container.Container(
|
||||||
|
# nonce=ByteString.copy(container.nonce),
|
||||||
|
placement_policy=PlacementPolicyMapper.to_grpc_message(container.placementPolicy),
|
||||||
|
attributes=attributes
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
# if container.owner_id:
|
||||||
|
# grpc_container.owner_id = OwnerIdMapper.to_grpc_message(container.owner_id)
|
||||||
|
|
||||||
|
# if container.version:
|
||||||
|
# grpc_container.version = VersionMapper.to_grpc_message(container.version)
|
||||||
|
|
||||||
|
return grpc_container
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_model(container_grpc: types_pb2_container.Container) -> Optional[Container]:
|
||||||
|
"""
|
||||||
|
Converts gRPC message to Container DTO
|
||||||
|
|
||||||
|
Args:
|
||||||
|
container_grpc: gRPC Container message
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Container DTO object
|
||||||
|
"""
|
||||||
|
if not container_grpc or container_grpc.ByteSize() == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
attributes = {attr.key: attr.value for attr in container_grpc.attributes}
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
# nonce=UuidUtils.as_uuid(container_grpc.nonce.to_bytes()),
|
||||||
|
placement_policy=PlacementPolicyMapper.to_model(container_grpc.placement_policy),
|
||||||
|
# version=VersionMapper.to_model(container_grpc.version),
|
||||||
|
# owner_id=OwnerIdMapper.to_model(container_grpc.owner_id),
|
||||||
|
attributes=attributes
|
||||||
|
)
|
60
frostfs_sdk/models/mappers/filter_mapper.py
Normal file
60
frostfs_sdk/models/mappers/filter_mapper.py
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from frostfs_sdk.models.enums.filter_operation import FilterOperation
|
||||||
|
from frostfs_sdk.models.dto.filter import Filter
|
||||||
|
import protos.models.netmap.types_pb2 as types_pb2_netmap
|
||||||
|
|
||||||
|
|
||||||
|
class FilterMapper:
|
||||||
|
@staticmethod
|
||||||
|
def to_grpc_messages(filters: List[Filter]) -> List[types_pb2_netmap.Filter]:
|
||||||
|
"""
|
||||||
|
Converts list of Filter DTOs to gRPC messages with nested conversion
|
||||||
|
"""
|
||||||
|
if not filters:
|
||||||
|
return []
|
||||||
|
|
||||||
|
return [FilterMapper.to_grpc_message(f) for f in filters]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_grpc_message(filter_dto: Filter) -> types_pb2_netmap.Filter:
|
||||||
|
"""
|
||||||
|
Converts Filter DTO to gRPC message with nested filters
|
||||||
|
"""
|
||||||
|
|
||||||
|
operation = types_pb2_netmap.Filter.Operation.Value(filter_dto.operation.value)
|
||||||
|
return types_pb2_netmap.Filter(
|
||||||
|
name=filter_dto.name,
|
||||||
|
key=filter_dto.key,
|
||||||
|
op=operation,
|
||||||
|
value=filter_dto.value,
|
||||||
|
filters=FilterMapper.to_grpc_messages(filter_dto.filters)
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_models(filters_grpc: List[types_pb2_netmap.Filter]) -> Optional[List[Filter]]:
|
||||||
|
"""
|
||||||
|
Converts gRPC messages to Filter DTOs with nested conversion
|
||||||
|
"""
|
||||||
|
if not filters_grpc:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return [FilterMapper.to_model(f) for f in filters_grpc]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_model(filter_grpc: types_pb2_netmap.Filter) -> Optional[Filter]:
|
||||||
|
"""
|
||||||
|
Converts gRPC message to Filter DTO with nested filters
|
||||||
|
"""
|
||||||
|
if not filter_grpc or filter_grpc.ByteSize() == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
operation = FilterOperation(filter_grpc.op)
|
||||||
|
|
||||||
|
return Filter(
|
||||||
|
name=filter_grpc.name,
|
||||||
|
key=filter_grpc.key,
|
||||||
|
operation=operation,
|
||||||
|
value=filter_grpc.value,
|
||||||
|
filters=FilterMapper.to_models(filter_grpc.filters)
|
||||||
|
)
|
0
frostfs_sdk/models/mappers/owner_id_mapper.py
Normal file
0
frostfs_sdk/models/mappers/owner_id_mapper.py
Normal file
50
frostfs_sdk/models/mappers/placement_policy_mapper.py
Normal file
50
frostfs_sdk/models/mappers/placement_policy_mapper.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
from typing import Optional
|
||||||
|
from frostfs_sdk.models.mappers.filter_mapper import FilterMapper
|
||||||
|
import protos.models.netmap.types_pb2 as types_pb2_netmap
|
||||||
|
from frostfs_sdk.models.dto.placement_policy import PlacementPolicy
|
||||||
|
|
||||||
|
|
||||||
|
class PlacementPolicyMapper:
|
||||||
|
@staticmethod
|
||||||
|
def to_grpc_message(policy: PlacementPolicy) -> Optional[types_pb2_netmap.PlacementPolicy]:
|
||||||
|
"""
|
||||||
|
Converts PlacementPolicy DTO to gRPC message
|
||||||
|
|
||||||
|
Args:
|
||||||
|
policy: PlacementPolicy DTO object
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
gRPC PlacementPolicy message
|
||||||
|
"""
|
||||||
|
if not policy:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return types_pb2_netmap.PlacementPolicy(
|
||||||
|
unique=policy.unique,
|
||||||
|
container_backup_factor=policy.backup_factor,
|
||||||
|
filters=FilterMapper.to_grpc_messages(policy.filters),
|
||||||
|
# selectors=SelectorMapper.to_grpc_messages(policy.selectors),
|
||||||
|
# replicas=ReplicaMapper.to_grpc_messages(policy.replicas)
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def to_model(policy_grpc: types_pb2_netmap.PlacementPolicy) -> Optional[PlacementPolicy]:
|
||||||
|
"""
|
||||||
|
Converts gRPC message to PlacementPolicy DTO
|
||||||
|
|
||||||
|
Args:
|
||||||
|
policy_grpc: gRPC PlacementPolicy message
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
PlacementPolicy DTO object
|
||||||
|
"""
|
||||||
|
if not policy_grpc or policy_grpc.ByteSize() == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return PlacementPolicy(
|
||||||
|
# replicas=ReplicaMapper.to_models(policy_grpc.replicas),
|
||||||
|
unique=policy_grpc.unique,
|
||||||
|
backup_factor=policy_grpc.container_backup_factor,
|
||||||
|
filters=FilterMapper.to_models(policy_grpc.filters),
|
||||||
|
# selectors=SelectorMapper.to_models(policy_grpc.selectors)
|
||||||
|
)
|
0
frostfs_sdk/models/mappers/replica_mapper.py
Normal file
0
frostfs_sdk/models/mappers/replica_mapper.py
Normal file
0
frostfs_sdk/models/mappers/selector_mapper.py
Normal file
0
frostfs_sdk/models/mappers/selector_mapper.py
Normal file
|
@ -13,23 +13,19 @@ REPO_FROSTFS_API_PROTOS="https://git.frostfs.info/TrueCloudLab/frostfs-api.git"
|
||||||
|
|
||||||
rm -rf "$PROTOS_DIR"
|
rm -rf "$PROTOS_DIR"
|
||||||
|
|
||||||
# Step 1: Create folder ./protos
|
|
||||||
echo "1. Create folder ./protos"
|
echo "1. Create folder ./protos"
|
||||||
mkdir -p "$PROTOS_DIR"
|
mkdir -p "$PROTOS_DIR"
|
||||||
|
|
||||||
# Step 2: Cloning repository into ./protos/source
|
|
||||||
echo "2. Cloning repository into ./protos/source..."
|
echo "2. Cloning repository into ./protos/source..."
|
||||||
mkdir -p "$SOURCE_DIR"
|
mkdir -p "$SOURCE_DIR"
|
||||||
git clone "$REPO_FROSTFS_API_PROTOS" "$SOURCE_DIR"
|
git clone "$REPO_FROSTFS_API_PROTOS" "$SOURCE_DIR"
|
||||||
|
|
||||||
# Step 3: Generating Python code from .proto files
|
|
||||||
echo "3. Generating Python code from .proto files to $MODELS_DIR"
|
echo "3. Generating Python code from .proto files to $MODELS_DIR"
|
||||||
mkdir -p "$MODELS_DIR"
|
mkdir -p "$MODELS_DIR"
|
||||||
find "$SOURCE_DIR" -name "*.proto" | while read -r proto_file; do
|
find "$SOURCE_DIR" -name "*.proto" | while read -r proto_file; do
|
||||||
python -m grpc_tools.protoc -I "$SOURCE_DIR" --python_out="$MODELS_DIR" --pyi_out="$MODELS_DIR" --grpc_python_out="$MODELS_DIR" "$proto_file"
|
python -m grpc_tools.protoc -I "$SOURCE_DIR" --python_out="$MODELS_DIR" --pyi_out="$MODELS_DIR" --grpc_python_out="$MODELS_DIR" "$proto_file"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Step 4: Making ./protos/models a Python package
|
|
||||||
echo "4. Making ./protos/models a Python package..."
|
echo "4. Making ./protos/models a Python package..."
|
||||||
touch "$MODELS_DIR/__init__.py"
|
touch "$MODELS_DIR/__init__.py"
|
||||||
|
|
||||||
|
|
14
tests/client/test_create_container.py
Normal file
14
tests/client/test_create_container.py
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# import pytest
|
||||||
|
|
||||||
|
# from frostfs_sdk.client.frostfs_client import FrostfsClient
|
||||||
|
# from frostfs_sdk.client.parameters.container_create import PrmContainerCreate
|
||||||
|
# from frostfs_sdk.models.dto.container import ContainerId
|
||||||
|
# from tests.helpers.models import Helpers
|
||||||
|
|
||||||
|
|
||||||
|
# @pytest.mark.container
|
||||||
|
# class TestContainer:
|
||||||
|
# def test_create_container(self, default_frostfs_client: FrostfsClient, helpers: Helpers):
|
||||||
|
# req_body: PrmContainerCreate = helpers.container.create_params_container_create()
|
||||||
|
# cid: ContainerId = default_frostfs_client.container.create(req_body)
|
||||||
|
# print(cid.value)
|
|
@ -1,8 +1,24 @@
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests.helpers.models import ClientCryptograpy
|
# from frostfs_sdk.client.frostfs_client import FrostfsClient
|
||||||
|
# from frostfs_sdk.client.models.client_settings import ClientSettings
|
||||||
|
from tests.helpers.models import ClientCryptograpy, Helpers
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def client_cryptography():
|
def client_cryptography():
|
||||||
return ClientCryptograpy()
|
return ClientCryptograpy()
|
||||||
|
|
||||||
|
|
||||||
|
# @pytest.fixture(scope="session")
|
||||||
|
# def default_frostfs_client():
|
||||||
|
# client_settings = ClientSettings(
|
||||||
|
# wif="",
|
||||||
|
# address="10.78.128.25:8080"
|
||||||
|
# )
|
||||||
|
# return FrostfsClient(client_settings)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def helpers():
|
||||||
|
return Helpers()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from frostfs_api.cryptography.key_extension import KeyExtension
|
from frostfs_sdk.cryptography.key_extension import KeyExtension
|
||||||
from frostfs_api.cryptography.verifier import Verifier
|
from frostfs_sdk.cryptography.verifier import Verifier
|
||||||
from frostfs_api.cryptography.signer import Signer
|
from frostfs_sdk.cryptography.signer import Signer
|
||||||
|
from tests.helpers.params_container import ParamsContainerHelper
|
||||||
|
|
||||||
|
|
||||||
class ClientCryptograpy:
|
class ClientCryptograpy:
|
||||||
|
@ -8,3 +9,8 @@ class ClientCryptograpy:
|
||||||
self.key_extension = KeyExtension()
|
self.key_extension = KeyExtension()
|
||||||
self.signer = Signer()
|
self.signer = Signer()
|
||||||
self.verifier = Verifier()
|
self.verifier = Verifier()
|
||||||
|
|
||||||
|
|
||||||
|
class Helpers:
|
||||||
|
def __init__(self):
|
||||||
|
self.container = ParamsContainerHelper()
|
||||||
|
|
22
tests/helpers/params_container.py
Normal file
22
tests/helpers/params_container.py
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
from frostfs_sdk.client.parameters.container_create import PrmContainerCreate
|
||||||
|
from frostfs_sdk.client.parameters.wait import PrmWait
|
||||||
|
from frostfs_sdk.models.dto.container import Container
|
||||||
|
from frostfs_sdk.models.dto.placement_policy import PlacementPolicy
|
||||||
|
from frostfs_sdk.models.dto.replica import Replica
|
||||||
|
from frostfs_sdk.models.dto.session_token import SessionToken
|
||||||
|
from frostfs_sdk.models.enums.basic_acl import BasicAcl
|
||||||
|
|
||||||
|
|
||||||
|
class ParamsContainerHelper:
|
||||||
|
def create_params_container_create(self):
|
||||||
|
req_container_create = PrmContainerCreate(
|
||||||
|
container=Container(
|
||||||
|
basicAcl=BasicAcl.PUBLIC_RW,
|
||||||
|
placementPolicy=PlacementPolicy(
|
||||||
|
replicas=[Replica(count=1)],
|
||||||
|
unique=True
|
||||||
|
)
|
||||||
|
),
|
||||||
|
wait_params=PrmWait()
|
||||||
|
)
|
||||||
|
return req_container_create
|
Loading…
Add table
Add a link
Reference in a new issue