use std::collections::HashMap;
use std::net::{Ipv4Addr, Ipv6Addr};
use netlink_packet_route::link::{self, InfoBond, InfoBondPort, InfoData};
use serde::{Deserialize, Serialize};
use super::super::mac::parse_as_mac;
use crate::{ControllerType, Iface, IfaceType, NisporError};
const BOND_MODE_ROUNDROBIN: u8 = 0;
const BOND_MODE_ACTIVEBACKUP: u8 = 1;
const BOND_MODE_XOR: u8 = 2;
const BOND_MODE_BROADCAST: u8 = 3;
const BOND_MODE_8023AD: u8 = 4;
const BOND_MODE_TLB: u8 = 5;
const BOND_MODE_ALB: u8 = 6;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum BondMode {
#[serde(rename = "balance-rr")]
BalanceRoundRobin,
#[serde(rename = "active-backup")]
ActiveBackup,
#[serde(rename = "balance-xor")]
BalanceXor,
#[serde(rename = "broadcast")]
Broadcast,
#[serde(rename = "802.3ad")]
Ieee8021AD,
#[serde(rename = "balance-tlb")]
BalanceTlb,
#[serde(rename = "balance-alb")]
BalanceAlb,
Other(u8),
Unknown,
}
impl Default for BondMode {
fn default() -> Self {
Self::BalanceRoundRobin
}
}
impl From<u8> for BondMode {
fn from(d: u8) -> Self {
match d {
BOND_MODE_ROUNDROBIN => Self::BalanceRoundRobin,
BOND_MODE_ACTIVEBACKUP => Self::ActiveBackup,
BOND_MODE_XOR => Self::BalanceXor,
BOND_MODE_BROADCAST => Self::Broadcast,
BOND_MODE_8023AD => Self::Ieee8021AD,
BOND_MODE_TLB => Self::BalanceTlb,
BOND_MODE_ALB => Self::BalanceAlb,
_ => Self::Other(d),
}
}
}
impl From<BondMode> for u8 {
fn from(v: BondMode) -> u8 {
match v {
BondMode::BalanceRoundRobin => BOND_MODE_ROUNDROBIN,
BondMode::ActiveBackup => BOND_MODE_ACTIVEBACKUP,
BondMode::BalanceXor => BOND_MODE_XOR,
BondMode::Broadcast => BOND_MODE_BROADCAST,
BondMode::Ieee8021AD => BOND_MODE_8023AD,
BondMode::BalanceTlb => BOND_MODE_TLB,
BondMode::BalanceAlb => BOND_MODE_ALB,
BondMode::Other(d) => d,
BondMode::Unknown => {
log::warn!(
"Treating BondMode::Unknown as \
BondMode::BalanceRoundRobin"
);
BOND_MODE_ROUNDROBIN
}
}
}
}
impl std::fmt::Display for BondMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BalanceRoundRobin => write!(f, "balance-rr"),
Self::ActiveBackup => write!(f, "active-backup"),
Self::BalanceXor => write!(f, "balance-xor"),
Self::Broadcast => write!(f, "broadcast"),
Self::Ieee8021AD => write!(f, "802.3ad"),
Self::BalanceTlb => write!(f, "balance-tlb"),
Self::BalanceAlb => write!(f, "balance-alb"),
Self::Other(u) => write!(f, "{u}"),
Self::Unknown => write!(f, "unknown"),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum BondModeArpAllTargets {
Any,
All,
Other(u32),
}
const BOND_OPT_ARP_ALL_TARGETS_ANY: u32 = 0;
const BOND_OPT_ARP_ALL_TARGETS_ALL: u32 = 1;
impl From<u32> for BondModeArpAllTargets {
fn from(d: u32) -> Self {
match d {
BOND_OPT_ARP_ALL_TARGETS_ANY => Self::Any,
BOND_OPT_ARP_ALL_TARGETS_ALL => Self::All,
_ => Self::Other(d),
}
}
}
const BOND_ARP_VALIDATE_NONE: u32 = 0;
const BOND_ARP_VALIDATE_ACTIVE: u32 = 1 << BOND_STATE_ACTIVE as u32;
const BOND_ARP_VALIDATE_BACKUP: u32 = 1 << BOND_STATE_BACKUP as u32;
const BOND_ARP_VALIDATE_ALL: u32 =
BOND_ARP_VALIDATE_ACTIVE | BOND_ARP_VALIDATE_BACKUP;
const BOND_ARP_FILTER: u32 = BOND_ARP_VALIDATE_ALL + 1;
const BOND_ARP_FILTER_ACTIVE: u32 = BOND_ARP_VALIDATE_ACTIVE | BOND_ARP_FILTER;
const BOND_ARP_FILTER_BACKUP: u32 = BOND_ARP_VALIDATE_BACKUP | BOND_ARP_FILTER;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum BondArpValidate {
None,
Active,
Backup,
All,
Filter,
#[serde(rename = "filter_active")]
FilterActive,
#[serde(rename = "filter_backkup")]
FilterBackup,
Other(u32),
}
impl From<u32> for BondArpValidate {
fn from(d: u32) -> Self {
match d {
BOND_ARP_VALIDATE_NONE => Self::None,
BOND_ARP_VALIDATE_ACTIVE => Self::Active,
BOND_ARP_VALIDATE_BACKUP => Self::Backup,
BOND_ARP_VALIDATE_ALL => Self::All,
BOND_ARP_FILTER => Self::Filter,
BOND_ARP_FILTER_ACTIVE => Self::FilterActive,
BOND_ARP_FILTER_BACKUP => Self::FilterBackup,
_ => Self::Other(d),
}
}
}
const BOND_PRI_RESELECT_ALWAYS: u8 = 0;
const BOND_PRI_RESELECT_BETTER: u8 = 1;
const BOND_PRI_RESELECT_FAILURE: u8 = 2;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum BondPrimaryReselect {
Always,
Better,
Failure,
Other(u8),
}
impl From<u8> for BondPrimaryReselect {
fn from(d: u8) -> Self {
match d {
BOND_PRI_RESELECT_ALWAYS => Self::Always,
BOND_PRI_RESELECT_BETTER => Self::Better,
BOND_PRI_RESELECT_FAILURE => Self::Failure,
_ => Self::Other(d),
}
}
}
const BOND_FOM_NONE: u8 = 0;
const BOND_FOM_ACTIVE: u8 = 1;
const BOND_FOM_FOLLOW: u8 = 2;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum BondFailOverMac {
None,
Active,
Follow,
Other(u8),
}
impl From<u8> for BondFailOverMac {
fn from(d: u8) -> Self {
match d {
BOND_FOM_NONE => Self::None,
BOND_FOM_ACTIVE => Self::Active,
BOND_FOM_FOLLOW => Self::Follow,
_ => Self::Other(d),
}
}
}
const BOND_XMIT_POLICY_LAYER2: u8 = 0;
const BOND_XMIT_POLICY_LAYER34: u8 = 1;
const BOND_XMIT_POLICY_LAYER23: u8 = 2;
const BOND_XMIT_POLICY_ENCAP23: u8 = 3;
const BOND_XMIT_POLICY_ENCAP34: u8 = 4;
const BOND_XMIT_POLICY_VLAN_SRCMAC: u8 = 5;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum BondXmitHashPolicy {
#[serde(rename = "layer2")]
Layer2,
#[serde(rename = "layer3+4")]
Layer34,
#[serde(rename = "layer2+3")]
Layer23,
#[serde(rename = "encap2+3")]
Encap23,
#[serde(rename = "encap3+4")]
Encap34,
#[serde(rename = "vlan+srcmac")]
VlanSrcMac,
Other(u8),
}
impl From<u8> for BondXmitHashPolicy {
fn from(d: u8) -> Self {
match d {
BOND_XMIT_POLICY_LAYER2 => Self::Layer2,
BOND_XMIT_POLICY_LAYER34 => Self::Layer34,
BOND_XMIT_POLICY_LAYER23 => Self::Layer23,
BOND_XMIT_POLICY_ENCAP23 => Self::Encap23,
BOND_XMIT_POLICY_ENCAP34 => Self::Encap34,
BOND_XMIT_POLICY_VLAN_SRCMAC => Self::VlanSrcMac,
_ => Self::Other(d),
}
}
}
const BOND_ALL_SUBORDINATES_ACTIVE_DROPPED: u8 = 0;
const BOND_ALL_SUBORDINATES_ACTIVE_DELIEVERD: u8 = 1;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum BondAllSubordinatesActive {
Dropped,
Delivered,
Other(u8),
}
impl From<u8> for BondAllSubordinatesActive {
fn from(d: u8) -> Self {
match d {
BOND_ALL_SUBORDINATES_ACTIVE_DROPPED => Self::Dropped,
BOND_ALL_SUBORDINATES_ACTIVE_DELIEVERD => Self::Delivered,
_ => Self::Other(d),
}
}
}
const AD_LACP_SLOW: u8 = 0;
const AD_LACP_FAST: u8 = 1;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum BondLacpRate {
Slow,
Fast,
Other(u8),
}
impl From<u8> for BondLacpRate {
fn from(d: u8) -> Self {
match d {
AD_LACP_SLOW => Self::Slow,
AD_LACP_FAST => Self::Fast,
_ => Self::Other(d),
}
}
}
const BOND_AD_STABLE: u8 = 0;
const BOND_AD_BANDWIDTH: u8 = 1;
const BOND_AD_COUNT: u8 = 2;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum BondAdSelect {
Stable,
Bandwidth,
Count,
Other(u8),
}
impl From<u8> for BondAdSelect {
fn from(d: u8) -> Self {
match d {
BOND_AD_STABLE => Self::Stable,
BOND_AD_BANDWIDTH => Self::Bandwidth,
BOND_AD_COUNT => Self::Count,
_ => Self::Other(d),
}
}
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone, Default)]
#[non_exhaustive]
pub struct BondAdInfo {
pub aggregator: u16,
pub num_ports: u16,
pub actor_key: u16,
pub partner_key: u16,
pub partner_mac: String,
}
impl From<&[link::BondAdInfo]> for BondAdInfo {
fn from(nlas: &[link::BondAdInfo]) -> Self {
let mut ret = Self::default();
for nla in nlas {
match nla {
link::BondAdInfo::Aggregator(v) => ret.aggregator = *v,
link::BondAdInfo::NumPorts(v) => ret.num_ports = *v,
link::BondAdInfo::ActorKey(v) => ret.actor_key = *v,
link::BondAdInfo::PartnerKey(v) => ret.partner_key = *v,
link::BondAdInfo::PartnerMac(v) => {
match parse_as_mac(v.len(), v) {
Ok(m) => ret.partner_mac = m,
Err(e) => {
log::warn!(
"Failed to parse BondAdInfo.parse_as_mac: {}",
e
);
}
}
}
_ => {
log::debug!("Unknown BondAdInfo NLA {:?}", nla);
}
}
}
ret
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
#[non_exhaustive]
pub struct BondInfo {
pub subordinates: Vec<String>,
pub mode: BondMode,
#[serde(skip_serializing_if = "Option::is_none")]
pub miimon: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub updelay: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub downdelay: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub use_carrier: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arp_interval: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arp_ip_target: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arp_all_targets: Option<BondModeArpAllTargets>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arp_validate: Option<BondArpValidate>,
#[serde(skip_serializing_if = "Option::is_none")]
pub primary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub primary_reselect: Option<BondPrimaryReselect>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fail_over_mac: Option<BondFailOverMac>,
#[serde(skip_serializing_if = "Option::is_none")]
pub xmit_hash_policy: Option<BondXmitHashPolicy>,
#[serde(skip_serializing_if = "Option::is_none")]
pub resend_igmp: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub num_unsol_na: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub num_grat_arp: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub all_subordinates_active: Option<BondAllSubordinatesActive>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_links: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lp_interval: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub packets_per_subordinate: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lacp_rate: Option<BondLacpRate>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ad_select: Option<BondAdSelect>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ad_actor_sys_prio: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ad_user_port_key: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ad_actor_system: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tlb_dynamic_lb: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub peer_notif_delay: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ad_info: Option<BondAdInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lacp_active: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arp_missed_max: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ns_ip6_target: Option<Vec<Ipv6Addr>>,
}
impl From<&[InfoBond]> for BondInfo {
fn from(nlas: &[InfoBond]) -> Self {
let mut ret = Self::default();
if let Some(mode) = nlas.iter().find_map(|nla| {
if let InfoBond::Mode(v) = nla {
Some(v)
} else {
None
}
}) {
ret.mode = (*mode).into();
}
for nla in nlas {
match nla {
InfoBond::Mode(_) => (),
InfoBond::MiiMon(v) => ret.miimon = Some(*v),
InfoBond::UpDelay(v) => ret.updelay = Some(*v),
InfoBond::DownDelay(v) => ret.downdelay = Some(*v),
InfoBond::UseCarrier(v) => ret.use_carrier = Some(*v > 0),
InfoBond::ArpInterval(v) => ret.arp_interval = Some(*v),
InfoBond::ArpIpTarget(v) => {
ret.arp_ip_target = ipv4_addr_array_to_string(v).ok()
}
InfoBond::ArpValidate(v) => {
ret.arp_validate = Some((*v).into())
}
InfoBond::ArpAllTargets(v) => {
ret.arp_all_targets = Some((*v).into())
}
InfoBond::Primary(v) => {
if [
BondMode::ActiveBackup,
BondMode::BalanceAlb,
BondMode::BalanceTlb,
]
.contains(&ret.mode)
{
ret.primary = Some(format!("{v}"));
}
}
InfoBond::PrimaryReselect(v) => {
ret.primary_reselect = Some((*v).into())
}
InfoBond::FailOverMac(v) => {
if ret.mode == BondMode::ActiveBackup {
ret.fail_over_mac = Some((*v).into());
}
}
InfoBond::XmitHashPolicy(v) => {
if [
BondMode::BalanceXor,
BondMode::Ieee8021AD,
BondMode::BalanceTlb,
]
.contains(&ret.mode)
{
ret.xmit_hash_policy = Some((*v).into());
}
}
InfoBond::ResendIgmp(v) => {
if [
BondMode::BalanceRoundRobin,
BondMode::ActiveBackup,
BondMode::BalanceTlb,
BondMode::BalanceAlb,
]
.contains(&ret.mode)
{
ret.resend_igmp = Some(*v);
}
}
InfoBond::NumPeerNotif(v) => {
if ret.mode == BondMode::ActiveBackup {
ret.num_unsol_na = Some(*v);
ret.num_grat_arp = Some(*v);
}
}
InfoBond::AllPortsActive(v) => {
ret.all_subordinates_active = Some((*v).into())
}
InfoBond::MinLinks(v) => ret.min_links = Some(*v),
InfoBond::LpInterval(v) => ret.lp_interval = Some(*v),
InfoBond::PacketsPerPort(v) => {
if ret.mode == BondMode::BalanceRoundRobin {
ret.packets_per_subordinate = Some(*v);
}
}
InfoBond::AdLacpRate(v) => {
if ret.mode == BondMode::Ieee8021AD {
ret.lacp_rate = Some((*v).into())
}
}
InfoBond::AdSelect(v) => {
if ret.mode == BondMode::Ieee8021AD {
ret.ad_select = Some((*v).into());
}
}
InfoBond::AdInfo(v) => ret.ad_info = Some(v.as_slice().into()),
InfoBond::AdActorSysPrio(v) => {
if ret.mode == BondMode::Ieee8021AD {
ret.ad_actor_sys_prio = Some(*v);
}
}
InfoBond::AdUserPortKey(v) => {
if ret.mode == BondMode::Ieee8021AD {
ret.ad_user_port_key = Some(*v);
}
}
InfoBond::AdActorSystem(v) => {
if ret.mode == BondMode::Ieee8021AD {
ret.ad_actor_system = parse_as_mac(v.len(), v).ok();
}
}
InfoBond::TlbDynamicLb(v) => {
if [BondMode::BalanceTlb, BondMode::BalanceAlb]
.contains(&ret.mode)
{
ret.tlb_dynamic_lb = Some(*v > 0);
}
}
InfoBond::PeerNotifDelay(v) => ret.peer_notif_delay = Some(*v),
InfoBond::AdLacpActive(v) => {
if ret.mode == BondMode::Ieee8021AD {
ret.lacp_active = Some(*v > 0);
}
}
InfoBond::MissedMax(v) => {
if ![
BondMode::Ieee8021AD,
BondMode::BalanceTlb,
BondMode::BalanceAlb,
]
.contains(&ret.mode)
{
ret.arp_missed_max = Some(*v);
}
}
InfoBond::NsIp6Target(v) => ret.ns_ip6_target = Some(v.clone()),
_ => {
log::warn!("Unsupported InfoBond: {:?}", nla);
}
}
}
ret
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum BondSubordinateState {
Active,
Backup,
Other(u8),
Unknown,
}
const BOND_STATE_ACTIVE: u8 = 0;
const BOND_STATE_BACKUP: u8 = 1;
impl From<u8> for BondSubordinateState {
fn from(d: u8) -> Self {
match d {
BOND_STATE_ACTIVE => Self::Active,
BOND_STATE_BACKUP => Self::Backup,
_ => Self::Other(d),
}
}
}
impl Default for BondSubordinateState {
fn default() -> Self {
Self::Unknown
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum BondMiiStatus {
LinkUp,
LinkFail,
LinkDown,
LinkBack,
Other(u8),
Unknown,
}
const BOND_LINK_UP: u8 = 0;
const BOND_LINK_FAIL: u8 = 1;
const BOND_LINK_DOWN: u8 = 2;
const BOND_LINK_BACK: u8 = 3;
impl From<u8> for BondMiiStatus {
fn from(d: u8) -> Self {
match d {
BOND_LINK_UP => Self::LinkUp,
BOND_LINK_FAIL => Self::LinkFail,
BOND_LINK_DOWN => Self::LinkDown,
BOND_LINK_BACK => Self::LinkBack,
_ => Self::Other(d),
}
}
}
impl Default for BondMiiStatus {
fn default() -> Self {
BondMiiStatus::Unknown
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
#[non_exhaustive]
pub struct BondSubordinateInfo {
pub subordinate_state: BondSubordinateState,
pub mii_status: BondMiiStatus,
pub link_failure_count: u32,
pub perm_hwaddr: String,
pub prio: i32,
pub queue_id: u16,
#[serde(skip_serializing_if = "Option::is_none")]
pub ad_aggregator_id: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ad_actor_oper_port_state: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ad_partner_oper_port_state: Option<u16>,
}
pub(crate) fn get_bond_info(
data: &InfoData,
) -> Result<Option<BondInfo>, NisporError> {
if let InfoData::Bond(nlas) = data {
Ok(Some(nlas.as_slice().into()))
} else {
Ok(None)
}
}
pub(crate) fn get_bond_subordinate_info(
nlas: &[InfoBondPort],
) -> Result<BondSubordinateInfo, NisporError> {
let mut ret = BondSubordinateInfo::default();
for nla in nlas {
match nla {
InfoBondPort::LinkFailureCount(d) => ret.link_failure_count = *d,
InfoBondPort::MiiStatus(d) => ret.mii_status = u8::from(*d).into(),
InfoBondPort::PermHwaddr(d) => {
ret.perm_hwaddr = parse_as_mac(d.len(), d)?;
}
InfoBondPort::Prio(d) => ret.prio = *d,
InfoBondPort::QueueId(d) => ret.queue_id = *d,
InfoBondPort::BondPortState(d) => {
ret.subordinate_state = u8::from(*d).into()
}
_ => {
log::info!("Unknown bond port info {:?}", nla);
}
}
}
Ok(ret)
}
pub(crate) fn bond_iface_tidy_up(iface_states: &mut HashMap<String, Iface>) {
gen_subordinate_list_of_controller(iface_states);
primary_index_to_iface_name(iface_states);
}
fn gen_subordinate_list_of_controller(
iface_states: &mut HashMap<String, Iface>,
) {
let mut controller_subordinates: HashMap<String, Vec<String>> =
HashMap::new();
for iface in iface_states.values() {
if iface.controller_type == Some(ControllerType::Bond) {
if let Some(controller) = &iface.controller {
match controller_subordinates.get_mut(controller) {
Some(subordinates) => subordinates.push(iface.name.clone()),
None => {
let new_subordinates: Vec<String> =
vec![iface.name.clone()];
controller_subordinates
.insert(controller.clone(), new_subordinates);
}
};
}
}
}
for (controller, subordinates) in controller_subordinates.iter_mut() {
if let Some(ref mut controller_iface) = iface_states.get_mut(controller)
{
if let Some(ref mut bond_info) = controller_iface.bond {
subordinates.sort();
bond_info.subordinates.clone_from(subordinates);
}
}
}
}
fn primary_index_to_iface_name(iface_states: &mut HashMap<String, Iface>) {
let mut index_to_name = HashMap::new();
for iface in iface_states.values() {
index_to_name.insert(format!("{}", iface.index), iface.name.clone());
}
for iface in iface_states.values_mut() {
if iface.iface_type != IfaceType::Bond {
continue;
}
if let Some(ref mut bond_info) = iface.bond {
if let Some(index) = &bond_info.primary {
if let Some(iface_name) = index_to_name.get(index) {
bond_info.primary = Some(iface_name.clone());
}
}
}
}
}
fn ipv4_addr_array_to_string(
addrs: &[Ipv4Addr],
) -> Result<String, NisporError> {
let mut rt = String::new();
for i in 0..(addrs.len()) {
let addr = &addrs.get(i).ok_or_else(|| {
NisporError::bug("wrong index at parsing ipv4 as string".into())
})?;
rt.push_str(&addr.to_string());
if i != addrs.len() - 1 {
rt.push(',');
}
}
Ok(rt)
}