fractal-server 2.16.5__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/job.py +3 -4
- 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 +29 -16
- 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.5.dist-info → fractal_server-2.17.0.dist-info}/METADATA +8 -10
- {fractal_server-2.16.5.dist-info → fractal_server-2.17.0.dist-info}/RECORD +137 -118
- {fractal_server-2.16.5.dist-info → fractal_server-2.17.0.dist-info}/WHEEL +1 -1
- 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.5.dist-info → fractal_server-2.17.0.dist-info}/entry_points.txt +0 -0
- {fractal_server-2.16.5.dist-info → fractal_server-2.17.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -27,19 +27,17 @@ from fractal_server.app.routes.api.v2._aux_functions_tasks import (
|
|
|
27
27
|
from fractal_server.app.routes.api.v2._aux_functions_tasks import (
|
|
28
28
|
_verify_non_duplication_user_constraint,
|
|
29
29
|
)
|
|
30
|
-
from fractal_server.app.routes.auth import
|
|
31
|
-
from fractal_server.app.routes.aux.
|
|
32
|
-
|
|
30
|
+
from fractal_server.app.routes.auth import current_user_act_ver_prof
|
|
31
|
+
from fractal_server.app.routes.aux.validate_user_profile import (
|
|
32
|
+
validate_user_profile,
|
|
33
33
|
)
|
|
34
34
|
from fractal_server.app.schemas.v2 import FractalUploadedFile
|
|
35
|
+
from fractal_server.app.schemas.v2 import ResourceType
|
|
35
36
|
from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
|
|
36
37
|
from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
|
|
37
38
|
from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
|
|
38
39
|
from fractal_server.app.schemas.v2.task_group import TaskGroupV2OriginEnum
|
|
39
|
-
from fractal_server.config import get_settings
|
|
40
40
|
from fractal_server.logger import set_logger
|
|
41
|
-
from fractal_server.ssh._fabric import SSHConfig
|
|
42
|
-
from fractal_server.syringe import Inject
|
|
43
41
|
from fractal_server.tasks.v2.local import collect_local_pixi
|
|
44
42
|
from fractal_server.tasks.v2.ssh import collect_ssh_pixi
|
|
45
43
|
from fractal_server.tasks.v2.utils_package_names import normalize_package_name
|
|
@@ -86,26 +84,30 @@ async def collect_task_pixi(
|
|
|
86
84
|
pixi_version: NonEmptyStr | None = Form(None),
|
|
87
85
|
private: bool = False,
|
|
88
86
|
user_group_id: int | None = None,
|
|
89
|
-
user: UserOAuth = Depends(
|
|
87
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
90
88
|
db: AsyncSession = Depends(get_async_db),
|
|
91
89
|
) -> TaskGroupActivityV2Read:
|
|
92
|
-
|
|
90
|
+
# Get validated resource and profile
|
|
91
|
+
resource, profile = await validate_user_profile(user=user, db=db)
|
|
92
|
+
resource_id = resource.id
|
|
93
|
+
|
|
93
94
|
# Check if Pixi is available
|
|
94
|
-
if
|
|
95
|
+
if not resource.tasks_pixi_config:
|
|
95
96
|
raise HTTPException(
|
|
96
97
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
97
98
|
detail="Pixi task collection is not available.",
|
|
98
99
|
)
|
|
99
100
|
# Check if provided Pixi version is available. Use default if not provided
|
|
100
101
|
if pixi_version is None:
|
|
101
|
-
pixi_version =
|
|
102
|
+
pixi_version = resource.tasks_pixi_config["default_version"]
|
|
102
103
|
else:
|
|
103
|
-
if pixi_version not in
|
|
104
|
+
if pixi_version not in resource.tasks_pixi_config["versions"]:
|
|
104
105
|
raise HTTPException(
|
|
105
106
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
106
107
|
detail=(
|
|
107
|
-
f"Pixi version {pixi_version} is not available.
|
|
108
|
-
|
|
108
|
+
f"Pixi version '{pixi_version}' is not available. "
|
|
109
|
+
"Available versions: "
|
|
110
|
+
f"{list(resource.tasks_pixi_config['versions'].keys())}"
|
|
109
111
|
),
|
|
110
112
|
)
|
|
111
113
|
|
|
@@ -123,14 +125,10 @@ async def collect_task_pixi(
|
|
|
123
125
|
db=db,
|
|
124
126
|
)
|
|
125
127
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
if settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
|
|
131
|
-
base_tasks_path = user_settings.ssh_tasks_dir
|
|
128
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
129
|
+
base_tasks_path = profile.tasks_remote_dir
|
|
132
130
|
else:
|
|
133
|
-
base_tasks_path =
|
|
131
|
+
base_tasks_path = resource.tasks_local_dir
|
|
134
132
|
task_group_path = (
|
|
135
133
|
Path(base_tasks_path) / str(user.id) / pkg_name / version
|
|
136
134
|
).as_posix()
|
|
@@ -138,6 +136,7 @@ async def collect_task_pixi(
|
|
|
138
136
|
task_group_attrs = dict(
|
|
139
137
|
user_id=user.id,
|
|
140
138
|
user_group_id=user_group_id,
|
|
139
|
+
resource_id=resource_id,
|
|
141
140
|
origin=TaskGroupV2OriginEnum.PIXI,
|
|
142
141
|
pixi_version=pixi_version,
|
|
143
142
|
pkg_name=pkg_name,
|
|
@@ -149,6 +148,7 @@ async def collect_task_pixi(
|
|
|
149
148
|
user_id=user.id,
|
|
150
149
|
pkg_name=task_group_attrs["pkg_name"],
|
|
151
150
|
version=task_group_attrs["version"],
|
|
151
|
+
user_resource_id=resource_id,
|
|
152
152
|
db=db,
|
|
153
153
|
)
|
|
154
154
|
await _verify_non_duplication_group_constraint(
|
|
@@ -159,10 +159,11 @@ async def collect_task_pixi(
|
|
|
159
159
|
)
|
|
160
160
|
await _verify_non_duplication_group_path(
|
|
161
161
|
path=task_group_attrs["path"],
|
|
162
|
+
resource_id=resource_id,
|
|
162
163
|
db=db,
|
|
163
164
|
)
|
|
164
165
|
|
|
165
|
-
if
|
|
166
|
+
if resource.type != ResourceType.SLURM_SSH:
|
|
166
167
|
if Path(task_group_path).exists():
|
|
167
168
|
raise HTTPException(
|
|
168
169
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
@@ -187,28 +188,19 @@ async def collect_task_pixi(
|
|
|
187
188
|
await db.commit()
|
|
188
189
|
await db.refresh(task_group_activity)
|
|
189
190
|
|
|
190
|
-
if
|
|
191
|
-
|
|
192
|
-
user=user_settings.ssh_username,
|
|
193
|
-
host=user_settings.ssh_host,
|
|
194
|
-
key_path=user_settings.ssh_private_key_path,
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
background_tasks.add_task(
|
|
198
|
-
collect_ssh_pixi,
|
|
199
|
-
task_group_id=task_group.id,
|
|
200
|
-
task_group_activity_id=task_group_activity.id,
|
|
201
|
-
ssh_config=ssh_config,
|
|
202
|
-
tasks_base_dir=user_settings.ssh_tasks_dir,
|
|
203
|
-
tar_gz_file=tar_gz_file,
|
|
204
|
-
)
|
|
191
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
192
|
+
collect_function = collect_ssh_pixi
|
|
205
193
|
else:
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
194
|
+
collect_function = collect_local_pixi
|
|
195
|
+
|
|
196
|
+
background_tasks.add_task(
|
|
197
|
+
collect_function,
|
|
198
|
+
task_group_id=task_group.id,
|
|
199
|
+
task_group_activity_id=task_group_activity.id,
|
|
200
|
+
tar_gz_file=tar_gz_file,
|
|
201
|
+
resource=resource,
|
|
202
|
+
profile=profile,
|
|
203
|
+
)
|
|
212
204
|
logger.info(
|
|
213
205
|
"Task-collection endpoint: start background collection "
|
|
214
206
|
"and return task_group_activity. "
|
|
@@ -11,6 +11,7 @@ from pydantic.types import AwareDatetime
|
|
|
11
11
|
from sqlmodel import or_
|
|
12
12
|
from sqlmodel import select
|
|
13
13
|
|
|
14
|
+
from ._aux_functions import _get_user_resource_id
|
|
14
15
|
from ._aux_functions_tasks import _get_task_group_full_access
|
|
15
16
|
from ._aux_functions_tasks import _get_task_group_read_access
|
|
16
17
|
from ._aux_functions_tasks import _verify_non_duplication_group_constraint
|
|
@@ -21,8 +22,10 @@ from fractal_server.app.models import LinkUserGroup
|
|
|
21
22
|
from fractal_server.app.models import UserOAuth
|
|
22
23
|
from fractal_server.app.models.v2 import TaskGroupActivityV2
|
|
23
24
|
from fractal_server.app.models.v2 import TaskGroupV2
|
|
24
|
-
from fractal_server.app.routes.auth import
|
|
25
|
-
from fractal_server.app.routes.auth._aux_auth import
|
|
25
|
+
from fractal_server.app.routes.auth import current_user_act_ver_prof
|
|
26
|
+
from fractal_server.app.routes.auth._aux_auth import (
|
|
27
|
+
_get_default_usergroup_id_or_none,
|
|
28
|
+
)
|
|
26
29
|
from fractal_server.app.routes.auth._aux_auth import (
|
|
27
30
|
_verify_user_belongs_to_group,
|
|
28
31
|
)
|
|
@@ -66,7 +69,7 @@ async def get_task_group_activity_list(
|
|
|
66
69
|
status: TaskGroupActivityStatusV2 | None = None,
|
|
67
70
|
action: TaskGroupActivityActionV2 | None = None,
|
|
68
71
|
timestamp_started_min: AwareDatetime | None = None,
|
|
69
|
-
user: UserOAuth = Depends(
|
|
72
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
70
73
|
db: AsyncSession = Depends(get_async_db),
|
|
71
74
|
) -> list[TaskGroupActivityV2Read]:
|
|
72
75
|
stm = select(TaskGroupActivityV2).where(
|
|
@@ -98,7 +101,7 @@ async def get_task_group_activity_list(
|
|
|
98
101
|
)
|
|
99
102
|
async def get_task_group_activity(
|
|
100
103
|
task_group_activity_id: int,
|
|
101
|
-
user: UserOAuth = Depends(
|
|
104
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
102
105
|
db: AsyncSession = Depends(get_async_db),
|
|
103
106
|
) -> TaskGroupActivityV2Read:
|
|
104
107
|
activity = await db.get(TaskGroupActivityV2, task_group_activity_id)
|
|
@@ -122,7 +125,7 @@ async def get_task_group_activity(
|
|
|
122
125
|
|
|
123
126
|
@router.get("/", response_model=list[tuple[str, list[TaskGroupReadV2]]])
|
|
124
127
|
async def get_task_group_list(
|
|
125
|
-
user: UserOAuth = Depends(
|
|
128
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
126
129
|
db: AsyncSession = Depends(get_async_db),
|
|
127
130
|
only_active: bool = False,
|
|
128
131
|
only_owner: bool = False,
|
|
@@ -142,7 +145,14 @@ async def get_task_group_list(
|
|
|
142
145
|
)
|
|
143
146
|
),
|
|
144
147
|
)
|
|
145
|
-
|
|
148
|
+
|
|
149
|
+
user_resource_id = await _get_user_resource_id(user_id=user.id, db=db)
|
|
150
|
+
stm = (
|
|
151
|
+
select(TaskGroupV2)
|
|
152
|
+
.where(TaskGroupV2.resource_id == user_resource_id)
|
|
153
|
+
.where(condition)
|
|
154
|
+
.order_by(TaskGroupV2.pkg_name)
|
|
155
|
+
)
|
|
146
156
|
if only_active:
|
|
147
157
|
stm = stm.where(TaskGroupV2.active)
|
|
148
158
|
|
|
@@ -155,7 +165,7 @@ async def get_task_group_list(
|
|
|
155
165
|
setattr(task, "args_schema_non_parallel", None)
|
|
156
166
|
setattr(task, "args_schema_parallel", None)
|
|
157
167
|
|
|
158
|
-
default_group_id = await
|
|
168
|
+
default_group_id = await _get_default_usergroup_id_or_none(db)
|
|
159
169
|
grouped_result = [
|
|
160
170
|
(
|
|
161
171
|
pkg_name,
|
|
@@ -182,7 +192,7 @@ async def get_task_group_list(
|
|
|
182
192
|
@router.get("/{task_group_id}/", response_model=TaskGroupReadV2)
|
|
183
193
|
async def get_task_group(
|
|
184
194
|
task_group_id: int,
|
|
185
|
-
user: UserOAuth = Depends(
|
|
195
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
186
196
|
db: AsyncSession = Depends(get_async_db),
|
|
187
197
|
) -> TaskGroupReadV2:
|
|
188
198
|
"""
|
|
@@ -200,7 +210,7 @@ async def get_task_group(
|
|
|
200
210
|
async def patch_task_group(
|
|
201
211
|
task_group_id: int,
|
|
202
212
|
task_group_update: TaskGroupUpdateV2,
|
|
203
|
-
user: UserOAuth = Depends(
|
|
213
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
204
214
|
db: AsyncSession = Depends(get_async_db),
|
|
205
215
|
) -> TaskGroupReadV2:
|
|
206
216
|
"""
|
|
@@ -5,7 +5,6 @@ from fastapi import HTTPException
|
|
|
5
5
|
from fastapi import Response
|
|
6
6
|
from fastapi import status
|
|
7
7
|
|
|
8
|
-
from ...aux.validate_user_settings import validate_user_settings
|
|
9
8
|
from ._aux_functions_task_lifecycle import check_no_ongoing_activity
|
|
10
9
|
from ._aux_functions_task_lifecycle import check_no_related_workflowtask
|
|
11
10
|
from ._aux_functions_task_lifecycle import check_no_submitted_job
|
|
@@ -14,16 +13,17 @@ from fractal_server.app.db import AsyncSession
|
|
|
14
13
|
from fractal_server.app.db import get_async_db
|
|
15
14
|
from fractal_server.app.models import UserOAuth
|
|
16
15
|
from fractal_server.app.models.v2 import TaskGroupActivityV2
|
|
17
|
-
from fractal_server.app.routes.auth import
|
|
16
|
+
from fractal_server.app.routes.auth import current_user_act_ver_prof
|
|
17
|
+
from fractal_server.app.routes.aux.validate_user_profile import (
|
|
18
|
+
validate_user_profile,
|
|
19
|
+
)
|
|
20
|
+
from fractal_server.app.schemas.v2 import ResourceType
|
|
18
21
|
from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
|
|
19
22
|
from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
|
|
20
23
|
from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
|
|
21
24
|
from fractal_server.app.schemas.v2 import TaskGroupReadV2
|
|
22
25
|
from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
|
|
23
|
-
from fractal_server.config import get_settings
|
|
24
26
|
from fractal_server.logger import set_logger
|
|
25
|
-
from fractal_server.ssh._fabric import SSHConfig
|
|
26
|
-
from fractal_server.syringe import Inject
|
|
27
27
|
from fractal_server.tasks.v2.local import deactivate_local
|
|
28
28
|
from fractal_server.tasks.v2.local import deactivate_local_pixi
|
|
29
29
|
from fractal_server.tasks.v2.local import delete_local
|
|
@@ -50,12 +50,16 @@ async def deactivate_task_group(
|
|
|
50
50
|
task_group_id: int,
|
|
51
51
|
background_tasks: BackgroundTasks,
|
|
52
52
|
response: Response,
|
|
53
|
-
user: UserOAuth = Depends(
|
|
53
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
54
54
|
db: AsyncSession = Depends(get_async_db),
|
|
55
55
|
) -> TaskGroupActivityV2Read:
|
|
56
56
|
"""
|
|
57
57
|
Deactivate task-group venv
|
|
58
58
|
"""
|
|
59
|
+
|
|
60
|
+
# Get validated resource and profile
|
|
61
|
+
resource, profile = await validate_user_profile(user=user, db=db)
|
|
62
|
+
|
|
59
63
|
# Check access
|
|
60
64
|
task_group = await _get_task_group_full_access(
|
|
61
65
|
task_group_id=task_group_id,
|
|
@@ -116,41 +120,23 @@ async def deactivate_task_group(
|
|
|
116
120
|
await db.commit()
|
|
117
121
|
|
|
118
122
|
# Submit background task
|
|
119
|
-
|
|
120
|
-
if settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
|
|
121
|
-
# Validate user settings (backend-specific)
|
|
122
|
-
user_settings = await validate_user_settings(
|
|
123
|
-
user=user, backend=settings.FRACTAL_RUNNER_BACKEND, db=db
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
# User appropriate FractalSSH object
|
|
127
|
-
ssh_config = SSHConfig(
|
|
128
|
-
user=user_settings.ssh_username,
|
|
129
|
-
host=user_settings.ssh_host,
|
|
130
|
-
key_path=user_settings.ssh_private_key_path,
|
|
131
|
-
)
|
|
123
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
132
124
|
if task_group.origin == TaskGroupV2OriginEnum.PIXI:
|
|
133
125
|
deactivate_function = deactivate_ssh_pixi
|
|
134
126
|
else:
|
|
135
127
|
deactivate_function = deactivate_ssh
|
|
136
|
-
background_tasks.add_task(
|
|
137
|
-
deactivate_function,
|
|
138
|
-
task_group_id=task_group.id,
|
|
139
|
-
task_group_activity_id=task_group_activity.id,
|
|
140
|
-
ssh_config=ssh_config,
|
|
141
|
-
tasks_base_dir=user_settings.ssh_tasks_dir,
|
|
142
|
-
)
|
|
143
|
-
|
|
144
128
|
else:
|
|
145
129
|
if task_group.origin == TaskGroupV2OriginEnum.PIXI:
|
|
146
130
|
deactivate_function = deactivate_local_pixi
|
|
147
131
|
else:
|
|
148
132
|
deactivate_function = deactivate_local
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
133
|
+
background_tasks.add_task(
|
|
134
|
+
deactivate_function,
|
|
135
|
+
task_group_id=task_group.id,
|
|
136
|
+
task_group_activity_id=task_group_activity.id,
|
|
137
|
+
resource=resource,
|
|
138
|
+
profile=profile,
|
|
139
|
+
)
|
|
154
140
|
|
|
155
141
|
logger.debug(
|
|
156
142
|
"Task group deactivation endpoint: start deactivate "
|
|
@@ -168,12 +154,14 @@ async def reactivate_task_group(
|
|
|
168
154
|
task_group_id: int,
|
|
169
155
|
background_tasks: BackgroundTasks,
|
|
170
156
|
response: Response,
|
|
171
|
-
user: UserOAuth = Depends(
|
|
157
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
172
158
|
db: AsyncSession = Depends(get_async_db),
|
|
173
159
|
) -> TaskGroupReadV2:
|
|
174
160
|
"""
|
|
175
161
|
Deactivate task-group venv
|
|
176
162
|
"""
|
|
163
|
+
# Get validated resource and profile
|
|
164
|
+
resource, profile = await validate_user_profile(user=user, db=db)
|
|
177
165
|
|
|
178
166
|
# Check access
|
|
179
167
|
task_group = await _get_task_group_full_access(
|
|
@@ -242,42 +230,23 @@ async def reactivate_task_group(
|
|
|
242
230
|
await db.commit()
|
|
243
231
|
|
|
244
232
|
# Submit background task
|
|
245
|
-
|
|
246
|
-
if settings.FRACTAL_RUNNER_BACKEND == "slurm_ssh":
|
|
247
|
-
# Validate user settings (backend-specific)
|
|
248
|
-
user_settings = await validate_user_settings(
|
|
249
|
-
user=user, backend=settings.FRACTAL_RUNNER_BACKEND, db=db
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
# Use appropriate SSH credentials
|
|
253
|
-
ssh_config = SSHConfig(
|
|
254
|
-
user=user_settings.ssh_username,
|
|
255
|
-
host=user_settings.ssh_host,
|
|
256
|
-
key_path=user_settings.ssh_private_key_path,
|
|
257
|
-
)
|
|
258
|
-
|
|
233
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
259
234
|
if task_group.origin == TaskGroupV2OriginEnum.PIXI:
|
|
260
235
|
reactivate_function = reactivate_ssh_pixi
|
|
261
236
|
else:
|
|
262
237
|
reactivate_function = reactivate_ssh
|
|
263
|
-
background_tasks.add_task(
|
|
264
|
-
reactivate_function,
|
|
265
|
-
task_group_id=task_group.id,
|
|
266
|
-
task_group_activity_id=task_group_activity.id,
|
|
267
|
-
ssh_config=ssh_config,
|
|
268
|
-
tasks_base_dir=user_settings.ssh_tasks_dir,
|
|
269
|
-
)
|
|
270
|
-
|
|
271
238
|
else:
|
|
272
239
|
if task_group.origin == TaskGroupV2OriginEnum.PIXI:
|
|
273
240
|
reactivate_function = reactivate_local_pixi
|
|
274
241
|
else:
|
|
275
242
|
reactivate_function = reactivate_local
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
243
|
+
background_tasks.add_task(
|
|
244
|
+
reactivate_function,
|
|
245
|
+
task_group_id=task_group.id,
|
|
246
|
+
task_group_activity_id=task_group_activity.id,
|
|
247
|
+
resource=resource,
|
|
248
|
+
profile=profile,
|
|
249
|
+
)
|
|
281
250
|
logger.debug(
|
|
282
251
|
"Task group reactivation endpoint: start reactivate "
|
|
283
252
|
"and return task_group_activity"
|
|
@@ -294,12 +263,14 @@ async def delete_task_group(
|
|
|
294
263
|
task_group_id: int,
|
|
295
264
|
background_tasks: BackgroundTasks,
|
|
296
265
|
response: Response,
|
|
297
|
-
user: UserOAuth = Depends(
|
|
266
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
298
267
|
db: AsyncSession = Depends(get_async_db),
|
|
299
268
|
) -> TaskGroupActivityV2Read:
|
|
300
269
|
"""
|
|
301
270
|
Deletion of task-group from db and file system
|
|
302
271
|
"""
|
|
272
|
+
# Get validated resource and profile
|
|
273
|
+
resource, profile = await validate_user_profile(user=user, db=db)
|
|
303
274
|
|
|
304
275
|
task_group = await _get_task_group_full_access(
|
|
305
276
|
task_group_id=task_group_id,
|
|
@@ -321,32 +292,18 @@ async def delete_task_group(
|
|
|
321
292
|
db.add(task_group_activity)
|
|
322
293
|
await db.commit()
|
|
323
294
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
# Validate user settings (backend-specific)
|
|
327
|
-
user_settings = await validate_user_settings(
|
|
328
|
-
user=user, backend=settings.FRACTAL_RUNNER_BACKEND, db=db
|
|
329
|
-
)
|
|
330
|
-
# User appropriate FractalSSH object
|
|
331
|
-
ssh_config = SSHConfig(
|
|
332
|
-
user=user_settings.ssh_username,
|
|
333
|
-
host=user_settings.ssh_host,
|
|
334
|
-
key_path=user_settings.ssh_private_key_path,
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
background_tasks.add_task(
|
|
338
|
-
delete_ssh,
|
|
339
|
-
task_group_id=task_group.id,
|
|
340
|
-
task_group_activity_id=task_group_activity.id,
|
|
341
|
-
ssh_config=ssh_config,
|
|
342
|
-
tasks_base_dir=user_settings.ssh_tasks_dir,
|
|
343
|
-
)
|
|
295
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
296
|
+
delete_function = delete_ssh
|
|
344
297
|
else:
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
298
|
+
delete_function = delete_local
|
|
299
|
+
|
|
300
|
+
background_tasks.add_task(
|
|
301
|
+
delete_function,
|
|
302
|
+
task_group_id=task_group.id,
|
|
303
|
+
task_group_activity_id=task_group_activity.id,
|
|
304
|
+
resource=resource,
|
|
305
|
+
profile=profile,
|
|
306
|
+
)
|
|
350
307
|
|
|
351
308
|
response.status_code = status.HTTP_202_ACCEPTED
|
|
352
309
|
return task_group_activity
|
|
@@ -23,7 +23,7 @@ from ._aux_functions_tasks import _get_task_group_or_404
|
|
|
23
23
|
from ._aux_functions_tasks import _get_task_read_access
|
|
24
24
|
from fractal_server.app.models import UserOAuth
|
|
25
25
|
from fractal_server.app.models.v2 import TaskGroupV2
|
|
26
|
-
from fractal_server.app.routes.auth import
|
|
26
|
+
from fractal_server.app.routes.auth import current_user_act_ver_prof
|
|
27
27
|
from fractal_server.app.schemas.v2 import TaskType
|
|
28
28
|
from fractal_server.app.schemas.v2 import WorkflowTaskReadV2
|
|
29
29
|
from fractal_server.app.schemas.v2 import WorkflowTaskReplaceV2
|
|
@@ -75,7 +75,7 @@ class TaskVersionRead(BaseModel):
|
|
|
75
75
|
async def get_workflow_version_update_candidates(
|
|
76
76
|
project_id: int,
|
|
77
77
|
workflow_id: int,
|
|
78
|
-
user: UserOAuth = Depends(
|
|
78
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
79
79
|
db: AsyncSession = Depends(get_async_db),
|
|
80
80
|
) -> list[list[TaskVersionRead]]:
|
|
81
81
|
workflow = await _get_workflow_check_owner(
|
|
@@ -177,7 +177,7 @@ async def replace_workflowtask(
|
|
|
177
177
|
workflow_task_id: int,
|
|
178
178
|
task_id: int,
|
|
179
179
|
replace: WorkflowTaskReplaceV2,
|
|
180
|
-
user: UserOAuth = Depends(
|
|
180
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
181
181
|
db: AsyncSession = Depends(get_async_db),
|
|
182
182
|
) -> WorkflowTaskReadV2:
|
|
183
183
|
# Get objects from database
|
|
@@ -26,7 +26,7 @@ from ._aux_functions import _workflow_has_submitted_job
|
|
|
26
26
|
from ._aux_functions_tasks import _add_warnings_to_workflow_tasks
|
|
27
27
|
from fractal_server.app.models import UserOAuth
|
|
28
28
|
from fractal_server.app.models.v2 import TaskGroupV2
|
|
29
|
-
from fractal_server.app.routes.auth import
|
|
29
|
+
from fractal_server.app.routes.auth import current_user_act_ver_prof
|
|
30
30
|
from fractal_server.images.tools import merge_type_filters
|
|
31
31
|
|
|
32
32
|
router = APIRouter()
|
|
@@ -38,7 +38,7 @@ router = APIRouter()
|
|
|
38
38
|
)
|
|
39
39
|
async def get_workflow_list(
|
|
40
40
|
project_id: int,
|
|
41
|
-
user: UserOAuth = Depends(
|
|
41
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
42
42
|
db: AsyncSession = Depends(get_async_db),
|
|
43
43
|
) -> list[WorkflowReadV2] | None:
|
|
44
44
|
"""
|
|
@@ -65,7 +65,7 @@ async def get_workflow_list(
|
|
|
65
65
|
async def create_workflow(
|
|
66
66
|
project_id: int,
|
|
67
67
|
workflow: WorkflowCreateV2,
|
|
68
|
-
user: UserOAuth = Depends(
|
|
68
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
69
69
|
db: AsyncSession = Depends(get_async_db),
|
|
70
70
|
) -> WorkflowReadV2 | None:
|
|
71
71
|
"""
|
|
@@ -93,7 +93,7 @@ async def create_workflow(
|
|
|
93
93
|
async def read_workflow(
|
|
94
94
|
project_id: int,
|
|
95
95
|
workflow_id: int,
|
|
96
|
-
user: UserOAuth = Depends(
|
|
96
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
97
97
|
db: AsyncSession = Depends(get_async_db),
|
|
98
98
|
) -> WorkflowReadV2WithWarnings | None:
|
|
99
99
|
"""
|
|
@@ -127,7 +127,7 @@ async def update_workflow(
|
|
|
127
127
|
project_id: int,
|
|
128
128
|
workflow_id: int,
|
|
129
129
|
patch: WorkflowUpdateV2,
|
|
130
|
-
user: UserOAuth = Depends(
|
|
130
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
131
131
|
db: AsyncSession = Depends(get_async_db),
|
|
132
132
|
) -> WorkflowReadV2WithWarnings | None:
|
|
133
133
|
"""
|
|
@@ -201,7 +201,7 @@ async def update_workflow(
|
|
|
201
201
|
async def delete_workflow(
|
|
202
202
|
project_id: int,
|
|
203
203
|
workflow_id: int,
|
|
204
|
-
user: UserOAuth = Depends(
|
|
204
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
205
205
|
db: AsyncSession = Depends(get_async_db),
|
|
206
206
|
) -> Response:
|
|
207
207
|
"""
|
|
@@ -246,7 +246,7 @@ async def delete_workflow(
|
|
|
246
246
|
async def export_workflow(
|
|
247
247
|
project_id: int,
|
|
248
248
|
workflow_id: int,
|
|
249
|
-
user: UserOAuth = Depends(
|
|
249
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
250
250
|
db: AsyncSession = Depends(get_async_db),
|
|
251
251
|
) -> WorkflowExportV2 | None:
|
|
252
252
|
"""
|
|
@@ -277,7 +277,7 @@ async def export_workflow(
|
|
|
277
277
|
|
|
278
278
|
@router.get("/workflow/", response_model=list[WorkflowReadV2])
|
|
279
279
|
async def get_user_workflows(
|
|
280
|
-
user: UserOAuth = Depends(
|
|
280
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
281
281
|
db: AsyncSession = Depends(get_async_db),
|
|
282
282
|
) -> list[WorkflowReadV2]:
|
|
283
283
|
"""
|
|
@@ -303,7 +303,7 @@ class WorkflowTaskTypeFiltersInfo(BaseModel):
|
|
|
303
303
|
async def get_workflow_type_filters(
|
|
304
304
|
project_id: int,
|
|
305
305
|
workflow_id: int,
|
|
306
|
-
user: UserOAuth = Depends(
|
|
306
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
307
307
|
db: AsyncSession = Depends(get_async_db),
|
|
308
308
|
) -> list[WorkflowTaskTypeFiltersInfo]:
|
|
309
309
|
"""
|
|
@@ -15,6 +15,7 @@ from ....schemas.v2 import WorkflowReadV2WithWarnings
|
|
|
15
15
|
from ....schemas.v2 import WorkflowTaskCreateV2
|
|
16
16
|
from ._aux_functions import _check_workflow_exists
|
|
17
17
|
from ._aux_functions import _get_project_check_owner
|
|
18
|
+
from ._aux_functions import _get_user_resource_id
|
|
18
19
|
from ._aux_functions import _workflow_insert_task
|
|
19
20
|
from ._aux_functions_tasks import _add_warnings_to_workflow_tasks
|
|
20
21
|
from ._aux_functions_tasks import _check_type_filters_compatibility
|
|
@@ -24,8 +25,10 @@ from fractal_server.app.models.v2 import TaskGroupV2
|
|
|
24
25
|
from fractal_server.app.routes.api.v2._aux_task_group_disambiguation import (
|
|
25
26
|
_disambiguate_task_groups,
|
|
26
27
|
)
|
|
27
|
-
from fractal_server.app.routes.auth import
|
|
28
|
-
from fractal_server.app.routes.auth._aux_auth import
|
|
28
|
+
from fractal_server.app.routes.auth import current_user_act_ver_prof
|
|
29
|
+
from fractal_server.app.routes.auth._aux_auth import (
|
|
30
|
+
_get_default_usergroup_id_or_none,
|
|
31
|
+
)
|
|
29
32
|
from fractal_server.app.schemas.v2 import TaskImportV2
|
|
30
33
|
from fractal_server.logger import set_logger
|
|
31
34
|
|
|
@@ -38,20 +41,26 @@ logger = set_logger(__name__)
|
|
|
38
41
|
async def _get_user_accessible_taskgroups(
|
|
39
42
|
*,
|
|
40
43
|
user_id: int,
|
|
44
|
+
user_resource_id: int,
|
|
41
45
|
db: AsyncSession,
|
|
42
46
|
) -> list[TaskGroupV2]:
|
|
43
47
|
"""
|
|
44
48
|
Retrieve list of task groups that the user has access to.
|
|
45
49
|
"""
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
|
|
51
|
+
stm = (
|
|
52
|
+
select(TaskGroupV2)
|
|
53
|
+
.where(
|
|
54
|
+
or_(
|
|
55
|
+
TaskGroupV2.user_id == user_id,
|
|
56
|
+
TaskGroupV2.user_group_id.in_(
|
|
57
|
+
select(LinkUserGroup.group_id).where(
|
|
58
|
+
LinkUserGroup.user_id == user_id
|
|
59
|
+
)
|
|
60
|
+
),
|
|
61
|
+
)
|
|
54
62
|
)
|
|
63
|
+
.where(TaskGroupV2.resource_id == user_resource_id)
|
|
55
64
|
)
|
|
56
65
|
res = await db.execute(stm)
|
|
57
66
|
accessible_task_groups = res.scalars().all()
|
|
@@ -93,7 +102,7 @@ async def _get_task_by_taskimport(
|
|
|
93
102
|
task_import: TaskImportV2,
|
|
94
103
|
task_groups_list: list[TaskGroupV2],
|
|
95
104
|
user_id: int,
|
|
96
|
-
default_group_id: int,
|
|
105
|
+
default_group_id: int | None,
|
|
97
106
|
db: AsyncSession,
|
|
98
107
|
) -> int | None:
|
|
99
108
|
"""
|
|
@@ -149,7 +158,7 @@ async def _get_task_by_taskimport(
|
|
|
149
158
|
|
|
150
159
|
# Filter task groups by version
|
|
151
160
|
final_matching_task_groups = list(
|
|
152
|
-
filter(lambda tg: tg.version == version,
|
|
161
|
+
filter(lambda tg: tg.version == version, matching_task_groups)
|
|
153
162
|
)
|
|
154
163
|
|
|
155
164
|
if len(final_matching_task_groups) < 1:
|
|
@@ -167,10 +176,11 @@ async def _get_task_by_taskimport(
|
|
|
167
176
|
else:
|
|
168
177
|
logger.info(
|
|
169
178
|
"[_get_task_by_taskimport] "
|
|
170
|
-
"Found
|
|
179
|
+
f"Found {len(final_matching_task_groups)} task groups, "
|
|
180
|
+
"after filtering by version."
|
|
171
181
|
)
|
|
172
182
|
final_task_group = await _disambiguate_task_groups(
|
|
173
|
-
matching_task_groups=
|
|
183
|
+
matching_task_groups=final_matching_task_groups,
|
|
174
184
|
user_id=user_id,
|
|
175
185
|
db=db,
|
|
176
186
|
default_group_id=default_group_id,
|
|
@@ -204,13 +214,15 @@ async def _get_task_by_taskimport(
|
|
|
204
214
|
async def import_workflow(
|
|
205
215
|
project_id: int,
|
|
206
216
|
workflow_import: WorkflowImportV2,
|
|
207
|
-
user: UserOAuth = Depends(
|
|
217
|
+
user: UserOAuth = Depends(current_user_act_ver_prof),
|
|
208
218
|
db: AsyncSession = Depends(get_async_db),
|
|
209
219
|
) -> WorkflowReadV2WithWarnings:
|
|
210
220
|
"""
|
|
211
221
|
Import an existing workflow into a project and create required objects.
|
|
212
222
|
"""
|
|
213
223
|
|
|
224
|
+
user_resource_id = await _get_user_resource_id(user_id=user.id, db=db)
|
|
225
|
+
|
|
214
226
|
# Preliminary checks
|
|
215
227
|
await _get_project_check_owner(
|
|
216
228
|
project_id=project_id,
|
|
@@ -226,8 +238,9 @@ async def import_workflow(
|
|
|
226
238
|
task_group_list = await _get_user_accessible_taskgroups(
|
|
227
239
|
user_id=user.id,
|
|
228
240
|
db=db,
|
|
241
|
+
user_resource_id=user_resource_id,
|
|
229
242
|
)
|
|
230
|
-
default_group_id = await
|
|
243
|
+
default_group_id = await _get_default_usergroup_id_or_none(db)
|
|
231
244
|
|
|
232
245
|
list_wf_tasks = []
|
|
233
246
|
list_task_ids = []
|