Skip to content

Commit

Permalink
[Nexus] Add a sled to an initialized rack (#4545)
Browse files Browse the repository at this point in the history
This commit provides an external API for adding a sled to an already
initialized rack.
  • Loading branch information
andrewjstone authored Nov 25, 2023
1 parent 3f702ef commit 47968b8
Show file tree
Hide file tree
Showing 22 changed files with 753 additions and 93 deletions.
2 changes: 2 additions & 0 deletions nexus/db-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ mod sled;
mod sled_instance;
mod sled_resource;
mod sled_resource_kind;
mod sled_underlay_subnet_allocation;
mod snapshot;
mod ssh_key;
mod switch;
Expand Down Expand Up @@ -153,6 +154,7 @@ pub use sled::*;
pub use sled_instance::*;
pub use sled_resource::*;
pub use sled_resource_kind::*;
pub use sled_underlay_subnet_allocation::*;
pub use snapshot::*;
pub use ssh_key::*;
pub use switch::*;
Expand Down
19 changes: 1 addition & 18 deletions nexus/db-model/src/rack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

use crate::schema::rack;
use db_macros::Asset;
use ipnetwork::{IpNetwork, Ipv6Network};
use ipnetwork::IpNetwork;
use nexus_types::{external_api::views, identity::Asset};
use omicron_common::api;
use uuid::Uuid;

/// Information about a local rack.
Expand All @@ -29,22 +28,6 @@ impl Rack {
rack_subnet: None,
}
}

