The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Nagios::Plugin::SNMP - Helper module to make writing SNMP-based plugins for Nagios easier.

SYNOPSIS

 This module requires Net::SNMP for its' SNMP functionality; it 
 subclasses Nagios::Plugin.

 It includes routines to do the following:

Parse and process common SNMP arguments:

 * --warning|-w: Warning threshold [optional]
 * --critical|-c: Warning threshold  [optional]
 * --hostname|-H: SNMP device to query
 * --alt-host|--ah: Additional SNMP devices to query
 * --port|-p: Port on remote device to connect to [default 161]
 * --snmp-local-ip: Local IP to bind to for outgoing requests
 * --snmp-version: SNMP version (1, 2c, 3)
 * --snmp-timeout: Connect timeout in seconds [default 0]
 * --snmp-debug: Turn on Net::SNMP debugging
 * --snmp-max-msg-size N: Set maximum SNMP message size in bytes
 * --rocommunity: Read-only community string for SNMP 1/v2c
 * --auth-username: Auth username for SNMP v3
 * --auth-password: Auth password for SNMP v3
 * --auth-protocol: Auth protocol for SNMP v3 (defaults to md5)
 * Connect to an SNMP device
 * Perform a get() or walk() request, each method does 'the right
   thing' based on the version of SNMP selected by the user.

noSuchObject and noSuchInstance behavior

You can configure your plugin to automatically exit with UNKNOWN if OIDs you query for on the remote agent do not exist on the agent by setting 'error_on_no_such' to 1 in new(). If you set it to 0 or do not specify the option at all, then results from get() and walk() will have the string tokens noSuchObject or noSuchInstance as values for OIDs that the agent does not support.

This option exists as some scripts will query agents for MIBs that have *some* tables or scalars that may or may not be supported depending on the remote agent version, operating system, etc. In this case it is nice to just parse the returns from get() and walk() to programatically determine if support exists.

In other cases the script may be querying a clustered application with multiple agents where only one agent will respond with the OIDs queried; the others will return noSuch* values .. for this case having the script exit with error if *none* of the agents return valid values makes sense at least one agent MUST have the OIDs for the check to succeed and all agents are known to respond to the OIDs in question (set error_on_no_such to 1).

 my $plugin = Nagios::Plugin::SNMP->new(
     'error_on_no_such' => 1
 );

Handle deltas for counters

 my $plugin = Nagios::Plugin::SNMP->new(
     'process_deltas' => { 
         'cache' => { 'type' => 'memcache' },
         'default_interval' => 300, # seconds 
         'delta_compute_function' => \&my_delta_function
     }
 );

 Will use any Cache::Cache compliant data cache to store counter 
 values and return deltas between counters to the end user.  In order 
 to do this, Nagios::Plugin::SNMP must be passed in a cache class name.  
 Each cache type will cause Nagios::Plugin::SNMP to add required 
 arguments for the cache type in question.  Additionally, enabling 
 counter delta code causes the script to require an interval for 
 time between checks.  From the command line this can be specified 
 with --check-interval.  The developer can pass in a default using 
 the 'default_interval' parameter.

 * cache => { 'type' => 'memcache' }

 Causes Nagios::Plugin::SNMP to use Cache::Memcached for 
 data persistence; also makes the following arguments to the plugin 
 available to the user.  Defaults for both can be provided in the 
 memcache option hash as memcache_addr and memcache_port.
 * --memcache-addr - IP address of Memcache instance
 * --memcache-port - TCP port number memcache is listening on

 my $plugin = Nagios::Plugin::SNMP->new(
     'process_deltas' => { 
         'cache' => {
             'type' => 'memcache',
             'options => { 'memcache_port' => 11211, 
                           'memcache_addr' => 127.0.0.1 
         },
         'default_interval' => 300, # seconds 
         'delta_compute_function' => \&my_delta_function
     }
 );

 Currently ONLY Cache::Memcached is supported.

 * 'process_deltas' => { 'delta_compute_function' => \&my_delta_function }

 Callback to allow user to pass in a function that will compute a
 smarter delta :).  The default function purely does the following:
 * If previous value does not exist, it stores the current value
   and returns -0
 * If previous value exists and is > 0, stores the current value and
   returns the delta between the current value and previous value
 * If delta between the two values is < 0, returns -0 as counter 
   has wrapped.

 sub my_delta_function {
     my ($self, $args) = @_;
     
     my $previous_value = $args->{'previous_value'};
     my $current_value = $args->{'current_value'};
     my $interval = $args->{'interval'};
     my $previous_run_at = $args->{'previous_run_at'};

     my ($value_to_store, $delta) = ();
    
     # Processing code

     return ($value_to_store, $delta);
 }
 

get_deltas(@oids)

Will query the agent for values associated with the SCALAR OIDs passed in and return a hash with the results of the query; the value for each oid will be the delta, massaged as needed by the built in delta-computation function or your own function.

get_delta_for_value($key, $value);

Will perform delta computation on the passed in value; using the key passed in in place of the OID that would be used in get_deltas as the hash key. This routine can be used by the end user for metrics that are created by the plugin developer as opposed to counters retrieved by the SNMP agent.

Other methods

_setup_delta_cache_options

