#[cfg(not(feature = "gen_conf"))]
use std::collections::HashMap;
use serde::{Deserialize, Deserializer, Serialize};
use crate::{
DnsState, ErrorKind, HostNameState, Interface, Interfaces, MergedDnsState,
MergedHostNameState, MergedInterfaces, MergedOvsDbGlobalConfig,
MergedRouteRules, MergedRoutes, NmstateError, OvsDbGlobalConfig,
RouteRules, Routes,
};
#[derive(Clone, Debug, Serialize, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct NetworkState {
#[serde(skip_serializing_if = "Option::is_none")]
pub hostname: Option<HostNameState>,
#[serde(rename = "dns-resolver", default)]
pub dns: DnsState,
#[serde(rename = "route-rules", default)]
pub rules: RouteRules,
#[serde(default)]
pub routes: Routes,
#[serde(default)]
pub interfaces: Interfaces,
#[serde(
default,
rename = "ovs-db",
skip_serializing_if = "OvsDbGlobalConfig::is_none"
)]
pub ovsdb: OvsDbGlobalConfig,
#[serde(skip)]
pub prop_list: Vec<&'static str>,
#[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,
}
impl<'de> Deserialize<'de> for NetworkState {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let mut net_state = NetworkState::new();
let mut v = serde_json::Value::deserialize(deserializer)?;
let v = match v.as_object_mut() {
Some(v) => v,
None => {
return Err(serde::de::Error::custom(format!(
"Expecting a HashMap/Object/Dictionary, but got {v}"
)));
}
};
if let Some(ifaces_value) = v.remove("interfaces") {
net_state.prop_list.push("interfaces");
net_state.interfaces = Interfaces::deserialize(ifaces_value)
.map_err(serde::de::Error::custom)?;
}
if let Some(dns_value) = v.remove("dns-resolver") {
net_state.prop_list.push("dns");
net_state.dns = DnsState::deserialize(dns_value)
.map_err(serde::de::Error::custom)?;
}
if let Some(route_value) = v.remove("routes") {
net_state.prop_list.push("routes");
net_state.routes = Routes::deserialize(route_value)
.map_err(serde::de::Error::custom)?;
}
if let Some(rule_value) = v.remove("route-rules") {
net_state.prop_list.push("rules");
net_state.rules = RouteRules::deserialize(rule_value)
.map_err(serde::de::Error::custom)?;
}
if let Some(ovsdb_value) = v.remove("ovs-db") {
net_state.prop_list.push("ovsdb");
net_state.ovsdb = OvsDbGlobalConfig::deserialize(ovsdb_value)
.map_err(serde::de::Error::custom)?;
}
if let Some(hostname_value) = v.remove("hostname") {
net_state.prop_list.push("hostname");
net_state.hostname = Some(
HostNameState::deserialize(hostname_value)
.map_err(serde::de::Error::custom)?,
);
}
if !v.is_empty() {
Err(serde::de::Error::custom(format!(
"Unsupported keys found: {:?}",
v.keys().collect::<Vec<&String>>()
)))
} else {
Ok(net_state)
}
}
}
impl NetworkState {
pub fn is_empty(&self) -> bool {
self.hostname.is_none()
&& self.dns.is_empty()
&& self.rules.is_empty()
&& self.routes.is_empty()
&& self.interfaces.is_empty()
&& self.ovsdb.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 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 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 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(),
))
}
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 = "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) hostname: MergedHostNameState,
pub(crate) dns: MergedDnsState,
pub(crate) interfaces: MergedInterfaces,
pub(crate) ovsdb: MergedOvsDbGlobalConfig,
pub(crate) routes: MergedRoutes,
pub(crate) rules: MergedRouteRules,
pub(crate) memory_only: bool,
pub(crate) prop_list: Vec<&'static str>,
}
impl MergedNetworkState {
pub(crate) fn new(
desired: NetworkState,
current: NetworkState,
gen_conf_mode: bool,
memory_only: bool,
) -> Result<Self, NmstateError> {
let interfaces = MergedInterfaces::new(
desired.interfaces,
current.interfaces,
gen_conf_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 ret = Self {
interfaces,
routes,
rules,
dns: MergedDnsState::new(desired.dns, current.dns)?,
ovsdb: MergedOvsDbGlobalConfig::new(desired.ovsdb, current.ovsdb),
hostname,
memory_only,
prop_list: desired.prop_list,
};
ret.validate_ipv6_link_local_address_dns_srv()?;
Ok(ret)
}
}