Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
322d5b3
Add FMD fault inventory to sled-agent API
smklein Apr 16, 2026
ab9399b
Run FMD collection on a blocking thread
smklein Apr 17, 2026
32c5872
Regenerate sled-agent OpenAPI spec
smklein Apr 17, 2026
887f61a
xtask: allowlist libfmd_adm.so.1 for sled-agent binaries
smklein Apr 17, 2026
2c83840
merge
smklein Apr 20, 2026
026bd3f
InventoryResult, less optional
smklein Apr 20, 2026
bcb288d
Convert 'FMD unavailable' to a default impl that's empty
smklein Apr 22, 2026
778afd5
Destructuring
smklein Apr 23, 2026
34884ba
review feedback
smklein Apr 23, 2026
a659107
Bump fmd-adm rev; use InvisibleResources::Included
smklein Apr 28, 2026
0c26760
fmd: replace match on spawn_blocking JoinError with .expect()
smklein Apr 28, 2026
6d0ec5f
merge
smklein Apr 28, 2026
67ec15a
fmd: import GenericUuid so from_untyped_uuid resolves on illumos
smklein Apr 28, 2026
058314f
merge
smklein Apr 28, 2026
829bd2f
Add CRDB schema + DB model for FMD inventory tables
smklein Apr 29, 2026
00e6347
nexus-types/inventory: add fmd field on SledAgent + builder passthrough
smklein Apr 29, 2026
cfb3eea
datastore: write + prune for inv_fmd_* tables
smklein Apr 29, 2026
8e47394
datastore: read path for FMD inventory
smklein Apr 29, 2026
39053f7
Display wrappers + omdb golden output for FMD inventory
smklein Apr 29, 2026
3291762
merge
smklein May 14, 2026
d44c588
omicron-rpaths: panic on illumos if no DEP_*_LIBDIRS env vars are set
smklein May 14, 2026
604a831
Replace FmdInventoryResult with Result<FmdInventory, String>
smklein May 14, 2026
115d388
merge
smklein May 14, 2026
040b815
Add foreign-key comments to inv_fmd_* tables
smklein May 14, 2026
f677b4b
sled-agent: use InlineErrorChain for FMD collection errors
smklein May 14, 2026
2e3d1e9
merge
smklein May 14, 2026
e43ae1f
Add typed FmdInventoryError and per-sled FMD bounds
smklein May 15, 2026
d9d3da3
merge add-fmd-inventory + wire typed error through DB
smklein May 15, 2026
25e6cea
Split inv-fmd up01.sql into per-DDL files
smklein May 15, 2026
2d63473
merge
smklein May 15, 2026
cff0320
merge
smklein May 15, 2026
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
6 changes: 6 additions & 0 deletions dev-tools/reconfigurator-cli/tests/output/cmds-example-stdout
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down Expand Up @@ -1893,6 +1895,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down Expand Up @@ -2137,6 +2141,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down Expand Up @@ -472,6 +474,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down Expand Up @@ -595,6 +599,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down Expand Up @@ -955,6 +957,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down Expand Up @@ -1148,6 +1152,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down Expand Up @@ -939,6 +941,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down Expand Up @@ -1132,6 +1136,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -800,6 +800,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down Expand Up @@ -993,6 +995,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down Expand Up @@ -1186,6 +1190,8 @@ LEDGERED SLED CONFIG
reconciler task status: idle (finished at <REDACTED_TIMESTAMP> after running for <REDACTED_DURATION>s)
reference measurements:
(measurement set is empty)
fmd:
no faults reported

SMF SERVICES STATUS
no data on SMF services has been collected
Expand Down
164 changes: 164 additions & 0 deletions nexus/db-model/src/inventory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ use nexus_db_schema::schema::inv_zone_manifest_zone;
use nexus_db_schema::schema::{
hw_baseboard_id, inv_caboose, inv_clickhouse_keeper_membership,
inv_cockroachdb_status, inv_collection, inv_collection_error, inv_dataset,
inv_fmd_host_case, inv_fmd_resource, inv_fmd_status,
inv_host_phase_1_active_slot, inv_host_phase_1_flash_hash,
inv_internal_dns, inv_last_reconciliation_dataset_result,
inv_last_reconciliation_disk_result,
Expand Down Expand Up @@ -64,6 +65,8 @@ use omicron_common::update::OmicronInstallManifestSource;
use omicron_common::zpool_name::ZpoolName;
use omicron_uuid_kinds::DatasetKind;
use omicron_uuid_kinds::DatasetUuid;
use omicron_uuid_kinds::FmdHostCaseKind;
use omicron_uuid_kinds::FmdResourceKind;
use omicron_uuid_kinds::InternalZpoolKind;
use omicron_uuid_kinds::MupdateKind;
use omicron_uuid_kinds::MupdateOverrideKind;
Expand All @@ -85,6 +88,10 @@ use omicron_uuid_kinds::{CollectionUuid, OmicronZoneUuid};
use sled_agent_types::inventory::BootImageHeader;
use sled_agent_types::inventory::BootPartitionDetails;
use sled_agent_types::inventory::ConfigReconcilerInventoryStatus;
use sled_agent_types::inventory::FmdHostCase;
use sled_agent_types::inventory::FmdInventory;
use sled_agent_types::inventory::FmdInventoryError;
use sled_agent_types::inventory::FmdResource;
use sled_agent_types::inventory::HostPhase2DesiredContents;
use sled_agent_types::inventory::HostPhase2DesiredSlots;
use sled_agent_types::inventory::ManifestBootInventory;
Expand Down Expand Up @@ -2128,6 +2135,163 @@ impl InvSvcEnabledNotOnlineParseError {
}
}

