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