adsb-1090/check_adsb_claude_v1.sh

305 lines
14 KiB
Bash
Executable File

#!/bin/bash
# ═══════════════════════════════════════════════════════════════════════════════
# check_adsb.sh — Vérification complète du stack ADS-B Linux-25
# Version 2.0 — JCA 2026
# ═══════════════════════════════════════════════════════════════════════════════
GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; BLUE='\033[1;34m'; BOLD='\033[1m'; NC='\033[0m'
ok() { echo -e " ${GREEN}${NC} $1"; }
fail() { echo -e " ${RED}${NC} $1"; ERRORS=$((ERRORS+1)); }
warn() { echo -e " ${YELLOW}${NC} $1"; WARNS=$((WARNS+1)); }
info() { echo -e " ${CYAN}${NC} $1"; }
hdr() { echo -e "\n${BLUE}${BOLD}═══ $1 ═══${NC}"; }
ERRORS=0; WARNS=0
IP=$(ip -4 addr show wlp58s0 2>/dev/null | grep -oP '(?<=inet )\d+\.\d+\.\d+\.\d+' | head -1)
[ -z "$IP" ] && IP=$(hostname -I | awk '{print $1}')
echo -e "${BOLD}╔══════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}║ Stack ADS-B Linux-25 — Vérification complète ║${NC}"
echo -e "${BOLD}$(date '+%a %d/%m/%Y %H:%M:%S')${NC}"
echo -e "${BOLD}╚══════════════════════════════════════════════════════╝${NC}"
info "IP détectée : ${IP:-inconnue}"
# ── 1. CONTENEURS ─────────────────────────────────────────────────────────────
hdr "1. Conteneurs Docker"
CONTAINERS="adsb-readsb adsb-tar1090 adsb-adsb2pg adsb-webapp wordpress-postgres"
for ctn in $CONTAINERS; do
STATUS=$(docker inspect --format '{{.State.Status}}' "$ctn" 2>/dev/null)
HEALTH=$(docker inspect --format '{{.State.Health.Status}}' "$ctn" 2>/dev/null)
if [ "$STATUS" = "running" ]; then
UPTIME=$(docker inspect --format '{{.State.StartedAt}}' "$ctn" | xargs -I{} date -d {} '+%d/%m %H:%M' 2>/dev/null)
LABEL="${ctn}"
[ -n "$HEALTH" ] && [ "$HEALTH" != "<nil>" ] && LABEL="$LABEL [$HEALTH]"
if [ "$HEALTH" = "unhealthy" ]; then
warn "$LABEL — démarré $UPTIME"
else
ok "$LABEL — démarré $UPTIME"
fi
else
fail "$ctn — STATUS=$STATUS"
fi
done
# ── 2. DONGLE RTL-SDR ─────────────────────────────────────────────────────────
hdr "2. Dongle RTL-SDR"
if docker logs adsb-readsb 2>&1 | grep -q "SN 00000010"; then
ok "AirNav FlightStick SN:00000010 détecté"
GAIN=$(docker logs adsb-readsb 2>&1 | grep "tuner gain set to" | tail -1 | grep -oP '[\d.]+(?= dB)')
[ -n "$GAIN" ] && info "Gain actuel : ${GAIN} dB (autogain)"
else
fail "Dongle SN:00000010 non détecté dans les logs"
fi
# Vérifier le device USB sur l'hôte
if ls /dev/adsb_dongle 2>/dev/null | grep -q adsb_dongle; then
ok "Symlink /dev/adsb_dongle présent"
elif lsusb 2>/dev/null | grep -q "RTL2838\|2838:0001\|0bda:2838"; then
ok "Dongle détecté via lsusb (symlink absent)"
else
warn "Symlink /dev/adsb_dongle absent"
fi
# ── 3. DONNÉES ADS-B ──────────────────────────────────────────────────────────
hdr "3. Données ADS-B"
# Vérifier aircraft.json via tar1090
AC_JSON=$(curl -s --max-time 5 "http://127.0.0.1:8090/data/aircraft.json" 2>/dev/null)
if [ -n "$AC_JSON" ]; then
MSGS=$(echo "$AC_JSON" | python3 -c "import json,sys; d=json.load(sys.stdin); print(d.get('messages',0))" 2>/dev/null)
AVIONS=$(echo "$AC_JSON" | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d.get('aircraft',[])))" 2>/dev/null)
WITH_POS=$(echo "$AC_JSON" | python3 -c "
import json,sys
d=json.load(sys.stdin)
n=sum(1 for a in d.get('aircraft',[]) if a.get('lat') and a.get('lon'))
print(n)" 2>/dev/null)
if [ "${MSGS:-0}" -gt 0 ] 2>/dev/null; then
ok "tar1090 actif — $MSGS messages décodés"
ok "$AVIONS avion(s) en vue dont $WITH_POS avec position GPS"
else
warn "tar1090 répond mais 0 message (démarrage récent ou aucun avion ?)"
fi
else
fail "tar1090 ne répond pas sur :8090/data/aircraft.json"
fi
# Volume partagé readsb-run
VOL_PATH=$(docker inspect adsb_readsb-run 2>/dev/null | \
python3 -c "import json,sys; d=json.load(sys.stdin); print(d[0]['Mountpoint'])" 2>/dev/null)
if [ -n "$VOL_PATH" ] && ls "$VOL_PATH/aircraft.pb" 2>/dev/null | grep -q pb; then
AGE=$(( $(date +%s) - $(stat -c %Y "$VOL_PATH/aircraft.pb" 2>/dev/null || echo 0) ))
if [ "$AGE" -lt 10 ]; then
ok "Volume readsb-run : aircraft.pb mis à jour il y a ${AGE}s"
else
warn "Volume readsb-run : aircraft.pb âgé de ${AGE}s"
fi
else
warn "Volume readsb-run : aircraft.pb introuvable"
fi
# ── 4. PORTS RÉSEAU ───────────────────────────────────────────────────────────
hdr "4. Ports réseau"
declare -A PORTS=(
[":8080"]="webapp PHP"
[":8090"]="tar1090 nginx"
[":30005"]="Beast TCP readsb"
[":5432"]="PostgreSQL"
[":30002"]="RAW TCP"
[":30003"]="SBS TCP"
)
for port_str in ":8080" ":8090" ":30005" ":5432" ":30002" ":30003"; do
port="${port_str:1}"
label="${PORTS[$port_str]}"
if ss -tlnp 2>/dev/null | grep -q ":${port}[[:space:]]"; then
ok "Port ${port} en écoute — ${label}"
else
fail "Port ${port} absent — ${label}"
fi
done
# Connexion Beast tar1090 → readsb
BEAST_ERRS=$(docker logs adsb-tar1090 2>&1 | grep "Beast TCP input.*failed" | tail -1)
if [ -n "$BEAST_ERRS" ]; then
FAIL_CNT=$(echo "$BEAST_ERRS" | grep -oP '\d+(?= times)')
if [ "${FAIL_CNT:-0}" -gt 50 ] 2>/dev/null; then
fail "Connexion Beast tar1090→readsb : $FAIL_CNT échecs (vérifier réseau adsb-net)"
else
warn "Quelques échecs Beast TCP au démarrage (normal): $FAIL_CNT"
fi
else
ok "Connexion Beast tar1090→readsb : OK"
fi
# Réseau adsb-net
READSB_IP=$(docker inspect adsb-readsb 2>/dev/null | \
python3 -c "
import json,sys
d=json.load(sys.stdin)[0]
nets=d['NetworkSettings']['Networks']
for n,v in nets.items():
if 'adsb' in n:
print(v['IPAddress'])
" 2>/dev/null)
if [ -n "$READSB_IP" ] && [ "$READSB_IP" != "None" ]; then
ok "readsb attaché à adsb-net : $READSB_IP"
else
fail "readsb non attaché au réseau adsb-net (Beast TCP impossible)"
fi
# ── 5. POSTGRESQL ─────────────────────────────────────────────────────────────
hdr "5. PostgreSQL — schéma adsb"
PG_CMD="docker exec wordpress-postgres psql -U pgz_admin -d wpz_postgres -t -c"
# Connexion
PG_VER=$($PG_CMD "SELECT version();" 2>/dev/null | head -1 | xargs)
if [ -n "$PG_VER" ]; then
ok "PostgreSQL accessible"
info "${PG_VER:0:60}"
else
fail "PostgreSQL inaccessible"
fi
# Partitions positions
PART_POS=$($PG_CMD "
SELECT COUNT(*) FROM pg_class c
JOIN pg_namespace n ON n.oid=c.relnamespace
WHERE n.nspname='adsb' AND c.relname LIKE 'positions_2%' AND c.relkind='r';" 2>/dev/null | xargs)
ok "${PART_POS:-0} sous-partitions adsb.positions"
# Partitions aircraft_history
PART_HIS=$($PG_CMD "
SELECT COUNT(*) FROM pg_class c
JOIN pg_namespace n ON n.oid=c.relnamespace
WHERE n.nspname='adsb' AND c.relname LIKE 'aircraft_history_%' AND c.relkind='r';" 2>/dev/null | xargs)
ok "${PART_HIS:-0} sous-partitions adsb.aircraft_history"
# Insertions récentes positions
POS_COUNT=$($PG_CMD "
SELECT COUNT(*) FROM adsb.positions
WHERE ts >= NOW() - INTERVAL '5 minutes';" 2>/dev/null | xargs)
if [ "${POS_COUNT:-0}" -gt 0 ] 2>/dev/null; then
ok "${POS_COUNT} positions insérées dans les 5 dernières minutes"
else
warn "Aucune position insérée dans les 5 dernières minutes"
fi
# Pourcentage aircraft_desc rempli
PCT_DESC=$($PG_CMD "
SELECT ROUND(COUNT(aircraft_desc)*100.0/NULLIF(COUNT(*),0),1)
FROM adsb.positions
WHERE ts >= NOW() - INTERVAL '30 minutes'
AND lat IS NOT NULL;" 2>/dev/null | xargs)
if [ -n "$PCT_DESC" ] && [ "$PCT_DESC" != "NULL" ]; then
if python3 -c "exit(0 if float('${PCT_DESC:-0}') >= 80 else 1)" 2>/dev/null; then
ok "aircraft_desc rempli à ${PCT_DESC}% (30 dernières min)"
else
warn "aircraft_desc rempli à ${PCT_DESC}% seulement (< 80%)"
fi
fi
# Stats générales
STATS=$($PG_CMD "
SELECT
COUNT(DISTINCT icao) AS avions_aujourd_hui,
COUNT(*) AS sessions_aujourd_hui
FROM adsb.aircraft_history
WHERE session_start >= CURRENT_DATE;" 2>/dev/null | xargs)
info "Aujourd'hui : $STATS"
# Taille de la base adsb
DB_SIZE=$($PG_CMD "
SELECT pg_size_pretty(SUM(pg_total_relation_size(c.oid)))
FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace
WHERE n.nspname='adsb';" 2>/dev/null | xargs)
info "Taille totale schéma adsb : ${DB_SIZE:-?}"
# ── 6. ADSB2PG ────────────────────────────────────────────────────────────────
hdr "6. adsb2pg — collecteur"
LAST_LOG=$(docker logs adsb-adsb2pg 2>&1 | grep "positions insérées" | tail -1)
if [ -n "$LAST_LOG" ]; then
ok "adsb2pg actif : $LAST_LOG"
# Vérifier la fraîcheur (dernière log < 30s ?)
LOG_TIME=$(docker logs adsb-adsb2pg 2>&1 | grep "positions insérées" | tail -1 | \
grep -oP '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}')
if [ -n "$LOG_TIME" ]; then
AGE_LOG=$(( $(date +%s) - $(date -d "$LOG_TIME" +%s 2>/dev/null || echo 0) ))
if [ "$AGE_LOG" -lt 30 ]; then
ok "Dernière insertion il y a ${AGE_LOG}s (cycle 5s actif)"
else
warn "Dernière insertion il y a ${AGE_LOG}s (cycle 5s peut être bloqué)"
fi
fi
else
warn "Aucun log d'insertion trouvé dans adsb2pg"
fi
# ── 7. WEBAPP ─────────────────────────────────────────────────────────────────
hdr "7. Webapp — interface utilisateur"
# Test API endpoints
for action in live kpi trajectories; do
RESP=$(curl -s --max-time 5 "http://127.0.0.1:8080/api.php?action=${action}&period=24&granularity=60" 2>/dev/null)
if echo "$RESP" | python3 -c "import json,sys; json.load(sys.stdin)" 2>/dev/null; then
# Vérifier pas d'erreur PHP
if echo "$RESP" | grep -q '"error"'; then
ERR=$(echo "$RESP" | python3 -c "import json,sys; print(json.load(sys.stdin).get('error','?'))" 2>/dev/null)
warn "api.php?action=${action} : erreur — $ERR"
else
ok "api.php?action=${action} : OK"
fi
else
fail "api.php?action=${action} : réponse non-JSON"
fi
done
# Vérifier les onglets dans index.php
TAB_COUNT=$(docker exec adsb-webapp grep -c 'href="#tab' /var/www/html/index.php 2>/dev/null)
ok "${TAB_COUNT:-?} onglets dans index.php"
[ "${TAB_COUNT:-0}" -lt 5 ] && warn "Moins de 5 onglets — index.php peut être obsolète"
# Taille du JS
JS_SIZE=$(docker exec adsb-webapp wc -c /var/www/html/js/app.js 2>/dev/null | awk '{print $1}')
info "app.js : ${JS_SIZE:-?} octets"
# Accès depuis le LAN
if [ -n "$IP" ]; then
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "http://${IP}:8080/" 2>/dev/null)
[ "$HTTP_CODE" = "200" ] && ok "Accès LAN http://${IP}:8080 : HTTP $HTTP_CODE" || \
warn "Accès LAN http://${IP}:8080 : HTTP $HTTP_CODE"
fi
# ── 8. AUTOGAIN ───────────────────────────────────────────────────────────────
hdr "8. Autogain"
GAIN_LOG=$(docker logs adsb-readsb 2>&1 | grep -E "gain|autogain" | tail -5)
CURRENT_GAIN=$(docker logs adsb-readsb 2>&1 | grep "tuner gain set to" | tail -1 | \
grep -oP '[\d.]+(?= dB)')
STATE=$(docker logs adsb-readsb 2>&1 | grep "autogain" | grep -oP "(?<=state ')[\w]+" | tail -1)
if [ -n "$CURRENT_GAIN" ]; then
ok "Gain courant : ${CURRENT_GAIN} dB (état: ${STATE:-inconnu})"
else
warn "Gain courant inconnu"
fi
INSUF=$(docker logs adsb-readsb 2>&1 | grep "Insufficient messages" | wc -l)
[ "$INSUF" -gt 3 ] && warn "$INSUF cycles avec messages insuffisants (antenne/portée ?)" || \
[ "$INSUF" -gt 0 ] && info "$INSUF cycle(s) avec messages insuffisants"
# ── RÉSUMÉ ────────────────────────────────────────────────────────────────────
echo ""
echo -e "${BOLD}╔══════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}║ RÉSUMÉ ║${NC}"
echo -e "${BOLD}╠══════════════════════════════════════════════════════╣${NC}"
if [ "$ERRORS" -eq 0 ] && [ "$WARNS" -eq 0 ]; then
echo -e "${BOLD}${GREEN}✓ Stack opérationnel — aucune anomalie détectée${NC}${BOLD}${NC}"
elif [ "$ERRORS" -eq 0 ]; then
echo -e "${BOLD}${YELLOW}⚠ Stack opérationnel — $WARNS avertissement(s)${NC}${BOLD}${NC}"
else
echo -e "${BOLD}${RED}$ERRORS erreur(s), $WARNS avertissement(s) détectés${NC}${BOLD}${NC}"
fi
echo -e "${BOLD}║ Dashboard : http://${IP}:8080${NC}${BOLD}${NC}"
echo -e "${BOLD}║ tar1090 : http://${IP}:8090${NC}${BOLD}${NC}"
echo -e "${BOLD}╚══════════════════════════════════════════════════════╝${NC}"
exit $ERRORS