diff --git a/src/link/link_info/bond.rs b/src/link/link_info/bond.rs index 26b38977..30156f7f 100644 --- a/src/link/link_info/bond.rs +++ b/src/link/link_info/bond.rs @@ -224,6 +224,27 @@ impl std::fmt::Display for BondMode { } } +impl std::str::FromStr for BondMode { + type Err = DecodeError; + + fn from_str(s: &str) -> Result { + Ok(match s { + "balance-rr" => Self::BalanceRr, + "active-backup" => Self::ActiveBackup, + "balance-xor" => Self::BalanceXor, + "broadcast" => Self::Broadcast, + "802.3ad" => Self::Ieee8023Ad, + "balance-tlb" => Self::BalanceTlb, + "balance-alb" => Self::BalanceAlb, + _ => { + return Err(DecodeError::from(format!( + "unknown bond mode: {s}" + ))) + } + }) + } +} + #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub enum BondArpValidate { @@ -286,6 +307,27 @@ impl std::fmt::Display for BondArpValidate { } } +impl std::str::FromStr for BondArpValidate { + type Err = DecodeError; + + fn from_str(s: &str) -> Result { + Ok(match s { + "none" => Self::None, + "active" => Self::Active, + "backup" => Self::Backup, + "all" => Self::All, + "filter" => Self::Filter, + "filter_active" => Self::FilterActive, + "filter_backup" => Self::FilterBackup, + _ => { + return Err(DecodeError::from(format!( + "unknown bond arp validate: {s}" + ))) + } + }) + } +} + #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub enum BondPrimaryReselect { @@ -332,6 +374,23 @@ impl std::fmt::Display for BondPrimaryReselect { } } +impl std::str::FromStr for BondPrimaryReselect { + type Err = DecodeError; + + fn from_str(s: &str) -> Result { + Ok(match s { + "always" => Self::Always, + "better" => Self::Better, + "failure" => Self::Failure, + _ => { + return Err(DecodeError::from(format!( + "unknown bond primary reselect: {s}" + ))) + } + }) + } +} + #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub enum BondXmitHashPolicy { @@ -390,6 +449,26 @@ impl std::fmt::Display for BondXmitHashPolicy { } } +impl std::str::FromStr for BondXmitHashPolicy { + type Err = DecodeError; + + fn from_str(s: &str) -> Result { + Ok(match s { + "layer2" => Self::Layer2, + "layer34" => Self::Layer34, + "layer23" => Self::Layer23, + "encap23" => Self::Encap23, + "encap34" => Self::Encap34, + "vlan-src-mac" => Self::VlanSrcMac, + _ => { + return Err(DecodeError::from(format!( + "unknown bond xmit hash policy: {s}" + ))) + } + }) + } +} + #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub enum BondArpAllTargets { @@ -432,6 +511,22 @@ impl std::fmt::Display for BondArpAllTargets { } } +impl std::str::FromStr for BondArpAllTargets { + type Err = DecodeError; + + fn from_str(s: &str) -> Result { + Ok(match s { + "any" => Self::Any, + "all" => Self::All, + _ => { + return Err(DecodeError::from(format!( + "unknown bond arp all targets: {s}" + ))) + } + }) + } +} + #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] #[non_exhaustive] pub enum BondFailOverMac { @@ -478,6 +573,23 @@ impl std::fmt::Display for BondFailOverMac { } } +impl std::str::FromStr for BondFailOverMac { + type Err = DecodeError; + + fn from_str(s: &str) -> Result { + Ok(match s { + "none" => Self::None, + "active" => Self::Active, + "follow" => Self::Follow, + _ => { + return Err(DecodeError::from(format!( + "unknown bond fail over mac: {s}" + ))) + } + }) + } +} + // Some attributes (ARP_IP_TARGET, NS_IP6_TARGET) contain a nested // list of IP addresses, where each element uses the index as NLA kind // and the address as value. InfoBond exposes vectors of IP addresses, @@ -643,6 +755,37 @@ impl From for u8 { } } +impl std::fmt::Display for BondAdSelect { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let kernel_name = match self { + BondAdSelect::Stable => "stable", + BondAdSelect::Bandwidth => "bandwidth", + BondAdSelect::Count => "count", + BondAdSelect::Other(d) => { + return write!(f, "unknown-variant ({d})") + } + }; + f.write_str(kernel_name) + } +} + +impl std::str::FromStr for BondAdSelect { + type Err = DecodeError; + + fn from_str(s: &str) -> Result { + Ok(match s { + "stable" => Self::Stable, + "bandwidth" => Self::Bandwidth, + "count" => Self::Count, + _ => { + return Err(DecodeError::from(format!( + "unknown bond ad select: {s}" + ))) + } + }) + } +} + #[derive(Debug, PartialEq, Eq, Clone)] #[non_exhaustive] pub enum InfoBond { diff --git a/src/link/tests/bond.rs b/src/link/tests/bond.rs index 5c73a623..62d50da5 100644 --- a/src/link/tests/bond.rs +++ b/src/link/tests/bond.rs @@ -230,3 +230,85 @@ fn test_bond_arp_validate() { assert_eq!(raw, buf); } + +#[test] +fn test_bond_mode_from_str() { + use std::str::FromStr; + assert_eq!(BondMode::BalanceRr, "balance-rr".parse().unwrap()); + assert_eq!(BondMode::ActiveBackup, "active-backup".parse().unwrap()); + assert_eq!(BondMode::BalanceXor, "balance-xor".parse().unwrap()); + assert_eq!(BondMode::Broadcast, "broadcast".parse().unwrap()); + assert_eq!(BondMode::Ieee8023Ad, "802.3ad".parse().unwrap()); + assert_eq!(BondMode::BalanceTlb, "balance-tlb".parse().unwrap()); + assert_eq!(BondMode::BalanceAlb, "balance-alb".parse().unwrap()); + assert!(BondMode::from_str("bogus").is_err()); +} + +#[test] +fn test_bond_arp_validate_from_str() { + use std::str::FromStr; + assert_eq!(BondArpValidate::None, "none".parse().unwrap()); + assert_eq!(BondArpValidate::Active, "active".parse().unwrap()); + assert_eq!(BondArpValidate::Backup, "backup".parse().unwrap()); + assert_eq!(BondArpValidate::All, "all".parse().unwrap()); + assert_eq!(BondArpValidate::Filter, "filter".parse().unwrap()); + assert_eq!( + BondArpValidate::FilterActive, + "filter_active".parse().unwrap() + ); + assert_eq!( + BondArpValidate::FilterBackup, + "filter_backup".parse().unwrap() + ); + assert!(BondArpValidate::from_str("bogus").is_err()); +} + +#[test] +fn test_bond_primary_reselect_from_str() { + use std::str::FromStr; + assert_eq!(BondPrimaryReselect::Always, "always".parse().unwrap()); + assert_eq!(BondPrimaryReselect::Better, "better".parse().unwrap()); + assert_eq!(BondPrimaryReselect::Failure, "failure".parse().unwrap()); + assert!(BondPrimaryReselect::from_str("bogus").is_err()); +} + +#[test] +fn test_bond_xmit_hash_policy_from_str() { + use std::str::FromStr; + assert_eq!(BondXmitHashPolicy::Layer2, "layer2".parse().unwrap()); + assert_eq!(BondXmitHashPolicy::Layer34, "layer34".parse().unwrap()); + assert_eq!(BondXmitHashPolicy::Layer23, "layer23".parse().unwrap()); + assert_eq!(BondXmitHashPolicy::Encap23, "encap23".parse().unwrap()); + assert_eq!(BondXmitHashPolicy::Encap34, "encap34".parse().unwrap()); + assert_eq!( + BondXmitHashPolicy::VlanSrcMac, + "vlan-src-mac".parse().unwrap() + ); + assert!(BondXmitHashPolicy::from_str("bogus").is_err()); +} + +#[test] +fn test_bond_arp_all_targets_from_str() { + use std::str::FromStr; + assert_eq!(BondArpAllTargets::Any, "any".parse().unwrap()); + assert_eq!(BondArpAllTargets::All, "all".parse().unwrap()); + assert!(BondArpAllTargets::from_str("bogus").is_err()); +} + +#[test] +fn test_bond_fail_over_mac_from_str() { + use std::str::FromStr; + assert_eq!(BondFailOverMac::None, "none".parse().unwrap()); + assert_eq!(BondFailOverMac::Active, "active".parse().unwrap()); + assert_eq!(BondFailOverMac::Follow, "follow".parse().unwrap()); + assert!(BondFailOverMac::from_str("bogus").is_err()); +} + +#[test] +fn test_bond_ad_select_from_str() { + use std::str::FromStr; + assert_eq!(BondAdSelect::Stable, "stable".parse().unwrap()); + assert_eq!(BondAdSelect::Bandwidth, "bandwidth".parse().unwrap()); + assert_eq!(BondAdSelect::Count, "count".parse().unwrap()); + assert!(BondAdSelect::from_str("bogus").is_err()); +}