All checks were successful
Build and Release / build-and-release (push) Successful in 1m25s
Add connection_method field to NetworkInterfaceData to track whether Tailscale is using direct P2P, DERP relay, or HTTP proxy connections. The connection method is displayed as a sub-service under tailscaled service, following the same pattern as VPN routes and firewall ports. Query tailscale status --json to determine active connection type and display as informational sub-service when tailscaled is active.
254 lines
7.3 KiB
Rust
254 lines
7.3 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use crate::Status;
|
|
|
|
/// Complete structured data from an agent
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct AgentData {
|
|
pub hostname: String,
|
|
pub agent_version: String,
|
|
pub build_version: Option<String>,
|
|
pub timestamp: u64,
|
|
pub system: SystemData,
|
|
pub services: Vec<ServiceData>,
|
|
pub backup: BackupData,
|
|
}
|
|
|
|
/// System-level monitoring data
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SystemData {
|
|
pub network: NetworkData,
|
|
pub cpu: CpuData,
|
|
pub memory: MemoryData,
|
|
pub storage: StorageData,
|
|
}
|
|
|
|
/// Network interface monitoring data
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct NetworkData {
|
|
pub interfaces: Vec<NetworkInterfaceData>,
|
|
}
|
|
|
|
/// Individual network interface data
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct NetworkInterfaceData {
|
|
pub name: String,
|
|
pub ipv4_addresses: Vec<String>,
|
|
pub ipv6_addresses: Vec<String>,
|
|
pub is_physical: bool,
|
|
pub link_status: Status,
|
|
pub parent_interface: Option<String>,
|
|
pub vlan_id: Option<u16>,
|
|
pub connection_method: Option<String>, // For Tailscale: "direct", "relay", or "proxy"
|
|
}
|
|
|
|
/// CPU C-state usage information
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CStateInfo {
|
|
pub name: String,
|
|
pub percent: f32,
|
|
}
|
|
|
|
/// CPU monitoring data
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CpuData {
|
|
pub load_1min: f32,
|
|
pub load_5min: f32,
|
|
pub load_15min: f32,
|
|
pub cstates: Vec<CStateInfo>, // C-state usage percentages (C1, C6, C10, etc.) - indicates CPU idle depth distribution
|
|
pub temperature_celsius: Option<f32>,
|
|
pub load_status: Status,
|
|
pub temperature_status: Status,
|
|
// Static CPU information (collected once at startup)
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub model_name: Option<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub core_count: Option<u32>,
|
|
}
|
|
|
|
/// Memory monitoring data
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct MemoryData {
|
|
pub usage_percent: f32,
|
|
pub total_gb: f32,
|
|
pub used_gb: f32,
|
|
pub available_gb: f32,
|
|
pub swap_total_gb: f32,
|
|
pub swap_used_gb: f32,
|
|
pub tmpfs: Vec<TmpfsData>,
|
|
pub usage_status: Status,
|
|
}
|
|
|
|
/// Tmpfs filesystem data
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TmpfsData {
|
|
pub mount: String,
|
|
pub usage_percent: f32,
|
|
pub used_gb: f32,
|
|
pub total_gb: f32,
|
|
}
|
|
|
|
/// Storage monitoring data
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct StorageData {
|
|
pub drives: Vec<DriveData>,
|
|
pub pools: Vec<PoolData>,
|
|
}
|
|
|
|
/// Individual drive data
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct DriveData {
|
|
pub name: String,
|
|
pub serial_number: Option<String>,
|
|
pub health: String,
|
|
pub temperature_celsius: Option<f32>,
|
|
pub wear_percent: Option<f32>,
|
|
pub filesystems: Vec<FilesystemData>,
|
|
pub temperature_status: Status,
|
|
pub health_status: Status,
|
|
}
|
|
|
|
/// Filesystem on a drive
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct FilesystemData {
|
|
pub mount: String,
|
|
pub usage_percent: f32,
|
|
pub used_gb: f32,
|
|
pub total_gb: f32,
|
|
pub usage_status: Status,
|
|
}
|
|
|
|
/// Storage pool (MergerFS, RAID, etc.)
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PoolData {
|
|
pub name: String,
|
|
pub mount: String,
|
|
pub pool_type: String, // "mergerfs", "raid", etc.
|
|
pub health: String,
|
|
pub usage_percent: f32,
|
|
pub used_gb: f32,
|
|
pub total_gb: f32,
|
|
pub data_drives: Vec<PoolDriveData>,
|
|
pub parity_drives: Vec<PoolDriveData>,
|
|
pub health_status: Status,
|
|
pub usage_status: Status,
|
|
}
|
|
|
|
/// Drive in a storage pool
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct PoolDriveData {
|
|
pub name: String,
|
|
pub serial_number: Option<String>,
|
|
pub temperature_celsius: Option<f32>,
|
|
pub wear_percent: Option<f32>,
|
|
pub health: String,
|
|
pub health_status: Status,
|
|
pub temperature_status: Status,
|
|
}
|
|
|
|
/// Service monitoring data
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ServiceData {
|
|
pub name: String,
|
|
pub user_stopped: bool,
|
|
pub service_status: Status,
|
|
pub sub_services: Vec<SubServiceData>,
|
|
/// Memory usage in bytes (from MemoryCurrent)
|
|
pub memory_bytes: Option<u64>,
|
|
/// Number of service restarts (from NRestarts)
|
|
pub restart_count: Option<u32>,
|
|
/// Uptime in seconds (calculated from ExecMainStartTimestamp)
|
|
pub uptime_seconds: Option<u64>,
|
|
}
|
|
|
|
/// Sub-service data (nginx sites, docker containers, etc.)
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SubServiceData {
|
|
pub name: String,
|
|
pub service_status: Status,
|
|
pub metrics: Vec<SubServiceMetric>,
|
|
/// Type of sub-service: "nginx_site", "container", "image"
|
|
#[serde(default)]
|
|
pub service_type: String,
|
|
}
|
|
|
|
/// Individual metric for a sub-service
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SubServiceMetric {
|
|
pub label: String,
|
|
pub value: f32,
|
|
pub unit: Option<String>,
|
|
}
|
|
|
|
/// Backup system data
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct BackupData {
|
|
pub repositories: Vec<String>,
|
|
pub repository_status: Status,
|
|
pub disks: Vec<BackupDiskData>,
|
|
}
|
|
|
|
/// Backup repository disk information
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct BackupDiskData {
|
|
pub serial: String,
|
|
pub product_name: Option<String>,
|
|
pub wear_percent: Option<f32>,
|
|
pub temperature_celsius: Option<f32>,
|
|
pub last_backup_time: Option<String>,
|
|
pub backup_status: Status,
|
|
pub disk_usage_percent: f32,
|
|
pub disk_used_gb: f32,
|
|
pub disk_total_gb: f32,
|
|
pub usage_status: Status,
|
|
pub services: Vec<String>,
|
|
pub archives_min: i64,
|
|
pub archives_max: i64,
|
|
}
|
|
|
|
impl AgentData {
|
|
/// Create new agent data with current timestamp
|
|
pub fn new(hostname: String, agent_version: String) -> Self {
|
|
Self {
|
|
hostname,
|
|
agent_version,
|
|
build_version: None,
|
|
timestamp: chrono::Utc::now().timestamp() as u64,
|
|
system: SystemData {
|
|
network: NetworkData {
|
|
interfaces: Vec::new(),
|
|
},
|
|
cpu: CpuData {
|
|
load_1min: 0.0,
|
|
load_5min: 0.0,
|
|
load_15min: 0.0,
|
|
cstates: Vec::new(),
|
|
temperature_celsius: None,
|
|
load_status: Status::Unknown,
|
|
temperature_status: Status::Unknown,
|
|
model_name: None,
|
|
core_count: None,
|
|
},
|
|
memory: MemoryData {
|
|
usage_percent: 0.0,
|
|
total_gb: 0.0,
|
|
used_gb: 0.0,
|
|
available_gb: 0.0,
|
|
swap_total_gb: 0.0,
|
|
swap_used_gb: 0.0,
|
|
tmpfs: Vec::new(),
|
|
usage_status: Status::Unknown,
|
|
},
|
|
storage: StorageData {
|
|
drives: Vec::new(),
|
|
pools: Vec::new(),
|
|
},
|
|
},
|
|
services: Vec::new(),
|
|
backup: BackupData {
|
|
repositories: Vec::new(),
|
|
repository_status: Status::Unknown,
|
|
disks: Vec::new(),
|
|
},
|
|
}
|
|
}
|
|
} |