Compare commits
5 Commits
66428863e6
...
v0.1.24
Author | SHA1 | Date | |
---|---|---|---|
1c7a169956 | |||
c7bce926e9 | |||
711083daa0 | |||
06cec6ff9f | |||
a7cae5e93f |
@@ -12,10 +12,12 @@
|
|||||||
/// These functions are called from the main agent loop and background tasks. All network operations are asynchronous and robust to transient failures.
|
/// These functions are called from the main agent loop and background tasks. All network operations are asynchronous and robust to transient failures.
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::docker::container;
|
||||||
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, DockerMetricDto, DockerRegistrationDto, HeartbeatDto, IdResponse, MetricDto,
|
||||||
|
RegistrationDto, ServerMessage,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
@@ -39,7 +41,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 +105,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()?;
|
||||||
@@ -151,6 +153,46 @@ async fn get_server_id_by_ip(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn broadcast_docker_containers(
|
||||||
|
base_url: &str,
|
||||||
|
server_id: u16,
|
||||||
|
container_dto: &mut DockerRegistrationDto,
|
||||||
|
) -> Result<(u16, String), Box<dyn Error + Send + Sync>> {
|
||||||
|
// First get local IP
|
||||||
|
println!("Preparing to broadcast docker containers...");
|
||||||
|
// Create HTTP client for registration
|
||||||
|
let client = Client::builder()
|
||||||
|
.danger_accept_invalid_certs(true)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
// Prepare registration data
|
||||||
|
let container_dto = container_dto;
|
||||||
|
container_dto.server_id = server_id;
|
||||||
|
|
||||||
|
// Try to register (will retry on failure)
|
||||||
|
loop {
|
||||||
|
println!("Attempting to broadcast containers...");
|
||||||
|
let url = format!("{}/monitoring/service-discovery", base_url);
|
||||||
|
match client.post(&url).json(&container_dto).send().await {
|
||||||
|
Ok(resp) if resp.status().is_success() => {
|
||||||
|
println!("✅ Successfully broadcasted docker container.");
|
||||||
|
}
|
||||||
|
Ok(resp) => {
|
||||||
|
let status = resp.status();
|
||||||
|
let text = resp.text().await.unwrap_or_default();
|
||||||
|
println!(
|
||||||
|
"⚠️ Broadcasting failed ({}): {} (will retry in 10 seconds)",
|
||||||
|
status, text
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("⚠️ Broadcasting failed: {} (will retry in 10 seconds)", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sleep(Duration::from_secs(10)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Periodically sends heartbeat signals to the backend server to indicate agent liveness.
|
/// Periodically sends heartbeat signals to the backend server to indicate agent liveness.
|
||||||
///
|
///
|
||||||
/// This function runs in a background task and will retry on network errors.
|
/// This function runs in a background task and will retry on network errors.
|
||||||
@@ -342,24 +384,20 @@ 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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn broadcast_docker_containers() {
|
|
||||||
// Placeholder for future implementation
|
|
||||||
}
|
|
@@ -4,7 +4,7 @@
|
|||||||
//!
|
//!
|
||||||
use crate::docker::stats;
|
use crate::docker::stats;
|
||||||
use crate::docker::stats::{ContainerCpuInfo, ContainerNetworkInfo};
|
use crate::docker::stats::{ContainerCpuInfo, ContainerNetworkInfo};
|
||||||
use crate::models::{DockerRegistrationDto, DockerMetricDto, DockerContainer};
|
use crate::models::DockerContainer;
|
||||||
|
|
||||||
use bollard::query_parameters::{
|
use bollard::query_parameters::{
|
||||||
CreateImageOptions, ListContainersOptions, RestartContainerOptions,
|
CreateImageOptions, ListContainersOptions, RestartContainerOptions,
|
||||||
@@ -178,14 +178,15 @@ pub async fn get_network_stats(
|
|||||||
Ok(net_info)
|
Ok(net_info)
|
||||||
} else {
|
} else {
|
||||||
// Return default network info if not found
|
// Return default network info if not found
|
||||||
|
println!("No network info found for container {}", container_id);
|
||||||
Ok(ContainerNetworkInfo {
|
Ok(ContainerNetworkInfo {
|
||||||
container_id: container_id.to_string(),
|
container_id: Some(container_id.to_string()),
|
||||||
rx_bytes: 0,
|
rx_bytes: None,
|
||||||
tx_bytes: 0,
|
tx_bytes: None,
|
||||||
rx_packets: 0,
|
rx_packets: None,
|
||||||
tx_packets: 0,
|
tx_packets: None,
|
||||||
rx_errors: 0,
|
rx_errors: None,
|
||||||
tx_errors: 0,
|
tx_errors: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,12 +202,13 @@ pub async fn get_cpu_stats(
|
|||||||
Ok(cpu_info)
|
Ok(cpu_info)
|
||||||
} else {
|
} else {
|
||||||
// Return default CPU info if not found
|
// Return default CPU info if not found
|
||||||
|
println!("No CPU info found for container {}", container_id);
|
||||||
Ok(ContainerCpuInfo {
|
Ok(ContainerCpuInfo {
|
||||||
container_id: container_id.to_string(),
|
container_id: Some(container_id.to_string()),
|
||||||
cpu_usage_percent: 0.0,
|
cpu_usage_percent: None,
|
||||||
system_cpu_usage: 0,
|
system_cpu_usage: None,
|
||||||
container_cpu_usage: 0,
|
container_cpu_usage: None,
|
||||||
online_cpus: 1,
|
online_cpus: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,8 +11,11 @@ pub mod container;
|
|||||||
pub mod serverclientcomm;
|
pub mod serverclientcomm;
|
||||||
pub mod stats;
|
pub mod stats;
|
||||||
|
|
||||||
use crate::models::{DockerRegistrationDto, DockerMetricDto, DockerContainer};
|
use crate::models::{
|
||||||
use bollard::{query_parameters::InspectContainerOptions, Docker};
|
DockerCollectMetricDto, DockerContainer, DockerContainerCpuDto, DockerContainerInfo,
|
||||||
|
DockerContainerNetworkDto, DockerContainerRamDto, DockerMetricDto, DockerRegistrationDto,
|
||||||
|
};
|
||||||
|
use bollard::Docker;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
/// Main Docker manager that holds the Docker client and provides all operations
|
/// Main Docker manager that holds the Docker client and provides all operations
|
||||||
@@ -60,14 +63,20 @@ impl DockerManager {
|
|||||||
id: container.id,
|
id: container.id,
|
||||||
image: container.image,
|
image: container.image,
|
||||||
name: container.name,
|
name: container.name,
|
||||||
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the current client version (image name) if running in Docker
|
/// Gets the current client version (image name) if running in Docker
|
||||||
pub async fn get_client_version(&self) -> String {
|
pub async fn get_client_version(&self) -> String {
|
||||||
match self.get_client_container().await {
|
match self.get_client_container().await {
|
||||||
Ok(Some(container)) => container.image.clone().unwrap().split(':').next().unwrap_or("unknown").to_string(),
|
Ok(Some(container)) => container
|
||||||
|
.image
|
||||||
|
.clone()
|
||||||
|
.unwrap()
|
||||||
|
.split(':')
|
||||||
|
.next()
|
||||||
|
.unwrap_or("unknown")
|
||||||
|
.to_string(),
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
println!("Warning: No WatcherAgent container found");
|
println!("Warning: No WatcherAgent container found");
|
||||||
"unknown".to_string()
|
"unknown".to_string()
|
||||||
@@ -88,7 +97,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 +112,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 +125,95 @@ 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_total: Vec<_> = containers
|
||||||
|
.into_iter()
|
||||||
|
.map(|container| {
|
||||||
|
let cpu = cpu_stats
|
||||||
|
.iter()
|
||||||
|
.find(|c| c.container_id == Some(container.id.clone()))
|
||||||
|
.cloned();
|
||||||
|
let network = net_stats
|
||||||
|
.iter()
|
||||||
|
.find(|n| n.container_id == Some(container.id.clone()))
|
||||||
|
.cloned();
|
||||||
|
let ram = mem_stats
|
||||||
|
.iter()
|
||||||
|
.find(|m| m.container_id == Some(container.id.clone()))
|
||||||
|
.cloned();
|
||||||
|
|
||||||
|
DockerContainerInfo {
|
||||||
|
container: Some(container),
|
||||||
|
status: None, // Status can be fetched if needed
|
||||||
|
cpu,
|
||||||
|
network,
|
||||||
|
ram,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let container_infos: Vec<DockerCollectMetricDto> = container_infos_total
|
||||||
|
.into_iter()
|
||||||
|
.map(|info| DockerCollectMetricDto {
|
||||||
|
id: Some(info.container.unwrap().id).unwrap_or("".to_string()),
|
||||||
|
cpu: info
|
||||||
|
.cpu
|
||||||
|
.unwrap()
|
||||||
|
.cpu_usage_percent
|
||||||
|
.map(|load| DockerContainerCpuDto {
|
||||||
|
cpu_load: Some(load),
|
||||||
|
})
|
||||||
|
.unwrap_or(DockerContainerCpuDto { cpu_load: None }),
|
||||||
|
ram: info
|
||||||
|
.ram
|
||||||
|
.unwrap()
|
||||||
|
.memory_usage_percent
|
||||||
|
.map(|load| DockerContainerRamDto {
|
||||||
|
cpu_load: Some(load),
|
||||||
|
})
|
||||||
|
.unwrap_or(DockerContainerRamDto { cpu_load: None }),
|
||||||
|
network: DockerContainerNetworkDto {
|
||||||
|
net_in: info
|
||||||
|
.network
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.rx_bytes
|
||||||
|
.map(|bytes| bytes as f64)
|
||||||
|
.or(Some(0.0)),
|
||||||
|
net_out: info
|
||||||
|
.network
|
||||||
|
.unwrap()
|
||||||
|
.tx_bytes
|
||||||
|
.map(|bytes| bytes as f64)
|
||||||
|
.or(Some(0.0)),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let dto = DockerMetricDto {
|
||||||
|
server_id: 0,
|
||||||
|
containers: serde_json::to_string(&container_infos)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(dto)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn create_registration_dto(
|
||||||
|
&self,
|
||||||
|
) -> Result<DockerRegistrationDto, Box<dyn Error + Send + Sync>> {
|
||||||
|
let containers = self.get_containers().await?;
|
||||||
|
let dto = DockerRegistrationDto {
|
||||||
|
server_id: 0,
|
||||||
|
//container_count,
|
||||||
|
containers: serde_json::to_string(&containers)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
use crate::models::ServerMessage;
|
use crate::models::ServerMessage;
|
||||||
|
|
||||||
use super::container::{restart_container, update_docker_image};
|
use super::container::{restart_container, update_docker_image};
|
||||||
use bollard::query_parameters::{CreateImageOptions, RestartContainerOptions};
|
//use bollard::query_parameters::{CreateImageOptions, RestartContainerOptions};
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ pub async fn handle_server_message(
|
|||||||
if let Some(image_name) = msg.data.get("image").and_then(|v| v.as_str()) {
|
if let Some(image_name) = msg.data.get("image").and_then(|v| v.as_str()) {
|
||||||
println!("Received restart command for image: {}", image_name);
|
println!("Received restart command for image: {}", image_name);
|
||||||
// Call your update_docker_image function here
|
// Call your update_docker_image function here
|
||||||
update_docker_image(docker, image_name).await?;
|
restart_container(docker, image_name).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err("Missing image name in update message".into())
|
Err("Missing image name in update message".into())
|
||||||
|
@@ -70,11 +70,11 @@ pub async fn get_single_container_cpu_stats(
|
|||||||
};
|
};
|
||||||
|
|
||||||
return Ok(Some(ContainerCpuInfo {
|
return Ok(Some(ContainerCpuInfo {
|
||||||
container_id: container_id.to_string(),
|
container_id: Some(container_id.to_string()),
|
||||||
cpu_usage_percent: cpu_percent,
|
cpu_usage_percent: Some(cpu_percent),
|
||||||
system_cpu_usage: cpu_stats.system_cpu_usage.unwrap_or(0),
|
system_cpu_usage: Some(cpu_stats.system_cpu_usage.unwrap_or(0)),
|
||||||
container_cpu_usage: cpu_usage.total_usage.unwrap_or(0),
|
container_cpu_usage: Some(cpu_usage.total_usage.unwrap_or(0)),
|
||||||
online_cpus,
|
online_cpus: Some(online_cpus),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,6 +91,9 @@ pub async fn get_average_cpu_usage(docker: &Docker) -> Result<f64, Box<dyn Error
|
|||||||
return Ok(0.0);
|
return Ok(0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let total_cpu: f64 = cpu_infos.iter().map(|cpu| cpu.cpu_usage_percent).sum();
|
let total_cpu: f64 = cpu_infos
|
||||||
|
.iter()
|
||||||
|
.map(|cpu| cpu.cpu_usage_percent.unwrap())
|
||||||
|
.sum();
|
||||||
Ok(total_cpu / cpu_infos.len() as f64)
|
Ok(total_cpu / cpu_infos.len() as f64)
|
||||||
}
|
}
|
@@ -6,30 +6,30 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct ContainerCpuInfo {
|
pub struct ContainerCpuInfo {
|
||||||
pub container_id: String,
|
pub container_id: Option<String>,
|
||||||
pub cpu_usage_percent: f64,
|
pub cpu_usage_percent: Option<f64>,
|
||||||
pub system_cpu_usage: u64,
|
pub system_cpu_usage: Option<u64>,
|
||||||
pub container_cpu_usage: u64,
|
pub container_cpu_usage: Option<u64>,
|
||||||
pub online_cpus: u32,
|
pub online_cpus: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct ContainerNetworkInfo {
|
pub struct ContainerNetworkInfo {
|
||||||
pub container_id: String,
|
pub container_id: Option<String>,
|
||||||
pub rx_bytes: u64,
|
pub rx_bytes: Option<u64>,
|
||||||
pub tx_bytes: u64,
|
pub tx_bytes: Option<u64>,
|
||||||
pub rx_packets: u64,
|
pub rx_packets: Option<u64>,
|
||||||
pub tx_packets: u64,
|
pub tx_packets: Option<u64>,
|
||||||
pub rx_errors: u64,
|
pub rx_errors: Option<u64>,
|
||||||
pub tx_errors: u64,
|
pub tx_errors: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct ContainerMemoryInfo {
|
pub struct ContainerMemoryInfo {
|
||||||
pub container_id: String,
|
pub container_id: Option<String>,
|
||||||
pub memory_usage: u64,
|
pub memory_usage: Option<u64>,
|
||||||
pub memory_limit: u64,
|
pub memory_limit: Option<u64>,
|
||||||
pub memory_usage_percent: f64,
|
pub memory_usage_percent: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
use bollard::Docker;
|
use bollard::Docker;
|
||||||
@@ -38,7 +38,14 @@ use std::error::Error;
|
|||||||
/// Get container statistics for all containers using an existing Docker client
|
/// Get container statistics for all containers using an existing Docker client
|
||||||
pub async fn get_container_stats(
|
pub async fn get_container_stats(
|
||||||
docker: &Docker,
|
docker: &Docker,
|
||||||
) -> Result<(Vec<ContainerCpuInfo>, Vec<ContainerNetworkInfo>, Vec<ContainerMemoryInfo>), Box<dyn Error + Send + Sync>> {
|
) -> Result<
|
||||||
|
(
|
||||||
|
Vec<ContainerCpuInfo>,
|
||||||
|
Vec<ContainerNetworkInfo>,
|
||||||
|
Vec<ContainerMemoryInfo>,
|
||||||
|
),
|
||||||
|
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?;
|
||||||
@@ -50,8 +57,14 @@ pub async fn get_container_stats(
|
|||||||
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<(Option<ContainerCpuInfo>, Option<ContainerNetworkInfo>, Option<ContainerMemoryInfo>), Box<dyn Error + Send + Sync>>
|
) -> Result<
|
||||||
{
|
(
|
||||||
|
Option<ContainerCpuInfo>,
|
||||||
|
Option<ContainerNetworkInfo>,
|
||||||
|
Option<ContainerMemoryInfo>,
|
||||||
|
),
|
||||||
|
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?;
|
||||||
|
@@ -51,13 +51,13 @@ pub async fn get_single_container_network_stats(
|
|||||||
// Take the first network interface (usually eth0)
|
// Take the first network interface (usually eth0)
|
||||||
if let Some((_name, net)) = networks.into_iter().next() {
|
if let Some((_name, net)) = networks.into_iter().next() {
|
||||||
return Ok(Some(ContainerNetworkInfo {
|
return Ok(Some(ContainerNetworkInfo {
|
||||||
container_id: container_id.to_string(),
|
container_id: Some(container_id.to_string()),
|
||||||
rx_bytes: net.rx_bytes.unwrap(),
|
rx_bytes: net.rx_bytes,
|
||||||
tx_bytes: net.tx_bytes.unwrap(),
|
tx_bytes: net.tx_bytes,
|
||||||
rx_packets: net.rx_packets.unwrap(),
|
rx_packets: net.rx_packets,
|
||||||
tx_packets: net.tx_packets.unwrap(),
|
tx_packets: net.tx_packets,
|
||||||
rx_errors: net.rx_errors.unwrap(),
|
rx_errors: net.rx_errors,
|
||||||
tx_errors: net.tx_errors.unwrap(),
|
tx_errors: net.tx_errors,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,8 +72,8 @@ pub async fn get_total_network_stats(
|
|||||||
) -> Result<(u64, u64), Box<dyn Error + Send + Sync>> {
|
) -> Result<(u64, u64), Box<dyn Error + Send + Sync>> {
|
||||||
let net_infos = get_all_containers_network_stats(docker).await?;
|
let net_infos = get_all_containers_network_stats(docker).await?;
|
||||||
|
|
||||||
let total_rx: u64 = net_infos.iter().map(|net| net.rx_bytes).sum();
|
let total_rx: u64 = net_infos.iter().map(|net| net.rx_bytes.unwrap()).sum();
|
||||||
let total_tx: u64 = net_infos.iter().map(|net| net.tx_bytes).sum();
|
let total_tx: u64 = net_infos.iter().map(|net| net.tx_bytes.unwrap()).sum();
|
||||||
|
|
||||||
Ok((total_rx, total_tx))
|
Ok((total_rx, total_tx))
|
||||||
}
|
}
|
@@ -58,10 +58,10 @@ pub async fn get_single_container_memory_stats(
|
|||||||
};
|
};
|
||||||
|
|
||||||
return Ok(Some(ContainerMemoryInfo {
|
return Ok(Some(ContainerMemoryInfo {
|
||||||
container_id: container_id.to_string(),
|
container_id: Some(container_id.to_string()),
|
||||||
memory_usage,
|
memory_usage: Some(memory_usage),
|
||||||
memory_limit,
|
memory_limit: Some(memory_limit),
|
||||||
memory_usage_percent,
|
memory_usage_percent: Some(memory_usage_percent),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,6 +72,6 @@ pub async fn get_single_container_memory_stats(
|
|||||||
/// Get total memory usage across all containers
|
/// Get total memory usage across all containers
|
||||||
pub async fn get_total_memory_usage(docker: &Docker) -> Result<u64, Box<dyn Error + Send + Sync>> {
|
pub async fn get_total_memory_usage(docker: &Docker) -> Result<u64, Box<dyn Error + Send + Sync>> {
|
||||||
let mem_infos = get_all_containers_memory_stats(docker).await?;
|
let mem_infos = get_all_containers_memory_stats(docker).await?;
|
||||||
let total_memory: u64 = mem_infos.iter().map(|mem| mem.memory_usage).sum();
|
let total_memory: u64 = mem_infos.iter().map(|mem| mem.memory_usage.unwrap()).sum();
|
||||||
Ok(total_memory)
|
Ok(total_memory)
|
||||||
}
|
}
|
@@ -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.
|
||||||
@@ -93,7 +94,7 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
|||||||
Ok((id, ip)) => {
|
Ok((id, ip)) => {
|
||||||
println!("Registered with server. ID: {}, IP: {}", id, ip);
|
println!("Registered with server. ID: {}, IP: {}", id, ip);
|
||||||
(id, ip)
|
(id, ip)
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Fehler bei der Registrierung am Server: {e}");
|
eprintln!("Fehler bei der Registrierung am Server: {e}");
|
||||||
return Err(e);
|
return Err(e);
|
||||||
@@ -111,9 +112,22 @@ async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
|
|||||||
};
|
};
|
||||||
println!("Client Version: {}", client_version);
|
println!("Client Version: {}", client_version);
|
||||||
|
|
||||||
|
// Prepare Docker registration DTO
|
||||||
|
let container_dto = if let Some(ref docker_manager) = docker_manager {
|
||||||
|
docker_manager.create_registration_dto().await?
|
||||||
|
} else {
|
||||||
|
models::DockerRegistrationDto {
|
||||||
|
server_id: 0,
|
||||||
|
//container_count: 0, --- IGNORE ---
|
||||||
|
containers: "[]".to_string(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let _ =
|
||||||
|
api::broadcast_docker_containers(server_url, server_id, &mut container_dto.clone()).await?;
|
||||||
|
|
||||||
// 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 +150,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
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -13,10 +13,11 @@ use std::error::Error;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
|
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 +28,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 +43,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 +75,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 +125,13 @@ 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.docker_manager.collect_metrics().await?;
|
||||||
let containers = container::get_available_containers(&self.docker).await;
|
Ok(DockerMetricDto {
|
||||||
let mut metrics = Vec::new();
|
server_id: self.server_id,
|
||||||
|
containers: metrics.containers,
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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,9 @@ 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,
|
||||||
|
/// Number of currently running containers
|
||||||
|
// pub container_count: usize, --- IGNORE ---
|
||||||
/// json stringified array of DockerContainer
|
/// json stringified array of DockerContainer
|
||||||
///
|
///
|
||||||
/// ## Json Example
|
/// ## Json Example
|
||||||
@@ -198,12 +200,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 +219,33 @@ 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)]
|
||||||
|
|
||||||
|
pub struct DockerCollectMetricDto {
|
||||||
|
pub id: String,
|
||||||
|
pub cpu: DockerContainerCpuDto,
|
||||||
|
pub ram: DockerContainerRamDto,
|
||||||
|
pub network: DockerContainerNetworkDto,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Clone)]
|
||||||
|
pub struct DockerContainerCpuDto {
|
||||||
|
pub cpu_load: Option<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Clone)]
|
||||||
|
pub struct DockerContainerRamDto {
|
||||||
|
pub cpu_load: Option<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Clone)]
|
||||||
|
|
||||||
|
pub struct DockerContainerNetworkDto {
|
||||||
|
pub net_in: Option<f64>,
|
||||||
|
pub net_out: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Clone)]
|
#[derive(Debug, Serialize, Clone)]
|
||||||
|
Reference in New Issue
Block a user