Integrated opnsense-config API in harmony, pretty easy, pretty happy. very much encouraging
This commit is contained in:
parent
d30e909b83
commit
9c18c8be6d
35
harmony-rs/Cargo.lock
generated
35
harmony-rs/Cargo.lock
generated
@ -895,6 +895,7 @@ dependencies = [
|
||||
"env_logger",
|
||||
"libredfish",
|
||||
"log",
|
||||
"opnsense-config",
|
||||
"reqwest",
|
||||
"russh",
|
||||
"rust-ipmi",
|
||||
@ -902,7 +903,6 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"xml_dom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1627,15 +1627,6 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.36.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
@ -2430,21 +2421,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-attributes"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.77",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.32"
|
||||
@ -2822,18 +2801,6 @@ version = "0.8.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26"
|
||||
|
||||
[[package]]
|
||||
name = "xml_dom"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "836bd445caf6b9e969199f2a9667d58b433b286ddb515764303ab75a6d17e51f"
|
||||
dependencies = [
|
||||
"log",
|
||||
"quick-xml",
|
||||
"regex",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
|
||||
@ -18,6 +18,8 @@ derive-new = "0.7.0"
|
||||
async-trait = "0.1.82"
|
||||
tokio = { version = "1.40.0", features = ["io-std"] }
|
||||
cidr = "0.2.3"
|
||||
xml_dom = "0.2.8"
|
||||
russh = "0.45.0"
|
||||
russh-keys = "0.45.0"
|
||||
|
||||
#[workspace.target.x86_64-unknown-linux-gnu]
|
||||
#rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||
|
||||
@ -17,4 +17,4 @@ log = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
cidr = { workspace = true }
|
||||
xml_dom = { workspace = true }
|
||||
opnsense-config = { path = "../opnsense-config" }
|
||||
|
||||
@ -22,6 +22,7 @@ pub struct HAClusterTopology {
|
||||
}
|
||||
|
||||
pub type IpAddress = IpAddr;
|
||||
|
||||
/// Represents a logical member of a cluster that provides one or more services.
|
||||
///
|
||||
/// A LogicalHost can represent various roles within the infrastructure, such as:
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::executors::ExecutorError;
|
||||
@ -8,12 +10,15 @@ use super::{IpAddress, LogicalHost};
|
||||
pub struct DHCPStaticEntry {
|
||||
pub name: String,
|
||||
pub mac: MacAddress,
|
||||
pub ip: IpAddress,
|
||||
pub ip: Ipv4Addr,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DHCPStaticEntry {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_fmt(format_args!("DHCPStaticEntry : name {}, mac {}, ip {}", self.name, self.mac, self.ip))
|
||||
f.write_fmt(format_args!(
|
||||
"DHCPStaticEntry : name {}, mac {}, ip {}",
|
||||
self.name, self.mac, self.ip
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +62,11 @@ pub trait DnsServer: Send + Sync {
|
||||
record_type: DnsRecordType,
|
||||
value: &str,
|
||||
) -> Result<(), ExecutorError>;
|
||||
fn remove_record(&mut self, name: &str, record_type: DnsRecordType) -> 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;
|
||||
@ -98,11 +107,21 @@ pub struct MacAddress(pub [u8; 6]);
|
||||
// MacAddress::from!("00:90:7f:df:2c:23"),
|
||||
|
||||
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!(
|
||||
"{}:{}:{}:{}:{}:{}",
|
||||
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!(
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
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 std::fs;
|
||||
use std::io::{BufReader, Read};
|
||||
use std::process::Command;
|
||||
|
||||
use xml_dom::parser::read_xml;
|
||||
|
||||
// #[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 = read_xml(&file_str).unwrap();
|
||||
|
||||
assert_eq!(&root.to_string(), &file_str);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,18 +1,21 @@
|
||||
mod management;
|
||||
mod config;
|
||||
use std::sync::{Arc, Mutex, RwLock, RwLockWriteGuard};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use log::debug;
|
||||
pub use management::*;
|
||||
|
||||
use crate::{
|
||||
executors::ExecutorError, infra::opnsense::config::OPNSenseXmlConfigEditor, topology::{
|
||||
executors::ExecutorError,
|
||||
topology::{
|
||||
Backend, DHCPStaticEntry, DhcpServer, DnsServer, Firewall, FirewallRule, Frontend,
|
||||
IpAddress, LoadBalancer, LogicalHost,
|
||||
}
|
||||
},
|
||||
};
|
||||
use derive_new::new;
|
||||
|
||||
#[derive(new, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct OPNSenseFirewall {
|
||||
opnsense_config: Arc<RwLock<opnsense_config::Config>>,
|
||||
host: LogicalHost,
|
||||
cluster_nic_name: String,
|
||||
}
|
||||
@ -22,6 +25,20 @@ impl OPNSenseFirewall {
|
||||
self.host.ip
|
||||
}
|
||||
|
||||
pub async fn new(
|
||||
host: LogicalHost,
|
||||
cluster_nic_name: &str,
|
||||
username: &str,
|
||||
password: &str,
|
||||
) -> Self {
|
||||
Self {
|
||||
opnsense_config: Arc::new(RwLock::new(
|
||||
opnsense_config::Config::from_credentials(host.ip, username, password).await,
|
||||
)),
|
||||
host,
|
||||
cluster_nic_name: cluster_nic_name.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Firewall for OPNSenseFirewall {
|
||||
@ -81,27 +98,17 @@ impl LoadBalancer for OPNSenseFirewall {
|
||||
#[async_trait]
|
||||
impl DhcpServer for OPNSenseFirewall {
|
||||
async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError> {
|
||||
let mac = &entry.mac;
|
||||
let name = &entry.name;
|
||||
let ip = &entry.ip;
|
||||
let mac: String = String::from(&entry.mac);
|
||||
|
||||
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)
|
||||
{
|
||||
let mut writable_opnsense = self.opnsense_config.write().unwrap();
|
||||
writable_opnsense
|
||||
.dhcp()
|
||||
.add_static_mapping(&mac, entry.ip, &entry.name).unwrap();
|
||||
}
|
||||
|
||||
debug!("Registered {:?}", entry);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_static_mapping(
|
||||
@ -122,6 +129,7 @@ impl DhcpServer for OPNSenseFirewall {
|
||||
self.host.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl DnsServer for OPNSenseFirewall {
|
||||
fn add_record(
|
||||
&mut self,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use std::sync::Arc;
|
||||
use std::{net::Ipv4Addr, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use derive_new::new;
|
||||
@ -120,10 +120,17 @@ impl Interpret for DhcpInterpret {
|
||||
.score
|
||||
.host_binding
|
||||
.iter()
|
||||
.map(|binding| DHCPStaticEntry {
|
||||
name: binding.logical_host.name.clone(),
|
||||
mac: binding.physical_host.cluster_mac(),
|
||||
ip: binding.logical_host.ip,
|
||||
.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);
|
||||
@ -136,6 +143,7 @@ impl Interpret for DhcpInterpret {
|
||||
Err(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
todo!("Configure DHCPServer");
|
||||
|
||||
Ok(Outcome::new(
|
||||
|
||||
@ -20,3 +20,6 @@ tokio = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.1"
|
||||
|
||||
[target.x86_64-unknown-linux-gnu]
|
||||
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
use std::{net::Ipv4Addr, sync::Arc, time::Duration};
|
||||
|
||||
use crate::{error::Error, modules::dhcp::DhcpConfig};
|
||||
use crate::{config::{SshConfigManager, SshCredentials, SshOPNSenseShell}, error::Error, modules::dhcp::DhcpConfig};
|
||||
use log::trace;
|
||||
use opnsense_config_xml::OPNsense;
|
||||
use russh::client;
|
||||
|
||||
use super::{ConfigManager, OPNsenseShell};
|
||||
|
||||
@ -39,6 +40,27 @@ impl Config {
|
||||
.apply_new_config(&self.opnsense.to_xml())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn from_credentials(ipaddr: std::net::IpAddr, username: &str, password: &str) -> Self {
|
||||
let config = Arc::new(client::Config {
|
||||
inactivity_timeout: Some(Duration::from_secs(5)),
|
||||
..<_>::default()
|
||||
});
|
||||
|
||||
let credentials = SshCredentials::Password {
|
||||
username: String::from(username),
|
||||
password: String::from(password),
|
||||
};
|
||||
|
||||
let shell = Arc::new(SshOPNSenseShell::new(
|
||||
(ipaddr, 22),
|
||||
credentials,
|
||||
config,
|
||||
));
|
||||
let manager = Arc::new(SshConfigManager::new(shell.clone()));
|
||||
|
||||
Config::new(manager, shell).await.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -7,7 +7,7 @@ pub use local_file::*;
|
||||
use crate::Error;
|
||||
|
||||
#[async_trait]
|
||||
pub trait ConfigManager: std::fmt::Debug {
|
||||
pub trait ConfigManager: std::fmt::Debug + Send + Sync {
|
||||
async fn load_as_str(&self) -> Result<String, Error>;
|
||||
async fn apply_new_config(&self, content: &str) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
net::Ipv4Addr,
|
||||
net::IpAddr,
|
||||
sync::Arc,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
@ -20,7 +20,7 @@ use super::OPNsenseShell;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SshOPNSenseShell {
|
||||
host: (Ipv4Addr, u16),
|
||||
host: (IpAddr, u16),
|
||||
credentials: SshCredentials,
|
||||
ssh_config: Arc<Config>,
|
||||
}
|
||||
@ -78,11 +78,7 @@ impl SshOPNSenseShell {
|
||||
wait_for_completion(&mut channel).await
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
host: (Ipv4Addr, u16),
|
||||
credentials: SshCredentials,
|
||||
ssh_config: Arc<Config>,
|
||||
) -> Self {
|
||||
pub fn new(host: (IpAddr, u16), credentials: SshCredentials, ssh_config: Arc<Config>) -> Self {
|
||||
Self {
|
||||
host,
|
||||
credentials,
|
||||
|
||||
@ -6,15 +6,10 @@ pub use config::Config;
|
||||
pub use error::Error;
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use config::SshConfigManager;
|
||||
use opnsense_config_xml::StaticMap;
|
||||
use russh::client;
|
||||
use std::{net::Ipv4Addr, sync::Arc, time::Duration};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::{
|
||||
config::{self, SshCredentials, SshOPNSenseShell},
|
||||
Config,
|
||||
};
|
||||
use crate::Config;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[tokio::test]
|
||||
@ -38,23 +33,7 @@ mod test {
|
||||
}
|
||||
|
||||
async fn initialize_config() -> Config {
|
||||
let config = Arc::new(client::Config {
|
||||
inactivity_timeout: Some(Duration::from_secs(5)),
|
||||
..<_>::default()
|
||||
});
|
||||
|
||||
let credentials = SshCredentials::Password {
|
||||
username: String::from("root"),
|
||||
password: String::from("opnsense"),
|
||||
};
|
||||
|
||||
let shell = Arc::new(SshOPNSenseShell::new(
|
||||
(Ipv4Addr::new(192, 168, 5, 229), 22),
|
||||
credentials,
|
||||
config,
|
||||
));
|
||||
let manager = Arc::new(SshConfigManager::new(shell.clone()));
|
||||
Config::new(manager, shell).await.unwrap()
|
||||
Config::from_credentials(Ipv4Addr::new(192, 168, 5, 229), "root", "opnsense").await
|
||||
}
|
||||
|
||||
async fn get_static_mappings() -> Vec<StaticMap> {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user