manta_server/service/
migrate.rs1use std::collections::HashMap;
17
18use futures::future::join_all;
19use manta_backend_dispatcher::error::Error;
20use manta_backend_dispatcher::interfaces::hsm::group::GroupTrait;
21use manta_backend_dispatcher::interfaces::migrate_backup::MigrateBackupTrait;
22use manta_backend_dispatcher::interfaces::migrate_restore::MigrateRestoreTrait;
23
24use crate::server::common::app_context::InfraContext;
25use crate::service::authorization::validate_user_group_members_access;
26use crate::service::node_ops;
27
28#[derive(serde::Serialize, utoipa::ToSchema)]
30pub struct NodeMigrationResult {
31 pub target_hsm_name: String,
33 pub parent_hsm_name: String,
35 pub target_members: Vec<String>,
37 pub parent_members: Vec<String>,
39}
40
41pub async fn migrate_nodes(
67 infra: &InfraContext<'_>,
68 token: &str,
69 target_group_name_vec: &[String],
70 parent_group_name_vec: &[String],
71 hosts_expression: &str,
72 dry_run: bool,
73 create_group: bool,
74) -> Result<(Vec<String>, Vec<NodeMigrationResult>), Error> {
75 let xname_to_move_vec = node_ops::from_user_hosts_expression_to_xname_vec(
76 infra,
77 token,
78 hosts_expression,
79 false,
80 )
81 .await?;
82
83 if xname_to_move_vec.is_empty() {
84 return Err(Error::BadRequest(
85 "The list of nodes to operate is empty. Nothing to do".to_string(),
86 ));
87 }
88
89 validate_user_group_members_access(infra, token, &xname_to_move_vec).await?;
97
98 let mut group_summary: HashMap<String, Vec<String>> =
99 node_ops::get_curated_group_from_xname_hostlist(
100 infra,
101 token,
102 &xname_to_move_vec,
103 )
104 .await?;
105
106 group_summary.retain(|hsm_name, _| parent_group_name_vec.contains(hsm_name));
107
108 tracing::debug!("xnames to move: {:?}", xname_to_move_vec);
109
110 let mut results = Vec::new();
111
112 let existence: Vec<bool> = join_all(
115 target_group_name_vec
116 .iter()
117 .map(|n| async { infra.backend.get_group(token, n).await.is_ok() }),
118 )
119 .await;
120
121 for (target_name, exists) in
122 target_group_name_vec.iter().zip(existence.iter())
123 {
124 if *exists {
125 tracing::debug!("The group '{target_name}' exists, good.");
126 } else if create_group {
127 tracing::info!(
128 "The group {} does not exist, it will be created",
129 target_name
130 );
131 if dry_run {
132 return Err(Error::BadRequest(format!(
133 "Dry-run selected, the group '{target_name}' created"
134 )));
135 }
136 } else {
137 return Err(Error::NotFound(format!(
138 "The group '{target_name}' does not exist and the option \
139 to create the group was not specified"
140 )));
141 }
142
143 for (parent_group_name, xnames) in &group_summary {
144 let xnames_ref: Vec<&str> = xnames.iter().map(String::as_str).collect();
145 let (mut target_members, mut parent_members) = infra
146 .backend
147 .migrate_group_members(
148 token,
149 target_name,
150 parent_group_name,
151 &xnames_ref,
152 dry_run,
153 )
154 .await?;
155
156 target_members.sort();
157 parent_members.sort();
158
159 results.push(NodeMigrationResult {
160 target_hsm_name: target_name.clone(),
161 parent_hsm_name: parent_group_name.clone(),
162 target_members,
163 parent_members,
164 });
165 }
166 }
167
168 Ok((xname_to_move_vec, results))
169}
170
171pub async fn backup(
177 infra: &InfraContext<'_>,
178 token: &str,
179 bos: Option<&str>,
180 destination: Option<&str>,
181) -> Result<(), Error> {
182 infra.backend.migrate_backup(token, bos, destination).await
183}
184
185#[allow(clippy::too_many_arguments)]
191pub async fn restore(
192 infra: &InfraContext<'_>,
193 token: &str,
194 bos_file: Option<&str>,
195 cfs_file: Option<&str>,
196 hsm_file: Option<&str>,
197 ims_file: Option<&str>,
198 image_dir: Option<&str>,
199 overwrite_group: bool,
200 overwrite_configuration: bool,
201 overwrite_image: bool,
202 overwrite_template: bool,
203) -> Result<(), Error> {
204 infra
205 .backend
206 .migrate_restore(
207 token,
208 bos_file,
209 cfs_file,
210 hsm_file,
211 ims_file,
212 image_dir,
213 overwrite_group,
214 overwrite_configuration,
215 overwrite_image,
216 overwrite_template,
217 )
218 .await
219}