diff --git a/src/api/cluster/node.rs b/src/api/cluster/node.rs index 6c4c3b4..b20d2a1 100644 --- a/src/api/cluster/node.rs +++ b/src/api/cluster/node.rs @@ -1,16 +1,79 @@ use std::time::Duration; +use k8s_openapi::api::core::v1::Node; +use kube::{Api, Client}; +use kube::api::ListParams; use proxmox_api::nodes::node::lxc::vmid::migrate; use proxmox_api::nodes::node::tasks::upid; use sea_orm::*; use tokio::time::sleep; -use log::{info}; +use log::{info, debug}; use proxmox_api::types::VmId; use crate::api::entity::{locks, nodes}; -use crate::api::entity::nodes::{NodeParams, P5xError}; +use crate::api::entity::nodes::{Model as NodeModel, NodeParams, P5xError}; use crate::api::entity::oplogs::Op; use crate::api::services::Services; +/** WIP: Read nodes from the K8s API and register them w/ P5x based on metadata labels. */ +pub async fn discover_nodes( + svc: &Services<'_>, +) -> Result<(), P5xError> { + debug!(target: "p5x", "Attempting to discover K8s cluster nodes..."); + + let client = Client::try_default().await.map_err(P5xError::KubeError)?; + let nodes: Api = Api::all(client); + + let lp = ListParams::default(); + let nodes = nodes.list(&lp).await.map_err(P5xError::KubeError)?; + for node in nodes { + let name = node.metadata.name.unwrap_or("(unknown)".to_string()); + + // Get the labels for the node + let labels = node.metadata.labels; + if labels.is_none() { + debug!(target: "p5x", "Skipping node {name}: could not load labels"); + continue; + } + let labels = labels.unwrap(); + + // Check if it has the required pve_host and pve_id labels: + let pve_host = labels.get("p5x.garrettmills.dev/pve-host"); + let pve_id = labels.get("p5x.garrettmills.dev/pve-id"); + if pve_host.is_none() || pve_id.is_none() { + debug!(target: "p5x", "Skipping node {name}: missing required pve-host or pve-id label(s)"); + continue; + } + + let pve_host = pve_host.unwrap(); + let pve_id: Result = pve_id.unwrap().parse(); + if pve_id.is_err() { + debug!(target: "p5x", "Skipping node {name}: could not parse pve-id into i32"); + continue; + } + let pve_id = pve_id.unwrap(); + + // Check if the node has been explicitly excluded from p5x management + let ignore = labels.get("p5x.garrettmills.dev/ignore"); + if ignore.is_some() { + debug!(target: "p5x", "Skipping node {name}: found explicit ignore label"); + continue; + } + + // Check if the node is already registered + let existing = NodeModel::from_pve(svc, pve_host, pve_id).await?; + if existing.is_some() { + debug!(target: "p5x", "Skipping node {name}: already registered"); + continue; + } + + info!(target: "p5x", "Discovered node {name} ({pve_host}:{pve_id}). Attempting to register it."); + // fixme: WIP + } + + Ok(()) +} + + /** Register an existing LXC container with P5x. */ pub async fn register_node( svc: &Services<'_>, diff --git a/src/api/entity/nodes.rs b/src/api/entity/nodes.rs index f0b82e5..d95fae1 100644 --- a/src/api/entity/nodes.rs +++ b/src/api/entity/nodes.rs @@ -301,6 +301,15 @@ pub struct Model { } impl Model { + pub async fn from_pve(svc: &Services<'_>, pve_host: &str, pve_id: i32) -> Result, P5xError> { + Entity::find() + .filter(Column::PveHost.eq(pve_host)) + .filter(Column::PveId.eq(pve_id)) + .one(svc.db) + .await + .map_err(P5xError::DbErr) + } + /** Get the VM ID used by the Proxmox API library. */ pub fn vm_id(&self) -> VmId { VmId::new(i64::from(self.pve_id)).unwrap()