fractal-server 2.13.0__py3-none-any.whl → 2.14.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 +3 -1
- fractal_server/app/models/linkusergroup.py +6 -2
- fractal_server/app/models/v2/__init__.py +11 -1
- fractal_server/app/models/v2/accounting.py +35 -0
- fractal_server/app/models/v2/dataset.py +1 -11
- fractal_server/app/models/v2/history.py +78 -0
- fractal_server/app/models/v2/job.py +10 -3
- fractal_server/app/models/v2/task_group.py +2 -2
- fractal_server/app/models/v2/workflow.py +1 -1
- fractal_server/app/models/v2/workflowtask.py +1 -1
- fractal_server/app/routes/admin/v2/__init__.py +4 -0
- fractal_server/app/routes/admin/v2/accounting.py +98 -0
- fractal_server/app/routes/admin/v2/impersonate.py +35 -0
- fractal_server/app/routes/admin/v2/job.py +5 -13
- fractal_server/app/routes/admin/v2/task.py +1 -1
- fractal_server/app/routes/admin/v2/task_group.py +4 -29
- fractal_server/app/routes/api/__init__.py +1 -1
- fractal_server/app/routes/api/v2/__init__.py +8 -2
- fractal_server/app/routes/api/v2/_aux_functions.py +66 -0
- fractal_server/app/routes/api/v2/_aux_functions_history.py +166 -0
- fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +3 -3
- fractal_server/app/routes/api/v2/dataset.py +0 -17
- fractal_server/app/routes/api/v2/history.py +544 -0
- fractal_server/app/routes/api/v2/images.py +31 -43
- fractal_server/app/routes/api/v2/job.py +30 -0
- fractal_server/app/routes/api/v2/project.py +1 -53
- fractal_server/app/routes/api/v2/{status.py → status_legacy.py} +6 -6
- fractal_server/app/routes/api/v2/submit.py +17 -14
- fractal_server/app/routes/api/v2/task.py +3 -10
- fractal_server/app/routes/api/v2/task_collection_custom.py +4 -9
- fractal_server/app/routes/api/v2/task_group.py +2 -22
- fractal_server/app/routes/api/v2/verify_image_types.py +61 -0
- fractal_server/app/routes/api/v2/workflow.py +28 -69
- fractal_server/app/routes/api/v2/workflowtask.py +53 -50
- fractal_server/app/routes/auth/group.py +0 -16
- fractal_server/app/routes/auth/oauth.py +5 -3
- fractal_server/app/routes/aux/__init__.py +0 -20
- fractal_server/app/routes/pagination.py +47 -0
- fractal_server/app/runner/components.py +0 -3
- fractal_server/app/runner/compress_folder.py +57 -29
- fractal_server/app/runner/exceptions.py +4 -0
- fractal_server/app/runner/executors/base_runner.py +157 -0
- fractal_server/app/runner/{v2/_local/_local_config.py → executors/local/get_local_config.py} +7 -9
- fractal_server/app/runner/executors/local/runner.py +248 -0
- fractal_server/app/runner/executors/{slurm → slurm_common}/_batching.py +1 -1
- fractal_server/app/runner/executors/{slurm → slurm_common}/_slurm_config.py +9 -7
- fractal_server/app/runner/executors/slurm_common/base_slurm_runner.py +868 -0
- fractal_server/app/runner/{v2/_slurm_common → executors/slurm_common}/get_slurm_config.py +48 -17
- fractal_server/app/runner/executors/{slurm → slurm_common}/remote.py +36 -47
- fractal_server/app/runner/executors/slurm_common/slurm_job_task_models.py +134 -0
- fractal_server/app/runner/executors/slurm_ssh/runner.py +268 -0
- fractal_server/app/runner/executors/slurm_sudo/__init__.py +0 -0
- fractal_server/app/runner/executors/{slurm/sudo → slurm_sudo}/_subprocess_run_as_user.py +2 -83
- fractal_server/app/runner/executors/slurm_sudo/runner.py +193 -0
- fractal_server/app/runner/extract_archive.py +1 -3
- fractal_server/app/runner/task_files.py +134 -87
- fractal_server/app/runner/v2/__init__.py +0 -395
- fractal_server/app/runner/v2/_local.py +88 -0
- fractal_server/app/runner/v2/{_slurm_ssh/__init__.py → _slurm_ssh.py} +22 -19
- fractal_server/app/runner/v2/{_slurm_sudo/__init__.py → _slurm_sudo.py} +19 -15
- fractal_server/app/runner/v2/db_tools.py +119 -0
- fractal_server/app/runner/v2/runner.py +219 -98
- fractal_server/app/runner/v2/runner_functions.py +491 -189
- fractal_server/app/runner/v2/runner_functions_low_level.py +40 -43
- fractal_server/app/runner/v2/submit_workflow.py +358 -0
- fractal_server/app/runner/v2/task_interface.py +31 -0
- fractal_server/app/schemas/_validators.py +13 -24
- fractal_server/app/schemas/user.py +10 -7
- fractal_server/app/schemas/user_settings.py +9 -21
- fractal_server/app/schemas/v2/__init__.py +10 -1
- fractal_server/app/schemas/v2/accounting.py +18 -0
- fractal_server/app/schemas/v2/dataset.py +12 -94
- fractal_server/app/schemas/v2/dumps.py +26 -9
- fractal_server/app/schemas/v2/history.py +80 -0
- fractal_server/app/schemas/v2/job.py +15 -8
- fractal_server/app/schemas/v2/manifest.py +14 -7
- fractal_server/app/schemas/v2/project.py +9 -7
- fractal_server/app/schemas/v2/status_legacy.py +35 -0
- fractal_server/app/schemas/v2/task.py +72 -77
- fractal_server/app/schemas/v2/task_collection.py +14 -32
- fractal_server/app/schemas/v2/task_group.py +10 -9
- fractal_server/app/schemas/v2/workflow.py +10 -11
- fractal_server/app/schemas/v2/workflowtask.py +2 -21
- fractal_server/app/security/__init__.py +3 -3
- fractal_server/app/security/signup_email.py +2 -2
- fractal_server/config.py +91 -90
- fractal_server/images/tools.py +23 -0
- fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py +50 -0
- fractal_server/migrations/versions/9db60297b8b2_set_ondelete.py +250 -0
- fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +57 -0
- fractal_server/migrations/versions/c90a7c76e996_job_id_in_history_run.py +41 -0
- fractal_server/migrations/versions/e81103413827_add_job_type_filters.py +36 -0
- fractal_server/migrations/versions/f37aceb45062_make_historyunit_logfile_required.py +39 -0
- fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +120 -0
- fractal_server/ssh/_fabric.py +28 -14
- fractal_server/tasks/v2/local/collect.py +2 -2
- fractal_server/tasks/v2/ssh/collect.py +2 -2
- fractal_server/tasks/v2/templates/2_pip_install.sh +1 -1
- fractal_server/tasks/v2/templates/4_pip_show.sh +1 -1
- fractal_server/tasks/v2/utils_background.py +1 -20
- fractal_server/tasks/v2/utils_database.py +30 -17
- fractal_server/tasks/v2/utils_templates.py +6 -0
- {fractal_server-2.13.0.dist-info → fractal_server-2.14.0.dist-info}/METADATA +4 -4
- {fractal_server-2.13.0.dist-info → fractal_server-2.14.0.dist-info}/RECORD +114 -99
- {fractal_server-2.13.0.dist-info → fractal_server-2.14.0.dist-info}/WHEEL +1 -1
- fractal_server/app/runner/executors/slurm/ssh/_executor_wait_thread.py +0 -126
- fractal_server/app/runner/executors/slurm/ssh/_slurm_job.py +0 -116
- fractal_server/app/runner/executors/slurm/ssh/executor.py +0 -1386
- fractal_server/app/runner/executors/slurm/sudo/_check_jobs_status.py +0 -71
- fractal_server/app/runner/executors/slurm/sudo/_executor_wait_thread.py +0 -130
- fractal_server/app/runner/executors/slurm/sudo/executor.py +0 -1281
- fractal_server/app/runner/v2/_local/__init__.py +0 -129
- fractal_server/app/runner/v2/_local/_submit_setup.py +0 -52
- fractal_server/app/runner/v2/_local/executor.py +0 -100
- fractal_server/app/runner/v2/_slurm_ssh/_submit_setup.py +0 -83
- fractal_server/app/runner/v2/_slurm_sudo/_submit_setup.py +0 -83
- fractal_server/app/runner/v2/handle_failed_job.py +0 -59
- fractal_server/app/schemas/v2/status.py +0 -16
- /fractal_server/app/{runner/executors/slurm → history}/__init__.py +0 -0
- /fractal_server/app/runner/executors/{slurm/ssh → local}/__init__.py +0 -0
- /fractal_server/app/runner/executors/{slurm/sudo → slurm_common}/__init__.py +0 -0
- /fractal_server/app/runner/executors/{_job_states.py → slurm_common/_job_states.py} +0 -0
- /fractal_server/app/runner/executors/{slurm → slurm_common}/utils_executors.py +0 -0
- /fractal_server/app/runner/{v2/_slurm_common → executors/slurm_ssh}/__init__.py +0 -0
- {fractal_server-2.13.0.dist-info → fractal_server-2.14.0.dist-info}/LICENSE +0 -0
- {fractal_server-2.13.0.dist-info → fractal_server-2.14.0.dist-info}/entry_points.txt +0 -0
@@ -1,71 +0,0 @@
|
|
1
|
-
from subprocess import run # nosec
|
2
|
-
|
3
|
-
from ......logger import set_logger
|
4
|
-
from ..._job_states import STATES_FINISHED
|
5
|
-
|
6
|
-
|
7
|
-
logger = set_logger(__name__)
|
8
|
-
|
9
|
-
|
10
|
-
def run_squeue(job_ids):
|
11
|
-
res = run( # nosec
|
12
|
-
[
|
13
|
-
"squeue",
|
14
|
-
"--noheader",
|
15
|
-
"--format=%i %T",
|
16
|
-
"--jobs",
|
17
|
-
",".join([str(j) for j in job_ids]),
|
18
|
-
"--states=all",
|
19
|
-
],
|
20
|
-
capture_output=True,
|
21
|
-
encoding="utf-8",
|
22
|
-
check=False,
|
23
|
-
)
|
24
|
-
if res.returncode != 0:
|
25
|
-
logger.warning(
|
26
|
-
f"squeue command with {job_ids}"
|
27
|
-
f" failed with:\n{res.stderr=}\n{res.stdout=}"
|
28
|
-
)
|
29
|
-
|
30
|
-
return res
|
31
|
-
|
32
|
-
|
33
|
-
def _jobs_finished(job_ids) -> set[str]:
|
34
|
-
"""
|
35
|
-
Check which ones of the given Slurm jobs already finished
|
36
|
-
|
37
|
-
The function is based on the `_jobs_finished` function from
|
38
|
-
clusterfutures (version 0.5).
|
39
|
-
Original Copyright: 2022 Adrian Sampson
|
40
|
-
(released under the MIT licence)
|
41
|
-
"""
|
42
|
-
|
43
|
-
# If there is no Slurm job to check, return right away
|
44
|
-
if not job_ids:
|
45
|
-
return set()
|
46
|
-
id_to_state = dict()
|
47
|
-
|
48
|
-
res = run_squeue(job_ids)
|
49
|
-
if res.returncode == 0:
|
50
|
-
id_to_state = {
|
51
|
-
out.split()[0]: out.split()[1] for out in res.stdout.splitlines()
|
52
|
-
}
|
53
|
-
else:
|
54
|
-
id_to_state = dict()
|
55
|
-
for j in job_ids:
|
56
|
-
res = run_squeue([j])
|
57
|
-
if res.returncode != 0:
|
58
|
-
logger.info(f"Job {j} not found. Marked it as completed")
|
59
|
-
id_to_state.update({str(j): "COMPLETED"})
|
60
|
-
else:
|
61
|
-
id_to_state.update(
|
62
|
-
{res.stdout.split()[0]: res.stdout.split()[1]}
|
63
|
-
)
|
64
|
-
|
65
|
-
# Finished jobs only stay in squeue for a few mins (configurable). If
|
66
|
-
# a job ID isn't there, we'll assume it's finished.
|
67
|
-
return {
|
68
|
-
j
|
69
|
-
for j in job_ids
|
70
|
-
if id_to_state.get(j, "COMPLETED") in STATES_FINISHED
|
71
|
-
}
|
@@ -1,130 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import threading
|
3
|
-
import time
|
4
|
-
import traceback
|
5
|
-
from itertools import count
|
6
|
-
from typing import Optional
|
7
|
-
|
8
|
-
from ......logger import set_logger
|
9
|
-
from ._check_jobs_status import _jobs_finished
|
10
|
-
from fractal_server.app.runner.exceptions import JobExecutionError
|
11
|
-
|
12
|
-
logger = set_logger(__name__)
|
13
|
-
|
14
|
-
|
15
|
-
class FractalSlurmSudoWaitThread(threading.Thread):
|
16
|
-
"""
|
17
|
-
Thread that monitors a pool of SLURM jobs
|
18
|
-
|
19
|
-
This class is a custom re-implementation of the waiting thread class from:
|
20
|
-
|
21
|
-
> clusterfutures <https://github.com/sampsyo/clusterfutures>
|
22
|
-
> Original Copyright
|
23
|
-
> Copyright 2021 Adrian Sampson <asampson@cs.washington.edu>
|
24
|
-
> License: MIT
|
25
|
-
|
26
|
-
Attributes:
|
27
|
-
slurm_user:
|
28
|
-
shutdown_file:
|
29
|
-
shutdown_callback:
|
30
|
-
slurm_poll_interval:
|
31
|
-
waiting:
|
32
|
-
shutdown:
|
33
|
-
lock:
|
34
|
-
"""
|
35
|
-
|
36
|
-
slurm_user: str
|
37
|
-
shutdown_file: Optional[str] = None
|
38
|
-
shutdown_callback: callable
|
39
|
-
slurm_poll_interval: int = 30
|
40
|
-
waiting: dict[tuple[str, ...], str]
|
41
|
-
shutdown: bool
|
42
|
-
_lock: threading.Lock
|
43
|
-
|
44
|
-
def __init__(self, callback: callable, interval=1):
|
45
|
-
threading.Thread.__init__(self, daemon=True)
|
46
|
-
self.callback = callback
|
47
|
-
self.interval = interval
|
48
|
-
self.waiting = {}
|
49
|
-
self._lock = threading.Lock() # To protect the .waiting dict
|
50
|
-
self.shutdown = False
|
51
|
-
self.active_job_ids = []
|
52
|
-
|
53
|
-
def wait(
|
54
|
-
self,
|
55
|
-
*,
|
56
|
-
filenames: tuple[str, ...],
|
57
|
-
jobid: str,
|
58
|
-
):
|
59
|
-
"""
|
60
|
-
Add a a new job to the set of jobs being waited for.
|
61
|
-
|
62
|
-
A job consists of a tuple of filenames and a callback value (i.e. a
|
63
|
-
SLURM job ID).
|
64
|
-
|
65
|
-
Note that (with respect to clusterfutures) we replaced `filename` with
|
66
|
-
`filenames`.
|
67
|
-
"""
|
68
|
-
if self.shutdown:
|
69
|
-
error_msg = "Cannot call `wait` method after executor shutdown."
|
70
|
-
logger.warning(error_msg)
|
71
|
-
raise JobExecutionError(info=error_msg)
|
72
|
-
with self._lock:
|
73
|
-
self.waiting[filenames] = jobid
|
74
|
-
|
75
|
-
def check_shutdown(self, i):
|
76
|
-
"""
|
77
|
-
Do one shutdown-file-existence check.
|
78
|
-
|
79
|
-
Note: the `i` parameter allows subclasses like `SlurmWaitThread` to do
|
80
|
-
something on every Nth check.
|
81
|
-
|
82
|
-
Changed from clusterfutures:
|
83
|
-
* Do not check for output-pickle-file existence (we rather rely on
|
84
|
-
`cfut.slurm.jobs_finished`);
|
85
|
-
* Check for the existence of shutdown-file.
|
86
|
-
"""
|
87
|
-
if self.shutdown_file and os.path.exists(self.shutdown_file):
|
88
|
-
logger.info(
|
89
|
-
f"Detected executor-shutdown file {str(self.shutdown_file)}"
|
90
|
-
)
|
91
|
-
self.shutdown = True
|
92
|
-
|
93
|
-
def run(self):
|
94
|
-
"""
|
95
|
-
Overrides the original clusterfutures.FileWaitThread.run, adding a call
|
96
|
-
to self.shutdown_callback.
|
97
|
-
|
98
|
-
Changed from clusterfutures:
|
99
|
-
* We do not rely on output-file-existence checks to verify whether a
|
100
|
-
job is complete.
|
101
|
-
|
102
|
-
Note that `shutdown_callback` only takes care of cleaning up the
|
103
|
-
FractalSlurmExecutor variables, and then the `return` here is enough to
|
104
|
-
fully clean up the `FractalFileWaitThread` object.
|
105
|
-
"""
|
106
|
-
for i in count():
|
107
|
-
if self.shutdown:
|
108
|
-
self.shutdown_callback()
|
109
|
-
return
|
110
|
-
with self._lock:
|
111
|
-
self.check(i)
|
112
|
-
time.sleep(self.interval)
|
113
|
-
|
114
|
-
def check(self, i):
|
115
|
-
self.check_shutdown(i)
|
116
|
-
if i % (self.slurm_poll_interval // self.interval) == 0:
|
117
|
-
try:
|
118
|
-
finished_jobs = _jobs_finished(self.waiting.values())
|
119
|
-
except Exception:
|
120
|
-
# Don't abandon completion checking if jobs_finished errors
|
121
|
-
traceback.print_exc()
|
122
|
-
return
|
123
|
-
|
124
|
-
if not finished_jobs:
|
125
|
-
return
|
126
|
-
|
127
|
-
id_to_filenames = {v: k for (k, v) in self.waiting.items()}
|
128
|
-
for finished_id in finished_jobs:
|
129
|
-
self.callback(finished_id)
|
130
|
-
self.waiting.pop(id_to_filenames[finished_id])
|