forked from NationTech/harmony
		
	feat: Refactored harmony into a workspace with independent modules per client, completed RusshClient test_connection implementation. Needs refactoring though
This commit is contained in:
		
							parent
							
								
									cc01ec5fe5
								
							
						
					
					
						commit
						8592a3bc36
					
				
							
								
								
									
										6
									
								
								harmony-rs/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								harmony-rs/Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -619,6 +619,12 @@ dependencies = [ | |||||||
| [[package]] | [[package]] | ||||||
| name = "fqm" | name = "fqm" | ||||||
| version = "0.1.0" | version = "0.1.0" | ||||||
|  | dependencies = [ | ||||||
|  |  "env_logger", | ||||||
|  |  "harmony", | ||||||
|  |  "log", | ||||||
|  |  "tokio", | ||||||
|  | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "funty" | name = "funty" | ||||||
|  | |||||||
| @ -15,3 +15,4 @@ log = "0.4.22" | |||||||
| env_logger = "0.11.5" | env_logger = "0.11.5" | ||||||
| derive-new = "0.7.0" | derive-new = "0.7.0" | ||||||
| async-trait = "0.1.82" | async-trait = "0.1.82" | ||||||
|  | tokio = { version = "1.40.0", features = ["io-std"] } | ||||||
|  | |||||||
| @ -4,3 +4,8 @@ version = "0.1.0" | |||||||
| edition = "2021" | edition = "2021" | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
|  | harmony = { path = "../harmony" } | ||||||
|  | log = { workspace = true } | ||||||
|  | env_logger = { workspace = true } | ||||||
|  | tokio = { workspace = true } | ||||||
|  | 
 | ||||||
|  | |||||||
| @ -1,9 +1,11 @@ | |||||||
| use crate::domain::{ | use harmony::domain::{ | ||||||
|     hardware::{Location, Host, HostCategory}, |     hardware::{Host, HostCategory, Location, NetworkInterface}, | ||||||
|     inventory::Inventory, |     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 { |     Inventory { | ||||||
|         location: Location::new( |         location: Location::new( | ||||||
|             "1134 Grande Allée Ouest 1er étage, Québec, Qc".into(), |             "1134 Grande Allée Ouest 1er étage, Québec, Qc".into(), | ||||||
| @ -11,8 +13,8 @@ pub fn get_fqm_inventory() -> Inventory { | |||||||
|         ), |         ), | ||||||
|         host: vec![Host { |         host: vec![Host { | ||||||
|             category: HostCategory::Server, |             category: HostCategory::Server, | ||||||
|             network: vec![], |             network, | ||||||
|             storage: vec![], |             storage, | ||||||
|             labels: vec![], |             labels: vec![], | ||||||
|         }], |         }], | ||||||
|         switch: vec![], |         switch: vec![], | ||||||
|  | |||||||
| @ -1,3 +1,4 @@ | |||||||
|  | pub mod inventory; | ||||||
| pub fn add(left: usize, right: usize) -> usize { | pub fn add(left: usize, right: usize) -> usize { | ||||||
|     left + right |     left + right | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										22
									
								
								harmony-rs/fqm/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								harmony-rs/fqm/src/main.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
|  | } | ||||||
| @ -11,7 +11,7 @@ rust-ipmi = "0.1.1" | |||||||
| semver = "1.0.23" | semver = "1.0.23" | ||||||
| serde = { version = "1.0.209", features = ["derive"] } | serde = { version = "1.0.209", features = ["derive"] } | ||||||
| serde_json = "1.0.127" | serde_json = "1.0.127" | ||||||
| tokio = { version = "1.40.0", features = ["io-std"] } | tokio = { workspace = true } | ||||||
| derive-new = { workspace = true } | derive-new = { workspace = true } | ||||||
| log = { workspace = true } | log = { workspace = true } | ||||||
| env_logger = { workspace = true } | env_logger = { workspace = true } | ||||||
|  | |||||||
| @ -29,5 +29,5 @@ impl std::error::Error for ExecutorError {} | |||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| pub trait SshClient { | 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>; | ||||||
| } | } | ||||||
|  | |||||||
| @ -18,14 +18,15 @@ pub enum HostCategory { | |||||||
|     Switch, |     Switch, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug, new)] | ||||||
| pub struct NetworkInterface { | pub struct NetworkInterface { | ||||||
|     speed: u64, |     speed: u64, | ||||||
|     mac_address: MacAddress, |     mac_address: MacAddress, | ||||||
|     plugged_in: bool, |     plugged_in: bool, | ||||||
| } | } | ||||||
| type MacAddress = String; | type MacAddress = String; | ||||||
| #[derive(Debug)] | 
 | ||||||
|  | #[derive(Debug, new)] | ||||||
| pub enum StorageConnectionType { | pub enum StorageConnectionType { | ||||||
|     Sata3g, |     Sata3g, | ||||||
|     Sata6g, |     Sata6g, | ||||||
| @ -56,7 +57,10 @@ pub struct Switch { | |||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Firewall {} | pub struct Firewall {} | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct Label; | pub struct Label { | ||||||
|  |     name: String, | ||||||
|  |     value: String, | ||||||
|  | } | ||||||
| pub type Address = String; | pub type Address = String; | ||||||
| 
 | 
 | ||||||
| #[derive(new, Debug)] | #[derive(new, Debug)] | ||||||
|  | |||||||
| @ -1,7 +1,10 @@ | |||||||
|  | use std::error::Error; | ||||||
|  | 
 | ||||||
|  | use async_trait::async_trait; | ||||||
|  | use derive_new::new; | ||||||
|  | 
 | ||||||
| use super::{ | use super::{ | ||||||
|     data::{Id, Version}, |     data::{Id, Version}, executors::ExecutorError, inventory::Inventory, score::Score | ||||||
|     inventory::Inventory, |  | ||||||
|     score::Score, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| pub enum InterpretName { | pub enum InterpretName { | ||||||
| @ -16,19 +19,25 @@ impl std::fmt::Display for InterpretName { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[async_trait] | ||||||
| pub trait Interpret { | pub trait Interpret { | ||||||
|     fn execute(&self, inventory: &Inventory) -> Result<Outcome, InterpretError>; |     async fn execute(&self, inventory: &Inventory) -> Result<Outcome, InterpretError>; | ||||||
|     fn get_name(&self) -> InterpretName; |     fn get_name(&self) -> InterpretName; | ||||||
|     fn get_version(&self) -> Version; |     fn get_version(&self) -> Version; | ||||||
|     fn get_status(&self) -> InterpretStatus; |     fn get_status(&self) -> InterpretStatus; | ||||||
|     fn get_children(&self) -> Vec<Id>; |     fn get_children(&self) -> Vec<Id>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Debug)] | #[derive(Debug, new)] | ||||||
| pub struct Outcome { | pub struct Outcome { | ||||||
|     status: InterpretStatus, |     status: InterpretStatus, | ||||||
|     message: String, |     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)] | #[derive(Debug, Clone)] | ||||||
| pub enum InterpretStatus { | pub enum InterpretStatus { | ||||||
| @ -39,7 +48,34 @@ pub enum InterpretStatus { | |||||||
|     BLOCKED, |     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)] | #[derive(Debug)] | ||||||
| pub struct InterpretError { | pub struct InterpretError { | ||||||
|     msg: String, |     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<ExecutorError> for InterpretError{ | ||||||
|  |     fn from(value: ExecutorError) -> Self { | ||||||
|  |         Self { msg: format!("InterpretError : {value}") } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| use derive_new::new; | use derive_new::new; | ||||||
| use log::info; | use log::info; | ||||||
| 
 | 
 | ||||||
| use super::{interpret::Interpret, inventory::Inventory, score::Score}; | use super::{interpret::{Interpret, InterpretError, Outcome}, inventory::Inventory, score::Score}; | ||||||
| 
 | 
 | ||||||
| #[derive(new)] | #[derive(new)] | ||||||
| pub struct Maestro { | pub struct Maestro { | ||||||
| @ -28,11 +28,12 @@ impl Maestro { | |||||||
|         todo!() |         todo!() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn interpret<S: Score>(&self, score: S) { |     pub async fn interpret<S: Score>(&self, score: S) -> Result<Outcome, InterpretError> { | ||||||
|         info!("Running score {score:?}"); |         info!("Running score {score:?}"); | ||||||
|         let interpret: S::InterpretType = score.create_interpret(); |         let interpret: S::InterpretType = score.create_interpret(); | ||||||
|         info!("Launching interpret {interpret:?}"); |         info!("Launching interpret {interpret:?}"); | ||||||
|         let result = interpret.execute(&self.inventory); |         let result = interpret.execute(&self.inventory).await; | ||||||
|         info!("Got result {result:?}"); |         info!("Got result {result:?}"); | ||||||
|  |         result | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| 
 | 
 | ||||||
|  | use log::info; | ||||||
| use russh::{client, keys::key}; | use russh::{client, keys::key}; | ||||||
| 
 | 
 | ||||||
| use crate::domain::executors::{ExecutorError, SshClient}; | use crate::domain::executors::{ExecutorError, SshClient}; | ||||||
| @ -9,11 +10,14 @@ pub struct RusshClient; | |||||||
| 
 | 
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl SshClient for RusshClient { | 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 config = client::Config::default(); | ||||||
|         let c = Client{}; |         let c = Client{}; | ||||||
|         let _client = client::connect(Arc::new(config), ("192.168.12.1", 22), c).await?; |         let mut client = client::connect(Arc::new(config), ("192.168.1.1", 22), c).await?; | ||||||
|         Ok(()) |         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<russh::Error> for ExecutorError { | impl From<russh::Error> for ExecutorError { | ||||||
|     fn from(_value: russh::Error) -> Self { |     fn from(_value: russh::Error) -> Self { | ||||||
|         todo!() |         // TODO handle various russh errors properly
 | ||||||
|  |         ExecutorError::NetworkError("Russh client error".to_string()) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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!() |  | ||||||
| } |  | ||||||
| @ -1,8 +1,9 @@ | |||||||
|  | use async_trait::async_trait; | ||||||
| use derive_new::new; | use derive_new::new; | ||||||
| use log::info; | use log::info; | ||||||
| 
 | 
 | ||||||
| use crate::{domain::{ | 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}; | }, infra::executors::russh::RusshClient}; | ||||||
| 
 | 
 | ||||||
| use crate::domain::{ | use crate::domain::{ | ||||||
| @ -54,6 +55,7 @@ impl OPNSenseDhcpInterpret { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | #[async_trait] | ||||||
| impl Interpret for OPNSenseDhcpInterpret { | impl Interpret for OPNSenseDhcpInterpret { | ||||||
|     fn get_name(&self) -> InterpretName { |     fn get_name(&self) -> InterpretName { | ||||||
|         InterpretName::OPNSenseDHCP |         InterpretName::OPNSenseDHCP | ||||||
| @ -71,11 +73,15 @@ impl Interpret for OPNSenseDhcpInterpret { | |||||||
|         todo!() |         todo!() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn execute(&self, _inventory: &Inventory) -> Result<Outcome, InterpretError> { |     async fn execute(&self, _inventory: &Inventory) -> Result<Outcome, InterpretError> { | ||||||
|         info!("Executing {} on inventory {_inventory:?}", self.get_name()); |         info!("Executing {} on inventory {_inventory:?}", self.get_name()); | ||||||
|         let _ssh_client = RusshClient{}; |         let ssh_client = RusshClient{}; | ||||||
|         // ssh_client.test_connection("username", "password");
 | 
 | ||||||
|         todo!() |         info!("RusshClient initiated"); | ||||||
|  |         ssh_client.test_connection("paul", "paul").await?; | ||||||
|  |         info!("Connection test complete"); | ||||||
|  | 
 | ||||||
|  |         Ok(Outcome::new(InterpretStatus::SUCCESS, "Connection test successful".to_string())) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user