From 5581f6803c4bc0be73c73617e54309034b83c682 Mon Sep 17 00:00:00 2001 From: Peter Palfrader Date: Fri, 30 Aug 2019 13:26:38 +0200 Subject: [PATCH] Add check_puppetdb_nodes --- dsa-nagios-checks/checks/check_puppetdb_nodes | 253 ++++++++++++++++++ dsa-nagios-checks/debian/changelog | 1 + 2 files changed, 254 insertions(+) create mode 100644 dsa-nagios-checks/checks/check_puppetdb_nodes diff --git a/dsa-nagios-checks/checks/check_puppetdb_nodes b/dsa-nagios-checks/checks/check_puppetdb_nodes new file mode 100644 index 0000000..f76674e --- /dev/null +++ b/dsa-nagios-checks/checks/check_puppetdb_nodes @@ -0,0 +1,253 @@ +#!/usr/bin/perl + +# Copyright (c) 2014, Evgeni Golov +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, this +# list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# * Neither the name of the {organization} nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use strict; +use warnings; +use JSON; +use LWP; +use Monitoring::Plugin; +use Date::Parse; + +my $np = Monitoring::Plugin->new( + usage => "Usage: %s [ -H|--hostname=] " + . "[ -p|--port= ] [-s] [ -w|--warning= ] " + . "[ -c|--critical= ] [ -W|--warnfails= ] " + . "[ -C|--critfails= ] [ -n|--node= ]" + . "[ -a|--apiversion= ]" + . "[ -i|--ignore= ]", + shortname => 'Check last node runs from PuppetDB', + url => 'https://github.com/evgeni/check_puppetdb_nodes', + version => '1.0', + license => 'This plugin is free software, and comes with ABSOLUTELY +NO WARRANTY. It may be used, redistributed and/or modified under +the terms of the BSD 3-clause license.', +); + +$np->add_arg( + spec => 'warning|w=i', + help => "Exit with WARNING status if nodes did not update for " + . "more than INTEGER minutes (default: %s)", + default => 120, +); + +$np->add_arg( + spec => 'critical|c=i', + help => "Exit with CRITICAL status if nodes did not update for " + . "more than INTEGER minutes (default: %s)", + default => 1440, +); + +$np->add_arg( + spec => 'warnfails|W=i', + help => "Exit with WARNING status if nodes had at least INTEGER " + . "failures in the last run (default: %s)", + default => 1, +); + +$np->add_arg( + spec => 'critfails|C=i', + help => "Exit with CRITICAL status if nodes had at least INTEGER " + . "failures in the last run (default: %s)", + default => 1, +); + +$np->add_arg( + spec => 'hostname|H=s', + help => 'Hostname of the PuppetDB (default: %s)', + default => 'localhost', +); + +$np->add_arg( + spec => 'port|p=i', + help => 'Port PuppetDB is running on (default: %s)', + default => 8080, +); + +$np->add_arg( + spec => 'node|n=s', + help => 'Node name to check, if not given, all nodes will be checked', +); + +$np->add_arg( + spec => 'ssl|s', + help => "Use HTTPS instead of HTTP", +); + +$np->add_arg( + spec => 'insecure|k', + help => "Allow connections via HTTPS without checking certificates", +); + +$np->add_arg( + spec => 'apiversion|a=n', + help => 'Specify PupppetDB API version (default: %s)', + default => 3, +); + +$np->add_arg( + spec => 'ignore|i=s', + help => 'Node names to ignore (comma-separated list) (default: %s)', + default => '', +); + +$np->getopts; + +my %apiurls = ( + 3 => { 'nodes' => 'v3/nodes', 'event-counts' => 'v3/event-counts' }, + 4 => { 'nodes' => 'pdb/query/v4/nodes', 'event-counts' => 'pdb/query/v4/event-counts', 'logs' => 'pdb/query/v4/reports/{hash}/logs' }, +); +if ( !exists $apiurls{$np->opts->apiversion} ) { + $np->nagios_exit( 'UNKNOWN', 'Unsupported PuppetDB API version ' . $np->opts->apiversion ); +} + +my @ignore_list = split( ',', $np->opts->ignore ); + +my $url = sprintf( 'http%s://%s:%d/', + defined( $np->opts->ssl ) ? 's' : '', + $np->opts->hostname, $np->opts->port ); + +my $ua = new LWP::UserAgent; +$ua->default_header( 'Accept' => 'application/json' ); +if ( defined( $np->opts->insecure ) ) { + $ua->ssl_opts( verify_hostname => 0 ,SSL_verify_mode => 0x00); +} + +my %parameters = (); +if ( defined( $np->opts->node ) ) { + %parameters = ( 'query' => '["=","certname","' . $np->opts->node . '"]' ); +} +my $uri = URI->new( $url . $apiurls{$np->opts->apiversion}{'nodes'} ); +$uri->query_form(%parameters); +my $response = $ua->get($uri); + +if ( !$response->is_success ) { + $np->nagios_exit( 'UNKNOWN', + $response->code . ": " . $response->status_line ); +} + +my $data = decode_json( $response->decoded_content ); + +my $now = time(); + +if ( defined( $np->opts->node ) and !@$data ) { + $np->add_message( CRITICAL, + $np->opts->node . " not found in puppetdb\n" ); +} + +foreach my $node (@$data) { + my $certname = defined($node->{'certname'}) ? $node->{'certname'} : $node->{'name'} ; + my $deactivated = $node->{'deactivated'}; + my $catalog_timestamp = $node->{'catalog_timestamp'}; + my $report_hash = $node->{'latest_report_hash'}; + my $ts = str2time($catalog_timestamp); + + next if grep { $certname eq $_ } @ignore_list; + + if ( !defined $deactivated and !length $catalog_timestamp ) { + $np->add_message( CRITICAL, + "$certname last run UNAVAILABLE\n" ); + } + if ( !defined $deactivated and length $catalog_timestamp ) { + my $delta = ( $now - $ts ); + if ( $delta > ( $np->opts->critical * 60 ) ) { + $np->add_message( CRITICAL, + "$certname did not update since $catalog_timestamp\n" ); + } + elsif ( $delta > ( $np->opts->warning * 60 ) ) { + $np->add_message( WARNING, + "$certname did not update since $catalog_timestamp\n" ); + } + + my %apiparameters = ( + 3 => { + 'query' => '["and",["=","certname","' + . $certname + . '"],["=","latest-report?",true]]', + 'summarize-by' => 'certname', + 'count-by' => 'resource', + }, + 4 => { + 'query' => '["and",["=","certname","' + . $certname + . '"],["=","latest_report?",true]]', + 'summarize_by' => 'certname', + 'count_by' => 'resource', + } + ); + my $uri = URI->new( $url . $apiurls{$np->opts->apiversion}{'event-counts'} ); + $uri->query_form($apiparameters{$np->opts->apiversion}); + $response = $ua->get($uri); + + if ( $response->is_success ) { + my $node_data = decode_json( $response->decoded_content ); + + my $failures = 0; + if ( defined( @$node_data[0] ) + and defined( @$node_data[0]->{'failures'} ) ) + { + $failures = @$node_data[0]->{'failures'}; + } + + if ( $failures >= $np->opts->critfails ) { + $np->add_message( CRITICAL, + "$certname had $failures failures in the last run\n" ); + } + elsif ( $failures >= $np->opts->warnfails ) { + $np->add_message( WARNING, + "$certname had $failures failures in the last run\n" ); + } + elsif ( exists $apiurls{$np->opts->apiversion}{'logs'} and $report_hash) { + my $apiurl = $apiurls{$np->opts->apiversion}{'logs'}; + $apiurl =~ s/{hash}/$report_hash/; + $uri = URI->new( $url . $apiurl ); + $response = $ua->get($uri); + if ( $response->is_success ) { + my $logs = decode_json( $response->decoded_content ); + foreach my $log (@$logs) { + my $tags = $log->{'tags'}; + if ( grep(/^err$/, @$tags) ) { + $np->add_message( WARNING, "$certname, $log->{'message'}" ); + } + } + } + } + + } else { + $np->nagios_exit( 'UNKNOWN', 'Unsupported query ' . $response->decoded_content); + } + + } +} + +my $code; +my $message; +( $code, $message ) = $np->check_messages; + +$np->nagios_exit( $code, $message ); diff --git a/dsa-nagios-checks/debian/changelog b/dsa-nagios-checks/debian/changelog index c7ffccd..664e04b 100644 --- a/dsa-nagios-checks/debian/changelog +++ b/dsa-nagios-checks/debian/changelog @@ -1,6 +1,7 @@ dsa-nagios-checks (119) UNRELEASED; urgency=medium * dsa-check-raid-sw: correctly parse resync percentages under 10%. + * Add check_puppetdb_nodes. -- Peter Palfrader Mon, 20 May 2019 12:52:00 +0200 -- 2.20.1