1use axum::{Json, extract::Query, http::StatusCode, response::IntoResponse};
14
15use super::{
16 ErrorResponse, RequestCtx, SiteHeader, resolve_xnames_from_request,
17 serialize_or_500, to_handler_error,
18};
19use crate::server::audit::AuditNote;
20use crate::service;
21
22pub use manta_shared::types::api::queries::KernelParametersQuery;
27
28#[utoipa::path(get, path = "/kernel-parameters", tag = "kernel-parameters",
30 params(KernelParametersQuery, SiteHeader),
31 security(("bearerAuth" = [])),
32 responses(
33 (status = 200, description = "Kernel parameters", body = Vec<manta_backend_dispatcher::types::bss::BootParameters>),
34 (status = 401, description = "Unauthorized", body = ErrorResponse),
35 (status = 500, description = "Internal error", body = ErrorResponse),
36 )
37)]
38#[tracing::instrument(skip_all)]
39pub async fn get_kernel_parameters(
40 ctx: RequestCtx,
41 Query(q): Query<KernelParametersQuery>,
42) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
43 let infra = ctx.infra();
44
45 let params = service::kernel_parameters::GetKernelParametersParams {
46 group_name: q.hsm_group,
47 nodes: q.nodes,
48 settings_group_name: None,
49 };
50
51 let kernel_params = service::kernel_parameters::get_kernel_parameters(
52 &infra, &ctx.token, ¶ms,
53 )
54 .await
55 .map_err(to_handler_error)?;
56
57 Ok(Json(kernel_params))
58}
59
60pub use manta_shared::types::api::kernel_parameters::{
65 ApplyKernelParametersRequest, KernelParamOp,
66};
67
68#[utoipa::path(post, path = "/kernel-parameters/apply", tag = "kernel-parameters",
70 params(SiteHeader),
71 request_body = ApplyKernelParametersRequest,
72 security(("bearerAuth" = [])),
73 responses(
74 (status = 200, description = "Kernel parameters applied or preview", body = serde_json::Value),
76 (status = 400, description = "Bad request", body = ErrorResponse),
77 (status = 401, description = "Unauthorized", body = ErrorResponse),
78 (status = 500, description = "Internal error", body = ErrorResponse),
79 )
80)]
81#[tracing::instrument(skip_all)]
82pub async fn apply_kernel_parameters(
83 ctx: RequestCtx,
84 note: AuditNote,
85 Json(body): Json<ApplyKernelParametersRequest>,
86) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
87 note
88 .action("kernel_parameters.apply")
89 .targets("hosts", body.xnames_expression.as_deref())
90 .targets("group", body.hsm_group.as_deref())
91 .detail("operation", body.operation)
92 .detail("params", &body.params)
93 .detail("overwrite", body.overwrite)
94 .detail("project_sbps", body.project_sbps)
95 .dry_run(body.dry_run);
96
97 let infra = ctx.infra();
98
99 let xnames = resolve_xnames_from_request(
100 &infra,
101 &ctx.token,
102 body.xnames_expression.as_deref(),
103 body.hsm_group.as_deref(),
104 )
105 .await?;
106 note.nodes(&xnames);
107
108 tracing::info!(
109 "apply_kernel_parameters xnames={:?} op={:?} dry_run={}",
110 xnames,
111 body.operation,
112 body.dry_run
113 );
114
115 let operation = match body.operation {
116 KernelParamOp::Add => {
117 service::kernel_parameters::KernelParamOperation::Add {
118 params: &body.params,
119 overwrite: body.overwrite,
120 }
121 }
122 KernelParamOp::Apply => {
123 service::kernel_parameters::KernelParamOperation::Apply {
124 params: &body.params,
125 }
126 }
127 KernelParamOp::Delete => {
128 service::kernel_parameters::KernelParamOperation::Delete {
129 params: &body.params,
130 }
131 }
132 };
133
134 let changeset = service::kernel_parameters::prepare_kernel_params_changes(
135 &infra, &ctx.token, &xnames, &operation,
136 )
137 .await
138 .map_err(to_handler_error)?;
139
140 if body.dry_run {
141 return Ok((StatusCode::OK, Json(serialize_or_500(&changeset)?)));
142 }
143
144 let images_to_project = service::kernel_parameters::build_images_to_project(
145 &changeset,
146 body.project_sbps,
147 );
148
149 service::kernel_parameters::apply_kernel_params_changes(
150 &infra,
151 &ctx.token,
152 &changeset,
153 &images_to_project,
154 )
155 .await
156 .map_err(to_handler_error)?;
157
158 Ok((
159 StatusCode::OK,
160 Json(serde_json::json!({
161 "applied": true,
162 "has_changes": changeset.has_changes,
163 "xnames_to_reboot": changeset.xnames_to_reboot,
164 })),
165 ))
166}
167
168pub use manta_shared::types::api::kernel_parameters::AddKernelParametersRequest;
173
174#[utoipa::path(post, path = "/kernel-parameters/add", tag = "kernel-parameters",
176 params(SiteHeader),
177 request_body = AddKernelParametersRequest,
178 security(("bearerAuth" = [])),
179 responses(
180 (status = 200, description = "Parameters added or preview", body = serde_json::Value),
182 (status = 400, description = "Bad request", body = ErrorResponse),
183 (status = 401, description = "Unauthorized", body = ErrorResponse),
184 (status = 500, description = "Internal error", body = ErrorResponse),
185 )
186)]
187#[tracing::instrument(skip_all)]
188pub async fn add_kernel_parameters(
189 ctx: RequestCtx,
190 note: AuditNote,
191 Json(body): Json<AddKernelParametersRequest>,
192) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
193 note
194 .action("kernel_parameters.add")
195 .targets("hosts", body.xnames_expression.as_deref())
196 .targets("group", body.hsm_group.as_deref())
197 .detail("params", &body.params)
198 .detail("overwrite", body.overwrite)
199 .detail("project_sbps", body.project_sbps)
200 .dry_run(body.dry_run);
201
202 let infra = ctx.infra();
203 let xnames = resolve_xnames_from_request(
204 &infra,
205 &ctx.token,
206 body.xnames_expression.as_deref(),
207 body.hsm_group.as_deref(),
208 )
209 .await?;
210 note.nodes(&xnames);
211
212 tracing::info!(
213 "add_kernel_parameters xnames={:?} dry_run={}",
214 xnames,
215 body.dry_run
216 );
217
218 let operation = service::kernel_parameters::KernelParamOperation::Add {
219 params: &body.params,
220 overwrite: body.overwrite,
221 };
222
223 let changeset = service::kernel_parameters::prepare_kernel_params_changes(
224 &infra, &ctx.token, &xnames, &operation,
225 )
226 .await
227 .map_err(to_handler_error)?;
228
229 if body.dry_run {
230 return Ok((StatusCode::OK, Json(serialize_or_500(&changeset)?)));
231 }
232
233 let images_to_project = service::kernel_parameters::build_images_to_project(
234 &changeset,
235 body.project_sbps,
236 );
237
238 service::kernel_parameters::apply_kernel_params_changes(
239 &infra,
240 &ctx.token,
241 &changeset,
242 &images_to_project,
243 )
244 .await
245 .map_err(to_handler_error)?;
246
247 Ok((
248 StatusCode::OK,
249 Json(serde_json::json!({
250 "applied": true,
251 "has_changes": changeset.has_changes,
252 "xnames_to_reboot": changeset.xnames_to_reboot,
253 })),
254 ))
255}
256
257pub use manta_shared::types::api::kernel_parameters::DeleteKernelParametersRequest;
262
263#[utoipa::path(delete, path = "/kernel-parameters", tag = "kernel-parameters",
265 params(SiteHeader),
266 request_body = DeleteKernelParametersRequest,
267 security(("bearerAuth" = [])),
268 responses(
269 (status = 200, description = "Parameters removed or preview", body = serde_json::Value),
271 (status = 400, description = "Bad request", body = ErrorResponse),
272 (status = 401, description = "Unauthorized", body = ErrorResponse),
273 (status = 500, description = "Internal error", body = ErrorResponse),
274 )
275)]
276#[tracing::instrument(skip_all)]
277pub async fn delete_kernel_parameters(
278 ctx: RequestCtx,
279 note: AuditNote,
280 Json(body): Json<DeleteKernelParametersRequest>,
281) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
282 note
283 .action("kernel_parameters.delete")
284 .targets("hosts", body.xnames_expression.as_deref())
285 .targets("group", body.hsm_group.as_deref())
286 .detail("params", &body.params)
287 .dry_run(body.dry_run);
288
289 let infra = ctx.infra();
290 let xnames = resolve_xnames_from_request(
291 &infra,
292 &ctx.token,
293 body.xnames_expression.as_deref(),
294 body.hsm_group.as_deref(),
295 )
296 .await?;
297 note.nodes(&xnames);
298
299 tracing::info!(
300 "delete_kernel_parameters xnames={:?} dry_run={}",
301 xnames,
302 body.dry_run
303 );
304
305 let operation = service::kernel_parameters::KernelParamOperation::Delete {
306 params: &body.params,
307 };
308
309 let changeset = service::kernel_parameters::prepare_kernel_params_changes(
310 &infra, &ctx.token, &xnames, &operation,
311 )
312 .await
313 .map_err(to_handler_error)?;
314
315 if body.dry_run {
316 return Ok((StatusCode::OK, Json(serialize_or_500(&changeset)?)));
317 }
318
319 service::kernel_parameters::apply_kernel_params_changes(
320 &infra,
321 &ctx.token,
322 &changeset,
323 &std::collections::HashMap::new(),
324 )
325 .await
326 .map_err(to_handler_error)?;
327
328 Ok((
329 StatusCode::OK,
330 Json(serde_json::json!({
331 "applied": true,
332 "has_changes": changeset.has_changes,
333 "xnames_to_reboot": changeset.xnames_to_reboot,
334 })),
335 ))
336}