Restructure into workspace with dashboard and agent

This commit is contained in:
2025-10-11 13:56:58 +02:00
parent 65d31514a1
commit 82afe3d4f1
23 changed files with 405 additions and 59 deletions

View File

@@ -0,0 +1,138 @@
#![allow(dead_code)]
use std::collections::HashMap;
use std::path::PathBuf;
use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
pub struct HostsConfig {
pub default_host: Option<String>,
#[serde(default)]
pub hosts: Vec<HostTarget>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct HostTarget {
pub name: String,
#[serde(default = "default_true")]
pub enabled: bool,
#[serde(default)]
pub metadata: HashMap<String, String>,
}
impl HostTarget {
pub fn from_name(name: String) -> Self {
Self {
name,
enabled: true,
metadata: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct DashboardConfig {
#[serde(default = "default_tick_rate_ms")]
pub tick_rate_ms: u64,
#[serde(default)]
pub history_duration_minutes: u64,
#[serde(default)]
pub widgets: Vec<WidgetConfig>,
}
impl Default for DashboardConfig {
fn default() -> Self {
Self {
tick_rate_ms: default_tick_rate_ms(),
history_duration_minutes: 60,
widgets: Vec::new(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct WidgetConfig {
pub id: String,
#[serde(default)]
pub enabled: bool,
#[serde(default)]
pub options: HashMap<String, String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AppFilesystem {
pub cache_dir: Option<PathBuf>,
pub history_dir: Option<PathBuf>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AppConfig {
pub hosts: HostsConfig,
#[serde(default)]
pub dashboard: DashboardConfig,
#[serde(default = "default_data_source_config")]
pub data_source: DataSourceConfig,
#[serde(default)]
pub filesystem: Option<AppFilesystem>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct DataSourceConfig {
#[serde(default = "default_data_source_kind")]
pub kind: DataSourceKind,
#[serde(default)]
pub zmq: ZmqConfig,
}
impl Default for DataSourceConfig {
fn default() -> Self {
Self {
kind: DataSourceKind::Zmq,
zmq: ZmqConfig::default(),
}
}
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DataSourceKind {
Zmq,
}
fn default_data_source_kind() -> DataSourceKind {
DataSourceKind::Zmq
}
#[derive(Debug, Clone, Deserialize)]
pub struct ZmqConfig {
#[serde(default = "default_zmq_endpoints")]
pub endpoints: Vec<String>,
#[serde(default)]
pub subscribe: Option<String>,
}
impl Default for ZmqConfig {
fn default() -> Self {
Self {
endpoints: default_zmq_endpoints(),
subscribe: None,
}
}
}
const fn default_true() -> bool {
true
}
const fn default_tick_rate_ms() -> u64 {
500
}
fn default_data_source_config() -> DataSourceConfig {
DataSourceConfig::default()
}
fn default_zmq_endpoints() -> Vec<String> {
vec!["tcp://127.0.0.1:6130".to_string()]
}

View File

@@ -0,0 +1,54 @@
#![allow(dead_code)]
use std::collections::VecDeque;
use std::time::Duration;
use chrono::{DateTime, Utc};
use crate::data::metrics::{BackupMetrics, ServiceMetrics, SmartMetrics};
/// Ring buffer for retaining recent samples for trend analysis.
#[derive(Debug)]
pub struct MetricsHistory {
capacity: usize,
smart: VecDeque<(DateTime<Utc>, SmartMetrics)>,
services: VecDeque<(DateTime<Utc>, ServiceMetrics)>,
backups: VecDeque<(DateTime<Utc>, BackupMetrics)>,
}
impl MetricsHistory {
pub fn with_capacity(capacity: usize) -> Self {
Self {
capacity,
smart: VecDeque::with_capacity(capacity),
services: VecDeque::with_capacity(capacity),
backups: VecDeque::with_capacity(capacity),
}
}
pub fn record_smart(&mut self, metrics: SmartMetrics) {
let entry = (Utc::now(), metrics);
Self::push_with_limit(&mut self.smart, entry, self.capacity);
}
pub fn record_services(&mut self, metrics: ServiceMetrics) {
let entry = (Utc::now(), metrics);
Self::push_with_limit(&mut self.services, entry, self.capacity);
}
pub fn record_backup(&mut self, metrics: BackupMetrics) {
let entry = (Utc::now(), metrics);
Self::push_with_limit(&mut self.backups, entry, self.capacity);
}
pub fn retention(&self) -> Duration {
Duration::from_secs((self.capacity as u64) * 30)
}
fn push_with_limit<T>(deque: &mut VecDeque<T>, item: T, capacity: usize) {
if deque.len() == capacity {
deque.pop_front();
}
deque.push_back(item);
}
}

View File

@@ -0,0 +1,96 @@
#![allow(dead_code)]
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SmartMetrics {
pub status: String,
pub drives: Vec<DriveInfo>,
pub summary: DriveSummary,
pub issues: Vec<String>,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DriveInfo {
pub name: String,
pub temperature_c: f32,
pub wear_level: f32,
pub power_on_hours: u64,
pub available_spare: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DriveSummary {
pub healthy: usize,
pub warning: usize,
pub critical: usize,
pub capacity_total_gb: f32,
pub capacity_used_gb: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceMetrics {
pub summary: ServiceSummary,
pub services: Vec<ServiceInfo>,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceSummary {
pub healthy: usize,
pub degraded: usize,
pub failed: usize,
pub memory_used_mb: f32,
pub memory_quota_mb: f32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceInfo {
pub name: String,
pub status: ServiceStatus,
pub memory_used_mb: f32,
pub memory_quota_mb: f32,
pub cpu_percent: f32,
pub sandbox_limit: Option<f32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ServiceStatus {
Running,
Degraded,
Restarting,
Stopped,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackupMetrics {
pub overall_status: BackupStatus,
pub backup: BackupInfo,
pub service: BackupServiceInfo,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackupInfo {
pub last_success: Option<DateTime<Utc>>,
pub last_failure: Option<DateTime<Utc>>,
pub size_gb: f32,
pub snapshot_count: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BackupServiceInfo {
pub enabled: bool,
pub pending_jobs: u32,
pub last_message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BackupStatus {
Healthy,
Warning,
Failed,
Unknown,
}

View File

@@ -0,0 +1,3 @@
pub mod config;
pub mod history;
pub mod metrics;