#!/usr/bin/perl -w ############################## check_snmp_vrrp ############## my $VERSION = "2.0.0"; # Date : Oct 17 2007 # Author : Patrick Proy (patrick at proy.org) # Help : http://nagios.manubulon.com/ # License : GPL - http://www.fsf.org/licenses/gpl.txt # Contrib : C. Maser (Alteon + Netscreen), Harm-Jan Blok (Foundry) ################################################################# # # Help : ./check_snmp_vrrp.pl -h # use strict; use Net::SNMP; use Getopt::Long; # Icinga specific my %ERRORS = ('OK' => 0, 'WARNING' => 1, 'CRITICAL' => 2, 'UNKNOWN' => 3, 'DEPENDENT' => 4); # SNMP Datas ######### Nokia (standard ???) my $all_vrrp = "1.3.6.1.2.1.68"; my $nokia_base_vrrp = "1.3.6.1.2.1.68.1.3.1"; # oid for vrrp my $nokia_vrrp_oper = "1.3.6.1.2.1.68.1.3.1.3"; # vrrp operational status my $nokia_vrrp_admin = "1.3.6.1.2.1.68.1.3.1.4"; # vrrp admin status my $nokia_vrrp_prio = "1.3.6.1.2.1.68.1.3.1.5"; # vrrp vrid priority ######### Nokia Ipso Clustering my $nokia_clust_table = "1.3.6.1.4.1.94.1.21.5.1.4.1"; # IpsoclusterEntry my $nokia_clust_index = "1.3.6.1.4.1.94.1.21.5.1.4.1.1.1"; #index my $nokia_clust_memberid = "1.3.6.1.4.1.94.1.21.5.1.4.1.2.1"; # member ID my $nokia_clust_percent = "1.3.6.1.4.1.94.1.21.5.1.4.1.3.1"; #percent assigned my $nokia_clust_rating = "1.3.6.1.4.1.94.1.21.5.1.4.1.4.1"; # node rating my $nokia_clust_addr = "1.3.6.1.4.1.94.1.21.5.1.4.1.5.1"; # ip address ######### LinkProof my $lp_base_vrrp = "1.3.6.1.2.1.68.1.3.1"; # oid for vrrp my $lp_vrrp_oper = "1.3.6.1.2.1.68.1.3.1.4"; # vrrp operational status my $lp_vrrp_admin = "1.3.6.1.2.1.68.1.3.1.5"; # vrrp admin status my $lp_vrrp_prio = "1.3.6.1.2.1.68.1.3.1.6"; # vrrp vrid priority ######### Alteon (AD4 Loadbalancers) my $alteon_base_vrrp = "1.3.6.1.4.1.1872.2.1.9.4"; my $alteon_vrrp_oper = "1.3.6.1.4.1.1872.2.1.9.4.1.1.2"; my $alteon_vrrp_admin = ""; my $alteon_vrrp_prio = "1.3.6.1.4.1.1872.2.1.9.4.1.1.3"; ######### Netscreen (ScreenOS 5.1) ### .0 is always the queried device itself ### so in a cluster every device has its own numbering of members my $ns_base_vrrp = "1.3.6.1.4.1.3224.6.2"; my $ns_vrrp_oper = "1.3.6.1.4.1.3224.6.2.2.1.3"; my $ns_vrrp_admin = ""; my $ns_vrrp_prio = "1.3.6.1.4.1.3224.6.2.2.1.4"; ######## Foundry my $foundry_base_vrrp = "1.3.6.1.4.1.1991.1.2.12.3.1.1"; # oid for vrrp my $foundry_vrrp_oper = "1.3.6.1.4.1.1991.1.2.12.3.1.1.10"; # vrrp operational status my $foundry_vrrp_admin = "1.3.6.1.4.1.1991.1.2.12.3.1.1.3"; # vrrp admin status my $foundry_vrrp_prio = "1.3.6.1.4.1.1991.1.2.12.3.1.1.6"; # vrrp vrid priority ######### Make an array my %base_vrrp = ( "nokia", $nokia_base_vrrp, "lp", $lp_base_vrrp, "alteon", $alteon_base_vrrp, "nsc", $ns_base_vrrp, "foundry", $foundry_base_vrrp ); my %vrrp_oper = ( "nokia", $nokia_vrrp_oper, "lp", $lp_vrrp_oper, "alteon", $alteon_vrrp_oper, "nsc", $ns_vrrp_oper, "foundry", $foundry_vrrp_oper ); my %vrrp_admin = ( "nokia", $nokia_vrrp_admin, "lp", $lp_vrrp_admin, "alteon", $alteon_vrrp_admin, "nsc", $ns_vrrp_admin, "foundry", $foundry_vrrp_admin ); my %vrrp_prio = ( "nokia", $nokia_vrrp_prio, "lp", $lp_vrrp_prio, "alteon", $alteon_vrrp_prio, "nsc", $ns_vrrp_prio, "foundry", $foundry_vrrp_oper ); my %state_master = ("nokia", 3, "alteon", 2, "lp", 3, "nsc", 2, "foundry", 1); my %state_backup = ("nokia", 2, "alteon", 3, "lp", 2, "nsc", 3, "foundry", 2); # Globals my $o_host = undef; # hostname my $o_community = undef; # community my $o_version2 = undef; #use snmp v2c my $o_port = 161; # port my $o_help = undef; # wan't some help ? my $o_verb = undef; # verbose mode my $o_version = undef; # print version my $o_state = undef; # Check master or backup state for ok my $o_clustnum = undef; # number of cluster members my $o_clustprct = undef; # Max % assigned to one cluster. my $o_type = 'nokia'; # Check type : nokia|alteon|lp|nsc|foundry my $o_long = undef; # Make output long my $o_timeout = 5; # Default 5s Timeout # SNMPv3 specific my $o_login = undef; # Login for snmpv3 my $o_passwd = undef; # Pass for snmpv3 my $v3protocols = undef; # V3 protocol list. my $o_authproto = 'md5'; # Auth protocol my $o_privproto = 'des'; # Priv protocol my $o_privpass = undef; # priv password # functions sub p_version { print "check_snmp_vrrp version : $VERSION\n"; } sub print_usage { print "Usage: $0 [-v] -H -C [-2] | (-l login -x passwd [-X pass -L ,]) -s [-T ] [-p ] [-t ] [-V]\n"; } sub isnnum { # Return true if arg is not a number my $num = shift; if ($num =~ /^(\d+\.?\d*)|(^\.\d+)$/) { return 0; } return 1; } sub help { print "\nSNMP VRRP Monitor for Icinga/Nagios/Naemon/Shinken, Version ", $VERSION, "\n"; print "GPL license, (c)2004-2007 Patrick Proy\n\n"; print_usage(); print <, : Authentication protocol (md5|sha : default md5) : Priv protocole (des|aes : default des) -P, --port=PORT SNMP port (Default 161) -T, --type= Type of vrrp router to check nokia (default) : Nokia vrrp. Should be working for most vrrp routers alteon : for Alteon AD4 Loadbalancers lp : Radware Linkproof nsc : Nescreen (ScreenOS 5.x NSRP) ipso : Nokia IPSO clustering foundry : Foundry VRRP -s, --state=master|backup|num,% Nokia ipso clustering : number of members, max % assigned to nodes. Other : check vrrp interface to be master or backup -g, --long Make output long even is all is OK -t, --timeout=INTEGER timeout for SNMP in seconds (Default: 5) -V, --version prints version number EOT } # For verbose output sub verb { my $t = shift; print $t, "\n" if defined($o_verb); } # Get the alarm signal (just in case snmp timout screws up) $SIG{'ALRM'} = sub { print("ERROR: Alarm signal (Nagios time-out)\n"); exit $ERRORS{"UNKNOWN"}; }; sub check_options { Getopt::Long::Configure("bundling"); GetOptions( 'v' => \$o_verb, 'verbose' => \$o_verb, 'h' => \$o_help, 'help' => \$o_help, 'H:s' => \$o_host, 'hostname:s' => \$o_host, 'p:i' => \$o_port, 'port:i' => \$o_port, 'C:s' => \$o_community, 'community:s' => \$o_community, 't:i' => \$o_timeout, 'timeout:i' => \$o_timeout, 'V' => \$o_version, 'version' => \$o_version, 'g' => \$o_long, 'long' => \$o_long, 'T:s' => \$o_type, 'type:s' => \$o_type, '2' => \$o_version2, 'v2c' => \$o_version2, 'l:s' => \$o_login, 'login:s' => \$o_login, 'x:s' => \$o_passwd, 'passwd:s' => \$o_passwd, 'X:s' => \$o_privpass, 'privpass:s' => \$o_privpass, 'L:s' => \$v3protocols, 'protocols:s' => \$v3protocols, 's:s' => \$o_state, 'state:s' => \$o_state ); if (defined($o_help)) { help(); exit $ERRORS{"UNKNOWN"} } if (defined($o_version)) { p_version(); exit $ERRORS{"UNKNOWN"} } if (!defined($o_host)) # check host and filter { print_usage(); exit $ERRORS{"UNKNOWN"}; } # check snmp information if (!defined($o_community) && (!defined($o_login) || !defined($o_passwd))) { print "Put snmp login info!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}; } if ((defined($o_login) || defined($o_passwd)) && (defined($o_community) || defined($o_version2))) { print "Can't mix snmp v1,2c,3 protocols!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}; } if (defined($v3protocols)) { if (!defined($o_login)) { print "Put snmp V3 login info with protocols!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}; } my @v3proto = split(/,/, $v3protocols); if ((defined($v3proto[0])) && ($v3proto[0] ne "")) { $o_authproto = $v3proto[0]; } # Auth protocol if (defined($v3proto[1])) { $o_privproto = $v3proto[1]; } # Priv protocol if ((defined($v3proto[1])) && (!defined($o_privpass))) { print "Put snmp V3 priv login info with priv protocols!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}; } } # Check state if ($o_type eq "ipso") { my @state = split(/,/, $o_state); if ($#state != 1) { print "On ipso clustering : number of nodes, % assigned\n"; print_usage(); exit $ERRORS{"UNKNOWN"}; } $state[1] =~ s/%//; if (isnnum($state[0]) || isnnum($state[1])) { print "On ipso clustering (numbers) : number of nodes, % assigned\n"; print_usage(); exit $ERRORS{"UNKNOWN"}; } $o_clustnum = $state[0]; $o_clustprct = $state[1]; } else { if (!defined($o_state) || ($o_state ne "master") && ($o_state ne "backup")) { print "state must be master or backup\n"; print_usage(); exit $ERRORS{"UNKNOWN"}; } } # Check type if ( !defined($o_type) || ( ($o_type ne "nokia") && ($o_type ne "alteon") && ($o_type ne "lp") && ($o_type ne "nsc") && ($o_type ne "ipso") && ($o_type ne "foundry")) ) { print "type must be alteon,nokia,lp,nsc or ipso\n"; print_usage(); exit $ERRORS{"UNKNOWN"}; } } ########## MAIN ####### check_options(); # Check timeout if snmp screws up if (defined($o_timeout)) { verb("Alarm in $o_timeout seconds"); alarm($o_timeout); } $SIG{'ALRM'} = sub { print "No answer from host $o_host:$o_port\n"; exit $ERRORS{"UNKNOWN"}; }; # Connect to host my ($session, $error); if (defined($o_login) && defined($o_passwd)) { # SNMPv3 login verb("SNMPv3 login"); if (!defined($o_privpass)) { verb("SNMPv3 AuthNoPriv login : $o_login, $o_authproto"); ($session, $error) = Net::SNMP->session( -hostname => $o_host, -version => '3', -username => $o_login, -authpassword => $o_passwd, -authprotocol => $o_authproto, -port => $o_port, -timeout => $o_timeout ); } else { verb("SNMPv3 AuthPriv login : $o_login, $o_authproto, $o_privproto"); ($session, $error) = Net::SNMP->session( -hostname => $o_host, -version => '3', -username => $o_login, -authpassword => $o_passwd, -authprotocol => $o_authproto, -privpassword => $o_privpass, -privprotocol => $o_privproto, -port => $o_port, -timeout => $o_timeout ); } } else { if (defined($o_version2)) { # SNMPv2 Login verb("SNMP v2c login"); ($session, $error) = Net::SNMP->session( -hostname => $o_host, -version => 2, -community => $o_community, -port => $o_port, -timeout => $o_timeout ); } else { # SNMPV1 login verb("SNMP v1 login"); ($session, $error) = Net::SNMP->session( -hostname => $o_host, -community => $o_community, -port => $o_port, -timeout => $o_timeout ); } } if (!defined($session)) { printf("ERROR opening session: %s.\n", $error); exit $ERRORS{"UNKNOWN"}; } ############ Nokia ipso clustering my $key = undef; if ($o_type eq "ipso") { # Get cluster table my $resultat; if (version->parse(Net::SNMP->VERSION) < 4) { $resultat = $session->get_table($nokia_clust_table); } else { $resultat = $session->get_table(Baseoid => $nokia_clust_table); } if (!defined($resultat)) { printf("ERROR: Description table : %s.\n", $session->error); $session->close; exit $ERRORS{"UNKNOWN"}; } $session->close; my $nclusterindex = 0; my $output = undef; my $overload = 0; foreach $key (keys %$resultat) { if ($key =~ /$nokia_clust_memberid/) { # Get rid of the vrrp oper part my $Cindex = $$resultat{$key}; $key =~ s/$nokia_clust_memberid\.//; verb("Found cluster, index $key"); my $Pkey = $nokia_clust_percent . "." . $key; my $percent = $$resultat{$Pkey}; verb("$percent / $Cindex"); if ($percent > $o_clustprct) { $overload = 1 } if (defined($output)) { $output .= "; Cluster " . $Cindex . " : " . $percent . "%"; } else { $output = "Cluster " . $Cindex . " : " . $percent . "%"; } $nclusterindex++; } } if ($nclusterindex == 0) { print "No Cluster membre found : CRITICAL\n"; exit $ERRORS{"CRITICAL"}; } if ($nclusterindex != $o_clustnum) { print $output, " : Not ", $o_clustnum, " members : CRITICAL\n"; exit $ERRORS{"CRITICAL"}; } if ($overload == 1) { print $output, " assigned % is > ", $o_clustprct, " : WARNING\n"; exit $ERRORS{"WARNING"}; } print $output, " : OK\n"; exit $ERRORS{"OK"}; } ########### get vrrp table ############ # Get vrrp table my $resultat; if (version->parse(Net::SNMP->VERSION) < 4) { $resultat = $session->get_table($base_vrrp{$o_type}); } else { $resultat = $session->get_table(Baseoid => $base_vrrp{$o_type}); } if (!defined($resultat)) { printf("ERROR: Description table : %s.\n", $session->error); $session->close; exit $ERRORS{"UNKNOWN"}; } $session->close; my @vrid = undef; my $nvrid = 0; my $oid0 = undef; my @oid = undef; if ($o_type eq 'nsc') { $nvrid = 1; $vrid[0] = '0'; } else { foreach $key (keys %$resultat) { if ($key =~ /$vrrp_oper{$o_type}/) { # Get rid of the vrrp oper part $key =~ s/$vrrp_oper{$o_type}\.//; @oid = split(/\./, $key); $vrid[$nvrid] = pop(@oid); $oid0 = pop(@oid); while (defined($oid0)) { $vrid[$nvrid] = $oid0 . "." . $vrid[$nvrid]; $oid0 = pop(@oid); } verb("Added vrid $vrid[$nvrid]"); $nvrid++; } } } if ($nvrid == 0) { printf("No vrid found : CRITICAL\n"); exit $ERRORS{"CRITICAL"}; } my $ok = 0; my $value; my $output = undef; my $vrid_out; for (my $i = 0; $i < $nvrid; $i++) { $output .= ", " if (defined($output)); # Get last part of oid to output the vrid $vrid_out = $vrid[$i]; $vrid_out =~ s/.*\.//; $output .= "$vrid_out("; # Get the state $key = $vrrp_oper{$o_type} . "." . $vrid[$i]; $value = ($$resultat{$key} == $state_master{$o_type}) ? "master" : ($$resultat{$key} == $state_backup{$o_type}) ? "backup" : "initialise : " . $$resultat{$key}; $output .= $value . "/"; ($value eq $o_state) && $ok++; # Get the administrative status if (($o_type eq 'alteon') || ($o_type eq 'nsc')) { $ok++; } else { $key = $vrrp_admin{$o_type} . "." . $vrid[$i]; $value = ($$resultat{$key} == 1) ? "up" : "down"; $output .= $value . "/"; if (($o_type eq 'foundry') && ($o_state eq 'backup') && ($value eq "down")) { $ok++; } else { ($value eq "up") && $ok++; } } # Get the priority $key = $vrrp_prio{$o_type} . "." . $vrid[$i]; $value = $$resultat{$key}; $output .= $value . ")"; } verb("verif : $ok"); if ($ok == (2 * $nvrid)) { if (defined($o_long)) { printf("Vrid : %s : %s %s : OK\n", $output, $nvrid, $o_state); } else { printf("%s vrid %s :OK\n", $nvrid, $o_state); } exit $ERRORS{"OK"}; } printf("Vrid : %s : not all %s : NOK\n", $output, $o_state); exit $ERRORS{"CRITICAL"};