Skip to content

Commit 34cae77

Browse files
committed
quick case pretty printer
1 parent 9a39748 commit 34cae77

File tree

2 files changed

+259
-6
lines changed

2 files changed

+259
-6
lines changed

dev-tools/omdb/src/bin/omdb/db/sitrep.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,5 +345,12 @@ async fn cmd_db_sitrep_show(
345345
}
346346
}
347347

348+
if !cases.is_empty() {
349+
println!("\n{:-<80}\n", "== CASES");
350+
for case in cases {
351+
println!("{}", case.display_indented(4));
352+
}
353+
}
354+
348355
Ok(())
349356
}

nexus/types/src/fm/case.rs

Lines changed: 252 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ use crate::fm::Ereport;
88
use crate::inventory::SpType;
99
use chrono::{DateTime, Utc};
1010
use iddqd::{IdOrdItem, IdOrdMap};
11-
use omicron_uuid_kinds::{
12-
CaseUuid, CollectionUuid, OmicronZoneUuid, SitrepUuid,
13-
};
11+
use omicron_uuid_kinds::{CaseUuid, SitrepUuid};
1412
use serde::{Deserialize, Serialize};
13+
use std::fmt;
1514
use std::sync::Arc;
1615

1716
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
@@ -25,10 +24,8 @@ pub struct Case {
2524

2625
pub de: DiagnosisEngine,
2726

28-
pub ereports: IdOrdMap<Arc<Ereport>>,
29-
27+
pub ereports: IdOrdMap<CaseEreport>,
3028
pub alerts_requested: IdOrdMap<AlertRequest>,
31-
3229
pub impacted_sp_slots: IdOrdMap<ImpactedSpSlot>,
3330

3431
pub comment: String,
@@ -38,6 +35,16 @@ impl Case {
3835
pub fn is_open(&self) -> bool {
3936
self.time_closed.is_none()
4037
}
38+
39+
pub fn display_indented(&self, indent: usize) -> impl fmt::Display + '_ {
40+
DisplayCase { case: self, indent }
41+
}
42+
}
43+
44+
impl fmt::Display for Case {
45+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46+
self.display_indented(0).fmt(f)
47+
}
4148
}
4249

