use std::collections::{HashMap, HashSet};
use serde::{Deserialize, Serialize};
use crate::{
BaseInterface, ErrorKind, InterfaceIdentifier, Interfaces,
MergedInterfaces, NmstateError,
};
#[derive(
Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
)]
#[non_exhaustive]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub enum AltNameState {
Absent,
}
#[derive(
Debug,
Clone,
PartialEq,
Eq,
Default,
Hash,
PartialOrd,
Ord,
Serialize,
Deserialize,
)]
#[non_exhaustive]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct AltNameEntry {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<AltNameState>,
}
impl AltNameEntry {
pub fn is_absent(&self) -> bool {
self.state == Some(AltNameState::Absent)
}
}
impl BaseInterface {
pub(crate) fn sanitize_alt_names(&mut self) -> Result<(), NmstateError> {
if let Some(alt_names) = self.alt_names.as_mut() {
for alt_name in alt_names.iter() {
if !alt_name.is_absent() && alt_name.name == self.name {
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Alternative name cannot be the same with \
interface name {}",
alt_name.name
),
));
}
}
alt_names.sort_unstable();
}
Ok(())
}
}
impl MergedInterfaces {
pub(crate) fn validate_alt_names(&self) -> Result<(), NmstateError> {
let mut all_alt_names: HashMap<&str, &str> = HashMap::new();
let all_iface_names: HashSet<&str> =
self.kernel_ifaces.keys().map(|s| s.as_str()).collect();
for cur_iface in
self.kernel_ifaces.values().filter_map(|merged_iface| {
merged_iface.current.as_ref().map(|i| i.base_iface())
})
{
if let Some(alt_names) = cur_iface.alt_names.as_ref() {
for alt_name in alt_names {
all_alt_names.insert(
alt_name.name.as_str(),
cur_iface.name.as_str(),
);
}
}
}
for des_iface in
self.kernel_ifaces.values().filter_map(|merged_iface| {
merged_iface.for_apply.as_ref().map(|i| i.base_iface())
})
{
if let Some(alt_names) = des_iface.alt_names.as_ref() {
for alt_name in alt_names {
if alt_name.is_absent() {
all_alt_names.remove(alt_name.name.as_str());
} else if let Some(other_iface_name) =
all_alt_names.get(alt_name.name.as_str())
{
if other_iface_name != &des_iface.name {
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Desired alt-name {} for interface {} is \
already used by interface {}",
alt_name.name,
des_iface.name,
other_iface_name
),
));
};
} else if all_iface_names.contains(&alt_name.name.as_str())
{
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Desired alt-name {} for interface {} is \
already an interface name of other NIC",
alt_name.name, des_iface.name,
),
));
} else {
all_alt_names.insert(
alt_name.name.as_str(),
des_iface.name.as_str(),
);
}
}
}
}
Ok(())
}
}
impl Interfaces {
pub(crate) fn resolve_alt_name_reference_in_desired(
&mut self,
current: &Self,
) -> Result<(), NmstateError> {
let mut alt_name_to_kernel_name: HashMap<&str, &str> = HashMap::new();
let mut pending_changes: HashMap<String, String> = HashMap::new();
for cur_iface in current.kernel_ifaces.values() {
if let Some(alt_names) = cur_iface.base_iface().alt_names.as_ref() {
for alt_name in alt_names {
alt_name_to_kernel_name
.insert(alt_name.name.as_str(), cur_iface.name());
}
}
}
for des_iface in self.kernel_ifaces.values() {
if !matches!(
des_iface.base_iface().identifier.as_ref(),
None | Some(InterfaceIdentifier::Name)
) {
continue;
}
if !current.kernel_ifaces.contains_key(des_iface.name())
&& let Some(kernel_name) =
alt_name_to_kernel_name.get(des_iface.name())
{
if let Some(exist_kernel_iface) =
self.kernel_ifaces.get(*kernel_name)
&& !(exist_kernel_iface.is_absent()
&& des_iface.is_absent())
{
return Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!(
"Interface {}/({}) is alt-name of kernel \
interface {kernel_name}, but desired state also \
contains a conflicting configuration for kernel \
interface {kernel_name}, please merge them",
des_iface.name(),
des_iface.iface_type(),
),
));
}
pending_changes.insert(
des_iface.name().to_string(),
kernel_name.to_string(),
);
}
}
for (alt_name, kernel_name) in pending_changes.drain() {
if let Some(mut des_iface) = self.kernel_ifaces.remove(&alt_name) {
if des_iface.is_up()
&& des_iface.base_iface().profile_name.is_none()
&& current
.kernel_ifaces
.get(&kernel_name)
.and_then(|i| i.base_iface().profile_name.as_ref())
.is_none()
{
des_iface.base_iface_mut().profile_name =
Some(des_iface.base_iface().name.to_string());
}
des_iface.base_iface_mut().name = kernel_name;
self.push(des_iface);
}
}
Ok(())
}
}