From 868f3eb13adebbf5dc06c0fa9e0182a5e773f9a9 Mon Sep 17 00:00:00 2001 From: Tristan Smith Date: Thu, 12 Sep 2024 22:36:14 -0400 Subject: [PATCH] it works --- .gitignore | 1 + Cargo.lock | 282 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 8 ++ src/main.rs | 173 ++++++++++++++++++++++++++++++++ 4 files changed, 464 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0ee0139 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,282 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "idrac" +version = "0.1.0" +dependencies = [ + "clap", + "regex", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a9c3b2a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "idrac" +version = "0.1.0" +edition = "2021" + +[dependencies] +clap = { version = "4.5.17", features = ["derive"] } +regex = "1.10.6" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..1096ebb --- /dev/null +++ b/src/main.rs @@ -0,0 +1,173 @@ +use clap::{ArgAction, Arg, Command}; +use regex::Regex; +use std::process::Command as Cmd; +use std::str; + +const IPMIHOST: &str = "192.168.1.223"; +const IPMIUSER: &str = "root"; +const IPMIPASS: &str = ""; + +fn execute_ipmi_command(command: &str) -> String { + let full_command = format!( + "ipmitool -I lanplus -H {} -U {} -P '{}' {}", + IPMIHOST, IPMIUSER, IPMIPASS, command + ); + + let output = Cmd::new("sh") + .arg("-c") + .arg(full_command) + .output() + .expect("Failed to execute command"); + + let stdout = str::from_utf8(&output.stdout).unwrap_or(""); + let stderr = str::from_utf8(&output.stderr).unwrap_or(""); + + if !stderr.is_empty() { + eprintln!("{}", stderr); + } + + stdout.trim().to_string() +} + +fn power_on() { + println!("Powering on the server..."); + execute_ipmi_command("chassis power on"); +} + +fn power_off() { + println!("Powering off the server..."); + execute_ipmi_command("chassis power off"); + println!("Enabling dynamic fan control..."); + execute_ipmi_command("raw 0x30 0x30 0x01 0x01"); +} + +fn set_fan_speed(speed: u8) { + println!("Setting fan speed to {}%...", speed); + execute_ipmi_command(&format!("raw 0x30 0x30 0x02 0xff {:x}", speed)); +} + +fn enable_dynamic_fan_control() { + println!("Enabling dynamic fan control..."); + execute_ipmi_command("raw 0x30 0x30 0x01 0x01"); +} + +fn disable_dynamic_fan_control() { + println!("Disabling dynamic fan control..."); + execute_ipmi_command("raw 0x30 0x30 0x01 0x00"); +} + +fn get_server_temperature() -> f32 { + println!("Getting server temperature..."); + let command = format!( + "ipmitool -I lanplus -H {} -U {} -P '{}' sdr type temperature", + IPMIHOST, IPMIUSER, IPMIPASS + ); + let result = Cmd::new("sh") + .arg("-c") + .arg(command) + .output() + .expect("Failed to get temperature"); + + let stdout = str::from_utf8(&result.stdout).unwrap_or(""); + + let re = Regex::new(r"\| (\d+) degrees C").unwrap(); + let mut temps = Vec::new(); + + for cap in re.captures_iter(stdout) { + if let Ok(temp) = cap[1].parse::() { + temps.push(temp); + } + } + + let avg_temp = if !temps.is_empty() { + temps.iter().sum::() / temps.len() as f32 + } else { + 0.0 + }; + + avg_temp +} + +fn adjust_fan_speed_based_on_temp() { + let temp = get_server_temperature(); + println!("Current temperature: {:.1} degrees C", temp); + + let warning_threshold = 75.0; + let critical_threshold = 90.0; + + if temp >= critical_threshold { + println!("Temperature is critical, setting fan speed to 100%."); + set_fan_speed(100); + } else if temp >= warning_threshold { + println!("Temperature is high, setting fan speed to 80%."); + set_fan_speed(80); + } else if temp >= 40.0 { + println!("Temperature is moderate, setting fan speed to 60%."); + set_fan_speed(60); + } else if temp >= 30.0 { + println!("Temperature is low, setting fan speed to 50%."); + set_fan_speed(50); + } else { + println!("Temperature is very low, setting fan speed to 40%."); + set_fan_speed(40); + } +} + +fn main() { + let matches = Command::new("IDRAC helper script.") + .version("1.0") + .about("Controls server power, fan speed, and temperature monitoring for Dell servers") + .arg( + Arg::new("power") + .short('p') + .long("power") + .value_parser(["on", "off"]) + .help("Power on or off the server"), + ) + .arg( + Arg::new("fan") + .short('f') + .long("fan") + .value_parser(clap::value_parser!(u8).range(0..=100)) + .help("Set fan speed percentage"), + ) + .arg( + Arg::new("temp") + .short('t') + .long("temp") + .action(ArgAction::SetTrue) + .help("Adjust fan speed based on temperature"), + ) + .arg( + Arg::new("dynamic") + .short('d') + .long("dynamic") + .value_parser(["on", "off"]) + .help("Toggle dynamic fan control"), + ) + .get_matches(); + + if let Some(power) = matches.get_one::("power") { + match power.as_str() { + "on" => power_on(), + "off" => power_off(), + _ => {} + } + } + + if let Some(fan_speed) = matches.get_one::("fan") { + set_fan_speed(*fan_speed); + } + + if matches.get_flag("temp") { + adjust_fan_speed_based_on_temp(); + } + + if let Some(dynamic) = matches.get_one::("dynamic") { + match dynamic.as_str() { + "on" => enable_dynamic_fan_control(), + "off" => disable_dynamic_fan_control(), + _ => {} + } + } +}