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,462 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import socket
|
|
3
|
+
import struct
|
|
4
|
+
import sys
|
|
5
|
+
import uuid
|
|
6
|
+
from logging import getLogger
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Dict, Optional, Union
|
|
9
|
+
|
|
10
|
+
import blake3
|
|
11
|
+
from aiodocker.exceptions import DockerError
|
|
12
|
+
from docker.errors import DockerException
|
|
13
|
+
from grpclib.exceptions import GRPCError
|
|
14
|
+
from grpclib.server import Server
|
|
15
|
+
|
|
16
|
+
from manta_common.build.common.informations import (
|
|
17
|
+
NodeOverview,
|
|
18
|
+
NodeStatus,
|
|
19
|
+
NodeStatusEnum,
|
|
20
|
+
)
|
|
21
|
+
from manta_common.build.common.system import Empty
|
|
22
|
+
from manta_common.conversions import HEX_SIZE, ID, oid
|
|
23
|
+
from manta_common.errors import MantaError
|
|
24
|
+
from manta_common.traces import Tracer
|
|
25
|
+
|
|
26
|
+
# NEW - use domain logic
|
|
27
|
+
|
|
28
|
+
# In node_orchestrator.py
|
|
29
|
+
from .infrastructure.config.node_config_manager import NodeConfigManager
|
|
30
|
+
from .infrastructure.filesystem.dataset_manager import DatasetManager
|
|
31
|
+
from .infrastructure.grpc.client import NodeClient
|
|
32
|
+
from .infrastructure.grpc.local_servicer import LocalServicer
|
|
33
|
+
from .infrastructure.grpc.world_servicer import WorldServicer
|
|
34
|
+
from .infrastructure.metrics.metrics_collector import GenericMetricsCollector
|
|
35
|
+
from .infrastructure.mqtt.command_handler import CommandHandler
|
|
36
|
+
|
|
37
|
+
__all__ = ["Node"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def make_id(random_id: bool, alias: Optional[str] = None) -> ID:
|
|
41
|
+
"""
|
|
42
|
+
Returns the node ID
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
random_id : bool
|
|
47
|
+
If :code:`True`, the ID is randomized
|
|
48
|
+
alias : Optional[str]
|
|
49
|
+
Alias name of the node
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
ID
|
|
54
|
+
Node ID
|
|
55
|
+
"""
|
|
56
|
+
if alias is not None:
|
|
57
|
+
return ID(oid(alias))
|
|
58
|
+
elif random_id:
|
|
59
|
+
return ID(oid(uuid.uuid4().hex))
|
|
60
|
+
else:
|
|
61
|
+
return ID(
|
|
62
|
+
oid(blake3.blake3(struct.pack("Q", uuid.getnode())).hexdigest(HEX_SIZE))
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def find_available_port(start_port: int = 50051, max_attempts: int = 100) -> int:
|
|
67
|
+
"""
|
|
68
|
+
Find an available port for the light service.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
start_port : int
|
|
73
|
+
The port to start searching from (default: 50051)
|
|
74
|
+
max_attempts : int
|
|
75
|
+
Maximum number of ports to try (default: 100)
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
int
|
|
80
|
+
An available port number
|
|
81
|
+
|
|
82
|
+
Raises
|
|
83
|
+
------
|
|
84
|
+
RuntimeError
|
|
85
|
+
If no available port is found after max_attempts
|
|
86
|
+
"""
|
|
87
|
+
for port_offset in range(max_attempts):
|
|
88
|
+
port = start_port + port_offset
|
|
89
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
90
|
+
try:
|
|
91
|
+
# Try to bind to the port
|
|
92
|
+
sock.bind(("", port))
|
|
93
|
+
sock.close()
|
|
94
|
+
return port
|
|
95
|
+
except OSError:
|
|
96
|
+
# Port is in use, try the next one
|
|
97
|
+
sock.close()
|
|
98
|
+
continue
|
|
99
|
+
|
|
100
|
+
raise RuntimeError(
|
|
101
|
+
f"Could not find an available port in range {start_port}-{start_port + max_attempts - 1}"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class Node:
|
|
106
|
+
"""
|
|
107
|
+
Node class runs on embedded device and executes tasks requested
|
|
108
|
+
by the Manager
|
|
109
|
+
|
|
110
|
+
It is composed of several components :
|
|
111
|
+
|
|
112
|
+
* the :code:`CommandHandler` which deploys tasks in containers \
|
|
113
|
+
and manages tasks given available resources
|
|
114
|
+
* the :code:`LocalServicer` to allow containers to access local data on the device
|
|
115
|
+
* the :code:`WorldServicer` to allow containers to access data stored in the Manager
|
|
116
|
+
* the :code:`MQTTClient` for gathering MQTT messages from the Manager
|
|
117
|
+
* the :code:`DatasetManager` for handling datasets and providing metadata
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
config : Union[NodeConfigManager, str, Path]
|
|
122
|
+
NodeConfigManager instance or path to configuration file.
|
|
123
|
+
If string/Path is provided, a NodeConfigManager will be created.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
__slots__ = [
|
|
127
|
+
"config_manager",
|
|
128
|
+
"config",
|
|
129
|
+
"node_id",
|
|
130
|
+
"node_client",
|
|
131
|
+
"task_runner",
|
|
132
|
+
"command_handler",
|
|
133
|
+
"server",
|
|
134
|
+
"tracer",
|
|
135
|
+
"cert_folder",
|
|
136
|
+
"dataset_manager",
|
|
137
|
+
"metrics_collector",
|
|
138
|
+
"command_loop",
|
|
139
|
+
"light_server_task",
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
def __init__(self, config: Union[NodeConfigManager, str, Path]):
|
|
143
|
+
# Initialize configuration manager
|
|
144
|
+
if isinstance(config, NodeConfigManager):
|
|
145
|
+
self.config_manager = config
|
|
146
|
+
else:
|
|
147
|
+
# Create NodeConfigManager from path
|
|
148
|
+
self.config_manager = NodeConfigManager(config)
|
|
149
|
+
|
|
150
|
+
# Get configuration
|
|
151
|
+
self.config = self.config_manager.config
|
|
152
|
+
|
|
153
|
+
# Validate configuration
|
|
154
|
+
issues = self.config_manager.validate()
|
|
155
|
+
if issues:
|
|
156
|
+
raise MantaError(f"Configuration validation failed: {'; '.join(issues)}")
|
|
157
|
+
|
|
158
|
+
# Initialize tracer with logging configuration
|
|
159
|
+
self.tracer = Tracer(getLogger(__name__), None)
|
|
160
|
+
|
|
161
|
+
# Certificate folder setup (if TLS enabled)
|
|
162
|
+
if self.config.security.use_tls:
|
|
163
|
+
self.cert_folder = Path(self.config.security.cert_folder).expanduser()
|
|
164
|
+
else:
|
|
165
|
+
self.cert_folder = None
|
|
166
|
+
|
|
167
|
+
self.tracer.info(f"Configuration: {self.config.metadata.name}")
|
|
168
|
+
|
|
169
|
+
# Initialize the dataset manager with configuration
|
|
170
|
+
self.dataset_manager = DatasetManager(self.config.datasets.mappings)
|
|
171
|
+
|
|
172
|
+
# Validate dataset paths exist and are accessible
|
|
173
|
+
dataset_issues = self.dataset_manager.validate_datasets()
|
|
174
|
+
if dataset_issues:
|
|
175
|
+
error_msg = "Dataset validation failed:\n - " + "\n - ".join(
|
|
176
|
+
dataset_issues
|
|
177
|
+
)
|
|
178
|
+
self.tracer.error(error_msg)
|
|
179
|
+
raise MantaError(error_msg)
|
|
180
|
+
|
|
181
|
+
# Initialize the metrics collector
|
|
182
|
+
self.metrics_collector = GenericMetricsCollector()
|
|
183
|
+
|
|
184
|
+
# Initialize node client with configuration
|
|
185
|
+
self.node_client = NodeClient(
|
|
186
|
+
manager_host=self.config.network.manager_host,
|
|
187
|
+
manager_port=self.config.network.manager_port,
|
|
188
|
+
secured_token=self.config.identity.secured_token,
|
|
189
|
+
ssl_cert_folder=self.cert_folder,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# Initialize the command handler, the command loop and light server task
|
|
193
|
+
self.command_handler = None
|
|
194
|
+
self.command_loop = None
|
|
195
|
+
self.light_server_task = None
|
|
196
|
+
|
|
197
|
+
# Register configuration observer for runtime updates
|
|
198
|
+
self.config_manager.register_observer(self._on_config_change)
|
|
199
|
+
|
|
200
|
+
def _ensure_light_service_port(self) -> None:
|
|
201
|
+
"""
|
|
202
|
+
Ensure light service port is assigned.
|
|
203
|
+
|
|
204
|
+
If not specified in configuration, automatically finds an available port.
|
|
205
|
+
Logs the assigned port for debugging purposes.
|
|
206
|
+
"""
|
|
207
|
+
if self.config.network.light_service_port is None:
|
|
208
|
+
self.tracer.info(
|
|
209
|
+
"Light service port not specified, finding an available port..."
|
|
210
|
+
)
|
|
211
|
+
self.config.network.light_service_port = find_available_port()
|
|
212
|
+
self.tracer.info(
|
|
213
|
+
f"Automatically assigned light service port: {self.config.network.light_service_port}"
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def _on_config_change(self, change: Dict[str, Any]) -> None:
|
|
217
|
+
"""Handle configuration changes at runtime.
|
|
218
|
+
|
|
219
|
+
Parameters
|
|
220
|
+
----------
|
|
221
|
+
change : Dict[str, Any]
|
|
222
|
+
Dictionary describing the configuration change
|
|
223
|
+
"""
|
|
224
|
+
self.tracer.info(f"Configuration changed: {change}")
|
|
225
|
+
|
|
226
|
+
# Handle safe runtime updates
|
|
227
|
+
if change.get("action") == "update":
|
|
228
|
+
updates = change.get("updates", {})
|
|
229
|
+
|
|
230
|
+
# Update logging level if changed
|
|
231
|
+
if "logging.level" in updates:
|
|
232
|
+
import logging
|
|
233
|
+
|
|
234
|
+
logging.getLogger().setLevel(updates["logging.level"])
|
|
235
|
+
self.tracer.info(f"Updated logging level to {updates['logging.level']}")
|
|
236
|
+
|
|
237
|
+
# Update dataset mappings if changed
|
|
238
|
+
if "datasets.mappings" in updates:
|
|
239
|
+
self.dataset_manager.update_datasets(updates["datasets.mappings"])
|
|
240
|
+
self.tracer.info("Updated dataset mappings")
|
|
241
|
+
|
|
242
|
+
# Update resource limits if changed
|
|
243
|
+
if any(key.startswith("resources.") for key in updates):
|
|
244
|
+
if self.command_handler:
|
|
245
|
+
# Command handler can update resource limits
|
|
246
|
+
self.tracer.info("Resource limits updated")
|
|
247
|
+
|
|
248
|
+
async def check_manager_availability(self) -> None:
|
|
249
|
+
"""
|
|
250
|
+
Check if the manager is available.
|
|
251
|
+
|
|
252
|
+
If secured mode is activated, this function will get the
|
|
253
|
+
certificates from the manager.
|
|
254
|
+
Then, it will check if the manager is available by calling
|
|
255
|
+
the `is_available` function of the node client.
|
|
256
|
+
If the manager is not available, it will log an error and
|
|
257
|
+
exit with a non-zero status code.
|
|
258
|
+
|
|
259
|
+
Raises
|
|
260
|
+
------
|
|
261
|
+
SystemExit
|
|
262
|
+
If the manager is not available.
|
|
263
|
+
"""
|
|
264
|
+
# Log endpoint details before attempting connection
|
|
265
|
+
security_mode = (
|
|
266
|
+
"TLS"
|
|
267
|
+
if (self.cert_folder is not None or self.config.network.manager_port == 443)
|
|
268
|
+
else "insecure"
|
|
269
|
+
)
|
|
270
|
+
endpoint = f"{security_mode}://{self.config.network.manager_host}:{self.config.network.manager_port}"
|
|
271
|
+
|
|
272
|
+
self.tracer.info(f"Checking manager availability at {endpoint}")
|
|
273
|
+
|
|
274
|
+
try:
|
|
275
|
+
# If secured mode, get the certificates
|
|
276
|
+
# if (
|
|
277
|
+
# self.secured_token
|
|
278
|
+
# ): # TODO: refactor with node bootstrap certificate and new identity service in node servicer
|
|
279
|
+
# raise NotImplementedError("Secured mode is not implemented yet")
|
|
280
|
+
await self.node_client.is_available(Empty())
|
|
281
|
+
self.tracer.info(f"Manager is available at {endpoint}")
|
|
282
|
+
except (GRPCError, ConnectionRefusedError) as error:
|
|
283
|
+
message = f"Manager is not available at {endpoint}: {error}"
|
|
284
|
+
self.tracer.exception(message)
|
|
285
|
+
|
|
286
|
+
# Provide troubleshooting guidance
|
|
287
|
+
troubleshooting = (
|
|
288
|
+
f"\n[ERROR] {message}\n"
|
|
289
|
+
f"\nTroubleshooting steps:\n"
|
|
290
|
+
f" 1. Verify endpoint: {endpoint}\n"
|
|
291
|
+
f" 2. Check if manager services are running\n"
|
|
292
|
+
f" 3. Verify JWT_SECRET matches between node and manager\n"
|
|
293
|
+
f" 4. Check network connectivity to {self.config.network.manager_host}:{self.config.network.manager_port}\n"
|
|
294
|
+
)
|
|
295
|
+
sys.exit(troubleshooting)
|
|
296
|
+
except MantaError as manta_error:
|
|
297
|
+
message = f"Manager authentication failed at {endpoint}: {manta_error}"
|
|
298
|
+
self.tracer.exception(message)
|
|
299
|
+
|
|
300
|
+
# Provide specific guidance for authentication errors
|
|
301
|
+
troubleshooting = (
|
|
302
|
+
f"\n[ERROR] {message}\n"
|
|
303
|
+
f"\nAuthentication issue detected:\n"
|
|
304
|
+
f" - Endpoint: {endpoint}\n"
|
|
305
|
+
f" - This usually means JWT_SECRET mismatch between node and manager\n"
|
|
306
|
+
f" - Verify secured_token in node config matches manager's JWT_SECRET\n"
|
|
307
|
+
f" - Check token format: should be a valid JWT with 3 parts (header.payload.signature)\n"
|
|
308
|
+
)
|
|
309
|
+
sys.exit(troubleshooting)
|
|
310
|
+
except Exception as exc:
|
|
311
|
+
message = f"Unknown error connecting to {endpoint}: {exc}"
|
|
312
|
+
self.tracer.exception(message)
|
|
313
|
+
sys.exit(f"[ERROR] {message}")
|
|
314
|
+
|
|
315
|
+
async def start(self) -> None:
|
|
316
|
+
"""
|
|
317
|
+
Start the Node:
|
|
318
|
+
|
|
319
|
+
- Register with the Manager
|
|
320
|
+
- Start the MQTT client
|
|
321
|
+
- Subscribe to topics
|
|
322
|
+
- Start the Light Server
|
|
323
|
+
- Start the MQTT client loop
|
|
324
|
+
|
|
325
|
+
Raises
|
|
326
|
+
------
|
|
327
|
+
SystemExit
|
|
328
|
+
If MQTT client unreacheable or gRPC error occurs
|
|
329
|
+
"""
|
|
330
|
+
await self.check_manager_availability()
|
|
331
|
+
try:
|
|
332
|
+
identification = NodeOverview(
|
|
333
|
+
self.metrics_collector.get_platform_info(),
|
|
334
|
+
self.dataset_manager.to_proto(),
|
|
335
|
+
)
|
|
336
|
+
registration = await self.node_client.register_node(identification)
|
|
337
|
+
|
|
338
|
+
# Initialize, start and prepare MQTT Client
|
|
339
|
+
self.tracer.debug(f"Registration: {registration}")
|
|
340
|
+
mqtt_broker_host = registration.mqtt_broker_host
|
|
341
|
+
mqtt_broker_port = registration.mqtt_broker_port
|
|
342
|
+
topics = registration.mqtt_topics
|
|
343
|
+
node_token = registration.node_token
|
|
344
|
+
|
|
345
|
+
# Update MQTT configuration with registration data
|
|
346
|
+
self.config.mqtt.broker_host = mqtt_broker_host
|
|
347
|
+
self.config.mqtt.broker_port = mqtt_broker_port
|
|
348
|
+
self.config.mqtt.topics = topics
|
|
349
|
+
|
|
350
|
+
# Save the node token to config file
|
|
351
|
+
if not node_token:
|
|
352
|
+
error_msg = "Manager did not return a node token during registration"
|
|
353
|
+
self.tracer.error(error_msg)
|
|
354
|
+
raise MantaError(error_msg)
|
|
355
|
+
|
|
356
|
+
self.tracer.info("Received node token from manager, updating config")
|
|
357
|
+
self.config.identity.secured_token = node_token
|
|
358
|
+
self.config_manager.save()
|
|
359
|
+
|
|
360
|
+
# Reinitialize the node client with the new token
|
|
361
|
+
self.node_client = NodeClient(
|
|
362
|
+
manager_host=self.config.network.manager_host,
|
|
363
|
+
manager_port=self.config.network.manager_port,
|
|
364
|
+
secured_token=node_token,
|
|
365
|
+
ssl_cert_folder=self.cert_folder,
|
|
366
|
+
)
|
|
367
|
+
|
|
368
|
+
# Ensure light service port is assigned
|
|
369
|
+
self._ensure_light_service_port()
|
|
370
|
+
|
|
371
|
+
# Initialize the command handler with configuration
|
|
372
|
+
self.tracer.info("Starting Command Handler")
|
|
373
|
+
self.command_handler = CommandHandler(
|
|
374
|
+
node_client=self.node_client,
|
|
375
|
+
mqtt_broker_host=mqtt_broker_host,
|
|
376
|
+
mqtt_broker_port=mqtt_broker_port,
|
|
377
|
+
light_service_port=self.config.network.light_service_port or 50052,
|
|
378
|
+
topics=topics,
|
|
379
|
+
dataset_manager=self.dataset_manager,
|
|
380
|
+
metrics_collector=self.metrics_collector,
|
|
381
|
+
stop_callback=self.stop,
|
|
382
|
+
ssl_cert_folder=self.cert_folder,
|
|
383
|
+
# Pass additional configuration
|
|
384
|
+
ping_interval=self.config.mqtt.ping_interval,
|
|
385
|
+
max_concurrent_tasks=self.config.tasks.max_concurrent,
|
|
386
|
+
task_timeout=self.config.tasks.task_timeout,
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
# Start the server for docker communications through
|
|
390
|
+
# manta light with manta node
|
|
391
|
+
self.tracer.info(
|
|
392
|
+
f"Starting Light Server at {self.config.network.light_service_host}:"
|
|
393
|
+
f"{self.config.network.light_service_port}"
|
|
394
|
+
)
|
|
395
|
+
local_servicer = LocalServicer(
|
|
396
|
+
self.node_client,
|
|
397
|
+
self.dataset_manager,
|
|
398
|
+
)
|
|
399
|
+
world_servicer = WorldServicer(
|
|
400
|
+
self.node_client,
|
|
401
|
+
self.command_handler.tasks,
|
|
402
|
+
)
|
|
403
|
+
self.server = Server([local_servicer, world_servicer])
|
|
404
|
+
await self.server.start(
|
|
405
|
+
self.config.network.light_service_host,
|
|
406
|
+
self.config.network.light_service_port,
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
# Create asyncio tasks and start them
|
|
410
|
+
self.command_loop = asyncio.create_task(self.command_handler.main())
|
|
411
|
+
self.light_server_task = asyncio.create_task(self.server.wait_closed())
|
|
412
|
+
await asyncio.gather(self.command_loop, self.light_server_task)
|
|
413
|
+
except ConnectionRefusedError:
|
|
414
|
+
self.tracer.exception("Unable to start and connect to MQTT Message Broker")
|
|
415
|
+
sys.exit("[ERROR] Unable to start and connect to MQTT Message Broker")
|
|
416
|
+
except GRPCError:
|
|
417
|
+
self.tracer.exception("Unable to register to the Manager")
|
|
418
|
+
sys.exit("[ERROR] Unable to register to the Manager")
|
|
419
|
+
except OSError as os_error:
|
|
420
|
+
self.tracer.exception(f"OS error: {os_error}")
|
|
421
|
+
await self.stop()
|
|
422
|
+
sys.exit(f"[ERROR] OS error: {os_error}")
|
|
423
|
+
except (DockerException, DockerError) as docker_error:
|
|
424
|
+
self.tracer.exception(f"Docker is not available: {docker_error}")
|
|
425
|
+
await self.stop()
|
|
426
|
+
sys.exit(f"[ERROR] Docker is not available: {docker_error}")
|
|
427
|
+
except Exception as exc:
|
|
428
|
+
self.tracer.exception(f"Unknown error: {exc}")
|
|
429
|
+
await self.stop()
|
|
430
|
+
sys.exit(f"[ERROR] Unknown error: {exc}")
|
|
431
|
+
|
|
432
|
+
async def stop(self) -> None:
|
|
433
|
+
"""
|
|
434
|
+
Stop the node
|
|
435
|
+
"""
|
|
436
|
+
self.tracer.info("Stopping node")
|
|
437
|
+
if (
|
|
438
|
+
self.command_handler is not None
|
|
439
|
+
and self.command_loop is not None
|
|
440
|
+
and not self.command_loop.cancelled()
|
|
441
|
+
):
|
|
442
|
+
self.tracer.info("Publishing node status to manager")
|
|
443
|
+
await self.command_handler.client.publish(
|
|
444
|
+
"manager/set_node_status",
|
|
445
|
+
NodeStatus(status=NodeStatusEnum.DISCONNECTED).SerializeToString(),
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
if self.command_handler is not None:
|
|
449
|
+
self.tracer.info("Cleaning up command handler")
|
|
450
|
+
await self.command_handler.clean()
|
|
451
|
+
|
|
452
|
+
if self.command_loop is not None:
|
|
453
|
+
self.tracer.info("Stopping command loop")
|
|
454
|
+
self.command_loop.cancel()
|
|
455
|
+
|
|
456
|
+
if self.light_server_task is not None:
|
|
457
|
+
self.tracer.info("Stopping light server task")
|
|
458
|
+
self.light_server_task.cancel()
|
|
459
|
+
|
|
460
|
+
# if self.server is not None:
|
|
461
|
+
# self.tracer.info("Stopping light server")
|
|
462
|
+
# self.server.close()
|