Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion dev-tools/omdb/src/bin/omdb/db/sitrep.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ async fn cmd_db_sitrep_show(
}
};

let fm::Sitrep { metadata } = sitrep;
let fm::Sitrep { metadata, cases } = sitrep;
let fm::SitrepMetadata {
id,
creator_id,
Expand Down Expand Up @@ -345,5 +345,12 @@ async fn cmd_db_sitrep_show(
}
}

if !cases.is_empty() {
println!("\n{:-<80}\n", "== CASES");
for case in cases {
println!("{}", case.display_indented(4, Some(id)));
}
}

Ok(())
}
14 changes: 13 additions & 1 deletion ereport/types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub struct Ereport {
Serialize,
Deserialize,
JsonSchema,
Hash,
)]
#[repr(transparent)]
#[serde(from = "u64", into = "u64")]
Expand Down Expand Up @@ -102,7 +103,18 @@ impl TryFrom<i64> for Ena {
}

/// Unique identifier for an ereport.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Serialize,
Deserialize,
PartialOrd,
Ord,
Hash,
)]
pub struct EreportId {
pub restart_id: EreporterRestartUuid,
pub ena: Ena,
Expand Down
5 changes: 5 additions & 0 deletions nexus/db-model/src/fm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ use chrono::{DateTime, Utc};
use nexus_db_schema::schema::{fm_sitrep, fm_sitrep_history};
use omicron_uuid_kinds::{CollectionKind, OmicronZoneKind, SitrepKind};

mod case;
pub use case::*;
mod diagnosis_engine;
pub use diagnosis_engine::*;

#[derive(Queryable, Insertable, Clone, Debug, Selectable)]
#[diesel(table_name = fm_sitrep)]
pub struct SitrepMetadata {
Expand Down
135 changes: 135 additions & 0 deletions nexus/db-model/src/fm/case.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! Fault management cases.

use super::DiagnosisEngine;
use crate::DbTypedUuid;
use crate::ereport;
use nexus_db_schema::schema::{fm_case, fm_ereport_in_case};
use nexus_types::fm;
use omicron_uuid_kinds::{
CaseEreportKind, CaseKind, EreporterRestartKind, SitrepKind, SitrepUuid,
};

/// Metadata describing a fault management case.
///
/// This corresponds to the fields in the `fm_case` table.
#[derive(Queryable, Insertable, Clone, Debug, Selectable)]
#[diesel(table_name = fm_case)]
pub struct CaseMetadata {
/// The ID of this case.
pub id: DbTypedUuid<CaseKind>,
/// The ID of the sitrep in which the case has this state.
pub sitrep_id: DbTypedUuid<SitrepKind>,
/// The diagnosis engine which owns this case.
pub de: DiagnosisEngine,

/// The ID of the sitrep in which this case was created.
pub created_sitrep_id: DbTypedUuid<SitrepKind>,
/// If this case is closed, the ID of the sitrep in which it was closed.
///
/// If this field is non-null, then the case has been closed. Closed cases
/// need not be copied forward into child sitreps that descend from the
/// sitrep in which the case was closed.
pub closed_sitrep_id: Option<DbTypedUuid<SitrepKind>>,

/// An optional, human-readable comment describing this case.
///
/// Sitrep comments are intended for debugging purposes only; i.e., they are
/// visible to Oxide support via OMDB, but are not presented to the
/// operator. The contents of comment fields are not stable, and a DE may
/// emit a different comment string for an analogous determination across
/// different software versions.
pub comment: String,
}

/// An association between an ereport and a case.
#[derive(Queryable, Insertable, Clone, Debug, Selectable)]
#[diesel(table_name = fm_ereport_in_case)]
pub struct CaseEreport {
/// The ID of this association. This is used primarily for pagination.
pub id: DbTypedUuid<CaseEreportKind>,
/// The restart ID of the reporter that produced this ereport.
pub restart_id: DbTypedUuid<EreporterRestartKind>,
/// The ENA of the ereport within that reporter restart.
///
/// As long as this `CaseEreport` entry exists, the corresponding entry in
/// the `ereport` table with this restart ID and ENA pair is assumed to also
/// exist.
pub ena: ereport::DbEna,
/// ID of the case.
///
/// This corresponds to a record in `fm_case` with this case ID and sitrep ID.
pub case_id: DbTypedUuid<CaseKind>,
/// ID of the current sitrep in which this association exists.
pub sitrep_id: DbTypedUuid<SitrepKind>,
/// ID of the first sitrep in which this association was added.
///
/// Since all relevant data for open cases is copied forward into new
/// sitreps, this field exists primarily for debugging purposes. There is
/// nothing that the sitrep in which an ereport was first assigned to a case
/// can tell you which the current sitrep cannot.
pub assigned_sitrep_id: DbTypedUuid<SitrepKind>,
/// An optional, human-readable comment added by the diagnosis engine to
/// explain why it felt that this ereport is related to this case.
///
/// Sitrep comments are intended for debugging purposes only; i.e., they are
/// visible to Oxide support via OMDB, but are not presented to the
/// operator. The contents of comment fields are not stable, and a DE may
/// emit a different comment string for an analogous determination across
/// different software versions.
pub comment: String,
}

/// The complete state of a case in a particular sitrep, consisting of the
/// [`CaseMetadata`] record and any other records belonging to the case.
#[derive(Clone, Debug)]
pub struct Case {
pub metadata: CaseMetadata,
pub ereports: Vec<CaseEreport>,
}

impl Case {
pub fn from_sitrep(sitrep_id: SitrepUuid, case: fm::Case) -> Self {
let sitrep_id = sitrep_id.into();
let case_id = case.id.into();
let ereports = case
.ereports
.into_iter()
.map(
|fm::case::CaseEreport {
id,
ereport,
assigned_sitrep_id,
comment,
}| {
let restart_id = ereport.id().restart_id.into();
let ena = ereport.id().ena.into();
CaseEreport {
id: id.into(),
case_id,
restart_id,
ena,
comment,
sitrep_id,
assigned_sitrep_id: assigned_sitrep_id.into(),
}
},
)
.collect();

Self {
metadata: CaseMetadata {
id: case_id,
sitrep_id,
de: case.de.into(),
created_sitrep_id: case.created_sitrep_id.into(),
closed_sitrep_id: case.closed_sitrep_id.map(Into::into),
comment: case.comment,
},
ereports,
}
}
}
50 changes: 50 additions & 0 deletions nexus/db-model/src/fm/diagnosis_engine.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use crate::impl_enum_type;
use nexus_types::fm;
use serde::{Deserialize, Serialize};
use std::fmt;

impl_enum_type!(
DiagnosisEngineEnum:

#[derive(
Copy,
Clone,
Debug,
PartialEq,
Serialize,
Deserialize,
AsExpression,
FromSqlRow,
)]
#[serde(rename_all = "snake_case")]
pub enum DiagnosisEngine;

PowerShelf => b"power_shelf"

);

