use std::{collections::HashMap, sync::Arc, time::Instant};
use crate::{error::Error, hsm};
use serde_json::Value;
use tokio::sync::Semaphore;
pub fn resolve_hw_description_to_xnames(
mut target_hsm_node_hw_component_count_vec: Vec<(
String,
HashMap<String, usize>,
)>,
mut parent_hsm_node_hw_component_count_vec: Vec<(
String,
HashMap<String, usize>,
)>,
user_defined_target_hsm_hw_component_count_hashmap: HashMap<String, usize>,
) -> Result<
(
Vec<(String, HashMap<String, usize>)>,
Vec<(String, HashMap<String, usize>)>,
),
Error,
> {
let mut combined_target_parent_hsm_node_hw_component_count_vec =
parent_hsm_node_hw_component_count_vec.clone();
for elem in &target_hsm_node_hw_component_count_vec {
if !parent_hsm_node_hw_component_count_vec
.iter()
.any(|(xname, _)| xname.eq(&elem.0))
{
combined_target_parent_hsm_node_hw_component_count_vec.push(elem.clone());
}
}
let combined_target_parent_hsm_hw_component_summary_hashmap =
calculate_hsm_hw_component_summary(
&combined_target_parent_hsm_node_hw_component_count_vec,
);
let hw_component_scarcity_scores_hashmap: HashMap<String, f32> =
calculate_hw_component_scarcity_scores(
&combined_target_parent_hsm_node_hw_component_count_vec,
);
let mut final_combined_target_parent_hsm_hw_component_summary =
user_defined_target_hsm_hw_component_count_hashmap.clone();
for (hw_component, qty) in
combined_target_parent_hsm_hw_component_summary_hashmap
{
final_combined_target_parent_hsm_hw_component_summary
.entry(hw_component)
.and_modify(|current_qty| *current_qty = qty - *current_qty);
}
let hw_component_counters_to_move_out_from_combined_hsm =
calculate_target_hsm_pin(
&final_combined_target_parent_hsm_hw_component_summary.clone(),
&mut combined_target_parent_hsm_node_hw_component_count_vec,
&mut target_hsm_node_hw_component_count_vec,
&mut parent_hsm_node_hw_component_count_vec,
&hw_component_scarcity_scores_hashmap,
)?;
let new_target_hsm_node_hw_component_count_vec =
hw_component_counters_to_move_out_from_combined_hsm;
Ok((
new_target_hsm_node_hw_component_count_vec,
combined_target_parent_hsm_node_hw_component_count_vec,
))
}
pub fn get_best_candidate_in_hsm_pin(
hsm_score_vec: &mut [(String, f32)],
hsm_hw_component_vec: &[(String, HashMap<String, usize>)],
) -> Option<((String, f32), HashMap<String, usize>)> {
if hsm_score_vec.is_empty() || hsm_hw_component_vec.is_empty() {
return None;
}
hsm_score_vec.sort_by_key(|elem| elem.0.clone());
hsm_score_vec.sort_by(|b, a| a.1.partial_cmp(&b.1).unwrap());
let best_candidate: (String, f32) = hsm_score_vec.first().unwrap().clone();
if let Some(best_candiate) = hsm_hw_component_vec
.iter()
.find(|(node, _)| node.eq(&best_candidate.0))
{
Some((best_candidate, best_candiate.1.clone()))
} else {
None
}
}
pub fn get_best_candidate_in_target_and_parent_hsm_pin(
target_hsm_node_score_tuple_vec: &mut [(String, f32)],
parent_hsm_node_score_tuple_vec: &mut [(String, f32)],
target_hsm_node_hw_component_count_vec: &mut Vec<(
String,
HashMap<String, usize>,
)>,
parent_hsm_node_hw_component_count_vec: &Vec<(
String,
HashMap<String, usize>,
)>,
) -> Option<((String, f32), HashMap<String, usize>)> {
let target_best_candidate_tuple = get_best_candidate_in_hsm_pin(
target_hsm_node_score_tuple_vec,
target_hsm_node_hw_component_count_vec,
);
let parent_best_candidate_tuple = get_best_candidate_in_hsm_pin(
parent_hsm_node_score_tuple_vec,
parent_hsm_node_hw_component_count_vec,
);
if target_best_candidate_tuple.is_some() {
target_best_candidate_tuple
} else if parent_best_candidate_tuple.is_some() {
parent_best_candidate_tuple
} else {
None
}
}
pub fn calculate_target_hsm_pin(
user_defined_hsm_hw_components_count_hashmap: &HashMap<String, usize>, combination_target_parent_hsm_node_hw_component_count_vec: &mut Vec<(
String,
HashMap<String, usize>,
)>, target_hsm_node_hw_component_count_vec: &mut Vec<(
String,
HashMap<String, usize>,
)>,
parent_hsm_node_hw_component_count_vec: &mut Vec<(
String,
HashMap<String, usize>,
)>,
hw_component_scarcity_scores_hashmap: &HashMap<String, f32>, ) -> Result<Vec<(String, HashMap<String, usize>)>, Error> {
let mut combination_target_parent_hsm_hw_component_summary_hashmap: HashMap<
String,
usize,
> = calculate_hsm_hw_component_summary(
combination_target_parent_hsm_node_hw_component_count_vec,
);
let target_hsm_hw_component_summary_hashmap: HashMap<String, usize> =
calculate_hsm_hw_component_summary(target_hsm_node_hw_component_count_vec);
let parent_hsm_hw_component_summary_hashmap: HashMap<String, usize> =
calculate_hsm_hw_component_summary(parent_hsm_node_hw_component_count_vec);
let mut target_hsm_node_score_tuple_vec: Vec<(String, f32)> =
calculate_hsm_node_scores_from_final_hsm(
target_hsm_node_hw_component_count_vec,
&target_hsm_hw_component_summary_hashmap,
user_defined_hsm_hw_components_count_hashmap,
hw_component_scarcity_scores_hashmap,
);
let mut parent_hsm_node_score_tuple_vec: Vec<(String, f32)> =
calculate_hsm_node_scores_from_final_hsm(
parent_hsm_node_hw_component_count_vec,
&parent_hsm_hw_component_summary_hashmap,
user_defined_hsm_hw_components_count_hashmap,
hw_component_scarcity_scores_hashmap,
);
let mut group_target_hsm_node_by_score_hashmap: HashMap<usize, Vec<String>> =
HashMap::new();
for (node, score) in &target_hsm_node_score_tuple_vec {
group_target_hsm_node_by_score_hashmap
.entry(*score as usize)
.and_modify(|node_vec| node_vec.push(node.to_string()))
.or_insert(vec![node.clone()]);
}
let mut group_parent_hsm_node_by_score_hashmap: HashMap<usize, Vec<String>> =
HashMap::new();
for (node, score) in &parent_hsm_node_score_tuple_vec {
group_parent_hsm_node_by_score_hashmap
.entry(*score as usize)
.and_modify(|node_vec| node_vec.push(node.to_string()))
.or_insert(vec![node.clone()]);
}
let mut nodes_migrated_from_combination_target_parent_hsm: Vec<(
String,
HashMap<String, usize>,
)> = Vec::new();
let (mut best_candidate, mut best_candidate_counters) =
get_best_candidate_in_target_and_parent_hsm_pin(
&mut target_hsm_node_score_tuple_vec,
&mut parent_hsm_node_score_tuple_vec,
target_hsm_node_hw_component_count_vec,
parent_hsm_node_hw_component_count_vec,
)
.ok_or_else(|| {
Error::Message("ERROR - No best candidate found.".to_string())
})?;
let mut work_to_do = keep_iterating_final_hsm(
user_defined_hsm_hw_components_count_hashmap,
&combination_target_parent_hsm_hw_component_summary_hashmap,
);
let mut iter = 0;
while work_to_do {
log::info!("----- ITERATION {} -----", iter);
log::info!(
"HSM group hw component counters: {:?}",
combination_target_parent_hsm_hw_component_summary_hashmap
);
log::info!(
"Final hw component counters the user wants: {:?}",
user_defined_hsm_hw_components_count_hashmap
);
log::info!(
"Best candidate is '{}' with score {} and hw component counters {:?}",
best_candidate.0,
best_candidate.1,
best_candidate_counters
);
nodes_migrated_from_combination_target_parent_hsm
.push((best_candidate.0.clone(), best_candidate_counters.clone()));
combination_target_parent_hsm_node_hw_component_count_vec
.retain(|(node, _)| !node.eq(&best_candidate.0));
target_hsm_node_hw_component_count_vec
.retain(|(node, _)| !node.eq(&best_candidate.0));
parent_hsm_node_hw_component_count_vec
.retain(|(node, _)| !node.eq(&best_candidate.0));
if combination_target_parent_hsm_node_hw_component_count_vec.is_empty() {
break;
}
combination_target_parent_hsm_hw_component_summary_hashmap =
calculate_hsm_hw_component_summary(
combination_target_parent_hsm_node_hw_component_count_vec,
);
target_hsm_node_score_tuple_vec
.retain(|(node, _)| !node.eq(&best_candidate.0));
parent_hsm_node_score_tuple_vec
.retain(|(node, _)| !node.eq(&best_candidate.0));
let mut target_hsm_node_score_tuple_vec: Vec<(String, f32)> =
calculate_hsm_node_scores_from_final_hsm(
target_hsm_node_hw_component_count_vec,
&combination_target_parent_hsm_hw_component_summary_hashmap,
user_defined_hsm_hw_components_count_hashmap,
hw_component_scarcity_scores_hashmap,
);
let mut parent_hsm_node_score_tuple_vec: Vec<(String, f32)> =
calculate_hsm_node_scores_from_final_hsm(
parent_hsm_node_hw_component_count_vec,
&combination_target_parent_hsm_hw_component_summary_hashmap,
user_defined_hsm_hw_components_count_hashmap,
hw_component_scarcity_scores_hashmap,
);
let mut group_target_hsm_node_by_score_hashmap: HashMap<
usize,
Vec<String>,
> = HashMap::new();
for (node, score) in &target_hsm_node_score_tuple_vec {
group_target_hsm_node_by_score_hashmap
.entry(*score as usize)
.and_modify(|node_vec| node_vec.push(node.to_string()))
.or_insert(vec![node.clone()]);
}
let mut group_parent_hsm_node_by_score_hashmap: HashMap<
usize,
Vec<String>,
> = HashMap::new();
for (node, score) in &parent_hsm_node_score_tuple_vec {
group_parent_hsm_node_by_score_hashmap
.entry(*score as usize)
.and_modify(|node_vec| node_vec.push(node.to_string()))
.or_insert(vec![node.clone()]);
}
(best_candidate, best_candidate_counters) =
get_best_candidate_in_target_and_parent_hsm_pin(
&mut target_hsm_node_score_tuple_vec,
&mut parent_hsm_node_score_tuple_vec,
target_hsm_node_hw_component_count_vec,
parent_hsm_node_hw_component_count_vec,
)
.ok_or_else(|| {
Error::Message("ERROR - No best candidate found.".to_string())
})?;
work_to_do = keep_iterating_final_hsm(
user_defined_hsm_hw_components_count_hashmap,
&combination_target_parent_hsm_hw_component_summary_hashmap,
);
iter += 1;
}
log::info!("----- FINAL RESULT -----");
log::info!("No candidates found");
Ok(nodes_migrated_from_combination_target_parent_hsm)
}
pub fn calculate_hw_component_scarcity_scores(
hsm_node_hw_component_count: &Vec<(String, HashMap<String, usize>)>,
) -> HashMap<String, f32> {
let total_num_hw_components: usize = hsm_node_hw_component_count
.iter()
.flat_map(|(_, hw_component_qty_hashmap)| {
hw_component_qty_hashmap
.iter()
.map(|(_, hw_component_qty)| hw_component_qty)
})
.sum();
let mut hw_component_vec: Vec<&String> = hsm_node_hw_component_count
.iter()
.flat_map(|(_, hw_component_counter_hashmap)| {
hw_component_counter_hashmap.keys()
})
.collect();
hw_component_vec.sort();
hw_component_vec.dedup();
let mut hw_component_scarcity_score_hashmap: HashMap<String, f32> =
HashMap::new();
for hw_component in hw_component_vec {
let mut hsm_hw_component_count = 0;
for (_, hw_component_counter_hashmap) in hsm_node_hw_component_count {
if let Some(hw_component_qty) =
hw_component_counter_hashmap.get(hw_component)
{
hsm_hw_component_count += hw_component_qty;
}
}
hw_component_scarcity_score_hashmap.insert(
hw_component.to_string(),
(total_num_hw_components as f32) / (hsm_hw_component_count as f32),
);
}
log::info!(
"Hw component scarcity scores: {:?}",
hw_component_scarcity_score_hashmap
);
hw_component_scarcity_score_hashmap
}
pub fn calculate_hsm_node_scores_from_final_hsm(
parent_hsm_node_hw_component_count_vec: &Vec<(
String,
HashMap<String, usize>,
)>,
parent_hsm_hw_component_summary_hashmap: &HashMap<String, usize>,
final_hsm_summary_hashmap: &HashMap<String, usize>,
hw_component_scarcity_scores_hashmap: &HashMap<String, f32>,
) -> Vec<(String, f32)> {
let mut node_score_vec: Vec<(String, f32)> = Vec::new();
for (xname, hw_component_count) in parent_hsm_node_hw_component_count_vec {
let mut node_score: f32 = 0.0;
for (hw_component, qty) in hw_component_count {
if final_hsm_summary_hashmap.get(hw_component).is_none() {
node_score -= hw_component_scarcity_scores_hashmap
.get(hw_component)
.unwrap()
* *qty as f32;
} else {
if final_hsm_summary_hashmap.get(hw_component).unwrap()
< parent_hsm_hw_component_summary_hashmap
.get(hw_component)
.unwrap()
{
node_score += hw_component_scarcity_scores_hashmap
.get(hw_component)
.unwrap()
* *qty as f32;
} else {
node_score -= hw_component_scarcity_scores_hashmap
.get(hw_component)
.unwrap()
* *qty as f32;
}
}
}
node_score_vec.push((xname.to_string(), node_score));
}
node_score_vec
}
pub fn keep_iterating_final_hsm(
hsm_final_hw_component_summary_hashmap: &HashMap<String, usize>, hsm_current_hw_component_summary_hashmap: &HashMap<String, usize>, ) -> bool {
for (hw_component, final_qty) in hsm_final_hw_component_summary_hashmap {
if hsm_current_hw_component_summary_hashmap
.get(hw_component)
.is_some_and(|current_qty| current_qty > final_qty)
{
return true;
}
}
false
}
pub async fn get_node_hw_component_count(
shasta_token: String,
shasta_base_url: &str,
shasta_root_cert: &[u8],
hsm_member: &str,
user_defined_hw_profile_vec: Vec<String>,
) -> Result<(String, Vec<String>, Vec<u64>), Error> {
let node_hw_inventory_value =
hsm::hw_inventory::hw_component::http_client::get_query(
&shasta_token,
shasta_base_url,
shasta_root_cert,
hsm_member,
)
.await?;
let node_hw_profile = get_node_hw_properties_from_value(
&node_hw_inventory_value,
user_defined_hw_profile_vec.clone(),
);
Ok((hsm_member.to_string(), node_hw_profile.0, node_hw_profile.1))
}
pub fn calculate_hsm_hw_component_summary(
target_hsm_group_node_hw_component_vec: &Vec<(
String,
HashMap<String, usize>,
)>,
) -> HashMap<String, usize> {
let mut hsm_hw_component_count_hashmap = HashMap::new();
for (_xname, node_hw_component_count_hashmap) in
target_hsm_group_node_hw_component_vec
{
for (hw_component, &qty) in node_hw_component_count_hashmap {
hsm_hw_component_count_hashmap
.entry(hw_component.to_string())
.and_modify(|qty_aux| *qty_aux += qty)
.or_insert(qty);
}
}
hsm_hw_component_count_hashmap
}
pub fn get_node_hw_properties_from_value(
node_hw_inventory_value: &Value,
hw_component_pattern_list: Vec<String>,
) -> (Vec<String>, Vec<u64>) {
let processor_vec =
hsm::hw_inventory::hw_component::utils::get_list_processor_model_from_hw_inventory_value(
node_hw_inventory_value,
)
.unwrap_or_default();
let accelerator_vec =
hsm::hw_inventory::hw_component::utils::get_list_accelerator_model_from_hw_inventory_value(
node_hw_inventory_value,
)
.unwrap_or_default();
let processor_and_accelerator = [processor_vec, accelerator_vec].concat();
let processor_and_accelerator_lowercase = processor_and_accelerator
.iter()
.map(|hw_component| hw_component.to_lowercase());
let mut node_hw_component_pattern_vec = Vec::new();
for actual_hw_component_pattern in processor_and_accelerator_lowercase {
if let Some(hw_component_pattern) = hw_component_pattern_list
.iter()
.find(|&hw_component| actual_hw_component_pattern.contains(hw_component))
{
node_hw_component_pattern_vec.push(hw_component_pattern.to_string());
} else {
node_hw_component_pattern_vec.push(actual_hw_component_pattern);
}
}
let memory_vec =
hsm::hw_inventory::hw_component::utils::get_list_memory_capacity_from_hw_inventory_value(
node_hw_inventory_value,
)
.unwrap_or_default();
(node_hw_component_pattern_vec, memory_vec)
}
pub async fn get_hsm_node_hw_component_counter(
shasta_token: &str,
shasta_base_url: &str,
shasta_root_cert: &[u8],
user_defined_hw_component_vec: &[String],
hsm_group_member_vec: &[String],
mem_lcm: u64,
) -> Vec<(String, HashMap<String, usize>)> {
let start = Instant::now();
let mut tasks = tokio::task::JoinSet::new();
let sem = Arc::new(Semaphore::new(5));
let mut target_hsm_node_hw_component_count_vec = Vec::new();
for hsm_member in hsm_group_member_vec.to_owned() {
let shasta_token_string = shasta_token.to_string(); let shasta_base_url_string = shasta_base_url.to_string(); let shasta_root_cert_vec = shasta_root_cert.to_vec(); let user_defined_hw_component_vec =
user_defined_hw_component_vec.to_owned();
let permit = Arc::clone(&sem).acquire_owned().await;
tasks.spawn(async move {
let _permit = permit;
get_node_hw_component_count(
shasta_token_string,
&shasta_base_url_string,
&shasta_root_cert_vec,
&hsm_member,
user_defined_hw_component_vec,
)
.await
});
}
while let Some(message) = tasks.join_next().await {
if let Ok(Ok(mut node_hw_component_vec_tuple)) = message {
node_hw_component_vec_tuple.1.sort();
let mut node_hw_component_count_hashmap: HashMap<String, usize> =
HashMap::new();
for node_hw_property_vec in node_hw_component_vec_tuple.1 {
let count = node_hw_component_count_hashmap
.entry(node_hw_property_vec)
.or_insert(0);
*count += 1;
}
let node_memory_total_capacity: u64 =
node_hw_component_vec_tuple.2.iter().sum();
node_hw_component_count_hashmap.insert(
"memory".to_string(),
(node_memory_total_capacity / mem_lcm)
.try_into()
.unwrap_or(0),
);
target_hsm_node_hw_component_count_vec.push((
node_hw_component_vec_tuple.0,
node_hw_component_count_hashmap,
));
} else {
log::error!("Failed procesing/fetching node hw information");
}
}
let duration = start.elapsed();
log::info!("Time elapsed to calculate hw components is: {:?}", duration);
target_hsm_node_hw_component_count_vec
}