src/config/network.rs: auto-add lo, and implement a few regression tests
This commit is contained in:
parent
9094186a57
commit
1ca540a63b
|
@ -290,6 +290,20 @@ pub struct NetworkConfig {
|
||||||
order: Vec<NetworkOrderEntry>,
|
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 {
|
impl NetworkConfig {
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
@ -381,8 +395,9 @@ pub fn config() -> Result<(NetworkConfig, [u8;32]), Error> {
|
||||||
|
|
||||||
let digest = openssl::sha::sha256(&content);
|
let digest = openssl::sha::sha256(&content);
|
||||||
|
|
||||||
|
let existing_interfaces = get_network_interfaces()?;
|
||||||
let mut parser = NetworkParser::new(&content[..]);
|
let mut parser = NetworkParser::new(&content[..]);
|
||||||
let data = parser.parse_interfaces()?;
|
let data = parser.parse_interfaces(Some(&existing_interfaces))?;
|
||||||
|
|
||||||
Ok((data, digest))
|
Ok((data, digest))
|
||||||
}
|
}
|
||||||
|
@ -421,3 +436,55 @@ pub fn complete_interface_name(_arg: &str, _param: &HashMap<String, String>) ->
|
||||||
Err(_) => return vec![],
|
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::io::{BufRead};
|
||||||
use std::iter::{Peekable, Iterator};
|
use std::iter::{Peekable, Iterator};
|
||||||
use std::collections::HashSet;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use anyhow::{Error, bail, format_err};
|
use anyhow::{Error, bail, format_err};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
@ -294,12 +294,12 @@ impl <R: BufRead> NetworkParser<R> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_interfaces(&mut self) -> Result<NetworkConfig, Error> {
|
pub fn parse_interfaces(&mut self, existing_interfaces: Option<&HashMap<String, bool>>) -> Result<NetworkConfig, Error> {
|
||||||
self._parse_interfaces()
|
self._parse_interfaces(existing_interfaces)
|
||||||
.map_err(|err| format_err!("line {}: {}", self.line_nr, err))
|
.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 config = NetworkConfig::new();
|
||||||
|
|
||||||
let mut auto_flag: HashSet<String> = HashSet::new();
|
let mut auto_flag: HashSet<String> = HashSet::new();
|
||||||
|
@ -339,14 +339,13 @@ impl <R: BufRead> NetworkParser<R> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let existing_interfaces = get_network_interfaces()?;
|
|
||||||
|
|
||||||
lazy_static!{
|
lazy_static!{
|
||||||
static ref PHYSICAL_NIC_REGEX: Regex = Regex::new(r"^(?:eth\d+|en[^:.]+|ib\d+)$").unwrap();
|
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 INTERFACE_ALIAS_REGEX: Regex = Regex::new(r"^\S+:\d+$").unwrap();
|
||||||
static ref VLAN_INTERFACE_REGEX: Regex = Regex::new(r"^\S+\.\d+$").unwrap();
|
static ref VLAN_INTERFACE_REGEX: Regex = Regex::new(r"^\S+\.\d+$").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(existing_interfaces) = existing_interfaces {
|
||||||
for (iface, active) in existing_interfaces.iter() {
|
for (iface, active) in existing_interfaces.iter() {
|
||||||
if let Some(interface) = config.interfaces.get_mut(iface) {
|
if let Some(interface) = config.interfaces.get_mut(iface) {
|
||||||
interface.active = *active;
|
interface.active = *active;
|
||||||
|
@ -362,6 +361,7 @@ impl <R: BufRead> NetworkParser<R> {
|
||||||
config.order.push(NetworkOrderEntry::Iface(iface.to_string()));
|
config.order.push(NetworkOrderEntry::Iface(iface.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (name, interface) in config.interfaces.iter_mut() {
|
for (name, interface) in config.interfaces.iter_mut() {
|
||||||
if interface.interface_type != NetworkInterfaceType::Unknown { continue; }
|
if interface.interface_type != NetworkInterfaceType::Unknown { continue; }
|
||||||
|
@ -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)
|
Ok(config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue