mirror of
https://github.com/Alkatrazz24/Funk-lab.git
synced 2026-07-09 00:54:42 +02:00
feat(monitoring): surveillance réseau active via blackbox_exporter
Sondes actives ICMP/HTTP/DNS qui complètent le monitoring passif
(node_exporter + up{}) : joignabilité hôtes/passerelle, accès internet,
santé DNS *.lab.local + forwarding, latence LAN, services de bout en
bout via Traefik.
- blackbox_exporter (chart 11.13.0, ArgoCD, ns monitoring) — ICMP via
NET_RAW, DNS du pod = dnsmasq (chemin réel lab → Traefik)
- 3 jobs blackbox-icmp/-http/-dns (additionalScrapeConfigs)
- alerts-network.yaml : internet, DNS, passerelle, hôtes, services,
latence, exporter (→ pipeline AlertManager existant)
- Asa : outil network_status + contexte présélectionnable reseau
- doc admin/infra/reseau.md + CLAUDE.md + progress
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rc3dLKfqEPHexvFCCx5ZEF
This commit is contained in:
parent
ebb2eec184
commit
649c6fde8e
11 changed files with 527 additions and 5 deletions
|
|
@ -42,8 +42,8 @@ CONTEXTS: dict[str, Context] = {
|
|||
id="asa", label="Asa · agent", icon="✨",
|
||||
description="Asa répond en s'appuyant sur des outils (doc + état live du homelab).",
|
||||
system_prompt=_BASE + " Tu disposes d'outils pour obtenir l'état RÉEL et LIVE du "
|
||||
"homelab (santé des machines, état du cluster, métriques Prometheus), chercher dans la "
|
||||
"documentation Funk, et faire une recherche sur INTERNET (web_search). Dès qu'une "
|
||||
"homelab (santé des machines, état du cluster, état du réseau, métriques Prometheus), "
|
||||
"chercher dans la documentation Funk, et faire une recherche sur INTERNET (web_search). Dès qu'une "
|
||||
"question porte sur l'état courant, un fait précis du homelab, ou une info externe / "
|
||||
"récente, APPELLE l'outil adéquat puis réponds à partir de son résultat — n'invente "
|
||||
"jamais. Si les outils ne donnent pas l'information, dis-le simplement. Pour le "
|
||||
|
|
@ -51,8 +51,8 @@ CONTEXTS: dict[str, Context] = {
|
|||
"explicitement d'AGIR (redémarrer, relancer, modifier…), utilise l'outil admin_action "
|
||||
"(s'il est disponible) — il proposera l'action et demandera confirmation, sans rien "
|
||||
"exécuter de toi-même.",
|
||||
tools=("search_docs", "host_health", "cluster_status", "prometheus_query", "web_search",
|
||||
"admin_action"),
|
||||
tools=("search_docs", "host_health", "cluster_status", "network_status",
|
||||
"prometheus_query", "web_search", "admin_action"),
|
||||
),
|
||||
"funk": Context(
|
||||
id="funk", label="Funk · cluster", icon="🛠️",
|
||||
|
|
@ -92,6 +92,15 @@ CONTEXTS: dict[str, Context] = {
|
|||
"Tu fais de la LECTURE SEULE : tu n'exécutes aucune action.",
|
||||
sources=("cluster", "docs"),
|
||||
),
|
||||
"reseau": Context(
|
||||
id="reseau", label="Réseau", icon="🌐",
|
||||
description="Surveillance réseau active : joignabilité, internet, DNS, latence.",
|
||||
system_prompt=_BASE + " Tu réponds sur l'état du RÉSEAU du homelab à partir des "
|
||||
"sondes actives fournies ci-dessous (joignabilité des hôtes et de la passerelle, "
|
||||
"accès internet, DNS *.lab.local, latence LAN, services injoignables). Reste "
|
||||
"factuel ; si une sonde est en échec, précise laquelle.",
|
||||
sources=("network",),
|
||||
),
|
||||
# Contexte « agent » : court-circuite le LLM — la demande est confirmée puis
|
||||
# exécutée par le vrai agent Hermes (hermes-exec). Géré entièrement dans app.py
|
||||
# (handshake), pas via assemble(). Exposé seulement si actions_enabled + jeton.
|
||||
|
|
@ -109,6 +118,7 @@ _SOURCE_TITLES = {
|
|||
"metrics": "Métriques Prometheus (live)",
|
||||
"alerts": "Alertes actives (Alertmanager)",
|
||||
"cluster": "État du cluster (live)",
|
||||
"network": "Réseau — sondes actives (live)",
|
||||
"docs": "Documentation Funk",
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -263,12 +263,43 @@ async def metrics_block(client: httpx.AsyncClient) -> str:
|
|||
return "\n".join(lines) if lines else "Aucune métrique disponible."
|
||||
|
||||
|
||||
async def network_block(client: httpx.AsyncClient) -> str:
|
||||
"""Surveillance réseau active (sondes blackbox_exporter) : joignabilité,
|
||||
internet, DNS, latence LAN, et liste des sondes en échec."""
|
||||
try:
|
||||
total = await _prom_query(client, 'count(probe_success{job=~"blackbox-.*"})')
|
||||
up = await _prom_query(client, 'count(probe_success{job=~"blackbox-.*"} == 1)')
|
||||
internet = await _prom_query(client, 'sum(probe_success{job="blackbox-icmp",scope="internet"})')
|
||||
dns_lab = await _prom_query(client, 'probe_success{job="blackbox-dns",module="dns_lab"}')
|
||||
rtt = await _prom_query(client, 'max(probe_icmp_duration_seconds{scope="lan",phase="rtt"})')
|
||||
failing = await _prom_query(client, 'probe_success{job=~"blackbox-.*"} == 0')
|
||||
except httpx.HTTPError:
|
||||
return "Données indisponibles : Prometheus injoignable."
|
||||
if not total:
|
||||
return ("Aucune sonde réseau active (blackbox_exporter pas encore déployé "
|
||||
"ou sans données).")
|
||||
lines = [f"Sondes réseau OK : {int(up[0][1]) if up else 0}/{int(total[0][1])}."]
|
||||
if internet:
|
||||
lines.append("Internet : " + ("accessible" if internet[0][1] > 0 else "INACCESSIBLE") + ".")
|
||||
if dns_lab:
|
||||
lines.append("DNS *.lab.local : " + ("OK" if dns_lab[0][1] == 1 else "EN ÉCHEC") + ".")
|
||||
if rtt:
|
||||
lines.append(f"Latence LAN max : {round(rtt[0][1] * 1000)} ms.")
|
||||
if failing:
|
||||
names = ", ".join(m.get("instance", "?") for m, _ in failing[:8] if m.get("instance"))
|
||||
lines.append(f"⚠️ Sondes en échec : {names}.")
|
||||
else:
|
||||
lines.append("Toutes les sondes répondent.")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
# Aiguillage source_id → fetcher
|
||||
_FETCHERS = {
|
||||
"ghostfolio": ghostfolio_block,
|
||||
"alerts": alerts_block,
|
||||
"cluster": cluster_block,
|
||||
"metrics": metrics_block,
|
||||
"network": network_block,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -131,6 +131,11 @@ async def _cluster_status(args: dict, deps: ToolDeps) -> str:
|
|||
return await sources.cluster_block(deps.client)
|
||||
|
||||
|
||||
async def _network_status(args: dict, deps: ToolDeps) -> str:
|
||||
# Réutilise le bloc surveillance réseau (sondes actives blackbox).
|
||||
return await sources.network_block(deps.client)
|
||||
|
||||
|
||||
async def _prometheus_query(args: dict, deps: ToolDeps) -> str:
|
||||
expr = (args.get("expr") or "").strip()
|
||||
if not expr:
|
||||
|
|
@ -243,6 +248,21 @@ _TOOLS: dict[str, tuple[dict, object]] = {
|
|||
},
|
||||
_cluster_status,
|
||||
),
|
||||
"network_status": (
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "network_status",
|
||||
"description": "État LIVE du réseau du homelab (sondes actives blackbox) : "
|
||||
"joignabilité des hôtes et de la passerelle, accès internet, santé DNS "
|
||||
"(*.lab.local), latence LAN et services injoignables. À utiliser pour "
|
||||
"« est-ce que le réseau / internet / le DNS fonctionne ? » ou "
|
||||
"« quel hôte ne répond plus ? ».",
|
||||
"parameters": {"type": "object", "properties": {}},
|
||||
},
|
||||
},
|
||||
_network_status,
|
||||
),
|
||||
"prometheus_query": (
|
||||
{
|
||||
"type": "function",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue