Skip to content

Commit b962606

Browse files
committed
Plumb disks into inventory
1 parent a0e89db commit b962606

File tree

11 files changed

+294
-3
lines changed

11 files changed

+294
-3
lines changed

clients/sled-agent-client/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,16 @@ impl omicron_common::api::external::ClientError for types::Error {
165165
}
166166
}
167167

168+
impl From<types::DiskIdentity> for omicron_common::disk::DiskIdentity {
169+
fn from(identity: types::DiskIdentity) -> Self {
170+
Self {
171+
vendor: identity.vendor,
172+
serial: identity.serial,
173+
model: identity.model,
174+
}
175+
}
176+
}
177+
168178
impl From<omicron_common::api::internal::nexus::InstanceRuntimeState>
169179
for types::InstanceRuntimeState
170180
{

nexus/db-model/src/inventory.rs

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@
77
use crate::omicron_zone_config::{OmicronZone, OmicronZoneNic};
88
use crate::schema::{
99
hw_baseboard_id, inv_caboose, inv_collection, inv_collection_error,
10-
inv_omicron_zone, inv_omicron_zone_nic, inv_root_of_trust,
11-
inv_root_of_trust_page, inv_service_processor, inv_sled_agent,
12-
inv_sled_omicron_zones, sw_caboose, sw_root_of_trust_page,
10+
inv_omicron_zone, inv_omicron_zone_nic, inv_physical_disk,
11+
inv_root_of_trust, inv_root_of_trust_page, inv_service_processor,
12+
inv_sled_agent, inv_sled_omicron_zones, sw_caboose, sw_root_of_trust_page,
1313
};
14+
use crate::PhysicalDiskKind;
1415
use crate::{
1516
impl_enum_type, ipv6, ByteCount, Generation, MacAddr, Name, SqlU16, SqlU32,
1617
SqlU8,
@@ -652,6 +653,48 @@ impl InvSledAgent {
652653
}
653654
}
654655

656+
/// See [`nexus_types::inventory::PhysicalDisk`].
657+
#[derive(Queryable, Clone, Debug, Selectable, Insertable)]
658+
#[diesel(table_name = inv_physical_disk)]
659+
pub struct InvPhysicalDisk {
660+
pub inv_collection_id: Uuid,
661+
pub sled_id: Uuid,
662+
pub vendor: String,
663+
pub serial: String,
664+
pub model: String,
665+
pub variant: PhysicalDiskKind,
666+
}
667+
668+
impl InvPhysicalDisk {
669+
pub fn new(
670+
inv_collection_id: Uuid,
671+
sled_id: Uuid,
672+
disk: nexus_types::inventory::PhysicalDisk,
673+
) -> Self {
674+
Self {
675+
inv_collection_id,
676+
sled_id,
677+
vendor: disk.identity.vendor,
678+
serial: disk.identity.serial,
679+
model: disk.identity.model,
680+
variant: disk.variant.into(),
681+
}
682+
}
683+
}
684+
685+
impl From<InvPhysicalDisk> for nexus_types::inventory::PhysicalDisk {
686+
fn from(disk: InvPhysicalDisk) -> Self {
687+
Self {
688+
identity: omicron_common::disk::DiskIdentity {
689+
vendor: disk.vendor,
690+
serial: disk.serial,
691+
model: disk.model,
692+
},
693+
variant: disk.variant.into(),
694+
}
695+
}
696+
}
697+
655698
/// See [`nexus_types::inventory::OmicronZonesFound`].
656699
#[derive(Queryable, Clone, Debug, Selectable, Insertable)]
657700
#[diesel(table_name = inv_sled_omicron_zones)]

nexus/db-model/src/schema.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,6 +1358,19 @@ table! {
13581358
}
13591359
}
13601360

