nv_redfish/chassis/
item.rs1use crate::hardware_id::HardwareIdRef;
17use crate::hardware_id::Manufacturer as HardwareIdManufacturer;
18use crate::hardware_id::Model as HardwareIdModel;
19use crate::hardware_id::PartNumber as HardwareIdPartNumber;
20use crate::hardware_id::SerialNumber as HardwareIdSerialNumber;
21use crate::patch_support::JsonValue;
22use crate::patch_support::Payload;
23use crate::patch_support::ReadPatchFn;
24use crate::schema::redfish::chassis::Chassis as ChassisSchema;
25use crate::Error;
26use crate::NvBmc;
27use crate::Resource;
28use crate::ResourceSchema;
29use crate::ServiceRoot;
30use nv_redfish_core::bmc::Bmc;
31use nv_redfish_core::NavProperty;
32use std::sync::Arc;
33
34#[cfg(feature = "assembly")]
35use crate::assembly::Assembly;
36#[cfg(feature = "network-adapters")]
37use crate::chassis::NetworkAdapter;
38#[cfg(feature = "network-adapters")]
39use crate::chassis::NetworkAdapterCollection;
40#[cfg(feature = "power")]
41use crate::chassis::Power;
42#[cfg(feature = "power-supplies")]
43use crate::chassis::PowerSupply;
44#[cfg(feature = "thermal")]
45use crate::chassis::Thermal;
46#[cfg(feature = "log-services")]
47use crate::log_service::LogService;
48#[cfg(feature = "oem-nvidia-baseboard")]
49use crate::oem::nvidia::baseboard::NvidiaCbcChassis;
50#[cfg(feature = "pcie-devices")]
51use crate::pcie_device::PcieDeviceCollection;
52#[cfg(feature = "sensors")]
53use crate::schema::redfish::sensor::Sensor as SchemaSensor;
54#[cfg(feature = "sensors")]
55use crate::sensor::extract_environment_sensors;
56#[cfg(feature = "sensors")]
57use crate::sensor::SensorRef;
58#[cfg(feature = "oem-nvidia-baseboard")]
59use std::convert::identity;
60
61#[doc(hidden)]
62pub enum ChassisTag {}
63
64pub type Manufacturer<T> = HardwareIdManufacturer<T, ChassisTag>;
66
67pub type Model<T> = HardwareIdModel<T, ChassisTag>;
69
70pub type PartNumber<T> = HardwareIdPartNumber<T, ChassisTag>;
72
73pub type SerialNumber<T> = HardwareIdSerialNumber<T, ChassisTag>;
75
76pub struct Config {
77 read_patch_fn: Option<ReadPatchFn>,
78}
79
80impl Config {
81 pub fn new<B: Bmc>(root: &ServiceRoot<B>) -> Self {
82 let mut patches = Vec::new();
83 if root.bug_invalid_contained_by_fields() {
84 patches.push(remove_invalid_contained_by_fields as fn(JsonValue) -> JsonValue);
85 }
86 if root.bug_missing_chassis_type_field() {
87 patches.push(add_default_chassis_type);
88 }
89 if root.bug_missing_chassis_name_field() {
90 patches.push(add_default_chassis_name);
91 }
92 let read_patch_fn = (!patches.is_empty())
93 .then(|| Arc::new(move |v| patches.iter().fold(v, |acc, f| f(acc))) as ReadPatchFn);
94 Self { read_patch_fn }
95 }
96}
97
98pub struct Chassis<B: Bmc> {
102 #[allow(dead_code)] bmc: NvBmc<B>,
104 data: Arc<ChassisSchema>,
105 #[allow(dead_code)] config: Arc<Config>,
107}
108
109impl<B: Bmc> Chassis<B> {
110 pub(crate) async fn new(
112 bmc: &NvBmc<B>,
113 nav: &NavProperty<ChassisSchema>,
114 config: Arc<Config>,
115 ) -> Result<Self, Error<B>> {
116 if let Some(read_patch_fn) = &config.read_patch_fn {
117 Payload::get(bmc.as_ref(), nav, read_patch_fn.as_ref()).await
118 } else {
119 nav.get(bmc.as_ref()).await.map_err(Error::Bmc)
120 }
121 .map(|data| Self {
122 bmc: bmc.clone(),
123 data,
124 config,
125 })
126 }
127
128 #[must_use]
133 pub fn raw(&self) -> Arc<ChassisSchema> {
134 self.data.clone()
135 }
136
137 #[must_use]
139 pub fn hardware_id(&self) -> HardwareIdRef<'_, ChassisTag> {
140 HardwareIdRef {
141 manufacturer: self
142 .data
143 .manufacturer
144 .as_ref()
145 .and_then(Option::as_deref)
146 .map(Manufacturer::new),
147 model: self
148 .data
149 .model
150 .as_ref()
151 .and_then(Option::as_deref)
152 .map(Model::new),
153 part_number: self
154 .data
155 .part_number
156 .as_ref()
157 .and_then(Option::as_deref)
158 .map(PartNumber::new),
159 serial_number: self
160 .data
161 .serial_number
162 .as_ref()
163 .and_then(Option::as_deref)
164 .map(SerialNumber::new),
165 }
166 }
167
168 #[cfg(feature = "assembly")]
176 pub async fn assembly(&self) -> Result<Option<Assembly<B>>, Error<B>> {
177 if let Some(assembly_ref) = &self.data.assembly {
178 Assembly::new(&self.bmc, assembly_ref).await.map(Some)
179 } else {
180 Ok(None)
181 }
182 }
183
184 #[cfg(feature = "power-supplies")]
193 pub async fn power_supplies(&self) -> Result<Vec<PowerSupply<B>>, Error<B>> {
194 if let Some(ps) = &self.data.power_subsystem {
195 let ps = ps.get(self.bmc.as_ref()).await.map_err(Error::Bmc)?;
196 if let Some(supplies) = &ps.power_supplies {
197 let supplies = &self.bmc.expand_property(supplies).await?.members;
198 let mut power_supplies = Vec::with_capacity(supplies.len());
199 for power_supply in supplies {
200 power_supplies.push(PowerSupply::new(&self.bmc, power_supply).await?);
201 }
202 return Ok(power_supplies);
203 }
204 }
205
206 Ok(Vec::new())
207 }
208
209 #[cfg(feature = "power")]
219 pub async fn power(&self) -> Result<Option<Power<B>>, Error<B>> {
220 if let Some(power_ref) = &self.data.power {
221 Ok(Some(Power::new(&self.bmc, power_ref).await?))
222 } else {
223 Ok(None)
224 }
225 }
226
227 #[cfg(feature = "thermal")]
237 pub async fn thermal(&self) -> Result<Option<Thermal<B>>, Error<B>> {
238 if let Some(thermal_ref) = &self.data.thermal {
239 Thermal::new(&self.bmc, thermal_ref).await.map(Some)
240 } else {
241 Ok(None)
242 }
243 }
244
245 #[cfg(feature = "network-adapters")]
254 pub async fn network_adapters(&self) -> Result<Option<Vec<NetworkAdapter<B>>>, Error<B>> {
255 if let Some(network_adapters_collection_ref) = &self.data.network_adapters {
256 NetworkAdapterCollection::new(&self.bmc, network_adapters_collection_ref)
257 .await?
258 .members()
259 .await
260 .map(Some)
261 } else {
262 Ok(None)
263 }
264 }
265
266 #[cfg(feature = "log-services")]
274 pub async fn log_services(&self) -> Result<Option<Vec<LogService<B>>>, Error<B>> {
275 if let Some(log_services_ref) = &self.data.log_services {
276 let log_services_collection = log_services_ref
277 .get(self.bmc.as_ref())
278 .await
279 .map_err(Error::Bmc)?;
280
281 let mut log_services = Vec::new();
282 for m in &log_services_collection.members {
283 log_services.push(LogService::new(&self.bmc, m).await?);
284 }
285
286 Ok(Some(log_services))
287 } else {
288 Ok(None)
289 }
290 }
291
292 #[cfg(feature = "sensors")]
300 pub async fn environment_sensors(&self) -> Result<Vec<SensorRef<B>>, Error<B>> {
301 let sensor_refs = if let Some(env_ref) = &self.data.environment_metrics {
302 extract_environment_sensors(env_ref, self.bmc.as_ref()).await?
303 } else {
304 Vec::new()
305 };
306
307 Ok(sensor_refs
308 .into_iter()
309 .map(|r| SensorRef::new(self.bmc.clone(), r))
310 .collect())
311 }
312
313 #[cfg(feature = "sensors")]
322 pub async fn sensors(&self) -> Result<Option<Vec<SensorRef<B>>>, Error<B>> {
323 if let Some(sensors_collection) = &self.data.sensors {
324 let sc = sensors_collection
325 .get(self.bmc.as_ref())
326 .await
327 .map_err(Error::Bmc)?;
328 let mut sensor_data = Vec::with_capacity(sc.members.len());
329 for sensor in &sc.members {
330 sensor_data.push(SensorRef::new(
331 self.bmc.clone(),
332 NavProperty::<SchemaSensor>::new_reference(sensor.id().clone()),
333 ));
334 }
335 Ok(Some(sensor_data))
336 } else {
337 Ok(None)
338 }
339 }
340
341 #[cfg(feature = "pcie-devices")]
349 pub async fn pcie_devices(&self) -> Result<Option<PcieDeviceCollection<B>>, crate::Error<B>> {
350 if let Some(p) = &self.data.pcie_devices {
351 PcieDeviceCollection::new(&self.bmc, p).await.map(Some)
352 } else {
353 Ok(None)
354 }
355 }
356
357 #[cfg(feature = "oem-nvidia-baseboard")]
365 pub fn oem_nvidia_baseboard_cbc(&self) -> Result<Option<NvidiaCbcChassis<B>>, Error<B>> {
366 self.data
367 .base
368 .base
369 .oem
370 .as_ref()
371 .map(NvidiaCbcChassis::new)
372 .transpose()
373 .map(|v| v.and_then(identity))
374 }
375}
376
377impl<B: Bmc> Resource for Chassis<B> {
378 fn resource_ref(&self) -> &ResourceSchema {
379 &self.data.as_ref().base
380 }
381}
382
383fn remove_invalid_contained_by_fields(mut v: JsonValue) -> JsonValue {
384 if let JsonValue::Object(ref mut obj) = v {
385 if let Some(JsonValue::Object(ref mut links_obj)) = obj.get_mut("Links") {
386 if let Some(JsonValue::Object(ref mut contained_by_obj)) =
387 links_obj.get_mut("ContainedBy")
388 {
389 contained_by_obj.retain(|k, _| k == "@odata.id");
390 }
391 }
392 }
393 v
394}
395
396fn add_default_chassis_type(v: JsonValue) -> JsonValue {
397 if let JsonValue::Object(mut obj) = v {
398 obj.entry("ChassisType")
399 .or_insert(JsonValue::String("Other".into()));
400 JsonValue::Object(obj)
401 } else {
402 v
403 }
404}
405
406fn add_default_chassis_name(v: JsonValue) -> JsonValue {
407 if let JsonValue::Object(mut obj) = v {
408 obj.entry("Name")
409 .or_insert(JsonValue::String("Unnamed chassis".into()));
410 JsonValue::Object(obj)
411 } else {
412 v
413 }
414}