manta-node 0.5b0.dev245__py3-none-any.whl → 0.5b0.dev259__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/__main__.py +1 -3
- manta_node/cli/commands/cluster.py +3 -4
- manta_node/cli/commands/config.py +2 -3
- manta_node/cli/commands/logs.py +3 -4
- manta_node/cli/commands/start.py +3 -6
- manta_node/cli/commands/status.py +5 -7
- manta_node/cli/commands/stop.py +1 -2
- manta_node/cli/config_manager.py +7 -7
- manta_node/cli/main.py +2 -3
- manta_node/cli/version.py +3 -3
- manta_node/domain/task_lifecycle.py +3 -1
- manta_node/infrastructure/config/__init__.py +9 -9
- manta_node/infrastructure/config/node_config_manager.py +49 -41
- manta_node/infrastructure/container/docker_adapter.py +34 -7
- manta_node/infrastructure/container/image_executor.py +3 -6
- manta_node/infrastructure/container/manager.py +33 -34
- manta_node/infrastructure/filesystem/dataset_manager.py +7 -6
- manta_node/infrastructure/grpc/__init__.py +1 -1
- manta_node/infrastructure/grpc/client.py +7 -12
- manta_node/infrastructure/grpc/local_servicer.py +4 -2
- manta_node/infrastructure/grpc/world_servicer.py +3 -2
- manta_node/infrastructure/metrics/collector.py +2 -1
- manta_node/infrastructure/metrics/metrics_collector.py +3 -9
- manta_node/infrastructure/mqtt/command_handler.py +9 -8
- manta_node/node_orchestrator.py +19 -19
- manta_node/task_manager.py +20 -20
- manta_node/tasks.py +9 -8
- manta_node/utils.py +1 -2
- {manta_node-0.5b0.dev245.dist-info → manta_node-0.5b0.dev259.dist-info}/METADATA +1 -1
- manta_node-0.5b0.dev259.dist-info/RECORD +43 -0
- manta_node-0.5b0.dev245.dist-info/RECORD +0 -43
- {manta_node-0.5b0.dev245.dist-info → manta_node-0.5b0.dev259.dist-info}/WHEEL +0 -0
- {manta_node-0.5b0.dev245.dist-info → manta_node-0.5b0.dev259.dist-info}/entry_points.txt +0 -0
- {manta_node-0.5b0.dev245.dist-info → manta_node-0.5b0.dev259.dist-info}/licenses/LICENSE +0 -0
- {manta_node-0.5b0.dev245.dist-info → manta_node-0.5b0.dev259.dist-info}/top_level.txt +0 -0
manta_node/__main__.py
CHANGED
|
@@ -40,9 +40,7 @@ def main():
|
|
|
40
40
|
]
|
|
41
41
|
|
|
42
42
|
if first_arg not in cli_commands and (
|
|
43
|
-
first_arg.endswith(".toml")
|
|
44
|
-
or first_arg.endswith(".yaml")
|
|
45
|
-
or first_arg.endswith(".json")
|
|
43
|
+
first_arg.endswith((".toml", ".yaml", ".json"))
|
|
46
44
|
or Path(first_arg).exists()
|
|
47
45
|
or (Path.home() / ".manta" / "nodes" / f"{first_arg}.toml").exists()
|
|
48
46
|
):
|
|
@@ -5,7 +5,6 @@ import subprocess
|
|
|
5
5
|
import sys
|
|
6
6
|
import time
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import List
|
|
9
8
|
|
|
10
9
|
from rich.console import Console
|
|
11
10
|
from rich.prompt import Confirm, Prompt
|
|
@@ -119,7 +118,7 @@ def start_cluster_multi_config(count: int) -> int:
|
|
|
119
118
|
"--background",
|
|
120
119
|
]
|
|
121
120
|
|
|
122
|
-
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
121
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
|
123
122
|
|
|
124
123
|
if result.returncode == 0:
|
|
125
124
|
started_nodes.append(node["alias"])
|
|
@@ -199,7 +198,7 @@ def start_cluster_simple(count: int, config_name: str) -> int:
|
|
|
199
198
|
"--background",
|
|
200
199
|
]
|
|
201
200
|
|
|
202
|
-
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
201
|
+
result = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
|
203
202
|
|
|
204
203
|
if result.returncode == 0:
|
|
205
204
|
started_nodes.append(node_alias)
|
|
@@ -289,7 +288,7 @@ def stop_cluster() -> int:
|
|
|
289
288
|
return 1
|
|
290
289
|
|
|
291
290
|
|
|
292
|
-
def cluster_command(args:
|
|
291
|
+
def cluster_command(args: list[str]) -> int:
|
|
293
292
|
"""Handle node cluster command.
|
|
294
293
|
|
|
295
294
|
Args:
|
|
@@ -5,7 +5,6 @@ import os
|
|
|
5
5
|
import subprocess
|
|
6
6
|
import sys
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import List
|
|
9
8
|
|
|
10
9
|
from rich.console import Console
|
|
11
10
|
from rich.panel import Panel
|
|
@@ -383,7 +382,7 @@ def edit_config(config_name: str) -> int:
|
|
|
383
382
|
console.print(f"Opening {config_path} in {editor}...")
|
|
384
383
|
|
|
385
384
|
try:
|
|
386
|
-
result = subprocess.run([editor, str(config_path)])
|
|
385
|
+
result = subprocess.run([editor, str(config_path)], check=False)
|
|
387
386
|
if result.returncode == 0:
|
|
388
387
|
print_success(f"Configuration '{config_name}' edited successfully")
|
|
389
388
|
|
|
@@ -410,7 +409,7 @@ def edit_config(config_name: str) -> int:
|
|
|
410
409
|
return 1
|
|
411
410
|
|
|
412
411
|
|
|
413
|
-
def config_command(args:
|
|
412
|
+
def config_command(args: list[str]) -> int:
|
|
414
413
|
"""Handle node config command.
|
|
415
414
|
|
|
416
415
|
Args:
|
manta_node/cli/commands/logs.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import argparse
|
|
4
4
|
import time
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import List
|
|
7
6
|
|
|
8
7
|
from rich.console import Console
|
|
9
8
|
|
|
@@ -73,7 +72,7 @@ def tail_log_file(log_file: Path, lines: int = 50, follow: bool = False) -> None
|
|
|
73
72
|
print_error(f"Error reading log file: {e}")
|
|
74
73
|
|
|
75
74
|
|
|
76
|
-
def logs_command(args:
|
|
75
|
+
def logs_command(args: list[str]) -> int:
|
|
77
76
|
"""Handle node logs command.
|
|
78
77
|
|
|
79
78
|
Args:
|
|
@@ -136,7 +135,7 @@ def logs_command(args: List[str]) -> int:
|
|
|
136
135
|
else:
|
|
137
136
|
# Multiple log files, ask user to specify
|
|
138
137
|
print_error("Multiple log files found. Please specify an instance:")
|
|
139
|
-
for instance_id in log_files
|
|
138
|
+
for instance_id in log_files:
|
|
140
139
|
console.print(f" - {instance_id}")
|
|
141
140
|
return 1
|
|
142
141
|
else:
|
|
@@ -153,7 +152,7 @@ def logs_command(args: List[str]) -> int:
|
|
|
153
152
|
print_error(f"No log file found for instance '{instance_name}'")
|
|
154
153
|
if log_files:
|
|
155
154
|
console.print("Available log files:")
|
|
156
|
-
for instance_id in log_files
|
|
155
|
+
for instance_id in log_files:
|
|
157
156
|
console.print(f" - {instance_id}")
|
|
158
157
|
return 1
|
|
159
158
|
|
manta_node/cli/commands/start.py
CHANGED
|
@@ -11,15 +11,12 @@ import time
|
|
|
11
11
|
import uuid
|
|
12
12
|
from datetime import datetime
|
|
13
13
|
from pathlib import Path
|
|
14
|
-
from typing import List
|
|
15
|
-
|
|
16
|
-
from rich.console import Console
|
|
17
14
|
|
|
18
15
|
from manta_common.logging_config import configure_logging
|
|
19
|
-
|
|
20
|
-
from ...node_orchestrator import Node
|
|
16
|
+
from rich.console import Console
|
|
21
17
|
|
|
22
18
|
from ...infrastructure.config import NodeConfigManager
|
|
19
|
+
from ...node_orchestrator import Node
|
|
23
20
|
|
|
24
21
|
console = Console()
|
|
25
22
|
|
|
@@ -302,7 +299,7 @@ def start_node_background(
|
|
|
302
299
|
return 1
|
|
303
300
|
|
|
304
301
|
|
|
305
|
-
def start_command(args:
|
|
302
|
+
def start_command(args: list[str]) -> int:
|
|
306
303
|
"""Handle node start command.
|
|
307
304
|
|
|
308
305
|
Args:
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"""Node status command implementation."""
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
+
import contextlib
|
|
4
5
|
import json
|
|
5
6
|
import os
|
|
6
7
|
from datetime import datetime
|
|
7
8
|
from pathlib import Path
|
|
8
|
-
from typing import List
|
|
9
9
|
|
|
10
10
|
import psutil
|
|
11
11
|
from rich.console import Console
|
|
@@ -59,10 +59,8 @@ def find_running_instances():
|
|
|
59
59
|
config_name = instance_data.get("config_name", "unknown")
|
|
60
60
|
start_time_str = instance_data.get("start_time")
|
|
61
61
|
if start_time_str and not create_time:
|
|
62
|
-
|
|
62
|
+
with contextlib.suppress(ValueError, TypeError):
|
|
63
63
|
create_time = datetime.fromisoformat(start_time_str)
|
|
64
|
-
except Exception:
|
|
65
|
-
pass
|
|
66
64
|
|
|
67
65
|
instances.append(
|
|
68
66
|
{
|
|
@@ -89,7 +87,7 @@ def find_running_instances():
|
|
|
89
87
|
return instances
|
|
90
88
|
|
|
91
89
|
|
|
92
|
-
def show_status_table(instances:
|
|
90
|
+
def show_status_table(instances: list[dict]) -> None:
|
|
93
91
|
"""Display instances in a formatted table."""
|
|
94
92
|
if not instances:
|
|
95
93
|
console.print("[yellow]No running node instances found.[/yellow]")
|
|
@@ -135,7 +133,7 @@ def show_status_table(instances: List[dict]) -> None:
|
|
|
135
133
|
console.print(table)
|
|
136
134
|
|
|
137
135
|
|
|
138
|
-
def show_status_plain(instances:
|
|
136
|
+
def show_status_plain(instances: list[dict]) -> None:
|
|
139
137
|
"""Display instances in plain text format."""
|
|
140
138
|
if not instances:
|
|
141
139
|
console.print("No running node instances found.")
|
|
@@ -164,7 +162,7 @@ def show_status_plain(instances: List[dict]) -> None:
|
|
|
164
162
|
console.print("")
|
|
165
163
|
|
|
166
164
|
|
|
167
|
-
def status_command(args:
|
|
165
|
+
def status_command(args: list[str]) -> int:
|
|
168
166
|
"""Handle node status command.
|
|
169
167
|
|
|
170
168
|
Args:
|
manta_node/cli/commands/stop.py
CHANGED
|
@@ -6,7 +6,6 @@ import os
|
|
|
6
6
|
import signal
|
|
7
7
|
import time
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import List
|
|
10
9
|
|
|
11
10
|
from rich.console import Console
|
|
12
11
|
|
|
@@ -202,7 +201,7 @@ def stop_specific_instance(
|
|
|
202
201
|
return 1
|
|
203
202
|
|
|
204
203
|
|
|
205
|
-
def stop_command(args:
|
|
204
|
+
def stop_command(args: list[str]) -> int:
|
|
206
205
|
"""Handle node stop command.
|
|
207
206
|
|
|
208
207
|
Args:
|
manta_node/cli/config_manager.py
CHANGED
|
@@ -6,7 +6,7 @@ but delegate to the new NodeConfiguration system.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from dataclasses import dataclass
|
|
9
|
-
from typing import Any
|
|
9
|
+
from typing import Any
|
|
10
10
|
|
|
11
11
|
from manta_node.infrastructure.config.node_config_manager import IdentityConfig
|
|
12
12
|
|
|
@@ -20,14 +20,14 @@ class ConnectionConfig:
|
|
|
20
20
|
host: str = "localhost"
|
|
21
21
|
port: int = 50051
|
|
22
22
|
use_tls: bool = False
|
|
23
|
-
cert_folder:
|
|
23
|
+
cert_folder: str | None = None
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
@dataclass
|
|
27
27
|
class HardwareConfig:
|
|
28
28
|
"""Compatibility class for old HardwareConfig."""
|
|
29
29
|
|
|
30
|
-
gpu_enabled:
|
|
30
|
+
gpu_enabled: bool | None = None
|
|
31
31
|
max_concurrent_tasks: int = 2
|
|
32
32
|
cpu_limit: str = "2.0"
|
|
33
33
|
memory_limit: str = "4G"
|
|
@@ -53,12 +53,12 @@ class NodeConfig:
|
|
|
53
53
|
name: str
|
|
54
54
|
alias: str
|
|
55
55
|
connection: ConnectionConfig
|
|
56
|
-
hardware:
|
|
57
|
-
datasets:
|
|
56
|
+
hardware: HardwareConfig | None = None
|
|
57
|
+
datasets: dict[str, str] | None = None
|
|
58
58
|
auto_register: bool = True
|
|
59
59
|
random_id: bool = False
|
|
60
|
-
created_at:
|
|
61
|
-
updated_at:
|
|
60
|
+
created_at: Any | None = None
|
|
61
|
+
updated_at: Any | None = None
|
|
62
62
|
|
|
63
63
|
def __post_init__(self):
|
|
64
64
|
"""Ensure datasets is a proper dict if provided."""
|
manta_node/cli/main.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Manta Node CLI main entry point."""
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
-
from typing import List, Optional
|
|
5
4
|
|
|
6
5
|
from rich.console import Console
|
|
7
6
|
|
|
@@ -11,7 +10,7 @@ from .commands.logs import logs_command
|
|
|
11
10
|
from .commands.start import start_command
|
|
12
11
|
from .commands.status import status_command
|
|
13
12
|
from .commands.stop import stop_command
|
|
14
|
-
from .version import
|
|
13
|
+
from .version import get_full_version_info, get_version_string
|
|
15
14
|
|
|
16
15
|
console = Console()
|
|
17
16
|
|
|
@@ -67,7 +66,7 @@ For more help on a specific command:
|
|
|
67
66
|
console.print(help_text)
|
|
68
67
|
|
|
69
68
|
|
|
70
|
-
def main(args:
|
|
69
|
+
def main(args: list[str] | None = None) -> int:
|
|
71
70
|
"""Main CLI entry point for manta-node.
|
|
72
71
|
|
|
73
72
|
Args:
|
manta_node/cli/version.py
CHANGED
|
@@ -8,10 +8,9 @@ Version information is loaded from:
|
|
|
8
8
|
import subprocess
|
|
9
9
|
from datetime import datetime
|
|
10
10
|
from pathlib import Path
|
|
11
|
-
from typing import Optional
|
|
12
11
|
|
|
13
12
|
|
|
14
|
-
def _load_build_version() ->
|
|
13
|
+
def _load_build_version() -> dict | None:
|
|
15
14
|
"""Try to load version from build-time generated file.
|
|
16
15
|
|
|
17
16
|
Returns:
|
|
@@ -32,7 +31,7 @@ def _load_build_version() -> Optional[dict]:
|
|
|
32
31
|
return None
|
|
33
32
|
|
|
34
33
|
|
|
35
|
-
def _get_git_commit_id() ->
|
|
34
|
+
def _get_git_commit_id() -> str | None:
|
|
36
35
|
"""Get the current git commit ID (short form) dynamically.
|
|
37
36
|
|
|
38
37
|
This is used as fallback when build-time version is not available.
|
|
@@ -50,6 +49,7 @@ def _get_git_commit_id() -> Optional[str]:
|
|
|
50
49
|
capture_output=True,
|
|
51
50
|
text=True,
|
|
52
51
|
timeout=2,
|
|
52
|
+
check=False,
|
|
53
53
|
)
|
|
54
54
|
|
|
55
55
|
if result.returncode == 0:
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# domain/task_lifecycle.py
|
|
2
2
|
"""Pure business logic for task lifecycle management."""
|
|
3
3
|
|
|
4
|
+
from typing import ClassVar
|
|
5
|
+
|
|
4
6
|
# Import domain entities (no infrastructure!)
|
|
5
7
|
from manta_common.build.common.tasks import TaskStatus
|
|
6
8
|
|
|
@@ -8,7 +10,7 @@ from manta_common.build.common.tasks import TaskStatus
|
|
|
8
10
|
class TaskTransitionRule:
|
|
9
11
|
"""Business rules for task state transitions."""
|
|
10
12
|
|
|
11
|
-
ALLOWED_TRANSITIONS = {
|
|
13
|
+
ALLOWED_TRANSITIONS: ClassVar[dict[TaskStatus, list[TaskStatus]]] = {
|
|
12
14
|
TaskStatus.PENDING: [TaskStatus.RUNNING, TaskStatus.FAILED],
|
|
13
15
|
TaskStatus.RUNNING: [
|
|
14
16
|
TaskStatus.COMPLETED,
|
|
@@ -8,24 +8,24 @@ from .node_config_manager import (
|
|
|
8
8
|
MetadataConfig,
|
|
9
9
|
MQTTConfig,
|
|
10
10
|
NetworkConfig,
|
|
11
|
-
NodeConfiguration,
|
|
12
11
|
NodeConfigManager,
|
|
12
|
+
NodeConfiguration,
|
|
13
13
|
ResourceConfig,
|
|
14
14
|
SecurityConfig,
|
|
15
15
|
TaskConfig,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
__all__ = [
|
|
19
|
-
"NodeConfigManager",
|
|
20
|
-
"NodeConfiguration",
|
|
21
|
-
"IdentityConfig",
|
|
22
|
-
"NetworkConfig",
|
|
23
|
-
"MQTTConfig",
|
|
24
19
|
"ContainerConfig",
|
|
25
20
|
"DatasetConfig",
|
|
26
|
-
"
|
|
27
|
-
"ResourceConfig",
|
|
21
|
+
"IdentityConfig",
|
|
28
22
|
"LoggingConfig",
|
|
29
|
-
"
|
|
23
|
+
"MQTTConfig",
|
|
30
24
|
"MetadataConfig",
|
|
25
|
+
"NetworkConfig",
|
|
26
|
+
"NodeConfigManager",
|
|
27
|
+
"NodeConfiguration",
|
|
28
|
+
"ResourceConfig",
|
|
29
|
+
"SecurityConfig",
|
|
30
|
+
"TaskConfig",
|
|
31
31
|
]
|
|
@@ -4,6 +4,7 @@ This module provides a comprehensive configuration management system for manta-n
|
|
|
4
4
|
using TOML files for persistent storage and dataclasses for type-safe configuration.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
import logging
|
|
7
8
|
import os
|
|
8
9
|
import threading
|
|
9
10
|
|
|
@@ -12,16 +13,19 @@ try:
|
|
|
12
13
|
except ImportError:
|
|
13
14
|
import tomli as tomllib # Python < 3.11 fallback
|
|
14
15
|
|
|
16
|
+
from collections.abc import Callable
|
|
15
17
|
from dataclasses import asdict, dataclass, field
|
|
16
18
|
from datetime import datetime
|
|
17
19
|
from pathlib import Path
|
|
18
|
-
from typing import Any,
|
|
20
|
+
from typing import Any, ClassVar
|
|
19
21
|
|
|
20
22
|
import toml # For writing TOML files
|
|
21
23
|
from manta_common.errors import MantaConfigurationError
|
|
22
24
|
|
|
23
25
|
__all__ = ["NodeConfigManager", "NodeConfiguration"]
|
|
24
26
|
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
25
29
|
|
|
26
30
|
# Configuration Section Dataclasses
|
|
27
31
|
|
|
@@ -31,7 +35,7 @@ class IdentityConfig:
|
|
|
31
35
|
"""Node identity configuration."""
|
|
32
36
|
|
|
33
37
|
secured_token: str # Required field - must be provided
|
|
34
|
-
alias:
|
|
38
|
+
alias: str | None = None
|
|
35
39
|
random_id: bool = False
|
|
36
40
|
|
|
37
41
|
|
|
@@ -42,7 +46,7 @@ class NetworkConfig:
|
|
|
42
46
|
manager_host: str = "localhost"
|
|
43
47
|
manager_port: int = 50051
|
|
44
48
|
light_service_host: str = "0.0.0.0"
|
|
45
|
-
light_service_port:
|
|
49
|
+
light_service_port: int | None = None # Auto-assigned if not specified
|
|
46
50
|
|
|
47
51
|
|
|
48
52
|
@dataclass
|
|
@@ -53,22 +57,22 @@ class MQTTConfig:
|
|
|
53
57
|
reconnect_attempts: int = 5
|
|
54
58
|
reconnect_delay: float = 5.0
|
|
55
59
|
# Runtime populated from manager registration
|
|
56
|
-
broker_host:
|
|
57
|
-
broker_port:
|
|
58
|
-
topics:
|
|
60
|
+
broker_host: str | None = None
|
|
61
|
+
broker_port: int | None = None
|
|
62
|
+
topics: list[str] = field(default_factory=list)
|
|
59
63
|
|
|
60
64
|
|
|
61
65
|
@dataclass
|
|
62
66
|
class ContainerConfig:
|
|
63
67
|
"""Container/Docker configuration."""
|
|
64
68
|
|
|
65
|
-
gpu_enabled:
|
|
69
|
+
gpu_enabled: bool | None = None # Auto-detect if None
|
|
66
70
|
default_memory_limit: str = "4G"
|
|
67
71
|
default_cpu_limit: str = "2.0"
|
|
68
72
|
docker_network: str = "host"
|
|
69
73
|
runtime: str = "docker" # docker or podman
|
|
70
74
|
pull_timeout: int = 300
|
|
71
|
-
log_config:
|
|
75
|
+
log_config: dict[str, Any] = field(
|
|
72
76
|
default_factory=lambda: {
|
|
73
77
|
"type": "json-file",
|
|
74
78
|
"config": {"max-size": "10m", "max-file": "3"},
|
|
@@ -82,7 +86,7 @@ class DatasetConfig:
|
|
|
82
86
|
|
|
83
87
|
base_path: str = "/data/datasets"
|
|
84
88
|
mount_readonly: bool = True
|
|
85
|
-
mappings:
|
|
89
|
+
mappings: dict[str, str] = field(default_factory=dict) # name -> path
|
|
86
90
|
cache_metadata: bool = True
|
|
87
91
|
|
|
88
92
|
|
|
@@ -93,7 +97,7 @@ class TaskConfig:
|
|
|
93
97
|
max_concurrent: int = 2
|
|
94
98
|
task_timeout: int = 3600 # seconds
|
|
95
99
|
retry_attempts: int = 3
|
|
96
|
-
temp_folder:
|
|
100
|
+
temp_folder: str | None = None
|
|
97
101
|
cleanup_on_failure: bool = True
|
|
98
102
|
stream_logs: bool = True
|
|
99
103
|
|
|
@@ -178,12 +182,12 @@ class NodeConfiguration:
|
|
|
178
182
|
Path.home() / ".manta" / "logs" / "nodes" / f"{safe_name}.log"
|
|
179
183
|
)
|
|
180
184
|
|
|
181
|
-
def to_dict(self) ->
|
|
185
|
+
def to_dict(self) -> dict[str, Any]:
|
|
182
186
|
"""Convert configuration to dictionary."""
|
|
183
187
|
return asdict(self)
|
|
184
188
|
|
|
185
189
|
@classmethod
|
|
186
|
-
def from_dict(cls, data:
|
|
190
|
+
def from_dict(cls, data: dict[str, Any]) -> "NodeConfiguration":
|
|
187
191
|
"""Create configuration from dictionary."""
|
|
188
192
|
# For identity, we need to ensure secured_token is present
|
|
189
193
|
identity_data = data.get("identity", {})
|
|
@@ -231,14 +235,14 @@ class NodeConfigManager:
|
|
|
231
235
|
"""
|
|
232
236
|
|
|
233
237
|
# Runtime update categories
|
|
234
|
-
SAFE_RUNTIME_UPDATES = [
|
|
238
|
+
SAFE_RUNTIME_UPDATES: ClassVar[list[str]] = [
|
|
235
239
|
"logging", # Can change log levels
|
|
236
240
|
"datasets.mappings", # Can add/remove datasets
|
|
237
241
|
"tasks.max_concurrent", # Can adjust concurrency
|
|
238
242
|
"resources", # Can adjust resource limits
|
|
239
243
|
]
|
|
240
244
|
|
|
241
|
-
REQUIRES_RESTART = [
|
|
245
|
+
REQUIRES_RESTART: ClassVar[list[str]] = [
|
|
242
246
|
"identity", # Node ID is immutable
|
|
243
247
|
"network", # Port changes need restart
|
|
244
248
|
"mqtt.broker_host", # MQTT connection needs restart
|
|
@@ -247,16 +251,16 @@ class NodeConfigManager:
|
|
|
247
251
|
"containers.runtime", # Runtime change needs restart
|
|
248
252
|
]
|
|
249
253
|
|
|
250
|
-
def __init__(self, config_path:
|
|
254
|
+
def __init__(self, config_path: str | Path | None = None):
|
|
251
255
|
"""Initialize configuration manager.
|
|
252
256
|
|
|
253
257
|
Args:
|
|
254
258
|
config_path: Path to configuration file or None for default
|
|
255
259
|
"""
|
|
256
260
|
self._lock = threading.RLock()
|
|
257
|
-
self._observers:
|
|
261
|
+
self._observers: list[Callable] = []
|
|
258
262
|
self._config = NodeConfiguration(IdentityConfig(secured_token="<REQUIRED>"))
|
|
259
|
-
self._instances:
|
|
263
|
+
self._instances: list[Any] = [] # Track running instances
|
|
260
264
|
|
|
261
265
|
# Determine config path
|
|
262
266
|
if config_path:
|
|
@@ -286,7 +290,7 @@ class NodeConfigManager:
|
|
|
286
290
|
return self.config
|
|
287
291
|
|
|
288
292
|
@current_config.setter
|
|
289
|
-
def current_config(self, config_dict:
|
|
293
|
+
def current_config(self, config_dict: dict[str, Any] | NodeConfiguration):
|
|
290
294
|
"""Set current configuration from dictionary or NodeConfiguration.
|
|
291
295
|
|
|
292
296
|
Args:
|
|
@@ -343,9 +347,9 @@ class NodeConfigManager:
|
|
|
343
347
|
|
|
344
348
|
# Print helpful message to inform user
|
|
345
349
|
print(f"📝 Created default configuration template at: {self._config_path}")
|
|
346
|
-
print(
|
|
350
|
+
print()
|
|
347
351
|
print("⚠️ IMPORTANT: Please set your secured_token before starting the node!")
|
|
348
|
-
print(
|
|
352
|
+
print()
|
|
349
353
|
print("Next steps:")
|
|
350
354
|
print(" 1. Get your JWT token from the manta manager")
|
|
351
355
|
print(f" 2. Edit the config file: {self._config_path}")
|
|
@@ -353,7 +357,7 @@ class NodeConfigManager:
|
|
|
353
357
|
print(
|
|
354
358
|
" 4. Or run interactive setup: manta_node config init <path/to/config.yaml>"
|
|
355
359
|
)
|
|
356
|
-
print(
|
|
360
|
+
print()
|
|
357
361
|
|
|
358
362
|
def _expand_env_vars(self, value: Any) -> Any:
|
|
359
363
|
"""Expand environment variables in configuration values.
|
|
@@ -407,7 +411,7 @@ class NodeConfigManager:
|
|
|
407
411
|
return [self._expand_paths(item) for item in value]
|
|
408
412
|
return value
|
|
409
413
|
|
|
410
|
-
def load(self, config_path:
|
|
414
|
+
def load(self, config_path: str | Path | None = None) -> NodeConfiguration:
|
|
411
415
|
"""Load configuration from TOML file.
|
|
412
416
|
|
|
413
417
|
Args:
|
|
@@ -448,7 +452,7 @@ class NodeConfigManager:
|
|
|
448
452
|
except Exception as e:
|
|
449
453
|
raise MantaConfigurationError(f"Failed to load configuration: {e}")
|
|
450
454
|
|
|
451
|
-
def save(self, config_path:
|
|
455
|
+
def save(self, config_path: str | Path | None = None) -> bool:
|
|
452
456
|
"""Save configuration to TOML file.
|
|
453
457
|
|
|
454
458
|
Args:
|
|
@@ -486,7 +490,7 @@ class NodeConfigManager:
|
|
|
486
490
|
raise MantaConfigurationError(f"Failed to save configuration: {e}")
|
|
487
491
|
|
|
488
492
|
def update(
|
|
489
|
-
self, updates:
|
|
493
|
+
self, updates: dict[str, Any], allow_restart_required: bool = False
|
|
490
494
|
) -> bool:
|
|
491
495
|
"""Update configuration with validation.
|
|
492
496
|
|
|
@@ -545,7 +549,7 @@ class NodeConfigManager:
|
|
|
545
549
|
# Set the value
|
|
546
550
|
setattr(target, parts[-1], value)
|
|
547
551
|
|
|
548
|
-
def validate(self) ->
|
|
552
|
+
def validate(self) -> list[str]:
|
|
549
553
|
"""Validate configuration.
|
|
550
554
|
|
|
551
555
|
Returns:
|
|
@@ -569,11 +573,12 @@ class NodeConfigManager:
|
|
|
569
573
|
issues.append(
|
|
570
574
|
f"network.manager_port ({self._config.network.manager_port}) must be between 443 and 65535"
|
|
571
575
|
)
|
|
572
|
-
if self._config.network.light_service_port is not None
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
)
|
|
576
|
+
if self._config.network.light_service_port is not None and not (
|
|
577
|
+
1024 <= self._config.network.light_service_port <= 65535
|
|
578
|
+
):
|
|
579
|
+
issues.append(
|
|
580
|
+
f"network.light_service_port ({self._config.network.light_service_port}) must be between 1024 and 65535"
|
|
581
|
+
)
|
|
577
582
|
|
|
578
583
|
# Validate dataset paths
|
|
579
584
|
for name, path in self._config.datasets.mappings.items():
|
|
@@ -587,7 +592,7 @@ class NodeConfigManager:
|
|
|
587
592
|
|
|
588
593
|
return issues
|
|
589
594
|
|
|
590
|
-
def register_observer(self, callback: Callable[[
|
|
595
|
+
def register_observer(self, callback: Callable[[dict[str, Any]], None]):
|
|
591
596
|
"""Register observer for configuration changes.
|
|
592
597
|
|
|
593
598
|
Args:
|
|
@@ -595,7 +600,7 @@ class NodeConfigManager:
|
|
|
595
600
|
"""
|
|
596
601
|
self._observers.append(callback)
|
|
597
602
|
|
|
598
|
-
def _notify_observers(self, change:
|
|
603
|
+
def _notify_observers(self, change: dict[str, Any]):
|
|
599
604
|
"""Notify all observers of configuration change.
|
|
600
605
|
|
|
601
606
|
Args:
|
|
@@ -605,9 +610,9 @@ class NodeConfigManager:
|
|
|
605
610
|
try:
|
|
606
611
|
observer(change)
|
|
607
612
|
except Exception:
|
|
608
|
-
|
|
613
|
+
logger.debug("Observer callback raised an error", exc_info=True)
|
|
609
614
|
|
|
610
|
-
def list_available_configs(self) ->
|
|
615
|
+
def list_available_configs(self) -> list[str]:
|
|
611
616
|
"""List available configuration files.
|
|
612
617
|
|
|
613
618
|
Returns:
|
|
@@ -623,7 +628,7 @@ class NodeConfigManager:
|
|
|
623
628
|
|
|
624
629
|
return sorted(configs)
|
|
625
630
|
|
|
626
|
-
def get_node_config(self, config_name: str) ->
|
|
631
|
+
def get_node_config(self, config_name: str) -> NodeConfiguration | None:
|
|
627
632
|
"""Load and return a specific node configuration by name.
|
|
628
633
|
|
|
629
634
|
Args:
|
|
@@ -647,7 +652,7 @@ class NodeConfigManager:
|
|
|
647
652
|
except Exception:
|
|
648
653
|
return None
|
|
649
654
|
|
|
650
|
-
def export_template(self, path:
|
|
655
|
+
def export_template(self, path: str | Path | None = None) -> str:
|
|
651
656
|
"""Export a configuration template.
|
|
652
657
|
|
|
653
658
|
Args:
|
|
@@ -704,7 +709,7 @@ class NodeConfigManager:
|
|
|
704
709
|
except Exception as e:
|
|
705
710
|
raise MantaConfigurationError(f"Failed to set manager configuration: {e}")
|
|
706
711
|
|
|
707
|
-
def get_manager_config(self) ->
|
|
712
|
+
def get_manager_config(self) -> dict[str, Any]:
|
|
708
713
|
"""Get manager configuration as dictionary.
|
|
709
714
|
|
|
710
715
|
Returns:
|
|
@@ -788,7 +793,7 @@ class NodeConfigManager:
|
|
|
788
793
|
|
|
789
794
|
@dataclass
|
|
790
795
|
class HardwareConfig:
|
|
791
|
-
gpu_enabled:
|
|
796
|
+
gpu_enabled: bool | None
|
|
792
797
|
max_concurrent_tasks: int
|
|
793
798
|
cpu_limit: str
|
|
794
799
|
memory_limit: str
|
|
@@ -799,7 +804,7 @@ class NodeConfigManager:
|
|
|
799
804
|
alias: str
|
|
800
805
|
connection: ConnectionConfig
|
|
801
806
|
hardware: HardwareConfig
|
|
802
|
-
datasets:
|
|
807
|
+
datasets: Any | None
|
|
803
808
|
auto_register: bool
|
|
804
809
|
random_id: bool
|
|
805
810
|
|
|
@@ -849,7 +854,7 @@ class NodeConfigManager:
|
|
|
849
854
|
random_id=False,
|
|
850
855
|
)
|
|
851
856
|
|
|
852
|
-
def list_running_instances(self) ->
|
|
857
|
+
def list_running_instances(self) -> list[Any]:
|
|
853
858
|
"""List all running node instances.
|
|
854
859
|
|
|
855
860
|
Returns:
|
|
@@ -880,7 +885,7 @@ class NodeConfigManager:
|
|
|
880
885
|
return True
|
|
881
886
|
return False
|
|
882
887
|
|
|
883
|
-
def list_node_configs(self) ->
|
|
888
|
+
def list_node_configs(self) -> list[Any]:
|
|
884
889
|
"""List all available node configurations.
|
|
885
890
|
|
|
886
891
|
Returns:
|
|
@@ -913,6 +918,9 @@ class NodeConfigManager:
|
|
|
913
918
|
configs.append(config_info)
|
|
914
919
|
except Exception:
|
|
915
920
|
# Skip invalid configs
|
|
921
|
+
logger.debug(
|
|
922
|
+
"Skipping invalid config file: %s", config_file, exc_info=True
|
|
923
|
+
)
|
|
916
924
|
continue
|
|
917
925
|
|
|
918
926
|
return configs
|