Skip to content

Commit bdb742b

Browse files
committed
put thing into db
1 parent b8d6cc8 commit bdb742b

File tree

5 files changed

+200
-24
lines changed

5 files changed

+200
-24
lines changed

nexus/db-model/src/fm/case.rs

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
//! Fault management cases.
66
7+
use super::AlertRequest;
78
use super::DiagnosisEngine;
89
use crate::DbTypedUuid;
910
use crate::SpMgsSlot;
@@ -13,7 +14,10 @@ use chrono::{DateTime, Utc};
1314
use nexus_db_schema::schema::{
1415
fm_case, fm_case_impacts_sp_slot, fm_ereport_in_case,
1516
};
16-
use omicron_uuid_kinds::{CaseKind, EreporterRestartKind, SitrepKind};
17+
use nexus_types::fm;
18+
use omicron_uuid_kinds::{
19+
CaseKind, EreporterRestartKind, SitrepKind, SitrepUuid,
20+
};
1721

1822
#[derive(Queryable, Insertable, Clone, Debug, Selectable)]
1923
#[diesel(table_name = fm_case)]
@@ -52,3 +56,94 @@ pub struct CaseImpactsSp {
5256
pub created_sitrep_id: DbTypedUuid<SitrepKind>,
5357
pub comment: String,
5458
}
59+
60+
#[derive(Clone, Debug)]
61+
pub struct Case {
62+
pub metadata: CaseMetadata,
63+
pub ereports: Vec<CaseEreport>,
64+
pub impacted_sp_slots: Vec<CaseImpactsSp>,
65+
pub alerts_requested: Vec<AlertRequest>,
66+
}
67+
68+
impl Case {
69+
pub fn from_sitrep(sitrep_id: SitrepUuid, case: fm::Case) -> Self {
70+
let sitrep_id = sitrep_id.into();
71+
let case_id = case.id.into();
72+
let ereports = case
73+
.ereports
74+
.into_iter()
75+
.map(
76+
|fm::case::CaseEreport {
77+
ereport,
78+
assigned_sitrep_id,
79+
comment,
80+
}| {
81+
let restart_id = ereport.id().restart_id.into();
82+
let ena = ereport.id().ena.into();
83+
CaseEreport {
84+
case_id,
85+
restart_id,
86+
ena,
87+
comment,
88+
sitrep_id,
89+
assigned_sitrep_id: assigned_sitrep_id.into(),
90+
}
91+
},
92+
)
93+
.collect();
94+
let impacted_sp_slots = case
95+
.impacted_sp_slots
96+
.into_iter()
97+
.map(
98+
|fm::case::ImpactedSpSlot {
99+
sp_type,
100+
slot,
101+
comment,
102+
created_sitrep_id,
103+
}| CaseImpactsSp {
104+
sitrep_id,
105+
case_id,
106+
sp_type: sp_type.into(),
107+
sp_slot: SpMgsSlot::from(slot as u16),
108+
created_sitrep_id: created_sitrep_id.into(),
109+
comment,
110+
},
111+
)
112+
.collect();
113+
let alerts_requested = case
114+
.alerts_requested
115+
.into_iter()
116+
.map(
117+
|fm::AlertRequest {
118+
id,
119+
class,
120+
payload,
121+
requested_sitrep_id,
122+
}| AlertRequest {
123+
sitrep_id,
124+
case_id,
125+
class: class.into(),
126+
id: id.into(),
127+
payload,
128+
requested_sitrep_id: requested_sitrep_id.into(),
129+
},
130+
)
131+
.collect();
132+
133+
Self {
134+
metadata: CaseMetadata {
135+
id: case_id,
136+
sitrep_id,
137+
de: case.de.into(),
138+
created_sitrep_id: case.created_sitrep_id.into(),
139+
time_created: case.time_created.into(),
140+
time_closed: case.time_closed.map(Into::into),
141+
closed_sitrep_id: case.closed_sitrep_id.map(Into::into),
142+
comment: case.comment,
143+
},
144+
ereports,
145+
impacted_sp_slots,
146+
alerts_requested,
147+
}
148+
}
149+
}

