manta_server/service/
node.rs1use csm_rs::node::types::NodeDetails;
4use manta_backend_dispatcher::error::Error;
5use manta_backend_dispatcher::interfaces::hsm::component::ComponentTrait;
6use manta_backend_dispatcher::interfaces::hsm::group::GroupTrait;
7use manta_backend_dispatcher::interfaces::hsm::hardware_inventory::HardwareInventory;
8use manta_backend_dispatcher::types::{
9 ComponentArrayPostArray, ComponentCreate, HWInventoryByLocationList,
10};
11use std::{fs::File, io::BufReader, path::PathBuf};
12
13use crate::server::common;
14use crate::server::common::app_context::InfraContext;
15pub use manta_shared::shared::params::node::GetNodesParams;
16
17pub async fn get_nodes(
19 infra: &InfraContext<'_>,
20 token: &str,
21 params: &GetNodesParams,
22) -> Result<Vec<NodeDetails>, Error> {
23 let node_list = common::node_ops::resolve_hosts_expression(
24 infra.backend,
25 token,
26 ¶ms.xname,
27 params.include_siblings,
28 )
29 .await?;
30
31 if node_list.is_empty() {
32 return Err(Error::BadRequest(
33 "The list of nodes to operate is empty. Nothing to do".to_string(),
34 ));
35 }
36
37 let mut node_details_list = csm_rs::node::utils::get_node_details(
38 token,
39 infra.shasta_base_url,
40 infra.shasta_root_cert,
41 infra.socks5_proxy,
42 node_list.to_vec(),
43 )
44 .await
45 .map_err(|e: csm_rs::error::Error| -> Error { e.into() })?;
46
47 if let Some(ref status) = params.status_filter {
49 node_details_list.retain(|nd| {
50 nd.power_status.eq_ignore_ascii_case(status)
51 || nd.configuration_status.eq_ignore_ascii_case(status)
52 });
53 }
54
55 node_details_list.sort_by(|a, b| a.xname.cmp(&b.xname));
56
57 Ok(node_details_list)
58}
59
60pub async fn delete_node(
65 infra: &InfraContext<'_>,
66 token: &str,
67 id: &str,
68) -> Result<(), Error> {
69 infra.backend.delete_node(token, id).await.map(|_| ())
70}
71
72pub async fn add_node(
77 infra: &InfraContext<'_>,
78 token: &str,
79 id: &str,
80 group: &str,
81 enabled: bool,
82 arch_opt: Option<String>,
83 hardware_file_path: Option<&PathBuf>,
84) -> Result<(), Error> {
85 let backend = infra.backend;
86
87 let component = ComponentCreate {
89 id: id.to_string(),
90 state: "Unknown".to_string(),
91 flag: None,
92 enabled: Some(enabled),
93 software_status: None,
94 role: None,
95 sub_role: None,
96 nid: None,
97 subtype: None,
98 net_type: None,
99 arch: arch_opt,
100 class: None,
101 };
102
103 let components = ComponentArrayPostArray {
104 components: vec![component],
105 force: Some(true),
106 };
107
108 backend.post_nodes(token, components).await?;
109
110 tracing::info!("Node saved '{}'", id);
111
112 let hw_inventory_opt: Option<HWInventoryByLocationList> =
114 if let Some(hardware_file) = hardware_file_path {
115 let file = match File::open(hardware_file) {
116 Ok(f) => f,
117 Err(e) => {
118 rollback_node(backend, token, id).await;
119 return Err(e.into());
120 }
121 };
122 let reader = BufReader::new(file);
123 let hw_inventory_value: serde_json::Value =
124 match serde_json::from_reader(reader) {
125 Ok(v) => v,
126 Err(e) => {
127 rollback_node(backend, token, id).await;
128 return Err(e.into());
129 }
130 };
131 Some(
132 match serde_json::from_value::<HWInventoryByLocationList>(
133 hw_inventory_value,
134 ) {
135 Ok(v) => v,
136 Err(e) => {
137 rollback_node(backend, token, id).await;
138 return Err(e.into());
139 }
140 },
141 )
142 } else {
143 None
144 };
145
146 if let Some(hw_inventory) = hw_inventory_opt {
147 tracing::info!("Adding hardware inventory for '{}'", id);
148 if let Err(error) =
149 backend.post_inventory_hardware(token, hw_inventory).await
150 {
151 rollback_node(backend, token, id).await;
152 return Err(error);
153 }
154 }
155
156 if let Err(error) = backend.post_member(token, group, id).await {
158 rollback_node(backend, token, id).await;
159 return Err(error);
160 }
161
162 Ok(())
163}
164
165async fn rollback_node(
167 backend: &crate::manta_backend_dispatcher::StaticBackendDispatcher,
168 token: &str,
169 id: &str,
170) {
171 tracing::warn!("Rolling back: attempting to delete node '{}'", id);
172 let delete_node_rslt = backend.delete_node(token, id).await;
173 if delete_node_rslt.is_ok() {
174 tracing::info!("Rollback: node '{}' deleted", id);
175 }
176}