diff --git a/nexus/src/app/rack.rs b/nexus/src/app/rack.rs index 569153f23e..2b38c62b23 100644 --- a/nexus/src/app/rack.rs +++ b/nexus/src/app/rack.rs @@ -52,7 +52,6 @@ use omicron_common::api::external::ListResultVec; use omicron_common::api::external::LookupResult; use omicron_common::api::external::Name; use omicron_common::api::external::NameOrId; -use omicron_common::api::external::ResourceType; use omicron_common::api::internal::shared::ExternalPortDiscovery; use sled_agent_client::types::AddSledRequest; use sled_agent_client::types::EarlyNetworkConfigBody; @@ -871,36 +870,15 @@ impl super::Nexus { ) .await?; - // Grab the SPs from the last collection - let collection = - self.db_datastore.inventory_get_latest_collection(opctx).await?; - - // If there isn't a collection, we don't know about the sled - let Some(collection) = collection else { - return Err(Error::unavail("no inventory data available")); - }; - - // Find the revision - let Some(sp) = collection.sps.get(&baseboard_id) else { - return Err(Error::ObjectNotFound { - type_name: ResourceType::Sled, - lookup_type: - omicron_common::api::external::LookupType::ByCompositeId( - format!("{sled:?}"), - ), - }); - }; - - // Convert the baseboard as necessary - let baseboard = sled_agent_client::types::Baseboard::Gimlet { - identifier: sled.serial.clone(), - model: sled.part.clone(), - revision: sp.baseboard_revision.into(), + // Convert `UninitializedSledId` to the sled-agent type + let baseboard_id = sled_agent_client::types::BaseboardId { + serial_number: sled.serial.clone(), + part_number: sled.part.clone(), }; // Make the call to sled-agent let req = AddSledRequest { - sled_id: baseboard, + sled_id: baseboard_id, start_request: StartSledAgentRequest { generation: 0, schema_version: 1, diff --git a/openapi/sled-agent.json b/openapi/sled-agent.json index 4b53397ffb..395394defb 100644 --- a/openapi/sled-agent.json +++ b/openapi/sled-agent.json @@ -1240,7 +1240,7 @@ "type": "object", "properties": { "sled_id": { - "$ref": "#/components/schemas/Baseboard" + "$ref": "#/components/schemas/BaseboardId" }, "start_request": { "$ref": "#/components/schemas/StartSledAgentRequest" @@ -1319,6 +1319,24 @@ } ] }, + "BaseboardId": { + "description": "A representation of a Baseboard ID as used in the inventory subsystem This type is essentially the same as a `Baseboard` except it doesn't have a revision or HW type (Gimlet, PC, Unknown).", + "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" + ] + }, "BgpConfig": { "type": "object", "properties": { diff --git a/sled-agent/src/bootstrap/params.rs b/sled-agent/src/bootstrap/params.rs index 79189e7f49..b684d96763 100644 --- a/sled-agent/src/bootstrap/params.rs +++ b/sled-agent/src/bootstrap/params.rs @@ -174,10 +174,21 @@ impl TryFrom for RackInitializeRequest { pub type Certificate = nexus_client::types::Certificate; pub type RecoverySiloConfig = nexus_client::types::RecoverySiloConfig; +/// A representation of a Baseboard ID as used in the inventory subsystem +/// This type is essentially the same as a `Baseboard` except it doesn't have a +/// revision or HW type (Gimlet, PC, Unknown). +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] +pub struct BaseboardId { + /// Oxide Part Number + pub part_number: String, + /// Serial number (unique for a given part number) + pub serial_number: String, +} + /// A request to Add a given sled after rack initialization has occurred #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] pub struct AddSledRequest { - pub sled_id: Baseboard, + pub sled_id: BaseboardId, pub start_request: StartSledAgentRequest, } @@ -255,9 +266,6 @@ pub struct StartSledAgentRequestBody { /// true. pub is_lrtq_learner: bool, - // Note: The order of these fields is load bearing, because we serialize - // `SledAgentRequest`s as toml. `subnet` serializes as a TOML table, so it - // must come after non-table fields. /// Portion of the IP space to be managed by the Sled Agent. pub subnet: Ipv6Subnet, } diff --git a/sled-agent/src/sled_agent.rs b/sled-agent/src/sled_agent.rs index eaf354db26..bcc354232e 100644 --- a/sled-agent/src/sled_agent.rs +++ b/sled-agent/src/sled_agent.rs @@ -9,7 +9,7 @@ use crate::bootstrap::config::BOOTSTRAP_AGENT_RACK_INIT_PORT; use crate::bootstrap::early_networking::{ EarlyNetworkConfig, EarlyNetworkSetupError, }; -use crate::bootstrap::params::StartSledAgentRequest; +use crate::bootstrap::params::{BaseboardId, StartSledAgentRequest}; use crate::config::Config; use crate::instance_manager::{InstanceManager, ReservoirMode}; use crate::long_running_tasks::LongRunningTaskHandles; @@ -1187,8 +1187,8 @@ pub enum AddSledError { }, #[error("Failed to connect to DDM")] DdmAdminClient(#[source] ddm_admin_client::DdmError), - #[error("Failed to learn bootstrap ip for {0}")] - NotFound(Baseboard), + #[error("Failed to learn bootstrap ip for {0:?}")] + NotFound(BaseboardId), #[error("Failed to initialize {sled_id}: {err}")] BootstrapTcpClient { sled_id: Baseboard, @@ -1199,7 +1199,7 @@ pub enum AddSledError { /// Add a sled to an initialized rack. pub async fn sled_add( log: Logger, - sled_id: Baseboard, + sled_id: BaseboardId, request: StartSledAgentRequest, ) -> Result<(), AddSledError> { // Get all known bootstrap addresses via DDM @@ -1227,16 +1227,20 @@ pub async fn sled_add( }) .collect::>(); - // Execute the futures until we find our matching sled or done searching + // Execute the futures until we find our matching sled or are done searching let mut target_ip = None; + let mut found_baseboard = None; while let Some((ip, result)) = addrs_to_sleds.next().await { match result { Ok(baseboard) => { // Convert from progenitor type back to `sled-hardware` // type. - let found = baseboard.into_inner().into(); - if sled_id == found { + let found: Baseboard = baseboard.into_inner().into(); + if sled_id.serial_number == found.identifier() + && sled_id.part_number == found.model() + { target_ip = Some(ip); + found_baseboard = Some(found); break; } } @@ -1259,10 +1263,14 @@ pub async fn sled_add( log.new(o!("BootstrapAgentClient" => bootstrap_addr.to_string())), ); + // Safe to unwrap, because we would have bailed when checking target_ip + // above otherwise. baseboard and target_ip are set together. + let baseboard = found_baseboard.unwrap(); + client.start_sled_agent(&request).await.map_err(|err| { - AddSledError::BootstrapTcpClient { sled_id: sled_id.clone(), err } + AddSledError::BootstrapTcpClient { sled_id: baseboard.clone(), err } })?; - info!(log, "Peer agent initialized"; "peer_bootstrap_addr" => %bootstrap_addr, "peer_id" => %sled_id); + info!(log, "Peer agent initialized"; "peer_bootstrap_addr" => %bootstrap_addr, "peer_id" => %baseboard); Ok(()) }