From 0895d17089723e58bdefc012a37381e80a9af9d5 Mon Sep 17 00:00:00 2001 From: David Pacheco Date: Wed, 5 Mar 2025 15:22:27 -0800 Subject: [PATCH 1/5] first cut --- Cargo.lock | 3 ++- Cargo.toml | 2 ++ clients/gateway-client/Cargo.toml | 1 + clients/gateway-client/src/lib.rs | 1 + common/src/update.rs | 4 ++++ .../db-queries/src/db/datastore/deployment.rs | 1 + nexus/db-queries/src/db/datastore/rack.rs | 5 +++++ nexus/reconfigurator/execution/src/dns.rs | 1 + .../execution/src/omicron_physical_disks.rs | 1 + .../execution/src/omicron_zones.rs | 1 + .../planning/src/blueprint_builder/builder.rs | 5 +++++ .../background/tasks/blueprint_execution.rs | 1 + .../app/background/tasks/blueprint_load.rs | 1 + nexus/test-utils/src/lib.rs | 1 + nexus/types/src/deployment.rs | 22 +++++++++++++++++++ nexus/types/src/deployment/blueprint_diff.rs | 1 + nexus/types/src/inventory.rs | 13 ++++++++++- sled-agent/src/rack_setup/service.rs | 1 + 18 files changed, 63 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f42fd8ce20b..78cb7b3aa78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3466,6 +3466,7 @@ version = "0.1.0" dependencies = [ "base64 0.22.1", "chrono", + "daft", "gateway-messages", "omicron-workspace-hack", "progenitor 0.9.1", @@ -12548,8 +12549,8 @@ dependencies = [ [[package]] name = "tufaceous-artifact" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/tufaceous?branch=main#d2387032714f66e31b7e255d89f9bf6eb9b3a010" dependencies = [ + "daft", "parse-display", "proptest", "schemars", diff --git a/Cargo.toml b/Cargo.toml index b25bb59812c..8f8c46669da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -865,6 +865,8 @@ opt-level = 3 # progenitor-client = { path = "../progenitor/progenitor-client" } # steno = { path = "../steno" } +[patch."https://github.com/oxidecomputer/tufaceous"] +tufaceous-artifact = { path = "../tufaceous/artifact" } # [patch."https://github.com/oxidecomputer/crucible"] # crucible-agent-client = { path = "../crucible/agent-client" } # crucible-pantry-client = { path = "../crucible/pantry-client" } diff --git a/clients/gateway-client/Cargo.toml b/clients/gateway-client/Cargo.toml index 96f6484122c..56ae822a249 100644 --- a/clients/gateway-client/Cargo.toml +++ b/clients/gateway-client/Cargo.toml @@ -10,6 +10,7 @@ workspace = true [dependencies] base64.workspace = true chrono.workspace = true +daft.workspace = true gateway-messages.workspace = true progenitor.workspace = true rand.workspace = true diff --git a/clients/gateway-client/src/lib.rs b/clients/gateway-client/src/lib.rs index a78204d731b..0daa4817c8a 100644 --- a/clients/gateway-client/src/lib.rs +++ b/clients/gateway-client/src/lib.rs @@ -69,6 +69,7 @@ progenitor::generate_api!( SpIgnition = { derives = [PartialEq, Eq, PartialOrd, Ord] }, SpIgnitionSystemType = { derives = [Copy, PartialEq, Eq, PartialOrd, Ord] }, SpState = { derives = [PartialEq, Eq, PartialOrd, Ord] }, + SpType = { derives = [daft::Diffable] }, }, ); diff --git a/common/src/update.rs b/common/src/update.rs index c4d24537614..597fd3eeeee 100644 --- a/common/src/update.rs +++ b/common/src/update.rs @@ -4,6 +4,7 @@ use std::{fmt, str::FromStr}; +use daft::Diffable; use hex::FromHexError; use schemars::{ JsonSchema, @@ -64,6 +65,7 @@ impl From for ArtifactId { /// by name and version. This type indicates that. #[derive( Debug, + Diffable, Clone, PartialEq, Eq, @@ -86,6 +88,7 @@ pub struct ArtifactHashId { #[derive( Copy, Clone, + Diffable, Eq, PartialEq, Ord, @@ -95,6 +98,7 @@ pub struct ArtifactHashId { Deserialize, JsonSchema, )] +#[daft(leaf)] // XXX-dap #[serde(transparent)] #[cfg_attr(feature = "testing", derive(test_strategy::Arbitrary))] pub struct ArtifactHash( diff --git a/nexus/db-queries/src/db/datastore/deployment.rs b/nexus/db-queries/src/db/datastore/deployment.rs index 710c3cd349d..fb036547d16 100644 --- a/nexus/db-queries/src/db/datastore/deployment.rs +++ b/nexus/db-queries/src/db/datastore/deployment.rs @@ -1100,6 +1100,7 @@ impl DataStore { Ok(Blueprint { id: blueprint_id, sleds, + pending_mgs_updates: BTreeMap::new(), // XXX-dap parent_blueprint_id, internal_dns_version, external_dns_version, diff --git a/nexus/db-queries/src/db/datastore/rack.rs b/nexus/db-queries/src/db/datastore/rack.rs index 37d184a1d44..c4205db8d3b 100644 --- a/nexus/db-queries/src/db/datastore/rack.rs +++ b/nexus/db-queries/src/db/datastore/rack.rs @@ -1055,6 +1055,7 @@ mod test { blueprint: Blueprint { id: BlueprintUuid::new_v4(), sleds: BTreeMap::new(), + pending_mgs_updates: BTreeMap::new(), cockroachdb_setting_preserve_downgrade: CockroachDbPreserveDowngrade::DoNotModify, parent_blueprint_id: None, @@ -1557,6 +1558,7 @@ mod test { let blueprint = Blueprint { id: BlueprintUuid::new_v4(), sleds: make_sled_config_only_zones(blueprint_zones), + pending_mgs_updates: BTreeMap::new(), cockroachdb_setting_preserve_downgrade: CockroachDbPreserveDowngrade::DoNotModify, parent_blueprint_id: None, @@ -1819,6 +1821,7 @@ mod test { let blueprint = Blueprint { id: BlueprintUuid::new_v4(), sleds: make_sled_config_only_zones(blueprint_zones), + pending_mgs_updates: BTreeMap::new(), cockroachdb_setting_preserve_downgrade: CockroachDbPreserveDowngrade::DoNotModify, parent_blueprint_id: None, @@ -2028,6 +2031,7 @@ mod test { let blueprint = Blueprint { id: BlueprintUuid::new_v4(), sleds: make_sled_config_only_zones(blueprint_zones), + pending_mgs_updates: BTreeMap::new(), cockroachdb_setting_preserve_downgrade: CockroachDbPreserveDowngrade::DoNotModify, parent_blueprint_id: None, @@ -2169,6 +2173,7 @@ mod test { let blueprint = Blueprint { id: BlueprintUuid::new_v4(), sleds: make_sled_config_only_zones(blueprint_zones), + pending_mgs_updates: BTreeMap::new(), cockroachdb_setting_preserve_downgrade: CockroachDbPreserveDowngrade::DoNotModify, parent_blueprint_id: None, diff --git a/nexus/reconfigurator/execution/src/dns.rs b/nexus/reconfigurator/execution/src/dns.rs index f9dda12605c..b7f0fad3504 100644 --- a/nexus/reconfigurator/execution/src/dns.rs +++ b/nexus/reconfigurator/execution/src/dns.rs @@ -682,6 +682,7 @@ mod test { let mut blueprint = Blueprint { id: BlueprintUuid::new_v4(), sleds: blueprint_sleds, + pending_mgs_updates: BTreeMap::new(), cockroachdb_setting_preserve_downgrade: CockroachDbPreserveDowngrade::DoNotModify, parent_blueprint_id: None, diff --git a/nexus/reconfigurator/execution/src/omicron_physical_disks.rs b/nexus/reconfigurator/execution/src/omicron_physical_disks.rs index c0ad68da0b2..8a025f53dfb 100644 --- a/nexus/reconfigurator/execution/src/omicron_physical_disks.rs +++ b/nexus/reconfigurator/execution/src/omicron_physical_disks.rs @@ -222,6 +222,7 @@ mod test { ) }) .collect(), + pending_mgs_updates: BTreeMap::new(), cockroachdb_setting_preserve_downgrade: CockroachDbPreserveDowngrade::DoNotModify, parent_blueprint_id: None, diff --git a/nexus/reconfigurator/execution/src/omicron_zones.rs b/nexus/reconfigurator/execution/src/omicron_zones.rs index 776984988c6..b3f307ee154 100644 --- a/nexus/reconfigurator/execution/src/omicron_zones.rs +++ b/nexus/reconfigurator/execution/src/omicron_zones.rs @@ -407,6 +407,7 @@ mod test { ) }) .collect(), + pending_mgs_updates: BTreeMap::new(), cockroachdb_setting_preserve_downgrade: CockroachDbPreserveDowngrade::DoNotModify, parent_blueprint_id: None, diff --git a/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs b/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs index ca8bae00d2c..9d8c2c289ab 100644 --- a/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs +++ b/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs @@ -456,6 +456,7 @@ impl<'a> BlueprintBuilder<'a> { Blueprint { id: rng.next_blueprint(), sleds, + pending_mgs_updates: BTreeMap::new(), parent_blueprint_id: None, internal_dns_version: Generation::new(), external_dns_version: Generation::new(), @@ -730,6 +731,10 @@ impl<'a> BlueprintBuilder<'a> { Blueprint { id: blueprint_id, sleds, + pending_mgs_updates: self + .parent_blueprint + .pending_mgs_updates + .clone(), parent_blueprint_id: Some(self.parent_blueprint.id), internal_dns_version: self.input.internal_dns_version(), external_dns_version: self.input.external_dns_version(), diff --git a/nexus/src/app/background/tasks/blueprint_execution.rs b/nexus/src/app/background/tasks/blueprint_execution.rs index b8f93bb58f5..527b736f179 100644 --- a/nexus/src/app/background/tasks/blueprint_execution.rs +++ b/nexus/src/app/background/tasks/blueprint_execution.rs @@ -247,6 +247,7 @@ mod test { let blueprint = Blueprint { id, sleds: blueprint_sleds, + pending_mgs_updates: BTreeMap::new(), cockroachdb_setting_preserve_downgrade: CockroachDbPreserveDowngrade::DoNotModify, parent_blueprint_id: Some(current_target.target_id), diff --git a/nexus/src/app/background/tasks/blueprint_load.rs b/nexus/src/app/background/tasks/blueprint_load.rs index da2bc6dee2b..31638b00f1d 100644 --- a/nexus/src/app/background/tasks/blueprint_load.rs +++ b/nexus/src/app/background/tasks/blueprint_load.rs @@ -217,6 +217,7 @@ mod test { Blueprint { id, sleds: BTreeMap::new(), + pending_mgs_updates: BTreeMap::new(), cockroachdb_setting_preserve_downgrade: CockroachDbPreserveDowngrade::DoNotModify, parent_blueprint_id: Some(parent_blueprint_id), diff --git a/nexus/test-utils/src/lib.rs b/nexus/test-utils/src/lib.rs index f4e6f7d4228..e4256f79c4a 100644 --- a/nexus/test-utils/src/lib.rs +++ b/nexus/test-utils/src/lib.rs @@ -939,6 +939,7 @@ impl<'a, N: NexusServer> ControlPlaneTestContextBuilder<'a, N> { Blueprint { id: BlueprintUuid::new_v4(), sleds: blueprint_sleds, + pending_mgs_updates: BTreeMap::new(), parent_blueprint_id: None, internal_dns_version: dns_config.generation, external_dns_version: Generation::new(), diff --git a/nexus/types/src/deployment.rs b/nexus/types/src/deployment.rs index 51b7a3227a2..f79c60f4ebc 100644 --- a/nexus/types/src/deployment.rs +++ b/nexus/types/src/deployment.rs @@ -102,7 +102,10 @@ use blueprint_display::{ }; use id_map::{IdMap, IdMappable}; +use crate::inventory::BaseboardId; pub use blueprint_diff::BlueprintDiffSummary; +use gateway_client::types::SpType; +use omicron_common::update::ArtifactHashId; /// Describes a complete set of software and configuration for the system // Blueprints are a fundamental part of how the system modifies itself. Each @@ -152,6 +155,9 @@ pub struct Blueprint { /// A map of sled id -> desired configuration of the sled. pub sleds: BTreeMap, + /// List of pending MGS-mediated updates + pub pending_mgs_updates: BTreeMap, + /// which blueprint this blueprint is based on pub parent_blueprint_id: Option, @@ -472,6 +478,7 @@ impl fmt::Display for BlueprintDisplay<'_> { let Blueprint { id, sleds, + pending_mgs_updates: _, // XXX-dap parent_blueprint_id, // These two cockroachdb_* fields are handled by // `make_cockroachdb_table()`, called below. @@ -898,6 +905,21 @@ impl fmt::Display for BlueprintZoneImageSource { } } +#[derive( + Clone, Debug, Eq, PartialEq, JsonSchema, Deserialize, Serialize, Diffable, +)] +pub struct PendingMgsUpdate { + /// id of the baseboard that we're going to update + baseboard_id: BaseboardId, + /// what type of baseboard this is + sp_type: SpType, + /// last known MGS slot (cubby number) of the baseboard + slot_id: u16, + /// which artifact to apply to this device + /// (implies which component is being updated) + artifact_hash_id: ArtifactHashId, +} + /// Filters that apply to blueprint datasets. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum BlueprintDatasetFilter { diff --git a/nexus/types/src/deployment/blueprint_diff.rs b/nexus/types/src/deployment/blueprint_diff.rs index ba93784a78c..8c17ed73a07 100644 --- a/nexus/types/src/deployment/blueprint_diff.rs +++ b/nexus/types/src/deployment/blueprint_diff.rs @@ -58,6 +58,7 @@ impl<'a> BlueprintDiffSummary<'a> { let BlueprintDiff { // Fields in which changes are meaningful. sleds, + pending_mgs_updates: _, // XXX-dap clickhouse_cluster_config, // Metadata fields for which changes don't reflect semantic // changes from one blueprint to the next. diff --git a/nexus/types/src/inventory.rs b/nexus/types/src/inventory.rs index 95c4f80e05f..c2aeba993fa 100644 --- a/nexus/types/src/inventory.rs +++ b/nexus/types/src/inventory.rs @@ -14,6 +14,7 @@ use crate::external_api::params::UninitializedSledId; use chrono::DateTime; use chrono::Utc; use clickhouse_admin_types::ClickhouseKeeperClusterMembership; +use daft::Diffable; pub use gateway_client::types::PowerState; pub use gateway_client::types::RotImageError; pub use gateway_client::types::RotSlot; @@ -34,6 +35,7 @@ use omicron_uuid_kinds::CollectionUuid; use omicron_uuid_kinds::DatasetUuid; use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::ZpoolUuid; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use std::collections::BTreeMap; @@ -205,7 +207,16 @@ impl Collection { /// the same part number and serial number but a new revision number, we'd want /// to treat that as the same baseboard as one with a different revision number. #[derive( - Clone, Debug, Ord, Eq, PartialOrd, PartialEq, Deserialize, Serialize, + Clone, + Debug, + Diffable, + Ord, + Eq, + PartialOrd, + PartialEq, + Deserialize, + Serialize, + JsonSchema, )] pub struct BaseboardId { /// Oxide Part Number diff --git a/sled-agent/src/rack_setup/service.rs b/sled-agent/src/rack_setup/service.rs index ff32a240ce3..f0675f87815 100644 --- a/sled-agent/src/rack_setup/service.rs +++ b/sled-agent/src/rack_setup/service.rs @@ -1589,6 +1589,7 @@ pub(crate) fn build_initial_blueprint_from_sled_configs( Ok(Blueprint { id: BlueprintUuid::new_v4(), sleds: blueprint_sleds, + pending_mgs_updates: BTreeMap::new(), parent_blueprint_id: None, internal_dns_version, // We don't configure external DNS during RSS, so set it to an initial From 1ddefe7b4ac24891186b8f0617abab8ab9fd1aa8 Mon Sep 17 00:00:00 2001 From: David Pacheco Date: Wed, 5 Mar 2025 15:22:33 -0800 Subject: [PATCH 2/5] update TODOs --- common/src/update.rs | 2 +- nexus/db-queries/src/db/datastore/deployment.rs | 3 ++- nexus/types/src/deployment.rs | 4 +++- nexus/types/src/deployment/blueprint_diff.rs | 4 +++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/common/src/update.rs b/common/src/update.rs index 597fd3eeeee..6201509cdcb 100644 --- a/common/src/update.rs +++ b/common/src/update.rs @@ -98,7 +98,7 @@ pub struct ArtifactHashId { Deserialize, JsonSchema, )] -#[daft(leaf)] // XXX-dap +#[daft(leaf)] #[serde(transparent)] #[cfg_attr(feature = "testing", derive(test_strategy::Arbitrary))] pub struct ArtifactHash( diff --git a/nexus/db-queries/src/db/datastore/deployment.rs b/nexus/db-queries/src/db/datastore/deployment.rs index fb036547d16..8128b18c2a0 100644 --- a/nexus/db-queries/src/db/datastore/deployment.rs +++ b/nexus/db-queries/src/db/datastore/deployment.rs @@ -1100,7 +1100,8 @@ impl DataStore { Ok(Blueprint { id: blueprint_id, sleds, - pending_mgs_updates: BTreeMap::new(), // XXX-dap + // TODO these need to be serialized to the database. + pending_mgs_updates: BTreeMap::new(), parent_blueprint_id, internal_dns_version, external_dns_version, diff --git a/nexus/types/src/deployment.rs b/nexus/types/src/deployment.rs index f79c60f4ebc..959eb3b9945 100644 --- a/nexus/types/src/deployment.rs +++ b/nexus/types/src/deployment.rs @@ -478,7 +478,9 @@ impl fmt::Display for BlueprintDisplay<'_> { let Blueprint { id, sleds, - pending_mgs_updates: _, // XXX-dap + // TODO Will need to print these when we can actually create + // blueprints with pending MGS updates. + pending_mgs_updates: _, parent_blueprint_id, // These two cockroachdb_* fields are handled by // `make_cockroachdb_table()`, called below. diff --git a/nexus/types/src/deployment/blueprint_diff.rs b/nexus/types/src/deployment/blueprint_diff.rs index 8c17ed73a07..04103370658 100644 --- a/nexus/types/src/deployment/blueprint_diff.rs +++ b/nexus/types/src/deployment/blueprint_diff.rs @@ -58,7 +58,9 @@ impl<'a> BlueprintDiffSummary<'a> { let BlueprintDiff { // Fields in which changes are meaningful. sleds, - pending_mgs_updates: _, // XXX-dap + // TODO Will need to diff these when we can actually create + // blueprints with pending MGS updates. + pending_mgs_updates: _, clickhouse_cluster_config, // Metadata fields for which changes don't reflect semantic // changes from one blueprint to the next. From c37807dc45ce2f9375846506fcae5e7665d11464 Mon Sep 17 00:00:00 2001 From: David Pacheco Date: Wed, 5 Mar 2025 17:00:56 -0800 Subject: [PATCH 3/5] tufaceous PR landed --- Cargo.lock | 9 +++++---- Cargo.toml | 2 -- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78cb7b3aa78..26ca3cf0439 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,7 +739,7 @@ dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.12.1", "log", "prettyplease", "proc-macro2", @@ -12528,7 +12528,7 @@ dependencies = [ [[package]] name = "tufaceous" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/tufaceous?branch=main#d2387032714f66e31b7e255d89f9bf6eb9b3a010" +source = "git+https://github.com/oxidecomputer/tufaceous?branch=main#bbadc279786d8da5376f61067b42738e2ad6342e" dependencies = [ "anyhow", "camino", @@ -12549,6 +12549,7 @@ dependencies = [ [[package]] name = "tufaceous-artifact" version = "0.1.0" +source = "git+https://github.com/oxidecomputer/tufaceous?branch=main#bbadc279786d8da5376f61067b42738e2ad6342e" dependencies = [ "daft", "parse-display", @@ -12564,7 +12565,7 @@ dependencies = [ [[package]] name = "tufaceous-brand-metadata" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/tufaceous?branch=main#d2387032714f66e31b7e255d89f9bf6eb9b3a010" +source = "git+https://github.com/oxidecomputer/tufaceous?branch=main#bbadc279786d8da5376f61067b42738e2ad6342e" dependencies = [ "semver 1.0.25", "serde", @@ -12575,7 +12576,7 @@ dependencies = [ [[package]] name = "tufaceous-lib" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/tufaceous?branch=main#d2387032714f66e31b7e255d89f9bf6eb9b3a010" +source = "git+https://github.com/oxidecomputer/tufaceous?branch=main#bbadc279786d8da5376f61067b42738e2ad6342e" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 8f8c46669da..b25bb59812c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -865,8 +865,6 @@ opt-level = 3 # progenitor-client = { path = "../progenitor/progenitor-client" } # steno = { path = "../steno" } -[patch."https://github.com/oxidecomputer/tufaceous"] -tufaceous-artifact = { path = "../tufaceous/artifact" } # [patch."https://github.com/oxidecomputer/crucible"] # crucible-agent-client = { path = "../crucible/agent-client" } # crucible-pantry-client = { path = "../crucible/pantry-client" } From aca67a13c61bb93ab5cb50b64939df3f14f8faf1 Mon Sep 17 00:00:00 2001 From: David Pacheco Date: Wed, 5 Mar 2025 19:10:58 -0800 Subject: [PATCH 4/5] fix checks --- Cargo.lock | 2 + openapi/nexus-internal.json | 95 +++++++++++++++++++++++++++++++++++++ workspace-hack/Cargo.toml | 8 +++- 3 files changed, 103 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3f240a79a7..dc877f17934 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7486,6 +7486,7 @@ dependencies = [ "crossterm", "crypto-common", "curve25519-dalek", + "daft", "digest", "dof", "dropshot 0.12.0", @@ -7519,6 +7520,7 @@ dependencies = [ "indexmap 2.7.1", "indicatif", "inout", + "ipnetwork", "itertools 0.10.5", "itertools 0.12.1", "lalrpop-util", diff --git a/openapi/nexus-internal.json b/openapi/nexus-internal.json index 75daa8f5d8e..48657674d9b 100644 --- a/openapi/nexus-internal.json +++ b/openapi/nexus-internal.json @@ -1546,6 +1546,25 @@ } ] }, + "ArtifactHashId": { + "description": "A hash-based identifier for an artifact.\n\nSome places, e.g. the installinator, request artifacts by hash rather than by name and version. This type indicates that.", + "type": "object", + "properties": { + "hash": { + "description": "The hash of the artifact.", + "type": "string", + "format": "hex string (32 bytes)" + }, + "kind": { + "description": "The kind of artifact this is.", + "type": "string" + } + }, + "required": [ + "hash", + "kind" + ] + }, "BackgroundTask": { "description": "Background tasks\n\nThese are currently only intended for observability by developers. We will eventually want to flesh this out into something more observable for end users.", "type": "object", @@ -1629,6 +1648,24 @@ "serial" ] }, + "BaseboardId": { + "description": "A unique baseboard id found during a collection\n\nBaseboard ids are the keys used to link up information from disparate sources (like a service processor and a sled agent).\n\nThese are normalized in the database. Each distinct baseboard id is assigned a uuid and shared across the many possible collections that reference it.\n\nUsually, the part number and serial number are combined with a revision number. We do not include that here. If we ever did find a baseboard with the same part number and serial number but a new revision number, we'd want to treat that as the same baseboard as one with a different revision number.", + "type": "object", + "properties": { + "part_number": { + "description": "Oxide Part Number", + "type": "string" + }, + "serial_number": { + "description": "Serial number (unique for a given part number)", + "type": "string" + } + }, + "required": [ + "part_number", + "serial_number" + ] + }, "BfdMode": { "description": "BFD connection mode.", "type": "string", @@ -1917,6 +1954,13 @@ } ] }, + "pending_mgs_updates": { + "description": "List of pending MGS-mediated updates", + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/PendingMgsUpdate" + } + }, "sleds": { "description": "A map of sled id -> desired configuration of the sled.", "type": "object", @@ -1938,6 +1982,7 @@ "external_dns_version", "id", "internal_dns_version", + "pending_mgs_updates", "sleds", "time_created" ] @@ -4645,6 +4690,47 @@ "collector_id" ] }, + "PendingMgsUpdate": { + "type": "object", + "properties": { + "artifact_hash_id": { + "description": "which artifact to apply to this device (implies which component is being updated)", + "allOf": [ + { + "$ref": "#/components/schemas/ArtifactHashId" + } + ] + }, + "baseboard_id": { + "description": "id of the baseboard that we're going to update", + "allOf": [ + { + "$ref": "#/components/schemas/BaseboardId" + } + ] + }, + "slot_id": { + "description": "last known MGS slot (cubby number) of the baseboard", + "type": "integer", + "format": "uint16", + "minimum": 0 + }, + "sp_type": { + "description": "what type of baseboard this is", + "allOf": [ + { + "$ref": "#/components/schemas/SpType" + } + ] + } + }, + "required": [ + "artifact_hash_id", + "baseboard_id", + "slot_id", + "sp_type" + ] + }, "PhysicalDiskKind": { "description": "Describes the form factor of physical disks.", "type": "string", @@ -5766,6 +5852,15 @@ "last_port" ] }, + "SpType": { + "description": "SpType\n\n
JSON schema\n\n```json { \"type\": \"string\", \"enum\": [ \"sled\", \"power\", \"switch\" ] } ```
", + "type": "string", + "enum": [ + "sled", + "power", + "switch" + ] + }, "Srv": { "type": "object", "properties": { diff --git a/workspace-hack/Cargo.toml b/workspace-hack/Cargo.toml index 152db01ebe2..a36622c84d8 100644 --- a/workspace-hack/Cargo.toml +++ b/workspace-hack/Cargo.toml @@ -41,6 +41,7 @@ crossbeam-utils = { version = "0.8.20" } crossterm = { version = "0.28.1", features = ["event-stream", "serde"] } crypto-common = { version = "0.1.6", default-features = false, features = ["getrandom", "std"] } curve25519-dalek = { version = "4.1.3", features = ["digest", "legacy_compatibility", "rand_core"] } +daft = { version = "0.1.1", features = ["derive", "newtype-uuid1", "oxnet01", "uuid1"] } digest = { version = "0.10.7", features = ["mac", "oid", "std"] } dropshot = { version = "0.12.0", default-features = false, features = ["usdt-probes"] } ecdsa = { version = "0.16.9", features = ["pem", "signing", "std", "verifying"] } @@ -69,6 +70,8 @@ hyper = { version = "1.6.0", features = ["full"] } idna = { version = "1.0.3" } indexmap = { version = "2.7.1", features = ["serde"] } inout = { version = "0.1.3", default-features = false, features = ["std"] } +ipnetwork = { version = "0.21.1", features = ["schemars", "serde"] } +itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12.1" } itertools-93f6ce9d446188ac = { package = "itertools", version = "0.10.5" } lalrpop-util = { version = "0.19.12" } lazy_static = { version = "1.5.0", default-features = false, features = ["spin_no_std"] } @@ -162,6 +165,7 @@ crossbeam-utils = { version = "0.8.20" } crossterm = { version = "0.28.1", features = ["event-stream", "serde"] } crypto-common = { version = "0.1.6", default-features = false, features = ["getrandom", "std"] } curve25519-dalek = { version = "4.1.3", features = ["digest", "legacy_compatibility", "rand_core"] } +daft = { version = "0.1.1", features = ["derive", "newtype-uuid1", "oxnet01", "uuid1"] } digest = { version = "0.10.7", features = ["mac", "oid", "std"] } dropshot = { version = "0.12.0", default-features = false, features = ["usdt-probes"] } ecdsa = { version = "0.16.9", features = ["pem", "signing", "std", "verifying"] } @@ -190,6 +194,8 @@ hyper = { version = "1.6.0", features = ["full"] } idna = { version = "1.0.3" } indexmap = { version = "2.7.1", features = ["serde"] } inout = { version = "0.1.3", default-features = false, features = ["std"] } +ipnetwork = { version = "0.21.1", features = ["schemars", "serde"] } +itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12.1" } itertools-93f6ce9d446188ac = { package = "itertools", version = "0.10.5" } lalrpop-util = { version = "0.19.12" } lazy_static = { version = "1.5.0", default-features = false, features = ["spin_no_std"] } @@ -333,7 +339,6 @@ getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3.1", default hyper-rustls = { version = "0.27.3", default-features = false, features = ["http1", "http2", "ring", "tls12", "webpki-tokio"] } hyper-util = { version = "0.1.10", features = ["full"] } indicatif = { version = "0.17.11", features = ["rayon"] } -itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12.1" } mio = { version = "1.0.2", features = ["net", "os-ext"] } rustix = { version = "0.38.37", features = ["event", "fs", "net", "pipe", "process", "stdio", "system", "termios", "time"] } tokio-rustls = { version = "0.26.0", default-features = false, features = ["logging", "ring", "tls12"] } @@ -348,7 +353,6 @@ getrandom-468e82937335b1c9 = { package = "getrandom", version = "0.3.1", default hyper-rustls = { version = "0.27.3", default-features = false, features = ["http1", "http2", "ring", "tls12", "webpki-tokio"] } hyper-util = { version = "0.1.10", features = ["full"] } indicatif = { version = "0.17.11", features = ["rayon"] } -itertools-5ef9efb8ec2df382 = { package = "itertools", version = "0.12.1" } mio = { version = "1.0.2", features = ["net", "os-ext"] } rustix = { version = "0.38.37", features = ["event", "fs", "net", "pipe", "process", "stdio", "system", "termios", "time"] } tokio-rustls = { version = "0.26.0", default-features = false, features = ["logging", "ring", "tls12"] } From 28e948dd2b6f2026d40a0d1e354a077d0812e8ec Mon Sep 17 00:00:00 2001 From: David Pacheco Date: Mon, 10 Mar 2025 11:18:43 -0700 Subject: [PATCH 5/5] add "show" impl --- .../tests/output/cmd-example-stdout | 2 + .../output/cmd-expunge-newly-added-stdout | 3 ++ .../tests/output/cmd-set-zone-images-stdout | 3 ++ nexus/types/src/deployment.rs | 37 +++++++++++++++++-- .../types/src/deployment/blueprint_display.rs | 37 +++++++++++++++---- 5 files changed, 72 insertions(+), 10 deletions(-) diff --git a/dev-tools/reconfigurator-cli/tests/output/cmd-example-stdout b/dev-tools/reconfigurator-cli/tests/output/cmd-example-stdout index 5d55e23342e..26c432366e7 100644 --- a/dev-tools/reconfigurator-cli/tests/output/cmd-example-stdout +++ b/dev-tools/reconfigurator-cli/tests/output/cmd-example-stdout @@ -358,6 +358,7 @@ parent: 02697f74-b14a-4418-90f0-c28b2a3a6aa9 internal DNS version: 1 external DNS version: 1 + PENDING MGS-MANAGED UPDATES: 0 > @@ -453,5 +454,6 @@ parent: 02697f74-b14a-4418-90f0-c28b2a3a6aa9 internal DNS version: 1 external DNS version: 1 + PENDING MGS-MANAGED UPDATES: 0 diff --git a/dev-tools/reconfigurator-cli/tests/output/cmd-expunge-newly-added-stdout b/dev-tools/reconfigurator-cli/tests/output/cmd-expunge-newly-added-stdout index f557344223a..a359d5a472a 100644 --- a/dev-tools/reconfigurator-cli/tests/output/cmd-expunge-newly-added-stdout +++ b/dev-tools/reconfigurator-cli/tests/output/cmd-expunge-newly-added-stdout @@ -302,6 +302,7 @@ parent: 06c88262-f435-410e-ba98-101bed41ec27 internal DNS version: 1 external DNS version: 1 + PENDING MGS-MANAGED UPDATES: 0 > blueprint-edit 3f00b694-1b16-4aaa-8f78-e6b3a527b434 expunge-zone 9995de32-dd52-4eb1-b0eb-141eb84bc739 @@ -605,6 +606,7 @@ parent: 3f00b694-1b16-4aaa-8f78-e6b3a527b434 internal DNS version: 1 external DNS version: 1 + PENDING MGS-MANAGED UPDATES: 0 > blueprint-plan 366b0b68-d80e-4bc1-abd3-dc69837847e0 @@ -922,6 +924,7 @@ parent: 366b0b68-d80e-4bc1-abd3-dc69837847e0 internal DNS version: 1 external DNS version: 1 + PENDING MGS-MANAGED UPDATES: 0 > blueprint-edit 9c998c1d-1a7b-440a-ae0c-40f781dea6e2 expunge-zone d786ef4a-5acb-4f5d-a732-a00addf986b5 diff --git a/dev-tools/reconfigurator-cli/tests/output/cmd-set-zone-images-stdout b/dev-tools/reconfigurator-cli/tests/output/cmd-set-zone-images-stdout index da917b7ca0c..9ae291c7dd2 100644 --- a/dev-tools/reconfigurator-cli/tests/output/cmd-set-zone-images-stdout +++ b/dev-tools/reconfigurator-cli/tests/output/cmd-set-zone-images-stdout @@ -94,6 +94,7 @@ parent: 1b013011-2062-4b48-b544-a32b23bce83a internal DNS version: 1 external DNS version: 1 + PENDING MGS-MANAGED UPDATES: 0 > @@ -192,6 +193,7 @@ parent: 971eeb12-1830-4fa0-a699-98ea0164505c internal DNS version: 1 external DNS version: 1 + PENDING MGS-MANAGED UPDATES: 0 > blueprint-diff 971eeb12-1830-4fa0-a699-98ea0164505c 9766ca20-38d4-4380-b005-e7c43c797e7c @@ -455,6 +457,7 @@ parent: 9766ca20-38d4-4380-b005-e7c43c797e7c internal DNS version: 1 external DNS version: 1 + PENDING MGS-MANAGED UPDATES: 0 > blueprint-diff 9766ca20-38d4-4380-b005-e7c43c797e7c f714e6ea-e85a-4d7d-93c2-a018744fe176 diff --git a/nexus/types/src/deployment.rs b/nexus/types/src/deployment.rs index 2b245ed3231..7a05c8dab76 100644 --- a/nexus/types/src/deployment.rs +++ b/nexus/types/src/deployment.rs @@ -104,6 +104,7 @@ use id_map::{IdMap, IdMappable}; use crate::inventory::BaseboardId; pub use blueprint_diff::BlueprintDiffSummary; +use blueprint_display::BpPendingMgsUpdates; use gateway_client::types::SpType; use omicron_common::update::ArtifactHashId; @@ -481,9 +482,7 @@ impl fmt::Display for BlueprintDisplay<'_> { let Blueprint { id, sleds, - // TODO Will need to print these when we can actually create - // blueprints with pending MGS updates. - pending_mgs_updates: _, + pending_mgs_updates, parent_blueprint_id, // These two cockroachdb_* fields are handled by // `make_cockroachdb_table()`, called below. @@ -557,6 +556,38 @@ impl fmt::Display for BlueprintDisplay<'_> { writeln!(f, "{}", self.make_cockroachdb_table())?; writeln!(f, "{}", self.make_metadata_table())?; + writeln!( + f, + " PENDING MGS-MANAGED UPDATES: {}", + pending_mgs_updates.len() + )?; + if !pending_mgs_updates.is_empty() { + writeln!( + f, + "{}", + BpTable::new( + BpPendingMgsUpdates {}, + BpGeneration::NotApplicable, + pending_mgs_updates + .values() + .map(|pu| { + BpTableRow::from_strings( + BpDiffState::Unchanged, + vec![ + pu.sp_type.to_string(), + pu.slot_id.to_string(), + pu.baseboard_id.part_number.clone(), + pu.baseboard_id.serial_number.clone(), + pu.artifact_hash_id.kind.to_string(), + pu.artifact_hash_id.hash.to_string(), + ], + ) + }) + .collect() + ) + )?; + } + Ok(()) } } diff --git a/nexus/types/src/deployment/blueprint_display.rs b/nexus/types/src/deployment/blueprint_display.rs index 6d85014661f..bd2f3aa8f12 100644 --- a/nexus/types/src/deployment/blueprint_display.rs +++ b/nexus/types/src/deployment/blueprint_display.rs @@ -82,6 +82,9 @@ impl fmt::Display for BpDiffState { /// A wrapper aound generation numbers for blueprints or blueprint diffs #[derive(Debug, Clone, Copy)] pub enum BpGeneration { + // This value has no generation associated with it + NotApplicable, + // A value in a single blueprint Value(Generation), @@ -99,24 +102,25 @@ impl BpGeneration { impl fmt::Display for BpGeneration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + BpGeneration::NotApplicable => fmt::Result::Ok(()), BpGeneration::Value(generation) => { - write!(f, "at generation {generation}") + write!(f, " at generation {generation}") } BpGeneration::Diff { before: None, after: Some(after) } => { - write!(f, "at generation {after}") + write!(f, " at generation {after}") } BpGeneration::Diff { before: Some(before), after: None } => { - write!(f, "from generation {before}") + write!(f, " from generation {before}") } BpGeneration::Diff { before: Some(before), after: Some(after) } => { if before == after { - write!(f, "at generation {after}") + write!(f, " at generation {after}") } else { - write!(f, "generation {before} -> {after}") + write!(f, " generation {before} -> {after}") } } BpGeneration::Diff { before: None, after: None } => { - write!(f, "unknown generation") + write!(f, " unknown generation") } } } @@ -253,7 +257,7 @@ impl fmt::Display for BpTable { // Write the name of the subtable writeln!( f, - "{: &'static str { + "Pending MGS-managed updates" + } + + fn column_names(&self) -> &'static [&'static str] { + &[ + "sp_type", + "slot", + "part_number", + "serial_number", + "artifact_kind", + "artifact_hash", + ] + } +} + // An entry in a [`KvListWithHeading`] #[derive(Debug)] pub struct KvPair {