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
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
from sqlmodel import select
|
|
4
|
-
|
|
5
|
-
from fractal_server.app.db import get_sync_db
|
|
6
|
-
from fractal_server.app.models import HistoryRun
|
|
7
|
-
from fractal_server.app.models import TaskV2
|
|
8
|
-
from fractal_server.app.models import WorkflowTaskV2
|
|
9
|
-
|
|
10
|
-
logger = logging.getLogger("fix_db")
|
|
11
|
-
logger.setLevel(logging.INFO)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def fix_db():
|
|
15
|
-
logger.info("START execution of fix_db function")
|
|
16
|
-
|
|
17
|
-
with next(get_sync_db()) as db:
|
|
18
|
-
stm = select(HistoryRun).order_by(HistoryRun.id)
|
|
19
|
-
history_runs = db.execute(stm).scalars().all()
|
|
20
|
-
|
|
21
|
-
for hr in history_runs:
|
|
22
|
-
logger.info(f"HistoryRun[{hr.id}] START")
|
|
23
|
-
if hr.workflowtask_id is None:
|
|
24
|
-
continue
|
|
25
|
-
wft = db.get(WorkflowTaskV2, hr.workflowtask_id)
|
|
26
|
-
if wft is None:
|
|
27
|
-
logger.warning(
|
|
28
|
-
f"WorkflowTaskV2[{hr.workflowtask_id}] not found. "
|
|
29
|
-
"Trying to use HistoryRun.workflowtask_dump"
|
|
30
|
-
)
|
|
31
|
-
task_id = hr.workflowtask_dump.get("task_id")
|
|
32
|
-
if task_id is not None and db.get(TaskV2, task_id) is not None:
|
|
33
|
-
hr.task_id = task_id
|
|
34
|
-
else:
|
|
35
|
-
logger.warning(f"TaskV2[{task_id}] not found")
|
|
36
|
-
else:
|
|
37
|
-
hr.task_id = wft.task_id
|
|
38
|
-
logger.info(
|
|
39
|
-
f"HistoryRun[{hr.id}].task_id set to {wft.task_id}"
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
db.add(hr)
|
|
43
|
-
logger.info(f"HistoryRun[{hr.id}] END")
|
|
44
|
-
|
|
45
|
-
db.commit()
|
|
46
|
-
logger.info("Changes committed.")
|
|
47
|
-
|
|
48
|
-
logger.info("END execution of fix_db function")
|
|
@@ -1,471 +0,0 @@
|
|
|
1
|
-
# Copyright 2022 (C) Friedrich Miescher Institute for Biomedical Research and
|
|
2
|
-
# University of Zurich
|
|
3
|
-
#
|
|
4
|
-
# Original authors:
|
|
5
|
-
# Jacopo Nespolo <jacopo.nespolo@exact-lab.it>
|
|
6
|
-
# Tommaso Comparin <tommaso.comparin@exact-lab.it>
|
|
7
|
-
#
|
|
8
|
-
# This file is part of Fractal and was originally developed by eXact lab S.r.l.
|
|
9
|
-
# <exact-lab.it> under contract with Liberali Lab from the Friedrich Miescher
|
|
10
|
-
# Institute for Biomedical Research and Pelkmans Lab from the University of
|
|
11
|
-
# Zurich.
|
|
12
|
-
"""
|
|
13
|
-
Submodule to handle the SLURM configuration for a WorkflowTask
|
|
14
|
-
"""
|
|
15
|
-
import json
|
|
16
|
-
from pathlib import Path
|
|
17
|
-
|
|
18
|
-
from pydantic import BaseModel
|
|
19
|
-
from pydantic import ConfigDict
|
|
20
|
-
from pydantic import Field
|
|
21
|
-
from pydantic import ValidationError
|
|
22
|
-
|
|
23
|
-
from fractal_server.config import get_settings
|
|
24
|
-
from fractal_server.logger import set_logger
|
|
25
|
-
from fractal_server.syringe import Inject
|
|
26
|
-
|
|
27
|
-
logger = set_logger(__name__)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class SlurmConfigError(ValueError):
|
|
31
|
-
"""
|
|
32
|
-
Slurm configuration error
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
pass
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class _SlurmConfigSet(BaseModel):
|
|
39
|
-
"""
|
|
40
|
-
Options that can be set in `FRACTAL_SLURM_CONFIG_FILE` for the default/gpu
|
|
41
|
-
SLURM config. Only used as part of `SlurmConfigFile`.
|
|
42
|
-
|
|
43
|
-
Attributes:
|
|
44
|
-
partition:
|
|
45
|
-
cpus_per_task:
|
|
46
|
-
mem:
|
|
47
|
-
See `_parse_mem_value` for details on allowed values.
|
|
48
|
-
constraint:
|
|
49
|
-
gres:
|
|
50
|
-
time:
|
|
51
|
-
exclude:
|
|
52
|
-
nodelist:
|
|
53
|
-
account:
|
|
54
|
-
extra_lines:
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
model_config = ConfigDict(extra="forbid")
|
|
58
|
-
|
|
59
|
-
partition: str | None = None
|
|
60
|
-
cpus_per_task: int | None = None
|
|
61
|
-
mem: int | str | None = None
|
|
62
|
-
constraint: str | None = None
|
|
63
|
-
gres: str | None = None
|
|
64
|
-
exclude: str | None = None
|
|
65
|
-
nodelist: str | None = None
|
|
66
|
-
time: str | None = None
|
|
67
|
-
account: str | None = None
|
|
68
|
-
extra_lines: list[str] | None = None
|
|
69
|
-
gpus: str | None = None
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
class _BatchingConfigSet(BaseModel):
|
|
73
|
-
"""
|
|
74
|
-
Options that can be set in `FRACTAL_SLURM_CONFIG_FILE` to configure the
|
|
75
|
-
batching strategy (that is, how to combine several tasks in a single SLURM
|
|
76
|
-
job). Only used as part of `SlurmConfigFile`.
|
|
77
|
-
|
|
78
|
-
Attributes:
|
|
79
|
-
target_cpus_per_job:
|
|
80
|
-
max_cpus_per_job:
|
|
81
|
-
target_mem_per_job:
|
|
82
|
-
(see `_parse_mem_value` for details on allowed values)
|
|
83
|
-
max_mem_per_job:
|
|
84
|
-
(see `_parse_mem_value` for details on allowed values)
|
|
85
|
-
target_num_jobs:
|
|
86
|
-
max_num_jobs:
|
|
87
|
-
"""
|
|
88
|
-
|
|
89
|
-
model_config = ConfigDict(extra="forbid")
|
|
90
|
-
|
|
91
|
-
target_cpus_per_job: int
|
|
92
|
-
max_cpus_per_job: int
|
|
93
|
-
target_mem_per_job: int | str
|
|
94
|
-
max_mem_per_job: int | str
|
|
95
|
-
target_num_jobs: int
|
|
96
|
-
max_num_jobs: int
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
class SlurmConfigFile(BaseModel):
|
|
100
|
-
"""
|
|
101
|
-
Specifications for the content of `FRACTAL_SLURM_CONFIG_FILE`
|
|
102
|
-
|
|
103
|
-
This must be a JSON file, and a valid example is
|
|
104
|
-
```JSON
|
|
105
|
-
{
|
|
106
|
-
"default_slurm_config": {
|
|
107
|
-
"partition": "main",
|
|
108
|
-
"cpus_per_task": 1
|
|
109
|
-
},
|
|
110
|
-
"gpu_slurm_config": {
|
|
111
|
-
"partition": "gpu",
|
|
112
|
-
"extra_lines": ["#SBATCH --gres=gpu:v100:1"]
|
|
113
|
-
},
|
|
114
|
-
"batching_config": {
|
|
115
|
-
"target_cpus_per_job": 1,
|
|
116
|
-
"max_cpus_per_job": 1,
|
|
117
|
-
"target_mem_per_job": 200,
|
|
118
|
-
"max_mem_per_job": 500,
|
|
119
|
-
"target_num_jobs": 2,
|
|
120
|
-
"max_num_jobs": 4
|
|
121
|
-
},
|
|
122
|
-
"user_local_exports": {
|
|
123
|
-
"CELLPOSE_LOCAL_MODELS_PATH": "CELLPOSE_LOCAL_MODELS_PATH",
|
|
124
|
-
"NAPARI_CONFIG": "napari_config.json"
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
See `_SlurmConfigSet` and `_BatchingConfigSet` for more details.
|
|
130
|
-
|
|
131
|
-
Attributes:
|
|
132
|
-
default_slurm_config:
|
|
133
|
-
Common default options for all tasks.
|
|
134
|
-
gpu_slurm_config:
|
|
135
|
-
Default configuration for all GPU tasks.
|
|
136
|
-
batching_config:
|
|
137
|
-
Configuration of the batching strategy.
|
|
138
|
-
user_local_exports:
|
|
139
|
-
Key-value pairs to be included as `export`-ed variables in SLURM
|
|
140
|
-
submission script, after prepending values with the user's cache
|
|
141
|
-
directory.
|
|
142
|
-
"""
|
|
143
|
-
|
|
144
|
-
model_config = ConfigDict(extra="forbid")
|
|
145
|
-
|
|
146
|
-
default_slurm_config: _SlurmConfigSet
|
|
147
|
-
gpu_slurm_config: _SlurmConfigSet | None = None
|
|
148
|
-
batching_config: _BatchingConfigSet
|
|
149
|
-
user_local_exports: dict[str, str] | None = None
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def load_slurm_config_file(
|
|
153
|
-
config_path: Path | None = None,
|
|
154
|
-
) -> SlurmConfigFile:
|
|
155
|
-
"""
|
|
156
|
-
Load a SLURM configuration file and validate its content with
|
|
157
|
-
`SlurmConfigFile`.
|
|
158
|
-
|
|
159
|
-
Arguments:
|
|
160
|
-
config_path:
|
|
161
|
-
"""
|
|
162
|
-
|
|
163
|
-
if not config_path:
|
|
164
|
-
settings = Inject(get_settings)
|
|
165
|
-
config_path = settings.FRACTAL_SLURM_CONFIG_FILE
|
|
166
|
-
|
|
167
|
-
# Load file
|
|
168
|
-
logger.debug(f"[get_slurm_config] Now loading {config_path=}")
|
|
169
|
-
try:
|
|
170
|
-
with config_path.open("r") as f:
|
|
171
|
-
slurm_env = json.load(f)
|
|
172
|
-
except Exception as e:
|
|
173
|
-
raise SlurmConfigError(
|
|
174
|
-
f"Error while loading {config_path=}. "
|
|
175
|
-
f"Original error:\n{str(e)}"
|
|
176
|
-
)
|
|
177
|
-
|
|
178
|
-
# Validate file content
|
|
179
|
-
logger.debug(f"[load_slurm_config_file] Now validating {config_path=}")
|
|
180
|
-
logger.debug(f"[load_slurm_config_file] {slurm_env=}")
|
|
181
|
-
try:
|
|
182
|
-
obj = SlurmConfigFile(**slurm_env)
|
|
183
|
-
except ValidationError as e:
|
|
184
|
-
raise SlurmConfigError(
|
|
185
|
-
f"Error while loading {config_path=}. "
|
|
186
|
-
f"Original error:\n{str(e)}"
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
# Convert memory to MB units, in all relevant attributes
|
|
190
|
-
if obj.default_slurm_config.mem:
|
|
191
|
-
obj.default_slurm_config.mem = _parse_mem_value(
|
|
192
|
-
obj.default_slurm_config.mem
|
|
193
|
-
)
|
|
194
|
-
if obj.gpu_slurm_config and obj.gpu_slurm_config.mem:
|
|
195
|
-
obj.gpu_slurm_config.mem = _parse_mem_value(obj.gpu_slurm_config.mem)
|
|
196
|
-
obj.batching_config.target_mem_per_job = _parse_mem_value(
|
|
197
|
-
obj.batching_config.target_mem_per_job
|
|
198
|
-
)
|
|
199
|
-
obj.batching_config.max_mem_per_job = _parse_mem_value(
|
|
200
|
-
obj.batching_config.max_mem_per_job
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
return obj
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
class SlurmConfig(BaseModel):
|
|
207
|
-
"""
|
|
208
|
-
Abstraction for SLURM parameters
|
|
209
|
-
|
|
210
|
-
**NOTE**: `SlurmConfig` objects are created internally in `fractal-server`,
|
|
211
|
-
and they are not meant to be initialized by the user; the same holds for
|
|
212
|
-
`SlurmConfig` attributes (e.g. `mem_per_task_MB`), which are not meant to
|
|
213
|
-
be part of the `FRACTAL_SLURM_CONFIG_FILE` JSON file (details on the
|
|
214
|
-
expected file content are defined in
|
|
215
|
-
[`SlurmConfigFile`](#fractal_server.runner._slurm._slurm_config.SlurmConfigFile)).
|
|
216
|
-
|
|
217
|
-
Part of the attributes map directly to some of the SLURM attributes (see
|
|
218
|
-
https://slurm.schedmd.com/sbatch.html), e.g. `partition`. Other attributes
|
|
219
|
-
are metaparameters which are needed in fractal-server to combine multiple
|
|
220
|
-
tasks in the same SLURM job (e.g. `parallel_tasks_per_job` or
|
|
221
|
-
`max_num_jobs`).
|
|
222
|
-
|
|
223
|
-
Attributes:
|
|
224
|
-
partition: Corresponds to SLURM option.
|
|
225
|
-
cpus_per_task: Corresponds to SLURM option.
|
|
226
|
-
mem_per_task_MB: Corresponds to `mem` SLURM option.
|
|
227
|
-
job_name: Corresponds to `name` SLURM option.
|
|
228
|
-
constraint: Corresponds to SLURM option.
|
|
229
|
-
gres: Corresponds to SLURM option.
|
|
230
|
-
account: Corresponds to SLURM option.
|
|
231
|
-
gpus: Corresponds to SLURM option.
|
|
232
|
-
time: Corresponds to SLURM option (WARNING: not fully supported).
|
|
233
|
-
nodelist: Corresponds to SLURM option.
|
|
234
|
-
exclude: Corresponds to SLURM option.
|
|
235
|
-
prefix: Prefix of configuration lines in SLURM submission scripts.
|
|
236
|
-
shebang_line: Shebang line for SLURM submission scripts.
|
|
237
|
-
extra_lines: Additional lines to include in SLURM submission scripts.
|
|
238
|
-
tasks_per_job: Number of tasks for each SLURM job.
|
|
239
|
-
parallel_tasks_per_job: Number of tasks to run in parallel for
|
|
240
|
-
each SLURM job.
|
|
241
|
-
target_cpus_per_job: Optimal number of CPUs to be requested in each
|
|
242
|
-
SLURM job.
|
|
243
|
-
max_cpus_per_job: Maximum number of CPUs that can be requested in each
|
|
244
|
-
SLURM job.
|
|
245
|
-
target_mem_per_job: Optimal amount of memory (in MB) to be requested in
|
|
246
|
-
each SLURM job.
|
|
247
|
-
max_mem_per_job: Maximum amount of memory (in MB) that can be requested
|
|
248
|
-
in each SLURM job.
|
|
249
|
-
target_num_jobs: Optimal number of SLURM jobs for a given WorkflowTask.
|
|
250
|
-
max_num_jobs: Maximum number of SLURM jobs for a given WorkflowTask.
|
|
251
|
-
user_local_exports:
|
|
252
|
-
Key-value pairs to be included as `export`-ed variables in SLURM
|
|
253
|
-
submission script, after prepending values with the user's cache
|
|
254
|
-
directory.
|
|
255
|
-
"""
|
|
256
|
-
|
|
257
|
-
model_config = ConfigDict(extra="forbid")
|
|
258
|
-
|
|
259
|
-
# Required SLURM parameters (note that the integer attributes are those
|
|
260
|
-
# that will need to scale up with the number of parallel tasks per job)
|
|
261
|
-
partition: str
|
|
262
|
-
cpus_per_task: int
|
|
263
|
-
mem_per_task_MB: int
|
|
264
|
-
prefix: str = "#SBATCH"
|
|
265
|
-
shebang_line: str = "#!/bin/sh"
|
|
266
|
-
|
|
267
|
-
# Optional SLURM parameters
|
|
268
|
-
job_name: str | None = None
|
|
269
|
-
constraint: str | None = None
|
|
270
|
-
gres: str | None = None
|
|
271
|
-
gpus: str | None = None
|
|
272
|
-
time: str | None = None
|
|
273
|
-
account: str | None = None
|
|
274
|
-
nodelist: str | None = None
|
|
275
|
-
exclude: str | None = None
|
|
276
|
-
|
|
277
|
-
# Free-field attribute for extra lines to be added to the SLURM job
|
|
278
|
-
# preamble
|
|
279
|
-
extra_lines: list[str] | None = Field(default_factory=list)
|
|
280
|
-
|
|
281
|
-
# Variables that will be `export`ed in the SLURM submission script
|
|
282
|
-
user_local_exports: dict[str, str] | None = None
|
|
283
|
-
|
|
284
|
-
# Metaparameters needed to combine multiple tasks in each SLURM job
|
|
285
|
-
tasks_per_job: int | None = None
|
|
286
|
-
parallel_tasks_per_job: int | None = None
|
|
287
|
-
target_cpus_per_job: int
|
|
288
|
-
max_cpus_per_job: int
|
|
289
|
-
target_mem_per_job: int
|
|
290
|
-
max_mem_per_job: int
|
|
291
|
-
target_num_jobs: int
|
|
292
|
-
max_num_jobs: int
|
|
293
|
-
|
|
294
|
-
def _sorted_extra_lines(self) -> list[str]:
|
|
295
|
-
"""
|
|
296
|
-
Return a copy of `self.extra_lines`, where lines starting with
|
|
297
|
-
`self.prefix` are listed first.
|
|
298
|
-
"""
|
|
299
|
-
|
|
300
|
-
def _no_prefix(_line):
|
|
301
|
-
if _line.startswith(self.prefix):
|
|
302
|
-
return 0
|
|
303
|
-
else:
|
|
304
|
-
return 1
|
|
305
|
-
|
|
306
|
-
return sorted(self.extra_lines, key=_no_prefix)
|
|
307
|
-
|
|
308
|
-
def sort_script_lines(self, script_lines: list[str]) -> list[str]:
|
|
309
|
-
"""
|
|
310
|
-
Return a copy of `script_lines`, where lines are sorted as in:
|
|
311
|
-
|
|
312
|
-
1. `self.shebang_line` (if present);
|
|
313
|
-
2. Lines starting with `self.prefix`;
|
|
314
|
-
3. Other lines.
|
|
315
|
-
|
|
316
|
-
Arguments:
|
|
317
|
-
script_lines:
|
|
318
|
-
"""
|
|
319
|
-
|
|
320
|
-
def _sorting_function(_line):
|
|
321
|
-
if _line == self.shebang_line:
|
|
322
|
-
return 0
|
|
323
|
-
elif _line.startswith(self.prefix):
|
|
324
|
-
return 1
|
|
325
|
-
else:
|
|
326
|
-
return 2
|
|
327
|
-
|
|
328
|
-
return sorted(script_lines, key=_sorting_function)
|
|
329
|
-
|
|
330
|
-
def to_sbatch_preamble(
|
|
331
|
-
self,
|
|
332
|
-
remote_export_dir: str | None = None,
|
|
333
|
-
) -> list[str]:
|
|
334
|
-
"""
|
|
335
|
-
Compile `SlurmConfig` object into the preamble of a SLURM submission
|
|
336
|
-
script.
|
|
337
|
-
|
|
338
|
-
Arguments:
|
|
339
|
-
remote_export_dir:
|
|
340
|
-
Base directory for exports defined in
|
|
341
|
-
`self.user_local_exports`.
|
|
342
|
-
"""
|
|
343
|
-
if self.parallel_tasks_per_job is None:
|
|
344
|
-
raise ValueError(
|
|
345
|
-
"SlurmConfig.sbatch_preamble requires that "
|
|
346
|
-
f"{self.parallel_tasks_per_job=} is not None."
|
|
347
|
-
)
|
|
348
|
-
if self.extra_lines:
|
|
349
|
-
if len(self.extra_lines) != len(set(self.extra_lines)):
|
|
350
|
-
raise ValueError(f"{self.extra_lines=} contains repetitions")
|
|
351
|
-
|
|
352
|
-
mem_per_job_MB = self.parallel_tasks_per_job * self.mem_per_task_MB
|
|
353
|
-
lines = [
|
|
354
|
-
self.shebang_line,
|
|
355
|
-
f"{self.prefix} --partition={self.partition}",
|
|
356
|
-
f"{self.prefix} --ntasks={self.parallel_tasks_per_job}",
|
|
357
|
-
f"{self.prefix} --cpus-per-task={self.cpus_per_task}",
|
|
358
|
-
f"{self.prefix} --mem={mem_per_job_MB}M",
|
|
359
|
-
]
|
|
360
|
-
for key in [
|
|
361
|
-
"job_name",
|
|
362
|
-
"constraint",
|
|
363
|
-
"gres",
|
|
364
|
-
"gpus",
|
|
365
|
-
"time",
|
|
366
|
-
"account",
|
|
367
|
-
"exclude",
|
|
368
|
-
"nodelist",
|
|
369
|
-
]:
|
|
370
|
-
value = getattr(self, key)
|
|
371
|
-
if value is not None:
|
|
372
|
-
# Handle the `time` parameter
|
|
373
|
-
if key == "time" and self.parallel_tasks_per_job > 1:
|
|
374
|
-
# NOTE: see issue #1632
|
|
375
|
-
logger.warning(
|
|
376
|
-
f"`time` SLURM parameter is set to {self.time}, "
|
|
377
|
-
"but this does not take into account the number of "
|
|
378
|
-
f"SLURM tasks ({self.parallel_tasks_per_job})."
|
|
379
|
-
)
|
|
380
|
-
option = key.replace("_", "-")
|
|
381
|
-
lines.append(f"{self.prefix} --{option}={value}")
|
|
382
|
-
|
|
383
|
-
if self.extra_lines:
|
|
384
|
-
for line in self._sorted_extra_lines():
|
|
385
|
-
lines.append(line)
|
|
386
|
-
|
|
387
|
-
if self.user_local_exports:
|
|
388
|
-
if remote_export_dir is None:
|
|
389
|
-
raise ValueError(
|
|
390
|
-
f"remote_export_dir=None but {self.user_local_exports=}"
|
|
391
|
-
)
|
|
392
|
-
for key, value in self.user_local_exports.items():
|
|
393
|
-
tmp_value = str(Path(remote_export_dir) / value)
|
|
394
|
-
lines.append(f"export {key}={tmp_value}")
|
|
395
|
-
|
|
396
|
-
"""
|
|
397
|
-
FIXME export SRUN_CPUS_PER_TASK
|
|
398
|
-
# From https://slurm.schedmd.com/sbatch.html: Beginning with 22.05,
|
|
399
|
-
# srun will not inherit the --cpus-per-task value requested by salloc
|
|
400
|
-
# or sbatch. It must be requested again with the call to srun or set
|
|
401
|
-
# with the SRUN_CPUS_PER_TASK environment variable if desired for the
|
|
402
|
-
# task(s).
|
|
403
|
-
if config.cpus_per_task:
|
|
404
|
-
#additional_setup_lines.append(
|
|
405
|
-
f"export SRUN_CPUS_PER_TASK={config.cpus_per_task}"
|
|
406
|
-
)
|
|
407
|
-
"""
|
|
408
|
-
|
|
409
|
-
return lines
|
|
410
|
-
|
|
411
|
-
@property
|
|
412
|
-
def batch_size(self) -> int:
|
|
413
|
-
return self.tasks_per_job
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
def _parse_mem_value(raw_mem: str | int) -> int:
|
|
417
|
-
"""
|
|
418
|
-
Convert a memory-specification string into an integer (in MB units), or
|
|
419
|
-
simply return the input if it is already an integer.
|
|
420
|
-
|
|
421
|
-
Supported units are `"M", "G", "T"`, with `"M"` being the default; some
|
|
422
|
-
parsing examples are: `"10M" -> 10000`, `"3G" -> 3000000`.
|
|
423
|
-
|
|
424
|
-
Arguments:
|
|
425
|
-
raw_mem:
|
|
426
|
-
A string (e.g. `"100M"`) or an integer (in MB).
|
|
427
|
-
|
|
428
|
-
Returns:
|
|
429
|
-
Integer value of memory in MB units.
|
|
430
|
-
"""
|
|
431
|
-
|
|
432
|
-
info = f"[_parse_mem_value] {raw_mem=}"
|
|
433
|
-
error_msg = (
|
|
434
|
-
f"{info}, invalid specification of memory requirements "
|
|
435
|
-
"(valid examples: 93, 71M, 93G, 71T)."
|
|
436
|
-
)
|
|
437
|
-
|
|
438
|
-
# Handle integer argument
|
|
439
|
-
if type(raw_mem) is int:
|
|
440
|
-
return raw_mem
|
|
441
|
-
|
|
442
|
-
# Handle string argument
|
|
443
|
-
if not raw_mem[0].isdigit(): # fail e.g. for raw_mem="M100"
|
|
444
|
-
logger.error(error_msg)
|
|
445
|
-
raise SlurmConfigError(error_msg)
|
|
446
|
-
if raw_mem.isdigit():
|
|
447
|
-
mem_MB = int(raw_mem)
|
|
448
|
-
elif raw_mem.endswith("M"):
|
|
449
|
-
stripped_raw_mem = raw_mem.strip("M")
|
|
450
|
-
if not stripped_raw_mem.isdigit():
|
|
451
|
-
logger.error(error_msg)
|
|
452
|
-
raise SlurmConfigError(error_msg)
|
|
453
|
-
mem_MB = int(stripped_raw_mem)
|
|
454
|
-
elif raw_mem.endswith("G"):
|
|
455
|
-
stripped_raw_mem = raw_mem.strip("G")
|
|
456
|
-
if not stripped_raw_mem.isdigit():
|
|
457
|
-
logger.error(error_msg)
|
|
458
|
-
raise SlurmConfigError(error_msg)
|
|
459
|
-
mem_MB = int(stripped_raw_mem) * 10**3
|
|
460
|
-
elif raw_mem.endswith("T"):
|
|
461
|
-
stripped_raw_mem = raw_mem.strip("T")
|
|
462
|
-
if not stripped_raw_mem.isdigit():
|
|
463
|
-
logger.error(error_msg)
|
|
464
|
-
raise SlurmConfigError(error_msg)
|
|
465
|
-
mem_MB = int(stripped_raw_mem) * 10**6
|
|
466
|
-
else:
|
|
467
|
-
logger.error(error_msg)
|
|
468
|
-
raise SlurmConfigError(error_msg)
|
|
469
|
-
|
|
470
|
-
logger.debug(f"{info}, return {mem_MB}")
|
|
471
|
-
return mem_MB
|
|
File without changes
|
|
File without changes
|
|
File without changes
|