Monitoring-Livestatus-Class-Lite/lib/Monitoring/Livestatus/Class/Lite.pm
package Monitoring::Livestatus::Class::Lite;
=head1 NAME
Monitoring::Livestatus::Class::Lite - Object-Oriented interface for
Monitoring::Livestatus
=head1 DESCRIPTION
This module is an object-oriented interface for Monitoring::Livestatus.
Just like Monitoring::Livestatus::Class but without Moose.
=head1 SYNOPSIS
use Monitoring::Livestatus::Class::Lite;
my $class = Monitoring::Livestatus::Class::Lite->new({
peer => '/var/lib/nagios3/rw/livestatus.sock'
});
# or shorter
my $class = Monitoring::Livestatus::Class::Lite->new(
'/var/lib/nagios3/rw/livestatus.sock'
);
my $hosts = $class->table('hosts');
my @data = $hosts->columns('display_name')->filter(
{ display_name => { '-or' => [qw/test_host_47 test_router_3/] } }
)->hashref_array();
use Data::Dumper;
print Dumper \@data;
=head1 ATTRIBUTES
=head2 peer
Connection point to the livestatus addon. This can be a unix
domain or tcp socket.
=head3 Socket
my $class = Monitoring::Livestatus::Class->new(
peer => '/var/lib/nagios3/rw/livestatus.sock'
);
=head3 TCP Connection
my $class = Monitoring::Livestatus::Class->new(
peer => '192.168.1.1:2134'
);
=head1 ENVIRONMENT VARIABLES
=head2 MONITORING_LIVESTATUS_CLASS_TRACE
Print tracer output from this object.
=head2 MONITORING_LIVESTATUS_CLASS_TEST_PEER
Set peer for live tests.
=cut
use warnings;
use strict;
use Carp qw/croak confess/;
use Monitoring::Livestatus ();
our $VERSION = '0.07';
our $compining_prefix = '';
our $filter_mode = '';
our $filter_cache = {};
my $operators = {
'and' => '_cond_compining',
'or' => '_cond_compining',
'groupby' => '_cond_op_groupby',
'sum' => '_cond_op_simple',
'min' => '_cond_op_simple',
'max' => '_cond_op_simple',
'avg' => '_cond_op_simple',
'std' => '_cond_op_simple',
'isa' => '_cond_op_isa',
};
################################################################################
=head1 METHODS
=head2 new
new($options)
create new Class module
=cut
sub new {
my($class, @args) = @_;
my $self = {};
if(scalar @args == 1) {
if(ref $args[0] eq 'HASH') {
$self = $args[0];
} else {
$self->{'peer'} = $args[0];
}
}
else {
my %args = @args;
$self = \%args;
}
$self->{backend_obj} = Monitoring::Livestatus->new(
name => $self->{'name'},
peer => $self->{'peer'},
verbose => $self->{'verbose'},
keepalive => $self->{'keepalive'},
);
bless($self, $class);
return $self;
}
################################################################################
=head2 table
table($tablename)
return instance for this table
=cut
sub table {
my($self, $name) = @_;
confess('need table name') unless $name;
my $table = {
'_class' => $self->{'backend_obj'},
'_table' => $name,
};
bless($table, 'Monitoring::Livestatus::Class::Lite');
return $table;
}
################################################################################
=head2 columns
columns($columns)
list of columns to fetch
=cut
sub columns {
my($self, @columns) = @_;
$self->{'_columns'} = \@columns;
return $self;
}
################################################################################
=head2 options
options($options)
set query options
=cut
sub options {
my($self, $options) = @_;
$self->{'_options'} = $options;
return $self;
}
################################################################################
=head2 filter
filter($filter)
filter result set
=cut
sub filter {
my($self, $filter) = @_;
$self->{'_filter'} = $self->{'_filter'} ? [@{$self->{'_filter'}}, $filter] : [$filter];
return $self;
}
################################################################################
=head2 stats
stats($statsfilter)
set stats filter
=cut
sub stats {
my($self, $filter) = @_;
$self->{'_statsfilter'} = $self->{'_statsfilter'} ? [@{$self->{'_statsfilter'}}, $filter] : [$filter];
return $self;
}
################################################################################
=head2 hashref_pk
hashref_pk($key)
return result as hash ref by key
=cut
sub hashref_pk {
my($self, $key) = @_;
confess("no key!") unless $key;
return([$self->statement(), $self->{_columns}, $self->{_options}]) if $ENV{'THRUK_SELECT'};
my %indexed;
my @data = $self->hashref_array();
confess('undefined index: '.$key) if(defined $data[0] && !defined $data[0]->{$key});
for my $row (@data) {
$indexed{$row->{$key}} = $row;
}
return wantarray ? %indexed : \%indexed;
}
################################################################################
=head2 hashref_array
hashref_array()
return result as array
=cut
sub hashref_array {
my($self) = @_;
return([$self->statement(), $self->{_columns}, $self->{_options}]) if $ENV{'THRUK_SELECT'};
my @data = $self->_execute();
return wantarray ? @data : \@data;
}
################################################################################
=head2 reset_filter
reset_filter()
removes all current filter
=cut
sub reset_filter {
my($self) = @_;
$self->{'_filter'} = undef;
$self->{'_statsfilter'} = undef;
return($self);
}
################################################################################
=head2 save_filter
save_filter($name)
save this filter with given name which can be reused later.
=cut
sub save_filter {
my($self, $name) = @_;
$filter_cache->{$name} = $self->statement(1);
return($self);
}
################################################################################
=head2 apply_filter
apply_filter($name)
returns true if a filter with this name has been applied. returns false if filter
does not exist.
=cut
sub apply_filter {
my($self, $name) = @_;
return unless $filter_cache->{$name};
$self->{'_extra_stm'} = $filter_cache->{$name};
$self->{'_columns'} = undef;
return($self);
}
################################################################################
=head2 statement
statement($filter_only)
return query as text.
=cut
sub statement {
my($self, $filter_only) = @_;
confess("no table??") unless $self->{'_table'};
my @statements = ();
if( $self->{'_columns'} ) {
push @statements, sprintf('Columns: %s',join(' ',@{ $self->{'_columns'} }));
}
# filtering
if( $self->{'_filter'} ) {
push @statements, @{$self->_apply_filter($self->{'_filter'})};
}
if( $self->{'_statsfilter'} ) {
push @statements, @{$self->_apply_filter($self->{'_statsfilter'}, 'Stats')};
}
if( $self->{'_extra_stm'} ) {
push @statements, @{$self->{'_extra_stm'}};
}
return(\@statements) if $filter_only;
unshift @statements, sprintf("GET %s", $self->{'_table'});
printf STDERR "EXEC: %s\n", join("\nEXEC: ",@statements) if $ENV{'MONITORING_LIVESTATUS_CLASS_TRACE'};
my $statement = join("\n",@statements);
return $statement;
}
################################################################################
# INTERNAL SUBs
################################################################################
sub _execute {
my($self) = @_;
my $statement = $self->statement();
my $options = $self->{'_options'};
$options->{'slice'} = {};
my $return = $self->{'_class'}->selectall_arrayref($statement, $options);
return wantarray ? @{ $return } : $return;
}
################################################################################
sub _apply_filter {
my($self, $filter, $mode) = @_;
$compining_prefix = $mode || '';
$filter_mode = $mode || 'Filter';
#my( $combining_count, @statements)...
my( undef, @statements) = &_recurse_cond($filter);
return wantarray ? @statements: \@statements;
}
################################################################################
sub _recurse_cond {
my($cond, $combining_count) = @_;
$combining_count = 0 unless defined $combining_count;
my $method = '_cond_'.&_refkind($cond);
my($child_combining_count, @statement) = &{\&{$method}}($cond,$combining_count);
$combining_count = $child_combining_count;
return ( $combining_count, @statement );
}
################################################################################
sub _cond_UNDEF { return ( () ); }
################################################################################
sub _cond_ARRAYREF {
my($conds, $combining_count) = @_;
$combining_count = $combining_count || 0;
my @statement = ();
my @cp_conds = @{ $conds }; # work with a copy
while(my $cond = shift @cp_conds) {
my($child_combining_count, @child_statement) = &_dispatch_refkind($cond, {
ARRAYREF => sub { &_recurse_cond($cond, $combining_count) },
HASHREF => sub { &_recurse_cond($cond, $combining_count) },
UNDEF => sub { croak "not supported : UNDEF in arrayref" },
SCALAR => sub { &_recurse_cond( { $cond => shift(@cp_conds) } , $combining_count ) },
});
push @statement, @child_statement;
$combining_count = $child_combining_count;
}
return($combining_count, @statement);
}
################################################################################
sub _cond_HASHREF {
my($cond, $combining_count) = @_;
$combining_count = 0 unless $combining_count;
my $child_combining_count = 0;
my @all_statement;
my @child_statement;
while(my($key, $value) = each %{$cond}) {
if(substr($key,0,1) eq '-'){
# Child key for combining filters ( -and / -or )
($child_combining_count, @child_statement) = &_cond_op_in_hash($key, $value, $combining_count);
$combining_count = $child_combining_count;
} else {
my $method = '_cond_hashpair_'.&_refkind($value);
($child_combining_count, @child_statement) = &{\&{$method}}($key, $value, undef ,$combining_count);
$combining_count = $child_combining_count;
}
push @all_statement, @child_statement;
}
return($combining_count, @all_statement);
}
################################################################################
sub _cond_hashpair_UNDEF {
#my($key, $value, $operator, $combining_count)...
my($key, undef, $operator, $combining_count) = @_;
$combining_count = 0 unless $combining_count;
$key = '' unless $key;
$operator = '=' unless $operator;
my @statement = (sprintf("%s: %s %s",$filter_mode,$key,$operator));
$combining_count++;
return ( $combining_count, @statement );
}
################################################################################
sub _cond_hashpair_SCALAR {
my($key, $value, $operator, $combining_count) = @_;
$combining_count = 0 unless $combining_count;
my @statement = (sprintf("%s: %s %s %s",
$filter_mode,
($key || '') ,
($operator || '='),
$value),
);
$combining_count++;
return ( $combining_count, @statement );
}
################################################################################
sub _cond_hashpair_ARRAYREF {
my $key = shift || '';
my $values = shift || [];
my $operator = shift || '=';
my $combining_count = shift || 0;
my @statement = ();
foreach my $value ( @{ $values }){
push @statement, sprintf("%s: %s %s %s",$filter_mode,$key,$operator,$value);
$combining_count++;
}
return ( $combining_count, @statement );
}
################################################################################
sub _cond_hashpair_HASHREF {
#my($key, $values, $combining, $combining_count)...
my($key, $values, undef, $combining_count) = @_;
$key = '' unless $key;
$values = {} unless $values;
$combining_count = 0 unless $combining_count;
my @statement = ();
foreach my $child_key ( keys %{ $values } ){
my $child_value = $values->{ $child_key };
if ( $child_key =~ /^-/mxo ){
my ( $child_combining_count, @child_statement ) = &_cond_op_in_hash($child_key, { $key => $child_value } , 0);
$combining_count += $child_combining_count;
push @statement, @child_statement;
} elsif ( $child_key =~ /^[!<>=~]/mxo ){
# Child key is a operator like:
# = equality
# ~ match regular expression (substring match)
# =~ equality ignoring case
# ~~ regular expression ignoring case
# < less than
# > greater than
# <= less or equal
# >= greater or equal
my $method = '_cond_hashpair_'.&_refkind($child_value);
my($child_combining_count, @child_statement) = &{\&{$method}}($key, $child_value,$child_key);
$combining_count += $child_combining_count;
push @statement, @child_statement;
} else {
my $method = '_cond_hashpair_'.&_refkind($child_value);
my ( $child_combining_count, @child_statement ) = &{\&{$method}}($key, $child_value);
$combining_count += $child_combining_count;
push @statement, @child_statement;
}
}
return ( $combining_count, @statement );
}
################################################################################
sub _cond_op_in_hash {
my($operator, $value, $combining_count) = @_;
if ($operator && substr($operator,0,1) eq '-'){
$operator = substr($operator, 1); # remove -
$operator =~ s/\s+$//gmxo; # remove trailing space
}
my $operator_handler = $operators->{lc $operator};
return &{\&{$operator_handler}}($operator,$value,$combining_count);
}
################################################################################
sub _cond_compining {
my $combining = shift;
my $value = shift;
my $combining_count = shift || 0;
$combining_count++;
my @statement = ();
if ($combining && substr($combining,0,1) eq '-'){
$combining = substr($combining, 1); # remove -
$combining =~ s/\s+$//gmxo; # remove trailing space
}
my($child_combining_count, @child_statement) = &_recurse_cond($value, 0);
push @statement, @child_statement;
if(defined $combining and $child_combining_count > 1) {
push @statement, sprintf("%s%s: %d",
$compining_prefix,
ucfirst( $combining ),
$child_combining_count,
);
}
return ( $combining_count, @statement );
}
################################################################################
sub _refkind {
my $ref = ref $_[0];
return(uc($ref).'REF') if $ref;
return('UNDEF') if !defined $_[0];
return('SCALAR');
}
################################################################################
sub _dispatch_refkind {
my($value, $dispatch_table) = @_;
my $type = &_refkind($value);
my $coderef = $dispatch_table->{$type} ||
die(sprintf("No coderef for %s ( %s ) found!",$value, $type));
return $coderef->();
}
################################################################################
sub _cond_op_groupby {
#my($operator, $value, $combining_count) = @_;
$_[2] = 0 unless defined $_[2];
return(++$_[2], (sprintf("%s%s: %s", $compining_prefix, 'GroupBy', $_[1])));
}
################################################################################
sub _cond_op_simple {
my($operator, $value, $combining_count) = @_;
$combining_count = 0 unless defined $combining_count;
return(++$combining_count, (sprintf("%s: %s %s",$compining_prefix,$operator,$value)));
}
################################################################################
sub _cond_op_isa {
#my($operator, $value, $combining_count) = @_;
my(undef, $value, $combining_count) = @_;
$combining_count = 0 unless defined $combining_count;
my @keys = keys %{$value};
if(scalar @keys != 1) {
die "Isa operator doesn't support more then one key.";
}
my $as_name = shift @keys;
my @values = values(%{$value});
my($child_combining_count, @statement) = &_recurse_cond(shift( @values ), 0);
$combining_count += $child_combining_count;
# append alias to last operator
$statement[-1] .= " as ".$as_name;
return($combining_count, @statement);
}
################################################################################
1;
__END__
=head1 REPOSITORY
Git: http://github.com/sni/Monitoring-Livestatus-Class-Lite
=head1 AUTHOR
Sven Nierlein, 2009-present, <sven@nierlein.org>
Robert Bohne, C<< <rbo at cpan.org> >>
=head1 COPYRIGHT & LICENSE
Sven Nierlein, 2009-present, <sven@nierlein.org>
This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.
=cut