1use manta_backend_dispatcher::error::Error;
4use manta_backend_dispatcher::interfaces::apply_session::ApplySessionTrait;
5use manta_backend_dispatcher::interfaces::bss::BootParametersTrait;
6use manta_backend_dispatcher::interfaces::cfs::CfsTrait;
7use manta_backend_dispatcher::interfaces::hsm::group::GroupTrait;
8use manta_backend_dispatcher::types::Group;
9use manta_backend_dispatcher::types::bss::BootParameters;
10use manta_backend_dispatcher::types::cfs::component::Component;
11use manta_backend_dispatcher::types::cfs::session::CfsSessionGetResponse;
12
13use crate::server::common::app_context::InfraContext;
14use crate::server::common::authorization::get_groups_names_available;
15pub use manta_shared::shared::params::session::GetSessionParams;
16
17pub async fn get_sessions(
19 infra: &InfraContext<'_>,
20 token: &str,
21 params: &GetSessionParams,
22) -> Result<Vec<CfsSessionGetResponse>, Error> {
23 tracing::info!("Get CFS sessions");
24
25 infra
26 .backend
27 .get_and_filter_sessions(
28 token,
29 infra.shasta_base_url,
30 infra.shasta_root_cert,
31 params
32 .hsm_group
33 .as_ref()
34 .map(|v| vec![v.clone()])
35 .unwrap_or_default(),
36 params.xnames.iter().map(String::as_str).collect(),
37 params.min_age.as_ref(),
38 params.max_age.as_ref(),
39 params.session_type.as_ref(),
40 params.status.as_ref(),
41 params.name.as_ref(),
42 params.limit.as_ref(),
43 None,
44 )
45 .await
46}
47
48#[derive(serde::Serialize)]
50pub struct SessionDeletionContext {
51 pub session: CfsSessionGetResponse,
53 pub image_ids: Vec<String>,
55 pub group_available_vec: Vec<Group>,
57 pub cfs_component_vec: Vec<Component>,
59 pub bss_bootparameters_vec: Vec<BootParameters>,
61}
62
63pub async fn prepare_session_deletion(
65 infra: &InfraContext<'_>,
66 token: &str,
67 session_name: &str,
68 settings_hsm_group_name_opt: Option<&str>,
69) -> Result<SessionDeletionContext, Error> {
70 let group_available_names = get_groups_names_available(
71 infra.backend,
72 token,
73 None,
74 settings_hsm_group_name_opt,
75 )
76 .await?;
77
78 tracing::info!("Fetching data from the backend...");
79 let start = std::time::Instant::now();
80
81 let (
82 group_available_vec,
83 cfs_session_vec,
84 cfs_component_vec,
85 bss_bootparameters_vec,
86 ) = tokio::try_join!(
87 infra.backend.get_group_available(token),
88 infra.backend.get_and_filter_sessions(
89 token,
90 infra.shasta_base_url,
91 infra.shasta_root_cert,
92 group_available_names,
93 Vec::new(),
94 None,
95 None,
96 None,
97 None,
98 None,
99 None,
100 None,
101 ),
102 infra.backend.get_cfs_components(
103 token,
104 infra.shasta_base_url,
105 infra.shasta_root_cert,
106 None,
107 None,
108 None,
109 ),
110 infra.backend.get_all_bootparameters(token),
111 )?;
112
113 tracing::info!(
114 "Time elapsed to fetch information from backend: {:?}",
115 start.elapsed()
116 );
117
118 let session = cfs_session_vec
119 .into_iter()
120 .find(|s| s.name == session_name)
121 .ok_or_else(|| Error::NotFound(format!("CFS session '{session_name}'")))?;
122
123 let image_ids = session.get_result_id_vec();
124
125 Ok(SessionDeletionContext {
126 session,
127 image_ids,
128 group_available_vec,
129 cfs_component_vec,
130 bss_bootparameters_vec,
131 })
132}
133
134pub async fn execute_session_deletion(
136 infra: &InfraContext<'_>,
137 token: &str,
138 deletion_ctx: &SessionDeletionContext,
139 dry_run: bool,
140) -> Result<(), Error> {
141 infra
142 .backend
143 .delete_and_cancel_session(
144 token,
145 infra.shasta_base_url,
146 infra.shasta_root_cert,
147 &deletion_ctx.group_available_vec,
148 &deletion_ctx.session,
149 &deletion_ctx.cfs_component_vec,
150 &deletion_ctx.bss_bootparameters_vec,
151 dry_run,
152 )
153 .await
154}
155
156#[allow(clippy::too_many_arguments)]
160pub async fn create_cfs_session(
161 infra: &InfraContext<'_>,
162 token: &str,
163 gitea_token: &str,
164 cfs_conf_sess_name: Option<&str>,
165 playbook_yaml_file_name_opt: Option<&str>,
166 hsm_group_opt: Option<&str>,
167 repo_name_vec: &[&str],
168 repo_last_commit_id_vec: &[&str],
169 ansible_limit_opt: Option<&str>,
170 ansible_verbosity: Option<&str>,
171 ansible_passthrough: Option<&str>,
172) -> Result<(String, String), Error> {
173 let backend = infra.backend;
174
175 let ansible_limit = if let Some(ansible_limit) = ansible_limit_opt {
176 let xname_vec = crate::server::common::node_ops::resolve_hosts_expression(
177 backend,
178 token,
179 ansible_limit,
180 false,
181 )
182 .await?;
183 Some(xname_vec.join(","))
184 } else {
185 None
186 };
187
188 backend
189 .apply_session(
190 gitea_token,
191 infra.gitea_base_url,
192 token,
193 infra.shasta_base_url,
194 infra.shasta_root_cert,
195 cfs_conf_sess_name,
196 playbook_yaml_file_name_opt,
197 hsm_group_opt,
198 repo_name_vec,
199 repo_last_commit_id_vec,
200 ansible_limit.as_deref(),
201 ansible_verbosity,
202 ansible_passthrough,
203 )
204 .await
205}
206
207pub async fn validate_console_session(
213 infra: &InfraContext<'_>,
214 token: &str,
215 name: &str,
216) -> Result<(), Error> {
217 let sessions = infra
218 .backend
219 .get_and_filter_sessions(
220 token,
221 infra.shasta_base_url,
222 infra.shasta_root_cert,
223 Vec::new(),
224 Vec::new(),
225 None,
226 None,
227 None,
228 None,
229 Some(&name.to_string()),
230 None,
231 None,
232 )
233 .await?;
234
235 let session = sessions
236 .first()
237 .ok_or_else(|| Error::NotFound(format!("CFS session '{name}'")))?;
238
239 let target_def = session
240 .target
241 .as_ref()
242 .and_then(|t| t.definition.as_ref())
243 .ok_or_else(|| {
244 Error::BadRequest(format!(
245 "CFS session '{name}' has no target definition"
246 ))
247 })?;
248
249 if target_def != "image" {
250 return Err(Error::BadRequest(format!(
251 "CFS session '{name}' is not an image-type session (got '{target_def}')"
252 )));
253 }
254
255 let status = session
256 .status
257 .as_ref()
258 .and_then(|s| s.session.as_ref())
259 .and_then(|s| s.status.as_ref())
260 .ok_or_else(|| {
261 Error::BadRequest(format!("CFS session '{name}' has no status"))
262 })?;
263
264 if status != "running" {
265 return Err(Error::Conflict(format!(
266 "CFS session '{name}' is not running (status: '{status}')"
267 )));
268 }
269
270 Ok(())
271}