fractal-server 2.14.5__py3-none-any.whl → 2.14.6__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.
Files changed (108) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/db/__init__.py +2 -2
  3. fractal_server/app/models/security.py +8 -8
  4. fractal_server/app/models/user_settings.py +8 -10
  5. fractal_server/app/models/v2/accounting.py +2 -3
  6. fractal_server/app/models/v2/dataset.py +1 -2
  7. fractal_server/app/models/v2/history.py +3 -4
  8. fractal_server/app/models/v2/job.py +10 -11
  9. fractal_server/app/models/v2/project.py +1 -2
  10. fractal_server/app/models/v2/task.py +13 -14
  11. fractal_server/app/models/v2/task_group.py +15 -16
  12. fractal_server/app/models/v2/workflow.py +1 -2
  13. fractal_server/app/models/v2/workflowtask.py +6 -7
  14. fractal_server/app/routes/admin/v2/accounting.py +3 -4
  15. fractal_server/app/routes/admin/v2/job.py +13 -14
  16. fractal_server/app/routes/admin/v2/project.py +2 -4
  17. fractal_server/app/routes/admin/v2/task.py +11 -13
  18. fractal_server/app/routes/admin/v2/task_group.py +15 -17
  19. fractal_server/app/routes/admin/v2/task_group_lifecycle.py +5 -8
  20. fractal_server/app/routes/api/v2/__init__.py +2 -0
  21. fractal_server/app/routes/api/v2/_aux_functions.py +7 -9
  22. fractal_server/app/routes/api/v2/_aux_functions_history.py +1 -1
  23. fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +1 -3
  24. fractal_server/app/routes/api/v2/_aux_functions_tasks.py +5 -6
  25. fractal_server/app/routes/api/v2/dataset.py +6 -8
  26. fractal_server/app/routes/api/v2/history.py +5 -8
  27. fractal_server/app/routes/api/v2/images.py +2 -3
  28. fractal_server/app/routes/api/v2/job.py +5 -6
  29. fractal_server/app/routes/api/v2/pre_submission_checks.py +1 -3
  30. fractal_server/app/routes/api/v2/project.py +2 -4
  31. fractal_server/app/routes/api/v2/status_legacy.py +2 -4
  32. fractal_server/app/routes/api/v2/submit.py +3 -4
  33. fractal_server/app/routes/api/v2/task.py +6 -7
  34. fractal_server/app/routes/api/v2/task_collection.py +11 -13
  35. fractal_server/app/routes/api/v2/task_collection_custom.py +4 -4
  36. fractal_server/app/routes/api/v2/task_group.py +6 -8
  37. fractal_server/app/routes/api/v2/task_group_lifecycle.py +6 -9
  38. fractal_server/app/routes/api/v2/task_version_update.py +270 -0
  39. fractal_server/app/routes/api/v2/workflow.py +5 -6
  40. fractal_server/app/routes/api/v2/workflow_import.py +3 -5
  41. fractal_server/app/routes/api/v2/workflowtask.py +2 -114
  42. fractal_server/app/routes/auth/current_user.py +2 -2
  43. fractal_server/app/routes/pagination.py +2 -3
  44. fractal_server/app/runner/exceptions.py +15 -16
  45. fractal_server/app/runner/executors/base_runner.py +3 -3
  46. fractal_server/app/runner/executors/call_command_wrapper.py +1 -1
  47. fractal_server/app/runner/executors/local/get_local_config.py +2 -3
  48. fractal_server/app/runner/executors/local/runner.py +1 -1
  49. fractal_server/app/runner/executors/slurm_common/_batching.py +2 -3
  50. fractal_server/app/runner/executors/slurm_common/_slurm_config.py +27 -29
  51. fractal_server/app/runner/executors/slurm_common/base_slurm_runner.py +11 -14
  52. fractal_server/app/runner/executors/slurm_common/get_slurm_config.py +2 -3
  53. fractal_server/app/runner/executors/slurm_common/remote.py +2 -2
  54. fractal_server/app/runner/executors/slurm_common/slurm_job_task_models.py +2 -3
  55. fractal_server/app/runner/executors/slurm_ssh/run_subprocess.py +2 -3
  56. fractal_server/app/runner/executors/slurm_ssh/runner.py +3 -4
  57. fractal_server/app/runner/executors/slurm_sudo/_subprocess_run_as_user.py +1 -2
  58. fractal_server/app/runner/executors/slurm_sudo/runner.py +6 -7
  59. fractal_server/app/runner/set_start_and_last_task_index.py +2 -5
  60. fractal_server/app/runner/shutdown.py +5 -11
  61. fractal_server/app/runner/task_files.py +3 -5
  62. fractal_server/app/runner/v2/_local.py +3 -4
  63. fractal_server/app/runner/v2/_slurm_ssh.py +4 -5
  64. fractal_server/app/runner/v2/_slurm_sudo.py +7 -8
  65. fractal_server/app/runner/v2/runner.py +4 -5
  66. fractal_server/app/runner/v2/runner_functions.py +4 -5
  67. fractal_server/app/runner/v2/submit_workflow.py +7 -10
  68. fractal_server/app/runner/v2/task_interface.py +2 -3
  69. fractal_server/app/runner/versions.py +1 -2
  70. fractal_server/app/schemas/user.py +2 -4
  71. fractal_server/app/schemas/user_group.py +1 -2
  72. fractal_server/app/schemas/user_settings.py +19 -21
  73. fractal_server/app/schemas/v2/dataset.py +2 -3
  74. fractal_server/app/schemas/v2/dumps.py +13 -15
  75. fractal_server/app/schemas/v2/history.py +6 -7
  76. fractal_server/app/schemas/v2/job.py +17 -18
  77. fractal_server/app/schemas/v2/manifest.py +12 -13
  78. fractal_server/app/schemas/v2/status_legacy.py +2 -2
  79. fractal_server/app/schemas/v2/task.py +29 -30
  80. fractal_server/app/schemas/v2/task_collection.py +8 -9
  81. fractal_server/app/schemas/v2/task_group.py +22 -23
  82. fractal_server/app/schemas/v2/workflow.py +1 -2
  83. fractal_server/app/schemas/v2/workflowtask.py +27 -29
  84. fractal_server/app/security/__init__.py +10 -12
  85. fractal_server/config.py +32 -33
  86. fractal_server/images/models.py +2 -4
  87. fractal_server/images/tools.py +4 -7
  88. fractal_server/logger.py +3 -5
  89. fractal_server/ssh/_fabric.py +37 -12
  90. fractal_server/string_tools.py +2 -2
  91. fractal_server/syringe.py +1 -1
  92. fractal_server/tasks/v2/local/collect.py +2 -3
  93. fractal_server/tasks/v2/local/deactivate.py +1 -1
  94. fractal_server/tasks/v2/local/reactivate.py +1 -1
  95. fractal_server/tasks/v2/ssh/collect.py +256 -245
  96. fractal_server/tasks/v2/ssh/deactivate.py +210 -187
  97. fractal_server/tasks/v2/ssh/reactivate.py +154 -146
  98. fractal_server/tasks/v2/utils_background.py +2 -3
  99. fractal_server/types/__init__.py +1 -2
  100. fractal_server/types/validators/_filter_validators.py +1 -2
  101. fractal_server/utils.py +4 -5
  102. fractal_server/zip_tools.py +1 -1
  103. {fractal_server-2.14.5.dist-info → fractal_server-2.14.6.dist-info}/METADATA +2 -3
  104. {fractal_server-2.14.5.dist-info → fractal_server-2.14.6.dist-info}/RECORD +107 -107
  105. fractal_server/app/history/__init__.py +0 -0
  106. {fractal_server-2.14.5.dist-info → fractal_server-2.14.6.dist-info}/LICENSE +0 -0
  107. {fractal_server-2.14.5.dist-info → fractal_server-2.14.6.dist-info}/WHEEL +0 -0
  108. {fractal_server-2.14.5.dist-info → fractal_server-2.14.6.dist-info}/entry_points.txt +0 -0
