sloth-snmp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5cea4c07bc9f9ce8c5e8d21f486a58186450aec95d818187ca64d998d641ede9
4
+ data.tar.gz: d1b59278de3b73f751ac6b2cfb4510522b09f8c26ed248d90babc43211f71ddf
5
+ SHA512:
6
+ metadata.gz: bb2092ad1507afc77094d4d21e7a1a215d4f96bf2bd6e93ba3e5f4dfb765159532107601b23382ebfdbf9da23525ddfc8c97cf98b4ccd34d0c33628783ac54b0
7
+ data.tar.gz: 8ac8f497c635fc6d63dff118dfce4fffd86acceacda57f18164ddd61e484edfc57039246d143d2f0cb7cac76da49c34d851310a66564bac4f2f814b70292a17e
@@ -0,0 +1,23 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ /vendor/
16
+ *.pid
17
+ *.log
18
+ *.log.*
19
+ /var/
20
+ /log/
21
+ *.swp
22
+ /.rspec_status
23
+
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.2
6
+ before_install: gem install bundler -v 2.1.4
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in snmp_handler.gemspec
4
+ gemspec
5
+
@@ -0,0 +1,213 @@
1
+ = Sloth::Snmp
2
+
3
+ Sloth::Snmp is yet another wrapper library for snmp.
4
+
5
+ == Features
6
+
7
+ * Handles SNMP asynchronously.
8
+ * By referring to the YAML file generated from the MIB file, you can operate without describing raw OID.
9
+
10
+ == Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ [source,ruby]
15
+ ----
16
+ gem 'sloth/snmp'
17
+ ----
18
+
19
+ And then execute:
20
+
21
+ $ bundle install
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install sloth-snmp
26
+ or
27
+ $ gem install -l sloth-snmp-x.x.x.gem
28
+
29
+ == Usage
30
+
31
+ === New
32
+ [source,ruby]
33
+ ----
34
+ require "sloth/snmp"
35
+
36
+ snmp = Sloth::Snmp.new
37
+ snmp = Sloth::Snmp.new( bind: 1161 )
38
+ snmp = Sloth::Snmp.new( bind: "127.0.0.1" )
39
+ snmp = Sloth::Snmp.new( bind: "192.168.0.1:1161" )
40
+ snmp = Sloth::Snmp.new( mibs: "RS-232-MIB.yaml" )
41
+ snmp = Sloth::Snmp.new( mibs: "spec/sloth/RFC1414-MIB.yaml" )
42
+ ----
43
+
44
+ === Get
45
+
46
+ [source,ruby]
47
+ ----
48
+ require "sloth/snmp"
49
+
50
+ snmp = Sloth::Snmp.new
51
+ peer = "127.0.0.1"
52
+ topics = [ "sysDescr.0", "sysUpTime.0", "sysName.0" ],
53
+ tuples = snmp.get( peer, topics )
54
+ tuples.each do |oid, tuple|
55
+ p tuple
56
+ end
57
+ ----
58
+
59
+ === Set
60
+
61
+ [source,ruby]
62
+ ----
63
+ require "sloth/snmp"
64
+
65
+ snmp = Sloth::Snmp.new
66
+ peer = "127.0.0.1"
67
+ tuple = { topic: "sysName.0", type: SNMP::OctetString, value: Time.now.to_s },
68
+ snmp.set( peer, tuple )
69
+
70
+ tuples = snmp.get( peer, tuple[:topic] )
71
+ tuples.each do |oid, tuple|
72
+ p tuple
73
+ end
74
+ ----
75
+
76
+ === Walk
77
+
78
+ [source,ruby]
79
+ ----
80
+ require "sloth/snmp"
81
+
82
+ snmp = Sloth::Snmp.new
83
+ peer = "127.0.0.1"
84
+ topic = "internet"
85
+ tuples = snmp.walk( peer, topic )
86
+ tuples.each do |oid, tuple|
87
+ p tuple
88
+ end
89
+ ----
90
+
91
+ === Trap
92
+
93
+ [source,ruby]
94
+ ----
95
+ require "sloth/snmp"
96
+
97
+ snmp = Sloth::Snmp.new( bind: 1162 )
98
+ snmp.trap( "snmpInTraps", "snmpOutTraps" ) do |trapname, source_ip, tuples|
99
+ p [:trapname, trapname]
100
+ p [:source_ip, source_ip]
101
+ tuples.each do |oid, tuple|
102
+ p tuple
103
+ end
104
+ end
105
+
106
+ sleep
107
+ ----
108
+
109
+ == Reference
110
+
111
+ === Create a new Sloth::Snmp.
112
+
113
+ [source,ruby]
114
+ ----
115
+ Sloth::Snmp.new( mibs: nil, bind: nil, rocommunity: "public", rwcommunity: "private" )
116
+ ----
117
+
118
+ * Result:
119
+ - Sloth::Snmp object.
120
+
121
+ * Parameter:
122
+ ** mibs: Additional MIB file path, or array of the paths. (default: nil)
123
+ ** bind: Bind host and port for trap. (default: "0.0.0.0:162")
124
+ ** rocommunity: Community string for Read Only. (default: "public")
125
+ ** rwcommunity: Community string for Read/Write. (default: "private")
126
+
127
+ === SNMP Get.
128
+
129
+ [source,ruby]
130
+ ----
131
+ Sloth::Snmp#get( peer, topics, community: nil, bindto: nil, device: nil )
132
+ ----
133
+
134
+ * Result:
135
+ * Hash of key=>oid, value=>{name, value}.
136
+
137
+ * Parameter:
138
+ ** peer: Target IP address and port. (default port: 162)
139
+ ** topics: Topic(s) for SNMP Get Request.
140
+ ** community: Community string. (default: nil)
141
+ ** bindto: Interface address. (default: nil)
142
+ ** device: Interface name. (default: nil)
143
+
144
+ === SNMP Set.
145
+
146
+ [source,ruby]
147
+ ----
148
+ Sloth::Snmp#set( peer, topics, community: nil, bindto: nil, device: nil )
149
+ ----
150
+
151
+ * Result:
152
+ * Hash of key=>oid, value=>{name, value}.
153
+
154
+ * Parameter:
155
+ ** peer: Target IP address and port. (default port: 162)
156
+ ** tuple: Hash of topic, type, value for SNMP Set Request.
157
+ ** community: Community string. (default: nil)
158
+ ** bindto: Interface address. (default: nil)
159
+ ** device: Interface name. (default: nil)
160
+
161
+ === SNMP Walk.
162
+
163
+ [source,ruby]
164
+ ----
165
+ Sloth::Snmp#walk( target, topic, port: 162, community: "public", bindto: nil, device: nil )
166
+ ----
167
+
168
+ * Result:
169
+ ** Hash of key=>oid, value=>{name, value}.
170
+
171
+ * Parameter:
172
+ ** peer: Target IP address and port. (default port: 162)
173
+ ** topic: Topic for SNMP Get Next.
174
+ ** community: Community string. (default: nil)
175
+ ** bindto: Interface address. (default: nil)
176
+ ** device: Interface name. (default: nil)
177
+
178
+ === SNMP Trap.
179
+
180
+ [source,ruby]
181
+ ----
182
+ Sloth::Snmp#trap( *topics, &block )
183
+ ----
184
+
185
+ * Result:
186
+ ** nil.
187
+
188
+ * Parameter:
189
+ ** topics: The topic(s) that enables trap monitoring.
190
+ ** block: Callback action.
191
+
192
+ === SNMP Untrap.
193
+
194
+ [source,ruby]
195
+ ----
196
+ Sloth::Snmp#untrap( *topics )
197
+ ----
198
+
199
+ * Result:
200
+ ** nil.
201
+
202
+ * Parameter:
203
+ ** topics: The topic(s) that disables trap monitoring.
204
+
205
+ == Contributing
206
+
207
+ Bug reports and pull requests are welcome on GitHub at https://github.com/arimay/sloth-snmp.
208
+
209
+ == License
210
+
211
+ The gem is available as open source under the terms of the http://opensource.org/licenses/MIT[MIT License].
212
+
213
+ Copyright (c) ARIMA Yasuhiro <arima.yasuhiro@gmail.com>
@@ -0,0 +1,213 @@
1
+ = Sloth::Snmp
2
+
3
+ Sloth::Snmp はさらにもうひとつの SNMP ラッパーライブラリ.
4
+
5
+ == 特徴
6
+
7
+ * 非同期で SNMP を扱う.
8
+ * MIBファイルから生成したYAMLファイルを参照することで、生のOIDを記述することなく操作できる.
9
+
10
+ == 導入
11
+
12
+ アプリの Gemfile にこの行を追加
13
+
14
+ [source,ruby]
15
+ ----
16
+ gem 'sloth/snmp'
17
+ ----
18
+
19
+ それから実行
20
+
21
+ $ bundle install
22
+
23
+ または次のように手動で導入
24
+
25
+ $ gem install sloth-snmp
26
+ or
27
+ $ gem install -l sloth-snmp-x.x.x.gem
28
+
29
+ == 使い方
30
+
31
+ === 新規
32
+ [source,ruby]
33
+ ----
34
+ require "sloth/snmp"
35
+
36
+ snmp = Sloth::Snmp.new
37
+ snmp = Sloth::Snmp.new( bind: 1161 )
38
+ snmp = Sloth::Snmp.new( bind: "127.0.0.1" )
39
+ snmp = Sloth::Snmp.new( bind: "192.168.0.1:1161" )
40
+ snmp = Sloth::Snmp.new( mibs: "RS-232-MIB.yaml" )
41
+ snmp = Sloth::Snmp.new( mibs: "spec/sloth/RFC1414-MIB.yaml" )
42
+ ----
43
+
44
+ === 取得
45
+
46
+ [source,ruby]
47
+ ----
48
+ require "sloth/snmp"
49
+
50
+ snmp = Sloth::Snmp.new
51
+ peer = "127.0.0.1"
52
+ topics = [ "sysDescr.0", "sysUpTime.0", "sysName.0" ],
53
+ tuples = snmp.get( peer, topics )
54
+ tuples.each do |oid, tuple|
55
+ p tuple
56
+ end
57
+ ----
58
+
59
+ === 更新
60
+
61
+ [source,ruby]
62
+ ----
63
+ require "sloth/snmp"
64
+
65
+ snmp = Sloth::Snmp.new
66
+ peer = "127.0.0.1"
67
+ tuple = { topic: "sysName.0", type: SNMP::OctetString, value: Time.now.to_s },
68
+ snmp.set( peer, tuple )
69
+
70
+ tuples = snmp.get( peer, tuple[:topic] )
71
+ tuples.each do |oid, tuple|
72
+ p tuple
73
+ end
74
+ ----
75
+
76
+ === 巡回
77
+
78
+ [source,ruby]
79
+ ----
80
+ require "sloth/snmp"
81
+
82
+ snmp = Sloth::Snmp.new
83
+ peer = "127.0.0.1"
84
+ topic = "internet"
85
+ tuples = snmp.walk( peer, topic )
86
+ tuples.each do |oid, tuple|
87
+ p tuple
88
+ end
89
+ ----
90
+
91
+ === トラップ
92
+
93
+ [source,ruby]
94
+ ----
95
+ require "sloth/snmp"
96
+
97
+ snmp = Sloth::Snmp.new( bind: 1162 )
98
+ snmp.trap( "snmpInTraps", "snmpOutTraps" ) do |trapname, source_ip, tuples|
99
+ p [:trapname, trapname]
100
+ p [:source_ip, source_ip]
101
+ tuples.each do |oid, tuple|
102
+ p tuple
103
+ end
104
+ end
105
+
106
+ sleep
107
+ ----
108
+
109
+ == リファレンス
110
+
111
+ === 新たな Sloth::Snmp を作成する.
112
+
113
+ [source,ruby]
114
+ ----
115
+ Sloth::Snmp.new( mibs: nil, bind: nil, rocommunity: "public", rwcommunity: "private" )
116
+ ----
117
+
118
+ * Result:
119
+ ** Sloth::Snmp オブジェクト.
120
+
121
+ * Parameter:
122
+ ** mibs: 追加 MIB ファイルパスの文字列. またはその配列. (default: nil)
123
+ ** bind: トラップ用のバインドホストとポート. (default: "0.0.0.0:162")
124
+ ** rocommunity: 読み取り用コミュニティ文字列. (default: "public")
125
+ ** rwcommunity: 読み書き用コミュニティ文字列. (default: "private")
126
+
127
+ === SNMP Get.
128
+
129
+ [source,ruby]
130
+ ----
131
+ Sloth::Snmp#get( peer, topics, community: nil, bindto: nil, device: nil )
132
+ ----
133
+
134
+ * Result:
135
+ ** hash of key=>oid, value=>{name, value}.
136
+
137
+ * Parameter:
138
+ ** peer: 宛先IPアドレスとポート. (default port: 162)
139
+ ** topics: SNMP Get Request 用のトピック.
140
+ ** community: コミュニティ文字列. (default: nil)
141
+ ** bindto: インターフェースアドレス. (default: nil)
142
+ ** device: インターフェース名称. (default: nil)
143
+
144
+ === SNMP Set.
145
+
146
+ [source,ruby]
147
+ ----
148
+ Sloth::Snmp#set( peer, topics, community: nil, bindto: nil, device: nil )
149
+ ----
150
+
151
+ * Result:
152
+ ** hash of key=>oid, value=>{name, value}.
153
+
154
+ * Parameter:
155
+ ** peer: 宛先IPアドレスとポート. (default port: 162)
156
+ ** tuple: SNMP Set Request 用のトピック、型、値のハッシュ.
157
+ ** community: コミュニティ文字列. (default: nil)
158
+ ** bindto: インターフェースアドレス. (default: nil)
159
+ ** device: インターフェース名称. (default: nil)
160
+
161
+ === SNMP Walk.
162
+
163
+ [source,ruby]
164
+ ----
165
+ Sloth::Snmp#walk( peer, topic, port: 162, community: "public", bindto: nil, device: nil )
166
+ ----
167
+
168
+ * Result:
169
+ ** hash of key=>oid, value=>{name, value}.
170
+
171
+ * Parameter:
172
+ ** peer: 宛先IPアドレスとポート. (default port: 162)
173
+ ** topic: SNMP Get Next 用のトピック.
174
+ ** community: コミュニティ文字列. (default: nil)
175
+ ** bindto: インターフェースアドレス. (default: nil)
176
+ ** device: インターフェース名称. (default: nil)
177
+
178
+ === SNMP Trap.
179
+
180
+ [source,ruby]
181
+ ----
182
+ Sloth::Snmp#trap( *topics, &block )
183
+ ----
184
+
185
+ * Result:
186
+ ** nil.
187
+
188
+ * Parameter:
189
+ ** topics: トラップを有効化するトピック.
190
+ ** block: コールバック動作.
191
+
192
+ === SNMP Untrap.
193
+
194
+ [source,ruby]
195
+ ----
196
+ Sloth::Snmp#untrap( *topics )
197
+ ----
198
+
199
+ * Result:
200
+ ** nil.
201
+
202
+ * Parameter:
203
+ ** topics: トラップの監視を無効化するトピック.
204
+
205
+ == 貢献
206
+
207
+ 不具合報告とプルリクエストは GitHub https://github.com/arimay/sloth-snmp まで.
208
+
209
+ == ライセンス
210
+
211
+ この Gem は、 http://opensource.org/licenses/MIT[MITライセンス] の条件に基づいてオープンソースとして入手できる.
212
+
213
+ Copyright (c) ARIMA Yasuhiro <arima.yasuhiro@gmail.com>
@@ -0,0 +1,96 @@
1
+ require "bundler/gem_helper"
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
8
+
9
+ class Bundler::GemHelper
10
+
11
+ def git_archive( dir = "../zip" )
12
+ FileUtils.mkdir_p dir
13
+ dest_path = File.join(dir, "#{name}-#{version}.zip")
14
+ cmnd = "git archive --format zip --prefix=#{name}/ HEAD > #{dest_path}"
15
+
16
+ _, code = sh_with_status( cmnd )
17
+ raise "Couldn't archive gem," unless code == 0
18
+
19
+ Bundler.ui.confirm "#{name} #{version} archived to #{dest_path}."
20
+ end
21
+
22
+ def git_push
23
+ ver = version.to_s
24
+
25
+ cmnd = "git push origin #{ver} "
26
+ _, code = sh_with_status( cmnd )
27
+ raise "Couldn't git push origin." unless code == 0
28
+
29
+ cmnd = "git push "
30
+ _, code = sh_with_status( cmnd )
31
+ raise "Couldn't git push." unless code == 0
32
+
33
+ Bundler.ui.confirm "Git Push #{ver}."
34
+ end
35
+
36
+ def update_version( new_version )
37
+ version_filename = %x[ find . -type f -name "version.rb" | grep -v vendor | head -1 ].chomp
38
+ version_pathname = File.expand_path( version_filename )
39
+ lines = File.open( version_pathname ).read
40
+ lines = lines.gsub( /VERSION\s*=\s*\"\d+\.\d+\.\d+\"/, "VERSION = \"#{new_version}\"" )
41
+ File.open( version_pathname, "w" ) do |file|
42
+ file.write( lines )
43
+ end
44
+
45
+ cmnd = "git add #{version_pathname} "
46
+ _, code = sh_with_status( cmnd )
47
+ raise "Couldn't git add," unless code == 0
48
+
49
+ cmnd = "git commit -m '#{new_version}' "
50
+ _, code = sh_with_status( cmnd )
51
+ raise "Couldn't git commit." unless code == 0
52
+
53
+ cmnd = "git tag #{new_version} "
54
+ _, code = sh_with_status( cmnd )
55
+ raise "Couldn't git tag." unless code == 0
56
+
57
+ Bundler.ui.confirm "Update Tags to #{new_version}."
58
+ end
59
+
60
+ end
61
+
62
+ Bundler::GemHelper.new(Dir.pwd).instance_eval do
63
+
64
+ desc "Archive #{name}-#{version}.zip from repository"
65
+ task 'zip' do
66
+ git_archive
67
+ end
68
+
69
+ desc "Git Push"
70
+ task 'push' do
71
+ git_push
72
+ end
73
+
74
+ desc "Update Version Tiny"
75
+ task 'tiny' do
76
+ major, minor, tiny = version.to_s.split('.')
77
+ new_version = [major, minor, tiny.to_i + 1].join('.')
78
+ update_version( new_version )
79
+ end
80
+
81
+ desc "Update Version Minor"
82
+ task 'minor' do
83
+ major, minor, _tiny = version.to_s.split('.')
84
+ new_version = [major, minor.to_i + 1, 0].join('.')
85
+ update_version( new_version )
86
+ end
87
+
88
+ desc "Update Version Major"
89
+ task 'major' do
90
+ major, _minor, _tiny = version.to_s.split('.')
91
+ new_version = [major.to_i + 1, 0, 0].join('.')
92
+ update_version( new_version )
93
+ end
94
+
95
+ end
96
+
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "sloth/snmp"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,3 @@
1
+ require "sloth/snmp/version"
2
+ require "sloth/snmp/impl"
3
+
@@ -0,0 +1,302 @@
1
+ require "snmp"
2
+
3
+ module Sloth
4
+ class Snmp
5
+
6
+ class Error < StandardError; end
7
+
8
+ class UDPTransportExt < ::SNMP::UDPTransport
9
+ def initialize( address_family, bindto: nil, device: nil )
10
+ super
11
+ @socket = UDPSocket.open( address_family )
12
+ if bindto
13
+ host, port = bindto.split(':') rescue nil
14
+ @socket.bind( host, port.to_i )
15
+ end
16
+ if device
17
+ @socket.setsockopt( Socket::SOL_SOCKET, Socket::SO_BINDTODEVICE, device )
18
+ end
19
+ end
20
+ end
21
+
22
+ def initialize( mibs: nil, bind: nil, rocommunity: "public", rwcommunity: "private" )
23
+ case bind
24
+ when Integer, NilClass
25
+ @host = "0.0.0.0"
26
+ @port = bind || 162
27
+ when String
28
+ host, port = bind.split(':') rescue nil
29
+ if host.nil? || host.empty?
30
+ @host = "0.0.0.0"
31
+ else
32
+ @host = host
33
+ end
34
+ if port.nil? || port.empty?
35
+ @port = 162
36
+ else
37
+ @port = port.to_i
38
+ end
39
+ else
40
+ raise Sloth::Snmp::Error, "invalid class. : %s" % bind.class
41
+ end
42
+ @rocommunity = rocommunity
43
+ @rwcommunity = rwcommunity
44
+
45
+ @mibs = SNMP::MIB.new
46
+ @mibs.load_module( "RFC1155-SMI" )
47
+ @mibs.load_module( "RFC1158-MIB" )
48
+
49
+ case mibs
50
+ when NilClass
51
+ mibs = []
52
+ when Array
53
+ # noop
54
+ when String
55
+ mibs = [mibs]
56
+ else
57
+ raise Sloth::Snmp::Error, "invalid class. : %s" % mibs.class
58
+ end
59
+
60
+ mibpath = mibs.shift
61
+ while mibpath
62
+ mibbase = File.basename( mibpath )
63
+ mibdir = File.dirname( mibpath )
64
+ if ( mibdir == "." && mibbase == mibpath )
65
+ @mibs.load_module( mibbase.gsub( /\..*\Z/, "" ) )
66
+ else
67
+ @mibs.load_module( mibbase.gsub( /\..*\Z/, "" ), mibdir )
68
+ end
69
+ mibpath = mibs.shift
70
+ end
71
+
72
+ @topics = {}
73
+ @mutex = ::Mutex.new
74
+ end
75
+
76
+ def start_listener
77
+ @thread ||= Thread.start do
78
+ listener = SNMP::TrapListener.new( host: @host, port: @port, community: @rocommunity ) do |manager|
79
+ manager.on_trap_v2c do |mesg|
80
+ begin
81
+ next if mesg.error_status != :noError
82
+
83
+ source_ip = mesg.source_ip
84
+ trap_oid = mesg.trap_oid.join(".")
85
+ trapname = @mibs.name( trap_oid )
86
+ items = {}
87
+ mesg.each_varbind do |varbind|
88
+ oid, item = * parse_varbind( varbind )
89
+ items[oid] = item
90
+ end
91
+ Thread.start do
92
+ @mutex.synchronize do
93
+ @topics[ trap_oid ]&.call( trapname, source_ip, items )
94
+ end
95
+ end
96
+
97
+ rescue => e
98
+ raise Sloth::Snmp::Error, e.message
99
+
100
+ end
101
+ end
102
+ end
103
+ Thread.current[:listener] = listener
104
+ listener.join
105
+ end
106
+ end
107
+
108
+ def stop_listener
109
+ @mutex.synchronize do
110
+ @topics.clear
111
+ @thread[:listener].exit
112
+ @thread = nil
113
+ end
114
+ end
115
+
116
+ def trap( *topics, &block )
117
+ @mutex.synchronize do
118
+ topics.each do |topic|
119
+ trap_oid = SNMP::ObjectId.new( topic ) rescue @mibs.oid( topic )
120
+ @topics[trap_oid.to_str] = block
121
+ end
122
+ end
123
+ start_listener
124
+ end
125
+
126
+ def untrap( *topics )
127
+ @mutex.synchronize do
128
+ topics.each do |topic|
129
+ trap_oid = SNMP::ObjectId.new( topic ) rescue @mibs.oid( topic )
130
+ @topics.delete( trap_oid.to_str )
131
+ end
132
+ end
133
+ stop_listener if @topics.empty?
134
+ end
135
+
136
+ def get( peer, topics, **options )
137
+ host, port = peer.split(':') rescue nil
138
+ host = "127.0.0.1" if host.nil? || host.empty?
139
+ port = (port || 161).to_i
140
+ community = options[:rocommunity] || @rocommunity
141
+
142
+ case topics
143
+ when String
144
+ oids = [ ( SNMP::ObjectId.new( topics ) rescue @mibs.oid( topics ) ) ]
145
+ when Array
146
+ oids = topics.map do |topic|
147
+ SNMP::ObjectId.new( topic ) rescue @mibs.oid( topic )
148
+ end
149
+ else
150
+ raise Sloth::Snmp::Error, "topics missing."
151
+ end
152
+
153
+ if options[:bindto] && options[:device]
154
+ transport = UDPTransportExt.new( Socket::AF_INET, bindto: options[:bindto], device: options[:device] )
155
+ end
156
+
157
+ manager = SNMP::Manager.new( host: host, port: port, community: community, transport: transport )
158
+
159
+ items = {}
160
+ begin
161
+ response = Thread.handle_interrupt( ::Timeout::Error => :on_blocking ) do
162
+ manager.get( oids )
163
+ end
164
+
165
+ response.each_varbind do |varbind|
166
+ oid, item = * parse_varbind( varbind )
167
+ items[oid] = item
168
+ end
169
+
170
+ rescue SNMP::RequestTimeout => e
171
+ raise Sloth::Snmp::Error, e.message
172
+
173
+ rescue => e
174
+ raise Sloth::Snmp::Error, e.message
175
+
176
+ end
177
+ items
178
+ end
179
+
180
+ def walk( peer, topic, **options )
181
+ host, port = peer.split(':') rescue nil
182
+ host = "127.0.0.1" if host.nil? || host.empty?
183
+ port = (port || 161).to_i
184
+ community = options[:rocommunity] || @rocommunity
185
+
186
+ if options[:bindto] && options[:device]
187
+ transport = UDPTransportExt.new( Socket::AF_INET, bindto: options[:bindto], device: options[:device] )
188
+ end
189
+
190
+ manager = SNMP::Manager.new( host: host, port: port, community: community, transport: transport )
191
+
192
+ topic ||= "internet"
193
+ base_oid = SNMP::ObjectId.new( topic ) rescue @mibs.oid( topic )
194
+
195
+ items = {}
196
+ begin
197
+ Thread.handle_interrupt( ::Timeout::Error => :on_blocking ) do
198
+ manager.walk( base_oid ) do |varbind|
199
+ oid, item = * parse_varbind( varbind )
200
+ items[oid] = item
201
+ end
202
+ end
203
+
204
+ rescue SNMP::RequestTimeout => e
205
+ raise Sloth::Snmp::Error, e.message
206
+
207
+ rescue => e
208
+ raise Sloth::Snmp::Error, e.message
209
+
210
+ end
211
+ items
212
+ end
213
+
214
+ def set( peer, tuple, **options )
215
+ host, port = peer.split(':') rescue nil
216
+ host = "127.0.0.1" if host.nil? || host.empty?
217
+ port = (port || 161).to_i
218
+ community = options[:rwcommunity] || @rwcommunity
219
+
220
+ if options[:bindto] && options[:device]
221
+ transport = UDPTransportExt.new( Socket::AF_INET, bindto: options[:bindto], device: options[:device] )
222
+ end
223
+
224
+ varbind = build_varbind( tuple[:topic], tuple[:type], tuple[:value] )
225
+
226
+ manager = SNMP::Manager.new( host: host, port: port, community: community, transport: transport )
227
+
228
+ begin
229
+ response = Thread.handle_interrupt( ::Timeout::Error => :on_blocking ) do
230
+ manager.set( varbind )
231
+ end
232
+ response
233
+
234
+ rescue SNMP::RequestTimeout => e
235
+ raise Sloth::Snmp::Error, e.message
236
+
237
+ rescue => e
238
+ raise Sloth::Snmp::Error, e.message
239
+
240
+ ensure
241
+ manager.close
242
+ end
243
+ end
244
+
245
+ def parse_varbind( varbind, keys: nil )
246
+ keys = [:name, :value] if keys.nil? || keys.empty?
247
+ oid = varbind.name.to_str
248
+ name = @mibs.name( oid )
249
+ case varbind.value
250
+ when SNMP::OctetString
251
+ type = SNMP::OctetString
252
+ valu = varbind.value.to_s
253
+ if /[^[:print:]]/.match( valu )
254
+ valu = "0x" + valu.unpack("H*").shift
255
+ end
256
+ when SNMP::Integer
257
+ type = SNMP::Integer
258
+ valu = varbind.value.to_i
259
+ when SNMP::Counter32
260
+ type = SNMP::Counter32
261
+ valu = varbind.value.to_i
262
+ when SNMP::IpAddress
263
+ type = SNMP::IpAddress
264
+ valu = varbind.value.to_s
265
+ when SNMP::ObjectId
266
+ type = SNMP::ObjectId
267
+ valu = varbind.value
268
+ when SNMP::NoSuchInstance, SNMP::NoSuchObject
269
+ type = varbind.value
270
+ valu = nil
271
+ else
272
+ type = varbind.value
273
+ valu = nil
274
+ end
275
+
276
+ tuple = {}
277
+ tuple[:name] = name if keys.include?( :name )
278
+ tuple[:type] = type if keys.include?( :type )
279
+ tuple[:value] = valu if keys.include?( :value )
280
+ [oid, tuple]
281
+ end
282
+
283
+ def build_varbind( topic, type, value )
284
+ oid = SNMP::ObjectId.new( topic ) rescue @mibs.oid( topic )
285
+ case type
286
+ when SNMP::OctetString
287
+ SNMP::VarBind.new( oid, SNMP::OctetString.new( value ))
288
+ when SNMP::Integer
289
+ SNMP::VarBind.new( oid, SNMP::Integer.new( value ))
290
+ when SNMP::Counter32
291
+ SNMP::VarBind.new( oid, SNMP::Counter32.new( value ))
292
+ when SNMP::IpAddress
293
+ SNMP::VarBind.new( oid, SNMP::IpAddress.new( value ))
294
+ when SNMP::ObjectId
295
+ SNMP::VarBind.new( oid, SNMP::ObjectId.new( value ))
296
+ else
297
+ SNMP::VarBind.new( oid, SNMP::OctetString.new( value.to_s ))
298
+ end
299
+ end
300
+
301
+ end
302
+ end
@@ -0,0 +1,5 @@
1
+ module Sloth
2
+ class Snmp
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ #
2
+ # bundle exec ruby sample/snmp_custom.rb
3
+ # bundle exec ruby sample/snmp_custom.rb sample/SAMPLE-MIB.yml 10.0.2.1
4
+ #
5
+
6
+ require "sloth/snmp"
7
+
8
+ config = {}
9
+ mibpath = ARGV.shift
10
+ if mibpath
11
+ config.merge!( mibs: mibpath )
12
+ end
13
+
14
+ Snmp = Sloth::Snmp.new( **config )
15
+
16
+ peer = ARGV.shift || "127.0.0.1:161"
17
+
18
+ topic = "internet"
19
+ tuples = Snmp.walk( peer, topic )
20
+ tuples.each do |_oid, tuple|
21
+ p tuple
22
+ end
@@ -0,0 +1,27 @@
1
+ #
2
+ # bundle exec ruby sample/snmpget.rb
3
+ #
4
+
5
+ require "sloth/snmp"
6
+
7
+ Snmp = Sloth::Snmp.new
8
+
9
+ options = {
10
+ # bindto: "10.0.2.15",
11
+ # device: "enp0s3",
12
+ }
13
+
14
+ peer = ARGV.shift || "127.0.0.1:161"
15
+
16
+ [
17
+ [ "sysDescr.0", "sysUpTime.0", "sysName.0" ],
18
+ [ "1.3.6.1.2.1.1.1.0", "1.3.6.1.2.1.1.3.0" ],
19
+ "1.3.6.1.2.1.1.5.0",
20
+ ].each do |topics|
21
+ p [:get, topics]
22
+ tuples = Snmp.get( peer, topics, **options )
23
+ tuples.each do |_oid, tuple|
24
+ p tuple
25
+ end
26
+ puts
27
+ end
@@ -0,0 +1,30 @@
1
+ #
2
+ # bundle exec ruby sample/snmpset.rb
3
+ #
4
+
5
+ require "sloth/snmp"
6
+
7
+ Snmp = Sloth::Snmp.new
8
+
9
+ options = {
10
+ # bindto: "10.0.2.15",
11
+ # device: "enp0s3",
12
+ }
13
+
14
+ peer = ARGV.shift || "127.0.0.1:161"
15
+
16
+ [
17
+ { topic: "sysName.0", type: SNMP::OctetString, value: Time.now.to_s },
18
+ ].each do |tuple|
19
+ p [:set, tuple]
20
+ pp Snmp.set( peer, tuple, **options )
21
+
22
+ # topics = [tuple[:topic]]
23
+ topics = tuple[:topic]
24
+ p [:get, topics]
25
+ tuples = Snmp.get( peer, topics, **options )
26
+ tuples.each do |_oid, tuple_|
27
+ p tuple_
28
+ end
29
+ puts
30
+ end
@@ -0,0 +1,45 @@
1
+ #
2
+ # bundle exec ruby sample/snmptrap.rb
3
+ # sudo bundle exec ruby sample/snmptrap.rb
4
+ #
5
+ # RFC1158-MIB.yaml:191:snmpInTraps: 1.3.6.1.2.1.11.19
6
+ # RFC1158-MIB.yaml:201:snmpOutTraps: 1.3.6.1.2.1.11.29
7
+ #
8
+
9
+ require "sloth/snmp"
10
+
11
+ port = ( ARGV.shift || ( Process::Sys.getuid == 0 ? 162 : 1162 ) )
12
+ p [:port, port]
13
+
14
+ Snmp = Sloth::Snmp.new( bind: port )
15
+
16
+ queue = Queue.new
17
+
18
+ p :ready
19
+ Snmp.trap( "snmpInTraps", "snmpOutTraps" ) do |trapname, source_ip, tuples|
20
+ #Snmp.trap( "1.3.6.1.2.1.11.19", "1.3.6.1.2.1.11.29" ) do |trapname, source_ip, tuples|
21
+ p ["TRAP!!", :trapname, trapname, :source_ip, source_ip]
22
+ tuples.each do |_oid, tuple|
23
+ p tuple
24
+ end
25
+ queue.push trapname
26
+ end
27
+
28
+ p :wait
29
+ sleep 1
30
+ puts
31
+
32
+ p cmnd = "snmptrap -v 2c -c public 127.0.0.1:#{port} '' .1.3.6.1.2.1.11.19 2> /dev/null "
33
+ %x[#{cmnd}]
34
+ p queue.pop
35
+
36
+ p cmnd = "snmptrap -v 2c -c public 127.0.0.1:#{port} '' .1.3.6.1.2.1.11.29 2> /dev/null "
37
+ %x[#{cmnd}]
38
+ p queue.pop
39
+
40
+ p :untrap
41
+ Snmp.untrap( "snmpInTraps", "snmpOutTraps" )
42
+ # Snmp.untrap( "1.3.6.1.2.1.11.19", "1.3.6.1.2.1.11.29" )
43
+
44
+ sleep 1
45
+ p :quit
@@ -0,0 +1,29 @@
1
+ #
2
+ # bundle exec ruby sample/snmpwalk.rb
3
+ #
4
+
5
+ require "sloth/snmp"
6
+
7
+ Snmp = Sloth::Snmp.new
8
+
9
+ options = {
10
+ # bindto: "10.0.2.15",
11
+ # device: "enp0s3",
12
+ }
13
+
14
+ peer = ARGV.shift || "127.0.0.1:161"
15
+
16
+ [
17
+ "system.8",
18
+ "system.9",
19
+ "mib-2.25.1",
20
+ "internet",
21
+ nil,
22
+ ].each do |topic|
23
+ p [:walk, topic]
24
+ tuples = Snmp.walk( peer, topic, **options )
25
+ tuples.each do |_oid, tuple|
26
+ p tuple
27
+ end
28
+ puts
29
+ end
@@ -0,0 +1,25 @@
1
+ require_relative 'lib/sloth/snmp/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "sloth-snmp"
5
+ spec.version = Sloth::Snmp::VERSION
6
+ spec.authors = ["arimay"]
7
+ spec.email = ["arima.yasuhiro@gmail.com"]
8
+
9
+ spec.summary = %q{ Sloth Snmp Library. }
10
+ spec.description = %q{ Sloth::Snmp is yet another wrapper library for snmp. }
11
+ spec.homepage = "https://github.com/arimay/sloth-snmp"
12
+ spec.license = "MIT"
13
+
14
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
15
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ end
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "snmp"
22
+
23
+ spec.add_development_dependency "rake", "~> 12.0"
24
+ spec.add_development_dependency "rspec", "~> 3.0"
25
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sloth-snmp
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - arimay
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-11-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: snmp
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '12.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '12.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: " Sloth::Snmp is yet another wrapper library for snmp. "
56
+ email:
57
+ - arima.yasuhiro@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - README.adoc
67
+ - README.ja.adoc
68
+ - Rakefile
69
+ - bin/console
70
+ - bin/setup
71
+ - lib/sloth/snmp.rb
72
+ - lib/sloth/snmp/impl.rb
73
+ - lib/sloth/snmp/version.rb
74
+ - sample/snmpcustom.rb
75
+ - sample/snmpget.rb
76
+ - sample/snmpset.rb
77
+ - sample/snmptrap.rb
78
+ - sample/snmpwalk.rb
79
+ - sloth-snmp.gemspec
80
+ homepage: https://github.com/arimay/sloth-snmp
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubygems_version: 3.1.4
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Sloth Snmp Library.
103
+ test_files: []