diff --git a/harmony-rs/Cargo.lock b/harmony-rs/Cargo.lock index 5d570c6..aa9664f 100644 --- a/harmony-rs/Cargo.lock +++ b/harmony-rs/Cargo.lock @@ -893,6 +893,8 @@ dependencies = [ "cidr", "derive-new", "env_logger", + "harmony_macros", + "harmony_types", "libredfish", "log", "opnsense-config", @@ -912,11 +914,15 @@ dependencies = [ name = "harmony_macros" version = "1.0.0" dependencies = [ - "harmony", + "harmony_types", "quote", "syn 2.0.90", ] +[[package]] +name = "harmony_types" +version = "1.0.0" + [[package]] name = "hashbrown" version = "0.14.5" diff --git a/harmony-rs/Cargo.toml b/harmony-rs/Cargo.toml index ff7b3a1..a7c37e2 100644 --- a/harmony-rs/Cargo.toml +++ b/harmony-rs/Cargo.toml @@ -4,7 +4,10 @@ members = [ "private_repos/*", "demo/*", "harmony", - "opnsense-config", "opnsense-config-xml", "harmony_macros", + "harmony_types", + "harmony_macros", + "opnsense-config", + "opnsense-config-xml", ] [workspace.package] diff --git a/harmony-rs/demo/vbox-opnsense/src/main.rs b/harmony-rs/demo/vbox-opnsense/src/main.rs index e8956f7..5084dd2 100644 --- a/harmony-rs/demo/vbox-opnsense/src/main.rs +++ b/harmony-rs/demo/vbox-opnsense/src/main.rs @@ -10,7 +10,9 @@ use harmony::{ inventory::Inventory, maestro::Maestro, modules::{ - http::HttpScore, okd::{dhcp::OKDBootstrapDhcpScore, dns::OKDBootstrapDnsScore}, tftp::TftpScore + http::HttpScore, + okd::{dhcp::OKDDhcpScore, dns::OKDDnsScore}, + tftp::TftpScore, }, topology::{LogicalHost, UnmanagedRouter, Url}, }; @@ -48,6 +50,10 @@ async fn main() { ip: ip!("10.100.8.20"), name: "cp0".to_string(), }], + bootstrap_host: LogicalHost { + ip: ip!("10.100.8.20"), + name: "cp0".to_string(), + }, workers: vec![], switch: vec![], }; @@ -73,8 +79,8 @@ async fn main() { // TODO regroup smaller scores in a larger one such as this // let okd_boostrap_preparation(); - // let dhcp_score = OKDBootstrapDhcpScore::new(&topology, &inventory); - // let dns_score = OKDBootstrapDnsScore::new(&topology); + // let dhcp_score = OKDDhcpScore::new(&topology, &inventory); + // let dns_score = OKDDnsScore::new(&topology); // let load_balancer_score = // harmony::modules::okd::load_balancer::OKDLoadBalancerScore::new(&topology); diff --git a/harmony-rs/harmony/Cargo.toml b/harmony-rs/harmony/Cargo.toml index 4a59c4b..f4c79f5 100644 --- a/harmony-rs/harmony/Cargo.toml +++ b/harmony-rs/harmony/Cargo.toml @@ -19,5 +19,7 @@ async-trait = { workspace = true } cidr = { workspace = true } opnsense-config = { path = "../opnsense-config" } opnsense-config-xml = { path = "../opnsense-config-xml" } +harmony_macros = { path = "../harmony_macros" } +harmony_types = { path = "../harmony_types" } uuid = { workspace = true } url = { workspace = true } diff --git a/harmony-rs/harmony/src/domain/hardware/mod.rs b/harmony-rs/harmony/src/domain/hardware/mod.rs index 54b03a5..a1d5614 100644 --- a/harmony-rs/harmony/src/domain/hardware/mod.rs +++ b/harmony-rs/harmony/src/domain/hardware/mod.rs @@ -1,8 +1,8 @@ use std::sync::Arc; use derive_new::new; +use harmony_types::net::MacAddress; -use crate::topology::MacAddress; pub type HostGroup = Vec; pub type SwitchGroup = Vec; diff --git a/harmony-rs/harmony/src/domain/topology/mod.rs b/harmony-rs/harmony/src/domain/topology/mod.rs index 9d2fc8a..9f7dd71 100644 --- a/harmony-rs/harmony/src/domain/topology/mod.rs +++ b/harmony-rs/harmony/src/domain/topology/mod.rs @@ -1,15 +1,15 @@ mod host_binding; +mod http; mod load_balancer; mod router; mod tftp; -mod http; pub use load_balancer::*; pub use router::*; mod network; pub use host_binding::*; +pub use http::*; pub use network::*; pub use tftp::*; -pub use http::*; use std::{net::IpAddr, sync::Arc}; @@ -23,6 +23,7 @@ pub struct HAClusterTopology { pub tftp_server: Arc, pub http_server: Arc, pub dns_server: Arc, + pub bootstrap_host: LogicalHost, pub control_plane: Vec, pub workers: Vec, pub switch: Vec, diff --git a/harmony-rs/harmony/src/domain/topology/network.rs b/harmony-rs/harmony/src/domain/topology/network.rs index eaedfee..c05142a 100644 --- a/harmony-rs/harmony/src/domain/topology/network.rs +++ b/harmony-rs/harmony/src/domain/topology/network.rs @@ -1,6 +1,7 @@ use std::{error::Error, net::Ipv4Addr, str::FromStr}; use async_trait::async_trait; +use harmony_types::net::MacAddress; use crate::executors::ExecutorError; @@ -117,35 +118,6 @@ pub enum Action { Deny, } -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct MacAddress(pub [u8; 6]); - - -impl MacAddress { - #[cfg(test)] - pub fn dummy() -> Self { - Self([0, 0, 0, 0, 0, 0]) - } -} - -impl From<&MacAddress> for String { - fn from(value: &MacAddress) -> Self { - format!( - "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", - value.0[0], value.0[1], value.0[2], value.0[3], value.0[4], value.0[5] - ) - } -} - -impl std::fmt::Display for MacAddress { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!( - "MacAddress {}", - String::from(self) - )) - } -} - #[derive(Clone, Debug, PartialEq, Eq)] pub enum DnsRecordType { A, diff --git a/harmony-rs/harmony/src/infra/hp_ilo/mod.rs b/harmony-rs/harmony/src/infra/hp_ilo/mod.rs index 5bc73b8..b7fc1a2 100644 --- a/harmony-rs/harmony/src/infra/hp_ilo/mod.rs +++ b/harmony-rs/harmony/src/infra/hp_ilo/mod.rs @@ -1,9 +1,12 @@ -use crate::{hardware::ManagementInterface, topology::{IpAddress, MacAddress}}; +use crate::hardware::ManagementInterface; +use crate::topology::IpAddress; use derive_new::new; +use harmony_types::net::MacAddress; #[derive(new)] pub struct HPIlo { - ip_address: IpAddress, + ip_address: Option, + mac_address: Option, } impl ManagementInterface for HPIlo { diff --git a/harmony-rs/harmony/src/infra/intel_amt/mod.rs b/harmony-rs/harmony/src/infra/intel_amt/mod.rs index fc537f0..62815c5 100644 --- a/harmony-rs/harmony/src/infra/intel_amt/mod.rs +++ b/harmony-rs/harmony/src/infra/intel_amt/mod.rs @@ -1,8 +1,6 @@ -use crate::{ - hardware::ManagementInterface, - topology::{IpAddress, MacAddress}, -}; +use crate::hardware::ManagementInterface; use derive_new::new; +use harmony_types::net::MacAddress; #[derive(new)] pub struct IntelAmtManagement { diff --git a/harmony-rs/harmony/src/infra/opnsense/dhcp.rs b/harmony-rs/harmony/src/infra/opnsense/dhcp.rs index d19ce4e..d98516d 100644 --- a/harmony-rs/harmony/src/infra/opnsense/dhcp.rs +++ b/harmony-rs/harmony/src/infra/opnsense/dhcp.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use harmony_types::net::MacAddress; use log::debug; use crate::{executors::ExecutorError, topology::{DHCPStaticEntry, DhcpServer, IpAddress, LogicalHost}}; @@ -28,12 +29,12 @@ impl DhcpServer for OPNSenseFirewall { async fn remove_static_mapping( &self, - _mac: &crate::topology::MacAddress, + _mac: &MacAddress, ) -> Result<(), ExecutorError> { todo!() } - async fn list_static_mappings(&self) -> Vec<(crate::topology::MacAddress, IpAddress)> { + async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)> { todo!() } diff --git a/harmony-rs/harmony/src/infra/opnsense/management.rs b/harmony-rs/harmony/src/infra/opnsense/management.rs index 2c2ea70..27408c9 100644 --- a/harmony-rs/harmony/src/infra/opnsense/management.rs +++ b/harmony-rs/harmony/src/infra/opnsense/management.rs @@ -1,6 +1,5 @@ use derive_new::new; - -use crate::{hardware::ManagementInterface, topology::MacAddress}; +use crate::hardware::ManagementInterface; #[derive(new)] pub struct OPNSenseManagementInterface {} diff --git a/harmony-rs/harmony/src/modules/okd/bootstrap_dhcp.rs b/harmony-rs/harmony/src/modules/okd/bootstrap_dhcp.rs new file mode 100644 index 0000000..abd96fe --- /dev/null +++ b/harmony-rs/harmony/src/modules/okd/bootstrap_dhcp.rs @@ -0,0 +1,55 @@ +use crate::{ + inventory::Inventory, + modules::dhcp::DhcpScore, + score::Score, + topology::{HAClusterTopology, HostBinding, LogicalHost}, +}; + +use harmony_macros::ip; +#[derive(Debug)] +pub struct OKDBootstrapDhcpScore { + dhcp_score: DhcpScore, +} + +impl OKDBootstrapDhcpScore { + pub fn new(topology: &HAClusterTopology, inventory: &Inventory) -> Self { + let mut host_binding: Vec<_> = topology + .control_plane + .iter() + .enumerate() + .map(|(index, topology_entry)| HostBinding { + logical_host: topology_entry.clone(), + physical_host: inventory + .control_plane_host + .get(index) + .expect("Iventory should contain at least as many physical hosts as topology") + .clone(), + }) + .collect(); + host_binding.push(HostBinding { + logical_host: topology.bootstrap_host.clone(), + physical_host: inventory + .worker_host + .get(0) + .expect("Should have at least one worker to be used as bootstrap node") + .clone(), + }); + Self { + dhcp_score: DhcpScore::new( + host_binding, + // TODO : we should add a tftp server to the topology instead of relying on the + // router address, this is leaking implementation details + Some(topology.router.get_gateway()), + Some("bootx64.efi".to_string()), + ), + } + } +} + +impl Score for OKDBootstrapDhcpScore { + type InterpretType = ::InterpretType; + + fn create_interpret(self) -> Self::InterpretType { + self.dhcp_score.create_interpret() + } +} diff --git a/harmony-rs/harmony/src/modules/okd/bootstrap_load_balancer.rs b/harmony-rs/harmony/src/modules/okd/bootstrap_load_balancer.rs new file mode 100644 index 0000000..ca0e063 --- /dev/null +++ b/harmony-rs/harmony/src/modules/okd/bootstrap_load_balancer.rs @@ -0,0 +1,76 @@ +use std::net::SocketAddr; + +use crate::{ + modules::load_balancer::LoadBalancerScore, + score::Score, + topology::{ + BackendServer, HAClusterTopology, HealthCheck, HttpMethod, HttpStatusCode, + LoadBalancerService, + }, +}; + +#[derive(Debug)] +pub struct OKDBootstrapLoadBalancerScore { + load_balancer_score: LoadBalancerScore, +} + +impl OKDBootstrapLoadBalancerScore { + pub fn new(topology: &HAClusterTopology) -> Self { + let private_ip = topology.router.get_gateway(); + + let bootstrap_host = &topology.bootstrap_host; + + let private_services = vec![ + LoadBalancerService { + backend_servers: vec![BackendServer { + address: bootstrap_host.ip.to_string(), + port: 80, + }], + listening_port: SocketAddr::new(private_ip, 80), + health_check: Some(HealthCheck::TCP(None)), + }, + LoadBalancerService { + backend_servers: vec![BackendServer { + address: bootstrap_host.ip.to_string(), + port: 443, + }], + listening_port: SocketAddr::new(private_ip, 443), + health_check: Some(HealthCheck::TCP(None)), + }, + LoadBalancerService { + backend_servers: vec![BackendServer { + address: bootstrap_host.ip.to_string(), + port: 22623, + }], + listening_port: SocketAddr::new(private_ip, 22623), + health_check: Some(HealthCheck::TCP(None)), + }, + LoadBalancerService { + backend_servers: vec![BackendServer { + address: bootstrap_host.ip.to_string(), + port: 6443, + }], + listening_port: SocketAddr::new(private_ip, 6443), + health_check: Some(HealthCheck::HTTP( + "/readyz".to_string(), + HttpMethod::GET, + HttpStatusCode::Success2xx, + )), + }, + ]; + Self { + load_balancer_score: LoadBalancerScore { + public_services: vec![], + private_services, + }, + } + } +} + +impl Score for OKDBootstrapLoadBalancerScore { + type InterpretType = ::InterpretType; + + fn create_interpret(self) -> Self::InterpretType { + self.load_balancer_score.create_interpret() + } +} diff --git a/harmony-rs/harmony/src/modules/okd/dhcp.rs b/harmony-rs/harmony/src/modules/okd/dhcp.rs index b09e9a3..c837c02 100644 --- a/harmony-rs/harmony/src/modules/okd/dhcp.rs +++ b/harmony-rs/harmony/src/modules/okd/dhcp.rs @@ -6,11 +6,11 @@ use crate::{ }; #[derive(Debug)] -pub struct OKDBootstrapDhcpScore { +pub struct OKDDhcpScore { dhcp_score: DhcpScore, } -impl OKDBootstrapDhcpScore { +impl OKDDhcpScore { pub fn new(topology: &HAClusterTopology, inventory: &Inventory) -> Self { let host_binding = topology .control_plane @@ -37,7 +37,7 @@ impl OKDBootstrapDhcpScore { } } -impl Score for OKDBootstrapDhcpScore { +impl Score for OKDDhcpScore { type InterpretType = ::InterpretType; fn create_interpret(self) -> Self::InterpretType { diff --git a/harmony-rs/harmony/src/modules/okd/dns.rs b/harmony-rs/harmony/src/modules/okd/dns.rs index 38d6c1e..b2e6978 100644 --- a/harmony-rs/harmony/src/modules/okd/dns.rs +++ b/harmony-rs/harmony/src/modules/okd/dns.rs @@ -5,11 +5,11 @@ use crate::{ }; #[derive(Debug)] -pub struct OKDBootstrapDnsScore { +pub struct OKDDnsScore { dns_score: DnsScore, } -impl OKDBootstrapDnsScore { +impl OKDDnsScore { pub fn new(topology: &HAClusterTopology) -> Self { let cluster_domain_name = &topology.domain_name; let dns_entries = vec![ @@ -39,7 +39,7 @@ impl OKDBootstrapDnsScore { } } -impl Score for OKDBootstrapDnsScore { +impl Score for OKDDnsScore { type InterpretType = ::InterpretType; fn create_interpret(self) -> Self::InterpretType { diff --git a/harmony-rs/harmony/src/modules/okd/mod.rs b/harmony-rs/harmony/src/modules/okd/mod.rs index eef476a..0f34a0e 100644 --- a/harmony-rs/harmony/src/modules/okd/mod.rs +++ b/harmony-rs/harmony/src/modules/okd/mod.rs @@ -1,4 +1,6 @@ pub mod dhcp; pub mod dns; pub mod load_balancer; +pub mod bootstrap_load_balancer; +pub mod bootstrap_dhcp; diff --git a/harmony-rs/harmony_macros/Cargo.toml b/harmony-rs/harmony_macros/Cargo.toml index e43389c..7865014 100644 --- a/harmony-rs/harmony_macros/Cargo.toml +++ b/harmony-rs/harmony_macros/Cargo.toml @@ -7,6 +7,6 @@ version = "1.0.0" proc-macro = true [dependencies] -harmony = { version = "0.1.0", path = "../harmony" } +harmony_types = { path = "../harmony_types" } quote = "1.0.37" syn = "2.0.90" diff --git a/harmony-rs/harmony_macros/src/lib.rs b/harmony-rs/harmony_macros/src/lib.rs index 69d7149..d73e12d 100644 --- a/harmony-rs/harmony_macros/src/lib.rs +++ b/harmony-rs/harmony_macros/src/lib.rs @@ -1,6 +1,5 @@ extern crate proc_macro; -use harmony::topology::MacAddress; use proc_macro::TokenStream; use quote::quote; use syn::{LitStr, parse_macro_input}; diff --git a/harmony-rs/harmony_types/Cargo.toml b/harmony-rs/harmony_types/Cargo.toml new file mode 100644 index 0000000..1ffd417 --- /dev/null +++ b/harmony-rs/harmony_types/Cargo.toml @@ -0,0 +1,4 @@ +[package] +name = "harmony_types" +edition = "2024" +version = "1.0.0" diff --git a/harmony-rs/harmony_types/src/lib.rs b/harmony-rs/harmony_types/src/lib.rs new file mode 100644 index 0000000..05048c7 --- /dev/null +++ b/harmony-rs/harmony_types/src/lib.rs @@ -0,0 +1,26 @@ +pub mod net { + #[derive(Clone, Debug, PartialEq, Eq, Hash)] + pub struct MacAddress(pub [u8; 6]); + + impl MacAddress { + #[cfg(test)] + pub fn dummy() -> Self { + Self([0, 0, 0, 0, 0, 0]) + } + } + + impl From<&MacAddress> for String { + fn from(value: &MacAddress) -> Self { + format!( + "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", + value.0[0], value.0[1], value.0[2], value.0[3], value.0[4], value.0[5] + ) + } + } + + impl std::fmt::Display for MacAddress { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("MacAddress {}", String::from(self))) + } + } +}