Skip to main content

nv_redfish/
service_root.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::bmc_quirks::BmcQuirks;
17use crate::core::Bmc;
18use crate::core::NavProperty;
19use crate::core::ODataId;
20use crate::schema::redfish::service_root::ServiceRoot as SchemaServiceRoot;
21use crate::Error;
22use crate::NvBmc;
23use crate::ProtocolFeatures;
24use crate::Resource;
25use crate::ResourceSchema;
26use std::sync::Arc;
27use tagged_types::TaggedType;
28
29#[cfg(feature = "accounts")]
30use crate::account::AccountService;
31#[cfg(feature = "chassis")]
32use crate::chassis::ChassisCollection;
33#[cfg(feature = "computer-systems")]
34use crate::computer_system::SystemCollection;
35#[cfg(feature = "event-service")]
36use crate::event_service::EventService;
37#[cfg(feature = "managers")]
38use crate::manager::ManagerCollection;
39#[cfg(feature = "oem-hpe")]
40use crate::oem::hpe::HpeiLoServiceExt;
41#[cfg(feature = "telemetry-service")]
42use crate::telemetry_service::TelemetryService;
43#[cfg(feature = "update-service")]
44use crate::update_service::UpdateService;
45#[cfg(feature = "session-service")]
46use crate::session_service::SessionService;
47
48/// The vendor or manufacturer associated with Redfish service.
49pub type Vendor<T> = TaggedType<T, VendorTag>;
50#[doc(hidden)]
51#[derive(tagged_types::Tag)]
52#[implement(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
53#[transparent(Debug, Display, Serialize, Deserialize)]
54#[capability(inner_access, cloned)]
55pub enum VendorTag {}
56
57/// The product associated with Redfish service..
58pub type Product<T> = TaggedType<T, ProductTag>;
59#[doc(hidden)]
60#[derive(tagged_types::Tag)]
61#[implement(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
62#[transparent(Debug, Display, Serialize, Deserialize)]
63#[capability(inner_access, cloned)]
64pub enum ProductTag {}
65
66/// The version of Redfish schema.
67pub type RedfishVersion<'a> = TaggedType<&'a str, RedfishVersionTag>;
68#[doc(hidden)]
69#[derive(tagged_types::Tag)]
70#[implement(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
71#[transparent(Debug, Display, Serialize, Deserialize)]
72#[capability(inner_access, cloned)]
73pub enum RedfishVersionTag {}
74
75/// Represents `ServiceRoot` in the BMC model.
76#[derive(Clone)]
77pub struct ServiceRoot<B: Bmc> {
78    /// Content of the root.
79    pub root: Arc<SchemaServiceRoot>,
80    #[allow(dead_code)] // feature-enabled field
81    bmc: NvBmc<B>,
82}
83
84impl<B: Bmc> ServiceRoot<B> {
85    /// Create a new service root.
86    ///
87    /// # Errors
88    ///
89    /// Returns error if retrieving the root path via Redfish fails.
90    pub async fn new(bmc: Arc<B>) -> Result<Self, Error<B>> {
91        let root = NavProperty::<SchemaServiceRoot>::new_reference(ODataId::service_root())
92            .get(bmc.as_ref())
93            .await
94            .map_err(Error::Bmc)?;
95        let quirks = BmcQuirks::new(&root);
96        let mut protocol_features = root
97            .protocol_features_supported
98            .as_ref()
99            .map(ProtocolFeatures::new)
100            .unwrap_or_default();
101
102        if quirks.expand_is_not_working_properly() {
103            protocol_features.expand.expand_all = false;
104            protocol_features.expand.no_links = false;
105        }
106
107        let bmc = NvBmc::new(bmc, protocol_features, quirks);
108        Ok(Self { root, bmc })
109    }
110
111    /// Replace BMC in this root.
112    #[must_use]
113    pub fn replace_bmc(self, bmc: Arc<B>) -> Self {
114        let root = self.root;
115        let bmc = self.bmc.replace_bmc(bmc);
116        Self { root, bmc }
117    }
118
119    /// Restrict usage of expand.
120    #[must_use]
121    pub fn restrict_expand(self) -> Self {
122        let root = self.root;
123        let bmc = self.bmc.restrict_expand();
124        Self { root, bmc }
125    }
126
127    /// The vendor or manufacturer associated with this Redfish service.
128    pub fn vendor(&self) -> Option<Vendor<&str>> {
129        self.root
130            .vendor
131            .as_ref()
132            .and_then(Option::as_ref)
133            .map(String::as_str)
134            .map(Vendor::new)
135    }
136
137    /// The product associated with this Redfish service.
138    pub fn product(&self) -> Option<Product<&str>> {
139        self.root
140            .product
141            .as_ref()
142            .and_then(Option::as_ref)
143            .map(String::as_str)
144            .map(Product::new)
145    }
146
147    /// The vendor or manufacturer associated with this Redfish service.
148    pub fn redfish_version(&self) -> Option<RedfishVersion<'_>> {
149        self.root
150            .redfish_version
151            .as_deref()
152            .map(RedfishVersion::new)
153    }
154
155    /// Get the account service belonging to the BMC.
156    ///
157    /// Returns `Ok(None)` when the BMC does not expose AccountService.
158    ///
159    /// # Errors
160    ///
161    /// Returns error if retrieving account service data fails.
162    #[cfg(feature = "accounts")]
163    pub async fn account_service(&self) -> Result<Option<AccountService<B>>, Error<B>> {
164        AccountService::new(&self.bmc, self).await
165    }
166
167    /// Get chassis collection in BMC
168    ///
169    /// Returns `Ok(None)` when the BMC does not expose Chassis.
170    ///
171    /// # Errors
172    ///
173    /// Returns error if retrieving chassis collection data fails.
174    #[cfg(feature = "chassis")]
175    pub async fn chassis(&self) -> Result<Option<ChassisCollection<B>>, Error<B>> {
176        ChassisCollection::new(&self.bmc, self).await
177    }
178
179    /// Get computer system collection in BMC
180    ///
181    /// Returns `Ok(None)` when the BMC does not expose Systems.
182    ///
183    /// # Errors
184    ///
185    /// Returns error if retrieving system collection data fails.
186    #[cfg(feature = "computer-systems")]
187    pub async fn systems(&self) -> Result<Option<SystemCollection<B>>, Error<B>> {
188        SystemCollection::new(&self.bmc, self).await
189    }
190
191    /// Get update service in BMC
192    ///
193    /// Returns `Ok(None)` when the BMC does not expose UpdateService.
194    ///
195    /// # Errors
196    ///
197    /// Returns error if retrieving update service data fails.
198    #[cfg(feature = "update-service")]
199    pub async fn update_service(&self) -> Result<Option<UpdateService<B>>, Error<B>> {
200        UpdateService::new(&self.bmc, self).await
201    }
202
203    /// Get event service in BMC
204    ///
205    /// Returns `Ok(None)` when the BMC does not expose EventService.
206    ///
207    /// # Errors
208    ///
209    /// Returns error if retrieving event service data fails.
210    #[cfg(feature = "event-service")]
211    pub async fn event_service(&self) -> Result<Option<EventService<B>>, Error<B>> {
212        EventService::new(&self.bmc, self).await
213    }
214
215    /// Get telemetry service in BMC
216    ///
217    /// Returns `Ok(None)` when the BMC does not expose TelemetryService.
218    ///
219    /// # Errors
220    ///
221    /// Returns error if retrieving telemetry service data fails.
222    #[cfg(feature = "telemetry-service")]
223    pub async fn telemetry_service(&self) -> Result<Option<TelemetryService<B>>, Error<B>> {
224        TelemetryService::new(&self.bmc, self).await
225    }
226
227    /// Get session service in BMC
228    ///
229    /// Returns `Ok(None)` when the BMC does not expose SessionService.
230    ///
231    /// # Errors
232    ///
233    /// Returns error if retrieving session service data fails.
234    #[cfg(feature = "session-service")]
235    pub async fn session_service(&self) -> Result<Option<SessionService<B>>, Error<B>> {
236        SessionService::new(&self.bmc, self).await
237    }
238
239    /// Get manager collection in BMC
240    ///
241    /// Returns `Ok(None)` when the BMC does not expose Managers.
242    ///
243    /// # Errors
244    ///
245    /// Returns error if retrieving manager collection data fails.
246    #[cfg(feature = "managers")]
247    pub async fn managers(&self) -> Result<Option<ManagerCollection<B>>, Error<B>> {
248        ManagerCollection::new(&self.bmc, self).await
249    }
250
251    /// Get HPE OEM extension in service root
252    ///
253    /// Returns `Ok(None)` when the BMC does not expose HPE extension.
254    ///
255    /// # Errors
256    ///
257    /// Returns error if retrieving manager collection data fails.
258    #[cfg(feature = "oem-hpe")]
259    pub fn oem_hpe_ilo_service_ext(&self) -> Result<Option<HpeiLoServiceExt<B>>, Error<B>> {
260        HpeiLoServiceExt::new(&self.root)
261    }
262}
263
264impl<B: Bmc> Resource for ServiceRoot<B> {
265    fn resource_ref(&self) -> &ResourceSchema {
266        &self.root.as_ref().base
267    }
268}