mod common;
use clap::Parser;
use indicatif::{ProgressBar, ProgressStyle};
use ipmi_rs::{
app::GetDeviceId,
connection::CompletionErrorCode,
sensor_event::{GetSensorReading, ThresholdReading},
storage::{
sdr::{
record::{IdentifiableSensor, InstancedSensor, RecordContents},
GetDeviceSdrInfo, GetSdrRepositoryInfo, SdrCount, SdrGetAllocInfo, SdrOperation,
},
sel::{GetSelEntry, GetSelInfo, RecordId as SelRecordId, SelCommand, SelGetAllocInfo},
},
IpmiError,
};
use ipmi_rs_log::{LogOutput, Logger};
fn main() -> std::io::Result<()> {
pretty_env_logger::formatted_builder()
.parse_filters(&std::env::var("RUST_LOG").unwrap_or("info".to_string()))
.init();
let opts = common::CliOpts::parse();
let mut ipmi = opts.get_connection()?;
let log_output = &LogOutput::LogTarget(log::Level::Info, "get_info".into());
let debug_log_output = &LogOutput::LogTarget(log::Level::Debug, "get_info".into());
log::info!("Getting SEL info");
let info = ipmi.send_recv(GetSelInfo).unwrap();
Logger::log(log_output, &info);
if info.supported_cmds.contains(&SelCommand::GetAllocInfo) {
log::info!("Getting SEL Alloc info");
let alloc_info = ipmi.send_recv(SelGetAllocInfo).unwrap();
Logger::log(log_output, &alloc_info);
} else {
log::info!("Getting SEL Alloc info is not supported");
}
if info.entries > 0 {
log::info!("Getting first record");
let first_record = ipmi
.send_recv(GetSelEntry::new(None, SelRecordId::LAST))
.unwrap();
Logger::log(log_output, &first_record);
}
let device_id = ipmi.send_recv(GetDeviceId).unwrap();
Logger::log(log_output, &device_id);
log::info!("Getting Device SDR Info");
if let Ok(sdr_info) = ipmi.send_recv(GetDeviceSdrInfo::new(SdrCount)) {
Logger::log(log_output, &sdr_info);
} else {
log::warn!("Could not get Device SDR info");
}
log::info!("Getting SDR repository info");
let sdr_info = ipmi.send_recv(GetSdrRepositoryInfo).unwrap();
Logger::log(log_output, &sdr_info);
if sdr_info.supported_ops.contains(&SdrOperation::GetAllocInfo) {
let sdr_alloc_info = ipmi.send_recv(SdrGetAllocInfo).unwrap();
Logger::log(log_output, &sdr_alloc_info);
};
let template = "[{bar:.green/white}] {prefix} ({pos}/{len})";
let progress_bar = ProgressBar::new(sdr_info.record_count as u64)
.with_style(
ProgressStyle::with_template(&template)
.unwrap()
.progress_chars("#>-"),
)
.with_prefix("Loading SDR Records");
let sensors = ipmi
.sdrs()
.map(|v| {
progress_bar.inc(1);
v
})
.collect::<Vec<_>>();
progress_bar.finish();
for sensor in &sensors {
match &sensor.contents {
RecordContents::FullSensor(full) => {
log_id("Full Sensor Record", full);
let result = ipmi.send_recv(GetSensorReading::for_sensor_key(full.key_data()));
let value = match result {
Ok(value) => value,
Err(IpmiError::Failed {
completion_code: CompletionErrorCode::RequestedDatapointNotPresent,
..
}) => {
log::warn!(" Sensor for {} not present", full.id_string());
continue;
}
Err(e) => {
log::error!(
"Failed to get sensor reading for {}: {e:?}",
full.id_string()
);
continue;
}
};
let reading: ThresholdReading = (&value).into();
if let Some(reading) = reading.reading {
if let Some(display) = full.display_reading(reading) {
log::info!(" {}: {}", full.id_string(), display);
Logger::log(debug_log_output, sensor);
}
} else {
log::warn!(" No reading for {}", full.id_string());
}
}
RecordContents::CompactSensor(compact) => log_sensor("Compact Sensor", compact),
RecordContents::EventOnlySensor(event) => log_sensor("Event-only Sensor", event),
RecordContents::GenericDeviceLocator(generic) => {
log_id("Generic Device Locator", generic);
log_device_type(generic.device_type, generic.device_type_modifier);
}
RecordContents::FruDeviceLocator(fru) => {
log_id("FRU Device Locator", fru);
log_device_type(fru.device_type, fru.device_type_modifier);
}
RecordContents::McDeviceLocator(mc) => log_id("MC Device Locator", mc),
RecordContents::Unknown { ty, .. } => {
log::info!("Unknown record type. Type: 0x{ty:02X}");
}
}
}
Ok(())
}
fn log_id<T: IdentifiableSensor>(ty: &str, sensor: &T) {
log::info!("{ty} {}", sensor.id_string());
}
fn log_sensor<T: InstancedSensor>(ty: &str, sensor: &T) {
log_id(ty, sensor);
log::info!(" Sensor type: {:?}", sensor.ty());
}
fn log_device_type(device_type: u8, device_type_modifier: u8) {
let name = device_type_name(device_type);
match name {
Some(name) => log::info!(
" Device type: {} (0x{device_type:02X}, {name})",
device_type
),
None => log::info!(" Device type: {} (0x{device_type:02X})", device_type),
}
let modifier_name = device_type_modifier_name(device_type, device_type_modifier);
match modifier_name {
Some(name) => log::info!(" Device type modifier: 0x{device_type_modifier:02X} ({name})"),
None => log::info!(" Device type modifier: 0x{device_type_modifier:02X}"),
}
}
fn device_type_name(device_type: u8) -> Option<&'static str> {
match device_type {
0x00 => Some("Reserved"),
0x01 => Some("Reserved"),
0x02 => Some("DS1624 temperature sensor / EEPROM or equivalent"),
0x03 => Some("DS1621 temperature sensor or equivalent"),
0x04 => Some("LM75 temperature sensor or equivalent"),
0x05 => Some("Heceta ASIC or similar"),
0x06 => Some("Reserved"),
0x07 => Some("Reserved"),
0x08 => Some("EEPROM, 24C01 or equivalent"),
0x09 => Some("EEPROM, 24C02 or equivalent"),
0x0A => Some("EEPROM, 24C04 or equivalent"),
0x0B => Some("EEPROM, 24C08 or equivalent"),
0x0C => Some("EEPROM, 24C16 or equivalent"),
0x0D => Some("EEPROM, 24C17 or equivalent"),
0x0E => Some("EEPROM, 24C32 or equivalent"),
0x0F => Some("EEPROM, 24C64 or equivalent"),
0x10 => {
Some("FRU Inventory Device behind management controller (Read/Write FRU at LUN != 00b)")
}
0x11 => Some("Reserved"),
0x12 => Some("Reserved"),
0x13 => Some("Reserved"),
0x14 => Some("PCF8570 256 byte RAM or equivalent"),
0x15 => Some("PCF8573 clock/calendar or equivalent"),
0x16 => Some("PCF8574A I/O port or equivalent"),
0x17 => Some("PCF8583 clock/calendar or equivalent"),
0x18 => Some("PCF8593 clock/calendar or equivalent"),
0x19 => Some("Clock calendar, type not specified"),
0x1A => Some("PCF8591 A/D, D/A Converter or equivalent"),
0x1B => Some("I/O port, specific device not specified"),
0x1C => Some("A/D Converter, specific device not specified"),
0x1D => Some("D/A Converter, specific device not specified"),
0x1E => Some("A/D, D/A Converter, specific device not specified"),
0x1F => Some("LCD controller/Driver, specific device not specified"),
0x20 => Some("Core Logic (chip set) device, specific device not specified"),
0x21 => Some("LMC6874 Intelligent Battery controller, or equivalent"),
0x22 => Some("Intelligent Battery controller, specific device not specified"),
0x23 => Some("Combo Management ASIC, specific device not specified"),
0x24 => Some("Maxim 1617 temperature sensor"),
0xBF => Some("Other/unspecified device"),
0xC0..=0xFF => Some("OEM specified device"),
_ => Some("Reserved"),
}
}
fn device_type_modifier_name(device_type: u8, modifier: u8) -> Option<&'static str> {
match device_type {
0x05 => match modifier {
0x00 => Some("Heceta 1 (LM78)"),
0x01 => Some("Heceta 2 (LM79)"),
0x02 => Some("LM80"),
0x03 => Some("Heceta 3 (LM81/ADM9240/DS1780)"),
0x04 => Some("Heceta 4"),
0x05 => Some("Heceta 5"),
_ => Some("Reserved"),
},
0x08..=0x0F => eeprom_modifier_name(modifier),
0x10 => fru_behind_mc_modifier_name(modifier),
0xBF => modifier_unspecified_or_none(modifier),
0xC0..=0xFF => Some("OEM specific"),
_ => modifier_unspecified_or_none(modifier),
}
}
fn modifier_unspecified_or_none(modifier: u8) -> Option<&'static str> {
if modifier == 0x00 {
Some("Unspecified")
} else {
None
}
}
fn eeprom_modifier_name(modifier: u8) -> Option<&'static str> {
match modifier {
0x00 => Some("Unspecified"),
0x01 => Some("DIMM Memory ID"),
0x02 => Some("IPMI FRU Inventory"),
0x03 => Some("System Processor Cartridge FRU/PIROM"),
_ => Some("Reserved"),
}
}
fn fru_behind_mc_modifier_name(modifier: u8) -> Option<&'static str> {
match modifier {
0x00 => Some("IPMI FRU Inventory"),
0x01 => Some("DIMM Memory ID"),
0x02 => Some("IPMI FRU Inventory"),
0x03 => Some("System Processor Cartridge FRU/PIROM"),
0xFF => Some("Unspecified"),
_ => Some("Reserved"),
}
}