added docker metrics
All checks were successful
Rust Cross-Platform Build / Detect Rust Project (push) Successful in 4s
Rust Cross-Platform Build / Run Tests (push) Successful in 1m3s
Rust Cross-Platform Build / Build (x86_64-unknown-linux-gnu) (push) Successful in 2m56s
Rust Cross-Platform Build / Build (x86_64-pc-windows-gnu) (push) Successful in 3m37s
Rust Cross-Platform Build / Set Tag Name (push) Successful in 5s
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 5s

This commit is contained in:
2025-10-05 22:43:48 +02:00
parent 66428863e6
commit a7cae5e93f
5 changed files with 89 additions and 77 deletions

View File

@@ -15,7 +15,7 @@ use std::time::Duration;
use crate::docker::serverclientcomm::handle_server_message; use crate::docker::serverclientcomm::handle_server_message;
use crate::hardware::HardwareInfo; use crate::hardware::HardwareInfo;
use crate::models::{ use crate::models::{
Acknowledgment, HeartbeatDto, IdResponse, MetricDto, RegistrationDto, ServerMessage, Acknowledgment, HeartbeatDto, IdResponse, MetricDto, RegistrationDto, ServerMessage, DockerMetricDto
}; };
use anyhow::Result; use anyhow::Result;
@@ -39,7 +39,7 @@ use bollard::Docker;
/// Returns an error if unable to register after repeated attempts. /// Returns an error if unable to register after repeated attempts.
pub async fn register_with_server( pub async fn register_with_server(
base_url: &str, base_url: &str,
) -> Result<(i32, String), Box<dyn Error + Send + Sync>> { ) -> Result<(u16, String), Box<dyn Error + Send + Sync>> {
// First get local IP // First get local IP
let ip = local_ip_address::local_ip()?.to_string(); let ip = local_ip_address::local_ip()?.to_string();
println!("Local IP address detected: {}", ip); println!("Local IP address detected: {}", ip);
@@ -103,7 +103,7 @@ pub async fn register_with_server(
async fn get_server_id_by_ip( async fn get_server_id_by_ip(
base_url: &str, base_url: &str,
ip: &str, ip: &str,
) -> Result<(i32, String), Box<dyn Error + Send + Sync>> { ) -> Result<(u16, String), Box<dyn Error + Send + Sync>> {
let client = Client::builder() let client = Client::builder()
.danger_accept_invalid_certs(true) .danger_accept_invalid_certs(true)
.build()?; .build()?;
@@ -342,19 +342,19 @@ pub async fn send_acknowledgment(
pub async fn send_docker_metrics( pub async fn send_docker_metrics(
base_url: &str, base_url: &str,
docker_metrics: &MetricDto, docker_metrics: &DockerMetricDto,
) -> Result<(), Box<dyn Error + Send + Sync>> { ) -> Result<(), Box<dyn Error + Send + Sync>> {
let client = Client::new(); let client = Client::new();
let url = format!("{}/monitoring/docker-metric", base_url); let url = format!("{}/monitoring/docker-metric", base_url);
println!("Metrics: {:?}", docker_metrics); println!("Docker Metrics: {:?}", docker_metrics);
match client.post(&url).json(&docker_metrics).send().await { match client.post(&url).json(&docker_metrics).send().await {
Ok(res) => println!( Ok(res) => println!(
"✅ Sent metrics for server {} | Status: {}", "✅ Sent docker metrics for server {} | Status: {}",
docker_metrics.server_id, docker_metrics.server_id,
res.status() res.status()
), ),
Err(err) => eprintln!("❌ Failed to send metrics: {}", err), Err(err) => eprintln!("❌ Failed to send docker metrics: {}", err),
} }
Ok(()) Ok(())

View File

@@ -11,7 +11,7 @@ pub mod container;
pub mod serverclientcomm; pub mod serverclientcomm;
pub mod stats; pub mod stats;
use crate::models::{DockerRegistrationDto, DockerMetricDto, DockerContainer}; use crate::models::{DockerRegistrationDto, DockerMetricDto, DockerContainer, DockerContainerInfo};
use bollard::{query_parameters::InspectContainerOptions, Docker}; use bollard::{query_parameters::InspectContainerOptions, Docker};
use std::error::Error; use std::error::Error;
@@ -88,7 +88,7 @@ impl DockerManager {
} }
/// Gets all available containers as DTOs for registration /// Gets all available containers as DTOs for registration
pub async fn get_containers_for_registration( pub async fn get_containers(
&self, &self,
) -> Result<Vec<DockerContainer>, Box<dyn Error + Send + Sync>> { ) -> Result<Vec<DockerContainer>, Box<dyn Error + Send + Sync>> {
let containers = container::get_available_containers(&self.docker).await; let containers = container::get_available_containers(&self.docker).await;
@@ -103,8 +103,6 @@ impl DockerManager {
.collect()) .collect())
} }
/// Gets the number of running containers /// Gets the number of running containers
pub async fn get_container_count(&self) -> Result<usize, Box<dyn Error + Send + Sync>> { pub async fn get_container_count(&self) -> Result<usize, Box<dyn Error + Send + Sync>> {
let containers = container::get_available_containers(&self.docker).await; let containers = container::get_available_containers(&self.docker).await;
@@ -118,6 +116,47 @@ impl DockerManager {
) -> Result<(), Box<dyn Error + Send + Sync>> { ) -> Result<(), Box<dyn Error + Send + Sync>> {
container::restart_container(&self.docker, container_id).await container::restart_container(&self.docker, container_id).await
} }
/// 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?;
let (cpu_stats, net_stats, mem_stats) = stats::get_container_stats(&self.docker).await?;
let container_infos: Vec<_> = containers
.into_iter()
.map(|container| {
let cpu = cpu_stats
.iter()
.find(|c| c.container_id == container.id)
.cloned();
let network = net_stats
.iter()
.find(|n| n.container_id == container.id)
.cloned();
let ram = mem_stats
.iter()
.find(|m| m.container_id == container.id)
.cloned();
DockerContainerInfo {
container: Some(container),
status: None, // Status can be fetched if needed
cpu,
network,
ram,
}
})
.collect();
let dto = DockerMetricDto {
server_id: 0,
containers: serde_json::to_string(&container_infos)?,
};
Ok(dto)
}
} }
// 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

@@ -34,6 +34,7 @@ pub mod models;
use bollard::Docker; use bollard::Docker;
use std::env; use std::env;
use std::error::Error; use std::error::Error;
use std::sync::Arc;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
/// Awaits a spawned asynchronous task and flattens its nested `Result` type. /// Awaits a spawned asynchronous task and flattens its nested `Result` type.
@@ -113,7 +114,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
// Start background tasks // Start background tasks
// Start server listening for commands (only if Docker is available) // Start server listening for commands (only if Docker is available)
let listening_handle = if let Some(docker_manager) = docker_manager { let listening_handle = if let Some(ref docker_manager) = docker_manager {
tokio::spawn({ tokio::spawn({
let docker = docker_manager.docker.clone(); let docker = docker_manager.docker.clone();
let server_url = server_url.to_string(); let server_url = server_url.to_string();
@@ -136,8 +137,9 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
let metrics_handle = tokio::spawn({ let metrics_handle = tokio::spawn({
let ip = ip.clone(); let ip = ip.clone();
let server_url = server_url.to_string(); let server_url = server_url.to_string();
let docker_manager = docker_manager.as_ref().cloned().unwrap();
async move { async move {
let mut collector = metrics::Collector::new(server_id, ip); let mut collector = metrics::Collector::new(server_id, ip, docker_manager);
collector.run(&server_url).await collector.run(&server_url).await
} }
}); });

View File

@@ -10,13 +10,15 @@
/// ## Usage /// ## Usage
/// The [`Collector`] struct is instantiated in the main loop and runs as a background task, continuously collecting and reporting metrics. /// The [`Collector`] struct is instantiated in the main loop and runs as a background task, continuously collecting and reporting metrics.
use std::error::Error; use std::error::Error;
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use crate::api; use crate::{api, docker};
use crate::docker::DockerManager;
//use crate::docker::DockerInfo; //use crate::docker::DockerInfo;
use crate::hardware::network::NetworkMonitor; use crate::hardware::network::NetworkMonitor;
use crate::hardware::HardwareInfo; use crate::hardware::HardwareInfo;
use crate::models::MetricDto; use crate::models::{DockerMetricDto, MetricDto};
/// Main orchestrator for hardware and network metric collection and reporting. /// Main orchestrator for hardware and network metric collection and reporting.
/// ///
@@ -27,8 +29,9 @@ use crate::models::MetricDto;
/// - `server_id`: Unique server ID assigned by the backend. /// - `server_id`: Unique server ID assigned by the backend.
/// - `ip_address`: IP address of the agent. /// - `ip_address`: IP address of the agent.
pub struct Collector { pub struct Collector {
docker_manager: DockerManager,
network_monitor: NetworkMonitor, network_monitor: NetworkMonitor,
server_id: i32, server_id: u16,
ip_address: String, ip_address: String,
} }
@@ -41,8 +44,9 @@ impl Collector {
/// ///
/// # Returns /// # Returns
/// A new `Collector` ready to collect and report metrics. /// A new `Collector` ready to collect and report metrics.
pub fn new(server_id: i32, ip_address: String) -> Self { pub fn new(server_id: u16, ip_address: String, docker_manager: DockerManager) -> Self {
Self { Self {
docker_manager,
network_monitor: NetworkMonitor::new(), network_monitor: NetworkMonitor::new(),
server_id, server_id,
ip_address, ip_address,
@@ -72,7 +76,16 @@ impl Collector {
continue; continue;
} }
}; };
let docker_metrics = match self.docker_collect().await {
Ok(metrics) => metrics,
Err(e) => {
eprintln!("Error collecting docker metrics: {}", e);
tokio::time::sleep(Duration::from_secs(10)).await;
continue;
}
};
api::send_metrics(base_url, &metrics).await?; api::send_metrics(base_url, &metrics).await?;
api::send_docker_metrics(base_url, &docker_metrics).await?;
tokio::time::sleep(Duration::from_secs(20)).await; tokio::time::sleep(Duration::from_secs(20)).await;
} }
} }
@@ -113,58 +126,16 @@ impl Collector {
}) })
} }
/// Gets container metrics for all containers /// NOTE: This is a compilation-safe stub. Implement the Docker collection using your
pub async fn docker_collect( /// DockerManager API and container helpers when available.
&self, pub async fn docker_collect(&self) -> Result<DockerMetricDto, Box<dyn Error + Send + Sync>> {
) -> Result<Vec<DockerContainer>, Box<dyn Error + Send + Sync>> { let metrics = self
let containers = container::get_available_containers(&self.docker).await; .docker_manager
let mut metrics = Vec::new(); .collect_metrics()
.await?;
for container in containers { Ok(DockerMetricDto {
// Get network stats (you'll need to implement this in container.rs) server_id: self.server_id,
let network_stats = container::get_network_stats(&self.docker, &container.id).await?; containers: metrics.containers,
// Get CPU stats (you'll need to implement this in container.rs) })
let cpu_stats = container::get_cpu_stats(&self.docker, &container.id).await?;
// Get current status by inspecting the container
let status = match self
.docker
.inspect_container(&container.id, None::<InspectContainerOptions>)
.await
{
Ok(container_info) => {
// Extract status from container state and convert to string
container_info
.state
.and_then(|state| state.status)
.map(|status_enum| {
match status_enum {
bollard::models::ContainerStateStatusEnum::CREATED => "created",
bollard::models::ContainerStateStatusEnum::RUNNING => "running",
bollard::models::ContainerStateStatusEnum::PAUSED => "paused",
bollard::models::ContainerStateStatusEnum::RESTARTING => {
"restarting"
}
bollard::models::ContainerStateStatusEnum::REMOVING => "removing",
bollard::models::ContainerStateStatusEnum::EXITED => "exited",
bollard::models::ContainerStateStatusEnum::DEAD => "dead",
bollard::secret::ContainerStateStatusEnum::EMPTY => todo!(),
}
.to_string()
})
.unwrap_or_else(|| "unknown".to_string())
}
Err(_) => "unknown".to_string(),
};
metrics.push(DockerContainerMetricDto {
server_id: container.id,
status: status,
network: network_stats,
cpu: cpu_stats,
});
}
Ok(metrics)
} }
} }

View File

@@ -25,7 +25,7 @@ use serde::{Deserialize, Serialize};
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
pub struct RegistrationDto { pub struct RegistrationDto {
#[serde(rename = "id")] #[serde(rename = "id")]
pub server_id: i32, pub server_id: u16,
#[serde(rename = "ipAddress")] #[serde(rename = "ipAddress")]
pub ip_address: String, pub ip_address: String,
#[serde(rename = "cpuType")] #[serde(rename = "cpuType")]
@@ -59,7 +59,7 @@ pub struct RegistrationDto {
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
pub struct MetricDto { pub struct MetricDto {
#[serde(rename = "serverId")] #[serde(rename = "serverId")]
pub server_id: i32, pub server_id: u16,
#[serde(rename = "ipAddress")] #[serde(rename = "ipAddress")]
pub ip_address: String, pub ip_address: String,
#[serde(rename = "cpu_Load")] #[serde(rename = "cpu_Load")]
@@ -116,7 +116,7 @@ pub struct DiskInfoDetailed {
/// - `ip_address`: IPv4 or IPv6 address (string) /// - `ip_address`: IPv4 or IPv6 address (string)
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct IdResponse { pub struct IdResponse {
pub id: i32, pub id: u16,
#[serde(rename = "ipAddress")] #[serde(rename = "ipAddress")]
pub ip_address: String, pub ip_address: String,
} }
@@ -188,7 +188,7 @@ pub struct Acknowledgment {
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone)]
pub struct DockerRegistrationDto { pub struct DockerRegistrationDto {
/// Unique server identifier (integer) /// Unique server identifier (integer)
pub server_id: u32, pub server_id: u16,
/// json stringified array of DockerContainer /// json stringified array of DockerContainer
/// ///
/// ## Json Example /// ## Json Example
@@ -198,12 +198,12 @@ pub struct DockerRegistrationDto {
/// id: unique container ID (first 12 hex digits) /// id: unique container ID (first 12 hex digits)
/// image: docker image name /// image: docker image name
/// name: container name /// name: container name
pub containers: String, pub containers: String // Vec<DockerContainer>,
} }
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone)]
pub struct DockerMetricDto { pub struct DockerMetricDto {
pub server_id: String, pub server_id: u16,
/// json stringified array of DockerContainer /// json stringified array of DockerContainer
/// ///
/// ## Json Example /// ## Json Example
@@ -217,7 +217,7 @@ pub struct DockerMetricDto {
/// network: network stats /// network: network stats
/// cpu: cpu stats /// cpu: cpu stats
/// ram: ram stats /// ram: ram stats
pub containers: String, pub containers: String // Vec<DockerContainerInfo>,
} }
#[derive(Debug, Serialize, Clone)] #[derive(Debug, Serialize, Clone)]