use std::collections::{HashMap, HashSet};
use serde::{
Deserialize, Deserializer, Serialize, Serializer, ser::SerializeSeq,
};
use crate::{
ErrorKind, EthernetInterface, Interface, InterfaceIdentifier,
InterfaceState, InterfaceType, MergedInterface, NetworkStateMode,
NmstateError,
};
const INTERFACES_SET_PRIORITY_MAX_RETRY: u32 = 4;
const COPY_MAC_ALLOWED_IFACE_TYPES: [InterfaceType; 3] = [
InterfaceType::Bond,
InterfaceType::LinuxBridge,
InterfaceType::OvsInterface,
];
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct Interfaces {
pub(crate) kernel_ifaces: HashMap<String, Interface>,
pub(crate) user_ifaces: HashMap<(String, InterfaceType), Interface>,
pub(crate) insert_order: Vec<(String, InterfaceType)>,
}
impl<'de> Deserialize<'de> for Interfaces {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let mut ret = Self::new();
for iface in <Vec<Interface> as Deserialize>::deserialize(deserializer)?
{
ret.push(iface);
}
Ok(ret)
}
}
impl Serialize for Interfaces {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let ifaces = self.to_vec();
let mut seq = serializer.serialize_seq(Some(ifaces.len()))?;
for iface in ifaces {
seq.serialize_element(iface)?;
}
seq.end()
}
}
impl Interfaces {
pub fn new() -> Self {
Self::default()
}
pub fn is_empty(&self) -> bool {
self.kernel_ifaces.is_empty() && self.user_ifaces.is_empty()
}
pub fn to_vec(&self) -> Vec<&Interface> {
let mut ifaces = Vec::new();
for iface in self.kernel_ifaces.values() {
ifaces.push(iface);
}
for iface in self.user_ifaces.values() {
ifaces.push(iface);
}
ifaces.sort_unstable_by_key(|iface| (iface.name(), iface.iface_type()));
ifaces.sort_by_key(|iface| iface.base_iface().up_priority);
ifaces
}
pub fn get_iface<'a>(
&'a self,
iface_name: &str,
iface_type: InterfaceType,
) -> Option<&'a Interface> {
if iface_type == InterfaceType::Unknown {
self.kernel_ifaces.get(iface_name).or_else(|| {
self.user_ifaces
.values()
.find(|&iface| iface.name() == iface_name)
})
} else if iface_type.is_userspace() {
self.user_ifaces.get(&(iface_name.to_string(), iface_type))
} else {
self.kernel_ifaces.get(iface_name)
}
}
pub fn remove_iface(
&mut self,
iface_name: &str,
iface_type: InterfaceType,
) -> Option<Interface> {
if iface_type == InterfaceType::Unknown {
self.kernel_ifaces.remove(iface_name).or_else(|| {
if let Some((n, t)) = self
.user_ifaces
.keys()
.find(|&(i, _)| i == iface_name)
.cloned()
{
self.user_ifaces.remove(&(n, t))
} else {
None
}
})
} else if iface_type.is_userspace() {
self.user_ifaces
.remove(&(iface_name.to_string(), iface_type))
} else {
self.kernel_ifaces.remove(iface_name)
}
}
pub fn push(&mut self, iface: Interface) {
self.insert_order
.push((iface.name().to_string(), iface.iface_type()));
if iface.is_userspace() {
self.user_ifaces
.insert((iface.name().to_string(), iface.iface_type()), iface);
} else {
self.kernel_ifaces.insert(iface.name().to_string(), iface);
}
}
pub(crate) fn hide_secrets(&mut self) {
for iface in self
.kernel_ifaces
.values_mut()
.chain(self.user_ifaces.values_mut())
{
iface.base_iface_mut().hide_secrets();
if let Interface::MacSec(macsec_iface) = iface
&& let Some(macsec_conf) = macsec_iface.macsec.as_mut()
{
macsec_conf.hide_secrets();
}
if let Interface::Ipsec(ipsec_iface) = iface {
ipsec_iface.hide_secrets();
}
}
}
pub fn iter(&self) -> impl Iterator<Item = &Interface> {
self.user_ifaces.values().chain(self.kernel_ifaces.values())
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Interface> {
self.user_ifaces
.values_mut()
.chain(self.kernel_ifaces.values_mut())
}
pub(crate) fn pre_ignore_check(
&self,
current: &Self,
ignored_ifaces: &[(String, InterfaceType)],
) -> Result<(), NmstateError> {
self.validate_change_veth_ignored_peer(current, ignored_ifaces)?;
self.validate_new_veth_without_peer(current)?;
Ok(())
}
fn ignored_kernel_iface_names(&self) -> HashSet<String> {
let mut ret = HashSet::new();
for iface in self.kernel_ifaces.values().filter(|i| i.is_ignore()) {
ret.insert(iface.name().to_string());
}
ret
}
fn ignored_user_iface_name_types(
&self,
) -> HashSet<(String, InterfaceType)> {
let mut ret = HashSet::new();
for iface in self.user_ifaces.values().filter(|i| i.is_ignore()) {
ret.insert((iface.name().to_string(), iface.iface_type()));
}
ret
}
pub(crate) fn unify_veth_and_eth(&mut self) {
for iface in self.kernel_ifaces.values_mut() {
if let Interface::Ethernet(iface) = iface {
iface.base.iface_type = InterfaceType::Ethernet;
}
}
for nt in self.insert_order.as_mut_slice() {
if nt.1 == InterfaceType::Veth {
nt.1 = InterfaceType::Ethernet;
}
}
}
pub(crate) fn remove_ignored_ifaces(
&mut self,
ignored_ifaces: &[(String, InterfaceType)],
) {
for (iface_name, iface_type) in ignored_ifaces {
self.remove_iface(iface_name.as_str(), iface_type.clone());
}
let kernel_iface_names: HashSet<String> = HashSet::from_iter(
ignored_ifaces
.iter()
.filter(|(_, t)| !t.is_userspace())
.map(|(n, _)| n.to_string()),
);
for iface in self
.kernel_ifaces
.values_mut()
.chain(self.user_ifaces.values_mut())
.filter(|i| i.is_controller())
{
if let Some(ports) = iface.ports() {
let ports: HashSet<String> =
HashSet::from_iter(ports.iter().map(|p| p.to_string()));
for ignore_port in kernel_iface_names.intersection(&ports) {
iface.remove_port(ignore_port);
}
}
if iface.iface_type() == InterfaceType::Veth
&& let Interface::Ethernet(eth_iface) = iface
&& let Some(veth_conf) = eth_iface.veth.as_ref()
&& kernel_iface_names.contains(veth_conf.peer.as_str())
{
log::info!(
"Veth interface {} is holding ignored peer {}",
eth_iface.base.name,
veth_conf.peer.as_str()
);
eth_iface.veth = None;
}
}
}
pub(crate) fn apply_memory_only_mode(&mut self) {
for iface in self.iter_mut().filter(|i| i.is_absent()) {
iface.base_iface_mut().state = InterfaceState::Down;
}
}
pub(crate) fn resolve_unknown_ifaces(
&mut self,
cur_ifaces: &Self,
) -> Result<(), NmstateError> {
let mut resolved_ifaces: Vec<Interface> = Vec::new();
for (iface_name, iface) in self.kernel_ifaces.iter() {
if iface.iface_type() != InterfaceType::Unknown || iface.is_ignore()
{
continue;
}
if iface.is_absent() {
for cur_iface in cur_ifaces.to_vec() {
if cur_iface.name() == iface_name {
let mut new_iface = cur_iface.clone_name_type_only();
new_iface.base_iface_mut().state =
InterfaceState::Absent;
resolved_ifaces.push(new_iface);
}
}
} else {
let mut founds = Vec::new();
for cur_iface in cur_ifaces.to_vec() {
if cur_iface.name() == iface_name {
let mut new_iface_value = serde_json::to_value(iface)?;
if let Some(obj) = new_iface_value.as_object_mut() {
obj.insert(
"type".to_string(),
serde_json::Value::String(
cur_iface.iface_type().to_string(),
),
);
}
founds.push(new_iface_value);
}
}
match founds.len() {
0 => {
let e = NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Failed to find unknown type interface \
{iface_name} in current state"
),
);
log::error!("{e}");
return Err(e);
}
1 => {
let new_iface = Interface::deserialize(&founds[0])?;
resolved_ifaces.push(new_iface);
}
_ => {
let e = NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Found 2+ interface matching desired unknown \
type interface {}: {:?}",
iface_name, &founds
),
);
log::error!("{e}");
return Err(e);
}
}
}
}
for new_iface in resolved_ifaces {
self.kernel_ifaces.remove(new_iface.name());
self.push(new_iface);
}
Ok(())
}
fn purge_unknown_state_iface(&mut self) {
self.user_ifaces.retain(|_, iface| {
iface.base_iface().state != InterfaceState::Unknown
});
self.kernel_ifaces.retain(|_, iface| {
iface.base_iface().state != InterfaceState::Unknown
});
self.insert_order.retain(|(iface_name, iface_type)| {
if iface_type.is_userspace() {
self.user_ifaces
.contains_key(&(iface_name.clone(), iface_type.clone()))
} else {
self.kernel_ifaces.contains_key(iface_name)
}
});
}
fn resolve_mac_identifier_in_desired(
&mut self,
current: &Self,
) -> Result<(), NmstateError> {
let mut changed_ifaces: Vec<Interface> = Vec::new();
for iface in self.iter().filter(|i| {
(!i.is_absent())
&& i.base_iface().identifier
== Some(InterfaceIdentifier::MacAddress)
&& i.base_iface().profile_name.is_none()
}) {
let mac_address = match iface.base_iface().mac_address.as_deref() {
Some(m) => m.to_ascii_uppercase(),
None => {
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Desired interface {} has `identifier: \
mac-address` but not MAC address defined",
iface.name()
),
));
}
};
let mut has_match = false;
for use_permanent_addr in [true, false] {
for cur_iface in current.kernel_ifaces.values() {
if iface.iface_type() != InterfaceType::Unknown
&& iface.iface_type() != cur_iface.iface_type()
{
continue;
}
let cur_mac_addr = if use_permanent_addr {
cur_iface
.base_iface()
.permanent_mac_address
.as_deref()
.map(|m| m.to_ascii_uppercase())
} else {
cur_iface
.base_iface()
.mac_address
.as_deref()
.map(|m| m.to_ascii_uppercase())
};
if cur_mac_addr.as_deref() == Some(&mac_address) {
let mut new_iface = if iface.iface_type()
== InterfaceType::Unknown
{
let mut new_iface_value =
serde_json::to_value(iface)?;
if let Some(obj) = new_iface_value.as_object_mut() {
obj.insert(
"type".to_string(),
serde_json::Value::String(
cur_iface.iface_type().to_string(),
),
);
}
Interface::deserialize(new_iface_value)?
} else {
iface.clone()
};
new_iface.base_iface_mut().profile_name =
Some(iface.base_iface().name.clone());
new_iface.base_iface_mut().name =
cur_iface.name().to_string();
changed_ifaces.push(new_iface);
has_match = true;
break;
}
}
if has_match {
break;
}
}
if !has_match {
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Desired interface {} has `identifier: mac-address` \
with MAC address {mac_address}, but no interface is \
holding that MAC address",
iface.name()
),
));
}
}
for changed_iface in changed_ifaces {
if let Some(profile_name) =
changed_iface.base_iface().profile_name.as_deref()
{
self.kernel_ifaces.remove(profile_name);
}
self.push(changed_iface);
}
Ok(())
}
fn resolve_pci_identifier(
&mut self,
current: &Self,
) -> Result<(), NmstateError> {
let mut name_changed_ifaces: Vec<String> = Vec::new();
for iface in self.iter_mut().filter(|i| {
(!i.is_absent())
&& i.base_iface().identifier
== Some(InterfaceIdentifier::PciAddress)
}) {
let pci_address = match iface.base_iface().pci_address.as_ref() {
Some(p) => p,
None => {
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Desired interface {} has `identifier: \
pci-address` but not PCI address defined",
iface.name()
),
));
}
};
if let Some(cur_iface) =
current.kernel_ifaces.values().find(|cur_iface| {
cur_iface.base_iface().pci_address.as_ref()
== Some(pci_address)
})
{
if iface.base_iface().profile_name.is_none() {
name_changed_ifaces
.push(iface.base_iface_mut().name.clone());
iface.base_iface_mut().profile_name =
Some(iface.base_iface_mut().name.clone());
iface.base_iface_mut().name = cur_iface.name().to_string();
} else {
iface.base_iface_mut().name = cur_iface.name().to_string();
}
} else {
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Desired interface {} has `identifier: pci-address` \
with PCI address {pci_address}, but no interface is \
holding that PCI address",
iface.name()
),
));
}
}
for profile_name in name_changed_ifaces {
if let Some(iface) = self.kernel_ifaces.remove(&profile_name) {
self.push(iface);
}
}
Ok(())
}
fn resolve_mac_identifier_in_current(
&mut self,
current: &Self,
) -> Result<(), NmstateError> {
let mut changed_ifaces: Vec<Interface> = Vec::new();
for cur_iface in current.kernel_ifaces.values().filter(|i| {
i.base_iface().identifier == Some(InterfaceIdentifier::MacAddress)
}) {
if let Some(profile_name) =
cur_iface.base_iface().profile_name.as_ref()
&& let Some(des_iface) = self.kernel_ifaces.get(profile_name)
{
let mut new_iface = if des_iface.iface_type()
== InterfaceType::Unknown
{
let mut new_iface_value = serde_json::to_value(des_iface)?;
if let Some(obj) = new_iface_value.as_object_mut() {
obj.insert(
"type".to_string(),
serde_json::Value::String(
cur_iface.iface_type().to_string(),
),
);
}
Interface::deserialize(new_iface_value)?
} else {
des_iface.clone()
};
new_iface.base_iface_mut().identifier =
Some(InterfaceIdentifier::MacAddress);
new_iface
.base_iface_mut()
.mac_address
.clone_from(&cur_iface.base_iface().mac_address);
new_iface.base_iface_mut().name = cur_iface.name().to_string();
new_iface.base_iface_mut().profile_name =
Some(profile_name.to_string());
changed_ifaces.push(new_iface);
}
}
for changed_iface in changed_ifaces {
if let Some(profile_name) =
changed_iface.base_iface().profile_name.as_deref()
{
self.kernel_ifaces.remove(profile_name);
}
self.push(changed_iface);
}
Ok(())
}
pub(crate) fn get_iface_mut<'a>(
&'a mut self,
iface_name: &str,
iface_type: InterfaceType,
) -> Option<&'a mut Interface> {
if iface_type.is_userspace() {
self.user_ifaces
.get_mut(&(iface_name.to_string(), iface_type))
} else {
self.kernel_ifaces.get_mut(iface_name)
}
}
pub(crate) fn set_missing_port_to_eth(&mut self) {
let mut iface_names_to_add = Vec::new();
for iface in
self.kernel_ifaces.values().chain(self.user_ifaces.values())
{
if let Some(ports) = iface.ports() {
for port in ports {
if !self.kernel_ifaces.contains_key(port) {
iface_names_to_add.push(port.to_string());
}
}
}
}
for iface_name in iface_names_to_add {
let mut iface = EthernetInterface::default();
iface.base.name.clone_from(&iface_name);
log::warn!("Assuming undefined port {iface_name} as ethernet");
self.kernel_ifaces
.insert(iface_name, Interface::Ethernet(Box::new(iface)));
}
}
pub(crate) fn set_unknown_iface_to_eth(
&mut self,
) -> Result<(), NmstateError> {
let mut new_ifaces = Vec::new();
for iface in self.kernel_ifaces.values() {
if let Interface::Unknown(iface) = &iface {
log::warn!(
"Setting unknown type interface {} to ethernet",
iface.base.name.as_str()
);
let iface_value = match serde_json::to_value(iface) {
Ok(mut v) => {
if let Some(v) = v.as_object_mut() {
v.insert(
"type".to_string(),
serde_json::Value::String(
"ethernet".to_string(),
),
);
}
v
}
Err(e) => {
return Err(NmstateError::new(
ErrorKind::Bug,
format!(
"BUG: Failed to convert {iface:?} to \
serde_json value: {e}"
),
));
}
};
match EthernetInterface::deserialize(&iface_value) {
Ok(i) => new_ifaces.push(i),
Err(e) => {
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Invalid property for ethernet interface: {e}"
),
));
}
}
}
}
for iface in new_ifaces {
self.kernel_ifaces.insert(
iface.base.name.to_string(),
Interface::Ethernet(Box::new(iface)),
);
}
Ok(())
}
pub(crate) fn clone_name_type_only(&self) -> Self {
let mut ret = Self::new();
for iface in self.to_vec() {
ret.push(iface.clone_name_type_only())
}
ret
}
pub(crate) fn handle_gen_conf_mac_identifer(&mut self) {
for iface in self.kernel_ifaces.values_mut().filter(|i| i.is_up()) {
if iface.base_iface().identifier.as_ref()
== Some(&InterfaceIdentifier::MacAddress)
&& iface.base_iface().profile_name.is_none()
{
iface.base_iface_mut().profile_name =
Some(iface.name().to_string());
}
}
}
}
fn is_opt_str_empty(opt_string: &Option<String>) -> bool {
if let Some(s) = opt_string {
s.is_empty()
} else {
true
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub(crate) struct MergedInterfaces {
pub(crate) kernel_ifaces: HashMap<String, MergedInterface>,
pub(crate) user_ifaces: HashMap<(String, InterfaceType), MergedInterface>,
pub(crate) insert_order: Vec<(String, InterfaceType)>,
pub(crate) ignored_ifaces: Vec<(String, InterfaceType)>,
pub(crate) memory_only: bool,
pub(crate) mode: NetworkStateMode,
pub(crate) iface_name_search: InterfaceNameSearch,
}
impl MergedInterfaces {
pub(crate) fn new(
mut desired: Interfaces,
mut current: Interfaces,
mode: NetworkStateMode,
memory_only: bool,
) -> Result<Self, NmstateError> {
let mut merged_kernel_ifaces: HashMap<String, MergedInterface> =
HashMap::new();
let mut merged_user_ifaces: HashMap<
(String, InterfaceType),
MergedInterface,
> = HashMap::new();
desired.unify_veth_and_eth();
current.unify_veth_and_eth();
desired.purge_unknown_state_iface();
if mode == NetworkStateMode::GenerateConfig {
desired.set_unknown_iface_to_eth()?;
desired.set_missing_port_to_eth();
desired.handle_gen_conf_mac_identifer();
} else {
if mode != NetworkStateMode::GenerateDiff {
desired.resolve_sriov_reference(¤t)?;
}
desired.resolve_mac_identifier_in_current(¤t)?;
desired.resolve_pci_identifier(¤t)?;
desired.resolve_unknown_ifaces(¤t)?;
desired.resolve_mac_identifier_in_desired(¤t)?;
desired.resolve_alt_name_reference_in_desired(¤t)?;
}
desired.auto_managed_controller_ports(¤t);
let ignored_ifaces = get_ignored_ifaces(&desired, ¤t);
desired.pre_ignore_check(¤t, ignored_ifaces.as_slice())?;
if memory_only {
desired.apply_memory_only_mode();
}
for (iface_name, iface_type) in ignored_ifaces.as_slice() {
log::info!("Ignoring interface {iface_name} type {iface_type}");
}
desired.remove_ignored_ifaces(ignored_ifaces.as_slice());
current.remove_ignored_ifaces(ignored_ifaces.as_slice());
for (iface_name, des_iface) in desired
.kernel_ifaces
.drain()
.chain(desired.user_ifaces.drain().map(|((n, _), i)| (n, i)))
{
let merged_iface = MergedInterface::new(
Some(des_iface.clone()),
current.remove_iface(&iface_name, des_iface.iface_type()),
)?;
if merged_iface.merged.is_userspace() {
merged_user_ifaces.insert(
(
merged_iface.merged.name().to_string(),
merged_iface.merged.iface_type(),
),
merged_iface,
);
} else {
merged_kernel_ifaces.insert(
merged_iface.merged.name().to_string(),
merged_iface,
);
}
}
for cur_iface in current
.kernel_ifaces
.drain()
.chain(current.user_ifaces.drain().map(|((n, _), i)| (n, i)))
.map(|(_, i)| i)
{
let merged_iface = MergedInterface::new(None, Some(cur_iface))?;
if merged_iface.merged.is_userspace() {
merged_user_ifaces.insert(
(
merged_iface.merged.name().to_string(),
merged_iface.merged.iface_type(),
),
merged_iface,
);
} else {
merged_kernel_ifaces.insert(
merged_iface.merged.name().to_string(),
merged_iface,
);
}
}
let iface_name_search =
InterfaceNameSearch::new(merged_kernel_ifaces.values());
let mut ret = Self {
kernel_ifaces: merged_kernel_ifaces,
user_ifaces: merged_user_ifaces,
insert_order: desired.insert_order,
ignored_ifaces,
memory_only,
mode,
iface_name_search,
};
ret.process()?;
Ok(ret)
}
pub(crate) fn get_iface<'a>(
&'a self,
iface_name: &str,
iface_type: InterfaceType,
) -> Option<&'a MergedInterface> {
if iface_type == InterfaceType::Unknown {
self.kernel_ifaces.get(iface_name).or_else(|| {
self.user_ifaces
.values()
.find(|&iface| iface.merged.name() == iface_name)
})
} else if iface_type.is_userspace() {
self.user_ifaces.get(&(iface_name.to_string(), iface_type))
} else {
self.kernel_ifaces.get(iface_name)
}
}
pub(crate) fn iter(&self) -> impl Iterator<Item = &MergedInterface> {
self.user_ifaces.values().chain(self.kernel_ifaces.values())
}
pub(crate) fn iter_mut(
&mut self,
) -> impl Iterator<Item = &mut MergedInterface> {
self.user_ifaces
.values_mut()
.chain(self.kernel_ifaces.values_mut())
}
fn process(&mut self) -> Result<(), NmstateError> {
self.process_allow_extra_ovs_patch_ports_for_apply();
self.process_allow_extra_bridge_ports_for_apply();
self.apply_copy_mac_from()?;
self.validate_hsr_mac()?;
self.copy_hsr_mac()?;
self.validate_controller_and_port_list_confliction()?;
self.resolve_port_name_ref()?;
self.resolve_parent_name_ref()?;
self.handle_changed_ports()?;
self.resolve_port_iface_controller_type()?;
self._set_up_priority()?;
self.check_overbook_ports()?;
self.check_infiniband_as_ports()?;
self.mark_orphan_interface_as_absent()?;
self.process_veth_peer_changes()?;
self.validate_dispatch_script_has_no_checkpoint()?;
self.validate_alt_names()?;
for iface in self
.kernel_ifaces
.values_mut()
.chain(self.user_ifaces.values_mut())
.filter(|i| i.is_changed())
{
iface.post_inter_ifaces_process()?;
}
Ok(())
}
fn _set_up_priority(&mut self) -> Result<(), NmstateError> {
for _ in 0..INTERFACES_SET_PRIORITY_MAX_RETRY {
if self.set_ifaces_up_priority() {
return Ok(());
}
}
log::error!(
"Failed to set up priority: please order the interfaces in desire \
state to place controller before its ports"
);
Err(NmstateError::new(
ErrorKind::InvalidArgument,
"Failed to set up priority: nmstate only support nested interface \
up to 4 levels. To support more nest level, please order the \
interfaces in desire state to place controller before its ports"
.to_string(),
))
}
fn process_allow_extra_bridge_ports_for_apply(&mut self) {
for merged_iface in self
.iter_mut()
.filter(|mi| mi.desired.as_ref().map(|i| i.is_up()) == Some(true))
{
merged_iface.process_allow_extra_bridge_ports();
}
}
fn apply_copy_mac_from(&mut self) -> Result<(), NmstateError> {
let mut pending_changes: HashMap<String, String> = HashMap::new();
for (iface_name, merged_iface) in self.kernel_ifaces.iter() {
if !merged_iface.is_desired()
|| !COPY_MAC_ALLOWED_IFACE_TYPES
.contains(&merged_iface.merged.iface_type())
{
continue;
}
if let Some(src_iface_name) =
&merged_iface.merged.base_iface().copy_mac_from
{
if let Some(src_iface) =
self.kernel_ifaces.get(src_iface_name).map(|i| &i.merged)
{
if !is_opt_str_empty(
&src_iface.base_iface().permanent_mac_address,
) {
if let Some(mac) = src_iface
.base_iface()
.permanent_mac_address
.as_ref()
{
pending_changes.insert(
iface_name.to_string(),
mac.to_string(),
);
}
} else if !is_opt_str_empty(
&src_iface.base_iface().mac_address,
) {
if let Some(mac) =
src_iface.base_iface().mac_address.as_ref()
{
pending_changes.insert(
iface_name.to_string(),
mac.to_string(),
);
}
} else {
let e = NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Failed to find mac address of interface \
{src_iface_name} for copy-mac-from of iface \
{iface_name}"
),
);
log::error!("{e}");
return Err(e);
}
} else {
let e = NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Failed to find interface {src_iface_name} for \
copy-mac-from of iface {iface_name}"
),
);
log::error!("{e}");
return Err(e);
}
}
}
for (iface_name, mac) in pending_changes.drain() {
if let Some(iface) = self.kernel_ifaces.get_mut(&iface_name) {
iface.set_copy_from_mac(mac);
}
}
Ok(())
}
fn mark_orphan_interface_as_absent(&mut self) -> Result<(), NmstateError> {
let gone_ifaces: Vec<String> = self
.kernel_ifaces
.values()
.filter(|i| {
i.is_changed()
&& i.merged.is_absent()
&& i.merged.iface_type() != InterfaceType::Ethernet
})
.map(|i| i.merged.name().to_string())
.collect();
for iface in self.kernel_ifaces.values_mut().filter(|i| {
i.merged.is_up()
&& i.merged.iface_type() != InterfaceType::OvsInterface
}) {
if let Some(parent) = iface.merged.parent()
&& gone_ifaces.contains(&parent.to_string())
{
if iface.is_desired() && iface.merged.is_up() {
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Interface {} cannot be in up state as its parent \
{parent} has been marked as absent",
iface.merged.name(),
),
));
}
log::info!(
"Marking interface {} as absent as its parent {} is so",
iface.merged.name(),
parent
);
iface.mark_as_absent();
}
}
Ok(())
}
fn resolve_parent_name_ref(&mut self) -> Result<(), NmstateError> {
let iface_name_search = &self.iface_name_search;
for iface in self.kernel_ifaces.values_mut() {
let des_iface = if let Some(d) = iface.desired.as_mut() {
d
} else {
continue;
};
if !des_iface.is_up() {
continue;
}
let parent = if let Some(p) = des_iface.parent() {
p
} else {
continue;
};
let for_apply = if let Some(i) = iface.for_apply.as_mut() {
i
} else {
continue;
};
let for_verify = if let Some(i) = iface.for_verify.as_mut() {
i
} else {
continue;
};
let kernel_names = iface_name_search.get(parent);
if kernel_names.contains(&parent) {
continue;
}
if kernel_names.len() > 1 {
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Interface {} ({}) defined with parent {} but \
multiple interfaces are sharing the this profile name",
des_iface.name(),
des_iface.iface_type(),
parent,
),
));
} else if let Some(kernel_name) = kernel_names.first() {
des_iface.change_parent_name(kernel_name);
for_apply.change_parent_name(kernel_name);
for_verify.change_parent_name(kernel_name);
iface.merged.change_parent_name(kernel_name);
} else {
}
}
Ok(())
}
}
fn get_ignored_ifaces(
desired: &Interfaces,
current: &Interfaces,
) -> Vec<(String, InterfaceType)> {
let mut ignored_kernel_ifaces = desired.ignored_kernel_iface_names();
let mut ignored_user_ifaces = desired.ignored_user_iface_name_types();
let desired_kernel_ifaces: HashSet<String> = desired
.kernel_ifaces
.values()
.filter(|i| !i.is_ignore())
.map(|i| i.name().to_string())
.collect();
let desired_user_ifaces: HashSet<(String, InterfaceType)> = desired
.user_ifaces
.values()
.filter(|i| !i.is_ignore())
.map(|i| (i.name().to_string(), i.iface_type()))
.collect();
for iface_name in current.ignored_kernel_iface_names().drain() {
if !desired_kernel_ifaces.contains(&iface_name) {
ignored_kernel_ifaces.insert(iface_name);
}
}
for (iface_name, iface_type) in
current.ignored_user_iface_name_types().drain()
{
if !desired_user_ifaces
.contains(&(iface_name.clone(), iface_type.clone()))
{
ignored_user_ifaces.insert((iface_name, iface_type));
}
}
let mut ignored_ifaces: Vec<(String, InterfaceType)> =
ignored_user_ifaces.drain().collect();
for iface_name in ignored_kernel_ifaces {
if let Some(iface) = desired
.get_iface(&iface_name, InterfaceType::Unknown)
.or_else(|| current.get_iface(&iface_name, InterfaceType::Unknown))
{
let iface_type = match iface.iface_type() {
InterfaceType::Veth => InterfaceType::Ethernet,
t => t,
};
ignored_ifaces.push((iface_name, iface_type));
}
}
ignored_ifaces
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub(crate) struct InterfaceNameSearch {
kernel_nics: HashSet<String>,
profile_2_kernel: HashMap<String, Vec<String>>,
alt_name_2_kernel: HashMap<String, String>,
}
impl InterfaceNameSearch {
pub(crate) fn new<'a, T>(merged_ifaces: T) -> Self
where
T: Iterator<Item = &'a MergedInterface> + Clone,
{
let mut kernel_nics: HashSet<String> = HashSet::new();
for iface in merged_ifaces.clone().filter(|i| i.merged.is_up()) {
if let Some(cur_iface) = iface.current.as_ref() {
kernel_nics.insert(cur_iface.name().to_string());
} else if let Some(des_iface) = iface.desired.as_ref() {
if des_iface.base_iface().identifier.as_ref()
!= Some(&InterfaceIdentifier::MacAddress)
{
kernel_nics.insert(des_iface.name().to_string());
}
}
}
let mut profile_2_kernel: HashMap<String, Vec<String>> = HashMap::new();
for iface in merged_ifaces.clone() {
let base_iface = iface.merged.base_iface();
if let Some(profile_name) = base_iface.profile_name.as_deref() {
profile_2_kernel
.entry(profile_name.to_string())
.or_default()
.push(base_iface.name.to_string());
}
}
let mut alt_name_2_kernel: HashMap<String, String> = HashMap::new();
for iface in merged_ifaces.clone().filter(|i| i.merged.is_up()) {
let base_iface = iface.merged.base_iface();
if let Some(alt_names) = base_iface.alt_names.as_deref() {
for alt_name in alt_names.iter().filter(|a| !a.is_absent()) {
alt_name_2_kernel.insert(
alt_name.name.to_string(),
base_iface.name.to_string(),
);
}
}
}
Self {
kernel_nics,
profile_2_kernel,
alt_name_2_kernel,
}
}
pub(crate) fn get<'a>(&'a self, name: &str) -> Vec<&'a str> {
if let Some(n) = self
.kernel_nics
.get(name)
.or_else(|| self.alt_name_2_kernel.get(name))
{
vec![n]
} else if let Some(nics) = self.profile_2_kernel.get(name) {
nics.as_slice().iter().map(|s| s.as_str()).collect()
} else {
Vec::new()
}
}
}