Ctrl K
ComunidadGuías…

deploy/vektor-diagnose.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/bin/bash
# vektor-diagnose — diagnostics lecture seule, déclenchés par Vektor via le
# canal d'actions (forced command). Playbooks inspirés des incidents réels
# (gel 30/09, flood logs, ENOSPC, flux LXC→NetBird, transcodage…).
# Formats acceptés (re-validés ici, format fermé) :
#   diagnose_pve       charge, mémoire, swap, PSI, watchdog, top process, thin pools
#   diagnose_disk_<ct> usage rootfs + data du CT (101-111)
#   diagnose_docker_<ct> conteneurs down/restarting + restartloop + volumes (101-111)
#   diagnose_net       PVE→VPS NetBird + DNS LAN + sonde dns-switch (dernier état)
#   diagnose_media     transcodage Jellyfin, ffmpeg actifs, fenêtre Tdarr, qBit
#   diagnose_services  checks Healthchecks en échec (lecture DB hc.sqlite via HTTP API? non: via ls/log)
#   diagnose_failover  état dns-switch (state, swapped), tunnel CF, caddy-failover, sso-switch
set -u
ACTION="${SSH_ORIGINAL_COMMAND:-}"
[ -n "$ACTION" ] || { echo "REFUS: action absente"; exit 1; }
echo "$ACTION" | grep -qE "^diagnose_(pve|disk_(101|102|103|104|105|106|107|108|109|110|111)|docker_(101|102|103|104|105|106|107|108|109|110|111)|net|media|services|failover)$" || { echo "REFUS: format invalide"; exit 1; }

hr() { printf '%s\n' "────────────────────────────────"; }

case "$ACTION" in
  diagnose_pve)
    hr; echo "PVE — charge / mémoire / I/O"
    echo "load: $(uptime | grep -oE 'load average.*')"
    free -h | sed -n '2p'
    # Swap : la saturation a déjà causé un gel (07/09)
    echo "swap: $(free -h | awk '/Swap/{print $2" utilisé sur "$3}')"
    for f in /sys/fs/cgroup/cpu.pressure /sys/fs/cgroup/io.pressure /sys/fs/cgroup/memory.pressure; do
      [ -f "$f" ] && echo "PSI $(basename $f): $(grep some "$f" | awk '{print $2" "$4}')"
    done
    hr; echo "Watchdog (gel 30/09 : softdog inopérant)"
    DRV=$(journalctl -u watchdog-mux -b --no-pager 2>/dev/null | grep -oE "driver '[A-Za-z_0-9]+'" | tail -1)
    echo "driver actif: ${DRV:-inconnu} (attendu: iTCO_wdt)"
    hr; journald_disk=$(journalctl --disk-usage 2>/var/log/none | grep -oE '[0-9.]+[KMG]'); echo "journal: $journald_disk (plafond 2G)"
    echo "top CPU:"; ps -eo pcpu,comm --sort=-pcpu | head -4
    echo "top MEM:"; ps -eo pmem,comm --sort=-pmem | head -4
    ;;
  diagnose_disk_*)
    CT="${ACTION#diagnose_disk_}"
    hr; echo "CT $CT — stockage"
    pct exec "$CT" -- df -h / | sed -n 2p
    pct exec "$CT" -- sh -c "command -v docker >/dev/null && docker system df 2>/dev/null | head -5" 2>/dev/null
    echo "(rootfs sur $(grep -oE '(ssd-disk|local-lvm):vm-[0-9]+' /etc/pve/lxc/$CT.conf 2>/dev/null | head -1))"
    ;;
  diagnose_docker_*)
    CT="${ACTION#diagnose_docker_}"
    hr; echo "CT $CT — conteneurs Docker"
    pct exec "$CT" -- sh -c 'docker ps -a --format "{{.Names}}: {{.Status}}" | grep -vE "Up .* \(healthy\)|^$"' 2>/dev/null | grep -E "Exited|Restarting|unhealthy|Dead" \
      || echo "tous les conteneurs UP"
    ;;
  diagnose_net)
    hr; echo "Réseau — PVE → VPS (NetBird) + DNS LAN"
    CODE=$(curl -s -o /dev/null -m 6 -w "%{http_code}" http://100.70.222.73:8011/ 2>/dev/null)
    echo "PVE → vektor-api (NetBird 8011): HTTP $CODE (404 attendu = joignable)"
    for dns in 192.168.1.70 192.168.1.62; do
      dig "@$dns" mayoraz-net.ch +short +time=2 +tries=1 >/dev/null 2>&1 \
        && echo "DNS $dns: OK" || echo "DNS $dns: KO"
    done
    ;;
  diagnose_media)
    hr; echo "Média — transcodage / fenêtre Tdarr / qBit"
    FF=$(pgrep -c ffmpeg 2>/dev/null); FF=${FF:-0}
    echo "ffmpeg actifs: $FF"
    JF=$(pct exec 102 -- systemctl is-active jellyfin 2>/dev/null)
    echo "jellyfin (systemd, CT 102): ${JF:-inconnu}"
    QT=$(pct exec 107 -- sh -c 'curl -s -b "SID=bypass-lan-whitelist" http://192.168.1.77:8181/api/v2/torrents/info' 2>/dev/null | grep -oE '"state":"[a-z_]*ing"' | wc -l)
    echo "qBit (107) torrents actifs: ${QT:-?}"
    ;;
  diagnose_services)
    hr; echo "Healthchecks — dernier rapport"
    # Lecture seule : le rapport du matin (09:35) résume l'état des checks.
    # Ici on liste simplement les checks dont le ping date (stat via API HTTP publique).
    pct exec 104 -- sh -c 'docker ps --filter name=healthchecks --format "{{.Status}}"' 2>/dev/null | sed 's/^/healthchecks: /'
    echo "(détail des checks : voir rapport Telegram 09:35 ou HC :8010)"
    ;;
  diagnose_failover)
    hr; echo "Failover DNS/SSO — état des gardiens"
    echo "dns-switch (CT 104):"
    pct exec 104 -- tail -3 /var/log/dns-switch.log 2>/dev/null | sed 's/^/  /'
    SW=$(pct exec 104 -- python3 -c "import json;print(len(json.load(open('/opt/failover/dns-switch.state')).get('swapped',[])))" 2>/dev/null)
    echo "  records basculés (swapped): ${SW:-?}"
    echo "caddy-failover: $(pct exec 104 -- docker ps --filter name=caddy-failover --format '{{.Status}}')"
    echo "tunnel cloudflared: $(pct exec 104 -- docker ps --filter name=cloudflared --format '{{.Status}}')"
    ;;
esac
echo "── fin diagnostic ($ACTION) ──"