nmstate 2.2.60

Library for networking management in a declarative manner
Documentation
// SPDX-License-Identifier: Apache-2.0

use crate::{
    ErrorKind, EthernetConfig, EthernetInterface, Interface, InterfaceType,
    Interfaces, MergedInterfaces, NetworkState, NmstateError, SrIovConfig,
    VethConfig,
};

impl EthernetInterface {
    pub(crate) fn sanitize_desired_for_verify(&mut self) {
        if let Some(sriov_conf) =
            self.ethernet.as_mut().and_then(|e| e.sr_iov.as_mut())
        {
            sriov_conf.sanitize_desired_for_verify();
        }

        if let Some(eth_conf) = self.ethernet.as_mut()
            && eth_conf.auto_neg == Some(true)
        {
            eth_conf.speed = None;
            eth_conf.duplex = None;
        }
    }

    pub(crate) fn sriov_is_enabled(&self) -> bool {
        self.ethernet
            .as_ref()
            .and_then(|eth_conf| {
                eth_conf.sr_iov.as_ref().map(SrIovConfig::sriov_is_enabled)
            })
            .unwrap_or_default()
    }

    pub(crate) fn update_ethernet(&mut self, other: &EthernetInterface) {
        if let Some(eth_conf) = &mut self.ethernet {
            eth_conf.update(other.ethernet.as_ref())
        } else {
            self.ethernet.clone_from(&other.ethernet)
        }
    }

    pub(crate) fn update_veth(&mut self, other: &EthernetInterface) {
        if let Some(veth_conf) = &mut self.veth {
            veth_conf.update(other.veth.as_ref());
        } else {
            self.veth.clone_from(&other.veth);
        }
    }

    pub(crate) fn verify_sriov(
        &self,
        cur_ifaces: &Interfaces,
    ) -> Result<(), NmstateError> {
        if let Some(eth_conf) = &self.ethernet
            && let Some(sriov_conf) = &eth_conf.sr_iov
        {
            sriov_conf.verify_sriov(self.base.name.as_str(), cur_ifaces)?;
        }
        Ok(())
    }
}

impl EthernetConfig {
    pub(crate) fn update(&mut self, other: Option<&EthernetConfig>) {
        if let Some(other) = other {
            if let Some(sr_iov_conf) = &mut self.sr_iov {
                sr_iov_conf.update(other.sr_iov.as_ref())
            } else {
                self.sr_iov.clone_from(&other.sr_iov)
            }
        }
    }
}

impl VethConfig {
    fn update(&mut self, other: Option<&VethConfig>) {
        if let Some(other) = other {
            self.peer.clone_from(&other.peer);
        }
    }
}

impl SrIovConfig {
    pub(crate) fn sriov_is_enabled(&self) -> bool {
        matches!(self.total_vfs, Some(i) if i > 0)
    }
}

// Checking existence of file:
//      /sys/class/net/<iface_name>/device/sriov_numvfs
fn is_sriov_supported(iface_name: &str) -> bool {
    let path = format!("/sys/class/net/{iface_name}/device/sriov_numvfs");
    std::path::Path::new(&path).exists()
}

impl Interfaces {
    pub(crate) fn check_sriov_capability(&self) -> Result<(), NmstateError> {
        for iface in self.kernel_ifaces.values() {
            if let Interface::Ethernet(eth_iface) = iface
                && eth_iface.sriov_is_enabled()
                && !is_sriov_supported(iface.name())
            {
                let e = NmstateError::new(
                    ErrorKind::NotSupportedError,
                    format!(
                        "SR-IOV is not supported by interface {}",
                        iface.name()
                    ),
                );
                log::error!("{e}");
                return Err(e);
            }
        }
        Ok(())
    }
}

impl NetworkState {
    pub(crate) fn has_sriov_and_missing_eth(&self, current: &Self) -> bool {
        self.has_sriov(current) && self.has_missing_eth(current)
    }

    fn has_sriov(&self, current: &Self) -> bool {
        for iface in
            self.interfaces.kernel_ifaces.values().filter(|i| i.is_up())
        {
            if let (
                Interface::Ethernet(iface),
                Some(Interface::Ethernet(cur_iface)),
            ) = (iface, current.interfaces.kernel_ifaces.get(iface.name()))
                && (iface.sriov_is_enabled() || cur_iface.sriov_is_enabled())
            {
                return true;
            }
        }
        false
    }

    fn has_missing_eth(&self, current: &Self) -> bool {
        self.interfaces
            .kernel_ifaces
            .values()
            .filter(|i| {
                i.is_up()
                    && (i.iface_type() == InterfaceType::Ethernet
                        || i.iface_type() == InterfaceType::Unknown)
            })
            .any(|i| !current.interfaces.kernel_ifaces.contains_key(i.name()))
    }

    // Return newly create NetworkState containing only ethernet section of
    // interface with SR-IOV PF changes.
    pub(crate) fn get_sriov_pf_conf_state(&self) -> Option<Self> {
        let mut pf_ifaces: Vec<Interface> = Vec::new();

        for iface in self.interfaces.kernel_ifaces.values().filter_map(|i| {
            if i.is_up() {
                if let Interface::Ethernet(iface) = i {
                    Some(iface)
                } else {
                    None
                }
            } else {
                None
            }
        }) {
            if let Some(true) =
                iface.ethernet.as_ref().map(|e| e.sr_iov.is_some())
                && let Some(eth_conf) = iface.ethernet.as_ref()
            {
                pf_ifaces.push(Interface::Ethernet(Box::new(
                    EthernetInterface {
                        base: iface.base.clone_name_type_only(),
                        ethernet: Some(eth_conf.clone()),
                        ..Default::default()
                    },
                )));
            }
        }

        if pf_ifaces.is_empty() {
            None
        } else {
            let mut pf_state = NetworkState::default();
            for pf_iface in pf_ifaces {
                pf_state.interfaces.push(pf_iface);
            }
            pf_state.set_override_iface(self.override_iface);
            Some(pf_state)
        }
    }
}

impl MergedInterfaces {
    pub(crate) fn get_sriov_vf_count(&self) -> u32 {
        let mut ret = 0u32;
        for iface in self.kernel_ifaces.values().filter(|i| {
            (i.is_desired() || i.is_changed())
                && i.merged.iface_type() == InterfaceType::Ethernet
        }) {
            if let Interface::Ethernet(iface) = &iface.merged {
                ret += iface
                    .ethernet
                    .as_ref()
                    .and_then(|e| e.sr_iov.as_ref())
                    .and_then(|s| s.total_vfs)
                    .unwrap_or_default();
            }
        }
        ret
    }
}