nv_redfish/update_service/
mod.rs1mod software_inventory;
22
23use crate::core::NavProperty;
24use crate::patch_support::Payload;
25use crate::patch_support::ReadPatchFn;
26use crate::schema::redfish::update_service::UpdateService as UpdateServiceSchema;
27use crate::schema::redfish::update_service::UpdateServiceSimpleUpdateAction;
28use crate::Error;
29use crate::NvBmc;
30use crate::Resource;
31use crate::ResourceSchema;
32use crate::ServiceRoot;
33use nv_redfish_core::Bmc;
34use serde_json::Value as JsonValue;
35use software_inventory::SoftwareInventoryCollection;
36use std::sync::Arc;
37
38#[doc(inline)]
39pub use crate::schema::redfish::update_service::TransferProtocolType;
40#[doc(inline)]
41pub use software_inventory::SoftwareInventory;
42#[doc(inline)]
43pub use software_inventory::Version;
44#[doc(inline)]
45pub use software_inventory::VersionRef;
46
47pub struct UpdateService<B: Bmc> {
51 bmc: NvBmc<B>,
52 data: Arc<UpdateServiceSchema>,
53 fw_inventory_read_patch_fn: Option<ReadPatchFn>,
54}
55
56impl<B: Bmc> UpdateService<B> {
57 pub(crate) async fn new(
59 bmc: &NvBmc<B>,
60 root: &ServiceRoot<B>,
61 ) -> Result<Option<Self>, Error<B>> {
62 let mut service_patches = Vec::new();
63 if root.bug_missing_update_service_name_field() {
64 service_patches.push(add_default_update_service_name);
65 }
66 let service_patch_fn = (!service_patches.is_empty()).then(|| {
67 Arc::new(move |v| service_patches.iter().fold(v, |acc, f| f(acc))) as ReadPatchFn
68 });
69
70 let mut fw_inventory_patches = Vec::new();
71 if root.fw_inventory_wrong_release_date() {
72 fw_inventory_patches.push(fw_inventory_patch_wrong_release_date);
73 }
74 let fw_inventory_read_patch_fn = (!fw_inventory_patches.is_empty()).then(|| {
75 Arc::new(move |v| fw_inventory_patches.iter().fold(v, |acc, f| f(acc))) as ReadPatchFn
76 });
77
78 if let Some(nav) = &root.root.update_service {
79 if let Some(service_patch_fn) = service_patch_fn {
80 Payload::get(bmc.as_ref(), nav, service_patch_fn.as_ref()).await
81 } else {
82 nav.get(bmc.as_ref()).await.map_err(Error::Bmc)
83 }
84 .map(Some)
85 } else if root.bug_missing_root_nav_properties() {
86 let nav =
87 NavProperty::new_reference(format!("{}/UpdateService", root.odata_id()).into());
88 if let Some(service_patch_fn) = service_patch_fn {
89 Payload::get(bmc.as_ref(), &nav, service_patch_fn.as_ref()).await
90 } else {
91 nav.get(bmc.as_ref()).await.map_err(Error::Bmc)
92 }
93 .map(Some)
94 } else {
95 Ok(None)
96 }
97 .map(|d| {
98 d.map(|data| Self {
99 bmc: bmc.clone(),
100 data,
101 fw_inventory_read_patch_fn,
102 })
103 })
104 }
105
106 #[must_use]
111 pub fn raw(&self) -> Arc<UpdateServiceSchema> {
112 self.data.clone()
113 }
114
115 pub async fn firmware_inventories(
123 &self,
124 ) -> Result<Option<Vec<SoftwareInventory<B>>>, Error<B>> {
125 if let Some(collection_ref) = &self.data.firmware_inventory {
126 SoftwareInventoryCollection::new(
127 &self.bmc,
128 collection_ref,
129 self.fw_inventory_read_patch_fn.clone(),
130 )
131 .await?
132 .members()
133 .await
134 .map(Some)
135 } else {
136 Ok(None)
137 }
138 }
139
140 pub async fn software_inventories(
148 &self,
149 ) -> Result<Option<Vec<SoftwareInventory<B>>>, Error<B>> {
150 if let Some(collection_ref) = &self.data.software_inventory {
151 let collection = self.bmc.expand_property(collection_ref).await?;
152 let mut items = Vec::new();
153 for item_ref in &collection.members {
154 items.push(SoftwareInventory::new(&self.bmc, item_ref, None).await?);
155 }
156 Ok(Some(items))
157 } else {
158 Ok(None)
159 }
160 }
161
162 #[allow(clippy::too_many_arguments)]
185 pub async fn simple_update(
186 &self,
187 image_uri: String,
188 transfer_protocol: Option<TransferProtocolType>,
189 targets: Option<Vec<String>>,
190 username: Option<String>,
191 password: Option<String>,
192 force_update: Option<bool>,
193 stage: Option<bool>,
194 local_image: Option<bool>,
195 exclude_targets: Option<Vec<String>>,
196 ) -> Result<(), Error<B>>
197 where
198 B::Error: nv_redfish_core::ActionError,
199 {
200 let actions = self
201 .data
202 .actions
203 .as_ref()
204 .ok_or(Error::ActionNotAvailable)?;
205
206 actions
207 .simple_update(
208 self.bmc.as_ref(),
209 &UpdateServiceSimpleUpdateAction {
210 image_uri: Some(image_uri),
211 transfer_protocol,
212 targets,
213 username,
214 password,
215 force_update,
216 stage,
217 local_image,
218 exclude_targets,
219 },
220 )
221 .await
222 .map_err(Error::Bmc)?;
223
224 Ok(())
225 }
226
227 pub async fn start_update(&self) -> Result<(), Error<B>>
235 where
236 B::Error: nv_redfish_core::ActionError,
237 {
238 let actions = self
239 .data
240 .actions
241 .as_ref()
242 .ok_or(Error::ActionNotAvailable)?;
243
244 actions
245 .start_update(self.bmc.as_ref())
246 .await
247 .map_err(Error::Bmc)?;
248
249 Ok(())
250 }
251}
252
253impl<B: Bmc> Resource for UpdateService<B> {
254 fn resource_ref(&self) -> &ResourceSchema {
255 &self.data.as_ref().base
256 }
257}
258
259fn fw_inventory_patch_wrong_release_date(v: JsonValue) -> JsonValue {
263 if let JsonValue::Object(mut obj) = v {
264 if let Some(JsonValue::String(date)) = obj.get("ReleaseDate") {
265 if date == "00:00:00Z" {
266 obj.remove("ReleaseDate");
267 }
268 }
269 JsonValue::Object(obj)
270 } else {
271 v
272 }
273}
274
275fn add_default_update_service_name(v: JsonValue) -> JsonValue {
276 if let JsonValue::Object(mut obj) = v {
277 obj.entry("Name")
278 .or_insert(JsonValue::String("Unnamed update service".into()));
279 JsonValue::Object(obj)
280 } else {
281 v
282 }
283}