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