munin-contrib/plugins/jchkmail/jchkmail_counters_

662 lines
18 KiB
Perl
Executable File

#!/usr/bin/perl
# -*- perl -*-
=pod
=encoding UTF-8
=head1 NAME
jchkmail_counters_
=head1 APPLICABLE SYSTEMS
MTAs running j-chkmail mail filter - http://www.j-chkmail.org
=head1 CONFIGURATION
[jchkmail_counters_*]
user root
The following environment variables may be defined :
# disable some instances of the plugin
env.disable no
=head1 BUGS/GOTCHAS
None known, but some improvements to do...
=head1 AUTHOR
Jose-Marcio Martins da Cruz - mailto:Jose-Marcio.Martins@mines-paristech.fr
Ecole Nationale Superieure des Mines de Paris
This plugin was inspired by the work of Christophe Decor
Christophe Decor - mailto:decor@supagro.inra.fr
INRA - Institut National de Recherche Agronomique
=head1 COPYRIGHT
Copyright Jose-Marcio Martins da Cruz
=head1 VERSION
1.0 - Feb 2014
=head1 MAGIC MARKERS
#%# family=contrib
#%# capabilities=autoconf suggest
=cut
use strict;
use warnings;
use lib $ENV{'MUNIN_LIBDIR'};
use Munin::Plugin;
my $SMCF = "/etc/mail/jchkmail/j-chkmail.cf";
my $FSTATS = "/var/jchkmail/files/j-stats";
my $WORKDIR = "/var/jchkmail/wdb";
my $CONSTDIR = "/var/jchkmail/cdb";
# ------------------------------------------------
#
#
my $debug = 0;
my $AppName = $0;
$AppName = `basename $AppName`;
chomp $AppName;
my $AppExt = undef;
if ($AppName =~ /_([a-z0-9.-]+)$/i) {
$AppExt = $1;
}
my $disable = defined $ENV{'disable'} && $ENV{'disable'} =~ /yes|oui/i;
my %StaticData = ();
GetStaticData(\%StaticData);
my $AppType = "";
if (defined $AppExt && exists $StaticData{config}{"$AppExt+type"}) {
$AppType = $StaticData{config}{"$AppExt+type"};
}
# valider results from Munin::Plugin
# statefile is used only by jchkmail_counters_ratiospamham
my $StateFile = undef;
if (exists $ENV{MUNIN_PLUGSTATE} && defined $ENV{MUNIN_PLUGSTATE}) {
$StateFile = "$ENV{MUNIN_PLUGSTATE}/$AppName.state-joe";
}
if (exists $ENV{statefile} && defined $ENV{statefile}) {
$StateFile = $ENV{statefile};
}
# ------------------------------------------------
#
#
if ($#ARGV >= 0 && $ARGV[0] eq "autoconf") {
unless (-f $SMCF) {
print "no\n";
exit 0;
}
print "yes\n";
exit 0;
}
# ------------------------------------------------
#
#
if ($#ARGV >= 0 && $ARGV[0] eq "suggest") {
# results from /var/jchkmail/files/j-stats
if (-f $SMCF && -f $FSTATS) {
my %Data = ();
my $line = GetStatsLine(\%Data);
exit 1 unless defined $line && $line ne "";
foreach my $k (keys %{$StaticData{config}}) {
next unless $k =~ /(.*)[+]type$/;
my $graph = $1;
my $type = $StaticData{config}{$k};
if ($type =~ /^(counters|ratio)$/) {
next unless exists $StaticData{config}{$graph . "+data"};
my $data = $StaticData{config}{$graph . "+data"};
$data =~ tr#,/# #;
my @fields = split " ", $data;
my $ok = 1;
my $bad = "";
for my $f (@fields) {
unless (exists $Data{$f}) {
$ok = 0;
$bad .= " $f";
}
}
if ($ok) {
print "$graph\n" if $ok;
} else {
printf "# %-12s - Lacking fields : %s\n", $graph, $bad;
}
}
}
}
# results from /var/jchkmail/wdb
if (-f $SMCF && (-d $WORKDIR || -d $CONSTDIR)) {
foreach my $k (keys %{$StaticData{config}}) {
next unless $k =~ /(.*)[+]type$/;
my $graph = $1;
my $type = $StaticData{config}{$k};
if ($type =~ /^(dbcounters)$/) {
next unless exists $StaticData{config}{$graph . "+data"};
my $data = $StaticData{config}{$graph . "+data"};
$data =~ tr#,/# #;
my @fields = split " ", $data;
my $ok = 1;
my $bad = "";
for my $f (@fields) {
unless (-f "$WORKDIR/$f.db" || -f "$CONSTDIR/$f.db") {
$ok = 0;
$bad .= " $f";
}
}
if ($ok) {
print "$graph\n" if $ok;
} else {
printf "# %-12s - Lacking fields : %s\n", $graph, $bad;
}
}
}
}
exit 0;
}
# ------------------------------------------------
#
#
if (defined $AppExt && $AppType eq "counters") {
my %Data = ();
my $line = GetStatsLine(\%Data);
exit 1 unless defined $line && $line ne "";
my $DataAggregate = $StaticData{config}{"$AppExt+data"};
printf "# Data Aggregate : %s\n", $DataAggregate;
my @Values = split " ", $DataAggregate;
if ($#ARGV >= 0 && $ARGV[0] eq "config") {
my @Keys = grep {$_ =~ /^$AppExt-/;} keys %{$StaticData{config}};
foreach my $k (@Keys) {
my $v = $StaticData{config}{$k};
$k =~ s/^$AppExt-//;
printf "%-16s %s\n", $k, $v;
}
printf "graph_order %s\n", (join " ", sort @Values);
foreach my $k (@Values) {
if (exists $Data{$k}) {
my $label = $k;
if (exists $StaticData{label}{$k}) {
$label = $StaticData{label}{$k};
}
printf "%s.label %s\n", $k, $label;
if (exists $StaticData{label}{"$k.info"}) {
$label = $StaticData{label}{"$k.info"};
}
printf "%s.info %s\n", $k, $label;
printf "%s.min 0\n", $k;
printf "%s.type DERIVE\n", $k;
}
}
exit 0;
}
if ($#ARGV < 0) {
foreach my $k (@Values) {
if (exists $Data{$k}) {
printf "%s.value %s\n", $k, $Data{$k};
}
}
exit 0;
}
exit 1;
}
# ------------------------------------------------
#
#
if (defined $AppExt && $AppType eq "ratio") {
my %Data = ();
my $line = GetStatsLine(\%Data);
exit 1 unless defined $line && $line ne "";
my $DataAggregate = $StaticData{config}{"$AppExt+data"};
printf "# Data Aggregate : %s\n", $DataAggregate;
my ($Ratios, $Base, undef) = split "/", $DataAggregate;
exit 1 unless defined $Ratios && defined $Base;
my @Values = split ",", $Ratios;
my @Den = split "[ ]+", $Base;
if ($#ARGV >= 0 && $ARGV[0] eq "config") {
my @Keys = grep {$_ =~ /^$AppExt-/;} keys %{$StaticData{config}};
foreach my $k (@Keys) {
my $v = $StaticData{config}{$k};
$k =~ s/^$AppExt-//;
printf "%-16s %s\n", $k, $v;
}
my $nr = 0;
printf "graph_order %s\n", (join " ", sort @Values);
foreach my $num (@Values) {
printf "# running for %s\n", $num;
my @Num = split "[ ]+", $num;
my $k = sprintf "%s%02d", $AppExt, $nr++;
my $label = $k;
if (exists $StaticData{label}{$k}) {
$label = $StaticData{label}{$k};
}
printf "%s.label %s\n", $k, $label;
if (exists $StaticData{label}{"$k.info"}) {
$label = $StaticData{label}{"$k.info"};
}
printf "%s.info %s\n", $k, $label;
printf "%s.min 0\n", $k;
printf "%s.max 100\n", $k;
printf "%s.type GAUGE\n", $k;
}
exit 0;
}
if ($#ARGV < 0) {
my %Old = ();
my %Diff = ();
if (EvalBoolean($StaticData{config}{"$AppExt+state"})) {
ReadState(\%Old);
%Diff = ();
DiffFromState(\%Old, \%Data, \%Diff);
} else {
%Diff = %Data;
}
if ($debug) {
foreach my $k (sort keys %Data) {
printf "# DIFF %-16s %12d %12d %12d\n", $k, $Old{$k}, $Data{$k},
$Diff{$k};
}
}
my $nr = 0;
foreach my $num (@Values) {
printf "# running for %s\n", $num;
my @Num = split "[ ]+", $num;
my $n = 0;
my $d = 0;
foreach my $k (@Num) {
if (exists $Diff{$k}) {
$n += $Diff{$k};
}
}
foreach my $k (@Den) {
if (exists $Diff{$k}) {
$d += $Diff{$k};
}
}
my $k = sprintf "%s%02d", $AppExt, $nr++;
printf "%s.value %s\n", $k, $d > 0 ? ((100. * $n) / $d) : 0;
}
if (EvalBoolean($StaticData{config}{"$AppExt+state"})) {
SaveState(\%Data);
}
exit 0;
}
exit 1;
}
# ------------------------------------------------
#
#
if (defined $AppExt && $AppType eq "dbcounters") {
my $DataAggregate = $StaticData{config}{"$AppExt+data"};
printf "# Data Aggregate : %s\n", $DataAggregate;
my @Values = split " ", $DataAggregate;
if ($#ARGV >= 0 && $ARGV[0] eq "config") {
my @Keys = grep {$_ =~ /^$AppExt-/;} keys %{$StaticData{config}};
foreach my $k (@Keys) {
my $v = $StaticData{config}{$k};
$k =~ s/^$AppExt-//;
printf "%-16s %s\n", $k, $v;
}
printf "graph_order %s\n", (join " ", sort @Values);
foreach my $k (@Values) {
if (1) {
my $label = $k;
if (exists $StaticData{label}{$k}) {
$label = $StaticData{label}{$k};
}
printf "%s.label %s\n", $k, $label;
if (exists $StaticData{label}{"$k.info"}) {
$label = $StaticData{label}{"$k.info"};
}
printf "%s.info %s\n", $k, $label;
printf "%s.type GAUGE\n", $k;
}
}
exit 0;
}
if ($#ARGV < 0) {
foreach my $k (@Values) {
my $dbfile = "$WORKDIR/$k.db";
$dbfile = "$CONSTDIR/$k.db" unless -f $dbfile;
next unless -f $dbfile;
my @DATA = `j-makemap -c -b $dbfile`;
chomp @DATA;
my $nrec = 0;
foreach my $line (@DATA) {
if ($line =~ /\s+(\d+)\s+records/) {
$nrec = $1;
printf "%s.value %s\n", $k, $nrec;
last;
}
}
}
exit 0;
}
exit 1;
}
# ------------------------------------------------
#
#
sub GetStatsLine {
my ($h, undef) = @_;
return undef unless -f $FSTATS;
my $line = `tail -1 $FSTATS`;
return undef unless defined $line && $line ne "";
my @Values = split / /, $line;
my $time = time();
if ($Values[0] =~ /^\d+$/) {
$time = shift @Values;
}
$h->{timestamp} = $time;
foreach my $lx (@Values) {
$lx = lc $lx;
if ($lx =~ /(\S+)=\((\d+)\)/) {
$h->{$1} = $2;
}
}
return $line;
}
# ------------------------------------------------
#
#
sub ReadState {
my ($h, undef) = @_;
return 0 unless defined $h;
return 0 unless defined $StateFile && $StateFile ne "" && -f $StateFile;
open FSTATE, "< $StateFile" || return 0;
while (my $s = <FSTATE>) {
chomp $s;
next if $s =~ /^\s*$/;
next if $s =~ /^\s*#/;
my ($a, $b) = split " ", $s;
$b = 0 unless defined $b;
$h->{$a} = $b;
}
close FSTATE;
{
my %St = Munin::Plugin::restore_state();
foreach my $k (sort keys %St) {
printf "# %-20s %s\n", $k, $St{$k};
}
}
return 1;
}
# ------------------------------------------------
#
#
sub SaveState {
my ($h, undef) = @_;
return 0 unless defined $StateFile && $StateFile ne "";
my @ST = qw();
open FSTATE, "> $StateFile" || return 0;
foreach my $k (keys %{$h}) {
push @ST, (sprintf "%s %s\n", $k, $h->{$k});
printf FSTATE "%-20s %s\n", $k, $h->{$k};
}
close FSTATE;
Munin::Plugin::save_state(%$h);
return 1;
}
# ------------------------------------------------
#
#
sub DiffFromState {
my ($old, $new, $delta, undef) = @_;
return 0 unless defined $old && defined $new;
$delta = $new unless defined $delta;
return 0 unless exists $old->{timestamp};
return 0 unless exists $new->{timestamp};
my $dt = $new->{timestamp} - $old->{timestamp};
return 0 unless $dt > 1;
$delta->{timestamp} = $new->{timestamp};
foreach my $k (sort keys %{$new}) {
next if $k eq "timestamp";
if (exists $old->{$k}) {
$delta->{$k} = $new->{$k} - $old->{$k};
$delta->{$k} *= 300. / $dt;
} else {
$delta->{$k} = 0.;
}
}
return 1;
}
# ------------------------------------------------
#
#
sub GetStaticData {
my ($h, undef) = @_;
my @DATA = <DATA>;
my @SOPT = qw();
chomp @DATA;
my $in = 0;
my $key = "";
foreach my $s (@DATA) {
if ($key eq "") {
if ($s =~ /==\s+BEGIN\s+(\S+)\s+==/) {
$key = $1;
}
next;
}
if ($s =~ /==\s+END\s+(\S*\s+)?==/) {
$key = "";
next;
}
next if $s =~ /^\s*$/;
push @{$h->{$key}{_data_}}, $s;
my ($a, $b) = split " ", $s, 2;
next unless defined $a && $a ne "";
$b = "" unless defined $b;
$h->{$key}{$a} = $b;
}
}
# ------------------------------------------------
#
#
sub EvalBoolean {
my ($v, undef) = @_;
# return defined $v && $v =~ /^(1|yes|true|oui|vrai)$/i;
if (defined $v) {
return 1 if $v =~ /^(1|yes|true|oui|vrai)$/i;
}
return 0;
}
# ------------------------------------------------
#
#
__DATA__
== BEGIN label ==
conn Connections
conn.info SMTP Connections per time unit
msgs Messages
msgs.info Messages per time unit
rcpt Recipients
rcpt.info Recipients per time unit
greymsgs Greylisted Messages
greymsgs.info Greylisted Messages per time unit
greyrcpt Greylisted Recipients
greyrcpt.info Greylisted Recipients per time unit
bayesham Legitimate (Stat Filter)
bayesham.info Legitimate Messages - Statistical Filter
bayesspam Unwanted (Stat Filter)
bayesspam.info Unwanted Messages - Statistical Filter
matching Pattern Matching
matching.info Unwanted Messages - Pattern Matching Filter
urlbl URL Blacklist
urlbl.info Unwanted Messages - URL Blacklist
throttle Connection Rate
throttle.info Connections rejected by connection rate
badrcpt Bad Recipients
badrcpt.info Connections rejected - excessive bad recipients
localuser Internal addresses
localuser.info Messages sent to internal use only addresses
badmx Bad Sender MX
badmx.info Messages rejected - Bad Sender MX
rcptrate Recipient Rate
rcptrate.info Messages rejected by recipient rate
files Files
files.info Files
xfiles X-Files
xfiles.info X-Files
kbytes Volume (KBytes)
kbytes.info Volume (KBytes) per time unit
j-greyvalid Grey Validated records
j-greyvalid.info Grey Validated records
j-greypend Grey Waiting records
j-greypend.info Grey Waiting records
j-greywhitelist Grey Whitelisted records
j-greywhitelist.info Grey Whitelisted records
j-greyblacklist j-greyblacklist
j-greyblacklist.info j-greyblacklist
ratiospamham Ratio Messages
ratiospamham.info Ratio Messages
ratiospamham00 Legitimate messages
ratiospamham00.info Legitimate messages
ratiospamham01 Unwanted messages
ratiospamham01.info Unwanted messages
== END label ==
== BEGIN config ==
activity-graph_title j-chkmail - Filter activity
activity-graph_category mail
activity-graph_vlabel counts per second
activity-graph_scale no
activity+data conn msgs rcpt
activity+type counters
activity+enable yes
greylisting-graph_title j-chkmail - Greylisting activity
greylisting-graph_category mail
greylisting-graph_vlabel counts per second
greylisting-graph_scale no
greylisting+data greymsgs greyrcpt
greylisting+type counters
greylisting+enable yes
statfilter-graph_title j-chkmail - Statistical classification
statfilter-graph_category mail
statfilter-graph_vlabel counts per second
statfilter-graph_scale no
statfilter+data bayesham bayesspam
statfilter+type counters
statfilter+enable yes
contentfilter-graph_title j-chkmail - Content Filtering
contentfilter-graph_category mail
contentfilter-graph_vlabel counts per second
contentfilter-graph_scale no
contentfilter+data bayesspam matching urlbl
contentfilter+type counters
contentfilter+enable yes
behaviour-graph_title j-chkmail - Behaviour filtering - rejections
behaviour-graph_category mail
behaviour-graph_vlabel counts per second
behaviour-graph_scale no
behaviour+data throttle badrcpt localuser badmx rcptrate
behaviour+type counters
behaviour+enable yes
xfiles-graph_title j-chkmail - Suspected files filtering
xfiles-graph_category mail
xfiles-graph_vlabel counts per second
xfiles-graph_scale no
xfiles+data files xfiles
xfiles+type counters
xfiles+enable yes
volume-graph_title j-chkmail - Volume handled by the filter
volume-graph_category mail
volume-graph_vlabel KBytes per second
volume-graph_scale no
volume+data kbytes
volume+type counters
volume+enable yes
ratiospamham-graph_title j-chkmail - Statistical classification
ratiospamham-graph_category mail
ratiospamham-graph_vlabel Ratio (%)
ratiospamham-graph_scale yes
ratiospamham-graph_args --lower-limit 0 --upper-limit 100 --rigid
ratiospamham+data bayesham, bayesspam / bayesham bayesspam
ratiospamham+type ratio
ratiospamham+state yes
ratiospamham+enable yes
greydb-graph_title j-chkmail - Greylisting Database Size
greydb-graph_category mail
greydb-graph_vlabel records
greydb-graph_scale no
greydb-graph_args --lower-limit 0
greydb+data j-greypend j-greyvalid j-greywhitelist
greydb+type dbcounters
greydb+enable yes
cdb-graph_title j-chkmail - Static databases
cdb-graph_category mail
cdb-graph_vlabel records
cdb-graph_scale no
cdb-graph_args --lower-limit 0
cdb+data j-policy j-urlbl j-bayes j-rcpt
cdb+type dbcounters
cdb+enable yes
== END config ==