src/config/network.rs: implement bond_mode
and rename bond_slaves to slaves to make it compatible with pve.
This commit is contained in:
		| @ -187,7 +187,11 @@ pub fn read_interface(iface: String) -> Result<Value, Error> { | ||||
| 	        type: bool, | ||||
| 	        optional: true, | ||||
|             }, | ||||
|             bond_slaves: { | ||||
|             bond_mode: { | ||||
|                 type: LinuxBondMode, | ||||
|                 optional: true, | ||||
|             }, | ||||
|             slaves: { | ||||
|                 schema: NETWORK_INTERFACE_LIST_SCHEMA, | ||||
|                 optional: true, | ||||
|             }, | ||||
| @ -212,7 +216,8 @@ pub fn create_interface( | ||||
|     mtu: Option<u64>, | ||||
|     bridge_ports: Option<Vec<String>>, | ||||
|     bridge_vlan_aware: Option<bool>, | ||||
|     bond_slaves: Option<Vec<String>>, | ||||
|     bond_mode: Option<LinuxBondMode>, | ||||
|     slaves: Option<Vec<String>>, | ||||
|     param: Value, | ||||
| ) -> Result<(), Error> { | ||||
|  | ||||
| @ -269,7 +274,8 @@ pub fn create_interface( | ||||
|             if bridge_vlan_aware.is_some() { interface.bridge_vlan_aware = bridge_vlan_aware; } | ||||
|         } | ||||
|         NetworkInterfaceType::Bond => { | ||||
|             if let Some(slaves) = bond_slaves { interface.set_bond_slaves(slaves)?; } | ||||
|             if bond_mode.is_some() { interface.bond_mode = bond_mode; } | ||||
|             if let Some(slaves) = slaves { interface.set_bond_slaves(slaves)?; } | ||||
|         } | ||||
|         _ => bail!("creating network interface type '{:?}' is not supported", interface_type), | ||||
|     } | ||||
| @ -323,7 +329,7 @@ pub enum DeletableProperty { | ||||
|     /// Delet bridge-vlan-aware flag | ||||
|     bridge_vlan_aware, | ||||
|     /// Delete bond-slaves (set to 'none') | ||||
|     bond_slaves, | ||||
|     slaves, | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -397,7 +403,11 @@ pub enum DeletableProperty { | ||||
| 	        type: bool, | ||||
| 	        optional: true, | ||||
|             }, | ||||
|             bond_slaves: { | ||||
|             bond_mode: { | ||||
|                 type: LinuxBondMode, | ||||
|                 optional: true, | ||||
|             }, | ||||
|             slaves: { | ||||
|                 schema: NETWORK_INTERFACE_LIST_SCHEMA, | ||||
|                 optional: true, | ||||
|             }, | ||||
| @ -434,7 +444,8 @@ pub fn update_interface( | ||||
|     mtu: Option<u64>, | ||||
|     bridge_ports: Option<Vec<String>>, | ||||
|     bridge_vlan_aware: Option<bool>, | ||||
|     bond_slaves: Option<Vec<String>>, | ||||
|     bond_mode: Option<LinuxBondMode>, | ||||
|     slaves: Option<Vec<String>>, | ||||
|     delete: Option<Vec<DeletableProperty>>, | ||||
|     digest: Option<String>, | ||||
|     param: Value, | ||||
| @ -476,7 +487,7 @@ pub fn update_interface( | ||||
|                 DeletableProperty::autostart => { interface.autostart = false; }, | ||||
|                 DeletableProperty::bridge_ports => { interface.set_bridge_ports(Vec::new())?; } | ||||
|                 DeletableProperty::bridge_vlan_aware => { interface.bridge_vlan_aware = None; } | ||||
|                 DeletableProperty::bond_slaves => { interface.set_bond_slaves(Vec::new())?; } | ||||
|                 DeletableProperty::slaves => { interface.set_bond_slaves(Vec::new())?; } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @ -487,7 +498,8 @@ pub fn update_interface( | ||||
|     if mtu.is_some() { interface.mtu = mtu; } | ||||
|     if let Some(ports) = bridge_ports { interface.set_bridge_ports(ports)?; } | ||||
|     if bridge_vlan_aware.is_some() { interface.bridge_vlan_aware = bridge_vlan_aware; } | ||||
|     if let Some(slaves) = bond_slaves { interface.set_bond_slaves(slaves)?; } | ||||
|     if let Some(slaves) = slaves { interface.set_bond_slaves(slaves)?; } | ||||
|     if bond_mode.is_some() { interface.bond_mode = bond_mode; } | ||||
|  | ||||
|     if let Some(cidr) = cidr { | ||||
|         let (_, _, is_v6) = network::parse_cidr(&cidr)?; | ||||
|  | ||||
| @ -549,6 +549,30 @@ pub enum NetworkConfigMethod { | ||||
|     Loopback, | ||||
| } | ||||
|  | ||||
| #[api()] | ||||
| #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] | ||||
| #[serde(rename_all = "kebab-case")] | ||||
| #[allow(non_camel_case_types)] | ||||
| #[repr(u8)] | ||||
| /// Linux Bond Mode | ||||
| pub enum LinuxBondMode { | ||||
|     /// Round-robin policy | ||||
|     balance_rr = 0, | ||||
|     /// Active-backup policy | ||||
|     active_backup = 1, | ||||
|     /// XOR policy | ||||
|     balance_xor = 2, | ||||
|     /// Broadcast policy | ||||
|     broadcast = 3, | ||||
|     /// IEEE 802.3ad Dynamic link aggregation | ||||
|     //#[serde(rename = "802.3ad")] | ||||
|     ieee802_3ad = 4, | ||||
|     /// Adaptive transmit load balancing | ||||
|     balance_tlb = 5, | ||||
|     /// Adaptive load balancing | ||||
|     balance_alb = 6, | ||||
| } | ||||
|  | ||||
| #[api()] | ||||
| #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] | ||||
| #[serde(rename_all = "lowercase")] | ||||
| @ -642,10 +666,14 @@ pub const NETWORK_INTERFACE_LIST_SCHEMA: Schema = ArraySchema::new( | ||||
|             schema: NETWORK_INTERFACE_LIST_SCHEMA, | ||||
|             optional: true, | ||||
|         }, | ||||
|         bond_slaves: { | ||||
|         slaves: { | ||||
|             schema: NETWORK_INTERFACE_LIST_SCHEMA, | ||||
|             optional: true, | ||||
|         }, | ||||
|         bond_mode: { | ||||
|             type: LinuxBondMode, | ||||
|             optional: true, | ||||
|         } | ||||
|     } | ||||
| )] | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
| @ -699,7 +727,9 @@ pub struct Interface { | ||||
|     pub bridge_vlan_aware: Option<bool>, | ||||
|  | ||||
|     #[serde(skip_serializing_if="Option::is_none")] | ||||
|     pub bond_slaves: Option<Vec<String>>, | ||||
|     pub slaves: Option<Vec<String>>, | ||||
|     #[serde(skip_serializing_if="Option::is_none")] | ||||
|     pub bond_mode: Option<LinuxBondMode>, | ||||
| } | ||||
|  | ||||
| // Regression tests | ||||
|  | ||||
| @ -2,6 +2,7 @@ use std::io::{Write}; | ||||
| use std::collections::{HashSet, HashMap}; | ||||
|  | ||||
| use anyhow::{Error, format_err, bail}; | ||||
| use serde::de::{value, IntoDeserializer, Deserialize}; | ||||
|  | ||||
| use proxmox::tools::{fs::replace_file, fs::CreateOptions}; | ||||
|  | ||||
| @ -14,7 +15,24 @@ pub use lexer::*; | ||||
| mod parser; | ||||
| pub use parser::*; | ||||
|  | ||||
| use crate::api2::types::{Interface, NetworkConfigMethod, NetworkInterfaceType}; | ||||
| use crate::api2::types::{Interface, NetworkConfigMethod, NetworkInterfaceType, LinuxBondMode}; | ||||
|  | ||||
| pub fn bond_mode_from_str(s: &str) -> Result<LinuxBondMode, Error> { | ||||
|     LinuxBondMode::deserialize(s.into_deserializer()) | ||||
|         .map_err(|_: value::Error| format_err!("invalid bond_mode '{}'", s)) | ||||
| } | ||||
|  | ||||
| pub fn bond_mode_to_str(mode: LinuxBondMode) -> &'static str { | ||||
|     match mode { | ||||
|         LinuxBondMode::balance_rr => "balance-rr", | ||||
|         LinuxBondMode::active_backup => "active-backup", | ||||
|         LinuxBondMode::balance_xor => "balance-xor", | ||||
|         LinuxBondMode::broadcast => "broadcast", | ||||
|         LinuxBondMode::ieee802_3ad => "802.3ad", | ||||
|         LinuxBondMode::balance_tlb => "balance-tlb", | ||||
|         LinuxBondMode::balance_alb => "balance-alb", | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Interface { | ||||
|  | ||||
| @ -37,7 +55,8 @@ impl Interface { | ||||
|             mtu: None, | ||||
|             bridge_ports: None, | ||||
|             bridge_vlan_aware: None, | ||||
|             bond_slaves: None, | ||||
|             slaves: None, | ||||
|             bond_mode: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @ -116,7 +135,7 @@ impl Interface { | ||||
|         if self.interface_type != NetworkInterfaceType::Bond { | ||||
|             bail!("interface '{}' is no bond (type is {:?})", self.name, self.interface_type); | ||||
|         } | ||||
|         self.bond_slaves = Some(slaves); | ||||
|         self.slaves = Some(slaves); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
| @ -137,7 +156,10 @@ impl Interface { | ||||
|                 } | ||||
|             } | ||||
|             NetworkInterfaceType::Bond => { | ||||
|                 if let Some(ref slaves) = self.bond_slaves { | ||||
|                 let mode = self.bond_mode.unwrap_or(LinuxBondMode::balance_rr); | ||||
|                 writeln!(w, "\tbond-mode {}", bond_mode_to_str(mode))?; | ||||
|  | ||||
|                 if let Some(ref slaves) = self.slaves { | ||||
|                     if slaves.is_empty() { | ||||
|                         writeln!(w, "\tbond-slaves none")?; | ||||
|                     } else { | ||||
| @ -226,7 +248,8 @@ impl Interface { | ||||
|                 mtu: _mtu, | ||||
|                 bridge_ports: _bridge_ports, | ||||
|                 bridge_vlan_aware: _bridge_vlan_aware, | ||||
|                 bond_slaves: _bond_slaves, | ||||
|                 slaves: _slaves, | ||||
|                 bond_mode: _bond_mode, | ||||
|             } => { | ||||
|                 method == method6 | ||||
|                     && comments.is_none() | ||||
| @ -264,20 +287,20 @@ impl Interface { | ||||
|             } | ||||
|             return Ok(()); | ||||
|         } | ||||
|                      | ||||
|  | ||||
|         if let Some(method) = self.method { | ||||
|             writeln!(w, "iface {} inet {}", self.name, method_to_str(method))?; | ||||
|             self.write_iface_attributes_v4(w, method)?; | ||||
|             self.write_iface_attributes(w)?; | ||||
|             writeln!(w)?; | ||||
|         } | ||||
|          | ||||
|  | ||||
|         if let Some(method6) = self.method6 { | ||||
|             let mut skip_v6 = false; // avoid empty inet6 manual entry | ||||
|             if self.method.is_some() && method6 == NetworkConfigMethod::Manual { | ||||
|                 if self.comments6.is_none() && self.options6.is_empty() { skip_v6 = true; } | ||||
|             } | ||||
|         | ||||
|  | ||||
|             if !skip_v6 { | ||||
|                 writeln!(w, "iface {} inet6 {}", self.name, method_to_str(method6))?; | ||||
|                 self.write_iface_attributes_v6(w, method6)?; | ||||
|  | ||||
| @ -25,6 +25,7 @@ pub enum Token { | ||||
|     BridgePorts, | ||||
|     BridgeVlanAware, | ||||
|     BondSlaves, | ||||
|     BondMode, | ||||
|     EOF, | ||||
| } | ||||
|  | ||||
| @ -49,6 +50,8 @@ lazy_static! { | ||||
|         map.insert("bridge_vlan_aware", Token::BridgeVlanAware); | ||||
|         map.insert("bond-slaves", Token::BondSlaves); | ||||
|         map.insert("bond_slaves", Token::BondSlaves); | ||||
|         map.insert("bond-mode", Token::BondMode); | ||||
|         map.insert("bond_mode", Token::BondMode); | ||||
|         map | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @ -9,7 +9,7 @@ use regex::Regex; | ||||
| use super::helper::*; | ||||
| use super::lexer::*; | ||||
|  | ||||
| use super::{NetworkConfig, NetworkOrderEntry, Interface, NetworkConfigMethod, NetworkInterfaceType}; | ||||
| use super::{NetworkConfig, NetworkOrderEntry, Interface, NetworkConfigMethod, NetworkInterfaceType, bond_mode_from_str}; | ||||
|  | ||||
| pub struct NetworkParser<R: BufRead> { | ||||
|     input: Peekable<Lexer<R>>, | ||||
| @ -236,9 +236,15 @@ impl <R: BufRead> NetworkParser<R> { | ||||
|                 Token::BondSlaves => { | ||||
|                     self.eat(Token::BondSlaves)?; | ||||
|                     let slaves = self.parse_iface_list()?; | ||||
|                     interface.bond_slaves = Some(slaves); | ||||
|                     interface.slaves = Some(slaves); | ||||
|                     interface.set_interface_type(NetworkInterfaceType::Bond)?; | ||||
|                 } | ||||
|                 Token::BondMode => { | ||||
|                     self.eat(Token::BondMode)?; | ||||
|                     let mode = self.next_text()?; | ||||
|                     interface.bond_mode = Some(bond_mode_from_str(&mode)?); | ||||
|                     self.eat(Token::Newline)?; | ||||
|                 } | ||||
|                 Token::Netmask => bail!("netmask is deprecated and no longer supported"), | ||||
|  | ||||
|                 _ => { // parse addon attributes | ||||
|  | ||||
		Reference in New Issue
	
	Block a user