Skip to content

Commit c03c2c4

Browse files
authored
Add account parameter in job_info method (#196)
1 parent 0c55f67 commit c03c2c4

File tree

8 files changed

+258
-11
lines changed

8 files changed

+258
-11
lines changed

firecrest/cli2/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -854,11 +854,19 @@ def job_info(
854854
jobid: Optional[str] = typer.Argument(
855855
None, help="Job ID to display."
856856
),
857+
allusers: bool = typer.Option(
858+
False, "--all-users", help="Retrieve information about all users' jobs."
859+
),
860+
account: Optional[str] = typer.Option(
861+
None,
862+
help=("An account to filter the jobs by. It will only be taken into "
863+
"account when you are not specifying a jobid."),
864+
),
857865
):
858866
"""Retrieve information about submitted jobs.
859867
"""
860868
try:
861-
result = asyncio.run(client.job_info(system, jobid))
869+
result = asyncio.run(client.job_info(system, jobid, allusers, account))
862870
json_out(result)
863871
except Exception as e:
864872
examine_exeption(e)

firecrest/v2/_async/Client.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1646,7 +1646,8 @@ async def job_info(
16461646
self,
16471647
system_name: str,
16481648
jobid: Optional[str] = None,
1649-
allusers: bool = False
1649+
allusers: bool = False,
1650+
account: Optional[str] = None
16501651
) -> list:
16511652
"""Get job information. When the job is not specified, it will return
16521653
all the jobs.
@@ -1655,6 +1656,8 @@ async def job_info(
16551656
:param jobid: the ID of the job
16561657
:param allusers: whether to return jobs of all users or only the
16571658
current user
1659+
:param account: an account to filter the jobs by. It will only be taken
1660+
into account when you are not specifying a jobid.
16581661
:calls: GET `/compute/{system_name}/jobs` or
16591662
GET `/compute/{system_name}/jobs/{job}`
16601663
"""
@@ -1667,9 +1670,15 @@ async def job_info(
16671670
"version <2.2.7 of the API."
16681671
)
16691672

1673+
if self._api_version < parse("2.4.2") and account:
1674+
raise NotImplementedOnAPIversion(
1675+
"The `account` parameter is not available for "
1676+
"version <2.4.2 of the API."
1677+
)
1678+
16701679
resp = await self._get_request(
16711680
endpoint=url,
1672-
params={"allusers": allusers}
1681+
params={"allusers": allusers, "account": account}
16731682
)
16741683
result_jobs = self._check_response(resp, 200)["jobs"]
16751684
return result_jobs if result_jobs is not None else []

firecrest/v2/_sync/Client.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1645,7 +1645,8 @@ def job_info(
16451645
self,
16461646
system_name: str,
16471647
jobid: Optional[str] = None,
1648-
allusers: bool = False
1648+
allusers: bool = False,
1649+
account: Optional[str] = None
16491650
) -> list:
16501651
"""Get job information. When the job is not specified, it will return
16511652
all the jobs.
@@ -1654,6 +1655,8 @@ def job_info(
16541655
:param jobid: the ID of the job
16551656
:param allusers: whether to return jobs of all users or only the
16561657
current user
1658+
:param account: an account to filter the jobs by. It will only be taken
1659+
into account when you are not specifying a jobid.
16571660
:calls: GET `/compute/{system_name}/jobs` or
16581661
GET `/compute/{system_name}/jobs/{job}`
16591662
"""
@@ -1666,9 +1669,15 @@ def job_info(
16661669
"version <2.2.7 of the API."
16671670
)
16681671

1672+
if self._api_version < parse("2.4.2") and account:
1673+
raise NotImplementedOnAPIversion(
1674+
"The `account` parameter is not available for "
1675+
"version <2.4.2 of the API."
1676+
)
1677+
16691678
resp = self._get_request(
16701679
endpoint=url,
1671-
params={"allusers": allusers}
1680+
params={"allusers": allusers, "account": account}
16721681
)
16731682
result_jobs = self._check_response(resp, 200)["jobs"]
16741683
return result_jobs if result_jobs is not None else []

tests/v2/handlers.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def fc_server(httpserver):
4747
for endpoint in ["jobs", "metadata"]:
4848
httpserver.expect_request(
4949
re.compile(rf"/compute/.*/{endpoint}"), method="GET"
50-
).respond_with_handler(filesystem_handler)
50+
).respond_with_handler(get_jobs_handler)
5151

