mostly done minus readme stuff
This commit is contained in:
parent
893682e547
commit
395e1c45d1
|
@ -6,6 +6,7 @@
|
|||
*.o
|
||||
*.pm.tdy
|
||||
*.bs
|
||||
bin/.exists
|
||||
|
||||
# Devel::Cover
|
||||
cover_db/
|
||||
|
|
|
@ -10,6 +10,7 @@ my %WriteMakefileArgs = (
|
|||
ABSTRACT_FROM => 'lib/Monitoring/Sneck.pm',
|
||||
LICENSE => 'artistic_2',
|
||||
MIN_PERL_VERSION => '5.006',
|
||||
INST_SCRIPT => 'bin',
|
||||
CONFIGURE_REQUIRES => {
|
||||
'ExtUtils::MakeMaker' => '0',
|
||||
},
|
||||
|
@ -17,8 +18,9 @@ my %WriteMakefileArgs = (
|
|||
'Test::More' => '0',
|
||||
},
|
||||
PREREQ_PM => {
|
||||
'JSON' => '0',
|
||||
'File::Slurp' => '0',
|
||||
'JSON' => '0',
|
||||
'File::Slurp' => '0',
|
||||
'Sys::Hostname' => '0',
|
||||
},
|
||||
dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
|
||||
clean => { FILES => 'Monitoring-Sneck-*' },
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
#!/usr/bin/env perl
|
||||
|
||||
=head1 NAME
|
||||
|
||||
sneck - a boopable LibreNMS JSON style SNMP extend for remotely running nagios style checks
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
sneck -u [B<-C> <cache file] [B<-f> <config file>] [B<-p>] [B<-i>]
|
||||
|
||||
sneck -c [B<-C> <cache file]
|
||||
|
||||
sneck [B<-f> <config file>] [B<-p>] [B<-i>]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
For a description of the config file format and output,
|
||||
please see L<Monitoring::Sneck>.
|
||||
|
||||
=head1 FLAGS
|
||||
|
||||
=head2 -f <config file>
|
||||
|
||||
The config file to use.
|
||||
|
||||
Defaults to '/usr/local/etc/sneck.conf'.
|
||||
|
||||
=head2 -p
|
||||
|
||||
Pretty it in a nicely formatted format.
|
||||
|
||||
=head2 -C <cache file>
|
||||
|
||||
The cache file to use.
|
||||
|
||||
Defaults to '/var/cache/sneck.cache'.
|
||||
|
||||
=head2 -u
|
||||
|
||||
Update the cache file. Will also print the was written to it.
|
||||
|
||||
=head2 -c
|
||||
|
||||
Print the cache file. Please note that B<-p> or B<-i> won't affect
|
||||
this as this flag only reads/prints the cache file.
|
||||
|
||||
=head2 -i
|
||||
|
||||
Includes the config file used.
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Getopt::Long;
|
||||
use File::Slurp;
|
||||
use JSON;
|
||||
use Monitoring::Sneck;
|
||||
|
||||
sub version {
|
||||
print "sneck v. 0.0.1\n";
|
||||
}
|
||||
|
||||
sub help {
|
||||
&version;
|
||||
|
||||
print '
|
||||
|
||||
-f <config> Config file to use.
|
||||
Default: /usr/local/etc/sneck.conf
|
||||
|
||||
-c Print the cache and exit. Requires -u being used previously.
|
||||
|
||||
-C Cache file.
|
||||
Default: /var/cache/sneck.cache
|
||||
|
||||
-u Run and write to cache.
|
||||
|
||||
-p Pretty print. Does not affect -c.
|
||||
|
||||
-i Include the raw config in the JSON.
|
||||
|
||||
-h Print help info.
|
||||
--help Print help info.
|
||||
-v Print version info.
|
||||
--version Print version info.
|
||||
|
||||
';
|
||||
}
|
||||
|
||||
my $cache_file = '/var/cache/sneck.cache';
|
||||
my $config_file = '/usr/local/etc/sneck.conf';
|
||||
my $update;
|
||||
my $print_cache;
|
||||
my $fallback;
|
||||
my $help;
|
||||
my $version;
|
||||
my $pretty;
|
||||
my $include;
|
||||
Getopt::Long::Configure('no_ignore_case');
|
||||
Getopt::Long::Configure('bundling');
|
||||
GetOptions(
|
||||
'version' => \$version,
|
||||
'v' => \$version,
|
||||
'help' => \$help,
|
||||
'h' => \$help,
|
||||
'c' => \$print_cache,
|
||||
'f=s' => \$config_file,
|
||||
'C=s' => \$cache_file,
|
||||
'p' => \$pretty,
|
||||
'u' => \$update,
|
||||
'i' => \$include,
|
||||
);
|
||||
|
||||
# print version or help if requested
|
||||
if ($help) {
|
||||
&help;
|
||||
exit 42;
|
||||
}
|
||||
if ($version) {
|
||||
&version;
|
||||
exit 42;
|
||||
}
|
||||
|
||||
# prints the cache and exit if requested
|
||||
if ($print_cache) {
|
||||
if ( !-f $cache_file || !-r $cache_file ) {
|
||||
my $error = 'Cache file does not exist or is not readable "' . $cache_file . '"';
|
||||
my $possible_error
|
||||
= { error => 1, version => 1, errorString => $error, data => { alert => 1, alertString => $error } };
|
||||
print encode_json($possible_error) . "\n";
|
||||
exit 3;
|
||||
}
|
||||
my $cache = read_file($cache_file);
|
||||
print $cache;
|
||||
exit;
|
||||
}
|
||||
|
||||
my $sneck = Monitoring::Sneck->new( { config => $config_file, include=>$include } );
|
||||
my $returned = $sneck->run;
|
||||
|
||||
# encode it and print it
|
||||
my $json = JSON->new->utf8->canonical(1);
|
||||
if ($pretty) {
|
||||
$json->pretty;
|
||||
}
|
||||
my $raw_json = $json->encode($returned);
|
||||
|
||||
# non-pretty does not include a new line, so add it
|
||||
if ( !$pretty ) {
|
||||
$raw_json = $raw_json . "\n";
|
||||
}
|
||||
print $raw_json;
|
||||
|
||||
if ($update) {
|
||||
my $fh;
|
||||
open( $fh, '>', $cache_file );
|
||||
print $fh $raw_json;
|
||||
close($fh);
|
||||
}
|
|
@ -4,6 +4,7 @@ use 5.006;
|
|||
use strict;
|
||||
use warnings;
|
||||
use File::Slurp;
|
||||
use Sys::Hostname;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
|
@ -11,11 +12,11 @@ Monitoring::Sneck - a boopable LibreNMS JSON style SNMP extend for remotely runn
|
|||
|
||||
=head1 VERSION
|
||||
|
||||
Version 0.0.0
|
||||
Version 0.0.1
|
||||
|
||||
=cut
|
||||
|
||||
our $VERSION = '0.0.0';
|
||||
our $VERSION = '0.0.1';
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
|
@ -25,6 +26,114 @@ our $VERSION = '0.0.0';
|
|||
|
||||
my $sneck=Monitoring::Sneck->new({config=>$file});
|
||||
|
||||
=head1 USAGE
|
||||
|
||||
Not really meant to be used as a library. The library is more of
|
||||
to support the script.
|
||||
|
||||
=head1 CONFIG FORMAT
|
||||
|
||||
White space is always cleared from the start of lines via /^[\t ]*/ for
|
||||
each file line that is read in.
|
||||
|
||||
Blank lines are ignored.
|
||||
|
||||
Lines starting with /\#/ are comments lines.
|
||||
|
||||
Lines matching /^[A-Za-z0-9\_]+\=/ are variables. Anything before the the
|
||||
/\=/ is used as the name with everything after being the value.
|
||||
|
||||
Lines matching /^[A-Za-z0-9\_]+\|/ are checks to run. Anything before the
|
||||
/\|/ is the name with everything after command to run.
|
||||
|
||||
Any other sort of lines are considered an error.
|
||||
|
||||
Variables in the checks are in the form of %%%varaible_name%%%.
|
||||
|
||||
Variable names and check names may not be redefined once defined in the config.
|
||||
|
||||
=head2 EXAMPLE CONFIG
|
||||
|
||||
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
|
||||
# this is a comment
|
||||
geom_foo|/usr/bin/env PATH=%%%PATH%%% /usr/local/libexec/nagios/check_geom mirror foo
|
||||
does_not_exist|/bin/this_will_error yup... that it will
|
||||
|
||||
does_not_exist_2|/usr/bin/env /bin/this_will_also_error
|
||||
|
||||
The first line creates a variable named path.
|
||||
|
||||
The second is ignored as it is a comment.
|
||||
|
||||
The third creates a check named geom_foo that calls env with and sets the PATH to the
|
||||
the variable defined on line 1 and calls check_geom_mirror.
|
||||
|
||||
The fourth is a example of an error that will show what will happen when you call to a
|
||||
file that does not exit.
|
||||
|
||||
The fifth line will be ignored as it is blank.
|
||||
|
||||
The sixth is a example of another command erroring.
|
||||
|
||||
When you run it, you will notice that errors for lines 4 and 5 are printed to STDERR.
|
||||
For this reason you should use '2> /dev/null' when calling it from snmpd or
|
||||
'2> /dev/null > /dev/null' when calling from cron.
|
||||
|
||||
=head1 USAGE
|
||||
|
||||
snmpd should be configured as below.
|
||||
|
||||
extend sneck /usr/bin/env PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin /usr/local/bin/sneck -c
|
||||
|
||||
Then just setup a entry in like cron such as below.
|
||||
|
||||
*/5 * * * * /usr/bin/env PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin /usr/local/bin/sneck -u 2> /dev/null > /dev/null
|
||||
|
||||
Most likely want to run it once per polling interval.
|
||||
|
||||
You can use it in a non-cached manner with out cron, but this will result in a
|
||||
longer polling time for LibreNMS or the like when it queries it.
|
||||
|
||||
=head1 RETURN HASH/JSON
|
||||
|
||||
The data section of the return hash/JSON is as below.
|
||||
|
||||
- $hash{data}{alert} :: 0/1 boolean for if there is a aloert or not.
|
||||
|
||||
- $hash{data}{ok} :: Count of the number of ok checks.
|
||||
|
||||
- $hash{data}{warning} :: Count of the number of warning checks.
|
||||
|
||||
- $hash{data}{critical} :: Count of the number of critical checks.
|
||||
|
||||
- $hash{data}{unknown} :: Count of the number of unkown checks.
|
||||
|
||||
- $hash{data}{errored} :: Count of the number of errored checks.
|
||||
|
||||
- $hash{data}{alertString} :: The cumulative outputs of anything
|
||||
that returned a warning, critical, or unknown.
|
||||
|
||||
- $hash{data}{vars} :: A hash with the variables to use.
|
||||
|
||||
- $hash{data}{time} :: Time since epoch.
|
||||
|
||||
- $hash{data}{time} :: The hostname the check was ran on.
|
||||
|
||||
- $hash{data}{config} :: The raw config file if told to include it.
|
||||
|
||||
- $hash{data}[checks}{$name} :: A hash with info on the checks ran.
|
||||
|
||||
- $hash{data}[checks}{$name}{check} :: The command pre-variable substitution.
|
||||
|
||||
- $hash{data}[checks}{$name}{ran} :: The command ran.
|
||||
|
||||
- $hash{data}[checks}{$name}{output} :: The output of the check.
|
||||
|
||||
- $hash{data}[checks}{$name}{exit} :: The exit code.
|
||||
|
||||
- $hash{data}[checks}{$name}{error} :: Only present it died on a
|
||||
signal or could not be executed. Provides a brief description.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=head2 new
|
||||
|
@ -33,9 +142,20 @@ Initiates the object.
|
|||
|
||||
One argument is taken and that is a hash ref. If the key 'config'
|
||||
is present, that will be the config file used. Otherwise
|
||||
'/usr/local/etc/sneck.conf' is used.
|
||||
'/usr/local/etc/sneck.conf' is used. The key 'include' is a Perl
|
||||
boolean for if the raw config should be included in the JSON.
|
||||
|
||||
my $sneck=Monitoring::Sneck->new({config=>$file});
|
||||
This function should always work. If there is an error with
|
||||
parsing or the like, it will be reported in the expected format
|
||||
when $sneck->run is called.
|
||||
|
||||
This is meant to be rock solid and always work, meaning LibreNMS
|
||||
style JSON is always returned(provided Perl and the other modules
|
||||
are working).
|
||||
|
||||
|
||||
|
||||
my $sneck=Monitoring::Sneck->new({config=>$file}, include=>0);
|
||||
|
||||
=cut
|
||||
|
||||
|
@ -51,7 +171,17 @@ sub new {
|
|||
to_return => {
|
||||
error => 0,
|
||||
errorString => '',
|
||||
data => { ok => 0, warning => 0, critical => 0, unknown => 0, erroed=>0, alert => 0, alertString => '', checks => {} },
|
||||
data => {
|
||||
hostname => hostname,
|
||||
ok => 0,
|
||||
warning => 0,
|
||||
critical => 0,
|
||||
unknown => 0,
|
||||
errored => 0,
|
||||
alert => 0,
|
||||
alertString => '',
|
||||
checks => {}
|
||||
},
|
||||
version => 1,
|
||||
},
|
||||
checks => {},
|
||||
|
@ -61,25 +191,32 @@ sub new {
|
|||
bless $self;
|
||||
|
||||
my $config_raw;
|
||||
eval { my $config_raw = read_file( $self->{config} ); };
|
||||
eval { $config_raw = read_file( $self->{config} ); };
|
||||
if ($@) {
|
||||
$self->{good} = 0;
|
||||
$self->{to_return}{error} = 1;
|
||||
$self->{to_return}{errorString} = 'Failed to read in the config file "' . $self->{conffig} . '"... ' . $@;
|
||||
$self->{to_return}{errorString} = 'Failed to read in the config file "' . $self->{config} . '"... ' . $@;
|
||||
$self->{checks} = {};
|
||||
return $self;
|
||||
}
|
||||
|
||||
# include the config file if requested
|
||||
if ( defined( $args{include} )
|
||||
&& $args{include} )
|
||||
{
|
||||
$self->{to_return}{data}{config} = $config_raw;
|
||||
}
|
||||
|
||||
# split the file and ignore any comments
|
||||
my @config_split = grep( !/^[\t\ ]*#/, split( /\n/, $config_raw ) );
|
||||
my $found_items = 0;
|
||||
foreach my $line (@config_split) {
|
||||
$line =~ s/^[\ \t]*//;
|
||||
if ( $line =~ /^[A-Za-z0-9\_]+]\=/ ) {
|
||||
if ( $line =~ /^[A-Za-z0-9\_]+\=/ ) {
|
||||
|
||||
# we found a variable
|
||||
|
||||
my ( $name, $value ) = split( /\|=/, $line, 2 );
|
||||
my ( $name, $value ) = split( /\=/, $line, 2 );
|
||||
|
||||
# make sure we have a value
|
||||
if ( !defined($value) ) {
|
||||
|
@ -94,7 +231,7 @@ sub new {
|
|||
$name =~ s/[\t\ ]*$//;
|
||||
|
||||
# check to make sure it is not already defined
|
||||
if ( defiend( $self->{vars}{$name} ) ) {
|
||||
if ( defined( $self->{vars}{$name} ) ) {
|
||||
$self->{good} = 0;
|
||||
$self->{to_return}{error} = 1;
|
||||
$self->{to_return}{errorString} = 'variable "' . $name . '" is redefined on the line "' . $line . '"';
|
||||
|
@ -103,9 +240,9 @@ sub new {
|
|||
|
||||
$self->{vars}{$name} = $value;
|
||||
}
|
||||
elsif ( $line =~ /^[A-Za-z0-9\_]+]\|/ ) {
|
||||
elsif ( $line =~ /^[A-Za-z0-9\_]+\|/ ) {
|
||||
|
||||
# we found a item to add
|
||||
# we found a check to add
|
||||
my ( $name, $check ) = split( /\|/, $line, 2 );
|
||||
|
||||
# make sure we have a check
|
||||
|
@ -121,7 +258,7 @@ sub new {
|
|||
$name =~ s/[\t\ ]*$//;
|
||||
|
||||
# check to make sure it is not already defined
|
||||
if ( defiend( $self->{checks}{$name} ) ) {
|
||||
if ( defined( $self->{checks}{$name} ) ) {
|
||||
$self->{good} = 0;
|
||||
$self->{to_return}{error} = 1;
|
||||
$self->{to_return}{errorString} = 'check "' . $name . '" is defined on the line "' . $line . '"';
|
||||
|
@ -167,33 +304,41 @@ sub run {
|
|||
return $self->{to_return};
|
||||
}
|
||||
|
||||
# set the time it ran
|
||||
$self->{to_return}{data}{time} = time;
|
||||
|
||||
my @vars = keys( %{ $self->{vars} } );
|
||||
my @checks = keys( %{ $self->{checks} } );
|
||||
foreach my $name (@checks) {
|
||||
my $check = $self->{checks}{$name};
|
||||
$self->{to_return}{checks}{$name} = { check => $check };
|
||||
$self->{to_return}{data}{checks}{$name} = { check => $check };
|
||||
|
||||
# put the variables in place
|
||||
foreach my $var_name (@vars) {
|
||||
my $value = $self->{vars}{$var_name};
|
||||
$check =~ s/%%%$var_name%%%/$value/g;
|
||||
}
|
||||
$self->{to_return}{checks}{$name}{ran} = $check;
|
||||
$self->{to_return}{data}{checks}{$name}{ran} = $check;
|
||||
|
||||
$self->{to_return}{data}{checks}{$name}{output} = system($check);
|
||||
$self->{to_return}{data}{checks}{$name}{output} = `$check`;
|
||||
my $exit_code = $?;
|
||||
chomp( $self->{to_return}{data}{checks}{$name}{output} );
|
||||
if ( defined( $self->{to_return}{data}{checks}{$name}{output} ) ) {
|
||||
chomp( $self->{to_return}{data}{checks}{$name}{output} );
|
||||
}
|
||||
|
||||
# handle the exit code
|
||||
if ( $? == -1 ) {
|
||||
if ( $exit_code == -1 ) {
|
||||
$self->{to_return}{data}{checks}{$name}{error} = 'failed to execute';
|
||||
}
|
||||
elsif ( $? & 127 ) {
|
||||
$self->{to_return}{data}{checks}{$name}{error} = sprintf( "child died with signal %d, %s coredump\n",
|
||||
( $? & 127 ), ( $? & 128 ) ? 'with' : 'without' );
|
||||
elsif ( $exit_code & 127 ) {
|
||||
$self->{to_return}{data}{checks}{$name}{error} = sprintf(
|
||||
"child died with signal %d, %s coredump\n",
|
||||
( $exit_code & 127 ),
|
||||
( $exit_code & 128 ) ? 'with' : 'without'
|
||||
);
|
||||
}
|
||||
else {
|
||||
$exit_code = $? >> 8;
|
||||
$exit_code = $exit_code >> 8;
|
||||
}
|
||||
$self->{to_return}{data}{checks}{$name}{exit} = $exit_code;
|
||||
|
||||
|
@ -225,45 +370,11 @@ sub run {
|
|||
}
|
||||
}
|
||||
|
||||
$self->{to_return}{data}{vars}=$self->{vars};
|
||||
$self->{to_return}{data}{vars} = $self->{vars};
|
||||
|
||||
return $self->{to_return};
|
||||
}
|
||||
|
||||
=head1 RETURN HASH
|
||||
|
||||
The data section of the return hash is as below.
|
||||
|
||||
- $hash{data}{alert} :: 0/1 boolean for if there is a aloert or not.
|
||||
|
||||
- $hash{data}{ok} :: Count of the number of ok checks.
|
||||
|
||||
- $hash{data}{warning} :: Count of the number of warning checks.
|
||||
|
||||
- $hash{data}{critical} :: Count of the number of critical checks.
|
||||
|
||||
- $hash{data}{unknown} :: Count of the number of unkown checks.
|
||||
|
||||
- $hash{data}{errored} :: Count of the number of errored checks.
|
||||
|
||||
- $hash{data}{alertString} :: The cumulative outputs of anything
|
||||
that returned a warning, critical, or unknown.
|
||||
|
||||
- $hash{data}{vars} :: A hash with the variables to use.
|
||||
|
||||
- $hash{data}[checks}{$name} :: A hash with info on the checks ran.
|
||||
|
||||
- $hash{data}[checks}{$name}{check} :: The command pre-variable substitution.
|
||||
|
||||
- $hash{data}[checks}{$name}{ran} :: The command ran.
|
||||
|
||||
- $hash{data}[checks}{$name}{output} :: The output of the check.
|
||||
|
||||
- $hash{data}[checks}{$name}{exit} :: The exit code.
|
||||
|
||||
- $hash{data}[checks}{$name}{error} :: Only present it died on a
|
||||
signal or could not be executed. Provides a brief description.
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Zane C. Bowers-Hadley, C<< <vvelox at vvelox.net> >>
|
||||
|
|
Loading…
Reference in New Issue