From ff3d53c70449344edcd61471079b862a8b0e4a35 Mon Sep 17 00:00:00 2001 From: Jean-Gabriel Gill-Couture Date: Sun, 8 Sep 2024 19:06:21 -0400 Subject: [PATCH] wip: Working hard on topology, seems to have something that may work, next step is put the building blocks together for real --- harmony-rs/Cargo.lock | 8 ++ harmony-rs/Cargo.toml | 1 + harmony-rs/fqm/Cargo.toml | 1 + harmony-rs/fqm/src/inventory.rs | 23 ----- harmony-rs/fqm/src/inventory/mod.rs | 82 ++++++++++++++++ harmony-rs/fqm/src/main.rs | 7 +- harmony-rs/fqm/src/topology.rs | 98 ++++--------------- harmony-rs/harmony/Cargo.toml | 1 + harmony-rs/harmony/src/domain/hardware/mod.rs | 4 +- .../src/domain/topology/load_balancer.rs | 27 +++++ harmony-rs/harmony/src/domain/topology/mod.rs | 35 +++---- .../harmony/src/domain/topology/network.rs | 43 ++------ .../harmony/src/domain/topology/router.rs | 25 +++++ harmony-rs/harmony/src/infra/mod.rs | 1 + harmony-rs/harmony/src/infra/opnsense/mod.rs | 78 +++++++++++++++ 15 files changed, 267 insertions(+), 167 deletions(-) delete mode 100644 harmony-rs/fqm/src/inventory.rs create mode 100644 harmony-rs/fqm/src/inventory/mod.rs create mode 100644 harmony-rs/harmony/src/domain/topology/load_balancer.rs create mode 100644 harmony-rs/harmony/src/domain/topology/router.rs create mode 100644 harmony-rs/harmony/src/infra/opnsense/mod.rs diff --git a/harmony-rs/Cargo.lock b/harmony-rs/Cargo.lock index db8e801..241c91c 100644 --- a/harmony-rs/Cargo.lock +++ b/harmony-rs/Cargo.lock @@ -282,6 +282,12 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "cidr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdf600c45bd958cf2945c445264471cca8b6c8e67bc87b71affd6d7e5682621" + [[package]] name = "cipher" version = "0.4.4" @@ -620,6 +626,7 @@ dependencies = [ name = "fqm" version = "0.1.0" dependencies = [ + "cidr", "env_logger", "harmony", "log", @@ -794,6 +801,7 @@ name = "harmony" version = "0.1.0" dependencies = [ "async-trait", + "cidr", "derive-new", "env_logger", "libredfish", diff --git a/harmony-rs/Cargo.toml b/harmony-rs/Cargo.toml index 3b9fa25..44002db 100644 --- a/harmony-rs/Cargo.toml +++ b/harmony-rs/Cargo.toml @@ -16,3 +16,4 @@ env_logger = "0.11.5" derive-new = "0.7.0" async-trait = "0.1.82" tokio = { version = "1.40.0", features = ["io-std"] } +cidr = "0.2.3" diff --git a/harmony-rs/fqm/Cargo.toml b/harmony-rs/fqm/Cargo.toml index 3a9ebdc..c04bebb 100644 --- a/harmony-rs/fqm/Cargo.toml +++ b/harmony-rs/fqm/Cargo.toml @@ -8,4 +8,5 @@ harmony = { path = "../harmony" } log = { workspace = true } env_logger = { workspace = true } tokio = { workspace = true } +cidr = { workspace = true } diff --git a/harmony-rs/fqm/src/inventory.rs b/harmony-rs/fqm/src/inventory.rs deleted file mode 100644 index 9efcefd..0000000 --- a/harmony-rs/fqm/src/inventory.rs +++ /dev/null @@ -1,23 +0,0 @@ -use harmony::domain::{ - hardware::{Host, HostCategory, Location, NetworkInterface}, - inventory::Inventory, -}; - -pub fn get_inventory() -> Inventory { - let network = vec![NetworkInterface::new(1_000_000_000, "TODO MAC ADDRESS".into(), true ) ]; - let storage = vec![]; - Inventory { - 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, - storage, - labels: vec![], - }], - switch: vec![], - firewall: vec![], - } -} diff --git a/harmony-rs/fqm/src/inventory/mod.rs b/harmony-rs/fqm/src/inventory/mod.rs new file mode 100644 index 0000000..5f0bd53 --- /dev/null +++ b/harmony-rs/fqm/src/inventory/mod.rs @@ -0,0 +1,82 @@ +use harmony::{ + hardware::{Host, HostCategory, Label, Location, NetworkInterface, Storage, StorageConnectionType, StorageKind}, + inventory::Inventory, +}; + +pub fn get_inventory() -> Inventory { + Inventory { + location: Location::new( + "1134 Grande Allée Ouest 1er étage, Québec, Qc".into(), + "FQM 1134 1er étage".into(), + ), + host: vec![], + switch: vec![], + firewall: vec![get_firewall_1()], + } +} + +fn get_firewall_1() -> Host { + Host { + category: HostCategory::Firewall, + network: vec![ + NetworkInterface::new( + "igb0".to_string(), + "00:90:7f:df:2c:1f".to_string(), + 1_000_000_000, + true, + ), + NetworkInterface::new( + "igb1".to_string(), + "00:90:7f:df:2c:20".to_string(), + 1_000_000_000, + true, + ), + NetworkInterface::new( + "igb2".to_string(), + "00:90:7f:df:2c:21".to_string(), + 1_000_000_000, + true, + ), + NetworkInterface::new( + "igb3".to_string(), + "00:90:7f:df:2c:22".to_string(), + 1_000_000_000, + true, + ), + NetworkInterface::new( + "igb4".to_string(), + "00:90:7f:df:2c:23".to_string(), + 1_000_000_000, + true, + ), + NetworkInterface::new( + "igb5".to_string(), + "00:90:7f:df:2c:24".to_string(), + 1_000_000_000, + true, + ), + NetworkInterface::new( + "igb6".to_string(), + "00:90:7f:df:2c:25".to_string(), + 1_000_000_000, + true, + ), + NetworkInterface::new( + "igb7".to_string(), + "00:90:7f:df:2c:26".to_string(), + 1_000_000_000, + true, + ), + ], + storage: vec![Storage::new( + StorageConnectionType::Sata6g, + StorageKind::SSD, + 240_000_000_000, + "TODO".to_string(), + )], + labels: vec![Label::new( + "operatingSystem".to_string(), + "opnsense".to_string(), + )], + } +} diff --git a/harmony-rs/fqm/src/main.rs b/harmony-rs/fqm/src/main.rs index 86df6fb..26f0579 100644 --- a/harmony-rs/fqm/src/main.rs +++ b/harmony-rs/fqm/src/main.rs @@ -1,9 +1,7 @@ -use fqm::inventory::get_inventory; +use fqm::{inventory::get_inventory, topology::fqm_topology}; use harmony::{ - domain::{ - inventory::{Inventory, InventoryFilter}, + inventory::InventoryFilter, maestro::Maestro, - }, modules::opnsense_dhcp::OPNSenseDhcpScore, }; use log::info; @@ -15,6 +13,7 @@ async fn main() { tokio::spawn(async move { info!("FQM Harmony Starting"); let maestro = Maestro::new(get_inventory()); + let topology = fqm_topology(); let score = OPNSenseDhcpScore::new(InventoryFilter::new(vec![])); let result = maestro.interpret(score).await.unwrap(); info!("{result}"); diff --git a/harmony-rs/fqm/src/topology.rs b/harmony-rs/fqm/src/topology.rs index 94638ca..abcc112 100644 --- a/harmony-rs/fqm/src/topology.rs +++ b/harmony-rs/fqm/src/topology.rs @@ -1,86 +1,24 @@ -use std::net::{IpAddr, Ipv4Addr}; +use std::net::Ipv4Addr; -use harmony::{ - hardware::*, - topology::{ClusterMember, HAClusterTopology, IpAddress, OPNSenseManagement}, -}; +use cidr::Ipv4Cidr; +use harmony::infra::opnsense::OPNSenseFirewall; +use harmony::topology::{HAClusterTopology, IpAddress, UnmanagedRouter}; pub fn fqm_topology() -> HAClusterTopology { + let opnsense_firewall = Box::new(OPNSenseFirewall::new(IpAddress::V4(Ipv4Addr::new(10, 10, 8, 2)))); + HAClusterTopology { - firewall: vec![ClusterMember { - management: Box::new(OPNSenseManagement::new( - "user".to_string(), - "password".to_string(), - )), - host: Host { - category: HostCategory::Firewall, - network: vec![ - NetworkInterface::new( - "igb0".to_string(), - "00:90:7f:df:2c:1f".to_string(), - 1_000_000_000, - true, - ), - NetworkInterface::new( - "igb1".to_string(), - "00:90:7f:df:2c:20".to_string(), - 1_000_000_000, - true, - ), - NetworkInterface::new( - "igb2".to_string(), - "00:90:7f:df:2c:21".to_string(), - 1_000_000_000, - true, - ), - NetworkInterface::new( - "igb3".to_string(), - "00:90:7f:df:2c:22".to_string(), - 1_000_000_000, - true, - ), - NetworkInterface::new( - "igb4".to_string(), - "00:90:7f:df:2c:23".to_string(), - 1_000_000_000, - true, - ), - NetworkInterface::new( - "igb5".to_string(), - "00:90:7f:df:2c:24".to_string(), - 1_000_000_000, - true, - ), - NetworkInterface::new( - "igb6".to_string(), - "00:90:7f:df:2c:25".to_string(), - 1_000_000_000, - true, - ), - NetworkInterface::new( - "igb7".to_string(), - "00:90:7f:df:2c:26".to_string(), - 1_000_000_000, - true, - ), - ], - storage: vec![Storage::new( - StorageConnectionType::Sata6g, - StorageKind::SSD, - 240_000_000_000, - "TODO".to_string(), - )], - labels: vec![Label::new( - "operatingSystem".to_string(), - "opnsense".to_string(), - )], - }, - }], - control_plane: todo!(), - workers: todo!(), - ceph_hosts: todo!(), - switch: todo!(), - gateway: IpAddress::V4(Ipv4Addr::new(10, 10, 8, 1)), - load_balancer: IpAddress::V4(Ipv4Addr::new(10, 10, 8, 1)), + firewall: opnsense_firewall.clone(), + control_plane: vec![], + ceph_hosts: vec![], + switch: vec![], + router: Box::new(UnmanagedRouter::new( + IpAddress::V4(Ipv4Addr::new(10, 10, 8, 1)), + Ipv4Cidr::new(Ipv4Addr::new(10, 10, 8, 0), 22).expect("Subnet is valid"), + )), + load_balancer: opnsense_firewall.clone(), + dhcp_server: opnsense_firewall.clone(), + dns_server: opnsense_firewall.clone(), + workers: vec![], } } diff --git a/harmony-rs/harmony/Cargo.toml b/harmony-rs/harmony/Cargo.toml index 0108040..17b07da 100644 --- a/harmony-rs/harmony/Cargo.toml +++ b/harmony-rs/harmony/Cargo.toml @@ -16,3 +16,4 @@ derive-new = { workspace = true } log = { workspace = true } env_logger = { workspace = true } async-trait = { workspace = true } +cidr = { workspace = true } diff --git a/harmony-rs/harmony/src/domain/hardware/mod.rs b/harmony-rs/harmony/src/domain/hardware/mod.rs index f6a4e20..ef03260 100644 --- a/harmony-rs/harmony/src/domain/hardware/mod.rs +++ b/harmony-rs/harmony/src/domain/hardware/mod.rs @@ -2,7 +2,7 @@ use derive_new::new; pub type HostGroup = Vec; pub type SwitchGroup = Vec; -pub type FirewallGroup = Vec; +pub type FirewallGroup = Vec; #[derive(Debug)] pub struct Host { pub category: HostCategory, @@ -55,8 +55,6 @@ pub struct Switch { management_interface: NetworkInterface, } -#[derive(Debug)] -pub struct Firewall {} #[derive(Debug, new)] pub struct Label { name: String, diff --git a/harmony-rs/harmony/src/domain/topology/load_balancer.rs b/harmony-rs/harmony/src/domain/topology/load_balancer.rs new file mode 100644 index 0000000..0259072 --- /dev/null +++ b/harmony-rs/harmony/src/domain/topology/load_balancer.rs @@ -0,0 +1,27 @@ +use super::IpAddress; + +pub trait LoadBalancer { + fn add_backend(&mut self, backend: Backend) -> Result<(), LoadBalancerError>; + fn remove_backend(&mut self, backend_id: &str) -> Result<(), LoadBalancerError>; + fn add_frontend(&mut self, frontend: Frontend) -> Result<(), LoadBalancerError>; + fn remove_frontend(&mut self, frontend_id: &str) -> Result<(), LoadBalancerError>; + fn list_backends(&self) -> Vec; + fn list_frontends(&self) -> Vec; +} +pub struct LoadBalancerError; + +#[derive(Clone, Debug)] +pub struct Backend { + pub id: String, + pub ip: IpAddress, + pub port: u16, + pub weight: u8, +} + +#[derive(Clone, Debug)] +pub struct Frontend { + pub id: String, + pub ip: IpAddress, + pub port: u16, + pub backend_ids: Vec, +} diff --git a/harmony-rs/harmony/src/domain/topology/mod.rs b/harmony-rs/harmony/src/domain/topology/mod.rs index 4ab5c9d..27507dd 100644 --- a/harmony-rs/harmony/src/domain/topology/mod.rs +++ b/harmony-rs/harmony/src/domain/topology/mod.rs @@ -1,18 +1,20 @@ +mod router; +mod load_balancer; +pub use router::*; +pub use load_balancer::*; mod network; pub use network::*; use std::net::IpAddr; -use derive_new::new; - -use super::hardware::{Host, HostGroup}; +use super::hardware::Host; pub struct HAClusterTopology { - pub gateway: IpAddress, - pub load_balancer: IpAddress, - pub firewall: Box, - pub dhcp_server: Box, - pub dns_server: Box, + pub router: Box, + pub load_balancer: Box, + pub firewall: Box, + pub dhcp_server: Box, + pub dns_server: Box, pub control_plane: Vec, pub workers: Vec, pub ceph_hosts: Vec, @@ -22,7 +24,7 @@ pub struct HAClusterTopology { pub type IpAddress = IpAddr; pub struct ClusterMember { - pub management: Box, + pub management: Box, pub host: Host, } @@ -31,18 +33,3 @@ pub trait ManagementInterface { fn get_ip(&self) -> IpAddress; } -#[derive(new)] -pub struct OPNSenseManagement { - username: String, - password: String, -} - -impl ManagementInterface for OPNSenseManagement { - fn boot_to_pxe(&self) { - todo!() - } - - fn get_ip(&self) -> IpAddress { - todo!() - } -} diff --git a/harmony-rs/harmony/src/domain/topology/network.rs b/harmony-rs/harmony/src/domain/topology/network.rs index abae511..4bc8cf2 100644 --- a/harmony-rs/harmony/src/domain/topology/network.rs +++ b/harmony-rs/harmony/src/domain/topology/network.rs @@ -1,15 +1,20 @@ -use std::net::{IpAddr, Ipv4Addr}; +use super::IpAddress; pub trait Firewall { fn add_rule(&mut self, rule: FirewallRule) -> Result<(), FirewallError>; fn remove_rule(&mut self, rule_id: &str) -> Result<(), FirewallError>; fn list_rules(&self) -> Vec; + fn get_ip(&self) -> IpAddress; +} + +pub struct NetworkDomain { + pub name: String } pub trait DhcpServer { - fn add_static_mapping(&mut self, mac: MacAddress, ip: Ipv4Addr) -> Result<(), DhcpError>; + fn add_static_mapping(&mut self, mac: MacAddress, ip: IpAddress) -> Result<(), DhcpError>; fn remove_static_mapping(&mut self, mac: &MacAddress) -> Result<(), DhcpError>; - fn list_static_mappings(&self) -> Vec<(MacAddress, Ipv4Addr)>; + fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>; } pub trait DnsServer { @@ -18,22 +23,11 @@ pub trait DnsServer { fn list_records(&self) -> Vec; } -pub trait LoadBalancer { - fn add_backend(&mut self, backend: Backend) -> Result<(), LoadBalancerError>; - fn remove_backend(&mut self, backend_id: &str) -> Result<(), LoadBalancerError>; - fn add_frontend(&mut self, frontend: Frontend) -> Result<(), LoadBalancerError>; - fn remove_frontend(&mut self, frontend_id: &str) -> Result<(), LoadBalancerError>; - fn list_backends(&self) -> Vec; - fn list_frontends(&self) -> Vec; -} - -// Supporting types - #[derive(Clone, Debug)] pub struct FirewallRule { pub id: String, - pub source: IpAddr, - pub destination: IpAddr, + pub source: IpAddress, + pub destination: IpAddress, pub port: u16, pub protocol: Protocol, pub action: Action, @@ -71,24 +65,7 @@ pub struct DnsRecord { pub value: String, } -#[derive(Clone, Debug)] -pub struct Backend { - pub id: String, - pub ip: IpAddr, - pub port: u16, - pub weight: u8, -} - -#[derive(Clone, Debug)] -pub struct Frontend { - pub id: String, - pub ip: IpAddr, - pub port: u16, - pub backend_ids: Vec, -} - // Error types pub struct FirewallError; pub struct DhcpError; pub struct DnsError; -pub struct LoadBalancerError; diff --git a/harmony-rs/harmony/src/domain/topology/router.rs b/harmony-rs/harmony/src/domain/topology/router.rs new file mode 100644 index 0000000..8e82503 --- /dev/null +++ b/harmony-rs/harmony/src/domain/topology/router.rs @@ -0,0 +1,25 @@ +use cidr::Ipv4Cidr; +use derive_new::new; + +use super::IpAddress; + +pub trait Router { + fn get_gateway(&self) -> IpAddress; + fn get_cidr(&self) -> Ipv4Cidr; +} + +#[derive(new)] +pub struct UnmanagedRouter { + gateway: IpAddress, + cidr: Ipv4Cidr, +} + +impl Router for UnmanagedRouter { + fn get_gateway(&self) -> IpAddress { + self.gateway.clone() + } + + fn get_cidr(&self) -> Ipv4Cidr { + self.cidr.clone() + } +} diff --git a/harmony-rs/harmony/src/infra/mod.rs b/harmony-rs/harmony/src/infra/mod.rs index 7c47003..e6d5fbf 100644 --- a/harmony-rs/harmony/src/infra/mod.rs +++ b/harmony-rs/harmony/src/infra/mod.rs @@ -1 +1,2 @@ pub mod executors; +pub mod opnsense; diff --git a/harmony-rs/harmony/src/infra/opnsense/mod.rs b/harmony-rs/harmony/src/infra/opnsense/mod.rs new file mode 100644 index 0000000..3def185 --- /dev/null +++ b/harmony-rs/harmony/src/infra/opnsense/mod.rs @@ -0,0 +1,78 @@ +use derive_new::new; +use crate::topology::{Backend, DhcpServer, DnsServer, Firewall, FirewallError, FirewallRule, Frontend, IpAddress, LoadBalancer, LoadBalancerError}; + +#[derive(new, Clone)] +pub struct OPNSenseFirewall { + ip_address: IpAddress, +} + +impl Firewall for OPNSenseFirewall { + fn add_rule(&mut self, _rule: FirewallRule) -> Result<(), FirewallError> { + todo!() + } + + fn remove_rule(&mut self, _rule_id: &str) -> Result<(), FirewallError> { + todo!() + } + + fn list_rules(&self) -> Vec { + todo!() + } + + fn get_ip(&self) -> IpAddress { + self.ip_address.clone() + } +} + +impl LoadBalancer for OPNSenseFirewall { + fn add_backend(&mut self, _backend: Backend) -> Result<(), LoadBalancerError> { + todo!() + } + + fn remove_backend(&mut self, _backend_id: &str) -> Result<(), LoadBalancerError> { + todo!() + } + + fn add_frontend(&mut self, _frontend: Frontend) -> Result<(), LoadBalancerError> { + todo!() + } + + fn remove_frontend(&mut self, _frontend_id: &str) -> Result<(), LoadBalancerError> { + todo!() + } + + fn list_backends(&self) -> Vec { + todo!() + } + + fn list_frontends(&self) -> Vec { + todo!() + } +} + +impl DhcpServer for OPNSenseFirewall { + fn add_static_mapping(&mut self, mac: crate::topology::MacAddress, ip: IpAddress) -> Result<(), crate::topology::DhcpError> { + todo!() + } + + fn remove_static_mapping(&mut self, mac: &crate::topology::MacAddress) -> Result<(), crate::topology::DhcpError> { + todo!() + } + + fn list_static_mappings(&self) -> Vec<(crate::topology::MacAddress, IpAddress)> { + todo!() + } +} +impl DnsServer for OPNSenseFirewall { + fn add_record(&mut self, name: &str, record_type: crate::topology::DnsRecordType, value: &str) -> Result<(), crate::topology::DnsError> { + todo!() + } + + fn remove_record(&mut self, name: &str, record_type: crate::topology::DnsRecordType) -> Result<(), crate::topology::DnsError> { + todo!() + } + + fn list_records(&self) -> Vec { + todo!() + } +}