5252
httpserver.expect_request(
5353
re.compile(r"/compute/.*/jobs"), method="POST"
@@ -268,3 +268,45 @@ def submit_handler(request: Request):
268268
return Response(json.dumps(ret),
269269
status=ret_status,
270270
content_type="application/json")
271+
272+
273+
def get_jobs_handler(request: Request):
274+
if request.headers["Authorization"] != "Bearer VALID_TOKEN":
275+
return Response(
276+
json.dumps({"message": "Bad token; invalid JSON"}),
277+
status=401,
278+
content_type="application/json",
279+
)
280+
281+
url, *params = request.url.split("?")
282+
283+
endpoint = url.split("/")[-1]
284+
285+
suffix = ""
286+
287+
if endpoint == "jobs":
288+
endpoint = "job"
289+
290+
if "account=users2" in "&".join(params):
291+
suffix = "_info_account"
292+
elif "allusers=true" in "&".join(params):
293+
suffix = "_info_all_users"
294+
else:
295+
suffix = "_info"
296+
297+
if endpoint == "1":
298+
endpoint = "job"
299+
suffix = "_info"
300+
301+
if endpoint == "metadata":
302+
endpoint = "job"
303+
suffix = "_metadata"
304+
305+
data = read_json_file(f"v2/responses/{endpoint}{suffix}.json")
306+
307+
ret = data["response"]
308+
ret_status = data["status_code"]
309+
310+
return Response(json.dumps(ret),
311+
status=ret_status,
312+
content_type="application/json")
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"status_code": 200,
3+
"response": {
4+
"jobs": [
5+
{
6+
"account": "test",
7+
"allocationNodes": 1,
8+
"cluster": "cluster",
9+
"exitCode": {
10+
"status": [
11+
"ERROR"
12+
],
13+
"returnCode": 8
14+
},
15+
"group": "users2",
16+
"jobId": 3,
17+
"name": "allocation",
18+
"nodes": "localhost",
19+
"partition": "part01",
20+
"priority": 1,
21+
"killRequestUser": "",
22+
"state": {
23+
"current": [
24+
"FAILED"
25+
],
26+
"reason": "None"
27+
},
28+
"time": {
29+
"elapsed": 0,
30+
"start": 1730968675,
31+
"end": 1730968675,
32+
"suspended": 0,
33+
"limit": 7200,
34+
"submission": 1730968674
35+
},
36+
"user": "test3",
37+
"workingDirectory": "/home/test3"
38+
}
39+
]
40+
}
41+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
{
2+
"status_code": 200,
3+
"response": {
4+
"jobs": [
5+
{
6+
"account": "test",
7+
"allocationNodes": 1,
8+
"cluster": "cluster",
9+
"exitCode": {
10+
"status": [
11+
"ERROR"
12+
],
13+
"returnCode": 8
14+
},
15+
"group": "users",
16+
"jobId": 1,
17+
"name": "allocation",
18+
"nodes": "localhost",
19+
"partition": "part01",
20+
"priority": 1,
21+
"killRequestUser": "",
22+
"state": {
23+
"current": [
24+
"FAILED"
25+
],
26+
"reason": "None"
27+
},
28+
"time": {
29+
"elapsed": 0,
30+
"start": 1730968675,
31+
"end": 1730968675,
32+
"suspended": 0,
33+
"limit": 7200,
34+
"submission": 1730968674
35+
},
36+
"user": "test1",
37+
"workingDirectory": "/home/test1"
38+
},
39+
{
40+
"account": "test",
41+
"allocationNodes": 2,
42+
"cluster": "cluster",
43+
"exitCode": {
44+
"status": [
45+
"ERROR"
46+
],
47+
"returnCode": 8
48+
},
49+
"group": "users",
50+
"jobId": 2,
51+
"name": "allocation",
52+
"nodes": "localhost",
53+
"partition": "part01",
54+
"priority": 1,
55+
"killRequestUser": "",
56+
"state": {
57+
"current": [
58+
"FAILED"
59+
],
60+
"reason": "None"
61+
},
62+
"time": {
63+
"elapsed": 0,
64+
"start": 1730968675,
65+
"end": 1730968675,
66+
"suspended": 0,
67+
"limit": 7200,
68+
"submission": 1730968674
69+
},
70+
"user": "test2",
71+
"workingDirectory": "/home/test2"
72+
},
73+
{
74+
"account": "test",
75+
"allocationNodes": 1,
76+
"cluster": "cluster",
77+
"exitCode": {
78+
"status": [
79+
"ERROR"
80+
],
81+
"returnCode": 8
82+
},
83+
"group": "users2",
84+
"jobId": 3,
85+
"name": "allocation",
86+
"nodes": "localhost",
87+
"partition": "part01",
88+
"priority": 1,
89+
"killRequestUser": "",
90+
"state": {
91+
"current": [
92+
"FAILED"
93+
],
94+
"reason": "None"
95+
},
96+
"time": {
97+
"elapsed": 0,
98+
"start": 1730968675,
99+
"end": 1730968675,
100+
"suspended": 0,
101+
"limit": 7200,
102+
"submission": 1730968674
103+
},
104+
"user": "test3",
105+
"workingDirectory": "/home/test3"
106+
}
107+
]
108+
}
109+
}

tests/v2/test_v2_async.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import json
22
import pytest
3-
import re
43

54
from context_v2 import AsyncFirecrest, UnexpectedStatusException
65
from werkzeug.wrappers import Response
@@ -18,10 +17,12 @@ class ValidAuthorization:
1817
def get_access_token(self):
1918
return "VALID_TOKEN"
2019

21-
return AsyncFirecrest(
20+
client = AsyncFirecrest(
2221
firecrest_url=fc_server.url_for("/"),
2322
authorization=ValidAuthorization()
2423
)
24+
client.set_api_version("2.99.0") # Set to a high enough version for all tests
25+
return client
2526

2627

2728
@pytest.fixture
@@ -317,6 +318,20 @@ async def test_job_info_jobid(valid_client):
317318
assert resp == data["response"]["jobs"]
318319

319320

321+
@pytest.mark.asyncio
322+
async def test_job_allusers(valid_client):
323+
data = read_json_file("v2/responses/job_info_all_users.json")
324+
resp = await valid_client.job_info("cluster", allusers=True)
325+
assert resp == data["response"]["jobs"]
326+
327+
328+
@pytest.mark.asyncio
329+
async def test_job_info_account(valid_client):
330+
data = read_json_file("v2/responses/job_info_account.json")
331+
resp = await valid_client.job_info("cluster", account="users2")
332+
assert resp == data["response"]["jobs"]
333+
334+
320335
@pytest.mark.asyncio
321336
async def test_job_metadata(valid_client):
322337
data = read_json_file("v2/responses/job_metadata.json")

tests/v2/test_v2_sync.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import json
22
import pytest
3-
import re
43

54
from context_v2 import Firecrest, UnexpectedStatusException
65
from werkzeug.wrappers import Response
@@ -18,11 +17,12 @@ class ValidAuthorization:
1817
def get_access_token(self):
1918
return "VALID_TOKEN"
2019

21-
return Firecrest(
20+
client = Firecrest(
2221
firecrest_url=fc_server.url_for("/"),
2322
authorization=ValidAuthorization()
2423
)
25-
24+
client.set_api_version("2.99.0") # Set to a high enough version for all tests
25+
return client
2626

2727
@pytest.fixture
2828
def invalid_client(fc_server):
@@ -293,6 +293,20 @@ def test_job_info_jobid(valid_client):
293293
assert resp == data["response"]["jobs"]
294294

295295

296+
def test_job_allusers(valid_client):
297+
data = read_json_file("v2/responses/job_info_all_users.json")
298+
resp = valid_client.job_info("cluster", allusers=True)
299+
300+
assert resp == data["response"]["jobs"]
301+
302+
303+
def test_job_info_account(valid_client):
304+
data = read_json_file("v2/responses/job_info_account.json")
305+
resp = valid_client.job_info("cluster", account="users2")
306+
307+
assert resp == data["response"]["jobs"]
308+
309+
296310
def test_job_metadata(valid_client):
297311
data = read_json_file("v2/responses/job_metadata.json")
298312
resp = valid_client.job_metadata("cluster", "1")

0 commit comments

Comments
 (0)