From 929d3a1cf2078ed62a4cf1380b3ee675a84ccff3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 05:28:04 +0000 Subject: [PATCH 1/2] Initial plan From c8bf8dc9fb5fd78288d88f4a18bf5121a967430f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 21 May 2026 05:58:06 +0000 Subject: [PATCH 2/2] feat: add name fields to Board/SoC monitoring information - Add `name` field to `SocInfo` and `BoardInfo` structs in data_structures.rs - Add `name` field to `SocInfo` and `BoardInfo` in settingsservice monitoring_types.rs - Create name_config.rs module with MonitoringNamesConfig for loading YAML config - Update DataStore to load name config via with_name_config() constructor - Update MonitoringServerManager::new() to load names from /etc/piccolo/monitoring_names.yaml - Add print display of name fields in print_soc_info() and print_board_info() - Create examples/resources/monitoring_names.yaml as reference config file - Add serde_yaml and tempfile dependencies to monitoringserver - Update all existing tests to include the new name field - Add new unit tests for name configuration loading and lookup Agent-Logs-Url: https://github.com/eclipse-pullpiri/pullpiri/sessions/8d02f053-98a4-4a99-a0b6-9c3f4291de86 Co-authored-by: Chulhee1Lee <104404644+Chulhee1Lee@users.noreply.github.com> --- examples/resources/monitoring_names.yaml | 31 +++ src/Cargo.lock | 2 + src/server/monitoringserver/Cargo.toml | 4 + .../monitoringserver/src/data_structures.rs | 123 +++++++++-- .../monitoringserver/src/etcd_storage.rs | 2 + src/server/monitoringserver/src/main.rs | 1 + src/server/monitoringserver/src/manager.rs | 25 ++- .../monitoringserver/src/name_config.rs | 199 ++++++++++++++++++ .../settingsservice/src/monitoring_etcd.rs | 3 + .../settingsservice/src/monitoring_types.rs | 15 ++ 10 files changed, 373 insertions(+), 32 deletions(-) create mode 100644 examples/resources/monitoring_names.yaml create mode 100644 src/server/monitoringserver/src/name_config.rs diff --git a/examples/resources/monitoring_names.yaml b/examples/resources/monitoring_names.yaml new file mode 100644 index 000000000..f33410e6b --- /dev/null +++ b/examples/resources/monitoring_names.yaml @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: Copyright 2024 LG Electronics Inc. +# SPDX-License-Identifier: Apache-2.0 +# +# Example monitoring_names.yaml +# +# This file assigns human-readable names to Boards and SoCs identified by +# their IP-based IDs (generated from the node IP addresses). +# +# Board ID: first three octets of node IP + (last octet rounded to nearest 100) +# e.g., nodes 192.168.10.201 to 192.168.10.299 -> board_id = 192.168.10.200 +# +# SoC ID: first three octets of node IP + (last octet rounded to nearest 10) +# e.g., nodes 192.168.10.201 to 192.168.10.209 -> soc_id = 192.168.10.200 +# +# Deploy this file to /etc/piccolo/monitoring_names.yaml on the monitoring server. + +boards: + "192.168.10.0": + name: "Main Vehicle Board" + "192.168.10.100": + name: "Sensor Board" + "192.168.10.200": + name: "Compute Board" + +socs: + "192.168.10.200": + name: "Alpha SoC" + "192.168.10.210": + name: "Beta SoC" + "192.168.10.220": + name: "Gamma SoC" diff --git a/src/Cargo.lock b/src/Cargo.lock index 46acddeea..d8f68ece1 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1561,6 +1561,8 @@ dependencies = [ "prost", "serde", "serde_json", + "serde_yaml", + "tempfile", "tokio", "tonic", ] diff --git a/src/server/monitoringserver/Cargo.toml b/src/server/monitoringserver/Cargo.toml index 48ec2e9fc..babbba9fc 100644 --- a/src/server/monitoringserver/Cargo.toml +++ b/src/server/monitoringserver/Cargo.toml @@ -12,5 +12,9 @@ common.workspace = true prost = "0.13.3" serde = "1.0.214" serde_json = "1.0.143" +serde_yaml = "0.9" tokio = "1.43.1" tonic = "0.12.3" + +[dev-dependencies] +tempfile = "3.0" diff --git a/src/server/monitoringserver/src/data_structures.rs b/src/server/monitoringserver/src/data_structures.rs index bf75f1e37..f2948cb0c 100644 --- a/src/server/monitoringserver/src/data_structures.rs +++ b/src/server/monitoringserver/src/data_structures.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +use crate::name_config::MonitoringNamesConfig; use common::monitoringserver::ContainerInfo; use common::monitoringserver::NodeInfo; use serde::{Deserialize, Serialize}; @@ -15,6 +16,8 @@ use std::str::FromStr; pub struct SocInfo { /// Temporary IP-based ID until proper SoC identification policy is defined pub soc_id: String, + /// Human-readable name for this SoC, configured via monitoring_names.yaml + pub name: String, pub nodes: Vec, pub total_cpu_usage: f64, pub total_cpu_count: u64, @@ -34,6 +37,8 @@ pub struct SocInfo { pub struct BoardInfo { /// Temporary IP-based ID until proper Board identification policy is defined pub board_id: String, + /// Human-readable name for this Board, configured via monitoring_names.yaml + pub name: String, pub nodes: Vec, pub socs: Vec, pub total_cpu_usage: f64, @@ -57,6 +62,8 @@ pub struct DataStore { pub boards: HashMap, pub containers: HashMap, pub container_node_mapping: HashMap, // ADD THIS LINE + /// Name configuration for Board and SoC human-readable names + pub names: MonitoringNamesConfig, } impl Default for DataStore { @@ -73,6 +80,19 @@ impl DataStore { boards: HashMap::new(), containers: HashMap::new(), container_node_mapping: HashMap::new(), // ADD THIS LINE + names: MonitoringNamesConfig::default(), + } + } + + /// Creates a new DataStore and loads name configuration from the given path. + pub fn with_name_config(config_path: &str) -> Self { + Self { + nodes: HashMap::new(), + socs: HashMap::new(), + boards: HashMap::new(), + containers: HashMap::new(), + container_node_mapping: HashMap::new(), + names: MonitoringNamesConfig::load_from_file(config_path), } } @@ -170,7 +190,8 @@ impl DataStore { soc_info.update_with_node(node_info); soc_info.last_updated = current_time; } else { - let soc_info = SocInfo::new(soc_id.clone(), node_info); + let name = self.names.soc_name(&soc_id); + let soc_info = SocInfo::new(soc_id.clone(), name, node_info); self.socs.insert(soc_id, soc_info); } @@ -185,7 +206,8 @@ impl DataStore { board_info.update_with_node(node_info); board_info.last_updated = current_time; } else { - let board_info = BoardInfo::new(board_id.clone(), node_info); + let name = self.names.board_name(&board_id); + let board_info = BoardInfo::new(board_id.clone(), name, node_info); self.boards.insert(board_id.clone(), board_info); } @@ -407,9 +429,10 @@ impl DataStore { impl SocInfo { /// Creates new SocInfo with the first node - pub fn new(soc_id: String, node_info: NodeInfo) -> Self { + pub fn new(soc_id: String, name: String, node_info: NodeInfo) -> Self { let mut soc_info = Self { soc_id, + name, nodes: vec![node_info.clone()], total_cpu_usage: node_info.cpu_usage, total_cpu_count: node_info.cpu_count, @@ -446,9 +469,10 @@ impl SocInfo { impl BoardInfo { /// Creates new BoardInfo with the first node - pub fn new(board_id: String, node_info: NodeInfo) -> Self { + pub fn new(board_id: String, name: String, node_info: NodeInfo) -> Self { let mut board_info = Self { board_id, + name, nodes: vec![node_info.clone()], socs: Vec::new(), // Populated by update_board_socs total_cpu_usage: node_info.cpu_usage, @@ -658,7 +682,7 @@ mod tests { #[test] fn test_socinfo_update_with_node() { let node1 = sample_node("node1", "192.168.10.201"); - let mut soc = SocInfo::new("192.168.10.200".to_string(), node1.clone()); + let mut soc = SocInfo::new("192.168.10.200".to_string(), String::new(), node1.clone()); assert_eq!(soc.nodes.len(), 1); let node2 = sample_node("node2", "192.168.10.202"); @@ -676,7 +700,7 @@ mod tests { #[test] fn test_boardinfo_update_with_node() { let node1 = sample_node("node1", "192.168.10.201"); - let mut board = BoardInfo::new("192.168.10.200".to_string(), node1.clone()); + let mut board = BoardInfo::new("192.168.10.200".to_string(), String::new(), node1.clone()); assert_eq!(board.nodes.len(), 1); let node2 = sample_node("node2", "192.168.10.202"); @@ -695,7 +719,7 @@ mod tests { fn test_aggregated_metrics_trait() { let node1 = sample_node("node1", "192.168.10.201"); let node2 = sample_node("node2", "192.168.10.202"); - let mut soc = SocInfo::new("192.168.10.200".to_string(), node1.clone()); + let mut soc = SocInfo::new("192.168.10.200".to_string(), String::new(), node1.clone()); soc.update_with_node(node2.clone()); soc.recalculate_totals(); assert_eq!(soc.total_cpu_count, 8); @@ -704,7 +728,7 @@ mod tests { assert_eq!(soc.total_memory, 8192); assert_eq!(soc.total_mem_usage, 50.0); - let mut board = BoardInfo::new("192.168.10.200".to_string(), node1.clone()); + let mut board = BoardInfo::new("192.168.10.200".to_string(), String::new(), node1.clone()); board.update_with_node(node2.clone()); board.recalculate_totals(); assert_eq!(board.total_cpu_count, 8); @@ -722,6 +746,55 @@ mod tests { assert!(ds.boards.is_empty()); assert!(ds.containers.is_empty()); assert!(ds.container_node_mapping.is_empty()); + assert!(ds.names.boards.is_empty()); + assert!(ds.names.socs.is_empty()); + } + + #[test] + fn test_datastore_with_name_config_nonexistent_path() { + let ds = DataStore::with_name_config("/nonexistent/path/monitoring_names.yaml"); + assert!(ds.names.boards.is_empty()); + assert!(ds.names.socs.is_empty()); + } + + #[tokio::test] + async fn test_store_node_info_uses_name_config() { + use crate::name_config::{MonitoringNamesConfig, NameEntry}; + let mut ds = DataStore::new(); + ds.names = MonitoringNamesConfig { + boards: { + let mut m = std::collections::HashMap::new(); + m.insert( + "192.168.10.200".to_string(), + NameEntry { + name: "Test Board".to_string(), + }, + ); + m + }, + socs: { + let mut m = std::collections::HashMap::new(); + m.insert( + "192.168.10.200".to_string(), + NameEntry { + name: "Test SoC".to_string(), + }, + ); + m + }, + }; + + let node = sample_node("node1", "192.168.10.201"); + let _ = ds.store_node_info(node).await; + + let soc_id = DataStore::generate_soc_id("192.168.10.201").unwrap(); + let board_id = DataStore::generate_board_id("192.168.10.201").unwrap(); + + let soc = ds.socs.get(&soc_id).unwrap(); + assert_eq!(soc.name, "Test SoC"); + + let board = ds.boards.get(&board_id).unwrap(); + assert_eq!(board.name, "Test Board"); } #[test] @@ -792,9 +865,9 @@ mod tests { let mut ds = DataStore::new(); let node = sample_node("node1", "192.168.10.201"); ds.nodes.insert("node1".to_string(), node.clone()); - let soc = SocInfo::new("192.168.10.200".to_string(), node.clone()); + let soc = SocInfo::new("192.168.10.200".to_string(), String::new(), node.clone()); ds.socs.insert("192.168.10.200".to_string(), soc.clone()); - let board = BoardInfo::new("192.168.10.200".to_string(), node.clone()); + let board = BoardInfo::new("192.168.10.200".to_string(), String::new(), node.clone()); ds.boards .insert("192.168.10.200".to_string(), board.clone()); @@ -810,9 +883,9 @@ mod tests { fn test_update_board_socs() { let mut ds = DataStore::new(); let node = sample_node("node1", "192.168.10.201"); - let soc = SocInfo::new("192.168.10.200".to_string(), node.clone()); + let soc = SocInfo::new("192.168.10.200".to_string(), String::new(), node.clone()); ds.socs.insert("192.168.10.200".to_string(), soc.clone()); - let board = BoardInfo::new("192.168.10.200".to_string(), node.clone()); + let board = BoardInfo::new("192.168.10.200".to_string(), String::new(), node.clone()); ds.boards .insert("192.168.10.200".to_string(), board.clone()); @@ -827,6 +900,7 @@ mod tests { fn test_socinfo_and_boardinfo_recalculate_totals_empty() { let mut soc = SocInfo { soc_id: "test".to_string(), + name: String::new(), nodes: vec![], total_cpu_usage: 0.0, total_cpu_count: 0, @@ -845,6 +919,7 @@ mod tests { let mut board = BoardInfo { board_id: "test".to_string(), + name: String::new(), nodes: vec![], socs: vec![], total_cpu_usage: 0.0, @@ -887,8 +962,8 @@ mod tests { fn test_get_all_containers_and_nodes_socs_boards() { let mut ds = DataStore::new(); let node = sample_node("node1", "192.168.10.201"); - let soc = SocInfo::new("192.168.10.200".to_string(), node.clone()); - let board = BoardInfo::new("192.168.10.200".to_string(), node.clone()); + let soc = SocInfo::new("192.168.10.200".to_string(), String::new(), node.clone()); + let board = BoardInfo::new("192.168.10.200".to_string(), String::new(), node.clone()); let container = sample_container("c1", "container1"); ds.nodes.insert("node1".to_string(), node.clone()); @@ -930,8 +1005,8 @@ mod tests { let node = sample_node("node1", "192.168.10.201"); let soc_id = "192.168.10.200".to_string(); let board_id = "192.168.10.200".to_string(); - let soc = SocInfo::new(soc_id.clone(), node.clone()); - let board = BoardInfo::new(board_id.clone(), node.clone()); + let soc = SocInfo::new(soc_id.clone(), String::new(), node.clone()); + let board = BoardInfo::new(board_id.clone(), String::new(), node.clone()); ds.socs.insert(soc_id.clone(), soc); ds.boards.insert(board_id.clone(), board); @@ -1033,11 +1108,11 @@ mod tests { #[test] fn test_socinfo_and_boardinfo_new_and_update_with_node() { let node = sample_node("node1", "192.168.10.201"); - let mut soc = SocInfo::new("socid".to_string(), node.clone()); + let mut soc = SocInfo::new("socid".to_string(), String::new(), node.clone()); assert_eq!(soc.soc_id, "socid"); assert_eq!(soc.nodes.len(), 1); - let mut board = BoardInfo::new("boardid".to_string(), node.clone()); + let mut board = BoardInfo::new("boardid".to_string(), String::new(), node.clone()); assert_eq!(board.board_id, "boardid"); assert_eq!(board.nodes.len(), 1); @@ -1097,8 +1172,8 @@ mod tests { let node = sample_node("node1", "192.168.10.201"); let soc_id = "192.168.10.200".to_string(); let board_id = "192.168.10.200".to_string(); - let soc = SocInfo::new(soc_id.clone(), node.clone()); - let board = BoardInfo::new(board_id.clone(), node.clone()); + let soc = SocInfo::new(soc_id.clone(), String::new(), node.clone()); + let board = BoardInfo::new(board_id.clone(), String::new(), node.clone()); ds.socs.insert(soc_id.clone(), soc); ds.boards.insert(board_id.clone(), board); @@ -1111,8 +1186,8 @@ mod tests { fn test_get_all_methods() { let mut ds = DataStore::new(); let node = sample_node("node1", "192.168.10.201"); - let soc = SocInfo::new("192.168.10.200".to_string(), node.clone()); - let board = BoardInfo::new("192.168.10.200".to_string(), node.clone()); + let soc = SocInfo::new("192.168.10.200".to_string(), String::new(), node.clone()); + let board = BoardInfo::new("192.168.10.200".to_string(), String::new(), node.clone()); let container = sample_container("c1", "container1"); ds.nodes.insert("node1".to_string(), node.clone()); @@ -1130,7 +1205,7 @@ mod tests { #[test] fn test_boardinfo_update_with_node_existing_and_new() { let node1 = sample_node("node1", "192.168.10.201"); - let mut board = BoardInfo::new("boardid".to_string(), node1.clone()); + let mut board = BoardInfo::new("boardid".to_string(), String::new(), node1.clone()); assert_eq!(board.nodes.len(), 1); // Add new node @@ -1150,6 +1225,7 @@ mod tests { fn test_aggregated_metrics_trait_empty_and_nonempty() { let mut soc = SocInfo { soc_id: "test".to_string(), + name: String::new(), nodes: vec![], total_cpu_usage: 0.0, total_cpu_count: 0, @@ -1173,6 +1249,7 @@ mod tests { let mut board = BoardInfo { board_id: "test".to_string(), + name: String::new(), nodes: vec![], socs: vec![], total_cpu_usage: 0.0, diff --git a/src/server/monitoringserver/src/etcd_storage.rs b/src/server/monitoringserver/src/etcd_storage.rs index 20cd655b9..65ab07bde 100644 --- a/src/server/monitoringserver/src/etcd_storage.rs +++ b/src/server/monitoringserver/src/etcd_storage.rs @@ -382,6 +382,7 @@ mod tests { fn sample_soc(soc_id: &str, node: NodeInfo) -> SocInfo { SocInfo { soc_id: soc_id.to_string(), + name: String::new(), nodes: vec![node], total_cpu_usage: 42.0, total_cpu_count: 2, @@ -400,6 +401,7 @@ mod tests { fn sample_board(board_id: &str, node: NodeInfo) -> BoardInfo { BoardInfo { board_id: board_id.to_string(), + name: String::new(), nodes: vec![node], socs: vec![], total_cpu_usage: 42.0, diff --git a/src/server/monitoringserver/src/main.rs b/src/server/monitoringserver/src/main.rs index ad89f3b66..467d4957f 100644 --- a/src/server/monitoringserver/src/main.rs +++ b/src/server/monitoringserver/src/main.rs @@ -12,6 +12,7 @@ pub mod data_structures; pub mod etcd_storage; pub mod grpc; pub mod manager; +pub mod name_config; use common::logd; use common::logd::logger; diff --git a/src/server/monitoringserver/src/manager.rs b/src/server/monitoringserver/src/manager.rs index 1629be3a8..decc8179f 100644 --- a/src/server/monitoringserver/src/manager.rs +++ b/src/server/monitoringserver/src/manager.rs @@ -30,6 +30,9 @@ pub struct MonitoringServerManager { impl MonitoringServerManager { /// Creates a new MonitoringServerManager instance. + /// + /// Loads Board and SoC name configuration from the default path + /// (`/etc/piccolo/monitoring_names.yaml`). pub async fn new( rx_container: mpsc::Receiver, rx_node: mpsc::Receiver, @@ -39,7 +42,9 @@ impl MonitoringServerManager { rx_container: Arc::new(Mutex::new(rx_container)), rx_node: Arc::new(Mutex::new(rx_node)), rx_stress: Arc::new(Mutex::new(rx_stress)), - data_store: Arc::new(Mutex::new(DataStore::new())), + data_store: Arc::new(Mutex::new(DataStore::with_name_config( + crate::name_config::DEFAULT_CONFIG_PATH, + ))), } } @@ -369,6 +374,7 @@ impl MonitoringServerManager { println!("\nBOARD INFORMATION"); println!("┌─────────────────────────────────────────────────────────────────────────────┐"); println!("│ Board ID: {:<65} │", board_info.board_id); + println!("│ Board Name: {:<63} │", board_info.name); println!( "│ Nodes Count: {:<6} │ SoCs Count: {:<6} │ Updated: {:<19} │", board_info.nodes.len(), @@ -491,6 +497,7 @@ impl MonitoringServerManager { println!("\n SOC INFORMATION"); println!("┌─────────────────────────────────────────────────────────────────────────────┐"); println!("│ SoC ID: {:<67} │", soc_info.soc_id); + println!("│ SoC Name: {:<65} │", soc_info.name); println!("│ Nodes Count: {:<62} │", soc_info.nodes.len()); println!("├─────────────────────────────────────────────────────────────────────────────┤"); println!("│ Aggregated Metrics: │"); @@ -948,8 +955,8 @@ mod tests { { let mut ds = mgr.data_store.lock().await; let node = sample_node("node1", "192.168.10.201"); - let soc = SocInfo::new("socid".to_string(), node.clone()); - let mut board = BoardInfo::new("boardid".to_string(), node.clone()); + let soc = SocInfo::new("socid".to_string(), String::new(), node.clone()); + let mut board = BoardInfo::new("boardid".to_string(), String::new(), node.clone()); board.socs.push(soc.clone()); ds.socs.insert("socid".to_string(), soc); ds.boards.insert("boardid".to_string(), board); @@ -965,10 +972,10 @@ mod tests { let node = sample_node("node1", "192.168.10.201"); mgr.print_node_info(&node); - let soc = SocInfo::new("socid".to_string(), node.clone()); + let soc = SocInfo::new("socid".to_string(), String::new(), node.clone()); mgr.print_soc_info(&soc); - let mut board = BoardInfo::new("boardid".to_string(), node.clone()); + let mut board = BoardInfo::new("boardid".to_string(), String::new(), node.clone()); board.socs.push(soc); mgr.print_board_info(&board); } @@ -1027,9 +1034,9 @@ mod tests { let mut ds = mgr.data_store.lock().await; let node = sample_node("node1", "192.168.10.201"); ds.nodes.insert("node1".to_string(), node.clone()); - let soc = SocInfo::new("socid".to_string(), node.clone()); + let soc = SocInfo::new("socid".to_string(), String::new(), node.clone()); ds.socs.insert("socid".to_string(), soc); - let board = BoardInfo::new("boardid".to_string(), node.clone()); + let board = BoardInfo::new("boardid".to_string(), String::new(), node.clone()); ds.boards.insert("boardid".to_string(), board); let container = sample_container("c1", "cont1", "running"); ds.containers.insert("c1".to_string(), container); @@ -1057,9 +1064,9 @@ mod tests { let mut ds = DataStore::new(); let node = sample_node("node1", "192.168.10.201"); - let soc = SocInfo::new("socid".to_string(), node.clone()); + let soc = SocInfo::new("socid".to_string(), String::new(), node.clone()); ds.socs.insert("socid".to_string(), soc); - let board = BoardInfo::new("boardid".to_string(), node.clone()); + let board = BoardInfo::new("boardid".to_string(), String::new(), node.clone()); ds.boards.insert("boardid".to_string(), board); mgr.print_detailed_soc_mapping(&ds).await; diff --git a/src/server/monitoringserver/src/name_config.rs b/src/server/monitoringserver/src/name_config.rs new file mode 100644 index 000000000..f747da002 --- /dev/null +++ b/src/server/monitoringserver/src/name_config.rs @@ -0,0 +1,199 @@ +/* + * SPDX-FileCopyrightText: Copyright 2024 LG Electronics Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Name configuration for Board and SoC monitoring information. +//! +//! This module loads a YAML configuration file that maps IP-based Board/SoC IDs +//! to human-readable names. The default configuration path is +//! `/etc/piccolo/monitoring_names.yaml`. + +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +/// Default path for the monitoring name configuration file +pub const DEFAULT_CONFIG_PATH: &str = "/etc/piccolo/monitoring_names.yaml"; + +/// Name entry for a single Board or SoC +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct NameEntry { + /// Human-readable name for this Board or SoC + pub name: String, +} + +/// Top-level name configuration loaded from YAML +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct MonitoringNamesConfig { + /// Maps board_id (IP-based) to name entry + #[serde(default)] + pub boards: HashMap, + /// Maps soc_id (IP-based) to name entry + #[serde(default)] + pub socs: HashMap, +} + +impl MonitoringNamesConfig { + /// Loads configuration from the given YAML file path. + /// + /// Returns an empty configuration if the file does not exist or cannot be parsed, + /// so that the service can still run without a name config. + pub fn load_from_file(path: &str) -> Self { + match std::fs::read_to_string(path) { + Ok(content) => match serde_yaml::from_str::(&content) { + Ok(config) => { + eprintln!( + "[NameConfig] Loaded monitoring names config from '{}'", + path + ); + config + } + Err(e) => { + eprintln!( + "[NameConfig] Failed to parse '{}': {}. Using empty config.", + path, e + ); + Self::default() + } + }, + Err(e) => { + eprintln!( + "[NameConfig] Could not read '{}': {}. Using empty config.", + path, e + ); + Self::default() + } + } + } + + /// Looks up the name for a given soc_id. Returns an empty string if not configured. + pub fn soc_name(&self, soc_id: &str) -> String { + self.socs + .get(soc_id) + .map(|e| e.name.clone()) + .unwrap_or_default() + } + + /// Looks up the name for a given board_id. Returns an empty string if not configured. + pub fn board_name(&self, board_id: &str) -> String { + self.boards + .get(board_id) + .map(|e| e.name.clone()) + .unwrap_or_default() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default_config_is_empty() { + let config = MonitoringNamesConfig::default(); + assert!(config.boards.is_empty()); + assert!(config.socs.is_empty()); + } + + #[test] + fn test_soc_name_lookup_missing_returns_empty() { + let config = MonitoringNamesConfig::default(); + assert_eq!(config.soc_name("192.168.10.200"), ""); + } + + #[test] + fn test_board_name_lookup_missing_returns_empty() { + let config = MonitoringNamesConfig::default(); + assert_eq!(config.board_name("192.168.10.0"), ""); + } + + #[test] + fn test_soc_name_lookup_found() { + let mut config = MonitoringNamesConfig::default(); + config.socs.insert( + "192.168.10.200".to_string(), + NameEntry { + name: "Alpha SoC".to_string(), + }, + ); + assert_eq!(config.soc_name("192.168.10.200"), "Alpha SoC"); + } + + #[test] + fn test_board_name_lookup_found() { + let mut config = MonitoringNamesConfig::default(); + config.boards.insert( + "192.168.10.0".to_string(), + NameEntry { + name: "Main Board".to_string(), + }, + ); + assert_eq!(config.board_name("192.168.10.0"), "Main Board"); + } + + #[test] + fn test_load_from_nonexistent_file_returns_default() { + let config = MonitoringNamesConfig::load_from_file("/nonexistent/path/config.yaml"); + assert!(config.boards.is_empty()); + assert!(config.socs.is_empty()); + } + + #[test] + fn test_deserialize_from_yaml() { + let yaml = r#" +boards: + "192.168.10.0": + name: "Main Board" + "192.168.11.0": + name: "Rear Board" +socs: + "192.168.10.200": + name: "Alpha SoC" + "192.168.10.210": + name: "Beta SoC" +"#; + let config: MonitoringNamesConfig = + serde_yaml::from_str(yaml).expect("Failed to parse YAML"); + + assert_eq!(config.boards.len(), 2); + assert_eq!(config.socs.len(), 2); + assert_eq!(config.board_name("192.168.10.0"), "Main Board"); + assert_eq!(config.board_name("192.168.11.0"), "Rear Board"); + assert_eq!(config.soc_name("192.168.10.200"), "Alpha SoC"); + assert_eq!(config.soc_name("192.168.10.210"), "Beta SoC"); + } + + #[test] + fn test_deserialize_partial_yaml() { + let yaml = r#" +boards: + "10.0.0.0": + name: "Only Board" +"#; + let config: MonitoringNamesConfig = + serde_yaml::from_str(yaml).expect("Failed to parse partial YAML"); + assert_eq!(config.board_name("10.0.0.0"), "Only Board"); + assert_eq!(config.soc_name("10.0.0.0"), ""); + } + + #[test] + fn test_load_from_file_with_valid_content() { + use std::io::Write; + + let mut temp = tempfile::NamedTempFile::new().expect("Failed to create temp file"); + let yaml = r#" +boards: + "192.168.1.0": + name: "Test Board" +socs: + "192.168.1.100": + name: "Test SoC" +"#; + temp.write_all(yaml.as_bytes()) + .expect("Failed to write temp file"); + + let config = + MonitoringNamesConfig::load_from_file(temp.path().to_str().expect("Invalid path")); + assert_eq!(config.board_name("192.168.1.0"), "Test Board"); + assert_eq!(config.soc_name("192.168.1.100"), "Test SoC"); + } +} diff --git a/src/server/settingsservice/src/monitoring_etcd.rs b/src/server/settingsservice/src/monitoring_etcd.rs index a4443b480..ef7cd5631 100644 --- a/src/server/settingsservice/src/monitoring_etcd.rs +++ b/src/server/settingsservice/src/monitoring_etcd.rs @@ -407,6 +407,7 @@ mod tests { SocInfo { soc_id: "soc-1".to_string(), + name: String::new(), nodes: vec![test_node], total_cpu_usage: 50.0, total_cpu_count: 8, @@ -442,6 +443,7 @@ mod tests { let test_soc = SocInfo { soc_id: "soc-1".to_string(), + name: String::new(), nodes: vec![test_node.clone()], total_cpu_usage: 50.0, total_cpu_count: 8, @@ -458,6 +460,7 @@ mod tests { BoardInfo { board_id: "board-1".to_string(), + name: String::new(), nodes: vec![test_node], socs: vec![test_soc], total_cpu_usage: 50.0, diff --git a/src/server/settingsservice/src/monitoring_types.rs b/src/server/settingsservice/src/monitoring_types.rs index 3ad32404b..9a87717ef 100644 --- a/src/server/settingsservice/src/monitoring_types.rs +++ b/src/server/settingsservice/src/monitoring_types.rs @@ -46,6 +46,8 @@ pub struct StressMetrics { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct SocInfo { pub soc_id: String, + /// Human-readable name for this SoC, configured via monitoring_names.yaml + pub name: String, pub nodes: Vec, pub total_cpu_usage: f64, pub total_cpu_count: u64, @@ -63,6 +65,8 @@ pub struct SocInfo { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct BoardInfo { pub board_id: String, + /// Human-readable name for this Board, configured via monitoring_names.yaml + pub name: String, pub nodes: Vec, pub socs: Vec, pub total_cpu_usage: f64, @@ -154,6 +158,7 @@ mod tests { SocInfo { soc_id: "soc-alpha-1".to_string(), + name: "Alpha SoC".to_string(), nodes: vec![node1, node2], total_cpu_usage: 65.8, total_cpu_count: 16, @@ -175,6 +180,7 @@ mod tests { BoardInfo { board_id: "board-main-1".to_string(), + name: "Main Board".to_string(), nodes, socs, total_cpu_usage: 68.2, @@ -216,6 +222,7 @@ mod tests { let soc = create_test_soc_info(); assert_eq!(soc.soc_id, "soc-alpha-1"); + assert_eq!(soc.name, "Alpha SoC"); assert_eq!(soc.nodes.len(), 2); assert_eq!(soc.total_cpu_usage, 65.8); assert_eq!(soc.total_cpu_count, 16); @@ -238,6 +245,7 @@ mod tests { let board = create_test_board_info(); assert_eq!(board.board_id, "board-main-1"); + assert_eq!(board.name, "Main Board"); assert_eq!(board.nodes.len(), 1); assert_eq!(board.socs.len(), 1); assert_eq!(board.total_cpu_usage, 68.2); @@ -286,6 +294,7 @@ mod tests { // Test serialization let serialized = serde_json::to_string(&soc).expect("Failed to serialize SocInfo"); assert!(serialized.contains("soc-alpha-1")); + assert!(serialized.contains("Alpha SoC")); assert!(serialized.contains("65.8")); assert!(serialized.contains("test-node-1")); assert!(serialized.contains("test-node-2")); @@ -294,6 +303,7 @@ mod tests { let deserialized: SocInfo = serde_json::from_str(&serialized).expect("Failed to deserialize SocInfo"); assert_eq!(deserialized.soc_id, soc.soc_id); + assert_eq!(deserialized.name, soc.name); assert_eq!(deserialized.total_cpu_usage, soc.total_cpu_usage); assert_eq!(deserialized.nodes.len(), soc.nodes.len()); assert_eq!(deserialized.nodes[0].node_name, soc.nodes[0].node_name); @@ -306,6 +316,7 @@ mod tests { // Test serialization let serialized = serde_json::to_string(&board).expect("Failed to serialize BoardInfo"); assert!(serialized.contains("board-main-1")); + assert!(serialized.contains("Main Board")); assert!(serialized.contains("68.2")); assert!(serialized.contains("soc-alpha-1")); @@ -313,6 +324,7 @@ mod tests { let deserialized: BoardInfo = serde_json::from_str(&serialized).expect("Failed to deserialize BoardInfo"); assert_eq!(deserialized.board_id, board.board_id); + assert_eq!(deserialized.name, board.name); assert_eq!(deserialized.total_cpu_usage, board.total_cpu_usage); assert_eq!(deserialized.nodes.len(), board.nodes.len()); assert_eq!(deserialized.socs.len(), board.socs.len()); @@ -486,6 +498,7 @@ mod tests { fn test_empty_collections() { let empty_soc = SocInfo { soc_id: "empty-soc".to_string(), + name: String::new(), nodes: Vec::new(), // Empty nodes total_cpu_usage: 0.0, total_cpu_count: 0, @@ -505,6 +518,7 @@ mod tests { let empty_board = BoardInfo { board_id: "empty-board".to_string(), + name: String::new(), nodes: Vec::new(), // Empty nodes socs: Vec::new(), // Empty socs total_cpu_usage: 0.0, @@ -564,6 +578,7 @@ mod tests { let now = SystemTime::now(); let soc = SocInfo { soc_id: "time-test".to_string(), + name: String::new(), nodes: Vec::new(), total_cpu_usage: 0.0, total_cpu_count: 0,