diff --git a/Cargo.toml b/Cargo.toml index 0aa4449e..d34224b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,9 @@ publish = false # cargo # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -mesa = "0.41.15" +mesa = "0.41.16" # mesa = { path = "../mesa" } # Only for development purposes +hostlist-parser = "0.1.6" strum = "0.25.0" strum_macros = "0.25" chrono = "0.4.31" diff --git a/src/cli/build.rs b/src/cli/build.rs index 204b30fd..8070cf25 100644 --- a/src/cli/build.rs +++ b/src/cli/build.rs @@ -426,8 +426,6 @@ pub fn subcommand_apply_hw_configuration() -> Command { .arg(arg!(-c --"create-target-hsm-group" "If the target cluster name does not exist as HSM group, create it.")) .arg(arg!(-d --"delete-empty-parent-hsm-group" "If the target HSM group is empty after this action, remove it.")) .arg(arg!(-u --"unpin-nodes" "It will try to get any nodes available.")) - - // .arg(arg!(-f --file "file with hw configuration details").value_parser(value_parser!(PathBuf)).required(true)) ) } diff --git a/src/cli/commands/add_nodes.rs b/src/cli/commands/add_nodes.rs index 54e16e77..47a2b04d 100644 --- a/src/cli/commands/add_nodes.rs +++ b/src/cli/commands/add_nodes.rs @@ -1,17 +1,65 @@ +use std::collections::HashMap; + +use hostlist_parser::parse; + pub async fn exec( shasta_token: &str, shasta_base_url: &str, shasta_root_cert: &[u8], target_hsm_group_name: &str, parent_hsm_group_name: &str, - xname_to_move_string: &str, + xname_requested_hostlist: &str, nodryrun: bool, create_hsm_group: bool, ) { - let xname_to_move_vec = xname_to_move_string - .split(',') - .map(|xname| xname.trim()) - .collect::>(); + // Get list of nodes the user is targeting + // + // Expand hostlist to a list of xnames + let xname_requested_vec = parse(xname_requested_hostlist) + .expect("Error - `host_list` crate could not parse hostlist"); + + println!("DEBUG - hostlist expanded: {:?}", xname_requested_vec); + + // 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; + + // Get all HSM groups in the system + // FIXME: client should not fetch all info in backend. Create a method in backend to do provide + // information already filtered to the client: + // mesa::hsm::groups::utils::get_hsm_group_available_vec(shasta_token, shasta_base_url, + // shasta_root_cert) -> Vec to get the list of HSM available to the user and return + // a Vec of HsmGroups the user has access to + 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 + .iter() + .filter(|&xname| xname_requested_vec.contains(&xname)) + .cloned() + .collect(); + if !xname_filtered.is_empty() { + hsm_group_summary.insert(hsm_group_name, xname_filtered); + } + } + + // 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(); + + xname_to_move_vec.sort(); + xname_to_move_vec.dedup(); if mesa::hsm::group::http_client::get( shasta_token, @@ -55,13 +103,18 @@ pub async fn exec( shasta_root_cert, target_hsm_group_name, parent_hsm_group_name, - xname_to_move_vec, + xname_to_move_vec + .iter() + .map(|xname| xname.as_str()) + .collect(), nodryrun, ) .await; match node_migration_rslt { - Ok((target_hsm_group_member_vec, parent_hsm_group_member_vec)) => { + Ok((mut target_hsm_group_member_vec, mut parent_hsm_group_member_vec)) => { + target_hsm_group_member_vec.sort(); + parent_hsm_group_member_vec.sort(); println!( "HSM '{}' members: {:?}", target_hsm_group_name, target_hsm_group_member_vec diff --git a/src/cli/commands/migrate_nodes.rs b/src/cli/commands/migrate_nodes.rs new file mode 100644 index 00000000..ba1bec91 --- /dev/null +++ b/src/cli/commands/migrate_nodes.rs @@ -0,0 +1,143 @@ +use std::collections::HashMap; + +use hostlist_parser::parse; + +pub async fn exec( + shasta_token: &str, + shasta_base_url: &str, + shasta_root_cert: &[u8], + target_hsm_group_name: &str, + // parent_hsm_group_name: &str, + xname_requested_hostlist: &str, + nodryrun: bool, + create_hsm_group: bool, +) { + // Get list of nodes the user is targeting + // + // Expand hostlist to a list of xnames + let xname_requested_vec = parse(xname_requested_hostlist) + .expect("Error - `host_list` crate could not parse hostlist"); + + println!("DEBUG - hostlist expanded: {:?}", xname_requested_vec); + + // 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; + + // Get all HSM groups in the system + // FIXME: client should not fetch all info in backend. Create a method in backend to do provide + // information already filtered to the client: + // mesa::hsm::groups::utils::get_hsm_group_available_vec(shasta_token, shasta_base_url, + // shasta_root_cert) -> Vec to get the list of HSM available to the user and return + // a Vec of HsmGroups the user has access to + 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 + .iter() + .filter(|&xname| xname_requested_vec.contains(&xname)) + .cloned() + .collect(); + if !xname_filtered.is_empty() { + hsm_group_summary.insert(hsm_group_name, xname_filtered); + } + } + + // 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(); + + xname_to_move_vec.sort(); + xname_to_move_vec.dedup(); + + println!( + "DEBUG - nodes in hostlist: {}, nodes hostlist filtered by cluster/group: {}", + xname_requested_vec.len(), + xname_to_move_vec.len() + ); + for (hsm_group_name, hsm_group_members) in &hsm_group_summary { + println!("{}: {}", hsm_group_name, hsm_group_members.len()); + } + println!("DEBUG - xnames to move: {:?}", xname_to_move_vec); + + if mesa::hsm::group::http_client::get( + shasta_token, + shasta_base_url, + shasta_root_cert, + Some(&target_hsm_group_name.to_string()), + ) + .await + .is_ok() + { + log::debug!("The HSM group {} exists, good.", target_hsm_group_name); + } else { + if create_hsm_group { + log::info!("HSM group {} does not exist, but the option to create the group has been selected, creating it now.", target_hsm_group_name.to_string()); + if nodryrun { + mesa::hsm::group::http_client::create_new_hsm_group( + shasta_token, + shasta_base_url, + shasta_root_cert, + target_hsm_group_name, + &[], + "false", + "", + &[], + ) + .await + .expect("Unable to create new HSM group"); + } else { + log::error!("Dry-run selected, cannot create the new group continue."); + std::process::exit(1); + } + } else { + log::error!("HSM group {} does not exist, but the option to create the group was NOT specificied, cannot continue.", target_hsm_group_name.to_string()); + std::process::exit(1); + } + } + + // Migrate nodes + for (parent_hsm_group_name, parent_members) in hsm_group_summary { + println!( + "DEBUG - migrate nodes {:?} from '{}' to '{}'", + parent_members, parent_hsm_group_name, target_hsm_group_name + ); + let node_migration_rslt = mesa::hsm::group::utils::migrate_hsm_members( + shasta_token, + shasta_base_url, + shasta_root_cert, + target_hsm_group_name, + &parent_hsm_group_name, + parent_members.iter().map(|xname| xname.as_str()).collect(), + nodryrun, + ) + .await; + + match node_migration_rslt { + Ok((mut target_hsm_group_member_vec, mut parent_hsm_group_member_vec)) => { + target_hsm_group_member_vec.sort(); + parent_hsm_group_member_vec.sort(); + println!( + "HSM '{}' members: {:?}", + target_hsm_group_name, target_hsm_group_member_vec + ); + println!( + "HSM '{}' members: {:?}", + parent_hsm_group_name, parent_hsm_group_member_vec + ); + } + Err(e) => eprintln!("{}", e), + } + } +} diff --git a/src/cli/commands/mod.rs b/src/cli/commands/mod.rs index 4a023aa4..21954bbe 100644 --- a/src/cli/commands/mod.rs +++ b/src/cli/commands/mod.rs @@ -34,6 +34,7 @@ pub mod get_session; pub mod get_template; pub mod log; pub mod migrate_backup; +pub mod migrate_nodes; pub mod migrate_restore; pub mod power_off_cluster; pub mod power_off_nodes;