Project

snmp4em

0.02
No commit activity in last 3 years
No release in over 3 years
A high-performance SNMP engine built on EventMachine and Ruby-SNMP
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 0
>= 0

Runtime

>= 1.0.0
>= 1.0.2
 Project Readme

SNMP Library for EventMachine¶ ↑

Summary¶ ↑

This gem extends Ruby-SNMP to use the asynchronous EventMachine library for added performance and scalability. This allows code to scale monitoring applications to access a very high number of devices without the need for complex asynchronous I/O handling.

Features¶ ↑

The library currently supports:

  • SNMP v1 and v2

  • SNMP Get, GetNext, Set, and Walk requests. For SNMPv2 clients, GetBulk and WalkBulk

  • Ability to query/set/walk multiple OIDs in parallel.

  • For Ruby 1.9 and above, fiber support to streamline asychronous code

Future revisions of this library may support:

  • Ability to act as an SNMP agent, responding to external queries.

There are no plans to support SNMP v3 or the parsing of MIB files.

Options¶ ↑

You may set the following options:

SNMP4EM::Manager.new(
  :version => :SNMPv2c,
  :host => "127.0.0.1",
  :port => 161,
  :community => "public",      # Shorthand for setting both :community_ro and :community_rw
  :community_ro => "public",
  :community_rw => "public",
  :fiber => false,
  :timeout => 1,
  :retries => 3                # Can also be specified as an array of individual timeouts
                               # For instance, [0.5, 1, 2.5] would send three requests with increasing timeout values
)

These options may also be passed to an individual query method, for instance:

snmp = SNMP4EM::Manager.new(...)
snmp.get(OID, :retries => 2, :timeout => 1)

And when receiving traps:

SNMP4EM::NotificationManager.new(
  :host => "127.0.0.1",        # Local interface on which to listen for traps. By default, localhost only!
  :port => 161                 # Port on which to listen for traps.
)

Backwards compatibility¶ ↑

Version 1.0.0 tweaks the return values of a few functions. Please test code thoroughly before upgrading to 1.0.0 in production!

Running tests¶ ↑

