305 lines
14 KiB
Bash
Executable File
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
|