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,
container_id: &str,
) -> 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 {
Ok(net_info)
@@ -196,7 +196,7 @@ pub async fn get_cpu_stats(
docker: &Docker,
container_id: &str,
) -> 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 {
Ok(cpu_info)

View File

@@ -14,6 +14,7 @@ pub mod stats;
use crate::models::{
DockerCollectMetricDto, DockerContainer, DockerContainerCpuDto, DockerContainerInfo,
DockerContainerNetworkDto, DockerContainerRamDto, DockerMetricDto, DockerRegistrationDto,
DockerContainerStatusDto
};
use bollard::Docker;
use std::error::Error;
@@ -129,28 +130,25 @@ impl DockerManager {
/// Collects Docker metrics for all containers
pub async fn collect_metrics(&self) -> Result<DockerMetricDto, Box<dyn Error + Send + Sync>> {
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 (cpu_stats, net_stats, mem_stats) = match stats_result {
let (cpu_stats, net_stats, mem_stats, status_stats) = match stats_result {
Ok(stats) => stats,
Err(e) => {
eprintln!("Warning: Failed to get container stats: {}", e);
// Return empty stats instead of failing completely
(Vec::new(), Vec::new(), Vec::new())
(Vec::new(), Vec::new(), Vec::new(), Vec::new())
}
};
println!(
"Debug: Found {} containers, {} CPU stats, {} network stats, {} memory stats",
"Debug: Found {} containers, {} CPU stats, {} network stats, {} memory stats, {} status stats",
containers.len(),
cpu_stats.len(),
net_stats.len(),
mem_stats.len()
mem_stats.len(),
status_stats.len(),
);
let container_infos_total: Vec<_> = containers
@@ -193,26 +191,49 @@ impl DockerManager {
})
.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
if cpu.is_none() || network.is_none() || ram.is_none() {
println!(
"Debug: Container {} - CPU: {:?}, Network: {:?}, RAM: {:?}",
"Debug: Container {} - CPU: {:?}, Network: {:?}, RAM: {:?}, Status {:?}",
container_short_id,
cpu.is_some(),
network.is_some(),
ram.is_some()
ram.is_some(),
status.is_some()
);
}
DockerContainerInfo {
container: Some(container),
status: None,
cpu,
network,
ram,
}
})
.collect();
// 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 {
container: Some(container),
status,
cpu,
network,
ram,
}
})
.collect();
let container_infos: Vec<DockerCollectMetricDto> = container_infos_total
.into_iter()
@@ -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 {
id: container.id,
status: status_dto,
cpu: cpu_dto,
ram: ram_dto,
network: network_dto,
@@ -267,7 +297,7 @@ impl DockerManager {
let dto = DockerMetricDto {
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)
@@ -287,42 +317,6 @@ impl DockerManager {
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

View File

@@ -1,9 +1,19 @@
pub mod cpu;
pub mod network;
pub mod ram;
pub mod status;
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)]
pub struct ContainerCpuInfo {
pub container_id: Option<String>,
@@ -43,33 +53,34 @@ pub async fn get_container_stats(
Vec<ContainerCpuInfo>,
Vec<ContainerNetworkInfo>,
Vec<ContainerMemoryInfo>,
Vec<ContainerStatusInfo>,
),
Box<dyn Error + Send + Sync>,
> {
let cpu_infos = cpu::get_all_containers_cpu_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 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
pub async fn get_single_container_stats(
docker: &Docker,
container_id: &str,
) -> Result<
(
Option<ContainerCpuInfo>,
Option<ContainerNetworkInfo>,
Option<ContainerMemoryInfo>,
),
Box<dyn Error + Send + Sync>,
> {
) -> Result<(
Option<ContainerCpuInfo>,
Option<ContainerNetworkInfo>,
Option<ContainerMemoryInfo>,
Option<ContainerStatusInfo>,
), Box<dyn Error + Send + Sync>> {
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 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

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
/// cpu: cpu stats
/// ram: ram stats
pub containers: String, // Vec<DockerContainerInfo>,
pub containers: Value, // Vec<DockerContainerInfo>,
}
#[derive(Debug, Serialize, Clone)]
pub struct DockerCollectMetricDto {
pub id: String,
pub status: DockerContainerStatusDto,
pub cpu: DockerContainerCpuDto,
pub ram: DockerContainerRamDto,
pub network: DockerContainerNetworkDto,
}
#[derive(Debug, Serialize, Clone)]
pub struct DockerContainerStatusDto {
pub status: Option<String>,
}
#[derive(Debug, Serialize, Clone)]
pub struct DockerContainerCpuDto {
pub cpu_load: Option<f64>,
@@ -251,7 +257,7 @@ pub struct DockerContainerNetworkDto {
#[derive(Debug, Serialize, Clone)]
pub struct DockerContainerInfo {
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 cpu: Option<stats::ContainerCpuInfo>,
pub ram: Option<stats::ContainerMemoryInfo>,