pub fn subnet(&self) -> Result<Ipv6Network, api::external::Error> {
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<Rack> for views::Rack {
Expand Down
12 changes: 11 additions & 1 deletion nexus/db-model/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,16 @@ table! {
}
}

table! {
sled_underlay_subnet_allocation (rack_id, sled_id) {
rack_id -> Uuid,
sled_id -> Uuid,
subnet_octet -> Int2,
hw_baseboard_id -> Uuid,
}
}
allow_tables_to_appear_in_same_query!(rack, sled_underlay_subnet_allocation);

table! {
switch (id) {
id -> Uuid,
Expand Down Expand Up @@ -1289,7 +1299,7 @@ table! {
///
/// This should be updated whenever the schema is changed. For more details,
/// refer to: schema/crdb/README.adoc
pub const SCHEMA_VERSION: SemverVersion = SemverVersion::new(13, 0, 0);
pub const SCHEMA_VERSION: SemverVersion = SemverVersion::new(14, 0, 0);

allow_tables_to_appear_in_same_query!(
system_update,
Expand Down
4 changes: 2 additions & 2 deletions nexus/db-model/src/sled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::ipv6;
use crate::schema::{physical_disk, service, sled, zpool};
use chrono::{DateTime, Utc};
use db_macros::Asset;
use nexus_types::{external_api::views, identity::Asset};
use nexus_types::{external_api::shared, external_api::views, identity::Asset};
use std::net::Ipv6Addr;
use std::net::SocketAddrV6;
use uuid::Uuid;
Expand Down Expand Up @@ -88,7 +88,7 @@ impl From<Sled> for views::Sled {
Self {
identity: sled.identity(),
rack_id: sled.rack_id,
baseboard: views::Baseboard {
baseboard: shared::Baseboard {
serial: sled.serial_number,
part: sled.part_number,
revision: sled.revision,
Expand Down
16 changes: 16 additions & 0 deletions nexus/db-model/src/sled_underlay_subnet_allocation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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/.

use crate::schema::sled_underlay_subnet_allocation;
use uuid::Uuid;

/// Underlay allocation for a sled added to an initialized rack
#[derive(Queryable, Insertable, Debug, Clone, Selectable)]
#[diesel(table_name = sled_underlay_subnet_allocation)]
pub struct SledUnderlaySubnetAllocation {
pub rack_id: Uuid,
pub sled_id: Uuid,
pub subnet_octet: i16,
pub hw_baseboard_id: Uuid,
}
4 changes: 2 additions & 2 deletions nexus/db-model/src/switch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::Generation;
use crate::schema::switch;
use chrono::{DateTime, Utc};
use db_macros::Asset;
use nexus_types::{external_api::views, identity::Asset};
use nexus_types::{external_api::shared, external_api::views, identity::Asset};
use uuid::Uuid;

/// Baseboard information about a switch.
Expand Down Expand Up @@ -57,7 +57,7 @@ impl From<Switch> for views::Switch {
Self {
identity: switch.identity(),
rack_id: switch.rack_id,
baseboard: views::Baseboard {
baseboard: shared::Baseboard {
serial: switch.serial_number,
part: switch.part_number,
revision: switch.revision,
Expand Down
73 changes: 65 additions & 8 deletions nexus/db-queries/src/db/datastore/inventory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::authz;
use crate::context::OpContext;
use crate::db;
use crate::db::error::public_error_from_diesel;
use crate::db::error::public_error_from_diesel_lookup;
use crate::db::error::ErrorHandler;
use crate::db::queries::ALLOW_FULL_TABLE_SCAN_SQL;
use crate::db::TransactionError;
Expand All @@ -21,6 +22,7 @@ use diesel::ExpressionMethods;
use diesel::IntoSql;
use diesel::JoinOnDsl;
use diesel::NullableExpressionMethods;
use diesel::OptionalExtension;
use diesel::QueryDsl;
use diesel::Table;
use futures::future::BoxFuture;
Expand All @@ -42,9 +44,12 @@ use nexus_db_model::SpType;
use nexus_db_model::SpTypeEnum;
use nexus_db_model::SwCaboose;
use nexus_db_model::SwRotPage;
use nexus_types::inventory::BaseboardId;
use nexus_types::inventory::Collection;
use omicron_common::api::external::Error;
use omicron_common::api::external::InternalContext;
use omicron_common::api::external::LookupType;
use omicron_common::api::external::ResourceType;
use omicron_common::bail_unless;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
Expand Down Expand Up @@ -910,30 +915,62 @@ impl DataStore {
Ok(())
}

// Find the primary key for `hw_baseboard_id` given a `BaseboardId`
pub async fn find_hw_baseboard_id(
&self,
opctx: &OpContext,
baseboard_id: BaseboardId,
) -> Result<Uuid, Error> {
opctx.authorize(authz::Action::Read, &authz::INVENTORY).await?;
let conn = self.pool_connection_authorized(opctx).await?;
use db::schema::hw_baseboard_id::dsl;
dsl::hw_baseboard_id
.filter(dsl::serial_number.eq(baseboard_id.serial_number.clone()))
.filter(dsl::part_number.eq(baseboard_id.part_number.clone()))
.select(dsl::id)
.first_async::<Uuid>(&*conn)
.await
.map_err(|e| {
public_error_from_diesel_lookup(
e,
ResourceType::Sled,
&LookupType::ByCompositeId(format!("{baseboard_id:?}")),
)
})
}

/// Attempt to read the latest collection while limiting queries to `limit`
/// records
///
/// If there aren't any collections, return `Ok(None)`.
pub async fn inventory_get_latest_collection(
&self,
opctx: &OpContext,
limit: NonZeroU32,
) -> Result<Collection, Error> {
) -> Result<Option<Collection>, Error> {
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::<Uuid>(&*conn)
.await
.optional()
.map_err(|e| public_error_from_diesel(e, ErrorHandler::Server))?;

self.inventory_collection_read_all_or_nothing(
opctx,
collection_id,
limit,
)
.await
let Some(collection_id) = collection_id else {
return Ok(None);
};

Ok(Some(
self.inventory_collection_read_all_or_nothing(
opctx,
collection_id,
limit,
)
.await?,
))
}

/// Attempt to read the given collection while limiting queries to `limit`
Expand Down Expand Up @@ -1335,9 +1372,11 @@ mod test {
use nexus_inventory::examples::Representative;
use nexus_test_utils::db::test_setup_database;
use nexus_test_utils::db::ALLOW_FULL_TABLE_SCAN_SQL;
use nexus_types::inventory::BaseboardId;
use nexus_types::inventory::CabooseWhich;
use nexus_types::inventory::Collection;
use nexus_types::inventory::RotPageWhich;
use omicron_common::api::external::Error;
use omicron_test_utils::dev;
use std::num::NonZeroU32;
use uuid::Uuid;
Expand Down Expand Up @@ -1393,6 +1432,24 @@ mod test {
}
}

#[tokio::test]
async fn test_find_hw_baseboard_id_missing_returns_not_found() {
let logctx = dev::test_setup_log("inventory_insert");
let mut db = test_setup_database(&logctx.log).await;
let (opctx, datastore) = datastore_test(&logctx, &db).await;
let baseboard_id = BaseboardId {
serial_number: "some-serial".into(),
part_number: "some-part".into(),
};
let err = datastore
.find_hw_baseboard_id(&opctx, baseboard_id)
.await
.unwrap_err();
assert!(matches!(err, Error::ObjectNotFound { .. }));
db.cleanup().await.unwrap();
logctx.cleanup_successful();
}

/// Tests inserting several collections, reading them back, and making sure
/// they look the same.
#[tokio::test]
Expand Down
Loading

0 comments on commit 47968b8

Please sign in to comment.