From 8592a3bc364faf7c8a3db8e332f54204f278650a Mon Sep 17 00:00:00 2001 From: jeangab Date: Fri, 6 Sep 2024 16:10:06 -0400 Subject: [PATCH] feat: Refactored harmony into a workspace with independent modules per client, completed RusshClient test_connection implementation. Needs refactoring though --- harmony-rs/Cargo.lock | 6 +++ harmony-rs/Cargo.toml | 1 + harmony-rs/fqm/Cargo.toml | 5 ++ harmony-rs/fqm/src/inventory.rs | 12 +++-- harmony-rs/fqm/src/lib.rs | 1 + harmony-rs/fqm/src/main.rs | 22 +++++++++ harmony-rs/harmony/Cargo.toml | 2 +- .../harmony/src/domain/executors/mod.rs | 2 +- harmony-rs/harmony/src/domain/hardware/mod.rs | 10 ++-- .../harmony/src/domain/interpret/mod.rs | 46 +++++++++++++++++-- harmony-rs/harmony/src/domain/maestro/mod.rs | 7 +-- .../harmony/src/infra/executors/russh/mod.rs | 13 ++++-- harmony-rs/harmony/src/main.rs | 19 -------- .../harmony/src/modules/opnsense_dhcp.rs | 16 +++++-- 14 files changed, 116 insertions(+), 46 deletions(-) create mode 100644 harmony-rs/fqm/src/main.rs delete mode 100644 harmony-rs/harmony/src/main.rs diff --git a/harmony-rs/Cargo.lock b/harmony-rs/Cargo.lock index 91876e4..db8e801 100644 --- a/harmony-rs/Cargo.lock +++ b/harmony-rs/Cargo.lock @@ -619,6 +619,12 @@ dependencies = [ [[package]] name = "fqm" version = "0.1.0" +dependencies = [ + "env_logger", + "harmony", + "log", + "tokio", +] [[package]] name = "funty" diff --git a/harmony-rs/Cargo.toml b/harmony-rs/Cargo.toml index 256f1bd..3b9fa25 100644 --- a/harmony-rs/Cargo.toml +++ b/harmony-rs/Cargo.toml @@ -15,3 +15,4 @@ log = "0.4.22" env_logger = "0.11.5" derive-new = "0.7.0" async-trait = "0.1.82" +tokio = { version = "1.40.0", features = ["io-std"] } diff --git a/harmony-rs/fqm/Cargo.toml b/harmony-rs/fqm/Cargo.toml index 69e1ce8..3a9ebdc 100644 --- a/harmony-rs/fqm/Cargo.toml +++ b/harmony-rs/fqm/Cargo.toml @@ -4,3 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +harmony = { path = "../harmony" } +log = { workspace = true } +env_logger = { workspace = true } +tokio = { workspace = true } + diff --git a/harmony-rs/fqm/src/inventory.rs b/harmony-rs/fqm/src/inventory.rs index cd9bf0e..9efcefd 100644 --- a/harmony-rs/fqm/src/inventory.rs +++ b/harmony-rs/fqm/src/inventory.rs @@ -1,9 +1,11 @@ -use crate::domain::{ - hardware::{Location, Host, HostCategory}, +use harmony::domain::{ + hardware::{Host, HostCategory, Location, NetworkInterface}, inventory::Inventory, }; -pub fn get_fqm_inventory() -> Inventory { +pub fn get_inventory() -> Inventory { + let network = vec![NetworkInterface::new(1_000_000_000, "TODO MAC ADDRESS".into(), true ) ]; + let storage = vec![]; Inventory { location: Location::new( "1134 Grande Allée Ouest 1er étage, Québec, Qc".into(), @@ -11,8 +13,8 @@ pub fn get_fqm_inventory() -> Inventory { ), host: vec![Host { category: HostCategory::Server, - network: vec![], - storage: vec![], + network, + storage, labels: vec![], }], switch: vec![], diff --git a/harmony-rs/fqm/src/lib.rs b/harmony-rs/fqm/src/lib.rs index 7d12d9a..e39d3a9 100644 --- a/harmony-rs/fqm/src/lib.rs +++ b/harmony-rs/fqm/src/lib.rs @@ -1,3 +1,4 @@ +pub mod inventory; pub fn add(left: usize, right: usize) -> usize { left + right } diff --git a/harmony-rs/fqm/src/main.rs b/harmony-rs/fqm/src/main.rs new file mode 100644 index 0000000..99b8082 --- /dev/null +++ b/harmony-rs/fqm/src/main.rs @@ -0,0 +1,22 @@ +use fqm::inventory::get_inventory; +use harmony::{ + domain::{ + inventory::{Inventory, InventoryFilter}, + maestro::Maestro, + }, + modules::opnsense_dhcp::OPNSenseDhcpScore, +}; +use log::info; + +#[tokio::main] +async fn main() { + env_logger::init(); + + tokio::spawn(async move { + info!("FQM Harmony Starting"); + let maestro = Maestro::new(get_inventory()); + let score = OPNSenseDhcpScore::new(InventoryFilter::new(vec![])); + let result = maestro.interpret(score).await.unwrap(); + info!("{result}"); + }).await; +} diff --git a/harmony-rs/harmony/Cargo.toml b/harmony-rs/harmony/Cargo.toml index be11fef..0108040 100644 --- a/harmony-rs/harmony/Cargo.toml +++ b/harmony-rs/harmony/Cargo.toml @@ -11,7 +11,7 @@ rust-ipmi = "0.1.1" semver = "1.0.23" serde = { version = "1.0.209", features = ["derive"] } serde_json = "1.0.127" -tokio = { version = "1.40.0", features = ["io-std"] } +tokio = { workspace = true } derive-new = { workspace = true } log = { workspace = true } env_logger = { workspace = true } diff --git a/harmony-rs/harmony/src/domain/executors/mod.rs b/harmony-rs/harmony/src/domain/executors/mod.rs index 6d8096f..21de8d7 100644 --- a/harmony-rs/harmony/src/domain/executors/mod.rs +++ b/harmony-rs/harmony/src/domain/executors/mod.rs @@ -29,5 +29,5 @@ impl std::error::Error for ExecutorError {} #[async_trait] pub trait SshClient { - async fn test_connection(&self, username: String, password: String) -> Result<(), ExecutorError>; + async fn test_connection(&self, username: &str, password: &str) -> Result<(), ExecutorError>; } diff --git a/harmony-rs/harmony/src/domain/hardware/mod.rs b/harmony-rs/harmony/src/domain/hardware/mod.rs index 28e4d8c..32de3e1 100644 --- a/harmony-rs/harmony/src/domain/hardware/mod.rs +++ b/harmony-rs/harmony/src/domain/hardware/mod.rs @@ -18,14 +18,15 @@ pub enum HostCategory { Switch, } -#[derive(Debug)] +#[derive(Debug, new)] pub struct NetworkInterface { speed: u64, mac_address: MacAddress, plugged_in: bool, } type MacAddress = String; -#[derive(Debug)] + +#[derive(Debug, new)] pub enum StorageConnectionType { Sata3g, Sata6g, @@ -56,7 +57,10 @@ pub struct Switch { #[derive(Debug)] pub struct Firewall {} #[derive(Debug)] -pub struct Label; +pub struct Label { + name: String, + value: String, +} pub type Address = String; #[derive(new, Debug)] diff --git a/harmony-rs/harmony/src/domain/interpret/mod.rs b/harmony-rs/harmony/src/domain/interpret/mod.rs index 3d0a6c1..f3350c2 100644 --- a/harmony-rs/harmony/src/domain/interpret/mod.rs +++ b/harmony-rs/harmony/src/domain/interpret/mod.rs @@ -1,7 +1,10 @@ +use std::error::Error; + +use async_trait::async_trait; +use derive_new::new; + use super::{ - data::{Id, Version}, - inventory::Inventory, - score::Score, + data::{Id, Version}, executors::ExecutorError, inventory::Inventory, score::Score }; pub enum InterpretName { @@ -16,19 +19,25 @@ impl std::fmt::Display for InterpretName { } } +#[async_trait] pub trait Interpret { - fn execute(&self, inventory: &Inventory) -> Result; + async fn execute(&self, inventory: &Inventory) -> Result; fn get_name(&self) -> InterpretName; fn get_version(&self) -> Version; fn get_status(&self) -> InterpretStatus; fn get_children(&self) -> Vec; } -#[derive(Debug)] +#[derive(Debug, new)] pub struct Outcome { status: InterpretStatus, message: String, } +impl std::fmt::Display for Outcome { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("Outcome {}: {}", self.status, self.message)) + } +} #[derive(Debug, Clone)] pub enum InterpretStatus { @@ -39,7 +48,34 @@ pub enum InterpretStatus { BLOCKED, } +impl std::fmt::Display for InterpretStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let msg = match self { + InterpretStatus::SUCCESS => "SUCCESS", + InterpretStatus::FAILURE => "FAILURE", + InterpretStatus::RUNNING => "RUNNING", + InterpretStatus::QUEUED => "QUEUED", + InterpretStatus::BLOCKED => "BLOCKED", + }; + f.write_str(msg) + } +} + + #[derive(Debug)] pub struct InterpretError { msg: String, } + +impl std::fmt::Display for InterpretError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.msg) + } +} +impl Error for InterpretError {} + +impl From for InterpretError{ + fn from(value: ExecutorError) -> Self { + Self { msg: format!("InterpretError : {value}") } + } +} diff --git a/harmony-rs/harmony/src/domain/maestro/mod.rs b/harmony-rs/harmony/src/domain/maestro/mod.rs index 48b65dc..d71a9af 100644 --- a/harmony-rs/harmony/src/domain/maestro/mod.rs +++ b/harmony-rs/harmony/src/domain/maestro/mod.rs @@ -1,7 +1,7 @@ use derive_new::new; use log::info; -use super::{interpret::Interpret, inventory::Inventory, score::Score}; +use super::{interpret::{Interpret, InterpretError, Outcome}, inventory::Inventory, score::Score}; #[derive(new)] pub struct Maestro { @@ -28,11 +28,12 @@ impl Maestro { todo!() } - pub fn interpret(&self, score: S) { + pub async fn interpret(&self, score: S) -> Result { info!("Running score {score:?}"); let interpret: S::InterpretType = score.create_interpret(); info!("Launching interpret {interpret:?}"); - let result = interpret.execute(&self.inventory); + let result = interpret.execute(&self.inventory).await; info!("Got result {result:?}"); + result } } diff --git a/harmony-rs/harmony/src/infra/executors/russh/mod.rs b/harmony-rs/harmony/src/infra/executors/russh/mod.rs index d6d1845..b61c227 100644 --- a/harmony-rs/harmony/src/infra/executors/russh/mod.rs +++ b/harmony-rs/harmony/src/infra/executors/russh/mod.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use async_trait::async_trait; +use log::info; use russh::{client, keys::key}; use crate::domain::executors::{ExecutorError, SshClient}; @@ -9,11 +10,14 @@ pub struct RusshClient; #[async_trait] impl SshClient for RusshClient { - async fn test_connection(&self, _username: String, _password: String) -> Result<(), crate::domain::executors::ExecutorError> { + async fn test_connection(&self, _username: &str, _password: &str) -> Result<(), crate::domain::executors::ExecutorError> { let config = client::Config::default(); let c = Client{}; - let _client = client::connect(Arc::new(config), ("192.168.12.1", 22), c).await?; - Ok(()) + let mut client = client::connect(Arc::new(config), ("192.168.1.1", 22), c).await?; + match client.authenticate_password("nationtech", "opnsense").await? { + true => Ok(()), + false => Err(ExecutorError::AuthenticationError("ssh authentication failed".to_string())), + } } } @@ -36,6 +40,7 @@ impl client::Handler for Client { impl From for ExecutorError { fn from(_value: russh::Error) -> Self { - todo!() + // TODO handle various russh errors properly + ExecutorError::NetworkError("Russh client error".to_string()) } } diff --git a/harmony-rs/harmony/src/main.rs b/harmony-rs/harmony/src/main.rs deleted file mode 100644 index a04c1d3..0000000 --- a/harmony-rs/harmony/src/main.rs +++ /dev/null @@ -1,19 +0,0 @@ -use harmony::{ - domain::{ - inventory::{Inventory, InventoryFilter}, - maestro::Maestro, - }, - modules::opnsense_dhcp::OPNSenseDhcpScore, -}; - -pub fn main() { - env_logger::init(); - - let maestro = Maestro::new(get_inventory()); - let score = OPNSenseDhcpScore::new(InventoryFilter::new(vec![])); - maestro.interpret(score); -} - -fn get_inventory() -> Inventory { - todo!() -} diff --git a/harmony-rs/harmony/src/modules/opnsense_dhcp.rs b/harmony-rs/harmony/src/modules/opnsense_dhcp.rs index 7b72a5d..ca21dc1 100644 --- a/harmony-rs/harmony/src/modules/opnsense_dhcp.rs +++ b/harmony-rs/harmony/src/modules/opnsense_dhcp.rs @@ -1,8 +1,9 @@ +use async_trait::async_trait; use derive_new::new; use log::info; use crate::{domain::{ - data::{Id, Version}, hardware::NetworkInterface, interpret::{InterpretError, InterpretStatus, Outcome}, inventory::Inventory, topology::IpAddress + data::{Id, Version}, executors::SshClient, hardware::NetworkInterface, interpret::{InterpretError, InterpretStatus, Outcome}, inventory::Inventory, topology::IpAddress }, infra::executors::russh::RusshClient}; use crate::domain::{ @@ -54,6 +55,7 @@ impl OPNSenseDhcpInterpret { } } +#[async_trait] impl Interpret for OPNSenseDhcpInterpret { fn get_name(&self) -> InterpretName { InterpretName::OPNSenseDHCP @@ -71,11 +73,15 @@ impl Interpret for OPNSenseDhcpInterpret { todo!() } - fn execute(&self, _inventory: &Inventory) -> Result { + async fn execute(&self, _inventory: &Inventory) -> Result { info!("Executing {} on inventory {_inventory:?}", self.get_name()); - let _ssh_client = RusshClient{}; - // ssh_client.test_connection("username", "password"); - todo!() + let ssh_client = RusshClient{}; + + info!("RusshClient initiated"); + ssh_client.test_connection("paul", "paul").await?; + info!("Connection test complete"); + + Ok(Outcome::new(InterpretStatus::SUCCESS, "Connection test successful".to_string())) } }