use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeConfig {
pub node_id: Uuid,
pub node_name: String,
pub address: String,
pub port: u16,
pub role: NodeRole,
pub capabilities: Vec<String>,
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum NodeRole {
Leader,
Follower,
Backup,
Observer,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum NodeStatus {
Online,
Offline,
Joining,
Leaving,
Maintenance,
Degraded,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterNode {
pub config: NodeConfig,
pub status: NodeStatus,
pub health_metrics: NodeHealthMetrics,
pub last_heartbeat: chrono::DateTime<chrono::Utc>,
pub uptime_secs: u64,
pub current_term: u64,
pub version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeHealthMetrics {
pub cpu_usage: f64,
pub memory_usage: f64,
pub disk_usage: f64,
pub network_io: f64,
pub load_average: f64,
pub active_connections: u32,
pub response_time_ms: f64,
pub error_rate: f64,
}
impl Default for NodeHealthMetrics {
fn default() -> Self {
Self {
cpu_usage: 0.0,
memory_usage: 0.0,
disk_usage: 0.0,
network_io: 0.0,
load_average: 0.0,
active_connections: 0,
response_time_ms: 0.0,
error_rate: 0.0,
}
}
}
impl ClusterNode {
pub fn new(config: NodeConfig) -> Self {
Self {
config,
status: NodeStatus::Joining,
health_metrics: NodeHealthMetrics::default(),
last_heartbeat: chrono::Utc::now(),
uptime_secs: 0,
current_term: 0,
version: "0.1.0".to_string(),
}
}
pub fn node_id(&self) -> Uuid {
self.config.node_id
}
pub fn address(&self) -> String {
format!("{}:{}", self.config.address, self.config.port)
}
pub fn is_healthy(&self) -> bool {
self.status == NodeStatus::Online &&
self.health_metrics.cpu_usage < 90.0 &&
self.health_metrics.memory_usage < 90.0 &&
self.health_metrics.disk_usage < 95.0 &&
self.health_metrics.error_rate < 5.0
}
pub fn update_status(&mut self, status: NodeStatus) {
self.status = status;
self.last_heartbeat = chrono::Utc::now();
}
pub fn update_health_metrics(&mut self, metrics: NodeHealthMetrics) {
self.health_metrics = metrics;
self.last_heartbeat = chrono::Utc::now();
}
pub fn update_heartbeat(&mut self) {
self.last_heartbeat = chrono::Utc::now();
self.uptime_secs += 1;
}
pub fn is_leader(&self) -> bool {
self.config.role == NodeRole::Leader
}
pub fn set_as_leader(&mut self) {
self.config.role = NodeRole::Leader;
self.status = NodeStatus::Online;
}
pub fn set_as_follower(&mut self) {
self.config.role = NodeRole::Follower;
self.status = NodeStatus::Online;
}
pub fn has_capability(&self, capability: &str) -> bool {
self.config.capabilities.contains(&capability.to_string())
}
pub fn add_capability(&mut self, capability: String) {
if !self.has_capability(&capability) {
self.config.capabilities.push(capability);
}
}
pub fn remove_capability(&mut self, capability: &str) {
self.config.capabilities.retain(|c| c != capability);
}
pub fn get_metadata(&self, key: &str) -> Option<&String> {
self.config.metadata.get(key)
}
pub fn set_metadata(&mut self, key: String, value: String) {
self.config.metadata.insert(key, value);
}
pub fn remove_metadata(&mut self, key: &str) {
self.config.metadata.remove(key);
}
}
impl Default for NodeConfig {
fn default() -> Self {
Self {
node_id: Uuid::new_v4(),
node_name: "fortress-node".to_string(),
address: "127.0.0.1".to_string(),
port: 8081,
role: NodeRole::Follower,
capabilities: vec![
"storage".to_string(),
"replication".to_string(),
"encryption".to_string(),
],
metadata: HashMap::new(),
}
}
}