src/api2/node/status.rs: rework api, implement reboot and shutdown
This commit is contained in:
		| @ -1,13 +1,14 @@ | ||||
| use anyhow::{Error}; | ||||
| use std::process::Command; | ||||
|  | ||||
| use anyhow::{Error, format_err, bail}; | ||||
| use serde_json::{json, Value}; | ||||
|  | ||||
| use proxmox::sys::linux::procfs; | ||||
|  | ||||
| use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, SubdirMap, Permission}; | ||||
| use proxmox::list_subdirs_api_method; | ||||
| use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; | ||||
|  | ||||
| use crate::api2::types::*; | ||||
| use crate::config::acl::PRIV_SYS_AUDIT; | ||||
| use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_POWER_MANAGEMENT}; | ||||
|  | ||||
| #[api( | ||||
|     input: { | ||||
| @ -70,12 +71,49 @@ fn get_usage( | ||||
|     })) | ||||
| } | ||||
|  | ||||
| pub const USAGE_ROUTER: Router = Router::new() | ||||
|     .get(&API_METHOD_GET_USAGE); | ||||
| #[api( | ||||
|     protected: true, | ||||
|     input: { | ||||
|         properties: { | ||||
|             node: { | ||||
|                 schema: NODE_SCHEMA, | ||||
|             }, | ||||
|             command: { | ||||
|                 type: NodePowerCommand, | ||||
|             }, | ||||
|         } | ||||
|     }, | ||||
|     access: { | ||||
|         permission: &Permission::Privilege(&["system", "status"], PRIV_SYS_POWER_MANAGEMENT, false), | ||||
|     }, | ||||
| )] | ||||
| /// Reboot or shutdown the node. | ||||
| fn reboot_or_shutdown(command: NodePowerCommand) -> Result<(), Error> { | ||||
|  | ||||
|     let systemctl_command = match command { | ||||
|         NodePowerCommand::Reboot => "reboot", | ||||
|         NodePowerCommand::Shutdown => "poweroff", | ||||
|     }; | ||||
|  | ||||
|     let output = Command::new("/bin/systemctl") | ||||
|         .arg(systemctl_command) | ||||
|         .output() | ||||
|         .map_err(|err| format_err!("failed to execute systemctl - {}", err))?; | ||||
|  | ||||
|     if !output.status.success() { | ||||
|         match output.status.code() { | ||||
|             Some(code) => { | ||||
|                 let msg = String::from_utf8(output.stderr) | ||||
|                     .map(|m| if m.is_empty() { String::from("no error message") } else { m }) | ||||
|                     .unwrap_or_else(|_| String::from("non utf8 error message (suppressed)")); | ||||
|                 bail!("diff failed with status code: {} - {}", code, msg); | ||||
|             } | ||||
|             None => bail!("systemctl terminated by signal"), | ||||
|         } | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| pub const SUBDIRS: SubdirMap = &[ | ||||
|     ("usage", &USAGE_ROUTER), | ||||
| ]; | ||||
| pub const ROUTER: Router = Router::new() | ||||
|     .get(&list_subdirs_api_method!(SUBDIRS)) | ||||
|     .subdirs(SUBDIRS); | ||||
|     .get(&API_METHOD_GET_USAGE) | ||||
|     .post(&API_METHOD_REBOOT_OR_SHUTDOWN); | ||||
|  | ||||
| @ -499,6 +499,17 @@ pub struct TaskListItem { | ||||
|     pub status: Option<String>, | ||||
| } | ||||
|  | ||||
| #[api()] | ||||
| #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] | ||||
| #[serde(rename_all = "lowercase")] | ||||
| /// Node Power command type. | ||||
| pub enum NodePowerCommand { | ||||
|     /// Restart the server | ||||
|     Reboot, | ||||
|     /// Shutdown the server | ||||
|     Shutdown, | ||||
| } | ||||
|  | ||||
| #[api()] | ||||
| #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] | ||||
| #[serde(rename_all = "lowercase")] | ||||
|  | ||||
		Reference in New Issue
	
	Block a user