manta_server/service/
kernel_parameters.rs1use manta_backend_dispatcher::error::Error;
4use manta_backend_dispatcher::interfaces::bss::BootParametersTrait;
5use manta_backend_dispatcher::interfaces::ims::ImsTrait;
6use manta_backend_dispatcher::types::bss::BootParameters;
7use manta_backend_dispatcher::types::ims::Image;
8use std::collections::HashMap;
9
10use crate::server::common;
11use crate::server::common::app_context::InfraContext;
12pub use manta_shared::shared::params::kernel_parameters::GetKernelParametersParams;
13
14pub async fn get_kernel_parameters(
19 infra: &InfraContext<'_>,
20 token: &str,
21 params: &GetKernelParametersParams,
22) -> Result<Vec<BootParameters>, Error> {
23 let xname_vec = common::node_ops::resolve_target_nodes(
24 infra.backend,
25 token,
26 params.nodes.as_deref(),
27 params.hsm_group.as_deref(),
28 params.settings_hsm_group_name.as_deref(),
29 )
30 .await?;
31
32 let boot_parameter_vec =
33 infra.backend.get_bootparameters(token, &xname_vec).await?;
34
35 Ok(boot_parameter_vec)
36}
37
38pub enum KernelParamOperation<'a> {
40 Add {
42 params: &'a str,
44 overwrite: bool,
47 },
48 Apply {
50 params: &'a str,
53 },
54 Delete {
56 params: &'a str,
59 },
60}
61
62impl<'a> KernelParamOperation<'a> {
63 fn mutate(&self, boot_parameter: &mut BootParameters) -> bool {
66 match self {
67 Self::Add { params, overwrite } => {
68 boot_parameter.add_kernel_params(params, *overwrite)
69 }
70 Self::Apply { params } => boot_parameter.apply_kernel_params(params),
71 Self::Delete { params } => boot_parameter.delete_kernel_params(params),
72 }
73 }
74
75 fn handles_sbps_images(&self) -> bool {
77 match self {
78 Self::Add { .. } | Self::Apply { .. } => true,
79 Self::Delete { .. } => false,
80 }
81 }
82}
83
84#[derive(serde::Serialize)]
86pub struct KernelParamsChangeset {
87 pub boot_params: Vec<BootParameters>,
89 pub xnames_to_reboot: Vec<String>,
91 pub has_changes: bool,
93 pub sbps_candidates: Vec<(String, Image)>,
96}
97
98pub async fn prepare_kernel_params_changes(
102 infra: &InfraContext<'_>,
103 token: &str,
104 xname_vec: &[String],
105 operation: &KernelParamOperation<'_>,
106) -> Result<KernelParamsChangeset, Error> {
107 let mut boot_params: Vec<BootParameters> =
108 infra.backend.get_bootparameters(token, xname_vec).await?;
109
110 let mut has_changes = false;
111 let mut xnames_to_reboot: Vec<String> = Vec::new();
112 let mut seen_images: HashMap<String, bool> = HashMap::new();
113 let mut sbps_candidates: Vec<(String, Image)> = Vec::new();
114
115 for bp in &mut boot_params {
116 let changed = operation.mutate(bp);
117 if changed {
118 has_changes = true;
119 xnames_to_reboot.extend(bp.hosts.iter().cloned());
120 }
121
122 if operation.handles_sbps_images()
124 && let Some(image_id) = bp.try_get_boot_image_id()
125 && !seen_images.contains_key(&image_id)
126 {
127 seen_images.insert(image_id.clone(), true);
128
129 let image: Image = infra
130 .backend
131 .get_images(token, Some(&image_id))
132 .await?
133 .first()
134 .ok_or_else(|| {
135 Error::NotFound("No image found for the given image id".to_string())
136 })?
137 .clone();
138
139 if bp.is_root_kernel_param_iscsi_ready() {
140 sbps_candidates.push((image_id, image));
141 }
142 }
143 }
144
145 Ok(KernelParamsChangeset {
146 boot_params,
147 xnames_to_reboot,
148 has_changes,
149 sbps_candidates,
150 })
151}
152
153pub async fn apply_kernel_params_changes(
155 infra: &InfraContext<'_>,
156 token: &str,
157 changeset: &KernelParamsChangeset,
158 images_to_project: &HashMap<String, Image>,
159) -> Result<(), Error> {
160 for bp in &changeset.boot_params {
162 infra.backend.update_bootparameters(token, bp).await?;
163 }
164
165 for image in images_to_project.values() {
167 infra
168 .backend
169 .update_image(
170 token,
171 image
172 .id
173 .clone()
174 .ok_or_else(|| Error::MissingField("Image has no id".to_string()))?
175 .as_str(),
176 &image.clone().into(),
177 )
178 .await?;
179 }
180
181 Ok(())
182}
183
184pub fn build_images_to_project(
189 changeset: &KernelParamsChangeset,
190 project_sbps: bool,
191) -> HashMap<String, Image> {
192 if !project_sbps {
193 return HashMap::new();
194 }
195 changeset
196 .sbps_candidates
197 .iter()
198 .map(|(id, img)| {
199 let mut img = img.clone();
200 img.set_boot_image_iscsi_ready();
201 (id.clone(), img)
202 })
203 .collect()
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 fn add(params: &str) -> KernelParamOperation<'_> {
211 KernelParamOperation::Add {
212 params,
213 overwrite: false,
214 }
215 }
216 fn add_overwrite(params: &str) -> KernelParamOperation<'_> {
217 KernelParamOperation::Add {
218 params,
219 overwrite: true,
220 }
221 }
222 fn apply(params: &str) -> KernelParamOperation<'_> {
223 KernelParamOperation::Apply { params }
224 }
225 fn delete(params: &str) -> KernelParamOperation<'_> {
226 KernelParamOperation::Delete { params }
227 }
228
229 #[test]
230 fn overwrite_flag_preserved() {
231 match add_overwrite("x") {
232 KernelParamOperation::Add { overwrite, .. } => assert!(overwrite),
233 _ => panic!("wrong variant"),
234 }
235 match add("x") {
236 KernelParamOperation::Add { overwrite, .. } => assert!(!overwrite),
237 _ => panic!("wrong variant"),
238 }
239 }
240
241 #[test]
242 fn handles_sbps_images_only_for_add_and_apply() {
243 assert!(add("quiet").handles_sbps_images());
244 assert!(apply("quiet").handles_sbps_images());
245 assert!(!delete("quiet").handles_sbps_images());
246 }
247}