]> git.proxmox.com Git - pve-manager.git/blob - PVE/Report.pm
c9109dcaca09d9a359ac46e9baca500a0045d07b
[pve-manager.git] / PVE / Report.pm
1 package PVE::Report;
2
3 use strict;
4 use warnings;
5
6 use PVE::Tools;
7
8 # output the content of all the files of a directory
9 my sub dir2text {
10 my ($target_dir, $regexp) = @_;
11
12 print STDERR "dir2text '${target_dir}${regexp}'...";
13 my $text = '';
14 PVE::Tools::dir_glob_foreach($target_dir, $regexp, sub {
15 my ($file) = @_;
16 $text .= "\n# cat $target_dir$file\n";
17 $text .= PVE::Tools::file_get_contents($target_dir.$file)."\n";
18 });
19 return $text;
20 }
21
22 # command -v is the posix equivalent of 'which'
23 my sub cmd_exists { system("command -v '$_[0]' > /dev/null 2>&1") == 0 }
24
25 my $init_report_cmds = sub {
26 my $report_def = {
27 general => {
28 title => 'general system info',
29 order => 10,
30 cmds => [
31 'hostname',
32 'date -R',
33 'pveversion --verbose',
34 'cat /etc/hosts',
35 'pvesubscription get',
36 'cat /etc/apt/sources.list',
37 sub { dir2text('/etc/apt/sources.list.d/', '.*list') },
38 sub { dir2text('/etc/apt/sources.list.d/', '.*sources') },
39 'lscpu',
40 'pvesh get /cluster/resources --type node --output-format=yaml',
41 ],
42 },
43 'system-load' => {
44 title => 'overall system load info',
45 order => 20,
46 cmds => [
47 'top -b -c -w512 -n 1 -o TIME | head -n 30',
48 'head /proc/pressure/*',
49 ],
50 },
51 storage => {
52 order => 30,
53 cmds => [
54 'cat /etc/pve/storage.cfg',
55 'pvesm status',
56 'cat /etc/fstab',
57 'findmnt --ascii',
58 'df --human -T',
59 'proxmox-boot-tool status',
60 ],
61 },
62 'virtual guests' => {
63 order => 40,
64 cmds => [
65 'qm list',
66 sub { dir2text('/etc/pve/qemu-server/', '\d.*conf') },
67 'pct list',
68 sub { dir2text('/etc/pve/lxc/', '\d.*conf') },
69 ],
70 },
71 network => {
72 order => 45,
73 cmds => [
74 'ip -details -statistics address',
75 'ip -details -4 route show',
76 'ip -details -6 route show',
77 'cat /etc/network/interfaces',
78 ],
79 },
80 firewall => {
81 order => 50,
82 cmds => [
83 sub { dir2text('/etc/pve/firewall/', '.*fw') },
84 'cat /etc/pve/local/host.fw',
85 'iptables-save',
86 ],
87 },
88 cluster => {
89 order => 60,
90 cmds => [
91 'pvecm nodes',
92 'pvecm status',
93 'cat /etc/pve/corosync.conf 2>/dev/null',
94 'ha-manager status',
95 'cat /etc/pve/datacenter.cfg',
96 ],
97 },
98 hardware => {
99 order => 70,
100 cmds => [
101 'dmidecode -t bios',
102 'lspci -nnk',
103 ],
104 },
105 'block devices' => {
106 order => 80,
107 cmds => [
108 'lsblk --ascii -M -o +HOTPLUG,ROTA,PHY-SEC,FSTYPE,MODEL,TRAN',
109 'ls -l /dev/disk/by-*/',
110 'iscsiadm -m node',
111 'iscsiadm -m session',
112 ],
113 },
114 volumes => {
115 order => 90,
116 cmds => [
117 'pvs',
118 'lvs',
119 'vgs',
120 ],
121 },
122 };
123
124 if (cmd_exists('zfs')) {
125 push @{$report_def->{volumes}->{cmds}},
126 'zpool status',
127 'zpool list -v',
128 'zfs list',
129 'arcstat',
130 ;
131 }
132
133 if (-e '/etc/ceph/ceph.conf') {
134 push @{$report_def->{volumes}->{cmds}},
135 'pveceph status',
136 'ceph osd status',
137 'ceph df',
138 'ceph osd df tree',
139 'ceph device ls',
140 'cat /etc/ceph/ceph.conf',
141 'ceph config dump',
142 'pveceph pool ls',
143 'ceph versions',
144 'ceph health detail',
145 ;
146 }
147
148 if (cmd_exists('multipath')) {
149 push @{$report_def->{disks}->{cmds}},
150 'cat /etc/multipath.conf',
151 'cat /etc/multipath/wwids',
152 'multipath -ll',
153 ;
154 }
155
156 return $report_def;
157 };
158
159 sub generate {
160 my $def = $init_report_cmds->();
161
162 my $report = '';
163 my $record_output = sub {
164 $report .= shift . "\n";
165 };
166
167 local $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
168 my $cmd_timeout = 10; # generous timeout
169
170 my $run_cmd_params = {
171 outfunc => $record_output,
172 errfunc => $record_output,
173 timeout => $cmd_timeout,
174 noerr => 1, # avoid checking programs exit code
175 };
176
177 my $sorter = sub { ($def->{$_[0]}->{order} // 1<<30) <=> ($def->{$_[1]}->{order} // 1<<30) };
178
179 for my $section ( sort { $sorter->($a, $b) } keys %$def) {
180 my $s = $def->{$section};
181 my $title = $s->{title} // "info about $section";
182
183 $report .= "\n==== $title ====\n";
184 for my $command (@{$s->{cmds}}) {
185 eval {
186 if (ref $command eq 'CODE') {
187 $report .= PVE::Tools::run_with_timeout($cmd_timeout, $command);
188 } else {
189 print STDERR "Process ".$command."...";
190 $report .= "\n# $command\n";
191 PVE::Tools::run_command($command, %$run_cmd_params);
192 }
193 print STDERR "OK";
194 };
195 print STDERR "\n";
196 $report .= "\nERROR: $@\n" if $@;
197 }
198 }
199
200 return $report;
201 }
202
203 1;