├── .gitignore ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── fluent-plugin-snmp.gemspec ├── lib └── fluent │ └── plugin │ └── in_snmp.rb ├── sample ├── out_exec.rb.sample └── snmp.conf.sample └── test ├── helper.rb └── plugin └── test_in_snmp.rb /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | .yardoc 6 | Gemfile.lock 7 | InstalledFiles 8 | _yardoc 9 | coverage 10 | doc/ 11 | lib/bundler/man 12 | pkg 13 | rdoc 14 | spec/reports 15 | test/tmp 16 | test/version_tmp 17 | tmp 18 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in fluent-plugin-snmp.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Internet Initiative Japan Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fluent::Plugin::Snmp, a plugin for [Fluentd](http://fluentd.org) 2 | 3 | Fluentd snmp input plugin 4 | 5 | ## Installation possibilities 6 | 7 | - With your application's Gemfile: 8 | 9 | `gem 'fluent-plugin-snmp'` 10 | 11 | - Install it yourself as: 12 | 13 | `$ gem install fluent-plugin-snmp` 14 | 15 | - Intall with td-agent: 16 | 17 | `$ td-agent-gem install fluent-plugin-snmp` 18 | 19 | - If you are using vanilla Fluentd: 20 | 21 | `$ fluent-gem install fluent-plugin-snmp` 22 | 23 | ## Usage 24 | 25 | 26 | type snmp 27 | tag snmp.server1 28 | nodes name, value 29 | host localhost 30 | community public 31 | mib sysContact.0, sysDescr.0, sysName.0 32 | method_type get 33 | polling_time 5 34 | polling_type async_run 35 | 36 | 37 | 38 | type snmp 39 | tag snmp.server2 40 | host localhost 41 | community public 42 | mib hrStorageIndex, hrStorageDescr, hrStorageSize, hrStorageUsed 43 | mib_modules HOST-RESOURCES-MIB 44 | polling_time 0,10,20,30,40,50 45 | out_executor sample/out_exec.rb.sample 46 | 47 | 48 | 49 | type stdout 50 | 51 | 52 | ## Copyright 53 | Copyright (c) 2012 Internet Initiative Inc. 54 | Apache Licence, Version 2.0 55 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require "bundler/gem_tasks" 3 | require 'rake/testtask' 4 | Rake::TestTask.new(:test) do |test| 5 | test.libs << 'lib' << 'test' 6 | test.pattern = 'test/**/test_*.rb' 7 | test.verbose = true 8 | end 9 | 10 | task :default => :test 11 | -------------------------------------------------------------------------------- /fluent-plugin-snmp.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | 4 | Gem::Specification.new do |gem| 5 | gem.name = "fluent-plugin-snmp" 6 | gem.version = "0.0.9" 7 | gem.authors = ["hiro-su"] 8 | gem.email = ["h-sugimoto@iij.ad.jp"] 9 | gem.description = %q{Input plugin to snmp} 10 | gem.summary = %q{Input plugin to snmp} 11 | 12 | gem.files = `git ls-files`.split($\) 13 | gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) } 14 | gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) 15 | gem.require_paths = ["lib"] 16 | 17 | gem.add_runtime_dependency "fluentd", ">= 0.10.58" 18 | gem.add_runtime_dependency "snmp", "~> 1.2.0" 19 | gem.add_runtime_dependency "polling", "0.1.5" 20 | 21 | gem.add_development_dependency "bundler", "~> 1.12" 22 | gem.add_development_dependency "rake", "~> 10.0" 23 | gem.add_development_dependency "mocha", "~> 1.1.0" 24 | end 25 | -------------------------------------------------------------------------------- /lib/fluent/plugin/in_snmp.rb: -------------------------------------------------------------------------------- 1 | require 'fluent/input' 2 | require 'snmp' # http://snmplib.rubyforge.org/doc/index.html 3 | require 'polling' 4 | 5 | module Fluent 6 | class SnmpInput < Input 7 | Plugin.register_input('snmp', self) 8 | 9 | # Fluent Params 10 | # require param: tag, mib 11 | config_param :tag, :string 12 | config_param :mib, :string 13 | config_param :nodes, :string, :default => nil 14 | config_param :polling_time, :string, :default => nil 15 | config_param :polling_offset, :time, :default => 0 16 | config_param :polling_type, :string, :default => "run" #or async_run 17 | config_param :method_type, :string, :default => "walk" #or get 18 | config_param :out_executor, :string, :default => nil 19 | 20 | # SNMP Lib Params 21 | # require param: host, community 22 | # 23 | # Option Default Value 24 | # -------------------------------------- 25 | # :host 'localhost' 26 | # :port 161 27 | # :trap_port 162 28 | # :community 'public' 29 | # :write_community Same as :community 30 | # :version :SNMPv2c 31 | # :timeout 1 (timeout units are seconds) 32 | # :retries 5 33 | # :transport UDPTransport 34 | # :max_recv_bytes 8000 bytes 35 | # :mib_dir MIB::DEFAULT_MIB_PATH 36 | # :mib_modules SNMPv2-SMI, SNMPv2-MIB, IF-MIB, IP-MIB, TCP-MIB, UDP-MIB 37 | # :use_IPv6 false, unless :host is formatted like an IPv6 address 38 | config_param :host, :string 39 | config_param :port, :integer, :default => nil 40 | config_param :trap_port, :integer, :default => nil 41 | config_param :community, :string 42 | config_param :write_community, :string, :default => nil 43 | config_param :version, :string, :default => nil # Use :SNMPv1 or :SNMPv2c 44 | config_param :timeout, :time, :default => nil 45 | config_param :retries, :integer, :default => nil 46 | config_param :transport, :string, :default => nil 47 | config_param :max_recv_bytes, :string, :default => nil 48 | config_param :mib_dir, :string, :default => nil 49 | config_param :mib_modules, :string, :default => nil 50 | config_param :use_IPv6, :string, :default => nil 51 | 52 | def configure(conf) 53 | super 54 | 55 | raise ConfigError, "snmp: 'tag' is required param" if @tag.empty? 56 | raise ConfigError, "snmp: 'polling_type' parameter is required on snmp input" if @polling_type.empty? 57 | 58 | # @mib, @mib_modules, @nodesを配列に変換 59 | @mib = @mib.split(',').map{|str| str.strip} 60 | raise ConfigError, "snmp: 'mib' parameter is required on snmp input" if @mib.empty? 61 | 62 | @mib_modules = @mib_modules.split(',').map{|str| str.strip} unless @mib_modules.nil? 63 | raise ConfigError, "snmp: 'mib_modules' parameter is required on snmp input" if !@mib_modules.nil? && @mib_modules.empty? 64 | 65 | @nodes = @nodes.split(',').map{|str| str.strip} unless @nodes.nil? 66 | raise ConfigError, "snmp: 'nodes' parameter is required on snmp input" if !@nodes.nil? && @nodes.empty? 67 | 68 | @polling_time = @polling_time.split(',').map{|str| str.strip} unless @polling_time.nil? 69 | raise ConfigError, "snmp: 'polling_time' parameter is required on snmp input" if !@polling_time.nil? && @polling_time.empty? 70 | 71 | # snmp version 72 | @version = @version == "1" ? :SNMPv1 : :SNMPv2c 73 | 74 | # SNMP Libraryの初期値を設定 75 | @snmp_init_params = { 76 | :host => @host, #or conf["host"] 77 | :port => @port, 78 | :trap_port => @trap_port, 79 | :community => @community, 80 | :write_community => @write_community, 81 | :version => @version, 82 | :timeout => @timeout, 83 | :retries => @retries, 84 | :transport => @transport, 85 | :max_recv_bytes => @max_recv_bytes, 86 | :mib_dir => @mib_dir, 87 | :mib_modules => @mib_modules, 88 | :use_IPv6 => @use_IPv6 89 | } 90 | 91 | unless @out_executor.nil? 92 | $log.info "load snmp out executor #{out_executor}" 93 | @out_exec = lambda do |manager| 94 | load @out_executor 95 | opts = { 96 | :tag => @tag, 97 | :mib => @mib, 98 | :mib_modules => @mib_modules, 99 | :nodes => @nodes, 100 | :conf => conf 101 | } 102 | out_exec(manager, opts) 103 | end 104 | end 105 | end 106 | 107 | def start 108 | super 109 | @manager = SNMP::Manager.new(@snmp_init_params) 110 | @thread = Thread.new(&method(:run)) 111 | @end_flag = false 112 | end 113 | 114 | def run 115 | Polling.setting offset: @polling_offset 116 | Polling.__send__(@polling_type, @polling_time) do 117 | break if @end_flag 118 | exec_snmp(manager: @manager, mib: @mib, nodes: @nodes, method_type: @method_type) 119 | end 120 | rescue TypeError => ex 121 | $log.error "run TypeError", :error=>ex.message 122 | exit 123 | rescue => ex 124 | $log.fatal "run failed", :error=>ex.inspect 125 | $log.error_backtrace ex.backtrace 126 | exit 127 | end 128 | 129 | def shutdown 130 | @end_flag = true 131 | if @thread 132 | @thread.run 133 | @thread.join 134 | @thread = nil 135 | end 136 | if @manager 137 | @manager.close 138 | end 139 | end 140 | 141 | private 142 | 143 | def exec_snmp opts={} 144 | if @out_executor.nil? 145 | case opts[:method_type] 146 | when /^walk$/ 147 | snmp_walk(opts[:manager], opts[:mib], opts[:nodes]) 148 | when /^get$/ 149 | snmp_get(opts[:manager], opts[:mib], opts[:nodes]) 150 | else 151 | $log.error "unknow exec method" 152 | raise 153 | end 154 | else 155 | @out_exec.call opts[:manager] 156 | end 157 | rescue SNMP::RequestTimeout => ex 158 | $log.error "exec_snmp failed #{@tag}", :error=>ex.inspect 159 | rescue => ex 160 | $log.error "exec_snmp failed #{@tag}", :error=>ex.inspect 161 | $log.error_backtrace ex.backtrace 162 | raise ex 163 | end 164 | 165 | def snmp_walk(manager, mib, nodes, test=false) 166 | manager.walk(mib) do |row| 167 | time = Engine.now 168 | time = time - time % 5 169 | record = {} 170 | row.each do |vb| 171 | if nodes.nil? 172 | record["value"] = vb 173 | else 174 | nodes.each{|param| record[param] = check_type(vb.__send__(param))} 175 | end 176 | router.emit(@tag, time, record) 177 | return {:time => time, :record => record} if test 178 | end 179 | end 180 | rescue => ex 181 | raise ex 182 | end 183 | 184 | def snmp_get(manager, mib, nodes, test=false) 185 | manager.get(mib).each_varbind do |vb| 186 | time = Engine.now 187 | time = time - time % 5 188 | record = {} 189 | if nodes.nil? 190 | record["value"] = vb 191 | else 192 | nodes.each{|param| record[param] = check_type(vb.__send__(param))} 193 | end 194 | router.emit(@tag, time, record) 195 | return {:time => time, :record => record} if test 196 | end 197 | rescue => ex 198 | raise ex 199 | end 200 | 201 | # data check from snmp 202 | def check_type(value) 203 | if value =~ /^\d+(\.\d+)?$/ 204 | return value.to_f 205 | elsif SNMP::Integer === value 206 | return value.to_i 207 | else 208 | return value.to_s 209 | end 210 | rescue => ex 211 | $log.error "snmp failed to check_type", :error=>ex.message 212 | $log.warn_backtrace ex.backtrace 213 | end 214 | end 215 | end 216 | -------------------------------------------------------------------------------- /sample/out_exec.rb.sample: -------------------------------------------------------------------------------- 1 | module Fluent 2 | class SnmpInput 3 | def out_exec manager, opts={} 4 | manager.walk(opts[:mib]) do |row| 5 | time = Time.now.to_i 6 | time = time - time % 5 7 | record = {} 8 | row.each do |vb| 9 | record["name"] = vb.name.to_s 10 | record["value"] = vb.value.to_s 11 | router.emit opts[:tag], time, record 12 | end 13 | end 14 | end 15 | end 16 | end 17 | 18 | -------------------------------------------------------------------------------- /sample/snmp.conf.sample: -------------------------------------------------------------------------------- 1 | 2 | @type snmp 3 | tag snmp.server1 4 | nodes name, value 5 | host localhost 6 | community public 7 | mib sysContact.0, sysDescr.0, sysName.0 8 | method_type get 9 | polling_time 5 10 | polling_type async_run 11 | 12 | 13 | 14 | @type snmp 15 | tag snmp.server2 16 | host localhost 17 | community public 18 | mib hrStorageIndex, hrStorageDescr, hrStorageSize, hrStorageUsed 19 | mib_modules HOST-RESOURCES-MIB 20 | polling_time 0,10,20,30,40,50 21 | out_executor sample/out_exec.rb.sample 22 | 23 | 24 | 25 | @type stdout 26 | 27 | -------------------------------------------------------------------------------- /test/helper.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | 3 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) 4 | $LOAD_PATH.unshift(File.dirname(__FILE__)) 5 | 6 | require 'fluent/test' 7 | 8 | unless ENV.has_key?('VERBOSE') 9 | nulllogger = Object.new 10 | nulllogger.instance_eval {|obj| 11 | def method_missing(method, *args) 12 | # pass 13 | end 14 | } 15 | $log = nulllogger 16 | end 17 | 18 | require 'fluent/plugin/in_snmp' 19 | 20 | class Test::Unit::TestCase 21 | end 22 | 23 | -------------------------------------------------------------------------------- /test/plugin/test_in_snmp.rb: -------------------------------------------------------------------------------- 1 | require 'helper' 2 | require 'mocha/setup' 3 | require 'time' 4 | 5 | class SnmpInputTest < Test::Unit::TestCase 6 | 7 | def setup 8 | Fluent::Test.setup 9 | #@obj = Fluent::SnmpInput.new 10 | end 11 | 12 | CONFIG = %[ 13 | tag snmp.server1 14 | mib hrStorageIndex, hrStorageDescr, hrStorageSize, hrStorageUsed 15 | nodes name, value 16 | polling_time 0,10,20,30,40,50 17 | polling_offset 0 18 | host localhost 19 | community public 20 | mib_modules HOST-RESOURCES-MIB, IF-MIB 21 | timeout 3s 22 | method_type walk 23 | out_executor sample/out_exec.rb.sample 24 | ] 25 | 26 | def create_driver(conf=CONFIG) 27 | Fluent::Test::InputTestDriver.new(Fluent::SnmpInput).configure(conf) 28 | end 29 | 30 | def test_configure 31 | d = create_driver 32 | 33 | # Fluent Params 34 | assert_equal "snmp.server1", d.instance.tag 35 | assert_equal ["hrStorageIndex","hrStorageDescr","hrStorageSize","hrStorageUsed"], d.instance.mib 36 | assert_equal ["name","value"], d.instance.nodes 37 | assert_equal ["0","10","20","30","40","50"], d.instance.polling_time 38 | assert_equal 0, d.instance.polling_offset 39 | assert_equal "walk", d.instance.method_type 40 | assert_equal "sample/out_exec.rb.sample", d.instance.out_executor 41 | 42 | # SNMP Lib Params 43 | assert_equal "localhost", d.instance.host 44 | assert_nil d.instance.port 45 | assert_nil d.instance.trap_port 46 | assert_equal "public", d.instance.community 47 | assert_nil d.instance.write_community 48 | assert_equal :SNMPv2c, d.instance.version 49 | assert_equal 3, d.instance.timeout 50 | assert_nil d.instance.transport 51 | assert_nil d.instance.max_recv_bytes 52 | assert_nil d.instance.mib_dir 53 | assert_equal ["HOST-RESOURCES-MIB","IF-MIB"], d.instance.mib_modules 54 | assert_nil d.instance.use_IPv6 55 | end 56 | 57 | def test_check_type 58 | d = create_driver 59 | 60 | assert_equal "test", d.instance.__send__(:check_type, "test") 61 | assert_equal "utrh0", d.instance.__send__(:check_type, "utrh0") 62 | assert_equal "sensorValue_degC", d.instance.__send__(:check_type, "sensorValue_degC") 63 | assert_equal "sensorValue_%RH", d.instance.__send__(:check_type, "sensorValue_%RH") 64 | assert_equal 12.00, d.instance.__send__(:check_type, "12") 65 | assert_equal 12.34, d.instance.__send__(:check_type, "12.34") 66 | assert_equal 12, d.instance.__send__(:check_type, SNMP::Integer.new("12")) 67 | assert_equal String, d.instance.__send__(:check_type, "test").class 68 | assert_equal String, d.instance.__send__(:check_type, "utrh0").class 69 | assert_equal String, d.instance.__send__(:check_type, "sensorValue_degC").class 70 | assert_equal String, d.instance.__send__(:check_type, "sensorValue_%RH").class 71 | assert_equal Float, d.instance.__send__(:check_type, "12").class 72 | assert_equal Float, d.instance.__send__(:check_type, "12.34").class 73 | assert_equal Fixnum, d.instance.__send__(:check_type, SNMP::Integer.new("12")).class 74 | end 75 | 76 | def test_snmp_walk 77 | d = create_driver 78 | nodes = d.instance.nodes 79 | mib = d.instance.mib 80 | 81 | snmp_init_params = { 82 | :host => d.instance.host, 83 | :community => d.instance.community, 84 | :timeout => d.instance.timeout, 85 | :retries => d.instance.retries, 86 | :mib_dir => d.instance.mib_dir, 87 | :mib_modules => d.instance.mib_modules, 88 | } 89 | 90 | # unixtime 1356965990 91 | Time.stubs(:now).returns(Time.parse "2012/12/31 23:59:50") 92 | manager = SNMP::Manager.new(snmp_init_params) 93 | 94 | data = d.instance.__send__(:snmp_walk, manager, mib, nodes, true) 95 | record = data[:record] 96 | 97 | assert_equal 1356965990, data[:time] 98 | assert_equal "HOST-RESOURCES-MIB::hrStorageIndex.1", record["name"] 99 | assert_equal 1, record["value"] 100 | end 101 | 102 | def test_snmp_get 103 | d = create_driver %[ 104 | tag snmp.server1 105 | mib hrStorageIndex.31 106 | nodes name, value 107 | polling_time 0,10,20,30,40,50 108 | host localhost 109 | community public 110 | mib_modules HOST-RESOURCES-MIB 111 | ] 112 | nodes = d.instance.nodes 113 | mib = d.instance.mib 114 | 115 | snmp_init_params = { 116 | :host => d.instance.host, 117 | :community => d.instance.community, 118 | :timeout => d.instance.timeout, 119 | :retries => d.instance.retries, 120 | :mib_dir => d.instance.mib_dir, 121 | :mib_modules => d.instance.mib_modules, 122 | } 123 | 124 | # unixtime 1356965990 125 | Time.stubs(:now).returns(Time.parse "2012/12/31 23:59:50") 126 | manager = SNMP::Manager.new(snmp_init_params) 127 | 128 | data = d.instance.__send__(:snmp_get, manager, mib, nodes, true) 129 | record = data[:record] 130 | 131 | assert_equal 1356965990, data[:time] 132 | assert_equal "HOST-RESOURCES-MIB::hrStorageIndex.31", record["name"] 133 | assert_equal 31, record["value"] 134 | end 135 | 136 | def test_exec_snmp 137 | d = create_driver 138 | snmp_init_params = { 139 | :host => d.instance.host, 140 | :community => d.instance.community, 141 | :timeout => d.instance.timeout, 142 | :retries => d.instance.retries, 143 | :mib_dir => d.instance.mib_dir, 144 | :mib_modules => d.instance.mib_modules, 145 | } 146 | 147 | manager = SNMP::Manager.new(snmp_init_params) 148 | 149 | opts = { 150 | :manager => manager, 151 | :method_type => d.instance.method_type, 152 | :mib => d.instance.mib, 153 | :nodes => d.instance.nodes, 154 | :test => true 155 | } 156 | 157 | exec = d.instance.__send__(:exec_snmp, opts) 158 | assert_equal nil, exec 159 | end 160 | end 161 | --------------------------------------------------------------------------------