use std::collections::{HashMap, HashSet};
use crate::{
state::get_json_value_difference, ErrorKind, Interface, InterfaceState,
InterfaceType, Interfaces, MergedInterfaces, MergedOvsDbGlobalConfig,
NetworkState, NmstateError, OvsBridgeBondConfig, OvsBridgeConfig,
OvsBridgeInterface, OvsDbGlobalConfig, OvsDbIfaceConfig, OvsInterface,
};
impl MergedOvsDbGlobalConfig {
pub(crate) fn is_changed(&self) -> bool {
self.is_changed
}
pub(crate) fn verify(
&self,
current: OvsDbGlobalConfig,
) -> Result<(), NmstateError> {
let desired = match self.desired.as_ref() {
Some(d) => d,
None => {
return Ok(());
}
};
let empty_map: HashMap<String, Option<String>> = HashMap::new();
let external_ids: HashMap<String, Option<String>> = desired
.external_ids
.as_ref()
.unwrap_or(&empty_map)
.iter()
.filter_map(|(k, v)| {
if v.is_some() {
Some((k.to_string(), v.clone()))
} else {
None
}
})
.collect();
let other_config: HashMap<String, Option<String>> = desired
.other_config
.as_ref()
.unwrap_or(&empty_map)
.iter()
.filter_map(|(k, v)| {
if v.is_some() {
Some((k.to_string(), v.clone()))
} else {
None
}
})
.collect();
let desired = OvsDbGlobalConfig {
external_ids: Some(external_ids),
other_config: Some(other_config),
};
let desired_value = serde_json::to_value(desired)?;
let current_value = if current.is_none() {
serde_json::to_value(OvsDbGlobalConfig {
external_ids: Some(HashMap::new()),
other_config: Some(HashMap::new()),
})?
} else {
serde_json::to_value(current)?
};
if let Some((reference, desire, current)) = get_json_value_difference(
"ovsdb".to_string(),
&desired_value,
¤t_value,
) {
Err(NmstateError::new(
ErrorKind::VerificationError,
format!(
"Verification failure: {reference} desire '{desire}', \
current '{current}'"
),
))
} else {
Ok(())
}
}
}
impl OvsBridgeConfig {
pub(crate) fn update(&mut self, other: Option<&OvsBridgeConfig>) {
if let Some(other) = other {
self.ports.clone_from(&other.ports);
}
}
}
impl OvsBridgeInterface {
pub(crate) fn sanitize_current_for_verify(&mut self) {
if let Some(port_confs) = self
.bridge
.as_mut()
.and_then(|br_conf| br_conf.ports.as_mut())
{
for port_conf in port_confs {
if let Some(bond_conf) = port_conf.bond.as_mut() {
bond_conf.sanitize_current_for_verify();
}
}
}
}
pub(crate) fn update_ovs_bridge(&mut self, other: &OvsBridgeInterface) {
if let Some(br_conf) = &mut self.bridge {
br_conf.update(other.bridge.as_ref());
} else {
self.bridge.clone_from(&other.bridge);
}
}
}
impl OvsInterface {
pub(crate) fn update_ovs_iface(&mut self, other: &Self) {
if other.patch.is_some() {
self.patch.clone_from(&other.patch);
}
if other.dpdk.is_some() {
self.dpdk.clone_from(&other.dpdk);
}
}
}
impl OvsDbIfaceConfig {
pub(crate) fn new_empty() -> Self {
Self {
external_ids: Some(HashMap::new()),
other_config: Some(HashMap::new()),
}
}
}
impl OvsBridgeBondConfig {
pub(crate) fn sanitize_current_for_verify(&mut self) {
if self.ovsdb.is_none() {
self.ovsdb = Some(OvsDbIfaceConfig::new_empty());
}
}
}
impl MergedInterfaces {
pub(crate) fn has_ovs(&self) -> bool {
self.kernel_ifaces
.values()
.chain(self.user_ifaces.values())
.any(|i| {
i.for_apply.as_ref().map(|i| {
i.iface_type() == InterfaceType::OvsBridge
|| i.iface_type() == InterfaceType::OvsInterface
}) == Some(true)
})
}
pub(crate) fn process_allow_extra_ovs_patch_ports_for_verify(
&mut self,
current: &mut Interfaces,
) {
let mut ovs_patch_port_names: HashSet<String> = HashSet::new();
for cur_iface in current.iter().filter_map(|i| {
if let Interface::OvsInterface(o) = i {
Some(o)
} else {
None
}
}) {
if cur_iface.is_ovs_patch_port() {
ovs_patch_port_names.insert(cur_iface.base.name.to_string());
}
}
for des_iface in self.iter_mut().filter_map(|i| {
if let Some(Interface::OvsBridge(o)) = i.desired.as_mut() {
if o.bridge.as_ref().and_then(|c| c.allow_extra_patch_ports)
== Some(true)
&& o.base.state == InterfaceState::Up
{
if let Some(Interface::OvsBridge(c)) = i.for_verify.as_mut()
{
if let Some(c) = c.bridge.as_mut() {
c.allow_extra_patch_ports = None;
}
}
Some(o)
} else {
None
}
} else {
None
}
}) {
if let Some(cur_iface) = current.get_iface_mut(
des_iface.base.name.as_str(),
InterfaceType::OvsBridge,
) {
let mut ports_to_delete: HashSet<String> = HashSet::new();
if let (Some(des_ports), Some(cur_ports)) =
(des_iface.ports(), cur_iface.ports())
{
for cur_port_name in cur_ports {
if ovs_patch_port_names.contains(cur_port_name)
&& !des_ports.contains(&cur_port_name)
{
ports_to_delete.insert(cur_port_name.to_string());
}
}
}
for port_name in ports_to_delete.iter() {
cur_iface.remove_port(port_name);
}
}
}
}
}
impl NetworkState {
pub(crate) fn isolate_ovn(&mut self) -> Result<(), NmstateError> {
if let Some(ovn_maps_str) = self
.ovsdb
.as_mut()
.and_then(|o| o.external_ids.as_mut())
.and_then(|eids| {
eids.remove(OvsDbGlobalConfig::OVN_BRIDGE_MAPPINGS_KEY)
})
.flatten()
{
self.ovn = ovn_maps_str.as_str().try_into()?;
}
Ok(())
}
}