impl_enum_type!(
FmdInventoryErrorKindEnum:

#[derive(Copy, Clone, Debug, AsExpression, FromSqlRow, PartialEq)]
pub enum FmdInventoryErrorKind;

// Enum values
FmdError => b"fmd_error"
TooManyCases => b"too_many_cases"
TooManyResources => b"too_many_resources"
);

impl From<sled_agent_types::inventory::FmdInventoryErrorKind>
for FmdInventoryErrorKind
{
fn from(value: sled_agent_types::inventory::FmdInventoryErrorKind) -> Self {
use sled_agent_types::inventory::FmdInventoryErrorKind as ApiKind;
match value {
ApiKind::FmdError => FmdInventoryErrorKind::FmdError,
ApiKind::TooManyCases => FmdInventoryErrorKind::TooManyCases,
ApiKind::TooManyResources => {
FmdInventoryErrorKind::TooManyResources
}
}
}
}

impl From<FmdInventoryErrorKind>
for sled_agent_types::inventory::FmdInventoryErrorKind
{
fn from(value: FmdInventoryErrorKind) -> Self {
use sled_agent_types::inventory::FmdInventoryErrorKind as ApiKind;
match value {
FmdInventoryErrorKind::FmdError => ApiKind::FmdError,
FmdInventoryErrorKind::TooManyCases => ApiKind::TooManyCases,
FmdInventoryErrorKind::TooManyResources => {
ApiKind::TooManyResources
}
}
}
}

/// One row per (collection, sled) recording the outcome of FMD inventory
/// collection. Both `error_kind` and `error_message` are `NULL` when the
/// daemon was queried successfully; both are set when collection failed.
#[derive(Queryable, Clone, Debug, Selectable, Insertable)]
#[diesel(table_name = inv_fmd_status)]
pub struct InvFmdStatus {
pub inv_collection_id: DbTypedUuid<CollectionKind>,
pub sled_id: DbTypedUuid<SledKind>,
pub error_kind: Option<FmdInventoryErrorKind>,
pub error_message: Option<String>,
}

impl InvFmdStatus {
pub fn new(
inv_collection_id: CollectionUuid,
sled_id: SledUuid,
result: &Result<FmdInventory, FmdInventoryError>,
) -> Self {
let (error_kind, error_message) = match result {
Ok(_) => (None, None),
Err(err) => (Some(err.kind.into()), Some(err.message.clone())),
};
Self {
inv_collection_id: inv_collection_id.into(),
sled_id: sled_id.into(),
error_kind,
error_message,
}
}
}

#[derive(Queryable, Clone, Debug, Selectable, Insertable)]
#[diesel(table_name = inv_fmd_host_case)]
pub struct InvFmdHostCase {
pub inv_collection_id: DbTypedUuid<CollectionKind>,
pub sled_id: DbTypedUuid<SledKind>,
pub case_id: DbTypedUuid<FmdHostCaseKind>,
pub code: String,
pub url: String,
pub event: Option<serde_json::Value>,
}

impl InvFmdHostCase {
pub fn new(
inv_collection_id: CollectionUuid,
sled_id: SledUuid,
case: &FmdHostCase,
) -> Self {
Self {
inv_collection_id: inv_collection_id.into(),
sled_id: sled_id.into(),
case_id: case.uuid.into(),
code: case.code.clone(),
url: case.url.clone(),
event: case.event.clone(),
}
}
}

impl From<InvFmdHostCase> for FmdHostCase {
fn from(row: InvFmdHostCase) -> Self {
Self {
uuid: row.case_id.into(),
code: row.code,
url: row.url,
event: row.event,
}
}
}

#[derive(Queryable, Clone, Debug, Selectable, Insertable)]
#[diesel(table_name = inv_fmd_resource)]
pub struct InvFmdResource {
pub inv_collection_id: DbTypedUuid<CollectionKind>,
pub sled_id: DbTypedUuid<SledKind>,
pub resource_id: DbTypedUuid<FmdResourceKind>,
pub fmri: String,
pub case_id: DbTypedUuid<FmdHostCaseKind>,
pub faulty: bool,
pub unusable: bool,
pub invisible: bool,
}

impl InvFmdResource {
pub fn new(
inv_collection_id: CollectionUuid,
sled_id: SledUuid,
resource: &FmdResource,
) -> Self {
Self {
inv_collection_id: inv_collection_id.into(),
sled_id: sled_id.into(),
resource_id: resource.uuid.into(),
fmri: resource.fmri.clone(),
case_id: resource.case_id.into(),
faulty: resource.faulty,
unusable: resource.unusable,
invisible: resource.invisible,
}
}
}

impl From<InvFmdResource> for FmdResource {
fn from(row: InvFmdResource) -> Self {
Self {
uuid: row.resource_id.into(),
fmri: row.fmri,
case_id: row.case_id.into(),
faulty: row.faulty,
unusable: row.unusable,
invisible: row.invisible,
}
}
}

// See [`sled_agent_types::inventory::SvcEnabledNotOnlineState`].
impl_enum_type!(
InvSvcEnabledNotOnlineStateEnum:
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(258, 0, 0);
pub const SCHEMA_VERSION: Version = Version::new(259, 0, 0);

/// List of all past database schema versions, in *reverse* order
///
Expand All @@ -28,6 +28,7 @@ pub 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(259, "inv-fmd"),
KnownVersion::new(258, "lookup-unmarked-ereports-by-class"),
KnownVersion::new(257, "add-disk-adoption-requests"),
KnownVersion::new(256, "bgp-unnumbered-peer-cleanup"),
Expand Down
Loading
Loading