]> git.proxmox.com Git - proxmox-backup.git/blob - src/api2/node/status.rs
move more tools for the client into subcrates
[proxmox-backup.git] / src / api2 / node / status.rs
1 use std::process::Command;
2 use std::path::Path;
3
4 use anyhow::{Error, format_err, bail};
5 use serde_json::Value;
6
7 use proxmox::sys::linux::procfs;
8
9 use proxmox::api::{api, ApiMethod, Router, RpcEnvironment, Permission};
10
11 use pbs_tools::cert::CertInfo;
12
13 use crate::api2::types::*;
14 use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_SYS_POWER_MANAGEMENT};
15
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
26 #[api(
27 input: {
28 properties: {
29 node: {
30 schema: NODE_SCHEMA,
31 },
32 },
33 },
34 returns: {
35 type: NodeStatus,
36 },
37 access: {
38 permission: &Permission::Privilege(&["system", "status"], PRIV_SYS_AUDIT, false),
39 },
40 )]
41 /// Read node memory, CPU and (root) disk usage
42 fn get_status(
43 _param: Value,
44 _info: &ApiMethod,
45 _rpcenv: &mut dyn RpcEnvironment,
46 ) -> Result<NodeStatus, Error> {
47 let meminfo: procfs::ProcFsMemInfo = procfs::read_meminfo()?;
48 let memory = NodeMemoryCounters {
49 total: meminfo.memtotal,
50 used: meminfo.memused,
51 free: meminfo.memfree,
52 };
53
54 let swap = NodeSwapCounters {
55 total: meminfo.swaptotal,
56 used: meminfo.swapused,
57 free: meminfo.swapfree,
58 };
59
60 let kstat: procfs::ProcFsStat = procfs::read_proc_stat()?;
61 let cpu = kstat.cpu;
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 );
77
78 Ok(NodeStatus {
79 memory,
80 swap,
81 root: crate::tools::disks::disk_usage(Path::new("/"))?,
82 uptime: procfs::read_proc_uptime()?.0 as u64,
83 loadavg,
84 kversion,
85 cpuinfo,
86 cpu,
87 wait,
88 info: NodeInformation {
89 fingerprint: CertInfo::new()?.fingerprint()?,
90 },
91 })
92 }
93
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
118 let output = Command::new("systemctl")
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 }
136
137 pub const ROUTER: Router = Router::new()
138 .get(&API_METHOD_GET_STATUS)
139 .post(&API_METHOD_REBOOT_OR_SHUTDOWN);