manta_server/server/handlers/
migrate.rs1use axum::{Json, http::StatusCode, response::IntoResponse};
4use serde::Deserialize;
5use utoipa::ToSchema;
6
7use super::{ErrorResponse, RequestCtx, SiteHeader, to_handler_error};
8use crate::service;
9
10#[derive(Deserialize, ToSchema)]
16pub struct MigrateNodesRequest {
17 pub target_hsm_names: Vec<String>,
19 pub parent_hsm_names: Vec<String>,
21 pub hosts_expression: String,
23 #[serde(default)]
25 pub dry_run: bool,
26 #[serde(default)]
28 pub create_hsm_group: bool,
29}
30
31#[utoipa::path(post, path = "/migrate/nodes", tag = "migrate",
33 params(SiteHeader),
34 request_body = MigrateNodesRequest,
35 security(("bearerAuth" = [])),
36 responses(
37 (status = 200, description = "Migration result", body = serde_json::Value),
38 (status = 400, description = "Bad request", body = ErrorResponse),
39 (status = 401, description = "Unauthorized", body = ErrorResponse),
40 (status = 500, description = "Internal error", body = ErrorResponse),
41 )
42)]
43#[tracing::instrument(skip_all)]
44pub async fn migrate_nodes(
45 ctx: RequestCtx,
46 Json(body): Json<MigrateNodesRequest>,
47) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
48 tracing::info!("migrate_nodes dry_run={}", body.dry_run);
49 let infra = ctx.infra();
50
51 for name in body
53 .target_hsm_names
54 .iter()
55 .chain(body.parent_hsm_names.iter())
56 {
57 service::group::validate_hsm_group_access(&infra, &ctx.token, name)
58 .await
59 .map_err(to_handler_error)?;
60 }
61
62 let (xnames, results) = service::migrate::migrate_nodes(
63 &infra,
64 &ctx.token,
65 &body.target_hsm_names,
66 &body.parent_hsm_names,
67 &body.hosts_expression,
68 body.dry_run,
69 body.create_hsm_group,
70 )
71 .await
72 .map_err(to_handler_error)?;
73
74 Ok(Json(serde_json::json!({
75 "xnames": xnames,
76 "results": results,
77 })))
78}
79
80#[derive(Deserialize, ToSchema)]
86pub struct MigrateBackupRequest {
87 pub bos: Option<String>,
89 pub destination: Option<String>,
91}
92
93#[utoipa::path(post, path = "/migrate/backup", tag = "migrate",
95 params(SiteHeader),
96 request_body = MigrateBackupRequest,
97 security(("bearerAuth" = [])),
98 responses(
99 (status = 200, description = "Backup completed", body = serde_json::Value),
100 (status = 401, description = "Unauthorized", body = ErrorResponse),
101 (status = 500, description = "Internal error", body = ErrorResponse),
102 )
103)]
104#[tracing::instrument(skip_all)]
105pub async fn migrate_backup(
106 ctx: RequestCtx,
107 Json(body): Json<MigrateBackupRequest>,
108) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
109 tracing::info!("migrate_backup");
110 let infra = ctx.infra();
111
112 service::migrate::migrate_backup(
113 &infra,
114 &ctx.token,
115 body.bos.as_deref(),
116 body.destination.as_deref(),
117 )
118 .await
119 .map_err(to_handler_error)?;
120
121 Ok(Json(serde_json::json!({ "completed": true })))
122}
123
124#[derive(Deserialize, ToSchema)]
130pub struct MigrateRestoreRequest {
131 pub bos_file: Option<String>,
133 pub cfs_file: Option<String>,
135 pub hsm_file: Option<String>,
137 pub ims_file: Option<String>,
139 pub image_dir: Option<String>,
141 #[serde(default)]
143 pub overwrite: bool,
144}
145
146#[utoipa::path(post, path = "/migrate/restore", tag = "migrate",
148 params(SiteHeader),
149 request_body = MigrateRestoreRequest,
150 security(("bearerAuth" = [])),
151 responses(
152 (status = 200, description = "Restore completed", body = serde_json::Value),
153 (status = 401, description = "Unauthorized", body = ErrorResponse),
154 (status = 500, description = "Internal error", body = ErrorResponse),
155 )
156)]
157#[tracing::instrument(skip_all)]
158pub async fn migrate_restore(
159 ctx: RequestCtx,
160 Json(body): Json<MigrateRestoreRequest>,
161) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
162 tracing::info!("migrate_restore overwrite={}", body.overwrite);
163 let infra = ctx.infra();
164
165 service::migrate::migrate_restore(
166 &infra,
167 &ctx.token,
168 body.bos_file.as_deref(),
169 body.cfs_file.as_deref(),
170 body.hsm_file.as_deref(),
171 body.ims_file.as_deref(),
172 body.image_dir.as_deref(),
173 body.overwrite,
174 )
175 .await
176 .map_err(to_handler_error)?;
177
178 Ok(Json(serde_json::json!({ "completed": true })))
179}