diff --git a/nexus/db-model/src/rack.rs b/nexus/db-model/src/rack.rs index 580ec155b44..c236e9ff7bd 100644 --- a/nexus/db-model/src/rack.rs +++ b/nexus/db-model/src/rack.rs @@ -4,8 +4,9 @@ use crate::schema::rack; use db_macros::Asset; -use ipnetwork::IpNetwork; +use ipnetwork::{IpNetwork, Ipv6Network}; use nexus_types::{external_api::views, identity::Asset}; +use omicron_common::api; use uuid::Uuid; /// Information about a local rack. @@ -28,6 +29,22 @@ impl Rack { rack_subnet: None, } } + + pub fn get_subnet(&self) -> Result { + match self.rack_subnet { + Some(IpNetwork::V6(subnet)) => Ok(subnet), + Some(IpNetwork::V4(_)) => { + return Err(api::external::Error::InternalError { + internal_message: "rack subnet not IPv6".into(), + }) + } + None => { + return Err(api::external::Error::InternalError { + internal_message: "rack subnet not set".into(), + }) + } + } + } } impl From for views::Rack { diff --git a/nexus/db-queries/src/db/datastore/inventory.rs b/nexus/db-queries/src/db/datastore/inventory.rs index 114b9dbe314..0bf39fb4854 100644 --- a/nexus/db-queries/src/db/datastore/inventory.rs +++ b/nexus/db-queries/src/db/datastore/inventory.rs @@ -798,6 +798,31 @@ impl DataStore { Ok(()) } + + /// Attempt to read the latest collection while limiting queries to `limit` + /// records + pub async fn inventory_get_latest_collection( + &self, + opctx: &OpContext, + limit: NonZeroU32, + ) -> Result { + opctx.authorize(authz::Action::Read, &authz::INVENTORY).await?; + let conn = self.pool_connection_authorized(opctx).await?; + use db::schema::inv_collection::dsl; + let collection_id = dsl::inv_collection + .select(dsl::id) + .order_by(dsl::time_started.desc()) + .limit(1) + .first_async::(&*conn) + .await + .map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))?; + + // TODO: Fix me to return a proper error + Ok(self + .inventory_collection_read_all_or_nothing(collection_id, limit) + .await + .unwrap()) + } } /// Extra interfaces that are not intended (and potentially unsafe) for use in @@ -845,7 +870,7 @@ impl DataStoreInventoryTest for DataStore { let conn = self .pool_connection_for_tests() .await - .context("getting connectoin")?; + .context("getting connection")?; conn.transaction_async(|conn| async move { conn.batch_execute_async(ALLOW_FULL_TABLE_SCAN_SQL) .await diff --git a/nexus/src/app/rack.rs b/nexus/src/app/rack.rs index 163f3bd5bb3..1aa9f418d28 100644 --- a/nexus/src/app/rack.rs +++ b/nexus/src/app/rack.rs @@ -31,6 +31,7 @@ use nexus_types::external_api::params::{ use nexus_types::external_api::shared::FleetRole; use nexus_types::external_api::shared::SiloIdentityMode; use nexus_types::external_api::shared::SiloRole; +use nexus_types::external_api::views::UninitializedSled; use nexus_types::internal_api::params::DnsRecord; use omicron_common::api::external::AddressLotKind; use omicron_common::api::external::DataPageParams; @@ -614,20 +615,7 @@ impl super::Nexus { opctx: &OpContext, ) -> Result { let rack = self.rack_lookup(opctx, &self.rack_id).await?; - - let subnet = match rack.rack_subnet { - Some(IpNetwork::V6(subnet)) => subnet, - Some(IpNetwork::V4(_)) => { - return Err(Error::InternalError { - internal_message: "rack subnet not IPv6".into(), - }) - } - None => { - return Err(Error::InternalError { - internal_message: "rack subnet not set".into(), - }) - } - }; + let subnet = rack.get_subnet()?; let db_ports = self.active_port_settings(opctx).await?; let mut ports = Vec::new(); @@ -724,4 +712,16 @@ impl super::Nexus { Ok(result) } + + async fn unintialized_sled_list( + &self, + opctx: &OpContext, + rack_id: Uuid, + pagparams: &DataPageParams<'_, Uuid>, + ) -> ListResultVec { + //let rack = self.rack_lookup(opctx, &self.rack_id).await?; + //let subnet = rack.get_subnet()?; + + // Grab the SPs from the last collection + } } diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index eba97a88ec7..6dc320a9ec0 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -4382,6 +4382,35 @@ async fn rack_view( apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await } +/// List uninitialized sleds in a given rack +#[endpoint { + method = GET, + path = "/v1/system/hardware/racks/{rack_id}/uninitialized-sleds", + tags = ["system/hardware"] +}] +async fn uninitialized_sled_list( + rqctx: RequestContext>, + path_params: Path, + query_params: Query, +) -> Result>, HttpError> { + let apictx = rqctx.context(); + let handler = async { + let nexus = apictx.nexus; + let path = path_params.into_inner(); + let query = query_params.into_inner(); + let opctx = crate::context::op_context_for_external_api(&rqctx).await?; + let sleds = nexus + .uninitialized_sled_list( + &opctx, + &path.rack_id, + &data_page_params_for(&rqctx, &query)?, + ) + .await?; + Ok(HttpResponseOk(sleds)) + }; + apictx.external_latencies.instrument_dropshot_handler(&rqctx, handler).await +} + // Sleds /// List sleds diff --git a/nexus/types/src/external_api/views.rs b/nexus/types/src/external_api/views.rs index ef3835c6182..8ee8fbf9ba1 100644 --- a/nexus/types/src/external_api/views.rs +++ b/nexus/types/src/external_api/views.rs @@ -274,6 +274,12 @@ pub struct Rack { pub identity: AssetIdentityMetadata, } +/// View of a sled that has not been added to an initialized rack yet +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] +pub struct UninitializedSled { + baseboard: Baseboard, +} + // FRUs /// Properties that uniquely identify an Oxide hardware component