mirror of
https://ak-git.vectorsigma.ru/terghalin/metalcheck-cli.git
synced 2025-11-01 09:37:37 +09:00
add(app): create python application
This commit is contained in:
32
README.md
32
README.md
@@ -1,3 +1,31 @@
|
|||||||
# metalcheck-ui
|
# metalcheck-cli
|
||||||
|
|
||||||
Text-based UI for Metal Check
|
CLI tool for managing and visualizing Metal Check data"
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
```shell
|
||||||
|
pip install -e .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uninstallation
|
||||||
|
|
||||||
|
```shell
|
||||||
|
pip uninstall metalcheck-cli
|
||||||
|
```
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
Usage: metalcheck [OPTIONS] COMMAND [ARGS]...
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--help Show help message
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
export Export Metal Check data in the specified format (yaml or json).
|
||||||
|
k8s Commands for managing Kubernetes Nodes.
|
||||||
|
metal Commands for managing Metal Nodes.
|
||||||
|
visual Displays the visual dashboard with Metal Nodes, Virtual...
|
||||||
|
vm Commands for managing Virtual Machines.
|
||||||
|
```
|
||||||
|
|||||||
0
cli/__init__.py
Normal file
0
cli/__init__.py
Normal file
0
cli/commands/__init__.py
Normal file
0
cli/commands/__init__.py
Normal file
36
cli/commands/export.py
Normal file
36
cli/commands/export.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import click
|
||||||
|
import requests
|
||||||
|
|
||||||
|
BASE_URL = "http://localhost:8000/export" # Backend URL for exporting data
|
||||||
|
|
||||||
|
@click.command()
|
||||||
|
@click.option(
|
||||||
|
"--format",
|
||||||
|
type=click.Choice(["yaml", "json"], case_sensitive=False),
|
||||||
|
default="json",
|
||||||
|
help="Specify the export format: yaml or json. Default is json.",
|
||||||
|
)
|
||||||
|
@click.argument("output", required=False, type=click.Path(writable=True))
|
||||||
|
def export_data(format, output):
|
||||||
|
"""
|
||||||
|
Export Metal Check data in the specified format (yaml or json).
|
||||||
|
|
||||||
|
If an OUTPUT file is provided, the data will be saved to the file.
|
||||||
|
Otherwise, it will be printed to the console.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
response = requests.get(f"{BASE_URL}?format={format}")
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.text
|
||||||
|
if output:
|
||||||
|
with open(output, "w") as file:
|
||||||
|
file.write(data)
|
||||||
|
click.echo(f"Data successfully exported to {output}.")
|
||||||
|
else:
|
||||||
|
click.echo("\n📤 Exported Data:")
|
||||||
|
click.echo(data)
|
||||||
|
else:
|
||||||
|
click.echo(f"Error: {response.status_code} - {response.text}")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
click.echo(f"Error: {e}")
|
||||||
|
|
||||||
57
cli/commands/k8s.py
Normal file
57
cli/commands/k8s.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import click
|
||||||
|
import requests
|
||||||
|
|
||||||
|
BASE_URL = "http://localhost:8000/k8s" # Backend URL for Kubernetes API
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
def kubernetes_nodes():
|
||||||
|
"""Commands for managing Kubernetes Nodes."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@kubernetes_nodes.command("list")
|
||||||
|
def list_command():
|
||||||
|
handle_command("list")
|
||||||
|
|
||||||
|
@kubernetes_nodes.command("think")
|
||||||
|
def think_command():
|
||||||
|
handle_command("think")
|
||||||
|
|
||||||
|
|
||||||
|
def handle_command(action):
|
||||||
|
"""Handle commands related to Kubernetes nodes."""
|
||||||
|
if action == "list":
|
||||||
|
list_kubernetes_nodes()
|
||||||
|
elif action == "analyze":
|
||||||
|
analyze_kubernetes_cluster()
|
||||||
|
|
||||||
|
def list_kubernetes_nodes():
|
||||||
|
"""List all Kubernetes nodes."""
|
||||||
|
try:
|
||||||
|
response = requests.get(f"{BASE_URL}/data")
|
||||||
|
if response.status_code == 200:
|
||||||
|
nodes = response.json().get("nodes", [])
|
||||||
|
click.echo("\n📦 Kubernetes Nodes:")
|
||||||
|
for node in nodes:
|
||||||
|
click.echo(
|
||||||
|
f"Node Name: {node['node_name']}, CPU: {node['cpu']}, "
|
||||||
|
f"Memory: {node['memory']} MiB, Storage: {node['storage']}, "
|
||||||
|
f"Type: {node['instance_type']}, Max Pods: {node['pods_allocatable']}, "
|
||||||
|
f"Time on Duty: {node['time_on_duty']}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
click.echo(f"Error: {response.status_code} - {response.text}")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
click.echo(f"Error: {e}")
|
||||||
|
|
||||||
|
def analyze_kubernetes_cluster():
|
||||||
|
"""Request an AI analysis of the Kubernetes cluster."""
|
||||||
|
try:
|
||||||
|
response = requests.get(f"{BASE_URL}/think/k8s")
|
||||||
|
if response.status_code == 200:
|
||||||
|
summary = response.json().get("summary", "No analysis provided.")
|
||||||
|
click.echo("\n🤖 AI Analysis of Kubernetes Cluster:")
|
||||||
|
click.echo(summary)
|
||||||
|
else:
|
||||||
|
click.echo(f"Error: {response.status_code} - {response.text}")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
click.echo(f"Error: {e}")
|
||||||
96
cli/commands/metal.py
Normal file
96
cli/commands/metal.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import click
|
||||||
|
import requests
|
||||||
|
|
||||||
|
BASE_URL = "http://localhost:8000/metal" # Backend URL for Metal Nodes API
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
def metal_nodes():
|
||||||
|
"""Commands for managing Metal Nodes."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@metal_nodes.command("list")
|
||||||
|
def list_command():
|
||||||
|
handle_command("list")
|
||||||
|
|
||||||
|
@metal_nodes.command("add")
|
||||||
|
def add_command():
|
||||||
|
handle_command("add")
|
||||||
|
|
||||||
|
@metal_nodes.command("delete")
|
||||||
|
def delete_command():
|
||||||
|
handle_command("delete")
|
||||||
|
|
||||||
|
def handle_command(action):
|
||||||
|
"""Handle commands related to Metal Nodes."""
|
||||||
|
if action == "list":
|
||||||
|
list_metal_nodes()
|
||||||
|
elif action == "add":
|
||||||
|
add_metal_node()
|
||||||
|
elif action == "delete":
|
||||||
|
delete_metal_node()
|
||||||
|
|
||||||
|
def list_metal_nodes():
|
||||||
|
"""List all metal nodes."""
|
||||||
|
try:
|
||||||
|
response = requests.get(f"{BASE_URL}/data")
|
||||||
|
if response.status_code == 200:
|
||||||
|
metal_nodes = response.json().get("metal_nodes", [])
|
||||||
|
click.echo("\n🖥️ Metal Nodes:")
|
||||||
|
for node in metal_nodes:
|
||||||
|
click.echo(
|
||||||
|
f"ID: {node[0]}, Name: {node[1]}, Location: {node[2]}, "
|
||||||
|
f"Vendor: {node[3]}, CPU: {node[4]}, Memory: {node[5]}, "
|
||||||
|
f"Storage: {node[6]}, Time on Duty: {node[7]}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
click.echo(f"Error: {response.status_code} - {response.text}")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
click.echo(f"Error: {e}")
|
||||||
|
|
||||||
|
def add_metal_node():
|
||||||
|
"""Add a new metal node."""
|
||||||
|
try:
|
||||||
|
# Gather inputs from the prompt
|
||||||
|
name = click.prompt("Name")
|
||||||
|
location = click.prompt("Location")
|
||||||
|
vendor = click.prompt("Vendor")
|
||||||
|
cpu = click.prompt("CPU (cores)", type=int)
|
||||||
|
memory = click.prompt("Memory (GB)")
|
||||||
|
storage = click.prompt("Storage (e.g., 1TB SSD)")
|
||||||
|
time_on_duty = click.prompt("Time on Duty (hours)", type=int)
|
||||||
|
initial_cost = click.prompt("Initial Cost ($)", type=float)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"name": name,
|
||||||
|
"location": location,
|
||||||
|
"vendor": vendor,
|
||||||
|
"cpu": cpu,
|
||||||
|
"memory": memory,
|
||||||
|
"storage": storage,
|
||||||
|
"time_on_duty": time_on_duty,
|
||||||
|
"initial_cost": initial_cost,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Send the POST request to the backend
|
||||||
|
response = requests.post(f"{BASE_URL}/data", json=data)
|
||||||
|
|
||||||
|
if response.status_code in [200, 201]:
|
||||||
|
response_data = response.json()
|
||||||
|
message = response_data.get("message", "Metal node added successfully!")
|
||||||
|
click.echo(f"✅ {message}")
|
||||||
|
else:
|
||||||
|
click.echo(f"Error: {response.status_code} - {response.text}")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
click.echo(f"Error: {e}")
|
||||||
|
|
||||||
|
def delete_metal_node():
|
||||||
|
"""Delete a metal node."""
|
||||||
|
try:
|
||||||
|
node_id = click.prompt("Enter the ID of the metal node to delete", type=int)
|
||||||
|
response = requests.delete(f"{BASE_URL}/data/{node_id}")
|
||||||
|
if response.status_code == 200:
|
||||||
|
click.echo("✅ Metal node deleted successfully!")
|
||||||
|
else:
|
||||||
|
click.echo(f"Error: {response.status_code} - {response.text}")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
click.echo(f"Error: {e}")
|
||||||
89
cli/commands/vm.py
Normal file
89
cli/commands/vm.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import click
|
||||||
|
import requests
|
||||||
|
|
||||||
|
BASE_URL = "http://localhost:8000/vm" # Backend URL for Virtual Machines API
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
def virtual_machines():
|
||||||
|
"""Commands for managing Virtual Machines."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@virtual_machines.command("list")
|
||||||
|
def list_command():
|
||||||
|
handle_command("list")
|
||||||
|
|
||||||
|
@virtual_machines.command("add")
|
||||||
|
def add_command():
|
||||||
|
handle_command("add")
|
||||||
|
|
||||||
|
@virtual_machines.command("delete")
|
||||||
|
def delete_command():
|
||||||
|
handle_command("delete")
|
||||||
|
|
||||||
|
def handle_command(action):
|
||||||
|
"""Handle commands related to Virtual Machines."""
|
||||||
|
if action == "list":
|
||||||
|
list_virtual_machines()
|
||||||
|
elif action == "add":
|
||||||
|
add_virtual_machine()
|
||||||
|
elif action == "delete":
|
||||||
|
delete_virtual_machine()
|
||||||
|
|
||||||
|
def list_virtual_machines():
|
||||||
|
"""List all virtual machines."""
|
||||||
|
try:
|
||||||
|
response = requests.get(f"{BASE_URL}/data")
|
||||||
|
if response.status_code == 200:
|
||||||
|
virtual_machines = response.json().get("virtual_machines", [])
|
||||||
|
click.echo("\n💻 Virtual Machines:")
|
||||||
|
for vm in virtual_machines:
|
||||||
|
click.echo(
|
||||||
|
f"ID: {vm[0]}, Name: {vm[1]}, Location: {vm[2]}, "
|
||||||
|
f"CPU: {vm[3]}, Memory: {vm[4]}, Storage: {vm[5]}, "
|
||||||
|
f"Type: {vm[6]}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
click.echo(f"Error: {response.status_code} - {response.text}")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
click.echo(f"Error: {e}")
|
||||||
|
|
||||||
|
def add_virtual_machine():
|
||||||
|
"""Add a new virtual machine."""
|
||||||
|
try:
|
||||||
|
name = click.prompt("Name")
|
||||||
|
location = click.prompt("Location")
|
||||||
|
cpu = click.prompt("CPU (cores)", type=int)
|
||||||
|
memory = click.prompt("Memory (GB)")
|
||||||
|
storage = click.prompt("Storage (e.g., 500GB SSD)")
|
||||||
|
vm_type = click.prompt("Type (e.g., cx-21)")
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"name": name,
|
||||||
|
"location": location,
|
||||||
|
"cpu": cpu,
|
||||||
|
"memory": memory,
|
||||||
|
"storage": storage,
|
||||||
|
"type": vm_type,
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(f"{BASE_URL}/data", json=data)
|
||||||
|
if response.status_code in [200, 201]:
|
||||||
|
response_data = response.json()
|
||||||
|
message = response_data.get("message", "Virtual machine added successfully!")
|
||||||
|
click.echo(f"✅ {message}")
|
||||||
|
else:
|
||||||
|
click.echo(f"Error: {response.status_code} - {response.text}")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
click.echo(f"Error: {e}")
|
||||||
|
|
||||||
|
def delete_virtual_machine():
|
||||||
|
"""Delete a virtual machine."""
|
||||||
|
try:
|
||||||
|
vm_id = click.prompt("Enter the ID of the virtual machine to delete", type=int)
|
||||||
|
response = requests.delete(f"{BASE_URL}/data/{vm_id}")
|
||||||
|
if response.status_code == 200:
|
||||||
|
click.echo("✅ Virtual machine deleted successfully!")
|
||||||
|
else:
|
||||||
|
click.echo(f"Error: {response.status_code} - {response.text}")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
click.echo(f"Error: {e}")
|
||||||
24
cli/main.py
Normal file
24
cli/main.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import click
|
||||||
|
from .commands.metal import metal_nodes
|
||||||
|
from .commands.vm import virtual_machines
|
||||||
|
from .commands.k8s import kubernetes_nodes
|
||||||
|
from .commands.export import export_data
|
||||||
|
from .visual import visual_dashboard
|
||||||
|
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
def cli():
|
||||||
|
"""
|
||||||
|
Metal Check CLI: A command-line interface for managing and monitoring Metal Check resources.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Register commands
|
||||||
|
cli.add_command(metal_nodes, name="metal")
|
||||||
|
cli.add_command(virtual_machines, name="vm")
|
||||||
|
cli.add_command(kubernetes_nodes, name="k8s")
|
||||||
|
cli.add_command(export_data, name="export")
|
||||||
|
cli.add_command(visual_dashboard, name="visual")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cli()
|
||||||
167
cli/visual.py
Normal file
167
cli/visual.py
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
import click
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.table import Table
|
||||||
|
from rich.progress import Progress
|
||||||
|
import requests
|
||||||
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
|
# Define constants
|
||||||
|
BACKEND_BASE_URL = "http://localhost:8000"
|
||||||
|
THINK_K8S_URL = f"{BACKEND_BASE_URL}/think/k8s"
|
||||||
|
METAL_NODES_URL = f"{BACKEND_BASE_URL}/metal/data"
|
||||||
|
VM_URL = f"{BACKEND_BASE_URL}/vm/data"
|
||||||
|
K8S_URL = f"{BACKEND_BASE_URL}/k8s/data"
|
||||||
|
|
||||||
|
console = Console()
|
||||||
|
|
||||||
|
# Helper functions for formatting
|
||||||
|
def calculate_time_on_duty_hours(hours):
|
||||||
|
if hours < 1:
|
||||||
|
minutes = round(hours * 60)
|
||||||
|
return f"{minutes} minutes" if minutes > 1 else "less than a minute"
|
||||||
|
elif hours < 24:
|
||||||
|
return f"{round(hours)} hours" if hours > 1 else "1 hour"
|
||||||
|
else:
|
||||||
|
days = hours // 24
|
||||||
|
remaining_hours = hours % 24
|
||||||
|
if remaining_hours:
|
||||||
|
return f"{int(days)} days {int(remaining_hours)} hours"
|
||||||
|
return f"{int(days)} days"
|
||||||
|
|
||||||
|
# Fetch and display metal nodes
|
||||||
|
def display_metal_nodes():
|
||||||
|
table = Table(title="🖥️ Metal Nodes", style="bold green")
|
||||||
|
table.add_column("ID", justify="right", style="cyan")
|
||||||
|
table.add_column("Name", style="magenta")
|
||||||
|
table.add_column("Location", style="white")
|
||||||
|
table.add_column("Vendor", style="green")
|
||||||
|
table.add_column("CPU", justify="right", style="yellow")
|
||||||
|
table.add_column("Memory (GB)", justify="right", style="cyan")
|
||||||
|
table.add_column("Storage", style="magenta")
|
||||||
|
table.add_column("Time on Duty", justify="right", style="magenta")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(METAL_NODES_URL)
|
||||||
|
if response.status_code == 200:
|
||||||
|
metal_nodes = response.json().get("metal_nodes", [])
|
||||||
|
for node in metal_nodes:
|
||||||
|
time_on_duty = calculate_time_on_duty_hours(node[7])
|
||||||
|
table.add_row(
|
||||||
|
f"{node[0]}",
|
||||||
|
node[1],
|
||||||
|
node[2],
|
||||||
|
node[3],
|
||||||
|
f"{node[4]}",
|
||||||
|
node[5],
|
||||||
|
node[6],
|
||||||
|
time_on_duty,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
console.print(f"[red]Failed to fetch metal nodes: {response.status_code}[/red]")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
console.print(f"[red]Error fetching metal nodes: {e}[/red]")
|
||||||
|
|
||||||
|
console.print(table)
|
||||||
|
|
||||||
|
# Fetch and display virtual machines
|
||||||
|
def display_virtual_machines():
|
||||||
|
table = Table(title="💻 Virtual Machines", style="bold blue")
|
||||||
|
table.add_column("ID", justify="right", style="cyan")
|
||||||
|
table.add_column("Name", style="magenta")
|
||||||
|
table.add_column("Location", style="white")
|
||||||
|
table.add_column("CPU", justify="right", style="yellow")
|
||||||
|
table.add_column("Memory (GB)", justify="right", style="cyan")
|
||||||
|
table.add_column("Storage", style="magenta")
|
||||||
|
table.add_column("Type", style="green")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(VM_URL)
|
||||||
|
if response.status_code == 200:
|
||||||
|
virtual_machines = response.json().get("virtual_machines", [])
|
||||||
|
for vm in virtual_machines:
|
||||||
|
table.add_row(
|
||||||
|
f"{vm[0]}",
|
||||||
|
vm[1],
|
||||||
|
vm[2],
|
||||||
|
f"{vm[3]}",
|
||||||
|
vm[4],
|
||||||
|
vm[5],
|
||||||
|
vm[6],
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
console.print(f"[red]Failed to fetch virtual machines: {response.status_code}[/red]")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
console.print(f"[red]Error fetching virtual machines: {e}[/red]")
|
||||||
|
|
||||||
|
console.print(table)
|
||||||
|
|
||||||
|
# Fetch and display Kubernetes nodes
|
||||||
|
def display_kubernetes_nodes():
|
||||||
|
table = Table(title="📦 Kubernetes Nodes", style="bold yellow")
|
||||||
|
table.add_column("Node Name", style="white")
|
||||||
|
table.add_column("CPU", justify="right", style="yellow")
|
||||||
|
table.add_column("Memory (MiB)", justify="right", style="cyan")
|
||||||
|
table.add_column("Storage (GB)", justify="right", style="green")
|
||||||
|
table.add_column("Type", style="blue")
|
||||||
|
table.add_column("Max Pods", justify="right", style="magenta")
|
||||||
|
table.add_column("Time on Duty", justify="right", style="magenta")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(K8S_URL)
|
||||||
|
if response.status_code == 200:
|
||||||
|
nodes = response.json().get("nodes", [])
|
||||||
|
for node in nodes:
|
||||||
|
table.add_row(
|
||||||
|
node["node_name"],
|
||||||
|
node["cpu"],
|
||||||
|
f"{node['memory']}",
|
||||||
|
f"{node['storage']}",
|
||||||
|
node["instance_type"],
|
||||||
|
node["pods_allocatable"],
|
||||||
|
node.get("time_on_duty", "N/A"),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
console.print(f"[red]Failed to fetch Kubernetes nodes: {response.status_code}[/red]")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
console.print(f"[red]Error fetching Kubernetes nodes: {e}[/red]")
|
||||||
|
|
||||||
|
console.print(table)
|
||||||
|
|
||||||
|
# Fetch and display AI summary
|
||||||
|
def display_ai_summary():
|
||||||
|
with Progress() as progress:
|
||||||
|
task = progress.add_task("[cyan]Fetching AI Summary...", total=100)
|
||||||
|
|
||||||
|
try:
|
||||||
|
for _ in range(10): # Simulate progress
|
||||||
|
progress.update(task, advance=10)
|
||||||
|
import time; time.sleep(0.1)
|
||||||
|
|
||||||
|
response = requests.get(THINK_K8S_URL)
|
||||||
|
progress.update(task, completed=100)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
summary = data.get("summary", "No summary provided.")
|
||||||
|
console.print("\n[bold magenta]AI Summary of Kubernetes Cluster:[/bold magenta]")
|
||||||
|
console.print(f"[green]{summary}[/green]\n")
|
||||||
|
else:
|
||||||
|
console.print(f"[red]Failed to fetch AI summary: {response.status_code}[/red]")
|
||||||
|
except requests.RequestException as e:
|
||||||
|
console.print(f"[red]Error fetching AI summary: {e}[/red]")
|
||||||
|
|
||||||
|
# Click command for visual dashboard
|
||||||
|
@click.command()
|
||||||
|
@click.option('--summary', is_flag=True, help="Include AI summary in the dashboard.")
|
||||||
|
def visual_dashboard(summary):
|
||||||
|
"""
|
||||||
|
Displays the visual dashboard with Metal Nodes, Virtual Machines,
|
||||||
|
Kubernetes Nodes, and optionally AI Summary.
|
||||||
|
"""
|
||||||
|
console.print("✨ [bold green]Welcome to the Metal Check Dashboard![/bold green] ✨\n")
|
||||||
|
display_metal_nodes()
|
||||||
|
display_virtual_machines()
|
||||||
|
display_kubernetes_nodes()
|
||||||
|
|
||||||
|
if summary:
|
||||||
|
display_ai_summary()
|
||||||
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
click==8.1.3
|
||||||
|
rich==13.4.0
|
||||||
|
requests==2.31.0
|
||||||
|
pandas==2.1.2
|
||||||
28
setup.py
Normal file
28
setup.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name="metalcheck-cli",
|
||||||
|
version="0.1.0",
|
||||||
|
description="CLI tool for managing and visualizing Metal Check data",
|
||||||
|
author="Aleksandr Tcitlionok",
|
||||||
|
author_email="satos@vskp.su",
|
||||||
|
url="https://github.com/terghalin/metalcheck-cli",
|
||||||
|
packages=find_packages(),
|
||||||
|
include_package_data=True,
|
||||||
|
install_requires=[
|
||||||
|
"click",
|
||||||
|
"requests",
|
||||||
|
"rich",
|
||||||
|
],
|
||||||
|
entry_points={
|
||||||
|
"console_scripts": [
|
||||||
|
"metalcheck=cli.main:cli",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
classifiers=[
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"License :: OSI Approved :: GNU Affero General Public License 3.0 (AGPL 3.0)",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
],
|
||||||
|
python_requires=">=3.9",
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user