Restructure into workspace with dashboard and agent
This commit is contained in:
18
agent/Cargo.toml
Normal file
18
agent/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "cm-dashboard-agent"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cm-dashboard-shared = { path = "../shared" }
|
||||
anyhow = "1.0"
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
|
||||
tracing-appender = "0.2"
|
||||
zmq = "0.10"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
rand = "0.8"
|
||||
182
agent/src/main.rs
Normal file
182
agent/src/main.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use chrono::Utc;
|
||||
use clap::{ArgAction, Parser};
|
||||
use cm_dashboard_shared::envelope::{AgentType, MetricsEnvelope};
|
||||
use rand::Rng;
|
||||
use serde_json::json;
|
||||
use tracing::info;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use zmq::{Context as ZmqContext, SocketType};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(
|
||||
name = "cm-dashboard-agent",
|
||||
version,
|
||||
about = "CM Dashboard metrics agent"
|
||||
)]
|
||||
struct Cli {
|
||||
/// Hostname to advertise in metric envelopes
|
||||
#[arg(long, value_name = "HOSTNAME")]
|
||||
hostname: String,
|
||||
|
||||
/// Bind endpoint for PUB socket (default tcp://*:6130)
|
||||
#[arg(long, default_value = "tcp://*:6130", value_name = "ENDPOINT")]
|
||||
bind: String,
|
||||
|
||||
/// Publish interval in milliseconds
|
||||
#[arg(long, default_value_t = 5000)]
|
||||
interval_ms: u64,
|
||||
|
||||
/// Disable smart metrics publisher
|
||||
#[arg(long, action = ArgAction::SetTrue)]
|
||||
disable_smart: bool,
|
||||
|
||||
/// Disable service metrics publisher
|
||||
#[arg(long, action = ArgAction::SetTrue)]
|
||||
disable_service: bool,
|
||||
|
||||
/// Disable backup metrics publisher
|
||||
#[arg(long, action = ArgAction::SetTrue)]
|
||||
disable_backup: bool,
|
||||
|
||||
/// Increase logging verbosity (-v, -vv)
|
||||
#[arg(short, long, action = ArgAction::Count)]
|
||||
verbose: u8,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
init_tracing(cli.verbose)?;
|
||||
|
||||
let context = ZmqContext::new();
|
||||
let socket = context
|
||||
.socket(SocketType::PUB)
|
||||
.context("failed to create ZMQ PUB socket")?;
|
||||
socket
|
||||
.bind(&cli.bind)
|
||||
.with_context(|| format!("failed to bind to {}", cli.bind))?;
|
||||
info!(endpoint = %cli.bind, host = %cli.hostname, "agent started");
|
||||
|
||||
let interval = Duration::from_millis(cli.interval_ms.max(100));
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
loop {
|
||||
let now = Utc::now();
|
||||
let timestamp = now.timestamp() as u64;
|
||||
let timestamp_rfc3339 = now.to_rfc3339();
|
||||
|
||||
if !cli.disable_smart {
|
||||
let envelope = MetricsEnvelope {
|
||||
hostname: cli.hostname.clone(),
|
||||
agent_type: AgentType::Smart,
|
||||
timestamp,
|
||||
metrics: json!({
|
||||
"status": "Healthy",
|
||||
"drives": [{
|
||||
"name": "nvme0n1",
|
||||
"temperature_c": rng.gen_range(30.0..60.0),
|
||||
"wear_level": rng.gen_range(1.0..10.0),
|
||||
"power_on_hours": rng.gen_range(1000..20000),
|
||||
"available_spare": rng.gen_range(90.0..100.0)
|
||||
}],
|
||||
"summary": {
|
||||
"healthy": 1,
|
||||
"warning": 0,
|
||||
"critical": 0,
|
||||
"capacity_total_gb": 1024,
|
||||
"capacity_used_gb": rng.gen_range(100.0..800.0)
|
||||
},
|
||||
"issues": [],
|
||||
"timestamp": timestamp_rfc3339
|
||||
}),
|
||||
};
|
||||
publish(&socket, &envelope)?;
|
||||
}
|
||||
|
||||
if !cli.disable_service {
|
||||
let envelope = MetricsEnvelope {
|
||||
hostname: cli.hostname.clone(),
|
||||
agent_type: AgentType::Service,
|
||||
timestamp,
|
||||
metrics: json!({
|
||||
"summary": {
|
||||
"healthy": 5,
|
||||
"degraded": 0,
|
||||
"failed": 0,
|
||||
"memory_used_mb": rng.gen_range(512.0..2048.0),
|
||||
"memory_quota_mb": 4096.0
|
||||
},
|
||||
"services": [
|
||||
{
|
||||
"name": "example",
|
||||
"status": "Running",
|
||||
"memory_used_mb": rng.gen_range(128.0..512.0),
|
||||
"memory_quota_mb": 1024.0,
|
||||
"cpu_percent": rng.gen_range(0.0..75.0),
|
||||
"sandbox_limit": null
|
||||
}
|
||||
],
|
||||
"timestamp": timestamp_rfc3339
|
||||
}),
|
||||
};
|
||||
publish(&socket, &envelope)?;
|
||||
}
|
||||
|
||||
if !cli.disable_backup {
|
||||
let envelope = MetricsEnvelope {
|
||||
hostname: cli.hostname.clone(),
|
||||
agent_type: AgentType::Backup,
|
||||
timestamp,
|
||||
metrics: json!({
|
||||
"overall_status": "Healthy",
|
||||
"backup": {
|
||||
"last_success": timestamp_rfc3339,
|
||||
"last_failure": null,
|
||||
"size_gb": rng.gen_range(100.0..500.0),
|
||||
"snapshot_count": rng.gen_range(10..40)
|
||||
},
|
||||
"service": {
|
||||
"enabled": true,
|
||||
"pending_jobs": 0,
|
||||
"last_message": "Backups up-to-date"
|
||||
},
|
||||
"timestamp": timestamp_rfc3339
|
||||
}),
|
||||
};
|
||||
publish(&socket, &envelope)?;
|
||||
}
|
||||
|
||||
thread::sleep(interval);
|
||||
}
|
||||
}
|
||||
|
||||
fn publish(socket: &zmq::Socket, envelope: &MetricsEnvelope) -> Result<()> {
|
||||
let serialized = serde_json::to_vec(envelope)?;
|
||||
socket.send(serialized, 0)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_tracing(verbosity: u8) -> Result<()> {
|
||||
let level = match verbosity {
|
||||
0 => "info",
|
||||
1 => "debug",
|
||||
_ => "trace",
|
||||
};
|
||||
|
||||
let env_filter = std::env::var("RUST_LOG")
|
||||
.ok()
|
||||
.and_then(|value| EnvFilter::try_new(value).ok())
|
||||
.unwrap_or_else(|| EnvFilter::new(level));
|
||||
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(env_filter)
|
||||
.with_target(false)
|
||||
.compact()
|
||||
.try_init()
|
||||
.map_err(|err| anyhow!(err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user