manta_server/service/
cluster.rs

1//! Cluster-scoped node detail queries using HSM group membership.
2//!
3//! Companion to [`crate::service::node`]: where `node` resolves an
4//! arbitrary hosts expression to xnames, this module starts from one
5//! or more HSM groups, expands them to xnames, and produces the same
6//! [`NodeDetails`] rows.
7
8use manta_backend_dispatcher::error::Error;
9use manta_backend_dispatcher::interfaces::hsm::group::GroupTrait;
10use manta_shared::types::dto::NodeDetails;
11
12use crate::server::common::app_context::InfraContext;
13use crate::service::authorization::validate_user_group_vec_access;
14use crate::service::node_details;
15pub use manta_shared::types::api::cluster::GetClusterParams;
16
17/// Fetch full node details for every member of the requested HSM
18/// groups.
19///
20/// When `params.group_name` is unset the scope expands to every
21/// group the token can access. The optional `status_filter` matches
22/// case-insensitively against either the power or configuration
23/// status. Results are sorted by xname for stable rendering.
24///
25/// # Errors
26///
27/// - [`Error::BadRequest`] when `params.group_name` names a group the
28///   caller can't access.
29/// - Backend errors from `get_group_available`,
30///   `get_member_vec_from_group_name_vec`, or the per-xname detail
31///   fetch in [`node_details::get_node_details`].
32pub async fn get_cluster_nodes(
33  infra: &InfraContext<'_>,
34  token: &str,
35  params: &GetClusterParams,
36) -> Result<Vec<NodeDetails>, Error> {
37  // Get list of target groups the user is asking for
38  let target_group_vec: Vec<String> = if let Some(group) = &params.group_name {
39    vec![group.clone()]
40  } else {
41    infra
42      .backend
43      .get_group_available(token)
44      .await?
45      .iter()
46      .map(|group| group.label.clone())
47      .collect()
48  };
49
50  // Validate groups and get list of groups available
51  validate_user_group_vec_access(infra, token, &target_group_vec).await?;
52
53  let mut group_vec_node_list = infra
54    .backend
55    .get_member_vec_from_group_name_vec(token, &target_group_vec)
56    .await?;
57
58  group_vec_node_list.sort();
59
60  let mut node_details_list =
61    node_details::get_node_details(infra, token, &group_vec_node_list).await?;
62
63  // Apply status filter
64  if let Some(ref status) = params.status_filter {
65    node_details_list.retain(|nd| {
66      nd.power_status.eq_ignore_ascii_case(status)
67        || nd.configuration_status.eq_ignore_ascii_case(status)
68    });
69  }
70
71  node_details_list.sort_by(|a, b| a.xname.cmp(&b.xname));
72
73  Ok(node_details_list)
74}