mod software_inventory;
use crate::core::NavProperty;
use crate::patch_support::Payload;
use crate::patch_support::ReadPatchFn;
use crate::schema::update_service::UpdateService as UpdateServiceSchema;
use crate::schema::update_service::UpdateServiceSimpleUpdateAction;
use crate::Error;
use crate::NvBmc;
use crate::Resource;
use crate::ResourceSchema;
use crate::ServiceRoot;
use nv_redfish_core::Bmc;
use nv_redfish_core::ModificationResponse;
use serde_json::Value as JsonValue;
use software_inventory::SoftwareInventoryCollection;
use std::sync::Arc;
#[doc(inline)]
pub use crate::schema::update_service::TransferProtocolType;
#[doc(inline)]
pub use software_inventory::SoftwareInventory;
#[doc(inline)]
pub use software_inventory::Version;
#[doc(inline)]
pub use software_inventory::VersionRef;
pub struct UpdateService<B: Bmc> {
bmc: NvBmc<B>,
data: Arc<UpdateServiceSchema>,
fw_inventory_read_patch_fn: Option<ReadPatchFn>,
}
impl<B: Bmc> UpdateService<B> {
pub(crate) async fn new(
bmc: &NvBmc<B>,
root: &ServiceRoot<B>,
) -> Result<Option<Self>, Error<B>> {
let mut service_patches = Vec::new();
if bmc.quirks.bug_missing_update_service_name_field() {
service_patches.push(add_default_update_service_name);
}
let service_patch_fn = (!service_patches.is_empty()).then(|| {
Arc::new(move |v| service_patches.iter().fold(v, |acc, f| f(acc))) as ReadPatchFn
});
let mut fw_inventory_patches = Vec::new();
if bmc.quirks.fw_inventory_wrong_release_date() {
fw_inventory_patches.push(fw_inventory_patch_wrong_release_date);
}
let fw_inventory_read_patch_fn = (!fw_inventory_patches.is_empty()).then(|| {
Arc::new(move |v| fw_inventory_patches.iter().fold(v, |acc, f| f(acc))) as ReadPatchFn
});
if let Some(nav) = &root.root.update_service {
if let Some(service_patch_fn) = service_patch_fn {
Payload::get(bmc.as_ref(), nav, service_patch_fn.as_ref()).await
} else {
nav.get(bmc.as_ref()).await.map_err(Error::Bmc)
}
.map(Some)
} else if bmc.quirks.bug_missing_root_nav_properties() {
let nav =
NavProperty::new_reference(format!("{}/UpdateService", root.odata_id()).into());
if let Some(service_patch_fn) = service_patch_fn {
Payload::get(bmc.as_ref(), &nav, service_patch_fn.as_ref()).await
} else {
nav.get(bmc.as_ref()).await.map_err(Error::Bmc)
}
.map(Some)
} else {
Ok(None)
}
.map(|d| {
d.map(|data| Self {
bmc: bmc.clone(),
data,
fw_inventory_read_patch_fn,
})
})
}
#[must_use]
pub fn raw(&self) -> Arc<UpdateServiceSchema> {
self.data.clone()
}
pub async fn firmware_inventories(
&self,
) -> Result<Option<Vec<SoftwareInventory<B>>>, Error<B>> {
if let Some(collection_ref) = &self.data.firmware_inventory {
SoftwareInventoryCollection::new(
&self.bmc,
collection_ref,
self.fw_inventory_read_patch_fn.clone(),
)
.await?
.members()
.await
.map(Some)
} else {
Ok(None)
}
}
pub async fn software_inventories(
&self,
) -> Result<Option<Vec<SoftwareInventory<B>>>, Error<B>> {
if let Some(collection_ref) = &self.data.software_inventory {
let collection = self.bmc.expand_property(collection_ref).await?;
let mut items = Vec::new();
for item_ref in &collection.members {
items.push(SoftwareInventory::new(&self.bmc, item_ref, None).await?);
}
Ok(Some(items))
} else {
Ok(None)
}
}
#[allow(clippy::too_many_arguments)]
pub async fn simple_update(
&self,
image_uri: String,
transfer_protocol: Option<TransferProtocolType>,
targets: Option<Vec<String>>,
username: Option<String>,
password: Option<String>,
force_update: Option<bool>,
stage: Option<bool>,
local_image: Option<bool>,
exclude_targets: Option<Vec<String>>,
) -> Result<ModificationResponse<()>, Error<B>>
where
B::Error: nv_redfish_core::ActionError,
{
let actions = self
.data
.actions
.as_ref()
.ok_or(Error::ActionNotAvailable)?;
actions
.simple_update(
self.bmc.as_ref(),
&UpdateServiceSimpleUpdateAction {
image_uri: Some(image_uri),
transfer_protocol,
targets,
username,
password,
force_update,
stage,
local_image,
exclude_targets,
},
)
.await
.map_err(Error::Bmc)
}
pub async fn start_update(&self) -> Result<ModificationResponse<()>, Error<B>>
where
B::Error: nv_redfish_core::ActionError,
{
let actions = self
.data
.actions
.as_ref()
.ok_or(Error::ActionNotAvailable)?;
actions
.start_update(self.bmc.as_ref())
.await
.map_err(Error::Bmc)
}
}
impl<B: Bmc> Resource for UpdateService<B> {
fn resource_ref(&self) -> &ResourceSchema {
&self.data.as_ref().base
}
}
fn fw_inventory_patch_wrong_release_date(v: JsonValue) -> JsonValue {
if let JsonValue::Object(mut obj) = v {
if let Some(JsonValue::String(date)) = obj.get("ReleaseDate") {
if date == "00:00:00Z" || date == "0000-00-00T00:00:00Z" {
obj.remove("ReleaseDate");
}
}
JsonValue::Object(obj)
} else {
v
}
}
fn add_default_update_service_name(v: JsonValue) -> JsonValue {
if let JsonValue::Object(mut obj) = v {
obj.entry("Name")
.or_insert(JsonValue::String("Unnamed update service".into()));
JsonValue::Object(obj)
} else {
v
}
}