Skip to main content

nv_redfish/computer_system/
item.rs

1// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use 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::Payload;
22use crate::patch_support::ReadPatchFn;
23use crate::resource::PowerState;
24use crate::schema::redfish::computer_system::ComputerSystem as ComputerSystemSchema;
25use crate::Error;
26use crate::NvBmc;
27use crate::Resource;
28use crate::ResourceSchema;
29use nv_redfish_core::Bmc;
30use nv_redfish_core::NavProperty;
31use std::convert::identity;
32use std::sync::Arc;
33use tagged_types::TaggedType;
34
35#[cfg(feature = "bios")]
36use crate::computer_system::Bios;
37#[cfg(feature = "boot-options")]
38use crate::computer_system::BootOptionCollection;
39#[cfg(feature = "memory")]
40use crate::computer_system::Memory;
41#[cfg(feature = "processors")]
42use crate::computer_system::Processor;
43#[cfg(feature = "secure-boot")]
44use crate::computer_system::SecureBoot;
45#[cfg(feature = "storages")]
46use crate::computer_system::Storage;
47#[cfg(feature = "ethernet-interfaces")]
48use crate::ethernet_interface::EthernetInterfaceCollection;
49#[cfg(feature = "log-services")]
50use crate::log_service::LogService;
51#[cfg(feature = "oem-lenovo")]
52use crate::oem::lenovo::computer_system::LenovoComputerSystem;
53#[cfg(feature = "oem-nvidia-bluefield")]
54use crate::oem::nvidia::bluefield::nvidia_computer_system::NvidiaComputerSystem;
55
56#[doc(hidden)]
57pub enum ComputerSystemTag {}
58
59/// Computer system manufacturer.
60pub type Manufacturer<T> = HardwareIdManufacturer<T, ComputerSystemTag>;
61
62/// Computer system model.
63pub type Model<T> = HardwareIdModel<T, ComputerSystemTag>;
64
65/// Computer system part number.
66pub type PartNumber<T> = HardwareIdPartNumber<T, ComputerSystemTag>;
67
68/// Computer system serial number.
69pub type SerialNumber<T> = HardwareIdSerialNumber<T, ComputerSystemTag>;
70
71/// Computer system SKU.
72pub type Sku<T> = TaggedType<T, ComputerSystemSkuTag>;
73#[doc(hidden)]
74#[derive(tagged_types::Tag)]
75#[implement(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
76#[transparent(Debug, Display, FromStr, Serialize, Deserialize)]
77#[capability(inner_access, cloned)]
78pub enum ComputerSystemSkuTag {}
79
80/// `BootOptionReference` type represent boot order of the `ComputerSystem`.
81pub type BootOptionReference<T> = TaggedType<T, BootOptionReferenceTag>;
82#[doc(hidden)]
83#[derive(tagged_types::Tag)]
84#[implement(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
85#[transparent(Debug, Display, FromStr, Serialize, Deserialize)]
86#[capability(inner_access, cloned)]
87pub enum BootOptionReferenceTag {}
88
89/// Represents a computer system in the BMC.
90///
91/// Provides access to system information and sub-resources such as processors.
92pub struct ComputerSystem<B: Bmc> {
93    #[allow(dead_code)] // feature-enabled...
94    bmc: NvBmc<B>,
95    data: Arc<ComputerSystemSchema>,
96}
97
98impl<B: Bmc> ComputerSystem<B> {
99    /// Create a new computer system handle.
100    pub(crate) async fn new(
101        bmc: &NvBmc<B>,
102        nav: &NavProperty<ComputerSystemSchema>,
103        read_patch_fn: Option<&ReadPatchFn>,
104    ) -> Result<Self, Error<B>> {
105        if let Some(read_patch_fn) = read_patch_fn {
106            Payload::get(bmc.as_ref(), nav, read_patch_fn.as_ref()).await
107        } else {
108            nav.get(bmc.as_ref()).await.map_err(Error::Bmc)
109        }
110        .map(|data| Self {
111            bmc: bmc.clone(),
112            data,
113        })
114    }
115
116    /// Get the raw schema data for this computer system.
117    ///
118    /// Returns an `Arc` to the underlying schema, allowing cheap cloning
119    /// and sharing of the data.
120    #[must_use]
121    pub fn raw(&self) -> Arc<ComputerSystemSchema> {
122        self.data.clone()
123    }
124
125    /// Get hardware identifier of the network adpater.
126    #[must_use]
127    pub fn hardware_id(&self) -> HardwareIdRef<'_, ComputerSystemTag> {
128        HardwareIdRef {
129            manufacturer: self
130                .data
131                .manufacturer
132                .as_ref()
133                .and_then(Option::as_deref)
134                .map(Manufacturer::new),
135            model: self
136                .data
137                .model
138                .as_ref()
139                .and_then(Option::as_deref)
140                .map(Model::new),
141            part_number: self
142                .data
143                .part_number
144                .as_ref()
145                .and_then(Option::as_deref)
146                .map(PartNumber::new),
147            serial_number: self
148                .data
149                .serial_number
150                .as_ref()
151                .and_then(Option::as_deref)
152                .map(SerialNumber::new),
153        }
154    }
155
156    /// The manufacturer SKU for this system.
157    #[must_use]
158    pub fn sku(&self) -> Option<Sku<&str>> {
159        self.data
160            .sku
161            .as_ref()
162            .and_then(Option::as_ref)
163            .map(String::as_str)
164            .map(Sku::new)
165    }
166
167    /// Power state of this system.
168    #[must_use]
169    pub fn power_state(&self) -> Option<PowerState> {
170        self.data.power_state.and_then(identity)
171    }
172
173    /// An array of `BootOptionReference` strings that represent the persistent boot order for with this
174    /// computer system.
175    #[must_use]
176    pub fn boot_order(&self) -> Option<Vec<BootOptionReference<&str>>> {
177        self.data
178            .as_ref()
179            .boot
180            .as_ref()
181            .and_then(|boot| boot.boot_order.as_ref().and_then(Option::as_ref))
182            .map(|v| {
183                v.iter()
184                    .map(String::as_str)
185                    .map(BootOptionReference::new)
186                    .collect::<Vec<_>>()
187            })
188    }
189
190    /// Bios associated with this system.
191    ///
192    /// Fetches the BIOS settings. Returns `Ok(None)` when the BIOS link is absent.
193    ///
194    /// # Errors
195    ///
196    /// Returns an error if fetching BIOS data fails.
197    #[cfg(feature = "bios")]
198    pub async fn bios(&self) -> Result<Option<Bios<B>>, Error<B>> {
199        if let Some(bios_ref) = &self.data.bios {
200            Bios::new(&self.bmc, bios_ref).await.map(Some)
201        } else {
202            Ok(None)
203        }
204    }
205
206    /// Get processors associated with this system.
207    ///
208    /// Fetches the processor collection and returns a list of [`Processor`] handles.
209    /// Returns `Ok(None)` when the processors link is absent.
210    ///
211    /// # Errors
212    ///
213    /// Returns an error if fetching processor data fails.
214    #[cfg(feature = "processors")]
215    pub async fn processors(&self) -> Result<Option<Vec<Processor<B>>>, Error<B>> {
216        if let Some(processors_ref) = &self.data.processors {
217            let processors_collection = self.bmc.expand_property(processors_ref).await?;
218
219            let mut processors = Vec::new();
220            for m in &processors_collection.members {
221                processors.push(Processor::new(&self.bmc, m).await?);
222            }
223
224            Ok(Some(processors))
225        } else {
226            Ok(None)
227        }
228    }
229
230    /// Get secure boot resource associated with this system.
231    ///
232    /// Returns `Ok(None)` when the secure boot link is absent.
233    ///
234    /// # Errors
235    ///
236    /// Returns an error if fetching secure boot data fails.
237    #[cfg(feature = "secure-boot")]
238    pub async fn secure_boot(&self) -> Result<Option<SecureBoot<B>>, Error<B>> {
239        if let Some(secure_boot_ref) = &self.data.secure_boot {
240            SecureBoot::new(&self.bmc, secure_boot_ref).await.map(Some)
241        } else {
242            Ok(None)
243        }
244    }
245
246    /// Get storage controllers associated with this system.
247    ///
248    /// Fetches the storage collection and returns a list of [`Storage`] handles.
249    /// Returns `Ok(None)` when the storage link is absent.
250    ///
251    /// # Errors
252    ///
253    /// Returns an error if fetching storage data fails.
254    #[cfg(feature = "storages")]
255    pub async fn storage_controllers(&self) -> Result<Option<Vec<Storage<B>>>, Error<B>> {
256        if let Some(storage_ref) = &self.data.storage {
257            let storage_collection = self.bmc.expand_property(storage_ref).await?;
258
259            let mut storage_controllers = Vec::new();
260            for m in &storage_collection.members {
261                storage_controllers.push(Storage::new(&self.bmc, m).await?);
262            }
263
264            Ok(Some(storage_controllers))
265        } else {
266            Ok(None)
267        }
268    }
269
270    /// Get memory modules associated with this system.
271    ///
272    /// Fetches the memory collection and returns a list of [`Memory`] handles.
273    /// Returns `Ok(None)` when the memory link is absent.
274    ///
275    /// # Errors
276    ///
277    /// Returns an error if fetching memory data fails.
278    #[cfg(feature = "memory")]
279    pub async fn memory_modules(&self) -> Result<Option<Vec<Memory<B>>>, Error<B>> {
280        if let Some(memory_ref) = &self.data.memory {
281            let memory_collection = self.bmc.expand_property(memory_ref).await?;
282
283            let mut memory_modules = Vec::new();
284            for m in &memory_collection.members {
285                memory_modules.push(Memory::new(&self.bmc, m).await?);
286            }
287
288            Ok(Some(memory_modules))
289        } else {
290            Ok(None)
291        }
292    }
293
294    /// Get log services for this computer system.
295    ///
296    /// Returns `Ok(None)` when the log services link is absent.
297    ///
298    /// # Errors
299    ///
300    /// Returns an error if fetching log service data fails.
301    #[cfg(feature = "log-services")]
302    pub async fn log_services(&self) -> Result<Option<Vec<LogService<B>>>, Error<B>> {
303        if let Some(log_services_ref) = &self.data.log_services {
304            let log_services_collection = log_services_ref
305                .get(self.bmc.as_ref())
306                .await
307                .map_err(Error::Bmc)?;
308
309            let mut log_services = Vec::new();
310            for m in &log_services_collection.members {
311                log_services.push(LogService::new(&self.bmc, m).await?);
312            }
313
314            Ok(Some(log_services))
315        } else {
316            Ok(None)
317        }
318    }
319
320    /// Get ethernet interfaces for this computer system.
321    ///
322    /// Returns `Ok(None)` when the ethernet interfaces link is absent.
323    ///
324    /// # Errors
325    ///
326    /// Returns an error if fetching ethernet interface data fails.
327    #[cfg(feature = "ethernet-interfaces")]
328    pub async fn ethernet_interfaces(
329        &self,
330    ) -> Result<Option<EthernetInterfaceCollection<B>>, Error<B>> {
331        if let Some(p) = &self.data.ethernet_interfaces {
332            EthernetInterfaceCollection::new(&self.bmc, p)
333                .await
334                .map(Some)
335        } else {
336            Ok(None)
337        }
338    }
339
340    /// Get collection of the UEFI boot options associated with this computer system.
341    ///
342    /// Returns `Ok(None)` when boot options are not exposed.
343    ///
344    /// # Errors
345    ///
346    /// Returns an error if fetching boot options data fails.
347    #[cfg(feature = "boot-options")]
348    pub async fn boot_options(&self) -> Result<Option<BootOptionCollection<B>>, Error<B>> {
349        if let Some(p) = &self
350            .data
351            .boot
352            .as_ref()
353            .and_then(|v| v.boot_options.as_ref())
354        {
355            BootOptionCollection::new(&self.bmc, p).await.map(Some)
356        } else {
357            Ok(None)
358        }
359    }
360
361    /// NVIDIA Bluefield OEM extension
362    ///
363    /// Returns `Ok(None)` when the system does not include NVIDIA OEM extension data.
364    ///
365    /// # Errors
366    ///
367    /// Returns an error if NVIDIA OEM data parsing/fetching fails.
368    #[cfg(feature = "oem-nvidia-bluefield")]
369    pub async fn oem_nvidia_bluefield(&self) -> Result<Option<NvidiaComputerSystem<B>>, Error<B>> {
370        if let Some(oem) = self.data.base.base.oem.as_ref() {
371            NvidiaComputerSystem::new(&self.bmc, oem).await
372        } else {
373            Ok(None)
374        }
375    }
376
377    /// Lenovo OEM extension
378    ///
379    /// Returns `Ok(None)` when the system does not include Lenovo OEM extension data.
380    ///
381    /// # Errors
382    ///
383    /// Returns an error if Lenovo OEM data parsing fails.
384    #[cfg(feature = "oem-lenovo")]
385    pub fn oem_lenovo(&self) -> Result<Option<LenovoComputerSystem<B>>, Error<B>> {
386        LenovoComputerSystem::new(&self.bmc, &self.data)
387    }
388}
389
390impl<B: Bmc> Resource for ComputerSystem<B> {
391    fn resource_ref(&self) -> &ResourceSchema {
392        &self.data.as_ref().base
393    }
394}