This method adds arguments to the Nagios::Plugin instance based on the type of cache along with making --check-interval required as a plugin needs to know the interval between plugin calls in order to calculate deltas and delta variance properly.

 'process_deltas' => { 
     'cache' => {
          'type' => 'memcache',
          'options => { 'memcache_port' => 11211, 
                        'memcache_addr' => 127.0.0.1 
          },
          'default_interval' => 300, # seconds 
          'delta_compute_function' => \&my_delta_function
      }
 }

_initialize_delta_cache()

Initialize cache for processing deltas. All validation of cache options is done in _setup_delta_cache_options.

_get_cache()

Return cache instance after ensuring it exists and is valid. Exit with error if it is not.

_get_from_cache($key)

Return the value associated with $key from the cache; if value does not exist, returns undef. If cache is invalid, will exit with error.

_store_in_cache($key, $value)

Store a value in the cache using the passed in key.

get_cache_key_for($key)

Returns a unique key that can be used to retrieve a value from the cache; feel free to override this with a different unique key algorithm if the default does not suit you (by subclassing Nagios::Plugin::SNMP). By default, the unique key will be made by concatenating the following values, separated by colons: * hostname of the host being checked * port of the host being checked * user provided key (in the case of get_delta_for_value) or OID of the scalar requested (in the case of get_deltas().

_snmp_validate_opts() - Validate passed in SNMP options

This method validates that any options passed to the plugin using this library make sense. Rules:

     * If SNMP is version 1 or 2c, rocommunity must be set
     * If SNMP is version 3, auth-username and auth-password must be set

connect() - Establish SNMP session

 Attempts to connect to the remote system specified in the 
 command-line arguments; will die() with an error message if the 
 session creation fails.

get(@oids) - Perform an SNMP get request

Performs an SNMP get request on each passed in OID; returns results as a hash reference where keys are the passed in OIDs and the values are the values returned from the Net::SNMP get() calls.

walk(@baseoids) - Perform an SNMP walk request

 Performs an SNMP walk on each passed in OID; uses the Net-SNMP
 get_table() method for each base OID to ensure that the method will
 work regardless of SNMP version in use.  Returns results as
 a hash reference where keys are the passed in base OIDs and the values are
 references to the results of the Net::SNMP get_table calls.

delta_compute_function

Default delta computation function; used if user does not provide a delta compute function. This function will do the following: * If no value was in the cache previous to this call, it will return -0. * If the current value is less than the cached value, the function will return -0 and treat the case as a counter wrap. * If neither of the above are true, the function will return the difference between the current and previous values and store the current value in the cache.

Is this overly-simplistic? Yes :), and it is designed to be replaced by your function that does a delta in a much more intelligent way.

To replace this function with yours, subclass Nagios::Plugin::SNMP's default delta_compute_function method OR pass in a reference to a function via the 'process_deltas' hash passed to new(), e.g.

 my $plugin = Nagios::Plugin::SNMP->new({ 
     'delta_compute_function' => \&my_delta_function,
     ...
 });

The delta computation function you create must accept the following arguments: * Reference to plugin instance (commonly called $self within a method) * Hash reference with the following key value pairs: * previous_value: Previous value (from the cache) * current_value: Current value (from the user or the remote SNMP agent) * interval: How long check interval is in seconds * previous_run_at: Unix timestamp representing the previous time a value was stored * key: Unique sub-key associated with this data, e.g. the OID for the data.

The function must return (as a 2-element list): * Value to store in the cache * Delta between the two values passed to the function

Note that this means your function can put any additional information in the value (and therefore cache) it would like as the function has total control over computing the delta between the previous and current values and control over what gets stored in the cache between runs of the plugin.

Example:

 sub my_better_function {
     my ($self, $args_ref) = @_;
     
     my $previous_value = $args->{'previous_value'};
     my $current_value = $args->{'current_value'};
     my $interval = $args->{'interval'};
     my $previous_run_at = $args->{'previous_run_at'};
     my $key = $args->{'key'};

     my ($value_to_store, $delta_value) = ();

     # ... code to compute ...

     return ($value_to_store, $delta_value);
 }

 my $plugin = Nagios::Plugin::SNMP->new(
     'shortname' => 'FOO',
     'usage'     => $usage,
     'process_deltas' => {
         'cache' => {
             'type' => 'memcache',
             ...
         }
         'delta_compute_function' => \&my_delta_function
     }
 );

_get_compute_delta_function()

Return user provided delta function reference if passed in or default delta compute function. Validation of function is done in new().

get_sys_info()

    my ($descr, $object_id) = $plugin->get_sys_info();

    Returns the sysDescr.0 and sysObjectId.0 OIDs from the remote
    agent, the sysObjectId.0 OID is translated to an OS family; string
    returned will be one of:

    *  hpux
    *  sunos4
    *  solaris
    *  osf
    *  ultrix
    *  hpux10
    *  netbsd1
    *  freebsd
    *  irix
    *  linux
    *  bsdi
    *  openbsd
    *  win32
    *  hpux11
    *  unknown

    sysDescr.0 is a free-text description containing more specific
    information on the OS being queried.

AUTHORS

 * Max Schubert  (maxschube@cpan.org)
 * Ryan Richins 
 * Shaofeng Yang

Special Thanks

Special thanks to my teammates Ryan Richins and Shaofeng Yang at Comcast for their significant contributions to this module and to my managers Jason Livingood and Mike Fischer at Comcast for allowing our team to contribute code we have created or modified at work back to the open source community. If you live in the northern Virginia area and are a talented developer / systems administrator, Comcast is hiring :).