Files
navidrome-litefs/scripts/cluster_status/output_formatter.py

116 lines
4.0 KiB
Python

from tabulate import tabulate
# ANSI Color Codes
GREEN = "\033[92m"
RED = "\033[91m"
CYAN = "\033[96m"
YELLOW = "\033[93m"
RESET = "\033[0m"
BOLD = "\033[1m"
def colorize(text, color, use_color=True):
if not use_color:
return text
return f"{color}{text}{RESET}"
def format_summary(cluster_data, use_color=True):
"""
Formats the cluster health summary.
"""
health = cluster_data["health"]
color = GREEN if health == "Healthy" else RED
if health == "Split Brain Detected (Multiple Primaries)":
color = YELLOW
summary = [
f"{BOLD}Cluster Health:{RESET} {colorize(health, color, use_color)}",
f"{BOLD}Total Nodes:{RESET} {len(cluster_data['nodes'])}",
f"{BOLD}Primaries:{RESET} {cluster_data['primary_count']}",
]
if not cluster_data.get("nomad_available", True):
summary.append(colorize("WARNING: Nomad CLI unavailable or connectivity failed. Logs and uptime may be missing.", RED, use_color))
summary.append("-" * 30)
return "\n".join(summary)
def format_node_table(nodes, use_color=True):
"""
Formats the node list as a table.
"""
headers = ["Node", "Role", "Consul Status", "LiteFS Role", "Uptime", "Lag", "DBs", "LiteFS Info"]
table_data = []
for node in nodes:
# Consul status color
status = node["status"]
status_color = GREEN if status == "passing" else RED
colored_status = colorize(status, status_color, use_color)
# Role color
role = node["role"]
role_color = CYAN if role == "primary" else RESET
colored_role = colorize(role, role_color, use_color)
# LiteFS role color & consistency check
litefs_primary = node["litefs_primary"]
litefs_role = "primary" if litefs_primary else "replica"
# Highlight discrepancy if consul and litefs disagree
litefs_role_color = RESET
if (role == "primary" and not litefs_primary) or (role == "replica" and litefs_primary):
litefs_role_color = YELLOW
litefs_role = f"!! {litefs_role} !!"
elif litefs_primary:
litefs_role_color = CYAN
colored_litefs_role = colorize(litefs_role, litefs_role_color, use_color)
# Error info
info = ""
if node.get("litefs_error"):
info = colorize("LiteFS API Error", RED, use_color)
else:
info = node.get("advertise_url", "")
table_data.append([
colorize(node["node"], BOLD, use_color),
colored_role,
colored_status,
colored_litefs_role,
node.get("uptime", "N/A"),
node.get("replication_lag", "N/A"),
", ".join(node.get("active_dbs", [])),
info
])
return tabulate(table_data, headers=headers, tablefmt="simple")
def format_diagnostics(nodes, use_color=True):
"""
Formats detailed diagnostic information for nodes with errors.
"""
error_nodes = [n for n in nodes if n["status"] != "passing" or n.get("litefs_error")]
if not error_nodes:
return ""
output = ["", colorize("DIAGNOSTICS", BOLD, use_color), "=" * 20]
for node in error_nodes:
output.append(f"\n{BOLD}Node:{RESET} {colorize(node['node'], RED, use_color)}")
if node["status"] != "passing":
output.append(f" {BOLD}Consul Check Status:{RESET} {colorize(node['status'], RED, use_color)}")
if node.get("check_output"):
output.append(f" {BOLD}Consul Check Output:{RESET}\n {node['check_output'].strip()}")
if node.get("nomad_logs"):
output.append(f" {BOLD}Nomad Stderr Logs (last 20 lines):{RESET}\n{node['nomad_logs']}")
if node.get("litefs_error"):
output.append(f" {BOLD}LiteFS API Error:{RESET} {colorize(node['litefs_error'], RED, use_color)}")
return "\n".join(output)