Add independent Ubuntu server diagnostic
This commit is contained in:
Executable
+310
@@ -0,0 +1,310 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
readonly SCRIPT_NAME="$(basename "$0")"
|
||||
readonly DEFAULT_OLLAMA_URL="${OLLAMA_URL:-http://127.0.0.1:11434}"
|
||||
readonly DEFAULT_LITELLM_URL="${LITELLM_URL:-http://127.0.0.1:8000}"
|
||||
readonly MODEL_NAME="${MODEL_NAME:-qwen2.5-coder:14b}"
|
||||
readonly OPENCLAW_CONFIG="${OPENCLAW_CONFIG:-${HOME}/.openclaw/openclaw.json}"
|
||||
|
||||
passed=0
|
||||
failed=0
|
||||
warnings=0
|
||||
report_file=""
|
||||
|
||||
show_help() {
|
||||
cat <<EOF
|
||||
Uso: $SCRIPT_NAME [opciones]
|
||||
|
||||
Ejecuta un diagnóstico de solo lectura del servidor Ubuntu y de los servicios
|
||||
Ollama, LiteLLM y OpenClaw. No modifica archivos ni reinicia servicios.
|
||||
|
||||
Opciones:
|
||||
--report FILE Guarda la salida en FILE además de mostrarla en pantalla.
|
||||
-h, --help Muestra esta ayuda.
|
||||
|
||||
Variables opcionales:
|
||||
OLLAMA_URL URL de Ollama (por defecto: $DEFAULT_OLLAMA_URL)
|
||||
LITELLM_URL URL de LiteLLM (por defecto: $DEFAULT_LITELLM_URL)
|
||||
MODEL_NAME Modelo esperado (por defecto: $MODEL_NAME)
|
||||
OPENCLAW_CONFIG Ruta de configuración de OpenClaw.
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--report)
|
||||
[[ $# -ge 2 ]] || { echo "Falta FILE para --report" >&2; exit 2; }
|
||||
report_file="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Opción desconocida: $1" >&2
|
||||
show_help >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -n "$report_file" ]]; then
|
||||
mkdir -p "$(dirname "$report_file")"
|
||||
: > "$report_file" || { echo "No se puede escribir $report_file" >&2; exit 1; }
|
||||
exec > >(tee -a "$report_file") 2>&1
|
||||
fi
|
||||
|
||||
pass() {
|
||||
printf '[ OK ] %s\n' "$1"
|
||||
passed=$((passed + 1))
|
||||
}
|
||||
|
||||
fail() {
|
||||
printf '[FAIL] %s\n' "$1"
|
||||
failed=$((failed + 1))
|
||||
}
|
||||
|
||||
warn() {
|
||||
printf '[WARN] %s\n' "$1"
|
||||
warnings=$((warnings + 1))
|
||||
}
|
||||
|
||||
section() {
|
||||
printf '\n=== %s ===\n' "$1"
|
||||
}
|
||||
|
||||
has_command() {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
check_command() {
|
||||
local command_name="$1"
|
||||
local description="$2"
|
||||
|
||||
if has_command "$command_name"; then
|
||||
pass "$description: $command_name"
|
||||
else
|
||||
fail "$description: falta $command_name"
|
||||
fi
|
||||
}
|
||||
|
||||
check_service() {
|
||||
local service="$1"
|
||||
|
||||
if ! has_command systemctl; then
|
||||
warn "No se puede consultar $service: falta systemctl"
|
||||
return
|
||||
fi
|
||||
|
||||
if systemctl is-active --quiet "$service"; then
|
||||
pass "Servicio activo: $service"
|
||||
elif systemctl is-enabled --quiet "$service" 2>/dev/null; then
|
||||
warn "Servicio habilitado pero no activo: $service"
|
||||
else
|
||||
fail "Servicio no activo: $service"
|
||||
fi
|
||||
}
|
||||
|
||||
check_file() {
|
||||
local file_path="$1"
|
||||
local description="$2"
|
||||
|
||||
if [[ -e "$file_path" || -L "$file_path" ]]; then
|
||||
pass "$description: $file_path"
|
||||
else
|
||||
warn "$description no encontrado: $file_path"
|
||||
fi
|
||||
}
|
||||
|
||||
http_check() {
|
||||
local url="$1"
|
||||
local description="$2"
|
||||
local status
|
||||
|
||||
if ! has_command curl; then
|
||||
fail "$description: falta curl"
|
||||
return
|
||||
fi
|
||||
|
||||
status="$(curl --silent --show-error --max-time 10 --output /dev/null --write-out '%{http_code}' "$url" 2>/dev/null)"
|
||||
if [[ "$status" =~ ^2[0-9][0-9]$ ]]; then
|
||||
pass "$description: HTTP $status ($url)"
|
||||
else
|
||||
fail "$description: HTTP ${status:-sin respuesta} ($url)"
|
||||
fi
|
||||
}
|
||||
|
||||
section "Sistema"
|
||||
printf 'Fecha: %s\n' "$(date --iso-8601=seconds 2>/dev/null || date)"
|
||||
printf 'Host: %s\n' "$(hostname 2>/dev/null || echo desconocido)"
|
||||
printf 'Usuario: %s\n' "$(id -un 2>/dev/null || echo desconocido)"
|
||||
|
||||
if [[ -r /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
printf 'Sistema: %s %s\n' "${NAME:-desconocido}" "${VERSION_ID:-desconocida}"
|
||||
if [[ "${ID:-}" == ubuntu ]]; then
|
||||
pass "Sistema operativo Ubuntu"
|
||||
else
|
||||
warn "El sistema operativo detectado no es Ubuntu: ${ID:-desconocido}"
|
||||
fi
|
||||
else
|
||||
fail "No se puede leer /etc/os-release"
|
||||
fi
|
||||
|
||||
check_command curl "Herramienta HTTP"
|
||||
check_command systemctl "Gestor de servicios"
|
||||
check_command ss "Inspección de puertos"
|
||||
|
||||
if has_command free; then
|
||||
free -h
|
||||
fi
|
||||
if has_command df; then
|
||||
df -hT / 2>/dev/null || true
|
||||
fi
|
||||
if has_command swapon; then
|
||||
if swapon --show --noheadings | grep -q .; then
|
||||
pass "Swap activa"
|
||||
else
|
||||
warn "No hay swap activa"
|
||||
fi
|
||||
fi
|
||||
|
||||
section "Hardware"
|
||||
if has_command nvidia-smi; then
|
||||
if nvidia-smi --query-gpu=name,driver_version,memory.total,memory.used --format=csv,noheader 2>/dev/null; then
|
||||
pass "NVIDIA disponible"
|
||||
else
|
||||
fail "nvidia-smi no puede consultar la GPU"
|
||||
fi
|
||||
else
|
||||
warn "nvidia-smi no está instalado o la GPU NVIDIA no está disponible"
|
||||
fi
|
||||
|
||||
if [[ -r /proc/sys/vm/swappiness ]]; then
|
||||
printf 'vm.swappiness: %s\n' "$(cat /proc/sys/vm/swappiness)"
|
||||
fi
|
||||
|
||||
section "Archivos de configuración"
|
||||
check_file /etc/systemd/system/ollama.service.d/override.conf "Override de Ollama"
|
||||
check_file /etc/systemd/system/litellm.service "Servicio de LiteLLM"
|
||||
check_file /etc/systemd/system/nvidia-power-limit.service "Servicio de límite NVIDIA"
|
||||
check_file /etc/ufw "Configuración UFW"
|
||||
check_file /etc/netplan "Configuración Netplan"
|
||||
check_file "$OPENCLAW_CONFIG" "Configuración OpenClaw"
|
||||
|
||||
if [[ -f "$OPENCLAW_CONFIG" ]]; then
|
||||
if grep -Eq '"(apiKey|token|password|secret)"[[:space:]]*:' "$OPENCLAW_CONFIG"; then
|
||||
warn "La configuración OpenClaw contiene campos sensibles; no se mostrarán"
|
||||
fi
|
||||
if grep -q 'litellm/qwen2.5-coder:14b' "$OPENCLAW_CONFIG"; then
|
||||
pass "OpenClaw apunta al modelo LiteLLM esperado"
|
||||
else
|
||||
warn "No se encontró el modelo LiteLLM esperado en OpenClaw"
|
||||
fi
|
||||
fi
|
||||
|
||||
for config_path in \
|
||||
/etc/litellm_config.yaml \
|
||||
/root/litellm_config.yaml \
|
||||
"$HOME/litellm_config.yaml"
|
||||
do
|
||||
if [[ -f "$config_path" ]]; then
|
||||
printf 'LiteLLM config detectada: %s\n' "$config_path"
|
||||
if grep -q "$MODEL_NAME" "$config_path"; then
|
||||
pass "LiteLLM contiene el modelo $MODEL_NAME"
|
||||
else
|
||||
warn "LiteLLM no contiene el modelo esperado: $MODEL_NAME"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
section "Servicios"
|
||||
check_service ollama
|
||||
check_service litellm
|
||||
if has_command openclaw; then
|
||||
printf 'El Gateway de OpenClaw se validará con su propio comando de estado.\n'
|
||||
else
|
||||
warn "OpenClaw no está instalado en PATH"
|
||||
fi
|
||||
|
||||
section "Puertos"
|
||||
if has_command ss; then
|
||||
printf 'Puertos escuchando relevantes:\n'
|
||||
ss -ltnp 2>/dev/null | awk 'NR == 1 || /:8000|:11434|:18789/' || true
|
||||
if ss -ltn 2>/dev/null | grep -Eq ':11434[[:space:]]'; then
|
||||
pass "Ollama escucha en el puerto 11434"
|
||||
else
|
||||
fail "No se detecta Ollama escuchando en el puerto 11434"
|
||||
fi
|
||||
if ss -ltn 2>/dev/null | grep -Eq ':8000[[:space:]]'; then
|
||||
pass "LiteLLM escucha en el puerto 8000"
|
||||
else
|
||||
fail "No se detecta LiteLLM escuchando en el puerto 8000"
|
||||
fi
|
||||
fi
|
||||
|
||||
section "Ollama"
|
||||
http_check "$DEFAULT_OLLAMA_URL/api/tags" "API de Ollama"
|
||||
if has_command ollama; then
|
||||
if ollama list 2>/dev/null | awk 'NR > 1 {print $1}' | grep -Fxq "$MODEL_NAME"; then
|
||||
pass "Modelo Ollama instalado: $MODEL_NAME"
|
||||
else
|
||||
fail "Modelo Ollama no encontrado: $MODEL_NAME"
|
||||
fi
|
||||
else
|
||||
warn "Comando ollama no disponible"
|
||||
fi
|
||||
|
||||
section "LiteLLM"
|
||||
http_check "$DEFAULT_LITELLM_URL/health/liveliness" "Salud de LiteLLM"
|
||||
if has_command curl; then
|
||||
models_response="$(curl --silent --show-error --max-time 10 "$DEFAULT_LITELLM_URL/v1/models" 2>/dev/null || true)"
|
||||
if [[ "$models_response" == *"$MODEL_NAME"* ]]; then
|
||||
pass "LiteLLM publica el modelo $MODEL_NAME"
|
||||
else
|
||||
fail "LiteLLM no publica el modelo $MODEL_NAME en /v1/models"
|
||||
fi
|
||||
fi
|
||||
|
||||
section "OpenClaw"
|
||||
if has_command openclaw; then
|
||||
openclaw --version 2>/dev/null || true
|
||||
if openclaw doctor 2>&1; then
|
||||
pass "openclaw doctor terminó correctamente"
|
||||
else
|
||||
fail "openclaw doctor reportó errores"
|
||||
fi
|
||||
if openclaw gateway status 2>&1; then
|
||||
pass "Estado del Gateway consultado correctamente"
|
||||
else
|
||||
warn "No se pudo consultar correctamente el Gateway"
|
||||
fi
|
||||
else
|
||||
warn "No se puede probar OpenClaw porque no está instalado"
|
||||
fi
|
||||
|
||||
section "Firewall"
|
||||
if has_command ufw; then
|
||||
ufw status verbose 2>/dev/null || true
|
||||
if ufw status 2>/dev/null | grep -qi active; then
|
||||
pass "UFW está activo"
|
||||
else
|
||||
warn "UFW no está activo"
|
||||
fi
|
||||
else
|
||||
warn "UFW no está instalado"
|
||||
fi
|
||||
|
||||
section "Resumen"
|
||||
printf 'Correctos: %d | Fallos: %d | Advertencias: %d\n' "$passed" "$failed" "$warnings"
|
||||
if [[ -n "$report_file" ]]; then
|
||||
printf 'Reporte: %s\n' "$report_file"
|
||||
fi
|
||||
|
||||
if [[ "$failed" -gt 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
exit 0
|
||||
Reference in New Issue
Block a user