311 lines
8.4 KiB
Bash
Executable File
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
|