From 70b916d6a95efc8f670ae085d27162d629c6abd3 Mon Sep 17 00:00:00 2001 From: Manuel Sopena Ballesteros Date: Mon, 21 Oct 2024 22:08:24 +0200 Subject: [PATCH] feat!: dryrun features in commands `add nodes to group` and `remove nodes to group` inverted feat!: remove feature to create hsm group in command `add nodes to group` feat!: remove feature to clean hsm group in command `remove nodes to group` feat: update mesa feat: command to `add nodes to group` now accepts regex feat: command to `remove nodes to group` now accepts regex feat!: command `add nodes to group` to shows a dialog asking user for configuration feat!: command `remove nodes from group` to shows a dialog asking user for configuration refactor: JWT operations refactor: clean code --- Cargo.toml | 2 +- src/cli/build.rs | 14 +- src/cli/commands/add_nodes_to_hsm_groups.rs | 139 ++++++++++-------- src/cli/commands/apply_session.rs | 11 +- src/cli/commands/config_set_hsm.rs | 13 +- src/cli/commands/config_show.rs | 3 +- src/cli/commands/power_off_nodes.rs | 31 +--- src/cli/commands/power_on_cluster.rs | 10 +- src/cli/commands/power_on_nodes.rs | 30 +--- .../commands/remove_nodes_from_hsm_groups.rs | 132 +++++++++-------- src/cli/commands/set_boot_image.rs | 76 ---------- src/cli/process.rs | 48 +++--- src/common/node_ops.rs | 95 ++++++++++-- 13 files changed, 276 insertions(+), 328 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 95746e90..91546c8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ publish = false # cargo # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -mesa = "0.41.20" +mesa = "0.41.21" # mesa = { path = "../mesa" } # Only for development purposes hostlist-parser = "0.1.6" strum = "0.25.0" diff --git a/src/cli/build.rs b/src/cli/build.rs index b61eee93..985d70d1 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -182,17 +182,17 @@ pub fn build_cli() -> Command { .visible_aliases(["ag"]) .about("Add nodes to a list of groups") .arg(arg!(-n --nodes "Comma separated list of nodes")) - .arg(arg!(-g --groups "Comma separated list of groups")) - .arg(arg!(-c --create "Create the group if missing")) + .arg(arg!(-g --group "HSM group to assign the nodes to")) + .arg(arg!(-r --"regex" "Input nodes in regex format.").action(ArgAction::SetTrue)) .arg(arg!(-d --"dry-run" "Simulates the execution of the command without making any actual changes.").action(ArgAction::SetTrue)) ) - .subcommand(Command::new("delete-nodes-from-groups") - .visible_aliases(["dg"]) + .subcommand(Command::new("remove-nodes-from-groups") + .visible_aliases(["rg"]) .about("Remove nodes from groups") .arg(arg!(-n --nodes "Comma separated list of nodes")) - .arg(arg!(-g --groups "Comma separated list of groups")) - .arg(arg!(-c --clean "Delete the group if empty")) - .arg(arg!(-d --"dry-run" "Simulates the execution of the command without making any actual changes.").action(ArgAction::SetTrue)) + .arg(arg!(-g --group "HSM group to remove the nodes from")) + .arg(arg!(-r --"regex" "Input nodes in regex format.").action(ArgAction::SetTrue)) + .arg(arg!(-d --"dry-run" "Simulates the execution of the command without making any actual changes.").action(ArgAction::SetTrue)) ) } diff --git a/src/cli/commands/add_nodes_to_hsm_groups.rs b/src/cli/commands/add_nodes_to_hsm_groups.rs index f35d2fd9..d1228dae 100644 --- a/src/cli/commands/add_nodes_to_hsm_groups.rs +++ b/src/cli/commands/add_nodes_to_hsm_groups.rs @@ -1,38 +1,45 @@ use std::collections::HashMap; +use dialoguer::{theme::ColorfulTheme, Confirm}; + +use crate::common; + /// Add/assign a list of xnames to a list of HSM groups pub async fn exec( shasta_token: &str, shasta_base_url: &str, shasta_root_cert: &[u8], - target_hsm_name_vec: Vec, - xname_requested_hostlist: &str, - nodryrun: bool, - create_hsm_group_if_does_not_exists: bool, + target_hsm_name: &String, + is_regex: bool, + xname_requested: &str, + dryrun: bool, ) { // Filter xnames to the ones members to HSM groups the user has access to // - // Get HashMap with HSM groups and members curated for this request. - // NOTE: the list of HSM groups are the ones the user has access to and containing nodes within - // the hostlist input. Also, each HSM goup member list is also curated so xnames not in - // hostlist have been removed - let mut hsm_group_summary: HashMap> = - crate::common::node_ops::get_curated_hsm_group_from_hostlist( + let hsm_group_summary: HashMap> = if is_regex { + common::node_ops::get_curated_hsm_group_from_hostregex( shasta_token, shasta_base_url, shasta_root_cert, - xname_requested_hostlist, + xname_requested, ) - .await; - - // Keep HSM groups based on list of target HSM groups provided - hsm_group_summary.retain(|hsm_name, _xname_vec| target_hsm_name_vec.contains(hsm_name)); + .await + } else { + // Get HashMap with HSM groups and members curated for this request. + // NOTE: the list of HSM groups are the ones the user has access to and containing nodes within + // the hostlist input. Also, each HSM goup member list is also curated so xnames not in + // hostlist have been removed + common::node_ops::get_curated_hsm_group_from_hostlist( + shasta_token, + shasta_base_url, + shasta_root_cert, + xname_requested, + ) + .await + }; // Get list of xnames available - let mut xname_to_move_vec: Vec<&String> = hsm_group_summary - .iter() - .flat_map(|(_hsm_group_name, hsm_group_members)| hsm_group_members) - .collect(); + let mut xname_to_move_vec: Vec<&String> = hsm_group_summary.values().flatten().collect(); xname_to_move_vec.sort(); xname_to_move_vec.dedup(); @@ -43,53 +50,57 @@ pub async fn exec( std::process::exit(0); } - for target_hsm_name in target_hsm_name_vec { - if mesa::hsm::group::http_client::get( - shasta_token, - shasta_base_url, - shasta_root_cert, - Some(&target_hsm_name), - ) - .await - .is_ok() - { - log::debug!("The HSM group {} exists, good.", target_hsm_name); - } else { - if create_hsm_group_if_does_not_exists { - log::info!( - "HSM group {} does not exist, it will be created", - target_hsm_name - ); - } else { - log::error!("HSM group {} does not exist, but the option to create the group was NOT specificied, cannot continue.", target_hsm_name); - std::process::exit(1); - } - } + if Confirm::with_theme(&ColorfulTheme::default()) + .with_prompt(format!( + "{:?}\nThe nodes above will be added to HSM group '{}'. Do you want to proceed?", + xname_to_move_vec, target_hsm_name + )) + .interact() + .unwrap() + { + log::info!("Continue",); + } else { + println!("Cancelled by user. Aborting."); + std::process::exit(0); + } + + let target_hsm_group_vec = mesa::hsm::group::http_client::get( + shasta_token, + shasta_base_url, + shasta_root_cert, + Some(&target_hsm_name), + ) + .await + .expect("ERROR - Could not get target HSM group"); + + if target_hsm_group_vec.is_empty() { + eprintln!( + "Target HSM group {} does not exist, Nothing to do. Exit", + target_hsm_name + ); + } - for (target_hsm_name, xname_to_move_vec) in &hsm_group_summary { - let node_migration_rslt = mesa::hsm::group::utils::add_hsm_members( - shasta_token, - shasta_base_url, - shasta_root_cert, - &target_hsm_name, - xname_to_move_vec - .iter() - .map(|xname| xname.as_str()) - .collect(), - nodryrun, - ) - .await; + let node_migration_rslt = mesa::hsm::group::utils::add_hsm_members( + shasta_token, + shasta_base_url, + shasta_root_cert, + &target_hsm_name, + xname_to_move_vec + .iter() + .map(|xname| xname.as_str()) + .collect(), + dryrun, + ) + .await; - match node_migration_rslt { - Ok(mut target_hsm_group_member_vec) => { - target_hsm_group_member_vec.sort(); - println!( - "HSM '{}' members: {:?}", - target_hsm_name, target_hsm_group_member_vec - ); - } - Err(e) => eprintln!("{}", e), - } + match node_migration_rslt { + Ok(mut target_hsm_group_member_vec) => { + target_hsm_group_member_vec.sort(); + println!( + "HSM '{}' members: {:?}", + target_hsm_name, target_hsm_group_member_vec + ); } + Err(e) => eprintln!("{}", e), } } diff --git a/src/cli/commands/apply_session.rs b/src/cli/commands/apply_session.rs index 33413328..b41d12da 100644 --- a/src/cli/commands/apply_session.rs +++ b/src/cli/commands/apply_session.rs @@ -4,10 +4,7 @@ use dialoguer::{theme::ColorfulTheme, Confirm}; use futures::TryStreamExt; use mesa::{ cfs::{self, session::mesa::r#struct::v3::CfsSessionPostRequest}, - common::{ - jwt_ops::get_claims_from_jwt_token, kubernetes, - vault::http_client::fetch_shasta_k8s_secrets, - }, + common::{jwt_ops, kubernetes, vault::http_client::fetch_shasta_k8s_secrets}, error::Error, node::utils::validate_xnames, }; @@ -199,11 +196,7 @@ pub async fn exec( // * End Create CFS session // Audit - let jwt_claims = get_claims_from_jwt_token(shasta_token).unwrap(); - /* println!("jwt_claims:\n{:#?}", jwt_claims); - println!("Name: {}", jwt_claims["name"]); */ - - log::info!(target: "app::audit", "User: {} ({}) ; Operation: Apply session", jwt_claims["name"].as_str().unwrap(), jwt_claims["preferred_username"].as_str().unwrap()); + log::info!(target: "app::audit", "User: {} ({}) ; Operation: Apply session", jwt_ops::get_name(shasta_token).unwrap(), jwt_ops::get_preferred_username(shasta_token).unwrap()); (cfs_configuration_name, cfs_session_name) } diff --git a/src/cli/commands/config_set_hsm.rs b/src/cli/commands/config_set_hsm.rs index 6128756f..2d530263 100644 --- a/src/cli/commands/config_set_hsm.rs +++ b/src/cli/commands/config_set_hsm.rs @@ -1,7 +1,7 @@ use std::{fs, io::Write, path::PathBuf}; use directories::ProjectDirs; -use mesa::common::jwt_ops::get_claims_from_jwt_token; +use mesa::common::jwt_ops; use toml_edit::{value, Document}; pub async fn exec( @@ -36,15 +36,8 @@ pub async fn exec( .parse::() .expect("ERROR: could not parse configuration file to TOML"); - let mut settings_hsm_available_vec = get_claims_from_jwt_token(shasta_token) - .unwrap() - .pointer("/realm_access/roles") - .unwrap_or(&serde_json::json!([])) - .as_array() - .unwrap() - .iter() - .map(|role_value| role_value.as_str().unwrap().to_string()) - .collect::>(); + let mut settings_hsm_available_vec = + jwt_ops::get_hsm_name_available(shasta_token).unwrap_or_default(); settings_hsm_available_vec .retain(|role| !role.eq("offline_access") && !role.eq("uma_authorization")); diff --git a/src/cli/commands/config_show.rs b/src/cli/commands/config_show.rs index 258f6164..5178d067 100644 --- a/src/cli/commands/config_show.rs +++ b/src/cli/commands/config_show.rs @@ -77,6 +77,7 @@ pub async fn get_hsm_name_available_from_jwt_or_all( shasta_base_url: &str, shasta_root_cert: &[u8], ) -> Vec { + // FIXME: stop calling `mesa::jwt_ops::get_claims_from_jwt_token` and use `mesa::jwt_ops::get_hsm_name_available` instead let mut realm_access_role_vec = get_claims_from_jwt_token(shasta_token) .unwrap() .pointer("/realm_access/roles") @@ -91,7 +92,7 @@ pub async fn get_hsm_name_available_from_jwt_or_all( .retain(|role| !role.eq("offline_access") && !role.eq("uma_authorization")); if !realm_access_role_vec.is_empty() { - //TODO: Get rid of this by making sure CSM admins don't create HSM groups for system + //FIXME: Get rid of this by making sure CSM admins don't create HSM groups for system //wide operations instead of using roles let mut realm_access_role_filtered_vec = mesa::hsm::group::hacks::filter_system_hsm_group_names(realm_access_role_vec); diff --git a/src/cli/commands/power_off_nodes.rs b/src/cli/commands/power_off_nodes.rs index 76bfcd17..5d5a59cf 100644 --- a/src/cli/commands/power_off_nodes.rs +++ b/src/cli/commands/power_off_nodes.rs @@ -1,5 +1,5 @@ use mesa::{ - common::jwt_ops::get_claims_from_jwt_token, + common::jwt_ops, error::Error, pcs::{self, transitions::r#struct::Location}, }; @@ -64,32 +64,5 @@ pub async fn exec( common::pcs_utils::print_summary_table(power_mgmt_summary, output); // Audit - let jwt_claims = get_claims_from_jwt_token(shasta_token).unwrap(); - - log::info!(target: "app::audit", "User: {} ({}) ; Operation: Power off nodes {:?}", jwt_claims["name"].as_str().unwrap(), jwt_claims["preferred_username"].as_str().unwrap(), xname_vec); - - /* // Check Nodes are shutdown - let _ = capmc::http_client::node_power_status::post( - shasta_token, - shasta_base_url, - shasta_root_cert, - &xname_vec, - ) - .await - .unwrap(); - - // Audit - let jwt_claims = get_claims_from_jwt_token(shasta_token).unwrap(); - - log::info!(target: "app::audit", "User: {} ({}) ; Operation: Power off nodes {:?}", jwt_claims["name"].as_str().unwrap(), jwt_claims["preferred_username"].as_str().unwrap(), xname_vec); - - let _ = wait_nodes_to_power_off( - shasta_token, - shasta_base_url, - shasta_root_cert, - xname_vec, - reason_opt, - force, - ) - .await; */ + log::info!(target: "app::audit", "User: {} ({}) ; Operation: Power off nodes {:?}", jwt_ops::get_name(shasta_token).unwrap(), jwt_ops::get_preferred_username(shasta_token).unwrap(), xname_vec); } diff --git a/src/cli/commands/power_on_cluster.rs b/src/cli/commands/power_on_cluster.rs index ff1fb392..d32b4722 100644 --- a/src/cli/commands/power_on_cluster.rs +++ b/src/cli/commands/power_on_cluster.rs @@ -1,4 +1,8 @@ -use mesa::{common::jwt_ops::get_claims_from_jwt_token, error::Error, pcs}; +use mesa::{ + common::jwt_ops::{self}, + error::Error, + pcs, +}; use crate::common; @@ -63,7 +67,5 @@ pub async fn exec( common::pcs_utils::print_summary_table(power_mgmt_summary, output); // Audit - let jwt_claims = get_claims_from_jwt_token(shasta_token).unwrap(); - - log::info!(target: "app::audit", "User: {} ({}) ; Operation: Power on cluster {}", jwt_claims["name"].as_str().unwrap(), jwt_claims["preferred_username"].as_str().unwrap(), hsm_group_name_arg_opt); + log::info!(target: "app::audit", "User: {} ({}) ; Operation: Power on cluster {}", jwt_ops::get_name(shasta_token).unwrap(), jwt_ops::get_preferred_username(shasta_token).unwrap(), hsm_group_name_arg_opt); } diff --git a/src/cli/commands/power_on_nodes.rs b/src/cli/commands/power_on_nodes.rs index 66865c70..40bb4030 100644 --- a/src/cli/commands/power_on_nodes.rs +++ b/src/cli/commands/power_on_nodes.rs @@ -1,4 +1,4 @@ -use mesa::{common::jwt_ops::get_claims_from_jwt_token, error::Error, pcs}; +use mesa::{common::jwt_ops, error::Error, pcs}; use crate::common; @@ -47,31 +47,5 @@ pub async fn exec( common::pcs_utils::print_summary_table(power_mgmt_summary, output); // Audit - let jwt_claims = get_claims_from_jwt_token(shasta_token).unwrap(); - - log::info!(target: "app::audit", "User: {} ({}) ; Operation: Power on nodes {:?}", jwt_claims["name"].as_str().unwrap(), jwt_claims["preferred_username"].as_str().unwrap(), xname_vec); - - /* // Check Nodes are shutdown - let _ = capmc::http_client::node_power_status::post( - shasta_token, - shasta_base_url, - shasta_root_cert, - &xname_vec, - ) - .await - .unwrap(); - - // Audit - let jwt_claims = get_claims_from_jwt_token(shasta_token).unwrap(); - - log::info!(target: "app::audit", "User: {} ({}) ; Operation: Power on nodes {:?}", jwt_claims["name"].as_str().unwrap(), jwt_claims["preferred_username"].as_str().unwrap(), xname_vec); - - let _ = wait_nodes_to_power_on( - shasta_token, - shasta_base_url, - shasta_root_cert, - xname_vec, - reason_opt, - ) - .await; */ + log::info!(target: "app::audit", "User: {} ({}) ; Operation: Power on nodes {:?}", jwt_ops::get_name(shasta_token).unwrap(), jwt_ops::get_preferred_username(shasta_token).unwrap(), xname_vec); } diff --git a/src/cli/commands/remove_nodes_from_hsm_groups.rs b/src/cli/commands/remove_nodes_from_hsm_groups.rs index 73c1a998..53ba745d 100644 --- a/src/cli/commands/remove_nodes_from_hsm_groups.rs +++ b/src/cli/commands/remove_nodes_from_hsm_groups.rs @@ -1,38 +1,43 @@ use std::collections::HashMap; +use dialoguer::{theme::ColorfulTheme, Confirm}; + /// Remove/unassign a list of xnames to a list of HSM groups pub async fn exec( shasta_token: &str, shasta_base_url: &str, shasta_root_cert: &[u8], - target_hsm_name_vec: Vec, - xname_requested_hostlist: &str, - nodryrun: bool, - remove_empty_hsm_group: bool, + target_hsm_name: &String, + is_regex: bool, + xname_requested: &str, + dryrun: bool, ) { // Filter xnames to the ones members to HSM groups the user has access to // - // Get HashMap with HSM groups and members curated for this request. - // NOTE: the list of HSM groups are the ones the user has access to and containing nodes within - // the hostlist input. Also, each HSM goup member list is also curated so xnames not in - // hostlist have been removed - let mut hsm_group_summary: HashMap> = + let hsm_group_summary: HashMap> = if is_regex { + crate::common::node_ops::get_curated_hsm_group_from_hostregex( + shasta_token, + shasta_base_url, + shasta_root_cert, + xname_requested, + ) + .await + } else { + // Get HashMap with HSM groups and members curated for this request. + // NOTE: the list of HSM groups are the ones the user has access to and containing nodes within + // the hostlist input. Also, each HSM goup member list is also curated so xnames not in + // hostlist have been removed crate::common::node_ops::get_curated_hsm_group_from_hostlist( shasta_token, shasta_base_url, shasta_root_cert, - xname_requested_hostlist, + xname_requested, ) - .await; - - // Keep HSM groups based on list of target HSM groups provided - hsm_group_summary.retain(|hsm_name, _xname_vec| target_hsm_name_vec.contains(hsm_name)); + .await + }; // Get list of xnames available - let mut xname_to_move_vec: Vec<&String> = hsm_group_summary - .iter() - .flat_map(|(_hsm_group_name, hsm_group_members)| hsm_group_members) - .collect(); + let mut xname_to_move_vec: Vec<&String> = hsm_group_summary.values().flatten().collect(); xname_to_move_vec.sort(); xname_to_move_vec.dedup(); @@ -43,53 +48,54 @@ pub async fn exec( std::process::exit(0); } - for target_hsm_name in target_hsm_name_vec { - if mesa::hsm::group::http_client::get( - shasta_token, - shasta_base_url, - shasta_root_cert, - Some(&target_hsm_name), - ) - .await - .is_ok() - { - log::debug!("The HSM group {} exists, good.", target_hsm_name); - } else { - if remove_empty_hsm_group { - log::info!( - "HSM group {} does not exist, it will be created", - target_hsm_name - ); - } else { - log::error!("HSM group {} does not exist, but the option to create the group was NOT specificied, cannot continue.", target_hsm_name); - std::process::exit(1); - } - } + if Confirm::with_theme(&ColorfulTheme::default()) + .with_prompt(format!( + "{:?}\nThe nodes above will be removed from HSM group '{}'. Do you want to proceed?", + xname_to_move_vec, target_hsm_name + )) + .interact() + .unwrap() + { + log::info!("Continue",); + } else { + println!("Cancelled by user. Aborting."); + std::process::exit(0); + } + + if mesa::hsm::group::http_client::get( + shasta_token, + shasta_base_url, + shasta_root_cert, + Some(&target_hsm_name), + ) + .await + .is_ok() + { + log::debug!("The HSM group {} exists, good.", target_hsm_name); + } - for (target_hsm_name, xname_to_move_vec) in &hsm_group_summary { - let node_migration_rslt = mesa::hsm::group::utils::remove_hsm_members( - shasta_token, - shasta_base_url, - shasta_root_cert, - &target_hsm_name, - xname_to_move_vec - .iter() - .map(|xname| xname.as_str()) - .collect(), - nodryrun, - ) - .await; + // Remove xnames from HSM group + let node_migration_rslt = mesa::hsm::group::utils::remove_hsm_members( + shasta_token, + shasta_base_url, + shasta_root_cert, + &target_hsm_name, + xname_to_move_vec + .iter() + .map(|xname| xname.as_str()) + .collect(), + dryrun, + ) + .await; - match node_migration_rslt { - Ok(mut target_hsm_group_member_vec) => { - target_hsm_group_member_vec.sort(); - println!( - "HSM '{}' members: {:?}", - target_hsm_name, target_hsm_group_member_vec - ); - } - Err(e) => eprintln!("{}", e), - } + match node_migration_rslt { + Ok(mut target_hsm_group_member_vec) => { + target_hsm_group_member_vec.sort(); + println!( + "HSM '{}' members: {:?}", + target_hsm_name, target_hsm_group_member_vec + ); } + Err(e) => eprintln!("{}", e), } } diff --git a/src/cli/commands/set_boot_image.rs b/src/cli/commands/set_boot_image.rs index 757ecf86..9af08a45 100644 --- a/src/cli/commands/set_boot_image.rs +++ b/src/cli/commands/set_boot_image.rs @@ -123,81 +123,5 @@ pub async fn exec( println!("Boot image did not change. No need to reboot."); } - /* let is_boot_image_change = current_node_boot_params - .iter() - .any(|boot_param| boot_param.get_boot_image() != image_id); - - if is_boot_image_change { - if Confirm::with_theme(&ColorfulTheme::default()) - .with_prompt(format!( - "New boot image detected. This operation will reboot the nodes so they can boot with the new image:\n{:?}\nDo you want to continue?", - xnames - )) - .interact() - .unwrap() - { - log::info!("Continue",); - } else { - println!("Cancelled by user. Aborting."); - std::process::exit(0); - } - - /* for mut boot_parameter in current_node_boot_params { - log::debug!("boot parameters:\n{:#?}", boot_parameter); - // Get list of xnames which boot image is different to the new one, those are the nodes - // we which boot parameters will change - if !boot_parameter.get_boot_image().eq(image_id) { - boot_parameter.update_boot_image(&image_id); - eprintln!( - "Updating {:?} boot image to '{}'", - boot_parameter.hosts.join(", "), - image_id - ); - - let _ = mesa::bss::bootparameters::http_client::patch( - shasta_base_url, - shasta_token, - shasta_root_cert, - &boot_parameter, - ) - .await; - - xname_to_reboot_vec.push(boot_parameter.hosts.first().unwrap().to_string()); - } - } */ - } else { - println!("Boot image did not change. No need to reboot."); - } - - for boot_parameter in current_node_boot_params { - let _ = mesa::bss::bootparameters::http_client::patch( - shasta_base_url, - shasta_token, - shasta_root_cert, - &boot_parameter, - ) - .await; - } - - // Audit - let jwt_claims = get_claims_from_jwt_token(shasta_token).unwrap(); - - log::info!(target: "app::audit", "User: {} ({}) ; Operation: Apply nodes on {:?}", jwt_claims["name"].as_str().unwrap(), jwt_claims["preferred_username"].as_str().unwrap(), xname_vec_opt.unwrap()); - - // Reboot if needed - if xname_to_reboot_vec.is_empty() { - println!("Nothing to change. Exit"); - } else { - let _ = mesa::capmc::http_client::node_power_reset::post_sync_vec( - shasta_token, - shasta_base_url, - shasta_root_cert, - xname_to_reboot_vec, - Some("Update kernel parameters".to_string()), - true, - ) - .await; - } */ - Ok(()) } diff --git a/src/cli/process.rs b/src/cli/process.rs index b5508894..a99e54d3 100644 --- a/src/cli/process.rs +++ b/src/cli/process.rs @@ -1978,53 +1978,51 @@ pub async fn process_cli( ) .await; } else if let Some(cli_add_nodes) = cli_root.subcommand_matches("add-nodes-to-groups") { - let nodryrun = *cli_add_nodes.get_one::("dry-run").unwrap_or(&true); - - let create_hsm_group = *cli_add_nodes.get_one::("create").unwrap_or(&false); + let dryrun = *cli_add_nodes + .get_one::("dry-run") + .expect("'dry-run' argument must be provided"); let nodes = cli_add_nodes.get_one::("nodes").unwrap(); - let target_hsm_name_vec: Vec = cli_add_nodes - .get_one::("groups") - .expect("Error - target cluster is mandatory") - .split(",") - .map(|hsm_name| hsm_name.trim().to_string()) - .collect(); + let target_hsm_name: &String = cli_add_nodes + .get_one::("group") + .expect("Error - target cluster is mandatory"); + + let is_regex = *cli_add_nodes.get_one::("regex").unwrap_or(&true); add_nodes_to_hsm_groups::exec( shasta_token, shasta_base_url, shasta_root_cert, - target_hsm_name_vec, + target_hsm_name, + is_regex, nodes, - nodryrun, - create_hsm_group, + dryrun, ) .await; } else if let Some(cli_remove_nodes) = - cli_root.subcommand_matches("delete-nodes-from-groups") + cli_root.subcommand_matches("remove-nodes-from-groups") { - let nodryrun = *cli_remove_nodes.get_one::("dry-run").unwrap_or(&true); - - let delete_hsm_group = *cli_remove_nodes.get_one::("clean").unwrap_or(&false); + let dryrun = *cli_remove_nodes + .get_one::("dry-run") + .expect("'dry-run' argument must be provided"); let nodes = cli_remove_nodes.get_one::("nodes").unwrap(); - let target_hsm_name_vec: Vec = cli_remove_nodes - .get_one::("groups") - .expect("Error - target cluster is mandatory") - .split(",") - .map(|hsm_name| hsm_name.trim().to_string()) - .collect(); + let target_hsm_name: &String = cli_remove_nodes + .get_one::("group") + .expect("Error - target cluster is mandatory"); + + let is_regex = *cli_remove_nodes.get_one::("regex").unwrap_or(&true); remove_nodes_from_hsm_groups::exec( shasta_token, shasta_base_url, shasta_root_cert, - target_hsm_name_vec, + target_hsm_name, + is_regex, nodes, - nodryrun, - delete_hsm_group, + dryrun, ) .await; } diff --git a/src/common/node_ops.rs b/src/common/node_ops.rs index bc6b26d7..3afd73a8 100644 --- a/src/common/node_ops.rs +++ b/src/common/node_ops.rs @@ -3,6 +3,62 @@ use std::collections::HashMap; use comfy_table::{Cell, Table}; use hostlist_parser::parse; use mesa::{bss::bootparameters::BootParameters, node::r#struct::NodeDetails}; +use regex::Regex; + +use crate::cli::commands::config_show::get_hsm_name_available_from_jwt_or_all; + +/// Get list of xnames user has access to based on input regex. +/// This method will: +/// 1) Breal down all regex in user input +/// 2) Fetch all HSM groups user has access to +/// 3) For each HSM group, get the list of xnames and filter the ones that matches the regex +pub async fn get_curated_hsm_group_from_hostregex( + shasta_token: &str, + shasta_base_url: &str, + shasta_root_cert: &[u8], + xname_requested_regex: &str, +) -> HashMap> { + let mut hsm_group_summary: HashMap> = HashMap::new(); + + // Get list of regex + let regex_vec: Vec = xname_requested_regex + .split(",") + .map(|regex_str| Regex::new(regex_str.trim()).expect("ERROR - regex not valid")) + .collect(); + + let hsm_name_available_vec = + get_hsm_name_available_from_jwt_or_all(shasta_token, shasta_base_url, shasta_root_cert) + .await; + + // Get HSM group user has access to + let hsm_group_available_map = mesa::hsm::group::utils::get_hsm_map_and_filter_by_hsm_name_vec( + shasta_token, + shasta_base_url, + shasta_root_cert, + hsm_name_available_vec + .iter() + .map(|hsm_name| hsm_name.as_str()) + .collect(), + ) + .await + .expect("ERROR - could not get HSM group summary"); + + // Filter hsm group members + for (hsm_name, xnames) in hsm_group_available_map { + for xname in xnames { + for regex in ®ex_vec { + if regex.is_match(&xname) { + hsm_group_summary + .entry(hsm_name.clone()) + .and_modify(|member_vec| member_vec.push(xname.clone())) + .or_insert(vec![xname.clone()]); + } + } + } + } + + hsm_group_summary +} /// Returns a HashMap with keys HSM group names the user has access to and values a curated list of memembers that matches /// hostlist @@ -12,13 +68,17 @@ pub async fn get_curated_hsm_group_from_hostlist( shasta_root_cert: &[u8], xname_requested_hostlist: &str, ) -> HashMap> { + // Create a summary of HSM groups and the list of members filtered by the list of nodes the + // user is targeting + let mut hsm_group_summary: HashMap> = HashMap::new(); + let xname_requested_vec = parse(xname_requested_hostlist) .expect("Error - `host_list` crate could not parse hostlist"); log::info!("hostlist: {}", xname_requested_hostlist); log::info!("hostlist expanded: {:?}", xname_requested_vec); - // Get final list of xnames to operate on + /* // Get final list of xnames to operate on // Get list of HSM groups available // NOTE: HSM available are the ones the user has access to // let hsm_group_name_available: Vec = get_hsm_name_available_from_jwt(shasta_token).await; @@ -32,21 +92,34 @@ pub async fn get_curated_hsm_group_from_hostlist( let hsm_group_vec_all = mesa::hsm::group::http_client::get_all(shasta_token, shasta_base_url, shasta_root_cert) .await - .expect("Error - fetching HSM groups"); - - // Create a summary of HSM groups and the list of members filtered by the list of nodes the - // user is targeting - let mut hsm_group_summary: HashMap> = HashMap::new(); - for hsm_group in hsm_group_vec_all { - let hsm_group_name: String = hsm_group.label; - let hsm_group_members: Vec = hsm_group.members.unwrap().ids.unwrap(); - let xname_filtered: Vec = hsm_group_members + .expect("Error - fetching HSM groups"); */ + + let hsm_name_available_vec = + get_hsm_name_available_from_jwt_or_all(shasta_token, shasta_base_url, shasta_root_cert) + .await; + + // Get HSM group user has access to + let hsm_group_available_map = mesa::hsm::group::utils::get_hsm_map_and_filter_by_hsm_name_vec( + shasta_token, + shasta_base_url, + shasta_root_cert, + hsm_name_available_vec + .iter() + .map(|hsm_name| hsm_name.as_str()) + .collect(), + ) + .await + .expect("ERROR - could not get HSM group summary"); + + // Filter hsm group members + for (hsm_name, hsm_members) in hsm_group_available_map { + let xname_filtered: Vec = hsm_members .iter() .filter(|&xname| xname_requested_vec.contains(&xname)) .cloned() .collect(); if !xname_filtered.is_empty() { - hsm_group_summary.insert(hsm_group_name, xname_filtered); + hsm_group_summary.insert(hsm_name, xname_filtered); } }