Core domain structure for harmony rs (#1)
Co-authored-by: jeangab <jeangabriel.gc@gmail.com> Co-authored-by: Jean-Gabriel Gill-Couture <jeangabriel.gc@gmail.com> Reviewed-on: https://git.nationtech.io/johnride/harmony/pulls/1 Co-authored-by: jeangab <jg@nationtech.io> Co-committed-by: jeangab <jg@nationtech.io>
This commit is contained in:
parent
231b1cca9f
commit
aa28ab37b8
1101
harmony-rs/Cargo.lock
generated
1101
harmony-rs/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,14 @@ version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
derive-new = "0.7.0"
|
||||
env_logger = "0.11.5"
|
||||
libredfish = "0.1.1"
|
||||
log = "0.4.22"
|
||||
reqwest = {version = "0.11", features = ["blocking", "json"] }
|
||||
russh = "0.45.0"
|
||||
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"] }
|
||||
|
0
harmony-rs/src/domain/cluster/mod.rs
Normal file
0
harmony-rs/src/domain/cluster/mod.rs
Normal file
12
harmony-rs/src/domain/data/id.rs
Normal file
12
harmony-rs/src/domain/data/id.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Id {
|
||||
value: String,
|
||||
}
|
||||
|
||||
impl Id {
|
||||
pub fn from_string(value: String) -> Self {
|
||||
Self { value }
|
||||
}
|
||||
}
|
4
harmony-rs/src/domain/data/mod.rs
Normal file
4
harmony-rs/src/domain/data/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
mod id;
|
||||
mod version;
|
||||
pub use id::*;
|
||||
pub use version::*;
|
76
harmony-rs/src/domain/data/version.rs
Normal file
76
harmony-rs/src/domain/data/version.rs
Normal file
@ -0,0 +1,76 @@
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Version {
|
||||
value: semver::Version,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VersionError {
|
||||
msg: String,
|
||||
}
|
||||
|
||||
impl From<semver::Error> for VersionError {
|
||||
fn from(value: semver::Error) -> Self {
|
||||
Self {
|
||||
msg: value.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Version {
|
||||
pub fn from(val: &str) -> Result<Self, VersionError> {
|
||||
Ok(Self {
|
||||
value: semver::Version::parse(val)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::Deserialize<'de> for Version {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
semver::Version::parse(&s)
|
||||
.map(|value| Version { value })
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::Serialize for Version {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
self.value.to_string().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Version {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
return self.value.fmt(f);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn version_serialize_deserialize() {
|
||||
let v = "10.0.1331-ababa+b123";
|
||||
let version = Version {
|
||||
value: semver::Version::parse(v).unwrap(),
|
||||
};
|
||||
|
||||
let s = serde_json::to_string(&version).unwrap();
|
||||
let version2: Version = serde_json::from_str(&s).unwrap();
|
||||
|
||||
assert_eq!(version2.value.major, 10);
|
||||
assert_eq!(version2.value.minor, 0);
|
||||
assert_eq!(version2.value.patch, 1331);
|
||||
assert_eq!(version2.value.build.to_string(), "b123");
|
||||
assert_eq!(version2.value.pre.to_string(), "ababa");
|
||||
assert_eq!(version2.value.to_string(), v);
|
||||
}
|
||||
}
|
30
harmony-rs/src/domain/executors/mod.rs
Normal file
30
harmony-rs/src/domain/executors/mod.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use std::fmt;
|
||||
|
||||
pub struct ExecutorResult {
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ExecutorError {
|
||||
NetworkError(String),
|
||||
AuthenticationError(String),
|
||||
ConfigurationError(String),
|
||||
UnexpectedError(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for ExecutorError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ExecutorError::NetworkError(msg) => write!(f, "Network error: {}", msg),
|
||||
ExecutorError::AuthenticationError(msg) => write!(f, "Authentication error: {}", msg),
|
||||
ExecutorError::ConfigurationError(msg) => write!(f, "Configuration error: {}", msg),
|
||||
ExecutorError::UnexpectedError(msg) => write!(f, "Unexpected error: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ExecutorError {}
|
||||
|
||||
pub trait SshClient {
|
||||
fn test_connection(&self, username: String, password: String) -> Result<(), ExecutorError>;
|
||||
}
|
15
harmony-rs/src/domain/filter.rs
Normal file
15
harmony-rs/src/domain/filter.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use derive_new::new;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum FilterKind {
|
||||
Label,
|
||||
Kind,
|
||||
}
|
||||
|
||||
pub type FilterValue = String;
|
||||
|
||||
#[derive(Debug, new, Clone)]
|
||||
pub struct Filter {
|
||||
kind: FilterKind,
|
||||
value: FilterValue,
|
||||
}
|
0
harmony-rs/src/domain/hardware/building.rs
Normal file
0
harmony-rs/src/domain/hardware/building.rs
Normal file
0
harmony-rs/src/domain/hardware/host.rs
Normal file
0
harmony-rs/src/domain/hardware/host.rs
Normal file
76
harmony-rs/src/domain/hardware/mod.rs
Normal file
76
harmony-rs/src/domain/hardware/mod.rs
Normal file
@ -0,0 +1,76 @@
|
||||
use derive_new::new;
|
||||
|
||||
pub type HostGroup = Vec<Host>;
|
||||
pub type SwitchGroup = Vec<Switch>;
|
||||
pub type FirewallGroup = Vec<Firewall>;
|
||||
#[derive(Debug)]
|
||||
pub struct Host {
|
||||
pub category: HostCategory,
|
||||
pub network: Vec<NetworkInterface>,
|
||||
pub storage: Vec<Storage>,
|
||||
pub labels: Vec<Label>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum HostCategory {
|
||||
Server,
|
||||
Firewall,
|
||||
Switch,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NetworkInterface {
|
||||
speed: u64,
|
||||
mac_address: MacAddress,
|
||||
plugged_in: bool,
|
||||
}
|
||||
type MacAddress = String;
|
||||
#[derive(Debug)]
|
||||
pub enum StorageConnectionType {
|
||||
Sata3g,
|
||||
Sata6g,
|
||||
Sas6g,
|
||||
Sas12g,
|
||||
PCIE,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub enum StorageKind {
|
||||
SSD,
|
||||
NVME,
|
||||
HDD,
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct Storage {
|
||||
connection: StorageConnectionType,
|
||||
kind: StorageKind,
|
||||
size: u64,
|
||||
serial: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Switch {
|
||||
interface: Vec<NetworkInterface>,
|
||||
management_interface: NetworkInterface,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Firewall {}
|
||||
#[derive(Debug)]
|
||||
pub struct Label;
|
||||
pub type Address = String;
|
||||
|
||||
#[derive(new, Debug)]
|
||||
pub struct Location {
|
||||
pub address: Address,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl Location {
|
||||
#[cfg(test)]
|
||||
pub fn test_building() -> Location {
|
||||
Self {
|
||||
address: String::new(),
|
||||
name: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
0
harmony-rs/src/domain/hardware/network_cable.rs
Normal file
0
harmony-rs/src/domain/hardware/network_cable.rs
Normal file
0
harmony-rs/src/domain/hardware/power_cable.rs
Normal file
0
harmony-rs/src/domain/hardware/power_cable.rs
Normal file
0
harmony-rs/src/domain/hardware/rack.rs
Normal file
0
harmony-rs/src/domain/hardware/rack.rs
Normal file
0
harmony-rs/src/domain/hardware/switch.rs
Normal file
0
harmony-rs/src/domain/hardware/switch.rs
Normal file
0
harmony-rs/src/domain/hardware/ups.rs
Normal file
0
harmony-rs/src/domain/hardware/ups.rs
Normal file
45
harmony-rs/src/domain/interpret/mod.rs
Normal file
45
harmony-rs/src/domain/interpret/mod.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use super::{
|
||||
data::{Id, Version},
|
||||
inventory::Inventory,
|
||||
score::Score,
|
||||
};
|
||||
|
||||
pub enum InterpretName {
|
||||
OPNSenseDHCP,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InterpretName {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
InterpretName::OPNSenseDHCP => f.write_str("OPNSenseDHCP"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Interpret {
|
||||
fn execute(&self, inventory: &Inventory) -> Result<Outcome, InterpretError>;
|
||||
fn get_name(&self) -> InterpretName;
|
||||
fn get_version(&self) -> Version;
|
||||
fn get_status(&self) -> InterpretStatus;
|
||||
fn get_children(&self) -> Vec<Id>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Outcome {
|
||||
status: InterpretStatus,
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InterpretStatus {
|
||||
SUCCESS,
|
||||
FAILURE,
|
||||
RUNNING,
|
||||
QUEUED,
|
||||
BLOCKED,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InterpretError {
|
||||
msg: String,
|
||||
}
|
40
harmony-rs/src/domain/inventory/mod.rs
Normal file
40
harmony-rs/src/domain/inventory/mod.rs
Normal file
@ -0,0 +1,40 @@
|
||||
#[derive(Debug, new, Clone)]
|
||||
pub struct InventoryFilter {
|
||||
target: Vec<Filter>,
|
||||
}
|
||||
|
||||
pub struct InventorySlice;
|
||||
|
||||
impl InventoryFilter {
|
||||
pub fn apply(&self, _inventory: &Inventory) -> InventorySlice {
|
||||
// TODO apply inventory filter, refactor as a slice
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
use derive_new::new;
|
||||
|
||||
use super::{
|
||||
filter::Filter,
|
||||
hardware::{Location, FirewallGroup, HostGroup, SwitchGroup},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Inventory {
|
||||
pub location: Location,
|
||||
pub host: HostGroup,
|
||||
pub switch: SwitchGroup,
|
||||
pub firewall: FirewallGroup,
|
||||
}
|
||||
|
||||
impl Inventory {
|
||||
#[cfg(test)]
|
||||
pub fn empty_inventory() -> Self {
|
||||
Self {
|
||||
location: Location::test_building(),
|
||||
host: HostGroup::new(),
|
||||
switch: SwitchGroup::new(),
|
||||
firewall: FirewallGroup::new(),
|
||||
}
|
||||
}
|
||||
}
|
38
harmony-rs/src/domain/maestro/mod.rs
Normal file
38
harmony-rs/src/domain/maestro/mod.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use derive_new::new;
|
||||
use log::info;
|
||||
|
||||
use super::{interpret::Interpret, inventory::Inventory, score::Score};
|
||||
|
||||
#[derive(new)]
|
||||
pub struct Maestro {
|
||||
inventory: Inventory,
|
||||
}
|
||||
|
||||
impl Maestro {
|
||||
pub fn start(&mut self) {
|
||||
info!("Starting Maestro");
|
||||
self.load_score();
|
||||
self.load_inventory();
|
||||
self.launch_interprets();
|
||||
}
|
||||
|
||||
fn load_score(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn load_inventory(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn launch_interprets(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn interpret<S: Score>(&self, score: S) {
|
||||
info!("Running score {score:?}");
|
||||
let interpret: S::InterpretType = score.create_interpret();
|
||||
info!("Launching interpret {interpret:?}");
|
||||
let result = interpret.execute(&self.inventory);
|
||||
info!("Got result {result:?}");
|
||||
}
|
||||
}
|
9
harmony-rs/src/domain/mod.rs
Normal file
9
harmony-rs/src/domain/mod.rs
Normal file
@ -0,0 +1,9 @@
|
||||
pub mod data;
|
||||
pub mod executors;
|
||||
pub mod filter;
|
||||
pub mod hardware;
|
||||
pub mod interpret;
|
||||
pub mod inventory;
|
||||
pub mod maestro;
|
||||
pub mod score;
|
||||
pub mod topology;
|
7
harmony-rs/src/domain/score.rs
Normal file
7
harmony-rs/src/domain/score.rs
Normal file
@ -0,0 +1,7 @@
|
||||
use super::{interpret::Interpret, inventory::InventorySlice};
|
||||
|
||||
pub trait Score: Send + Sync + std::fmt::Debug {
|
||||
type InterpretType: Interpret + std::fmt::Debug;
|
||||
fn get_inventory_filter(&self) -> InventorySlice;
|
||||
fn create_interpret(self) -> Self::InterpretType;
|
||||
}
|
13
harmony-rs/src/domain/topology/mod.rs
Normal file
13
harmony-rs/src/domain/topology/mod.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use super::hardware::HostGroup;
|
||||
|
||||
pub struct OKDHACluster {
|
||||
firewall: HostGroup,
|
||||
control_plane: HostGroup,
|
||||
workers: HostGroup,
|
||||
ceph_hosts: HostGroup,
|
||||
switch: HostGroup,
|
||||
}
|
||||
|
||||
pub struct IpAddress(IpAddr);
|
0
harmony-rs/src/infra/executors/boot/mod.rs
Normal file
0
harmony-rs/src/infra/executors/boot/mod.rs
Normal file
2
harmony-rs/src/infra/executors/mod.rs
Normal file
2
harmony-rs/src/infra/executors/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod russh;
|
||||
|
0
harmony-rs/src/infra/executors/power/mod.rs
Normal file
0
harmony-rs/src/infra/executors/power/mod.rs
Normal file
104
harmony-rs/src/infra/executors/russh/mod.rs
Normal file
104
harmony-rs/src/infra/executors/russh/mod.rs
Normal file
@ -0,0 +1,104 @@
|
||||
use std::{net::ToSocketAddrs, path::Path, sync::Arc, time::Duration};
|
||||
|
||||
use russh::{client, keys::{key, load_secret_key}, ChannelMsg, Disconnect};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
|
||||
use crate::domain::executors::SshClient;
|
||||
|
||||
pub struct RusshClient;
|
||||
|
||||
impl SshClient for RusshClient {
|
||||
fn test_connection(&self, username: String, password: String) -> Result<(), crate::domain::executors::ExecutorError> {
|
||||
todo!()
|
||||
//Session::connect();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct Client {}
|
||||
|
||||
// More SSH event handlers
|
||||
// can be defined in this trait
|
||||
// In this example, we're only using Channel, so these aren't needed.
|
||||
#[async_trait]
|
||||
impl client::Handler for Client {
|
||||
type Error = russh::Error;
|
||||
|
||||
async fn check_server_key(
|
||||
&mut self,
|
||||
_server_public_key: &key::PublicKey,
|
||||
) -> Result<bool, Self::Error> {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// This struct is a convenience wrapper
|
||||
/// around a russh client
|
||||
pub struct Session {
|
||||
session: client::Handle<Client>,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
async fn connect<P: AsRef<Path>, A: ToSocketAddrs>(
|
||||
key_path: P,
|
||||
user: impl Into<String>,
|
||||
addrs: A,
|
||||
) -> Result<Self, String> {
|
||||
let key_pair = load_secret_key(key_path, None)?;
|
||||
let config = client::Config {
|
||||
inactivity_timeout: Some(Duration::from_secs(5)),
|
||||
..<_>::default()
|
||||
};
|
||||
|
||||
let config = Arc::new(config);
|
||||
let sh = Client {};
|
||||
|
||||
let mut session = client::connect(config, addrs, sh).await?;
|
||||
let auth_res = session
|
||||
.authenticate_publickey(user, Arc::new(key_pair))
|
||||
.await?;
|
||||
|
||||
if !auth_res {
|
||||
Err("Authentication failed")
|
||||
}
|
||||
|
||||
Ok(Self { session })
|
||||
}
|
||||
|
||||
async fn call(&mut self, command: &str) -> Result<u32> {
|
||||
let mut channel = self.session.channel_open_session().await?;
|
||||
channel.exec(true, command).await?;
|
||||
|
||||
let mut code = None;
|
||||
let mut stdout = tokio::io::stdout();
|
||||
|
||||
loop {
|
||||
// There's an event available on the session channel
|
||||
let Some(msg) = channel.wait().await else {
|
||||
break;
|
||||
};
|
||||
match msg {
|
||||
// Write data to the terminal
|
||||
ChannelMsg::Data { ref data } => {
|
||||
stdout.write_all(data).await?;
|
||||
stdout.flush().await?;
|
||||
}
|
||||
// The command has returned an exit code
|
||||
ChannelMsg::ExitStatus { exit_status } => {
|
||||
code = Some(exit_status);
|
||||
// cannot leave the loop immediately, there might still be more data to receive
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(code.expect("program did not exit cleanly"))
|
||||
}
|
||||
|
||||
async fn close(&mut self) -> Result<()> {
|
||||
self.session
|
||||
.disconnect(Disconnect::ByApplication, "", "English")
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
21
harmony-rs/src/infra/inventory/fqm.rs
Normal file
21
harmony-rs/src/infra/inventory/fqm.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use crate::domain::{
|
||||
hardware::{Location, Host, HostCategory},
|
||||
inventory::Inventory,
|
||||
};
|
||||
|
||||
pub fn get_fqm_inventory() -> Inventory {
|
||||
Inventory {
|
||||
location: Location::new(
|
||||
"1134 Grande Allée Ouest 1er étage, Québec, Qc".into(),
|
||||
"FQM 1134 1er étage".into(),
|
||||
),
|
||||
host: vec![Host {
|
||||
category: HostCategory::Server,
|
||||
network: vec![],
|
||||
storage: vec![],
|
||||
labels: vec![],
|
||||
}],
|
||||
switch: vec![],
|
||||
firewall: vec![],
|
||||
}
|
||||
}
|
1
harmony-rs/src/infra/inventory/mod.rs
Normal file
1
harmony-rs/src/infra/inventory/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod fqm;
|
2
harmony-rs/src/infra/mod.rs
Normal file
2
harmony-rs/src/infra/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod inventory;
|
||||
pub mod executors;
|
3
harmony-rs/src/lib.rs
Normal file
3
harmony-rs/src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
||||
pub mod domain;
|
||||
pub mod infra;
|
||||
pub mod modules;
|
@ -1,20 +1,19 @@
|
||||
use libredfish::{Config, Redfish};
|
||||
use reqwest::blocking::Client;
|
||||
use harmony_rs::{
|
||||
domain::{
|
||||
inventory::{Inventory, InventoryFilter},
|
||||
maestro::Maestro,
|
||||
},
|
||||
infra::inventory::fqm::get_fqm_inventory, modules::opnsense_dhcp::OPNSenseDhcpScore,
|
||||
};
|
||||
|
||||
pub fn main() {
|
||||
let client = Client::builder().danger_accept_invalid_certs(true).build().expect("Failed to build reqwest client");
|
||||
let redfish = Redfish::new(
|
||||
client,
|
||||
Config {
|
||||
user: Some(String::from("Administrator")),
|
||||
endpoint: String::from("10.10.8.104/redfish/v1"),
|
||||
// password: Some(String::from("YOUR_PASSWORD")),
|
||||
password: Some(String::from("wrongpass")),
|
||||
port: None,
|
||||
},
|
||||
);
|
||||
env_logger::init();
|
||||
|
||||
let response = redfish.get_power_status().expect("Failed redfish request");
|
||||
|
||||
println!("Got power {:?}", response);
|
||||
let maestro = Maestro::new(get_inventory());
|
||||
let score = OPNSenseDhcpScore::new(InventoryFilter::new(vec![]));
|
||||
maestro.interpret(score);
|
||||
}
|
||||
|
||||
fn get_inventory() -> Inventory {
|
||||
get_fqm_inventory()
|
||||
}
|
||||
|
20
harmony-rs/src/main_redfish.rs
Normal file
20
harmony-rs/src/main_redfish.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use libredfish::{Config, Redfish};
|
||||
use reqwest::blocking::Client;
|
||||
|
||||
pub fn main() {
|
||||
let client = Client::builder().danger_accept_invalid_certs(true).build().expect("Failed to build reqwest client");
|
||||
let redfish = Redfish::new(
|
||||
client,
|
||||
Config {
|
||||
user: Some(String::from("Administrator")),
|
||||
endpoint: String::from("10.10.8.104/redfish/v1"),
|
||||
// password: Some(String::from("YOUR_PASSWORD")),
|
||||
password: Some(String::from("wrongpass")),
|
||||
port: None,
|
||||
},
|
||||
);
|
||||
|
||||
let response = redfish.get_power_status().expect("Failed redfish request");
|
||||
|
||||
println!("Got power {:?}", response);
|
||||
}
|
1
harmony-rs/src/modules/mod.rs
Normal file
1
harmony-rs/src/modules/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod opnsense_dhcp;
|
119
harmony-rs/src/modules/opnsense_dhcp.rs
Normal file
119
harmony-rs/src/modules/opnsense_dhcp.rs
Normal file
@ -0,0 +1,119 @@
|
||||
use derive_new::new;
|
||||
use log::info;
|
||||
|
||||
use crate::{domain::{
|
||||
data::{Id, Version}, executors::SshClient, hardware::NetworkInterface, interpret::{InterpretError, InterpretStatus, Outcome}, inventory::Inventory, topology::IpAddress
|
||||
}, infra::executors::russh::RusshClient};
|
||||
|
||||
use crate::domain::{
|
||||
interpret::Interpret, interpret::InterpretName, inventory::InventoryFilter,
|
||||
inventory::InventorySlice, score::Score,
|
||||
};
|
||||
|
||||
use crate::domain::executors::{ExecutorError, ExecutorResult};
|
||||
#[derive(Debug, new)]
|
||||
pub struct OPNSenseDhcpScore {
|
||||
inventory_filter: InventoryFilter,
|
||||
}
|
||||
|
||||
impl Score for OPNSenseDhcpScore {
|
||||
type InterpretType = OPNSenseDhcpInterpret;
|
||||
|
||||
fn get_inventory_filter(&self) -> InventorySlice {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn create_interpret(self) -> OPNSenseDhcpInterpret {
|
||||
OPNSenseDhcpInterpret::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// https://docs.opnsense.org/manual/dhcp.html#advanced-settings
|
||||
#[derive(Debug)]
|
||||
pub struct OPNSenseDhcpInterpret {
|
||||
score: OPNSenseDhcpScore,
|
||||
version: Version,
|
||||
id: Id,
|
||||
name: String,
|
||||
status: InterpretStatus,
|
||||
}
|
||||
|
||||
impl OPNSenseDhcpInterpret {
|
||||
pub fn new(score: OPNSenseDhcpScore) -> Self {
|
||||
let version = Version::from("1.0.0").expect("Version should be valid");
|
||||
let name = "OPNSenseDhcpScore".to_string();
|
||||
let id = Id::from_string(format!("{name}_{version}"));
|
||||
|
||||
Self {
|
||||
version,
|
||||
id,
|
||||
name,
|
||||
score,
|
||||
status: InterpretStatus::QUEUED,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Interpret for OPNSenseDhcpInterpret {
|
||||
fn get_name(&self) -> InterpretName {
|
||||
InterpretName::OPNSenseDHCP
|
||||
}
|
||||
|
||||
fn get_version(&self) -> crate::domain::data::Version {
|
||||
self.version.clone()
|
||||
}
|
||||
|
||||
fn get_status(&self) -> InterpretStatus {
|
||||
self.status.clone()
|
||||
}
|
||||
|
||||
fn get_children(&self) -> Vec<crate::domain::data::Id> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn execute(&self, _inventory: &Inventory) -> Result<Outcome, InterpretError> {
|
||||
info!("Executing {} on inventory {_inventory:?}", self.get_name());
|
||||
let ssh_client = RusshClient{};
|
||||
// ssh_client.test_connection("username", "password");
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait OPNSenseDhcpConfigEditor {
|
||||
fn add_static_host(
|
||||
&self,
|
||||
opnsense_host: IpAddress,
|
||||
credentials: OPNSenseCredentials,
|
||||
interface: NetworkInterface,
|
||||
address: IpAddress,
|
||||
) -> Result<ExecutorResult, ExecutorError>;
|
||||
}
|
||||
|
||||
pub struct OPNSenseCredentials {
|
||||
pub user: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
#[test]
|
||||
fn opnsense_dns_score_should_do_nothing_on_empty_inventory() {
|
||||
todo!();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opnsense_dns_score_should_set_entry_for_bootstrap_node() {
|
||||
todo!();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opnsense_dns_score_should_set_entry_for_control_plane_members() {
|
||||
todo!();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opnsense_dns_score_should_set_entry_for_workers() {
|
||||
todo!();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user