Skip to content

Commit

Permalink
feat: add new command 'apply template' to crate a new BOS session fro…
Browse files Browse the repository at this point in the history
…m a BOS sessiontemplate
  • Loading branch information
Manuel Sopena Ballesteros committed Oct 14, 2024
1 parent b182fe4 commit 1c9feac
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 53 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.17"
mesa = "0.41.18"
# mesa = { path = "../mesa" } # Only for development purposes
hostlist-parser = "0.1.6"
strum = "0.25.0"
Expand Down
13 changes: 12 additions & 1 deletion src/cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,18 @@ pub fn build_cli() -> Command {
// .arg(arg!(-b --block "Blocks this operation and won't return prompt until the ephemeral environment has been created."))
// .arg(arg!(-p --"public-ssh-key-id" <PUBLIC_SSH_ID> "Public ssh key id stored in Alps"))
.arg(arg!(-i --"image-id" <IMAGE_ID> "Image ID to use as a container image").required(true))
),
)
.subcommand(Command::new("template")
.visible_aliases(["t", "temp", "tmpl"])
.arg_required_else_help(true)
.about("Create a new BOS session from an existing BOS sessiontemplate")
.arg(arg!(-n --name <VALUE> "Name of the Session"))
.arg(arg!(-o --operation <VALUE> "An operation to perform on Components in this Session. Boot Applies the Template to the Components and boots/reboots if necessary. Reboot Applies the Template to the Components; guarantees a reboot. Shutdown Power down Components that are on").value_parser(["reboot", "boot", "shutdown"]).default_value("reboot"))
.arg(arg!(-t --template <VALUE> "Name of the Session Template").required(true))
.arg(arg!(-l --limit <VALUE> "A comma-separated list of nodes, groups, or roles to which the Session will be limited. Components are treated as OR operations unless preceded by '&' for AND or '!' for NOT").required(true))
.arg(arg!(-i --"include-disabled" <VALUE> "Set to include nodes that have been disabled as indicated in the Hardware State Manager (HSM)").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("migrate")
.visible_alias("m")
Expand Down
89 changes: 68 additions & 21 deletions src/cli/commands/apply_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ pub async fn exec(
shasta_token: &str,
shasta_base_url: &str,
shasta_root_cert: &[u8],
bos_session_name_opt: Option<&String>,
bos_sessiontemplate_name: &str,
bos_session_operation: &str,
limit_opt: Option<&String>,
include_disabled: bool,
dry_run: bool,
) {
//***********************************************************
// GET DATA
Expand Down Expand Up @@ -43,14 +46,18 @@ pub async fn exec(
} else {
bos_sessiontemplate_vec.first().unwrap()
};

// END GET DATA
//***********************************************************

//***********************************************************
// VALIDATION
//
// Validate user has access to the HSM groups and/or xnames in the BOS sessiontemplate
log::info!("Start BOS sessiontemplate validation");

// Validate user has access to the BOS sessiontemplate targets (either HSM groups or xnames)
//
log::info!("Validate user has access to HSM group in BOS sessiontemplate");
let target_hsm_vec = bos_sessiontemplate.get_target_hsm();
let target_xname_vec: Vec<String> = if !target_hsm_vec.is_empty() {
mesa::hsm::group::utils::get_member_vec_from_hsm_name_vec(
Expand All @@ -74,20 +81,58 @@ pub async fn exec(

// Validate user has access to the xnames defined in `limit` argument
//
log::info!("Validate user has access to xnames in BOS sessiontemplate");
let limit_vec_opt = if let Some(limit) = limit_opt {
let limit_vec: Vec<String> = limit.split(",").map(|value| value.to_string()).collect();
let mut xnames_to_validate_access_vec = Vec::new();
for limit_value in &limit_vec {
log::info!("Check if limit value '{}', is an xname", limit_value);
if mesa::node::utils::validate_xname_format(limit_value) {
// limit_value is an xname
log::info!("limit value '{}' is an xname", limit_value);
xnames_to_validate_access_vec.push(limit_value.to_string());
} else if let Some(mut hsm_members_vec) =
mesa::hsm::group::utils::get_member_vec_from_hsm_group_name_opt(
shasta_token,
shasta_base_url,
shasta_root_cert,
limit_value,
)
.await
{
// limit_value is an HSM group
log::info!(
"Check if limit value '{}', is an HSM group name",
limit_value
);

xnames_to_validate_access_vec.append(&mut hsm_members_vec);
} else {
// limit_value neither is an xname nor an HSM group
panic!(
"Value '{}' in 'limit' argument does not match an xname or a HSM group name.",
limit_value
);
}
}

log::info!("Validate list of xnames translated from 'limit argument'");

let _ = validate_target_hsm_members(
shasta_token,
shasta_base_url,
shasta_root_cert,
limit_vec.clone(),
xnames_to_validate_access_vec,
)
.await;

log::info!("Access to '{}' granted. Continue.", limit);

Some(limit_vec)
} else {
None
};

// END VALIDATION
//***********************************************************

Expand All @@ -97,8 +142,7 @@ pub async fn exec(
// Create BOS session request payload
//
let bos_session = bos::session::shasta::http_client::v2::BosSession {
// name: Some(bos_sessiontemplate_name.to_string()),
name: None,
name: bos_session_name_opt.cloned(),
tenant: None,
operation: bos::session::shasta::http_client::v2::Operation::from_str(
bos_session_operation,
Expand All @@ -108,27 +152,30 @@ pub async fn exec(
limit: limit_vec_opt.clone().map(|limit_vec| limit_vec.join(",")),
stage: Some(false),
components: None,
include_disabled: None,
include_disabled: Some(include_disabled),
status: None,
};

let create_bos_session_rslt = bos::session::shasta::http_client::v2::post(
shasta_token,
shasta_base_url,
shasta_root_cert,
bos_session,
)
.await;
if dry_run {
println!("Dry-run enabled. No changes persisted into the system");
println!("BOS session info:\n{:#?}", bos_session);
} else {
let create_bos_session_rslt = bos::session::shasta::http_client::v2::post(
shasta_token,
shasta_base_url,
shasta_root_cert,
bos_session,
)
.await;

match create_bos_session_rslt {
Ok(_) => println!(
"Template for nodes {:?} updated to '{}'.\nPlease wait a few minutes for CFS batcher to start. Otherwise reboot the nodes manually.",
limit_vec_opt, bos_sessiontemplate_name
),
Err(e) => eprintln!(
"ERROR - could not create BOS session. Reason:\n{:#?}.\nExit",
e
),
match create_bos_session_rslt {
Ok(_) => println!(
"Template for nodes {:?} updated to '{}'.\nPlease wait a few minutes for CFS batcher to start. Otherwise reboot the nodes manually.",
limit_vec_opt, bos_sessiontemplate_name
),
Err(e) => eprintln!(
"ERROR - could not create BOS session. Reason:\n{:#?}.\nExit", e),
}
}
// END CREATE BOS SESSION
//***********************************************************
Expand Down
156 changes: 126 additions & 30 deletions src/cli/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1473,39 +1473,40 @@ pub async fn process_cli(
false,
)
.await;
/* } else if let Some(cli_apply_template) = cli_apply.subcommand_matches("template") {
apply_template::exec(
shasta_token,
shasta_base_url,
shasta_root_cert,
cli_apply_template
.get_one::<String>("template-name")
.expect("template-name parameter missing"),
cli_apply_template
.get_one::<String>("operation")
.expect("operation parameter missing"),
cli_apply_template.get_one::<String>("limit"),
)
.await; */
} else if let Some(cli_apply_template) = cli_apply.subcommand_matches("template") {
let bos_session_name_opt: Option<&String> = cli_apply_template.get_one("name");
let bos_sessiontemplate_name: &String = cli_apply_template
.get_one("template")
.expect("ERROR - template name is mandatory");
let limit_opt: Option<&String> = cli_apply_template.get_one("limit");
let bos_session_operation: &String = cli_apply_template
.get_one("operation")
.expect("ERROR - operation is mandatory");

let include_disabled: bool = *cli_apply_template
.get_one("include-disabled")
.expect("ERROR - include disabled must have a value");

let dry_run: bool = *cli_apply_template
.get_one("dry-run")
.expect("ERROR - dry-run must have a value");

apply_template::exec(
shasta_token,
shasta_base_url,
shasta_root_cert,
bos_session_name_opt,
&bos_sessiontemplate_name,
&bos_session_operation,
limit_opt,
include_disabled,
dry_run,
)
.await;
} else if let Some(cli_apply_node) = cli_apply.subcommand_matches("node") {
if let Some(cli_apply_node_on) = cli_apply_node.subcommand_matches("on") {
log::warn!("Deprecated - Please use 'manta power on' command instead.");

/* apply_node_on::exec(
settings_hsm_group_name_opt,
shasta_token,
shasta_base_url,
shasta_root_cert,
cli_apply_node_on
.get_one::<String>("XNAMES")
.unwrap()
.split(',')
.map(|xname| xname.trim())
.collect(),
cli_apply_node_on.get_one::<String>("reason").cloned(),
)
.await; */

let xname_vec: Vec<String> = cli_apply_node_on
.get_one::<String>("XNAMES")
.unwrap()
Expand Down Expand Up @@ -1712,6 +1713,101 @@ pub async fn process_cli(
.await;
}
}
/* else if let Some(cli_apply_template) = cli_apply.subcommand_matches("template") {
let name_opt: Option<&String> = cli_apply_template.get_one("name");
let template_name: &String = cli_apply_template
.get_one("template")
.expect("ERROR - template name is mandatory");
let limit_opt: Option<&String> = cli_apply_template.get_one("limit");
let operation: &String = cli_apply_template
.get_one("operation")
.expect("ERROR - operation is mandatory");
let operation =
mesa::bos::session::shasta::http_client::v2::Operation::from_str(operation)
.expect("ERROR - operation not valid");
let include_disabled: bool = *cli_apply_template
.get_one("include-disabled")
.expect("ERROR - include disabled must have a value");
let dry_run: bool = *cli_apply_template
.get_one("dry-run")
.expect("ERROR - dry-run must have a value");
// Validate BOS sessiontemplate exists and user has access to it
let bos_sessiontemplate_vec = mesa::bos::template::mesa::http_client::get(
shasta_token,
shasta_base_url,
shasta_root_cert,
Some(template_name),
)
.await
.expect("ERROR - template not found.");
let bos_sessiontemplate = bos_sessiontemplate_vec
.first()
.expect("ERROR - template not found");
if !bos_sessiontemplate.get_target_hsm().is_empty() {
// Validate if user has access to HSM groups in BOS session template
let hsm_group_name_vec = bos_sessiontemplate.get_target_hsm();
let target_hsm_group_vec = get_target_hsm_group_vec_or_all(
shasta_token,
shasta_base_url,
shasta_root_cert,
hsm_group_name_arg_rslt.unwrap_or(None),
settings_hsm_group_name_opt,
)
.await;
// validate_hsm_groups(shasta_root_cert, shasta_root_cert, shasta_root_cert, hsm_group_name_vec),await;
get_target_hsm_group_vec_or_all(
shasta_token,
shasta_base_url,
shasta_root_cert,
Some(&target_hsm_group_vec.join(",")),
settings_hsm_group_name_opt,
)
.await;
} else {
// Validate if user has access to xnames in BOS session template
let xname_vec = bos_sessiontemplate.get_target_xname();
let _ = validate_target_hsm_members(
shasta_token,
shasta_base_url,
shasta_root_cert,
xname_vec.clone(),
)
.await;
}
let bos_session = mesa::bos::session::shasta::http_client::v2::BosSession {
name: name_opt.cloned(),
tenant: None,
operation: Some(operation),
template_name: template_name.clone(),
limit: limit_opt.cloned(),
stage: None,
components: None,
include_disabled: Some(include_disabled),
status: None,
};
if dry_run {
println!("dry-run - BOS session to create:\n{:#?}", bos_session);
} else {
let _ = mesa::bos::session::shasta::http_client::v2::post(
shasta_token,
shasta_base_url,
shasta_root_cert,
bos_session,
)
.await;
}
} */
} else if let Some(cli_update) = cli_root.subcommand_matches("update") {
if let Some(cli_update_node) = cli_update.subcommand_matches("nodes") {
log::warn!("Deprecated - Please use 'manta apply boot nodes' command instead.");
Expand Down Expand Up @@ -2420,7 +2516,7 @@ pub async fn validate_target_hsm_members(
{
hsm_group_members_opt
} else {
println!("Can't access all or any of the HSM members '{}'.\nPlease choose members form the list of HSM groups below:\n{}\nExit", hsm_group_members_opt.join(","), hsm_groups_user_has_access.join(","));
println!("Can't access all or any of the HSM members '{}'.\nPlease choose members form the list of HSM groups below:\n{}\nExit", hsm_group_members_opt.join(", "), hsm_groups_user_has_access.join(", "));
std::process::exit(1);
}
}

0 comments on commit 1c9feac

Please sign in to comment.