update(app): refactor commands

This commit is contained in:
Aleksandr Tcitlionok
2024-12-11 12:56:18 +00:00
parent 24e09330ea
commit b031148f61
7 changed files with 212 additions and 111 deletions

View File

@@ -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
```

View File

@@ -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}")

View File

@@ -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:")

View File

@@ -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:

View File

@@ -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:

View File

@@ -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")

View File

@@ -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"])