forked from TrueCloudLab/frostfs-testlib
[#326] Automation of PATCH in GRPC
Signed-off-by: Kirill Sosnovskikh <k.sosnovskikh@yadro.com>
This commit is contained in:
parent
24e1dfef28
commit
cf2882a383
6 changed files with 216 additions and 1 deletions
|
@ -276,6 +276,53 @@ class FrostfsCliObject(CliCommand):
|
|||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def patch(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
cid: str,
|
||||
oid: str,
|
||||
range: list[str] = None,
|
||||
payload: list[str] = None,
|
||||
new_attrs: Optional[str] = None,
|
||||
replace_attrs: bool = False,
|
||||
address: Optional[str] = None,
|
||||
bearer: Optional[str] = None,
|
||||
generate_key: Optional[bool] = None,
|
||||
session: Optional[str] = None,
|
||||
timeout: Optional[str] = None,
|
||||
trace: bool = False,
|
||||
ttl: Optional[int] = None,
|
||||
wallet: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> CommandResult:
|
||||
"""
|
||||
PATCH an object.
|
||||
|
||||
Args:
|
||||
rpc_endpoint: Remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
cid: Container ID
|
||||
oid: Object ID
|
||||
range: An array of ranges in which to replace data in the format [offset1:length1, offset2:length2]
|
||||
payload: An array of file paths to be applied in each range
|
||||
new_attrs: Attributes to be changed in the format Key1=Value1,Key2=Value2
|
||||
replace_attrs: Replace all attributes completely with new ones specified in new_attrs
|
||||
address: Address of wallet account
|
||||
bearer: File with signed JSON or binary encoded bearer token
|
||||
generate_key: Generate new private key
|
||||
session: Filepath to a JSON- or binary-encoded token of the object RANGE session
|
||||
timeout: Timeout for the operation
|
||||
trace: Generate trace ID and print it
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
wallet: WIF (NEP-2) string or path to the wallet or binary key
|
||||
xhdr: Dict with request X-Headers
|
||||
Returns:
|
||||
(str): ID of patched Object
|
||||
"""
|
||||
return self._execute(
|
||||
"object patch",
|
||||
**{param: value for param, value in locals().items() if param not in ["self"]},
|
||||
)
|
||||
|
||||
def range(
|
||||
self,
|
||||
rpc_endpoint: str,
|
||||
|
|
|
@ -283,6 +283,62 @@ def put_object(
|
|||
return oid.strip()
|
||||
|
||||
|
||||
@reporter.step("Patch object {cid}/{oid} from {endpoint}")
|
||||
def patch_object(
|
||||
wallet: WalletInfo,
|
||||
cid: str,
|
||||
oid: str,
|
||||
shell: Shell,
|
||||
endpoint: str,
|
||||
ranges: list[str] = None,
|
||||
payloads: list[str] = None,
|
||||
new_attrs: Optional[str] = None,
|
||||
replace_attrs: bool = False,
|
||||
bearer: str = "",
|
||||
xhdr: Optional[dict] = None,
|
||||
session: Optional[str] = None,
|
||||
timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
|
||||
trace: bool = False,
|
||||
):
|
||||
"""
|
||||
PATCH an object.
|
||||
|
||||
Args:
|
||||
wallet: Wallet on whose behalf PATCH is done
|
||||
cid: ID of Container where we get the Object from
|
||||
oid: Object ID
|
||||
shell: Executor for cli command
|
||||
endpoint: FrostFS endpoint to send request to, appends to `--rpc-endpoint` key
|
||||
ranges: An array of ranges in which to replace data in the format [offset1:length1, offset2:length2]
|
||||
payloads: An array of file paths to be applied in each range
|
||||
new_attrs: Attributes to be changed in the format "key1=value1,key2=value2"
|
||||
replace_attrs: Replace all attributes completely with new ones specified in new_attrs
|
||||
bearer: Path to Bearer Token file, appends to `--bearer` key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
session: Path to a JSON-encoded container session token
|
||||
timeout: Timeout for the operation
|
||||
trace: Generate trace ID and print it
|
||||
Returns:
|
||||
(str): ID of patched Object
|
||||
"""
|
||||
cli = FrostfsCli(shell, FROSTFS_CLI_EXEC, wallet.config_path)
|
||||
result = cli.object.patch(
|
||||
rpc_endpoint=endpoint,
|
||||
cid=cid,
|
||||
oid=oid,
|
||||
range=ranges,
|
||||
payload=payloads,
|
||||
new_attrs=new_attrs,
|
||||
replace_attrs=replace_attrs,
|
||||
bearer=bearer,
|
||||
xhdr=xhdr,
|
||||
session=session,
|
||||
timeout=timeout,
|
||||
trace=trace,
|
||||
)
|
||||
return result.stdout.split(":")[1].strip()
|
||||
|
||||
|
||||
@reporter.step("Delete object {cid}/{oid} from {endpoint}")
|
||||
def delete_object(
|
||||
wallet: WalletInfo,
|
||||
|
|
|
@ -22,5 +22,6 @@ class ConfigAttributes:
|
|||
class PlacementRule:
|
||||
DEFAULT_PLACEMENT_RULE = "REP 2 IN X CBF 1 SELECT 4 FROM * AS X"
|
||||
SINGLE_PLACEMENT_RULE = "REP 1 IN X CBF 1 SELECT 4 FROM * AS X"
|
||||
REP_2_FOR_3_NODES_PLACEMENT_RULE = "REP 2 IN X CBF 1 SELECT 3 FROM * AS X"
|
||||
REP_1_FOR_2_NODES_PLACEMENT_RULE = "REP 1 IN X CBF 1 SELECT 2 FROM * AS X"
|
||||
DEFAULT_EC_PLACEMENT_RULE = "EC 3.1"
|
||||
EC_1_1_FOR_2_NODES_PLACEMENT_RULE = "EC 1.1 IN X CBF 1 SELECT 2 FROM * AS X"
|
||||
|
|
|
@ -13,6 +13,7 @@ FROSTFS_CONTRACT_CACHE_TIMEOUT = 30
|
|||
|
||||
class ObjectOperations(HumanReadableEnum):
|
||||
PUT = "object.put"
|
||||
PATCH = "object.patch"
|
||||
GET = "object.get"
|
||||
HEAD = "object.head"
|
||||
GET_RANGE = "object.range"
|
||||
|
|
|
@ -407,6 +407,57 @@ class ObjectOperations(interfaces.ObjectInterface):
|
|||
oid = id_str.split(":")[1]
|
||||
return oid.strip()
|
||||
|
||||
@reporter.step("Patch object")
|
||||
def patch(
|
||||
self,
|
||||
cid: str,
|
||||
oid: str,
|
||||
endpoint: str,
|
||||
ranges: list[str] = None,
|
||||
payloads: list[str] = None,
|
||||
new_attrs: Optional[str] = None,
|
||||
replace_attrs: bool = False,
|
||||
bearer: str = "",
|
||||
xhdr: Optional[dict] = None,
|
||||
session: Optional[str] = None,
|
||||
timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
|
||||
trace: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
PATCH an object.
|
||||
|
||||
Args:
|
||||
cid: ID of Container where we get the Object from
|
||||
oid: Object ID
|
||||
endpoint: FrostFS endpoint to send request to, appends to `--rpc-endpoint` key
|
||||
ranges: An array of ranges in which to replace data in the format [offset1:length1, offset2:length2]
|
||||
payloads: An array of file paths to be applied in each range
|
||||
new_attrs: Attributes to be changed in the format "key1=value1,key2=value2"
|
||||
replace_attrs: Replace all attributes completely with new ones specified in new_attrs
|
||||
bearer: Path to Bearer Token file, appends to `--bearer` key
|
||||
xhdr: Request X-Headers in form of Key=Value
|
||||
session: Path to a JSON-encoded container session token
|
||||
timeout: Timeout for the operation
|
||||
trace: Generate trace ID and print it
|
||||
Returns:
|
||||
(str): ID of patched Object
|
||||
"""
|
||||
result = self.cli.object.patch(
|
||||
rpc_endpoint=endpoint,
|
||||
cid=cid,
|
||||
oid=oid,
|
||||
range=ranges,
|
||||
payload=payloads,
|
||||
new_attrs=new_attrs,
|
||||
replace_attrs=replace_attrs,
|
||||
bearer=bearer,
|
||||
xhdr=xhdr,
|
||||
session=session,
|
||||
timeout=timeout,
|
||||
trace=trace,
|
||||
)
|
||||
return result.stdout.split(":")[1].strip()
|
||||
|
||||
@reporter.step("Put object to random node")
|
||||
def put_to_random_node(
|
||||
self,
|
||||
|
@ -622,3 +673,30 @@ class ObjectOperations(interfaces.ObjectInterface):
|
|||
]
|
||||
|
||||
return object_nodes
|
||||
|
||||
@reporter.step("Search parts of object")
|
||||
def parts(
|
||||
self,
|
||||
cid: str,
|
||||
oid: str,
|
||||
alive_node: ClusterNode,
|
||||
bearer: str = "",
|
||||
xhdr: Optional[dict] = None,
|
||||
is_direct: bool = False,
|
||||
verify_presence_all: bool = False,
|
||||
timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
|
||||
) -> list[str]:
|
||||
endpoint = alive_node.storage_node.get_rpc_endpoint()
|
||||
response = self.cli.object.nodes(
|
||||
rpc_endpoint=endpoint,
|
||||
cid=cid,
|
||||
oid=oid,
|
||||
bearer=bearer,
|
||||
ttl=1 if is_direct else None,
|
||||
json=True,
|
||||
xhdr=xhdr,
|
||||
timeout=timeout,
|
||||
verify_presence_all=verify_presence_all,
|
||||
)
|
||||
response_json = json.loads(response.stdout)
|
||||
return [data_object["object_id"] for data_object in response_json["data_objects"]]
|
||||
|
|
|
@ -198,6 +198,24 @@ class ObjectInterface(ABC):
|
|||
) -> str:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def patch(
|
||||
self,
|
||||
cid: str,
|
||||
oid: str,
|
||||
endpoint: str,
|
||||
ranges: Optional[list[str]] = None,
|
||||
payloads: Optional[list[str]] = None,
|
||||
new_attrs: Optional[str] = None,
|
||||
replace_attrs: bool = False,
|
||||
bearer: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
session: Optional[str] = None,
|
||||
timeout: Optional[str] = None,
|
||||
trace: bool = False,
|
||||
) -> str:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def put_to_random_node(
|
||||
self,
|
||||
|
@ -264,6 +282,20 @@ class ObjectInterface(ABC):
|
|||
) -> List[ClusterNode]:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def parts(
|
||||
self,
|
||||
cid: str,
|
||||
oid: str,
|
||||
alive_node: ClusterNode,
|
||||
bearer: str = "",
|
||||
xhdr: Optional[dict] = None,
|
||||
is_direct: bool = False,
|
||||
verify_presence_all: bool = False,
|
||||
timeout: Optional[str] = None,
|
||||
) -> List[str]:
|
||||
pass
|
||||
|
||||
|
||||
class ContainerInterface(ABC):
|
||||
@abstractmethod
|
||||
|
|
Loading…
Reference in a new issue