1361+
table! {
1362+
inv_physical_disk (inv_collection_id, sled_id, vendor, serial, model) {
1363+
inv_collection_id -> Uuid,
1364+
sled_id -> Uuid,
1365+
1366+
vendor -> Text,
1367+
serial -> Text,
1368+
model -> Text,
1369+
1370+
variant -> crate::PhysicalDiskKindEnum,
1371+
}
1372+
}
1373+
13611374
table! {
13621375
inv_sled_omicron_zones (inv_collection_id, sled_id) {
13631376
inv_collection_id -> Uuid,

nexus/db-queries/src/db/datastore/inventory.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use nexus_db_model::InvCollection;
3939
use nexus_db_model::InvCollectionError;
4040
use nexus_db_model::InvOmicronZone;
4141
use nexus_db_model::InvOmicronZoneNic;
42+
use nexus_db_model::InvPhysicalDisk;
4243
use nexus_db_model::InvRootOfTrust;
4344
use nexus_db_model::InvRotPage;
4445
use nexus_db_model::InvServiceProcessor;
@@ -125,6 +126,25 @@ impl DataStore {
125126
))
126127
})
127128
.collect::<Result<Vec<_>, Error>>()?;
129+
// Pull disks out of all sled agents
130+
let disks: Vec<_> = collection
131+
.sled_agents
132+
.iter()
133+
.flat_map(|(sled_id, sled_agent)| {
134+
sled_agent
135+
.disks
136+
.iter()
137+
.map(|disk| {
138+
InvPhysicalDisk::new(
139+
collection_id,
140+
*sled_id,
141+
disk.clone(),
142+
)
143+
})
144+
.collect::<Vec<_>>()
145+
})
146+
.collect();
147+
128148
// Partition the sled agents into those with an associated baseboard id
129149
// and those without one. We handle these pretty differently.
130150
let (sled_agents_baseboards, sled_agents_no_baseboards): (
@@ -639,6 +659,20 @@ impl DataStore {
639659
}
640660
}
641661

662+
// Insert rows for all the physical disks we found.
663+
{
664+
use db::schema::inv_physical_disk::dsl;
665+
666+
let mut iter =
667+
disks.chunks(SQL_BATCH_SIZE.get().try_into().unwrap());
668+
while let Some(some_disks) = iter.next() {
669+
let _ = diesel::insert_into(dsl::inv_physical_disk)
670+
.values(some_disks.to_vec())
671+
.execute_async(&conn)
672+
.await?;
673+
}
674+
}
675+
642676
// Insert rows for the sled agents that we found. In practice, we'd
643677
// expect these to all have baseboards (if using Oxide hardware) or
644678
// none have baseboards (if not).
@@ -1391,6 +1425,83 @@ impl DataStore {
13911425
rows
13921426
};
13931427

1428+
// Mapping of "Sled ID" -> "All disks reported by that sled"
1429+
let physical_disks: BTreeMap<
1430+
Uuid,
1431+
Vec<nexus_types::inventory::PhysicalDisk>,
1432+
> = {
1433+
use db::schema::inv_physical_disk::dsl;
1434+
1435+
let mut disks = BTreeMap::<
1436+
Uuid,
1437+
Vec<nexus_types::inventory::PhysicalDisk>,
1438+
>::new();
1439+
let mut paginator = Paginator::new(batch_size);
1440+
while let Some(p) = paginator.next() {
1441+
// The primary key of physical disks is more than two columns,
1442+
// which complicates a bit of the pagination query. This
1443+
// code below is effectively an implementation of
1444+
// "paginated_multicolumn" for these four columns (sled_id,
1445+
// vendor, model, serial).
1446+
type PK = (Uuid, String, String, String);
1447+
1448+
let pagparams = &p.current_pagparams();
1449+
let mut query = dsl::inv_physical_disk
1450+
.into_boxed()
1451+
.limit(pagparams.limit.get().into())
1452+
.filter(dsl::inv_collection_id.eq(id));
1453+
let marker = pagparams.marker.map(|m: &PK| m.clone());
1454+
if let Some((sled_id, vendor, serial, model)) = marker {
1455+
query = query.filter(
1456+
dsl::sled_id
1457+
.eq(sled_id)
1458+
.and(dsl::vendor.eq(vendor.clone()))
1459+
.and(dsl::model.eq(model.clone()))
1460+
.and(dsl::serial.gt(serial.clone())),
1461+
);
1462+
query = query.or_filter(
1463+
dsl::sled_id
1464+
.eq(sled_id)
1465+
.and(dsl::vendor.eq(vendor.clone()))
1466+
.and(dsl::model.gt(model.clone())),
1467+
);
1468+
query = query.or_filter(
1469+
dsl::sled_id
1470+
.eq(sled_id)
1471+
.and(dsl::vendor.gt(vendor.clone())),
1472+
);
1473+
query = query.or_filter(dsl::sled_id.gt(sled_id));
1474+
}
1475+
query = query
1476+
.order(dsl::sled_id.asc())
1477+
.then_order_by(dsl::vendor.asc())
1478+
.then_order_by(dsl::model.asc())
1479+
.then_order_by(dsl::serial.asc());
1480+
1481+
let batch: Vec<_> = query
1482+
.select(InvPhysicalDisk::as_select())
1483+
.load_async(&*conn)
1484+
.await
1485+
.map_err(|e| {
1486+
public_error_from_diesel(e, ErrorHandler::Server)
1487+
})?;
1488+
paginator = p.found_batch(&batch, &|row| {
1489+
(
1490+
row.sled_id,
1491+
row.vendor.clone(),
1492+
row.serial.clone(),
1493+
row.model.clone(),
1494+
)
1495+
});
1496+
1497+
for disk in batch {
1498+
disks.entry(disk.sled_id).or_default().push(disk.into());
1499+
}
1500+
}
1501+
1502+
disks
1503+
};
1504+
13941505
// Collect the unique baseboard ids referenced by SPs, RoTs, and Sled
13951506
// Agents.
13961507
let baseboard_id_ids: BTreeSet<_> = sps
@@ -1494,6 +1605,10 @@ impl DataStore {
14941605
),
14951606
usable_physical_ram: s.usable_physical_ram.into(),
14961607
reservoir_size: s.reservoir_size.into(),
1608+
disks: physical_disks
1609+
.get(&sled_id)
1610+
.map(|disks| disks.to_vec())
1611+
.unwrap_or_default(),
14971612
};
14981613
Ok((sled_id, sled_agent))
14991614
})