nexus/db-queries/src/db/datastore/fm.rs

Lines changed: 98 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use nexus_db_errors::public_error_from_diesel;
3333
use nexus_db_lookup::DbConnection;
3434
use nexus_db_schema::schema::fm_alert_request::dsl as alert_req_dsl;
3535
use nexus_db_schema::schema::fm_case::dsl as case_dsl;
36+
use nexus_db_schema::schema::fm_case_impacts_sp_slot::dsl as impacted_sp_dsl;
3637
use nexus_db_schema::schema::fm_ereport_in_case::dsl as case_ereport_dsl;
3738
use nexus_db_schema::schema::fm_sitrep::dsl as sitrep_dsl;
3839
use nexus_db_schema::schema::fm_sitrep_history::dsl as history_dsl;
@@ -130,7 +131,8 @@ impl DataStore {
130131
Ok(Some((version, sitrep)))
131132
}
132133

133-
/// Reads the entire content of the sitrep with the provided ID, if one exists.
134+
/// Reads the entire content of the sitrep with the provided ID, if one
135+
/// exists.
134136
pub async fn fm_sitrep_read(
135137
&self,
136138
opctx: &OpContext,
@@ -356,27 +358,105 @@ impl DataStore {
356358
pub async fn fm_sitrep_insert(
357359
&self,
358360
opctx: &OpContext,
359-
sitrep: &Sitrep,
361+
sitrep: Sitrep,
360362
) -> Result<(), InsertSitrepError> {
361363
let conn = self.pool_connection_authorized(opctx).await?;
362364

363365
// TODO(eliza): there should probably be an authz object for the fm sitrep?
364366
opctx.authorize(authz::Action::Modify, &authz::FLEET).await?;
365367

368+
let sitrep_id = sitrep.id();
369+
366370
// Create the sitrep metadata record.
371+
//
372+
// NOTE: we must insert this record before anything else, because it's
373+
// how orphaned sitreps are found when performing garbage collection.
374+
// Were we to first insert some other records and insert the metadata
375+
// record *last*, we could die when we have inserted some sitrep data
376+
// but have yet to create the metadata record. If this occurs, those
377+
// records could not be easily found by the garbage collection task.
378+
// Those (unused) records would then be permanently leaked without
379+
// manual human intervention to delete them.
367380
diesel::insert_into(sitrep_dsl::fm_sitrep)
368-
.values(model::SitrepMetadata::from(sitrep.metadata.clone()))
381+
.values(model::SitrepMetadata::from(sitrep.metadata))
369382
.execute_async(&*conn)
370383
.await
371384
.map_err(|e| {
372385
public_error_from_diesel(e, ErrorHandler::Server)
373386
.internal_context("failed to insert sitrep metadata record")
374387
})?;
375388

376-
// TODO(eliza): other sitrep records would be inserted here...
389+
// Create case records.
390+
let mut cases = Vec::with_capacity(sitrep.cases.len());
391+
for case in sitrep.cases {
392+
// TODO(eliza): some of this could be done in parallel using a
393+
// `ParallelTaskSet`, if the time it takes to insert a sitrep were
394+
// to become important?
395+
let model::fm::Case {
396+
metadata,
397+
ereports,
398+
alerts_requested,
399+
impacted_sp_slots,
400+
} = model::fm::Case::from_sitrep(sitrep_id, case);
401+
402+
if !ereports.is_empty() {
403+
diesel::insert_into(case_ereport_dsl::fm_ereport_in_case)
404+
.values(ereports)
405+
.execute_async(&*conn)
406+
.await
407+
.map_err(|e| {
408+
public_error_from_diesel(e, ErrorHandler::Server)
409+
.internal_context(format!(
410+
"failed to insert ereport records for case {}",
411+
metadata.id
412+
))
413+
})?;
414+
}
415+
416+
if !alerts_requested.is_empty() {
417+
diesel::insert_into(alert_req_dsl::fm_alert_request)
418+
.values(alerts_requested)
419+
.execute_async(&*conn)
420+
.await
421+
.map_err(|e| {
422+
public_error_from_diesel(e, ErrorHandler::Server)
423+
.internal_context(format!(
424+
"failed to insert ereport alert requests for case {}",
425+
metadata.id
426+
))
427+
})?;
428+
}
429+
430+
if !impacted_sp_slots.is_empty() {
431+
diesel::insert_into(impacted_sp_dsl::fm_case_impacts_sp_slot)
432+
.values(impacted_sp_slots)
433+
.execute_async(&*conn)
434+
.await
435+
.map_err(|e| {
436+
public_error_from_diesel(e, ErrorHandler::Server)
437+
.internal_context(format!(
438+
"failed to insert impacted SP slots for case {}",
439+
metadata.id
440+
))
441+
})?;
442+
}
443+
444+
cases.push(metadata);
445+
}
446+
447+
if !cases.is_empty() {
448+
diesel::insert_into(case_dsl::fm_case)
449+
.values(cases)
450+
.execute_async(&*conn)
451+
.await
452+
.map_err(|e| {
453+
public_error_from_diesel(e, ErrorHandler::Server)
454+
.internal_context("failed to insert case records")
455+
})?;
456+
}
377457

378458
// Now, try to make the sitrep current.
379-
let query = Self::insert_sitrep_version_query(sitrep.id());
459+
let query = Self::insert_sitrep_version_query(sitrep_id);
380460
query
381461
.execute_async(&*conn)
382462
.await
@@ -387,7 +467,7 @@ impl DataStore {
387467
) if info.message()
388468
== Self::PARENT_NOT_CURRENT_ERROR_MESSAGE =>
389469
{
390-
InsertSitrepError::ParentNotCurrent(sitrep.id())
470+
InsertSitrepError::ParentNotCurrent(sitrep_id)
391471
}
392472
err => {
393473
let err =
@@ -943,7 +1023,7 @@ mod tests {
9431023
cases: Default::default(),
9441024
};
9451025

946-
datastore.fm_sitrep_insert(&opctx, &sitrep).await.unwrap();
1026+
datastore.fm_sitrep_insert(&opctx, sitrep.clone()).await.unwrap();
9471027

9481028
let current = datastore
9491029
.fm_sitrep_read_current(&opctx)
@@ -962,7 +1042,7 @@ mod tests {
9621042

9631043
// Trying to insert the same sitrep again should fail.
9641044
let err =
965-
datastore.fm_sitrep_insert(&opctx, &sitrep).await.unwrap_err();
1045+
datastore.fm_sitrep_insert(&opctx, sitrep.clone()).await.unwrap_err();
9661046
assert!(err.to_string().contains("duplicate key"));
9671047

9681048
// Clean up.
@@ -989,7 +1069,7 @@ mod tests {
9891069
},
9901070
cases: Default::default(),
9911071
};
992-
datastore.fm_sitrep_insert(&opctx, &sitrep1).await.unwrap();
1072+
datastore.fm_sitrep_insert(&opctx, sitrep1.clone()).await.unwrap();
9931073

9941074
// Create a second sitrep with the first as parent
9951075
let sitrep2 = nexus_types::fm::Sitrep {
@@ -1003,7 +1083,7 @@ mod tests {
10031083
},
10041084
cases: Default::default(),
10051085
};
1006-
datastore.fm_sitrep_insert(&opctx, &sitrep2).await.expect(
1086+
datastore.fm_sitrep_insert(&opctx, sitrep2.clone()).await.expect(
10071087
"inserting a sitrep whose parent is current should succeed",
10081088
);
10091089

@@ -1044,7 +1124,7 @@ mod tests {
10441124
},
10451125
cases: Default::default(),
10461126
};
1047-
datastore.fm_sitrep_insert(&opctx, &sitrep1).await.unwrap();
1127+
datastore.fm_sitrep_insert(&opctx, sitrep1.clone()).await.unwrap();
10481128

10491129
// Try to insert a sitrep with a non-existent parent ID
10501130
let nonexistent_id = SitrepUuid::new_v4();
@@ -1060,7 +1140,7 @@ mod tests {
10601140
cases: Default::default(),
10611141
};
10621142

1063-
let result = datastore.fm_sitrep_insert(&opctx, &sitrep2).await;
1143+
let result = datastore.fm_sitrep_insert(&opctx, sitrep2).await;
10641144

10651145
// Should fail with ParentNotCurrent error
10661146
match result {
@@ -1094,7 +1174,7 @@ mod tests {
10941174
},
10951175
cases: Default::default(),
10961176
};
1097-
datastore.fm_sitrep_insert(&opctx, &sitrep1).await.unwrap();
1177+
datastore.fm_sitrep_insert(&opctx, sitrep1.clone()).await.unwrap();
10981178

10991179
// Create a second sitrep with the first as parent
11001180
let sitrep2 = nexus_types::fm::Sitrep {
@@ -1108,7 +1188,7 @@ mod tests {
11081188
},
11091189
cases: Default::default(),
11101190
};
1111-
datastore.fm_sitrep_insert(&opctx, &sitrep2).await.unwrap();
1191+
datastore.fm_sitrep_insert(&opctx, sitrep2.clone()).await.unwrap();
11121192

11131193
// Try to create a third sitrep with sitrep1 (outdated) as parent.
11141194
// This should fail, as sitrep2 is now the current sitrep.
@@ -1123,7 +1203,7 @@ mod tests {
11231203
},
11241204
cases: Default::default(),
11251205
};
1126-
let result = datastore.fm_sitrep_insert(&opctx, &sitrep3).await;
1206+
let result = datastore.fm_sitrep_insert(&opctx, sitrep3.clone()).await;
11271207

11281208
// Should fail with ParentNotCurrent error
11291209
match result {
@@ -1165,7 +1245,7 @@ mod tests {
11651245
cases: Default::default(),
11661246
};
11671247
datastore
1168-
.fm_sitrep_insert(&opctx, &sitrep1)
1248+
.fm_sitrep_insert(&opctx, sitrep1.clone())
11691249
.await
11701250
.expect("inserting initial sitrep should succeed");
11711251

@@ -1206,7 +1286,7 @@ mod tests {
12061286
cases: Default::default(),
12071287
};
12081288
datastore
1209-
.fm_sitrep_insert(&opctx, &sitrep2)
1289+
.fm_sitrep_insert(&opctx, sitrep2.clone())
12101290
.await
12111291
.expect("inserting child sitrep should succeed");
12121292

@@ -1269,7 +1349,7 @@ mod tests {
12691349
},
12701350
cases: Default::default(),
12711351
};
1272-
match datastore.fm_sitrep_insert(&opctx, &sitrep).await {
1352+
match datastore.fm_sitrep_insert(&opctx, sitrep).await {
12731353
Ok(_) => {
12741354
panic!("inserting sitrep v{v} orphan {i} should not succeed")
12751355
}

nexus/src/app/background/tasks/fm_sitrep_gc.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ mod tests {
155155
cases: Default::default(),
156156
};
157157
datastore
158-
.fm_sitrep_insert(&opctx, &sitrep1)
158+
.fm_sitrep_insert(&opctx, sitrep1.clone())
159159
.await
160160
.expect("inserting initial sitrep should succeed");
161161

@@ -178,7 +178,7 @@ mod tests {
178178
cases: Default::default(),
179179
};
180180
datastore
181-
.fm_sitrep_insert(&opctx, &sitrep2)
181+
.fm_sitrep_insert(&opctx, sitrep2.clone())
182182
.await
183183
.expect("inserting child sitrep should succeed");
184184

@@ -271,7 +271,7 @@ mod tests {
271271
},
272272
cases: Default::default(),
273273
};
274-
match datastore.fm_sitrep_insert(&opctx, &sitrep).await {
274+
match datastore.fm_sitrep_insert(&opctx, sitrep).await {
275275
Ok(_) => {
276276
panic!("inserting sitrep v{v} orphan {i} should not succeed")
277277
}

0 commit comments

Comments
 (0)