]> git.proxmox.com Git - proxmox-backup.git/blame - src/server/report.rs
report: add extra newline between files of directory output
[proxmox-backup.git] / src / server / report.rs
CommitLineData
0b9614d5 1use std::fmt::Write;
b0ef9631
HL
2use std::path::Path;
3use std::process::Command;
4
5736fa91
TL
5fn get_top_processes() -> String {
6 let (exe, args) = ("top", vec!["-b", "-c", "-w512", "-n", "1", "-o", "TIME"]);
7 let output = Command::new(exe)
8 .args(&args)
9 .output();
10 let output = match output {
11 Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(),
12 Err(err) => err.to_string(),
13 };
14 let output = output.lines().take(30).collect::<Vec<&str>>().join("\n");
15 format!("$ `{exe} {}`\n```\n{output}\n```", args.join(" "))
16}
17
eb32373e 18fn files() -> Vec<(&'static str, Vec<&'static str>)> {
93f077c5 19 vec![
eb32373e 20 (
2f7b3e2e
TL
21 "General System Info",
22 vec![
23 "/etc/hostname",
24 "/etc/hosts",
25 "/etc/network/interfaces",
26 "/etc/apt/sources.list",
27 "/etc/apt/sources.list.d/",
49d465c7 28 "/proc/pressure/",
2f7b3e2e 29 ],
eb32373e
TL
30 ),
31 (
32 "Datastores & Remotes",
33 vec!["/etc/proxmox-backup/datastore.cfg"],
34 ),
35 (
36 "User & Access",
37 vec![
38 "/etc/proxmox-backup/user.cfg",
39 "/etc/proxmox-backup/acl.cfg",
40 ],
41 ),
42 ("Remotes", vec!["/etc/proxmox-backup/remote.cfg"]),
43 (
44 "Jobs",
45 vec![
46 "/etc/proxmox-backup/sync.cfg",
47 "/etc/proxmox-backup/verification.cfg",
48 ],
49 ),
50 (
51 "Tape",
52 vec![
53 "/etc/proxmox-backup/tape.cfg",
54 "/etc/proxmox-backup/media-pool.cfg",
55 ],
56 ),
57 (
58 "Others",
59 vec![
60 "/etc/proxmox-backup/node.cfg",
61 "/etc/proxmox-backup/traffic-control.cfg",
62 ],
63 ),
93f077c5
TL
64 ]
65}
b0ef9631 66
93f077c5
TL
67fn commands() -> Vec<(&'static str, Vec<&'static str>)> {
68 vec![
ee0ea735 69 // ("<command>", vec![<arg [, arg]>])
a1a9fdd8 70 ("date", vec!["-R"]),
c100fe91 71 ("proxmox-backup-manager", vec!["versions", "--verbose"]),
b2fc573a 72 ("proxmox-backup-manager", vec!["subscription", "get"]),
11c4632b
TL
73 ("proxmox-backup-manager", vec!["ldap", "list"]),
74 ("proxmox-backup-manager", vec!["openid", "list"]),
3b5cb8fd 75 ("proxmox-boot-tool", vec!["status"]),
93f077c5 76 ("df", vec!["-h"]),
84fb190e
TL
77 (
78 "lsblk",
79 vec![
80 "--ascii",
81 "-M",
82 "-o",
83 "+HOTPLUG,ROTA,PHY-SEC,FSTYPE,MODEL,TRAN",
84 ],
85 ),
bb9e5039 86 ("ls", vec!["-l", "/dev/disk/by-id", "/dev/disk/by-path"]),
2f08ee1f
TL
87 ("zpool", vec!["status"]),
88 ("zfs", vec!["list"]),
fcc8e354 89 ("arcstat", vec![]),
93f077c5
TL
90 ]
91}
b0ef9631 92
432fe441
FG
93// (description, function())
94type FunctionMapping = (&'static str, fn() -> String);
95
96fn function_calls() -> Vec<FunctionMapping> {
49d465c7
TL
97 vec![
98 ("Datastores", || {
99 let config = match pbs_config::datastore::config() {
100 Ok((config, _digest)) => config,
101 _ => return String::from("could not read datastore config"),
102 };
b0ef9631 103
49d465c7
TL
104 let mut list = Vec::new();
105 for store in config.sections.keys() {
106 list.push(store.as_str());
107 }
108 list.join(", ")
109 }),
5736fa91 110 ("System Load & Uptime", get_top_processes),
49d465c7 111 ]
b0ef9631
HL
112}
113
0b9614d5 114fn get_file_content(file: impl AsRef<Path>) -> String {
25877d05 115 use proxmox_sys::fs::file_read_optional_string;
0b9614d5
TL
116 let content = match file_read_optional_string(&file) {
117 Ok(Some(content)) => content,
118 Ok(None) => String::from("# file does not exist"),
119 Err(err) => err.to_string(),
120 };
121 let file_name = file.as_ref().display();
122 format!("`$ cat '{file_name}'`\n```\n{}\n```", content.trim_end())
123}
124
125fn get_directory_content(path: impl AsRef<Path>) -> String {
126 let read_dir_iter = match std::fs::read_dir(&path) {
127 Ok(iter) => iter,
128 Err(err) => {
129 return format!(
130 "`$ cat '{}*'`\n```\n# read dir failed - {}\n```",
131 path.as_ref().display(),
132 err.to_string(),
133 );
134 }
135 };
136 let mut out = String::new();
460c3d16 137 let mut first = true;
0b9614d5
TL
138 for entry in read_dir_iter {
139 let entry = match entry {
140 Ok(entry) => entry,
141 Err(err) => {
142 let _ = writeln!(out, "error during read-dir - {}", err.to_string());
143 continue;
144 }
145 };
146 let path = entry.path();
147 if path.is_file() {
460c3d16
TL
148 if first {
149 let _ = writeln!(out, "{}", get_file_content(path));
150 first = false;
151 } else {
152 let _ = writeln!(out, "\n{}", get_file_content(path));
153 }
0b9614d5
TL
154 } else {
155 let _ = writeln!(out, "skipping sub-directory `{}`", path.display());
156 }
157 }
158 out
159}
b0ef9631 160
c55884d1
TL
161fn get_command_output(exe: &str, args: &Vec<&str>) -> String {
162 let output = Command::new(exe)
163 .env("PROXMOX_OUTPUT_NO_BORDER", "1")
164 .args(args)
165 .output();
166 let output = match output {
12217941
TL
167 Ok(output) => {
168 let mut out = String::from_utf8_lossy(&output.stdout)
169 .trim_end()
170 .to_string();
171 let stderr = String::from_utf8_lossy(&output.stderr)
172 .trim_end()
173 .to_string();
174 if !stderr.is_empty() {
164f96a5 175 let _ = writeln!(out, "\n```\nSTDERR:\n```\n{stderr}");
12217941
TL
176 }
177 out
178 }
c55884d1
TL
179 Err(err) => err.to_string(),
180 };
c55884d1
TL
181 format!("$ `{exe} {}`\n```\n{output}\n```", args.join(" "))
182}
183
0b9614d5 184pub fn generate_report() -> String {
93f077c5 185 let file_contents = files()
b0ef9631 186 .iter()
eb32373e
TL
187 .map(|group| {
188 let (group, files) = group;
189 let group_content = files
190 .iter()
191 .map(|file_name| {
0b9614d5
TL
192 let path = Path::new(file_name);
193 if path.is_dir() {
194 get_directory_content(&path)
195 } else {
196 get_file_content(file_name)
197 }
eb32373e
TL
198 })
199 .collect::<Vec<String>>()
200 .join("\n\n");
201
202 format!("### {group}\n\n{group_content}")
b0ef9631
HL
203 })
204 .collect::<Vec<String>>()
205 .join("\n\n");
206
93f077c5 207 let command_outputs = commands()
b0ef9631 208 .iter()
c55884d1 209 .map(|(command, args)| get_command_output(command, args))
b0ef9631
HL
210 .collect::<Vec<String>>()
211 .join("\n\n");
212
93f077c5 213 let function_outputs = function_calls()
b0ef9631 214 .iter()
20875de2
TL
215 .map(|(desc, function)| {
216 let output = function();
217 format!("#### {desc}\n```\n{}\n```", output.trim_end())
218 })
b0ef9631
HL
219 .collect::<Vec<String>>()
220 .join("\n\n");
221
222 format!(
20875de2 223 "## FILES\n\n{file_contents}\n## COMMANDS\n\n{command_outputs}\n## FUNCTIONS\n\n{function_outputs}\n"
b0ef9631
HL
224 )
225}