forked from NationTech/harmony
		
	feat: Add OKD DNS score with DNS entries and registering dhcp leases
This commit is contained in:
		
							parent
							
								
									b098757683
								
							
						
					
					
						commit
						367e96b36a
					
				
							
								
								
									
										24
									
								
								harmony-rs/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										24
									
								
								harmony-rs/Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -896,6 +896,7 @@ dependencies = [ | ||||
|  "libredfish", | ||||
|  "log", | ||||
|  "opnsense-config", | ||||
|  "opnsense-config-xml", | ||||
|  "reqwest", | ||||
|  "russh", | ||||
|  "rust-ipmi", | ||||
| @ -1406,6 +1407,7 @@ dependencies = [ | ||||
|  "serde", | ||||
|  "thiserror", | ||||
|  "tokio", | ||||
|  "uuid", | ||||
|  "xml-rs", | ||||
|  "yaserde", | ||||
|  "yaserde_derive", | ||||
| @ -2502,6 +2504,28 @@ version = "0.2.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "uuid" | ||||
| version = "1.11.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" | ||||
| dependencies = [ | ||||
|  "getrandom", | ||||
|  "rand", | ||||
|  "uuid-macro-internal", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "uuid-macro-internal" | ||||
| version = "1.11.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| checksum = "6b91f57fe13a38d0ce9e28a03463d8d3c2468ed03d75375110ec71d93b449a08" | ||||
| dependencies = [ | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn 2.0.90", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "vcpkg" | ||||
| version = "0.2.15" | ||||
|  | ||||
| @ -18,3 +18,4 @@ env_logger = { workspace = true } | ||||
| async-trait = { workspace = true } | ||||
| cidr = { workspace = true } | ||||
| opnsense-config = { path = "../opnsense-config" } | ||||
| opnsense-config-xml = { path = "../opnsense-config-xml" } | ||||
|  | ||||
| @ -11,6 +11,7 @@ use std::{net::IpAddr, sync::Arc}; | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct HAClusterTopology { | ||||
|     pub domain_name: String, | ||||
|     pub router: Arc<dyn Router>, | ||||
|     pub load_balancer: Arc<dyn LoadBalancer>, | ||||
|     pub firewall: Arc<dyn Firewall>, | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| use std::net::Ipv4Addr; | ||||
| use std::{error::Error, net::Ipv4Addr, str::FromStr}; | ||||
| 
 | ||||
| use async_trait::async_trait; | ||||
| 
 | ||||
| @ -60,21 +60,32 @@ impl std::fmt::Debug for dyn DhcpServer { | ||||
| #[async_trait] | ||||
| pub trait DnsServer: Send + Sync { | ||||
|     async fn register_dhcp_leases(&self, register: bool) -> Result<(), ExecutorError>; | ||||
|     fn add_record( | ||||
|         &mut self, | ||||
|         name: &str, | ||||
|         record_type: DnsRecordType, | ||||
|         value: &str, | ||||
|     ) -> Result<(), ExecutorError>; | ||||
|     async fn register_hosts(&self, hosts: Vec<DnsRecord>) -> Result<(), ExecutorError>; | ||||
|     fn remove_record( | ||||
|         &mut self, | ||||
|         name: &str, | ||||
|         record_type: DnsRecordType, | ||||
|     ) -> Result<(), ExecutorError>; | ||||
|     fn list_records(&self) -> Vec<DnsRecord>; | ||||
|     async fn list_records(&self) -> Vec<DnsRecord>; | ||||
|     fn get_ip(&self) -> IpAddress; | ||||
|     fn get_host(&self) -> LogicalHost; | ||||
|     async fn commit_config(&self) -> Result<(), ExecutorError>; | ||||
|     async fn ensure_hosts_registered(&self, hosts: Vec<DnsRecord>) -> Result<(), ExecutorError> { | ||||
|         let current_hosts = self.list_records().await; | ||||
|         let mut hosts_to_register = vec![]; | ||||
| 
 | ||||
|         for host in hosts { | ||||
|             if !current_hosts.iter().any(|h| h == &host) { | ||||
|                 hosts_to_register.push(host); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if !hosts_to_register.is_empty() { | ||||
|             self.register_hosts(hosts_to_register).await?; | ||||
|         } | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl std::fmt::Debug for dyn DnsServer { | ||||
| @ -136,7 +147,7 @@ impl std::fmt::Display for MacAddress { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| #[derive(Clone, Debug, PartialEq, Eq)] | ||||
| pub enum DnsRecordType { | ||||
|     A, | ||||
|     AAAA, | ||||
| @ -145,9 +156,170 @@ pub enum DnsRecordType { | ||||
|     TXT, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct DnsRecord { | ||||
|     pub name: String, | ||||
|     pub record_type: DnsRecordType, | ||||
|     pub value: String, | ||||
| impl std::fmt::Display for DnsRecordType { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||||
|         match self { | ||||
|             DnsRecordType::A => write!(f, "A"), | ||||
|             DnsRecordType::AAAA => write!(f, "AAAA"), | ||||
|             DnsRecordType::CNAME => write!(f, "CNAME"), | ||||
|             DnsRecordType::MX => write!(f, "MX"), | ||||
|             DnsRecordType::TXT => write!(f, "TXT"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq, Eq)] | ||||
| pub struct DnsRecord { | ||||
|     pub host: String, | ||||
|     pub domain: String, | ||||
|     pub record_type: DnsRecordType, | ||||
|     pub value: IpAddress, | ||||
| } | ||||
| 
 | ||||
| impl FromStr for DnsRecordType { | ||||
|     type Err = String; | ||||
| 
 | ||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { | ||||
|         match s { | ||||
|             "A" => Ok(DnsRecordType::A), | ||||
|             "AAAA" => Ok(DnsRecordType::AAAA), | ||||
|             "CNAME" => Ok(DnsRecordType::CNAME), | ||||
|             "MX" => Ok(DnsRecordType::MX), | ||||
|             "TXT" => Ok(DnsRecordType::TXT), | ||||
|             _ => Err(format!("Unknown DNSRecordType {s}")), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod test { | ||||
|     use std::sync::Arc; | ||||
| 
 | ||||
|     use tokio::sync::RwLock; | ||||
| 
 | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[tokio::test] | ||||
|     async fn test_ensure_hosts_registered_no_new_hosts() { | ||||
|         let server = DummyDnsServer::default(); | ||||
|         let existing_host = DnsRecord { | ||||
|             host: "existing".to_string(), | ||||
|             domain: "example.com".to_string(), | ||||
|             record_type: DnsRecordType::A, | ||||
|             value: IpAddress::V4(Ipv4Addr::new(192, 168, 1, 2)), | ||||
|         }; | ||||
| 
 | ||||
|         server | ||||
|             .register_hosts(vec![existing_host.clone()]) | ||||
|             .await | ||||
|             .unwrap(); | ||||
| 
 | ||||
|         let new_hosts = vec![ | ||||
|             existing_host, // already exists
 | ||||
|         ]; | ||||
| 
 | ||||
|         server.ensure_hosts_registered(new_hosts).await.unwrap(); | ||||
|         assert_eq!(server.list_records().await.len(), 1); | ||||
|     } | ||||
| 
 | ||||
|     #[tokio::test] | ||||
|     async fn test_ensure_hosts_registered_with_new_hosts() { | ||||
|         let server = DummyDnsServer::default(); | ||||
| 
 | ||||
|         let existing_host = DnsRecord { | ||||
|             host: "existing".to_string(), | ||||
|             domain: "example.com".to_string(), | ||||
|             record_type: DnsRecordType::A, | ||||
|             value: IpAddress::V4(Ipv4Addr::new(192, 168, 1, 2)), | ||||
|         }; | ||||
| 
 | ||||
|         server | ||||
|             .register_hosts(vec![existing_host.clone()]) | ||||
|             .await | ||||
|             .unwrap(); | ||||
| 
 | ||||
|         let new_hosts = vec![ | ||||
|             existing_host.clone(), // already exists
 | ||||
|             DnsRecord { | ||||
|                 host: "new".to_string(), | ||||
|                 domain: "example.com".to_string(), | ||||
|                 record_type: DnsRecordType::A, | ||||
|                 value: IpAddress::V4(Ipv4Addr::new(192, 168, 1, 3)), | ||||
|             }, | ||||
|         ]; | ||||
| 
 | ||||
|         server.ensure_hosts_registered(new_hosts).await.unwrap(); | ||||
|         assert_eq!(server.list_records().await.len(), 2); | ||||
|     } | ||||
| 
 | ||||
|     #[tokio::test] | ||||
|     async fn test_ensure_hosts_registered_no_hosts() { | ||||
|         let server = DummyDnsServer::default(); | ||||
| 
 | ||||
|         let new_hosts = vec![]; | ||||
| 
 | ||||
|         server.ensure_hosts_registered(new_hosts).await.unwrap(); | ||||
|         assert_eq!(server.list_records().await.len(), 0); | ||||
|     } | ||||
| 
 | ||||
|     #[tokio::test] | ||||
|     async fn test_ensure_existing_host_kept_no_new_host() { | ||||
|         let server = DummyDnsServer::default(); | ||||
| 
 | ||||
|         let new_hosts = vec![]; | ||||
| 
 | ||||
|         server.ensure_hosts_registered(new_hosts).await.unwrap(); | ||||
|         assert_eq!(server.list_records().await.len(), 0); | ||||
|     } | ||||
| 
 | ||||
|     #[async_trait::async_trait] | ||||
|     impl DnsServer for DummyDnsServer { | ||||
|         async fn register_dhcp_leases(&self, _register: bool) -> Result<(), ExecutorError> { | ||||
|             Ok(()) | ||||
|         } | ||||
| 
 | ||||
|         async fn register_hosts(&self, hosts: Vec<DnsRecord>) -> Result<(), ExecutorError> { | ||||
|             self.hosts.write().await.extend(hosts); | ||||
|             Ok(()) | ||||
|         } | ||||
| 
 | ||||
|         fn remove_record( | ||||
|             &mut self, | ||||
|             _name: &str, | ||||
|             _record_type: DnsRecordType, | ||||
|         ) -> Result<(), ExecutorError> { | ||||
|             Ok(()) | ||||
|         } | ||||
| 
 | ||||
|         async fn list_records(&self) -> Vec<DnsRecord> { | ||||
|             self.hosts.read().await.clone() | ||||
|         } | ||||
| 
 | ||||
|         fn get_ip(&self) -> IpAddress { | ||||
|             IpAddress::V4(Ipv4Addr::new(192, 168, 0, 1)) | ||||
|         } | ||||
| 
 | ||||
|         fn get_host(&self) -> LogicalHost { | ||||
|             LogicalHost { | ||||
|                 ip: self.get_ip(), | ||||
|                 name: "dummy-host".to_string(), | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         async fn commit_config(&self) -> Result<(), ExecutorError> { | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     struct DummyDnsServer { | ||||
|         hosts: Arc<RwLock<Vec<DnsRecord>>>, | ||||
|     } | ||||
| 
 | ||||
|     impl Default for DummyDnsServer { | ||||
|         fn default() -> Self { | ||||
|             DummyDnsServer { | ||||
|                 hosts: Arc::new(RwLock::new(vec![])), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -4,13 +4,14 @@ use std::sync::Arc; | ||||
| use async_trait::async_trait; | ||||
| use log::debug; | ||||
| pub use management::*; | ||||
| use opnsense_config_xml::Host; | ||||
| use tokio::sync::RwLock; | ||||
| 
 | ||||
| use crate::{ | ||||
|     executors::ExecutorError, | ||||
|     topology::{ | ||||
|         Backend, DHCPStaticEntry, DhcpServer, DnsServer, Firewall, FirewallRule, Frontend, | ||||
|         IpAddress, LoadBalancer, LogicalHost, | ||||
|         Backend, DHCPStaticEntry, DhcpServer, DnsRecord, DnsServer, Firewall, FirewallRule, | ||||
|         Frontend, IpAddress, LoadBalancer, LogicalHost, | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| @ -158,13 +159,22 @@ impl DhcpServer for OPNSenseFirewall { | ||||
| 
 | ||||
| #[async_trait] | ||||
| impl DnsServer for OPNSenseFirewall { | ||||
|     fn add_record( | ||||
|         &mut self, | ||||
|         _name: &str, | ||||
|         _record_type: crate::topology::DnsRecordType, | ||||
|         _value: &str, | ||||
|     ) -> Result<(), ExecutorError> { | ||||
|         todo!() | ||||
|     async fn register_hosts(&self, hosts: Vec<DnsRecord>) -> Result<(), ExecutorError> { | ||||
|         let mut writable_opnsense = self.opnsense_config.write().await; | ||||
|         let mut dns = writable_opnsense.dns(); | ||||
|         let hosts = hosts | ||||
|             .iter() | ||||
|             .map(|h| { | ||||
|                 Host::new( | ||||
|                     h.host.clone(), | ||||
|                     h.domain.clone(), | ||||
|                     h.record_type.to_string(), | ||||
|                     h.value.to_string(), | ||||
|                 ) | ||||
|             }) | ||||
|             .collect(); | ||||
|         dns.register_hosts(hosts); | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     fn remove_record( | ||||
| @ -175,8 +185,26 @@ impl DnsServer for OPNSenseFirewall { | ||||
|         todo!() | ||||
|     } | ||||
| 
 | ||||
|     fn list_records(&self) -> Vec<crate::topology::DnsRecord> { | ||||
|         todo!() | ||||
|     async fn list_records(&self) -> Vec<crate::topology::DnsRecord> { | ||||
|         self.opnsense_config | ||||
|             .write() | ||||
|             .await | ||||
|             .dns() | ||||
|             .get_hosts() | ||||
|             .iter() | ||||
|             .map(|h| DnsRecord { | ||||
|                 host: h.hostname.clone(), | ||||
|                 domain: h.domain.clone(), | ||||
|                 record_type: h | ||||
|                     .rr | ||||
|                     .parse() | ||||
|                     .expect("received invalid record type {h.rr} from opnsense"), | ||||
|                 value: h | ||||
|                     .server | ||||
|                     .parse() | ||||
|                     .expect("received invalid ipv4 record from opnsense {h.server}"), | ||||
|             }) | ||||
|             .collect() | ||||
|     } | ||||
| 
 | ||||
|     fn get_ip(&self) -> IpAddress { | ||||
|  | ||||
| @ -7,11 +7,12 @@ use crate::{ | ||||
|     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, | ||||
|     inventory::Inventory, | ||||
|     score::Score, | ||||
|     topology::HAClusterTopology, | ||||
|     topology::{DnsRecord, HAClusterTopology}, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Debug, new, Clone)] | ||||
| pub struct DnsScore { | ||||
|     dns_entries: Vec<DnsRecord>, | ||||
|     register_dhcp_leases: Option<bool>, | ||||
| } | ||||
| 
 | ||||
| @ -62,6 +63,25 @@ impl DnsInterpret { | ||||
|             "DNS Interpret execution successfull".to_string(), | ||||
|         )) | ||||
|     } | ||||
| 
 | ||||
|     async fn ensure_hosts_registered( | ||||
|         &self, | ||||
|         topology: &HAClusterTopology, | ||||
|     ) -> Result<Outcome, InterpretError> { | ||||
|         let entries = &self.score.dns_entries; | ||||
|         topology | ||||
|             .dns_server | ||||
|             .ensure_hosts_registered(entries.clone()) | ||||
|             .await?; | ||||
| 
 | ||||
|         Ok(Outcome::new( | ||||
|             InterpretStatus::SUCCESS, | ||||
|             format!( | ||||
|                 "DnsInterpret registered {} hosts successfully", | ||||
|                 entries.len() | ||||
|             ), | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[async_trait] | ||||
| @ -90,6 +110,7 @@ impl Interpret for DnsInterpret { | ||||
|         info!("Executing {} on inventory {inventory:?}", self.get_name()); | ||||
| 
 | ||||
|         self.serve_dhcp_entries(inventory, topology).await?; | ||||
|         self.ensure_hosts_registered(&topology).await?; | ||||
| 
 | ||||
|         topology.dns_server.commit_config().await?; | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										48
									
								
								harmony-rs/harmony/src/modules/okd/dns.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								harmony-rs/harmony/src/modules/okd/dns.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | ||||
| use crate::{ | ||||
|     modules::dns::DnsScore, | ||||
|     score::Score, | ||||
|     topology::{DnsRecord, DnsRecordType, HAClusterTopology}, | ||||
| }; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct OKDBootstrapDnsScore { | ||||
|     dns_score: DnsScore, | ||||
| } | ||||
| 
 | ||||
| impl OKDBootstrapDnsScore { | ||||
|     pub fn new(topology: &HAClusterTopology) -> Self { | ||||
|         let cluster_domain_name = &topology.domain_name; | ||||
|         let dns_entries = vec![ | ||||
|             DnsRecord { | ||||
|                 host: "api".to_string(), | ||||
|                 domain: cluster_domain_name.clone(), | ||||
|                 record_type: DnsRecordType::A, | ||||
|                 value: topology.dns_server.get_ip(), | ||||
|             }, | ||||
|             DnsRecord { | ||||
|                 host: "api-int".to_string(), | ||||
|                 domain: cluster_domain_name.clone(), | ||||
|                 record_type: DnsRecordType::A, | ||||
|                 value: topology.dns_server.get_ip(), | ||||
|             }, | ||||
|             DnsRecord { | ||||
|                 host: "*".to_string(), | ||||
|                 domain: format!("apps.{}", cluster_domain_name), | ||||
|                 record_type: DnsRecordType::A, | ||||
|                 value: topology.dns_server.get_ip(), | ||||
|             }, | ||||
|         ]; | ||||
| 
 | ||||
|         Self { | ||||
|             dns_score: DnsScore::new(dns_entries, Some(true)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Score for OKDBootstrapDnsScore { | ||||
|     type InterpretType = <DnsScore as Score>::InterpretType; | ||||
| 
 | ||||
|     fn create_interpret(self) -> Self::InterpretType { | ||||
|         self.dns_score.create_interpret() | ||||
|     } | ||||
| } | ||||
| @ -1,2 +1,3 @@ | ||||
| pub mod dhcp; | ||||
| pub mod dns; | ||||
| 
 | ||||
|  | ||||
| @ -18,6 +18,14 @@ thiserror = "1.0" | ||||
| async-trait = { workspace = true } | ||||
| tokio = { workspace = true } | ||||
| 
 | ||||
| [dependencies.uuid] | ||||
| version = "1.11.0" | ||||
| features = [ | ||||
|     "v4",                # Lets you generate random UUIDs | ||||
|     "fast-rng",          # Use a faster (but still sufficiently random) RNG | ||||
|     "macro-diagnostics", # Enable better diagnostics for compile-time UUIDs | ||||
| ] | ||||
| 
 | ||||
| [dev-dependencies] | ||||
| pretty_assertions = "1.4.1" | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| use crate::{data::dhcpd::DhcpInterface, xml_utils::to_xml_str}; | ||||
| use log::error; | ||||
| use uuid::Uuid; | ||||
| use yaserde::{MaybeString, NamedList, RawXml}; | ||||
| use yaserde_derive::{YaDeserialize, YaSerialize}; | ||||
| 
 | ||||
| @ -1167,18 +1168,34 @@ pub struct Hosts { | ||||
|     pub hosts: Vec<Host>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] | ||||
| #[derive(Default, Clone, PartialEq, Debug, YaSerialize, YaDeserialize)] | ||||
| pub struct Host { | ||||
|     #[yaserde(attribute)] | ||||
|     pub uuid: String, | ||||
|     pub enabled: i32, | ||||
|     pub enabled: i8, | ||||
|     pub hostname: String, | ||||
|     pub domain: String, | ||||
|     pub rr: String, | ||||
|     pub mxprio: MaybeString, | ||||
|     pub mx: MaybeString, | ||||
|     pub server: String, | ||||
|     pub description: String, | ||||
|     pub description: Option<String>, | ||||
| } | ||||
| 
 | ||||
| impl Host { | ||||
|     pub fn new(hostname: String, domain: String, rr: String, server: String) -> Self { | ||||
|         Host { | ||||
|             uuid: Uuid::new_v4().to_string(), | ||||
|             enabled: true as i8, | ||||
|             hostname, | ||||
|             domain, | ||||
|             rr, | ||||
|             server, | ||||
|             mxprio: MaybeString::default(), | ||||
|             mx: MaybeString::default(), | ||||
|             description: None | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] | ||||
| @ -1468,7 +1485,6 @@ pub struct Tuning { | ||||
|     pub h2_max_concurrent_streams_outgoing: Option<MaybeString>, | ||||
|     #[yaserde(rename = "h2_maxConcurrentStreamsIncoming")] | ||||
|     pub h2_max_concurrent_streams_incoming: Option<MaybeString>, | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| use std::sync::Arc; | ||||
| 
 | ||||
| use opnsense_config_xml::OPNsense; | ||||
| use opnsense_config_xml::{Host, OPNsense}; | ||||
| 
 | ||||
| use crate::config::OPNsenseShell; | ||||
| 
 | ||||
| @ -17,6 +17,22 @@ impl<'a> DnsConfig<'a> { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn register_hosts(&mut self, mut hosts: Vec<Host>) { | ||||
|         let unbound = match &mut self.opnsense.opnsense.unboundplus { | ||||
|             Some(unbound) => unbound, | ||||
|             None => todo!("Handle case where unboundplus is not used"), | ||||
|         }; | ||||
|         unbound.hosts.hosts.append(&mut hosts); | ||||
|     } | ||||
| 
 | ||||
|     pub fn get_hosts(&self) -> Vec<Host> { | ||||
|         let unbound = match &self.opnsense.opnsense.unboundplus { | ||||
|             Some(unbound) => unbound, | ||||
|             None => todo!("Handle case where unboundplus is not used"), | ||||
|         }; | ||||
|         unbound.hosts.hosts.clone() | ||||
|     } | ||||
| 
 | ||||
|     pub fn register_dhcp_leases(&mut self, register: bool) { | ||||
|         let unbound = match &mut self.opnsense.opnsense.unboundplus { | ||||
|             Some(unbound) => unbound, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user