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