src/config/network.rs: auto-add lo, and implement a few regression tests
This commit is contained in:
		| @ -290,6 +290,20 @@ pub struct NetworkConfig { | ||||
|     order: Vec<NetworkOrderEntry>, | ||||
| } | ||||
|  | ||||
| use std::convert::TryFrom; | ||||
|  | ||||
| impl TryFrom<NetworkConfig> for String  { | ||||
|  | ||||
|     type Error = Error; | ||||
|  | ||||
|     fn try_from(config: NetworkConfig) -> Result<Self, Self::Error> { | ||||
|         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<String, String>) -> | ||||
|         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(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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 <R: BufRead> NetworkParser<R> { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     pub fn parse_interfaces(&mut self) -> Result<NetworkConfig, Error> { | ||||
|         self._parse_interfaces() | ||||
|     pub fn parse_interfaces(&mut self, existing_interfaces: Option<&HashMap<String, bool>>) -> Result<NetworkConfig, Error> { | ||||
|         self._parse_interfaces(existing_interfaces) | ||||
|             .map_err(|err| format_err!("line {}: {}", self.line_nr, err)) | ||||
|     } | ||||
|  | ||||
|     pub fn _parse_interfaces(&mut self) -> Result<NetworkConfig, Error> { | ||||
|     pub fn _parse_interfaces(&mut self, existing_interfaces: Option<&HashMap<String, bool>>) -> Result<NetworkConfig, Error> { | ||||
|         let mut config = NetworkConfig::new(); | ||||
|  | ||||
|         let mut auto_flag: HashSet<String> = HashSet::new(); | ||||
| @ -339,27 +339,27 @@ impl <R: BufRead> NetworkParser<R> { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         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 <R: BufRead> NetworkParser<R> { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         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) | ||||
|     } | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user