diff --git a/src/config/network.rs b/src/config/network.rs index 1b7d02cc..1b739a37 100644 --- a/src/config/network.rs +++ b/src/config/network.rs @@ -290,6 +290,20 @@ pub struct NetworkConfig { order: Vec, } +use std::convert::TryFrom; + +impl TryFrom for String { + + type Error = Error; + + fn try_from(config: NetworkConfig) -> Result { + let mut output = Vec::new(); + config.write_config(&mut output)?; + let res = String::from_utf8(output)?; + Ok(res) + } +} + impl NetworkConfig { pub fn new() -> Self { @@ -381,8 +395,9 @@ pub fn config() -> Result<(NetworkConfig, [u8;32]), Error> { let digest = openssl::sha::sha256(&content); + let existing_interfaces = get_network_interfaces()?; let mut parser = NetworkParser::new(&content[..]); - let data = parser.parse_interfaces()?; + let data = parser.parse_interfaces(Some(&existing_interfaces))?; Ok((data, digest)) } @@ -421,3 +436,55 @@ pub fn complete_interface_name(_arg: &str, _param: &HashMap) -> Err(_) => return vec![], } } + +#[cfg(test)] +mod test { + + use anyhow::{Error}; + + use super::*; + + #[test] + fn test_network_config_create_lo_1() -> Result<(), Error> { + + let input = ""; + + let mut parser = NetworkParser::new(&input.as_bytes()[..]); + + let config = parser.parse_interfaces(None)?; + + let output = String::try_from(config)?; + + let expected = "auto lo\niface lo inet loopback\n\n"; + assert_eq!(output, expected); + + // run again using output as input + let mut parser = NetworkParser::new(&output.as_bytes()[..]); + + let config = parser.parse_interfaces(None)?; + + let output = String::try_from(config)?; + + assert_eq!(output, expected); + + Ok(()) + } + + #[test] + fn test_network_config_create_lo_2() -> Result<(), Error> { + + let input = "#c1\n\n#c2\n\niface test inet manual\n"; + + let mut parser = NetworkParser::new(&input.as_bytes()[..]); + + let config = parser.parse_interfaces(None)?; + + let output = String::try_from(config)?; + + // Note: loopback should be added in front of other interfaces + let expected = "#c1\n#c2\n\nauto lo\niface lo inet loopback\n\niface test inet manual\n\n"; + assert_eq!(output, expected); + + Ok(()) + } +} diff --git a/src/config/network/parser.rs b/src/config/network/parser.rs index c63c3477..c4ab538a 100644 --- a/src/config/network/parser.rs +++ b/src/config/network/parser.rs @@ -1,6 +1,6 @@ use std::io::{BufRead}; use std::iter::{Peekable, Iterator}; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use anyhow::{Error, bail, format_err}; use lazy_static::lazy_static; @@ -294,12 +294,12 @@ impl NetworkParser { Ok(()) } - pub fn parse_interfaces(&mut self) -> Result { - self._parse_interfaces() + pub fn parse_interfaces(&mut self, existing_interfaces: Option<&HashMap>) -> Result { + self._parse_interfaces(existing_interfaces) .map_err(|err| format_err!("line {}: {}", self.line_nr, err)) } - pub fn _parse_interfaces(&mut self) -> Result { + pub fn _parse_interfaces(&mut self, existing_interfaces: Option<&HashMap>) -> Result { let mut config = NetworkConfig::new(); let mut auto_flag: HashSet = HashSet::new(); @@ -339,27 +339,27 @@ impl NetworkParser { } } - let existing_interfaces = get_network_interfaces()?; - lazy_static!{ static ref PHYSICAL_NIC_REGEX: Regex = Regex::new(r"^(?:eth\d+|en[^:.]+|ib\d+)$").unwrap(); static ref INTERFACE_ALIAS_REGEX: Regex = Regex::new(r"^\S+:\d+$").unwrap(); static ref VLAN_INTERFACE_REGEX: Regex = Regex::new(r"^\S+\.\d+$").unwrap(); } - for (iface, active) in existing_interfaces.iter() { - if let Some(interface) = config.interfaces.get_mut(iface) { - interface.active = *active; - if interface.interface_type == NetworkInterfaceType::Unknown { + if let Some(existing_interfaces) = existing_interfaces { + for (iface, active) in existing_interfaces.iter() { + if let Some(interface) = config.interfaces.get_mut(iface) { + interface.active = *active; + if interface.interface_type == NetworkInterfaceType::Unknown { + interface.interface_type = NetworkInterfaceType::Ethernet; + } + } else if PHYSICAL_NIC_REGEX.is_match(iface) { // also add all physical NICs + let mut interface = Interface::new(iface.clone()); + interface.set_method_v4(NetworkConfigMethod::Manual)?; interface.interface_type = NetworkInterfaceType::Ethernet; + interface.active = *active; + config.interfaces.insert(interface.name.clone(), interface); + config.order.push(NetworkOrderEntry::Iface(iface.to_string())); } - } else if PHYSICAL_NIC_REGEX.is_match(iface) { // also add all physical NICs - let mut interface = Interface::new(iface.clone()); - interface.set_method_v4(NetworkConfigMethod::Manual)?; - interface.interface_type = NetworkInterfaceType::Ethernet; - interface.active = *active; - config.interfaces.insert(interface.name.clone(), interface); - config.order.push(NetworkOrderEntry::Iface(iface.to_string())); } } @@ -383,6 +383,32 @@ impl NetworkParser { } } + if config.interfaces.get("lo").is_none() { + let mut interface = Interface::new(String::from("lo")); + interface.set_method_v4(NetworkConfigMethod::Loopback)?; + interface.interface_type = NetworkInterfaceType::Loopback; + interface.auto = true; + config.interfaces.insert(interface.name.clone(), interface); + + // Note: insert 'lo' as first interface after initial comments + let mut new_order = Vec::new(); + let mut added_lo = false; + for entry in config.order { + if added_lo { new_order.push(entry); continue; } // copy the rest + match entry { + NetworkOrderEntry::Comment(_) => { + new_order.push(entry); + } + _ => { + new_order.push(NetworkOrderEntry::Iface(String::from("lo"))); + added_lo = true; + new_order.push(entry); + } + } + } + config.order = new_order; + } + Ok(config) } }