Skip to content

Commit 9e3dda5

Browse files
committed
woag
1 parent 16c901a commit 9e3dda5

File tree

3 files changed

+109
-28
lines changed

3 files changed

+109
-28
lines changed

nexus/fm/src/alert/power_shelf.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub struct PscPsu {
4040
pub psc_id: VpdIdentity,
4141
pub psc_slot: u16,
4242
pub psu_id: PsuIdentity,
43-
pub psu_slot: u16,
43+
pub psu_slot: Option<u16>,
4444
}
4545

4646
#[derive(Debug, Serialize, Deserialize, JsonSchema)]

nexus/fm/src/de/power_shelf.rs

Lines changed: 106 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
//! Power shelf diagnosis
66
77
use crate::SitrepBuilder;
8-
use crate::alert
9-
use nexus_types::fm::AlertRequest;
8+
use crate::alert;
109
use nexus_types::fm::DiagnosisEngine;
1110
use nexus_types::fm::Ereport;
1211
use nexus_types::fm::ereport;
1312
use nexus_types::inventory::SpType;
13+
use serde::de::DeserializeOwned;
14+
use serde_json::Value;
1415
use std::sync::Arc;
1516

1617
pub fn diagnose(
@@ -19,7 +20,9 @@ pub fn diagnose(
1920
) -> anyhow::Result<()> {
2021
for ereport in new_ereports {
2122
// Skip non-power shelf reports
22-
let ereport::Reporter::Sp { sp_type: SpType::Power, slot, } = ereport.reporter else {
23+
let ereport::Reporter::Sp { sp_type: SpType::Power, slot } =
24+
ereport.reporter
25+
else {
2326
continue;
2427
};
2528

@@ -29,31 +32,30 @@ pub fn diagnose(
2932
match ereport.data.class.as_deref() {
3033
// PSU inserted
3134
Some("hw.insert.psu") => {
35+
let psc_psu = extract_psc_psu(&ereport, slot, &sitrep.log);
3236
let mut case = sitrep.open_case(DiagnosisEngine::PowerShelf)?;
3337
case.add_ereport(ereport);
34-
case.comment = "PSU inserted".to_string();
35-
let psu_id = match ereport.get("fruid") {
36-
Some(serde_json::Value::Object(fruid)) => {
37-
todo!()
38-
},
39-
None => {
40-
todo!()
41-
}
42-
};
43-
case.request_alert(alert::power_shelf::PsuInserted::V0 {
44-
psc_psu: alert::power_shelf::PscPsu {
45-
psc_id: alert::VpdIdentity {
46-
serial_number: ereport.serial_number.clone(),
47-
revision: ereport.report.get("baseboard_rev").map(ToString::to_string),
48-
part_number: ereport.part_number.clone(),
49-
},
50-
psc_slot: slot,
51-
psu_id,
52-
psu_slot: ereport.report.get("slot").map(|s| todo!()),
53-
}
54-
})
38+
case.comment =
39+
format!("PSC {slot} PSU {:?} inserted", psc_psu.psu_slot);
40+
case.request_alert(&alert::power_shelf::PsuInserted::V0 {
41+
psc_psu,
42+
})?;
43+
// Nothing else to do at this time.
44+
case.close();
45+
}
46+
Some("hw.remove.psu") => {
47+
let psc_psu = extract_psc_psu(&ereport, slot, &sitrep.log);
48+
let mut case = sitrep.open_case(DiagnosisEngine::PowerShelf)?;
49+
case.add_ereport(ereport);
50+
case.comment =
51+
format!("PSC {slot} PSU {:?} removed", psc_psu.psu_slot);
52+
case.request_alert(&alert::power_shelf::PsuRemoved::V0 {
53+
psc_psu,
54+
})?;
55+
56+
// Nothing else to do at this time.
57+
case.close();
5558
}
56-
Some("hw.remove.psu") => {}
5759
Some(unknown) => {
5860
slog::warn!(
5961
&sitrep.log,
@@ -74,3 +76,82 @@ pub fn diagnose(
7476

7577
Ok(())
7678
}
79+
80+
fn extract_psc_psu(
81+
ereport: &Ereport,
82+
psc_slot: u16,
83+
log: &slog::Logger,
84+
) -> alert::power_shelf::PscPsu {
85+
let psc_id = extract_psc_id(ereport, log);
86+
let psu_id = extract_psu_id(ereport, log);
87+
let psu_slot = grab_json_value(ereport, "slot", &ereport.report, log);
88+
alert::power_shelf::PscPsu { psc_id, psc_slot, psu_id, psu_slot }
89+
}
90+
91+
fn extract_psc_id(ereport: &Ereport, log: &slog::Logger) -> alert::VpdIdentity {
92+
let serial_number = ereport.serial_number.clone();
93+
let revision =
94+
grab_json_value(ereport, "baseboard_rev", &ereport.report, log);
95+
let part_number = ereport.part_number.clone();
96+
alert::VpdIdentity { serial_number, revision, part_number }
97+
}
98+
99+
fn extract_psu_id(
100+
ereport: &Ereport,
101+
log: &slog::Logger,
102+
) -> alert::power_shelf::PsuIdentity {
103+
// These are the same field names that Hubris uses in the ereport. See:
104+
// https://github.com/oxidecomputer/hubris/blob/ec18e4f11aaa14600c61f67335c32b250ef38269/drv/psc-seq-server/src/main.rs#L1107-L1117
105+
#[derive(serde::Deserialize, Default)]
106+
struct Fruid {
107+
mfr: Option<String>,
108+
mpn: Option<String>,
109+
serial: Option<String>,
110+
fw_rev: Option<String>,
111+
}
112+
113+
let Fruid { mfr, mpn, serial, fw_rev } =
114+
grab_json_value(ereport, "fruid", &ereport.report, log)
115+
.unwrap_or_default();
116+
117+
alert::power_shelf::PsuIdentity {
118+
serial_number: serial,
119+
part_number: mpn,
120+
firmware_revision: fw_rev,
121+
manufacturer: mfr,
122+
}
123+
}
124+
125+
fn grab_json_value<T: DeserializeOwned>(
126+
ereport: &Ereport,
127+
key: &str,
128+
obj: &Value,
129+
log: &slog::Logger,
130+
) -> Option<T> {
131+
let v = match obj.get("key") {
132+
Some(v) => v,
133+
None => {
134+
slog::warn!(
135+
log,
136+
"expected ereport to contain a '{key}' field";
137+
"ereport_id" => %ereport.id,
138+
"ereport_class" => ?ereport.class,
139+
);
140+
return None;
141+
}
142+
};
143+
match serde_json::from_value(v.clone()) {
144+
Ok(v) => Some(v),
145+
Err(e) => {
146+
slog::warn!(
147+
log,
148+
"expected ereport '{key}' field to deserialize as a {}",
149+
std::any::type_name::<T>();
150+
"ereport_id" => %ereport.id,
151+
"ereport_class" => ?ereport.class,
152+
"error" => %e,
153+
);
154+
None
155+
}
156+
}
157+
}

nexus/fm/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,10 @@ impl CaseBuilder {
167167
Ok(())
168168
}
169169

170-
pub fn close(&mut self, log: &slog::Logger) {
170+
pub fn close(&mut self) {
171171
self.case.time_closed = Some(Utc::now());
172172

173-
slog::info!(log, "case closed");
173+
slog::info!(&self.log, "case closed");
174174
}
175175

176176
pub fn add_ereport(&mut self, report: &Arc<fm::Ereport>) {

0 commit comments

Comments
 (0)