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