manta-node 0.5b0.dev9__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.
- manta_node/__init__.py +5 -0
- manta_node/__main__.py +57 -0
- manta_node/cli/__init__.py +6 -0
- manta_node/cli/commands/__init__.py +21 -0
- manta_node/cli/commands/cluster.py +419 -0
- manta_node/cli/commands/config.py +490 -0
- manta_node/cli/commands/logs.py +168 -0
- manta_node/cli/commands/start.py +459 -0
- manta_node/cli/commands/status.py +204 -0
- manta_node/cli/commands/stop.py +253 -0
- manta_node/cli/config_manager.py +139 -0
- manta_node/cli/main.py +133 -0
- manta_node/cli/version.py +106 -0
- manta_node/domain/__init__.py +8 -0
- manta_node/domain/task_lifecycle.py +83 -0
- manta_node/infrastructure/__init__.py +13 -0
- manta_node/infrastructure/config/__init__.py +31 -0
- manta_node/infrastructure/config/node_config_manager.py +918 -0
- manta_node/infrastructure/container/__init__.py +9 -0
- manta_node/infrastructure/container/docker_adapter.py +253 -0
- manta_node/infrastructure/container/manager.py +536 -0
- manta_node/infrastructure/filesystem/__init__.py +7 -0
- manta_node/infrastructure/filesystem/dataset_manager.py +469 -0
- manta_node/infrastructure/grpc/__init__.py +11 -0
- manta_node/infrastructure/grpc/client.py +373 -0
- manta_node/infrastructure/grpc/local_servicer.py +151 -0
- manta_node/infrastructure/grpc/world_servicer.py +284 -0
- manta_node/infrastructure/metrics/__init__.py +10 -0
- manta_node/infrastructure/metrics/collector.py +94 -0
- manta_node/infrastructure/metrics/metrics_collector.py +351 -0
- manta_node/infrastructure/mqtt/__init__.py +7 -0
- manta_node/infrastructure/mqtt/command_handler.py +315 -0
- manta_node/node_orchestrator.py +462 -0
- manta_node/task_manager.py +519 -0
- manta_node/tasks.py +272 -0
- manta_node/utils.py +52 -0
- manta_node-0.5b0.dev9.dist-info/METADATA +799 -0
- manta_node-0.5b0.dev9.dist-info/RECORD +42 -0
- manta_node-0.5b0.dev9.dist-info/WHEEL +5 -0
- manta_node-0.5b0.dev9.dist-info/entry_points.txt +2 -0
- manta_node-0.5b0.dev9.dist-info/licenses/LICENSE +683 -0
- manta_node-0.5b0.dev9.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from collections import deque
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from tempfile import TemporaryDirectory
|
|
6
|
+
from typing import List, Optional, Tuple
|
|
7
|
+
|
|
8
|
+
from manta_common.build.common.system import EnvId, PairId
|
|
9
|
+
from manta_common.build.common.tasks import MqttTask, TaskStatus, TaskUpdate
|
|
10
|
+
from manta_common.conversions import ID
|
|
11
|
+
from manta_common.errors import (
|
|
12
|
+
MantaError,
|
|
13
|
+
MantaGRPCError,
|
|
14
|
+
MantaMQTTError,
|
|
15
|
+
MantaNodeError,
|
|
16
|
+
)
|
|
17
|
+
from manta_common.traces import Tracer
|
|
18
|
+
from .domain.task_lifecycle import TaskLifecycleService
|
|
19
|
+
from .infrastructure.container.manager import ContainerManager
|
|
20
|
+
from .infrastructure.grpc.client import NodeClient
|
|
21
|
+
from .tasks import TaskProgression, TaskProgressions
|
|
22
|
+
|
|
23
|
+
__all__ = ["TaskManager"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TaskManager(ContainerManager):
|
|
27
|
+
"""
|
|
28
|
+
The Task Manager is responsible to distribute tasks depending on device resources.
|
|
29
|
+
It is composed with a Queue of Tasks.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
light_service_port : int
|
|
34
|
+
Light service port
|
|
35
|
+
data_folder : str
|
|
36
|
+
Data folder
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
tracer: Tracer
|
|
40
|
+
node_client: NodeClient
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
light_service_port: int,
|
|
45
|
+
) -> None:
|
|
46
|
+
# Initialize the TaskManager
|
|
47
|
+
self.ls_port = light_service_port
|
|
48
|
+
self.temp_folder = TemporaryDirectory()
|
|
49
|
+
|
|
50
|
+
# Temporary directory for the module python files
|
|
51
|
+
self.module_folder = Path(self.temp_folder.name)
|
|
52
|
+
self.tasks = TaskProgressions()
|
|
53
|
+
|
|
54
|
+
# Initialize the task queue
|
|
55
|
+
self.task_queue = deque()
|
|
56
|
+
self.future_task = None
|
|
57
|
+
|
|
58
|
+
ContainerManager.__init__(self)
|
|
59
|
+
|
|
60
|
+
async def check_tasks(self) -> List[TaskProgression]:
|
|
61
|
+
"""
|
|
62
|
+
Check which tasks are active and update new status to the Manager
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
List[TaskProgression]
|
|
67
|
+
List of updated tasks
|
|
68
|
+
"""
|
|
69
|
+
if len(self.task_queue) > 0 or len(self.tasks._env_ids) > 0:
|
|
70
|
+
self.tracer.debug(
|
|
71
|
+
f"Checking tasks (task_queue={len(self.task_queue)}, tasks={len(self.tasks._env_ids)})"
|
|
72
|
+
)
|
|
73
|
+
await self.start_next_task()
|
|
74
|
+
updated = await self.update_active_tasks()
|
|
75
|
+
updated.extend(await self.update_finished_tasks())
|
|
76
|
+
return updated
|
|
77
|
+
|
|
78
|
+
# TODO : make subfunctions with decorator
|
|
79
|
+
async def start_next_task(self):
|
|
80
|
+
"""
|
|
81
|
+
Start next start
|
|
82
|
+
"""
|
|
83
|
+
if self.future_task is None and len(self.task_queue) > 0:
|
|
84
|
+
task = self.task_queue.pop()
|
|
85
|
+
self.future_task = asyncio.create_task(self.exec_task(task))
|
|
86
|
+
await self.check_future_task()
|
|
87
|
+
|
|
88
|
+
async def check_future_task(self):
|
|
89
|
+
"""
|
|
90
|
+
Check future task
|
|
91
|
+
"""
|
|
92
|
+
if self.future_task is None or not self.future_task.done():
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
task_id = await self.future_task
|
|
97
|
+
self.tracer.info(f"Task {task_id} deployed from queue")
|
|
98
|
+
except MantaGRPCError as e:
|
|
99
|
+
self.tracer.exception(f"Error when executing task: {e}")
|
|
100
|
+
except MantaError as manta_error:
|
|
101
|
+
self.tracer.exception("Error when executing task.")
|
|
102
|
+
await self.node_client.send_error(manta_error)
|
|
103
|
+
except Exception as exc:
|
|
104
|
+
message = f"Error when executing task: {repr(exc)}"
|
|
105
|
+
self.tracer.exception(message)
|
|
106
|
+
await self.node_client.send_error(
|
|
107
|
+
MantaNodeError(message, metadata=self.tracer.metadata)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Reinitialize the future task
|
|
111
|
+
self.future_task = None
|
|
112
|
+
|
|
113
|
+
# TODO : make subfunctions with decorator
|
|
114
|
+
async def update_active_tasks(self) -> List[TaskProgression]:
|
|
115
|
+
"""
|
|
116
|
+
Update active tasks
|
|
117
|
+
|
|
118
|
+
Returns
|
|
119
|
+
-------
|
|
120
|
+
List[TaskProgression]
|
|
121
|
+
List of updated tasks
|
|
122
|
+
"""
|
|
123
|
+
# Update the tasks status
|
|
124
|
+
updated = []
|
|
125
|
+
for task_progression in self.tasks.active_tasks():
|
|
126
|
+
try:
|
|
127
|
+
has_changed = await self.from_pending_to_running(task_progression)
|
|
128
|
+
if not task_progression.is_finished_task() and has_changed:
|
|
129
|
+
updated.append(task_progression)
|
|
130
|
+
except Exception as exc:
|
|
131
|
+
message = f"Error updating active tasks: {repr(exc)}"
|
|
132
|
+
self.tracer.exception("Error updating active tasks.")
|
|
133
|
+
await self.node_client.send_error(
|
|
134
|
+
MantaNodeError(message, metadata=self.tracer.metadata)
|
|
135
|
+
)
|
|
136
|
+
return updated
|
|
137
|
+
|
|
138
|
+
# TODO : make subfunctions with decorator
|
|
139
|
+
async def update_finished_tasks(self) -> List[TaskProgression]:
|
|
140
|
+
"""
|
|
141
|
+
Update finished tasks
|
|
142
|
+
|
|
143
|
+
Returns
|
|
144
|
+
-------
|
|
145
|
+
List[TaskProgression]
|
|
146
|
+
List of updated tasks
|
|
147
|
+
"""
|
|
148
|
+
# Send logs of the finished tasks
|
|
149
|
+
updated = []
|
|
150
|
+
for task_progression in self.tasks.finished_tasks():
|
|
151
|
+
task_progression.task_status = TaskStatus.STOPPED
|
|
152
|
+
self.tracer.info(
|
|
153
|
+
f"Task {task_progression.task_id} finished: {task_progression.task_status}"
|
|
154
|
+
)
|
|
155
|
+
updated.append(task_progression)
|
|
156
|
+
return updated
|
|
157
|
+
|
|
158
|
+
async def from_pending_to_running(self, task_progression: TaskProgression) -> bool:
|
|
159
|
+
"""
|
|
160
|
+
Change a task progression from pending status (supposed) to running status
|
|
161
|
+
|
|
162
|
+
Parameters
|
|
163
|
+
----------
|
|
164
|
+
task_progression : TaskProgression
|
|
165
|
+
Task Progression
|
|
166
|
+
|
|
167
|
+
Returns
|
|
168
|
+
-------
|
|
169
|
+
bool
|
|
170
|
+
If task status has changed
|
|
171
|
+
"""
|
|
172
|
+
task_id = task_progression.task_id
|
|
173
|
+
swarm_id = task_progression.swarm_id
|
|
174
|
+
old_task_status = task_progression.task_status
|
|
175
|
+
new_task_status = await self.get_task_status(swarm_id, task_id)
|
|
176
|
+
task_progression.task_status = new_task_status
|
|
177
|
+
return old_task_status != new_task_status
|
|
178
|
+
|
|
179
|
+
async def from_completed_to_pending(
|
|
180
|
+
self, task_progression: TaskProgression, task: MqttTask
|
|
181
|
+
):
|
|
182
|
+
"""
|
|
183
|
+
Change a task progression from completed status to pending status
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
task_progression : TaskProgression
|
|
188
|
+
Task Progression
|
|
189
|
+
task : MqttTask
|
|
190
|
+
Task from MQTT
|
|
191
|
+
"""
|
|
192
|
+
# Update the task information
|
|
193
|
+
task_progression.task_update.iteration = task.iteration
|
|
194
|
+
task_progression.task_update.circular = task.circular
|
|
195
|
+
task_progression.task_update.task_status = TaskStatus.PENDING
|
|
196
|
+
|
|
197
|
+
# Restart the container
|
|
198
|
+
start_time = await self.restart_container(
|
|
199
|
+
task_progression.swarm_id, task_progression.task_id
|
|
200
|
+
)
|
|
201
|
+
task_progression.start_time = start_time
|
|
202
|
+
|
|
203
|
+
async def create_task(self, task: MqttTask):
|
|
204
|
+
"""
|
|
205
|
+
Create a task
|
|
206
|
+
|
|
207
|
+
Parameters
|
|
208
|
+
----------
|
|
209
|
+
task : MqttTask
|
|
210
|
+
Task information from MQTT
|
|
211
|
+
"""
|
|
212
|
+
task_id = ID(task.task_id)
|
|
213
|
+
swarm_id = ID(task.swarm_id)
|
|
214
|
+
self.tracer.set_metadata(
|
|
215
|
+
task_id=task_id,
|
|
216
|
+
swarm_id=swarm_id,
|
|
217
|
+
iteration=task.iteration,
|
|
218
|
+
circular=task.circular,
|
|
219
|
+
)
|
|
220
|
+
self.tracer.debug(f"Creating task {task_id} for {swarm_id}")
|
|
221
|
+
|
|
222
|
+
# Create a temporary file and write the module code
|
|
223
|
+
# (even if the file does not exist, it will be created)
|
|
224
|
+
module_file = self.module_folder / f"{swarm_id.oid}_{task_id.oid}.pyz"
|
|
225
|
+
module_file.unlink(missing_ok=True)
|
|
226
|
+
module_file.touch(exist_ok=True)
|
|
227
|
+
module_file.write_bytes(task.payload)
|
|
228
|
+
|
|
229
|
+
container_file = "/usr/src/module.pyz"
|
|
230
|
+
|
|
231
|
+
network = None
|
|
232
|
+
if task.network != str(None):
|
|
233
|
+
if task.auth_key != str(None) or task.login_server != str(None):
|
|
234
|
+
self.tracer.debug(
|
|
235
|
+
f"Task {task_id} has auth_key {task.auth_key} and login_server {task.login_server}"
|
|
236
|
+
)
|
|
237
|
+
network = await self.ensure_tailscale_service(
|
|
238
|
+
swarm_id,
|
|
239
|
+
task.auth_key,
|
|
240
|
+
task.login_server,
|
|
241
|
+
task.alias,
|
|
242
|
+
self.module_folder,
|
|
243
|
+
)
|
|
244
|
+
else:
|
|
245
|
+
self.tracer.error(
|
|
246
|
+
f"Task {task_id} has network {task.network} but no auth_key or login_server"
|
|
247
|
+
)
|
|
248
|
+
raise MantaNodeError(
|
|
249
|
+
f"Task {task_id} has network {task.network} but no auth_key or login_server",
|
|
250
|
+
metadata=self.tracer.metadata,
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Deploy the task
|
|
254
|
+
start_time = await self.run_container(
|
|
255
|
+
swarm_id=swarm_id,
|
|
256
|
+
task_id=task_id,
|
|
257
|
+
image=task.image,
|
|
258
|
+
command=f"python {container_file}",
|
|
259
|
+
volumes={module_file.absolute(): {"bind": container_file, "mode": "ro"}},
|
|
260
|
+
environment={
|
|
261
|
+
"TASK_ID": task_id.xid,
|
|
262
|
+
"SWARM_ID": swarm_id.xid,
|
|
263
|
+
"RPC_PORT": self.ls_port,
|
|
264
|
+
"RPC_HOST": "host.docker.internal",
|
|
265
|
+
},
|
|
266
|
+
gpu=task.gpu,
|
|
267
|
+
network=network,
|
|
268
|
+
alias=task.alias,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
# Update the task information
|
|
272
|
+
self.tasks.append(
|
|
273
|
+
TaskProgression(
|
|
274
|
+
task_update=TaskUpdate(
|
|
275
|
+
swarm_id=swarm_id.oid,
|
|
276
|
+
node_id=self.tracer.metadata.node_id,
|
|
277
|
+
task_id=task_id.oid,
|
|
278
|
+
iteration=task.iteration,
|
|
279
|
+
circular=task.circular,
|
|
280
|
+
task_status=TaskStatus.PENDING,
|
|
281
|
+
),
|
|
282
|
+
module_file=module_file,
|
|
283
|
+
start_time=start_time,
|
|
284
|
+
)
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
async def exec_task(self, task: MqttTask) -> ID:
|
|
288
|
+
"""
|
|
289
|
+
Deploy a task
|
|
290
|
+
|
|
291
|
+
Parameters
|
|
292
|
+
----------
|
|
293
|
+
task : MqttTask
|
|
294
|
+
Task
|
|
295
|
+
|
|
296
|
+
Returns
|
|
297
|
+
-------
|
|
298
|
+
ID
|
|
299
|
+
Task ID
|
|
300
|
+
"""
|
|
301
|
+
task_id = ID(task.task_id)
|
|
302
|
+
swarm_id = ID(task.swarm_id)
|
|
303
|
+
self.tracer.info(f"Deploying task {task_id}")
|
|
304
|
+
|
|
305
|
+
if (swarm_id.tid, task_id.tid) in self.tasks:
|
|
306
|
+
await self.restart_task(self.tasks[(swarm_id.tid, task_id.tid)], task)
|
|
307
|
+
else:
|
|
308
|
+
await self.create_task(task)
|
|
309
|
+
return task_id
|
|
310
|
+
|
|
311
|
+
async def restart_task(self, task_progression: TaskProgression, task: MqttTask):
|
|
312
|
+
"""
|
|
313
|
+
Restart a task
|
|
314
|
+
|
|
315
|
+
Parameters
|
|
316
|
+
----------
|
|
317
|
+
task_progression : TaskProgression
|
|
318
|
+
Task Progression
|
|
319
|
+
task : MqttTask
|
|
320
|
+
Task from MQTT
|
|
321
|
+
"""
|
|
322
|
+
swarm_id = task_progression.swarm_id
|
|
323
|
+
task_id = task_progression.task_id
|
|
324
|
+
self.tracer.set_metadata(
|
|
325
|
+
task_id=task_id,
|
|
326
|
+
swarm_id=swarm_id,
|
|
327
|
+
iteration=task.iteration,
|
|
328
|
+
circular=task.circular,
|
|
329
|
+
)
|
|
330
|
+
# Use domain service to check if task should restart
|
|
331
|
+
if not TaskLifecycleService.should_restart_task(
|
|
332
|
+
task_progression.task_update.iteration,
|
|
333
|
+
task.iteration,
|
|
334
|
+
task_progression.task_update.circular,
|
|
335
|
+
task.circular,
|
|
336
|
+
):
|
|
337
|
+
# Same iteration/circular - this is a duplicate message, ignore it
|
|
338
|
+
self.tracer.warning(
|
|
339
|
+
f"Ignoring duplicate deploy message for task {task_id} "
|
|
340
|
+
f"(iteration={task.iteration}, circular={task.circular}). "
|
|
341
|
+
f"Task already exists with same parameters."
|
|
342
|
+
)
|
|
343
|
+
return
|
|
344
|
+
|
|
345
|
+
self.tracer.debug(f"Task {task_id} is already created, restarting it")
|
|
346
|
+
await self.from_completed_to_pending(task_progression, task)
|
|
347
|
+
self.tracer.info(f"Task {task_id} restarted successfully")
|
|
348
|
+
|
|
349
|
+
async def get_task_logs(
|
|
350
|
+
self, task_progression: TaskProgression
|
|
351
|
+
) -> Tuple[datetime, bytes]:
|
|
352
|
+
"""
|
|
353
|
+
Send the logs to the Manager
|
|
354
|
+
|
|
355
|
+
Parameters
|
|
356
|
+
----------
|
|
357
|
+
task_id : str
|
|
358
|
+
Task ID
|
|
359
|
+
|
|
360
|
+
Raises
|
|
361
|
+
------
|
|
362
|
+
TaskManagerException
|
|
363
|
+
If an error occurs during the add_task_logs
|
|
364
|
+
"""
|
|
365
|
+
task_id = task_progression.task_id
|
|
366
|
+
swarm_id = task_progression.swarm_id
|
|
367
|
+
self.tracer.set_metadata(
|
|
368
|
+
task_id=task_id,
|
|
369
|
+
swarm_id=swarm_id,
|
|
370
|
+
iteration=task_progression.task_update.iteration,
|
|
371
|
+
circular=task_progression.task_update.circular,
|
|
372
|
+
)
|
|
373
|
+
logs_bytes = await self.get_container_logs(
|
|
374
|
+
swarm_id,
|
|
375
|
+
task_id,
|
|
376
|
+
since=task_progression.start_time,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
return self.tracer.store_raw_logs(logs_bytes)
|
|
380
|
+
|
|
381
|
+
async def deploy_task(self, message: bytes):
|
|
382
|
+
"""
|
|
383
|
+
Convert the message into a MqttTask and add it to the
|
|
384
|
+
task queue
|
|
385
|
+
|
|
386
|
+
Parameters
|
|
387
|
+
----------
|
|
388
|
+
message : bytes
|
|
389
|
+
Message
|
|
390
|
+
|
|
391
|
+
Raises
|
|
392
|
+
------
|
|
393
|
+
MantaMQTTError
|
|
394
|
+
Could not convert the message
|
|
395
|
+
"""
|
|
396
|
+
try:
|
|
397
|
+
task = MqttTask().parse(message)
|
|
398
|
+
task_id = ID(task.task_id)
|
|
399
|
+
swarm_id = ID(task.swarm_id)
|
|
400
|
+
self.tracer.set_metadata(
|
|
401
|
+
task_id=task_id,
|
|
402
|
+
swarm_id=swarm_id,
|
|
403
|
+
iteration=task.iteration,
|
|
404
|
+
circular=task.circular,
|
|
405
|
+
)
|
|
406
|
+
self.task_queue.append(task)
|
|
407
|
+
self.tracer.info(f"Task {task_id} added to queue")
|
|
408
|
+
except Exception as exc:
|
|
409
|
+
trace_message = f"Unable to convert message into 'MqttTask': {repr(exc)}"
|
|
410
|
+
self.tracer.exception(trace_message)
|
|
411
|
+
raise MantaMQTTError(trace_message, metadata=self.tracer.metadata)
|
|
412
|
+
|
|
413
|
+
async def stop_task(self, message: bytes) -> Optional[TaskUpdate]:
|
|
414
|
+
"""
|
|
415
|
+
Stop a task
|
|
416
|
+
|
|
417
|
+
Parameters
|
|
418
|
+
----------
|
|
419
|
+
message : bytes
|
|
420
|
+
Message from MQTT client
|
|
421
|
+
|
|
422
|
+
Returns
|
|
423
|
+
-------
|
|
424
|
+
Optional[TaskUpdate]
|
|
425
|
+
Task update
|
|
426
|
+
"""
|
|
427
|
+
try:
|
|
428
|
+
env_id = EnvId().parse(message)
|
|
429
|
+
swarm_id = ID(env_id.swarm_id)
|
|
430
|
+
task_id = ID(env_id.task_id)
|
|
431
|
+
task_progression = self.tasks[(swarm_id.tid, task_id.tid)]
|
|
432
|
+
self.tracer.set_metadata(
|
|
433
|
+
task_id=task_id,
|
|
434
|
+
swarm_id=swarm_id,
|
|
435
|
+
iteration=task_progression.task_update.iteration,
|
|
436
|
+
circular=task_progression.task_update.circular,
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
# Stop the container
|
|
440
|
+
await self.stop_container(swarm_id, task_id)
|
|
441
|
+
|
|
442
|
+
# Set the new TaskStatus "Stopped"
|
|
443
|
+
task_progression.task_status = TaskStatus.STOPPED
|
|
444
|
+
self.tracer.info(f"Task {task_id} stopped successfully")
|
|
445
|
+
return task_progression.task_update
|
|
446
|
+
except MantaGRPCError as e:
|
|
447
|
+
self.tracer.exception(f"gRPCError when stopping task: {e}")
|
|
448
|
+
except MantaError as manta_error:
|
|
449
|
+
self.tracer.error("Unable to stop task.")
|
|
450
|
+
raise manta_error
|
|
451
|
+
except Exception as exc:
|
|
452
|
+
trace_message = f"Unable to stop task: {repr(exc)}"
|
|
453
|
+
self.tracer.exception(trace_message)
|
|
454
|
+
raise MantaNodeError(trace_message, metadata=self.tracer.metadata)
|
|
455
|
+
|
|
456
|
+
# TODO : add decorator for loop
|
|
457
|
+
async def stop_swarm(self, message: bytes):
|
|
458
|
+
"""
|
|
459
|
+
Stop a swarm
|
|
460
|
+
|
|
461
|
+
Parameters
|
|
462
|
+
----------
|
|
463
|
+
swarm_id : bytess
|
|
464
|
+
Swarm ID
|
|
465
|
+
"""
|
|
466
|
+
swarm_id = ID(PairId().parse(message))
|
|
467
|
+
self.tracer.set_metadata(swarm_id=swarm_id)
|
|
468
|
+
|
|
469
|
+
def match_swarm_id(task_progression: TaskProgression):
|
|
470
|
+
return task_progression.swarm_id == swarm_id
|
|
471
|
+
|
|
472
|
+
# Convert to list to avoid concurrent modification during iteration
|
|
473
|
+
tasks_to_stop = list(self.tasks.filter(match_swarm_id))
|
|
474
|
+
for task_progression in tasks_to_stop:
|
|
475
|
+
try:
|
|
476
|
+
await self.stop_and_remove_task(task_progression)
|
|
477
|
+
except MantaGRPCError as e:
|
|
478
|
+
self.tracer.exception(f"gRPCError when stopping swarm: {e}")
|
|
479
|
+
except MantaError as manta_error:
|
|
480
|
+
self.tracer.exception(
|
|
481
|
+
f"Unable to stop task {task_progression.task_id}"
|
|
482
|
+
f" of swarm {swarm_id}"
|
|
483
|
+
)
|
|
484
|
+
await self.node_client.send_error(manta_error)
|
|
485
|
+
except Exception as exc:
|
|
486
|
+
trace_message = (
|
|
487
|
+
f"Unable to stop task {task_progression.task_id}"
|
|
488
|
+
f" of swarm {swarm_id}: {repr(exc)}"
|
|
489
|
+
)
|
|
490
|
+
self.tracer.exception(trace_message)
|
|
491
|
+
await self.node_client.send_error(
|
|
492
|
+
MantaNodeError(trace_message, metadata=self.tracer.metadata)
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
self.tracer.info(f"All tasks in swarm {swarm_id} stopped successfully")
|
|
496
|
+
|
|
497
|
+
async def stop_and_remove_task(self, task_progression: TaskProgression):
|
|
498
|
+
"""
|
|
499
|
+
Stop task's container, remove the task's container and remove the task
|
|
500
|
+
|
|
501
|
+
Parameters
|
|
502
|
+
----------
|
|
503
|
+
task_progression : TaskProgression
|
|
504
|
+
Task Progression
|
|
505
|
+
"""
|
|
506
|
+
await self.stop_container(
|
|
507
|
+
task_progression.swarm_id,
|
|
508
|
+
task_progression.task_id,
|
|
509
|
+
)
|
|
510
|
+
await self.remove_container(
|
|
511
|
+
task_progression.swarm_id,
|
|
512
|
+
task_progression.task_id,
|
|
513
|
+
)
|
|
514
|
+
self.tasks.pop(
|
|
515
|
+
(
|
|
516
|
+
task_progression.swarm_id.tid,
|
|
517
|
+
task_progression.task_id.tid,
|
|
518
|
+
)
|
|
519
|
+
)
|