#[cfg(not(feature = "gen_conf"))]
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::{
DnsState, ErrorKind, HostNameState, Interface, Interfaces, MergedDnsState,
MergedHostNameState, MergedInterfaces, MergedOvnConfiguration,
MergedOvsDbGlobalConfig, MergedRouteRules, MergedRoutes, NmstateError,
OvnConfiguration, OvsDbGlobalConfig, RouteRules, Routes,
};
#[derive(Clone, Debug, Deserialize, Serialize, Default, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct NetworkState {
#[serde(default, skip_serializing_if = "String::is_empty")]
pub description: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub hostname: Option<HostNameState>,
#[serde(rename = "dns-resolver", skip_serializing_if = "Option::is_none")]
pub dns: Option<DnsState>,
#[serde(
rename = "route-rules",
default,
skip_serializing_if = "RouteRules::is_empty"
)]
pub rules: RouteRules,
#[serde(default, skip_serializing_if = "Routes::is_empty")]
pub routes: Routes,
#[serde(default)]
pub interfaces: Interfaces,
#[serde(rename = "ovs-db", skip_serializing_if = "Option::is_none")]
pub ovsdb: Option<OvsDbGlobalConfig>,
#[serde(default, skip_serializing_if = "OvnConfiguration::is_none")]
pub ovn: OvnConfiguration,
#[serde(skip)]
pub(crate) kernel_only: bool,
#[serde(skip)]
pub(crate) no_verify: bool,
#[serde(skip)]
pub(crate) no_commit: bool,
#[serde(skip)]
pub(crate) timeout: Option<u32>,
#[serde(skip)]
pub(crate) include_secrets: bool,
#[serde(skip)]
pub(crate) include_status_data: bool,
#[serde(skip)]
pub(crate) running_config_only: bool,
#[serde(skip)]
pub(crate) memory_only: bool,
#[serde(skip)]
pub(crate) override_iface: bool,
}
impl NetworkState {
pub fn is_empty(&self) -> bool {
self.hostname.is_none()
&& self.dns.is_none()
&& self.ovsdb.is_none()
&& self.rules.is_empty()
&& self.routes.is_empty()
&& self.interfaces.is_empty()
&& self.ovn.is_none()
}
pub(crate) const PASSWORD_HID_BY_NMSTATE: &'static str =
"<_password_hid_by_nmstate>";
pub fn set_kernel_only(&mut self, value: bool) -> &mut Self {
self.kernel_only = value;
self
}
pub fn kernel_only(&self) -> bool {
self.kernel_only
}
pub fn set_verify_change(&mut self, value: bool) -> &mut Self {
self.no_verify = !value;
self
}
pub fn set_commit(&mut self, value: bool) -> &mut Self {
self.no_commit = !value;
self
}
pub fn set_timeout(&mut self, value: u32) -> &mut Self {
self.timeout = Some(value);
self
}
pub fn set_include_secrets(&mut self, value: bool) -> &mut Self {
self.include_secrets = value;
self
}
pub fn set_include_status_data(&mut self, value: bool) -> &mut Self {
self.include_status_data = value;
self
}
pub fn set_running_config_only(&mut self, value: bool) -> &mut Self {
self.running_config_only = value;
self
}
pub fn set_memory_only(&mut self, value: bool) -> &mut Self {
self.memory_only = value;
self
}
pub fn set_override_iface(&mut self, value: bool) -> &mut Self {
self.override_iface = value;
self
}
pub fn new() -> Self {
Default::default()
}
pub fn new_from_json(net_state_json: &str) -> Result<Self, NmstateError> {
match serde_json::from_str(net_state_json) {
Ok(s) => Ok(s),
Err(e) => Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!("Invalid JSON string: {e}"),
)),
}
}
pub fn new_from_yaml(net_state_yaml: &str) -> Result<Self, NmstateError> {
match serde_yaml::from_str(net_state_yaml) {
Ok(s) => Ok(s),
Err(e) => Err(NmstateError::new(
ErrorKind::InvalidArgument,
format!("Invalid YAML string: {e}"),
)),
}
}
pub fn append_interface_data(&mut self, iface: Interface) {
self.interfaces.push(iface);
}
#[cfg(not(feature = "query_apply"))]
pub fn retrieve(&mut self) -> Result<&mut Self, NmstateError> {
Err(NmstateError::new(
ErrorKind::DependencyError,
"NetworkState::retrieve() need `query_apply` feature enabled"
.into(),
))
}
#[cfg(not(feature = "query_apply"))]
pub async fn retrieve_async(&mut self) -> Result<&mut Self, NmstateError> {
Err(NmstateError::new(
ErrorKind::DependencyError,
"NetworkState::retrieve_async() need `query_apply` feature enabled"
.into(),
))
}
pub fn hide_secrets(&mut self) {
self.interfaces.hide_secrets();
}
#[cfg(not(feature = "query_apply"))]
pub fn apply(&mut self) -> Result<(), NmstateError> {
Err(NmstateError::new(
ErrorKind::DependencyError,
"NetworkState::apply() need `query_apply` feature enabled".into(),
))
}
#[cfg(not(feature = "query_apply"))]
pub async fn apply_async(&mut self) -> Result<(), NmstateError> {
Err(NmstateError::new(
ErrorKind::DependencyError,
"NetworkState::apply() need `query_apply` feature enabled".into(),
))
}
#[cfg(not(feature = "gen_conf"))]
pub fn gen_conf(
&self,
) -> Result<HashMap<String, Vec<(String, String)>>, NmstateError> {
Err(NmstateError::new(
ErrorKind::DependencyError,
"NetworkState::gen_conf() need `genconf` feature enabled".into(),
))
}
#[cfg(not(feature = "query_apply"))]
pub fn checkpoint_rollback(_checkpoint: &str) -> Result<(), NmstateError> {
Err(NmstateError::new(
ErrorKind::DependencyError,
"NetworkState::checkpoint_rollback() need `query_apply` feature \
enabled"
.into(),
))
}
#[cfg(not(feature = "query_apply"))]
pub fn checkpoint_commit(_checkpoint: &str) -> Result<(), NmstateError> {
Err(NmstateError::new(
ErrorKind::DependencyError,
"NetworkState::checkpoint_commit() need `query_apply` feature \
enabled"
.into(),
))
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub(crate) struct MergedNetworkState {
pub(crate) interfaces: MergedInterfaces,
pub(crate) hostname: MergedHostNameState,
pub(crate) dns: MergedDnsState,
pub(crate) ovn: MergedOvnConfiguration,
pub(crate) ovsdb: MergedOvsDbGlobalConfig,
pub(crate) routes: MergedRoutes,
pub(crate) rules: MergedRouteRules,
pub(crate) memory_only: bool,
pub(crate) override_iface: bool,
}
impl MergedNetworkState {
pub(crate) fn new(
desired: NetworkState,
current: NetworkState,
mode: NetworkStateMode,
memory_only: bool,
) -> Result<Self, NmstateError> {
let mut current = current;
if desired.override_iface {
current.interfaces = current.interfaces.clone_name_type_only();
};
let interfaces = MergedInterfaces::new(
desired.interfaces,
current.interfaces,
mode,
memory_only,
)?;
let ignored_ifaces = interfaces.ignored_ifaces.as_slice();
let mut routes =
MergedRoutes::new(desired.routes, current.routes, &interfaces)?;
routes.remove_routes_to_ignored_ifaces(ignored_ifaces);
let mut rules = MergedRouteRules::new(desired.rules, current.rules)?;
rules.remove_rules_to_ignored_ifaces(ignored_ifaces);
let hostname =
MergedHostNameState::new(desired.hostname, current.hostname);
let ovn =
MergedOvnConfiguration::new(desired.ovn, current.ovn, &interfaces)?;
let ovsdb = MergedOvsDbGlobalConfig::new(
desired.ovsdb,
current.ovsdb.unwrap_or_default(),
)?;
let ret = Self {
interfaces,
routes,
rules,
dns: MergedDnsState::new(
desired.dns,
current.dns.unwrap_or_default(),
)?,
ovn,
ovsdb,
hostname,
memory_only,
override_iface: desired.override_iface,
};
ret.validate_ipv6_link_local_address_dns_srv()?;
Ok(ret)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub(crate) enum NetworkStateMode {
#[default]
Apply,
GenerateConfig,
GenerateDiff,
}