4350
impl IdOrdItem for Case {
@@ -49,6 +56,22 @@ impl IdOrdItem for Case {
4956
iddqd::id_upcast!();
5057
}
5158

59+
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
60+
pub struct CaseEreport {
61+
pub ereport: Arc<Ereport>,
62+
pub assigned_sitrep_id: SitrepUuid,
63+
pub comment: String,
64+
}
65+
66+
impl IdOrdItem for CaseEreport {
67+
type Key<'a> = <Arc<Ereport> as IdOrdItem>::Key<'a>;
68+
fn key(&self) -> Self::Key<'_> {
69+
self.ereport.key()
70+
}
71+
72+
iddqd::id_upcast!();
73+
}
74+
5275
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
5376
pub struct ImpactedSpSlot {
5477
pub sp_type: SpType,
@@ -65,3 +88,226 @@ impl IdOrdItem for ImpactedSpSlot {
6588

6689
iddqd::id_upcast!();
6790
}
91+
92+
struct DisplayCase<'a> {
93+
case: &'a Case,
94+
indent: usize,
95+
}
96+
97+
impl fmt::Display for DisplayCase<'_> {
98+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99+
const BULLET: &str = "* ";
100+
const LIST_INDENT: usize = 4;
101+
102+
let &Self {
103+
case:
104+
Case {
105+
ref id,
106+
ref created_sitrep_id,
107+
ref time_created,
108+
ref closed_sitrep_id,
109+
ref time_closed,
110+
ref de,
111+
ref ereports,
112+
ref alerts_requested,
113+
ref impacted_sp_slots,
114+
ref comment,
115+
},
116+
indent,
117+
} = self;
118+
writeln!(
119+
f,
120+
"{:>indent$}case: {id:?}",
121+
if indent > 0 { BULLET } else { "" }
122+
)?;
123+
writeln!(f, "{:>indent$}comment: {comment}", "")?;
124+
writeln!(f, "{:>indent$}diagnosis engine: {de}", "")?;
125+
writeln!(f, "{:>indent$}created in sitrep: {created_sitrep_id}", "")?;
126+
writeln!(f, "{:>indent$} at: {time_created}", "")?;
127+
if let Some(closed_id) = closed_sitrep_id {
128+
writeln!(f, "{:>indent$}closed in sitrep: {closed_id}", "")?;
129+
if let Some(time_closed) = time_closed {
130+
writeln!(f, "{:>indent$} at: {time_closed}", "")?;
131+
} else {
132+
writeln!(f, "{:>indent$} at: <MISSING TIME CLOSED>", "")?;
133+
}
134+
}
135+
136+
if !ereports.is_empty() {
137+
writeln!(f, "\n{:>indent$}ereports:", "")?;
138+
let indent = indent + LIST_INDENT;
139+
for CaseEreport { ereport, assigned_sitrep_id, comment } in ereports
140+
{
141+
writeln!(f, "{BULLET:>indent$}{}", ereport.id())?;
142+
writeln!(f, "{:>indent$}class: {:?}", "", ereport.class)?;
143+
writeln!(f, "{:>indent$}reporter: {}", "", ereport.reporter)?;
144+
writeln!(
145+
f,
146+
"{:>indent$}added in sitrep: {assigned_sitrep_id}",
147+
""
148+
)?;
149+
writeln!(f, "{:>indent$}comment: {comment}", "")?;
150+
}
151+
}
152+
153+
if !impacted_sp_slots.is_empty() {
154+
writeln!(f, "\n{:>indent$}SP slots impacted:", "")?;
155+
let indent = indent + LIST_INDENT;
156+
for ImpactedSpSlot { sp_type, slot, created_sitrep_id, comment } in
157+
impacted_sp_slots
158+
{
159+
writeln!(f, "{BULLET:>indent$}{sp_type:<6} {slot:02}")?;
160+
writeln!(
161+
f,
162+
"{:>indent$}added in sitrep: {created_sitrep_id}",
163+
""
164+
)?;
165+
writeln!(f, "{:>indent$}comment: {comment}", "")?;
166+
}
167+
}
168+
169+
if !alerts_requested.is_empty() {
170+
writeln!(f, "\n{:>indent$}alerts requested:", "")?;
171+
let indent = indent + LIST_INDENT;
172+
for AlertRequest { id, class, requested_sitrep_id, .. } in
173+
alerts_requested
174+
{
175+
writeln!(f, "{BULLET:>indent$}{id:?}")?;
176+
writeln!(f, "{:>indent$}class: {class:?}", "")?;
177+
writeln!(
178+
f,
179+
"{:>indent$}requested in sitrep: {requested_sitrep_id}",
180+
""
181+
)?;
182+
}
183+
}
184+
185+
writeln!(f)?;
186+
187+
Ok(())
188+
}
189+
}
190+
191+
#[cfg(test)]
192+
mod tests {
193+
use super::*;
194+
use crate::fm::{AlertClass, AlertRequest, DiagnosisEngine};
195+
use chrono::Utc;
196+
use ereport_types::{Ena, EreportId};
197+
use omicron_uuid_kinds::{
198+
AlertUuid, CaseUuid, EreporterRestartUuid, OmicronZoneUuid, SitrepUuid,
199+
};
200+
use std::sync::Arc;
201+
202+
#[test]
203+
fn test_case_display() {
204+
// Create UUIDs for the case
205+
let case_id = CaseUuid::new_v4();
206+
let created_sitrep_id = SitrepUuid::new_v4();
207+
let closed_sitrep_id = SitrepUuid::new_v4();
208+
let time_created = Utc::now();
209+
let time_closed = Utc::now();
210+
211+
// Create some ereports
212+
let mut ereports = IdOrdMap::new();
213+
214+
let ereport1 = CaseEreport {
215+
ereport: Arc::new(Ereport {
216+
data: crate::fm::ereport::EreportData {
217+
id: EreportId {
218+
restart_id: EreporterRestartUuid::new_v4(),
219+
ena: Ena::from(2u64),
220+
},
221+
time_collected: time_created,
222+
collector_id: OmicronZoneUuid::new_v4(),
223+
serial_number: Some("BRM6900420".to_string()),
224+
part_number: Some("913-0000037".to_string()),
225+
class: Some("hw.pwr.remove.psu".to_string()),
226+
report: serde_json::json!({}),
227+
},
228+
reporter: crate::fm::ereport::Reporter::Sp {
229+
sp_type: SpType::Power,
230+
slot: 0,
231+
},
232+
}),
233+
assigned_sitrep_id: created_sitrep_id,
234+
comment: "PSU removed".to_string(),
235+
};
236+
ereports.insert_unique(ereport1).unwrap();
237+
238+
let ereport2 = CaseEreport {
239+
ereport: Arc::new(Ereport {
240+
data: crate::fm::ereport::EreportData {
241+
id: EreportId {
242+
restart_id: EreporterRestartUuid::new_v4(),
243+
ena: Ena::from(3u64),
244+
},
245+
time_collected: time_created,
246+
collector_id: OmicronZoneUuid::new_v4(),
247+
serial_number: Some("BRM6900420".to_string()),
248+
part_number: Some("913-0000037".to_string()),
249+
class: Some("hw.pwr.insert.psu".to_string()),
250+
report: serde_json::json!({"link": "eth0", "status": "down"}),
251+
},
252+
reporter: crate::fm::ereport::Reporter::Sp {
253+
sp_type: SpType::Power,
254+
slot: 0,
255+
},
256+
}),
257+
assigned_sitrep_id: closed_sitrep_id,
258+
comment: "PSU inserted, closing this case".to_string(),
259+
};
260+
ereports.insert_unique(ereport2).unwrap();
261+
262+
// Create some alerts
263+
let mut alerts_requested = IdOrdMap::new();
264+
265+
let alert1 = AlertRequest {
266+
id: AlertUuid::new_v4(),
267+
class: AlertClass::PsuRemoved,
268+
payload: serde_json::json!({}),
269+
requested_sitrep_id: created_sitrep_id,
270+
};
271+
alerts_requested.insert_unique(alert1).unwrap();
272+
273+
let alert2 = AlertRequest {
274+
id: AlertUuid::new_v4(),
275+
class: AlertClass::PsuInserted,
276+
payload: serde_json::json!({}),
277+
requested_sitrep_id: closed_sitrep_id,
278+
};
279+
alerts_requested.insert_unique(alert2).unwrap();
280+
281+
let mut impacted_sp_slots = IdOrdMap::new();
282+
let slot2 = ImpactedSpSlot {
283+
sp_type: SpType::Power,
284+
slot: 0,
285+
created_sitrep_id,
286+
comment: "Power shelf 0 reduced redundancy".to_string(),
287+
};
288+
impacted_sp_slots.insert_unique(slot2).unwrap();
289+
290+
// Create the case
291+
let case = Case {
292+
id: case_id,
293+
created_sitrep_id,
294+
time_created,
295+
closed_sitrep_id: Some(closed_sitrep_id),
296+
time_closed: Some(time_closed),
297+
de: DiagnosisEngine::PowerShelf,
298+
ereports,
299+
alerts_requested,
300+
impacted_sp_slots,
301+
comment: "Power shelf rectifier added and removed here :-)"
302+
.to_string(),
303+
};
304+
305+
eprintln!("example case display:");
306+
eprintln!("=====================");
307+
eprintln!("{case}");
308+
309+
eprintln!("example case display (indented by 4):");
310+
eprintln!("======================================");
311+
eprintln!("{}", case.display_indented(4));
312+
}
313+
}

0 commit comments

Comments
 (0)