tatara_core/domain/
node.rs1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use uuid::Uuid;
5
6use super::job::{DriverType, Resources};
7
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
9#[serde(rename_all = "snake_case")]
10pub enum NodeStatus {
11 Ready,
12 Down,
13 Draining,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct Node {
18 pub id: String,
19 pub address: String,
20 pub status: NodeStatus,
21 #[serde(default = "default_eligible")]
22 pub eligible: bool,
23 pub total_resources: Resources,
24 pub available_resources: Resources,
25 pub attributes: HashMap<String, String>,
26 pub drivers: Vec<DriverType>,
27 pub last_heartbeat: DateTime<Utc>,
28 pub allocations: Vec<Uuid>,
29}
30
31fn default_eligible() -> bool {
32 true
33}
34
35impl Node {
36 pub fn local() -> Self {
37 let os = std::env::consts::OS.to_string();
38 let arch = std::env::consts::ARCH.to_string();
39 let hostname = hostname::get()
40 .map(|h| h.to_string_lossy().to_string())
41 .unwrap_or_else(|_| "unknown".to_string());
42
43 let mut attributes = HashMap::new();
44 attributes.insert("os".to_string(), os.clone());
45 attributes.insert("arch".to_string(), arch);
46 attributes.insert("hostname".to_string(), hostname.clone());
47
48 let (cpu_mhz, memory_mb) = detect_resources();
49
50 Self {
51 id: hostname,
52 address: "127.0.0.1:4647".to_string(),
53 status: NodeStatus::Ready,
54 eligible: true,
55 total_resources: Resources { cpu_mhz, memory_mb },
56 available_resources: Resources { cpu_mhz, memory_mb },
57 attributes,
58 drivers: vec![DriverType::Exec],
59 last_heartbeat: Utc::now(),
60 allocations: Vec::new(),
61 }
62 }
63}
64
65fn detect_resources() -> (u64, u64) {
66 let cpu_mhz = (num_cpus() as u64) * 1000;
67
68 #[cfg(target_os = "macos")]
69 let memory_mb = {
70 use std::process::Command;
71 Command::new("sysctl")
72 .args(["-n", "hw.memsize"])
73 .output()
74 .ok()
75 .and_then(|o| {
76 String::from_utf8(o.stdout)
77 .ok()
78 .and_then(|s| s.trim().parse::<u64>().ok())
79 })
80 .unwrap_or(0)
81 / (1024 * 1024)
82 };
83
84 #[cfg(target_os = "linux")]
85 let memory_mb = {
86 std::fs::read_to_string("/proc/meminfo")
87 .ok()
88 .and_then(|s| {
89 s.lines()
90 .find(|l| l.starts_with("MemTotal:"))
91 .and_then(|l| {
92 l.split_whitespace()
93 .nth(1)
94 .and_then(|v| v.parse::<u64>().ok())
95 })
96 })
97 .unwrap_or(0)
98 / 1024
99 };
100
101 #[cfg(not(any(target_os = "macos", target_os = "linux")))]
102 let memory_mb = 0;
103
104 (cpu_mhz, memory_mb)
105}
106
107fn num_cpus() -> usize {
108 std::thread::available_parallelism()
109 .map(|p| p.get())
110 .unwrap_or(1)
111}