diff --git a/harmony-rs/Cargo.lock b/harmony-rs/Cargo.lock index a0a529d..22e902d 100644 --- a/harmony-rs/Cargo.lock +++ b/harmony-rs/Cargo.lock @@ -59,14 +59,16 @@ dependencies = [ ] [[package]] -name = "affilium" -version = "0.1.0" +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ - "cidr", - "env_logger", - "harmony", - "log", - "tokio", + "cfg-if", + "const-random", + "once_cell", + "version_check", + "zerocopy", ] [[package]] @@ -78,6 +80,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.15" @@ -135,7 +152,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -199,6 +216,9 @@ name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] [[package]] name = "bitvec" @@ -254,9 +274,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "cbc" @@ -293,6 +313,20 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.6", +] + [[package]] name = "cidr" version = "0.2.3" @@ -321,6 +355,26 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -355,6 +409,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -411,7 +471,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -439,7 +499,7 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -451,6 +511,12 @@ dependencies = [ "cipher", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.10.7" @@ -603,6 +669,18 @@ dependencies = [ "miniz_oxide 0.8.0", ] +[[package]] +name = "flurry" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf5efcf77a4da27927d3ab0509dec5b0954bb3bc59da5a1de9e52642ebd4cdf9" +dependencies = [ + "ahash", + "num_cpus", + "parking_lot", + "seize", +] + [[package]] name = "fnv" version = "1.0.7" @@ -706,7 +784,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -817,6 +895,7 @@ dependencies = [ "env_logger", "libredfish", "log", + "opnsense-config", "reqwest", "russh", "rust-ipmi", @@ -832,6 +911,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -948,6 +1033,29 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "idna" version = "0.5.0" @@ -1045,6 +1153,16 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.22" @@ -1174,6 +1292,16 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.36.4" @@ -1218,7 +1346,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -1239,6 +1367,41 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "opnsense-config" +version = "0.1.0" +dependencies = [ + "async-trait", + "chrono", + "env_logger", + "log", + "opnsense-config-xml", + "pretty_assertions", + "russh", + "russh-keys", + "russh-sftp", + "serde", + "serde_json", + "thiserror", + "tokio", +] + +[[package]] +name = "opnsense-config-xml" +version = "0.1.0" +dependencies = [ + "async-trait", + "env_logger", + "log", + "pretty_assertions", + "serde", + "thiserror", + "tokio", + "xml-rs", + "yaserde", + "yaserde_derive", +] + [[package]] name = "p256" version = "0.13.2" @@ -1277,6 +1440,29 @@ dependencies = [ "sha2", ] +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + [[package]] name = "password-hash" version = "0.4.2" @@ -1413,6 +1599,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -1476,6 +1672,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "regex" version = "1.10.6" @@ -1680,6 +1885,24 @@ dependencies = [ "zeroize", ] +[[package]] +name = "russh-sftp" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a72c8afe2041c17435eecd85d0b7291841486fd3d1c4082e0b212e5437ca42" +dependencies = [ + "async-trait", + "bitflags 2.6.0", + "bytes", + "chrono", + "flurry", + "log", + "serde", + "thiserror", + "tokio", + "tokio-util", +] + [[package]] name = "rust-ipmi" version = "0.1.1" @@ -1756,6 +1979,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "scrypt" version = "0.11.0" @@ -1804,6 +2033,12 @@ dependencies = [ "libc", ] +[[package]] +name = "seize" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "689224d06523904ebcc9b482c6a3f4f7fb396096645c4cd10c0d2ff7371a34d3" + [[package]] name = "semver" version = "1.0.23" @@ -1827,14 +2062,14 @@ checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "itoa", "memchr", @@ -1999,6 +2234,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.77" @@ -2073,7 +2319,16 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", ] [[package]] @@ -2116,7 +2371,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] @@ -2287,7 +2542,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.77", "wasm-bindgen-shared", ] @@ -2321,7 +2576,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2364,6 +2619,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -2531,6 +2795,38 @@ dependencies = [ "tap", ] +[[package]] +name = "xml-rs" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4e2e2f7cba5a093896c1e150fbfe177d1883e7448200efb81d40b9d339ef26" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yaserde" +version = "0.11.1" +dependencies = [ + "log", + "xml-rs", +] + +[[package]] +name = "yaserde_derive" +version = "0.11.1" +dependencies = [ + "heck", + "log", + "proc-macro2", + "quote", + "syn 1.0.109", + "xml-rs", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -2549,7 +2845,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", ] [[package]] diff --git a/harmony-rs/Cargo.toml b/harmony-rs/Cargo.toml index 9e5cc19..20aba71 100644 --- a/harmony-rs/Cargo.toml +++ b/harmony-rs/Cargo.toml @@ -3,6 +3,7 @@ resolver = "2" members = [ "private_repos/*", "harmony", + "opnsense-config", "opnsense-config-xml", ] [workspace.package] @@ -17,3 +18,8 @@ derive-new = "0.7.0" async-trait = "0.1.82" tokio = { version = "1.40.0", features = ["io-std"] } cidr = "0.2.3" +russh = "0.45.0" +russh-keys = "0.45.0" + +#[workspace.target.x86_64-unknown-linux-gnu] +#rustflags = ["-C", "link-arg=-fuse-ld=mold"] diff --git a/harmony-rs/harmony/Cargo.toml b/harmony-rs/harmony/Cargo.toml index 17b07da..8029cf1 100644 --- a/harmony-rs/harmony/Cargo.toml +++ b/harmony-rs/harmony/Cargo.toml @@ -17,3 +17,4 @@ log = { workspace = true } env_logger = { workspace = true } async-trait = { workspace = true } cidr = { workspace = true } +opnsense-config = { path = "../opnsense-config" } diff --git a/harmony-rs/harmony/src/domain/hardware/mod.rs b/harmony-rs/harmony/src/domain/hardware/mod.rs index 985e16e..8ec4f81 100644 --- a/harmony-rs/harmony/src/domain/hardware/mod.rs +++ b/harmony-rs/harmony/src/domain/hardware/mod.rs @@ -7,6 +7,7 @@ use crate::topology::MacAddress; pub type HostGroup = Vec; pub type SwitchGroup = Vec; pub type FirewallGroup = Vec; + #[derive(Debug, Clone)] pub struct PhysicalHost { pub category: HostCategory, diff --git a/harmony-rs/harmony/src/domain/inventory/mod.rs b/harmony-rs/harmony/src/domain/inventory/mod.rs index af4a00c..930ee02 100644 --- a/harmony-rs/harmony/src/domain/inventory/mod.rs +++ b/harmony-rs/harmony/src/domain/inventory/mod.rs @@ -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(), } } } diff --git a/harmony-rs/harmony/src/domain/topology/load_balancer.rs b/harmony-rs/harmony/src/domain/topology/load_balancer.rs index cf79ac3..9f5226a 100644 --- a/harmony-rs/harmony/src/domain/topology/load_balancer.rs +++ b/harmony-rs/harmony/src/domain/topology/load_balancer.rs @@ -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; fn list_frontends(&self) -> Vec; 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, diff --git a/harmony-rs/harmony/src/domain/topology/mod.rs b/harmony-rs/harmony/src/domain/topology/mod.rs index bd4a172..bfb9c96 100644 --- a/harmony-rs/harmony/src/domain/topology/mod.rs +++ b/harmony-rs/harmony/src/domain/topology/mod.rs @@ -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: diff --git a/harmony-rs/harmony/src/domain/topology/network.rs b/harmony-rs/harmony/src/domain/topology/network.rs index 00189e4..be55f37 100644 --- a/harmony-rs/harmony/src/domain/topology/network.rs +++ b/harmony-rs/harmony/src/domain/topology/network.rs @@ -1,21 +1,30 @@ +use std::net::Ipv4Addr; + +use async_trait::async_trait; + +use crate::executors::ExecutorError; + use super::{IpAddress, LogicalHost}; #[derive(Debug)] 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 + )) } } 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; fn get_ip(&self) -> IpAddress; fn get_host(&self) -> LogicalHost; @@ -31,10 +40,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 +61,12 @@ 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; fn get_ip(&self) -> IpAddress; fn get_host(&self) -> LogicalHost; @@ -89,12 +103,25 @@ 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 { + #[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!( @@ -119,8 +146,3 @@ pub struct DnsRecord { pub record_type: DnsRecordType, pub value: String, } - -// Error types -pub struct FirewallError; -pub struct DhcpError; -pub struct DnsError; diff --git a/harmony-rs/harmony/src/infra/opnsense/mod.rs b/harmony-rs/harmony/src/infra/opnsense/mod.rs index 92f1826..66b00e6 100644 --- a/harmony-rs/harmony/src/infra/opnsense/mod.rs +++ b/harmony-rs/harmony/src/infra/opnsense/mod.rs @@ -1,29 +1,52 @@ mod management; +use std::sync::{Arc, Mutex, RwLock, RwLockWriteGuard}; + +use async_trait::async_trait; +use log::debug; pub use management::*; -use crate::topology::{ - Backend, DHCPStaticEntry, DhcpServer, DnsServer, Firewall, FirewallError, FirewallRule, - Frontend, IpAddress, LoadBalancer, LoadBalancerError, LogicalHost, +use crate::{ + 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>, host: LogicalHost, + cluster_nic_name: String, } impl OPNSenseFirewall { pub fn get_ip(&self) -> IpAddress { 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 { - 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!() } @@ -40,19 +63,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!() } @@ -72,22 +95,30 @@ impl LoadBalancer for OPNSenseFirewall { } } +#[async_trait] impl DhcpServer for OPNSenseFirewall { - fn add_static_mapping( - &self, - entry: &DHCPStaticEntry, - ) -> Result<(), crate::topology::DhcpError> { - todo!("Register {:?}", entry) + async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError> { + let mac: String = String::from(&entry.mac); + + { + 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(()) } - 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!() } @@ -98,13 +129,14 @@ impl DhcpServer for OPNSenseFirewall { self.host.clone() } } + impl DnsServer for OPNSenseFirewall { fn add_record( &mut self, _name: &str, _record_type: crate::topology::DnsRecordType, _value: &str, - ) -> Result<(), crate::topology::DnsError> { + ) -> Result<(), ExecutorError> { todo!() } @@ -112,7 +144,7 @@ impl DnsServer for OPNSenseFirewall { &mut self, _name: &str, _record_type: crate::topology::DnsRecordType, - ) -> Result<(), crate::topology::DnsError> { + ) -> Result<(), ExecutorError> { todo!() } diff --git a/harmony-rs/harmony/src/modules/dhcp.rs b/harmony-rs/harmony/src/modules/dhcp.rs index ec100ea..37e49cb 100644 --- a/harmony-rs/harmony/src/modules/dhcp.rs +++ b/harmony-rs/harmony/src/modules/dhcp.rs @@ -1,3 +1,5 @@ +use std::{net::Ipv4Addr, 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,28 +115,35 @@ impl Interpret for DhcpInterpret { topology: &HAClusterTopology, ) -> Result { info!("Executing {} on inventory {inventory:?}", self.get_name()); - let ssh_client = RusshClient {}; - let entries: Vec = self + let dhcp_entries: Vec = self .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 : {:?}", 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( diff --git a/harmony-rs/opnsense-config-xml/Cargo.toml b/harmony-rs/opnsense-config-xml/Cargo.toml new file mode 100644 index 0000000..3b28dcb --- /dev/null +++ b/harmony-rs/opnsense-config-xml/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "opnsense-config-xml" +edition = "2021" +version.workspace = true +readme.workspace = true +license.workspace = true + +[dependencies] +serde = { version = "1.0.123", features = [ "derive" ] } +log = { workspace = true } +env_logger = { workspace = true } +#yaserde = { git = "https://git.nationtech.io/NationTech/yaserde" } +#yaserde_derive = { git = "https://git.nationtech.io/NationTech/yaserde" } +yaserde = { path = "../../../../github/yaserde/yaserde" } +yaserde_derive = { path = "../../../../github/yaserde/yaserde_derive" } +xml-rs = "0.8" +thiserror = "1.0" +async-trait = { workspace = true } +tokio = { workspace = true } + +[dev-dependencies] +pretty_assertions = "1.4.1" + +[target.x86_64-unknown-linux-gnu] +rustflags = ["-C", "link-arg=-fuse-ld=mold"] diff --git a/harmony-rs/opnsense-config-xml/interfaces_expand.rs b/harmony-rs/opnsense-config-xml/interfaces_expand.rs new file mode 100644 index 0000000..3ca4b76 --- /dev/null +++ b/harmony-rs/opnsense-config-xml/interfaces_expand.rs @@ -0,0 +1,2134 @@ +mod interfaces { + use yaserde::{ + NamedList, YaDeserialize as YaDeserializeTrait, YaSerialize as YaSerializeTrait, + }; + use yaserde_derive::{YaDeserialize, YaSerialize}; + use yaserde::MaybeString; + pub struct Interface { + #[yaserde(rename = "if")] + pub physical_interface_name: String, + pub descr: String, + pub enable: MaybeString, + #[yaserde(rename = "spoofmac")] + pub spoof_mac: Option, + pub internal_dynamic: Option, + pub ipaddr: Option, + #[yaserde(rename = "blockpriv")] + pub block_priv: Option, + #[yaserde(rename = "blockbogons")] + pub block_bogons: Option, + pub lock: Option, + #[yaserde(rename = "type")] + pub r#type: Option, + #[yaserde(rename = "virtual")] + pub r#virtual: Option, + pub subnet: Option, + pub networks: Option, + pub subnetv6: Option, + pub ipaddrv6: Option, + #[yaserde(rename = "track6-interface")] + pub track6_interface: Option, + #[yaserde(rename = "track6-prefix-id")] + pub track6_prefix_id: Option, + } + #[automatically_derived] + impl ::core::default::Default for Interface { + #[inline] + fn default() -> Interface { + Interface { + physical_interface_name: ::core::default::Default::default(), + descr: ::core::default::Default::default(), + enable: ::core::default::Default::default(), + spoof_mac: ::core::default::Default::default(), + internal_dynamic: ::core::default::Default::default(), + ipaddr: ::core::default::Default::default(), + block_priv: ::core::default::Default::default(), + block_bogons: ::core::default::Default::default(), + lock: ::core::default::Default::default(), + r#type: ::core::default::Default::default(), + r#virtual: ::core::default::Default::default(), + subnet: ::core::default::Default::default(), + networks: ::core::default::Default::default(), + subnetv6: ::core::default::Default::default(), + ipaddrv6: ::core::default::Default::default(), + track6_interface: ::core::default::Default::default(), + track6_prefix_id: ::core::default::Default::default(), + } + } + } + #[automatically_derived] + impl ::core::marker::StructuralPartialEq for Interface {} + #[automatically_derived] + impl ::core::cmp::PartialEq for Interface { + #[inline] + fn eq(&self, other: &Interface) -> bool { + self.physical_interface_name == other.physical_interface_name + && self.descr == other.descr && self.enable == other.enable + && self.spoof_mac == other.spoof_mac + && self.internal_dynamic == other.internal_dynamic + && self.ipaddr == other.ipaddr && self.block_priv == other.block_priv + && self.block_bogons == other.block_bogons && self.lock == other.lock + && self.r#type == other.r#type && self.r#virtual == other.r#virtual + && self.subnet == other.subnet && self.networks == other.networks + && self.subnetv6 == other.subnetv6 && self.ipaddrv6 == other.ipaddrv6 + && self.track6_interface == other.track6_interface + && self.track6_prefix_id == other.track6_prefix_id + } + } + #[automatically_derived] + impl ::core::fmt::Debug for Interface { + #[inline] + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let names: &'static _ = &[ + "physical_interface_name", + "descr", + "enable", + "spoof_mac", + "internal_dynamic", + "ipaddr", + "block_priv", + "block_bogons", + "lock", + "type", + "virtual", + "subnet", + "networks", + "subnetv6", + "ipaddrv6", + "track6_interface", + "track6_prefix_id", + ]; + let values: &[&dyn ::core::fmt::Debug] = &[ + &self.physical_interface_name, + &self.descr, + &self.enable, + &self.spoof_mac, + &self.internal_dynamic, + &self.ipaddr, + &self.block_priv, + &self.block_bogons, + &self.lock, + &self.r#type, + &self.r#virtual, + &self.subnet, + &self.networks, + &self.subnetv6, + &self.ipaddrv6, + &self.track6_interface, + &&self.track6_prefix_id, + ]; + ::core::fmt::Formatter::debug_struct_fields_finish( + f, + "Interface", + names, + values, + ) + } + } + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _IMPL_YA_DESERIALIZE_FOR_Interface: () = { + use ::std::str::FromStr as _; + use ::yaserde::Visitor as _; + impl ::yaserde::YaDeserialize for Interface { + #[allow(unused_variables)] + fn deserialize( + reader: &mut ::yaserde::de::Deserializer, + ) -> ::std::result::Result { + let (named_element, struct_namespace) = if let ::yaserde::__xml::reader::XmlEvent::StartElement { + name, + .. + } = reader.peek()?.to_owned() + { + (name.local_name.to_owned(), name.namespace.clone()) + } else { + ( + ::std::string::String::from("Interface"), + ::std::option::Option::None, + ) + }; + let start_depth = reader.depth(); + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Struct {0} @ {1}: start to parse {2:?}", + "Interface", + start_depth, + named_element, + ), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if reader.depth() == 0 { + if let Some(namespace) = struct_namespace { + match namespace.as_str() { + bad_namespace => { + let msg = ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!( + "bad namespace for {0}, found {1}", + named_element, + bad_namespace, + ), + ); + res + }); + return Err(msg); + } + } + } + } + #[allow(unused_mut)] + let mut __physical_interface_name_value: Option<::std::string::String> = None; + #[allow(unused_mut)] + let mut __descr_value: Option<::std::string::String> = None; + #[allow(unused_mut)] + let mut __enable_value: Option = None; + #[allow(unused_mut)] + let mut __spoof_mac_value = None; + #[allow(unused_mut)] + let mut __internal_dynamic_value = None; + #[allow(unused_mut)] + let mut __ipaddr_value = None; + #[allow(unused_mut)] + let mut __block_priv_value = None; + #[allow(unused_mut)] + let mut __block_bogons_value = None; + #[allow(unused_mut)] + let mut __lock_value = None; + #[allow(unused_mut)] + let mut __type_value = None; + #[allow(unused_mut)] + let mut __virtual_value = None; + #[allow(unused_mut)] + let mut __subnet_value = None; + #[allow(unused_mut)] + let mut __networks_value = None; + #[allow(unused_mut)] + let mut __subnetv6_value = None; + #[allow(unused_mut)] + let mut __ipaddrv6_value = None; + #[allow(unused_mut)] + let mut __track6_interface_value = None; + #[allow(unused_mut)] + let mut __track6_prefix_id_value = None; + #[allow(non_snake_case, non_camel_case_types)] + struct __Visitor_If_; + impl<'de> ::yaserde::Visitor<'de> for __Visitor_If_ { + type Value = ::std::string::String; + fn visit_str( + self, + v: &str, + ) -> ::std::result::Result { + ::std::string::String::from_str(v).map_err(|e| e.to_string()) + } + } + #[allow(non_snake_case, non_camel_case_types)] + struct __Visitor_Descr_; + impl<'de> ::yaserde::Visitor<'de> for __Visitor_Descr_ { + type Value = ::std::string::String; + fn visit_str( + self, + v: &str, + ) -> ::std::result::Result { + ::std::string::String::from_str(v).map_err(|e| e.to_string()) + } + } + let mut depth = 0; + loop { + let event = reader.peek()?.to_owned(); + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Struct {0} @ {1}: matching {2:?}", + "Interface", + start_depth, + event, + ), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + match event { + ::yaserde::__xml::reader::XmlEvent::StartElement { + ref name, + ref attributes, + .. + } => { + let namespace = name.namespace.clone().unwrap_or_default(); + if depth == 0 && name.local_name == "Interface" + && namespace.as_str() == "" + { + let event = reader.next_event()?; + } else { + match (namespace.as_str(), name.local_name.as_str()) { + ("", "if") => { + let visitor = __Visitor_If_ {}; + if let Some(namespace) = name.namespace.as_ref() { + match namespace.as_str() { + bad_namespace => { + let msg = ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!( + "bad namespace for {0}, found {1}", + name.local_name.as_str(), + bad_namespace, + ), + ); + res + }); + return Err(msg); + } + } + } + let result = reader + .read_inner_value::< + ::std::string::String, + _, + >(|reader| { + if let ::std::result::Result::Ok( + ::yaserde::__xml::reader::XmlEvent::Characters(s), + ) = reader.peek() + { + let val = visitor.visit_str(&s); + let _event = reader.next_event()?; + val + } else { + ::std::result::Result::Err( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("unable to parse content for {0}", "if"), + ); + res + }), + ) + } + }); + if let ::std::result::Result::Ok(value) = result { + __physical_interface_name_value = ::std::option::Option::Some( + value, + ); + } + } + ("", "descr") => { + let visitor = __Visitor_Descr_ {}; + if let Some(namespace) = name.namespace.as_ref() { + match namespace.as_str() { + bad_namespace => { + let msg = ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!( + "bad namespace for {0}, found {1}", + name.local_name.as_str(), + bad_namespace, + ), + ); + res + }); + return Err(msg); + } + } + } + let result = reader + .read_inner_value::< + ::std::string::String, + _, + >(|reader| { + if let ::std::result::Result::Ok( + ::yaserde::__xml::reader::XmlEvent::Characters(s), + ) = reader.peek() + { + let val = visitor.visit_str(&s); + let _event = reader.next_event()?; + val + } else { + ::std::result::Result::Err( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("unable to parse content for {0}", "descr"), + ); + res + }), + ) + } + }); + if let ::std::result::Result::Ok(value) = result { + __descr_value = ::std::option::Option::Some(value); + } + } + ("", "enable") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __enable_value = ::std::option::Option::Some(value); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "spoofmac") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __spoof_mac_value = ::std::option::Option::Some(value); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "internal_dynamic") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __internal_dynamic_value = ::std::option::Option::Some( + value, + ); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "ipaddr") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __ipaddr_value = ::std::option::Option::Some(value); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "blockpriv") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __block_priv_value = ::std::option::Option::Some(value); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "blockbogons") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __block_bogons_value = ::std::option::Option::Some(value); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "lock") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __lock_value = ::std::option::Option::Some(value); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "type") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __type_value = ::std::option::Option::Some(value); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "virtual") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __virtual_value = ::std::option::Option::Some(value); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "subnet") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __subnet_value = ::std::option::Option::Some(value); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "networks") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __networks_value = ::std::option::Option::Some(value); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "subnetv6") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __subnetv6_value = ::std::option::Option::Some(value); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "ipaddrv6") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __ipaddrv6_value = ::std::option::Option::Some(value); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "track6-interface") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __track6_interface_value = ::std::option::Option::Some( + value, + ); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + ("", "track6-prefix-id") => { + if depth == 0 { + let _root = reader.next_event(); + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Looking at startElement"), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("matching field type ASPDOJIASDPJIDASPJASDJI"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if let Ok( + ::yaserde::__xml::reader::XmlEvent::StartElement { .. }, + ) = reader.peek() + { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Found start element ?? {0}", "MaybeString"), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let value = ::deserialize( + reader, + )?; + __track6_prefix_id_value = ::std::option::Option::Some( + value, + ); + let _event = reader.next_event()?; + } else { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!( + "matching field type did not find substruct start element ? {0}", + "MaybeString", + ), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + } + } + _ => { + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Got StartElement {0:?}", name.local_name), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + let event = reader.next_event()?; + { + let lvl = ::log::Level::Trace; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("Next event {0:?}", event), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if depth > 0 { + return Err( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!( + "Found unauthorized element {0}", + name.local_name, + ), + ); + res + }), + ); + reader.skip_element(|event| {})?; + } + } + } + } + if depth == 0 {} + depth += 1; + } + ::yaserde::__xml::reader::XmlEvent::EndElement { ref name } => { + { + let lvl = ::log::Level::Warn; + if lvl <= ::log::STATIC_MAX_LEVEL + && lvl <= ::log::max_level() + { + ::log::__private_api::log( + format_args!("endElement {0}", named_element), + lvl, + &( + "opnsense_config_xml::data::interfaces", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + if name.local_name == named_element + && reader.depth() == start_depth + 1 + { + break; + } + let event = reader.next_event()?; + depth -= 1; + } + ::yaserde::__xml::reader::XmlEvent::EndDocument => { + if false { + break; + } + } + ::yaserde::__xml::reader::XmlEvent::Characters( + ref text_content, + ) => { + let event = reader.next_event()?; + } + event => { + return ::std::result::Result::Err( + ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("unknown event {0:?}", event), + ); + res + }), + ); + } + } + } + { + let lvl = ::log::Level::Debug; + if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() { + ::log::__private_api::log( + format_args!( + "Struct {0} @ {1}: success", + "Interface", + start_depth, + ), + lvl, + &( + "yaserde_derive", + "opnsense_config_xml::data::interfaces", + ::log::__private_api::loc(), + ), + (), + ); + } + }; + ::std::result::Result::Ok(Interface { + physical_interface_name: __physical_interface_name_value + .ok_or_else(|| { + "physical_interface_name is a required field of Interface" + .to_string() + })?, + descr: __descr_value + .ok_or_else(|| { + "descr is a required field of Interface".to_string() + })?, + enable: __enable_value + .ok_or_else(|| { + "enable is a required field of Interface".to_string() + })?, + spoof_mac: __spoof_mac_value, + internal_dynamic: __internal_dynamic_value, + ipaddr: __ipaddr_value, + block_priv: __block_priv_value, + block_bogons: __block_bogons_value, + lock: __lock_value, + r#type: __type_value, + r#virtual: __virtual_value, + subnet: __subnet_value, + networks: __networks_value, + subnetv6: __subnetv6_value, + ipaddrv6: __ipaddrv6_value, + track6_interface: __track6_interface_value, + track6_prefix_id: __track6_prefix_id_value, + }) + } + } + }; + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + const _IMPL_YA_SERIALIZE_FOR_Interface: () = { + use ::std::str::FromStr as _; + impl ::yaserde::YaSerialize for Interface { + #[allow(unused_variables)] + fn serialize( + &self, + writer: &mut ::yaserde::ser::Serializer, + ) -> ::std::result::Result<(), ::std::string::String> { + let skip = writer.skip_start_end(); + if !false && !skip { + let mut child_attributes = ::alloc::vec::Vec::new(); + let mut child_attributes_namespace = ::yaserde::__xml::namespace::Namespace::empty(); + let yaserde_label = writer + .get_start_event_name() + .unwrap_or_else(|| "Interface".to_string()); + let struct_start_event = ::yaserde::__xml::writer::XmlEvent::start_element( + yaserde_label.as_ref(), + ); + let event: ::yaserde::__xml::writer::events::XmlEvent = struct_start_event + .into(); + if let ::yaserde::__xml::writer::events::XmlEvent::StartElement { + name, + attributes, + namespace, + } = event { + let mut attributes: ::std::vec::Vec< + ::yaserde::__xml::attribute::OwnedAttribute, + > = attributes + .into_owned() + .to_vec() + .iter() + .map(|k| k.to_owned()) + .collect(); + attributes.extend(child_attributes); + let all_attributes = attributes + .iter() + .map(|ca| ca.borrow()) + .collect(); + let mut all_namespaces = namespace.into_owned(); + all_namespaces.extend(&child_attributes_namespace); + writer + .write(::yaserde::__xml::writer::events::XmlEvent::StartElement { + name, + attributes: ::std::borrow::Cow::Owned(all_attributes), + namespace: ::std::borrow::Cow::Owned(all_namespaces), + }) + .map_err(|e| e.to_string())?; + } else { + ::core::panicking::panic( + "internal error: entered unreachable code", + ) + } + } + if !false { + let start_event = ::yaserde::__xml::writer::XmlEvent::start_element( + "if", + ); + writer.write(start_event).map_err(|e| e.to_string())?; + let yaserde_value = ::alloc::__export::must_use({ + let res = ::alloc::fmt::format( + format_args!("{0}", self.physical_interface_name), + ); + res + }); + let data_event = ::yaserde::__xml::writer::XmlEvent::characters( + &yaserde_value, + ); + writer.write(data_event).map_err(|e| e.to_string())?; + let end_event = ::yaserde::__xml::writer::XmlEvent::end_element(); + writer.write(end_event).map_err(|e| e.to_string())?; + } + if !false { + let start_event = ::yaserde::__xml::writer::XmlEvent::start_element( + "descr", + ); + writer.write(start_event).map_err(|e| e.to_string())?; + let yaserde_value = ::alloc::__export::must_use({ + let res = ::alloc::fmt::format(format_args!("{0}", self.descr)); + res + }); + let data_event = ::yaserde::__xml::writer::XmlEvent::characters( + &yaserde_value, + ); + writer.write(data_event).map_err(|e| e.to_string())?; + let end_event = ::yaserde::__xml::writer::XmlEvent::end_element(); + writer.write(end_event).map_err(|e| e.to_string())?; + } + if !false { + writer + .set_start_event_name( + ::std::option::Option::Some("enable".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(&self.enable, writer)?; + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.spoof_mac { + writer + .set_start_event_name( + ::std::option::Option::Some("spoofmac".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.internal_dynamic + { + writer + .set_start_event_name( + ::std::option::Option::Some("internal_dynamic".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.ipaddr { + writer + .set_start_event_name( + ::std::option::Option::Some("ipaddr".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.block_priv { + writer + .set_start_event_name( + ::std::option::Option::Some("blockpriv".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.block_bogons { + writer + .set_start_event_name( + ::std::option::Option::Some("blockbogons".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.lock { + writer + .set_start_event_name( + ::std::option::Option::Some("lock".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.r#type { + writer + .set_start_event_name( + ::std::option::Option::Some("type".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.r#virtual { + writer + .set_start_event_name( + ::std::option::Option::Some("virtual".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.subnet { + writer + .set_start_event_name( + ::std::option::Option::Some("subnet".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.networks { + writer + .set_start_event_name( + ::std::option::Option::Some("networks".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.subnetv6 { + writer + .set_start_event_name( + ::std::option::Option::Some("subnetv6".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.ipaddrv6 { + writer + .set_start_event_name( + ::std::option::Option::Some("ipaddrv6".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.track6_interface + { + writer + .set_start_event_name( + ::std::option::Option::Some("track6-interface".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false { + if let ::std::option::Option::Some(ref item) = &self.track6_prefix_id + { + writer + .set_start_event_name( + ::std::option::Option::Some("track6-prefix-id".to_string()), + ); + writer.set_skip_start_end(false); + ::yaserde::YaSerialize::serialize(item, writer)?; + } + } + if !false && !skip { + let struct_end_event = ::yaserde::__xml::writer::XmlEvent::end_element(); + writer.write(struct_end_event).map_err(|e| e.to_string())?; + } + ::std::result::Result::Ok(()) + } + fn serialize_attributes( + &self, + mut source_attributes: ::std::vec::Vec< + ::yaserde::__xml::attribute::OwnedAttribute, + >, + mut source_namespace: ::yaserde::__xml::namespace::Namespace, + ) -> ::std::result::Result< + ( + ::std::vec::Vec<::yaserde::__xml::attribute::OwnedAttribute>, + ::yaserde::__xml::namespace::Namespace, + ), + ::std::string::String, + > { + let mut child_attributes = ::std::vec::Vec::< + ::yaserde::__xml::attribute::OwnedAttribute, + >::new(); + let mut child_attributes_namespace = ::yaserde::__xml::namespace::Namespace::empty(); + let struct_start_event = ::yaserde::__xml::writer::XmlEvent::start_element( + "temporary_element_to_generate_attributes", + ); + let event: ::yaserde::__xml::writer::events::XmlEvent = struct_start_event + .into(); + if let ::yaserde::__xml::writer::events::XmlEvent::StartElement { + attributes, + namespace, + .. + } = event { + source_namespace.extend(&namespace.into_owned()); + source_namespace.extend(&child_attributes_namespace); + let a: ::std::vec::Vec< + ::yaserde::__xml::attribute::OwnedAttribute, + > = attributes + .into_owned() + .to_vec() + .iter() + .map(|k| k.to_owned()) + .collect(); + source_attributes.extend(a); + source_attributes.extend(child_attributes); + ::std::result::Result::Ok((source_attributes, source_namespace)) + } else { + ::core::panicking::panic("internal error: entered unreachable code"); + } + } + } + }; + pub trait MyDeserialize: YaDeserializeTrait {} + pub trait MySerialize: YaSerializeTrait {} +} diff --git a/harmony-rs/opnsense-config-xml/src/data/dhcpd.rs b/harmony-rs/opnsense-config-xml/src/data/dhcpd.rs new file mode 100644 index 0000000..9c06716 --- /dev/null +++ b/harmony-rs/opnsense-config-xml/src/data/dhcpd.rs @@ -0,0 +1,113 @@ +use yaserde_derive::{YaDeserialize, YaSerialize}; + +use yaserde::MaybeString; + +use super::opnsense::{NumberOption, Range, StaticMap}; + +// #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +// #[yaserde(rename = "dhcpd")] +// pub struct Dhcpd { +// #[yaserde(rename = "lan")] +// pub lan: DhcpInterface, +// } + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct DhcpInterface { + pub enable: Option, + pub gateway: Option, + pub domain: Option, + #[yaserde(rename = "ddnsdomainalgorithm")] + pub ddns_domain_algorithm: Option, + #[yaserde(rename = "numberoptions")] + pub number_options: Vec, + #[yaserde(rename = "range")] + pub range: Range, + pub winsserver: Option, + pub dnsserver: Option, + pub ntpserver: Option, + #[yaserde(rename = "staticmap")] + pub staticmaps: Vec, + pub pool: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct DhcpRange { + #[yaserde(rename = "from")] + pub from: String, + #[yaserde(rename = "to")] + pub to: String, +} + +#[cfg(test)] +mod test { + use crate::xml_utils::to_xml_str; + + use pretty_assertions::assert_eq; + + #[test] + fn dhcpd_should_deserialize_serialize_identical() { + let dhcpd: Dhcpd = + yaserde::de::from_str(SERIALIZED_DHCPD).expect("Deserialize Dhcpd failed"); + + assert_eq!( + to_xml_str(&dhcpd).expect("Serialize Dhcpd failed"), + SERIALIZED_DHCPD + ); + } + + const SERIALIZED_DHCPD: &str = " + + + 1 + 192.168.20.1 + somedomain.yourlocal.mcd + hmac-md5 + + + + + 192.168.20.50 + 192.168.20.200 + + + 192.168.20.1 + + + 55:55:55:55:55:1c + 192.168.20.160 + somehost983 + someservire8 + + + + + + 55:55:55:55:55:1c + 192.168.20.155 + somehost893 + + + + + + 55:55:55:55:55:1c + 192.168.20.165 + somehost893 + + + + + + + 55:55:55:55:55:1c + 192.168.20.50 + hostswitch2 + switch-2 (bottom) + + + + + + +\n"; +} diff --git a/harmony-rs/opnsense-config-xml/src/data/interfaces.rs b/harmony-rs/opnsense-config-xml/src/data/interfaces.rs new file mode 100644 index 0000000..e980c73 --- /dev/null +++ b/harmony-rs/opnsense-config-xml/src/data/interfaces.rs @@ -0,0 +1,193 @@ +use yaserde_derive::{YaDeserialize, YaSerialize}; + +use yaserde::MaybeString; + +#[derive(Default, PartialEq, Debug, YaDeserialize, YaSerialize)] +pub struct Interface { + pub internal_dynamic: Option, + #[yaserde(rename = "if")] + pub physical_interface_name: String, + pub descr: Option, + pub enable: MaybeString, + pub lock: Option, + #[yaserde(rename = "spoofmac")] + pub spoof_mac: Option, + pub ipaddr: Option, + pub dhcphostname: Option, + #[yaserde(rename = "alias-address")] + pub alias_address: Option, + #[yaserde(rename = "alias-subnet")] + pub alias_subnet: Option, + #[yaserde(rename = "blockpriv")] + pub block_priv: Option, + #[yaserde(rename = "blockbogons")] + pub block_bogons: Option, + #[yaserde(rename = "type")] + pub r#type: Option, + #[yaserde(rename = "virtual")] + pub r#virtual: Option, + pub subnet: Option, + pub ipaddrv6: Option, + pub networks: Option, + pub subnetv6: Option, + pub media: Option, + pub mediaopt: Option, + #[yaserde(rename = "track6-interface")] + pub track6_interface: Option, + #[yaserde(rename = "track6-prefix-id")] + pub track6_prefix_id: Option, + #[yaserde(rename = "dhcprejectfrom")] + pub dhcprejectfrom: Option, + pub adv_dhcp_pt_timeout: Option, + pub adv_dhcp_pt_retry: Option, + pub adv_dhcp_pt_select_timeout: Option, + pub adv_dhcp_pt_reboot: Option, + pub adv_dhcp_pt_backoff_cutoff: Option, + pub adv_dhcp_pt_initial_interval: Option, + pub adv_dhcp_pt_values: Option, + pub adv_dhcp_send_options: Option, + pub adv_dhcp_request_options: Option, + pub adv_dhcp_required_options: Option, + pub adv_dhcp_option_modifiers: Option, + pub adv_dhcp_config_advanced: Option, + pub adv_dhcp_config_file_override: Option, + pub adv_dhcp_config_file_override_path: Option, +} + +#[cfg(test)] +mod test { + use crate::xml_utils::to_xml_str; + + use super::*; + use pretty_assertions::assert_eq; + use yaserde::NamedList; + + #[derive(Default, PartialEq, Debug, YaDeserialize, YaSerialize)] + pub struct InterfacesParent { + foo: String, + interfaces: NamedList, + bar: String, + } + + #[test] + fn should_deserialize_interfaces() { + let interfaces = + yaserde::de::from_str::>(FULL_INTERFACES_XML).unwrap(); + assert_eq!(interfaces.elements.len(), 6) + } + + #[test] + fn should_serialize_interfaces() { + let named_list = NamedList { + elements: vec![ + (String::from("paul"), Interface::default()), + (String::from("anotherpaul"), Interface::default()), + (String::from("thirdone"), Interface::default()), + (String::from("andgofor4"), Interface::default()), + ], + }; + + let parent = InterfacesParent { + foo: String::from("foo"), + interfaces: named_list, + bar: String::from("foo"), + }; + + assert_eq!( + &to_xml_str(&parent).unwrap(), + r#" + + foo + + + + + + + + + + + + + + + + + + + + + + + foo + +"# + ) + } + + const FULL_INTERFACES_XML: &str = " + + em1 + LAN + 1 + + + + 192.168.20.1 + 24 + track6 + + 0 + + + pppoe0 + WAN + 1 + 1 + + 1 + 1 + pppoe + + + 1 + Loopback + 1 + lo0 + 127.0.0.1 + ::1 + 8 + 128 + none + 1 + + + em5 + backup_sync + 1 + 1 + + 10.10.5.1 + 24 + + + 1 + WireGuard (Group) + wireguard + 1 + 1 + group + + + + 1 + 1 + openvpn + OpenVPN + group + 1 + + + "; +} diff --git a/harmony-rs/opnsense-config-xml/src/data/mod.rs b/harmony-rs/opnsense-config-xml/src/data/mod.rs new file mode 100644 index 0000000..5d2a4da --- /dev/null +++ b/harmony-rs/opnsense-config-xml/src/data/mod.rs @@ -0,0 +1,6 @@ +mod opnsense; +mod interfaces; +mod dhcpd; +pub use opnsense::*; +pub use interfaces::*; +pub use dhcpd::*; diff --git a/harmony-rs/opnsense-config-xml/src/data/opnsense.rs b/harmony-rs/opnsense-config-xml/src/data/opnsense.rs new file mode 100644 index 0000000..48ff582 --- /dev/null +++ b/harmony-rs/opnsense-config-xml/src/data/opnsense.rs @@ -0,0 +1,2052 @@ +use crate::{data::dhcpd::DhcpInterface, xml_utils::to_xml_str}; +use log::error; +use yaserde::{MaybeString, NamedList, RawXml}; +use yaserde_derive::{YaDeserialize, YaSerialize}; + +use super::Interface; + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +#[yaserde(rename = "opnsense")] +pub struct OPNsense { + pub theme: String, + pub sysctl: Sysctl, + pub system: RawXml, + // pub interfaces: RawXml, + pub interfaces: NamedList, + pub dhcpd: NamedList, + pub snmpd: Snmpd, + pub syslog: Syslog, + pub nat: Nat, + pub filter: Filters, + pub load_balancer: Option, + pub rrd: Option, + pub ntpd: Ntpd, + pub widgets: Widgets, + pub revision: Revision, + #[yaserde(rename = "OPNsense")] + pub opnsense: OPNsenseXmlSection, + pub staticroutes: StaticRoutes, + pub ca: MaybeString, + pub gateways: Option, + pub cert: Vec, + pub dhcpdv6: DhcpDv6, + pub virtualip: VirtualIp, + pub openvpn: OpenVpn, + pub ppps: Ppps, + pub dyndnses: Option, + pub vlans: Vlans, + pub bridges: Bridges, + pub gifs: Gifs, + pub gres: Gres, + pub laggs: Laggs, + pub wireless: Wireless, + pub hasync: Hasync, + pub ifgroups: Ifgroups, +} + +impl From for OPNsense { + fn from(content: String) -> Self { + yaserde::de::from_str(&content) + .map_err(|e| println!("{}", e.to_string())) + .expect("OPNSense received invalid string, should be full XML") + } +} + +impl OPNsense { + pub fn to_xml(&self) -> String { + to_xml_str(self) + .map_err(|e| error!("{}", e.to_string())) + .expect("OPNSense could not serialize to XML") + } +} + + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct LoadBalancer { + pub monitor_type: Vec, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct MonitorType { + pub name: String, + #[yaserde(rename = "type")] + pub r#type: String, + pub descr: String, + pub options: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Ntpd { + pub prefer: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Widgets { + pub sequence: String, + pub column_count: i32, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Revision { + pub username: String, + pub time: f64, + pub description: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Options { + pub path: Option, + pub host: Option, + pub code: Option, + pub send: Option, + pub expect: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Filters { + #[yaserde(rename = "rule")] + pub rules: Vec, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Rule { + #[yaserde(attribute)] + pub uuid: Option, + #[yaserde(rename = "associated-rule-id")] + pub associated_rule_id: Option, + #[yaserde(rename = "type")] + pub r#type: Option, + pub interface: String, + pub ipprotocol: String, + pub statetype: Option, + pub descr: Option, + pub direction: Option, + pub category: Option, + pub quick: Option, + pub protocol: Option, + pub source: Source, + pub icmptype: Option, + pub destination: Destination, + pub updated: Option, + pub created: Option, + pub disabled: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Source { + pub any: Option, + pub network: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Destination { + pub network: Option, + pub address: Option, + pub port: Option, + pub any: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Username { + pub user: String, + pub host: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Sysctl { + pub item: Vec, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct SysctlItem { + pub descr: String, + pub tunable: String, + pub value: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct System { + #[yaserde(rename = "use_mfs_tmp")] + pub use_mfs_tmp: MaybeString, + #[yaserde(rename = "use_mfs_var")] + pub use_mfs_var: MaybeString, + pub serialspeed: u32, + pub primaryconsole: String, + pub secondaryconsole: String, + pub optimization: String, + pub hostname: String, + pub domain: String, + pub group: Vec, + pub user: Vec, + pub nextuid: u32, + pub nextgid: u32, + pub timezone: String, + pub timeservers: String, + pub webgui: WebGui, + pub usevirtualterminal: u8, + pub disableconsolemenu: u8, + pub disablevlanhwfilter: u8, + pub disablechecksumoffloading: u8, + pub disablesegmentationoffloading: u8, + pub disablelargereceiveoffloading: u8, + pub ipv6allow: u8, + pub powerd_ac_mode: String, + pub powerd_battery_mode: String, + pub powerd_normal_mode: String, + pub bogons: Bogons, + pub crypto_hardware: String, + pub pf_share_forward: u8, + pub lb_use_sticky: u8, + pub kill_states: u8, + pub ssh: Ssh, + pub firmware: Firmware, + pub sudo_allow_wheel: u8, + pub sudo_allow_group: String, + pub enablenatreflectionhelper: String, + pub rulesetoptimization: String, + pub maximumstates: MaybeString, + pub maximumfrags: MaybeString, + pub aliasesresolveinterval: MaybeString, + pub maximumtableentries: MaybeString, + pub language: String, + pub dnsserver: MaybeString, + #[yaserde(rename = "dns1gw")] + pub dns1gw: String, + #[yaserde(rename = "dns2gw")] + pub dns2gw: String, + #[yaserde(rename = "dns3gw")] + pub dns3gw: String, + #[yaserde(rename = "dns4gw")] + pub dns4gw: String, + #[yaserde(rename = "dns5gw")] + pub dns5gw: String, + #[yaserde(rename = "dns6gw")] + pub dns6gw: String, + #[yaserde(rename = "dns7gw")] + pub dns7gw: String, + #[yaserde(rename = "dns8gw")] + pub dns8gw: String, + pub dnsallowoverride: u8, + pub dnsallowoverride_exclude: MaybeString, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Ssh { + pub group: String, + pub noauto: u8, + pub interfaces: MaybeString, + pub kex: MaybeString, + pub ciphers: MaybeString, + pub macs: MaybeString, + pub keys: MaybeString, + pub enabled: String, + pub passwordauth: u8, + pub keysig: MaybeString, + pub permitrootlogin: u8, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Firmware { + #[yaserde(attribute)] + pub version: String, + pub mirror: MaybeString, + pub flavour: MaybeString, + pub plugins: String, + #[yaserde(rename = "type")] + pub firmware_type: MaybeString, + pub subscription: MaybeString, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Bogons { + pub interval: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Group { + pub name: String, + pub description: String, + pub scope: String, + pub gid: u32, + pub member: Vec, + #[yaserde(rename = "priv")] + pub priv_: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct User { + pub name: String, + pub descr: MaybeString, + pub scope: String, + pub groupname: MaybeString, + pub password: String, + pub uid: u32, + pub expires: MaybeString, + pub authorizedkeys: MaybeString, + pub ipsecpsk: MaybeString, + pub otp_seed: MaybeString, + pub shell: MaybeString, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct WebGui { + pub protocol: String, + #[yaserde(rename = "ssl-certref")] + pub ssl_certref: String, + pub port: MaybeString, + #[yaserde(rename = "ssl-ciphers")] + pub ssl_ciphers: MaybeString, + pub interfaces: MaybeString, + + pub compression: MaybeString, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct NumberOption { + item: MaybeString, +} + +#[derive(Default, Clone, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Range { + pub from: String, + pub to: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct StaticMap { + pub mac: String, + pub ipaddr: String, + pub hostname: String, + pub descr: Option, + pub winsserver: MaybeString, + pub dnsserver: MaybeString, + pub ntpserver: MaybeString, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Snmpd { + pub syslocation: MaybeString, + pub syscontact: MaybeString, + pub rocommunity: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Syslog { + pub reverse: Option, + pub preservelogs: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Nat { + #[yaserde(rename = "outbound")] + pub outbound: Outbound, + #[yaserde(rename = "rule")] + pub rules: Vec, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Outbound { + pub mode: String, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct NatRule { + pub protocol: String, + pub interface: String, + pub category: MaybeString, + pub ipprotocol: String, + pub descr: MaybeString, + pub tag: MaybeString, + pub tagged: Option, + pub poolopts: PoolOpts, + #[yaserde(rename = "associated-rule-id")] + pub associated_rule_id: Option, + pub disabled: Option, + pub target: String, + #[yaserde(rename = "local-port")] + pub local_port: i32, + pub source: Source, + pub destination: Destination, + pub updated: Updated, + pub created: Created, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct PoolOpts { + // No specific fields for this element, can be added as needed +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Updated { + pub username: String, + pub time: f64, + pub description: MaybeString, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Created { + pub username: String, + pub time: f64, + pub description: MaybeString, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Filter { + #[yaserde(attribute)] + version: String, + rules: Option, + snatrules: Option, + npt: Option, + onetoone: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +#[yaserde(rename = "OPNsense")] +pub struct OPNsenseXmlSection { + pub captiveportal: Option, + pub cron: Option, + #[yaserde(rename = "Netflow")] + pub netflow: Option, + #[yaserde(rename = "Firewall")] + pub firewall: Option, + #[yaserde(rename = "IDS")] + pub ids: Option, + #[yaserde(rename = "IPsec")] + pub ipsec: Option, + #[yaserde(rename = "Interfaces")] + pub interfaces: Option, + #[yaserde(rename = "Kea")] + pub kea: Option, + pub monit: Option, + #[yaserde(rename = "OpenVPNExport")] + pub openvpn_export: Option, + pub proxy: Option, + #[yaserde(rename = "Syslog")] + pub syslog: Option, + #[yaserde(rename = "TrafficShaper")] + pub traffic_shaper: Option, + pub unboundplus: Option, + #[yaserde(rename = "DHCRelay")] + pub dhcrelay: Option, + pub wireguard: Option, + #[yaserde(rename = "Swanctl")] + pub swanctl: Swanctl, + #[yaserde(rename = "DynDNS")] + pub dyndns: Option, + #[yaserde(rename = "OpenVPN")] + pub openvpn: ConfigOpenVPN, + #[yaserde(rename = "Gateways")] + pub gateways: RawXml, + #[yaserde(rename = "HAProxy")] + pub haproxy: Option, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +#[yaserde(rename = "IDS")] +pub struct IDS { + #[yaserde(attribute)] + version: String, + rules: MaybeString, + policies: MaybeString, + #[yaserde(rename = "userDefinedRules")] + user_defined_rules: MaybeString, + files: MaybeString, + #[yaserde(rename = "fileTags")] + file_tags: MaybeString, + general: IDSGeneral, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct IDSGeneral { + enabled: Option, + ips: Option, + promisc: Option, + interfaces: String, + homenet: String, + #[yaserde(rename = "defaultPacketSize")] + default_packet_size: MaybeString, + #[yaserde(rename = "UpdateCron")] + update_cron: MaybeString, + #[yaserde(rename = "AlertLogrotate")] + alert_logrotate: String, + #[yaserde(rename = "AlertSaveLogs")] + alert_save_logs: u8, + #[yaserde(rename = "MPMAlgo")] + mpm_algo: MaybeString, + detect: Detect, + syslog: Option, + syslog_eve: Option, + #[yaserde(rename = "LogPayload")] + log_payload: Option, + verbosity: MaybeString, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct Detect { + #[yaserde(rename = "Profile")] + profile: MaybeString, + toclient_groups: MaybeString, + toserver_groups: MaybeString, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct IPsec { + #[yaserde(attribute)] + version: String, + general: GeneralIpsec, + #[yaserde(rename = "keyPairs")] + key_pairs: MaybeString, + #[yaserde(rename = "preSharedKeys")] + pre_shared_keys: MaybeString, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct GeneralIpsec { + enabled: MaybeString, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +#[yaserde(rename = "Interfaces")] +pub struct ConfigInterfaces { + vxlans: Vxlan, + loopbacks: Loopback, + neighbors: Option, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct Neighbors { + #[yaserde(attribute)] + version: String, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct Vxlan { + #[yaserde(attribute)] + version: String, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct Loopback { + #[yaserde(attribute)] + version: String, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +#[yaserde(rename = "monit")] +pub struct Monit { + #[yaserde(attribute)] + version: String, + general: GeneralMonit, + alert: Option, + service: Vec, + test: Vec, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct GeneralMonit { + enabled: u8, + interval: u32, + startdelay: u32, + mailserver: String, + port: u16, + username: MaybeString, + password: MaybeString, + ssl: u8, + sslversion: String, + sslverify: u8, + logfile: MaybeString, + statefile: MaybeString, + #[yaserde(rename = "eventqueuePath")] + event_queue_path: MaybeString, + #[yaserde(rename = "eventqueueSlots")] + event_queue_slots: MaybeString, + #[yaserde(rename = "httpdEnabled")] + httpd_enabled: u8, + #[yaserde(rename = "httpdUsername")] + httpd_username: String, + #[yaserde(rename = "httpdPassword")] + httpd_password: MaybeString, + #[yaserde(rename = "httpdPort")] + httpd_port: u16, + #[yaserde(rename = "httpdAllow")] + httpd_allow: MaybeString, + #[yaserde(rename = "mmonitUrl")] + mmonit_url: MaybeString, + #[yaserde(rename = "mmonitTimeout")] + mmonit_timeout: u32, + #[yaserde(rename = "mmonitRegisterCredentials")] + mmonit_register_credentials: u8, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct Alert { + #[yaserde(attribute)] + uuid: String, + enabled: u8, + recipient: String, + noton: u8, + events: MaybeString, + format: MaybeString, + reminder: MaybeString, + description: MaybeString, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct Service { + #[yaserde(attribute)] + uuid: String, + enabled: u8, + name: String, + description: MaybeString, + #[yaserde(rename = "type")] + r#type: String, + pidfile: MaybeString, + #[yaserde(rename = "match")] + r#match: MaybeString, + path: MaybeString, + timeout: u32, + starttimeout: u32, + address: MaybeString, + interface: MaybeString, + start: MaybeString, + stop: MaybeString, + tests: String, + depends: MaybeString, + polltime: MaybeString, +} + +#[derive(Debug, YaSerialize, YaDeserialize, PartialEq)] +pub struct Test { + #[yaserde(attribute)] + uuid: String, + name: String, + #[yaserde(rename = "type")] + r#type: String, + condition: String, + action: String, + path: MaybeString, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct CaptivePortal { + #[yaserde(attribute)] + pub version: String, + #[yaserde(rename = "zones")] + pub zones: Option, + #[yaserde(rename = "templates")] + pub templates: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Cron { + #[yaserde(attribute)] + pub version: String, + #[yaserde(rename = "jobs")] + pub jobs: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Netflow { + #[yaserde(attribute)] + pub version: String, + #[yaserde(rename = "capture")] + pub capture: Option, + #[yaserde(rename = "collect")] + pub collect: Option, + #[yaserde(rename = "activeTimeout")] + pub active_timeout: Option, + #[yaserde(rename = "inactiveTimeout")] + pub inactive_timeout: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Capture { + #[yaserde(rename = "interfaces")] + pub interfaces: MaybeString, + #[yaserde(rename = "egress_only")] + pub egress_only: MaybeString, + #[yaserde(rename = "version")] + pub version: MaybeString, + #[yaserde(rename = "targets")] + pub targets: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Collect { + #[yaserde(rename = "enable")] + pub enable: Option, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Zones { + // Define fields for Zones, e.g.: + #[yaserde(rename = "zone")] + pub zones: Vec, +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Zone { + #[yaserde(attribute)] + pub uuid: MaybeString, + #[yaserde(rename = "name")] + pub name: MaybeString, + // Add other fields as needed +} + +#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] +pub struct Templates { + // Define fields for Templates, e.g.: + #[yaserde(rename = "template")] + pub templates: Vec