added status in docker metric collect
All checks were successful
Rust Cross-Platform Build / Detect Rust Project (push) Successful in 4s
Rust Cross-Platform Build / Run Tests (push) Successful in 1m9s
Rust Cross-Platform Build / Build (x86_64-unknown-linux-gnu) (push) Successful in 3m10s
Rust Cross-Platform Build / Build (x86_64-pc-windows-gnu) (push) Successful in 4m0s
Rust Cross-Platform Build / Set Tag Name (push) Successful in 4s
Rust Cross-Platform Build / Build and Push Docker Image (push) Successful in 2m12s
Rust Cross-Platform Build / Workflow Summary (push) Successful in 2s
Rust Cross-Platform Build / Create Tag (push) Successful in 4s

This commit is contained in:
2025-10-29 22:23:58 +01:00
parent c36b17fa05
commit 2a4cc4b2d5
5 changed files with 208 additions and 71 deletions

View File

@@ -172,7 +172,7 @@ pub async fn get_network_stats(
docker: &Docker, docker: &Docker,
container_id: &str, container_id: &str,
) -> Result<ContainerNetworkInfo, Box<dyn Error + Send + Sync>> { ) -> Result<ContainerNetworkInfo, Box<dyn Error + Send + Sync>> {
let (_, net_info, _) = stats::get_single_container_stats(docker, container_id).await?; let (_, net_info, _, _) = stats::get_single_container_stats(docker, container_id).await?;
if let Some(net_info) = net_info { if let Some(net_info) = net_info {
Ok(net_info) Ok(net_info)
@@ -196,7 +196,7 @@ pub async fn get_cpu_stats(
docker: &Docker, docker: &Docker,
container_id: &str, container_id: &str,
) -> Result<ContainerCpuInfo, Box<dyn Error + Send + Sync>> { ) -> Result<ContainerCpuInfo, Box<dyn Error + Send + Sync>> {
let (cpu_info, _, _) = stats::get_single_container_stats(docker, container_id).await?; let (cpu_info, _, _, _) = stats::get_single_container_stats(docker, container_id).await?;
if let Some(cpu_info) = cpu_info { if let Some(cpu_info) = cpu_info {
Ok(cpu_info) Ok(cpu_info)

View File

@@ -14,6 +14,7 @@ pub mod stats;
use crate::models::{ use crate::models::{
DockerCollectMetricDto, DockerContainer, DockerContainerCpuDto, DockerContainerInfo, DockerCollectMetricDto, DockerContainer, DockerContainerCpuDto, DockerContainerInfo,
DockerContainerNetworkDto, DockerContainerRamDto, DockerMetricDto, DockerRegistrationDto, DockerContainerNetworkDto, DockerContainerRamDto, DockerMetricDto, DockerRegistrationDto,
DockerContainerStatusDto
}; };
use bollard::Docker; use bollard::Docker;
use std::error::Error; use std::error::Error;
@@ -129,28 +130,25 @@ impl DockerManager {
/// Collects Docker metrics for all containers /// Collects Docker metrics for all containers
pub async fn collect_metrics(&self) -> Result<DockerMetricDto, Box<dyn Error + Send + Sync>> { pub async fn collect_metrics(&self) -> Result<DockerMetricDto, Box<dyn Error + Send + Sync>> {
let containers = self.get_containers().await?; let containers = self.get_containers().await?;
if let Some(first_container) = containers.first() {
println!("Debug: Testing stats for container {}", first_container.id);
let _ = self.debug_container_stats(&first_container.id).await;
}
// Get stats with proper error handling // Get stats with status information
let stats_result = stats::get_container_stats(&self.docker).await; let stats_result = stats::get_container_stats(&self.docker).await;
let (cpu_stats, net_stats, mem_stats) = match stats_result { let (cpu_stats, net_stats, mem_stats, status_stats) = match stats_result {
Ok(stats) => stats, Ok(stats) => stats,
Err(e) => { Err(e) => {
eprintln!("Warning: Failed to get container stats: {}", e); eprintln!("Warning: Failed to get container stats: {}", e);
// Return empty stats instead of failing completely // Return empty stats instead of failing completely
(Vec::new(), Vec::new(), Vec::new()) (Vec::new(), Vec::new(), Vec::new(), Vec::new())
} }
}; };
println!( println!(
"Debug: Found {} containers, {} CPU stats, {} network stats, {} memory stats", "Debug: Found {} containers, {} CPU stats, {} network stats, {} memory stats, {} status stats",
containers.len(), containers.len(),
cpu_stats.len(), cpu_stats.len(),
net_stats.len(), net_stats.len(),
mem_stats.len() mem_stats.len(),
status_stats.len(),
); );
let container_infos_total: Vec<_> = containers let container_infos_total: Vec<_> = containers
@@ -193,20 +191,43 @@ impl DockerManager {
}) })
.cloned(); .cloned();
let status = status_stats
.iter()
.find(|s| {
s.container_id
.as_ref()
.map(|id| id.starts_with(container_short_id))
.unwrap_or(false)
})
.cloned(); // Clone the entire ContainerStatusInfo
// Debug output for this container // Debug output for this container
if cpu.is_none() || network.is_none() || ram.is_none() { if cpu.is_none() || network.is_none() || ram.is_none() {
println!( println!(
"Debug: Container {} - CPU: {:?}, Network: {:?}, RAM: {:?}", "Debug: Container {} - CPU: {:?}, Network: {:?}, RAM: {:?}, Status {:?}",
container_short_id, container_short_id,
cpu.is_some(), cpu.is_some(),
network.is_some(), network.is_some(),
ram.is_some() ram.is_some(),
status.is_some()
);
}
// Debug output for this container
if cpu.is_none() || network.is_none() || ram.is_none() || status.is_none() {
println!(
"Debug: Container {} - CPU: {:?}, Network: {:?}, RAM: {:?}, Status: {:?}",
container_short_id,
cpu.is_some(),
network.is_some(),
ram.is_some(),
status.is_some()
); );
} }
DockerContainerInfo { DockerContainerInfo {
container: Some(container), container: Some(container),
status: None, status,
cpu, cpu,
network, network,
ram, ram,
@@ -256,8 +277,17 @@ impl DockerManager {
} }
}; };
let status_dto = if let Some(status_info) = info.status {
DockerContainerStatusDto {
status: status_info.status, // Extract the status string
}
} else {
DockerContainerStatusDto { status: None }
};
Some(DockerCollectMetricDto { Some(DockerCollectMetricDto {
id: container.id, id: container.id,
status: status_dto,
cpu: cpu_dto, cpu: cpu_dto,
ram: ram_dto, ram: ram_dto,
network: network_dto, network: network_dto,
@@ -267,7 +297,7 @@ impl DockerManager {
let dto = DockerMetricDto { let dto = DockerMetricDto {
server_id: 0, // This should be set by the caller server_id: 0, // This should be set by the caller
containers: serde_json::to_string(&container_infos)?, containers: serde_json::to_value(&container_infos)?,
}; };
Ok(dto) Ok(dto)
@@ -287,42 +317,6 @@ impl DockerManager {
Ok(dto) Ok(dto)
} }
/// Debug function to check stats collection for a specific container
pub async fn debug_container_stats(
&self,
container_id: &str,
) -> Result<(), Box<dyn Error + Send + Sync>> {
println!("=== DEBUG STATS FOR CONTAINER {} ===", container_id);
let (cpu_info, net_info, mem_info) =
stats::get_single_container_stats(&self.docker, container_id).await?;
println!("CPU Info: {:?}", cpu_info);
println!("Network Info: {:?}", net_info);
println!("Memory Info: {:?}", mem_info);
// Also try the individual stats functions
println!("--- Individual CPU Stats ---");
match stats::cpu::get_single_container_cpu_stats(&self.docker, container_id).await {
Ok(cpu) => println!("CPU: {:?}", cpu),
Err(e) => println!("CPU Error: {}", e),
}
println!("--- Individual Network Stats ---");
match stats::network::get_single_container_network_stats(&self.docker, container_id).await {
Ok(net) => println!("Network: {:?}", net),
Err(e) => println!("Network Error: {}", e),
}
println!("--- Individual Memory Stats ---");
match stats::ram::get_single_container_memory_stats(&self.docker, container_id).await {
Ok(mem) => println!("Memory: {:?}", mem),
Err(e) => println!("Memory Error: {}", e),
}
Ok(())
}
} }
// Keep these as utility functions if needed, but they should use DockerManager internally // Keep these as utility functions if needed, but they should use DockerManager internally

View File

@@ -1,9 +1,19 @@
pub mod cpu; pub mod cpu;
pub mod network; pub mod network;
pub mod ram; pub mod ram;
pub mod status;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ContainerStatusInfo {
pub container_id: Option<String>,
pub status: Option<String>, // "running", "stopped", "paused", "exited", etc.
pub state: Option<String>, // More detailed state information
pub started_at: Option<String>,
pub finished_at: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ContainerCpuInfo { pub struct ContainerCpuInfo {
pub container_id: Option<String>, pub container_id: Option<String>,
@@ -43,33 +53,34 @@ pub async fn get_container_stats(
Vec<ContainerCpuInfo>, Vec<ContainerCpuInfo>,
Vec<ContainerNetworkInfo>, Vec<ContainerNetworkInfo>,
Vec<ContainerMemoryInfo>, Vec<ContainerMemoryInfo>,
Vec<ContainerStatusInfo>,
), ),
Box<dyn Error + Send + Sync>, Box<dyn Error + Send + Sync>,
> { > {
let cpu_infos = cpu::get_all_containers_cpu_stats(docker).await?; let cpu_infos = cpu::get_all_containers_cpu_stats(docker).await?;
let net_infos = network::get_all_containers_network_stats(docker).await?; let net_infos = network::get_all_containers_network_stats(docker).await?;
let mem_infos = ram::get_all_containers_memory_stats(docker).await?; let mem_infos = ram::get_all_containers_memory_stats(docker).await?;
let status_infos = status::get_all_containers_status(docker).await?;
Ok((cpu_infos, net_infos, mem_infos)) Ok((cpu_infos, net_infos, mem_infos, status_infos))
} }
/// Get container statistics for a specific container /// Get container statistics for a specific container
pub async fn get_single_container_stats( pub async fn get_single_container_stats(
docker: &Docker, docker: &Docker,
container_id: &str, container_id: &str,
) -> Result< ) -> Result<(
(
Option<ContainerCpuInfo>, Option<ContainerCpuInfo>,
Option<ContainerNetworkInfo>, Option<ContainerNetworkInfo>,
Option<ContainerMemoryInfo>, Option<ContainerMemoryInfo>,
), Option<ContainerStatusInfo>,
Box<dyn Error + Send + Sync>, ), Box<dyn Error + Send + Sync>> {
> {
let cpu_info = cpu::get_single_container_cpu_stats(docker, container_id).await?; let cpu_info = cpu::get_single_container_cpu_stats(docker, container_id).await?;
let net_info = network::get_single_container_network_stats(docker, container_id).await?; let net_info = network::get_single_container_network_stats(docker, container_id).await?;
let mem_info = ram::get_single_container_memory_stats(docker, container_id).await?; let mem_info = ram::get_single_container_memory_stats(docker, container_id).await?;
let status_info = status::get_single_container_status(docker, container_id).await?;
Ok((cpu_info, net_info, mem_info)) Ok((cpu_info, net_info, mem_info, status_info))
} }
/// Get total network statistics across all containers /// Get total network statistics across all containers

View File

@@ -0,0 +1,126 @@
use super::ContainerStatusInfo;
use std::error::Error;
use bollard::Docker;
use bollard::query_parameters::{ListContainersOptions, InspectContainerOptions};
use bollard::models::{ContainerSummaryStateEnum, ContainerStateStatusEnum};
/// Get status information for all containers
pub async fn get_all_containers_status(
docker: &Docker,
) -> Result<Vec<ContainerStatusInfo>, Box<dyn Error + Send + Sync>> {
let containers = docker
.list_containers(Some(ListContainersOptions {
all: true, // Include stopped containers
..Default::default()
}))
.await?;
let mut status_infos = Vec::new();
for container in containers {
let id = container.id.unwrap_or_default();
if id.is_empty() {
continue;
}
// Convert ContainerSummaryStateEnum to String
let status = container.state.map(|state| match state {
ContainerSummaryStateEnum::CREATED => "created".to_string(),
ContainerSummaryStateEnum::RUNNING => "running".to_string(),
ContainerSummaryStateEnum::PAUSED => "paused".to_string(),
ContainerSummaryStateEnum::RESTARTING => "restarting".to_string(),
ContainerSummaryStateEnum::REMOVING => "removing".to_string(),
ContainerSummaryStateEnum::EXITED => "exited".to_string(),
ContainerSummaryStateEnum::DEAD => "dead".to_string(),
_ => "unknown".to_string(),
});
// Convert timestamp from i64 to String
let started_at = container.created.map(|timestamp| timestamp.to_string());
status_infos.push(ContainerStatusInfo {
container_id: Some(id.clone()),
status,
state: container.status,
started_at,
finished_at: None, // Docker API doesn't provide finished_at in list
});
}
Ok(status_infos)
}
/// Get status information for a specific container
pub async fn get_single_container_status(
docker: &Docker,
container_id: &str,
) -> Result<Option<ContainerStatusInfo>, Box<dyn Error + Send + Sync>> {
// First try to get from list (faster)
let containers = docker
.list_containers(Some(ListContainersOptions {
all: true,
..Default::default()
}))
.await?;
if let Some(container) = containers.into_iter().find(|c| {
c.id.as_ref().map(|id| id == container_id).unwrap_or(false)
}) {
// Convert ContainerSummaryStateEnum to String
let status = container.state.map(|state| match state {
ContainerSummaryStateEnum::CREATED => "created".to_string(),
ContainerSummaryStateEnum::RUNNING => "running".to_string(),
ContainerSummaryStateEnum::PAUSED => "paused".to_string(),
ContainerSummaryStateEnum::RESTARTING => "restarting".to_string(),
ContainerSummaryStateEnum::REMOVING => "removing".to_string(),
ContainerSummaryStateEnum::EXITED => "exited".to_string(),
ContainerSummaryStateEnum::DEAD => "dead".to_string(),
_ => "unknown".to_string(),
});
// Convert timestamp from i64 to String
let started_at = container.created.map(|timestamp| timestamp.to_string());
return Ok(Some(ContainerStatusInfo {
container_id: Some(container_id.to_string()),
status,
state: container.status,
started_at,
finished_at: None,
}));
}
// Fallback to inspect for more detailed info
match docker.inspect_container(container_id, None::<InspectContainerOptions>).await {
Ok(container_details) => {
let state = container_details.state.unwrap_or_default();
// Convert ContainerStateStatusEnum to String
let status = state.status.map(|status_enum| match status_enum {
ContainerStateStatusEnum::CREATED => "created".to_string(),
ContainerStateStatusEnum::RUNNING => "running".to_string(),
ContainerStateStatusEnum::PAUSED => "paused".to_string(),
ContainerStateStatusEnum::RESTARTING => "restarting".to_string(),
ContainerStateStatusEnum::REMOVING => "removing".to_string(),
ContainerStateStatusEnum::EXITED => "exited".to_string(),
ContainerStateStatusEnum::DEAD => "dead".to_string(),
_ => "unknown".to_string(),
});
// These are already Option<String> from the Docker API
let started_at = state.clone().started_at;
let finished_at = state.clone().finished_at;
Ok(Some(ContainerStatusInfo {
container_id: Some(container_id.to_string()),
status,
state: Some(format!("{:?}", state)), // Convert state to string
started_at,
finished_at,
}))
}
Err(_) => Ok(None), // Container not found
}
}

View File

@@ -219,18 +219,24 @@ pub struct DockerMetricDto {
/// network: network stats /// network: network stats
/// cpu: cpu stats /// cpu: cpu stats
/// ram: ram stats /// ram: ram stats
pub containers: String, // Vec<DockerContainerInfo>, pub containers: Value, // Vec<DockerContainerInfo>,
} }
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone)]
pub struct DockerCollectMetricDto { pub struct DockerCollectMetricDto {
pub id: String, pub id: String,
pub status: DockerContainerStatusDto,
pub cpu: DockerContainerCpuDto, pub cpu: DockerContainerCpuDto,
pub ram: DockerContainerRamDto, pub ram: DockerContainerRamDto,
pub network: DockerContainerNetworkDto, pub network: DockerContainerNetworkDto,
} }
#[derive(Debug, Serialize, Clone)]
pub struct DockerContainerStatusDto {
pub status: Option<String>,
}
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone)]
pub struct DockerContainerCpuDto { pub struct DockerContainerCpuDto {
pub cpu_load: Option<f64>, pub cpu_load: Option<f64>,
@@ -251,7 +257,7 @@ pub struct DockerContainerNetworkDto {
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone)]
pub struct DockerContainerInfo { pub struct DockerContainerInfo {
pub container: Option<DockerContainer>, pub container: Option<DockerContainer>,
pub status: Option<String>, // "running";"stopped";others pub status: Option<stats::ContainerStatusInfo>, // "running";"stopped";others
pub network: Option<stats::ContainerNetworkInfo>, pub network: Option<stats::ContainerNetworkInfo>,
pub cpu: Option<stats::ContainerCpuInfo>, pub cpu: Option<stats::ContainerCpuInfo>,
pub ram: Option<stats::ContainerMemoryInfo>, pub ram: Option<stats::ContainerMemoryInfo>,