From 3592b176e5f59136869c9ffc98e67fac61dd1c25 Mon Sep 17 00:00:00 2001 From: jeangab Date: Tue, 3 Dec 2024 10:43:50 -0500 Subject: [PATCH 1/5] chore: Some cleanup in harmony repo --- harmony-rs/harmony/src/infra/opnsense/mod.rs | 2 +- harmony-rs/harmony/src/lib.rs | 5 ++ harmony-rs/harmony/src/modules/dhcp.rs | 75 ++++++++++---------- 3 files changed, 44 insertions(+), 38 deletions(-) diff --git a/harmony-rs/harmony/src/infra/opnsense/mod.rs b/harmony-rs/harmony/src/infra/opnsense/mod.rs index 66b00e6..deca92a 100644 --- a/harmony-rs/harmony/src/infra/opnsense/mod.rs +++ b/harmony-rs/harmony/src/infra/opnsense/mod.rs @@ -1,5 +1,5 @@ mod management; -use std::sync::{Arc, Mutex, RwLock, RwLockWriteGuard}; +use std::sync::{Arc, RwLock }; use async_trait::async_trait; use log::debug; diff --git a/harmony-rs/harmony/src/lib.rs b/harmony-rs/harmony/src/lib.rs index be55110..3c2dff0 100644 --- a/harmony-rs/harmony/src/lib.rs +++ b/harmony-rs/harmony/src/lib.rs @@ -2,3 +2,8 @@ mod domain; pub use domain::*; pub mod infra; pub mod modules; + +#[cfg(test)] +mod test { + use crate::infra::opnsense::OPNSenseFirewall; +} diff --git a/harmony-rs/harmony/src/modules/dhcp.rs b/harmony-rs/harmony/src/modules/dhcp.rs index 37e49cb..c569354 100644 --- a/harmony-rs/harmony/src/modules/dhcp.rs +++ b/harmony-rs/harmony/src/modules/dhcp.rs @@ -16,42 +16,43 @@ use crate::{ use crate::domain::score::Score; -/// OPNSenseDhcpScore will set static DHCP entries using index based hostname -/// and ip addresses. -/// -/// For example : -/// ```rust -/// -/// let node1 = todo!(); // Node pointing to clustermember controlplane0 with ip 10.10.0.20 and host with mac 01 -/// let node2 = todo!(); // Node pointing to clustermember controlplane1 with ip 10.10.0.21 and host with mac 02 -/// let node3 = todo!(); // Node pointing to clustermember controlplane2 with ip 10.10.0.22 and host with mac 03 -/// -/// let score = OPNSenseDhcpScore { -/// nodes: vec![node1, node2, node3], -/// } -/// ``` -/// -/// Running such a score would create these static entries : -/// -/// ```rust -/// let entries = vec![ -/// DHCPEntry { -/// mac: 01, -/// ip: 10.10.0.20, -/// hostname: "controlplane0" -/// } -/// DHCPEntry { -/// mac: 02, -/// ip: 10.10.0.21, -/// hostname: "controlplane0" -/// } -/// DHCPEntry { -/// mac: 03, -/// ip: 10.10.0.22, -/// hostname: "controlplane2" -/// } -/// ] -/// ``` +/** +OPNSenseDhcpScore will set static DHCP entries using index based hostname +and ip addresses. + +For example : +```rust +let node1 = todo!(); // Node pointing to clustermember controlplane0 with ip 10.10.0.20 and host with mac 01 +let node2 = todo!(); // Node pointing to clustermember controlplane1 with ip 10.10.0.21 and host with mac 02 +let node3 = todo!(); // Node pointing to clustermember controlplane2 with ip 10.10.0.22 and host with mac 03 + +let score = OPNSenseDhcpScore { + nodes: vec![node1, node2, node3], +} +``` + +Running such a score would create these static entries : + +```rust +let entries = vec![ + DHCPEntry { + mac: 01, + ip: 10.10.0.20, + hostname: "controlplane0" + } + DHCPEntry { + mac: 02, + ip: 10.10.0.21, + hostname: "controlplane0" + } + DHCPEntry { + mac: 03, + ip: 10.10.0.22, + hostname: "controlplane2" + } +] +``` +*/ #[derive(Debug, new, Clone)] pub struct DhcpScore { host_binding: Vec, @@ -78,7 +79,7 @@ pub struct DhcpInterpret { impl DhcpInterpret { pub fn new(score: DhcpScore) -> Self { let version = Version::from("1.0.0").expect("Version should be valid"); - let name = "OPNSenseDhcpScore".to_string(); + let name = "DhcpInterpret".to_string(); let id = Id::from_string(format!("{name}_{version}")); Self { From 1e0c2eb4700875bd516a4ca51afbb3ae0bb62b91 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Mon, 9 Dec 2024 15:23:12 -0500 Subject: [PATCH 2/5] feat: Added new crate harmony_macros with ip! macro to facilitate creating ip addresses --- harmony-rs/Cargo.lock | 50 +++++++++---- harmony-rs/Cargo.toml | 2 +- harmony-rs/harmony/src/domain/topology/mod.rs | 74 ++++++++++++++++++- harmony-rs/harmony/src/modules/dhcp.rs | 39 +--------- harmony-rs/harmony_macros/Cargo.lock | 46 ++++++++++++ harmony-rs/harmony_macros/Cargo.toml | 11 +++ harmony-rs/harmony_macros/src/lib.rs | 23 ++++++ 7 files changed, 189 insertions(+), 56 deletions(-) create mode 100644 harmony-rs/harmony_macros/Cargo.lock create mode 100644 harmony-rs/harmony_macros/Cargo.toml create mode 100644 harmony-rs/harmony_macros/src/lib.rs diff --git a/harmony-rs/Cargo.lock b/harmony-rs/Cargo.lock index 22e902d..2c2c29b 100644 --- a/harmony-rs/Cargo.lock +++ b/harmony-rs/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -152,7 +152,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -471,7 +471,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -499,7 +499,7 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -784,7 +784,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -905,6 +905,14 @@ dependencies = [ "tokio", ] +[[package]] +name = "harmony_macros" +version = "1.0.0" +dependencies = [ + "quote", + "syn 2.0.90", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1346,7 +1354,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -1620,9 +1628,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -2062,7 +2070,7 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2247,9 +2255,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -2319,7 +2327,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2371,7 +2379,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] @@ -2542,7 +2550,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -2576,7 +2584,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2786,6 +2794,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wk" +version = "0.1.0" +dependencies = [ + "cidr", + "harmony", + "harmony_macros", + "tokio", +] + [[package]] name = "wyz" version = "0.5.1" @@ -2845,7 +2863,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.90", ] [[package]] diff --git a/harmony-rs/Cargo.toml b/harmony-rs/Cargo.toml index 20aba71..d965ab6 100644 --- a/harmony-rs/Cargo.toml +++ b/harmony-rs/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = [ "private_repos/*", "harmony", - "opnsense-config", "opnsense-config-xml", + "opnsense-config", "opnsense-config-xml", "harmony_macros", ] [workspace.package] diff --git a/harmony-rs/harmony/src/domain/topology/mod.rs b/harmony-rs/harmony/src/domain/topology/mod.rs index bfb9c96..ccad53b 100644 --- a/harmony-rs/harmony/src/domain/topology/mod.rs +++ b/harmony-rs/harmony/src/domain/topology/mod.rs @@ -34,7 +34,79 @@ pub type IpAddress = IpAddr; /// This abstraction focuses on the logical role and services, independent of the physical hardware. #[derive(Debug, Clone)] pub struct LogicalHost { - /// The set of services this logical host provides + /// The IP address of this logical host. pub ip: IpAddress, + /// The name of this logical host. pub name: String, } + +impl LogicalHost { + /// Creates a list of `LogicalHost` instances. + /// + /// # Arguments + /// + /// * `number_hosts` - The number of logical hosts to create. + /// * `start_ip` - The starting IP address. Each subsequent host's IP will be incremented. + /// * `hostname_prefix` - The prefix for the host names. Host names will be in the form `prefix`. + /// + /// # Returns + /// + /// A `Vec` containing the specified number of logical hosts, each with a unique IP and name. + /// + /// # Panics + /// + /// This function will panic if adding `number_hosts` to `start_ip` exceeds the valid range of IP addresses. + /// + /// # Examples + /// + /// ``` + /// use std::str::FromStr; + /// use harmony::topology::{IpAddress, LogicalHost}; + /// + /// let start_ip = IpAddress::from_str("192.168.0.20").unwrap(); + /// let hosts = LogicalHost::create_hosts(3, start_ip, "worker"); + /// + /// assert_eq!(hosts.len(), 3); + /// assert_eq!(hosts[0].ip, IpAddress::from_str("192.168.0.20").unwrap()); + /// assert_eq!(hosts[0].name, "worker0"); + /// assert_eq!(hosts[1].ip, IpAddress::from_str("192.168.0.21").unwrap()); + /// assert_eq!(hosts[1].name, "worker1"); + /// assert_eq!(hosts[2].ip, IpAddress::from_str("192.168.0.22").unwrap()); + /// assert_eq!(hosts[2].name, "worker2"); + /// ``` + pub fn create_hosts(number_hosts: u32, start_ip: IpAddress, hostname_prefix: &str) -> Vec { + let mut hosts = Vec::with_capacity(number_hosts.try_into().unwrap()); + for i in 0..number_hosts { + let new_ip = increment_ip(start_ip, i).expect("IP address overflow"); + let name = format!("{}{}", hostname_prefix, i); + hosts.push(LogicalHost { ip: new_ip, name }); + } + hosts + } +} + +/// Increments an IP address by a given value. +/// +/// # Arguments +/// +/// * `ip` - The starting IP address. +/// * `increment` - The amount to add to the IP address. +/// +/// # Returns +/// +/// A new `IpAddress` that is the result of incrementing the original by `increment`. +/// +/// # Panics +/// +/// This function panics if the resulting IP address exceeds the valid range. +fn increment_ip(ip: IpAddress, increment: u32) -> Option { + match ip { + IpAddress::V4(ipv4) => { + let new_ip = u32::from(ipv4) + increment; + Some(IpAddress::V4(new_ip.into())) + } + IpAddress::V6(_) => { + todo!("Ipv6 not supported yet") + } + } +} diff --git a/harmony-rs/harmony/src/modules/dhcp.rs b/harmony-rs/harmony/src/modules/dhcp.rs index c569354..fb3470e 100644 --- a/harmony-rs/harmony/src/modules/dhcp.rs +++ b/harmony-rs/harmony/src/modules/dhcp.rs @@ -1,4 +1,4 @@ -use std::{net::Ipv4Addr, sync::Arc}; +use std::sync::Arc; use async_trait::async_trait; use derive_new::new; @@ -16,43 +16,6 @@ use crate::{ use crate::domain::score::Score; -/** -OPNSenseDhcpScore will set static DHCP entries using index based hostname -and ip addresses. - -For example : -```rust -let node1 = todo!(); // Node pointing to clustermember controlplane0 with ip 10.10.0.20 and host with mac 01 -let node2 = todo!(); // Node pointing to clustermember controlplane1 with ip 10.10.0.21 and host with mac 02 -let node3 = todo!(); // Node pointing to clustermember controlplane2 with ip 10.10.0.22 and host with mac 03 - -let score = OPNSenseDhcpScore { - nodes: vec![node1, node2, node3], -} -``` - -Running such a score would create these static entries : - -```rust -let entries = vec![ - DHCPEntry { - mac: 01, - ip: 10.10.0.20, - hostname: "controlplane0" - } - DHCPEntry { - mac: 02, - ip: 10.10.0.21, - hostname: "controlplane0" - } - DHCPEntry { - mac: 03, - ip: 10.10.0.22, - hostname: "controlplane2" - } -] -``` -*/ #[derive(Debug, new, Clone)] pub struct DhcpScore { host_binding: Vec, diff --git a/harmony-rs/harmony_macros/Cargo.lock b/harmony-rs/harmony_macros/Cargo.lock new file mode 100644 index 0000000..7070e6a --- /dev/null +++ b/harmony-rs/harmony_macros/Cargo.lock @@ -0,0 +1,46 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "harmony_macros" +version = "1.0.0" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" diff --git a/harmony-rs/harmony_macros/Cargo.toml b/harmony-rs/harmony_macros/Cargo.toml new file mode 100644 index 0000000..8dbebeb --- /dev/null +++ b/harmony-rs/harmony_macros/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "harmony_macros" +edition = "2024" +version = "1.0.0" + +[lib] +proc-macro = true + +[dependencies] +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 new file mode 100644 index 0000000..3ea6a40 --- /dev/null +++ b/harmony-rs/harmony_macros/src/lib.rs @@ -0,0 +1,23 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, LitStr}; + +#[proc_macro] +pub fn ip(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as LitStr); + let ip_str = input.value(); + + if let Ok(_) = ip_str.parse::() { + let expanded = quote! { std::net::IpAddr::V4(#ip_str.parse::().unwrap()) }; + return TokenStream::from(expanded); + } + + if let Ok(_) = ip_str.parse::() { + let expanded = quote! { std::net::IpAddr::V4(#ip_str.parse::().unwrap()) }; + return TokenStream::from(expanded); + } + + panic!("Invalid IP address: {}", ip_str); +} From d0d81af796c9bcd778618acfa4f2000060f0d509 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Tue, 17 Dec 2024 09:54:23 -0500 Subject: [PATCH 3/5] feat(macro): Add mac_address macro --- harmony-rs/Cargo.lock | 1 + .../harmony/src/infra/opnsense/management.rs | 6 +-- harmony-rs/harmony_macros/Cargo.toml | 1 + harmony-rs/harmony_macros/src/lib.rs | 49 +++++++++++++++++-- 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/harmony-rs/Cargo.lock b/harmony-rs/Cargo.lock index 2c2c29b..dc80e38 100644 --- a/harmony-rs/Cargo.lock +++ b/harmony-rs/Cargo.lock @@ -909,6 +909,7 @@ dependencies = [ name = "harmony_macros" version = "1.0.0" dependencies = [ + "harmony", "quote", "syn 2.0.90", ] diff --git a/harmony-rs/harmony/src/infra/opnsense/management.rs b/harmony-rs/harmony/src/infra/opnsense/management.rs index ce0c6b9..81f062f 100644 --- a/harmony-rs/harmony/src/infra/opnsense/management.rs +++ b/harmony-rs/harmony/src/infra/opnsense/management.rs @@ -3,9 +3,7 @@ use derive_new::new; use crate::{hardware::ManagementInterface, topology::MacAddress}; #[derive(new)] -pub struct OPNSenseManagementInterface { - mac: MacAddress, -} +pub struct OPNSenseManagementInterface {} impl ManagementInterface for OPNSenseManagementInterface { fn boot_to_pxe(&self) { @@ -13,7 +11,7 @@ impl ManagementInterface for OPNSenseManagementInterface { } fn get_mac_address(&self) -> MacAddress { - self.mac.clone() + todo!("OPNSense can have multiple mac addresses using SSH. I'm not sure it even belongs in the ManagementInterface trait") } fn get_supported_protocol_names(&self) -> String { diff --git a/harmony-rs/harmony_macros/Cargo.toml b/harmony-rs/harmony_macros/Cargo.toml index 8dbebeb..e43389c 100644 --- a/harmony-rs/harmony_macros/Cargo.toml +++ b/harmony-rs/harmony_macros/Cargo.toml @@ -7,5 +7,6 @@ version = "1.0.0" proc-macro = true [dependencies] +harmony = { version = "0.1.0", path = "../harmony" } 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 3ea6a40..6a86640 100644 --- a/harmony-rs/harmony_macros/src/lib.rs +++ b/harmony-rs/harmony_macros/src/lib.rs @@ -1,8 +1,9 @@ extern crate proc_macro; +use harmony::topology::MacAddress; use proc_macro::TokenStream; use quote::quote; -use syn::{parse_macro_input, LitStr}; +use syn::{LitStr, parse_macro_input}; #[proc_macro] pub fn ip(input: TokenStream) -> TokenStream { @@ -10,14 +11,56 @@ pub fn ip(input: TokenStream) -> TokenStream { let ip_str = input.value(); if let Ok(_) = ip_str.parse::() { - let expanded = quote! { std::net::IpAddr::V4(#ip_str.parse::().unwrap()) }; + let expanded = + quote! { std::net::IpAddr::V4(#ip_str.parse::().unwrap()) }; return TokenStream::from(expanded); } if let Ok(_) = ip_str.parse::() { - let expanded = quote! { std::net::IpAddr::V4(#ip_str.parse::().unwrap()) }; + let expanded = + quote! { std::net::IpAddr::V4(#ip_str.parse::().unwrap()) }; return TokenStream::from(expanded); } panic!("Invalid IP address: {}", ip_str); } + +#[proc_macro] +pub fn mac_address(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as LitStr); + let mac_str = input.value(); + + match parse_mac_address(&mac_str) { + Ok(bytes) => { + let b0 = bytes[0]; + let b1 = bytes[1]; + let b2 = bytes[2]; + let b3 = bytes[3]; + let b4 = bytes[4]; + let b5 = bytes[5]; + + quote! { + MacAddress( [#b0, #b1, #b2, #b3, #b4, #b5] ) + } + .into() + } + Err(err) => syn::Error::new(input.span(), err).to_compile_error().into(), + } +} + +fn parse_mac_address(mac: &str) -> Result<[u8; 6], String> { + let parts: Vec<&str> = mac.split(':').collect(); + if parts.len() != 6 { + return Err("MAC address must contain exactly six octets separated by colons".to_string()); + } + + let mut bytes = [0u8; 6]; + for (i, part) in parts.iter().enumerate() { + match u8::from_str_radix(part, 16) { + Ok(byte) => bytes[i] = byte, + Err(_) => return Err(format!("Invalid MAC address octet: {}", part)), + } + } + + Ok(bytes) +} From b15df3c93f6b92bdb35b8ca3cfded1cc4bfbd63a Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Tue, 17 Dec 2024 15:15:41 -0500 Subject: [PATCH 4/5] feat: OKD Dhcp config works and is actually savec on opnsense firewall, its alive!! First real run on wk cluster --- harmony-rs/harmony/src/domain/hardware/mod.rs | 8 +-- .../harmony/src/domain/topology/network.rs | 1 + harmony-rs/harmony/src/infra/opnsense/mod.rs | 17 ++++- harmony-rs/harmony/src/modules/dhcp.rs | 17 +++-- harmony-rs/harmony/src/modules/mod.rs | 1 + harmony-rs/harmony/src/modules/okd/dhcp.rs | 45 +++++++++++++ harmony-rs/harmony/src/modules/okd/mod.rs | 2 + .../opnsense-config-xml/src/data/opnsense.rs | 65 ++++++++++++------- .../opnsense-config/src/config/manager/ssh.rs | 5 +- .../opnsense-config/src/modules/dhcp.rs | 12 ++-- 10 files changed, 132 insertions(+), 41 deletions(-) create mode 100644 harmony-rs/harmony/src/modules/okd/dhcp.rs create mode 100644 harmony-rs/harmony/src/modules/okd/mod.rs diff --git a/harmony-rs/harmony/src/domain/hardware/mod.rs b/harmony-rs/harmony/src/domain/hardware/mod.rs index 8ec4f81..2b2c02a 100644 --- a/harmony-rs/harmony/src/domain/hardware/mod.rs +++ b/harmony-rs/harmony/src/domain/hardware/mod.rs @@ -96,10 +96,10 @@ pub enum StorageKind { } #[derive(Debug, new, Clone)] pub struct Storage { - connection: StorageConnectionType, - kind: StorageKind, - size: u64, - serial: String, + pub connection: StorageConnectionType, + pub kind: StorageKind, + pub size: u64, + pub serial: String, } #[derive(Debug, Clone)] diff --git a/harmony-rs/harmony/src/domain/topology/network.rs b/harmony-rs/harmony/src/domain/topology/network.rs index be55f37..536370c 100644 --- a/harmony-rs/harmony/src/domain/topology/network.rs +++ b/harmony-rs/harmony/src/domain/topology/network.rs @@ -47,6 +47,7 @@ pub trait DhcpServer: Send + Sync { async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>; fn get_ip(&self) -> IpAddress; fn get_host(&self) -> LogicalHost; + async fn commit_config(&self) -> Result<(), ExecutorError>; } impl std::fmt::Debug for dyn DhcpServer { diff --git a/harmony-rs/harmony/src/infra/opnsense/mod.rs b/harmony-rs/harmony/src/infra/opnsense/mod.rs index deca92a..8bc27d6 100644 --- a/harmony-rs/harmony/src/infra/opnsense/mod.rs +++ b/harmony-rs/harmony/src/infra/opnsense/mod.rs @@ -1,9 +1,10 @@ mod management; -use std::sync::{Arc, RwLock }; +use std::sync::Arc; use async_trait::async_trait; use log::debug; pub use management::*; +use tokio::sync::RwLock; use crate::{ executors::ExecutorError, @@ -97,14 +98,24 @@ impl LoadBalancer for OPNSenseFirewall { #[async_trait] impl DhcpServer for OPNSenseFirewall { + async fn commit_config(&self) -> Result<(), ExecutorError> { + self.opnsense_config + .read() + .await + .apply() + .await + .map_err(|e| ExecutorError::UnexpectedError(e.to_string())) + } + async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError> { let mac: String = String::from(&entry.mac); { - let mut writable_opnsense = self.opnsense_config.write().unwrap(); + let mut writable_opnsense = self.opnsense_config.write().await; writable_opnsense .dhcp() - .add_static_mapping(&mac, entry.ip, &entry.name).unwrap(); + .add_static_mapping(&mac, entry.ip, &entry.name) + .unwrap(); } debug!("Registered {:?}", entry); diff --git a/harmony-rs/harmony/src/modules/dhcp.rs b/harmony-rs/harmony/src/modules/dhcp.rs index fb3470e..73e6d65 100644 --- a/harmony-rs/harmony/src/modules/dhcp.rs +++ b/harmony-rs/harmony/src/modules/dhcp.rs @@ -87,7 +87,9 @@ impl Interpret for DhcpInterpret { .map(|binding| { let ip = match binding.logical_host.ip { std::net::IpAddr::V4(ipv4) => ipv4, - std::net::IpAddr::V6(_) => unimplemented!("DHCPStaticEntry only supports ipv4 at the moment"), + std::net::IpAddr::V6(_) => { + unimplemented!("DHCPStaticEntry only supports ipv4 at the moment") + } }; DHCPStaticEntry { @@ -99,20 +101,23 @@ impl Interpret for DhcpInterpret { .collect(); info!("DHCPStaticEntry : {:?}", dhcp_entries); - let dhcp = Arc::new(Box::new(topology.dhcp_server.clone())); - info!("DHCP server : {:?}", dhcp); + let dhcp_server = Arc::new(Box::new(topology.dhcp_server.clone())); + info!("DHCP server : {:?}", dhcp_server); + + let number_new_entries = dhcp_entries.len(); + for entry in dhcp_entries.into_iter() { - match dhcp.add_static_mapping(&entry).await { + match dhcp_server.add_static_mapping(&entry).await { Ok(_) => info!("Successfully registered DHCPStaticEntry {}", entry), Err(_) => todo!(), } } - todo!("Configure DHCPServer"); + dhcp_server.commit_config().await; Ok(Outcome::new( InterpretStatus::SUCCESS, - "Connection test successful".to_string(), + format!("Dhcp Interpret registered {} entries", number_new_entries), )) } } diff --git a/harmony-rs/harmony/src/modules/mod.rs b/harmony-rs/harmony/src/modules/mod.rs index 5833493..84aeb83 100644 --- a/harmony-rs/harmony/src/modules/mod.rs +++ b/harmony-rs/harmony/src/modules/mod.rs @@ -1 +1,2 @@ pub mod dhcp; +pub mod okd; diff --git a/harmony-rs/harmony/src/modules/okd/dhcp.rs b/harmony-rs/harmony/src/modules/okd/dhcp.rs new file mode 100644 index 0000000..3603696 --- /dev/null +++ b/harmony-rs/harmony/src/modules/okd/dhcp.rs @@ -0,0 +1,45 @@ +use crate::{ + inventory::Inventory, + modules::dhcp::DhcpScore, + score::Score, + topology::{HAClusterTopology, HostBinding}, +}; + +#[derive(Debug)] +pub struct OKDBootstrapDhcpScore { + dhcp_score: DhcpScore, +} + +impl OKDBootstrapDhcpScore { + pub fn new(topology: &HAClusterTopology, inventory: &Inventory) -> Self { + Self { + dhcp_score: DhcpScore::new( + 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(), + ), + } + } +} + +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/mod.rs b/harmony-rs/harmony/src/modules/okd/mod.rs new file mode 100644 index 0000000..8c32480 --- /dev/null +++ b/harmony-rs/harmony/src/modules/okd/mod.rs @@ -0,0 +1,2 @@ +pub mod dhcp; + diff --git a/harmony-rs/opnsense-config-xml/src/data/opnsense.rs b/harmony-rs/opnsense-config-xml/src/data/opnsense.rs index 48ff582..04c4977 100644 --- a/harmony-rs/opnsense-config-xml/src/data/opnsense.rs +++ b/harmony-rs/opnsense-config-xml/src/data/opnsense.rs @@ -27,7 +27,7 @@ pub struct OPNsense { pub opnsense: OPNsenseXmlSection, pub staticroutes: StaticRoutes, pub ca: MaybeString, - pub gateways: Option, + pub gateways: Option, pub cert: Vec, pub dhcpdv6: DhcpDv6, pub virtualip: VirtualIp, @@ -60,7 +60,6 @@ impl OPNsense { } } - #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] pub struct LoadBalancer { pub monitor_type: Vec, @@ -415,6 +414,8 @@ pub struct OPNsenseXmlSection { pub ipsec: Option, #[yaserde(rename = "Interfaces")] pub interfaces: Option, + #[yaserde(rename = "NodeExporter")] + pub node_exporter: Option, #[yaserde(rename = "Kea")] pub kea: Option, pub monit: Option, @@ -428,6 +429,7 @@ pub struct OPNsenseXmlSection { pub unboundplus: Option, #[yaserde(rename = "DHCRelay")] pub dhcrelay: Option, + pub trust: Option, pub wireguard: Option, #[yaserde(rename = "Swanctl")] pub swanctl: Swanctl, @@ -479,6 +481,8 @@ pub struct IDSGeneral { #[yaserde(rename = "LogPayload")] log_payload: Option, verbosity: MaybeString, + #[yaserde(rename = "eveLog")] + eve_log: Option, } #[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] @@ -498,11 +502,15 @@ pub struct IPsec { key_pairs: MaybeString, #[yaserde(rename = "preSharedKeys")] pre_shared_keys: MaybeString, + charon: Option, } #[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] pub struct GeneralIpsec { enabled: MaybeString, + preferred_oldsa: MaybeString, + disablevpnrules: MaybeString, + passthrough_networks: MaybeString, } #[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] @@ -1214,6 +1222,8 @@ pub struct WireguardServerItem { pub gateway: MaybeString, pub carp_depend_on: MaybeString, pub peers: String, + pub endpoint: MaybeString, + pub peer_dns: MaybeString, } #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] @@ -1320,7 +1330,6 @@ pub struct ConfigOpenVPN { pub StaticKeys: MaybeString, } - #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] #[yaserde(rename = "HAProxy")] pub struct HAProxy { @@ -1411,6 +1420,8 @@ pub struct Tuning { #[yaserde(rename = "maxConnections")] pub max_connections: MaybeString, pub nbthread: i32, + #[yaserde(rename = "resolversPrefer")] + pub resolvers_prefer: String, #[yaserde(rename = "sslServerVerify")] pub ssl_server_verify: String, #[yaserde(rename = "maxDHSize")] @@ -1425,6 +1436,12 @@ pub struct Tuning { pub lua_max_mem: i32, #[yaserde(rename = "customOptions")] pub custom_options: MaybeString, + #[yaserde(rename = "ocspUpdateEnabled")] + pub ocs_update_enabled: MaybeString, + #[yaserde(rename = "ocspUpdateMinDelay")] + pub ocs_update_min_delay: MaybeString, + #[yaserde(rename = "ocspUpdateMaxDelay")] + pub ocs_update_max_delay: MaybeString, #[yaserde(rename = "ssl_defaultsEnabled")] pub ssl_defaults_enabled: i32, #[yaserde(rename = "ssl_bindOptions")] @@ -1437,6 +1454,19 @@ pub struct Tuning { pub ssl_cipher_list: String, #[yaserde(rename = "ssl_cipherSuites")] pub ssl_cipher_suites: String, + #[yaserde(rename = "h2_initialWindowSize")] + pub h2_initial_window_size: Option, + #[yaserde(rename = "h2_initialWindowSizeOutgoing")] + pub h2_initial_window_size_outgoing: Option, + #[yaserde(rename = "h2_initialWindowSizeIncoming")] + pub h2_initial_window_size_incoming: Option, + #[yaserde(rename = "h2_maxConcurrentStreams")] + pub h2_max_concurrent_streams: Option, + #[yaserde(rename = "h2_maxConcurrentStreamsOutgoing")] + pub h2_max_concurrent_streams_outgoing: Option, + #[yaserde(rename = "h2_maxConcurrentStreamsIncoming")] + pub h2_max_concurrent_streams_incoming: Option, + } #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] @@ -1684,25 +1714,22 @@ pub struct Backend { pub http2_enabled_nontls: u8, #[yaserde(rename = "ba_advertised_protocols")] pub ba_advertised_protocols: String, - #[yaserde(rename = "persistence")] - pub persistence: String, - #[yaserde(rename = "persistence_cookiemode")] + #[yaserde(rename = "forwardFor")] + pub forward_for: Option, + #[yaserde(rename = "forwardedHeader")] + pub forwarded_header: Option, + #[yaserde(rename = "forwardedHeaderParameters")] + pub forwarded_header_parameters: Option, + pub persistence: MaybeString, pub persistence_cookiemode: String, - #[yaserde(rename = "persistence_cookiename")] pub persistence_cookiename: MaybeString, - #[yaserde(rename = "persistence_stripquotes")] pub persistence_stripquotes: u8, - #[yaserde(rename = "stickiness_pattern")] - pub stickiness_pattern: String, + pub stickiness_pattern: MaybeString, #[yaserde(rename = "stickiness_dataTypes")] pub stickiness_data_types: MaybeString, - #[yaserde(rename = "stickiness_expire")] pub stickiness_expire: String, - #[yaserde(rename = "stickiness_size")] pub stickiness_size: String, - #[yaserde(rename = "stickiness_cookiename")] pub stickiness_cookiename: MaybeString, - #[yaserde(rename = "stickiness_cookielength")] pub stickiness_cookielength: MaybeString, #[yaserde(rename = "stickiness_connRatePeriod")] pub stickiness_conn_rate_period: String, @@ -1863,12 +1890,6 @@ pub struct StaticRoutes { #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] pub struct Ca {} -#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] -pub struct Gateways { - #[yaserde(rename = "gateway_item")] - pub gateway_item: RawXml -} - #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] pub struct Cert { #[yaserde(attribute)] @@ -1975,14 +1996,14 @@ pub struct Bridges { pub struct Gifs { #[yaserde(attribute)] pub version: Option, - pub gif: MaybeString, + pub gif: Option, } #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] pub struct Gres { #[yaserde(attribute)] pub version: Option, - pub gre: MaybeString, + pub gre: Option, } #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] diff --git a/harmony-rs/opnsense-config/src/config/manager/ssh.rs b/harmony-rs/opnsense-config/src/config/manager/ssh.rs index a32298b..fdd8d03 100644 --- a/harmony-rs/opnsense-config/src/config/manager/ssh.rs +++ b/harmony-rs/opnsense-config/src/config/manager/ssh.rs @@ -24,9 +24,10 @@ impl SshConfigManager { impl SshConfigManager { async fn backup_config_remote(&self) -> Result { - let backup_filename = format!("config_{}.xml", chrono::Local::now().format("%Y%m%d%H%M%S")); + let ts = chrono::Utc::now(); + let backup_filename = format!("config-{}-harmony.xml", ts.format("%s%.3f")); - self.opnsense_shell.exec(&format!("cp /conf/config.xml /tmp/{}", backup_filename)) + self.opnsense_shell.exec(&format!("cp /conf/config.xml /conf/backup/{}", backup_filename)) .await } diff --git a/harmony-rs/opnsense-config/src/modules/dhcp.rs b/harmony-rs/opnsense-config/src/modules/dhcp.rs index a337cac..537c3c4 100644 --- a/harmony-rs/opnsense-config/src/modules/dhcp.rs +++ b/harmony-rs/opnsense-config/src/modules/dhcp.rs @@ -84,9 +84,13 @@ impl<'a> DhcpConfig<'a> { return Err(DhcpError::InvalidMacAddress(mac)); } - if !Self::is_ip_in_range(&ipaddr, range) { - return Err(DhcpError::IpAddressOutOfRange(ipaddr.to_string())); - } + // TODO verify if address is in subnet range + // This check here does not do what we want to do, as we want to assign static leases + // outside of the dynamic DHCP pool + // + // if !Self::is_ip_in_range(&ipaddr, range) { + // return Err(DhcpError::IpAddressOutOfRange(ipaddr.to_string())); + // } if existing_mappings.iter().any(|m| { m.ipaddr @@ -123,7 +127,7 @@ impl<'a> DhcpConfig<'a> { parts .iter() - .all(|part| part.len() == 2 && part.chars().all(|c| c.is_ascii_hexdigit())) + .all(|part| part.len() <= 2 && part.chars().all(|c| c.is_ascii_hexdigit())) } fn is_ip_in_range(ip: &Ipv4Addr, range: &Range) -> bool { From 18c67adfad7e2c76d6b535be2bad19354eb2d7b3 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Tue, 17 Dec 2024 22:45:37 -0500 Subject: [PATCH 5/5] feat: Harmony now sets dhcp next boot server for PXE on okd setup --- .../harmony/src/domain/interpret/mod.rs | 13 +- .../harmony/src/domain/topology/network.rs | 1 + harmony-rs/harmony/src/infra/opnsense/mod.rs | 15 +++ harmony-rs/harmony/src/modules/dhcp.rs | 125 ++++++++++-------- harmony-rs/harmony/src/modules/okd/dhcp.rs | 34 ++--- .../opnsense-config-xml/src/data/dhcpd.rs | 2 + .../opnsense-config/src/modules/dhcp.rs | 15 ++- 7 files changed, 123 insertions(+), 82 deletions(-) diff --git a/harmony-rs/harmony/src/domain/interpret/mod.rs b/harmony-rs/harmony/src/domain/interpret/mod.rs index ecf953e..7ec1362 100644 --- a/harmony-rs/harmony/src/domain/interpret/mod.rs +++ b/harmony-rs/harmony/src/domain/interpret/mod.rs @@ -7,7 +7,6 @@ use super::{ data::{Id, Version}, executors::ExecutorError, inventory::Inventory, - score::Score, topology::HAClusterTopology, }; @@ -41,9 +40,13 @@ 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)) + +impl Outcome { + pub fn noop() -> Self { + Self { + status: InterpretStatus::NOOP, + message: String::new(), + } } } @@ -54,6 +57,7 @@ pub enum InterpretStatus { RUNNING, QUEUED, BLOCKED, + NOOP, } impl std::fmt::Display for InterpretStatus { @@ -64,6 +68,7 @@ impl std::fmt::Display for InterpretStatus { InterpretStatus::RUNNING => "RUNNING", InterpretStatus::QUEUED => "QUEUED", InterpretStatus::BLOCKED => "BLOCKED", + InterpretStatus::NOOP => "NO_OP", }; f.write_str(msg) } diff --git a/harmony-rs/harmony/src/domain/topology/network.rs b/harmony-rs/harmony/src/domain/topology/network.rs index 536370c..ff19dc0 100644 --- a/harmony-rs/harmony/src/domain/topology/network.rs +++ b/harmony-rs/harmony/src/domain/topology/network.rs @@ -45,6 +45,7 @@ pub trait DhcpServer: Send + Sync { async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError>; async fn remove_static_mapping(&self, mac: &MacAddress) -> Result<(), ExecutorError>; async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>; + async fn set_next_server(&self, ip: IpAddress) -> Result<(), ExecutorError>; fn get_ip(&self) -> IpAddress; fn get_host(&self) -> LogicalHost; async fn commit_config(&self) -> Result<(), ExecutorError>; diff --git a/harmony-rs/harmony/src/infra/opnsense/mod.rs b/harmony-rs/harmony/src/infra/opnsense/mod.rs index 8bc27d6..7aa4407 100644 --- a/harmony-rs/harmony/src/infra/opnsense/mod.rs +++ b/harmony-rs/harmony/src/infra/opnsense/mod.rs @@ -136,9 +136,24 @@ impl DhcpServer for OPNSenseFirewall { fn get_ip(&self) -> IpAddress { OPNSenseFirewall::get_ip(self) } + fn get_host(&self) -> LogicalHost { self.host.clone() } + + async fn set_next_server(&self, ip: IpAddress) -> Result<(), ExecutorError> { + let ipv4 = match ip { + std::net::IpAddr::V4(ipv4_addr) => ipv4_addr, + std::net::IpAddr::V6(_) => todo!("ipv6 not supported yet"), + }; + { + let mut writable_opnsense = self.opnsense_config.write().await; + writable_opnsense.dhcp().set_next_server(ipv4); + debug!("OPNsense dhcp server set next server {ipv4}"); + } + + Ok(()) + } } impl DnsServer for OPNSenseFirewall { diff --git a/harmony-rs/harmony/src/modules/dhcp.rs b/harmony-rs/harmony/src/modules/dhcp.rs index 73e6d65..09dd87a 100644 --- a/harmony-rs/harmony/src/modules/dhcp.rs +++ b/harmony-rs/harmony/src/modules/dhcp.rs @@ -11,7 +11,7 @@ use crate::{ }, interpret::{Interpret, InterpretError, InterpretName, Outcome}, inventory::Inventory, - topology::{DHCPStaticEntry, HAClusterTopology, HostBinding}, + topology::{DHCPStaticEntry, HAClusterTopology, HostBinding, IpAddress}, }; use crate::domain::score::Score; @@ -19,6 +19,7 @@ use crate::domain::score::Score; #[derive(Debug, new, Clone)] pub struct DhcpScore { host_binding: Vec, + next_server: Option, } impl Score for DhcpScore { @@ -53,6 +54,67 @@ impl DhcpInterpret { status: InterpretStatus::QUEUED, } } + async fn add_static_entries( + &self, + _inventory: &Inventory, + topology: &HAClusterTopology, + ) -> Result { + let dhcp_entries: Vec = self + .score + .host_binding + .iter() + .map(|binding| { + let ip = match binding.logical_host.ip { + std::net::IpAddr::V4(ipv4) => ipv4, + std::net::IpAddr::V6(_) => { + unimplemented!("DHCPStaticEntry only supports ipv4 at the moment") + } + }; + + DHCPStaticEntry { + name: binding.logical_host.name.clone(), + mac: binding.physical_host.cluster_mac(), + ip, + } + }) + .collect(); + info!("DHCPStaticEntry : {:?}", dhcp_entries); + + let dhcp_server = Arc::new(topology.dhcp_server.clone()); + info!("DHCP server : {:?}", dhcp_server); + + let number_new_entries = dhcp_entries.len(); + + for entry in dhcp_entries.into_iter() { + match dhcp_server.add_static_mapping(&entry).await { + Ok(_) => info!("Successfully registered DHCPStaticEntry {}", entry), + Err(_) => todo!(), + } + } + + Ok(Outcome::new( + InterpretStatus::SUCCESS, + format!("Dhcp Interpret registered {} entries", number_new_entries), + )) + } + + async fn set_next_server( + &self, + _inventory: &Inventory, + topology: &HAClusterTopology, + ) -> Result { + match self.score.next_server { + Some(next_server) => { + let dhcp_server = Arc::new(topology.dhcp_server.clone()); + dhcp_server.set_next_server(next_server).await?; + Ok(Outcome::new( + InterpretStatus::SUCCESS, + format!("Dhcp Interpret Set next boot to {next_server}"), + )) + } + None => Ok(Outcome::noop()), + } + } } #[async_trait] @@ -80,68 +142,15 @@ impl Interpret for DhcpInterpret { ) -> Result { info!("Executing {} on inventory {inventory:?}", self.get_name()); - let dhcp_entries: Vec = self - .score - .host_binding - .iter() - .map(|binding| { - let ip = match binding.logical_host.ip { - std::net::IpAddr::V4(ipv4) => ipv4, - std::net::IpAddr::V6(_) => { - unimplemented!("DHCPStaticEntry only supports ipv4 at the moment") - } - }; + self.set_next_server(inventory, topology).await?; - DHCPStaticEntry { - name: binding.logical_host.name.clone(), - mac: binding.physical_host.cluster_mac(), - ip, - } - }) - .collect(); - info!("DHCPStaticEntry : {:?}", dhcp_entries); + // self.add_static_entries(inventory, topology).await?; - let dhcp_server = Arc::new(Box::new(topology.dhcp_server.clone())); - info!("DHCP server : {:?}", dhcp_server); - - let number_new_entries = dhcp_entries.len(); - - for entry in dhcp_entries.into_iter() { - match dhcp_server.add_static_mapping(&entry).await { - Ok(_) => info!("Successfully registered DHCPStaticEntry {}", entry), - Err(_) => todo!(), - } - } - - dhcp_server.commit_config().await; + topology.dhcp_server.commit_config().await?; Ok(Outcome::new( InterpretStatus::SUCCESS, - format!("Dhcp Interpret registered {} entries", number_new_entries), + format!("Dhcp Interpret execution successful"), )) } } - -#[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!(); - } -} diff --git a/harmony-rs/harmony/src/modules/okd/dhcp.rs b/harmony-rs/harmony/src/modules/okd/dhcp.rs index 3603696..b22efff 100644 --- a/harmony-rs/harmony/src/modules/okd/dhcp.rs +++ b/harmony-rs/harmony/src/modules/okd/dhcp.rs @@ -12,25 +12,25 @@ pub struct OKDBootstrapDhcpScore { impl OKDBootstrapDhcpScore { pub fn new(topology: &HAClusterTopology, inventory: &Inventory) -> Self { + let host_binding = 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(); Self { dhcp_score: DhcpScore::new( - 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, + // 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()), ), } } diff --git a/harmony-rs/opnsense-config-xml/src/data/dhcpd.rs b/harmony-rs/opnsense-config-xml/src/data/dhcpd.rs index 9c06716..212758a 100644 --- a/harmony-rs/opnsense-config-xml/src/data/dhcpd.rs +++ b/harmony-rs/opnsense-config-xml/src/data/dhcpd.rs @@ -16,6 +16,8 @@ pub struct DhcpInterface { pub enable: Option, pub gateway: Option, pub domain: Option, + pub netboot: Option, + pub nextserver: Option, #[yaserde(rename = "ddnsdomainalgorithm")] pub ddns_domain_algorithm: Option, #[yaserde(rename = "numberoptions")] diff --git a/harmony-rs/opnsense-config/src/modules/dhcp.rs b/harmony-rs/opnsense-config/src/modules/dhcp.rs index 537c3c4..ae9ccbc 100644 --- a/harmony-rs/opnsense-config/src/modules/dhcp.rs +++ b/harmony-rs/opnsense-config/src/modules/dhcp.rs @@ -54,7 +54,9 @@ impl<'a> DhcpConfig<'a> { pub fn remove_static_mapping(&mut self, mac: &str) { let lan_dhcpd = self.get_lan_dhcpd(); - lan_dhcpd.staticmaps.retain(|static_entry| static_entry.mac != mac); + lan_dhcpd + .staticmaps + .retain(|static_entry| static_entry.mac != mac); } fn get_lan_dhcpd(&mut self) -> &mut opnsense_config_xml::DhcpInterface { @@ -77,7 +79,6 @@ impl<'a> DhcpConfig<'a> { let mac = mac.to_string(); let hostname = hostname.to_string(); let lan_dhcpd = self.get_lan_dhcpd(); - let range = &lan_dhcpd.range; let existing_mappings: &mut Vec = &mut lan_dhcpd.staticmaps; if !Self::is_valid_mac(&mac) { @@ -87,7 +88,7 @@ impl<'a> DhcpConfig<'a> { // TODO verify if address is in subnet range // This check here does not do what we want to do, as we want to assign static leases // outside of the dynamic DHCP pool - // + // let range = &lan_dhcpd.range; // if !Self::is_ip_in_range(&ipaddr, range) { // return Err(DhcpError::IpAddressOutOfRange(ipaddr.to_string())); // } @@ -177,6 +178,14 @@ impl<'a> DhcpConfig<'a> { Ok(static_maps) } + pub fn enable_netboot(&mut self) { + self.get_lan_dhcpd().netboot = Some(1); + } + + pub fn set_next_server(&mut self, ip: Ipv4Addr) { + self.enable_netboot(); + self.get_lan_dhcpd().nextserver = Some(ip.to_string()); + } } #[cfg(test)]