nexus/deployment/src/blueprint_builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,6 +806,7 @@ pub mod test {
806806
sled_id,
807807
usable_hardware_threads: 10,
808808
usable_physical_ram: ByteCount::from(1024 * 1024),
809+
disks: vec![],
809810
},
810811
)
811812
.unwrap();

nexus/inventory/src/builder.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ impl CollectionBuilder {
458458
reservoir_size: inventory.reservoir_size,
459459
time_collected: now_db_precision(),
460460
sled_id,
461+
disks: inventory.disks.into_iter().map(|d| d.into()).collect(),
461462
};
462463

463464
if let Some(previous) = self.sleds.get(&sled_id) {

nexus/inventory/src/examples.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,5 +448,6 @@ pub fn sled_agent(
448448
sled_id,
449449
usable_hardware_threads: 10,
450450
usable_physical_ram: ByteCount::from(1024 * 1024),
451+
disks: vec![],
451452
}
452453
}

nexus/types/src/external_api/params.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,6 +1319,16 @@ pub enum PhysicalDiskKind {
13191319
U2,
13201320
}
13211321

1322+
impl From<sled_agent_client::types::DiskVariant> for PhysicalDiskKind {
1323+
fn from(variant: sled_agent_client::types::DiskVariant) -> Self {
1324+
use sled_agent_client::types::DiskVariant;
1325+
match variant {
1326+
DiskVariant::U2 => Self::U2,
1327+
DiskVariant::M2 => Self::M2,
1328+
}
1329+
}
1330+
}
1331+
13221332
/// Different sources for a disk
13231333
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
13241334
#[serde(tag = "type", rename_all = "snake_case")]

nexus/types/src/inventory.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
//! nexus/inventory does not currently know about nexus/db-model and it's
1010
//! convenient to separate these concerns.)
1111
12+
use crate::external_api::params::PhysicalDiskKind;
1213
use crate::external_api::params::UninitializedSledId;
1314
use crate::external_api::shared::Baseboard;
1415
use chrono::DateTime;
@@ -301,6 +302,26 @@ impl IntoRotPage for gateway_client::types::RotCfpa {
301302
}
302303
}
303304

305+
/// A physical disk reported by a sled agent.
306+
///
307+
/// This identifies that a physical disk appears in a Sled.
308+
/// The existence of this object does not necessarily imply that
309+
/// the disk is being actively managed by the control plane.
310+
#[derive(Clone, Debug, PartialEq, Eq)]
311+
pub struct PhysicalDisk {
312+
pub identity: omicron_common::disk::DiskIdentity,
313+
pub variant: PhysicalDiskKind,
314+
}
315+
316+
impl From<sled_agent_client::types::InventoryDisk> for PhysicalDisk {
317+
fn from(disk: sled_agent_client::types::InventoryDisk) -> PhysicalDisk {
318+
PhysicalDisk {
319+
identity: disk.identity.into(),
320+
variant: disk.variant.into(),
321+
}
322+
}
323+
}
324+
304325
/// Inventory reported by sled agent
305326
///
306327
/// This is a software notion of a sled, distinct from an underlying baseboard.
@@ -318,6 +339,7 @@ pub struct SledAgent {
318339
pub usable_hardware_threads: u32,
319340
pub usable_physical_ram: ByteCount,
320341
pub reservoir_size: ByteCount,
342+
pub disks: Vec<PhysicalDisk>,
321343
}
322344

323345
#[derive(Clone, Debug, PartialEq, Eq)]

0 commit comments

Comments
 (0)