fractal-server 2.16.6__py3-none-any.whl → 2.17.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fractal_server/__init__.py +1 -1
- fractal_server/__main__.py +178 -52
- fractal_server/app/db/__init__.py +9 -11
- fractal_server/app/models/security.py +30 -22
- fractal_server/app/models/user_settings.py +5 -4
- fractal_server/app/models/v2/__init__.py +4 -0
- fractal_server/app/models/v2/profile.py +16 -0
- fractal_server/app/models/v2/project.py +5 -0
- fractal_server/app/models/v2/resource.py +130 -0
- fractal_server/app/models/v2/task_group.py +4 -0
- fractal_server/app/routes/admin/v2/__init__.py +4 -0
- fractal_server/app/routes/admin/v2/_aux_functions.py +55 -0
- fractal_server/app/routes/admin/v2/accounting.py +3 -3
- fractal_server/app/routes/admin/v2/impersonate.py +2 -2
- fractal_server/app/routes/admin/v2/job.py +51 -15
- fractal_server/app/routes/admin/v2/profile.py +100 -0
- fractal_server/app/routes/admin/v2/project.py +2 -2
- fractal_server/app/routes/admin/v2/resource.py +222 -0
- fractal_server/app/routes/admin/v2/task.py +59 -32
- fractal_server/app/routes/admin/v2/task_group.py +17 -12
- fractal_server/app/routes/admin/v2/task_group_lifecycle.py +52 -86
- fractal_server/app/routes/api/__init__.py +45 -8
- fractal_server/app/routes/api/v2/_aux_functions.py +17 -1
- fractal_server/app/routes/api/v2/_aux_functions_history.py +2 -2
- fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +3 -3
- fractal_server/app/routes/api/v2/_aux_functions_tasks.py +55 -19
- fractal_server/app/routes/api/v2/_aux_task_group_disambiguation.py +21 -17
- fractal_server/app/routes/api/v2/dataset.py +10 -19
- fractal_server/app/routes/api/v2/history.py +8 -8
- fractal_server/app/routes/api/v2/images.py +5 -5
- fractal_server/app/routes/api/v2/job.py +8 -8
- fractal_server/app/routes/api/v2/pre_submission_checks.py +3 -3
- fractal_server/app/routes/api/v2/project.py +15 -7
- fractal_server/app/routes/api/v2/status_legacy.py +2 -2
- fractal_server/app/routes/api/v2/submit.py +49 -42
- fractal_server/app/routes/api/v2/task.py +26 -8
- fractal_server/app/routes/api/v2/task_collection.py +39 -50
- fractal_server/app/routes/api/v2/task_collection_custom.py +10 -6
- fractal_server/app/routes/api/v2/task_collection_pixi.py +34 -42
- fractal_server/app/routes/api/v2/task_group.py +19 -9
- fractal_server/app/routes/api/v2/task_group_lifecycle.py +43 -86
- fractal_server/app/routes/api/v2/task_version_update.py +3 -3
- fractal_server/app/routes/api/v2/workflow.py +9 -9
- fractal_server/app/routes/api/v2/workflow_import.py +25 -13
- fractal_server/app/routes/api/v2/workflowtask.py +5 -5
- fractal_server/app/routes/auth/__init__.py +34 -5
- fractal_server/app/routes/auth/_aux_auth.py +39 -20
- fractal_server/app/routes/auth/current_user.py +56 -67
- fractal_server/app/routes/auth/group.py +29 -46
- fractal_server/app/routes/auth/oauth.py +55 -38
- fractal_server/app/routes/auth/register.py +2 -2
- fractal_server/app/routes/auth/router.py +4 -2
- fractal_server/app/routes/auth/users.py +29 -53
- fractal_server/app/routes/aux/_runner.py +2 -1
- fractal_server/app/routes/aux/validate_user_profile.py +62 -0
- fractal_server/app/schemas/__init__.py +0 -1
- fractal_server/app/schemas/user.py +43 -13
- fractal_server/app/schemas/user_group.py +2 -1
- fractal_server/app/schemas/v2/__init__.py +12 -0
- fractal_server/app/schemas/v2/profile.py +78 -0
- fractal_server/app/schemas/v2/resource.py +137 -0
- fractal_server/app/schemas/v2/task_collection.py +11 -3
- fractal_server/app/schemas/v2/task_group.py +5 -0
- fractal_server/app/security/__init__.py +174 -75
- fractal_server/app/security/signup_email.py +52 -34
- fractal_server/config/__init__.py +27 -0
- fractal_server/config/_data.py +68 -0
- fractal_server/config/_database.py +59 -0
- fractal_server/config/_email.py +133 -0
- fractal_server/config/_main.py +78 -0
- fractal_server/config/_oauth.py +69 -0
- fractal_server/config/_settings_config.py +7 -0
- fractal_server/data_migrations/2_17_0.py +339 -0
- fractal_server/images/tools.py +3 -3
- fractal_server/logger.py +3 -3
- fractal_server/main.py +17 -23
- fractal_server/migrations/naming_convention.py +1 -1
- fractal_server/migrations/versions/83bc2ad3ffcc_2_17_0.py +195 -0
- fractal_server/runner/config/__init__.py +2 -0
- fractal_server/runner/config/_local.py +21 -0
- fractal_server/runner/config/_slurm.py +129 -0
- fractal_server/runner/config/slurm_mem_to_MB.py +63 -0
- fractal_server/runner/exceptions.py +4 -0
- fractal_server/runner/executors/base_runner.py +17 -7
- fractal_server/runner/executors/local/get_local_config.py +21 -86
- fractal_server/runner/executors/local/runner.py +48 -5
- fractal_server/runner/executors/slurm_common/_batching.py +2 -2
- fractal_server/runner/executors/slurm_common/base_slurm_runner.py +60 -26
- fractal_server/runner/executors/slurm_common/get_slurm_config.py +39 -55
- fractal_server/runner/executors/slurm_common/remote.py +1 -1
- fractal_server/runner/executors/slurm_common/slurm_config.py +214 -0
- fractal_server/runner/executors/slurm_common/slurm_job_task_models.py +1 -1
- fractal_server/runner/executors/slurm_ssh/runner.py +12 -14
- fractal_server/runner/executors/slurm_sudo/_subprocess_run_as_user.py +2 -2
- fractal_server/runner/executors/slurm_sudo/runner.py +12 -12
- fractal_server/runner/v2/_local.py +36 -21
- fractal_server/runner/v2/_slurm_ssh.py +41 -4
- fractal_server/runner/v2/_slurm_sudo.py +42 -12
- fractal_server/runner/v2/db_tools.py +1 -1
- fractal_server/runner/v2/runner.py +3 -11
- fractal_server/runner/v2/runner_functions.py +42 -28
- fractal_server/runner/v2/submit_workflow.py +88 -109
- fractal_server/runner/versions.py +8 -3
- fractal_server/ssh/_fabric.py +6 -6
- fractal_server/tasks/config/__init__.py +3 -0
- fractal_server/tasks/config/_pixi.py +127 -0
- fractal_server/tasks/config/_python.py +51 -0
- fractal_server/tasks/v2/local/_utils.py +7 -7
- fractal_server/tasks/v2/local/collect.py +13 -5
- fractal_server/tasks/v2/local/collect_pixi.py +26 -10
- fractal_server/tasks/v2/local/deactivate.py +7 -1
- fractal_server/tasks/v2/local/deactivate_pixi.py +5 -1
- fractal_server/tasks/v2/local/delete.py +5 -1
- fractal_server/tasks/v2/local/reactivate.py +13 -5
- fractal_server/tasks/v2/local/reactivate_pixi.py +27 -9
- fractal_server/tasks/v2/ssh/_pixi_slurm_ssh.py +11 -10
- fractal_server/tasks/v2/ssh/_utils.py +6 -7
- fractal_server/tasks/v2/ssh/collect.py +19 -12
- fractal_server/tasks/v2/ssh/collect_pixi.py +34 -16
- fractal_server/tasks/v2/ssh/deactivate.py +12 -8
- fractal_server/tasks/v2/ssh/deactivate_pixi.py +14 -10
- fractal_server/tasks/v2/ssh/delete.py +12 -9
- fractal_server/tasks/v2/ssh/reactivate.py +18 -12
- fractal_server/tasks/v2/ssh/reactivate_pixi.py +36 -17
- fractal_server/tasks/v2/templates/4_pip_show.sh +4 -6
- fractal_server/tasks/v2/utils_database.py +2 -2
- fractal_server/tasks/v2/utils_pixi.py +3 -0
- fractal_server/tasks/v2/utils_python_interpreter.py +8 -16
- fractal_server/tasks/v2/utils_templates.py +7 -10
- fractal_server/utils.py +1 -1
- {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/METADATA +4 -6
- {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/RECORD +136 -117
- fractal_server/app/routes/aux/validate_user_settings.py +0 -73
- fractal_server/app/schemas/user_settings.py +0 -67
- fractal_server/app/user_settings.py +0 -42
- fractal_server/config.py +0 -906
- fractal_server/data_migrations/2_14_10.py +0 -48
- fractal_server/runner/executors/slurm_common/_slurm_config.py +0 -471
- /fractal_server/{runner → app}/shutdown.py +0 -0
- {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/WHEEL +0 -0
- {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/entry_points.txt +0 -0
- {fractal_server-2.16.6.dist-info → fractal_server-2.17.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
from fastapi import APIRouter
|
|
2
2
|
from fastapi import Depends
|
|
3
|
-
from fastapi import HTTPException
|
|
4
|
-
from fastapi import status
|
|
5
3
|
from pydantic import BaseModel
|
|
6
4
|
from pydantic import EmailStr
|
|
7
5
|
from pydantic import Field
|
|
@@ -10,11 +8,16 @@ from sqlmodel import select
|
|
|
10
8
|
|
|
11
9
|
from fractal_server.app.db import AsyncSession
|
|
12
10
|
from fractal_server.app.db import get_async_db
|
|
11
|
+
from fractal_server.app.models import TaskGroupV2
|
|
13
12
|
from fractal_server.app.models import UserOAuth
|
|
14
13
|
from fractal_server.app.models.v2 import TaskV2
|
|
15
14
|
from fractal_server.app.models.v2 import WorkflowTaskV2
|
|
16
15
|
from fractal_server.app.models.v2 import WorkflowV2
|
|
17
|
-
from fractal_server.app.routes.auth import
|
|
16
|
+
from fractal_server.app.routes.auth import current_superuser_act
|
|
17
|
+
from fractal_server.app.routes.pagination import get_pagination_params
|
|
18
|
+
from fractal_server.app.routes.pagination import PaginationRequest
|
|
19
|
+
from fractal_server.app.routes.pagination import PaginationResponse
|
|
20
|
+
from fractal_server.app.schemas.v2.task import TaskType
|
|
18
21
|
|
|
19
22
|
router = APIRouter()
|
|
20
23
|
|
|
@@ -48,66 +51,85 @@ class TaskV2Info(BaseModel):
|
|
|
48
51
|
relationships: list[TaskV2Relationship]
|
|
49
52
|
|
|
50
53
|
|
|
51
|
-
@router.get("/", response_model=
|
|
54
|
+
@router.get("/", response_model=PaginationResponse[TaskV2Info])
|
|
52
55
|
async def query_tasks(
|
|
53
56
|
id: int | None = None,
|
|
54
57
|
source: str | None = None,
|
|
55
58
|
version: str | None = None,
|
|
56
59
|
name: str | None = None,
|
|
57
|
-
|
|
60
|
+
task_type: TaskType | None = None,
|
|
58
61
|
category: str | None = None,
|
|
59
62
|
modality: str | None = None,
|
|
60
63
|
author: str | None = None,
|
|
61
|
-
|
|
64
|
+
resource_id: int | None = None,
|
|
65
|
+
pagination: PaginationRequest = Depends(get_pagination_params),
|
|
66
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
62
67
|
db: AsyncSession = Depends(get_async_db),
|
|
63
|
-
) ->
|
|
68
|
+
) -> PaginationResponse[TaskV2Info]:
|
|
64
69
|
"""
|
|
65
|
-
Query `TaskV2`
|
|
66
|
-
(WorkflowV2s and ProjectV2s)
|
|
67
|
-
|
|
68
|
-
Args:
|
|
69
|
-
id: If not `None`, query for matching `task.id`.
|
|
70
|
-
source: If not `None`, query for contained case insensitive
|
|
71
|
-
`task.source`.
|
|
72
|
-
version: If not `None`, query for matching `task.version`.
|
|
73
|
-
name: If not `None`, query for contained case insensitive `task.name`.
|
|
74
|
-
max_number_of_results: The maximum length of the response.
|
|
75
|
-
category:
|
|
76
|
-
modality:
|
|
77
|
-
author:
|
|
70
|
+
Query `TaskV2` and get information about related workflows and projects.
|
|
78
71
|
"""
|
|
79
72
|
|
|
80
|
-
|
|
73
|
+
# Assign pagination parameters
|
|
74
|
+
page = pagination.page
|
|
75
|
+
page_size = pagination.page_size
|
|
81
76
|
|
|
77
|
+
# Prepare statements
|
|
78
|
+
stm = select(TaskV2).order_by(TaskV2.id)
|
|
79
|
+
stm_count = select(func.count(TaskV2.id))
|
|
82
80
|
if id is not None:
|
|
83
81
|
stm = stm.where(TaskV2.id == id)
|
|
82
|
+
stm_count = stm_count.where(TaskV2.id == id)
|
|
84
83
|
if source is not None:
|
|
85
84
|
stm = stm.where(TaskV2.source.icontains(source))
|
|
85
|
+
stm_count = stm_count.where(TaskV2.source.icontains(source))
|
|
86
86
|
if version is not None:
|
|
87
87
|
stm = stm.where(TaskV2.version == version)
|
|
88
|
+
stm_count = stm_count.where(TaskV2.version == version)
|
|
88
89
|
if name is not None:
|
|
89
90
|
stm = stm.where(TaskV2.name.icontains(name))
|
|
91
|
+
stm_count = stm_count.where(TaskV2.name.icontains(name))
|
|
92
|
+
if task_type is not None:
|
|
93
|
+
stm = stm.where(TaskV2.type == task_type)
|
|
94
|
+
stm_count = stm_count.where(TaskV2.type == task_type)
|
|
90
95
|
if category is not None:
|
|
91
96
|
stm = stm.where(func.lower(TaskV2.category) == category.lower())
|
|
97
|
+
stm_count = stm_count.where(
|
|
98
|
+
func.lower(TaskV2.category) == category.lower()
|
|
99
|
+
)
|
|
92
100
|
if modality is not None:
|
|
93
101
|
stm = stm.where(func.lower(TaskV2.modality) == modality.lower())
|
|
102
|
+
stm_count = stm_count.where(
|
|
103
|
+
func.lower(TaskV2.modality) == modality.lower()
|
|
104
|
+
)
|
|
94
105
|
if author is not None:
|
|
95
106
|
stm = stm.where(TaskV2.authors.icontains(author))
|
|
107
|
+
stm_count = stm_count.where(TaskV2.authors.icontains(author))
|
|
108
|
+
if resource_id is not None:
|
|
109
|
+
stm = (
|
|
110
|
+
stm.join(TaskGroupV2)
|
|
111
|
+
.where(TaskGroupV2.id == TaskV2.taskgroupv2_id)
|
|
112
|
+
.where(TaskGroupV2.resource_id == resource_id)
|
|
113
|
+
)
|
|
114
|
+
stm_count = (
|
|
115
|
+
stm_count.join(TaskGroupV2)
|
|
116
|
+
.where(TaskGroupV2.id == TaskV2.taskgroupv2_id)
|
|
117
|
+
.where(TaskGroupV2.resource_id == resource_id)
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Find total number of elements
|
|
121
|
+
res_total_count = await db.execute(stm_count)
|
|
122
|
+
total_count = res_total_count.scalar()
|
|
123
|
+
if page_size is None:
|
|
124
|
+
page_size = total_count
|
|
125
|
+
else:
|
|
126
|
+
stm = stm.offset((page - 1) * page_size).limit(page_size)
|
|
96
127
|
|
|
128
|
+
# Get `page_size` rows
|
|
97
129
|
res = await db.execute(stm)
|
|
98
130
|
task_list = res.scalars().all()
|
|
99
|
-
if len(task_list) > max_number_of_results:
|
|
100
|
-
await db.close()
|
|
101
|
-
raise HTTPException(
|
|
102
|
-
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
103
|
-
detail=(
|
|
104
|
-
f"Too many Tasks ({len(task_list)} > {max_number_of_results})."
|
|
105
|
-
" Please add more query filters."
|
|
106
|
-
),
|
|
107
|
-
)
|
|
108
131
|
|
|
109
132
|
task_info_list = []
|
|
110
|
-
|
|
111
133
|
for task in task_list:
|
|
112
134
|
stm = (
|
|
113
135
|
select(WorkflowV2)
|
|
@@ -137,4 +159,9 @@ async def query_tasks(
|
|
|
137
159
|
)
|
|
138
160
|
)
|
|
139
161
|
|
|
140
|
-
return
|
|
162
|
+
return PaginationResponse[TaskV2Info](
|
|
163
|
+
total_count=total_count,
|
|
164
|
+
page_size=page_size,
|
|
165
|
+
current_page=page,
|
|
166
|
+
items=task_info_list,
|
|
167
|
+
)
|
|
@@ -12,18 +12,19 @@ from fractal_server.app.db import get_async_db
|
|
|
12
12
|
from fractal_server.app.models import UserOAuth
|
|
13
13
|
from fractal_server.app.models.v2 import TaskGroupActivityV2
|
|
14
14
|
from fractal_server.app.models.v2 import TaskGroupV2
|
|
15
|
-
from fractal_server.app.routes.auth import
|
|
15
|
+
from fractal_server.app.routes.auth import current_superuser_act
|
|
16
16
|
from fractal_server.app.routes.auth._aux_auth import (
|
|
17
17
|
_verify_user_belongs_to_group,
|
|
18
18
|
)
|
|
19
19
|
from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
|
|
20
20
|
from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
|
|
21
21
|
from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
|
|
22
|
-
from fractal_server.app.schemas.v2 import
|
|
22
|
+
from fractal_server.app.schemas.v2 import TaskGroupReadSuperuser
|
|
23
23
|
from fractal_server.app.schemas.v2 import TaskGroupUpdateV2
|
|
24
24
|
from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
|
|
25
25
|
from fractal_server.logger import set_logger
|
|
26
26
|
|
|
27
|
+
|
|
27
28
|
router = APIRouter()
|
|
28
29
|
|
|
29
30
|
logger = set_logger(__name__)
|
|
@@ -38,7 +39,7 @@ async def get_task_group_activity_list(
|
|
|
38
39
|
status: TaskGroupActivityStatusV2 | None = None,
|
|
39
40
|
action: TaskGroupActivityActionV2 | None = None,
|
|
40
41
|
timestamp_started_min: AwareDatetime | None = None,
|
|
41
|
-
superuser: UserOAuth = Depends(
|
|
42
|
+
superuser: UserOAuth = Depends(current_superuser_act),
|
|
42
43
|
db: AsyncSession = Depends(get_async_db),
|
|
43
44
|
) -> list[TaskGroupActivityV2Read]:
|
|
44
45
|
stm = select(TaskGroupActivityV2)
|
|
@@ -64,12 +65,12 @@ async def get_task_group_activity_list(
|
|
|
64
65
|
return activities
|
|
65
66
|
|
|
66
67
|
|
|
67
|
-
@router.get("/{task_group_id}/", response_model=
|
|
68
|
+
@router.get("/{task_group_id}/", response_model=TaskGroupReadSuperuser)
|
|
68
69
|
async def query_task_group(
|
|
69
70
|
task_group_id: int,
|
|
70
|
-
user: UserOAuth = Depends(
|
|
71
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
71
72
|
db: AsyncSession = Depends(get_async_db),
|
|
72
|
-
) ->
|
|
73
|
+
) -> TaskGroupReadSuperuser:
|
|
73
74
|
task_group = await db.get(TaskGroupV2, task_group_id)
|
|
74
75
|
if task_group is None:
|
|
75
76
|
raise HTTPException(
|
|
@@ -79,7 +80,7 @@ async def query_task_group(
|
|
|
79
80
|
return task_group
|
|
80
81
|
|
|
81
82
|
|
|
82
|
-
@router.get("/", response_model=list[
|
|
83
|
+
@router.get("/", response_model=list[TaskGroupReadSuperuser])
|
|
83
84
|
async def query_task_group_list(
|
|
84
85
|
user_id: int | None = None,
|
|
85
86
|
user_group_id: int | None = None,
|
|
@@ -89,9 +90,10 @@ async def query_task_group_list(
|
|
|
89
90
|
origin: TaskGroupV2OriginEnum | None = None,
|
|
90
91
|
timestamp_last_used_min: AwareDatetime | None = None,
|
|
91
92
|
timestamp_last_used_max: AwareDatetime | None = None,
|
|
92
|
-
|
|
93
|
+
resource_id: int | None = None,
|
|
94
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
93
95
|
db: AsyncSession = Depends(get_async_db),
|
|
94
|
-
) -> list[
|
|
96
|
+
) -> list[TaskGroupReadSuperuser]:
|
|
95
97
|
stm = select(TaskGroupV2)
|
|
96
98
|
|
|
97
99
|
if user_group_id is not None and private is True:
|
|
@@ -128,19 +130,22 @@ async def query_task_group_list(
|
|
|
128
130
|
stm = stm.where(
|
|
129
131
|
TaskGroupV2.timestamp_last_used <= timestamp_last_used_max
|
|
130
132
|
)
|
|
133
|
+
if resource_id is not None:
|
|
134
|
+
stm = stm.where(TaskGroupV2.resource_id == resource_id)
|
|
131
135
|
|
|
136
|
+
stm = stm.order_by(TaskGroupV2.id)
|
|
132
137
|
res = await db.execute(stm)
|
|
133
138
|
task_groups_list = res.scalars().all()
|
|
134
139
|
return task_groups_list
|
|
135
140
|
|
|
136
141
|
|
|
137
|
-
@router.patch("/{task_group_id}/", response_model=
|
|
142
|
+
@router.patch("/{task_group_id}/", response_model=TaskGroupReadSuperuser)
|
|
138
143
|
async def patch_task_group(
|
|
139
144
|
task_group_id: int,
|
|
140
145
|
task_group_update: TaskGroupUpdateV2,
|
|
141
|
-
user: UserOAuth = Depends(
|
|
146
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
142
147
|
db: AsyncSession = Depends(get_async_db),
|
|
143
|
-
) -> list[
|
|
148
|
+
) -> list[TaskGroupReadSuperuser]:
|
|
144
149
|
task_group = await db.get(TaskGroupV2, task_group_id)
|
|
145
150
|
if task_group is None:
|
|
146
151
|
raise HTTPException(
|
|
@@ -21,18 +21,16 @@ from fractal_server.app.routes.api.v2._aux_functions_task_lifecycle import (
|
|
|
21
21
|
from fractal_server.app.routes.api.v2._aux_functions_tasks import (
|
|
22
22
|
_get_task_group_or_404,
|
|
23
23
|
)
|
|
24
|
-
from fractal_server.app.routes.auth import
|
|
25
|
-
from fractal_server.app.routes.aux.
|
|
26
|
-
|
|
24
|
+
from fractal_server.app.routes.auth import current_superuser_act
|
|
25
|
+
from fractal_server.app.routes.aux.validate_user_profile import (
|
|
26
|
+
validate_user_profile,
|
|
27
27
|
)
|
|
28
|
+
from fractal_server.app.schemas.v2 import ResourceType
|
|
28
29
|
from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
|
|
29
30
|
from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
|
|
30
31
|
from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
|
|
31
32
|
from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
|
|
32
|
-
from fractal_server.config import get_settings
|
|
33
33
|
from fractal_server.logger import set_logger
|
|
34
|
-
from fractal_server.ssh._fabric import SSHConfig
|
|
35
|
-
from fractal_server.syringe import Inject
|
|
36
34
|
from fractal_server.tasks.v2.local import deactivate_local
|
|
37
35
|
from fractal_server.tasks.v2.local import delete_local
|
|
38
36
|
from fractal_server.tasks.v2.local import reactivate_local
|
|
@@ -54,7 +52,7 @@ async def deactivate_task_group(
|
|
|
54
52
|
task_group_id: int,
|
|
55
53
|
background_tasks: BackgroundTasks,
|
|
56
54
|
response: Response,
|
|
57
|
-
superuser: UserOAuth = Depends(
|
|
55
|
+
superuser: UserOAuth = Depends(current_superuser_act),
|
|
58
56
|
db: AsyncSession = Depends(get_async_db),
|
|
59
57
|
) -> TaskGroupActivityV2Read:
|
|
60
58
|
"""
|
|
@@ -114,34 +112,23 @@ async def deactivate_task_group(
|
|
|
114
112
|
db.add(task_group_activity)
|
|
115
113
|
await db.commit()
|
|
116
114
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
# Validate user settings (backend-specific)
|
|
121
|
-
user = await db.get(UserOAuth, task_group.user_id)
|
|
122
|
-
user_settings = await validate_user_settings(
|
|
123
|
-
user=user, backend=settings.FRACTAL_RUNNER_BACKEND, db=db
|
|
124
|
-
)
|
|
125
|
-
# User appropriate FractalSSH object
|
|
126
|
-
ssh_config = SSHConfig(
|
|
127
|
-
user=user_settings.ssh_username,
|
|
128
|
-
host=user_settings.ssh_host,
|
|
129
|
-
key_path=user_settings.ssh_private_key_path,
|
|
130
|
-
)
|
|
115
|
+
user = await db.get(UserOAuth, task_group.user_id)
|
|
116
|
+
# Get validated resource and profile
|
|
117
|
+
resource, profile = await validate_user_profile(user=user, db=db)
|
|
131
118
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
task_group_activity_id=task_group_activity.id,
|
|
136
|
-
ssh_config=ssh_config,
|
|
137
|
-
tasks_base_dir=user_settings.ssh_tasks_dir,
|
|
138
|
-
)
|
|
119
|
+
# Submit background task
|
|
120
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
121
|
+
deactivate_function = deactivate_ssh
|
|
139
122
|
else:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
123
|
+
deactivate_function = deactivate_local
|
|
124
|
+
|
|
125
|
+
background_tasks.add_task(
|
|
126
|
+
deactivate_function,
|
|
127
|
+
task_group_id=task_group.id,
|
|
128
|
+
task_group_activity_id=task_group_activity.id,
|
|
129
|
+
resource=resource,
|
|
130
|
+
profile=profile,
|
|
131
|
+
)
|
|
145
132
|
|
|
146
133
|
logger.debug(
|
|
147
134
|
"Admin task group deactivation endpoint: start deactivate "
|
|
@@ -159,7 +146,7 @@ async def reactivate_task_group(
|
|
|
159
146
|
task_group_id: int,
|
|
160
147
|
background_tasks: BackgroundTasks,
|
|
161
148
|
response: Response,
|
|
162
|
-
superuser: UserOAuth = Depends(
|
|
149
|
+
superuser: UserOAuth = Depends(current_superuser_act),
|
|
163
150
|
db: AsyncSession = Depends(get_async_db),
|
|
164
151
|
) -> TaskGroupActivityV2Read:
|
|
165
152
|
"""
|
|
@@ -229,34 +216,24 @@ async def reactivate_task_group(
|
|
|
229
216
|
db.add(task_group_activity)
|
|
230
217
|
await db.commit()
|
|
231
218
|
|
|
232
|
-
#
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
# Validate user settings (backend-specific)
|
|
236
|
-
user = await db.get(UserOAuth, task_group.user_id)
|
|
237
|
-
user_settings = await validate_user_settings(
|
|
238
|
-
user=user, backend=settings.FRACTAL_RUNNER_BACKEND, db=db
|
|
239
|
-
)
|
|
240
|
-
# Use appropriate FractalSSH object
|
|
241
|
-
ssh_config = SSHConfig(
|
|
242
|
-
user=user_settings.ssh_username,
|
|
243
|
-
host=user_settings.ssh_host,
|
|
244
|
-
key_path=user_settings.ssh_private_key_path,
|
|
245
|
-
)
|
|
219
|
+
# Get validated resource and profile
|
|
220
|
+
user = await db.get(UserOAuth, task_group.user_id)
|
|
221
|
+
resource, profile = await validate_user_profile(user=user, db=db)
|
|
246
222
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
task_group_activity_id=task_group_activity.id,
|
|
251
|
-
ssh_config=ssh_config,
|
|
252
|
-
tasks_base_dir=user_settings.ssh_tasks_dir,
|
|
253
|
-
)
|
|
223
|
+
# Submit background task
|
|
224
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
225
|
+
reactivate_function = reactivate_ssh
|
|
254
226
|
else:
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
227
|
+
reactivate_function = reactivate_local
|
|
228
|
+
|
|
229
|
+
background_tasks.add_task(
|
|
230
|
+
reactivate_function,
|
|
231
|
+
task_group_id=task_group.id,
|
|
232
|
+
task_group_activity_id=task_group_activity.id,
|
|
233
|
+
resource=resource,
|
|
234
|
+
profile=profile,
|
|
235
|
+
)
|
|
236
|
+
|
|
260
237
|
logger.debug(
|
|
261
238
|
"Admin task group reactivation endpoint: start reactivate "
|
|
262
239
|
"and return task_group_activity"
|
|
@@ -270,7 +247,7 @@ async def delete_task_group(
|
|
|
270
247
|
task_group_id: int,
|
|
271
248
|
background_tasks: BackgroundTasks,
|
|
272
249
|
response: Response,
|
|
273
|
-
superuser: UserOAuth = Depends(
|
|
250
|
+
superuser: UserOAuth = Depends(current_superuser_act),
|
|
274
251
|
db: AsyncSession = Depends(get_async_db),
|
|
275
252
|
):
|
|
276
253
|
task_group = await _get_task_group_or_404(
|
|
@@ -292,33 +269,22 @@ async def delete_task_group(
|
|
|
292
269
|
db.add(task_group_activity)
|
|
293
270
|
await db.commit()
|
|
294
271
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
task_owner = await db.get(UserOAuth, task_group.user_id)
|
|
299
|
-
task_owner_settings = await validate_user_settings(
|
|
300
|
-
user=task_owner, backend=settings.FRACTAL_RUNNER_BACKEND, db=db
|
|
301
|
-
)
|
|
302
|
-
# Use appropriate FractalSSH object
|
|
303
|
-
ssh_config = SSHConfig(
|
|
304
|
-
user=task_owner_settings.ssh_username,
|
|
305
|
-
host=task_owner_settings.ssh_host,
|
|
306
|
-
key_path=task_owner_settings.ssh_private_key_path,
|
|
307
|
-
)
|
|
272
|
+
# Get validated resource and profile
|
|
273
|
+
task_owner = await db.get(UserOAuth, task_group.user_id)
|
|
274
|
+
resource, profile = await validate_user_profile(user=task_owner, db=db)
|
|
308
275
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
task_group_id=task_group.id,
|
|
312
|
-
task_group_activity_id=task_group_activity.id,
|
|
313
|
-
ssh_config=ssh_config,
|
|
314
|
-
tasks_base_dir=task_owner_settings.ssh_tasks_dir,
|
|
315
|
-
)
|
|
276
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
277
|
+
delete_function = delete_ssh
|
|
316
278
|
else:
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
279
|
+
delete_function = delete_local
|
|
280
|
+
|
|
281
|
+
background_tasks.add_task(
|
|
282
|
+
delete_function,
|
|
283
|
+
task_group_activity_id=task_group_activity.id,
|
|
284
|
+
task_group_id=task_group.id,
|
|
285
|
+
resource=resource,
|
|
286
|
+
profile=profile,
|
|
287
|
+
)
|
|
322
288
|
logger.debug(
|
|
323
289
|
"Admin task group deletion endpoint: start deletion "
|
|
324
290
|
"and return task_group_activity"
|
|
@@ -4,25 +4,62 @@
|
|
|
4
4
|
from fastapi import APIRouter
|
|
5
5
|
from fastapi import Depends
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
from ....syringe import Inject
|
|
7
|
+
import fractal_server
|
|
9
8
|
from fractal_server.app.models import UserOAuth
|
|
10
|
-
from fractal_server.app.routes.auth import
|
|
11
|
-
|
|
9
|
+
from fractal_server.app.routes.auth import current_superuser_act
|
|
10
|
+
from fractal_server.config import get_data_settings
|
|
11
|
+
from fractal_server.config import get_db_settings
|
|
12
|
+
from fractal_server.config import get_email_settings
|
|
13
|
+
from fractal_server.config import get_oauth_settings
|
|
14
|
+
from fractal_server.config import get_settings
|
|
15
|
+
from fractal_server.syringe import Inject
|
|
12
16
|
|
|
13
17
|
router_api = APIRouter()
|
|
14
18
|
|
|
15
19
|
|
|
16
20
|
@router_api.get("/alive/")
|
|
17
21
|
async def alive():
|
|
18
|
-
settings = Inject(get_settings)
|
|
19
22
|
return dict(
|
|
20
23
|
alive=True,
|
|
21
|
-
version=
|
|
24
|
+
version=fractal_server.__VERSION__,
|
|
22
25
|
)
|
|
23
26
|
|
|
24
27
|
|
|
25
|
-
@router_api.get("/settings/")
|
|
26
|
-
async def view_settings(
|
|
28
|
+
@router_api.get("/settings/app/")
|
|
29
|
+
async def view_settings(
|
|
30
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
31
|
+
):
|
|
27
32
|
settings = Inject(get_settings)
|
|
28
33
|
return settings.model_dump()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@router_api.get("/settings/database/")
|
|
37
|
+
async def view_db_settings(
|
|
38
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
39
|
+
):
|
|
40
|
+
settings = Inject(get_db_settings)
|
|
41
|
+
return settings.model_dump()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@router_api.get("/settings/email/")
|
|
45
|
+
async def view_email_settings(
|
|
46
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
47
|
+
):
|
|
48
|
+
settings = Inject(get_email_settings)
|
|
49
|
+
return settings.model_dump()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@router_api.get("/settings/data/")
|
|
53
|
+
async def view_data_settings(
|
|
54
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
55
|
+
):
|
|
56
|
+
settings = Inject(get_data_settings)
|
|
57
|
+
return settings.model_dump()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@router_api.get("/settings/oauth/")
|
|
61
|
+
async def view_oauth_settings(
|
|
62
|
+
user: UserOAuth = Depends(current_superuser_act),
|
|
63
|
+
):
|
|
64
|
+
settings = Inject(get_oauth_settings)
|
|
65
|
+
return settings.model_dump()
|
|
@@ -11,7 +11,6 @@ from sqlalchemy.orm.attributes import flag_modified
|
|
|
11
11
|
from sqlmodel import select
|
|
12
12
|
from sqlmodel.sql.expression import SelectOfScalar
|
|
13
13
|
|
|
14
|
-
from ....db import AsyncSession
|
|
15
14
|
from ....models.v2 import DatasetV2
|
|
16
15
|
from ....models.v2 import JobV2
|
|
17
16
|
from ....models.v2 import LinkUserProjectV2
|
|
@@ -20,6 +19,10 @@ from ....models.v2 import TaskV2
|
|
|
20
19
|
from ....models.v2 import WorkflowTaskV2
|
|
21
20
|
from ....models.v2 import WorkflowV2
|
|
22
21
|
from ....schemas.v2 import JobStatusTypeV2
|
|
22
|
+
from fractal_server.app.db import AsyncSession
|
|
23
|
+
from fractal_server.app.models import Profile
|
|
24
|
+
from fractal_server.app.models import Resource
|
|
25
|
+
from fractal_server.app.models import UserOAuth
|
|
23
26
|
from fractal_server.logger import set_logger
|
|
24
27
|
|
|
25
28
|
logger = set_logger(__name__)
|
|
@@ -538,3 +541,16 @@ async def _get_submitted_job_or_none(
|
|
|
538
541
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
539
542
|
detail=error_msg,
|
|
540
543
|
)
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
async def _get_user_resource_id(user_id: int, db: AsyncSession) -> int | None:
|
|
547
|
+
res = await db.execute(
|
|
548
|
+
select(Resource.id)
|
|
549
|
+
.join(Profile)
|
|
550
|
+
.join(UserOAuth)
|
|
551
|
+
.where(Resource.id == Profile.resource_id)
|
|
552
|
+
.where(Profile.id == UserOAuth.profile_id)
|
|
553
|
+
.where(UserOAuth.id == user_id)
|
|
554
|
+
)
|
|
555
|
+
resource_id = res.scalar_one_or_none()
|
|
556
|
+
return resource_id
|
|
@@ -32,7 +32,7 @@ async def get_history_unit_or_404(
|
|
|
32
32
|
"""
|
|
33
33
|
Get an existing HistoryUnit or raise a 404.
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
Args:
|
|
36
36
|
history_unit_id: The `HistoryUnit` id
|
|
37
37
|
db: An asynchronous db session
|
|
38
38
|
"""
|
|
@@ -51,7 +51,7 @@ async def get_history_run_or_404(
|
|
|
51
51
|
"""
|
|
52
52
|
Get an existing HistoryRun or raise a 404.
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
Args:
|
|
55
55
|
history_run_id:
|
|
56
56
|
db:
|
|
57
57
|
"""
|
|
@@ -59,7 +59,7 @@ async def get_package_version_from_pypi(
|
|
|
59
59
|
|
|
60
60
|
Ref https://warehouse.pypa.io/api-reference/json.html.
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
Args:
|
|
63
63
|
name: Package name.
|
|
64
64
|
version:
|
|
65
65
|
Could be a correct version (`1.3.0`), an incomplete one
|
|
@@ -165,7 +165,7 @@ async def check_no_ongoing_activity(
|
|
|
165
165
|
"""
|
|
166
166
|
Find ongoing activities for the same task group.
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
Args:
|
|
169
169
|
task_group_id:
|
|
170
170
|
db:
|
|
171
171
|
"""
|
|
@@ -204,7 +204,7 @@ async def check_no_submitted_job(
|
|
|
204
204
|
"""
|
|
205
205
|
Find submitted jobs which include tasks from a given task group.
|
|
206
206
|
|
|
207
|
-
|
|
207
|
+
Args:
|
|
208
208
|
task_group_id: ID of the `TaskGroupV2` object.
|
|
209
209
|
db: Asynchronous database session.
|
|
210
210
|
"""
|