nv_redfish/update_service/
mod.rs1mod software_inventory;
22
23use std::sync::Arc;
24use std::time::Duration;
25
26use crate::core::NavProperty;
27use crate::patch_support::Payload;
28use crate::patch_support::ReadPatchFn;
29use crate::schema::update_service::UpdateService as UpdateServiceSchema;
30use crate::schema::update_service::UpdateServiceSimpleUpdateAction;
31use crate::Error;
32use crate::NvBmc;
33use crate::Resource;
34use crate::ResourceSchema;
35use crate::ServiceRoot;
36
37use nv_redfish_core::Bmc;
38use nv_redfish_core::DataStream;
39#[cfg(feature = "update-service-deprecated")]
40use nv_redfish_core::EntityTypeRef as _;
41#[cfg(feature = "update-service-deprecated")]
42use nv_redfish_core::HttpPushUriUpdateRequest;
43use nv_redfish_core::ModificationResponse;
44use nv_redfish_core::MultipartUpdateRequest;
45use nv_redfish_core::UploadReader;
46#[cfg(feature = "update-service-deprecated")]
47use nv_redfish_core::UploadStream;
48use serde_json::Value as JsonValue;
49use software_inventory::SoftwareInventoryCollection;
50
51#[doc(inline)]
52pub use crate::schema::update_service::TransferProtocolType;
53#[doc(inline)]
54pub use crate::schema::update_service::UpdateParametersUpdate as MultipartUpdateParameters;
55#[cfg(feature = "update-service-deprecated")]
56#[doc(inline)]
57pub use crate::schema::update_service::UpdateServiceUpdate;
58#[doc(inline)]
59pub use software_inventory::SoftwareInventory;
60#[doc(inline)]
61pub use software_inventory::Version;
62#[doc(inline)]
63pub use software_inventory::VersionRef;
64
65pub struct UpdateService<B: Bmc> {
69 bmc: NvBmc<B>,
70 data: Arc<UpdateServiceSchema>,
71 fw_inventory_read_patch_fn: Option<ReadPatchFn>,
72}
73
74impl<B: Bmc> UpdateService<B> {
75 pub(crate) async fn new(
77 bmc: &NvBmc<B>,
78 root: &ServiceRoot<B>,
79 ) -> Result<Option<Self>, Error<B>> {
80 let mut service_patches = Vec::new();
81 if bmc.quirks.bug_missing_update_service_name_field() {
82 service_patches.push(add_default_update_service_name);
83 }
84 let service_patch_fn = (!service_patches.is_empty()).then(|| {
85 Arc::new(move |v| service_patches.iter().fold(v, |acc, f| f(acc))) as ReadPatchFn
86 });
87
88 let mut fw_inventory_patches = Vec::new();
89 if bmc.quirks.fw_inventory_wrong_release_date() {
90 fw_inventory_patches.push(fw_inventory_patch_wrong_release_date);
91 }
92 let fw_inventory_read_patch_fn = (!fw_inventory_patches.is_empty()).then(|| {
93 Arc::new(move |v| fw_inventory_patches.iter().fold(v, |acc, f| f(acc))) as ReadPatchFn
94 });
95
96 if let Some(nav) = &root.root.update_service {
97 if let Some(service_patch_fn) = service_patch_fn {
98 Payload::get(bmc.as_ref(), nav, service_patch_fn.as_ref()).await
99 } else {
100 nav.get(bmc.as_ref()).await.map_err(Error::Bmc)
101 }
102 .map(Some)
103 } else if bmc.quirks.bug_missing_root_nav_properties() {
104 let nav =
105 NavProperty::new_reference(format!("{}/UpdateService", root.odata_id()).into());
106 if let Some(service_patch_fn) = service_patch_fn {
107 Payload::get(bmc.as_ref(), &nav, service_patch_fn.as_ref()).await
108 } else {
109 nav.get(bmc.as_ref()).await.map_err(Error::Bmc)
110 }
111 .map(Some)
112 } else {
113 Ok(None)
114 }
115 .map(|d| {
116 d.map(|data| Self {
117 bmc: bmc.clone(),
118 data,
119 fw_inventory_read_patch_fn,
120 })
121 })
122 }
123
124 #[must_use]
129 pub fn raw(&self) -> Arc<UpdateServiceSchema> {
130 self.data.clone()
131 }
132
133 pub async fn firmware_inventories(
141 &self,
142 ) -> Result<Option<Vec<SoftwareInventory<B>>>, Error<B>> {
143 if let Some(collection_ref) = &self.data.firmware_inventory {
144 SoftwareInventoryCollection::new(
145 &self.bmc,
146 collection_ref,
147 self.fw_inventory_read_patch_fn.clone(),
148 )
149 .await?
150 .members()
151 .await
152 .map(Some)
153 } else {
154 Ok(None)
155 }
156 }
157
158 pub async fn software_inventories(
166 &self,
167 ) -> Result<Option<Vec<SoftwareInventory<B>>>, Error<B>> {
168 if let Some(collection_ref) = &self.data.software_inventory {
169 let collection = self.bmc.expand_property(collection_ref).await?;
170 let mut items = Vec::new();
171 for item_ref in &collection.members {
172 items.push(SoftwareInventory::new(&self.bmc, item_ref, None).await?);
173 }
174 Ok(Some(items))
175 } else {
176 Ok(None)
177 }
178 }
179
180 #[allow(clippy::too_many_arguments)]
205 pub async fn simple_update(
206 &self,
207 image_uri: String,
208 transfer_protocol: Option<TransferProtocolType>,
209 targets: Option<Vec<String>>,
210 username: Option<String>,
211 password: Option<String>,
212 force_update: Option<bool>,
213 stage: Option<bool>,
214 local_image: Option<bool>,
215 exclude_targets: Option<Vec<String>>,
216 ) -> Result<ModificationResponse<()>, Error<B>>
217 where
218 B::Error: nv_redfish_core::ActionError,
219 {
220 let actions = self
221 .data
222 .actions
223 .as_ref()
224 .ok_or(Error::ActionNotAvailable)?;
225
226 actions
227 .simple_update(
228 self.bmc.as_ref(),
229 &UpdateServiceSimpleUpdateAction {
230 image_uri: Some(image_uri),
231 transfer_protocol,
232 targets,
233 username,
234 password,
235 force_update,
236 stage,
237 local_image,
238 exclude_targets,
239 },
240 )
241 .await
242 .map_err(Error::Bmc)
243 }
244
245 pub async fn start_update(&self) -> Result<ModificationResponse<()>, Error<B>>
254 where
255 B::Error: nv_redfish_core::ActionError,
256 {
257 let actions = self
258 .data
259 .actions
260 .as_ref()
261 .ok_or(Error::ActionNotAvailable)?;
262
263 actions
264 .start_update(self.bmc.as_ref())
265 .await
266 .map_err(Error::Bmc)
267 }
268
269 #[cfg(feature = "update-service-deprecated")]
278 pub async fn update(
279 &self,
280 update: &UpdateServiceUpdate,
281 ) -> Result<ModificationResponse<Self>, Error<B>> {
282 self.bmc
283 .as_ref()
284 .update::<_, NavProperty<UpdateServiceSchema>>(
285 self.data.odata_id(),
286 self.data.etag(),
287 update,
288 )
289 .await
290 .map_err(Error::Bmc)?
291 .try_map_entity_async(|nav| async move {
292 let data = nav.get(self.bmc.as_ref()).await.map_err(Error::Bmc)?;
293
294 Ok(Self {
295 bmc: self.bmc.clone(),
296 data,
297 fw_inventory_read_patch_fn: self.fw_inventory_read_patch_fn.clone(),
298 })
299 })
300 .await
301 }
302
303 #[cfg(feature = "update-service-deprecated")]
312 pub async fn http_push_uri_update_from_reader<U, R>(
313 &self,
314 update_stream: UploadStream<U>,
315 upload_timeout: Duration,
316 ) -> Result<ModificationResponse<R>, Error<B>>
317 where
318 U: UploadReader,
319 R: Send + Sync + for<'de> serde::Deserialize<'de>,
320 {
321 self.http_push_uri_update(HttpPushUriUpdateRequest {
322 update_stream,
323 upload_timeout,
324 })
325 .await
326 }
327
328 #[cfg(feature = "update-service-deprecated")]
334 pub async fn http_push_uri_update<U, R>(
335 &self,
336 request: HttpPushUriUpdateRequest<U>,
337 ) -> Result<ModificationResponse<R>, Error<B>>
338 where
339 U: UploadReader,
340 R: Send + Sync + for<'de> serde::Deserialize<'de>,
341 {
342 let http_push_uri = self
343 .data
344 .http_push_uri
345 .as_ref()
346 .ok_or(Error::UpdateServiceHttpPushUriNotAvailable)?;
347
348 self.bmc
349 .as_ref()
350 .http_push_uri_update(http_push_uri, request)
351 .await
352 .map_err(Error::Bmc)
353 }
354
355 pub async fn multipart_update_from_reader<U, V, R>(
364 &self,
365 update_parameters: &V,
366 update_stream: DataStream<U>,
367 upload_timeout: Duration,
368 ) -> Result<ModificationResponse<R>, Error<B>>
369 where
370 U: UploadReader,
371 V: Send + Sync + serde::Serialize,
372 R: Send + Sync + for<'de> serde::Deserialize<'de>,
373 {
374 self.multipart_update(MultipartUpdateRequest {
375 update_parameters,
376 update_stream,
377 oem_parts: Vec::new(),
378 upload_timeout,
379 })
380 .await
381 }
382
383 pub async fn multipart_update<U, V, R>(
391 &self,
392 request: MultipartUpdateRequest<'_, U, V>,
393 ) -> Result<ModificationResponse<R>, Error<B>>
394 where
395 U: UploadReader,
396 V: Send + Sync + serde::Serialize,
397 R: Send + Sync + for<'de> serde::Deserialize<'de>,
398 {
399 let multipart_uri = self
400 .data
401 .multipart_http_push_uri
402 .as_ref()
403 .ok_or(Error::UpdateServiceMultipartHttpPushUriNotAvailable)?;
404
405 self.bmc
406 .as_ref()
407 .multipart_update(multipart_uri, request)
408 .await
409 .map_err(Error::Bmc)
410 }
411}
412
413impl<B: Bmc> Resource for UpdateService<B> {
414 fn resource_ref(&self) -> &ResourceSchema {
415 &self.data.as_ref().base
416 }
417}
418
419fn fw_inventory_patch_wrong_release_date(v: JsonValue) -> JsonValue {
423 if let JsonValue::Object(mut obj) = v {
424 if let Some(JsonValue::String(date)) = obj.get("ReleaseDate") {
425 if date == "00:00:00Z" || date == "0000-00-00T00:00:00Z" {
426 obj.remove("ReleaseDate");
427 }
428 }
429 JsonValue::Object(obj)
430 } else {
431 v
432 }
433}
434
435fn add_default_update_service_name(v: JsonValue) -> JsonValue {
436 if let JsonValue::Object(mut obj) = v {
437 obj.entry("Name")
438 .or_insert(JsonValue::String("Unnamed update service".into()));
439 JsonValue::Object(obj)
440 } else {
441 v
442 }
443}