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] 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); +}