wip: OPNSense XML config editor coming along
This commit is contained in:
parent
465106438a
commit
407bdbc032
74
harmony-rs/Cargo.lock
generated
74
harmony-rs/Cargo.lock
generated
@ -58,17 +58,6 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "affilium"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cidr",
|
||||
"env_logger",
|
||||
"harmony",
|
||||
"log",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
@ -258,6 +247,15 @@ version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cbc"
|
||||
version = "0.1.2"
|
||||
@ -315,6 +313,19 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
@ -817,6 +828,7 @@ dependencies = [
|
||||
"env_logger",
|
||||
"libredfish",
|
||||
"log",
|
||||
"minidom",
|
||||
"reqwest",
|
||||
"russh",
|
||||
"rust-ipmi",
|
||||
@ -1069,6 +1081,15 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "minidom"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e394a0e3c7ccc2daea3dffabe82f09857b6b510cb25af87d54bf3e910ac1642d"
|
||||
dependencies = [
|
||||
"rxml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.4"
|
||||
@ -1732,6 +1753,31 @@ dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
||||
|
||||
[[package]]
|
||||
name = "rxml"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc94b580d0f5a6b7a2d604e597513d3c673154b52ddeccd1d5c32360d945ee"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"rxml_validation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rxml_validation"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "826e80413b9a35e9d33217b3dcac04cf95f6559d15944b93887a08be5496c4a4"
|
||||
dependencies = [
|
||||
"compact_str",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
@ -1993,6 +2039,12 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
|
@ -17,3 +17,4 @@ derive-new = "0.7.0"
|
||||
async-trait = "0.1.82"
|
||||
tokio = { version = "1.40.0", features = ["io-std"] }
|
||||
cidr = "0.2.3"
|
||||
minidom = "0.16.0"
|
||||
|
@ -17,3 +17,4 @@ log = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
cidr = { workspace = true }
|
||||
minidom = { workspace = true }
|
||||
|
@ -7,6 +7,7 @@ use crate::topology::MacAddress;
|
||||
pub type HostGroup = Vec<PhysicalHost>;
|
||||
pub type SwitchGroup = Vec<Switch>;
|
||||
pub type FirewallGroup = Vec<PhysicalHost>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PhysicalHost {
|
||||
pub category: HostCategory,
|
||||
|
@ -23,6 +23,9 @@ use super::{
|
||||
pub struct Inventory {
|
||||
pub location: Location,
|
||||
pub switch: SwitchGroup,
|
||||
// Firewall is really just a host but with somewhat specialized hardware
|
||||
// I'm not entirely sure it belongs to its own category but it helps make things easier and
|
||||
// clearer for now so let's try it this way.
|
||||
pub firewall: FirewallGroup,
|
||||
pub worker_host: HostGroup,
|
||||
pub storage_host: HostGroup,
|
||||
@ -34,9 +37,11 @@ impl Inventory {
|
||||
pub fn empty_inventory() -> Self {
|
||||
Self {
|
||||
location: Location::test_building(),
|
||||
host: HostGroup::new(),
|
||||
switch: SwitchGroup::new(),
|
||||
firewall: FirewallGroup::new(),
|
||||
worker_host: HostGroup::new(),
|
||||
storage_host: HostGroup::new(),
|
||||
control_plane_host: HostGroup::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
use crate::executors::ExecutorError;
|
||||
use super::{IpAddress, LogicalHost};
|
||||
|
||||
pub trait LoadBalancer: Send + Sync {
|
||||
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 add_backend(&mut self, backend: Backend) -> Result<(), ExecutorError>;
|
||||
fn remove_backend(&mut self, backend_id: &str) -> Result<(), ExecutorError>;
|
||||
fn add_frontend(&mut self, frontend: Frontend) -> Result<(), ExecutorError>;
|
||||
fn remove_frontend(&mut self, frontend_id: &str) -> Result<(), ExecutorError>;
|
||||
fn list_backends(&self) -> Vec<Backend>;
|
||||
fn list_frontends(&self) -> Vec<Frontend>;
|
||||
fn get_ip(&self) -> IpAddress;
|
||||
@ -17,8 +18,6 @@ impl std::fmt::Debug for dyn LoadBalancer {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LoadBalancerError;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Backend {
|
||||
pub id: String,
|
||||
|
@ -1,3 +1,7 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::executors::ExecutorError;
|
||||
|
||||
use super::{IpAddress, LogicalHost};
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -14,8 +18,8 @@ impl std::fmt::Display for DHCPStaticEntry {
|
||||
}
|
||||
|
||||
pub trait Firewall: Send + Sync {
|
||||
fn add_rule(&mut self, rule: FirewallRule) -> Result<(), FirewallError>;
|
||||
fn remove_rule(&mut self, rule_id: &str) -> Result<(), FirewallError>;
|
||||
fn add_rule(&mut self, rule: FirewallRule) -> Result<(), ExecutorError>;
|
||||
fn remove_rule(&mut self, rule_id: &str) -> Result<(), ExecutorError>;
|
||||
fn list_rules(&self) -> Vec<FirewallRule>;
|
||||
fn get_ip(&self) -> IpAddress;
|
||||
fn get_host(&self) -> LogicalHost;
|
||||
@ -31,10 +35,11 @@ pub struct NetworkDomain {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait DhcpServer: Send + Sync {
|
||||
fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), DhcpError>;
|
||||
fn remove_static_mapping(&self, mac: &MacAddress) -> Result<(), DhcpError>;
|
||||
fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>;
|
||||
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)>;
|
||||
fn get_ip(&self) -> IpAddress;
|
||||
fn get_host(&self) -> LogicalHost;
|
||||
}
|
||||
@ -51,8 +56,8 @@ pub trait DnsServer: Send + Sync {
|
||||
name: &str,
|
||||
record_type: DnsRecordType,
|
||||
value: &str,
|
||||
) -> Result<(), DnsError>;
|
||||
fn remove_record(&mut self, name: &str, record_type: DnsRecordType) -> Result<(), DnsError>;
|
||||
) -> Result<(), ExecutorError>;
|
||||
fn remove_record(&mut self, name: &str, record_type: DnsRecordType) -> Result<(), ExecutorError>;
|
||||
fn list_records(&self) -> Vec<DnsRecord>;
|
||||
fn get_ip(&self) -> IpAddress;
|
||||
fn get_host(&self) -> LogicalHost;
|
||||
@ -89,6 +94,9 @@ pub enum Action {
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct MacAddress(pub [u8; 6]);
|
||||
|
||||
// TODO create a small macro to provide a nice API to initiate a MacAddress
|
||||
// MacAddress::from!("00:90:7f:df:2c:23"),
|
||||
|
||||
impl MacAddress {
|
||||
pub fn dummy() -> Self {
|
||||
Self([0, 0, 0, 0, 0, 0])
|
||||
@ -119,8 +127,3 @@ pub struct DnsRecord {
|
||||
pub record_type: DnsRecordType,
|
||||
pub value: String,
|
||||
}
|
||||
|
||||
// Error types
|
||||
pub struct FirewallError;
|
||||
pub struct DhcpError;
|
||||
pub struct DnsError;
|
||||
|
43
harmony-rs/harmony/src/infra/opnsense/config.rs
Normal file
43
harmony-rs/harmony/src/infra/opnsense/config.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use crate::executors::ExecutorError;
|
||||
|
||||
pub struct OPNSenseXmlConfigEditor;
|
||||
|
||||
impl OPNSenseXmlConfigEditor {
|
||||
pub(crate) async fn add(vec: Vec<&str>, xml_entry: &str) -> Result<(), ExecutorError>{
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use minidom::Element;
|
||||
use std::fs;
|
||||
use std::io::{BufReader, Read};
|
||||
use std::process::Command;
|
||||
|
||||
// #[test]
|
||||
// fn should_not_alter_config() {
|
||||
// let path = "./private_repos/affilium_mcd/private/config.xml";
|
||||
// // TODO
|
||||
// // Load file to string
|
||||
// // Parse with minidom (ex: `let root: Element = file_str.parse().unwrap()`)
|
||||
// // save file with suffix name
|
||||
// // Verify that file is still identical with md5sum after removing indentation
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn should_not_alter_config() {
|
||||
let path = "/home/jeangab/work/nationtech/harmony/harmony-rs/affilium_mcd/private/config.xml";
|
||||
let output_path = format!("{}.test_output", path);
|
||||
|
||||
// Load file to string
|
||||
let file_str = fs::read_to_string(path).expect("Failed to read file");
|
||||
|
||||
// Parse with minidom
|
||||
let root: Element = file_str.parse().unwrap();
|
||||
|
||||
// Save file with suffix name
|
||||
fs::write(&output_path, String::from(&root)).expect("Failed to write output file");
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +1,20 @@
|
||||
mod management;
|
||||
mod config;
|
||||
use async_trait::async_trait;
|
||||
pub use management::*;
|
||||
|
||||
use crate::{executors::ExecutorError, topology::{
|
||||
Backend, DHCPStaticEntry, DhcpServer, DnsServer, Firewall, FirewallError, FirewallRule,
|
||||
Frontend, IpAddress, LoadBalancer, LoadBalancerError, LogicalHost,
|
||||
}};
|
||||
use crate::{
|
||||
executors::ExecutorError, infra::opnsense::config::OPNSenseXmlConfigEditor, topology::{
|
||||
Backend, DHCPStaticEntry, DhcpServer, DnsServer, Firewall, FirewallRule, Frontend,
|
||||
IpAddress, LoadBalancer, LogicalHost,
|
||||
}
|
||||
};
|
||||
use derive_new::new;
|
||||
|
||||
#[derive(new, Clone)]
|
||||
pub struct OPNSenseFirewall {
|
||||
host: LogicalHost,
|
||||
cluster_nic_name: String,
|
||||
}
|
||||
|
||||
impl OPNSenseFirewall {
|
||||
@ -17,17 +22,14 @@ impl OPNSenseFirewall {
|
||||
self.host.ip
|
||||
}
|
||||
|
||||
fn save_xml_to_config(&self, xml_entry: &str) -> Result<(), ExecutorError> {
|
||||
todo!("Save XML Entry in opnsense /conf/config.xml {xml_entry}");
|
||||
}
|
||||
}
|
||||
|
||||
impl Firewall for OPNSenseFirewall {
|
||||
fn add_rule(&mut self, _rule: FirewallRule) -> Result<(), FirewallError> {
|
||||
fn add_rule(&mut self, _rule: FirewallRule) -> Result<(), ExecutorError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn remove_rule(&mut self, _rule_id: &str) -> Result<(), FirewallError> {
|
||||
fn remove_rule(&mut self, _rule_id: &str) -> Result<(), ExecutorError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@ -44,19 +46,19 @@ impl Firewall for OPNSenseFirewall {
|
||||
}
|
||||
|
||||
impl LoadBalancer for OPNSenseFirewall {
|
||||
fn add_backend(&mut self, _backend: Backend) -> Result<(), LoadBalancerError> {
|
||||
fn add_backend(&mut self, _backend: Backend) -> Result<(), ExecutorError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn remove_backend(&mut self, _backend_id: &str) -> Result<(), LoadBalancerError> {
|
||||
fn remove_backend(&mut self, _backend_id: &str) -> Result<(), ExecutorError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn add_frontend(&mut self, _frontend: Frontend) -> Result<(), LoadBalancerError> {
|
||||
fn add_frontend(&mut self, _frontend: Frontend) -> Result<(), ExecutorError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn remove_frontend(&mut self, _frontend_id: &str) -> Result<(), LoadBalancerError> {
|
||||
fn remove_frontend(&mut self, _frontend_id: &str) -> Result<(), ExecutorError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@ -76,36 +78,40 @@ impl LoadBalancer for OPNSenseFirewall {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl DhcpServer for OPNSenseFirewall {
|
||||
fn add_static_mapping(
|
||||
&self,
|
||||
entry: &DHCPStaticEntry,
|
||||
) -> Result<(), crate::topology::DhcpError> {
|
||||
async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError> {
|
||||
let mac = &entry.mac;
|
||||
let name = &entry.name;
|
||||
let ip = &entry.ip;
|
||||
|
||||
let xml_entry = format!("<staticmap>
|
||||
> <mac>{mac}</mac>
|
||||
> <ipaddr>{ip}</ipaddr>
|
||||
> <hostname>{name}</hostname>
|
||||
> <descr>{name}</descr>
|
||||
> <winsserver/>
|
||||
> <dnsserver/>
|
||||
> <ntpserver/>
|
||||
> </staticmap>");
|
||||
self.save_xml_to_config(&xml_entry)?;
|
||||
let xml_entry = format!(
|
||||
"<staticmap>
|
||||
<mac>{mac}</mac>
|
||||
<ipaddr>{ip}</ipaddr>
|
||||
<hostname>{name}</hostname>
|
||||
<descr>{name}</descr>
|
||||
<winsserver/>
|
||||
<dnsserver/>
|
||||
<ntpserver/>
|
||||
</staticmap>"
|
||||
);
|
||||
// XML Path : <opnsense><dhcpd><DHCPD_INTERFACE_NAME><staticmap>
|
||||
OPNSenseXmlConfigEditor::add(
|
||||
vec!["opnsense", "dhcpd", &self.cluster_nic_name, "staticmap"],
|
||||
&xml_entry,
|
||||
).await?;
|
||||
todo!("Register {:?}", entry)
|
||||
}
|
||||
|
||||
fn remove_static_mapping(
|
||||
async fn remove_static_mapping(
|
||||
&self,
|
||||
_mac: &crate::topology::MacAddress,
|
||||
) -> Result<(), crate::topology::DhcpError> {
|
||||
) -> Result<(), ExecutorError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn list_static_mappings(&self) -> Vec<(crate::topology::MacAddress, IpAddress)> {
|
||||
async fn list_static_mappings(&self) -> Vec<(crate::topology::MacAddress, IpAddress)> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@ -122,7 +128,7 @@ impl DnsServer for OPNSenseFirewall {
|
||||
_name: &str,
|
||||
_record_type: crate::topology::DnsRecordType,
|
||||
_value: &str,
|
||||
) -> Result<(), crate::topology::DnsError> {
|
||||
) -> Result<(), ExecutorError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
@ -130,7 +136,7 @@ impl DnsServer for OPNSenseFirewall {
|
||||
&mut self,
|
||||
_name: &str,
|
||||
_record_type: crate::topology::DnsRecordType,
|
||||
) -> Result<(), crate::topology::DnsError> {
|
||||
) -> Result<(), ExecutorError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use derive_new::new;
|
||||
use log::info;
|
||||
@ -7,7 +9,6 @@ use crate::{
|
||||
data::{Id, Version},
|
||||
interpret::InterpretStatus,
|
||||
},
|
||||
infra::executors::russh::RusshClient,
|
||||
interpret::{Interpret, InterpretError, InterpretName, Outcome},
|
||||
inventory::Inventory,
|
||||
topology::{DHCPStaticEntry, HAClusterTopology, HostBinding},
|
||||
@ -114,9 +115,8 @@ impl Interpret for DhcpInterpret {
|
||||
topology: &HAClusterTopology,
|
||||
) -> Result<Outcome, InterpretError> {
|
||||
info!("Executing {} on inventory {inventory:?}", self.get_name());
|
||||
let ssh_client = RusshClient {};
|
||||
|
||||
let entries: Vec<DHCPStaticEntry> = self
|
||||
let dhcp_entries: Vec<DHCPStaticEntry> = self
|
||||
.score
|
||||
.host_binding
|
||||
.iter()
|
||||
@ -126,16 +126,16 @@ impl Interpret for DhcpInterpret {
|
||||
ip: binding.logical_host.ip,
|
||||
})
|
||||
.collect();
|
||||
info!("DHCPStaticEntry : {:?}", entries);
|
||||
info!("DHCPStaticEntry : {:?}", dhcp_entries);
|
||||
|
||||
let dhcp = topology.dhcp_server.clone();
|
||||
let dhcp = Arc::new(Box::new(topology.dhcp_server.clone()));
|
||||
info!("DHCP server : {:?}", dhcp);
|
||||
entries.iter().for_each(|entry| {
|
||||
match dhcp.add_static_mapping(&entry) {
|
||||
for entry in dhcp_entries.into_iter() {
|
||||
match dhcp.add_static_mapping(&entry).await {
|
||||
Ok(_) => info!("Successfully registered DHCPStaticEntry {}", entry),
|
||||
Err(_) => todo!(),
|
||||
}
|
||||
});
|
||||
}
|
||||
todo!("Configure DHCPServer");
|
||||
|
||||
Ok(Outcome::new(
|
||||
|
Loading…
Reference in New Issue
Block a user