diff --git a/src/api2/types.rs b/src/api2/types.rs index 52752e4e..7595a54c 100644 --- a/src/api2/types.rs +++ b/src/api2/types.rs @@ -586,6 +586,22 @@ pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema = ArraySchema::new( type: String, }, }, + comments_v4: { + description: "Comments (inet)", + type: Array, + items: { + description: "Comment line.", + type: String, + }, + }, + comments_v6: { + description: "Comments (inet6)", + type: Array, + items: { + description: "Comment line.", + type: String, + }, + }, bridge_ports: { schema: NETWORK_INTERFACE_LIST_SCHEMA, optional: true, @@ -629,6 +645,11 @@ pub struct Interface { #[serde(skip_serializing_if="Vec::is_empty")] pub options_v6: Vec, + #[serde(skip_serializing_if="Vec::is_empty")] + pub comments_v4: Vec, + #[serde(skip_serializing_if="Vec::is_empty")] + pub comments_v6: Vec, + #[serde(skip_serializing_if="Option::is_none")] /// Maximum Transmission Unit pub mtu: Option, diff --git a/src/config/network.rs b/src/config/network.rs index 0648678c..12e13b8c 100644 --- a/src/config/network.rs +++ b/src/config/network.rs @@ -32,6 +32,8 @@ impl Interface { gateway_v6: None, options_v4: Vec::new(), options_v6: Vec::new(), + comments_v4: Vec::new(), + comments_v6: Vec::new(), mtu: None, bridge_ports: None, bond_slaves: None, @@ -117,14 +119,6 @@ impl Interface { Ok(()) } - fn push_addon_option(&mut self, text: String) { - if self.method_v4.is_none() && self.method_v6.is_some() { - self.options_v6.push(text); - } else { - self.options_v4.push(text); - } - } - /// Write attributes not dependening on address family fn write_iface_attributes(&self, w: &mut dyn Write) -> Result<(), Error> { @@ -172,6 +166,10 @@ impl Interface { writeln!(w, " {}", option)?; } + for comment in &self.comments_v4 { + writeln!(w, "#4{}", comment)?; + } + Ok(()) } @@ -190,6 +188,10 @@ impl Interface { writeln!(w, " {}", option)?; } + for comment in &self.comments_v6 { + writeln!(w, "#6{}", comment)?; + } + Ok(()) } @@ -202,6 +204,8 @@ impl Interface { method_v6, options_v4, options_v6, + comments_v4, + comments_v6, // the rest does not matter name: _name, interface_type: _interface_type, @@ -216,6 +220,8 @@ impl Interface { bond_slaves: _bond_slaves, } => { method_v4 == method_v6 + && comments_v4.is_empty() + && comments_v6.is_empty() && options_v4.is_empty() && options_v6.is_empty() } @@ -257,7 +263,9 @@ impl Interface { if let Some(method) = self.method_v6 { writeln!(w, "iface {} inet6 {}", self.name, method_to_str(method))?; self.write_iface_attributes_v6(w, method)?; - self.write_iface_attributes(w)?; + if self.method_v4.is_none() { // only write common attributes once + self.write_iface_attributes(w)?; + } writeln!(w)?; } } diff --git a/src/config/network/parser.rs b/src/config/network/parser.rs index 82446517..afb6c4ac 100644 --- a/src/config/network/parser.rs +++ b/src/config/network/parser.rs @@ -59,12 +59,12 @@ impl NetworkParser { } } - fn eat(&mut self, expected: Token) -> Result<(), Error> { - let (next, _) = self.next()?; + fn eat(&mut self, expected: Token) -> Result { + let (next, text) = self.next()?; if next != expected { bail!("expected {:?}, got {:?}", expected, next); } - Ok(()) + Ok(text) } fn parse_auto(&mut self, auto_flag: &mut HashSet) -> Result<(), Error> { @@ -150,12 +150,6 @@ impl NetworkParser { } } - fn parse_iface_addon_attribute(&mut self, interface: &mut Interface) -> Result<(), Error> { - let option = self.parse_to_eol()?; - if !option.is_empty() { interface.push_addon_option(option) }; - Ok(()) - } - fn parse_iface_list(&mut self) -> Result, Error> { let mut list = Vec::new(); @@ -175,13 +169,29 @@ impl NetworkParser { Ok(list) } - fn parse_iface_attributes(&mut self, interface: &mut Interface) -> Result<(), Error> { + fn parse_iface_attributes( + &mut self, + interface: &mut Interface, + address_family_v4: bool, + address_family_v6: bool, + ) -> Result<(), Error> { loop { match self.peek()? { - Token::Attribute => self.eat(Token::Attribute)?, + Token::Attribute => { self.eat(Token::Attribute)?; }, + Token::Comment => { + let comment = self.eat(Token::Comment)?; + if !address_family_v4 && address_family_v6 { + interface.comments_v6.push(comment); + } else { + interface.comments_v4.push(comment); + } + self.eat(Token::Newline)?; + continue; + } Token::Newline => break, - unexpected => bail!("unknown token {:?}", unexpected), + Token::EOF => break, + unexpected => bail!("unexpected token {:?} (expected iface attribute)", unexpected), } match self.peek()? { @@ -204,9 +214,17 @@ impl NetworkParser { interface.set_interface_type(NetworkInterfaceType::Bond)?; } Token::Netmask => bail!("netmask is deprecated and no longer supported"), - _ => { - self.parse_iface_addon_attribute(interface)?; - }, + + _ => { // parse addon attributes + let option = self.parse_to_eol()?; + if !option.is_empty() { + if !address_family_v4 && address_family_v6 { + interface.options_v6.push(option); + } else { + interface.options_v4.push(option); + } + }; + }, } } @@ -235,7 +253,6 @@ impl NetworkParser { } } - let has_attributes = self.peek()? == Token::Attribute; let config_method = config_method.unwrap_or(NetworkConfigMethod::Static); if !(address_family_v4 || address_family_v6) { @@ -251,7 +268,7 @@ impl NetworkParser { interface.set_method_v6(config_method)?; } - if has_attributes { self.parse_iface_attributes(&mut interface)?; } + self.parse_iface_attributes(&mut interface, address_family_v4, address_family_v6)?; } else { let mut interface = Interface::new(iface.clone()); if address_family_v4 { @@ -261,7 +278,7 @@ impl NetworkParser { interface.set_method_v6(config_method)?; } - if has_attributes { self.parse_iface_attributes(&mut interface)?; } + self.parse_iface_attributes(&mut interface, address_family_v4, address_family_v6)?; config.interfaces.insert(interface.name.clone(), interface);