This library now includes a full suite of functional tests. They communicate with an SNMP daemon running on the localhost. The library comes with the necessary configuration, but it’s much easier to fire up the provided rake task (‘rake server`), running in a separate terminal window, before running `rspec`. This works fine on a Mac OS X machine using homebrew (`brew install net-snmp`).

Acknowledgements¶ ↑

  • The SNMP packet processing is handled by the Ruby-SNMP library, by David Halliday

  • EventMachine, by Francis Cianfrocca and Aman Gupta

  • All the helpful folks on the Freenode #eventmachine channel

Examples¶ ↑

A few definitions:

OID_SYSTEM = "1.3.6.1.2.1.1"
OID_SYSNAME = "1.3.6.1.2.1.1.5.0"
OID_SYSLOCATION = "1.3.6.1.2.1.1.6.0"

A simple SNMP-GET:

EM.run {
  snmp = SNMP4EM::Manager.new(:host => "192.168.1.1")

  request = snmp.get([OID_SYSNAME, OID_SYSLOCATION])

  request.callback do |response|
    puts "System name = #{response[OID_SYSNAME]}"
    puts "System location = #{response[OID_SYSLOCATION]}"
  end

  request.errback do |error|
    puts "GET got error #{error}"
  end
}

A simple SNMP-GETNEXT:

EM.run {
  snmp = SNMP4EM::Manager.new(:host => "192.168.1.1")

  request = snmp.getnext(OID_SYSNAME)

  request.callback do |response|
    r = response[OID_SYSNAME]
    puts "The next OID is #{r[0]}, the next value is #{r[1]}"
  end

  request.errback do |error|
    puts "GETNEXT got error #{error}"
  end
}

A simple SNMP-SET:

EM.run {
  snmp = SNMP4EM::Manager.new(:host => "192.168.1.1")

  request = snmp.set({OID_SYSNAME => "My System Name", OID_SYSLOCATION => "My System Location"})

  request.callback do |response|
    if (response[OID_SYSNAME] == true)
      puts "System name set successful"
    else
      puts "System name set unsuccessful: #{response[OID_SYSNAME]}"
    end

    if (response[OID_SYSLOCATION] == true)
      puts "System location set successful"
    else
      puts "System location set unsuccessful: #{response[OID_SYSLOCATION]}"
    end
  end

  request.errback do |error|
    puts "SET got error #{error}"
  end
}

A simple SNMP-WALK:

EM.run {
  snmp = SNMP4EM::Manager.new(:host => "192.168.1.1")

  request = snmp.walk(OID_SYSTEM)

  request.callback do |response|
    response[OID_SYSTEM].each do |oid, value|
      puts "#{oid} = #{value}"
    end
  end

  request.errback do |error|
    puts "WALK got error #{error}"
  end
}

A simple SNMP-BULKWALK:

EM.run {
  snmp = SNMP4EM::Manager.new(:host => "192.168.1.1")

  request = snmp.bulkwalk(OID_SYSTEM)

  request.callback do |response|
    response[OID_SYSTEM].each do |oid, value|
      puts "#{oid} = #{value}"
    end
  end

  request.errback do |error|
    puts "BULK-WALK got error #{error}"
  end
}

A simple SNMP-GET-BULK:

EM.run {
  snmp = SNMP4EM::Manager.new(:host => "192.168.1.1")

  request = snmp.getbulk(OID_SYSTEM)

  request.callback do |response|
    response[OID_SYSTEM].each do |oid, value|
      puts "#{oid} = #{value}"
    end
  end

  request.errback do |error|
    puts "GET-BULK got error #{error}"
  end
}

Handling traps:

EM.run {
  manager = SNMP4EM::NotificationManager.new

  manager.on_trap do |trap|
    if trap.is_a? SNMP::SNMPv1_Trap
      puts "Got a SNMPv1_Trap"
    elsif trap.is_a? SNMP::SNMPv2_Trap
      puts "Got a SNMPv2_Trap"
    end
  end
}

Fiber Support¶ ↑

If your Ruby supports it, you can pass enable fibers, either per-manager or per-request. This unwinds much of the complexity in writing event-driven code. Instead of receiving an object on which to hang callbacks, the current fiber will be suspended until the response is available. If an error occurs, it will be returned as a SNMP::RequestTimeout. Unfortunately, there is no way to raise this error, you are responsible for manually checking whether the returned object is an error or not.

For example:

EM.run {
  snmp = SNMP4EM::Manager.new(:host => "192.168.1.1")

  f = Fiber.new do
    response = snmp.get([OID_SYSNAME, OID_SYSLOCATION])

    if response.is_a? SNMP::RequestTimeout
      puts "Uh oh!"
    else
      puts "System name = #{response[OID_SYSNAME]}"
      puts "System location = #{response[OID_SYSLOCATION]}"
    end
  end
}

Change Log¶ ↑

Version 1.1.5:

  • Fix memory leak (#10), many thanks to paddor

Version 1.1.5:

  • Fix an error when constructing a GetBulkRequest when using version >= 1.3.1 of the SNMP gem

Version 1.1.2:

  • Changed timeout error handling. We now raise a SNMP::RequestTimeout instead of a StandardError.

Version 1.1.0:

  • Fiber support

Version 1.0.2:

Version 1.0.1:

  • Requests resulting in an error now return nil unless return_raw is enabled

Version 1.0.0:

  • Added SNMPv2 BULKWALK support

  • Added ability to receive SNMP traps / informs

  • Added functional tests (finally!)

  • Moved documentation to Yard

  • Lots of code cleanup

Version 0.2.1:

  • Code cleanups, speed boosts

Version 0.2.0:

  • Added support for SNMPv2, including GET-BULK operations

Version 0.1.0:

  • Initial deployment, ability to run get/getnext/set/walk requests in parallel

Credits¶ ↑

Author: Norman Elton normelton@gmail.com