1use std::collections::HashMap;
2
3use manta_backend_dispatcher::types::bss::BootParameters as FrontEndBootParameters;
4
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use utils::get_image_id_from_s3_path;
8
9use crate::error::Error;
10
11use super::utils;
12
13#[derive(Debug, Serialize, Deserialize, Default, Clone)]
14pub struct BootParameters {
15 #[serde(default)]
16 pub hosts: Vec<String>,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub macs: Option<Vec<String>>,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub nids: Option<Vec<u32>>,
21 #[serde(default)]
22 pub params: String, #[serde(default)]
24 pub kernel: String,
25 #[serde(default)]
26 pub initrd: String,
27 #[serde(rename = "cloud-init")]
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub cloud_init: Option<Value>,
30}
31
32impl From<FrontEndBootParameters> for BootParameters {
33 fn from(value: FrontEndBootParameters) -> Self {
34 BootParameters {
35 hosts: value.hosts,
36 macs: value.macs,
37 nids: value.nids,
38 params: value.params,
39 kernel: value.kernel,
40 initrd: value.initrd,
41 cloud_init: value.cloud_init,
42 }
43 }
44}
45
46impl Into<FrontEndBootParameters> for BootParameters {
47 fn into(self) -> FrontEndBootParameters {
48 FrontEndBootParameters {
49 hosts: self.hosts,
50 macs: self.macs,
51 nids: self.nids,
52 params: self.params,
53 kernel: self.kernel,
54 initrd: self.initrd,
55 cloud_init: self.cloud_init,
56 }
57 }
58}
59
60impl BootParameters {
61 pub fn get_boot_image(&self) -> String {
64 let params: HashMap<&str, &str> = self
65 .params
66 .split_whitespace()
67 .map(|kernel_param| {
68 kernel_param
69 .split_once('=')
70 .map(|(key, value)| (key.trim(), value.trim()))
71 .unwrap_or((kernel_param, ""))
72 })
73 .collect();
74
75 let root_kernel_param_opt = params.get("root");
78 let metal_server_kernel_param_opt = params.get("metal.server");
81
82 let boot_image_id_opt: Option<&str> =
83 if let Some(root_kernel_param) = root_kernel_param_opt {
84 get_image_id_from_s3_path(root_kernel_param)
85 } else if let Some(metal_server_kernel_param) =
86 metal_server_kernel_param_opt
87 {
88 get_image_id_from_s3_path(metal_server_kernel_param)
89 } else {
90 None
91 };
92
93 boot_image_id_opt.unwrap_or("").to_string()
94 }
95
96 pub fn update_boot_image(
103 &mut self,
104 new_image_id: &str,
105 ) -> Result<bool, Error> {
106 let mut changed = false;
107 let mut params: HashMap<&str, &str> = self
111 .params
112 .split_whitespace()
113 .map(|kernel_param| {
114 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
115 })
116 .collect();
117
118 let root_kernel_param_rslt = params.get("root");
121
122 let mut root_kernel_param: Vec<&str> = match root_kernel_param_rslt {
123 Some(root_kernel_param) => {
124 root_kernel_param.split("/").collect::<Vec<&str>>()
125 }
126 None => {
127 return Err(Error::Message(
128 "ERROR - The 'root' kernel param is missing from user input"
129 .to_string(),
130 ));
131 }
132 };
133
134 for current_image_id in &mut root_kernel_param {
136 if uuid::Uuid::try_parse(current_image_id).is_ok() {
139 if *current_image_id != new_image_id {
140 changed = true;
141 }
142 *current_image_id = new_image_id;
144 }
145 }
146
147 let new_root_kernel_param = root_kernel_param.join("/");
149
150 params
152 .entry("root")
153 .and_modify(|root_param| *root_param = &new_root_kernel_param);
154
155 self.update_kernel_param("root", &new_root_kernel_param);
156
157 let mut params: HashMap<&str, &str> = self
161 .params
162 .split_whitespace()
163 .map(|kernel_param| {
164 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
165 })
166 .map(|(key, value)| (key.trim(), value.trim()))
167 .collect();
168
169 let mut metal_server_kernel_param: Vec<&str>;
171 if let Some(metal_server_data) = params.get("metal.server") {
172 metal_server_kernel_param = metal_server_data.split("/").collect();
173
174 for substring in &mut metal_server_kernel_param {
175 if uuid::Uuid::try_parse(substring).is_ok() {
176 *substring = new_image_id;
177 changed = true;
178 }
179 }
180
181 let new_metal_server_kernel_param = metal_server_kernel_param.join("/");
182
183 params
184 .entry("metal.server")
185 .and_modify(|metal_server_param| {
186 *metal_server_param = &new_metal_server_kernel_param
187 });
188
189 self.update_kernel_param("metal.server", &new_metal_server_kernel_param);
190
191 params = self
193 .params
194 .split_whitespace()
195 .map(|kernel_param| {
196 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
197 })
198 .collect();
199 } else {
200 };
201
202 let mut nmd_kernel_param: Vec<&str>;
204 if let Some(nmd_data) = params.get("nmd_data") {
205 nmd_kernel_param = nmd_data.split("/").collect();
206
207 for substring in &mut nmd_kernel_param {
208 if uuid::Uuid::try_parse(substring).is_ok() {
209 *substring = new_image_id;
210 changed = true;
211 }
212 }
213
214 let new_nmd_kernel_param = nmd_kernel_param.join("/");
215
216 params
217 .entry("nmd_data")
218 .and_modify(|nmd_param| *nmd_param = &new_nmd_kernel_param);
219
220 self.update_kernel_param("nmd_data", &new_nmd_kernel_param);
221 } else {
222 };
223
224 self.kernel = format!("s3://boot-images/{}/kernel", new_image_id);
225
226 self.initrd = format!("s3://boot-images/{}/initrd", new_image_id);
227
228 Ok(changed)
229 }
230
231 pub fn get_kernel_param_value(&self, key: &str) -> Option<String> {
232 let params: HashMap<&str, &str> = self
233 .params
234 .split_whitespace()
235 .map(|kernel_param| {
236 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
237 })
238 .map(|(key, value)| (key.trim(), value.trim()))
239 .collect();
240
241 params.get(key).cloned().map(str::to_string)
242 }
243
244 pub fn get_num_kernel_params(&self) -> usize {
245 let params: HashMap<&str, &str> = self
246 .params
247 .split_whitespace()
248 .map(|kernel_param| {
249 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
250 })
251 .map(|(key, value)| (key.trim(), value.trim()))
252 .collect();
253
254 params.len()
255 }
256
257 pub fn apply_kernel_params(&mut self, new_params: &str) -> bool {
262 let mut change = false;
263
264 let new_params: Vec<(&str, &str)> = new_params
265 .split_whitespace()
266 .map(|kernel_param| {
267 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
268 })
269 .map(|(key, value)| (key.trim(), value.trim()))
270 .collect();
271
272 let mut params: HashMap<&str, &str> = HashMap::new();
273
274 for (new_key, new_value) in &new_params {
275 for (key, value) in params.iter_mut() {
276 if *key == *new_key {
277 log::debug!("key '{}' found", key);
278 if value != new_value {
279 log::info!("changing key {} from {} to {}", key, value, new_value);
280
281 *value = new_value;
282 change = true
283 } else {
284 log::debug!("key '{}' value does not change ({})", key, value);
285 }
286 }
287 }
288 }
289
290 if change == false {
291 log::debug!(
292 "No value change in kernel params. Checking is either new params have been added or removed"
293 );
294 if new_params.len() != params.len() {
295 log::info!("num kernel parameters have changed");
296 change = true;
297 }
298 }
299
300 self.params = new_params
301 .iter()
302 .map(|(key, value)| {
303 if !value.is_empty() {
304 format!("{key}={value}")
305 } else {
306 key.to_string()
307 }
308 })
309 .collect::<Vec<String>>()
310 .join(" ");
311
312 change
313 }
314
315 pub fn update_kernel_params(&mut self, new_params: &str) -> bool {
320 let mut change = false;
321
322 let new_params: Vec<(&str, &str)> = new_params
323 .split_whitespace()
324 .map(|kernel_param| {
325 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
326 })
327 .map(|(key, value)| (key.trim(), value.trim()))
328 .collect();
329
330 let mut params: HashMap<&str, &str> = self
331 .params
332 .split_whitespace()
333 .map(|kernel_param| {
334 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
335 })
336 .collect();
337
338 for (new_key, new_value) in &new_params {
339 for (key, value) in params.iter_mut() {
340 if *key == *new_key {
341 log::debug!("key '{}' found", key);
342 if value != new_value {
343 log::info!("changing key {} from {} to {}", key, value, new_value);
344
345 *value = new_value;
346 change = true
347 } else {
348 log::debug!("key '{}' value does not change ({})", key, value);
349 }
350 }
351 }
352 }
353
354 if change == false {
355 log::debug!(
356 "No value change in kernel params. Checking is either new params have been added or removed"
357 );
358 if new_params.len() != params.len() {
359 log::info!("num kernel parameters have changed");
360 change = true;
361 }
362 }
363
364 self.params = params
365 .iter()
366 .map(|(key, value)| {
367 if !value.is_empty() {
368 format!("{key}={value}")
369 } else {
370 key.to_string()
371 }
372 })
373 .collect::<Vec<String>>()
374 .join(" ");
375
376 change
377 }
378
379 pub fn update_kernel_param(
384 &mut self,
385 new_key: &str,
386 new_value: &str,
387 ) -> bool {
388 let mut changed = false;
389 let mut params: HashMap<&str, &str> = self
391 .params
392 .split_whitespace()
393 .map(|kernel_param| {
394 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
395 })
396 .map(|(key, value)| (key.trim(), value.trim()))
397 .collect();
398
399 for (current_key, current_value) in params.iter_mut() {
402 if *current_key == new_key {
403 log::debug!("key '{}' found", new_key);
404 if *current_value != new_value {
405 log::info!(
406 "changing key {} from {} to {}",
407 new_key,
408 current_value,
409 new_value
410 );
411
412 *current_value = new_value;
413 changed = true
414 } else {
415 log::debug!(
416 "key '{}' value does not change ({})",
417 new_key,
418 current_value
419 );
420 }
421 }
422 }
423
424 self.params = params
426 .iter()
427 .map(|(key, value)| {
428 if !value.is_empty() {
429 format!("{key}={value}")
430 } else {
431 key.to_string()
432 }
433 })
434 .collect::<Vec<String>>()
435 .join(" ");
436
437 changed
438 }
439
440 pub fn add_kernel_params(&mut self, new_kernel_params: &str) -> bool {
445 let mut changed = false;
446 let mut params: HashMap<&str, &str> = self
447 .params
448 .split_whitespace()
449 .map(|kernel_param| {
450 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
451 })
452 .map(|(key, value)| (key.trim(), value.trim()))
453 .collect();
454
455 let new_kernel_params_tuple: HashMap<&str, &str> = new_kernel_params
456 .split_whitespace()
457 .map(|kernel_param| {
458 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
459 })
460 .collect();
461
462 for (key, new_value) in new_kernel_params_tuple {
463 if params.contains_key(key) {
466 log::info!(
467 "key '{}' already exists, the new kernel parameter won't be added since it already exists",
468 key
469 );
470 } else {
471 log::info!(
472 "key '{}' not found, adding new kernel param with value '{}'",
473 key,
474 new_value
475 );
476 params.insert(key, new_value);
477 changed = true
478 }
479 }
480
481 self.params = params
482 .iter()
483 .map(|(key, value)| {
484 if !value.is_empty() {
485 format!("{key}={value}")
486 } else {
487 key.to_string()
488 }
489 })
490 .collect::<Vec<String>>()
491 .join(" ");
492
493 changed
494 }
495
496 pub fn delete_kernel_params(
501 &mut self,
502 kernel_params_to_delete: &str,
503 ) -> bool {
504 let mut changed = false;
505 let mut params: HashMap<&str, &str> = self
506 .params
507 .split_whitespace()
508 .map(|kernel_param| {
509 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
510 })
511 .map(|(key, value)| (key.trim(), value.trim()))
512 .collect();
513
514 let kernel_params_to_delete_tuple: HashMap<&str, &str> =
515 kernel_params_to_delete
516 .split_whitespace()
517 .map(|kernel_param| {
518 kernel_param.split_once('=').unwrap_or((kernel_param, ""))
519 })
520 .collect();
521
522 for (key, _value) in kernel_params_to_delete_tuple {
523 changed = changed | params.remove(key).is_some();
524 }
525
526 self.params = params
527 .iter()
528 .map(|(key, value)| {
529 if !value.is_empty() {
530 format!("{key}={value}")
531 } else {
532 key.to_string()
533 }
534 })
535 .collect::<Vec<String>>()
536 .join(" ");
537
538 changed
539 }
540}