mantatech-sdk 0.5b0.dev65__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/__init__.light.py +22 -0
- manta/__init__.py +83 -0
- manta/__main__.py +21 -0
- manta/apis/__init__.py +7 -0
- manta/apis/async_user_api.py +6458 -0
- manta/apis/graph.py +498 -0
- manta/apis/module.py +316 -0
- manta/apis/results.py +251 -0
- manta/apis/swarm.py +206 -0
- manta/apis/user_api.py +1016 -0
- manta/cli/__init__.py +1 -0
- manta/cli/commands/__init__.py +1 -0
- manta/cli/commands/base_handler.py +229 -0
- manta/cli/commands/doc.py +192 -0
- manta/cli/commands/install.py +346 -0
- manta/cli/commands/sdk.py +9 -0
- manta/cli/commands/sdk_cluster.py +211 -0
- manta/cli/commands/sdk_config.py +347 -0
- manta/cli/commands/sdk_globals.py +280 -0
- manta/cli/commands/sdk_logs.py +174 -0
- manta/cli/commands/sdk_main.py +167 -0
- manta/cli/commands/sdk_module.py +516 -0
- manta/cli/commands/sdk_nodes.py +168 -0
- manta/cli/commands/sdk_original.py +3873 -0
- manta/cli/commands/sdk_results.py +265 -0
- manta/cli/commands/sdk_swarm.py +454 -0
- manta/cli/commands/sdk_user.py +234 -0
- manta/cli/commands/status.py +292 -0
- manta/cli/component_detector.py +112 -0
- manta/cli/config_manager.py +445 -0
- manta/cli/main.py +265 -0
- manta/cli/utils/__init__.py +27 -0
- manta/cli/utils/converters.py +140 -0
- manta/clients/cluster_management_client.py +486 -0
- manta/clients/local_client.py +149 -0
- manta/clients/module_management_client.py +217 -0
- manta/clients/swarm_management_client.py +562 -0
- manta/clients/user_management_client.py +395 -0
- manta/clients/world_client.py +195 -0
- manta/light/__init__.py +31 -0
- manta/light/globals.py +245 -0
- manta/light/local.py +407 -0
- manta/light/logging_config.py +39 -0
- manta/light/path.py +116 -0
- manta/light/results.py +236 -0
- manta/light/task.py +100 -0
- manta/light/utils.py +217 -0
- manta/light/world.py +177 -0
- mantatech_sdk-0.5b0.dev65.dist-info/METADATA +1039 -0
- mantatech_sdk-0.5b0.dev65.dist-info/RECORD +54 -0
- mantatech_sdk-0.5b0.dev65.dist-info/WHEEL +5 -0
- mantatech_sdk-0.5b0.dev65.dist-info/entry_points.txt +2 -0
- mantatech_sdk-0.5b0.dev65.dist-info/licenses/LICENSE +683 -0
- mantatech_sdk-0.5b0.dev65.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"""Installation helper commands for Manta CLI."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
9
|
+
from rich.prompt import Confirm
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
from ..component_detector import ComponentDetector
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class InstallCommands:
|
|
16
|
+
"""Handle installation-related CLI commands."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, detector: ComponentDetector, console: Optional[Console] = None):
|
|
19
|
+
self.detector = detector
|
|
20
|
+
self.console = console
|
|
21
|
+
|
|
22
|
+
def print(self, *args, **kwargs):
|
|
23
|
+
"""Print with console if available."""
|
|
24
|
+
self.console.print(*args, **kwargs)
|
|
25
|
+
|
|
26
|
+
def print_error(self, message: str):
|
|
27
|
+
"""Print error message."""
|
|
28
|
+
self.console.print(f"[red]Error:[/red] {message}")
|
|
29
|
+
|
|
30
|
+
def print_success(self, message: str):
|
|
31
|
+
"""Print success message."""
|
|
32
|
+
self.console.print(f"[green]Success:[/green] {message}")
|
|
33
|
+
|
|
34
|
+
def print_warning(self, message: str):
|
|
35
|
+
"""Print warning message."""
|
|
36
|
+
self.console.print(f"[yellow]Warning:[/yellow] {message}")
|
|
37
|
+
|
|
38
|
+
def handle(self, args) -> int:
|
|
39
|
+
"""Handle installation commands."""
|
|
40
|
+
if hasattr(args, "install_command") and args.install_command:
|
|
41
|
+
if args.install_command == "wizard":
|
|
42
|
+
return self.run_installation_wizard()
|
|
43
|
+
elif args.install_command == "check":
|
|
44
|
+
return self.check_requirements()
|
|
45
|
+
elif args.install_command == "component":
|
|
46
|
+
# Handle component installation with name
|
|
47
|
+
if hasattr(args, "name") and args.name:
|
|
48
|
+
return self.install_component(args.name)
|
|
49
|
+
else:
|
|
50
|
+
self.print_error("Component name is required")
|
|
51
|
+
return 1
|
|
52
|
+
elif args.install_command == "sdk":
|
|
53
|
+
return self.install_component("sdk")
|
|
54
|
+
elif args.install_command == "admin":
|
|
55
|
+
return self.install_component("admin")
|
|
56
|
+
elif args.install_command == "node":
|
|
57
|
+
return self.install_component("node")
|
|
58
|
+
elif args.install_command == "all":
|
|
59
|
+
return self.install_all_components()
|
|
60
|
+
else:
|
|
61
|
+
self.print_error(f"Unknown install command: {args.install_command}")
|
|
62
|
+
return 1
|
|
63
|
+
else:
|
|
64
|
+
# Show installation help
|
|
65
|
+
return self.show_installation_help()
|
|
66
|
+
|
|
67
|
+
def show_installation_help(self) -> int:
|
|
68
|
+
"""Show installation help and current status."""
|
|
69
|
+
self.print("📦 Manta Installation Guide")
|
|
70
|
+
self.print("=" * 30)
|
|
71
|
+
|
|
72
|
+
# Refresh detection
|
|
73
|
+
self.detector.refresh()
|
|
74
|
+
|
|
75
|
+
# Show current status
|
|
76
|
+
table = Table(title="Component Status")
|
|
77
|
+
table.add_column("Component", style="cyan")
|
|
78
|
+
table.add_column("Status", style="green")
|
|
79
|
+
table.add_column("Install Command", style="magenta")
|
|
80
|
+
|
|
81
|
+
for comp_name in self.detector.list_components():
|
|
82
|
+
comp_info = self.detector.get_component_info(comp_name)
|
|
83
|
+
if comp_info:
|
|
84
|
+
status = "✅ Installed" if comp_info.installed else "❌ Not Installed"
|
|
85
|
+
install_cmd = (
|
|
86
|
+
self.detector.get_installation_command(comp_name)
|
|
87
|
+
if not comp_info.installed
|
|
88
|
+
else "-"
|
|
89
|
+
)
|
|
90
|
+
table.add_row(comp_info.name, status, install_cmd)
|
|
91
|
+
|
|
92
|
+
self.console.print(table)
|
|
93
|
+
|
|
94
|
+
# Show available commands
|
|
95
|
+
self.print("\n💡 Available Commands:")
|
|
96
|
+
self.print(" • manta install wizard - Interactive installation wizard")
|
|
97
|
+
self.print(" • manta install check - Check system requirements")
|
|
98
|
+
self.print(" • manta install sdk - Install Manta SDK")
|
|
99
|
+
self.print(" • manta install admin - Install Manta Admin")
|
|
100
|
+
self.print(" • manta install node - Install Manta Node")
|
|
101
|
+
self.print(" • manta install all - Install all components")
|
|
102
|
+
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
def run_installation_wizard(self) -> int:
|
|
106
|
+
"""Run interactive installation wizard."""
|
|
107
|
+
self.print("🧙 Manta Installation Wizard")
|
|
108
|
+
self.print("=" * 30)
|
|
109
|
+
|
|
110
|
+
# Check requirements first
|
|
111
|
+
self.print("\n1️⃣ Checking system requirements...")
|
|
112
|
+
if not self._check_python_version():
|
|
113
|
+
return 1
|
|
114
|
+
|
|
115
|
+
if not self._check_pip():
|
|
116
|
+
return 1
|
|
117
|
+
|
|
118
|
+
self.print_success("System requirements met!")
|
|
119
|
+
|
|
120
|
+
# Detect missing components
|
|
121
|
+
self.detector.refresh()
|
|
122
|
+
summary = self.detector.get_status_summary()
|
|
123
|
+
|
|
124
|
+
if not summary["missing"]:
|
|
125
|
+
self.print_success("\n✅ All components are already installed!")
|
|
126
|
+
return 0
|
|
127
|
+
|
|
128
|
+
# Ask what to install
|
|
129
|
+
self.print(f"\n2️⃣ Found {len(summary['missing'])} missing component(s):")
|
|
130
|
+
for comp in summary["missing"]:
|
|
131
|
+
self.print(f" • {comp}")
|
|
132
|
+
|
|
133
|
+
if Confirm.ask("\nWould you like to install all missing components?"):
|
|
134
|
+
return self._install_components(summary["missing"])
|
|
135
|
+
else:
|
|
136
|
+
# Let user select specific components
|
|
137
|
+
to_install = []
|
|
138
|
+
for comp in summary["missing"]:
|
|
139
|
+
if Confirm.ask(f"Install {comp}?"):
|
|
140
|
+
to_install.append(comp)
|
|
141
|
+
|
|
142
|
+
if to_install:
|
|
143
|
+
return self._install_components(to_install)
|
|
144
|
+
else:
|
|
145
|
+
self.print("No components selected for installation.")
|
|
146
|
+
return 0
|
|
147
|
+
|
|
148
|
+
def check_requirements(self) -> int:
|
|
149
|
+
"""Check system requirements."""
|
|
150
|
+
self.print("🔍 Checking System Requirements")
|
|
151
|
+
self.print("=" * 35)
|
|
152
|
+
|
|
153
|
+
all_good = True
|
|
154
|
+
|
|
155
|
+
# Check Python version
|
|
156
|
+
if not self._check_python_version():
|
|
157
|
+
all_good = False
|
|
158
|
+
|
|
159
|
+
# Check pip
|
|
160
|
+
if not self._check_pip():
|
|
161
|
+
all_good = False
|
|
162
|
+
|
|
163
|
+
# Check git
|
|
164
|
+
if not self._check_git():
|
|
165
|
+
all_good = False
|
|
166
|
+
|
|
167
|
+
# Check Docker (optional)
|
|
168
|
+
self._check_docker()
|
|
169
|
+
|
|
170
|
+
if all_good:
|
|
171
|
+
self.print_success("\n✅ All requirements met!")
|
|
172
|
+
else:
|
|
173
|
+
self.print_error(
|
|
174
|
+
"\n❌ Some requirements are not met. Please install missing dependencies."
|
|
175
|
+
)
|
|
176
|
+
return 1
|
|
177
|
+
|
|
178
|
+
return 0
|
|
179
|
+
|
|
180
|
+
def install_component(self, component: str) -> int:
|
|
181
|
+
"""Install a specific component."""
|
|
182
|
+
comp_info = self.detector.get_component_info(component)
|
|
183
|
+
|
|
184
|
+
if not comp_info:
|
|
185
|
+
self.print_error(f"Unknown component: {component}")
|
|
186
|
+
return 1
|
|
187
|
+
|
|
188
|
+
if comp_info.installed:
|
|
189
|
+
self.print_success(f"{comp_info.name} is already installed!")
|
|
190
|
+
return 0
|
|
191
|
+
|
|
192
|
+
return self._install_components([component])
|
|
193
|
+
|
|
194
|
+
def install_all_components(self) -> int:
|
|
195
|
+
"""Install all missing components."""
|
|
196
|
+
self.detector.refresh()
|
|
197
|
+
summary = self.detector.get_status_summary()
|
|
198
|
+
|
|
199
|
+
if not summary["missing"]:
|
|
200
|
+
self.print_success("All components are already installed!")
|
|
201
|
+
return 0
|
|
202
|
+
|
|
203
|
+
return self._install_components(summary["missing"])
|
|
204
|
+
|
|
205
|
+
def _install_components(self, components: List[str]) -> int:
|
|
206
|
+
"""Install multiple components."""
|
|
207
|
+
self.print(f"\n📦 Installing {len(components)} component(s)...")
|
|
208
|
+
|
|
209
|
+
failed = []
|
|
210
|
+
|
|
211
|
+
with Progress(
|
|
212
|
+
SpinnerColumn(),
|
|
213
|
+
TextColumn("[progress.description]{task.description}"),
|
|
214
|
+
console=self.console,
|
|
215
|
+
) as progress:
|
|
216
|
+
for comp in components:
|
|
217
|
+
task = progress.add_task(f"Installing {comp}...", total=None)
|
|
218
|
+
|
|
219
|
+
install_cmd = self.detector.get_installation_command(comp)
|
|
220
|
+
if not install_cmd:
|
|
221
|
+
progress.update(
|
|
222
|
+
task,
|
|
223
|
+
description=f"[red]Failed to get install command for {comp}",
|
|
224
|
+
)
|
|
225
|
+
failed.append(comp)
|
|
226
|
+
continue
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
# Run pip install
|
|
230
|
+
result = subprocess.run(
|
|
231
|
+
[sys.executable, "-m", "pip", "install"]
|
|
232
|
+
+ install_cmd.split()[2:],
|
|
233
|
+
capture_output=True,
|
|
234
|
+
text=True,
|
|
235
|
+
check=False,
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
if result.returncode == 0:
|
|
239
|
+
progress.update(
|
|
240
|
+
task, description=f"[green]✅ {comp} installed successfully"
|
|
241
|
+
)
|
|
242
|
+
else:
|
|
243
|
+
progress.update(
|
|
244
|
+
task, description=f"[red]❌ Failed to install {comp}"
|
|
245
|
+
)
|
|
246
|
+
failed.append(comp)
|
|
247
|
+
if result.stderr:
|
|
248
|
+
self.print_error(f"Error: {result.stderr}")
|
|
249
|
+
|
|
250
|
+
except Exception as e:
|
|
251
|
+
progress.update(
|
|
252
|
+
task, description=f"[red]❌ Error installing {comp}: {e}"
|
|
253
|
+
)
|
|
254
|
+
failed.append(comp)
|
|
255
|
+
|
|
256
|
+
# Show summary
|
|
257
|
+
if failed:
|
|
258
|
+
self.print_error(
|
|
259
|
+
f"\n❌ Failed to install {len(failed)} component(s): {', '.join(failed)}"
|
|
260
|
+
)
|
|
261
|
+
return 1
|
|
262
|
+
else:
|
|
263
|
+
self.print_success(
|
|
264
|
+
f"\n✅ Successfully installed {len(components)} component(s)!"
|
|
265
|
+
)
|
|
266
|
+
self.print("\n💡 Next steps:")
|
|
267
|
+
self.print(" • Configure the platform: manta config init --interactive")
|
|
268
|
+
self.print(" • View documentation: manta doc")
|
|
269
|
+
return 0
|
|
270
|
+
|
|
271
|
+
def _check_python_version(self) -> bool:
|
|
272
|
+
"""Check Python version requirement."""
|
|
273
|
+
python_version = f"{sys.version_info.major}.{sys.version_info.minor}"
|
|
274
|
+
required_version = "3.9"
|
|
275
|
+
|
|
276
|
+
if sys.version_info.major == 3 and sys.version_info.minor >= 9:
|
|
277
|
+
self.print(f"✅ Python {python_version} (>= {required_version} required)")
|
|
278
|
+
return True
|
|
279
|
+
else:
|
|
280
|
+
self.print_error(
|
|
281
|
+
f"❌ Python {python_version} (>= {required_version} required)"
|
|
282
|
+
)
|
|
283
|
+
return False
|
|
284
|
+
|
|
285
|
+
def _check_pip(self) -> bool:
|
|
286
|
+
"""Check if pip is available."""
|
|
287
|
+
try:
|
|
288
|
+
result = subprocess.run(
|
|
289
|
+
[sys.executable, "-m", "pip", "--version"],
|
|
290
|
+
capture_output=True,
|
|
291
|
+
text=True,
|
|
292
|
+
check=False,
|
|
293
|
+
timeout=5,
|
|
294
|
+
)
|
|
295
|
+
if result.returncode == 0:
|
|
296
|
+
pip_version = result.stdout.split()[1] if result.stdout else "Unknown"
|
|
297
|
+
self.print(f"✅ pip {pip_version}")
|
|
298
|
+
return True
|
|
299
|
+
else:
|
|
300
|
+
self.print_error("❌ pip is not available")
|
|
301
|
+
return False
|
|
302
|
+
except Exception:
|
|
303
|
+
self.print_error("❌ pip is not available")
|
|
304
|
+
return False
|
|
305
|
+
|
|
306
|
+
def _check_git(self) -> bool:
|
|
307
|
+
"""Check if git is available."""
|
|
308
|
+
try:
|
|
309
|
+
result = subprocess.run(
|
|
310
|
+
["git", "--version"],
|
|
311
|
+
capture_output=True,
|
|
312
|
+
text=True,
|
|
313
|
+
check=False,
|
|
314
|
+
timeout=5,
|
|
315
|
+
)
|
|
316
|
+
if result.returncode == 0:
|
|
317
|
+
git_version = result.stdout.strip()
|
|
318
|
+
self.print(f"✅ {git_version}")
|
|
319
|
+
return True
|
|
320
|
+
else:
|
|
321
|
+
self.print_warning("⚠️ Git is not available (optional)")
|
|
322
|
+
return True # Git is optional
|
|
323
|
+
except Exception:
|
|
324
|
+
self.print_warning("⚠️ Git is not available (optional)")
|
|
325
|
+
return True # Git is optional
|
|
326
|
+
|
|
327
|
+
def _check_docker(self) -> bool:
|
|
328
|
+
"""Check if Docker is available."""
|
|
329
|
+
try:
|
|
330
|
+
result = subprocess.run(
|
|
331
|
+
["docker", "--version"],
|
|
332
|
+
capture_output=True,
|
|
333
|
+
text=True,
|
|
334
|
+
check=False,
|
|
335
|
+
timeout=5,
|
|
336
|
+
)
|
|
337
|
+
if result.returncode == 0:
|
|
338
|
+
docker_version = result.stdout.strip()
|
|
339
|
+
self.print(f"✅ {docker_version} (optional)")
|
|
340
|
+
return True
|
|
341
|
+
else:
|
|
342
|
+
self.print_warning("⚠️ Docker is not available (optional)")
|
|
343
|
+
return True # Docker is optional
|
|
344
|
+
except Exception:
|
|
345
|
+
self.print_warning("⚠️ Docker is not available (optional)")
|
|
346
|
+
return True # Docker is optional
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"""SDK cluster information and management commands."""
|
|
2
|
+
|
|
3
|
+
from rich.panel import Panel
|
|
4
|
+
from rich.table import Table
|
|
5
|
+
|
|
6
|
+
from .base_handler import BaseSDKHandler
|
|
7
|
+
from ..utils import (
|
|
8
|
+
enum_to_string,
|
|
9
|
+
timestamp_to_string,
|
|
10
|
+
extract_platform_type,
|
|
11
|
+
NODE_STATUS_MAP,
|
|
12
|
+
CLUSTER_STATUS_MAP,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SDKClusterHandler(BaseSDKHandler):
|
|
17
|
+
"""Handle cluster-related SDK commands."""
|
|
18
|
+
|
|
19
|
+
def add_subparsers(self, parent_parser):
|
|
20
|
+
"""Add cluster command subparsers."""
|
|
21
|
+
self.parser = parent_parser # Store parser reference
|
|
22
|
+
subparsers = parent_parser.add_subparsers(
|
|
23
|
+
dest="cluster_command", help="Cluster commands"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# List clusters command
|
|
27
|
+
list_parser = subparsers.add_parser("list", help="List available clusters")
|
|
28
|
+
|
|
29
|
+
# Show cluster command
|
|
30
|
+
show_parser = subparsers.add_parser(
|
|
31
|
+
"show", help="Show detailed cluster information"
|
|
32
|
+
)
|
|
33
|
+
show_parser.add_argument("cluster_id", help="Cluster ID to show")
|
|
34
|
+
|
|
35
|
+
# Get cluster nodes command
|
|
36
|
+
nodes_parser = subparsers.add_parser("nodes", help="List nodes in cluster")
|
|
37
|
+
nodes_parser.add_argument("cluster_id", help="Cluster ID to show nodes for")
|
|
38
|
+
|
|
39
|
+
# Add common arguments to all subcommands
|
|
40
|
+
for parser in [list_parser, show_parser, nodes_parser]:
|
|
41
|
+
parser.add_argument("--profile", help="Use specific profile")
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--debug", action="store_true", help="Enable debug output"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def handle(self, args) -> int:
|
|
47
|
+
"""Handle cluster commands."""
|
|
48
|
+
# Enable debug if requested
|
|
49
|
+
if getattr(args, "debug", False):
|
|
50
|
+
self.enable_debug(True)
|
|
51
|
+
|
|
52
|
+
if not args.cluster_command:
|
|
53
|
+
if hasattr(self, "parser"):
|
|
54
|
+
self.parser.print_help()
|
|
55
|
+
return 0 # Return 0 for help display
|
|
56
|
+
|
|
57
|
+
if args.cluster_command == "list":
|
|
58
|
+
return self.list_clusters()
|
|
59
|
+
elif args.cluster_command == "show":
|
|
60
|
+
return self.show_cluster(args.cluster_id)
|
|
61
|
+
elif args.cluster_command == "nodes":
|
|
62
|
+
return self.show_cluster_nodes(args.cluster_id)
|
|
63
|
+
else:
|
|
64
|
+
self.print_error(f"Unknown cluster command: {args.cluster_command}")
|
|
65
|
+
return 1
|
|
66
|
+
|
|
67
|
+
def list_clusters(self) -> int:
|
|
68
|
+
"""List available clusters."""
|
|
69
|
+
api = self._get_user_api()
|
|
70
|
+
if not api:
|
|
71
|
+
return 1
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
|
|
75
|
+
async def run():
|
|
76
|
+
clusters = await api.stream_and_fetch_clusters()
|
|
77
|
+
return clusters
|
|
78
|
+
|
|
79
|
+
clusters = self._run_with_progress(run, "Fetching clusters...")
|
|
80
|
+
|
|
81
|
+
# Check if the operation failed
|
|
82
|
+
if clusters is None:
|
|
83
|
+
return 1
|
|
84
|
+
|
|
85
|
+
# Display clusters
|
|
86
|
+
if not clusters:
|
|
87
|
+
self.print("No clusters found.")
|
|
88
|
+
return 0
|
|
89
|
+
|
|
90
|
+
# Always use console (it's always available in SDK context)
|
|
91
|
+
table = Table(title="Available Clusters")
|
|
92
|
+
table.add_column("Cluster ID", style="cyan")
|
|
93
|
+
table.add_column("Name", style="blue")
|
|
94
|
+
table.add_column("Status", style="green")
|
|
95
|
+
table.add_column("Nodes", style="magenta")
|
|
96
|
+
|
|
97
|
+
for cluster in clusters:
|
|
98
|
+
status = enum_to_string(cluster.get("status", 0), CLUSTER_STATUS_MAP)
|
|
99
|
+
|
|
100
|
+
table.add_row(
|
|
101
|
+
cluster.get("cluster_id", "Unknown"),
|
|
102
|
+
cluster.get("name", "Unknown"),
|
|
103
|
+
status,
|
|
104
|
+
str(cluster.get("node_count", 0)),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
self.console.print(table)
|
|
108
|
+
|
|
109
|
+
return 0
|
|
110
|
+
|
|
111
|
+
except KeyboardInterrupt:
|
|
112
|
+
return 130 # Standard exit code for SIGINT
|
|
113
|
+
except Exception:
|
|
114
|
+
# Error already printed by base handler
|
|
115
|
+
return 1
|
|
116
|
+
|
|
117
|
+
def show_cluster(self, cluster_id: str) -> int:
|
|
118
|
+
"""Show detailed cluster information."""
|
|
119
|
+
api = self._get_user_api()
|
|
120
|
+
if not api:
|
|
121
|
+
return 1
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
|
|
125
|
+
async def run():
|
|
126
|
+
return await api.get_cluster(cluster_id)
|
|
127
|
+
|
|
128
|
+
cluster = self._run_with_progress(run, f"Fetching cluster {cluster_id}...")
|
|
129
|
+
|
|
130
|
+
# Check if the operation failed
|
|
131
|
+
if cluster is None:
|
|
132
|
+
return 1
|
|
133
|
+
|
|
134
|
+
# Extract and format fields
|
|
135
|
+
status = enum_to_string(cluster.get("status", 0), CLUSTER_STATUS_MAP)
|
|
136
|
+
created = timestamp_to_string(
|
|
137
|
+
cluster.get("created_at") or cluster.get("creation_date")
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Display cluster details
|
|
141
|
+
panel = Panel.fit(
|
|
142
|
+
f"[cyan]Cluster ID:[/cyan] {cluster.get('cluster_id', 'Unknown')}\n"
|
|
143
|
+
f"[cyan]Name:[/cyan] {cluster.get('name', 'Unknown')}\n"
|
|
144
|
+
f"[cyan]Status:[/cyan] {status}\n"
|
|
145
|
+
f"[cyan]Node Count:[/cyan] {cluster.get('node_count', 0)}\n"
|
|
146
|
+
f"[cyan]Created:[/cyan] {created}",
|
|
147
|
+
title=f"Cluster: {cluster_id}",
|
|
148
|
+
)
|
|
149
|
+
self.console.print(panel)
|
|
150
|
+
|
|
151
|
+
return 0
|
|
152
|
+
|
|
153
|
+
except KeyboardInterrupt:
|
|
154
|
+
return 130
|
|
155
|
+
except Exception:
|
|
156
|
+
# Error already printed by base handler
|
|
157
|
+
return 1
|
|
158
|
+
|
|
159
|
+
def show_cluster_nodes(self, cluster_id: str) -> int:
|
|
160
|
+
"""Show nodes in a cluster."""
|
|
161
|
+
api = self._get_user_api()
|
|
162
|
+
if not api:
|
|
163
|
+
return 1
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
|
|
167
|
+
async def run():
|
|
168
|
+
return await api.stream_and_fetch_nodes(cluster_id)
|
|
169
|
+
|
|
170
|
+
nodes = self._run_with_progress(
|
|
171
|
+
run, f"Fetching nodes for cluster {cluster_id}..."
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Check if the operation failed
|
|
175
|
+
if nodes is None:
|
|
176
|
+
return 1
|
|
177
|
+
|
|
178
|
+
if not nodes:
|
|
179
|
+
self.print("No nodes found in this cluster.")
|
|
180
|
+
return 0
|
|
181
|
+
|
|
182
|
+
# Create and display nodes table
|
|
183
|
+
table = Table(title=f"Nodes in Cluster: {cluster_id}")
|
|
184
|
+
table.add_column("Node ID", style="cyan")
|
|
185
|
+
table.add_column("Status", style="green")
|
|
186
|
+
table.add_column("Type", style="blue")
|
|
187
|
+
table.add_column("Last Seen", style="yellow")
|
|
188
|
+
|
|
189
|
+
for node in nodes:
|
|
190
|
+
# Use converter functions for consistent parsing
|
|
191
|
+
status = enum_to_string(node.get("node_status", 0), NODE_STATUS_MAP)
|
|
192
|
+
|
|
193
|
+
node_type = extract_platform_type(node.get("platform_info"))
|
|
194
|
+
|
|
195
|
+
last_seen = timestamp_to_string(
|
|
196
|
+
node.get("updated_at") or node.get("created_at")
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
table.add_row(
|
|
200
|
+
node.get("node_id", "Unknown"), status, node_type, last_seen
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
self.console.print(table)
|
|
204
|
+
|
|
205
|
+
return 0
|
|
206
|
+
|
|
207
|
+
except KeyboardInterrupt:
|
|
208
|
+
return 130
|
|
209
|
+
except Exception:
|
|
210
|
+
# Error already printed by base handler
|
|
211
|
+
return 1
|