2026-06-11 02:50:30 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
|
|
/// Cluster information
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct ClusterInfo {
|
|
|
|
|
pub id: String,
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub cluster_type: ClusterType,
|
|
|
|
|
pub url: String,
|
|
|
|
|
pub port: u16,
|
|
|
|
|
pub username: String,
|
|
|
|
|
pub created_at: String,
|
|
|
|
|
pub updated_at: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Cluster type
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
|
|
|
|
#[serde(rename_all = "lowercase")]
|
|
|
|
|
pub enum ClusterType {
|
|
|
|
|
#[default]
|
2026-06-11 14:38:36 +00:00
|
|
|
VE, // Proxmox VE
|
2026-06-11 02:50:30 +00:00
|
|
|
PBS, // Proxmox Backup Server
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Cluster registry for managing multiple clusters
|
|
|
|
|
pub struct ClusterRegistry {
|
|
|
|
|
clusters: HashMap<String, ClusterInfo>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ClusterRegistry {
|
|
|
|
|
/// Create a new cluster registry
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
clusters: HashMap::new(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Add a cluster
|
|
|
|
|
pub fn add_cluster(&mut self, cluster: ClusterInfo) {
|
|
|
|
|
self.clusters.insert(cluster.id.clone(), cluster);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Remove a cluster
|
|
|
|
|
pub fn remove_cluster(&mut self, id: &str) -> Option<ClusterInfo> {
|
|
|
|
|
self.clusters.remove(id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get a cluster by ID
|
|
|
|
|
pub fn get_cluster(&self, id: &str) -> Option<&ClusterInfo> {
|
|
|
|
|
self.clusters.get(id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get all clusters
|
|
|
|
|
pub fn list_clusters(&self) -> Vec<&ClusterInfo> {
|
|
|
|
|
self.clusters.values().collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get clusters by type
|
|
|
|
|
pub fn list_clusters_by_type(&self, cluster_type: &ClusterType) -> Vec<&ClusterInfo> {
|
|
|
|
|
self.clusters
|
|
|
|
|
.values()
|
|
|
|
|
.filter(|c| &c.cluster_type == cluster_type)
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Get cluster count
|
|
|
|
|
pub fn cluster_count(&self) -> usize {
|
|
|
|
|
self.clusters.len()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Check if a cluster exists
|
|
|
|
|
pub fn has_cluster(&self, id: &str) -> bool {
|
|
|
|
|
self.clusters.contains_key(id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for ClusterRegistry {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self::new()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_cluster_registry_new() {
|
|
|
|
|
let registry = ClusterRegistry::new();
|
|
|
|
|
assert_eq!(registry.cluster_count(), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_cluster_registry_add_and_get() {
|
|
|
|
|
let mut registry = ClusterRegistry::new();
|
|
|
|
|
|
|
|
|
|
let cluster = ClusterInfo {
|
|
|
|
|
id: "cluster-1".to_string(),
|
|
|
|
|
name: "Production".to_string(),
|
|
|
|
|
cluster_type: ClusterType::VE,
|
|
|
|
|
url: "https://pve.example.com".to_string(),
|
|
|
|
|
port: 8006,
|
|
|
|
|
username: "root@pam".to_string(),
|
|
|
|
|
created_at: "2026-06-10 12:00:00".to_string(),
|
|
|
|
|
updated_at: "2026-06-10 12:00:00".to_string(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
registry.add_cluster(cluster.clone());
|
|
|
|
|
assert_eq!(registry.cluster_count(), 1);
|
|
|
|
|
|
|
|
|
|
let retrieved = registry.get_cluster("cluster-1");
|
|
|
|
|
assert!(retrieved.is_some());
|
|
|
|
|
assert_eq!(retrieved.unwrap().name, "Production");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_cluster_registry_remove() {
|
|
|
|
|
let mut registry = ClusterRegistry::new();
|
|
|
|
|
|
|
|
|
|
let cluster = ClusterInfo {
|
|
|
|
|
id: "cluster-1".to_string(),
|
|
|
|
|
name: "Production".to_string(),
|
|
|
|
|
cluster_type: ClusterType::VE,
|
|
|
|
|
url: "https://pve.example.com".to_string(),
|
|
|
|
|
port: 8006,
|
|
|
|
|
username: "root@pam".to_string(),
|
|
|
|
|
created_at: "2026-06-10 12:00:00".to_string(),
|
|
|
|
|
updated_at: "2026-06-10 12:00:00".to_string(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
registry.add_cluster(cluster);
|
|
|
|
|
assert_eq!(registry.cluster_count(), 1);
|
|
|
|
|
|
|
|
|
|
let removed = registry.remove_cluster("cluster-1");
|
|
|
|
|
assert!(removed.is_some());
|
|
|
|
|
assert_eq!(registry.cluster_count(), 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_cluster_registry_list_by_type() {
|
|
|
|
|
let mut registry = ClusterRegistry::new();
|
|
|
|
|
|
|
|
|
|
let ve_cluster = ClusterInfo {
|
|
|
|
|
id: "ve-1".to_string(),
|
|
|
|
|
name: "VE Cluster".to_string(),
|
|
|
|
|
cluster_type: ClusterType::VE,
|
|
|
|
|
url: "https://pve.example.com".to_string(),
|
|
|
|
|
port: 8006,
|
|
|
|
|
username: "root@pam".to_string(),
|
|
|
|
|
created_at: "2026-06-10 12:00:00".to_string(),
|
|
|
|
|
updated_at: "2026-06-10 12:00:00".to_string(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let pbs_cluster = ClusterInfo {
|
|
|
|
|
id: "pbs-1".to_string(),
|
|
|
|
|
name: "PBS Cluster".to_string(),
|
|
|
|
|
cluster_type: ClusterType::PBS,
|
|
|
|
|
url: "https://pbs.example.com".to_string(),
|
|
|
|
|
port: 8007,
|
|
|
|
|
username: "root@pam".to_string(),
|
|
|
|
|
created_at: "2026-06-10 12:00:00".to_string(),
|
|
|
|
|
updated_at: "2026-06-10 12:00:00".to_string(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
registry.add_cluster(ve_cluster);
|
|
|
|
|
registry.add_cluster(pbs_cluster);
|
|
|
|
|
|
|
|
|
|
let ve_clusters = registry.list_clusters_by_type(&ClusterType::VE);
|
|
|
|
|
assert_eq!(ve_clusters.len(), 1);
|
|
|
|
|
|
|
|
|
|
let pbs_clusters = registry.list_clusters_by_type(&ClusterType::PBS);
|
|
|
|
|
assert_eq!(pbs_clusters.len(), 1);
|
|
|
|
|
}
|
|
|
|
|
}
|