From 9d8208c7a591f5014ee36012a85d4e273f9094dd Mon Sep 17 00:00:00 2001 From: Manuel Sopena Ballesteros Date: Sat, 2 Mar 2024 00:38:34 +0100 Subject: [PATCH] feat: update command no longer reboot nodes if boot image did not change refactor: clean code feat: update manta version --- Cargo.toml | 2 +- src/cli/commands/get_configuration.rs | 2 +- src/cli/commands/update_hsm_group.rs | 198 +++------------------- src/cli/commands/update_node.rs | 231 ++++++++++++++++++-------- src/cli/process.rs | 2 +- src/common/sat_file.rs | 32 ++-- 6 files changed, 206 insertions(+), 261 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f78733c0..a87580ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ publish = false # cargo # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -mesa = "0.31.0" +mesa = "0.31.2" # mesa = { path = "../mesa" } # Only for development purposes strum = "0.25.0" strum_macros = "0.25" diff --git a/src/cli/commands/get_configuration.rs b/src/cli/commands/get_configuration.rs index a8f1ffb8..7dbaa48b 100644 --- a/src/cli/commands/get_configuration.rs +++ b/src/cli/commands/get_configuration.rs @@ -215,7 +215,7 @@ pub async fn get_configuration_layer_details( // check if layer commit is the most recent if commit_sha.eq(layer.commit.as_ref().unwrap()) { // CFS layer commit is the same as the HEAD of the branch - most_recent_commit = most_recent_commit || true; + most_recent_commit = true; } } diff --git a/src/cli/commands/update_hsm_group.rs b/src/cli/commands/update_hsm_group.rs index 5df455ea..6e04ae35 100644 --- a/src/cli/commands/update_hsm_group.rs +++ b/src/cli/commands/update_hsm_group.rs @@ -1,7 +1,6 @@ -use dialoguer::{theme::ColorfulTheme, Confirm}; -use mesa::{capmc, cfs, hsm}; +use mesa::hsm; -use crate::common::ims_ops::get_image_id_from_cfs_configuration_name; +use crate::cli::commands::update_node; /// Updates boot params and desired configuration for all nodes that belongs to a HSM group /// If boot params defined, then nodes in HSM group will be rebooted @@ -13,46 +12,9 @@ pub async fn exec( desired_configuration_opt: Option<&String>, hsm_group_name: &String, ) { - let need_restart = boot_image_configuration_opt.is_some(); - - let desired_configuration_detail_list_rslt = cfs::configuration::mesa::http_client::get( - shasta_token, - shasta_base_url, - shasta_root_cert, - desired_configuration_opt.map(|elem| elem.as_str()), - ) - .await; - - // Check desired configuration exists - if desired_configuration_detail_list_rslt.is_ok() - && !desired_configuration_detail_list_rslt - .as_ref() - .unwrap() - .is_empty() - { - let desired_configuration_detail_list = desired_configuration_detail_list_rslt.unwrap(); - - log::debug!( - "CFS configuration resp:\n{:#?}", - desired_configuration_detail_list - ); - - let _ = desired_configuration_detail_list - .first() - .unwrap() - .name - .clone(); - } else { - eprintln!( - "Desired configuration {} does not exists. Exit", - desired_configuration_opt.unwrap() - ); - std::process::exit(1); - }; - // Get nodes members of HSM group // Get HSM group details - let hsm_group_details = hsm::group::shasta::http_client::get( + let hsm_group_details_rslt = hsm::group::shasta::http_client::get( shasta_token, shasta_base_url, shasta_root_cert, @@ -60,147 +22,29 @@ pub async fn exec( ) .await; - log::debug!("HSM group response:\n{:#?}", hsm_group_details); + let hsm_group_details = if let Ok(hsm_group_details) = hsm_group_details_rslt { + hsm_group_details + } else { + eprintln!("Cluster '{}' not found. Exit", hsm_group_name); + std::process::exit(1); + }; // Get list of xnames in HSM group - let nodes: Vec = hsm_group_details.unwrap().first().unwrap()["members"]["ids"] + let nodes: Vec<&str> = hsm_group_details[0]["members"]["ids"] .as_array() .unwrap() .iter() - .map(|node| node.as_str().unwrap().to_string()) + .map(|node| node.as_str().unwrap()) .collect(); - if need_restart { - if Confirm::with_theme(&ColorfulTheme::default()) - .with_prompt(format!( - "This operation will reboot all nodes in HSM group '{}'.\nDo you want to continue?", - hsm_group_name - )) - .interact() - .unwrap() - { - log::info!("Continue",); - } else { - println!("Cancelled by user. Aborting."); - std::process::exit(0); - } - } - - // Process boot parameters - if let Some(boot_image_cfs_configuration_name) = boot_image_configuration_opt { - let image_id_opt = get_image_id_from_cfs_configuration_name( - shasta_token, - shasta_base_url, - shasta_root_cert, - boot_image_cfs_configuration_name.clone(), - ) - .await; - - let image_value = if let Some(image_id) = image_id_opt { - mesa::ims::image::shasta::http_client::get( - shasta_token, - shasta_base_url, - shasta_root_cert, - Some(&image_id), - ) - .await - .unwrap() - } else { - eprintln!( - "Image ID related to CFS configuration name {} not found. Exit", - boot_image_cfs_configuration_name - ); - std::process::exit(1); - }; - - log::debug!("image_details:\n{:#?}", image_value); - - let image_path = image_value.first().unwrap()["link"]["path"] - .as_str() - .unwrap() - .to_string(); - - let image_id = image_path - .strip_prefix("s3://boot-images/") - .unwrap() - .strip_suffix("/manifest.json") - .unwrap() - .to_string(); - - let component_patch_rep = mesa::bss::http_client::put( - shasta_base_url, - shasta_token, - shasta_root_cert, - &nodes, - &format!("console=ttyS0,115200 bad_page=panic crashkernel=360M hugepagelist=2m-2g intel_iommu=off intel_pstate=disable iommu.passthrough=on numa_interleave_omit=headless oops=panic pageblock_order=14 rd.neednet=1 rd.retry=10 rd.shell ip=dhcp quiet ksocklnd.skip_mr_route_setup=1 cxi_core.disable_default_svc=0 cxi_core.enable_fgfc=1 cxi_core.disable_default_svc=0 cxi_core.sct_pid_mask=0xf spire_join_token=${{SPIRE_JOIN_TOKEN}} root=craycps-s3:s3://boot-images/{}/rootfs:37df9a2dc2c4b50679def2193c193c40-230:dvs:api-gw-service-nmn.local:300:nmn0", image_id), - &format!("s3://boot-images/{}/kernel", image_id), - &format!("s3://boot-images/{}/initrd", image_id), - ) - .await; - - log::debug!( - "Component boot parameters resp:\n{:#?}", - component_patch_rep - ); - } - - // Process dessired configuration - if let Some(desired_configuration_name) = desired_configuration_opt { - log::info!( - "Updating desired configuration. Need restart? {}", - need_restart - ); - - mesa::cfs::component::shasta::utils::update_component_list_desired_configuration( - shasta_token, - shasta_base_url, - shasta_root_cert, - nodes.clone(), - // for this field so it accepts - // Vec<&str> instead of - // Vec - desired_configuration_name, - !need_restart, - ) - .await; - } - - // Check if need to reboot - if need_restart { - // Create BOS session. Note: reboot operation shuts down the nodes and don't bring them back - // up... hence we will split the reboot into 2 operations shutdown and start - - log::info!("Restarting nodes"); - - // Create CAPMC operation shutdown - let capmc_shutdown_nodes_resp = capmc::http_client::node_power_off::post_sync( - shasta_token, - shasta_base_url, - shasta_root_cert, - nodes.clone(), - Some("Update node boot params and/or desired configuration".to_string()), - true, - ) - .await; - - log::debug!( - "CAPMC shutdown nodes response:\n{:#?}", - capmc_shutdown_nodes_resp - ); - - // Create CAPMC operation to start - let capmc_start_nodes_resp = capmc::http_client::node_power_on::post( - shasta_token, - shasta_base_url, - shasta_root_cert, - nodes, - Some("Update node boot params and/or desired configuration".to_string()), - ) - .await; - - log::debug!( - "CAPMC starting nodes response:\n{:#?}", - capmc_start_nodes_resp - ); - } + update_node::exec( + shasta_token, + shasta_base_url, + shasta_root_cert, + Some(hsm_group_name), + boot_image_configuration_opt, + desired_configuration_opt, + nodes.clone(), + ) + .await; } diff --git a/src/cli/commands/update_node.rs b/src/cli/commands/update_node.rs index 1318a347..93f5c580 100644 --- a/src/cli/commands/update_node.rs +++ b/src/cli/commands/update_node.rs @@ -12,7 +12,7 @@ pub async fn exec( desired_configuration_opt: Option<&String>, xnames: Vec<&str>, ) { - let need_restart = boot_image_configuration_opt.is_some(); + let mut need_restart = false; let desired_configuration_detail_list_rslt = cfs::configuration::mesa::http_client::get( shasta_token, @@ -36,19 +36,29 @@ pub async fn exec( desired_configuration_detail_list ); - desired_configuration_detail_list + let desired_configuration_name = desired_configuration_detail_list .first() .unwrap() .name - .clone() + .clone(); + + log::info!( + "Desired configuration '{}' exists", + desired_configuration_name + ); } else { eprintln!( - "Desired configuration {} does not exists. Exit", + "Desired configuration '{}' does not exists. Exit", desired_configuration_opt.unwrap() ); std::process::exit(1); }; + log::info!( + "Desired configuration '{}' exists", + desired_configuration_opt.unwrap() + ); + // Check user has provided valid XNAMES if hsm_group_name.is_some() && !validate_xnames( @@ -64,6 +74,94 @@ pub async fn exec( std::process::exit(1); } + // Get new image id + let (new_image_id_opt, node_boot_params_opt) = + if let Some(boot_image_cfs_configuration_name) = boot_image_configuration_opt { + // Get image id related to the boot CFS configuration + let boot_image_id_opt = get_image_id_from_cfs_configuration_name( + shasta_token, + shasta_base_url, + shasta_root_cert, + boot_image_cfs_configuration_name.clone(), + ) + .await; + + // Check image artifact exists + let boot_image_value_vec = if let Some(boot_image_id) = boot_image_id_opt { + mesa::ims::image::shasta::http_client::get( + shasta_token, + shasta_base_url, + shasta_root_cert, + Some(&boot_image_id), + ) + .await + .unwrap() + } else { + eprintln!( + "Image ID related to CFS configuration name {} not found. Exit", + boot_image_cfs_configuration_name + ); + std::process::exit(1); + }; + + log::info!( + "Boot image found with id '{}'", + boot_image_value_vec.first().unwrap()["id"] + .as_str() + .unwrap() + ); + + log::debug!("image_details_value_vec:\n{:#?}", boot_image_value_vec); + + let image_path = boot_image_value_vec.first().unwrap()["link"]["path"] + .as_str() + .unwrap() + .to_string(); + + let new_image_id = image_path + .strip_prefix("s3://boot-images/") + .unwrap() + .strip_suffix("/manifest.json") + .unwrap() + .to_string(); + + // Check if need to reboot. We will reboot if the new boot image is different than the current + // one + // Get node boot params + let node_boot_params = mesa::bss::http_client::get_boot_params( + shasta_token, + shasta_base_url, + shasta_root_cert, + &xnames + .iter() + .map(|xname| xname.to_string()) + .collect::>(), + ) + .await + .unwrap() + .first_mut() + .unwrap() + .clone(); + + // Get current image id + let current_image_id = node_boot_params["kernel"] + .as_str() + .unwrap() + .trim_start_matches("s3://boot-images/") + .trim_end_matches("/kernel"); + + // Check if new image id is different to the current one to find out if need to restart + if current_image_id != new_image_id { + need_restart = true; + } else { + println!("Boot image does not change. No need to reboot."); + } + + (Some(new_image_id), Some(node_boot_params)) + } else { + (None, None) + }; + if need_restart { if Confirm::with_theme(&ColorfulTheme::default()) .with_prompt(format!( @@ -78,57 +176,54 @@ pub async fn exec( println!("Cancelled by user. Aborting."); std::process::exit(0); } - } - - // Process boot parameters - if let Some(boot_image_cfs_configuration_name) = boot_image_configuration_opt { - let image_id_opt = get_image_id_from_cfs_configuration_name( - shasta_token, - shasta_base_url, - shasta_root_cert, - boot_image_cfs_configuration_name.clone(), - ) - .await; - let image_details_value_vec = if let Some(image_id) = image_id_opt { - mesa::ims::image::shasta::http_client::get( - shasta_token, - shasta_base_url, - shasta_root_cert, - Some(&image_id), - ) - .await - .unwrap() - } else { - eprintln!( - "Image ID related to CFS configuration name {} not found. Exit", - boot_image_cfs_configuration_name - ); - std::process::exit(1); - }; - - log::debug!("image_details_value_vec:\n{:#?}", image_details_value_vec); + println!( + "Updating boot configuration to '{}'", + boot_image_configuration_opt.unwrap() + ); - let image_path = image_details_value_vec.first().unwrap()["link"]["path"] + // Update root kernel param to it uses the new image id + let new_kernel_params_vec: Vec = node_boot_params_opt.unwrap()["params"] .as_str() .unwrap() - .to_string(); + .split_whitespace() + .map(|boot_param| { + if boot_param.contains("root=") { + let aux = boot_param + .trim_start_matches("root=craycps-s3:s3://boot-images/") + .split_once('/') + .unwrap() + .1; - let image_id = image_path - .strip_prefix("s3://boot-images/") - .unwrap() - .strip_suffix("/manifest.json") - .unwrap() - .to_string(); + format!( + "root=craycps-s3:s3://boot-images/{}/{}", + new_image_id_opt.clone().unwrap(), + aux + ) + } else { + boot_param.to_string() + } + }) + .collect(); let component_patch_rep = mesa::bss::http_client::patch( shasta_base_url, shasta_token, shasta_root_cert, - &xnames.iter().map(|&xname| xname.to_string()).collect(), - Some(&format!("console=ttyS0,115200 bad_page=panic crashkernel=360M hugepagelist=2m-2g intel_iommu=off intel_pstate=disable iommu.passthrough=on numa_interleave_omit=headless oops=panic pageblock_order=14 rd.neednet=1 rd.retry=10 rd.shell ip=dhcp quiet ksocklnd.skip_mr_route_setup=1 cxi_core.disable_default_svc=0 cxi_core.enable_fgfc=1 cxi_core.disable_default_svc=0 cxi_core.sct_pid_mask=0xf spire_join_token=${{SPIRE_JOIN_TOKEN}} root=craycps-s3:s3://boot-images/{}/rootfs:37df9a2dc2c4b50679def2193c193c40-230:dvs:api-gw-service-nmn.local:300:nmn0", image_id)), - Some(&format!("s3://boot-images/{}/kernel", image_id)), - Some(&format!("s3://boot-images/{}/initrd", image_id)), + &xnames + .iter() + .map(|xname| xname.to_string()) + .collect::>(), + Some(&new_kernel_params_vec.join(" ")), + // Some(&format!("console=ttyS0,115200 bad_page=panic crashkernel=360M hugepagelist=2m-2g intel_iommu=off intel_pstate=disable iommu.passthrough=on numa_interleave_omit=headless oops=panic pageblock_order=14 rd.neednet=1 rd.retry=10 rd.shell ip=dhcp quiet ksocklnd.skip_mr_route_setup=1 cxi_core.disable_default_svc=0 cxi_core.enable_fgfc=1 cxi_core.disable_default_svc=0 cxi_core.sct_pid_mask=0xf spire_join_token=${{SPIRE_JOIN_TOKEN}} root=craycps-s3:s3://boot-images/{}/rootfs:37df9a2dc2c4b50679def2193c193c40-230:dvs:api-gw-service-nmn.local:300:nmn0", image_id)), + Some(&format!( + "s3://boot-images/{}/kernel", + new_image_id_opt.clone().unwrap() + )), + Some(&format!( + "s3://boot-images/{}/initrd", + new_image_id_opt.unwrap() + )), ) .await; @@ -136,32 +231,34 @@ pub async fn exec( "Component boot parameters resp:\n{:#?}", component_patch_rep ); - } - // Update desired configuration + log::info!("Boot params for nodes {:?} updated", xnames); - if let Some(desired_configuration_name) = desired_configuration_opt { - log::info!( - "Updating desired configuration. Need restart? {}", - need_restart - ); + // Update desired configuration - mesa::cfs::component::shasta::utils::update_component_list_desired_configuration( - shasta_token, - shasta_base_url, - shasta_root_cert, - xnames.iter().map(|xname| xname.to_string()).collect(), // TODO: modify function signature - // for this field so it accepts - // Vec<&str> instead of - // Vec - desired_configuration_name, - !need_restart, - ) - .await; - } + if let Some(desired_configuration_name) = desired_configuration_opt { + println!( + "Updating desired configuration to '{}'", + desired_configuration_name + ); + + mesa::cfs::component::shasta::utils::update_component_list_desired_configuration( + shasta_token, + shasta_base_url, + shasta_root_cert, + xnames.iter().map(|xname| xname.to_string()).collect(), // TODO: modify function signature + // for this field so it accepts + // Vec<&str> instead of + // Vec + desired_configuration_name, + true, + ) + .await; + } + + // Create BOS session. Note: reboot operation shuts down the nodes and don't bring them back + // up... hence we will split the reboot into 2 operations shutdown and start - // Check if need to reboot - if need_restart { log::info!("Restarting nodes"); let nodes: Vec = xnames.into_iter().map(|xname| xname.to_string()).collect(); diff --git a/src/cli/process.rs b/src/cli/process.rs index 88a247cf..98a6d1a0 100644 --- a/src/cli/process.rs +++ b/src/cli/process.rs @@ -731,7 +731,7 @@ pub async fn process_cli( ) .await; - Some(target_hsm_group_vec.first().unwrap()); + target_hsm_group_vec.first().unwrap(); if let Some(ansible_limit) = hsm_group_members_opt { validate_target_hsm_members( diff --git a/src/common/sat_file.rs b/src/common/sat_file.rs index 442a46ac..6872c6ac 100644 --- a/src/common/sat_file.rs +++ b/src/common/sat_file.rs @@ -140,9 +140,9 @@ pub fn render_jinja2_sat_file_yaml( log::info!("'Session vars' file provided. Going to process SAT file as a template."); // TEMPLATE // Read sesson vars file - serde_yaml::from_str(&values_file_content).unwrap() + serde_yaml::from_str(values_file_content).unwrap() } else { - serde_yaml::from_str(&sat_file_content).unwrap() + serde_yaml::from_str(sat_file_content).unwrap() }; if let Some(value_option_vec) = value_cli_vec_opt { @@ -154,7 +154,7 @@ pub fn render_jinja2_sat_file_yaml( // Render SAT file template let env = minijinja::Environment::new(); - let sat_file_rendered = env.render_str(&sat_file_content, values_file_yaml).unwrap(); + let sat_file_rendered = env.render_str(sat_file_content, values_file_yaml).unwrap(); log::debug!("SAT file rendered:\n{}", sat_file_rendered); @@ -207,8 +207,8 @@ pub async fn create_cfs_configuration_from_sat_file( /// ref_name_processed) /// - It has not been already processed pub fn get_next_image_to_process( - image_yaml_vec: &Vec, - ref_name_processed_vec: &Vec, + image_yaml_vec: &[serde_yaml::Value], + ref_name_processed_vec: &[String], ) -> Option { image_yaml_vec .iter() @@ -277,11 +277,11 @@ pub async fn import_images_section_in_sat_file( shasta_token, shasta_base_url, shasta_root_cert, - &image_yaml, - &cray_product_catalog, + image_yaml, + cray_product_catalog, ansible_verbosity_opt, ansible_passthrough_opt, - &ref_name_processed_hashmap, + ref_name_processed_hashmap, // tag, ) .await @@ -289,7 +289,7 @@ pub async fn import_images_section_in_sat_file( image_processed_hashmap.insert(image_id.clone(), image_yaml.clone()); - ref_name_processed_hashmap.insert(get_ref_name(&image_yaml), image_id.clone()); + ref_name_processed_hashmap.insert(get_ref_name(image_yaml), image_id.clone()); next_image_to_process_opt = get_next_image_to_process( &image_yaml_vec, @@ -323,7 +323,10 @@ pub async fn create_image_from_sat_file_serde_yaml( // .to_string() // .replace("__DATE__", tag); - log::info!("Creating CFS session related to build image '{}'", image_name); + log::info!( + "Creating CFS session related to build image '{}'", + image_name + ); // Get CFS configuration related to CFS session in SAT file let configuration: String = image_yaml["configuration"] @@ -475,7 +478,8 @@ pub async fn create_image_from_sat_file_serde_yaml( "Image '{}' imported image_id '{}'", image_name, base_image_id ); - return Ok(base_image_id); + + Ok(base_image_id) } else { // Create a CFS session log::info!("Creating CFS session"); @@ -512,7 +516,7 @@ pub async fn create_image_from_sat_file_serde_yaml( let image_id = cfs_session.get_result_id().unwrap(); println!("Image '{}' imported image_id '{}'", image_name, image_id); - return Ok(image_id); + Ok(image_id) } } @@ -521,7 +525,7 @@ async fn process_sat_file_image_product_type_ims_recipe( shasta_base_url: &str, shasta_root_cert: &[u8], product_details: &serde_json::Value, - image_name: &String, + image_name: &str, ) -> Result { let recipe_id: String = product_details .as_object() @@ -548,7 +552,7 @@ async fn process_sat_file_image_product_type_ims_recipe( let ims_job = ims::job::r#struct::JobPostRequest { job_type: "create".to_string(), - image_root_archive_name: image_name.clone(), + image_root_archive_name: image_name.to_string(), kernel_file_name: Some("vmlinuz".to_string()), initrd_file_name: Some("initrd".to_string()), kernel_parameters_file_name: Some("kernel-parameters".to_string()),