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