fractal-server 2.16.5__py3-none-any.whl → 2.17.0a0__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 +129 -22
- fractal_server/app/db/__init__.py +9 -11
- fractal_server/app/models/security.py +7 -3
- fractal_server/app/models/user_settings.py +0 -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 +3 -0
- fractal_server/app/models/v2/resource.py +130 -0
- fractal_server/app/models/v2/task_group.py +3 -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/profile.py +86 -0
- fractal_server/app/routes/admin/v2/resource.py +229 -0
- fractal_server/app/routes/admin/v2/task_group_lifecycle.py +48 -82
- fractal_server/app/routes/api/__init__.py +26 -7
- fractal_server/app/routes/api/v2/_aux_functions.py +27 -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 +7 -7
- fractal_server/app/routes/api/v2/project.py +5 -1
- fractal_server/app/routes/api/v2/submit.py +32 -24
- fractal_server/app/routes/api/v2/task.py +5 -0
- fractal_server/app/routes/api/v2/task_collection.py +36 -47
- fractal_server/app/routes/api/v2/task_collection_custom.py +11 -5
- fractal_server/app/routes/api/v2/task_collection_pixi.py +34 -40
- fractal_server/app/routes/api/v2/task_group_lifecycle.py +39 -82
- fractal_server/app/routes/api/v2/workflow_import.py +4 -3
- fractal_server/app/routes/auth/_aux_auth.py +3 -3
- fractal_server/app/routes/auth/current_user.py +45 -7
- fractal_server/app/routes/auth/oauth.py +1 -1
- fractal_server/app/routes/auth/users.py +9 -0
- fractal_server/app/routes/aux/_runner.py +2 -1
- fractal_server/app/routes/aux/validate_user_profile.py +62 -0
- fractal_server/app/routes/aux/validate_user_settings.py +12 -9
- fractal_server/app/schemas/user.py +20 -13
- fractal_server/app/schemas/user_settings.py +0 -4
- fractal_server/app/schemas/v2/__init__.py +11 -0
- fractal_server/app/schemas/v2/profile.py +72 -0
- fractal_server/app/schemas/v2/resource.py +117 -0
- fractal_server/app/security/__init__.py +6 -13
- fractal_server/app/security/signup_email.py +2 -2
- fractal_server/app/user_settings.py +2 -12
- fractal_server/config/__init__.py +23 -0
- fractal_server/config/_database.py +58 -0
- fractal_server/config/_email.py +170 -0
- fractal_server/config/_init_data.py +27 -0
- fractal_server/config/_main.py +216 -0
- fractal_server/config/_settings_config.py +7 -0
- fractal_server/images/tools.py +3 -3
- fractal_server/logger.py +3 -3
- fractal_server/main.py +14 -21
- fractal_server/migrations/versions/90f6508c6379_drop_useroauth_username.py +36 -0
- fractal_server/migrations/versions/a80ac5a352bf_resource_profile.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 +128 -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 +59 -25
- fractal_server/runner/executors/slurm_common/get_slurm_config.py +38 -54
- fractal_server/runner/executors/slurm_common/remote.py +1 -1
- fractal_server/runner/executors/slurm_common/{_slurm_config.py → slurm_config.py} +3 -254
- 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 +40 -4
- fractal_server/runner/v2/_slurm_sudo.py +41 -11
- 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 +87 -108
- 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 +4 -0
- 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_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.0a0.dist-info}/METADATA +5 -5
- {fractal_server-2.16.5.dist-info → fractal_server-2.17.0a0.dist-info}/RECORD +112 -90
- {fractal_server-2.16.5.dist-info → fractal_server-2.17.0a0.dist-info}/WHEEL +1 -1
- fractal_server/config.py +0 -906
- /fractal_server/{runner → app}/shutdown.py +0 -0
- {fractal_server-2.16.5.dist-info → fractal_server-2.17.0a0.dist-info}/entry_points.txt +0 -0
- {fractal_server-2.16.5.dist-info → fractal_server-2.17.0a0.dist-info/licenses}/LICENSE +0 -0
|
@@ -10,6 +10,7 @@ from fastapi import Request
|
|
|
10
10
|
from fastapi import status
|
|
11
11
|
from sqlmodel import select
|
|
12
12
|
|
|
13
|
+
from ...aux.validate_user_profile import validate_user_profile
|
|
13
14
|
from ._aux_functions import _get_dataset_check_owner
|
|
14
15
|
from ._aux_functions import _get_workflow_check_owner
|
|
15
16
|
from ._aux_functions import clean_app_job_list_v2
|
|
@@ -29,6 +30,7 @@ from fractal_server.app.routes.aux.validate_user_settings import (
|
|
|
29
30
|
from fractal_server.app.schemas.v2 import JobCreateV2
|
|
30
31
|
from fractal_server.app.schemas.v2 import JobReadV2
|
|
31
32
|
from fractal_server.app.schemas.v2 import JobStatusTypeV2
|
|
33
|
+
from fractal_server.app.schemas.v2 import ResourceType
|
|
32
34
|
from fractal_server.config import get_settings
|
|
33
35
|
from fractal_server.logger import set_logger
|
|
34
36
|
from fractal_server.runner.set_start_and_last_task_index import (
|
|
@@ -122,10 +124,14 @@ async def apply_workflow(
|
|
|
122
124
|
)
|
|
123
125
|
used_task_group_ids.add(task.taskgroupv2_id)
|
|
124
126
|
|
|
127
|
+
# Get validated resource and profile
|
|
128
|
+
resource, profile = await validate_user_profile(
|
|
129
|
+
user=user,
|
|
130
|
+
db=db,
|
|
131
|
+
)
|
|
125
132
|
# Validate user settings
|
|
126
|
-
FRACTAL_RUNNER_BACKEND = settings.FRACTAL_RUNNER_BACKEND
|
|
127
133
|
user_settings = await validate_user_settings(
|
|
128
|
-
user=user, backend=
|
|
134
|
+
user=user, backend=resource.type, db=db
|
|
129
135
|
)
|
|
130
136
|
# Check that no other job with the same dataset_id is SUBMITTED
|
|
131
137
|
stm = (
|
|
@@ -157,11 +163,11 @@ async def apply_workflow(
|
|
|
157
163
|
job_create.slurm_account = user_settings.slurm_accounts[0]
|
|
158
164
|
|
|
159
165
|
# User appropriate FractalSSH object
|
|
160
|
-
if
|
|
166
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
161
167
|
ssh_config = dict(
|
|
162
|
-
user=
|
|
163
|
-
host=
|
|
164
|
-
key_path=
|
|
168
|
+
user=profile.username,
|
|
169
|
+
host=resource.host,
|
|
170
|
+
key_path=profile.ssh_key_path,
|
|
165
171
|
)
|
|
166
172
|
fractal_ssh_list = request.app.state.fractal_ssh_list
|
|
167
173
|
try:
|
|
@@ -193,7 +199,7 @@ async def apply_workflow(
|
|
|
193
199
|
workflow.model_dump_json(exclude={"task_list"})
|
|
194
200
|
),
|
|
195
201
|
project_dump=json.loads(
|
|
196
|
-
project.model_dump_json(exclude={"user_list"})
|
|
202
|
+
project.model_dump_json(exclude={"user_list", "resource_id"})
|
|
197
203
|
),
|
|
198
204
|
**job_create.model_dump(),
|
|
199
205
|
)
|
|
@@ -214,26 +220,28 @@ async def apply_workflow(
|
|
|
214
220
|
|
|
215
221
|
# Define server-side job directory
|
|
216
222
|
timestamp_string = job.start_timestamp.strftime("%Y%m%d_%H%M%S")
|
|
217
|
-
WORKFLOW_DIR_LOCAL =
|
|
223
|
+
WORKFLOW_DIR_LOCAL = Path(resource.jobs_local_dir) / (
|
|
218
224
|
f"proj_v2_{project_id:07d}_wf_{workflow_id:07d}_job_{job.id:07d}"
|
|
219
225
|
f"_{timestamp_string}"
|
|
220
226
|
)
|
|
221
227
|
|
|
222
|
-
cache_dir = (
|
|
223
|
-
Path(user_settings.project_dir) / ".fractal_cache"
|
|
224
|
-
if user_settings.project_dir is not None
|
|
225
|
-
else None
|
|
226
|
-
)
|
|
227
|
-
|
|
228
228
|
# Define user-side job directory
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
229
|
+
match resource.type:
|
|
230
|
+
case ResourceType.LOCAL:
|
|
231
|
+
WORKFLOW_DIR_REMOTE = WORKFLOW_DIR_LOCAL
|
|
232
|
+
cache_dir = None
|
|
233
|
+
case ResourceType.SLURM_SUDO:
|
|
234
|
+
cache_dir = (
|
|
235
|
+
Path(user_settings.project_dir) / ".fractal_cache"
|
|
236
|
+
if user_settings.project_dir is not None
|
|
237
|
+
else None
|
|
238
|
+
)
|
|
239
|
+
WORKFLOW_DIR_REMOTE = cache_dir / WORKFLOW_DIR_LOCAL.name
|
|
240
|
+
case ResourceType.SLURM_SSH:
|
|
241
|
+
WORKFLOW_DIR_REMOTE = (
|
|
242
|
+
Path(profile.jobs_remote_dir) / WORKFLOW_DIR_LOCAL.name
|
|
243
|
+
)
|
|
244
|
+
cache_dir = None
|
|
237
245
|
|
|
238
246
|
# Update job folders in the db
|
|
239
247
|
job.working_dir = WORKFLOW_DIR_LOCAL.as_posix()
|
|
@@ -250,11 +258,11 @@ async def apply_workflow(
|
|
|
250
258
|
dataset_id=dataset.id,
|
|
251
259
|
job_id=job.id,
|
|
252
260
|
user_id=user.id,
|
|
253
|
-
user_settings=user_settings,
|
|
254
261
|
worker_init=job.worker_init,
|
|
255
|
-
slurm_user=user_settings.slurm_user,
|
|
256
262
|
user_cache_dir=cache_dir.as_posix() if cache_dir else None,
|
|
257
263
|
fractal_ssh=fractal_ssh,
|
|
264
|
+
resource=resource,
|
|
265
|
+
profile=profile,
|
|
258
266
|
)
|
|
259
267
|
request.app.state.jobsV2.append(job.id)
|
|
260
268
|
logger.info(
|
|
@@ -9,6 +9,7 @@ from sqlmodel import func
|
|
|
9
9
|
from sqlmodel import or_
|
|
10
10
|
from sqlmodel import select
|
|
11
11
|
|
|
12
|
+
from ._aux_functions import _get_resource_and_profile_ids
|
|
12
13
|
from ._aux_functions_tasks import _get_task_full_access
|
|
13
14
|
from ._aux_functions_tasks import _get_task_read_access
|
|
14
15
|
from ._aux_functions_tasks import _get_valid_user_group_id
|
|
@@ -187,9 +188,13 @@ async def create_task(
|
|
|
187
188
|
user_group_id=user_group_id,
|
|
188
189
|
version=db_task.version,
|
|
189
190
|
)
|
|
191
|
+
resource_id, _ = await _get_resource_and_profile_ids(
|
|
192
|
+
user_id=user.id, db=db
|
|
193
|
+
)
|
|
190
194
|
db_task_group = TaskGroupV2(
|
|
191
195
|
user_id=user.id,
|
|
192
196
|
user_group_id=user_group_id,
|
|
197
|
+
resource_id=resource_id,
|
|
193
198
|
active=True,
|
|
194
199
|
task_list=[db_task],
|
|
195
200
|
origin=TaskGroupV2OriginEnum.OTHER,
|
|
@@ -14,10 +14,8 @@ from pydantic import BaseModel
|
|
|
14
14
|
from pydantic import model_validator
|
|
15
15
|
from pydantic import ValidationError
|
|
16
16
|
|
|
17
|
-
from .....config import get_settings
|
|
18
17
|
from .....logger import reset_logger_handlers
|
|
19
18
|
from .....logger import set_logger
|
|
20
|
-
from .....syringe import Inject
|
|
21
19
|
from ....db import AsyncSession
|
|
22
20
|
from ....db import get_async_db
|
|
23
21
|
from ....models.v2 import TaskGroupV2
|
|
@@ -26,7 +24,8 @@ from ....schemas.v2 import TaskCollectPipV2
|
|
|
26
24
|
from ....schemas.v2 import TaskGroupActivityStatusV2
|
|
27
25
|
from ....schemas.v2 import TaskGroupActivityV2Read
|
|
28
26
|
from ....schemas.v2 import TaskGroupCreateV2Strict
|
|
29
|
-
from ...aux.
|
|
27
|
+
from ...aux.validate_user_profile import validate_user_profile
|
|
28
|
+
from ._aux_functions import _get_resource_and_profile_ids
|
|
30
29
|
from ._aux_functions_task_lifecycle import get_package_version_from_pypi
|
|
31
30
|
from ._aux_functions_tasks import _get_valid_user_group_id
|
|
32
31
|
from ._aux_functions_tasks import _verify_non_duplication_group_constraint
|
|
@@ -35,11 +34,11 @@ from ._aux_functions_tasks import _verify_non_duplication_user_constraint
|
|
|
35
34
|
from fractal_server.app.models import UserOAuth
|
|
36
35
|
from fractal_server.app.models.v2 import TaskGroupActivityV2
|
|
37
36
|
from fractal_server.app.routes.auth import current_active_verified_user
|
|
37
|
+
from fractal_server.app.schemas.v2 import ResourceType
|
|
38
38
|
from fractal_server.app.schemas.v2 import (
|
|
39
39
|
TaskGroupActivityActionV2,
|
|
40
40
|
)
|
|
41
41
|
from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
|
|
42
|
-
from fractal_server.ssh._fabric import SSHConfig
|
|
43
42
|
from fractal_server.tasks.v2.local.collect import (
|
|
44
43
|
collect_local,
|
|
45
44
|
)
|
|
@@ -47,7 +46,7 @@ from fractal_server.tasks.v2.ssh import collect_ssh
|
|
|
47
46
|
from fractal_server.tasks.v2.utils_package_names import _parse_wheel_filename
|
|
48
47
|
from fractal_server.tasks.v2.utils_package_names import normalize_package_name
|
|
49
48
|
from fractal_server.tasks.v2.utils_python_interpreter import (
|
|
50
|
-
|
|
49
|
+
get_python_interpreter,
|
|
51
50
|
)
|
|
52
51
|
|
|
53
52
|
|
|
@@ -168,12 +167,19 @@ async def collect_tasks_pip(
|
|
|
168
167
|
"""
|
|
169
168
|
Task-collection endpoint
|
|
170
169
|
"""
|
|
171
|
-
# Get settings
|
|
172
|
-
settings = Inject(get_settings)
|
|
173
170
|
|
|
171
|
+
# Get validated resource and profile
|
|
172
|
+
resource, profile = await validate_user_profile(
|
|
173
|
+
user=user,
|
|
174
|
+
db=db,
|
|
175
|
+
)
|
|
174
176
|
# Get some validated request data
|
|
175
177
|
task_collect = request_data.task_collect
|
|
176
178
|
|
|
179
|
+
resource_id, _ = await _get_resource_and_profile_ids(
|
|
180
|
+
user_id=user.id, db=db
|
|
181
|
+
)
|
|
182
|
+
|
|
177
183
|
# Initialize task-group attributes
|
|
178
184
|
task_group_attrs = dict(
|
|
179
185
|
user_id=user.id,
|
|
@@ -182,14 +188,15 @@ async def collect_tasks_pip(
|
|
|
182
188
|
|
|
183
189
|
# Set/check python version
|
|
184
190
|
if task_collect.python_version is None:
|
|
185
|
-
task_group_attrs[
|
|
186
|
-
"
|
|
187
|
-
]
|
|
191
|
+
task_group_attrs["python_version"] = resource.tasks_python_config[
|
|
192
|
+
"default_version"
|
|
193
|
+
]
|
|
188
194
|
else:
|
|
189
195
|
task_group_attrs["python_version"] = task_collect.python_version
|
|
190
196
|
try:
|
|
191
|
-
|
|
192
|
-
python_version=task_group_attrs["python_version"]
|
|
197
|
+
get_python_interpreter(
|
|
198
|
+
python_version=task_group_attrs["python_version"],
|
|
199
|
+
resource=resource,
|
|
193
200
|
)
|
|
194
201
|
except ValueError:
|
|
195
202
|
raise HTTPException(
|
|
@@ -259,16 +266,11 @@ async def collect_tasks_pip(
|
|
|
259
266
|
# Set user_group_id
|
|
260
267
|
task_group_attrs["user_group_id"] = user_group_id
|
|
261
268
|
|
|
262
|
-
# Validate user settings (backend-specific)
|
|
263
|
-
user_settings = await validate_user_settings(
|
|
264
|
-
user=user, backend=settings.FRACTAL_RUNNER_BACKEND, db=db
|
|
265
|
-
)
|
|
266
|
-
|
|
267
269
|
# Set path and venv_path
|
|
268
|
-
if
|
|
269
|
-
base_tasks_path =
|
|
270
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
271
|
+
base_tasks_path = profile.tasks_remote_dir
|
|
270
272
|
else:
|
|
271
|
-
base_tasks_path =
|
|
273
|
+
base_tasks_path = resource.tasks_local_dir
|
|
272
274
|
task_group_path = (
|
|
273
275
|
Path(base_tasks_path)
|
|
274
276
|
/ str(user.id)
|
|
@@ -309,7 +311,7 @@ async def collect_tasks_pip(
|
|
|
309
311
|
|
|
310
312
|
# On-disk checks
|
|
311
313
|
|
|
312
|
-
if
|
|
314
|
+
if resource.type != ResourceType.SLURM_SSH:
|
|
313
315
|
# Verify that folder does not exist (for local collection)
|
|
314
316
|
if Path(task_group_path).exists():
|
|
315
317
|
raise HTTPException(
|
|
@@ -318,7 +320,7 @@ async def collect_tasks_pip(
|
|
|
318
320
|
)
|
|
319
321
|
|
|
320
322
|
# Create TaskGroupV2 object
|
|
321
|
-
task_group = TaskGroupV2(**task_group_attrs)
|
|
323
|
+
task_group = TaskGroupV2(**task_group_attrs, resource_id=resource_id)
|
|
322
324
|
db.add(task_group)
|
|
323
325
|
await db.commit()
|
|
324
326
|
await db.refresh(task_group)
|
|
@@ -340,33 +342,20 @@ async def collect_tasks_pip(
|
|
|
340
342
|
|
|
341
343
|
# END of SSH/non-SSH common part
|
|
342
344
|
|
|
343
|
-
if
|
|
344
|
-
|
|
345
|
-
# Use appropriate FractalSSH object
|
|
346
|
-
ssh_config = SSHConfig(
|
|
347
|
-
user=user_settings.ssh_username,
|
|
348
|
-
host=user_settings.ssh_host,
|
|
349
|
-
key_path=user_settings.ssh_private_key_path,
|
|
350
|
-
)
|
|
351
|
-
|
|
352
|
-
background_tasks.add_task(
|
|
353
|
-
collect_ssh,
|
|
354
|
-
task_group_id=task_group.id,
|
|
355
|
-
task_group_activity_id=task_group_activity.id,
|
|
356
|
-
ssh_config=ssh_config,
|
|
357
|
-
tasks_base_dir=user_settings.ssh_tasks_dir,
|
|
358
|
-
wheel_file=wheel_file,
|
|
359
|
-
)
|
|
360
|
-
|
|
345
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
346
|
+
collect_function = collect_ssh
|
|
361
347
|
else:
|
|
362
|
-
|
|
348
|
+
collect_function = collect_local
|
|
349
|
+
|
|
350
|
+
background_tasks.add_task(
|
|
351
|
+
collect_function,
|
|
352
|
+
task_group_id=task_group.id,
|
|
353
|
+
task_group_activity_id=task_group_activity.id,
|
|
354
|
+
wheel_file=wheel_file,
|
|
355
|
+
resource=resource,
|
|
356
|
+
profile=profile,
|
|
357
|
+
)
|
|
363
358
|
|
|
364
|
-
background_tasks.add_task(
|
|
365
|
-
collect_local,
|
|
366
|
-
task_group_id=task_group.id,
|
|
367
|
-
task_group_activity_id=task_group_activity.id,
|
|
368
|
-
wheel_file=wheel_file,
|
|
369
|
-
)
|
|
370
359
|
logger.debug(
|
|
371
360
|
"Task-collection endpoint: start background collection "
|
|
372
361
|
"and return task_group_activity"
|
|
@@ -9,6 +9,8 @@ from fastapi import HTTPException
|
|
|
9
9
|
from fastapi import status
|
|
10
10
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
11
11
|
|
|
12
|
+
from ...aux.validate_user_profile import validate_user_profile
|
|
13
|
+
from ._aux_functions import _get_resource_and_profile_ids
|
|
12
14
|
from ._aux_functions_tasks import _get_valid_user_group_id
|
|
13
15
|
from ._aux_functions_tasks import _verify_non_duplication_group_constraint
|
|
14
16
|
from ._aux_functions_tasks import _verify_non_duplication_user_constraint
|
|
@@ -16,15 +18,14 @@ from fractal_server.app.db import get_async_db
|
|
|
16
18
|
from fractal_server.app.models import UserOAuth
|
|
17
19
|
from fractal_server.app.models.v2 import TaskGroupV2
|
|
18
20
|
from fractal_server.app.routes.auth import current_active_verified_user
|
|
21
|
+
from fractal_server.app.schemas.v2 import ResourceType
|
|
19
22
|
from fractal_server.app.schemas.v2 import TaskCollectCustomV2
|
|
20
23
|
from fractal_server.app.schemas.v2 import TaskCreateV2
|
|
21
24
|
from fractal_server.app.schemas.v2 import TaskGroupCreateV2
|
|
22
25
|
from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
|
|
23
26
|
from fractal_server.app.schemas.v2 import TaskReadV2
|
|
24
|
-
from fractal_server.config import get_settings
|
|
25
27
|
from fractal_server.logger import set_logger
|
|
26
28
|
from fractal_server.string_tools import validate_cmd
|
|
27
|
-
from fractal_server.syringe import Inject
|
|
28
29
|
from fractal_server.tasks.v2.utils_background import (
|
|
29
30
|
prepare_tasks_metadata,
|
|
30
31
|
)
|
|
@@ -47,7 +48,8 @@ async def collect_task_custom(
|
|
|
47
48
|
user: UserOAuth = Depends(current_active_verified_user),
|
|
48
49
|
db: AsyncSession = Depends(get_async_db),
|
|
49
50
|
) -> list[TaskReadV2]:
|
|
50
|
-
|
|
51
|
+
# Get validated resource and profile
|
|
52
|
+
resource, profile = await validate_user_profile(user=user, db=db)
|
|
51
53
|
|
|
52
54
|
# Validate query parameters related to user-group ownership
|
|
53
55
|
user_group_id = await _get_valid_user_group_id(
|
|
@@ -57,7 +59,7 @@ async def collect_task_custom(
|
|
|
57
59
|
db=db,
|
|
58
60
|
)
|
|
59
61
|
|
|
60
|
-
if
|
|
62
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
61
63
|
if task_collect.package_root is None:
|
|
62
64
|
raise HTTPException(
|
|
63
65
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
@@ -143,6 +145,10 @@ async def collect_task_custom(
|
|
|
143
145
|
package_version=task_collect.version,
|
|
144
146
|
)
|
|
145
147
|
|
|
148
|
+
resource_id, _ = await _get_resource_and_profile_ids(
|
|
149
|
+
user_id=user.id, db=db
|
|
150
|
+
)
|
|
151
|
+
|
|
146
152
|
# Prepare task-group attributes
|
|
147
153
|
task_group_attrs = dict(
|
|
148
154
|
origin=TaskGroupV2OriginEnum.OTHER,
|
|
@@ -167,7 +173,7 @@ async def collect_task_custom(
|
|
|
167
173
|
db=db,
|
|
168
174
|
)
|
|
169
175
|
|
|
170
|
-
task_group = TaskGroupV2(**task_group_attrs)
|
|
176
|
+
task_group = TaskGroupV2(**task_group_attrs, resource_id=resource_id)
|
|
171
177
|
db.add(task_group)
|
|
172
178
|
await db.commit()
|
|
173
179
|
await db.refresh(task_group)
|
|
@@ -10,6 +10,7 @@ from fastapi import Response
|
|
|
10
10
|
from fastapi import status
|
|
11
11
|
from fastapi import UploadFile
|
|
12
12
|
|
|
13
|
+
from ._aux_functions import _get_resource_and_profile_ids
|
|
13
14
|
from fractal_server.app.db import AsyncSession
|
|
14
15
|
from fractal_server.app.db import get_async_db
|
|
15
16
|
from fractal_server.app.models import UserOAuth
|
|
@@ -28,18 +29,16 @@ from fractal_server.app.routes.api.v2._aux_functions_tasks import (
|
|
|
28
29
|
_verify_non_duplication_user_constraint,
|
|
29
30
|
)
|
|
30
31
|
from fractal_server.app.routes.auth import current_active_verified_user
|
|
31
|
-
from fractal_server.app.routes.aux.
|
|
32
|
-
|
|
32
|
+
from fractal_server.app.routes.aux.validate_user_profile import (
|
|
33
|
+
validate_user_profile,
|
|
33
34
|
)
|
|
34
35
|
from fractal_server.app.schemas.v2 import FractalUploadedFile
|
|
36
|
+
from fractal_server.app.schemas.v2 import ResourceType
|
|
35
37
|
from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
|
|
36
38
|
from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
|
|
37
39
|
from fractal_server.app.schemas.v2 import TaskGroupActivityV2Read
|
|
38
40
|
from fractal_server.app.schemas.v2.task_group import TaskGroupV2OriginEnum
|
|
39
|
-
from fractal_server.config import get_settings
|
|
40
41
|
from fractal_server.logger import set_logger
|
|
41
|
-
from fractal_server.ssh._fabric import SSHConfig
|
|
42
|
-
from fractal_server.syringe import Inject
|
|
43
42
|
from fractal_server.tasks.v2.local import collect_local_pixi
|
|
44
43
|
from fractal_server.tasks.v2.ssh import collect_ssh_pixi
|
|
45
44
|
from fractal_server.tasks.v2.utils_package_names import normalize_package_name
|
|
@@ -89,23 +88,26 @@ async def collect_task_pixi(
|
|
|
89
88
|
user: UserOAuth = Depends(current_active_verified_user),
|
|
90
89
|
db: AsyncSession = Depends(get_async_db),
|
|
91
90
|
) -> TaskGroupActivityV2Read:
|
|
92
|
-
|
|
91
|
+
# Get validated resource and profile
|
|
92
|
+
resource, profile = await validate_user_profile(user=user, db=db)
|
|
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,21 +125,22 @@ 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()
|
|
137
135
|
|
|
136
|
+
resource_id, _ = await _get_resource_and_profile_ids(
|
|
137
|
+
user_id=user.id, db=db
|
|
138
|
+
)
|
|
139
|
+
|
|
138
140
|
task_group_attrs = dict(
|
|
139
141
|
user_id=user.id,
|
|
140
142
|
user_group_id=user_group_id,
|
|
143
|
+
resource_id=resource_id,
|
|
141
144
|
origin=TaskGroupV2OriginEnum.PIXI,
|
|
142
145
|
pixi_version=pixi_version,
|
|
143
146
|
pkg_name=pkg_name,
|
|
@@ -162,7 +165,7 @@ async def collect_task_pixi(
|
|
|
162
165
|
db=db,
|
|
163
166
|
)
|
|
164
167
|
|
|
165
|
-
if
|
|
168
|
+
if resource.type != ResourceType.SLURM_SSH:
|
|
166
169
|
if Path(task_group_path).exists():
|
|
167
170
|
raise HTTPException(
|
|
168
171
|
status_code=status.HTTP_422_UNPROCESSABLE_CONTENT,
|
|
@@ -187,28 +190,19 @@ async def collect_task_pixi(
|
|
|
187
190
|
await db.commit()
|
|
188
191
|
await db.refresh(task_group_activity)
|
|
189
192
|
|
|
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
|
-
)
|
|
193
|
+
if resource.type == ResourceType.SLURM_SSH:
|
|
194
|
+
collect_function = collect_ssh_pixi
|
|
205
195
|
else:
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
196
|
+
collect_function = collect_local_pixi
|
|
197
|
+
|
|
198
|
+
background_tasks.add_task(
|
|
199
|
+
collect_function,
|
|
200
|
+
task_group_id=task_group.id,
|
|
201
|
+
task_group_activity_id=task_group_activity.id,
|
|
202
|
+
tar_gz_file=tar_gz_file,
|
|
203
|
+
resource=resource,
|
|
204
|
+
profile=profile,
|
|
205
|
+
)
|
|
212
206
|
logger.info(
|
|
213
207
|
"Task-collection endpoint: start background collection "
|
|
214
208
|
"and return task_group_activity. "
|