impl From<DiagnosisEngine> for fm::DiagnosisEngineKind {
fn from(de: DiagnosisEngine) -> Self {
match de {
DiagnosisEngine::PowerShelf => fm::DiagnosisEngineKind::PowerShelf,
}
}
}

impl From<fm::DiagnosisEngineKind> for DiagnosisEngine {
fn from(fm_de: fm::DiagnosisEngineKind) -> Self {
match fm_de {
fm::DiagnosisEngineKind::PowerShelf => DiagnosisEngine::PowerShelf,
}
}
}

impl fmt::Display for DiagnosisEngine {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fm::DiagnosisEngineKind::from(*self).fmt(f)
}
}
4 changes: 2 additions & 2 deletions nexus/db-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ mod downstairs;
pub mod ereport;
mod ereporter_type;
mod external_ip;
mod fm;
pub mod fm;
mod generation;
mod identity_provider;
mod image;
Expand Down Expand Up @@ -191,7 +191,7 @@ pub use downstairs::*;
pub use ereport::Ereport;
pub use ereporter_type::*;
pub use external_ip::*;
pub use fm::*;
pub use fm::{SitrepMetadata, SitrepVersion};
pub use generation::*;
pub use identity_provider::*;
pub use image::*;
Expand Down
3 changes: 2 additions & 1 deletion nexus/db-model/src/schema_versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::{collections::BTreeMap, sync::LazyLock};
///
/// This must be updated when you change the database schema. Refer to
/// schema/crdb/README.adoc in the root of this repository for details.
pub const SCHEMA_VERSION: Version = Version::new(212, 0, 0);
pub const SCHEMA_VERSION: Version = Version::new(213, 0, 0);

/// List of all past database schema versions, in *reverse* order
///
Expand All @@ -28,6 +28,7 @@ static KNOWN_VERSIONS: LazyLock<Vec<KnownVersion>> = LazyLock::new(|| {
// | leaving the first copy as an example for the next person.
// v
// KnownVersion::new(next_int, "unique-dirname-with-the-sql-files"),
KnownVersion::new(213, "fm-cases"),
KnownVersion::new(212, "local-storage-disk-type"),
KnownVersion::new(211, "blueprint-sled-config-subnet"),
KnownVersion::new(210, "one-big-ereport-table"),
Expand Down
10 changes: 9 additions & 1 deletion nexus/db-queries/src/db/datastore/ereport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ impl DataStore {
) -> LookupResult<Ereport> {
opctx.authorize(authz::Action::ListChildren, &authz::FLEET).await?;
let conn = self.pool_connection_authorized(opctx).await?;
self.ereport_fetch_on_conn(&conn, id).await
}

pub(crate) async fn ereport_fetch_on_conn(
&self,
conn: &async_bb8_diesel::Connection<DbConnection>,
id: fm::EreportId,
) -> LookupResult<Ereport> {
let restart_id = id.restart_id.into_untyped_uuid();
let ena = DbEna::from(id.ena);

Expand All @@ -106,7 +114,7 @@ impl DataStore {
.filter(dsl::ena.eq(ena))
.filter(dsl::time_deleted.is_null())
.select(Ereport::as_select())
.first_async(&*conn)
.first_async(conn)
.await
.optional()
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))?
Expand Down
Loading
Loading