use std::process::Command; use std::path::Path; use anyhow::{Error, format_err, bail}; use serde_json::Value; use proxmox::sys::linux::procfs; use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; use crate::api2::types::*; use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_POWER_MANAGEMENT}; use crate::tools::cert::CertInfo; impl std::convert::From for NodeCpuInformation { fn from(info: procfs::ProcFsCPUInfo) -> Self { Self { model: info.model, sockets: info.sockets, cpus: info.cpus, } } } #[api( input: { properties: { node: { schema: NODE_SCHEMA, }, }, }, returns: { type: NodeStatus, }, access: { permission: &Permission::Privilege(&["system", "status"], PRIV_SYS_AUDIT, false), }, )] /// Read node memory, CPU and (root) disk usage fn get_status( _param: Value, _info: &ApiMethod, _rpcenv: &mut dyn RpcEnvironment, ) -> Result { let meminfo: procfs::ProcFsMemInfo = procfs::read_meminfo()?; let memory = NodeMemoryCounters { total: meminfo.memtotal, used: meminfo.memused, free: meminfo.memfree, }; let swap = NodeSwapCounters { total: meminfo.swaptotal, used: meminfo.swapused, free: meminfo.swapfree, }; let kstat: procfs::ProcFsStat = procfs::read_proc_stat()?; let cpu = kstat.cpu; let wait = kstat.iowait_percent; let loadavg = procfs::Loadavg::read()?; let loadavg = [loadavg.one(), loadavg.five(), loadavg.fifteen()]; let cpuinfo = procfs::read_cpuinfo()?; let cpuinfo = cpuinfo.into(); let uname = nix::sys::utsname::uname(); let kversion = format!( "{} {} {}", uname.sysname(), uname.release(), uname.version() ); Ok(NodeStatus { memory, swap, root: crate::tools::disks::disk_usage(Path::new("/"))?, uptime: procfs::read_proc_uptime()?.0 as u64, loadavg, kversion, cpuinfo, cpu, wait, info: NodeInformation { fingerprint: CertInfo::new()?.fingerprint()?, }, }) } #[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("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 ROUTER: Router = Router::new() .get(&API_METHOD_GET_STATUS) .post(&API_METHOD_REBOOT_OR_SHUTDOWN);