All checks were successful
Build and Release / build-and-release (push) Successful in 1m43s
Move network collection from NixOS collector to dedicated NetworkCollector. Add link status detection for physical interfaces (up/down). Group interfaces by physical/virtual, show status icons for physical NICs only. Down interfaces show as Inactive instead of Critical. Version bump to 0.1.165
138 lines
5.2 KiB
Rust
138 lines
5.2 KiB
Rust
use async_trait::async_trait;
|
|
use cm_dashboard_shared::{AgentData, NetworkInterfaceData, Status};
|
|
use std::process::Command;
|
|
use tracing::debug;
|
|
|
|
use super::{Collector, CollectorError};
|
|
use crate::config::NetworkConfig;
|
|
|
|
/// Network interface collector with physical/virtual classification and link status
|
|
pub struct NetworkCollector {
|
|
_config: NetworkConfig,
|
|
}
|
|
|
|
impl NetworkCollector {
|
|
pub fn new(config: NetworkConfig) -> Self {
|
|
Self { _config: config }
|
|
}
|
|
|
|
/// Check if interface is physical (not virtual)
|
|
fn is_physical_interface(name: &str) -> bool {
|
|
// Physical interface patterns
|
|
matches!(
|
|
&name[..],
|
|
s if s.starts_with("eth")
|
|
|| s.starts_with("ens")
|
|
|| s.starts_with("enp")
|
|
|| s.starts_with("wlan")
|
|
|| s.starts_with("wlp")
|
|
|| s.starts_with("eno")
|
|
|| s.starts_with("enx")
|
|
)
|
|
}
|
|
|
|
/// Get link status for an interface
|
|
fn get_link_status(interface: &str) -> Status {
|
|
let operstate_path = format!("/sys/class/net/{}/operstate", interface);
|
|
|
|
match std::fs::read_to_string(&operstate_path) {
|
|
Ok(state) => {
|
|
let state = state.trim();
|
|
match state {
|
|
"up" => Status::Ok,
|
|
"down" => Status::Inactive,
|
|
"unknown" => Status::Warning,
|
|
_ => Status::Unknown,
|
|
}
|
|
}
|
|
Err(_) => Status::Unknown,
|
|
}
|
|
}
|
|
|
|
/// Collect network interfaces using ip command
|
|
async fn collect_interfaces(&self) -> Vec<NetworkInterfaceData> {
|
|
let mut interfaces = Vec::new();
|
|
|
|
match Command::new("ip").args(["-j", "addr"]).output() {
|
|
Ok(output) if output.status.success() => {
|
|
let json_str = String::from_utf8_lossy(&output.stdout);
|
|
|
|
if let Ok(json_data) = serde_json::from_str::<serde_json::Value>(&json_str) {
|
|
if let Some(ifaces) = json_data.as_array() {
|
|
for iface in ifaces {
|
|
let name = iface["ifname"].as_str().unwrap_or("").to_string();
|
|
|
|
// Skip loopback and empty names
|
|
if name.is_empty() || name == "lo" {
|
|
continue;
|
|
}
|
|
|
|
let mut ipv4_addresses = Vec::new();
|
|
let mut ipv6_addresses = Vec::new();
|
|
|
|
// Extract IP addresses
|
|
if let Some(addr_info) = iface["addr_info"].as_array() {
|
|
for addr in addr_info {
|
|
if let Some(family) = addr["family"].as_str() {
|
|
if let Some(local) = addr["local"].as_str() {
|
|
match family {
|
|
"inet" => ipv4_addresses.push(local.to_string()),
|
|
"inet6" => {
|
|
// Skip link-local IPv6 addresses (fe80::)
|
|
if !local.starts_with("fe80:") {
|
|
ipv6_addresses.push(local.to_string());
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine if physical and get status
|
|
let is_physical = Self::is_physical_interface(&name);
|
|
let link_status = if is_physical {
|
|
Self::get_link_status(&name)
|
|
} else {
|
|
Status::Unknown // Virtual interfaces don't have meaningful link status
|
|
};
|
|
|
|
interfaces.push(NetworkInterfaceData {
|
|
name,
|
|
ipv4_addresses,
|
|
ipv6_addresses,
|
|
is_physical,
|
|
link_status,
|
|
parent_interface: None, // TODO: Implement virtual interface parent detection
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Err(e) => {
|
|
debug!("Failed to execute ip command: {}", e);
|
|
}
|
|
Ok(output) => {
|
|
debug!("ip command failed with status: {}", output.status);
|
|
}
|
|
}
|
|
|
|
interfaces
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl Collector for NetworkCollector {
|
|
async fn collect_structured(&self, agent_data: &mut AgentData) -> Result<(), CollectorError> {
|
|
debug!("Collecting network interface data");
|
|
|
|
// Collect all network interfaces
|
|
let interfaces = self.collect_interfaces().await;
|
|
|
|
agent_data.system.network.interfaces = interfaces;
|
|
|
|
Ok(())
|
|
}
|
|
}
|