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