diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ad453e..5473a43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.2.0 (2017-XX-XX) +- added command server to check status of Load Balancing Servers +- added command hwinfo to just print information about the Netscaler itself +- added command interfaces to check state of all interfaces and add performance data for each interface +- added command to request performance data +- added command to check the state of a servicegroup and its members (set warning and critical values for member quorum) +- added Icinga2 config templates + ## 1.1.1 (2017-06-10) - bugfix for servicegroups in 12.0 (#12) - new option to connect to an alternate port (for CPX instances) diff --git a/README.md b/README.md index 27e2e5b..88668f1 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,11 @@ Currently the plugin has the following subcommands: - **sslcert:** check the lifetime for installed ssl certificates - **nsconfig:** check for configuration changes which are not saved to disk - **staserver:** check if configured STA servers are available +- **server:** check status of Load Balancing Servers +- **servicegroup:** check the state of a servicegroup and its members +- **hwinfo:** just print information about the Netscaler itself +- **interfaces:** check state of all interfaces and add performance data for each interface +- **performancedata:** gather performancedata from all sorts of API endpoints - **debug:** debug command, print all data for a endpoint This plugin works with VPX, MPX, SDX and CPX NetScaler Appliances. The api responses may differ by build, appliance type and your installed license. @@ -108,6 +113,51 @@ If you want to connect to your NetScaler with SSL/HTTPS you should also install # NetScaler::STA::vs_vpn_gateway ./check_netscaler.pl -H ${IPADDR} -s -C staserver -n vs_vpn_gateway +## Check if Load Balancer server are working + + # NetScaler::Server + ./check_netscaler.pl -H ${IPADDR} -s -C server + +## Check status of a service group +##### define member quorum (in percent) with warning and critical values + # NetScaler::Servicegroup::vs_vpn_gateway + ./check_netscaler.pl -H ${IPADDR} -s -C servicegroup -n vs_vpn_gateway -w 75 -c 50 + +## Get information about the netscaler + + # NetScaler::HWInfo + ./check_netscaler.pl -H ${IPADDR} -s -C hwinfo + +## Check status of all network interfaces + + # NetScaler::Interfaces + ./check_netscaler.pl -H ${IPADDR} -s -C interfaces + +## Request performance data +##### all fields must be defined via "-n" option and be seperated with a comma + + # NetScaler::Performancedata on Cache hit/misses + ./check_netscaler.pl -H ${IPADDR} -s -C performancedata -o ns -n cachetothits,cachetotmisses + + # NetScaler::Performancedata on tcp connections + ./check_netscaler.pl -H ${IPADDR} -s -C performancedata -o ns -n tcpcurclientconn,tcpcurclientconnestablished,tcpcurserverconn,tcpcurserverconnestablished + + # NetScaler::Performancedata on network interfaces + ./check_netscaler.pl -H ${IPADDR} -s -C performancedata -o Interface -n id.totrxbytes + + # NetScaler::Current user sessions + ./check_netscaler.pl -H ${IPADDR} -s -C performancedata -o aaa -n aaacuricasessions,aaacuricaonlyconn + + # find more object names to check out for object type "ns" + /check_netscaler.pl -H ${IPADDR} -s -C debug -o ns + + # more interesting performance data object types + * ns + * cache + * protocolhttp + * protocolip + * protocoltcp + ## Debug command # Print all LB vServers (stat endpoint) ./check_netscaler.pl -H ${IPADDR} -s -C debug -o lbvserver @@ -133,6 +183,14 @@ username=nagios password=password ``` +# Authors +- @slauger + +# Contributors +- @macampo +- @Velociraptor85 +- @bb-ricardo + # NITRO API Documentation You will find a full documentation about the NITRO API on your NetScaler Appliance in the "Download" area. diff --git a/check_netscaler.pl b/check_netscaler.pl index 3a31e7b..ce429f9 100755 --- a/check_netscaler.pl +++ b/check_netscaler.pl @@ -1,11 +1,13 @@ #!/usr/bin/perl ############################################################################## # check_netscaler.pl -# Nagios Plugin for Citrix NetScaler +# Nagios Plugin for Citrix NetScaler # Simon Lauger # # https://github.com/slauger/check_netscaler # +# Version: 1.2.0 (2017-XX-XX) +# # Copyright 2015-2017 Simon Lauger # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,7 +35,7 @@ my $plugin = Nagios::Plugin->new( plugin => 'check_netscaler', shortname => 'NetScaler', - version => '1.1.1', + version => '1.2.X', url => 'https://github.com/slauger/check_netscaler', blurb => 'Nagios Plugin for Citrix NetScaler Appliance (VPX/MPX/SDX/CPX)', usage => 'Usage: %s -H [ -u ] [ -p ] @@ -120,6 +122,12 @@ desc => 'Value for critical', required => 0, }, + { + spec => 'urlopts|x=s', + usage => '-x, --urlopts=STRING', + desc => 'DEBUG ONLY: add additional url options', + required => 0, + }, ); foreach my $arg (@args) { @@ -133,16 +141,16 @@ check_state($plugin); } elsif ($plugin->opts->command eq 'above') { # check if a response is above a threshold - check_threshold_above($plugin); + check_threshold($plugin, $plugin->opts->command); } elsif ($plugin->opts->command eq 'below') { # check if a response is below a threshold - check_threshold_below($plugin); + check_threshold($plugin, $plugin->opts->command); } elsif ($plugin->opts->command eq 'string') { # check if a response does contains a specific string - check_string($plugin); + check_string($plugin, "matches"); } elsif ($plugin->opts->command eq 'string_not') { # check if a response does not contains a specific string - check_string_not($plugin); + check_string($plugin, "matches not"); } elsif ($plugin->opts->command eq 'sslcert') { # check for the lifetime of installed certificates check_sslcert($plugin); @@ -152,8 +160,23 @@ } elsif ($plugin->opts->command eq 'staserver') { # check the state of the staservers check_staserver($plugin); +} elsif ($plugin->opts->command eq 'server') { + # check the state of the servers + check_server($plugin); +} elsif ($plugin->opts->command eq 'hwinfo') { + # print infos about hardware and build version + get_hardware_info($plugin); +} elsif ($plugin->opts->command eq 'performancedata') { + # print performance data of protocol stats + get_performancedata($plugin); +} elsif ($plugin->opts->command eq 'interfaces') { + # check the state of all interfaces + check_interfaces($plugin); +} elsif ($plugin->opts->command eq 'servicegroup') { + # check the state of a servicegroup and its members + check_servicegroup($plugin); } elsif ($plugin->opts->command eq 'debug') { - # dump the full response of the nitro api + # dump the full response of the nitro api check_debug($plugin); } else { # error, unkown command given @@ -197,19 +220,19 @@ sub nitro_client { my $plugin = shift; my $params = shift; - + my $lwp = LWP::UserAgent->new( - env_proxy => 1, - keep_alive => 1, - timeout => $plugin->opts->timeout, - ssl_opts => { - verify_hostname => 0, + env_proxy => 1, + keep_alive => 1, + timeout => $plugin->opts->timeout, + ssl_opts => { + verify_hostname => 0, SSL_verify_mode => 0 }, ); - + my $protocol = undef; - + if ($plugin->opts->ssl) { $protocol = 'https://'; } else { @@ -229,58 +252,58 @@ sub nitro_client { if ($params->{'objectname'} && $params->{'objectname'} ne '') { $url = $url . '/' . uri_escape(uri_escape($params->{'objectname'})); } - + if ($params->{'options'} && $params->{'options'} ne '') { $url = $url . '?' . $params->{'options'}; } - + if ($plugin->opts->verbose) { print "debug: target url is " . $url . "\n"; } - + my $request = HTTP::Request->new(GET => $url); - + $request->header('X-NITRO-USER', $plugin->opts->username); $request->header('X-NITRO-PASS', $plugin->opts->password); $request->header('Content-Type', 'application/vnd.com.citrix.netscaler.' . $params->{'objecttype'} . '+json'); - + my $response = $lwp->request($request); - + if ($plugin->opts->verbose) { print "debug: response of request is:\n"; print Dumper($response->content); } - + if (HTTP::Status::is_error($response->code)) { $plugin->nagios_die($response->content); } else { $response = JSON->new->allow_blessed->convert_blessed->decode($response->content); } - + return $response; } sub check_state { my $plugin = shift; - + if (!defined $plugin->opts->objecttype) { $plugin->nagios_die('command requires objecttype parameter'); } - + my %counter; - + $counter{'up'} = 0; $counter{'down'} = 0; $counter{'oos'} = 0; $counter{'unkown'} = 0; my %params; - + my $field_name; my $field_state; - - # well, i guess the citrix api developers were drunk + + # well, i guess the citrix api developers were drunk if ($plugin->opts->objecttype eq 'service') { $params{'endpoint'} = $plugin->opts->endpoint || 'config'; $field_name = 'name'; @@ -294,14 +317,14 @@ sub check_state $field_name = 'name'; $field_state = 'state'; } - + $params{'objecttype'} = $plugin->opts->objecttype; $params{'objectname'} = $plugin->opts->objectname; $params{'options'} = undef; my $response = nitro_client($plugin, \%params); $response = $response->{$plugin->opts->objecttype}; - + foreach my $response (@{$response}) { if ($response->{$field_state} eq 'UP') { $counter{'up'}++; @@ -321,11 +344,11 @@ sub check_state $counter{'unkown'}++; $plugin->add_message(CRITICAL, $response->{$field_name} . ' unknown;'); } - } + } my ($code, $message) = $plugin->check_messages; - + my $stats = ' (' . $counter{'up'} . ' up, ' . $counter{'down'} . ' down, ' . $counter{'oos'} . ' oos, ' . $counter{'unkown'} . ' unkown)'; - + $plugin->add_perfdata( label => 'up', value => $counter{'up'}, @@ -364,11 +387,12 @@ sub check_state sub check_string { my $plugin = shift; - + my $type_of_string_comparison = shift; + if (!defined $plugin->opts->objecttype) { $plugin->nagios_die('command requires parameter for objecttype'); } - + if (!defined $plugin->opts->objectname) { $plugin->nagios_die('command requires parameter for objectname'); } @@ -377,38 +401,8 @@ sub check_string $plugin->nagios_die('command requires parameter for warning and critical'); } - my %params; - $params{'endpoint'} = $plugin->opts->endpoint || 'stat'; - $params{'objecttype'} = $plugin->opts->objecttype; - $params{'objectname'} = undef; - $params{'options'} = undef; - - my $response = nitro_client($plugin, \%params); - $response = $response->{$plugin->opts->objecttype}; - - if ($response->{$plugin->opts->objectname} eq $plugin->opts->critical) { - $plugin->nagios_exit(CRITICAL, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' matches keyword (current: ' . $response->{$plugin->opts->objectname} . ', critical: ' . $plugin->opts->critical . ')'); - } elsif ($response->{$plugin->opts->objectname} eq $plugin->opts->warning) { - $plugin->nagios_exit(WARNING, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' matches keyword (current: ' . $response->{$plugin->opts->objectname} . ', warning: ' . $plugin->opts->warning . ')'); - } else { - $plugin->nagios_exit(OK, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' OK ('.$response->{$plugin->opts->objectname}.')'); - } -} - -sub check_string_not -{ - my $plugin = shift; - - if (!defined $plugin->opts->objecttype) { - $plugin->nagios_die('command requires parameter for objecttype'); - } - - if (!defined $plugin->opts->objectname) { - $plugin->nagios_die('command requires parameter for objectname'); - } - - if (!defined $plugin->opts->warning || !defined $plugin->opts->critical) { - $plugin->nagios_die('command requires parameter for warning and critical'); + if ($type_of_string_comparison ne "matches" && $type_of_string_comparison ne "matches not") { + $plugin->nagios_die('string can only be checked for "matches" and "matches not"'); } my %params; @@ -416,27 +410,28 @@ sub check_string_not $params{'objecttype'} = $plugin->opts->objecttype; $params{'objectname'} = undef; $params{'options'} = undef; - + my $response = nitro_client($plugin, \%params); $response = $response->{$plugin->opts->objecttype}; - - if ($response->{$plugin->opts->objectname} ne $plugin->opts->critical) { - $plugin->nagios_exit(CRITICAL, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' not matches keyword (current: ' . $response->{$plugin->opts->objectname} . ', critical: ' . $plugin->opts->critical . ')'); - } elsif ($response->{$plugin->opts->objectname} ne $plugin->opts->warning) { - $plugin->nagios_exit(WARNING, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' not matches keyword (current: ' . $response->{$plugin->opts->objectname} . ', warning: ' . $plugin->opts->warning . ')'); + + if (($type_of_string_comparison eq "matches" && $response->{$plugin->opts->objectname} eq $plugin->opts->critical) || ($type_of_string_comparison eq "matches not" && $response->{$plugin->opts->objectname} ne $plugin->opts->critical)) { + $plugin->nagios_exit(CRITICAL, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' ' . $type_of_string_comparison . ' keyword (current: ' . $response->{$plugin->opts->objectname} . ', critical: ' . $plugin->opts->critical . ')'); + } elsif (($type_of_string_comparison eq "matches" && $response->{$plugin->opts->objectname} eq $plugin->opts->warning) || ($type_of_string_comparison eq "matches not" && $response->{$plugin->opts->objectname} ne $plugin->opts->warning)) { + $plugin->nagios_exit(WARNING, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' ' . $type_of_string_comparison . ' keyword (current: ' . $response->{$plugin->opts->objectname} . ', warning: ' . $plugin->opts->warning . ')'); } else { $plugin->nagios_exit(OK, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' OK ('.$response->{$plugin->opts->objectname}.')'); } } -sub check_threshold_above +sub check_threshold { my $plugin = shift; + my $direction = shift; if (!defined $plugin->opts->objecttype) { $plugin->nagios_die('command requires parameter for objecttype'); } - + if (!defined $plugin->opts->objectname) { $plugin->nagios_die('command requires parameter for objectname'); } @@ -445,12 +440,16 @@ sub check_threshold_above $plugin->nagios_die('command requires parameter for warning and critical'); } + if ($direction ne "above" && $direction ne "below") { + $plugin->nagios_die('threshold can only be checked for "above" and "below"'); + } + my %params; $params{'endpoint'} = $plugin->opts->endpoint || 'stat'; $params{'objecttype'} = $plugin->opts->objecttype; $params{'objectname'} = undef; $params{'options'} = undef; - + my $response = nitro_client($plugin, \%params); $response = $response->{$plugin->opts->objecttype}; @@ -458,72 +457,29 @@ sub check_threshold_above $plugin->add_perfdata( label => $plugin->opts->objecttype . '::' . $plugin->opts->objectname, value => $response->{$plugin->opts->objectname}, - min => 0, + min => undef, max => undef, warning => $plugin->opts->warning, critical => $plugin->opts->critical, ); - if ($response->{$plugin->opts->objectname} >= $plugin->opts->critical) { - $plugin->nagios_exit(CRITICAL, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' is above threshold (current: ' . $response->{$plugin->opts->objectname} . ', critical: ' . $plugin->opts->critical . ')'); - } elsif ($response->{$plugin->opts->objectname} >= $plugin->opts->warning) { - $plugin->nagios_exit(WARNING, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' is above threshold (current: ' . $response->{$plugin->opts->objectname} . ', warning: ' . $plugin->opts->warning . ')'); + if (($direction eq "above" && $response->{$plugin->opts->objectname} >= $plugin->opts->critical) || ($direction eq "below" && $response->{$plugin->opts->objectname} <= $plugin->opts->critical)) { + $plugin->nagios_exit(CRITICAL, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' is ' . $direction . ' threshold (current: ' . $response->{$plugin->opts->objectname} . ', critical: ' . $plugin->opts->critical . ')'); + } elsif (($direction eq "above" && $response->{$plugin->opts->objectname} >= $plugin->opts->warning) || ($direction eq "below" && $response->{$plugin->opts->objectname} <= $plugin->opts->warning)) { + $plugin->nagios_exit(WARNING, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' is ' . $direction . ' threshold (current: ' . $response->{$plugin->opts->objectname} . ', warning: ' . $plugin->opts->warning . ')'); } else { $plugin->nagios_exit(OK, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' OK ('.$response->{$plugin->opts->objectname}.')'); } } -sub check_threshold_below +sub check_sslcert { my $plugin = shift; - - if (!defined $plugin->opts->objecttype) { - $plugin->nagios_die('command requires parameter for objecttype'); - } - - if (!defined $plugin->opts->objectname) { - $plugin->nagios_die('command requires parameter for objectname'); - } if (!defined $plugin->opts->warning || !defined $plugin->opts->critical) { $plugin->nagios_die('command requires parameter for warning and critical'); } - my %params; - $params{'endpoint'} = $plugin->opts->endpoint || 'stat'; - $params{'objecttype'} = $plugin->opts->objecttype; - $params{'objectname'} = undef; - $params{'options'} = undef; - - my $response = nitro_client($plugin, \%params); - $response = $response->{$plugin->opts->objecttype}; - - $plugin->add_perfdata( - label => $plugin->opts->objecttype . '::' . $plugin->opts->objectname, - value => $response->{$plugin->opts->objectname}, - min => 0, - max => undef, - warning => $plugin->opts->warning, - critical => $plugin->opts->critical, - ); - - if ($response->{$plugin->opts->objectname} <= $plugin->opts->critical) { - $plugin->nagios_exit(CRITICAL, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' is below threshold (current: ' . $response->{$plugin->opts->objectname} . ', critical: ' . $plugin->opts->critical . ')'); - } elsif ($response->{$plugin->opts->objectname} <= $plugin->opts->warning) { - $plugin->nagios_exit(WARNING, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' is below threshold (current: ' . $response->{$plugin->opts->objectname} . ', warning: ' . $plugin->opts->warning . ')'); - } else { - $plugin->nagios_exit(OK, $plugin->opts->objecttype . '::' . $plugin->opts->objectname . ' ('.$response->{$plugin->opts->objectname}.')'); - } -} - -sub check_sslcert -{ - my $plugin = shift; - - if (!defined $plugin->opts->warning || !defined $plugin->opts->critical) { - $plugin->nagios_die('command requires parameter for warning and critical'); - } - my %params; $params{'endpoint'} = $plugin->opts->endpoint || 'config'; $params{'objecttype'} = $plugin->opts->objecttype || 'sslcertkey'; @@ -535,14 +491,14 @@ sub check_sslcert foreach $response (@{$response}) { if ($response->{daystoexpiration} <= $plugin->opts->critical) { - $plugin->add_message(CRITICAL, $response->{certkey} . ' expires in ' . $response->{daystoexpiration} . ' days;'); + $plugin->add_message(CRITICAL, $response->{certkey} . ' expires in ' . $response->{daystoexpiration} . ' days;'); } elsif ($response->{daystoexpiration} <= $plugin->opts->warning) { $plugin->add_message(WARNING, $response->{certkey} . ' expires in ' . $response->{daystoexpiration} . ' days;'); } } - + my ($code, $message) = $plugin->check_messages; - + if ($code == OK) { $plugin->nagios_exit($code, 'sslcertkey OK'); } else { @@ -553,7 +509,7 @@ sub check_sslcert sub check_staserver { my $plugin = shift; - + my %params; $params{'endpoint'} = $plugin->opts->endpoint || 'config'; $params{'objectname'} = $plugin->opts->objectname || ''; @@ -564,13 +520,13 @@ sub check_staserver } else { $params{'objecttype'} = $plugin->opts->objecttype || 'vpnvserver_staserver_binding'; } - + my $response = nitro_client($plugin, \%params); $response = $response->{$params{'objecttype'}}; - + # return critical if all staservers are down at once my $critical = 1; - + # check if any stas are in down state foreach $response (@{$response}) { if ($response->{'staauthid'} eq '') { @@ -580,20 +536,51 @@ sub check_staserver $critical = 0; } } - + my ($code, $message) = $plugin->check_messages; - - if ($critical) { - $plugin->nagios_exit(CRITICAL, 'staservice ' . $message); - } else { - $plugin->nagios_exit($code, 'staservice ' . $message); + + if ( $critical == 1) { $code = CRITICAL ; } + + $plugin->nagios_exit($code, 'server ' . $message); +} + +sub check_server +{ + my $plugin = shift; + + my %params; + $params{'endpoint'} = $plugin->opts->endpoint || 'config'; + $params{'objectname'} = $plugin->opts->objectname || ''; + $params{'options'} = undef; + $params{'objecttype'} = "server"; + + my $response = nitro_client($plugin, \%params); + $response = $response->{$params{'objecttype'}}; + + # return critical if all staservers are down at once + my $critical = 1; + + # check if any stas are in down state + foreach $response (@{$response}) { + if ($response->{'state'} ne 'ENABLED') { + $plugin->add_message(WARNING, $response->{'name'} . '('. $response->{'ipaddress'} .') ' . $response->{'state'} . ' ;'); + } else { + $plugin->add_message(OK, $response->{'name'} . '('. $response->{'ipaddress'} .') ' . $response->{'state'} . ' ;'); + $critical = 0; + } } + + my ($code, $message) = $plugin->check_messages; + + if ( $critical == 1) { $code = CRITICAL ; } + + $plugin->nagios_exit($code, 'server ' . $message); } sub check_nsconfig { my $plugin = shift; - + my %params; $params{'endpoint'} = $plugin->opts->endpoint || 'config'; $params{'objecttype'} = $plugin->opts->objecttype || 'nsconfig'; @@ -602,7 +589,7 @@ sub check_nsconfig my $response = nitro_client($plugin, \%params); $response = $response->{$params{'objecttype'}}; - + if (!defined $response->{'configchanged'} || $response->{'configchanged'}) { $plugin->nagios_exit(WARNING, 'nsconfig::configchanged unsaved configuration changes'); } else { @@ -610,17 +597,282 @@ sub check_nsconfig } } -sub check_debug +sub get_hardware_info +{ + my $plugin = shift; + + my %params; + $params{'endpoint'} = 'config'; + $params{'objecttype'} = 'nshardware'; + $params{'objectname'} = undef; + $params{'options'} = undef; + + my $response = nitro_client($plugin, \%params); + $response = $response->{$params{'objecttype'}}; + + $plugin->add_message(OK, "Platform: " . $response->{'hwdescription'} . ' ' . $response->{'sysid'} . ';'); + $plugin->add_message(OK, "Manufactured on: " . $response->{'manufactureyear'} . '/' . $response->{'manufacturemonth'} . '/' . $response->{'manufactureday'} . ';'); + $plugin->add_message(OK, "CPU: " . $response->{'cpufrequncy'} . 'MHz;'); + $plugin->add_message(OK, "Serial no: " . $response->{'serialno'} . ';'); + + $params{'objecttype'} = 'nsversion'; + + $response = nitro_client($plugin, \%params); + $response = $response->{$params{'objecttype'}}; + + $plugin->add_message(OK, "Build Version: " . $response->{'version'} . ';'); + + my ($code, $message) = $plugin->check_messages; + $plugin->nagios_exit($code, 'INFO: ' . $message); +} + +sub get_performancedata { my $plugin = shift; - + my %params; $params{'endpoint'} = $plugin->opts->endpoint || 'stat'; $params{'objecttype'} = $plugin->opts->objecttype; + $params{'objectname'} = undef; + $params{'options'} = undef; + + if (not defined ($plugin->opts->objectname)) { + $plugin->nagios_die('performancedata: no object name(s) "-n" set'); + } + + my $response = nitro_client($plugin, \%params); + $response = $response->{$params{'objecttype'}}; + + if ( ref $response eq "ARRAY" ) { + foreach $response (@{$response}) { + foreach my $objectname (split(",",$plugin->opts->objectname)) { + if (not index($objectname, ".") != -1) { + $plugin->nagios_die('performancedata: return data is an array and contains multible objects. You need te seperate id and name with a ".".'); + } + + my ($objectname_id, $objectname_name) = split /\./, $objectname; + + if (not defined($response->{$objectname_id})) { + $plugin->nagios_die('performancedata: object id "' . $objectname_id . '" not found in output.'); + } + if (not defined($response->{$objectname_name})) { + $plugin->nagios_die('performancedata: object name "' . $objectname_name . '" not found in output.'); + } + + $plugin->add_message(OK, $params{'objecttype'} . "." . $response->{$objectname_id} . "." . $objectname_name . ":" . $response->{$objectname_name} . ","); + + $plugin->add_perfdata( + label => "'" . $params{'objecttype'} . "." . $response->{$objectname_id} . "." . $objectname_name . "'", + value => $response->{$objectname_name}, + min => undef, + max => undef, + warning => $plugin->opts->warning, + critical => $plugin->opts->critical, + ); + } + } + } elsif ( ref $response eq "HASH" ) { + foreach my $objectname (split(",",$plugin->opts->objectname)) { + if (not defined($response->{$objectname})) { + $plugin->nagios_die('performancedata: object name "' . $objectname . '" not found in output.'); + } + $plugin->add_message(OK, $params{'objecttype'} . "." . $objectname .":", $response->{$objectname}. ","); + + $plugin->add_perfdata( + label => "'" . $params{'objecttype'} . "." . $objectname . "'", + value => $response->{$objectname}, + min => undef, + max => undef, + warning => $plugin->opts->warning, + critical => $plugin->opts->critical, + ); + } + } else { + $plugin->nagios_die('performancedata: unable to parse data. Returned data is not a HASH or ARRAY!'); + } + + my ($code, $message) = $plugin->check_messages; + $plugin->nagios_exit($code, 'performancedata: ' . $message); +} + +sub check_interfaces +{ + my $plugin = shift; + my @interface_errors; + + my %params; + $params{'endpoint'} = 'config'; + $params{'objecttype'} = 'interface'; + $params{'objectname'} = undef; + $params{'options'} = undef; + + my $response = nitro_client($plugin, \%params); + + foreach my $interface (@{$response->{'Interface'}}) { + + my $interface_state = OK; + + my $interface_speed = "N/A"; + if ($interface->{'actspeed'}) { $interface_speed = $interface->{'actspeed'}; } + + if ($interface->{'linkstate'} != 1 ) { + push(@interface_errors, "interface " . $interface->{'devicename'} . " has linkstate \"DOWN\""); + $interface_state = CRITICAL; + } + if ($interface->{'intfstate'} != 1 ) { + push(@interface_errors, "interface " . $interface->{'devicename'} . " has intstate \"DOWN\""); + $interface_state = CRITICAL; + } + if ($interface->{'state'} ne "ENABLED" ) { + push(@interface_errors, "interface " . $interface->{'devicename'} . " has state \"".$interface->{'state'}."\""); + $interface_state = CRITICAL; + } + + $plugin->add_message($interface_state, "device: " . $interface->{'devicename'} . ' (speed: ' . $interface_speed . ', MTU: ' . $interface->{'actualmtu'} . ', VLAN: ' . $interface->{'vlan'} . ', type: ' . $interface->{'intftype'} . ') ' . $interface->{'state'} . ';'); + + $plugin->add_perfdata( + label => "\'".$interface->{'devicename'} . ".rxbytes'", + value => $interface->{'rxbytes'}."B" + ); + $plugin->add_perfdata( + label => "\'".$interface->{'devicename'} . ".txbytes'", + value => $interface->{'txbytes'}."B" + ); + $plugin->add_perfdata( + label => "\'".$interface->{'devicename'} . ".rxerrors'", + value => $interface->{'rxerrors'}."c" + ); + $plugin->add_perfdata( + label => "\'".$interface->{'devicename'} . ".txerrors'", + value => $interface->{'txerrors'}."c" + ); + } + + my ($code, $message) = $plugin->check_messages; + if (scalar @interface_errors != 0 ) { + $message = join(", ",@interface_errors). " - ". $message + } + $plugin->nagios_exit($code, 'Interfaces: ' . $message); +} + +sub check_servicegroup +{ + my $plugin = shift; + my @servicegroup_errors; + + # define quorum (in percent) of working servicegroup members + my $member_quorum_warning = $plugin->opts->warning || "90"; + my $member_quorum_critical = $plugin->opts->critical || "50"; + + my %member_state; + + my %params; + $params{'endpoint'} = 'config'; + $params{'objecttype'} = 'servicegroup'; $params{'objectname'} = $plugin->opts->objectname; $params{'options'} = undef; - + + if (not defined ($plugin->opts->objectname)) { + $plugin->nagios_die('servicegroup: no object name "-n" set'); + } + + my %healthy_servicegroup_states; + $healthy_servicegroup_states{"state"} = "ENABLED"; + $healthy_servicegroup_states{"servicegroupeffectivestate"} = "UP"; + $healthy_servicegroup_states{"monstate"} = "ENABLED"; + $healthy_servicegroup_states{"healthmonitor"} = "YES"; + + my %healthy_servicegroup_member_states; + $healthy_servicegroup_member_states{"state"} = "ENABLED"; + $healthy_servicegroup_member_states{"svrstate"} = "UP"; + my $response = nitro_client($plugin, \%params); - + my $servicegroup_response = $response->{$params{'objecttype'}}; + my $servicegroup_state = OK; + + # check servicegroup health status + foreach my $servicegroup_response (@{$servicegroup_response}) { + + foreach my $servicegroup_check_key ( keys %healthy_servicegroup_states ) { + + if ($servicegroup_response->{$servicegroup_check_key} ne $healthy_servicegroup_states{$servicegroup_check_key}) { + push(@servicegroup_errors, 'servicegroup ' . $servicegroup_response->{"servicegroupname"} . ' "'. $servicegroup_check_key . '" is: '. $healthy_servicegroup_states{$servicegroup_check_key}); + } + } + $plugin->add_message(OK, $servicegroup_response->{'servicegroupname'} . ' (' . $servicegroup_response->{'servicetype'} . ') - state: ' . $servicegroup_response->{'servicegroupeffectivestate'} . ' -'); + } + + # get servicegroup members status + $params{'objecttype'} = 'servicegroup_servicegroupmember_binding'; + + $response = nitro_client($plugin, \%params); + my $servicegroup_members_response = $response->{$params{'objecttype'}}; + + # check servicegroup members health status + foreach my $servicegroup_members_response (@{$servicegroup_members_response}) { + + foreach my $servicegroup_members_check_key ( keys %healthy_servicegroup_member_states ) { + + if ($servicegroup_members_response->{$servicegroup_members_check_key} ne $healthy_servicegroup_member_states{$servicegroup_members_check_key}) { + push(@servicegroup_errors, 'servicegroup member ' . $servicegroup_members_response->{"servername"} . ' "'. $servicegroup_members_check_key . '" is '. $healthy_servicegroup_member_states{$servicegroup_members_check_key}); + $member_state{$servicegroup_members_response->{"servername"}} = "DOWN"; + } + } + if (not defined $member_state{$servicegroup_members_response->{"servername"}}) { + $member_state{$servicegroup_members_response->{"servername"}} = "UP"; + } + $plugin->add_message(OK, $servicegroup_members_response->{'servername'} . ' (' . $servicegroup_members_response->{'ip'}.':'. $servicegroup_members_response->{'port'} . ') is ' . $servicegroup_members_response->{'svrstate'} .','); + } + + # count states + my $members_up = 0; + my $members_down = 0; + foreach my $member_state_key ( keys %member_state ) { + if ($member_state{$member_state_key} eq "DOWN") { + $members_down++; + } else { + $members_up++; + } + } + + # check quorum + my $member_quorum = sprintf("%1.2f", 100 / ( $members_up + $members_down ) * $members_up); + + if ($member_quorum <= $member_quorum_critical) { + $servicegroup_state = CRITICAL; + } elsif ($member_quorum <= $member_quorum_warning) { + $servicegroup_state = WARNING; + } + + $plugin->add_message(OK, "member quorum: " . $member_quorum . "% (UP/DOWN): " . $members_up . "/" . $members_down); + + $plugin->add_perfdata( + label => "'" . $plugin->opts->objectname . ".member_quorum'", + value => $member_quorum."%", + min => 0, + max => 100, + warning => $member_quorum_warning, + critical => $member_quorum_critical, + ); + + my ($code, $message) = $plugin->check_messages; + if (scalar @servicegroup_errors != 0 ) { + $message = join(", ",@servicegroup_errors). " - ". $message + } + $plugin->nagios_exit($servicegroup_state, 'servicegroup: ' . $message); +} + +sub check_debug +{ + my $plugin = shift; + + my %params; + $params{'endpoint'} = $plugin->opts->endpoint || 'stat'; + $params{'objecttype'} = $plugin->opts->objecttype; + $params{'objectname'} = $plugin->opts->objectname; + $params{'options'} = $plugin->opts->urlopts; + + my $response = nitro_client($plugin, \%params); + print Dumper($response); } diff --git a/examples/icinga2_command.conf b/examples/icinga2_command.conf new file mode 100644 index 0000000..3494fce --- /dev/null +++ b/examples/icinga2_command.conf @@ -0,0 +1,23 @@ +object CheckCommand "netscaler" { + import "plugin-check-command" + + command = [ PluginDir + "/check_netscaler.pl" ] + + arguments = { + "-H" = "$address$" + "-u" = "$netscaler_user$" + "-p" = "$netscaler_password$" + "--ssl" = { + set_if = "$netscaler_ssl$" + } + "-P" = "$netscaler_port$" + "-C" = "$netscaler_command$" + "-o" = "$netscaler_objecttype$" + "-n" = "$netscaler_objectname$" + "-e" = "$netscaler_endpoint$" + "-w" = "$warning$" + "-c" = "$critical$" + "-t" = "$netscaler_timeout$" + } + +} diff --git a/examples/icinga2_service_definitions.conf b/examples/icinga2_service_definitions.conf new file mode 100644 index 0000000..e6d63d5 --- /dev/null +++ b/examples/icinga2_service_definitions.conf @@ -0,0 +1,139 @@ +/******************************* + NetScaler Checks +*******************************/ +template Service "netscaler_env_details" { + + vars.netscaler_user = "icinga" + vars.netscaler_password = "super_secret" + vars.netscaler_ssl = true +} + +apply Service "CPU" { + import "netscaler_cpu_template" + import "netscaler_env_details" + + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "Mgmt CPU" { + import "netscaler_mgmtcpu_template" + import "netscaler_env_details" + + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "MEM" { + import "netscaler_mem_template" + import "netscaler_env_details" + + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "Disk 0" { + import "netscaler_disk0_template" + import "netscaler_env_details" + + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "Disk 1" { + import "netscaler_disk1_template" + import "netscaler_env_details" + + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "HA Status" { + import "netscaler_ha_status_template" + import "netscaler_env_details" + + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "HA State" { + import "netscaler_ha_state_template" + import "netscaler_env_details" + + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "Config Status" { + import "netscaler_config_status_template" + import "netscaler_env_details" + + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "HW Info" { + import "netscaler_hwinfo_template" + import "netscaler_env_details" + + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "Interfaces" { + import "netscaler_interfaces_template" + import "netscaler_env_details" + + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "Load Balanced server status" { + import "netscaler_server_template" + import "netscaler_env_details" + + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "STA Status " for (vpnverver in [ "my_vpnvserver" ] ) { + import "netscaler_staserver_template" + import "netscaler_env_details" + + vars.netscaler_objectname = vpnverver + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "VPNV Status " for (vpnverver in [ "my_vpnvserver_a" ] ) { + import "netscaler_vpnvserver_template" + import "netscaler_env_details" + + vars.netscaler_objectname = vpnverver + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "LB Status " for (lbvserver in [ "my_lbvserver_a", "my_lbvserver_b" ] ) { + import "netscaler_lbvserver_template" + import "netscaler_env_details" + + vars.netscaler_objectname = lbvserver + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "Servicegroup Status " for (servicegroup in [ "my_servicegroup_a", "my_servicegroup_b" ] ) { + import "netscaler_servicegroup_template" + import "netscaler_env_details" + + vars.netscaler_objectname = servicegroup + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +// Perfdata +apply Service "Perf Ctirix Sessions" { + import "netscaler_performancedata_template" + import "netscaler_env_details" + + vars.netscaler_objecttype = "aaa" + vars.netscaler_objectname = "aaacuricasessions,aaacuricaonlyconn" + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +apply Service "Perf TCP connections" { + import "netscaler_performancedata_template" + import "netscaler_env_details" + + vars.netscaler_objecttype = "ns" + vars.netscaler_objectname = "tcpcurclientconn,tcpcurclientconnestablished,tcpcurserverconn,tcpcurserverconnestablished" + assign where host.vars.hw_type == "Netscaler" && host.vars.ns_env == "my_netscaler_env_a" +} + +/* EOF */ diff --git a/examples/icinga2_service_templates.conf b/examples/icinga2_service_templates.conf new file mode 100644 index 0000000..c755847 --- /dev/null +++ b/examples/icinga2_service_templates.conf @@ -0,0 +1,188 @@ +/* + * service templates for check_netscaler.pl + */ + +// Health Checks +template Service "netscaler_cpu_template" { + import "generic-service" + import "perf_enabled" + + check_command = "netscaler" + + vars.netscaler_command = "above" + vars.netscaler_objecttype = "system" + vars.netscaler_objectname = "cpuusagepcnt" + + vars.warning = 75 + vars.critical = 80 +} + +template Service "netscaler_mem_template" { + import "generic-service" + import "perf_enabled" + + check_command = "netscaler" + + vars.netscaler_command = "above" + vars.netscaler_objecttype = "system" + vars.netscaler_objectname = "memusagepcnt" + + vars.warning = 75 + vars.critical = 80 +} + +template Service "netscaler_mgmtcpu_template" { + import "generic-service" + import "perf_enabled" + + check_command = "netscaler" + + vars.netscaler_command = "above" + vars.netscaler_objecttype = "system" + vars.netscaler_objectname = "mgmtcpuusagepcnt" + + vars.warning = 75 + vars.critical = 80 +} + +template Service "netscaler_disk0_template" { + import "generic-service" + import "perf_enabled" + + check_command = "netscaler" + + vars.netscaler_command = "above" + vars.netscaler_objecttype = "system" + vars.netscaler_objectname = "disk0perusage" + + vars.warning = 75 + vars.critical = 80 +} + +template Service "netscaler_disk1_template" { + import "generic-service" + import "perf_enabled" + + check_command = "netscaler" + + vars.netscaler_command = "above" + vars.netscaler_objecttype = "system" + vars.netscaler_objectname = "disk1perusage" + + vars.warning = 75 + vars.critical = 80 +} + +// HA Status +template Service "netscaler_ha_status_template" { + import "generic-service" + + check_command = "netscaler" + + vars.netscaler_command = "string_not" + vars.netscaler_objecttype = "hanode" + vars.netscaler_objectname = "hacurstatus" + + vars.warning = "YES" + vars.critical = "YES" +} + +template Service "netscaler_ha_state_template" { + import "generic-service" + + check_command = "netscaler" + + vars.netscaler_command = "string_not" + vars.netscaler_objecttype = "hanode" + vars.netscaler_objectname = "hacurstate" + + vars.warning = "UP" + vars.critical = "UP" +} + +// Config Changes +template Service "netscaler_config_status_template" { + import "generic-service" + + check_command = "netscaler" + + vars.netscaler_command = "nsconfig" +} + +// Netscaler info +template Service "netscaler_hwinfo_template" { + import "generic-service" + + check_command = "netscaler" + + vars.netscaler_command = "hwinfo" +} + +// Interfaces status +template Service "netscaler_interfaces_template" { + import "generic-service" + import "perf_enabled" + + check_command = "netscaler" + + vars.netscaler_command = "interfaces" +} + +// Server Status +template Service "netscaler_staserver_template" { + import "generic-service" + + check_command = "netscaler" + + vars.netscaler_command = "staserver" +} + +template Service "netscaler_server_template" { + import "generic-service" + + check_command = "netscaler" + + vars.netscaler_command = "server" +} + +template Service "netscaler_lbvserver_template" { + import "generic-service" + import "perf_enabled" + + check_command = "netscaler" + + vars.netscaler_command = "state" + vars.netscaler_objecttype = "lbvserver" +} + +template Service "netscaler_vpnvserver_template" { + import "generic-service" + import "perf_enabled" + + check_command = "netscaler" + + vars.netscaler_command = "state" + vars.netscaler_objecttype = "vpnvserver" +} + +// Servicegroup status +template Service "netscaler_servicegroup_template" { + import "generic-service" + import "perf_enabled" + + check_command = "netscaler" + + vars.netscaler_command = "servicegroup" +} + +// Performancedata +template Service "netscaler_performancedata_template" { + import "generic-service" + import "perf_enabled" + + check_command = "netscaler" + + vars.netscaler_command = "performancedata" +} + +// EOF