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