#!/usr/bin/perl # # AUTHORS: # Copyright (C) 2008 Altinity Limited # Written by Neil Ferguson, kindly sponsored by GotVMail # # This file is part of Opsview # # Opsview 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. # # Opsview 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 Opsview; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # use strict; use lib qw ( /usr/local/nagios/perl/lib ); use Storable qw(lock_store lock_retrieve); use Net::SNMP; use Getopt::Std; # About us my $script = "check_snmp_vmware_netio"; my $script_version = "0.1"; my $script_description = "Reports the network throughput on a specified VMware interface"; my $store_fn = "/tmp/$script.dat"; my $store; my $hostname = ''; # SNMP variables my $oid_sysDescr = ".1.3.6.1.2.1.1.1.0"; # Used to check whether SNMP is actually responding my $oid_namebase = ".1.3.6.1.4.1.6876.2.1.1.2."; my $oid_vmidbase = ".1.3.6.1.4.1.6876.2.1.1.7."; my $oid_nicbase = ".1.3.6.1.4.1.6876.3.4.1.3."; my $oid_macbase = ".1.3.6.1.4.1.6876.3.4.1.4."; my $oid_rxbase = ".1.3.6.1.4.1.6876.3.4.1.9."; my $oid_txbase = ".1.3.6.1.4.1.6876.3.4.1.7."; my $vm_name = ""; my $vm_mac = ""; my $mode = "name"; my $community = "public"; # Default community my $timeout = 10; # SNMP timeout my $retval = 3; my $retmsg = 3; my $perfdata = ""; my $warning = 0; my $critical = 0; my $warn_in = 0; my $crit_in = 0; my $warn_out = 0; my $crit_out = 0; my $throughput_in = 0; my $throughput_in_kb = 0; my $throughput_out = 0; my $throughput_out_kb = 0; my $version = "2c"; our ( $s, $e ); # Command line arguments our ( $opt_h, $opt_H, $opt_C, $opt_t, $opt_v, $opt_m, $opt_w, $opt_c, $opt_i, $opt_o ); getopts("hH:C:t:v:m:w:c:io"); if ($opt_h) { usage(); exit 0; } if ($opt_H) { $hostname = $opt_H; } else { print "No hostname specified\n"; usage(); exit 3; } if ($opt_C) { $community = $opt_C; } if ($opt_t) { # Validity test - must be numeric unless ( $opt_t =~ /^\d+$/ ) { print "Specify time in seconds - $opt_t is not a valid integer\n"; exit 3; } $timeout = $opt_t; } if ($opt_v) { $vm_name = $opt_v; } elsif ($opt_m) { if ($opt_v) { print "Must specify either VM name or MAC address\n"; usage(); exit 3; } $vm_mac = $opt_m; $mode = "mac"; } else { print "Must specify either MAC address or VM name\n"; usage(); exit 3; } if ($opt_w) { if ( $opt_w =~ /^\d+$/ ) { $warning = $opt_w; } else { print "Must specify warning threshold as an integer in KB/sec\n"; usage(); exit 3; } } if ($opt_c) { if ( $opt_c =~ /^\d+$/ ) { $critical = $opt_c; } else { print "Must specify critical threshold as an integer in KB/sec\n"; usage(); exit 3; } } if ( $opt_i && $opt_o ) { print "Specify either of -i or -o, or specify neither option to mean 'both'\n"; usage(); exit 3; } if ($opt_i) { $warn_in = $warning; $crit_in = $critical; } elsif ($opt_o) { $warn_out = $warning; $crit_out = $critical; } else { $warn_in = $warning; $crit_in = $critical; $warn_out = $warning; $crit_out = $critical; } sub usage { print < -C [-t ] ... Options: -H Hostname or IP address -C SNMP community string -t SNMP timeout (in seconds) -v VM name (alternatively, specify MAC) -m MAC address -w Warning threshold (KB/sec) -c Critical threshold (KB/sec) Note: Using the VM name is fine when you only have one interface on that VM, but you should use the MAC address otherwise. -------------------------------------------------------------------- Copyright 2008 Altinity Limited Plugin development was sponsored by GotVMail (http://www.gotvmail.com) This program is free software; you can redistribute it or modify it under the terms of the GNU General Public License ------------------------------------------------------------------ EOF } # Call this when you know you'll get a single value back sub get_oid_value { our ( $oid, $result, $status, $returnstring ); $oid = shift(@_); if ( !defined( $s->get_request($oid) ) ) { if ( !defined( $s->get_request($oid_sysDescr) ) ) { print "SNMP agent not responding\n"; exit 3; } else { print "SNMP OID does not exist\n"; exit 3; } } foreach ( $s->var_bind_names() ) { $result = $s->var_bind_list()->{$_}; } return $result; } sub sane_units { # Input should be in kilobytes - we'll convert to something more human readible my $kbits = shift(@_); my $gigabits = 1024 * 1024; my $megabits = 1024; # Gigabits if ( $kbits > $gigabits ) { return int( ( 100 * $kbits ) / $gigabits ) / 100 . " GB/s"; } elsif ( $kbits > $megabits - 1 ) { return int( ( 100 * $kbits ) / $megabits ) / 100 . " MB/s"; } else { return $kbits . " KB/s"; } } sub get_vm_id { my $name = shift; my $i = 0; my $result; # Avoid accidental infinite loops by limiting # ourselves to 100 VMs. Real solution is to # get the whole table. while ( $i < 100 ) { $result = get_oid_value( $oid_namebase . $i ); if ( $result eq "" || $result eq "noSuchInstance" ) { last; } elsif ( $result =~ /^$name$/i ) { return get_oid_value( $oid_vmidbase . $i ); } $i++; } return undef; } sub get_nic_id { my $oid_base = shift(@_); my $search = shift(@_); my $result; my $i = 1; while ( $i < 100 ) { $result = get_oid_value( $oid_base . $i ); if ( $result eq "" || $result eq "noSuchInstance" ) { last; } elsif ( $result =~ /^$search$/i ) { return $i; } $i++; } } sub get_nic_data { my $mode = shift(@_); my $vm = shift(@_); my $mac = shift(@_); my $storename = $hostname . $vm; my $txrate = 0; my $rxrate = 0; our ( $vmid, $nic, $rxkb, $txkb ); if ( $mode eq "name" ) { # Get VM ID $vmid = get_vm_id($vm); return undef unless defined($vmid); # Get NIC ID from VM ID $nic = get_nic_id( $oid_nicbase, $vmid ); } else { # Store by MAC not by name $storename = $hostname . $mac; # Get NIC ID from MAC ID $nic = get_nic_id( $oid_macbase, $mac ); } # Ensure we have a NIC id return undef unless defined($nic); # Get throughputs $rxkb = get_oid_value( $oid_rxbase . $nic ); $txkb = get_oid_value( $oid_txbase . $nic ); # Retrieve info from store if it exists if ( -f $store_fn ) { $store = lock_retrieve($store_fn); } if ( defined( $store->{$storename} ) ) { my $time = $store->{$storename}[0]; $rxrate = ( $rxkb - $store->{$storename}[1] ) / $time; $txrate = ( $txkb - $store->{$storename}[2] ) / $time; if ( $rxrate < 0 ) { $rxrate = 0; } if ( $txrate < 0 ) { $txrate = 0; } } # Always write current data $store->{$storename} = [ time(), $rxkb, $txkb ]; lock_store( $store, $store_fn ); # Calculate rates return ( int($rxrate), int($txrate) ); } # Create the SNMP session $version = "2c"; ( $s, $e ) = Net::SNMP->session( -community => $community, -hostname => $hostname, -version => $version, -timeout => $timeout, ); if ( !defined( $s->get_request($oid_sysDescr) ) ) { # If we can't connect using SNMPv1 lets try as SNMPv2 $s->close(); sleep 0.5; $version = "1"; ( $s, $e ) = Net::SNMP->session( -community => $community, -hostname => $hostname, -version => $version, -timeout => $timeout, ); if ( !defined( $s->get_request($oid_sysDescr) ) ) { print "Agent not responding, tried SNMP v1 and v2\n"; exit 3; } } # Check for an SNMP error first... if ( $s->error ) { print "UNKNOWN - " . $s->error . "|\n"; exit 3; } # Check what the value is ( $throughput_in_kb, $throughput_out_kb ) = get_nic_data( $mode, $vm_name, $vm_mac ); # Check we got some data if ( !defined($throughput_in_kb) ) { print "UNKNOWN - Could not find data for the specified interface\n"; exit 3; } $throughput_in = sane_units($throughput_in_kb); $throughput_out = sane_units($throughput_out_kb); $retmsg = " - $throughput_in in, $throughput_out out | throughput_in=$throughput_in_kb;$warn_in;$crit_in;; throughput_out=$throughput_out_kb;$warn_out;$crit_out;;\n"; if ( ( $throughput_in_kb > $crit_in && $crit_in > 0 ) || ( $throughput_out_kb > $crit_out && $crit_out > 0 ) ) { print "CRITICAL $retmsg"; exit 2; } elsif ( ( $throughput_in_kb > $warn_in && $warn_in > 0 ) || ( $throughput_out_kb > $warn_out && $warn_out > 0 ) ) { print "WARNING $retmsg"; exit 1; } else { print "OK $retmsg"; exit 0; }