diff options
-rw-r--r-- | CHANGES | 3 | ||||
-rwxr-xr-x | bin/smokeinfo | 115 | ||||
-rw-r--r-- | lib/Smokeping/Info.pm | 204 |
3 files changed, 322 insertions, 0 deletions
@@ -1,3 +1,6 @@ +* smokeinfo: new tool to extract numeric data from a smokeping installation + creation sponsored by Swisscom Hospitality -- tobi + * new matcher: ExpLoss.pm matcher. It produces an exponential weighted average and supports RMON-like thresholds to obtain stable node status detection. -- Veniamin Konoplev V.Konoplev rssi.ru diff --git a/bin/smokeinfo b/bin/smokeinfo new file mode 100755 index 0000000..a143abc --- /dev/null +++ b/bin/smokeinfo @@ -0,0 +1,115 @@ +#!/usr/bin/perl -w +use strict; + +use lib qw(/usr/pack/rrdtool-1.3.2-to/lib/perl); +use FindBin; +use lib "$FindBin::Bin/../lib"; +use Smokeping::Info; +use Getopt::Long 2.25 qw(:config no_ignore_case); +use Pod::Usage 1.14; + +'$Revision: 3879 $ ' =~ /Revision: (\S*)/; +my $Revision = $1; + +sub main() +{ + # parse options + my %opt = (mode=>'plain',pattern=>undef,separator=>';',format=>'%le'); + + GetOptions(\%opt, 'help|h', 'man', 'version', 'noaction|no-action|n', + 'start=s','end=s','pattern=s','mode=s','separator=s','format=s') or exit(1); + if($opt{help}) { pod2usage(1) } + if($opt{man}) { pod2usage(-exitstatus => 0, -verbose => 2) } + if($opt{version}) { print "smokeinfo $Revision\n"; exit(0) } + if($opt{noaction}) { die "ERROR: don't know how to \"no-action\".\n" } + my $config = shift @ARGV; + + my $si = Smokeping::Info->new($config); + my $nodes = $si->fetch_nodes(pattern=>$opt{pattern},mode=>$opt{mode}); + my @rows = qw(med_avg med_min med_max med_now loss_avg loss_max loss_now); + print '# ',join $opt{separator}, 'node_path',@rows; + print "\n"; + for my $node (@$nodes) { + my $data = $si->stat_node($node,'end-24h','now'); + print join $opt{separator},$node->{path},map {sprintf($opt{format},$data->{$_})} @rows; + print "\n"; + } +} + +main; + +__END__ + +=head1 NAME + +smokeinfo - poll smokeping site for numeric information + +=head1 SYNOPSIS + +B<smokeinfo> path/to/config.cfg [I<options>] + + --start x rrd graph start time + + --end y rrd graph end time + + --filter filter_pattern search pattern for node selection + + --mode plain (default) how to use the pattern + - plain + - recursive + - regexp + + --separator ; (default) + + --format %le (default) + + --man show man-page and exit + -h, --help display this help and exit + --version output version information and exit + +=head1 DESCRIPTION + +SmokeInfo is a simple frontend to the L<Smokeping::Info> module. It provides +access to numeric data stored in the rrd files. + +=head1 COPYRIGHT + +Copyright (c) 2009 by OETIKER+PARTNER AG. All rights reserved. + +=head1 LICENSE + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +=head1 AUTHOR + +S<Tobi Oetiker E<lt>tobi@oetiker.chE<gt>> + +=head1 HISTORY + + 2009-01-05 to Initial Version + +=cut + +# Emacs Configuration +# +# Local Variables: +# mode: cperl +# eval: (cperl-set-style "PerlStyle") +# mode: flyspell +# mode: flyspell-prog +# End: +# +# vi: sw=4 et + diff --git a/lib/Smokeping/Info.pm b/lib/Smokeping/Info.pm new file mode 100644 index 0000000..00ce096 --- /dev/null +++ b/lib/Smokeping/Info.pm @@ -0,0 +1,204 @@ +# -*- perl -*- +package Smokeping::Info; +use warnings; +use strict; +use RRDs; +use Smokeping; +use Carp; +use Data::Dumper; + +sub new { + my $this = shift; + my $class = ref($this) || $this; + my $self = { cfg_file => shift }; + bless $self, $class; + my $parser = Smokeping::get_parser(); + $self->{cfg_hash} = $parser->parse( $self->{cfg_file} ) + or croak "ERROR reading config file $parser->{err}"; + $self->{probe_hash} = Smokeping::load_probes $self->{cfg_hash}; + return $self; +} + +# get a list of all rrd files in the config file + +sub __flatten_targets; +sub __flatten_targets { + my $probes = shift; + my $root = shift; + my $prefix = shift; + my @paths; + for my $target ( sort {$root->{$a}{_order} <=> $root->{$b}{_order}} + grep { ref $root->{$_} eq 'HASH' } keys %$root ) { + push @paths, __flatten_targets($probes,$root->{$target},$prefix.'/'.$target); + }; + if (exists $root->{host} and not $root->{host} =~ m|/|){ + my $probe = $probes->{$root->{probe}}; + my $pings = $probe->_pings($root); + if (not $root->{nomasterpoll}) { + push @paths, { path => $prefix, pings=>$pings }; + }; + if ($root->{slaves}) { + for my $slave (split /\s+/,$root->{slaves}){ + push @paths, { path => $prefix.'~'.$slave, pings=>$pings }; + } + } + }; + return @paths; +} + +sub fetch_nodes { + my $self = shift; + my %args = ( 'mode' => 'plain', @_); # no mode is default + my %valid = ( pattern=>1, mode => 1 ); + my %valid_modes = ( plain=>1, recursive=>1, regexp=>1); + map { + croak "Invalid fetch nodes argument '$_'" + if not $valid{$_}; + } keys %args; + + croak "Invalid fetch mode $args{mode}" + if not $valid_modes{$args{mode}}; + + my $cfg = $self->{cfg_hash}; + my @flat = __flatten_targets($self->{probe_hash},$cfg->{Targets},''); + + my $rx = qr{.*}; + if ( defined $args{pattern} ) { + if ( $args{mode} eq 'recursive' ) { + $rx = qr{^\Q$args{pattern}\E}; + } + elsif ( $args{mode} eq 'regexp' ) { + $rx = qr{$args{pattern}}; + } + else { + $rx = qr{^\Q$args{pattern}\E[^/]*$}; + } + } + return [ grep { $_->{path} =~ /${rx}/ } @flat ]; +} + + +sub stat_node { + my $self = shift; + my $path = shift; + my $start = shift; + my $end = shift; + my $cfg = $self->{cfg_hash}; + my ($graphret,$xs,$ys) = RRDs::graph ( + '/tmp/dummy', + '--start'=>$start, + '--end'=>$end, + 'DEF:loss_avg_r='.$cfg->{General}{datadir}.$path->{path}.'.rrd:loss:AVERAGE', + 'CDEF:loss_avg=loss_avg_r,'.$path->{pings}.',/', + 'VDEF:loss_avg_tot=loss_avg,AVERAGE', + 'PRINT:loss_avg_tot:%.8le', + 'DEF:loss_max_r='.$cfg->{General}{datadir}.$path->{path}.'.rrd:loss:MAX', + 'CDEF:loss_max=loss_max_r,'.$path->{pings}.',/', + 'VDEF:loss_max_tot=loss_max,MAXIMUM', + 'PRINT:loss_max_tot:%.8le', + 'VDEF:loss_now=loss_avg,LAST', + 'PRINT:loss_now:%.8le', + 'DEF:median_avg='.$cfg->{General}{datadir}.$path->{path}.'.rrd:median:AVERAGE', + 'VDEF:median_avg_tot=median_avg,AVERAGE', + 'PRINT:median_avg_tot:%.8le', + 'DEF:median_min='.$cfg->{General}{datadir}.$path->{path}.'.rrd:median:MIN', + 'VDEF:median_min_tot=median_min,MINIMUM', + 'PRINT:median_min_tot:%.8le', + 'DEF:median_max='.$cfg->{General}{datadir}.$path->{path}.'.rrd:median:MAX', + 'VDEF:median_max_tot=median_max,MAXIMUM', + 'PRINT:median_max_tot:%.8le', + 'VDEF:median_now=median_avg,LAST', + 'PRINT:median_now:%.8le' + ); + if (my $ERROR = RRDs::error()){ + croak "$path->{$path}: $ERROR"; + } + my %data; + @data{qw(loss_avg loss_max loss_now med_avg med_min med_max med_now)} = @$graphret; + return \%data; +}; +1; + +__END__ + +=head1 NAME + +Smokeping::Info - Pull numerical info out of the rrd databases + +=head1 OVERVIEW + +This module provides methods to further process information contained in +smokeping rrd files. The smokeinfo tool is a simple wrapper around the +functionality containd in here. + + my $si = Smokeping::Info->new("config/file/path"); + + my $array_ref = $si->fetch_nodes(pattern=>'/node/path', + mode=>'recursive'); + + my $hash_ref = $si->stat_node(path,start,end); + +=head2 IMPLEMENTATION + +=head3 new(path) + +Create a new Smokeping::Info instance. Instantiating Smokeping::Info entails +reading the configuration file. This is a compte heavy procedure. So you may +want to use a single info object to handle multiple requests. + +=head3 fetch_nodes(pattern=>'/...',mode=>{recursive|regexp}) + +The fetch_nodes method will find all nodes sitting in the given pattern +(absolute path) including the path itself. By setting the recursive mode, +all rrd files in paths below will be returned as well. In regexp mode, all +rrd paths matching the given expression will be returned. + +=head3 stat_node(node,start,end) + +Return a hash pointer to statistics based on the data stored in the given +rrd path. + + med_avg - average median + med_min - minimal median + med_max - maximal median + med_now - current median + loss_avg - average loss + loss_max - maximum loss + loss_now - current loss + +=head1 COPYRIGHT + +Copyright 2009 by OETIKER+PARTNER AG + +=head1 LICENSE + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 675 Mass +Ave, Cambridge, MA 02139, USA. + +=head1 AUTHOR + +Tobias Oetiker E<lt>tobi@oetiker.chE<gt>, development sponsored by Swisscom Hospitality + +=cut + +# Emacs Configuration +# +# Local Variables: +# mode: cperl +# eval: (cperl-set-style "PerlStyle") +# mode: flyspell +# mode: flyspell-prog +# End: +# +# vi: sw=4 |