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