use std::collections::HashMap;
use std::time::Duration;
const LEGACY_POWER_JSON_POINTER: &str = "/PowerControl/0/PowerConsumedWatts";
const ENVIRONMENT_METRICS_JSON_POINTER: &str = "/PowerWatts/Reading";
#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RedfishSchema {
LegacyPower,
EnvironmentMetrics,
}
impl RedfishSchema {
#[must_use]
pub const fn json_pointer(self) -> &'static str {
match self {
Self::LegacyPower => LEGACY_POWER_JSON_POINTER,
Self::EnvironmentMetrics => ENVIRONMENT_METRICS_JSON_POINTER,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RedfishEndpoint {
pub url: String,
pub schema: RedfishSchema,
}
pub const MIN_SCRAPE_INTERVAL_SECS: u64 = 15;
pub const MAX_SCRAPE_INTERVAL_SECS: u64 = 3600;
#[derive(Clone)]
pub struct RedfishConfig {
pub endpoints: HashMap<String, RedfishEndpoint>,
pub scrape_interval: Duration,
pub service_mappings: HashMap<String, String>,
pub ca_bundle_path: Option<String>,
pub auth_header: Option<String>,
}
impl std::fmt::Debug for RedfishConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RedfishConfig")
.field("endpoints", &self.endpoints)
.field("scrape_interval", &self.scrape_interval)
.field("service_mappings", &self.service_mappings)
.field("ca_bundle_path", &self.ca_bundle_path)
.field(
"auth_header",
&self.auth_header.as_ref().map(|_| "[REDACTED]"),
)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_config() -> RedfishConfig {
let mut endpoints = HashMap::new();
endpoints.insert(
"chassis-1".to_string(),
RedfishEndpoint {
url: "https://bmc/redfish/v1/Chassis/1/Power".to_string(),
schema: RedfishSchema::LegacyPower,
},
);
let mut mappings = HashMap::new();
mappings.insert("order-svc".to_string(), "chassis-1".to_string());
RedfishConfig {
endpoints,
scrape_interval: Duration::from_mins(1),
service_mappings: mappings,
ca_bundle_path: None,
auth_header: Some("Authorization: Basic super-secret-do-not-log".to_string()),
}
}
#[test]
fn debug_impl_redacts_auth_header() {
let cfg = sample_config();
crate::test_helpers::assert_debug_redacts_secret!(&cfg, "super-secret-do-not-log");
}
#[test]
fn debug_impl_preserves_non_secret_fields() {
let cfg = sample_config();
let dbg = format!("{cfg:?}");
assert!(dbg.contains("chassis-1"));
assert!(dbg.contains("https://bmc/redfish/v1/Chassis/1/Power"));
assert!(dbg.contains("LegacyPower"));
assert!(dbg.contains("order-svc"));
}
#[test]
fn schema_dispatches_to_canonical_json_pointer() {
assert_eq!(
RedfishSchema::LegacyPower.json_pointer(),
"/PowerControl/0/PowerConsumedWatts"
);
assert_eq!(
RedfishSchema::EnvironmentMetrics.json_pointer(),
"/PowerWatts/Reading"
);
}
}