@@ -16,7 +16,8 @@ from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
16
16
  from fractal_server.app.schemas.v2.task_group import TaskGroupActivityStatusV2
17
17
  from fractal_server.logger import reset_logger_handlers
18
18
  from fractal_server.logger import set_logger
19
- from fractal_server.ssh._fabric import FractalSSH
19
+ from fractal_server.ssh._fabric import SingleUseFractalSSH
20
+ from fractal_server.ssh._fabric import SSHConfig
20
21
  from fractal_server.tasks.utils import FORBIDDEN_DEPENDENCY_STRINGS
21
22
  from fractal_server.tasks.utils import get_log_path
22
23
  from fractal_server.tasks.v2.utils_background import get_current_log
@@ -28,7 +29,7 @@ def deactivate_ssh(
28
29
  *,
29
30
  task_group_activity_id: int,
30
31
  task_group_id: int,
31
- fractal_ssh: FractalSSH,
32
+ ssh_config: SSHConfig,
32
33
  tasks_base_dir: str,
33
34
  ) -> None:
34
35
  """
@@ -40,7 +41,7 @@ def deactivate_ssh(
40
41
  Arguments:
41
42
  task_group_id:
42
43
  task_group_activity_id:
43
- fractal_ssh:
44
+ ssh_config:
44
45
  tasks_base_dir:
45
46
  Only used as a `safe_root` in `remove_dir`, and typically set to
46
47
  `user_settings.ssh_tasks_dir`.
@@ -54,214 +55,236 @@ def deactivate_ssh(
54
55
  logger_name=LOGGER_NAME,
55
56
  log_file_path=log_file_path,
56
57
  )
57
-
58
- with next(get_sync_db()) as db:
59
-
60
- # Get main objects from db
61
- activity = db.get(TaskGroupActivityV2, task_group_activity_id)
62
- task_group = db.get(TaskGroupV2, task_group_id)
63
- if activity is None or task_group is None:
64
- # Use `logging` directly
65
- logging.error(
66
- "Cannot find database rows with "
67
- f"{task_group_id=} and {task_group_activity_id=}:\n"
68
- f"{task_group=}\n{activity=}. Exit."
69
- )
70
- return
71
-
72
- # Log some info
73
- logger.debug("START")
74
- for key, value in task_group.model_dump().items():
75
- logger.debug(f"task_group.{key}: {value}")
76
-
77
- # Check that SSH connection works
78
- try:
79
- fractal_ssh.check_connection()
80
- except Exception as e:
81
- logger.error("Cannot establish SSH connection.")
82
- fail_and_cleanup(
83
- task_group=task_group,
84
- task_group_activity=activity,
85
- logger_name=LOGGER_NAME,
86
- log_file_path=log_file_path,
87
- exception=e,
88
- db=db,
89
- )
90
- return
91
-
92
- # Check that the (local) task_group venv_path does exist
93
- if not fractal_ssh.remote_exists(task_group.venv_path):
94
- error_msg = f"{task_group.venv_path} does not exist."
95
- logger.error(error_msg)
96
- fail_and_cleanup(
97
- task_group=task_group,
98
- task_group_activity=activity,
99
- logger_name=LOGGER_NAME,
100
- log_file_path=log_file_path,
101
- exception=FileNotFoundError(error_msg),
102
- db=db,
103
- )
104
- return
105
-
106
- try:
107
-
108
- activity.status = TaskGroupActivityStatusV2.ONGOING
109
- activity = add_commit_refresh(obj=activity, db=db)
110
-
111
- if task_group.pip_freeze is None:
112
- logger.warning(
113
- "Recreate pip-freeze information, since "
114
- f"{task_group.pip_freeze=}. NOTE: this should only "
115
- "happen for task groups created before 2.9.0."
58
+ with SingleUseFractalSSH(
59
+ ssh_config=ssh_config,
60
+ logger_name=LOGGER_NAME,
61
+ ) as fractal_ssh:
62
+
63
+ with next(get_sync_db()) as db:
64
+
65
+ # Get main objects from db
66
+ activity = db.get(TaskGroupActivityV2, task_group_activity_id)
67
+ task_group = db.get(TaskGroupV2, task_group_id)
68
+ if activity is None or task_group is None:
69
+ # Use `logging` directly
70
+ logging.error(
71
+ "Cannot find database rows with "
72
+ f"{task_group_id=} and {task_group_activity_id=}:\n"
73
+ f"{task_group=}\n{activity=}. Exit."
116
74
  )
117
-
118
- # Prepare replacements for templates
119
- replacements = get_collection_replacements(
75
+ return
76
+
77
+ # Log some info
78
+ logger.debug("START")
79
+ for key, value in task_group.model_dump().items():
80
+ logger.debug(f"task_group.{key}: {value}")
81
+
82
+ # Check that SSH connection works
83
+ try:
84
+ fractal_ssh.check_connection()
85
+ except Exception as e:
86
+ logger.error("Cannot establish SSH connection.")
87
+ fail_and_cleanup(
120
88
  task_group=task_group,
121
- python_bin="/not/applicable",
89
+ task_group_activity=activity,
90
+ logger_name=LOGGER_NAME,
91
+ log_file_path=log_file_path,
92
+ exception=e,
93
+ db=db,
122
94
  )
95
+ return
123
96
 
124
- # Define script_dir_remote and create it if missing
125
- script_dir_remote = (
126
- Path(task_group.path) / SCRIPTS_SUBFOLDER
127
- ).as_posix()
128
- fractal_ssh.mkdir(folder=script_dir_remote, parents=True)
129
-
130
- # Prepare arguments for `_customize_and_run_template`
131
- common_args = dict(
132
- replacements=replacements,
133
- script_dir_local=(
134
- Path(tmpdir) / SCRIPTS_SUBFOLDER
135
- ).as_posix(),
136
- script_dir_remote=script_dir_remote,
137
- prefix=(
138
- f"{int(time.time())}_"
139
- f"{TaskGroupActivityActionV2.DEACTIVATE.value}"
140
- ),
141
- fractal_ssh=fractal_ssh,
97
+ # Check that the (local) task_group venv_path does exist
98
+ if not fractal_ssh.remote_exists(task_group.venv_path):
99
+ error_msg = f"{task_group.venv_path} does not exist."
100
+ logger.error(error_msg)
101
+ fail_and_cleanup(
102
+ task_group=task_group,
103
+ task_group_activity=activity,
142
104
  logger_name=LOGGER_NAME,
105
+ log_file_path=log_file_path,
106
+ exception=FileNotFoundError(error_msg),
107
+ db=db,
143
108
  )
109
+ return
144
110
 
145
- # Run `pip freeze`
146
- pip_freeze_stdout = _customize_and_run_template(
147
- template_filename="3_pip_freeze.sh",
148
- **common_args,
149
- )
111
+ try:
150
112
 
151
- # Update pip-freeze data
152
- logger.info("Add pip freeze stdout to TaskGroupV2 - start")
153
- activity.log = get_current_log(log_file_path)
113
+ activity.status = TaskGroupActivityStatusV2.ONGOING
154
114
  activity = add_commit_refresh(obj=activity, db=db)
155
- task_group.pip_freeze = pip_freeze_stdout
156
- task_group = add_commit_refresh(obj=task_group, db=db)
157
- logger.info("Add pip freeze stdout to TaskGroupV2 - end")
158
-
159
- # Handle some specific cases for wheel-file case
160
- if task_group.origin == TaskGroupV2OriginEnum.WHEELFILE:
161
-
162
- logger.info(
163
- f"Handle specific cases for {task_group.origin=}."
164
- )
165
115
 
166
- # Blocking situation: `wheel_path` is not set or points
167
- # to a missing path
168
- if (
169
- task_group.wheel_path is None
170
- or not fractal_ssh.remote_exists(task_group.wheel_path)
171
- ):
172
- error_msg = (
173
- "Invalid wheel path for task group with "
174
- f"{task_group_id=}. {task_group.wheel_path=} is "
175
- "unset or does not exist."
116
+ if task_group.pip_freeze is None:
117
+ logger.warning(
118
+ "Recreate pip-freeze information, since "
119
+ f"{task_group.pip_freeze=}. NOTE: this should "
120
+ "only happen for task groups created before 2.9.0."
176
121
  )
177
- logger.error(error_msg)
178
- fail_and_cleanup(
122
+
123
+ # Prepare replacements for templates
124
+ replacements = get_collection_replacements(
179
125
  task_group=task_group,
180
- task_group_activity=activity,
181
- logger_name=LOGGER_NAME,
182
- log_file_path=log_file_path,
183
- exception=FileNotFoundError(error_msg),
184
- db=db,
126
+ python_bin="/not/applicable",
185
127
  )
186
- return
187
128
 
188
- # Recoverable situation: `wheel_path` was not yet copied
189
- # over to the correct server-side folder
190
- wheel_path_parent_dir = Path(task_group.wheel_path).parent
191
- if wheel_path_parent_dir != Path(task_group.path):
192
- logger.warning(
193
- f"{wheel_path_parent_dir.as_posix()} differs from "
194
- f"{task_group.path}. NOTE: this should only "
195
- "happen for task groups created before 2.9.0."
129
+ # Define script_dir_remote and create it if missing
130
+ script_dir_remote = (
131
+ Path(task_group.path) / SCRIPTS_SUBFOLDER
132
+ ).as_posix()
133
+ fractal_ssh.mkdir(
134
+ folder=script_dir_remote, parents=True
196
135
  )
197
136
 
198
- if task_group.wheel_path not in task_group.pip_freeze:
199
- raise ValueError(
200
- f"Cannot find {task_group.wheel_path=} in "
201
- "pip-freeze data. Exit."
202
- )
203
-
204
- logger.info(
205
- f"Now copy wheel file into {task_group.path}."
206
- )
207
- new_wheel_path = _copy_wheel_file_ssh(
208
- task_group=task_group,
137
+ # Prepare arguments for `_customize_and_run_template`
138
+ common_args = dict(
139
+ replacements=replacements,
140
+ script_dir_local=(
141
+ Path(tmpdir) / SCRIPTS_SUBFOLDER
142
+ ).as_posix(),
143
+ script_dir_remote=script_dir_remote,
144
+ prefix=(
145
+ f"{int(time.time())}_"
146
+ f"{TaskGroupActivityActionV2.DEACTIVATE}"
147
+ ),
209
148
  fractal_ssh=fractal_ssh,
210
149
  logger_name=LOGGER_NAME,
211
150
  )
212
- logger.info(f"Copied wheel file to {new_wheel_path}.")
213
151
 
214
- task_group.wheel_path = new_wheel_path
215
- new_pip_freeze = task_group.pip_freeze.replace(
216
- task_group.wheel_path,
217
- new_wheel_path,
152
+ # Run `pip freeze`
153
+ pip_freeze_stdout = _customize_and_run_template(
154
+ template_filename="3_pip_freeze.sh",
155
+ **common_args,
218
156
  )
219
- task_group.pip_freeze = new_pip_freeze
157
+
158
+ # Update pip-freeze data
159
+ logger.info(
160
+ "Add pip freeze stdout to TaskGroupV2 - start"
161
+ )
162
+ activity.log = get_current_log(log_file_path)
163
+ activity = add_commit_refresh(obj=activity, db=db)
164
+ task_group.pip_freeze = pip_freeze_stdout
220
165
  task_group = add_commit_refresh(obj=task_group, db=db)
221
166
  logger.info(
222
- "Updated `wheel_path` and `pip_freeze` "
223
- "task-group attributes."
167
+ "Add pip freeze stdout to TaskGroupV2 - end"
224
168
  )
225
169
 
226
- # Fail if `pip_freeze` includes "github", see
227
- # https://github.com/fractal-analytics-platform/fractal-server/issues/2142
228
- for forbidden_string in FORBIDDEN_DEPENDENCY_STRINGS:
229
- if forbidden_string in task_group.pip_freeze:
230
- raise ValueError(
231
- "Deactivation and reactivation of task packages "
232
- f"with direct {forbidden_string} dependencies "
233
- "are not currently supported. Exit."
170
+ # Handle some specific cases for wheel-file case
171
+ if task_group.origin == TaskGroupV2OriginEnum.WHEELFILE:
172
+
173
+ logger.info(
174
+ f"Handle specific cases for {task_group.origin=}."
234
175
  )
235
176
 
236
- # We now have all required information for reactivating the
237
- # virtual environment at a later point
238
-
239
- # Actually mark the task group as non-active
240
- logger.info("Now setting `active=False`.")
241
- task_group.active = False
242
- task_group = add_commit_refresh(obj=task_group, db=db)
243
-
244
- # Proceed with deactivation
245
- logger.info(f"Now removing {task_group.venv_path}.")
246
- fractal_ssh.remove_folder(
247
- folder=task_group.venv_path,
248
- safe_root=tasks_base_dir,
249
- )
250
- logger.info(f"All good, {task_group.venv_path} removed.")
251
- activity.status = TaskGroupActivityStatusV2.OK
252
- activity.log = get_current_log(log_file_path)
253
- activity.timestamp_ended = get_timestamp()
254
- activity = add_commit_refresh(obj=activity, db=db)
255
-
256
- reset_logger_handlers(logger)
257
-
258
- except Exception as e:
259
- fail_and_cleanup(
260
- task_group=task_group,
261
- task_group_activity=activity,
262
- logger_name=LOGGER_NAME,
263
- log_file_path=log_file_path,
264
- exception=e,
265
- db=db,
266
- )
267
- return
177
+ # Blocking situation: `wheel_path` is not set or points
178
+ # to a missing path
179
+ if (
180
+ task_group.wheel_path is None
181
+ or not fractal_ssh.remote_exists(
182
+ task_group.wheel_path
183
+ )
184
+ ):
185
+ error_msg = (
186
+ "Invalid wheel path for task group with "
187
+ f"{task_group_id=}. {task_group.wheel_path=} "
188
+ "is unset or does not exist."
189
+ )
190
+ logger.error(error_msg)
191
+ fail_and_cleanup(
192
+ task_group=task_group,
193
+ task_group_activity=activity,
194
+ logger_name=LOGGER_NAME,
195
+ log_file_path=log_file_path,
196
+ exception=FileNotFoundError(error_msg),
197
+ db=db,
198
+ )
199
+ return
200
+
201
+ # Recoverable situation: `wheel_path` was not yet
202
+ # copied over to the correct server-side folder
203
+ wheel_path_parent_dir = Path(
204
+ task_group.wheel_path
205
+ ).parent
206
+ if wheel_path_parent_dir != Path(task_group.path):
207
+ logger.warning(
208
+ f"{wheel_path_parent_dir.as_posix()} differs "
209
+ f"from {task_group.path}. NOTE: this should "
210
+ "only happen for task groups created before "
211
+ "2.9.0."
212
+ )
213
+
214
+ if (
215
+ task_group.wheel_path
216
+ not in task_group.pip_freeze
217
+ ):
218
+ raise ValueError(
219
+ f"Cannot find {task_group.wheel_path=} in "
220
+ "pip-freeze data. Exit."
221
+ )
222
+
223
+ logger.info(
224
+ f"Now copy wheel file into {task_group.path}."
225
+ )
226
+ new_wheel_path = _copy_wheel_file_ssh(
227
+ task_group=task_group,
228
+ fractal_ssh=fractal_ssh,
229
+ logger_name=LOGGER_NAME,
230
+ )
231
+ logger.info(
232
+ f"Copied wheel file to {new_wheel_path}."
233
+ )
234
+
235
+ task_group.wheel_path = new_wheel_path
236
+ new_pip_freeze = task_group.pip_freeze.replace(
237
+ task_group.wheel_path,
238
+ new_wheel_path,
239
+ )
240
+ task_group.pip_freeze = new_pip_freeze
241
+ task_group = add_commit_refresh(
242
+ obj=task_group, db=db
243
+ )
244
+ logger.info(
245
+ "Updated `wheel_path` and `pip_freeze` "
246
+ "task-group attributes."
247
+ )
248
+
249
+ # Fail if `pip_freeze` includes "github", see
250
+ # https://github.com/fractal-analytics-platform/fractal-server/issues/2142
251
+ for forbidden_string in FORBIDDEN_DEPENDENCY_STRINGS:
252
+ if forbidden_string in task_group.pip_freeze:
253
+ raise ValueError(
254
+ "Deactivation and reactivation of task "
255
+ f"packages with direct {forbidden_string} "
256
+ "dependencies are not currently supported. "
257
+ "Exit."
258
+ )
259
+
260
+ # We now have all required information for reactivating the
261
+ # virtual environment at a later point
262
+
263
+ # Actually mark the task group as non-active
264
+ logger.info("Now setting `active=False`.")
265
+ task_group.active = False
266
+ task_group = add_commit_refresh(obj=task_group, db=db)
267
+
268
+ # Proceed with deactivation
269
+ logger.info(f"Now removing {task_group.venv_path}.")
270
+ fractal_ssh.remove_folder(
271
+ folder=task_group.venv_path,
272
+ safe_root=tasks_base_dir,
273
+ )
274
+ logger.info(f"All good, {task_group.venv_path} removed.")
275
+ activity.status = TaskGroupActivityStatusV2.OK
276
+ activity.log = get_current_log(log_file_path)
277
+ activity.timestamp_ended = get_timestamp()
278
+ activity = add_commit_refresh(obj=activity, db=db)
279
+
280
+ reset_logger_handlers(logger)
281
+
282
+ except Exception as e:
283
+ fail_and_cleanup(
284
+ task_group=task_group,
285
+ task_group_activity=activity,
286
+ logger_name=LOGGER_NAME,
287
+ log_file_path=log_file_path,
288
+ exception=e,
289
+ db=db,
290
+ )