manta_server/service/
node.rs1use manta_backend_dispatcher::error::Error;
4use manta_backend_dispatcher::interfaces::hsm::{
5 component::ComponentTrait, group::GroupTrait,
6 hardware_inventory::HardwareInventory,
7};
8use manta_backend_dispatcher::types::{
9 ComponentArrayPostArray, ComponentCreate, HWInventoryByLocationList,
10};
11use manta_shared::types::dto::NodeDetails;
12use std::path::PathBuf;
13
14use crate::server::common::app_context::InfraContext;
15use crate::service::authorization::validate_user_group_members_access;
16use crate::service::node_details;
17use crate::service::node_ops::from_user_hosts_expression_to_xname_vec;
18pub use manta_shared::types::api::node::GetNodesParams;
19
20pub async fn get_nodes(
32 infra: &InfraContext<'_>,
33 token: &str,
34 params: &GetNodesParams,
35) -> Result<Vec<NodeDetails>, Error> {
36 let node_list = from_user_hosts_expression_to_xname_vec(
37 infra,
38 token,
39 ¶ms.host_expression,
40 params.include_siblings,
41 )
42 .await?;
43
44 if node_list.is_empty() {
45 return Err(Error::BadRequest(
46 "The list of nodes to operate is empty. Nothing to do".to_string(),
47 ));
48 }
49
50 validate_user_group_members_access(infra, token, &node_list).await?;
52
53 let mut node_details_list =
54 node_details::get_node_details(infra, token, &node_list).await?;
55
56 if let Some(ref status) = params.status_filter {
58 node_details_list.retain(|nd| {
59 nd.power_status.eq_ignore_ascii_case(status)
60 || nd.configuration_status.eq_ignore_ascii_case(status)
61 });
62 }
63
64 node_details_list.sort_by(|a, b| a.xname.cmp(&b.xname));
65
66 Ok(node_details_list)
67}
68
69pub async fn delete_node(
77 infra: &InfraContext<'_>,
78 token: &str,
79 id: &str,
80) -> Result<(), Error> {
81 validate_user_group_members_access(infra, token, &[id.to_string()]).await?;
82
83 infra.backend.delete_node(token, id).await.map(|_| ())
84}
85
86pub async fn add_node(
97 infra: &InfraContext<'_>,
98 token: &str,
99 id: &str,
100 group: &str,
101 enabled: bool,
102 arch_opt: Option<String>,
103 hardware_file_path: Option<&PathBuf>,
104) -> Result<(), Error> {
105 validate_user_group_members_access(infra, token, &[id.to_string()]).await?;
106
107 let component = ComponentCreate {
109 id: id.to_string(),
110 state: "Unknown".to_string(),
111 flag: None,
112 enabled: Some(enabled),
113 software_status: None,
114 role: None,
115 sub_role: None,
116 nid: None,
117 subtype: None,
118 net_type: None,
119 arch: arch_opt,
120 class: None,
121 };
122
123 let components = ComponentArrayPostArray {
124 components: vec![component],
125 force: Some(true),
126 };
127
128 infra.backend.post_nodes(token, components).await?;
129
130 tracing::info!("Node saved '{}'", id);
131
132 let hw_inventory_opt: Option<HWInventoryByLocationList> =
142 if let Some(hardware_file) = hardware_file_path {
143 match read_hw_inventory(hardware_file).await {
144 Ok(inv) => Some(inv),
145 Err(e) => {
146 rollback_node(infra, token, id).await;
147 return Err(e);
148 }
149 }
150 } else {
151 None
152 };
153
154 if let Some(hw_inventory) = hw_inventory_opt {
155 tracing::info!("Adding hardware inventory for '{}'", id);
156 if let Err(error) = infra
157 .backend
158 .post_inventory_hardware(token, hw_inventory)
159 .await
160 .map(|_| ())
161 {
162 rollback_node(infra, token, id).await;
163 return Err(error);
164 }
165 }
166
167 if let Err(error) = infra
169 .backend
170 .post_member(token, group, id)
171 .await
172 .map(|_| ())
173 {
174 rollback_node(infra, token, id).await;
175 return Err(error);
176 }
177
178 Ok(())
179}
180
181async fn read_hw_inventory(
187 path: &PathBuf,
188) -> Result<HWInventoryByLocationList, Error> {
189 let bytes = tokio::fs::read(path).await?;
190 let value: serde_json::Value = serde_json::from_slice(&bytes)?;
191 let inv = serde_json::from_value::<HWInventoryByLocationList>(value)?;
192 Ok(inv)
193}
194
195async fn rollback_node(infra: &InfraContext<'_>, token: &str, id: &str) {
197 tracing::warn!("Rolling back: attempting to delete node '{}'", id);
198 let delete_node_rslt = infra.backend.delete_node(token, id).await;
199 if delete_node_rslt.is_ok() {
200 tracing::info!("Rollback: node '{}' deleted", id);
201 }
202}