netcfg/telemetry/processors/
network_properties.rs1use crate::telemetry::NetworkEventMetadata;
6use fuchsia_inspect::Node as InspectNode;
7use fuchsia_inspect_contrib::nodes::LruCacheNode;
8use fuchsia_inspect_derive::Unit;
9use windowed_stats::experimental::inspect::{InspectSender, InspectedTimeMatrix};
10use windowed_stats::experimental::series::interpolation::LastSample;
11use windowed_stats::experimental::series::metadata::BitSetNode;
12use windowed_stats::experimental::series::statistic::Union;
13use windowed_stats::experimental::series::{SamplingProfile, TimeMatrix};
14
15pub struct NetworkPropertiesProcessor {
16 default_network_detailed_matrix: InspectedTimeMatrix<u64>,
17 default_network_type_matrix: InspectedTimeMatrix<u64>,
18 inspect_metadata_node: InspectMetadataNode,
19}
20
21const METADATA_NODE_NAME: &str = "metadata";
22
23impl NetworkPropertiesProcessor {
24 pub fn new<S: InspectSender>(parent: &InspectNode, parent_path: &str, client: &S) -> Self {
25 let inspect_metadata_node = parent.create_child(METADATA_NODE_NAME);
26 let inspect_metadata_path = format!("{}/{}", parent_path, METADATA_NODE_NAME);
27 let detailed_time_matrix = TimeMatrix::<Union<u64>, LastSample>::new(
28 SamplingProfile::granular(),
29 LastSample::or(0),
30 );
31 let default_network_detailed_matrix = client.inspect_time_matrix_with_metadata(
32 "default_network_detailed",
33 detailed_time_matrix,
34 BitSetNode::from_path(format!(
35 "{}/{}",
36 inspect_metadata_path,
37 InspectMetadataNode::NETWORK_REGISTRY
38 )),
39 );
40
41 let types_time_matrix = TimeMatrix::<Union<u64>, LastSample>::new(
42 SamplingProfile::granular(),
43 LastSample::or(0),
44 );
45 let default_network_type_matrix = client.inspect_time_matrix_with_metadata(
46 "default_network_type",
47 types_time_matrix,
48 BitSetNode::from_path(format!(
49 "{}/{}",
50 inspect_metadata_path,
51 InspectMetadataNode::NETWORK_TYPES
52 )),
53 );
54
55 Self {
56 default_network_detailed_matrix,
57 default_network_type_matrix,
58 inspect_metadata_node: InspectMetadataNode::new(inspect_metadata_node),
59 }
60 }
61
62 pub fn log_default_network_lost(&mut self) {
63 self.default_network_detailed_matrix.fold_or_log_error(0);
64 self.default_network_type_matrix.fold_or_log_error(0);
65 }
66
67 pub fn log_default_network_changed(&mut self, metadata: NetworkEventMetadata) {
68 let data = NetworkData::from(metadata);
69 let types_mapped_id =
70 self.inspect_metadata_node.network_types.insert(data.transport.clone());
71 self.default_network_type_matrix.fold_or_log_error(1u64 << types_mapped_id);
72
73 let detailed_mapped_id = self.inspect_metadata_node.network_registry.insert(data);
74 self.default_network_detailed_matrix.fold_or_log_error(1u64 << detailed_mapped_id);
75 }
76}
77
78#[derive(Unit, PartialEq, Eq, Hash)]
79struct NetworkData {
80 pub id: u64,
81 pub name: String,
82 pub transport: String,
83 pub is_fuchsia_provisioned: bool,
84}
85
86impl From<NetworkEventMetadata> for NetworkData {
87 fn from(metadata: NetworkEventMetadata) -> Self {
88 let NetworkEventMetadata { id, name, transport, is_fuchsia_provisioned } = metadata;
89 Self {
90 id: id,
91 name: name.unwrap_or_else(|| "unknown".to_string()),
92 transport: format!("{:?}", transport),
93 is_fuchsia_provisioned,
94 }
95 }
96}
97
98const NETWORKS_METADATA_CACHE_SIZE: usize = 16;
99const NETWORK_TYPES_CACHE_SIZE: usize = 8;
100
101struct InspectMetadataNode {
104 _node: InspectNode,
105 network_registry: LruCacheNode<NetworkData>,
106 network_types: LruCacheNode<String>,
107}
108
109impl InspectMetadataNode {
110 const NETWORK_REGISTRY: &'static str = "network_registry";
111 const NETWORK_TYPES: &'static str = "network_types";
112
113 fn new(inspect_node: InspectNode) -> Self {
114 let network_registry = LruCacheNode::new(
117 inspect_node.create_child(Self::NETWORK_REGISTRY),
118 NETWORKS_METADATA_CACHE_SIZE,
119 );
120
121 let network_types = LruCacheNode::new(
123 inspect_node.create_child(Self::NETWORK_TYPES),
124 NETWORK_TYPES_CACHE_SIZE,
125 );
126
127 Self { _node: inspect_node, network_registry, network_types }
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use diagnostics_assertions::{AnyBytesProperty, assert_data_tree};
135 use fidl_fuchsia_net_policy_socketproxy as fnp_socketproxy;
136 use fuchsia_inspect::Inspector;
137 use fuchsia_inspect::reader::DiagnosticsHierarchy;
138 use futures::task::Poll;
139 use std::pin::pin;
140 use windowed_stats::experimental::clock::Timed;
141 use windowed_stats::experimental::inspect::TimeMatrixClient;
142 use windowed_stats::experimental::testing::{MockTimeMatrixClient, TimeMatrixCall};
143
144 pub struct TestHelper {
145 pub inspector: Inspector,
146 pub inspect_node: InspectNode,
147 pub parent_path: String,
148 pub mock_time_matrix_client: MockTimeMatrixClient,
149
150 pub exec: fuchsia_async::TestExecutor,
152 }
153
154 impl TestHelper {
155 pub fn get_inspect_data_tree(&mut self) -> DiagnosticsHierarchy {
156 let read_fut = fuchsia_inspect::reader::read(&self.inspector);
157 let mut read_fut = pin!(read_fut);
158 match self.exec.run_until_stalled(&mut read_fut) {
159 Poll::Pending => {
160 panic!("Unexpected pending state");
161 }
162 Poll::Ready(result) => result.expect("failed to get hierarchy"),
163 }
164 }
165 }
166
167 pub fn setup_test() -> TestHelper {
168 let exec = fuchsia_async::TestExecutor::new_with_fake_time();
169 exec.set_fake_time(fuchsia_async::MonotonicInstant::from_nanos(0));
170
171 let inspector = Inspector::default();
172 let inspect_node = inspector.root().create_child("test_stats");
173 let parent_path = "root/test_stats".to_string();
174
175 TestHelper {
176 inspector,
177 inspect_node,
178 parent_path,
179 mock_time_matrix_client: MockTimeMatrixClient::new(),
180 exec,
181 }
182 }
183
184 fn log_network_events(processor: &mut NetworkPropertiesProcessor) {
185 let eth_metadata = NetworkEventMetadata {
186 id: 0,
187 name: Some("eth0".to_string()),
188 transport: fnp_socketproxy::NetworkType::Ethernet,
189 is_fuchsia_provisioned: true,
190 };
191
192 let wlan_metadata = NetworkEventMetadata {
193 id: 1,
194 name: Some("wlan0".to_string()),
195 transport: fnp_socketproxy::NetworkType::Wifi,
196 is_fuchsia_provisioned: false,
197 };
198
199 processor.log_default_network_changed(eth_metadata);
200 processor.log_default_network_lost();
201 processor.log_default_network_changed(wlan_metadata);
202 }
203
204 #[fuchsia::test]
205 fn log_default_network_time_series_calls() {
206 let harness = setup_test();
207 let mut processor = NetworkPropertiesProcessor::new(
208 &harness.inspect_node,
209 &harness.parent_path,
210 &harness.mock_time_matrix_client,
211 );
212 log_network_events(&mut processor);
213
214 let mut time_matrix_calls = harness.mock_time_matrix_client.drain_calls();
215 assert_eq!(
216 &time_matrix_calls.drain::<u64>("default_network_detailed")[..],
217 &[
218 TimeMatrixCall::Fold(Timed::now(1 << 0)),
219 TimeMatrixCall::Fold(Timed::now(0)),
220 TimeMatrixCall::Fold(Timed::now(1 << 1)),
221 ]
222 );
223 assert_eq!(
224 &time_matrix_calls.drain::<u64>("default_network_type")[..],
225 &[
226 TimeMatrixCall::Fold(Timed::now(1 << 0)),
227 TimeMatrixCall::Fold(Timed::now(0)),
228 TimeMatrixCall::Fold(Timed::now(1 << 1)),
229 ]
230 );
231 }
232
233 #[fuchsia::test]
234 fn log_default_network_inspect_tree() {
235 let mut harness = setup_test();
236 let time_matrix_client =
237 TimeMatrixClient::new(harness.inspect_node.create_child("time_series"));
238 let mut processor = NetworkPropertiesProcessor::new(
239 &harness.inspect_node,
240 &harness.parent_path,
241 &time_matrix_client,
242 );
243 log_network_events(&mut processor);
244
245 let hierarchy = harness.get_inspect_data_tree();
246
247 assert_data_tree!(
248 @executor harness.exec,
249 hierarchy,
250 root: contains {
251 test_stats: contains {
252 metadata: contains {
253 network_registry: contains {
254 "0": contains {
255 data: {
256 id: 0u64,
257 name: "eth0",
258 transport: "Ethernet",
259 is_fuchsia_provisioned: true,
260 }
261 },
262 "1": contains {
263 data: {
264 id: 1u64,
265 name: "wlan0",
266 transport: "Wifi",
267 is_fuchsia_provisioned: false,
268 }
269 }
270 },
271 network_types: contains {
272 "0": contains {
273 data: "Ethernet",
274 },
275 "1": contains {
276 data: "Wifi",
277 }
278 }
279 },
280 time_series: contains {
281 default_network_detailed: {
282 "type": "bitset",
283 "data": AnyBytesProperty,
284 metadata: {
285 index_node_path: "root/test_stats/metadata/network_registry",
286 }
287 },
288 default_network_type: {
289 "type": "bitset",
290 "data": AnyBytesProperty,
291 metadata: {
292 index_node_path: "root/test_stats/metadata/network_types",
293 }
294 },
295 }
296 }
297 }
298 );
299 }
300}