lib/logstash/inputs/snmp.rb in logstash-input-snmp-0.1.0.beta4 vs lib/logstash/inputs/snmp.rb in logstash-input-snmp-0.1.0.beta5

- old
+ new

@@ -2,10 +2,11 @@ require "logstash/inputs/base" require "logstash/namespace" require "stud/interval" require "socket" # for Socket.gethostname require_relative "snmp/client" +require_relative "snmp/clientv3" require_relative "snmp/mib" # Generate a repeating message. # # This plugin is intented only as an example. @@ -24,15 +25,19 @@ # Each host definition is a hash and must define the `host` key and value. # `host` must use the format {tcp|udp}:{ip address}/{port} # for example `host => "udp:127.0.0.1/161"` # Each host definition can optionally include the following keys and values: # `community` with a default value of `public` - # `version` `1` or `2c` with a default value of `2c` - # `retries` with a detault value of `2` + # `version` `1`, `2c` or `3` with a default value of `2c` + # `retries` with a default value of `2` # `timeout` in milliseconds with a default value of `1000` config :hosts, :validate => :array #[ {"host" => "udp:127.0.0.1/161", "community" => "public"} ] + # This plugin provides sets of MIBs publicly available. The full paths to these provided MIBs paths + # Will be displayed at plugin startup. + config :use_provided_mibs, :validate => :boolean, :default => true + # List of paths of MIB .dic files of dirs. If a dir path is specified, all files with .dic extension will be loaded. # # ATTENTION: a MIB .dic file must be generated using the libsmi library `smidump` command line utility # like this for example. Here the `RFC1213-MIB.txt` file is an ASN.1 MIB file. # @@ -48,32 +53,69 @@ # as "1.3.6.1.2.mib-2.system.sysDescr.0" would become "mib-2.system.sysDescr.0" config :oid_root_skip, :validate => :number, :default => 0 # Set polling interval in seconds # - # The default, `30`, means poll each host every 30second. + # The default, `30`, means poll each host every 30 seconds. config :interval, :validate => :number, :default => 30 # Add the default "host" field to the event. config :add_field, :validate => :hash, :default => { "host" => "%{[@metadata][host_address]}" } + # SNMPv3 Credentials + # + # A single user can be configured and will be used for all defined SNMPv3 hosts. + # Multiple snmp input declarations will be needed if multiple SNMPv3 users are required. + # If not using SNMPv3 simply leave options empty. + + # The SNMPv3 security name or user name + config :security_name, :validate => :string + + # The SNMPv3 authentication protocol or type + config :auth_protocol, :validate => ["md5", "sha", "sha2", "hmac128sha224", "hmac192sha256", "hmac256sha384", "hmac384sha512"] + + # The SNMPv3 authentication passphrase or password + config :auth_pass, :validate => :password + + # The SNMPv3 privacy/encryption protocol + config :priv_protocol, :validate => ["des", "3des", "aes", "aes128", "aes192", "aes256"] + + # The SNMPv3 encryption password + config :priv_pass, :validate => :password + + # The SNMPv3 security level can be Authentication, No Privacy; Authentication, Privacy; or no Authentication, no Privacy + config :security_level, :validate => ["noAuthNoPriv", "authNoPriv", "authPriv"] + + BASE_MIB_PATH = ::File.join(__FILE__, "..", "..", "..", "mibs") + PROVIDED_MIB_PATHS = [::File.join(BASE_MIB_PATH, "logstash"), ::File.join(BASE_MIB_PATH, "ietf")].map { |path| ::File.expand_path(path) } + def register validate_oids! validate_hosts! mib = LogStash::SnmpMib.new + + if @use_provided_mibs + PROVIDED_MIB_PATHS.each do |path| + logger.info("using plugin provided MIB path #{path}") + mib.add_mib_path(path) + end + end + Array(@mib_paths).each do |path| - # TODO handle errors + logger.info("using user provided MIB path #{path}") mib.add_mib_path(path) end + # setup client definitions per provided host + @client_definitions = [] @hosts.each do |host| host_name = host["host"] community = host["community"] || "public" version = host["version"] || "2c" - raise(LogStash::ConfigurationError, "only protocol version '1' and '2c' are supported for host option '#{host_name}'") unless version =~ VERSION_REGEX + raise(LogStash::ConfigurationError, "only protocol version '1', '2c' and '3' are supported for host option '#{host_name}'") unless version =~ VERSION_REGEX retries = host["retries"] || 2 timeout = host["timeout"] || 1000 # TODO: move these validations in a custom validator so it happens before the register method is called. @@ -84,19 +126,27 @@ protocol = host_details[:host_protocol] address = host_details[:host_address] port = host_details[:host_port] definition = { - :client => LogStash::SnmpClient.new(protocol, address, port, community, version, retries, timeout, mib), :get => Array(get), :walk => Array(walk), :host_protocol => protocol, :host_address => address, :host_port => port, :host_community => community, } + + if version == "3" + validate_v3_user! # don't really care if verified for every host + auth_pass = @auth_pass.nil? ? nil : @auth_pass.value + priv_pass = @priv_pass.nil? ? nil : @priv_pass.value + definition[:client] = LogStash::SnmpClientV3.new(protocol, address, port, retries, timeout, mib, @security_name, @auth_protocol, auth_pass, @priv_protocol, priv_pass, @security_level) + else + definition[:client] = LogStash::SnmpClient.new(protocol, address, port, community, version, retries, timeout, mib) + end @client_definitions << definition end end def run(queue) @@ -107,11 +157,11 @@ result = {} if !definition[:get].empty? begin result = result.merge(definition[:client].get(definition[:get], @oid_root_skip)) rescue => e - logger.error("error invoking get operation on OIDs: #{definition[:get]}, ignoring", :exception => e, :backtrace => e.backtrace) + logger.error("error invoking get operation on #{definition[:host_address]} for OIDs: #{definition[:get]}, ignoring", :exception => e, :backtrace => e.backtrace) end end if !definition[:walk].empty? definition[:walk].each do |oid| begin @@ -122,14 +172,14 @@ end end unless result.empty? metadata = { - "host_protocol" => definition[:host_protocol], - "host_address" => definition[:host_address], - "host_port" => definition[:host_port], - "host_community" => definition[:host_community], + "host_protocol" => definition[:host_protocol], + "host_address" => definition[:host_address], + "host_port" => definition[:host_port], + "host_community" => definition[:host_community], } result["@metadata"] = metadata event = LogStash::Event.new(result) decorate(event) @@ -146,11 +196,11 @@ private OID_REGEX = /^\.?([0-9\.]+)$/ HOST_REGEX = /^(?<host_protocol>udp|tcp):(?<host_address>.+)\/(?<host_port>\d+)$/i - VERSION_REGEX =/^1|2c$/ + VERSION_REGEX =/^1|2c|3$/ def validate_oids! @get = Array(@get).map do |oid| # verify oids for valid pattern and get rid or any leading dot if present unless oid =~ OID_REGEX @@ -167,9 +217,22 @@ $1 end raise(LogStash::ConfigurationError, "at least one get OID or one walk OID is required") if @get.empty? && @walk.empty? end + + def validate_v3_user! + errors = [] + + errors << "v3 user must have a \"security_name\" option" if @security_name.nil? + errors << "you must specify an auth protocol if you specify an auth pass" if @auth_protocol.nil? && !@auth_pass.nil? + errors << "you must specify an auth pass if you specify an auth protocol" if !@auth_protocol.nil? && @auth_pass.nil? + errors << "you must specify a priv protocol if you specify a priv pass" if @priv_protocol.nil? && !@priv_pass.nil? + errors << "you must specify a priv pass if you specify a priv protocol" if !@priv_protocol.nil? && @priv_pass.nil? + + raise(LogStash::ConfigurationError, errors.join(", ")) unless errors.empty? + end + def validate_hosts! # TODO: for new we only validate the host part, not the other optional options raise(LogStash::ConfigurationError, "at least one host definition is required") if Array(@hosts).empty?