diff --git a/firecrest/AsyncClient.py b/firecrest/AsyncClient.py index fea058b..5785993 100644 --- a/firecrest/AsyncClient.py +++ b/firecrest/AsyncClient.py @@ -1306,6 +1306,33 @@ async def partitions( result = await t.poll_task("200") return result + async def reservations( + self, + machine: str, + reservations: Optional[Sequence[str]] = None, + ) -> List[t.ReservationInfo]: + """Retrieves information about the reservations. + This call uses the `scontrol show reservations` command. + :param machine: the machine name where the scheduler belongs to + :param reservations: specific reservations to query + :calls: GET `/compute/reservations` + GET `/tasks/{taskid}` + .. warning:: This is available only for FirecREST>=1.16.0 + """ + params = {} + if reservations: + params["reservations"] = ",".join(reservations) + + resp = await self._get_request( + endpoint="/compute/reservations", + additional_headers={"X-Machine-Name": machine}, + params=params, + ) + json_response = self._json_response([resp], 200) + t = ComputeTask(self, json_response["task_id"], [resp]) + result = await t.poll_task("200") + return result + async def cancel(self, machine: str, job_id: str | int) -> str: """Cancels running job. This call uses the `scancel` command. diff --git a/firecrest/BasicClient.py b/firecrest/BasicClient.py index 448d32c..34a7096 100644 --- a/firecrest/BasicClient.py +++ b/firecrest/BasicClient.py @@ -1146,6 +1146,35 @@ def partitions( ) return result + def reservations( + self, + machine: str, + reservations: Optional[Sequence[str]] = None, + ) -> List[t.ReservationInfo]: + """Retrieves information about the compute reservations. + This call uses the `scontrol show reservations` command. + :param machine: the machine name where the scheduler belongs to + :param nodes: specific reservations to query + :calls: GET `/compute/reservations` + GET `/tasks/{taskid}` + .. warning:: This is available only for FirecREST>=1.16.0 + """ + params = {} + if reservations: + params["reservations"] = ",".join(reservations) + + resp = self._get_request( + endpoint="/compute/reservations", + additional_headers={"X-Machine-Name": machine}, + params=params, + ) + self._current_method_requests.append(resp) + json_response = self._json_response(self._current_method_requests, 200) + result = self._poll_tasks( + json_response["task_id"], "200", iter([1, 0.5, 0.25]) + ) + return result + def cancel(self, machine: str, job_id: str | int) -> str: """Cancels running job. This call uses the `scancel` command. diff --git a/firecrest/cli/__init__.py b/firecrest/cli/__init__.py index 060a255..e26ff49 100644 --- a/firecrest/cli/__init__.py +++ b/firecrest/cli/__init__.py @@ -1409,63 +1409,8 @@ def cancel( raise typer.Exit(code=1) -@reservation_app.command(name='list', rich_help_panel="Reservation commands") -def list_command( - config_from_parent: str = typer.Option(None, - callback=config_parent_load_callback, - is_eager=True, - hidden=True - ), - system: str = typer.Option( - ..., "-s", "--system", help="The name of the system.", envvar="FIRECREST_SYSTEM" - ), -): - """List all active reservations and their status""" - try: - res = client.all_reservations(system) - console.print(res) - except Exception as e: - examine_exeption(e) - raise typer.Exit(code=1) - - -@reservation_app.command(rich_help_panel="Reservation commands") -def create( - config_from_parent: str = typer.Option(None, - callback=config_parent_load_callback, - is_eager=True, - hidden=True - ), - system: str = typer.Option( - ..., "-s", "--system", help="The name of the system.", envvar="FIRECREST_SYSTEM" - ), - name: str = typer.Argument(..., help="The reservation name."), - account: str = typer.Argument( - ..., help="The account in SLURM to which the reservation is made for." - ), - num_nodes: str = typer.Argument( - ..., help="The number of nodes needed for the reservation." - ), - node_type: str = typer.Argument(..., help="The node type."), - start_time: str = typer.Argument( - ..., help="The start time for reservation (YYYY-MM-DDTHH:MM:SS)." - ), - end_time: str = typer.Argument( - ..., help="The end time for reservation (YYYY-MM-DDTHH:MM:SS)." - ), -): - """Create a reservation""" - try: - client.create_reservation( - system, name, account, num_nodes, node_type, start_time, end_time - ) - except Exception as e: - examine_exeption(e) - raise typer.Exit(code=1) - - -@reservation_app.command(rich_help_panel="Reservation commands") -def update( +@app.command(rich_help_panel="Compute commands") +def get_reservations( config_from_parent: str = typer.Option(None, callback=config_parent_load_callback, is_eager=True, @@ -1474,46 +1419,41 @@ def update( system: str = typer.Option( ..., "-s", "--system", help="The name of the system.", envvar="FIRECREST_SYSTEM" ), - name: str = typer.Argument(..., help="The reservation name."), - account: str = typer.Argument( - ..., help="The account in SLURM to which the reservation is made for." - ), - num_nodes: str = typer.Argument( - ..., help="The number of nodes needed for the reservation." - ), - node_type: str = typer.Argument(..., help="The node type."), - start_time: str = typer.Argument( - ..., help="The start time for reservation (YYYY-MM-DDTHH:MM:SS)." - ), - end_time: str = typer.Argument( - ..., help="The end time for reservation (YYYY-MM-DDTHH:MM:SS)." + reservations: Optional[List[str]] = typer.Argument( + None, help="List of specific reservations to query." ), + raw: bool = typer.Option(False, "--raw", help="Print unformatted."), ): - """Update a reservation""" + """Retrieves information about the reservations. + This call uses the `scontrol show reservations` command + """ try: - client.update_reservation( - system, name, account, num_nodes, node_type, start_time, end_time - ) - except Exception as e: - examine_exeption(e) - raise typer.Exit(code=1) + results = client.reservations(system, reservations) + if raw: + console.print(results) + else: + parsed_results = [] + for item in results: + parsed_item = {} + for key, value in item.items(): + if isinstance(value, list): + parsed_item[key] = ", ".join(value) + else: + parsed_item[key] = str(value) + parsed_results.append(parsed_item) -@reservation_app.command(rich_help_panel="Reservation commands") -def delete( - config_from_parent: str = typer.Option(None, - callback=config_parent_load_callback, - is_eager=True, - hidden=True - ), - system: str = typer.Option( - ..., "-s", "--system", help="The name of the system.", envvar="FIRECREST_SYSTEM" - ), - name: str = typer.Argument(..., help="The reservation name."), -): - """Delete a reservation""" - try: - client.delete_reservation(system, name) + table = create_table( + "Information about reservations in the system", + parsed_results, + ("Name", "ReservationName"), + ("State", "State"), + ("Nodes", "Nodes"), + ("StartTime", "StartTime"), + ("EndTime", "EndTime"), + ("Features", "Features"), + ) + console.print(table) except Exception as e: examine_exeption(e) raise typer.Exit(code=1) diff --git a/firecrest/types.py b/firecrest/types.py index 637b99a..ac9e3fa 100644 --- a/firecrest/types.py +++ b/firecrest/types.py @@ -158,6 +158,16 @@ class PartitionInfo(TypedDict): TotalNodes: str +class ReservationInfo(TypedDict): + """A job queue record, from `compute/reservations`""" + + ReservationName: str + State: str + Nodes: str + StartTime: str + EndTime: str + Features: str + class JobSubmit(TypedDict): """A job submit record, from `compute/jobs`""" diff --git a/tests/test_compute.py b/tests/test_compute.py index a688266..f45394b 100644 --- a/tests/test_compute.py +++ b/tests/test_compute.py @@ -124,19 +124,41 @@ def submit_path_handler(request: Request): def nodes_request_handler(request: Request): - if not request.query_string or request.query_string == b'nodes=nid001': + if not request.query_string or request.query_string == b"nodes=nid001": ret = { "success": "Task created", "task_id": "nodes_info", - "task_url": "/tasks/nodes_info" + "task_url": "/tasks/nodes_info", } status_code = 200 - if request.query_string == b'nodes=nidunknown': + if request.query_string == b"nodes=nidunknown": ret = { "success": "Task created", "task_id": "info_unknown_node", - "task_url": "/tasks/info_unknown_node" + "task_url": "/tasks/info_unknown_node", + } + status_code = 200 + + return Response( + json.dumps(ret), status=status_code, content_type="application/json" + ) + + +def reservations_request_handler(request: Request): + if not request.query_string or request.query_string == b"reservations=res01": + ret = { + "success": "Task created", + "task_id": "reservations_info", + "task_url": "/tasks/reservations_info", + } + status_code = 200 + + if request.query_string == b"reservations=invalid_res": + ret = { + "success": "Task created", + "task_id": "info_unknown_reservations", + "task_url": "/tasks/info_unknown_reservations", } status_code = 200 @@ -146,7 +168,6 @@ def nodes_request_handler(request: Request): def partitions_request_handler(request: Request): - print(request.query_string) if ( not request.query_string or request.query_string == b'partitions=part01%2Cpart02%2Cxfer' @@ -899,17 +920,13 @@ def tasks_handler(request: Request): "created_at": "2024-04-16T09:47:06", "data": [ { - "ActiveFeatures": [ - "f7t" - ], + "ActiveFeatures": ["f7t"], "NodeName": "nid001", "Partitions": [ "part01", "part02", ], - "State": [ - "IDLE" - ] + "State": ["IDLE"], } ], "description": "Finished successfully", @@ -921,7 +938,7 @@ def tasks_handler(request: Request): "task_id": "nodes_info", "task_url": "/tasks/nodes_info", "updated_at": "2024-04-16T09:47:06", - "user": "service-account-firecrest-sample" + "user": "service-account-firecrest-sample", } } } @@ -941,7 +958,7 @@ def tasks_handler(request: Request): "task_id": "info_unknown_node", "task_url": "/tasks/info_unknown_node", "updated_at": "2024-04-16T09:56:14", - "user": "service-account-firecrest-sample" + "user": "service-account-firecrest-sample", } } } @@ -1008,6 +1025,63 @@ def tasks_handler(request: Request): } } status_code = 200 + elif taskid == "reservations_info": + ret = { + "tasks": { + taskid: { + "created_at": "2024-04-24T12:02:25", + "data": [ + { + "EndTime": "2024-05-01T15:00:00", + "Features": "(null)", + "Nodes": "nid001", + "ReservationName": "res01", + "StartTime": "2024-05-01T12:00:00", + "State": "INACTIVE" + }, + { + "EndTime": "2024-06-01T15:00:00", + "Features": ["f7t1", "f7t2"], + "Nodes": "nid002", + "ReservationName": "res04", + "StartTime": "2024-06-01T12:00:00", + "State": "INACTIVE" + } + ], + "description": "Finished successfully", + "hash_id": taskid, + "last_modify": "2024-04-24T12:02:25", + "service": "compute", + "status": "200", + "system": "cluster", + "task_id": taskid, + "task_url": f"/tasks/{taskid}", + "updated_at": "2024-04-24T12:02:25", + "user": "service-account-firecrest-sample", + } + } + } + status_code = 200 + elif taskid == "info_unknown_reservations": + ret = { + "tasks": { + taskid: { + "created_at": "2024-04-24T12:17:13", + "data": [], + "description": "Finished with errors", + "hash_id": taskid, + "last_modify": "2024-04-24T12:02:25", + "service": "compute", + "status": "200", + "system": "cluster", + "task_id": taskid, + "task_url": f"/tasks/{taskid}", + "updated_at": "2024-04-24T12:17:14", + "user": "service-account-firecrest-sample", + } + } + } + status_code = 200 return Response( json.dumps(ret), status=status_code, content_type="application/json" @@ -1036,13 +1110,17 @@ def fc_server(httpserver): re.compile("^/compute/jobs.*"), method="DELETE" ).respond_with_handler(cancel_handler) - httpserver.expect_request( - "/tasks", method="GET" - ).respond_with_handler(tasks_handler) + httpserver.expect_request("/tasks", method="GET").respond_with_handler( + tasks_handler + ) + + httpserver.expect_request("/compute/nodes", method="GET").respond_with_handler( + nodes_request_handler + ) httpserver.expect_request( - "/compute/nodes", method="GET" - ).respond_with_handler(nodes_request_handler) + "/compute/reservations", method="GET" + ).respond_with_handler(reservations_request_handler) httpserver.expect_request( "/compute/partitions", method="GET" @@ -1156,7 +1234,7 @@ def test_submit_local(valid_client, slurm_script): def test_cli_submit_local(valid_credentials, slurm_script): global submit_upload_retry submit_upload_retry = 0 - args = valid_credentials + ["submit", "--system", "cluster1", str(slurm_script)] + args = valid_credentials + ["submit", "--system", "cluster1", str(slurm_script)] result = runner.invoke(cli.app, args=args) stdout = common.clean_stdout(result.stdout) assert result.exit_code == 0 @@ -1391,7 +1469,14 @@ def test_poll_active(valid_client): def test_cli_poll_active(valid_credentials): global queue_retry queue_retry = 0 - args = valid_credentials + ["poll-active", "--system", "cluster1", "352", "2", "334"] + args = valid_credentials + [ + "poll-active", + "--system", + "cluster1", + "352", + "2", + "334", + ] result = runner.invoke(cli.app, args=args) stdout = common.clean_stdout(result.stdout) assert result.exit_code == 0 @@ -1469,32 +1554,26 @@ def test_cancel_invalid_client(invalid_client): def test_get_nodes(valid_client): - response = [{ - "ActiveFeatures": ["f7t"], - "NodeName": "nid001", - "Partitions": [ - "part01", - "part02" - ], - "State": [ - "IDLE" - ] - }] + response = [ + { + "ActiveFeatures": ["f7t"], + "NodeName": "nid001", + "Partitions": ["part01", "part02"], + "State": ["IDLE"], + } + ] assert valid_client.nodes(machine="cluster1") == response def test_get_nodes_from_list(valid_client): - response = [{ - "ActiveFeatures": ["f7t"], - "NodeName": "nid001", - "Partitions": [ - "part01", - "part02" - ], - "State": [ - "IDLE" - ] - }] + response = [ + { + "ActiveFeatures": ["f7t"], + "NodeName": "nid001", + "Partitions": ["part01", "part02"], + "State": ["IDLE"], + } + ] assert valid_client.nodes(machine="cluster1", nodes=["nid001"]) == response @@ -1591,3 +1670,32 @@ def test_cli_get_partitions(valid_credentials): assert "part01" in stdout assert "part02" in stdout assert "UP" in stdout + +def test_get_reservations(valid_client): + response = [ + { + "EndTime": "2024-05-01T15:00:00", + "Features": "(null)", + "Nodes": "nid001", + "ReservationName": "res01", + "StartTime": "2024-05-01T12:00:00", + "State": "INACTIVE" + }, + { + "EndTime": "2024-06-01T15:00:00", + "Features": ["f7t1", "f7t2"], + "Nodes": "nid002", + "ReservationName": "res04", + "StartTime": "2024-06-01T12:00:00", + "State": "INACTIVE" + } + ] + assert valid_client.reservations(machine="cluster1") == response + + +def test_cli_get_reservations(valid_credentials): + args = valid_credentials + ["get-reservations", "--system", "cluster1"] + result = runner.invoke(cli.app, args=args) + stdout = common.clean_stdout(result.stdout) + assert result.exit_code == 0 + assert "Information about reservations in the system" in stdout diff --git a/tests/test_compute_async.py b/tests/test_compute_async.py index e7aaca4..ec56051 100644 --- a/tests/test_compute_async.py +++ b/tests/test_compute_async.py @@ -100,6 +100,10 @@ def fc_server(httpserver): "/compute/partitions", method="GET" ).respond_with_handler(basic_compute.partitions_request_handler) + httpserver.expect_request( + "/compute/reservations", method="GET" + ).respond_with_handler(basic_compute.reservations_request_handler) + return httpserver @@ -522,3 +526,26 @@ async def test_get_partitions_unknown(valid_client): machine="cluster1", partitions=["invalid_part"] ) + + +@pytest.mark.asyncio +async def test_get_reservations(valid_client): + response = [ + { + "EndTime": "2024-05-01T15:00:00", + "Features": "(null)", + "Nodes": "nid001", + "ReservationName": "res01", + "StartTime": "2024-05-01T12:00:00", + "State": "INACTIVE" + }, + { + "EndTime": "2024-06-01T15:00:00", + "Features": ["f7t1", "f7t2"], + "Nodes": "nid002", + "ReservationName": "res04", + "StartTime": "2024-06-01T12:00:00", + "State": "INACTIVE" + } + ] + assert await valid_client.reservations(machine="cluster1") == response diff --git a/tests/test_reservation.py b/tests/test_reservation.py deleted file mode 100644 index ffe491e..0000000 --- a/tests/test_reservation.py +++ /dev/null @@ -1,234 +0,0 @@ -import json -import pytest -import re -import test_authorisation as auth - -from context import firecrest -from firecrest import __app_name__, __version__, cli -from typer.testing import CliRunner -from werkzeug.wrappers import Response -from werkzeug.wrappers import Request - - -runner = CliRunner() - - -@pytest.fixture -def valid_client(fc_server): - class ValidAuthorization: - def get_access_token(self): - return "VALID_TOKEN" - - return firecrest.Firecrest( - firecrest_url=fc_server.url_for("/"), authorization=ValidAuthorization() - ) - - -@pytest.fixture -def valid_credentials(fc_server, auth_server): - return [ - f"--firecrest-url={fc_server.url_for('/')}", - "--client-id=valid_id", - "--client-secret=valid_secret", - f"--token-url={auth_server.url_for('/auth/token')}", - ] - - -@pytest.fixture -def invalid_client(fc_server): - class InvalidAuthorization: - def get_access_token(self): - return "INVALID_TOKEN" - - return firecrest.Firecrest( - firecrest_url=fc_server.url_for("/"), authorization=InvalidAuthorization() - ) - - -def all_reservations_handler(request: Request): - if request.headers["Authorization"] != "Bearer VALID_TOKEN": - return Response( - json.dumps({"message": "Bad token; invalid JSON"}), - status=401, - content_type="application/json", - ) - - ret = {"success": []} - return Response(json.dumps(ret), status=200, content_type="application/json") - - -def create_reservation_handler(request: Request): - if request.headers["Authorization"] != "Bearer VALID_TOKEN": - return Response( - json.dumps({"message": "Bad token; invalid JSON"}), - status=401, - content_type="application/json", - ) - - ret = {} - return Response(json.dumps(ret), status=201, content_type="application/json") - - -def update_reservation_handler(request: Request): - if request.headers["Authorization"] != "Bearer VALID_TOKEN": - return Response( - json.dumps({"message": "Bad token; invalid JSON"}), - status=401, - content_type="application/json", - ) - - ret = {} - return Response(json.dumps(ret), status=200, content_type="application/json") - - -def delete_reservation_handler(request: Request): - if request.headers["Authorization"] != "Bearer VALID_TOKEN": - return Response( - json.dumps({"message": "Bad token; invalid JSON"}), - status=401, - content_type="application/json", - ) - - ret = {} - return Response(json.dumps(ret), status=204, content_type="application/json") - - -@pytest.fixture -def fc_server(httpserver): - httpserver.expect_request("/reservations", method="GET").respond_with_handler( - all_reservations_handler - ) - - httpserver.expect_request("/reservations", method="POST").respond_with_handler( - create_reservation_handler - ) - - httpserver.expect_request( - re.compile("^/reservations/.*"), method="PUT" - ).respond_with_handler(update_reservation_handler) - - httpserver.expect_request( - re.compile("^/reservations/.*"), method="DELETE" - ).respond_with_handler(delete_reservation_handler) - - return httpserver - - -@pytest.fixture -def auth_server(httpserver): - httpserver.expect_request("/auth/token").respond_with_handler(auth.auth_handler) - return httpserver - - -def test_all_reservations(valid_client): - assert valid_client.all_reservations("cluster1") == [] - - -def test_cli_all_reservations(valid_credentials): - args = valid_credentials + ["reservation", "list", "--system", "cluster1"] - result = runner.invoke(cli.app, args=args) - assert result.exit_code == 0 - - -def test_all_reservations_invalid(invalid_client): - with pytest.raises(firecrest.UnauthorizedException): - invalid_client.all_reservations("cluster1") - - -def test_create_reservation(valid_client): - valid_client.create_reservation( - "cluster1", - "reservation", - "account", - "number_of_nodes", - "node_type", - "start_time", - "end_time", - ) - - -def test_cli_create_reservation(valid_credentials): - args = valid_credentials + [ - "reservation", - "create", - "--system", - "cluster1", - "reservation", - "account", - "number_of_nodes", - "node_type", - "start_time", - "end_time", - ] - result = runner.invoke(cli.app, args=args) - assert result.exit_code == 0 - - -def test_create_reservation_invalid(invalid_client): - with pytest.raises(firecrest.UnauthorizedException): - invalid_client.create_reservation( - "cluster1", - "reservation", - "account", - "number_of_nodes", - "node_type", - "start_time", - "end_time", - ) - - -def test_update_reservation(valid_client): - valid_client.update_reservation( - "cluster1", - "reservation", - "account", - "number_of_nodes", - "node_type", - "start_time", - "end_time", - ) - - -def test_cli_update_reservation(valid_credentials): - args = valid_credentials + [ - "reservation", - "update", - "--system", - "cluster1", - "reservation", - "account", - "number_of_nodes", - "node_type", - "start_time", - "end_time", - ] - result = runner.invoke(cli.app, args=args) - assert result.exit_code == 0 - - -def test_update_reservation_invalid(invalid_client): - with pytest.raises(firecrest.UnauthorizedException): - invalid_client.update_reservation( - "cluster1", - "reservation", - "account", - "number_of_nodes", - "node_type", - "start_time", - "end_time", - ) - - -def test_delete_reservation(valid_client): - valid_client.delete_reservation("cluster1", "reservation") - - -def test_cli_delete_reservation(valid_credentials): - args = valid_credentials + ["reservation", "delete", "--system", "cluster1", "reservation"] - result = runner.invoke(cli.app, args=args) - assert result.exit_code == 0 - - -def test_delete_reservation_invalid(invalid_client): - with pytest.raises(firecrest.UnauthorizedException): - invalid_client.delete_reservation("cluster1", "reservation") diff --git a/tests/test_reservation_async.py b/tests/test_reservation_async.py deleted file mode 100644 index 205e44a..0000000 --- a/tests/test_reservation_async.py +++ /dev/null @@ -1,145 +0,0 @@ -import pytest -import re -import test_reservation as basic_reservation - -from context import firecrest -from firecrest import __app_name__, __version__ - - -@pytest.fixture -def valid_client(fc_server): - class ValidAuthorization: - def get_access_token(self): - return "VALID_TOKEN" - - client = firecrest.AsyncFirecrest( - firecrest_url=fc_server.url_for("/"), authorization=ValidAuthorization() - ) - client.time_between_calls = { - "compute": 0, - "reservations": 0, - "status": 0, - "storage": 0, - "tasks": 0, - "utilities": 0, - } - - return client - - -@pytest.fixture -def invalid_client(fc_server): - class InvalidAuthorization: - def get_access_token(self): - return "INVALID_TOKEN" - - client = firecrest.AsyncFirecrest( - firecrest_url=fc_server.url_for("/"), authorization=InvalidAuthorization() - ) - client.time_between_calls = { - "compute": 0, - "reservations": 0, - "status": 0, - "storage": 0, - "tasks": 0, - "utilities": 0, - } - - return client - - -@pytest.fixture -def fc_server(httpserver): - httpserver.expect_request("/reservations", method="GET").respond_with_handler( - basic_reservation.all_reservations_handler - ) - - httpserver.expect_request("/reservations", method="POST").respond_with_handler( - basic_reservation.create_reservation_handler - ) - - httpserver.expect_request( - re.compile("^/reservations/.*"), method="PUT" - ).respond_with_handler(basic_reservation.update_reservation_handler) - - httpserver.expect_request( - re.compile("^/reservations/.*"), method="DELETE" - ).respond_with_handler(basic_reservation.delete_reservation_handler) - - return httpserver - - -@pytest.mark.asyncio -async def test_all_reservations(valid_client): - assert await valid_client.all_reservations("cluster1") == [] - - -@pytest.mark.asyncio -async def test_all_reservations_invalid(invalid_client): - with pytest.raises(firecrest.UnauthorizedException): - await invalid_client.all_reservations("cluster1") - - -@pytest.mark.asyncio -async def test_create_reservation(valid_client): - await valid_client.create_reservation( - "cluster1", - "reservation", - "account", - "number_of_nodes", - "node_type", - "start_time", - "end_time", - ) - - -@pytest.mark.asyncio -async def test_create_reservation_invalid(invalid_client): - with pytest.raises(firecrest.UnauthorizedException): - await invalid_client.create_reservation( - "cluster1", - "reservation", - "account", - "number_of_nodes", - "node_type", - "start_time", - "end_time", - ) - - -@pytest.mark.asyncio -async def test_update_reservation(valid_client): - await valid_client.update_reservation( - "cluster1", - "reservation", - "account", - "number_of_nodes", - "node_type", - "start_time", - "end_time", - ) - - -@pytest.mark.asyncio -async def test_update_reservation_invalid(invalid_client): - with pytest.raises(firecrest.UnauthorizedException): - await invalid_client.update_reservation( - "cluster1", - "reservation", - "account", - "number_of_nodes", - "node_type", - "start_time", - "end_time", - ) - - -@pytest.mark.asyncio -async def test_delete_reservation(valid_client): - await valid_client.delete_reservation("cluster1", "reservation") - - -@pytest.mark.asyncio -async def test_delete_reservation_invalid(invalid_client): - with pytest.raises(firecrest.UnauthorizedException): - await invalid_client.delete_reservation("cluster1", "reservation")