diff --git a/rust/migrate-wicked/Cargo.lock b/rust/migrate-wicked/Cargo.lock index fabce440fe..92b8882f46 100644 --- a/rust/migrate-wicked/Cargo.lock +++ b/rust/migrate-wicked/Cargo.lock @@ -24,15 +24,15 @@ dependencies = [ "agama-lib", "agama-locale-data", "anyhow", - "async-std", "cidr", - "futures", "log", "serde", "serde_yaml", "simplelog", "systemd-journal-logger", "thiserror", + "tokio", + "tokio-stream", "uuid", "zbus", "zbus_macros", @@ -53,10 +53,8 @@ version = "1.0.0" dependencies = [ "agama-settings", "anyhow", - "async-std", "cidr", "curl", - "futures", "futures-util", "jsonschema", "log", @@ -64,6 +62,8 @@ dependencies = [ "serde_json", "tempfile", "thiserror", + "tokio", + "tokio-stream", "zbus", ] @@ -179,16 +179,6 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" -[[package]] -name = "async-attributes" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" -dependencies = [ - "quote", - "syn 1.0.109", -] - [[package]] name = "async-broadcast" version = "0.5.1" @@ -199,17 +189,6 @@ dependencies = [ "futures-core", ] -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - [[package]] name = "async-channel" version = "2.1.0" @@ -223,47 +202,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-executor" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0" -dependencies = [ - "async-lock 2.8.0", - "async-task", - "concurrent-queue", - "fastrand 2.0.1", - "futures-lite 1.13.0", - "slab", -] - -[[package]] -name = "async-fs" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "blocking", - "futures-lite 1.13.0", -] - -[[package]] -name = "async-global-executor" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" -dependencies = [ - "async-channel 1.9.0", - "async-executor", - "async-io 1.13.0", - "async-lock 2.8.0", - "blocking", - "futures-lite 1.13.0", - "once_cell", -] - [[package]] name = "async-io" version = "1.13.0" @@ -280,7 +218,7 @@ dependencies = [ "polling 2.8.0", "rustix 0.37.27", "slab", - "socket2", + "socket2 0.4.10", "waker-fn", ] @@ -370,33 +308,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "async-std" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" -dependencies = [ - "async-attributes", - "async-channel 1.9.0", - "async-global-executor", - "async-io 1.13.0", - "async-lock 2.8.0", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite 1.13.0", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - [[package]] name = "async-task" version = "4.5.0" @@ -495,7 +406,7 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel 2.1.0", + "async-channel", "async-lock 3.1.0", "async-task", "fastrand 2.0.1", @@ -523,6 +434,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + [[package]] name = "cc" version = "1.0.83" @@ -692,7 +609,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "socket2", + "socket2 0.4.10", "winapi", ] @@ -902,48 +819,12 @@ dependencies = [ "num", ] -[[package]] -name = "futures" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" -dependencies = [ - "futures-core", - "futures-sink", -] - [[package]] name = "futures-core" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" -[[package]] -name = "futures-executor" -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - [[package]] name = "futures-io" version = "0.3.29" @@ -1004,13 +885,10 @@ version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ - "futures-channel", "futures-core", - "futures-io", "futures-macro", "futures-sink", "futures-task", - "memchr", "pin-project-lite", "pin-utils", "slab", @@ -1043,18 +921,6 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -1226,15 +1092,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -1336,6 +1193,7 @@ dependencies = [ "quick-xml", "regex", "serde", + "serde_ignored", "serde_json", "serde_with", "serde_yaml", @@ -1360,6 +1218,17 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" +dependencies = [ + "libc", + "wasi", + "windows-sys", +] + [[package]] name = "nix" version = "0.26.4" @@ -1865,6 +1734,15 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "serde_ignored" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80c31d5c53fd39f208e770f5a20a0bb214dee2a8d0d8adba18e19ad95a482ca5" +dependencies = [ + "serde", +] + [[package]] name = "serde_json" version = "1.0.108" @@ -2002,6 +1880,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "static_assertions" version = "1.1.0" @@ -2176,9 +2064,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" dependencies = [ "backtrace", + "bytes", + "libc", + "mio", "num_cpus", "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.5", "tokio-macros", + "tracing", + "windows-sys", ] [[package]] @@ -2192,6 +2087,17 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml_datetime" version = "0.6.5" @@ -2365,18 +2271,6 @@ dependencies = [ "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "wasm-bindgen-macro" version = "0.2.88" @@ -2406,16 +2300,6 @@ version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" -[[package]] -name = "web-sys" -version = "0.3.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "winapi" version = "0.3.9" @@ -2548,15 +2432,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948" dependencies = [ "async-broadcast", - "async-executor", - "async-fs", - "async-io 1.13.0", - "async-lock 2.8.0", "async-process", "async-recursion", - "async-task", "async-trait", - "blocking", "byteorder", "derivative", "enumflags2", @@ -2573,6 +2451,7 @@ dependencies = [ "serde_repr", "sha1", "static_assertions", + "tokio", "tracing", "uds_windows", "winapi", diff --git a/rust/migrate-wicked/Cargo.toml b/rust/migrate-wicked/Cargo.toml index 919eb87bfe..16e30705d5 100644 --- a/rust/migrate-wicked/Cargo.toml +++ b/rust/migrate-wicked/Cargo.toml @@ -22,6 +22,7 @@ strum = "0.25.0" strum_macros = "0.25.2" serde_with = "3.3.0" tokio = { version = "1.33.0", features = ["macros", "rt-multi-thread"] } +serde_ignored = "0.1.9" [[bin]] name = "migrate-wicked" diff --git a/rust/migrate-wicked/src/interface.rs b/rust/migrate-wicked/src/interface.rs index 3e6a6d64a5..1004f1001b 100644 --- a/rust/migrate-wicked/src/interface.rs +++ b/rust/migrate-wicked/src/interface.rs @@ -325,8 +325,8 @@ where Ok(Slaves::deserialize(deserializer)?.slave) } -impl From for model::BondConfig { - fn from(bond: Bond) -> model::BondConfig { +impl From<&Bond> for model::BondConfig { + fn from(bond: &Bond) -> model::BondConfig { let mut h: HashMap = HashMap::new(); h.insert(String::from("mode"), bond.mode.to_string()); @@ -433,7 +433,7 @@ impl From for model::BondConfig { model::BondConfig { options: h, - hwaddr: match bond.address { + hwaddr: match &bond.address { Some(s) => model::MacAddr::try_from(s.as_ref()).ok(), _ => None, }, @@ -441,65 +441,81 @@ impl From for model::BondConfig { } } -impl From for model::Connection { - fn from(ifc: Interface) -> model::Connection { +pub struct ConnectionResult { + pub connection: model::Connection, + pub warnings: Vec, +} + +pub struct IpConfigResult { + ip_config: IpConfig, + warnings: Vec, +} + +impl Interface { + pub fn to_connection(&self) -> Result { + let ip_config = self.to_ip_config()?; let mut base = model::BaseConnection { - id: ifc.name.clone(), - interface: ifc.name.clone(), - ip_config: (&ifc).into(), + id: self.name.clone(), + interface: self.name.clone(), + ip_config: ip_config.ip_config, ..Default::default() }; - if ifc.link.kind.is_some() && ifc.link.parent.is_some() { - let interface = ifc.link.parent.clone().unwrap(); - let kind = match ifc.link.kind { + if self.link.kind.is_some() && self.link.parent.is_some() { + let interface = self.link.parent.clone().unwrap(); + let kind = match &self.link.kind { Some(p) => match &p { ParentKind::Bond => model::ParentKind::Bond, }, - None => panic!("Missing ParentType"), + None => return Err(anyhow::anyhow!("Missing ParentType for {}", self.name)), }; base.parent = Some(Parent { interface, kind }); } - if let Some(b) = ifc.bond { - model::Connection::Bond(model::BondConnection { - base, - bond: b.into(), - }) - } else { - model::Connection::Ethernet(model::EthernetConnection { base }) - } + Ok(ConnectionResult { + connection: if let Some(b) = &self.bond { + model::Connection::Bond(model::BondConnection { + base, + bond: b.into(), + }) + } else { + model::Connection::Ethernet(model::EthernetConnection { base }) + }, + warnings: ip_config.warnings, + }) } -} -impl From<&Interface> for IpConfig { - fn from(val: &Interface) -> Self { - let method4 = Ipv4Method::from_str(if val.ipv4.enabled && val.ipv4_static.is_some() { - "manual" - } else if !val.ipv4.enabled { - "disabled" + pub fn to_ip_config(&self) -> Result { + let mut connection_result = IpConfigResult { + ip_config: IpConfig { + ..Default::default() + }, + warnings: vec![], + }; + let method4 = if self.ipv4.enabled && self.ipv4_static.is_some() { + Ipv4Method::Manual + } else if !self.ipv4.enabled { + Ipv4Method::Disabled } else { - "auto" - }) - .unwrap(); - let method6 = Ipv6Method::from_str(if val.ipv6.enabled && val.ipv6_static.is_some() { - "manual" - } else if val.ipv6.enabled - && val.ipv6_dhcp.is_some() - && val.ipv6_dhcp.as_ref().unwrap().mode == "managed" + Ipv4Method::Auto + }; + let method6 = if self.ipv6.enabled && self.ipv6_static.is_some() { + Ipv6Method::Manual + } else if self.ipv6.enabled + && self.ipv6_dhcp.is_some() + && self.ipv6_dhcp.as_ref().unwrap().mode == "managed" { - "dhcp" - } else if !val.ipv6.enabled { - "disabled" + Ipv6Method::Dhcp + } else if !self.ipv6.enabled { + Ipv6Method::Disabled } else { - "auto" - }) - .unwrap(); + Ipv6Method::Auto + }; let mut addresses: Vec = vec![]; let mut gateway4 = None; let mut gateway6 = None; - if let Some(ipv4_static) = &val.ipv4_static { + if let Some(ipv4_static) = &self.ipv4_static { if let Some(addresses_in) = &ipv4_static.addresses { for addr in addresses_in { addresses.push(IpInet::from_str(addr.local.as_str()).unwrap()); @@ -512,7 +528,9 @@ impl From<&Interface> for IpConfig { // the logged warning isn't really true for multiple hops // as gateways just can't have multiple nexthops AFAICT if gateway4.is_some() || nexthops.len() > 1 { - log::warn!("Multiple gateways aren't supported yet"); + connection_result.warnings.push(anyhow::anyhow!( + "Multipath routing isn't natively supported by NetworkManager" + )); } else { gateway4 = Some(IpAddr::from_str(&nexthops[0].gateway).unwrap()); } @@ -520,7 +538,7 @@ impl From<&Interface> for IpConfig { } } } - if let Some(ipv6_static) = &val.ipv6_static { + if let Some(ipv6_static) = &self.ipv6_static { if let Some(addresses_in) = &ipv6_static.addresses { for addr in addresses_in { addresses.push(IpInet::from_str(addr.local.as_str()).unwrap()); @@ -533,7 +551,9 @@ impl From<&Interface> for IpConfig { // the logged warning isn't really true for multiple hops // as gateways just can't have multiple nexthops AFAICT if gateway6.is_some() || nexthops.len() > 1 { - log::warn!("Multiple gateways aren't supported yet"); + connection_result.warnings.push(anyhow::anyhow!( + "Multipath routing isn't natively supported by NetworkManager" + )); } else { gateway6 = Some(IpAddr::from_str(&nexthops[0].gateway).unwrap()); } @@ -542,14 +562,15 @@ impl From<&Interface> for IpConfig { } } - IpConfig { + connection_result.ip_config = IpConfig { addresses, method4, method6, gateway4, gateway6, ..Default::default() - } + }; + Ok(connection_result) } } @@ -593,7 +614,8 @@ mod tests { ..Default::default() }; - let static_connection: model::Connection = static_interface.into(); + let static_connection: model::Connection = + static_interface.to_connection().unwrap().connection; assert_eq!( static_connection.base().ip_config.method4, Ipv4Method::Manual @@ -652,7 +674,8 @@ mod tests { ..Default::default() }; - let static_connection: model::Connection = static_interface.into(); + let static_connection: model::Connection = + static_interface.to_connection().unwrap().connection; assert_eq!(static_connection.base().ip_config.method4, Ipv4Method::Auto); assert_eq!(static_connection.base().ip_config.method6, Ipv6Method::Auto); assert_eq!(static_connection.base().ip_config.addresses.len(), 0); @@ -694,7 +717,7 @@ mod tests { address: Some(String::from("02:11:22:33:44:55")), }; - let bondconfig: model::BondConfig = bond.into(); + let bondconfig: model::BondConfig = (&bond).into(); let s = HashMap::from([ ("xmit_hash_policy", String::from("encap34")), ("packets_per_slave", 23.to_string()), diff --git a/rust/migrate-wicked/src/main.rs b/rust/migrate-wicked/src/main.rs index c593b6d6a8..fdfc30839b 100644 --- a/rust/migrate-wicked/src/main.rs +++ b/rust/migrate-wicked/src/main.rs @@ -8,6 +8,7 @@ use log::*; use migrate::migrate; use reader::read as wicked_read; use std::process::{ExitCode, Termination}; +use tokio::sync::OnceCell; #[derive(Parser)] #[command(name = "migrate-wicked", version, about, long_about = None)] @@ -40,6 +41,10 @@ pub enum Commands { Migrate { /// Wicked XML Files or directories where the wicked xml configs are located paths: Vec, + + /// Continue migration if warnings are encountered + #[arg(short, long, global = true)] + continue_migration: bool, }, } @@ -56,20 +61,37 @@ pub enum Format { async fn run_command(cli: Cli) -> anyhow::Result<()> { match cli.command { Commands::Show { paths, format } => { - let interfaces = wicked_read(paths)?; + MIGRATION_SETTINGS + .set(MigrationSettings { + continue_migration: true, + }) + .expect("MIGRATION_SETTINGS was set too early"); + + let interfaces_result = wicked_read(paths)?; let output: String = match format { - Format::Json => serde_json::to_string(&interfaces)?, - Format::PrettyJson => serde_json::to_string_pretty(&interfaces)?, - Format::Yaml => serde_yaml::to_string(&interfaces)?, - Format::Xml => quick_xml::se::to_string_with_root("interface", &interfaces)?, - Format::Text => format!("{:?}", interfaces), + Format::Json => serde_json::to_string(&interfaces_result.interfaces)?, + Format::PrettyJson => serde_json::to_string_pretty(&interfaces_result.interfaces)?, + Format::Yaml => serde_yaml::to_string(&interfaces_result.interfaces)?, + Format::Xml => { + quick_xml::se::to_string_with_root("interface", &interfaces_result.interfaces)? + } + Format::Text => format!("{:?}", interfaces_result.interfaces), }; println!("{}", output); Ok(()) } - Commands::Migrate { paths } => { - migrate(paths).await.unwrap(); - Ok(()) + Commands::Migrate { + paths, + continue_migration, + } => { + MIGRATION_SETTINGS + .set(MigrationSettings { continue_migration }) + .expect("MIGRATION_SETTINGS was set too early"); + + match migrate(paths).await { + Ok(()) => Ok(()), + Err(e) => Err(anyhow::anyhow!("Migration failed: {:?}", e)), + } } } } @@ -88,6 +110,13 @@ impl Termination for CliResult { } } +#[derive(Debug)] +struct MigrationSettings { + continue_migration: bool, +} + +static MIGRATION_SETTINGS: OnceCell = OnceCell::const_new(); + #[tokio::main] async fn main() -> CliResult { let cli = Cli::parse(); @@ -104,5 +133,6 @@ async fn main() -> CliResult { eprintln!("{:?}", error); return CliResult::Error; } + CliResult::Ok } diff --git a/rust/migrate-wicked/src/migrate.rs b/rust/migrate-wicked/src/migrate.rs index 060e3742b8..3e9a28a579 100644 --- a/rust/migrate-wicked/src/migrate.rs +++ b/rust/migrate-wicked/src/migrate.rs @@ -1,7 +1,6 @@ -use crate::reader::read as wicked_read; +use crate::{reader::read as wicked_read, MIGRATION_SETTINGS}; use agama_dbus_server::network::{model, Adapter, NetworkManagerAdapter, NetworkState}; use std::error::Error; -use tokio::{runtime::Handle, task}; struct WickedAdapter { paths: Vec, @@ -15,18 +14,32 @@ impl WickedAdapter { impl Adapter for WickedAdapter { fn read(&self) -> Result> { - task::block_in_place(|| { - Handle::current().block_on(async { - let interfaces = wicked_read(self.paths.clone())?; - let mut state = NetworkState::new(vec![], vec![]); - - for interface in interfaces { - let conn: model::Connection = interface.into(); - state.add_connection(conn)?; + let interfaces = wicked_read(self.paths.clone())?; + let settings = MIGRATION_SETTINGS.get().unwrap(); + + if !settings.continue_migration && interfaces.warning.is_some() { + return Err(interfaces.warning.unwrap().into()); + } + + let mut state = NetworkState::new(vec![], vec![]); + + for interface in interfaces.interfaces { + let connection_result = interface.to_connection()?; + if !connection_result.warnings.is_empty() { + for connection_error in &connection_result.warnings { + log::warn!("{}", connection_error); + } + if !settings.continue_migration { + return Err(anyhow::anyhow!( + "Migration of {} failed", + connection_result.connection.id() + ) + .into()); } - Ok(state) - }) - }) + } + state.add_connection(connection_result.connection)?; + } + Ok(state) } fn write(&self, _network: &model::NetworkState) -> Result<(), Box> { diff --git a/rust/migrate-wicked/src/reader.rs b/rust/migrate-wicked/src/reader.rs index 4778006283..1f6700caa7 100644 --- a/rust/migrate-wicked/src/reader.rs +++ b/rust/migrate-wicked/src/reader.rs @@ -1,15 +1,16 @@ use crate::interface::{Interface, ParentKind}; -use quick_xml::de::from_str; + use regex::Regex; use std::collections::HashMap; use std::fs::{self, read_dir}; use std::path::{Path, PathBuf}; -pub fn read_xml(str: &str) -> Result, quick_xml::DeError> { - from_str(replace_colons(str).as_str()) +pub struct InterfacesResult { + pub interfaces: Vec, + pub warning: Option, } -pub fn read_xml_file(path: PathBuf) -> Result, anyhow::Error> { +pub fn read_xml_file(path: PathBuf) -> Result { let contents = match fs::read_to_string(path.clone()) { Ok(contents) => contents, Err(e) => { @@ -20,7 +21,28 @@ pub fn read_xml_file(path: PathBuf) -> Result, anyhow::Error> { )) } }; - Ok(read_xml(contents.as_str())?) + let replaced_string = replace_colons(contents.as_str()); + let deserializer = &mut quick_xml::de::Deserializer::from_str(replaced_string.as_str()); + let mut unhandled_fields = vec![]; + let interfaces: Vec = serde_ignored::deserialize(deserializer, |path| { + unhandled_fields.push(path.to_string()); + })?; + let mut result = InterfacesResult { + interfaces, + warning: None, + }; + if !unhandled_fields.is_empty() { + for unused_str in unhandled_fields { + let split_str = unused_str.split_once('.').unwrap(); + log::warn!( + "Unhandled field in interface {}: {}", + result.interfaces[split_str.0.parse::().unwrap()].name, + split_str.1 + ); + } + result.warning = Some(anyhow::anyhow!("Unhandled fields")) + } + Ok(result) } fn replace_colons(colon_string: &str) -> String { @@ -69,21 +91,32 @@ fn recurse_files(path: impl AsRef) -> std::io::Result> { Ok(buf) } -pub fn read(paths: Vec) -> Result, anyhow::Error> { - let mut interfaces: Vec = vec![]; +pub fn read(paths: Vec) -> Result { + let mut result = InterfacesResult { + interfaces: vec![], + warning: None, + }; for path in paths { let path: PathBuf = path.into(); if path.is_dir() { let files = recurse_files(path)?; for file in files { - interfaces.append(&mut read_xml_file(file)?); + let mut read_xml = read_xml_file(file)?; + if result.warning.is_none() && read_xml.warning.is_some() { + result.warning = read_xml.warning + } + result.interfaces.append(&mut read_xml.interfaces); } } else { - interfaces.append(&mut read_xml_file(path)?); + let mut read_xml = read_xml_file(path)?; + if result.warning.is_none() && read_xml.warning.is_some() { + result.warning = read_xml.warning + } + result.interfaces.append(&mut read_xml.interfaces); } } - post_process_interface(&mut interfaces); - Ok(interfaces) + post_process_interface(&mut result.interfaces); + Ok(result) } #[cfg(test)] @@ -136,7 +169,10 @@ mod tests { "##; - let ifc = read_xml(xml).unwrap().pop().unwrap(); + let ifc = quick_xml::de::from_str::>(replace_colons(xml).as_str()) + .unwrap() + .pop() + .unwrap(); assert!(ifc.bond.is_some()); let bond = ifc.bond.unwrap(); @@ -191,7 +227,7 @@ mod tests { "##; - let err = read_xml(xml); + let err = quick_xml::de::from_str::>(replace_colons(xml).as_str()); assert!(err.is_err()); } } diff --git a/rust/migrate-wicked/tests/multipath_routing_failure/wicked_xml/single_gateway.xml b/rust/migrate-wicked/tests/multipath_routing_failure/wicked_xml/single_gateway.xml new file mode 100644 index 0000000000..3e26fe4126 --- /dev/null +++ b/rust/migrate-wicked/tests/multipath_routing_failure/wicked_xml/single_gateway.xml @@ -0,0 +1,50 @@ + + eth0 + + boot + + + + + true + true + + +
+ 192.168.101.5/24 +
+
+ 192.168.102.5/24 +
+ + 192.168.101.0/24 + + + 192.168.102.0/24 + + + + 192.168.102.1 + + + 192.168.102.2 + + 1 + +
+ + true + prefer-public + + +
+ 2001:db8:1::5/64 +
+ + + 2001:db8:1::1 + + 1 + +
+
diff --git a/rust/migrate-wicked/tests/test.sh b/rust/migrate-wicked/tests/test.sh index 869d923274..05b2398b2d 100755 --- a/rust/migrate-wicked/tests/test.sh +++ b/rust/migrate-wicked/tests/test.sh @@ -34,16 +34,30 @@ fi for test_dir in ${TEST_DIRS}; do echo -e "${BOLD}Testing ${test_dir}${NC}" + + if [[ $test_dir == *"failure" ]]; then + expect_fail=true + else + expect_fail=false + fi + $MIGRATE_WICKED_BIN show $test_dir/wicked_xml - if [ $? -ne 0 ]; then + if [ $? -ne 0 ] && [ "$expect_fail" = false ]; then error_msg ${test_dir} "show failed" RESULT=1 fi - $MIGRATE_WICKED_BIN migrate $test_dir/wicked_xml - if [ $? -ne 0 ]; then + + if [ "$expect_fail" = true ]; then + $MIGRATE_WICKED_BIN migrate $test_dir/wicked_xml + else + $MIGRATE_WICKED_BIN migrate -c $test_dir/wicked_xml + fi + if [ $? -ne 0 ] && [ "$expect_fail" = false ]; then error_msg ${test_dir} "migration failed" RESULT=1 continue + elif [ $? -ne 0 ] && [ "$expect_fail" = true ]; then + echo -e "${GREEN}Migration for $test_dir failed as expected${NC}" fi for cmp_file in $(ls $test_dir/system-connections/); do diff --unified=0 --color=always -I uuid $test_dir/system-connections/$cmp_file /etc/NetworkManager/system-connections/${cmp_file/\./\*.}