use anyhow::{Context, Error};
use manta_backend_dispatcher::interfaces::bss::BootParametersTrait;
use manta_backend_dispatcher::interfaces::ims::ImsTrait;
use manta_backend_dispatcher::types::bss::BootParameters;
use manta_backend_dispatcher::types::ims::Image;
use std::collections::HashMap;
use crate::common;
use crate::common::app_context::InfraContext;
pub struct GetKernelParametersParams {
pub hsm_group: Option<String>,
pub nodes: Option<String>,
pub settings_hsm_group_name: Option<String>,
}
pub async fn get_kernel_parameters(
infra: &InfraContext<'_>,
token: &str,
params: &GetKernelParametersParams,
) -> Result<Vec<BootParameters>, Error> {
let xname_vec = common::node_ops::resolve_target_nodes(
infra.backend,
token,
params.nodes.as_deref(),
params.hsm_group.as_deref(),
params.settings_hsm_group_name.as_deref(),
)
.await?;
let boot_parameter_vec = infra.backend
.get_bootparameters(token, &xname_vec)
.await
.context("Could not get boot parameters")?;
Ok(boot_parameter_vec)
}
pub enum KernelParamOperation<'a> {
Add { params: &'a str, overwrite: bool },
Apply { params: &'a str },
Delete { params: &'a str },
}
impl<'a> KernelParamOperation<'a> {
pub fn verb(&self) -> &'static str {
match self {
Self::Add { .. } => "Add",
Self::Apply { .. } => "Apply",
Self::Delete { .. } => "Delete",
}
}
pub fn params(&self) -> &str {
match self {
Self::Add { params, .. }
| Self::Apply { params }
| Self::Delete { params } => params,
}
}
fn mutate(
&self,
boot_parameter: &mut BootParameters,
) -> bool {
match self {
Self::Add { params, overwrite } => {
boot_parameter.add_kernel_params(params, *overwrite)
}
Self::Apply { params } => {
boot_parameter.apply_kernel_params(params)
}
Self::Delete { params } => {
boot_parameter.delete_kernel_params(params)
}
}
}
pub fn confirm_message(&self) -> &'static str {
match self {
Self::Add { .. } => {
"This operation will add the kernel parameters for the nodes below. \
Please confirm to proceed"
}
Self::Apply { .. } => {
"This operation will replace the kernel parameters for the nodes below. \
Please confirm to proceed"
}
Self::Delete { .. } => {
"This operation will delete the kernel parameters for the nodes below. \
Please confirm to proceed"
}
}
}
fn handles_sbps_images(&self) -> bool {
match self {
Self::Add { .. } | Self::Apply { .. } => true,
Self::Delete { .. } => false,
}
}
}
pub struct KernelParamsChangeset {
pub boot_params: Vec<BootParameters>,
pub xnames_to_reboot: Vec<String>,
pub has_changes: bool,
pub sbps_candidates: Vec<(String, Image)>,
}
pub async fn prepare_kernel_params_changes(
infra: &InfraContext<'_>,
token: &str,
xname_vec: &[String],
operation: &KernelParamOperation<'_>,
) -> Result<KernelParamsChangeset, Error> {
let mut boot_params: Vec<BootParameters> = infra
.backend
.get_bootparameters(token, xname_vec)
.await
.context("Failed to get boot parameters")?;
let mut has_changes = false;
let mut xnames_to_reboot: Vec<String> = Vec::new();
let mut seen_images: HashMap<String, bool> = HashMap::new();
let mut sbps_candidates: Vec<(String, Image)> = Vec::new();
for bp in &mut boot_params {
let changed = operation.mutate(bp);
if changed {
has_changes = true;
xnames_to_reboot.extend(bp.hosts.iter().cloned());
}
if operation.handles_sbps_images() {
if let Some(image_id) = bp.try_get_boot_image_id() {
if !seen_images.contains_key(&image_id) {
seen_images.insert(image_id.clone(), true);
let image: Image = infra
.backend
.get_images(token, Some(&image_id))
.await?
.first()
.context("No image found for the given image id")?
.clone();
if bp.is_root_kernel_param_iscsi_ready() {
sbps_candidates.push((image_id, image));
}
}
}
}
}
Ok(KernelParamsChangeset {
boot_params,
xnames_to_reboot,
has_changes,
sbps_candidates,
})
}
pub async fn apply_kernel_params_changes(
infra: &InfraContext<'_>,
token: &str,
changeset: &KernelParamsChangeset,
images_to_project: &HashMap<String, Image>,
) -> Result<(), Error> {
for bp in &changeset.boot_params {
infra
.backend
.update_bootparameters(token, bp)
.await?;
}
for (_, image) in images_to_project {
infra
.backend
.update_image(
token,
image
.id
.clone()
.context("Image has no id")?
.as_str(),
&image.clone().into(),
)
.await?;
}
Ok(())
}