From c352f46357d96ff7aec229bb7616806ec3eff196 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Tue, 16 Jul 2024 13:14:51 -0400 Subject: [PATCH 1/3] Remove sprockets (#6087) The approach in the existing sprockets code was never fully implemented. We're going to replace it with something else. Just remove the old code. --- Cargo.lock | 104 ++++-------------------------------------- Cargo.toml | 5 -- sp-sim/Cargo.toml | 1 - sp-sim/src/gimlet.rs | 24 ---------- sp-sim/src/lib.rs | 14 ------ sp-sim/src/rot.rs | 46 ------------------- sp-sim/src/sidecar.rs | 24 ---------- 7 files changed, 8 insertions(+), 210 deletions(-) delete mode 100644 sp-sim/src/rot.rs diff --git a/Cargo.lock b/Cargo.lock index 22e647c69c..95420642e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1220,12 +1220,6 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" -[[package]] -name = "corncobs" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9236877021b66ad90f833d8a73a7acb702b985b64c5986682d9f1f1a184f0fb" - [[package]] name = "cpufeatures" version = "0.2.12" @@ -2123,19 +2117,10 @@ dependencies = [ "digest", "elliptic-curve", "rfc6979", - "signature 2.2.0", + "signature", "spki", ] -[[package]] -name = "ed25519" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" -dependencies = [ - "signature 1.6.4", -] - [[package]] name = "ed25519" version = "2.2.3" @@ -2143,7 +2128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", - "signature 2.2.0", + "signature", ] [[package]] @@ -2153,7 +2138,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", - "ed25519 2.2.3", + "ed25519", "rand_core 0.6.4", "serde", "sha2", @@ -2715,7 +2700,7 @@ version = "0.1.0" source = "git+https://github.com/oxidecomputer/management-gateway-service?rev=c85a4ca043aaa389df12aac5348d8a3feda28762#c85a4ca043aaa389df12aac5348d8a3feda28762" dependencies = [ "bitflags 2.5.0", - "hubpack 0.1.2", + "hubpack", "serde", "serde_repr", "smoltcp 0.9.1", @@ -2736,14 +2721,14 @@ dependencies = [ "fxhash", "gateway-messages", "hex", - "hubpack 0.1.2", + "hubpack", "hubtools", "lru-cache", "nix 0.27.1", "once_cell", "paste", "serde", - "serde-big-array 0.5.1", + "serde-big-array", "slog", "slog-error-chain", "socket2 0.5.7", @@ -3225,35 +3210,16 @@ dependencies = [ "tokio", ] -[[package]] -name = "hubpack" -version = "0.1.0" -source = "git+https://github.com/cbiffle/hubpack.git?rev=df08cc3a6e1f97381cd0472ae348e310f0119e25#df08cc3a6e1f97381cd0472ae348e310f0119e25" -dependencies = [ - "hubpack_derive 0.1.0", - "serde", -] - [[package]] name = "hubpack" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a0b84aeae519f65e0ba3aa998327080993426024edbd5cc38dbaf5ec524303" dependencies = [ - "hubpack_derive 0.1.1", + "hubpack_derive", "serde", ] -[[package]] -name = "hubpack_derive" -version = "0.1.0" -source = "git+https://github.com/cbiffle/hubpack.git?rev=df08cc3a6e1f97381cd0472ae348e310f0119e25#df08cc3a6e1f97381cd0472ae348e310f0119e25" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "hubpack_derive" version = "0.1.1" @@ -8035,7 +8001,7 @@ dependencies = [ "rand_core 0.6.4", "serde", "sha2", - "signature 2.2.0", + "signature", "spki", "subtle", "zeroize", @@ -8380,17 +8346,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" -[[package]] -name = "salty" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77cdd38ed8bfe51e53ee991aae0791b94349d0a05cfdecd283835a8a965d4c37" -dependencies = [ - "ed25519 1.5.3", - "subtle", - "zeroize", -] - [[package]] name = "samael" version = "0.0.15" @@ -8578,15 +8533,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-big-array" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3323f09a748af288c3dc2474ea6803ee81f118321775bffa3ac8f7e65c5e90e7" -dependencies = [ - "serde", -] - [[package]] name = "serde-big-array" version = "0.5.1" @@ -8850,12 +8796,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "signature" -version = "1.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" - [[package]] name = "signature" version = "2.2.0" @@ -9263,7 +9203,6 @@ dependencies = [ "serde", "slog", "slog-dtrace", - "sprockets-rot", "thiserror", "tokio", "toml 0.8.14", @@ -9294,33 +9233,6 @@ dependencies = [ "der", ] -[[package]] -name = "sprockets-common" -version = "0.1.0" -source = "git+https://github.com/oxidecomputer/sprockets?rev=77df31efa5619d0767ffc837ef7468101608aee9#77df31efa5619d0767ffc837ef7468101608aee9" -dependencies = [ - "derive_more", - "hubpack 0.1.0", - "salty", - "serde", - "serde-big-array 0.4.1", -] - -[[package]] -name = "sprockets-rot" -version = "0.1.0" -source = "git+https://github.com/oxidecomputer/sprockets?rev=77df31efa5619d0767ffc837ef7468101608aee9#77df31efa5619d0767ffc837ef7468101608aee9" -dependencies = [ - "corncobs", - "derive_more", - "hubpack 0.1.0", - "rand 0.8.5", - "salty", - "serde", - "sprockets-common", - "tinyvec", -] - [[package]] name = "sqlformat" version = "0.2.4" diff --git a/Cargo.toml b/Cargo.toml index 96f962708a..6c67dbd6c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -483,9 +483,6 @@ slog-term = "2.9.1" smf = "0.2" socket2 = { version = "0.5", features = ["all"] } sp-sim = { path = "sp-sim" } -sprockets-common = { git = "https://github.com/oxidecomputer/sprockets", rev = "77df31efa5619d0767ffc837ef7468101608aee9" } -sprockets-host = { git = "https://github.com/oxidecomputer/sprockets", rev = "77df31efa5619d0767ffc837ef7468101608aee9" } -sprockets-rot = { git = "https://github.com/oxidecomputer/sprockets", rev = "77df31efa5619d0767ffc837ef7468101608aee9" } sqlformat = "0.2.4" sqlparser = { version = "0.45.0", features = [ "visitor" ] } static_assertions = "1.1.0" @@ -683,8 +680,6 @@ opt-level = 3 opt-level = 3 [profile.dev.package.rsa] opt-level = 3 -[profile.dev.package.salty] -opt-level = 3 [profile.dev.package.signature] opt-level = 3 [profile.dev.package.subtle] diff --git a/sp-sim/Cargo.toml b/sp-sim/Cargo.toml index 35cb791f4c..7270db1a67 100644 --- a/sp-sim/Cargo.toml +++ b/sp-sim/Cargo.toml @@ -20,7 +20,6 @@ omicron-common.workspace = true serde.workspace = true slog.workspace = true slog-dtrace.workspace = true -sprockets-rot.workspace = true thiserror.workspace = true tokio = { workspace = true, features = [ "full" ] } toml.workspace = true diff --git a/sp-sim/src/gimlet.rs b/sp-sim/src/gimlet.rs index 4e0b264e64..ac465cb217 100644 --- a/sp-sim/src/gimlet.rs +++ b/sp-sim/src/gimlet.rs @@ -6,7 +6,6 @@ use crate::config::GimletConfig; use crate::config::SpComponentConfig; use crate::helpers::rot_slot_id_from_u16; use crate::helpers::rot_slot_id_to_u16; -use crate::rot::RotSprocketExt; use crate::serial_number_padded; use crate::server; use crate::server::SimSpHandler; @@ -38,9 +37,6 @@ use gateway_messages::{version, MessageKind}; use gateway_messages::{ComponentDetails, Message, MgsError, StartupOptions}; use gateway_messages::{DiscoverResponse, IgnitionState, PowerState}; use slog::{debug, error, info, warn, Logger}; -use sprockets_rot::common::msgs::{RotRequestV1, RotResponseV1}; -use sprockets_rot::common::Ed25519PublicKey; -use sprockets_rot::{RotSprocket, RotSprocketError}; use std::cell::Cell; use std::collections::HashMap; use std::iter; @@ -88,8 +84,6 @@ pub enum SimSpHandledRequest { } pub struct Gimlet { - rot: Mutex, - manufacturing_public_key: Ed25519PublicKey, local_addrs: Option<[SocketAddrV6; 2]>, handler: Option>>, serial_console_addrs: HashMap, @@ -116,10 +110,6 @@ impl SimulatedSp for Gimlet { ) } - fn manufacturing_public_key(&self) -> Ed25519PublicKey { - self.manufacturing_public_key - } - fn local_addr(&self, port: SpPort) -> Option { let i = match port { SpPort::One => 0, @@ -135,13 +125,6 @@ impl SimulatedSp for Gimlet { } } - fn rot_request( - &self, - request: RotRequestV1, - ) -> Result { - self.rot.lock().unwrap().handle_deserialized(request) - } - async fn last_sp_update_data(&self) -> Option> { let handler = self.handler.as_ref()?; let handler = handler.lock().await; @@ -201,16 +184,11 @@ impl Gimlet { let (commands, commands_rx) = mpsc::unbounded_channel(); let last_request_handled = Arc::default(); - let (manufacturing_public_key, rot) = - RotSprocket::bootstrap_from_config(&gimlet.common); - // Weird case - if we don't have any bind addresses, we're only being // created to simulate an RoT, so go ahead and return without actually // starting a simulated SP. let Some(bind_addrs) = gimlet.common.bind_addrs else { return Ok(Self { - rot: Mutex::new(rot), - manufacturing_public_key, local_addrs: None, handler: None, serial_console_addrs, @@ -299,8 +277,6 @@ impl Gimlet { .push(task::spawn(async move { inner.run().await.unwrap() })); Ok(Self { - rot: Mutex::new(rot), - manufacturing_public_key, local_addrs: Some(local_addrs), handler: Some(handler), serial_console_addrs, diff --git a/sp-sim/src/lib.rs b/sp-sim/src/lib.rs index ca9231bec0..868d7ded2c 100644 --- a/sp-sim/src/lib.rs +++ b/sp-sim/src/lib.rs @@ -5,7 +5,6 @@ pub mod config; mod gimlet; mod helpers; -mod rot; mod server; mod sidecar; mod update; @@ -21,10 +20,6 @@ pub use server::logger; pub use sidecar::Sidecar; pub use sidecar::SIM_SIDECAR_BOARD; pub use slog::Logger; -pub use sprockets_rot::common::msgs::RotRequestV1; -pub use sprockets_rot::common::msgs::RotResponseV1; -use sprockets_rot::common::Ed25519PublicKey; -pub use sprockets_rot::RotSprocketError; use std::net::SocketAddrV6; use tokio::sync::mpsc; use tokio::sync::watch; @@ -43,9 +38,6 @@ pub trait SimulatedSp { /// Serial number. async fn state(&self) -> omicron_gateway::http_entrypoints::SpState; - /// Public key for the manufacturing cert used to sign this SP's RoT certs. - fn manufacturing_public_key(&self) -> Ed25519PublicKey; - /// Listening UDP address of the given port of this simulated SP, if it was /// configured to listen. fn local_addr(&self, port: SpPort) -> Option; @@ -54,12 +46,6 @@ pub trait SimulatedSp { /// messages. async fn set_responsiveness(&self, r: Responsiveness); - /// Send a request to the (simulated) RoT. - fn rot_request( - &self, - request: RotRequestV1, - ) -> Result; - /// Get the last completed update delivered to this simulated SP. /// /// Only returns data after a simulated reset of the SP. diff --git a/sp-sim/src/rot.rs b/sp-sim/src/rot.rs deleted file mode 100644 index 9f0bf61cc0..0000000000 --- a/sp-sim/src/rot.rs +++ /dev/null @@ -1,46 +0,0 @@ -// 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/. - -//! Simualting a Root of Trust - -use crate::config::SpCommonConfig; -use sprockets_rot::common::certificates::SerialNumber; -use sprockets_rot::common::Ed25519PublicKey; -use sprockets_rot::salty; -use sprockets_rot::RotConfig; -use sprockets_rot::RotSprocket; - -pub(crate) trait RotSprocketExt { - // Returns the (derived-from-config) manufacturing public key and the - // `RotSprocket`. - fn bootstrap_from_config( - config: &SpCommonConfig, - ) -> (Ed25519PublicKey, Self); -} - -impl RotSprocketExt for RotSprocket { - fn bootstrap_from_config( - config: &SpCommonConfig, - ) -> (Ed25519PublicKey, Self) { - let mut serial_number = [0; 16]; - serial_number - .get_mut(0..config.serial_number.len()) - .expect("simulated serial number too long") - .copy_from_slice(config.serial_number.as_bytes()); - - let manufacturing_keypair = - salty::Keypair::from(&config.manufacturing_root_cert_seed); - let device_id_keypair = - salty::Keypair::from(&config.device_id_cert_seed); - let serial_number = SerialNumber(serial_number); - let config = RotConfig::bootstrap_for_testing( - &manufacturing_keypair, - device_id_keypair, - serial_number, - ); - let manufacturing_public_key = - Ed25519PublicKey(manufacturing_keypair.public.to_bytes()); - (manufacturing_public_key, Self::new(config)) - } -} diff --git a/sp-sim/src/sidecar.rs b/sp-sim/src/sidecar.rs index 696989f791..a6bc49e609 100644 --- a/sp-sim/src/sidecar.rs +++ b/sp-sim/src/sidecar.rs @@ -8,7 +8,6 @@ use crate::config::SimulatedSpsConfig; use crate::config::SpComponentConfig; use crate::helpers::rot_slot_id_from_u16; use crate::helpers::rot_slot_id_to_u16; -use crate::rot::RotSprocketExt; use crate::serial_number_padded; use crate::server; use crate::server::SimSpHandler; @@ -49,16 +48,10 @@ use slog::debug; use slog::info; use slog::warn; use slog::Logger; -use sprockets_rot::common::msgs::RotRequestV1; -use sprockets_rot::common::msgs::RotResponseV1; -use sprockets_rot::common::Ed25519PublicKey; -use sprockets_rot::RotSprocket; -use sprockets_rot::RotSprocketError; use std::iter; use std::net::SocketAddrV6; use std::pin::Pin; use std::sync::Arc; -use std::sync::Mutex; use tokio::select; use tokio::sync::mpsc; use tokio::sync::oneshot; @@ -70,8 +63,6 @@ use tokio::task::JoinHandle; pub const SIM_SIDECAR_BOARD: &str = "SimSidecarSp"; pub struct Sidecar { - rot: Mutex, - manufacturing_public_key: Ed25519PublicKey, local_addrs: Option<[SocketAddrV6; 2]>, handler: Option>>, commands: mpsc::UnboundedSender, @@ -96,10 +87,6 @@ impl SimulatedSp for Sidecar { ) } - fn manufacturing_public_key(&self) -> Ed25519PublicKey { - self.manufacturing_public_key - } - fn local_addr(&self, port: SpPort) -> Option { let i = match port { SpPort::One => 0, @@ -117,13 +104,6 @@ impl SimulatedSp for Sidecar { rx.await.unwrap(); } - fn rot_request( - &self, - request: RotRequestV1, - ) -> Result { - self.rot.lock().unwrap().handle_deserialized(request) - } - async fn last_sp_update_data(&self) -> Option> { let handler = self.handler.as_ref()?; let handler = handler.lock().await; @@ -224,11 +204,7 @@ impl Sidecar { (None, None, None, None) }; - let (manufacturing_public_key, rot) = - RotSprocket::bootstrap_from_config(&sidecar.common); Ok(Self { - rot: Mutex::new(rot), - manufacturing_public_key, local_addrs, handler, commands, From 6344feb5737680033524787a5618f3cdda358224 Mon Sep 17 00:00:00 2001 From: Michael Zeller Date: Tue, 16 Jul 2024 15:06:05 -0400 Subject: [PATCH 2/3] Restore nvme firmware log query (#6026) This restores the nvme firmware log query that was removed for R9. --- sled-hardware/src/illumos/mod.rs | 43 +++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/sled-hardware/src/illumos/mod.rs b/sled-hardware/src/illumos/mod.rs index ff627f3e6e..18e360c919 100644 --- a/sled-hardware/src/illumos/mod.rs +++ b/sled-hardware/src/illumos/mod.rs @@ -9,6 +9,7 @@ use crate::{ use camino::Utf8PathBuf; use gethostname::gethostname; use illumos_devinfo::{DevInfo, DevLinkType, DevLinks, Node, Property}; +use libnvme::{controller::Controller, Nvme}; use omicron_common::disk::DiskIdentity; use sled_hardware_types::Baseboard; use slog::debug; @@ -58,6 +59,9 @@ enum Error { #[error("Failed to issue request to sysconf: {0}")] SysconfError(#[from] sysconf::Error), + #[error("Node {node} missing device instance")] + MissingNvmeDevinfoInstance { node: String }, + #[error("Failed to init nvme handle: {0}")] NvmeHandleInit(#[from] libnvme::NvmeInitError), @@ -67,6 +71,9 @@ enum Error { #[error("libnvme controller error: {0}")] NvmeController(#[from] libnvme::controller::NvmeControllerError), + #[error("Unable to grab NVMe Controller lock")] + NvmeControllerLocked, + #[error("Failed to get NVMe Controller's firmware log page: {0}")] FirmwareLogPage(#[from] libnvme::firmware::FirmwareLogPageError), } @@ -493,6 +500,13 @@ fn poll_blkdev_node( // We expect that the parent of the "blkdev" node is an "nvme" driver. let nvme_node = get_parent_node(&node, "nvme")?; + // Importantly we grab the NVMe instance and not the blkdev instance. + // Eventually we should switch the logic here to search for nvme instances + // and confirm that we only have one blkdev sibling: + // https://github.com/oxidecomputer/omicron/issues/5241 + let nvme_instance = nvme_node + .instance() + .ok_or(Error::MissingNvmeDevinfoInstance { node: node.node_name() })?; let vendor_id = i64_from_property(&find_properties(&nvme_node, ["vendor-id"])?[0])?; @@ -526,10 +540,31 @@ fn poll_blkdev_node( return Err(Error::UnrecognizedSlot { slot }); }; - // XXX See https://github.com/oxidecomputer/meta/issues/443 - // Temporarily providing static data until the issue is resolved. - let firmware = - DiskFirmware::new(1, None, true, vec![Some("meta-443".to_string())]); + let nvme = Nvme::new()?; + let controller = Controller::init_by_instance(&nvme, nvme_instance)?; + let controller_lock = match controller.try_read_lock() { + libnvme::controller::TryLockResult::Ok(locked) => locked, + // We should only hit this if something in the system has locked the + // controller in question for writing. + libnvme::controller::TryLockResult::Locked(_) => { + warn!( + log, + "NVMe Controller is already locked so we will try again + in the next hardware snapshot" + ); + return Err(Error::NvmeControllerLocked); + } + libnvme::controller::TryLockResult::Err(err) => { + return Err(Error::from(err)) + } + }; + let firmware_log_page = controller_lock.get_firmware_log_page()?; + let firmware = DiskFirmware::new( + firmware_log_page.active_slot, + firmware_log_page.next_active_slot, + firmware_log_page.slot1_is_read_only, + firmware_log_page.slot_iter().map(|s| s.map(str::to_string)).collect(), + ); let disk = UnparsedDisk::new( Utf8PathBuf::from(&devfs_path), From 8316247eaf4c629301be3bbafaa8c77aca93eb13 Mon Sep 17 00:00:00 2001 From: Benjamin Naecker Date: Tue, 16 Jul 2024 12:29:01 -0700 Subject: [PATCH 3/3] Add sled-agent endpoint for fetching sled identifiers (#6086) - Adds a `SledIdentifiers` type, with the most salient bits of identifying metadata for a single sled - Adds sled-agent endpoint `/sled-identifiers` for fetching the above from the sled. The main goal is to provide the main data for #5267, attaching sled identifiers to most timeseries. - Small improvement to instructions in package-manifest.toml. - Small bugfix to error-reporting in `xtask download`. --- clients/sled-agent-client/src/lib.rs | 30 ++++++++++++++ common/src/api/internal/shared.rs | 20 +++++++++ dev-tools/xtask/src/download.rs | 2 +- openapi/sled-agent.json | 61 ++++++++++++++++++++++++++++ package-manifest.toml | 12 ++++-- sled-agent/src/http_entrypoints.rs | 19 ++++++++- sled-agent/src/sled_agent.rs | 30 +++++++++++++- 7 files changed, 168 insertions(+), 6 deletions(-) diff --git a/clients/sled-agent-client/src/lib.rs b/clients/sled-agent-client/src/lib.rs index 42eefaf8b5..8a63cecd4f 100644 --- a/clients/sled-agent-client/src/lib.rs +++ b/clients/sled-agent-client/src/lib.rs @@ -593,6 +593,36 @@ impl From } } +impl From + for types::SledIdentifiers +{ + fn from( + value: omicron_common::api::internal::shared::SledIdentifiers, + ) -> Self { + Self { + model: value.model, + rack_id: value.rack_id, + revision: value.revision, + serial: value.serial, + sled_id: value.sled_id, + } + } +} + +impl From + for omicron_common::api::internal::shared::SledIdentifiers +{ + fn from(value: types::SledIdentifiers) -> Self { + Self { + model: value.model, + rack_id: value.rack_id, + revision: value.revision, + serial: value.serial, + sled_id: value.sled_id, + } + } +} + /// Exposes additional [`Client`] interfaces for use by the test suite. These /// are bonus endpoints, not generated in the real client. #[async_trait] diff --git a/common/src/api/internal/shared.rs b/common/src/api/internal/shared.rs index 884b4dc165..24bb339112 100644 --- a/common/src/api/internal/shared.rs +++ b/common/src/api/internal/shared.rs @@ -702,6 +702,26 @@ pub struct ResolvedVpcRouteSet { pub routes: HashSet, } +/// Identifiers for a single sled. +/// +/// This is intended primarily to be used in timeseries, to identify +/// sled from which metric data originates. +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] +pub struct SledIdentifiers { + /// Control plane ID of the rack this sled is a member of + pub rack_id: Uuid, + /// Control plane ID for the sled itself + pub sled_id: Uuid, + /// Model name of the sled + pub model: String, + /// Revision number of the sled + pub revision: u32, + /// Serial number of the sled + // + // NOTE: This is only guaranteed to be unique within a model. + pub serial: String, +} + #[cfg(test)] mod tests { use crate::api::internal::shared::AllowedSourceIps; diff --git a/dev-tools/xtask/src/download.rs b/dev-tools/xtask/src/download.rs index 37c9b7be8a..b5910e3915 100644 --- a/dev-tools/xtask/src/download.rs +++ b/dev-tools/xtask/src/download.rs @@ -242,7 +242,7 @@ async fn get_values_from_file( let content = tokio::fs::read_to_string(&path) .await - .context("Failed to read {path}")?; + .with_context(|| format!("Failed to read {path}"))?; for line in content.lines() { let line = line.trim(); let Some((key, value)) = line.split_once('=') else { diff --git a/openapi/sled-agent.json b/openapi/sled-agent.json index 8165cfa9d6..13036f115b 100644 --- a/openapi/sled-agent.json +++ b/openapi/sled-agent.json @@ -710,6 +710,30 @@ } } }, + "/sled-identifiers": { + "get": { + "summary": "Fetch sled identifiers", + "operationId": "sled_identifiers", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SledIdentifiers" + } + } + } + }, + "4XX": { + "$ref": "#/components/responses/Error" + }, + "5XX": { + "$ref": "#/components/responses/Error" + } + } + } + }, "/sled-role": { "get": { "operationId": "sled_role_get", @@ -4549,6 +4573,43 @@ "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, + "SledIdentifiers": { + "description": "Identifiers for a single sled.\n\nThis is intended primarily to be used in timeseries, to identify sled from which metric data originates.", + "type": "object", + "properties": { + "model": { + "description": "Model name of the sled", + "type": "string" + }, + "rack_id": { + "description": "Control plane ID of the rack this sled is a member of", + "type": "string", + "format": "uuid" + }, + "revision": { + "description": "Revision number of the sled", + "type": "integer", + "format": "uint32", + "minimum": 0 + }, + "serial": { + "description": "Serial number of the sled", + "type": "string" + }, + "sled_id": { + "description": "Control plane ID for the sled itself", + "type": "string", + "format": "uuid" + } + }, + "required": [ + "model", + "rack_id", + "revision", + "serial", + "sled_id" + ] + }, "SledInstanceState": { "description": "A wrapper type containing a sled's total knowledge of the state of a specific VMM and the instance it incarnates.", "type": "object", diff --git a/package-manifest.toml b/package-manifest.toml index 561a61ec4c..2eb643ecb0 100644 --- a/package-manifest.toml +++ b/package-manifest.toml @@ -639,8 +639,10 @@ only_for_targets.image = "standard" # 1. Build the zone image manually # 1a. cd # 1b. cargo build --features=tofino_stub --release -# 1c. cargo xtask dist -o -r --features tofino_stub +# 1c. cargo xtask dist --format omicron --release --features tofino_stub # 2. Copy dendrite.tar.gz from dendrite/out to omicron/out +# 3. Change the below `source.type` key to `"manual"` and comment out or remove +# the other `source.*` keys. source.type = "prebuilt" source.repo = "dendrite" source.commit = "e83f4f164fd3dbb2100989a399a4fa087232ac36" @@ -664,8 +666,10 @@ only_for_targets.image = "standard" # 1. Build the zone image manually # 1a. cd # 1b. cargo build --features=tofino_asic --release -# 1c. cargo xtask dist -o -r --features tofino_asic +# 1c. cargo xtask dist --format omicron --release --features tofino_asic # 2. Copy the output zone image from dendrite/out to omicron/out +# 3. Change the below `source.type` key to `"manual"` and comment out or remove +# the other `source.*` keys. source.type = "prebuilt" source.repo = "dendrite" source.commit = "e83f4f164fd3dbb2100989a399a4fa087232ac36" @@ -682,8 +686,10 @@ only_for_targets.image = "standard" # 1. Build the zone image manually # 1a. cd # 1b. cargo build --features=softnpu --release -# 1c. cargo xtask dist -o -r --features softnpu +# 1c. cargo xtask dist --format omicron --release --features softnpu # 2. Copy dendrite.tar.gz from dendrite/out to omicron/out/dendrite-softnpu.tar.gz +# 3. Change the below `source.type` key to `"manual"` and comment out or remove +# the other `source.*` keys. source.type = "prebuilt" source.repo = "dendrite" source.commit = "e83f4f164fd3dbb2100989a399a4fa087232ac36" diff --git a/sled-agent/src/http_entrypoints.rs b/sled-agent/src/http_entrypoints.rs index a21c278699..ff8c13105a 100644 --- a/sled-agent/src/http_entrypoints.rs +++ b/sled-agent/src/http_entrypoints.rs @@ -32,7 +32,7 @@ use omicron_common::api::internal::nexus::{ DiskRuntimeState, SledInstanceState, UpdateArtifactId, }; use omicron_common::api::internal::shared::{ - ResolvedVpcRouteSet, ResolvedVpcRouteState, SwitchPorts, + ResolvedVpcRouteSet, ResolvedVpcRouteState, SledIdentifiers, SwitchPorts, }; use omicron_uuid_kinds::{GenericUuid, InstanceUuid}; use schemars::JsonSchema; @@ -89,6 +89,7 @@ pub fn api() -> SledApiDescription { api.register(host_os_write_status_get)?; api.register(host_os_write_status_delete)?; api.register(inventory)?; + api.register(sled_identifiers)?; api.register(bootstore_status)?; api.register(list_vpc_routes)?; api.register(set_vpc_routes)?; @@ -1012,6 +1013,22 @@ async fn inventory( Ok(HttpResponseOk(sa.inventory().await?)) } +/// Fetch sled identifiers +#[endpoint { + method = GET, + path = "/sled-identifiers", +}] +async fn sled_identifiers( + request_context: RequestContext, +) -> Result, HttpError> { + request_context + .context() + .sled_identifiers() + .await + .map(HttpResponseOk) + .map_err(HttpError::from) +} + /// Get the internal state of the local bootstore node #[endpoint { method = GET, diff --git a/sled-agent/src/sled_agent.rs b/sled-agent/src/sled_agent.rs index 9832144791..23a13487ef 100644 --- a/sled-agent/src/sled_agent.rs +++ b/sled-agent/src/sled_agent.rs @@ -51,7 +51,7 @@ use omicron_common::api::internal::nexus::{ }; use omicron_common::api::internal::shared::{ HostPortConfig, RackNetworkConfig, ResolvedVpcRouteSet, - ResolvedVpcRouteState, + ResolvedVpcRouteState, SledIdentifiers, }; use omicron_common::api::{ internal::nexus::DiskRuntimeState, internal::nexus::InstanceRuntimeState, @@ -156,6 +156,9 @@ pub enum Error { #[error("Metrics error: {0}")] Metrics(#[from] crate::metrics::Error), + + #[error("Expected revision to fit in a u32, but found {0}")] + UnexpectedRevision(i64), } impl From for omicron_common::api::external::Error { @@ -1185,6 +1188,31 @@ impl SledAgent { &self.inner.boot_disk_os_writer } + /// Return identifiers for this sled. + /// + /// This is mostly used to identify timeseries data with the originating + /// sled. + /// + /// NOTE: This only returns the identifiers for the _sled_ itself. If you're + /// interested in the switch identifiers, MGS is the current best way to do + /// that, by asking for the local switch's slot, and then that switch's SP + /// state. + pub(crate) async fn sled_identifiers( + &self, + ) -> Result { + let baseboard = self.inner.hardware.baseboard(); + Ok(SledIdentifiers { + rack_id: self.inner.start_request.body.rack_id, + sled_id: self.inner.id, + model: baseboard.model().to_string(), + revision: baseboard + .revision() + .try_into() + .map_err(|_| Error::UnexpectedRevision(baseboard.revision()))?, + serial: baseboard.identifier().to_string(), + }) + } + /// Return basic information about ourselves: identity and status /// /// This is basically a GET version of the information we push to Nexus on