Add comprehensive service descriptions with useful metrics
This commit is contained in:
parent
d11d8a74f3
commit
6da23019e5
@ -433,11 +433,17 @@ impl ServiceCollector {
|
|||||||
async fn get_service_description(&self, service: &str) -> Option<Vec<String>> {
|
async fn get_service_description(&self, service: &str) -> Option<Vec<String>> {
|
||||||
let result = match service {
|
let result = match service {
|
||||||
"sshd" | "ssh" => self.get_ssh_active_users().await.map(|s| vec![s]),
|
"sshd" | "ssh" => self.get_ssh_active_users().await.map(|s| vec![s]),
|
||||||
"nginx" => self.get_nginx_sites().await,
|
"nginx" => self.get_nginx_description().await.map(|s| vec![s]),
|
||||||
"apache2" | "httpd" => self.get_web_server_connections().await.map(|s| vec![s]),
|
"apache2" | "httpd" => self.get_web_server_connections().await.map(|s| vec![s]),
|
||||||
"docker" => self.get_docker_containers().await.map(|s| vec![s]),
|
"docker" | "docker-registry" => self.get_docker_containers().await.map(|s| vec![s]),
|
||||||
"postgresql" | "postgres" => self.get_postgres_connections().await.map(|s| vec![s]),
|
"postgresql" | "postgres" => self.get_postgres_connections().await.map(|s| vec![s]),
|
||||||
"mysql" | "mariadb" => self.get_mysql_connections().await.map(|s| vec![s]),
|
"mysql" | "mariadb" => self.get_mysql_connections().await.map(|s| vec![s]),
|
||||||
|
"redis" | "redis-immich" => self.get_redis_info().await.map(|s| vec![s]),
|
||||||
|
"gitea" => self.get_gitea_info().await.map(|s| vec![s]),
|
||||||
|
"immich-server" | "immich" => self.get_immich_info().await.map(|s| vec![s]),
|
||||||
|
"vaultwarden" => self.get_vaultwarden_info().await.map(|s| vec![s]),
|
||||||
|
"unifi" => self.get_unifi_info().await.map(|s| vec![s]),
|
||||||
|
"mosquitto" => self.get_mosquitto_info().await.map(|s| vec![s]),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -791,6 +797,167 @@ impl ServiceCollector {
|
|||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_nginx_description(&self) -> Option<String> {
|
||||||
|
// Get site count and active connections
|
||||||
|
let sites = self.get_nginx_sites().await?;
|
||||||
|
let site_count = sites.len();
|
||||||
|
|
||||||
|
// Get active connections
|
||||||
|
let connections = self.get_web_server_connections().await;
|
||||||
|
|
||||||
|
if let Some(conn_info) = connections {
|
||||||
|
Some(format!("{} sites, {}", site_count, conn_info))
|
||||||
|
} else {
|
||||||
|
Some(format!("{} sites", site_count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_redis_info(&self) -> Option<String> {
|
||||||
|
let output = Command::new("redis-cli")
|
||||||
|
.args(["info", "clients"])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
for line in stdout.lines() {
|
||||||
|
if line.starts_with("connected_clients:") {
|
||||||
|
if let Some(count) = line.split(':').nth(1) {
|
||||||
|
if let Ok(client_count) = count.trim().parse::<i32>() {
|
||||||
|
return Some(format!("{} connected clients", client_count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_gitea_info(&self) -> Option<String> {
|
||||||
|
// Try to get gitea stats from API (if accessible)
|
||||||
|
let output = Command::new("curl")
|
||||||
|
.args(["-s", "-f", "http://localhost:3000/api/v1/repos/search?limit=1"])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&stdout) {
|
||||||
|
if let Some(total_count) = json["total_count"].as_u64() {
|
||||||
|
return Some(format!("{} repositories", total_count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: check data directory
|
||||||
|
if let Ok(metadata) = tokio::fs::metadata("/var/lib/gitea/data/gitea.db").await {
|
||||||
|
let size_mb = metadata.len() as f32 / (1024.0 * 1024.0);
|
||||||
|
return Some(format!("DB: {:.1} MB", size_mb));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_immich_info(&self) -> Option<String> {
|
||||||
|
// Check upload directory for photo count estimate
|
||||||
|
let output = Command::new("find")
|
||||||
|
.args(["/var/lib/immich/upload", "-type", "f", "-name", "*.jpg", "-o", "-name", "*.png", "-o", "-name", "*.mp4", "-o", "-name", "*.mov"])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let file_count = stdout.lines().count();
|
||||||
|
if file_count > 0 {
|
||||||
|
return Some(format!("~{} media files", file_count));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: check storage usage
|
||||||
|
let output = Command::new("sudo")
|
||||||
|
.args(["/run/current-system/sw/bin/du", "-sh", "/var/lib/immich/upload"])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
if let Some(size) = stdout.split_whitespace().next() {
|
||||||
|
return Some(format!("Storage: {}", size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_vaultwarden_info(&self) -> Option<String> {
|
||||||
|
// Check database for basic stats (SQLite)
|
||||||
|
if let Ok(metadata) = tokio::fs::metadata("/var/lib/bitwarden_rs/db.sqlite3").await {
|
||||||
|
let size_mb = metadata.len() as f32 / (1024.0 * 1024.0);
|
||||||
|
return Some(format!("DB: {:.1} MB", size_mb));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_unifi_info(&self) -> Option<String> {
|
||||||
|
// Check for device count via UniFi API (if accessible)
|
||||||
|
let output = Command::new("curl")
|
||||||
|
.args(["-s", "-f", "--insecure", "https://localhost:8443/api/self/sites"])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
return Some("Controller active".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: check data directory
|
||||||
|
if tokio::fs::metadata("/var/lib/unifi/data").await.is_ok() {
|
||||||
|
return Some("Data directory exists".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_mosquitto_info(&self) -> Option<String> {
|
||||||
|
// Check for active connections using netstat on MQTT ports
|
||||||
|
let output = Command::new("/run/current-system/sw/bin/ss")
|
||||||
|
.args(["-tn", "state", "established", "sport", "= :1883", "or", "sport", "= :8883"])
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
if output.status.success() {
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let connection_count = stdout.lines().count().saturating_sub(1);
|
||||||
|
if connection_count > 0 {
|
||||||
|
return Some(format!("{} MQTT connections", connection_count));
|
||||||
|
} else {
|
||||||
|
return Some("No active connections".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@ -202,6 +202,20 @@ impl AutoDiscovery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_monitorable_service(service_name: &str) -> bool {
|
fn is_monitorable_service(service_name: &str) -> bool {
|
||||||
|
// Skip setup/certificate services that don't need monitoring
|
||||||
|
let excluded_services = [
|
||||||
|
"mosquitto-certs",
|
||||||
|
"immich-setup",
|
||||||
|
"phpfpm-kryddorten",
|
||||||
|
"phpfpm-mariehall2",
|
||||||
|
];
|
||||||
|
|
||||||
|
for excluded in &excluded_services {
|
||||||
|
if service_name.contains(excluded) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Define patterns for services we want to monitor
|
// Define patterns for services we want to monitor
|
||||||
let interesting_services = [
|
let interesting_services = [
|
||||||
// Web applications
|
// Web applications
|
||||||
@ -235,8 +249,6 @@ impl AutoDiscovery {
|
|||||||
"haasp",
|
"haasp",
|
||||||
// Backup services
|
// Backup services
|
||||||
"backup",
|
"backup",
|
||||||
// Status web services
|
|
||||||
"mqtt-status",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Check if service name contains any of our interesting patterns
|
// Check if service name contains any of our interesting patterns
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user