Skip to content

Commit

Permalink
for debugging
Browse files Browse the repository at this point in the history
  • Loading branch information
davepacheco committed Sep 29, 2023
1 parent 9337423 commit bdcbd0e
Show file tree
Hide file tree
Showing 15 changed files with 465 additions and 120 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 56 additions & 2 deletions nexus/db-model/src/inventory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@

use crate::impl_enum_type;
use crate::schema::{
hw_baseboard_id, inv_caboose, inv_root_of_trust, inv_service_processor,
sw_caboose,
hw_baseboard_id, inv_caboose, inv_collection, inv_root_of_trust,
inv_service_processor, sw_caboose,
};
use chrono::DateTime;
use chrono::Utc;
use db_macros::Asset;
use diesel::expression::AsExpression;
use nexus_types::identity::Asset;
use nexus_types::inventory::{BaseboardId, Collection, PowerState};
use uuid::Uuid;

impl_enum_type!(
Expand All @@ -26,6 +30,16 @@ impl_enum_type!(
A2 => b"A2"
);

impl From<PowerState> for HwPowerState {
fn from(p: PowerState) -> Self {
match p {
PowerState::A0 => HwPowerState::A0,
PowerState::A1 => HwPowerState::A1,
PowerState::A2 => HwPowerState::A2,
}
}
}

impl_enum_type!(
#[derive(SqlType, Debug, QueryId)]
#[diesel(postgres_type(name = "hw_rot_slot"))]
Expand Down Expand Up @@ -55,3 +69,43 @@ impl_enum_type!(
RotSlotA => b"rot_slot_A"
RotSlotB => b"rot_slot_B"
);

#[derive(Queryable, Insertable, Clone, Debug, Selectable)]
#[diesel(table_name = inv_collection)]
pub struct InvCollection {
pub id: Uuid,
pub time_started: DateTime<Utc>,
pub time_done: DateTime<Utc>,
pub collector: String,
pub comment: String,
}

impl<'a> From<&'a Collection> for InvCollection {
fn from(c: &'a Collection) -> Self {
InvCollection {
id: Uuid::new_v4(),
time_started: c.time_started,
time_done: c.time_done,
collector: c.collector.clone(),
comment: c.comment.clone(),
}
}
}

#[derive(Queryable, Insertable, Clone, Debug, Selectable)]
#[diesel(table_name = hw_baseboard_id)]
pub struct HwBaseboardId {
pub id: Uuid,
pub part_number: String,
pub serial_number: String,
}

impl<'a> From<&'a BaseboardId> for HwBaseboardId {
fn from(c: &'a BaseboardId) -> Self {
HwBaseboardId {
id: Uuid::new_v4(),
part_number: c.part_number.clone(),
serial_number: c.serial_number.clone(),
}
}
}
2 changes: 1 addition & 1 deletion nexus/db-model/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1160,7 +1160,7 @@ table! {
time_collected -> Timestamptz,
source -> Text,

baseboard_revision -> Int4,
baseboard_revision -> Int8,
hubris_archive_id -> Text,
power_state -> crate::HwPowerStateEnum,
}
Expand Down
55 changes: 55 additions & 0 deletions nexus/db-queries/src/authz/api_resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,61 @@ impl AuthorizedResource for DeviceAuthRequestList {
}
}

/// Synthetic resource used for modeling access to low-level hardware inventory
/// data
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Inventory;
pub const INVENTORY: Inventory = Inventory {};

impl oso::PolarClass for Inventory {
fn get_polar_class_builder() -> oso::ClassBuilder<Self> {
// Roles are not directly attached to Inventory
oso::Class::builder()
.with_equality_check()
.add_method(
"has_role",
|_: &Inventory, _actor: AuthenticatedActor, _role: String| {
false
},
)
.add_attribute_getter("fleet", |_| FLEET)
}
}

impl AuthorizedResource for Inventory {
fn load_roles<'a, 'b, 'c, 'd, 'e, 'f>(
&'a self,
opctx: &'b OpContext,
datastore: &'c DataStore,
authn: &'d authn::Context,
roleset: &'e mut RoleSet,
) -> futures::future::BoxFuture<'f, Result<(), Error>>
where
'a: 'f,
'b: 'f,
'c: 'f,
'd: 'f,
'e: 'f,
{
load_roles_for_resource_tree(&FLEET, opctx, datastore, authn, roleset)
.boxed()
}

fn on_unauthorized(
&self,
_: &Authz,
error: Error,
_: AnyActor,
_: Action,
) -> Error {
error
}

fn polar_class(&self) -> oso::Class {
Self::get_polar_class()
}
}

/// Synthetic resource describing the list of Certificates associated with a
/// Silo
#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down
10 changes: 10 additions & 0 deletions nexus/db-queries/src/authz/omicron.polar
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,16 @@ resource DnsConfig {
has_relation(fleet: Fleet, "parent_fleet", dns_config: DnsConfig)
if dns_config.fleet = fleet;

# Describes the policy for reading and modifying low-level inventory
resource Inventory {
permissions = [ "read", "modify" ];
relations = { parent_fleet: Fleet };
"read" if "viewer" on "parent_fleet";
"modify" if "admin" on "parent_fleet";
}
has_relation(fleet: Fleet, "parent_fleet", inventory: Inventory)
if inventory.fleet = fleet;

# Describes the policy for accessing "/v1/system/ip-pools" in the API
resource IpPoolList {
permissions = [
Expand Down
1 change: 1 addition & 0 deletions nexus/db-queries/src/authz/oso_generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ pub fn make_omicron_oso(log: &slog::Logger) -> Result<OsoInit, anyhow::Error> {
Database::get_polar_class(),
DnsConfig::get_polar_class(),
Fleet::get_polar_class(),
Inventory::get_polar_class(),
IpPoolList::get_polar_class(),
ConsoleSessionList::get_polar_class(),
DeviceAuthRequestList::get_polar_class(),
Expand Down
205 changes: 205 additions & 0 deletions nexus/db-queries/src/db/datastore/inventory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// 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 super::DataStore;
use crate::authz;
use crate::context::OpContext;
use crate::db;
use crate::db::error::public_error_from_diesel_pool;
use crate::db::error::ErrorHandler;
use crate::db::TransactionError;
use async_bb8_diesel::AsyncConnection;
use async_bb8_diesel::AsyncRunQueryDsl;
use async_bb8_diesel::PoolError;
use diesel::expression::AsExpression;
use diesel::query_dsl::methods::SelectDsl;
use diesel::IntoSql;
use nexus_db_model::HwBaseboardId;
use nexus_db_model::HwPowerState;
use nexus_db_model::HwPowerStateEnum;
use nexus_db_model::InvCollection;
use nexus_types::inventory::BaseboardId;
use nexus_types::inventory::Collection;
use omicron_common::api::external::Error;

impl DataStore {
/// Store a complete inventory collection into the database
pub async fn inventory_insert_collection(
&self,
opctx: &OpContext,
collection: &Collection,
) -> Result<(), Error> {
opctx.authorize(authz::Action::Modify, &authz::INVENTORY).await?;

// It's not critical that this be done in one transaction. But it keeps
// the database a little tidier because we won't have half-inserted
// collections in there.
let pool = self.pool_authorized(opctx).await?;
pool.transaction_async(|conn| async move {
let row_collection = InvCollection::from(collection);
let collection_id = row_collection.id;

// Insert a record describing the collection itself.
{
use db::schema::inv_collection::dsl;
let _ = diesel::insert_into(dsl::inv_collection)
.values(row_collection)
.execute_async(&conn)
.await
.map_err(|e| {
TransactionError::CustomError(
public_error_from_diesel_pool(
e.into(),
ErrorHandler::Server,
)
.internal_context(
"inserting new inventory collection",
),
)
})?;
}

// Insert records for any baseboards that do not already exist in
// the database.
{
use db::schema::hw_baseboard_id::dsl;
for baseboard_id in &collection.baseboards {
let _ = diesel::insert_into(dsl::hw_baseboard_id)
.values(HwBaseboardId::from(baseboard_id.as_ref()))
.on_conflict_do_nothing()
.execute_async(&conn)
.await
.map_err(|e| {
TransactionError::CustomError(
public_error_from_diesel_pool(
e.into(),
ErrorHandler::Server,
)
.internal_context("inserting baseboard"),
)
});
}
}

// XXX-dap insert the errors

// XXX-dap remove this
// We now need to fetch the ids for those baseboards. We need these
// ids in order to insert, for example, inv_service_processor
// records, which have a foreign key into the hw_baseboard_id table.
//
// This approach sucks. With raw SQL, we could do something like
//
// INSERT INTO inv_service_processor
// SELECT
// id
// [other service_processor column values]
// FROM hw_baseboard_id
// WHERE part_number = ... AND serial_number = ...;
//
// This way, we don't need to know the id directly.

// XXX-dap
//self.inventory_insert_cabooses(opctx, &collection.cabooses, &conn)
// .await?;

{
use db::schema::hw_baseboard_id::dsl as baseboard_dsl;
use db::schema::inv_service_processor::dsl as sp_dsl;

for (_, sp) in &collection.sps {
let selection =
db::schema::hw_baseboard_id::table.select((
collection_id.into_sql::<diesel::sql_types::Uuid>(),
baseboard_dsl::id,
sp.time_collected
.into_sql::<diesel::sql_types::Timestamptz>(),
sp.source.into_sql::<diesel::sql_types::Text>(),
i64::from(sp.baseboard_revision)
.into_sql::<diesel::sql_types::Int8>(),
sp.hubris_archive
.into_sql::<diesel::sql_types::Text>(),
HwPowerState::from(sp.power_state).into_sql(),
));

diesel::insert_into(
db::schema::inv_service_processor::table,
)
.values(selection)
.into_columns((
sp_dsl::inv_collection_id,
sp_dsl::hw_baseboard_id,
sp_dsl::time_collected,
sp_dsl::source,
sp_dsl::baseboard_revision,
sp_dsl::hubris_archive_id,
sp_dsl::power_state,
))
.execute_async(&conn)
.await
.map_err(|e| {
TransactionError::CustomError(
public_error_from_diesel_pool(
e.into(),
ErrorHandler::Server,
)
.internal_context("inserting service processor"),
)
});
}
}

//self.inventory_insert_sps(opctx, &collection.sps, &conn).await?;
//self.inventory_insert_cabooses_found(
// opctx,
// &collection.cabooses,
// &collection.sps,
// &conn,
//)
//.await?;
Ok(())
})
.await
.map_err(|error| match error {
TransactionError::CustomError(e) => e,
TransactionError::Pool(e) => {
public_error_from_diesel_pool(e, ErrorHandler::Server)
}
})
}

//async fn inventory_insert_baseboards<ConnErr>(
// &self,
// baseboards: impl Iterator<Item = &'_ BaseboardId>,
// conn: &(impl async_bb8_diesel::AsyncConnection<
// crate::db::pool::DbConnection,
// ConnErr,
// > + Sync),
//) -> Result<(), TransactionError<Error>>
//where
// ConnErr: From<diesel::result::Error> + Send + 'static,
// ConnErr: Into<PoolError>,
//{
// use db::schema::hw_baseboard_id::dsl;

// for baseboard_id in baseboards {
// let _ = diesel::insert_into(dsl::hw_baseboard_id)
// .values(HwBaseboardId::from(baseboard_id))
// .on_conflict_do_nothing()
// .execute_async(conn)
// .await
// .map_err(|e| {
// TransactionError::CustomError(
// public_error_from_diesel_pool(
// e.into(),
// ErrorHandler::Server,
// )
// .internal_context("inserting baseboard"),
// )
// });
// }

// Ok(())
//}
}
Loading

0 comments on commit bdcbd0e

Please sign in to comment.