]>
Commit | Line | Data |
---|---|---|
ed751dc2 | 1 | use std::process::Command; |
33070956 | 2 | use std::path::Path; |
ed751dc2 DM |
3 | |
4 | use anyhow::{Error, format_err, bail}; | |
eb704648 | 5 | use serde_json::Value; |
2337df7b TL |
6 | |
7 | use proxmox::sys::linux::procfs; | |
8 | ||
ed751dc2 | 9 | use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission}; |
2337df7b | 10 | |
4805edc4 WB |
11 | use pbs_tools::cert::CertInfo; |
12 | ||
2337df7b | 13 | use crate::api2::types::*; |
ed751dc2 | 14 | use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_POWER_MANAGEMENT}; |
2337df7b | 15 | |
398636b6 DC |
16 | impl std::convert::From<procfs::ProcFsCPUInfo> for NodeCpuInformation { |
17 | fn from(info: procfs::ProcFsCPUInfo) -> Self { | |
18 | Self { | |
19 | model: info.model, | |
20 | sockets: info.sockets, | |
21 | cpus: info.cpus, | |
22 | } | |
23 | } | |
24 | } | |
25 | ||
2337df7b TL |
26 | #[api( |
27 | input: { | |
28 | properties: { | |
29 | node: { | |
30 | schema: NODE_SCHEMA, | |
31 | }, | |
32 | }, | |
33 | }, | |
34 | returns: { | |
eb704648 | 35 | type: NodeStatus, |
b5037fa8 DM |
36 | }, |
37 | access: { | |
f1490da8 | 38 | permission: &Permission::Privilege(&["system", "status"], PRIV_SYS_AUDIT, false), |
b5037fa8 | 39 | }, |
2337df7b TL |
40 | )] |
41 | /// Read node memory, CPU and (root) disk usage | |
bfcef26a | 42 | fn get_status( |
2337df7b TL |
43 | _param: Value, |
44 | _info: &ApiMethod, | |
45 | _rpcenv: &mut dyn RpcEnvironment, | |
eb704648 | 46 | ) -> Result<NodeStatus, Error> { |
2337df7b | 47 | let meminfo: procfs::ProcFsMemInfo = procfs::read_meminfo()?; |
eb704648 DC |
48 | let memory = NodeMemoryCounters { |
49 | total: meminfo.memtotal, | |
50 | used: meminfo.memused, | |
51 | free: meminfo.memfree, | |
52 | }; | |
2337df7b | 53 | |
398636b6 DC |
54 | let swap = NodeSwapCounters { |
55 | total: meminfo.swaptotal, | |
56 | used: meminfo.swapused, | |
57 | free: meminfo.swapfree, | |
58 | }; | |
59 | ||
eb704648 DC |
60 | let kstat: procfs::ProcFsStat = procfs::read_proc_stat()?; |
61 | let cpu = kstat.cpu; | |
398636b6 DC |
62 | let wait = kstat.iowait_percent; |
63 | ||
64 | let loadavg = procfs::Loadavg::read()?; | |
65 | let loadavg = [loadavg.one(), loadavg.five(), loadavg.fifteen()]; | |
66 | ||
67 | let cpuinfo = procfs::read_cpuinfo()?; | |
68 | let cpuinfo = cpuinfo.into(); | |
69 | ||
70 | let uname = nix::sys::utsname::uname(); | |
71 | let kversion = format!( | |
72 | "{} {} {}", | |
73 | uname.sysname(), | |
74 | uname.release(), | |
75 | uname.version() | |
76 | ); | |
bfcef26a | 77 | |
eb704648 DC |
78 | Ok(NodeStatus { |
79 | memory, | |
398636b6 | 80 | swap, |
eb704648 | 81 | root: crate::tools::disks::disk_usage(Path::new("/"))?, |
398636b6 DC |
82 | uptime: procfs::read_proc_uptime()?.0 as u64, |
83 | loadavg, | |
84 | kversion, | |
85 | cpuinfo, | |
eb704648 | 86 | cpu, |
398636b6 | 87 | wait, |
eb704648 DC |
88 | info: NodeInformation { |
89 | fingerprint: CertInfo::new()?.fingerprint()?, | |
bfcef26a | 90 | }, |
eb704648 | 91 | }) |
2337df7b TL |
92 | } |
93 | ||
ed751dc2 DM |
94 | #[api( |
95 | protected: true, | |
96 | input: { | |
97 | properties: { | |
98 | node: { | |
99 | schema: NODE_SCHEMA, | |
100 | }, | |
101 | command: { | |
102 | type: NodePowerCommand, | |
103 | }, | |
104 | } | |
105 | }, | |
106 | access: { | |
107 | permission: &Permission::Privilege(&["system", "status"], PRIV_SYS_POWER_MANAGEMENT, false), | |
108 | }, | |
109 | )] | |
110 | /// Reboot or shutdown the node. | |
111 | fn reboot_or_shutdown(command: NodePowerCommand) -> Result<(), Error> { | |
112 | ||
113 | let systemctl_command = match command { | |
114 | NodePowerCommand::Reboot => "reboot", | |
115 | NodePowerCommand::Shutdown => "poweroff", | |
116 | }; | |
117 | ||
cbef49bf | 118 | let output = Command::new("systemctl") |
ed751dc2 DM |
119 | .arg(systemctl_command) |
120 | .output() | |
121 | .map_err(|err| format_err!("failed to execute systemctl - {}", err))?; | |
122 | ||
123 | if !output.status.success() { | |
124 | match output.status.code() { | |
125 | Some(code) => { | |
126 | let msg = String::from_utf8(output.stderr) | |
127 | .map(|m| if m.is_empty() { String::from("no error message") } else { m }) | |
128 | .unwrap_or_else(|_| String::from("non utf8 error message (suppressed)")); | |
129 | bail!("diff failed with status code: {} - {}", code, msg); | |
130 | } | |
131 | None => bail!("systemctl terminated by signal"), | |
132 | } | |
133 | } | |
134 | Ok(()) | |
135 | } | |
2337df7b | 136 | |
2337df7b | 137 | pub const ROUTER: Router = Router::new() |
bfcef26a | 138 | .get(&API_METHOD_GET_STATUS) |
ed751dc2 | 139 | .post(&API_METHOD_REBOOT_OR_SHUTDOWN); |