nv_redfish/computer_system/
mod.rs1mod item;
22
23#[cfg(feature = "bios")]
24pub mod bios;
25#[cfg(feature = "boot-options")]
26pub mod boot_option;
27#[cfg(feature = "storages")]
28pub mod drive;
29#[cfg(feature = "memory")]
30pub mod memory;
31#[cfg(feature = "processors")]
32pub mod processor;
33#[cfg(feature = "secure-boot")]
34pub mod secure_boot;
35#[cfg(feature = "storages")]
36pub mod storage;
37
38use crate::patch_support::CollectionWithPatch;
39use crate::patch_support::FilterFn;
40use crate::patch_support::JsonValue;
41use crate::patch_support::ReadPatchFn;
42use crate::resource::Resource as _;
43use crate::schema::computer_system::ComputerSystem as ComputerSystemSchema;
44use crate::schema::computer_system_collection::ComputerSystemCollection as ComputerSystemCollectionSchema;
45use crate::schema::resource::ResourceCollection;
46use crate::Error;
47use crate::NvBmc;
48use crate::ServiceRoot;
49use nv_redfish_core::Bmc;
50use nv_redfish_core::NavProperty;
51use std::convert::identity;
52use std::sync::Arc;
53
54#[doc(inline)]
55pub use item::BootOptionReference;
56#[doc(inline)]
57pub use item::ComputerSystem;
58
59#[doc(inline)]
60#[cfg(feature = "bios")]
61pub use bios::Bios;
62#[doc(inline)]
63#[cfg(feature = "boot-options")]
64pub use boot_option::BootOption;
65#[doc(inline)]
66#[cfg(feature = "boot-options")]
67pub use boot_option::BootOptionCollection;
68#[doc(inline)]
69#[cfg(feature = "storages")]
70pub use drive::Drive;
71#[doc(inline)]
72#[cfg(feature = "memory")]
73pub use memory::Memory;
74#[doc(inline)]
75#[cfg(feature = "processors")]
76pub use processor::Processor;
77#[doc(inline)]
78#[cfg(feature = "secure-boot")]
79pub use secure_boot::SecureBoot;
80#[doc(inline)]
81#[cfg(feature = "secure-boot")]
82pub use secure_boot::SecureBootCurrentBootType;
83#[doc(inline)]
84#[cfg(feature = "storages")]
85pub use storage::Storage;
86
87pub struct SystemCollection<B: Bmc> {
91 bmc: NvBmc<B>,
92 collection: Arc<ComputerSystemCollectionSchema>,
93 read_patch_fn: Option<ReadPatchFn>,
94}
95
96impl<B: Bmc> SystemCollection<B> {
97 pub(crate) async fn new(
98 bmc: &NvBmc<B>,
99 root: &ServiceRoot<B>,
100 ) -> Result<Option<Self>, Error<B>> {
101 let mut patches = Vec::new();
102 let mut filters = Vec::new();
103 if let Some(odata_id_filter) = bmc.quirks.filter_computer_system_odata_ids() {
104 filters.push(Box::new(move |js: &JsonValue| {
105 js.get("@odata.id")
106 .and_then(|v| v.as_str())
107 .map(odata_id_filter)
108 .is_some_and(identity)
109 }));
110 }
111 if bmc.quirks.computer_systems_wrong_last_reset_time() {
112 patches.push(computer_systems_wrong_last_reset_time as fn(JsonValue) -> JsonValue);
113 }
114 if bmc.quirks.bug_empty_uuid_field() {
115 patches.push(normalize_empty_uuid_field);
116 }
117 if bmc.quirks.vera_rubin_composite_boot_order_entries() {
118 patches.push(normalize_vera_rubin_composite_boot_order);
119 }
120 let read_patch_fn = (!patches.is_empty())
121 .then(|| Arc::new(move |v| patches.iter().fold(v, |acc, f| f(acc))) as ReadPatchFn);
122 let filters_fn = (!filters.is_empty())
123 .then(move || Arc::new(move |v: &JsonValue| filters.iter().any(|f| f(v))) as FilterFn);
124
125 if let Some(collection_ref) = &root.root.systems {
126 Self::expand_collection(
127 bmc,
128 collection_ref,
129 read_patch_fn.as_ref(),
130 filters_fn.as_ref(),
131 )
132 .await
133 .map(Some)
134 } else if bmc.quirks.bug_missing_root_nav_properties() {
135 bmc.expand_property(&NavProperty::new_reference(
136 format!("{}/Systems", root.odata_id()).into(),
137 ))
138 .await
139 .map(Some)
140 } else {
141 Ok(None)
142 }
143 .map(|c| {
144 c.map(|collection| Self {
145 bmc: bmc.clone(),
146 collection,
147 read_patch_fn,
148 })
149 })
150 }
151
152 pub async fn members(&self) -> Result<Vec<ComputerSystem<B>>, Error<B>> {
158 let mut members = Vec::new();
159 for m in &self.collection.members {
160 members.push(ComputerSystem::new(&self.bmc, m, self.read_patch_fn.as_ref()).await?);
161 }
162 Ok(members)
163 }
164}
165
166impl<B: Bmc> CollectionWithPatch<ComputerSystemCollectionSchema, ComputerSystemSchema, B>
167 for SystemCollection<B>
168{
169 fn convert_patched(
170 base: ResourceCollection,
171 members: Vec<NavProperty<ComputerSystemSchema>>,
172 ) -> ComputerSystemCollectionSchema {
173 ComputerSystemCollectionSchema { base, members }
174 }
175}
176
177fn computer_systems_wrong_last_reset_time(v: JsonValue) -> JsonValue {
181 if let JsonValue::Object(mut obj) = v {
182 if let Some(JsonValue::String(date)) = obj.get("LastResetTime") {
183 if date.starts_with("0000-00-00") {
184 obj.remove("LastResetTime");
185 }
186 }
187 JsonValue::Object(obj)
188 } else {
189 v
190 }
191}
192
193fn normalize_empty_uuid_field(mut v: JsonValue) -> JsonValue {
194 if let JsonValue::Object(ref mut obj) = v {
195 if let Some(uuid) = obj.get_mut("UUID") {
196 let is_empty = uuid.as_str().is_some_and(str::is_empty);
197 if is_empty {
198 *uuid = JsonValue::Null;
199 }
200 }
201 }
202 v
203}
204
205fn normalize_vera_rubin_composite_boot_order(mut v: JsonValue) -> JsonValue {
208 if let JsonValue::Object(ref mut obj) = v {
209 if let Some(JsonValue::Object(ref mut boot)) = obj.get_mut("Boot") {
210 if let Some(JsonValue::Array(ref mut boot_order)) = boot.get_mut("BootOrder") {
211 for entry in boot_order.iter_mut() {
212 if let JsonValue::String(entry) = entry {
213 *entry = vera_rubin_boot_order_entry_reference(entry).to_string();
214 }
215 }
216 }
217 }
218 }
219 v
220}
221
222fn vera_rubin_boot_order_entry_reference(entry: &str) -> &str {
223 entry
224 .split_once(": ")
225 .map_or(entry, |(reference, _)| reference)
226}
227
228#[cfg(test)]
229mod vera_rubin_boot_order_tests {
230 use super::*;
231 use serde_json::json;
232
233 #[test]
234 fn vera_rubin_boot_order_entry_reference_strips_display_name_suffix() {
235 assert_eq!(
236 vera_rubin_boot_order_entry_reference("Boot0019: Ubuntu"),
237 "Boot0019"
238 );
239 assert_eq!(
240 vera_rubin_boot_order_entry_reference("Boot0010: UEFI HTTPv4 (MAC:AA)"),
241 "Boot0010"
242 );
243 assert_eq!(
244 vera_rubin_boot_order_entry_reference("Boot0010"),
245 "Boot0010"
246 );
247 }
248
249 #[test]
250 fn normalize_vera_rubin_composite_boot_order_patches_boot_order_array() {
251 let patched = normalize_vera_rubin_composite_boot_order(json!({
252 "Boot": {
253 "BootOrder": [
254 "Boot0019: Ubuntu",
255 "Boot0010: UEFI HTTPv4 (MAC:AA)"
256 ]
257 }
258 }));
259 assert_eq!(
260 patched,
261 json!({
262 "Boot": {
263 "BootOrder": ["Boot0019", "Boot0010"]
264 }
265 })
266 );
267 }
268}