mirror of
https://ak-git.vectorsigma.ru/terghalin/metalcheck-cli.git
synced 2025-10-26 07:25:51 +09:00
update(app): refactor commands
This commit is contained in:
98
README.md
98
README.md
@@ -1,31 +1,103 @@
|
||||
# metalcheck-cli
|
||||
# MetalCheck CLI
|
||||
|
||||
CLI tool for managing and visualizing Metal Check data"
|
||||
**MetalCheck** CLI is a command-line tool for managing and visualizing Metal Check data, including physical nodes, virtual machines, and Kubernetes clusters.
|
||||
|
||||
# Installation
|
||||
|
||||
Install the tool locally using pip:
|
||||
|
||||
```shell
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
## Uninstallation
|
||||
|
||||
To remove the tool:
|
||||
|
||||
```shell
|
||||
pip uninstall metalcheck-cli
|
||||
```
|
||||
|
||||
# Configuration
|
||||
|
||||
You can configure the base URL for the Metal Check API by creating a configuration file at `~/.config/metalcheck.conf`:
|
||||
|
||||
```plaintext
|
||||
[DEFAULT]
|
||||
base_url = http://localhost:8000
|
||||
```
|
||||
|
||||
If no configuration is provided, the CLI defaults to `http://localhost:8000`.
|
||||
|
||||
# Usage
|
||||
|
||||
```plaintext
|
||||
Usage: metalcheck [OPTIONS] COMMAND [ARGS]...
|
||||
General Command Structure
|
||||
|
||||
Options:
|
||||
--help Show help message
|
||||
```plaintext
|
||||
Usage: metalcheck [OPTIONS] COMMAND [ARGS]...
|
||||
|
||||
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.
|
||||
```
|
||||
Options:
|
||||
--base-url TEXT Set the base URL for the Metal Check API (default: http://localhost:8000)
|
||||
--help Show this message and exit.
|
||||
|
||||
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 Machines, and Kubernetes Nodes
|
||||
vm Commands for managing Virtual Machines
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
1. List Metal Nodes
|
||||
|
||||
```shell
|
||||
metalcheck metal list
|
||||
```
|
||||
|
||||
2. Add a new Metal Node
|
||||
|
||||
```shell
|
||||
metalcheck metal add
|
||||
```
|
||||
|
||||
3. Delete a Metal Node
|
||||
|
||||
```shell
|
||||
metalcheck metal delete
|
||||
```
|
||||
|
||||
4. List Kubernetes Nodes
|
||||
|
||||
```shell
|
||||
metalcheck k8s list
|
||||
```
|
||||
|
||||
5. Analyze Kubernetes Cluster
|
||||
|
||||
```shell
|
||||
metalcheck k8s think
|
||||
```
|
||||
|
||||
|
||||
6. Export Data
|
||||
|
||||
```shell
|
||||
metalcheck export --format yaml exported-data.yaml
|
||||
```
|
||||
|
||||
7. Visual Dashboard
|
||||
|
||||
Run the interactive dashboard to visualize Metal Nodes, Virtual Machines, and Kubernetes Nodes:
|
||||
|
||||
|
||||
```shell
|
||||
metalcheck visual
|
||||
```
|
||||
|
||||
Run visual with the AI summary for Kubernetes cluster:
|
||||
|
||||
```shell
|
||||
metalcheck visual --summary
|
||||
```
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import click
|
||||
import requests
|
||||
|
||||
BASE_URL = "http://localhost:8000/export" # Backend URL for exporting data
|
||||
|
||||
@click.command()
|
||||
@click.option(
|
||||
"--format",
|
||||
@@ -11,15 +9,20 @@ BASE_URL = "http://localhost:8000/export" # Backend URL for exporting data
|
||||
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):
|
||||
@click.pass_context
|
||||
def export_data(ctx, 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.
|
||||
"""
|
||||
|
||||
base_url = ctx.obj["BASE_URL"]
|
||||
export_url = f"{base_url}/export"
|
||||
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}?format={format}")
|
||||
response = requests.get(f"{export_url}?format={format}")
|
||||
if response.status_code == 200:
|
||||
data = response.text
|
||||
if output:
|
||||
@@ -33,4 +36,3 @@ def export_data(format, output):
|
||||
click.echo(f"Error: {response.status_code} - {response.text}")
|
||||
except requests.RequestException as e:
|
||||
click.echo(f"Error: {e}")
|
||||
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
import click
|
||||
import requests
|
||||
|
||||
BASE_URL = "http://localhost:8000/k8s" # Backend URL for Kubernetes API
|
||||
|
||||
@click.group()
|
||||
def kubernetes_nodes():
|
||||
@click.pass_context
|
||||
def kubernetes_nodes(ctx):
|
||||
"""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():
|
||||
@click.pass_context
|
||||
def list_command(ctx):
|
||||
"""List all Kubernetes nodes."""
|
||||
base_url = ctx.obj["BASE_URL"]
|
||||
list_kubernetes_nodes(base_url)
|
||||
|
||||
@kubernetes_nodes.command("analyze")
|
||||
@click.pass_context
|
||||
def analyze_command(ctx):
|
||||
"""Request an AI analysis of the Kubernetes cluster."""
|
||||
base_url = ctx.obj["BASE_URL"]
|
||||
analyze_kubernetes_cluster(base_url)
|
||||
|
||||
def list_kubernetes_nodes(base_url):
|
||||
"""List all Kubernetes nodes."""
|
||||
k8s_url = f"{base_url}/k8s/data"
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/data")
|
||||
response = requests.get(k8s_url)
|
||||
if response.status_code == 200:
|
||||
nodes = response.json().get("nodes", [])
|
||||
click.echo("\n📦 Kubernetes Nodes:")
|
||||
@@ -43,10 +41,11 @@ def list_kubernetes_nodes():
|
||||
except requests.RequestException as e:
|
||||
click.echo(f"Error: {e}")
|
||||
|
||||
def analyze_kubernetes_cluster():
|
||||
def analyze_kubernetes_cluster(base_url):
|
||||
"""Request an AI analysis of the Kubernetes cluster."""
|
||||
analyze_url = f"{base_url}/think/k8s"
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/think/k8s")
|
||||
response = requests.get(analyze_url)
|
||||
if response.status_code == 200:
|
||||
summary = response.json().get("summary", "No analysis provided.")
|
||||
click.echo("\n🤖 AI Analysis of Kubernetes Cluster:")
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
import click
|
||||
import requests
|
||||
|
||||
BASE_URL = "http://localhost:8000/metal" # Backend URL for Metal Nodes API
|
||||
|
||||
@click.group()
|
||||
def metal_nodes():
|
||||
@click.pass_context
|
||||
def metal_nodes(ctx):
|
||||
"""Commands for managing Metal Nodes."""
|
||||
pass
|
||||
|
||||
@metal_nodes.command("list")
|
||||
def list_command():
|
||||
handle_command("list")
|
||||
@click.pass_context
|
||||
def list_command(ctx):
|
||||
"""List all metal nodes."""
|
||||
base_url = ctx.obj["BASE_URL"]
|
||||
list_metal_nodes(base_url)
|
||||
|
||||
@metal_nodes.command("add")
|
||||
def add_command():
|
||||
handle_command("add")
|
||||
@click.pass_context
|
||||
def add_command(ctx):
|
||||
"""Add a new metal node."""
|
||||
base_url = ctx.obj["BASE_URL"]
|
||||
add_metal_node(base_url)
|
||||
|
||||
@metal_nodes.command("delete")
|
||||
def delete_command():
|
||||
handle_command("delete")
|
||||
@click.pass_context
|
||||
def delete_command(ctx):
|
||||
"""Delete a metal node."""
|
||||
base_url = ctx.obj["BASE_URL"]
|
||||
delete_metal_node(base_url)
|
||||
|
||||
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():
|
||||
def list_metal_nodes(base_url):
|
||||
"""List all metal nodes."""
|
||||
metal_url = f"{base_url}/metal/data"
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/data")
|
||||
response = requests.get(metal_url)
|
||||
if response.status_code == 200:
|
||||
metal_nodes = response.json().get("metal_nodes", [])
|
||||
click.echo("\n🖥️ Metal Nodes:")
|
||||
@@ -47,8 +47,9 @@ def list_metal_nodes():
|
||||
except requests.RequestException as e:
|
||||
click.echo(f"Error: {e}")
|
||||
|
||||
def add_metal_node():
|
||||
def add_metal_node(base_url):
|
||||
"""Add a new metal node."""
|
||||
metal_url = f"{base_url}/metal/data"
|
||||
try:
|
||||
# Gather inputs from the prompt
|
||||
name = click.prompt("Name")
|
||||
@@ -72,7 +73,7 @@ def add_metal_node():
|
||||
}
|
||||
|
||||
# Send the POST request to the backend
|
||||
response = requests.post(f"{BASE_URL}/data", json=data)
|
||||
response = requests.post(metal_url, json=data)
|
||||
|
||||
if response.status_code in [200, 201]:
|
||||
response_data = response.json()
|
||||
@@ -83,11 +84,12 @@ def add_metal_node():
|
||||
except requests.RequestException as e:
|
||||
click.echo(f"Error: {e}")
|
||||
|
||||
def delete_metal_node():
|
||||
def delete_metal_node(base_url):
|
||||
"""Delete a metal node."""
|
||||
metal_url = f"{base_url}/metal/data"
|
||||
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}")
|
||||
response = requests.delete(f"{metal_url}/{node_id}")
|
||||
if response.status_code == 200:
|
||||
click.echo("✅ Metal node deleted successfully!")
|
||||
else:
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
import click
|
||||
import requests
|
||||
|
||||
BASE_URL = "http://localhost:8000/vm" # Backend URL for Virtual Machines API
|
||||
|
||||
@click.group()
|
||||
def virtual_machines():
|
||||
@click.pass_context
|
||||
def virtual_machines(ctx):
|
||||
"""Commands for managing Virtual Machines."""
|
||||
pass
|
||||
|
||||
@virtual_machines.command("list")
|
||||
def list_command():
|
||||
handle_command("list")
|
||||
@click.pass_context
|
||||
def list_command(ctx):
|
||||
"""List all virtual machines."""
|
||||
base_url = ctx.obj["BASE_URL"]
|
||||
list_virtual_machines(base_url)
|
||||
|
||||
@virtual_machines.command("add")
|
||||
def add_command():
|
||||
handle_command("add")
|
||||
@click.pass_context
|
||||
def add_command(ctx):
|
||||
"""Add a new virtual machine."""
|
||||
base_url = ctx.obj["BASE_URL"]
|
||||
add_virtual_machine(base_url)
|
||||
|
||||
@virtual_machines.command("delete")
|
||||
def delete_command():
|
||||
handle_command("delete")
|
||||
@click.pass_context
|
||||
def delete_command(ctx):
|
||||
"""Delete a virtual machine."""
|
||||
base_url = ctx.obj["BASE_URL"]
|
||||
delete_virtual_machine(base_url)
|
||||
|
||||
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():
|
||||
def list_virtual_machines(base_url):
|
||||
"""List all virtual machines."""
|
||||
vm_url = f"{base_url}/vm/data"
|
||||
try:
|
||||
response = requests.get(f"{BASE_URL}/data")
|
||||
response = requests.get(vm_url)
|
||||
if response.status_code == 200:
|
||||
virtual_machines = response.json().get("virtual_machines", [])
|
||||
click.echo("\n💻 Virtual Machines:")
|
||||
@@ -47,8 +47,9 @@ def list_virtual_machines():
|
||||
except requests.RequestException as e:
|
||||
click.echo(f"Error: {e}")
|
||||
|
||||
def add_virtual_machine():
|
||||
def add_virtual_machine(base_url):
|
||||
"""Add a new virtual machine."""
|
||||
vm_url = f"{base_url}/vm/data"
|
||||
try:
|
||||
name = click.prompt("Name")
|
||||
location = click.prompt("Location")
|
||||
@@ -66,7 +67,7 @@ def add_virtual_machine():
|
||||
"type": vm_type,
|
||||
}
|
||||
|
||||
response = requests.post(f"{BASE_URL}/data", json=data)
|
||||
response = requests.post(vm_url, json=data)
|
||||
if response.status_code in [200, 201]:
|
||||
response_data = response.json()
|
||||
message = response_data.get("message", "Virtual machine added successfully!")
|
||||
@@ -76,11 +77,12 @@ def add_virtual_machine():
|
||||
except requests.RequestException as e:
|
||||
click.echo(f"Error: {e}")
|
||||
|
||||
def delete_virtual_machine():
|
||||
def delete_virtual_machine(base_url):
|
||||
"""Delete a virtual machine."""
|
||||
vm_url = f"{base_url}/vm/data"
|
||||
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}")
|
||||
response = requests.delete(f"{vm_url}/{vm_id}")
|
||||
if response.status_code == 200:
|
||||
click.echo("✅ Virtual machine deleted successfully!")
|
||||
else:
|
||||
|
||||
22
cli/main.py
22
cli/main.py
@@ -1,3 +1,5 @@
|
||||
import os
|
||||
import configparser
|
||||
import click
|
||||
from .commands.metal import metal_nodes
|
||||
from .commands.vm import virtual_machines
|
||||
@@ -5,13 +7,29 @@ from .commands.k8s import kubernetes_nodes
|
||||
from .commands.export import export_data
|
||||
from .visual import visual_dashboard
|
||||
|
||||
# Default configuration file path
|
||||
CONFIG_FILE_PATH = os.path.expanduser("~/.config/metalcheck.conf")
|
||||
|
||||
# Load configuration
|
||||
def load_config():
|
||||
config = configparser.ConfigParser()
|
||||
if os.path.exists(CONFIG_FILE_PATH):
|
||||
config.read(CONFIG_FILE_PATH)
|
||||
return config
|
||||
|
||||
@click.group()
|
||||
def cli():
|
||||
@click.option("--base-url", help="Set the backend base URL.")
|
||||
@click.pass_context
|
||||
def cli(ctx, base_url):
|
||||
"""
|
||||
Metal Check CLI: A command-line interface for managing and monitoring Metal Check resources.
|
||||
"""
|
||||
pass
|
||||
config = load_config()
|
||||
|
||||
# Default backend URL
|
||||
default_base_url = config.get("DEFAULT", "base_url", fallback="http://localhost:8000")
|
||||
ctx.ensure_object(dict)
|
||||
ctx.obj["BASE_URL"] = base_url or default_base_url
|
||||
|
||||
# Register commands
|
||||
cli.add_command(metal_nodes, name="metal")
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import click
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
from rich.progress import Progress
|
||||
import requests
|
||||
import click
|
||||
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 function to set backend URLs
|
||||
def get_backend_urls(base_url):
|
||||
return {
|
||||
"THINK_K8S_URL": f"{base_url}/think/k8s",
|
||||
"METAL_NODES_URL": f"{base_url}/metal/data",
|
||||
"VM_URL": f"{base_url}/vm/data",
|
||||
"K8S_URL": f"{base_url}/k8s/data",
|
||||
}
|
||||
|
||||
# Helper functions for formatting
|
||||
def calculate_time_on_duty_hours(hours):
|
||||
if hours < 1:
|
||||
@@ -29,7 +31,7 @@ def calculate_time_on_duty_hours(hours):
|
||||
return f"{int(days)} days"
|
||||
|
||||
# Fetch and display metal nodes
|
||||
def display_metal_nodes():
|
||||
def display_metal_nodes(metal_nodes_url):
|
||||
table = Table(title="🖥️ Metal Nodes", style="bold green")
|
||||
table.add_column("ID", justify="right", style="cyan")
|
||||
table.add_column("Name", style="magenta")
|
||||
@@ -41,7 +43,7 @@ def display_metal_nodes():
|
||||
table.add_column("Time on Duty", justify="right", style="magenta")
|
||||
|
||||
try:
|
||||
response = requests.get(METAL_NODES_URL)
|
||||
response = requests.get(metal_nodes_url)
|
||||
if response.status_code == 200:
|
||||
metal_nodes = response.json().get("metal_nodes", [])
|
||||
for node in metal_nodes:
|
||||
@@ -64,7 +66,7 @@ def display_metal_nodes():
|
||||
console.print(table)
|
||||
|
||||
# Fetch and display virtual machines
|
||||
def display_virtual_machines():
|
||||
def display_virtual_machines(vm_url):
|
||||
table = Table(title="💻 Virtual Machines", style="bold blue")
|
||||
table.add_column("ID", justify="right", style="cyan")
|
||||
table.add_column("Name", style="magenta")
|
||||
@@ -75,7 +77,7 @@ def display_virtual_machines():
|
||||
table.add_column("Type", style="green")
|
||||
|
||||
try:
|
||||
response = requests.get(VM_URL)
|
||||
response = requests.get(vm_url)
|
||||
if response.status_code == 200:
|
||||
virtual_machines = response.json().get("virtual_machines", [])
|
||||
for vm in virtual_machines:
|
||||
@@ -96,7 +98,7 @@ def display_virtual_machines():
|
||||
console.print(table)
|
||||
|
||||
# Fetch and display Kubernetes nodes
|
||||
def display_kubernetes_nodes():
|
||||
def display_kubernetes_nodes(k8s_url):
|
||||
table = Table(title="📦 Kubernetes Nodes", style="bold yellow")
|
||||
table.add_column("Node Name", style="white")
|
||||
table.add_column("CPU", justify="right", style="yellow")
|
||||
@@ -107,7 +109,7 @@ def display_kubernetes_nodes():
|
||||
table.add_column("Time on Duty", justify="right", style="magenta")
|
||||
|
||||
try:
|
||||
response = requests.get(K8S_URL)
|
||||
response = requests.get(k8s_url)
|
||||
if response.status_code == 200:
|
||||
nodes = response.json().get("nodes", [])
|
||||
for node in nodes:
|
||||
@@ -128,7 +130,7 @@ def display_kubernetes_nodes():
|
||||
console.print(table)
|
||||
|
||||
# Fetch and display AI summary
|
||||
def display_ai_summary():
|
||||
def display_ai_summary(think_k8s_url):
|
||||
with Progress() as progress:
|
||||
task = progress.add_task("[cyan]Fetching AI Summary...", total=100)
|
||||
|
||||
@@ -137,7 +139,7 @@ def display_ai_summary():
|
||||
progress.update(task, advance=10)
|
||||
import time; time.sleep(0.1)
|
||||
|
||||
response = requests.get(THINK_K8S_URL)
|
||||
response = requests.get(think_k8s_url)
|
||||
progress.update(task, completed=100)
|
||||
|
||||
if response.status_code == 200:
|
||||
@@ -153,15 +155,19 @@ def display_ai_summary():
|
||||
# Click command for visual dashboard
|
||||
@click.command()
|
||||
@click.option('--summary', is_flag=True, help="Include AI summary in the dashboard.")
|
||||
def visual_dashboard(summary):
|
||||
@click.pass_context
|
||||
def visual_dashboard(ctx, summary):
|
||||
"""
|
||||
Displays the visual dashboard with Metal Nodes, Virtual Machines,
|
||||
Kubernetes Nodes, and optionally AI Summary.
|
||||
"""
|
||||
base_url = ctx.obj["BASE_URL"]
|
||||
urls = get_backend_urls(base_url)
|
||||
|
||||
console.print("✨ [bold green]Welcome to the Metal Check Dashboard![/bold green] ✨\n")
|
||||
display_metal_nodes()
|
||||
display_virtual_machines()
|
||||
display_kubernetes_nodes()
|
||||
display_metal_nodes(urls["METAL_NODES_URL"])
|
||||
display_virtual_machines(urls["VM_URL"])
|
||||
display_kubernetes_nodes(urls["K8S_URL"])
|
||||
|
||||
if summary:
|
||||
display_ai_summary()
|
||||
display_ai_summary(urls["THINK_K8S_URL"])
|
||||
|
||||
Reference in New Issue
Block a user