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