Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new methods for the /status/filesystems endpoint #88

Merged
merged 3 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions firecrest/AsyncClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -589,6 +589,26 @@ async def parameters(self) -> t.Parameters:
resp = await self._get_request(endpoint="/status/parameters")
return self._json_response([resp], 200)["out"]

async def filesystems(self, system_name: Optional[str] = None) -> dict[str, List[t.Filesystem]]:
"""Returns the status of the filesystems per system.

:param system_name: the system name
:calls: GET `/status/filesystems`
:calls: GET `/status/filesystems/{system_name}`

.. warning:: This is available only for FirecREST>=1.15.0
"""
if system_name:
resp = await self._get_request(endpoint=f"/status/filesystems/{system_name}")
# Return the result in the same structure
result = {
system_name: self._json_response([resp], 200)["out"]
}
return result
else:
resp = await self._get_request(endpoint="/status/filesystems")
return self._json_response([resp], 200)["out"]

# Utilities
async def list_files(
self, machine: str, target_path: str, show_hidden: bool = False
Expand Down
20 changes: 20 additions & 0 deletions firecrest/BasicClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,26 @@ def parameters(self) -> t.Parameters:
resp = self._get_request(endpoint="/status/parameters")
return self._json_response([resp], 200)["out"]

def filesystems(self, system_name: Optional[str] = None) -> dict[str, List[t.Filesystem]]:
"""Returns the status of the filesystems per system.

:param system_name: the system name
:calls: GET `/status/filesystems`
:calls: GET `/status/filesystems/{system_name}`

.. warning:: This is available only for FirecREST>=1.15.0
"""
if system_name:
resp = self._get_request(endpoint=f"/status/filesystems/{system_name}")
# Return the result in the same structure
result = {
system_name: self._json_response([resp], 200)["out"]
}
return result
else:
resp = self._get_request(endpoint="/status/filesystems")
return self._json_response([resp], 200)["out"]

# Utilities
def list_files(
self, machine: str, target_path: str, show_hidden: bool = False
Expand Down
29 changes: 29 additions & 0 deletions firecrest/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,35 @@ def parameters():
raise typer.Exit(code=1)


@app.command(rich_help_panel="Status commands")
def filesystems(
system: Optional[str] = typer.Option(
None,
"-s",
"--system",
help="The name of the system where the filesystems belongs to.",
envvar="FIRECREST_SYSTEM",
),
):
"""Information about the filesystems that are available through FirecREST"""
try:
result = client.filesystems(system)
for system in result.keys():
table = create_table(
f"Status of filesystems for `{system}`",
result[system],
("Name", "name"),
("Path", "path"),
("Status code", "status_code"),
("Status", "status"),
("Description", "description"),
)
console.print(table, overflow="fold")
except Exception as e:
examine_exeption(e)
raise typer.Exit(code=1)


@app.command(rich_help_panel="Status commands")
def tasks(
taskids: Optional[List[str]] = typer.Argument(
Expand Down
10 changes: 10 additions & 0 deletions firecrest/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ class System(TypedDict):
system: str


class Filesystem(TypedDict):
"""A filesystem record, from `status/filesystems`"""

name: str
path: str
description: str
status: int
status_code: str


class Task(TypedDict):
"""A task record, from `/tasks`"""

Expand Down
114 changes: 114 additions & 0 deletions tests/test_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
import pytest
import re
import os
import test_authorisation as auth

from context import firecrest
Expand Down Expand Up @@ -60,6 +61,10 @@ def fc_server(httpserver):
parameters_handler
)

httpserver.expect_request(
re.compile("^/status/filesystems.*"), method="GET"
).respond_with_handler(filesystems_handler)

return httpserver


Expand Down Expand Up @@ -195,6 +200,49 @@ def parameters_handler(request: Request):
return Response(json.dumps(ret), status=200, content_type="application/json")


def filesystems_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 = {
"description": "Filesystem information",
"out": {
"cluster": [
{
"description": "Users home filesystem",
"name": "HOME",
"path": "/home",
"status": "available",
"status_code": 200
},
{
"description": "Scratch filesystem",
"name": "SCRATCH",
"path": "/scratch",
"status": "not available",
"status_code": 400
}
]
}
}
ret_status = 200
uri = request.url
if not uri.endswith("/status/filesystems"):
system = uri.split("/")[-1]
if system == "cluster":
ret["description"] = f"Filesystem information for system {system}"
ret["out"] = ret["out"][system]
else:
ret = {"description": f"System '{system}' does not exists."}
ret_status = 400

return Response(json.dumps(ret), status=ret_status, content_type="application/json")


def test_all_services(valid_client):
assert valid_client.all_services() == [
{
Expand Down Expand Up @@ -334,3 +382,69 @@ def test_cli_parameters(valid_credentials):
def test_parameters_invalid(invalid_client):
with pytest.raises(firecrest.UnauthorizedException):
invalid_client.parameters()


def test_filesystems(valid_client):
assert valid_client.filesystems() == {
"cluster": [
{
"description": "Users home filesystem",
"name": "HOME",
"path": "/home",
"status": "available",
"status_code": 200
},
{
"description": "Scratch filesystem",
"name": "SCRATCH",
"path": "/scratch",
"status": "not available",
"status_code": 400
}
]
}

assert valid_client.filesystems(system_name="cluster") == {
"cluster": [
{
"description": "Users home filesystem",
"name": "HOME",
"path": "/home",
"status": "available",
"status_code": 200
},
{
"description": "Scratch filesystem",
"name": "SCRATCH",
"path": "/scratch",
"status": "not available",
"status_code": 400
}
]
}


def test_cli_filesystems(valid_credentials):
# Clean up the env var that may be set in the environment
os.environ.pop("FIRECREST_SYSTEM", None)
args = valid_credentials + ["filesystems"]
result = runner.invoke(cli.app, args=args)
stdout = common.clean_stdout(result.stdout)
assert result.exit_code == 0
assert "Status of filesystems for `cluster`" in stdout

args = valid_credentials + ["filesystems", "--system", "cluster"]
result = runner.invoke(cli.app, args=args)
stdout = common.clean_stdout(result.stdout)
assert result.exit_code == 0
assert "Status of filesystems for `cluster`" in stdout

args = valid_credentials + ["filesystems", "--system", "invalid_cluster"]
result = runner.invoke(cli.app, args=args)
stdout = common.clean_stdout(result.stdout)
assert result.exit_code == 1


def test_filesystems_invalid(invalid_client):
with pytest.raises(firecrest.UnauthorizedException):
invalid_client.filesystems()
51 changes: 51 additions & 0 deletions tests/test_status_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ def fc_server(httpserver):
basic_status.parameters_handler
)

httpserver.expect_request(
re.compile("^/status/filesystems.*"), method="GET"
).respond_with_handler(basic_status.filesystems_handler)

return httpserver


Expand Down Expand Up @@ -167,3 +171,50 @@ async def test_parameters(valid_client):
async def test_parameters_invalid(invalid_client):
with pytest.raises(firecrest.UnauthorizedException):
await invalid_client.parameters()


@pytest.mark.asyncio
async def test_filesystems(valid_client):
assert await valid_client.filesystems() == {
"cluster": [
{
"description": "Users home filesystem",
"name": "HOME",
"path": "/home",
"status": "available",
"status_code": 200
},
{
"description": "Scratch filesystem",
"name": "SCRATCH",
"path": "/scratch",
"status": "not available",
"status_code": 400
}
]
}

assert await valid_client.filesystems(system_name="cluster") == {
"cluster": [
{
"description": "Users home filesystem",
"name": "HOME",
"path": "/home",
"status": "available",
"status_code": 200
},
{
"description": "Scratch filesystem",
"name": "SCRATCH",
"path": "/scratch",
"status": "not available",
"status_code": 400
}
]
}


@pytest.mark.asyncio
async def test_filesystems_invalid(invalid_client):
with pytest.raises(firecrest.UnauthorizedException):
await invalid_client.filesystems()
Loading