nv_redfish/update_service/
mod.rs1mod software_inventory;
22
23use std::sync::Arc;
24use std::time::Duration;
25
26use crate::Error;
27use crate::NvBmc;
28use crate::Resource;
29use crate::ResourceSchema;
30use crate::ServiceRoot;
31use crate::core::NavProperty;
32use crate::patch_support::Payload;
33use crate::patch_support::ReadPatchFn;
34use crate::schema::update_service::UpdateService as UpdateServiceSchema;
35use crate::schema::update_service::UpdateServiceSimpleUpdateAction;
36
37use nv_redfish_core::Bmc;
38use nv_redfish_core::DataStream;
39#[cfg(feature = "update-service-deprecated")]
40use nv_redfish_core::EntityTypeRef;
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 let response = self
283 .bmc
284 .as_ref()
285 .update::<_, NavProperty<UpdateServiceSchema>>(
286 self.data.odata_id(),
287 self.data.etag(),
288 update,
289 )
290 .await
291 .map_err(Error::Bmc)?;
292
293 match response {
294 ModificationResponse::Entity(nav) => {
295 let data = nav.get(self.bmc.as_ref()).await.map_err(Error::Bmc)?;
296
297 Ok(ModificationResponse::Entity(Self {
298 bmc: self.bmc.clone(),
299 data,
300 fw_inventory_read_patch_fn: self.fw_inventory_read_patch_fn.clone(),
301 }))
302 }
303 ModificationResponse::Task(task) => Ok(ModificationResponse::Task(task)),
304 ModificationResponse::Empty => Ok(ModificationResponse::Empty),
305 }
306 }
307
308 #[cfg(feature = "update-service-deprecated")]
317 pub async fn http_push_uri_update_from_reader<U, R>(
318 &self,
319 update_stream: UploadStream<U>,
320 upload_timeout: Duration,
321 ) -> Result<ModificationResponse<R>, Error<B>>
322 where
323 U: UploadReader,
324 R: Send + Sync + for<'de> serde::Deserialize<'de>,
325 {
326 self.http_push_uri_update(HttpPushUriUpdateRequest {
327 update_stream,
328 upload_timeout,
329 })
330 .await
331 }
332
333 #[cfg(feature = "update-service-deprecated")]
339 pub async fn http_push_uri_update<U, R>(
340 &self,
341 request: HttpPushUriUpdateRequest<U>,
342 ) -> Result<ModificationResponse<R>, Error<B>>
343 where
344 U: UploadReader,
345 R: Send + Sync + for<'de> serde::Deserialize<'de>,
346 {
347 let http_push_uri = self
348 .data
349 .http_push_uri
350 .as_ref()
351 .ok_or(Error::UpdateServiceHttpPushUriNotAvailable)?;
352
353 self.bmc
354 .as_ref()
355 .http_push_uri_update(http_push_uri, request)
356 .await
357 .map_err(Error::Bmc)
358 }
359
360 pub async fn multipart_update_from_reader<U, V, R>(
369 &self,
370 update_parameters: &V,
371 update_stream: DataStream<U>,
372 upload_timeout: Duration,
373 ) -> Result<ModificationResponse<R>, Error<B>>
374 where
375 U: UploadReader,
376 V: Send + Sync + serde::Serialize,
377 R: Send + Sync + for<'de> serde::Deserialize<'de>,
378 {
379 self.multipart_update(MultipartUpdateRequest {
380 update_parameters,
381 update_stream,
382 oem_parts: Vec::new(),
383 upload_timeout,
384 })
385 .await
386 }
387
388 pub async fn multipart_update<U, V, R>(
396 &self,
397 request: MultipartUpdateRequest<'_, U, V>,
398 ) -> Result<ModificationResponse<R>, Error<B>>
399 where
400 U: UploadReader,
401 V: Send + Sync + serde::Serialize,
402 R: Send + Sync + for<'de> serde::Deserialize<'de>,
403 {
404 let multipart_uri = self
405 .data
406 .multipart_http_push_uri
407 .as_ref()
408 .ok_or(Error::UpdateServiceMultipartHttpPushUriNotAvailable)?;
409
410 self.bmc
411 .as_ref()
412 .multipart_update(multipart_uri, request)
413 .await
414 .map_err(Error::Bmc)
415 }
416}
417
418impl<B: Bmc> Resource for UpdateService<B> {
419 fn resource_ref(&self) -> &ResourceSchema {
420 &self.data.as_ref().base
421 }
422}
423
424fn fw_inventory_patch_wrong_release_date(v: JsonValue) -> JsonValue {
428 if let JsonValue::Object(mut obj) = v {
429 if let Some(JsonValue::String(date)) = obj.get("ReleaseDate") {
430 if date == "00:00:00Z" || date == "0000-00-00T00:00:00Z" {
431 obj.remove("ReleaseDate");
432 }
433 }
434 JsonValue::Object(obj)
435 } else {
436 v
437 }
438}
439
440fn add_default_update_service_name(v: JsonValue) -> JsonValue {
441 if let JsonValue::Object(mut obj) = v {
442 obj.entry("Name")
443 .or_insert(JsonValue::String("Unnamed update service".into()));
444 JsonValue::Object(obj)
445 } else {
446 v
447 }
448}