Files
llm-server-setup/ubuntu_server_diagnostic.sh

311 lines
8.4 KiB
Bash
Executable File

#!/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