Files
SentinelMesh/agents/agent-metric/src/main.rs
T
gilles 6bda1a2b59 feat(agent-metric): implémentation Phase 3 — métriques système
- Collecte temps réel (1s) : CPU, RAM, charge réseau, top 5 processus
- Collecte medium (30min) : disques via sysinfo, températures hwmon, SMART smartctl
- Collecte statique (boot) : DMI/BIOS via /sys, interfaces réseau, CPU model
- API locale Axum sur :9101 — GET /metrics (réaltime + medium + hardware)
- Push backend : /api/v1/metrics (réaltime + medium) et /api/v1/events (hardware, boot)
- Architecture modulaire : collectors/realtime, medium, static_info
- ROADMAP Phase 3 marquée complète

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 06:18:08 +02:00

90 lines
3.2 KiB
Rust

use std::sync::Arc;
use serde_json::json;
use tokio::{sync::RwLock, time};
use tracing::{error, info};
use tracing_subscriber::EnvFilter;
mod api;
mod backend;
mod collectors;
mod config;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(EnvFilter::from_default_env().add_directive("info".parse()?))
.init();
let config_path = std::env::args().nth(1).unwrap_or_else(|| "config.yaml".into());
let cfg = config::Config::load(&config_path)?;
info!("Agent ID : {} | hostname : {}", cfg.agent.id, cfg.agent.hostname);
let client = backend::BackendClient::new(&cfg);
// Enregistrement initial
if let Err(e) = client.register(&cfg).await {
error!("Enregistrement backend : {e}");
}
// Collecte hardware au démarrage
let hardware = collectors::static_info::collect();
info!("Hardware : {} — {} cœurs — {} Mo RAM", hardware.cpu_model, hardware.cpu_cores, hardware.ram_total_mb);
if let Err(e) = client.push_static(&cfg.agent.id, &hardware).await {
error!("Push hardware : {e}");
}
// Événement boot
if let Err(e) = backend::push_event(&client, &cfg.agent.id, "boot", json!({ "hostname": cfg.agent.hostname })).await {
error!("Push événement boot : {e}");
}
// État partagé avec l'API locale
let shared = Arc::new(RwLock::new(api::AgentState {
hardware: Some(hardware),
..Default::default()
}));
// API locale (tâche de fond)
let api_state = shared.clone();
let api_listen = cfg.api.listen.clone();
tokio::spawn(async move { api::serve(api_listen, api_state).await });
// Collecteur temps réel
let mut rt_collector = collectors::realtime::RealtimeCollector::new();
let realtime_interval = time::Duration::from_millis(cfg.intervals.realtime_ms);
let medium_interval = time::Duration::from_secs(cfg.intervals.medium_s);
// Première collecte medium au démarrage
let medium = collectors::medium::collect();
shared.write().await.medium = medium.clone();
if let Err(e) = client.push_medium(&cfg.agent.id, &medium).await {
error!("Push medium initial : {e}");
}
// Minuteries
let mut rt_ticker = time::interval(realtime_interval);
let mut med_ticker = time::interval(medium_interval);
med_ticker.tick().await; // consomme le premier tick immédiat
loop {
tokio::select! {
_ = rt_ticker.tick() => {
let metrics = rt_collector.collect(cfg.intervals.realtime_ms);
shared.write().await.realtime = metrics.clone();
if let Err(e) = client.push_realtime(&cfg.agent.id, &metrics).await {
error!("Push temps réel : {e}");
}
}
_ = med_ticker.tick() => {
info!("Collecte medium (disques, températures)…");
let metrics = collectors::medium::collect();
shared.write().await.medium = metrics.clone();
if let Err(e) = client.push_medium(&cfg.agent.id, &metrics).await {
error!("Push medium : {e}");
}
}
}
}
}