├── .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 |
--------------------------------------------------------------------------------