monitord/
varlink_networkd.rs1use std::str::FromStr;
11
12use tracing::debug;
13
14use crate::networkd::{
15 AddressState, AdminState, BoolState, CarrierState, InterfaceState, NetworkdState, OperState,
16};
17use crate::varlink::network::{Interface, Network};
18
19pub use crate::varlink::network::NETWORK_SOCKET_PATH;
20
21fn map_interface(iface: &Interface) -> InterfaceState {
23 let address_state = AddressState::from_str(&iface.address_state).unwrap_or_else(|_| {
24 debug!("Unknown address_state value: {:?}", iface.address_state);
25 AddressState::unknown
26 });
27 let admin_state = AdminState::from_str(&iface.administrative_state).unwrap_or_else(|_| {
28 debug!(
29 "Unknown administrative_state value: {:?}",
30 iface.administrative_state
31 );
32 AdminState::unknown
33 });
34 let carrier_state = CarrierState::from_str(&iface.carrier_state).unwrap_or_else(|_| {
35 debug!("Unknown carrier_state value: {:?}", iface.carrier_state);
36 CarrierState::unknown
37 });
38 let ipv4_address_state =
39 AddressState::from_str(&iface.ipv4_address_state).unwrap_or_else(|_| {
40 debug!(
41 "Unknown ipv4_address_state value: {:?}",
42 iface.ipv4_address_state
43 );
44 AddressState::unknown
45 });
46 let ipv6_address_state =
47 AddressState::from_str(&iface.ipv6_address_state).unwrap_or_else(|_| {
48 debug!(
49 "Unknown ipv6_address_state value: {:?}",
50 iface.ipv6_address_state
51 );
52 AddressState::unknown
53 });
54 let oper_state = OperState::from_str(&iface.operational_state).unwrap_or_else(|_| {
55 debug!(
56 "Unknown operational_state value: {:?}",
57 iface.operational_state
58 );
59 OperState::unknown
60 });
61 let required_for_online = match iface.required_for_online {
62 Some(true) => BoolState::True,
63 Some(false) => BoolState::False,
64 None => BoolState::unknown,
65 };
66 let network_file = iface.network_file.clone().unwrap_or_default();
67
68 InterfaceState {
69 address_state,
70 admin_state,
71 carrier_state,
72 ipv4_address_state,
73 ipv6_address_state,
74 name: iface.name.clone(),
75 network_file,
76 oper_state,
77 required_for_online,
78 }
79}
80
81pub async fn get_networkd_state(socket_path: &str) -> anyhow::Result<NetworkdState> {
86 let socket_path = socket_path.to_string();
87 tokio::task::spawn_blocking(move || {
88 let rt = tokio::runtime::Builder::new_current_thread()
89 .enable_all()
90 .build()?;
91 rt.block_on(async move {
92 let mut conn = zlink::unix::connect(&socket_path).await?;
93 let result = conn.describe().await?;
94 match result {
95 Ok(output) => {
96 let mut interfaces_state = Vec::new();
97 let mut managed_interfaces: u64 = 0;
98 for iface in output.interfaces.unwrap_or_default() {
99 if iface.network_file.is_none() {
102 continue;
103 }
104 managed_interfaces += 1;
105 interfaces_state.push(map_interface(&iface));
106 }
107 Ok(NetworkdState {
108 interfaces_state,
109 managed_interfaces,
110 })
111 }
112 Err(e) => Err(anyhow::anyhow!("io.systemd.Network.Describe error: {}", e)),
113 }
114 })
115 })
116 .await?
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_map_interface_full() {
125 let iface = Interface {
126 index: 2,
127 name: "eth0".to_string(),
128 administrative_state: "configured".to_string(),
129 operational_state: "routable".to_string(),
130 carrier_state: "carrier".to_string(),
131 address_state: "routable".to_string(),
132 ipv4_address_state: "degraded".to_string(),
133 ipv6_address_state: "routable".to_string(),
134 online_state: Some("online".to_string()),
135 network_file: Some("/etc/systemd/network/69-eno4.network".to_string()),
136 required_for_online: Some(true),
137 };
138 let state = map_interface(&iface);
139 assert_eq!(state.name, "eth0");
140 assert_eq!(state.admin_state, AdminState::configured);
141 assert_eq!(state.oper_state, OperState::routable);
142 assert_eq!(state.carrier_state, CarrierState::carrier);
143 assert_eq!(state.address_state, AddressState::routable);
144 assert_eq!(state.ipv4_address_state, AddressState::degraded);
145 assert_eq!(state.ipv6_address_state, AddressState::routable);
146 assert_eq!(state.required_for_online, BoolState::True);
147 assert_eq!(
148 state.network_file,
149 "/etc/systemd/network/69-eno4.network".to_string()
150 );
151 }
152
153 #[test]
154 fn test_map_interface_unknown_states() {
155 let iface = Interface {
156 index: 3,
157 name: "wg0".to_string(),
158 administrative_state: "initialized".to_string(), operational_state: "unknown-oper".to_string(),
160 carrier_state: "no-carrier".to_string(),
161 address_state: "off".to_string(),
162 ipv4_address_state: "off".to_string(),
163 ipv6_address_state: "off".to_string(),
164 online_state: None,
165 network_file: Some("/etc/systemd/network/wg0.network".to_string()),
166 required_for_online: Some(false),
167 };
168 let state = map_interface(&iface);
169 assert_eq!(state.admin_state, AdminState::unknown);
170 assert_eq!(state.oper_state, OperState::unknown);
171 assert_eq!(state.carrier_state, CarrierState::no_carrier);
172 assert_eq!(state.address_state, AddressState::off);
173 assert_eq!(state.required_for_online, BoolState::False);
174 }
175
176 #[test]
177 fn test_map_interface_no_required_for_online() {
178 let iface = Interface {
179 index: 1,
180 name: "lo".to_string(),
181 administrative_state: "unmanaged".to_string(),
182 operational_state: "carrier".to_string(),
183 carrier_state: "carrier".to_string(),
184 address_state: "degraded".to_string(),
185 ipv4_address_state: "degraded".to_string(),
186 ipv6_address_state: "degraded".to_string(),
187 online_state: None,
188 network_file: None,
189 required_for_online: None,
190 };
191 let state = map_interface(&iface);
192 assert_eq!(state.required_for_online, BoolState::unknown);
193 assert_eq!(state.network_file, "");
194 }
195}