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

View File

@@ -11,7 +11,7 @@ pub mod container;
pub mod serverclientcomm;
pub mod stats;
use crate::models::{DockerRegistrationDto, DockerMetricDto, DockerContainer};
use crate::models::{DockerRegistrationDto, DockerMetricDto, DockerContainer, DockerContainerInfo};
use bollard::{query_parameters::InspectContainerOptions, Docker};
use std::error::Error;
@@ -88,7 +88,7 @@ impl DockerManager {
}
/// Gets all available containers as DTOs for registration
pub async fn get_containers_for_registration(
pub async fn get_containers(
&self,
) -> Result<Vec<DockerContainer>, Box<dyn Error + Send + Sync>> {
let containers = container::get_available_containers(&self.docker).await;
@@ -103,8 +103,6 @@ impl DockerManager {
.collect())
}
/// Gets the number of running containers
pub async fn get_container_count(&self) -> Result<usize, Box<dyn Error + Send + Sync>> {
let containers = container::get_available_containers(&self.docker).await;
@@ -118,6 +116,47 @@ impl DockerManager {
) -> Result<(), Box<dyn Error + Send + Sync>> {
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

View File

@@ -34,6 +34,7 @@ pub mod models;
use bollard::Docker;
use std::env;
use std::error::Error;
use std::sync::Arc;
use tokio::task::JoinHandle;
/// 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 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({
let docker = docker_manager.docker.clone();
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 ip = ip.clone();
let server_url = server_url.to_string();
let docker_manager = docker_manager.as_ref().cloned().unwrap();
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
}
});

View File

@@ -10,13 +10,15 @@
/// ## Usage
/// 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::sync::Arc;
use std::time::Duration;
use crate::api;
use crate::{api, docker};
use crate::docker::DockerManager;
//use crate::docker::DockerInfo;
use crate::hardware::network::NetworkMonitor;
use crate::hardware::HardwareInfo;
use crate::models::MetricDto;
use crate::models::{DockerMetricDto, MetricDto};
/// 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.
/// - `ip_address`: IP address of the agent.
pub struct Collector {
docker_manager: DockerManager,
network_monitor: NetworkMonitor,
server_id: i32,
server_id: u16,
ip_address: String,
}
@@ -41,8 +44,9 @@ impl Collector {
///
/// # Returns
/// 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 {
docker_manager,
network_monitor: NetworkMonitor::new(),
server_id,
ip_address,
@@ -72,7 +76,16 @@ impl Collector {
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_docker_metrics(base_url, &docker_metrics).await?;
tokio::time::sleep(Duration::from_secs(20)).await;
}
}
@@ -113,58 +126,16 @@ impl Collector {
})
}
/// Gets container metrics for all containers
pub async fn docker_collect(
&self,
) -> Result<Vec<DockerContainer>, Box<dyn Error + Send + Sync>> {
let containers = container::get_available_containers(&self.docker).await;
let mut metrics = Vec::new();
for container in containers {
// Get network stats (you'll need to implement this in container.rs)
let network_stats = container::get_network_stats(&self.docker, &container.id).await?;
// 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)
/// NOTE: This is a compilation-safe stub. Implement the Docker collection using your
/// DockerManager API and container helpers when available.
pub async fn docker_collect(&self) -> Result<DockerMetricDto, Box<dyn Error + Send + Sync>> {
let metrics = self
.docker_manager
.collect_metrics()
.await?;
Ok(DockerMetricDto {
server_id: self.server_id,
containers: metrics.containers,
})
}
}

View File

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