From 933742388813bd20271340b977ac14f645ea6bcc Mon Sep 17 00:00:00 2001 From: David Pacheco Date: Fri, 29 Sep 2023 10:41:20 -0700 Subject: [PATCH] db model changes --- nexus/db-model/src/inventory.rs | 57 +++++++++++++ nexus/db-model/src/lib.rs | 2 + nexus/db-model/src/schema.rs | 79 +++++++++++++++++++ schema/crdb/dbinit.sql | 136 ++++++++++++++++++++++++++++++++ 4 files changed, 274 insertions(+) create mode 100644 nexus/db-model/src/inventory.rs diff --git a/nexus/db-model/src/inventory.rs b/nexus/db-model/src/inventory.rs new file mode 100644 index 0000000000..84a5fa4ed2 --- /dev/null +++ b/nexus/db-model/src/inventory.rs @@ -0,0 +1,57 @@ +// 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::impl_enum_type; +use crate::schema::{ + hw_baseboard_id, inv_caboose, inv_root_of_trust, inv_service_processor, + sw_caboose, +}; +use db_macros::Asset; +use nexus_types::identity::Asset; +use uuid::Uuid; + +impl_enum_type!( + #[derive(SqlType, Debug, QueryId)] + #[diesel(postgres_type(name = "hw_power_state"))] + pub struct HwPowerStateEnum; + + #[derive(Copy, Clone, Debug, AsExpression, FromSqlRow, PartialEq)] + #[diesel(sql_type = HwRotSlotEnum)] + pub enum HwPowerState; + + // Enum values + A0 => b"A0" + A1 => b"A1" + A2 => b"A2" +); + +impl_enum_type!( + #[derive(SqlType, Debug, QueryId)] + #[diesel(postgres_type(name = "hw_rot_slot"))] + pub struct HwRotSlotEnum; + + #[derive(Copy, Clone, Debug, AsExpression, FromSqlRow, PartialEq)] + #[diesel(sql_type = HwRotSlotEnum)] + pub enum HwRotSlot; + + // Enum values + A => b"A" + B => b"B" +); + +impl_enum_type!( + #[derive(SqlType, Debug, QueryId)] + #[diesel(postgres_type(name = "caboose_which"))] + pub struct CabooseWhichEnum; + + #[derive(Copy, Clone, Debug, AsExpression, FromSqlRow, PartialEq)] + #[diesel(sql_type = CabooseWhichEnum)] + pub enum CabooseWhich; + + // Enum values + SpSlot0 => b"sp_slot_0" + SpSlot1 => b"sp_slot_1" + RotSlotA => b"rot_slot_A" + RotSlotB => b"rot_slot_B" +); diff --git a/nexus/db-model/src/lib.rs b/nexus/db-model/src/lib.rs index 334dedad9f..b5b9e61194 100644 --- a/nexus/db-model/src/lib.rs +++ b/nexus/db-model/src/lib.rs @@ -31,6 +31,7 @@ mod image; mod instance; mod instance_cpu_count; mod instance_state; +mod inventory; mod ip_pool; mod ipv4net; mod ipv6; @@ -118,6 +119,7 @@ pub use image::*; pub use instance::*; pub use instance_cpu_count::*; pub use instance_state::*; +pub use inventory::*; pub use ip_pool::*; pub use ipv4net::*; pub use ipv6::*; diff --git a/nexus/db-model/src/schema.rs b/nexus/db-model/src/schema.rs index 94a770e2ca..014385c6b2 100644 --- a/nexus/db-model/src/schema.rs +++ b/nexus/db-model/src/schema.rs @@ -1115,6 +1115,85 @@ table! { } } +/* hardware inventory */ + +table! { + hw_baseboard_id (id) { + id -> Uuid, + part_number -> Text, + serial_number -> Text, + } +} + +table! { + sw_caboose (id) { + id -> Uuid, + board -> Text, + git_commit -> Text, + name -> Text, + version -> Text, + } +} + +table! { + inv_collection (id) { + id -> Uuid, + time_started -> Timestamptz, + time_done -> Nullable, + collector -> Text, + comment -> Text, + } +} + +table! { + inv_collection_errors (inv_collection_id, i) { + inv_collection_id -> Uuid, + i -> Int4, + message -> Text, + } +} + +table! { + inv_service_processor (inv_collection_id, hw_baseboard_id) { + inv_collection_id -> Uuid, + hw_baseboard_id -> Uuid, + time_collected -> Timestamptz, + source -> Text, + + baseboard_revision -> Int4, + hubris_archive_id -> Text, + power_state -> crate::HwPowerStateEnum, + } +} + +table! { + inv_root_of_trust (inv_collection_id, hw_baseboard_id) { + inv_collection_id -> Uuid, + hw_baseboard_id -> Uuid, + time_collected -> Timestamptz, + source -> Text, + + rot_slot_active -> crate::HwRotSlotEnum, + rot_slot_boot_pref_transient -> Nullable, + rot_slot_boot_pref_persistent -> crate::HwRotSlotEnum, + rot_slot_boot_pref_persistent_pending -> Nullable, + rot_slot_a_sha3_256 -> Text, + rot_slot_b_sha3_256 -> Text, + } +} + +table! { + inv_caboose (inv_collection_id, hw_baseboard_id, which) { + inv_collection_id -> Uuid, + hw_baseboard_id -> Uuid, + time_collected -> Timestamptz, + source -> Text, + + which -> crate::CabooseWhichEnum, + sw_caboose_id -> Uuid, + } +} + table! { db_metadata (singleton) { singleton -> Bool, diff --git a/schema/crdb/dbinit.sql b/schema/crdb/dbinit.sql index ad09092f8f..e280a8e035 100644 --- a/schema/crdb/dbinit.sql +++ b/schema/crdb/dbinit.sql @@ -2541,6 +2541,142 @@ CREATE TABLE IF NOT EXISTS omicron.public.switch_port_settings_address_config ( ); +/*******************************************************************/ + +/* Hardware inventory */ + +/* baseboard ids: this table assigns uuids to distinct part/serial values */ +CREATE TABLE IF NOT EXISTS omicron.public.hw_baseboard_id ( + id UUID PRIMARY KEY, + part_number TEXT NOT NULL, + serial_number TEXT NOT NULL +); +CREATE UNIQUE INDEX IF NOT EXISTS lookup_baseboard_id_by_props + ON omicron.public.hw_baseboard_id (part_number, serial_number); + +/* power states reportable by the SP */ +CREATE TYPE IF NOT EXISTS omicron.public.hw_power_state AS ENUM ( + 'A0', + 'A1', + 'A2' +); + +/* root of trust firmware slots */ +CREATE TYPE IF NOT EXISTS omicron.public.hw_rot_slot AS ENUM ( + 'A', + 'B' +); + +/* cabooses: assigns unique ids to distinct caboose contents */ +CREATE TABLE IF NOT EXISTS omicron.public.sw_caboose ( + id UUID PRIMARY KEY, + board TEXT NOT NULL, + git_commit TEXT NOT NULL, + name TEXT NOT NULL, + version TEXT NOT NULL -- TODO-doc explain why this is not nullable +); +CREATE UNIQUE INDEX IF NOT EXISTS caboose_properties + on omicron.public.sw_caboose (board, git_commit, name, version); + + +/* Collections */ + +-- list of all collections +CREATE TABLE IF NOT EXISTS inv_collection ( + id UUID PRIMARY KEY, + time_started TIMESTAMPTZ NOT NULL, + time_done TIMESTAMPTZ, + collector UUID NOT NULL, + comment TEXT NOT NULL +); +-- Supports: finding latest collection to use, finding oldest collection to +-- clean up +CREATE INDEX IF NOT EXISTS inv_collection_by_time + ON omicron.public.inv_collection (time_done); + +-- list of errors generated during a collection +CREATE TABLE IF NOT EXISTS omicron.public.inv_collection_errors ( + inv_collection_id UUID NOT NULL, + i INT4 NOT NULL, + message TEXT +); +CREATE INDEX IF NOT EXISTS errors_by_collection + ON omicron.public.inv_collection_errors (inv_collection_id, i); + +-- observations from and about service processors +-- also see inv_root_of_trust +CREATE TABLE IF NOT EXISTS omicron.public.inv_service_processor ( + -- where this observation came from + -- (foreign key into `inv_collection` table) + inv_collection_id UUID NOT NULL, + -- which system this SP reports it is part of + -- (foreign key into `hw_baseboard_id` table) + hw_baseboard_id UUID NOT NULL, + -- when this observation was made + time_collected TIMESTAMPTZ NOT NULL, + -- which MGS instance reported this data + source TEXT NOT NULL, + + -- Data from MGS "Get SP Info" API. See MGS API documentation. + baseboard_revision INT4 NOT NULL, + hubris_archive_id TEXT NOT NULL, + power_state omicron.public.hw_power_state NOT NULL, + + PRIMARY KEY (inv_collection_id, hw_baseboard_id) +); + +-- root of trust information reported by SP +-- There's usually one row here for each row in inv_service_processor, but not +-- necessarily. +CREATE TABLE IF NOT EXISTS omicron.public.inv_root_of_trust ( + -- where this observation came from + -- (foreign key into `inv_collection` table) + inv_collection_id UUID NOT NULL, + -- which system this SP reports it is part of + -- (foreign key into `hw_baseboard_id` table) + hw_baseboard_id UUID NOT NULL, + -- when this observation was made + time_collected TIMESTAMPTZ NOT NULL, + -- which MGS instance reported this data + source TEXT NOT NULL, + + rot_slot_active omicron.public.hw_rot_slot NOT NULL, + rot_slot_boot_pref_transient omicron.public.hw_rot_slot, -- nullable + rot_slot_boot_pref_persistent omicron.public.hw_rot_slot NOT NULL, + rot_slot_boot_pref_persistent_pending omicron.public.hw_rot_slot, -- nullable + rot_slot_a_sha3_256 TEXT NOT NULL, + rot_slot_b_sha3_256 TEXT NOT NULL, + + PRIMARY KEY (inv_collection_id, hw_baseboard_id) +); + +CREATE TYPE IF NOT EXISTS omicron.public.caboose_which AS ENUM ( + 'sp_slot_0', + 'sp_slot_1', + 'rot_slot_A', + 'rot_slot_B' +); + +-- cabooses found +CREATE TABLE IF NOT EXISTS omicron.public.inv_caboose ( + -- where this observation came from + -- (foreign key into `inv_collection` table) + inv_collection_id UUID NOT NULL, + -- which system this SP reports it is part of + -- (foreign key into `hw_baseboard_id` table) + hw_baseboard_id UUID NOT NULL, + -- when this observation was made + time_collected TIMESTAMPTZ NOT NULL, + -- which MGS instance reported this data + source TEXT NOT NULL, + + which omicron.public.caboose_which NOT NULL, + sw_caboose_id UUID NOT NULL, + + PRIMARY KEY (inv_collection_id, hw_baseboard_id, which) +); + + /*******************************************************************/ /*