manta_server/server/handlers/
kernel_parameters.rs

1//! Kernel-parameters handlers (get/apply/add/delete).
2
3use axum::{Json, extract::Query, http::StatusCode, response::IntoResponse};
4
5use super::{
6  ErrorResponse, RequestCtx, SiteHeader, resolve_xnames_from_request,
7  serialize_or_500, to_handler_error,
8};
9use crate::service;
10
11// ---------------------------------------------------------------------------
12// GET /api/v1/kernel-parameters
13// ---------------------------------------------------------------------------
14
15pub use manta_shared::types::api::queries::KernelParametersQuery;
16
17/// GET /kernel-parameters — fetch BSS kernel parameters for a group or node list.
18#[utoipa::path(get, path = "/kernel-parameters", tag = "kernel-parameters",
19  params(KernelParametersQuery, SiteHeader),
20  security(("bearerAuth" = [])),
21  responses(
22    (status = 200, description = "Kernel parameters", body = Vec<manta_backend_dispatcher::types::bss::BootParameters>),
23    (status = 401, description = "Unauthorized",      body = ErrorResponse),
24    (status = 500, description = "Internal error",    body = ErrorResponse),
25  )
26)]
27#[tracing::instrument(skip_all)]
28pub async fn get_kernel_parameters(
29  ctx: RequestCtx,
30  Query(q): Query<KernelParametersQuery>,
31) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
32  let infra = ctx.infra();
33
34  let params = service::kernel_parameters::GetKernelParametersParams {
35    group_name: q.hsm_group,
36    nodes: q.nodes,
37    settings_group_name: None,
38  };
39
40  let kernel_params = service::kernel_parameters::get_kernel_parameters(
41    &infra, &ctx.token, &params,
42  )
43  .await
44  .map_err(to_handler_error)?;
45
46  Ok(Json(kernel_params))
47}
48
49// ---------------------------------------------------------------------------
50// POST /api/v1/kernel-parameters/apply — Apply kernel parameter changes
51// ---------------------------------------------------------------------------
52
53pub use manta_shared::types::api::kernel_parameters::{
54  ApplyKernelParametersRequest, KernelParamOp,
55};
56
57/// `POST /api/v1/kernel-parameters/apply` — add, replace, or delete kernel parameters on nodes.
58#[utoipa::path(post, path = "/kernel-parameters/apply", tag = "kernel-parameters",
59  params(SiteHeader),
60  request_body = ApplyKernelParametersRequest,
61  security(("bearerAuth" = [])),
62  responses(
63    // dry_run/real result union — kept as Value until the union shape is formalised
64    (status = 200, description = "Kernel parameters applied or preview", body = serde_json::Value),
65    (status = 400, description = "Bad request",                          body = ErrorResponse),
66    (status = 401, description = "Unauthorized",                         body = ErrorResponse),
67    (status = 500, description = "Internal error",                       body = ErrorResponse),
68  )
69)]
70#[tracing::instrument(skip_all)]
71pub async fn apply_kernel_parameters(
72  ctx: RequestCtx,
73  Json(body): Json<ApplyKernelParametersRequest>,
74) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
75  let infra = ctx.infra();
76
77  let xnames = resolve_xnames_from_request(
78    &infra,
79    &ctx.token,
80    body.xnames_expression.as_deref(),
81    body.hsm_group.as_deref(),
82  )
83  .await?;
84
85  tracing::info!(
86    "apply_kernel_parameters xnames={:?} op={:?} dry_run={}",
87    xnames,
88    body.operation,
89    body.dry_run
90  );
91
92  let operation = match body.operation {
93    KernelParamOp::Add => {
94      service::kernel_parameters::KernelParamOperation::Add {
95        params: &body.params,
96        overwrite: body.overwrite,
97      }
98    }
99    KernelParamOp::Apply => {
100      service::kernel_parameters::KernelParamOperation::Apply {
101        params: &body.params,
102      }
103    }
104    KernelParamOp::Delete => {
105      service::kernel_parameters::KernelParamOperation::Delete {
106        params: &body.params,
107      }
108    }
109  };
110
111  let changeset = service::kernel_parameters::prepare_kernel_params_changes(
112    &infra, &ctx.token, &xnames, &operation,
113  )
114  .await
115  .map_err(to_handler_error)?;
116
117  if body.dry_run {
118    return Ok((StatusCode::OK, Json(serialize_or_500(&changeset)?)));
119  }
120
121  let images_to_project = service::kernel_parameters::build_images_to_project(
122    &changeset,
123    body.project_sbps,
124  );
125
126  service::kernel_parameters::apply_kernel_params_changes(
127    &infra,
128    &ctx.token,
129    &changeset,
130    &images_to_project,
131  )
132  .await
133  .map_err(to_handler_error)?;
134
135  Ok((
136    StatusCode::OK,
137    Json(serde_json::json!({
138      "applied": true,
139      "has_changes": changeset.has_changes,
140      "xnames_to_reboot": changeset.xnames_to_reboot,
141    })),
142  ))
143}
144
145// ---------------------------------------------------------------------------
146// POST /api/v1/kernel-parameters/add
147// ---------------------------------------------------------------------------
148
149pub use manta_shared::types::api::kernel_parameters::AddKernelParametersRequest;
150
151/// `POST /api/v1/kernel-parameters/add` — merge new kernel parameters into existing node BSS entries.
152#[utoipa::path(post, path = "/kernel-parameters/add", tag = "kernel-parameters",
153  params(SiteHeader),
154  request_body = AddKernelParametersRequest,
155  security(("bearerAuth" = [])),
156  responses(
157    // dry_run/real result union — kept as Value until the union shape is formalised
158    (status = 200, description = "Parameters added or preview", body = serde_json::Value),
159    (status = 400, description = "Bad request",                 body = ErrorResponse),
160    (status = 401, description = "Unauthorized",                body = ErrorResponse),
161    (status = 500, description = "Internal error",              body = ErrorResponse),
162  )
163)]
164#[tracing::instrument(skip_all)]
165pub async fn add_kernel_parameters(
166  ctx: RequestCtx,
167  Json(body): Json<AddKernelParametersRequest>,
168) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
169  let infra = ctx.infra();
170  let xnames = resolve_xnames_from_request(
171    &infra,
172    &ctx.token,
173    body.xnames_expression.as_deref(),
174    body.hsm_group.as_deref(),
175  )
176  .await?;
177
178  tracing::info!(
179    "add_kernel_parameters xnames={:?} dry_run={}",
180    xnames,
181    body.dry_run
182  );
183
184  let operation = service::kernel_parameters::KernelParamOperation::Add {
185    params: &body.params,
186    overwrite: body.overwrite,
187  };
188
189  let changeset = service::kernel_parameters::prepare_kernel_params_changes(
190    &infra, &ctx.token, &xnames, &operation,
191  )
192  .await
193  .map_err(to_handler_error)?;
194
195  if body.dry_run {
196    return Ok((StatusCode::OK, Json(serialize_or_500(&changeset)?)));
197  }
198
199  let images_to_project = service::kernel_parameters::build_images_to_project(
200    &changeset,
201    body.project_sbps,
202  );
203
204  service::kernel_parameters::apply_kernel_params_changes(
205    &infra,
206    &ctx.token,
207    &changeset,
208    &images_to_project,
209  )
210  .await
211  .map_err(to_handler_error)?;
212
213  Ok((
214    StatusCode::OK,
215    Json(serde_json::json!({
216      "applied": true,
217      "has_changes": changeset.has_changes,
218      "xnames_to_reboot": changeset.xnames_to_reboot,
219    })),
220  ))
221}
222
223// ---------------------------------------------------------------------------
224// DELETE /api/v1/kernel-parameters
225// ---------------------------------------------------------------------------
226
227pub use manta_shared::types::api::kernel_parameters::DeleteKernelParametersRequest;
228
229/// `DELETE /api/v1/kernel-parameters` — remove named kernel parameters from node BSS entries.
230#[utoipa::path(delete, path = "/kernel-parameters", tag = "kernel-parameters",
231  params(SiteHeader),
232  request_body = DeleteKernelParametersRequest,
233  security(("bearerAuth" = [])),
234  responses(
235    // dry_run/real result union — kept as Value until the union shape is formalised
236    (status = 200, description = "Parameters removed or preview", body = serde_json::Value),
237    (status = 400, description = "Bad request",                   body = ErrorResponse),
238    (status = 401, description = "Unauthorized",                  body = ErrorResponse),
239    (status = 500, description = "Internal error",                body = ErrorResponse),
240  )
241)]
242#[tracing::instrument(skip_all)]
243pub async fn delete_kernel_parameters(
244  ctx: RequestCtx,
245  Json(body): Json<DeleteKernelParametersRequest>,
246) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
247  let infra = ctx.infra();
248  let xnames = resolve_xnames_from_request(
249    &infra,
250    &ctx.token,
251    body.xnames_expression.as_deref(),
252    body.hsm_group.as_deref(),
253  )
254  .await?;
255
256  tracing::info!(
257    "delete_kernel_parameters xnames={:?} dry_run={}",
258    xnames,
259    body.dry_run
260  );
261
262  let operation = service::kernel_parameters::KernelParamOperation::Delete {
263    params: &body.params,
264  };
265
266  let changeset = service::kernel_parameters::prepare_kernel_params_changes(
267    &infra, &ctx.token, &xnames, &operation,
268  )
269  .await
270  .map_err(to_handler_error)?;
271
272  if body.dry_run {
273    return Ok((StatusCode::OK, Json(serialize_or_500(&changeset)?)));
274  }
275
276  service::kernel_parameters::apply_kernel_params_changes(
277    &infra,
278    &ctx.token,
279    &changeset,
280    &std::collections::HashMap::new(),
281  )
282  .await
283  .map_err(to_handler_error)?;
284
285  Ok((
286    StatusCode::OK,
287    Json(serde_json::json!({
288      "applied": true,
289      "has_changes": changeset.has_changes,
290      "xnames_to_reboot": changeset.xnames_to_reboot,
291    })),
292  ))
293}