1#[cfg(not(feature = "gen_conf"))]
4use std::collections::HashMap;
5
6use serde::{Deserialize, Serialize};
7
8use crate::{
9 DnsState, ErrorKind, HostNameState, Interface, Interfaces, MergedDnsState,
10 MergedHostNameState, MergedInterfaces, MergedOvnConfiguration,
11 MergedOvsDbGlobalConfig, MergedRouteRules, MergedRoutes, NmstateError,
12 OvnConfiguration, OvsDbGlobalConfig, RouteRules, Routes,
13};
14
15#[derive(Clone, Debug, Deserialize, Serialize, Default, PartialEq, Eq)]
78#[serde(deny_unknown_fields)]
79#[non_exhaustive]
80pub struct NetworkState {
81 #[serde(default, skip_serializing_if = "String::is_empty")]
82 pub description: String,
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub hostname: Option<HostNameState>,
89 #[serde(rename = "dns-resolver", skip_serializing_if = "Option::is_none")]
91 pub dns: Option<DnsState>,
92 #[serde(
94 rename = "route-rules",
95 default,
96 skip_serializing_if = "RouteRules::is_empty"
97 )]
98 pub rules: RouteRules,
99 #[serde(default, skip_serializing_if = "Routes::is_empty")]
101 pub routes: Routes,
102 #[serde(default)]
104 pub interfaces: Interfaces,
105 #[serde(rename = "ovs-db", skip_serializing_if = "Option::is_none")]
107 pub ovsdb: Option<OvsDbGlobalConfig>,
108 #[serde(default, skip_serializing_if = "OvnConfiguration::is_none")]
109 pub ovn: OvnConfiguration,
111 #[serde(skip)]
112 pub(crate) kernel_only: bool,
113 #[serde(skip)]
114 pub(crate) no_verify: bool,
115 #[serde(skip)]
116 pub(crate) no_commit: bool,
117 #[serde(skip)]
118 pub(crate) timeout: Option<u32>,
119 #[serde(skip)]
120 pub(crate) include_secrets: bool,
121 #[serde(skip)]
122 pub(crate) include_status_data: bool,
123 #[serde(skip)]
124 pub(crate) running_config_only: bool,
125 #[serde(skip)]
126 pub(crate) memory_only: bool,
127 #[serde(skip)]
128 pub(crate) override_iface: bool,
129}
130
131impl NetworkState {
132 pub fn is_empty(&self) -> bool {
133 self.hostname.is_none()
134 && self.dns.is_none()
135 && self.ovsdb.is_none()
136 && self.rules.is_empty()
137 && self.routes.is_empty()
138 && self.interfaces.is_empty()
139 && self.ovn.is_none()
140 }
141
142 pub(crate) const PASSWORD_HID_BY_NMSTATE: &'static str =
143 "<_password_hid_by_nmstate>";
144
145 pub fn set_kernel_only(&mut self, value: bool) -> &mut Self {
151 self.kernel_only = value;
152 self
153 }
154
155 pub fn kernel_only(&self) -> bool {
156 self.kernel_only
157 }
158
159 pub fn set_verify_change(&mut self, value: bool) -> &mut Self {
165 self.no_verify = !value;
166 self
167 }
168
169 pub fn set_commit(&mut self, value: bool) -> &mut Self {
175 self.no_commit = !value;
176 self
177 }
178
179 pub fn set_timeout(&mut self, value: u32) -> &mut Self {
183 self.timeout = Some(value);
184 self
185 }
186
187 pub fn set_include_secrets(&mut self, value: bool) -> &mut Self {
190 self.include_secrets = value;
191 self
192 }
193
194 pub fn set_include_status_data(&mut self, value: bool) -> &mut Self {
196 self.include_status_data = value;
197 self
198 }
199
200 pub fn set_running_config_only(&mut self, value: bool) -> &mut Self {
206 self.running_config_only = value;
207 self
208 }
209
210 pub fn set_memory_only(&mut self, value: bool) -> &mut Self {
213 self.memory_only = value;
214 self
215 }
216
217 pub fn set_override_iface(&mut self, value: bool) -> &mut Self {
229 self.override_iface = value;
230 self
231 }
232
233 pub fn new() -> Self {
235 Default::default()
236 }
237
238 pub fn new_from_json(net_state_json: &str) -> Result<Self, NmstateError> {
241 match serde_json::from_str(net_state_json) {
242 Ok(s) => Ok(s),
243 Err(e) => Err(NmstateError::new(
244 ErrorKind::InvalidArgument,
245 format!("Invalid JSON string: {e}"),
246 )),
247 }
248 }
249
250 pub fn new_from_yaml(net_state_yaml: &str) -> Result<Self, NmstateError> {
253 match serde_yaml::from_str(net_state_yaml) {
254 Ok(s) => Ok(s),
255 Err(e) => Err(NmstateError::new(
256 ErrorKind::InvalidArgument,
257 format!("Invalid YAML string: {e}"),
258 )),
259 }
260 }
261
262 pub fn append_interface_data(&mut self, iface: Interface) {
264 self.interfaces.push(iface);
265 }
266
267 #[cfg(not(feature = "query_apply"))]
268 pub fn retrieve(&mut self) -> Result<&mut Self, NmstateError> {
269 Err(NmstateError::new(
270 ErrorKind::DependencyError,
271 "NetworkState::retrieve() need `query_apply` feature enabled"
272 .into(),
273 ))
274 }
275
276 #[cfg(not(feature = "query_apply"))]
277 pub async fn retrieve_async(&mut self) -> Result<&mut Self, NmstateError> {
278 Err(NmstateError::new(
279 ErrorKind::DependencyError,
280 "NetworkState::retrieve_async() need `query_apply` feature enabled"
281 .into(),
282 ))
283 }
284
285 pub fn hide_secrets(&mut self) {
287 self.interfaces.hide_secrets();
288 }
289
290 #[cfg(not(feature = "query_apply"))]
291 pub fn apply(&mut self) -> Result<(), NmstateError> {
292 Err(NmstateError::new(
293 ErrorKind::DependencyError,
294 "NetworkState::apply() need `query_apply` feature enabled".into(),
295 ))
296 }
297
298 #[cfg(not(feature = "query_apply"))]
299 pub async fn apply_async(&mut self) -> Result<(), NmstateError> {
300 Err(NmstateError::new(
301 ErrorKind::DependencyError,
302 "NetworkState::apply() need `query_apply` feature enabled".into(),
303 ))
304 }
305
306 #[cfg(not(feature = "gen_conf"))]
307 pub fn gen_conf(
308 &self,
309 ) -> Result<HashMap<String, Vec<(String, String)>>, NmstateError> {
310 Err(NmstateError::new(
311 ErrorKind::DependencyError,
312 "NetworkState::gen_conf() need `genconf` feature enabled".into(),
313 ))
314 }
315
316 #[cfg(not(feature = "query_apply"))]
317 pub fn checkpoint_rollback(_checkpoint: &str) -> Result<(), NmstateError> {
318 Err(NmstateError::new(
319 ErrorKind::DependencyError,
320 "NetworkState::checkpoint_rollback() need `query_apply` \
321 feature enabled"
322 .into(),
323 ))
324 }
325
326 #[cfg(not(feature = "query_apply"))]
327 pub fn checkpoint_commit(_checkpoint: &str) -> Result<(), NmstateError> {
328 Err(NmstateError::new(
329 ErrorKind::DependencyError,
330 "NetworkState::checkpoint_commit() need `query_apply` \
331 feature enabled"
332 .into(),
333 ))
334 }
335}
336
337#[derive(Clone, Debug, Default, PartialEq, Eq)]
338pub(crate) struct MergedNetworkState {
339 pub(crate) interfaces: MergedInterfaces,
340 pub(crate) hostname: MergedHostNameState,
341 pub(crate) dns: MergedDnsState,
342 pub(crate) ovn: MergedOvnConfiguration,
343 pub(crate) ovsdb: MergedOvsDbGlobalConfig,
344 pub(crate) routes: MergedRoutes,
345 pub(crate) rules: MergedRouteRules,
346 pub(crate) memory_only: bool,
347 pub(crate) override_iface: bool,
348}
349
350impl MergedNetworkState {
351 pub(crate) fn new(
352 desired: NetworkState,
353 current: NetworkState,
354 gen_conf_mode: bool,
355 memory_only: bool,
356 ) -> Result<Self, NmstateError> {
357 let mut current = current;
358 if desired.override_iface {
359 current.interfaces = current.interfaces.clone_name_type_only();
360 };
361
362 let interfaces = MergedInterfaces::new(
363 desired.interfaces,
364 current.interfaces,
365 gen_conf_mode,
366 memory_only,
367 )?;
368 let ignored_ifaces = interfaces.ignored_ifaces.as_slice();
369
370 let mut routes =
371 MergedRoutes::new(desired.routes, current.routes, &interfaces)?;
372 routes.remove_routes_to_ignored_ifaces(ignored_ifaces);
373
374 let mut rules = MergedRouteRules::new(desired.rules, current.rules)?;
375 rules.remove_rules_to_ignored_ifaces(ignored_ifaces);
376
377 let hostname =
378 MergedHostNameState::new(desired.hostname, current.hostname);
379
380 let ovn = MergedOvnConfiguration::new(desired.ovn, current.ovn)?;
381
382 let ovsdb = MergedOvsDbGlobalConfig::new(
383 desired.ovsdb,
384 current.ovsdb.unwrap_or_default(),
385 )?;
386
387 let ret = Self {
388 interfaces,
389 routes,
390 rules,
391 dns: MergedDnsState::new(
392 desired.dns,
393 current.dns.unwrap_or_default(),
394 )?,
395 ovn,
396 ovsdb,
397 hostname,
398 memory_only,
399 override_iface: desired.override_iface,
400 };
401 ret.validate_ipv6_link_local_address_dns_srv()?;
402
403 Ok(ret)
404 }
405}