#! /usr/bin/perl # nagios: +epn # # check_alyvix3_testcases.pl - Get Monitoring Values from Alyvix3 Server API passively # # Copyright (C) 2020 Juergen Vigna # # 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # Report bugs to: juergen.vigna@wuerth-phoenix.com # # use strict; use warnings; use LWP::Simple; use JSON; use Data::Dumper; use Getopt::Long; use Date::Parse; require HTTP::Request; use LWP::UserAgent; use IO::Socket::SSL qw(SSL_VERIFY_NONE); use REST::Client; use MIME::Base64; my $PROGNAME = "check_alyvix3_testcases.pl"; my $VERSION = "1.0.0"; sub print_help (); sub print_usage (); my @opt_verbose = []; my $opt_help = undef; my $opt_debug = 0; my $opt_host = undef; my $opt_hostname = undef; my $opt_servicepre = "Alyvix"; my $opt_testuser = undef; my $opt_timeout = 0; my $opt_testing = 0; my $opt_apibase = 'v0/testcases'; my $opt_proxybase = undef; my $opt_statedir = "/var/spool/neteye/tmp"; my $opt_userpass = "alyvix:password"; # Global Variables my $request_url = "https://icinga2-master.neteyelocal:5665"; my @services; my %testcases; my %timeouts; # Get the options Getopt::Long::Configure('bundling'); GetOptions( 'h' => \$opt_help, 'v' => \@opt_verbose, 'verbose' => \@opt_verbose, 'help' => \$opt_help, 'D' => \$opt_debug, 'debug' => \$opt_debug, 'testing' => \$opt_testing, 'H=s' => \$opt_host, 'host=s' => \$opt_host, 'N=s' => \$opt_hostname, 'hostname=s' => \$opt_hostname, 'T=s' => \$opt_servicepre, 'testcasepre=s' => \$opt_servicepre, 'U=s' => \$opt_testuser, 'testuser=s' => \$opt_testuser, 't=i' => \$opt_timeout, 'timeout=i' => \$opt_timeout, 'd=s' => \$opt_statedir, 'statedir=s' => \$opt_statedir, 'p=s' => \$opt_userpass, 'userpass=s' => \$opt_userpass, 'A=s' => \$opt_apibase, 'apibase=s' => \$opt_apibase, 'P=s' => \$opt_proxybase, 'useproxypass=s' => \$opt_proxybase, ) || print_help(); # If somebody wants the help ... if ($opt_help) { print_help(); } if (! defined($opt_host)) { print "ERROR: Missing Alyvix3 Server Host Name/IP (-H)!\n"; exit 3; } if (! defined($opt_hostname)) { print "ERROR: Missing Monitoring Hostname (-N)!\n"; exit 3; } # --------------------------------------------------- helper ----------------------------------------- # sub get_testcase_status { my $opt_host = shift; my $opt_testcase = shift; my $opt_timeout = shift; my $opt_hostname = shift; my $opt_service = shift; my $base_url = "https://${opt_host}/${opt_apibase}/${opt_testcase}/"; my $output_url = $base_url; if (defined($opt_proxybase)) { $output_url = "${opt_proxybase}/${opt_apibase}/${opt_testcase}"; } my $useragent = LWP::UserAgent->new; $useragent->ssl_opts( SSL_verify_mode => SSL_VERIFY_NONE, verify_hostname => 0 ); my $request = HTTP::Request->new('GET', $base_url); my $response = $useragent->request($request); if (!$response->is_success) { print "UNKNOWN - Could not connect to server (${base_url}) [", $response->status_line , "]\n"; exit 3; } my $json_content = $response->content; if ($opt_debug) { print "JSCN:$json_content\n"; } if (!defined($json_content)) { print "UNKNOWN - cannot access Alyvix Server API ($opt_testcase)\n"; exit 3; } if ($opt_debug) { printf "%s\n", $json_content; } my $hash_content = JSON::decode_json($json_content); if (!defined($hash_content)) { printf "UNKNOWN - cannot decode JSON string ($opt_testcase)\n"; exit 3; } my $m = $hash_content->{measures}; my @measures = @$m; my $size = @measures; my $n = 0; my $testuser = "ALL"; my $teststate; my $testduration; my $testcode = undef; my $testtime; my $perfout = ""; my $perfname; my $perfvalue = 0; my $perfvalout; my $perfstate; my $perfexit; my $perfwarn; my $perfcrit; my $statestr = "OK"; my $nprob = 0; my $ntot = 0; my $oldcode = ""; my $oldstr = "OLD"; my $now = time(); my $probstr = ""; my $outstr = ""; my $verbstr = ""; if (defined($opt_testuser)) { $testuser = $opt_testuser; } if ($size <= 0) { return 20; } my $statefile = $opt_statedir . "/alyvix3_${opt_host}_${opt_testcase}_${testuser}.state"; if (-e $statefile) { open(my $fh_in, '<', $statefile) or die "Can't open \"$statefile\": $!\n"; while (<$fh_in>) { chomp; $oldcode = "$_"; } close($fh_in); } while($n < $size) { if (defined($opt_testuser)) { $testuser = $measures[$n]->{domain_username}; if ($testuser ne $opt_testuser) { $n++; next; } } if (!defined($testcode)) { $testcode = $measures[$n]->{test_case_execution_code}; if ($opt_debug) { print "First TESTCODE: $testcode\n"; } $teststate = $measures[$n]->{test_case_state}; $testduration = $measures[$n]->{test_case_duration_ms}; $testtime = substr($measures[$n]->{timestamp_epoch}, 0, 10); } elsif ($testcode ne $measures[$n]->{test_case_execution_code}) { if ($opt_debug) { print "TESTCODE: $testcode" . " : " . $measures[$n]->{test_case_execution_code} . "\n"; } my $newtesttime = substr($measures[$n]->{timestamp_epoch}, 0, 10); if ($opt_debug) { print "Changed TESTCODE: $testtime < $newtesttime\n"; } if ($testtime > $newtesttime) { $n++; next; } # Reset all newer data available $perfout = ""; $probstr = ""; $verbstr = ""; $ntot = 0; $nprob = 0; $testcode = $measures[$n]->{test_case_execution_code}; if ($opt_debug) { print "New TESTCODE: $testcode\n"; } $teststate = $measures[$n]->{test_case_state}; $testduration = $measures[$n]->{test_case_duration_ms}; $testtime = substr($measures[$n]->{timestamp_epoch}, 0, 10); } if ($measures[$n]->{transaction_name} ne $measures[$n]->{transaction_alias}) { $perfname = $measures[$n]->{transaction_name} . "_" . $measures[$n]->{transaction_alias}; } else { $perfname = $measures[$n]->{transaction_name}; } $perfvalue = $measures[$n]->{transaction_performance_ms}; $perfstate = $measures[$n]->{transaction_state}; $perfwarn = $measures[$n]->{transaction_warning_ms}; $perfexit = $measures[$n]->{transaction_exit}; if (!defined($perfwarn) || $perfwarn !~ /[0-9]*/) { $perfwarn = ""; } $perfcrit = $measures[$n]->{transaction_critical_ms}; if (!defined($perfcrit) || $perfcrit !~ /[0-9]*/) { $perfcrit = ""; } if (defined($perfvalue) && $perfwarn && $perfcrit) { $perfout .= " ${perfname}=${perfvalue}ms;${perfwarn};${perfcrit};0;"; } if ($opt_debug) { print "PERFSTATE:${perfname}->${perfstate}:\n"; } if ($perfwarn && $perfcrit && ($perfstate != 0) && defined($perfvalue)) { $ntot++; if ($perfstate == 1) { $nprob++; $probstr .= ",$perfname:WARNING"; } elsif ($perfstate == 2) { $nprob++; $probstr .= ",$perfname:CRITICAL"; } else { $nprob++; $probstr .= ",$perfname:UNKNOWN"; } } elsif ($perfexit =~ /fail/) { $nprob++; $probstr .= ",$perfname:FAIL"; } elsif ($perfwarn && $perfcrit) { $ntot++; } if ($#opt_verbose) { my $pv; if (defined($perfvalue)) { $pv = $perfvalue; } else { $pv = "[n/a]"; } if ($perfwarn && $perfcrit) { $perfvalout = "${pv}ms/$perfwarn/$perfcrit"; } else { $perfvalout = "${pv}ms"; } if (($#opt_verbose > 1) || ($perfwarn && $perfcrit)) { if ($perfstate == 0) { $verbstr .= "OK - $perfname ($perfvalout)\n"; } elsif ($perfstate == 1) { $verbstr .= "WARNING - $perfname ($perfvalout)\n"; } elsif ($perfstate == 2) { $verbstr .= "CRITICAL - $perfname ($perfvalout)\n"; } else { $verbstr .= "UNKNOWN - $perfname ($perfvalout)"; } } } $n++; } if (!defined($testcode)) { $teststate = 3; $outstr = "UNKNOWN - Could not find any performace data for the testcase $opt_testcase!"; $verbstr = ""; passive_set_service($opt_hostname, $opt_service, $teststate, $outstr, $verbstr, ""); return 3; } if ($teststate == 1) { $statestr = "WARNING"; } elsif ($teststate == 2) { $statestr = "CRITICAL"; } elsif ($teststate > 2) { $statestr = "UNKNOWN"; } if ($opt_timeout > 0) { my $timediff = $now - $testtime; if ($timediff > $opt_timeout) { if ($opt_debug) { print "TIMEOUT $timediff > $opt_timeout\n"; } $statestr = "UNKNOWN"; $teststate = 3; $oldcode = $testcode; $oldstr = "TIMEOUT"; } } if ($opt_debug) { print "$testcode -> $oldcode\n"; } if ($opt_testing) { print "${statestr} - $nprob/$ntot problem(s)${probstr} (Log) | duration=${testduration}ms;;;0;${perfout}\n"; if ($#opt_verbose) { print "$verbstr"; } } elsif ($testcode ne $oldcode) { $outstr="${statestr} - $nprob problem(s)${probstr} (Log)"; passive_set_service($opt_hostname, $opt_service, $teststate, $outstr, $verbstr, "duration=${testduration}ms;;;0;${perfout}"); open(my $fh_out, '>', $statefile) or die "Can't create \"$statefile\": $!\n"; print($fh_out "${testcode}\n"); close($fh_out); } elsif ($oldstr eq "TIMEOUT") { $outstr="${statestr} - $nprob problem(s)${probstr} [$oldstr] (Log)\n"; passive_set_service($opt_hostname, $opt_service,$teststate, $outstr, $verbstr, ""); } else { $teststate += 10; } return $teststate; } sub get_alyvix_services { my $hostname = shift; my $servicepre = shift; my $client = REST::Client->new(); $client->setHost($request_url); $client->getUseragent()->ssl_opts(verify_hostname => 0); #$client->setCa("/neteye/shared/monitoring/data/root-ca.crt"); $client->addHeader("Accept", "application/json"); $client->addHeader("X-HTTP-Method-Override", "GET"); $client->addHeader("Authorization", "Basic " . encode_base64($opt_userpass)); my %json_data = ( filter => "host.name==\"$hostname\" && match(\"$servicepre*\",service.name)", ); my $data = encode_json(\%json_data); $client->POST("/v1/objects/services", $data); my $status = $client->responseCode(); my $response = $client->responseContent(); if ($status != 200) { print "UNKNOWN: Cannot get Services -> Error: " . $response . "\n"; exit 3; } my $hash_content = JSON::decode_json($response); if (!defined($hash_content)) { printf "UNKNOWN - cannot decode JSON string for Services\n"; exit 3; } my $m = $hash_content->{results}; my @results = @$m; my $size = @results; if (!@results) { printf "UNKNOWN - No Services with '$servicepre' found on Host '$hostname'\n"; exit 3; } my $n = 0; foreach my $service ( @results ) { if (!defined($service->{attrs}->{vars}->{alyvix_testcase_name})) { next; } if ($opt_debug) { print $service->{attrs}->{name} . ":" . $service->{attrs}->{vars}->{alyvix_testcase_name} . "\n"; } $services[$n] = $service->{attrs}->{name}; $testcases{$service->{attrs}->{name}} = $service->{attrs}->{vars}->{alyvix_testcase_name}; if (defined($service->{attrs}->{vars}->{alyvix_timeout})) { $timeouts{$service->{attrs}->{name}} = $service->{attrs}->{vars}->{alyvix_timeout}; } else { $timeouts{$service->{attrs}->{name}} = $opt_timeout; } if ($opt_debug) { print "Timeout: " . $timeouts{$service->{attrs}->{name}} . "\n"; } $n++; } } sub passive_set_service { my $HOSTNAME = shift; my $SERVICE = shift; my $state = shift; my $outstr = shift; my $verbstr = shift; my $perfstr = shift; my $client = REST::Client->new(); $client->setHost($request_url); $client->getUseragent()->ssl_opts(verify_hostname => 0); #$client->setCa("pki/icinga2-ca.crt"); $client->addHeader("Accept", "application/json"); $client->addHeader("X-HTTP-Method-Override", "POST"); $client->addHeader("Authorization", "Basic " . encode_base64($opt_userpass)); my $thost = `hostname`; chomp $thost; #pretty => 'true', my $pout; if (defined($verbstr)) { $pout = "$outstr\n$verbstr"; } else { $pout = "$outstr"; } my %json_data = ( type => 'Service', filter => "host.name==\"$HOSTNAME\" && service.name==\"$SERVICE\"", exit_status => $state, plugin_output => "$pout", check_source => "$thost", performance_data => $perfstr, ); my $data = encode_json(\%json_data); $client->POST("/v1/actions/process-check-result", $data); my $status = $client->responseCode(); my $response = $client->responseContent(); if ($status != 200) { print "UNKNOWN: Cannot set Service in PassiveMode -> Error: ($status)" . $response . "\n"; exit 3; } } sub print_help() { printf "%s, Version %s\n",$PROGNAME, $VERSION; print "Copyright (c) 2020 Juergen Vigna\n"; print "This program is licensed under the terms of the\n"; print "GNU General Public License\n(check source code for details)\n"; print "\n"; printf "Get monitoring results for a Testcase from Alyvix3 Server\n"; print "\n"; print_usage(); print "\n"; print " -V (--version) Programm version\n"; print " -h (--help) usage help\n"; print " -v (--verbose) verbose output\n"; print " -D (--debug) debug output\n"; print " -H (--host) Alyvix3 Server hostname/ip\n"; print " -N (--hostname) Alyvix3 Monitoring Hostname\n"; print " -T (--testcasepre) Alyvix3 Testcase Monitoring Service Prefix\n"; print " -U (--testuser) Alyvix3 Testcase user (default: ALL_USERS)\n"; print " -t (--timeout) Alyvix3 Testcase values older then timeout gives UNKNOWN (default: $opt_timeout)\n"; print " -d (--statedir) Directory where to write the statefiles (default: $opt_statedir)\n"; print " -p (--userpass) User:Password for Icinga2 API access (default: alyvix:***)\n"; print " -A (--apibase) Alyvix3 Server API BaseURL (default: $opt_apibase)\n"; print " -P (--proxypass) The Output Url to access logs uses a proxypass\n"; print "\n"; exit 0; } sub print_usage() { print "Usage: \n"; print " $PROGNAME (-H|--host ) (-N|--hostname ) [-T|--testcasepre ] [-U|--testuser ] [-t|--timeout ] [-d|--statedir ] [-p|--userpass ] [-A|--apibase ] [-P|--proxypass ]\n"; print " $PROGNAME [-h | --help]\n"; print " $PROGNAME [-V | --version]\n"; } # # ------------------------------------------ START MAIN -------------------------------------------- # get_alyvix_services($opt_hostname, $opt_servicepre); my $n = 0; my $outstr = ""; my @statestr = ( "OK", "WARNING", "CRITICAL", "UNKNOWN" ); foreach my $service ( sort @services ) { my $state = get_testcase_status($opt_host, $testcases{$service}, $timeouts{$service}, $opt_hostname, $service); if ($state >= 20) { $state = 3; $outstr .= "[" . $statestr[$state] . "]\t{DISABLED} $service\n"; } elsif ($state >= 10) { $state -= 10; $outstr .= "[" . $statestr[$state] . "]\t(OLD) $service\n"; } else { $outstr .= "[" . $statestr[$state] . "]\t$service\n"; } $n++; } print "OK - Run all '$opt_servicepre' services\n$outstr"; exit 0;