/ crates / acdc-check / src / lib.rs
lib.rs
  1  //! System requirements checking for AC/DC.
  2  //!
  3  //! This crate provides functionality to check if a system meets the requirements
  4  //! for running various node roles in the ALPHA/DELTA network.
  5  
  6  pub mod cpu;
  7  pub mod environment;
  8  pub mod gpu;
  9  pub mod memory;
 10  pub mod network;
 11  pub mod ports;
 12  pub mod storage;
 13  
 14  use acdc_core::{NodeRole, Requirements, Result};
 15  use serde::{Deserialize, Serialize};
 16  
 17  /// Complete system information.
 18  #[derive(Debug, Clone, Serialize, Deserialize)]
 19  pub struct SystemInfo {
 20      pub cpu: cpu::CpuInfo,
 21      pub memory: memory::MemoryInfo,
 22      pub storage: storage::StorageInfo,
 23      pub gpu: Option<gpu::GpuInfo>,
 24      pub network: network::NetworkInfo,
 25      pub environment: environment::EnvironmentInfo,
 26  }
 27  
 28  impl SystemInfo {
 29      /// Gather all system information.
 30      pub fn gather() -> Result<Self> {
 31          Ok(Self {
 32              cpu: cpu::CpuInfo::detect()?,
 33              memory: memory::MemoryInfo::detect()?,
 34              storage: storage::StorageInfo::detect()?,
 35              gpu: gpu::GpuInfo::detect().ok(),
 36              network: network::NetworkInfo::detect()?,
 37              environment: environment::EnvironmentInfo::detect()?,
 38          })
 39      }
 40  
 41      /// Check if the system meets requirements for a role.
 42      pub fn check_role(&self, role: NodeRole) -> CheckResult {
 43          let requirements = role.min_requirements();
 44          self.check_requirements(&requirements, role)
 45      }
 46  
 47      /// Check system against specific requirements.
 48      pub fn check_requirements(&self, req: &Requirements, role: NodeRole) -> CheckResult {
 49          let mut result = CheckResult {
 50              role,
 51              passed: true,
 52              cpu: CheckStatus::pass("CPU cores sufficient"),
 53              memory: CheckStatus::pass("Memory sufficient"),
 54              storage: CheckStatus::pass("Storage sufficient"),
 55              gpu: CheckStatus::pass("GPU not required"),
 56              issues: Vec::new(),
 57          };
 58  
 59          // CPU check
 60          if self.cpu.physical_cores < req.cpu_cores {
 61              result.passed = false;
 62              result.cpu = CheckStatus::fail(format!(
 63                  "Need {} cores, have {}",
 64                  req.cpu_cores, self.cpu.physical_cores
 65              ));
 66              result.issues.push(format!(
 67                  "CPU: {} cores required, {} available",
 68                  req.cpu_cores, self.cpu.physical_cores
 69              ));
 70          }
 71  
 72          // Memory check
 73          let ram_gb = self.memory.total_bytes / (1024 * 1024 * 1024);
 74          if ram_gb < req.ram_gb {
 75              result.passed = false;
 76              result.memory =
 77                  CheckStatus::fail(format!("Need {} GB RAM, have {} GB", req.ram_gb, ram_gb));
 78              result.issues.push(format!(
 79                  "Memory: {} GB required, {} GB available",
 80                  req.ram_gb, ram_gb
 81              ));
 82          }
 83  
 84          // Storage check
 85          let storage_gb = self.storage.available_bytes / (1024 * 1024 * 1024);
 86          if storage_gb < req.storage_gb {
 87              result.passed = false;
 88              result.storage = CheckStatus::fail(format!(
 89                  "Need {} GB storage, have {} GB",
 90                  req.storage_gb, storage_gb
 91              ));
 92              result.issues.push(format!(
 93                  "Storage: {} GB required, {} GB available",
 94                  req.storage_gb, storage_gb
 95              ));
 96          }
 97  
 98          // GPU check
 99          if req.gpu_required {
100              match &self.gpu {
101                  Some(gpu) if gpu.available => {
102                      if let Some(required_vram) = req.gpu_vram_gb {
103                          let vram_gb = gpu.vram_bytes / (1024 * 1024 * 1024);
104                          if vram_gb < required_vram as u64 {
105                              result.passed = false;
106                              result.gpu = CheckStatus::fail(format!(
107                                  "Need {} GB VRAM, have {} GB",
108                                  required_vram, vram_gb
109                              ));
110                              result.issues.push(format!(
111                                  "GPU VRAM: {} GB required, {} GB available",
112                                  required_vram, vram_gb
113                              ));
114                          } else {
115                              result.gpu =
116                                  CheckStatus::pass(format!("{} with {} GB VRAM", gpu.name, vram_gb));
117                          }
118                      }
119                  }
120                  _ => {
121                      result.passed = false;
122                      result.gpu = CheckStatus::fail("GPU required but not detected");
123                      result
124                          .issues
125                          .push("GPU: Required but not detected".to_string());
126                  }
127              }
128          }
129  
130          result
131      }
132  }
133  
134  /// Result of a system requirements check.
135  #[derive(Debug, Clone, Serialize)]
136  pub struct CheckResult {
137      pub role: NodeRole,
138      pub passed: bool,
139      pub cpu: CheckStatus,
140      pub memory: CheckStatus,
141      pub storage: CheckStatus,
142      pub gpu: CheckStatus,
143      pub issues: Vec<String>,
144  }
145  
146  /// Status of a single check.
147  #[derive(Debug, Clone, Serialize)]
148  pub struct CheckStatus {
149      pub passed: bool,
150      pub message: String,
151  }
152  
153  impl CheckStatus {
154      pub fn pass(msg: impl Into<String>) -> Self {
155          Self {
156              passed: true,
157              message: msg.into(),
158          }
159      }
160  
161      pub fn fail(msg: impl Into<String>) -> Self {
162          Self {
163              passed: false,
164              message: msg.into(),
165          }
166      }
167  }
168  
169  /// Check if specific ports are available.
170  pub fn check_ports(ports: &[u16]) -> Vec<ports::PortStatus> {
171      ports.iter().map(|&p| ports::check_port(p)).collect()
172  }