manta_server/server/handlers/
power.rs1use axum::{Json, http::StatusCode, response::IntoResponse};
4use manta_backend_dispatcher::interfaces::hsm::group::GroupTrait;
5use serde::Deserialize;
6use utoipa::ToSchema;
7
8use super::{ErrorResponse, RequestCtx, SiteHeader, to_handler_error};
9use crate::service;
10
11#[derive(Debug, Deserialize, ToSchema)]
17#[serde(rename_all = "lowercase")]
18pub enum PowerAction {
19 On,
21 Off,
23 Reset,
25}
26
27#[derive(Debug, Deserialize, ToSchema)]
29#[serde(rename_all = "lowercase")]
30pub enum PowerTargetType {
31 Nodes,
33 Cluster,
35}
36
37#[derive(Deserialize, ToSchema)]
39pub struct PowerRequest {
40 pub action: PowerAction,
42 pub targets_expression: String,
45 pub target_type: PowerTargetType,
47 #[serde(default)]
49 pub force: bool,
50}
51
52#[utoipa::path(post, path = "/power", tag = "power",
54 params(SiteHeader),
55 request_body = PowerRequest,
56 security(("bearerAuth" = [])),
57 responses(
58 (status = 200, description = "Power operation result", body = serde_json::Value),
59 (status = 400, description = "Bad request", body = ErrorResponse),
60 (status = 401, description = "Unauthorized", body = ErrorResponse),
61 (status = 500, description = "Internal error", body = ErrorResponse),
62 )
63)]
64#[tracing::instrument(skip_all)]
65pub async fn post_power(
66 ctx: RequestCtx,
67 Json(body): Json<PowerRequest>,
68) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
69 tracing::info!(
70 "post_power action={:?} target_type={:?}",
71 body.action,
72 body.target_type
73 );
74 let infra = ctx.infra();
75
76 let xnames: Vec<String> = match body.target_type {
77 PowerTargetType::Cluster => infra
78 .backend
79 .get_member_vec_from_group_name_vec(
80 &ctx.token,
81 std::slice::from_ref(&body.targets_expression),
82 )
83 .await
84 .map_err(to_handler_error)?,
85 PowerTargetType::Nodes => {
86 crate::server::common::node_ops::resolve_hosts_expression(
87 infra.backend,
88 &ctx.token,
89 &body.targets_expression,
90 false,
91 )
92 .await
93 .map_err(to_handler_error)?
94 }
95 };
96
97 if xnames.is_empty() {
98 return Err((
99 StatusCode::BAD_REQUEST,
100 Json(ErrorResponse {
101 error: "No nodes to operate on".into(),
102 }),
103 ));
104 }
105
106 let params = service::power::ApplyPowerParams {
107 action: match body.action {
108 PowerAction::On => service::power::PowerAction::On,
109 PowerAction::Off => service::power::PowerAction::Off,
110 PowerAction::Reset => service::power::PowerAction::Reset,
111 },
112 xnames,
113 force: body.force,
114 };
115 let result = service::power::apply_power(&infra, &ctx.token, ¶ms)
116 .await
117 .map_err(to_handler_error)?;
118
119 Ok(Json(result))
120}