diff --git a/harmony/src/domain/hardware/mod.rs b/harmony/src/domain/hardware/mod.rs index 8ff3db1..1b1a72c 100644 --- a/harmony/src/domain/hardware/mod.rs +++ b/harmony/src/domain/hardware/mod.rs @@ -149,6 +149,98 @@ impl PhysicalHost { parts.join(" | ") } + pub fn parts_list(&self) -> String { + let PhysicalHost { + id, + category, + network, + storage, + labels, + memory_modules, + cpus, + } = self; + + let mut parts_list = String::new(); + parts_list.push_str("\n\n====================="); + parts_list.push_str(&format!("\nHost ID {id}")); + parts_list.push_str("\n====================="); + parts_list.push_str("\n\n====================="); + parts_list.push_str(&format!("\nCPU count {}", cpus.len())); + parts_list.push_str("\n====================="); + cpus.iter().for_each(|c| { + let CPU { + model, + vendor, + cores, + threads, + frequency_mhz, + } = c; + parts_list.push_str(&format!( + "\n{vendor} {model}, {cores}/{threads} {}Ghz", + *frequency_mhz as f64 / 1000.0 + )); + }); + + parts_list.push_str("\n\n====================="); + parts_list.push_str(&format!("\nNetwork Interfaces count {}", network.len())); + parts_list.push_str("\n====================="); + network.iter().for_each(|nic| { + parts_list.push_str(&format!( + "\nNic({} {}Gbps mac({}) ipv4({}), ipv6({})", + nic.name, + nic.speed_mbps.unwrap_or(0) / 1000, + nic.mac_address, + nic.ipv4_addresses.join(","), + nic.ipv6_addresses.join(",") + )); + }); + + parts_list.push_str("\n\n====================="); + parts_list.push_str(&format!("\nStorage drives count {}", storage.len())); + parts_list.push_str("\n====================="); + storage.iter().for_each(|drive| { + let StorageDrive { + name, + model, + serial, + size_bytes, + logical_block_size: _, + physical_block_size: _, + rotational: _, + wwn: _, + interface_type, + smart_status, + } = drive; + parts_list.push_str(&format!( + "\n{name} {}Gb {model} {interface_type} smart({smart_status:?}) {serial}", + size_bytes / 1000 / 1000 / 1000 + )); + }); + + parts_list.push_str("\n\n====================="); + parts_list.push_str(&format!("\nMemory modules count {}", memory_modules.len())); + parts_list.push_str("\n====================="); + memory_modules.iter().for_each(|mem| { + let MemoryModule { + size_bytes, + speed_mhz, + manufacturer, + part_number, + serial_number, + rank, + } = mem; + parts_list.push_str(&format!( + "\n{}Gb, {}Mhz, Manufacturer ({}), Part Number ({})", + size_bytes / 1000 / 1000 / 1000, + speed_mhz.unwrap_or(0), + manufacturer.as_ref().unwrap_or(&String::new()), + part_number.as_ref().unwrap_or(&String::new()), + )); + }); + + parts_list + } + pub fn cluster_mac(&self) -> MacAddress { self.network .first() diff --git a/harmony/src/domain/inventory/mod.rs b/harmony/src/domain/inventory/mod.rs index 072ab79..7d160d7 100644 --- a/harmony/src/domain/inventory/mod.rs +++ b/harmony/src/domain/inventory/mod.rs @@ -18,6 +18,7 @@ impl InventoryFilter { use derive_new::new; use log::info; use serde::{Deserialize, Serialize}; +use strum::EnumIter; use crate::hardware::{ManagementInterface, ManualManagementInterface}; @@ -63,7 +64,7 @@ impl Inventory { } } -#[derive(Debug, Serialize, Deserialize, sqlx::Type, Clone)] +#[derive(Debug, Serialize, Deserialize, sqlx::Type, Clone, EnumIter)] pub enum HostRole { Bootstrap, ControlPlane, diff --git a/harmony/src/domain/inventory/repository.rs b/harmony/src/domain/inventory/repository.rs index 0728cc7..7b6d798 100644 --- a/harmony/src/domain/inventory/repository.rs +++ b/harmony/src/domain/inventory/repository.rs @@ -29,7 +29,7 @@ pub trait InventoryRepository: Send + Sync + 'static { async fn save(&self, host: &PhysicalHost) -> Result<(), RepoError>; async fn get_latest_by_id(&self, host_id: &str) -> Result, RepoError>; async fn get_all_hosts(&self) -> Result, RepoError>; - async fn get_host_for_role(&self, role: HostRole) -> Result, RepoError>; + async fn get_host_for_role(&self, role: &HostRole) -> Result, RepoError>; async fn save_role_mapping( &self, role: &HostRole, diff --git a/harmony/src/infra/inventory/sqlite.rs b/harmony/src/infra/inventory/sqlite.rs index cd83df7..f772f72 100644 --- a/harmony/src/infra/inventory/sqlite.rs +++ b/harmony/src/infra/inventory/sqlite.rs @@ -109,7 +109,7 @@ impl InventoryRepository for SqliteInventoryRepository { Ok(()) } - async fn get_host_for_role(&self, role: HostRole) -> Result, RepoError> { + async fn get_host_for_role(&self, role: &HostRole) -> Result, RepoError> { struct HostIdRow { host_id: String, } diff --git a/harmony/src/modules/inventory/discovery.rs b/harmony/src/modules/inventory/discovery.rs index 1cbb23d..143c56a 100644 --- a/harmony/src/modules/inventory/discovery.rs +++ b/harmony/src/modules/inventory/discovery.rs @@ -76,7 +76,7 @@ impl Interpret for DiscoverHostForRoleInterpret { Ok(choice) => { info!("Selected {} as the bootstrap node.", choice.summary()); host_repo - .save_role_mapping(&HostRole::Bootstrap, &choice) + .save_role_mapping(&self.score.role, &choice) .await?; host = choice; break; diff --git a/harmony/src/modules/inventory/inspect.rs b/harmony/src/modules/inventory/inspect.rs new file mode 100644 index 0000000..aa40a42 --- /dev/null +++ b/harmony/src/modules/inventory/inspect.rs @@ -0,0 +1,72 @@ +use async_trait::async_trait; +use harmony_types::id::Id; +use log::info; +use serde::{Deserialize, Serialize}; +use strum::IntoEnumIterator; + +use crate::{ + data::Version, + infra::inventory::InventoryRepositoryFactory, + interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, + inventory::{HostRole, Inventory}, + score::Score, + topology::Topology, +}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct InspectInventoryScore {} + +impl Score for InspectInventoryScore { + fn name(&self) -> String { + "InspectInventoryScore".to_string() + } + + #[doc(hidden)] + fn create_interpret(&self) -> Box> { + Box::new(InspectInventoryInterpret {}) + } +} + +#[derive(Debug)] +pub struct InspectInventoryInterpret; + +#[async_trait] +impl Interpret for InspectInventoryInterpret { + async fn execute( + &self, + _inventory: &Inventory, + _topology: &T, + ) -> Result { + let repo = InventoryRepositoryFactory::build().await?; + for role in HostRole::iter() { + info!("Inspecting hosts for role {role:?}"); + let hosts = repo.get_host_for_role(&role).await?; + info!("Hosts with role {role:?} : {}", hosts.len()); + hosts.iter().enumerate().for_each(|(idx, h)| { + info!( + "Found host index {idx} with role {role:?} => \n{}\n{}", + h.summary(), + h.parts_list() + ) + }); + } + Ok(Outcome::success( + "Inventory inspection complete".to_string(), + )) + } + fn get_name(&self) -> InterpretName { + InterpretName::Custom("InspectInventoryInterpret") + } + + fn get_version(&self) -> Version { + todo!() + } + + fn get_status(&self) -> InterpretStatus { + todo!() + } + + fn get_children(&self) -> Vec { + todo!() + } +} diff --git a/harmony/src/modules/inventory/mod.rs b/harmony/src/modules/inventory/mod.rs index a0f6443..0274dc4 100644 --- a/harmony/src/modules/inventory/mod.rs +++ b/harmony/src/modules/inventory/mod.rs @@ -1,4 +1,5 @@ mod discovery; +pub mod inspect; pub use discovery::*; use async_trait::async_trait; diff --git a/harmony/src/modules/okd/bootstrap_01_prepare.rs b/harmony/src/modules/okd/bootstrap_01_prepare.rs index 70a0b1a..d3409e2 100644 --- a/harmony/src/modules/okd/bootstrap_01_prepare.rs +++ b/harmony/src/modules/okd/bootstrap_01_prepare.rs @@ -100,7 +100,7 @@ When you can dig them, confirm to continue. let repo = InventoryRepositoryFactory::build().await?; while bootstrap_host.is_none() { - let hosts = repo.get_host_for_role(HostRole::Bootstrap).await?; + let hosts = repo.get_host_for_role(&HostRole::Bootstrap).await?; bootstrap_host = hosts.into_iter().next().to_owned(); DiscoverHostForRoleScore { role: HostRole::Bootstrap, diff --git a/harmony/src/modules/okd/bootstrap_02_bootstrap.rs b/harmony/src/modules/okd/bootstrap_02_bootstrap.rs index b2bde35..c08179a 100644 --- a/harmony/src/modules/okd/bootstrap_02_bootstrap.rs +++ b/harmony/src/modules/okd/bootstrap_02_bootstrap.rs @@ -67,7 +67,7 @@ impl OKDSetup02BootstrapInterpret { async fn get_bootstrap_node(&self) -> Result { let repo = InventoryRepositoryFactory::build().await?; match repo - .get_host_for_role(HostRole::Bootstrap) + .get_host_for_role(&HostRole::Bootstrap) .await? .into_iter() .next() diff --git a/harmony/src/modules/okd/bootstrap_03_control_plane.rs b/harmony/src/modules/okd/bootstrap_03_control_plane.rs index bc5ab6f..0ee7f2e 100644 --- a/harmony/src/modules/okd/bootstrap_03_control_plane.rs +++ b/harmony/src/modules/okd/bootstrap_03_control_plane.rs @@ -66,7 +66,7 @@ impl OKDSetup03ControlPlaneInterpret { ) -> Result, InterpretError> { const REQUIRED_HOSTS: usize = 3; let repo = InventoryRepositoryFactory::build().await?; - let mut control_plane_hosts = repo.get_host_for_role(HostRole::ControlPlane).await?; + let mut control_plane_hosts = repo.get_host_for_role(&HostRole::ControlPlane).await?; while control_plane_hosts.len() < REQUIRED_HOSTS { info!( @@ -80,7 +80,7 @@ impl OKDSetup03ControlPlaneInterpret { } .interpret(inventory, topology) .await?; - control_plane_hosts = repo.get_host_for_role(HostRole::ControlPlane).await?; + control_plane_hosts = repo.get_host_for_role(&HostRole::ControlPlane).await?; } if control_plane_hosts.len() < REQUIRED_HOSTS {