]> git.proxmox.com Git - pmg-api.git/blob - PMG/CLI/pmgreport.pm
c41de8ca98337e4b445fb5b127b19003fb4a92b8
[pmg-api.git] / PMG / CLI / pmgreport.pm
1 package PMG::CLI::pmgreport;
2
3 use strict;
4 use Data::Dumper;
5 use Template;
6 use POSIX qw(strftime);
7 use HTML::Entities;
8
9 use PVE::INotify;
10 use PVE::CLIHandler;
11
12 use PMG::Utils;
13 use PMG::Config;
14 use PMG::RESTEnvironment;
15
16 use PMG::API2::Nodes;
17 use PMG::ClusterConfig;
18 use PMG::Cluster;
19 use PMG::API2::Cluster;
20 use PMG::RuleDB;
21 use PMG::DBTools;
22 use PMG::Statistic;
23
24 use base qw(PVE::CLIHandler);
25
26 my $nodename = PVE::INotify::nodename();
27
28 sub setup_environment {
29 PMG::RESTEnvironment->setup_default_cli_env();
30
31 my $rpcenv = PMG::RESTEnvironment->get();
32 # API /config/cluster/nodes need a ticket to connect to other nodes
33 my $ticket = PMG::Ticket::assemble_ticket('root@pam');
34 $rpcenv->set_ticket($ticket);
35 }
36
37 my $get_system_table_data = sub {
38
39 my $ni = PMG::API2::NodeInfo->status({ node => $nodename });
40
41 my $data = [];
42
43 push @$data, { text => 'Hostname', value => $nodename };
44
45 my $uptime = $ni->{uptime} ? PMG::Utils::format_uptime($ni->{uptime}) : '-';
46
47 push @$data, { text => 'Uptime', value => $uptime };
48
49 push @$data, { text => 'Version', value => $ni->{pmgversion} };
50
51 my $loadavg15 = '-';
52 if (my $d = $ni->{loadavg}) {
53 $loadavg15 = sprintf("%.2f", $d->[2]);
54 }
55 push @$data, { text => 'Load', value => $loadavg15 };
56
57 my $mem = '-';
58 if (my $d = $ni->{memory}) {
59 $mem = sprintf("%.2f%%", $d->{used}*100/$d->{total});
60 }
61 push @$data, { text => 'Memory', value => $mem };
62
63 my $disk = '-';
64 if (my $d = $ni->{rootfs}) {
65 $disk = sprintf("%.2f%%", $d->{used}*100/$d->{total});
66 }
67 push @$data, { text => 'Disk', value => $disk };
68
69 return $data
70 };
71
72 my $get_cluster_table_data = sub {
73
74 my $res = PMG::API2::Cluster->status({});
75 return undef if !scalar(@$res);
76
77 my $data = [];
78
79 foreach my $ni (@$res) {
80 my $state = 'A';
81 $state = 'S' if !$ni->{insync};
82
83 if (my $err = $ni->{conn_error}) {
84 $err =~ s/\n/ /g;
85 $state = encode_entities("ERROR: $err");
86 }
87
88 my $loadavg1 = '-';
89 if (my $d = $ni->{loadavg}) {
90 $loadavg1 = sprintf("%.2f", $d->[0]);
91 }
92
93 my $memory = '-';
94 if (my $d = $ni->{memory}) {
95 $memory = sprintf("%.2f%%", $d->{used}*100/$d->{total});
96 }
97
98 my $disk = '-';
99 if (my $d = $ni->{rootfs}) {
100 $disk = sprintf("%.2f%%", $d->{used}*100/$d->{total});
101 }
102
103 push @$data, {
104 hostname => $ni->{name},
105 ip => $ni->{ip},
106 type => $ni->{type},
107 state => $state,
108 loadavg1 => $loadavg1,
109 memory => $memory,
110 disk => $disk,
111 };
112 };
113
114 return $data;
115 };
116
117 my $get_incoming_table_data = sub {
118 my ($totals) = @_;
119
120 my $data = [];
121
122 push @$data, {
123 text => 'Incoming Mails',
124 value => $totals->{count_in},
125 percentage => $totals->{count_in_per},
126 };
127
128 push @$data, {
129 text => 'Spam Mails',
130 value => $totals->{spamcount_in},
131 percentage => $totals->{spamcount_in_per},
132 };
133
134 push @$data, {
135 text => 'Virus Mails',
136 value => $totals->{viruscount_in},
137 percentage => $totals->{viruscount_in_per},
138 };
139
140 push @$data, {
141 text => 'SPF rejects',
142 value => $totals->{spfcount},
143 percentage => $totals->{spfcount_per},
144 };
145
146 push @$data, {
147 text => 'Mail Traffic',
148 value => sprintf ("%.3f MByte", $totals->{traffic_in}),
149 };
150
151 return $data;
152 };
153
154 my $get_outgoing_table_data = sub {
155 my ($totals) = @_;
156
157 my $data = [];
158
159 push @$data, {
160 text => 'Outgoing Mails',
161 value => $totals->{count_out},
162 percentage => $totals->{count_out_per},
163 };
164
165 push @$data, {
166 text => 'Bounces',
167 value => $totals->{bounces_out},
168 percentage => $totals->{bounces_out_per},
169 };
170
171 push @$data, {
172 text => 'Mail Traffic',
173 value => sprintf ("%.3f MByte", $totals->{traffic_out}),
174 };
175
176 return $data;
177 };
178
179 my $get_virus_table_data = sub {
180 my ($virusinfo) = @_;
181
182 my $data = [];
183
184 foreach my $entry (@$virusinfo) {
185 next if !$entry->{count};
186 last if scalar(@$data) >= 10;
187 push @$data, { name => $entry->{name}, count => $entry->{count} };
188 }
189
190 return undef if !scalar(@$data);
191
192 return $data;
193 };
194
195 my $get_quarantine_table_data = sub {
196 my ($dbh, $qtype) = @_;
197
198 my $ref = PMG::DBTools::get_quarantine_count($dbh, $qtype);
199
200 return undef if !($ref && $ref->{count});
201
202 my $data = [];
203
204 push @$data, {
205 text => "Quarantine Size (MBytes)",
206 value => int($ref->{mbytes}),
207 };
208
209 push @$data, {
210 text => "Number of Mails",
211 value => $ref->{count},
212 };
213
214 push @$data, {
215 text => "Average Size (Bytes)",
216 value => int($ref->{avgbytes}),
217 };
218
219 if ($qtype eq 'S') {
220 push @$data, {
221 text => "Average Spam Level",
222 value => int($ref->{avgspam}),
223 };
224 }
225
226 return $data;
227 };
228
229 __PACKAGE__->register_method ({
230 name => 'pmgreport',
231 path => 'pmgreport',
232 method => 'POST',
233 description => "Generate and send daily system report email.",
234 parameters => {
235 additionalProperties => 0,
236 properties => {
237 debug => {
238 description => "Debug mode. Print raw email to stdout instead of sending them.",
239 type => 'boolean',
240 optional => 1,
241 default => 0,
242 },
243 auto => {
244 description => "Auto mode. Use setting from system configuration (set when invoked fron cron).",
245 type => 'boolean',
246 optional => 1,
247 default => 0,
248 },
249 receiver => {
250 description => "Send report to this email address. Default is the administratior email address.",
251 type => 'string', format => 'email',
252 optional => 1,
253 },
254 timespan => {
255 description => "Select time span for included email statistics.\n\nNOTE: System and cluster performance data is always from current time (when script is run).",
256 type => 'string',
257 enum => ['today', 'yesterday'],
258 default => 'today',
259 optional => 1,
260 },
261 },
262 },
263 returns => { type => 'null'},
264 code => sub {
265 my ($param) = @_;
266
267 my $timespan = $param->{timespan} // 'today';
268 my ($start, $end) = PMG::Utils::lookup_timespan($timespan);
269
270 my $fqdn = PVE::Tools::get_fqdn($nodename);
271
272 my $vars = {
273 hostname => $nodename,
274 fqdn => $fqdn,
275 date => strftime("%F", localtime($end - 1)),
276 };
277
278 my $cinfo = PMG::ClusterConfig->new();
279 my $role = $cinfo->{local}->{type} // '-';
280
281 if ($role eq '-') {
282 $vars->{system} = $get_system_table_data->();
283 } else {
284 $vars->{cluster} = $get_cluster_table_data->();
285 if ($role eq 'master') {
286 # OK
287 } else {
288 return undef if $param->{auto}; # silent exit - do not send report
289 }
290 }
291
292
293 my $stat = PMG::Statistic->new ($start, $end);
294 my $rdb = PMG::RuleDB->new();
295
296 # update statistics
297 PMG::Statistic::update_stats($rdb->{dbh}, $cinfo);
298
299 my $totals = $stat->total_mail_stat($rdb);
300
301 $vars->{incoming} = $get_incoming_table_data->($totals);
302
303 $vars->{outgoing} = $get_outgoing_table_data->($totals);
304
305 my $virusinfo = $stat->total_virus_stat ($rdb);
306 if (my $data = $get_virus_table_data->($virusinfo)) {
307 $vars->{virusstat} = $data;
308 }
309
310 if (my $data = $get_quarantine_table_data->($rdb->{dbh}, 'V')) {
311 $vars->{virusquar} = $data;
312 }
313
314 if (my $data = $get_quarantine_table_data->($rdb->{dbh}, 'S')) {
315 $vars->{spamquar} = $data;
316 }
317
318 my $tt = PMG::Config::get_template_toolkit();
319
320 my $cfg = PMG::Config->new();
321 my $email = $param->{receiver} // $cfg->get ('admin', 'email');
322
323 if (!defined($email)) {
324 return undef if $param->{auto}; # silent exit - do not send report
325 die "no receiver configured\n";
326 }
327
328 my $mailfrom = "Proxmox Mail Gateway <postmaster>";
329 PMG::Utils::finalize_report($tt, 'pmgreport.tt', $vars, $mailfrom, $email, $param->{debug});
330
331 return undef;
332 }});
333
334 our $cmddef = [ __PACKAGE__, 'pmgreport', [], undef ];
335
336 1;