snmp-open 0.1.3 → 0.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 42cc48b7cd27987ef9bf4a9ab604a7c819ff2dae
4
- data.tar.gz: c3a38d4e43063ed42a0ce786628e3af6a915504b
3
+ metadata.gz: 9ae08ac661e97b5c47899d166a7bdcd6b71e0c55
4
+ data.tar.gz: b10aae94bb31fc05b38ec3d3ab9ce1d450c3b104
5
5
  SHA512:
6
- metadata.gz: 423425fb07efe8265c9384d1f60ebe18ec12ea5a08f9e58551b6837e817fb84fb9604a63690398d23e21d2b537a23b2c00d958d3b2b00c0e3eb5aca5f23faaa8
7
- data.tar.gz: 4af15c0e555126e1015936069aeac4c8aaf9ec3e8b067089f3e9092205609478a30bc566232d1c2c0f3c519b0283dcd4e8b881e4d199101c36ab7e1ccbcae3a7
6
+ metadata.gz: 998d8abcd4e3e7b5967affb6441f590201c7a0c15b24ebe7f56911fa17a103086a2c6392586baa4fb9fe26083985e9de8cbc174eb2524a4303602a11757301be
7
+ data.tar.gz: ecf925444abd22e218b7235985aa5d98357f9e6e34b759ac760d3bbc5c1e83c89c46c261132663ee479124932c5b8d07e3313c88edd812cccf18aed1a179ce31
data/bin/console CHANGED
@@ -6,5 +6,5 @@ require 'snmp/open'
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
8
8
 
9
- require "pry"
9
+ require 'pry'
10
10
  Pry.start
data/bin/get ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/ruby
2
+ require 'json'
3
+ $LOAD_PATH << File.join(__dir__, '..', 'lib')
4
+ require 'snmp/open'
5
+
6
+ host = ARGV.fetch(0)
7
+ oids = ARGV[1..-1]
8
+ jj SNMP::Open.new(host: host).get(oids).to_a
data/bin/walk ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/ruby
2
+ require 'json'
3
+ $LOAD_PATH << File.join(__dir__, '..', 'lib')
4
+ require 'snmp/open'
5
+
6
+ host = ARGV.fetch(0)
7
+ oids = ARGV[1..-1]
8
+ jj SNMP::Open.new(host: host).walk(oids).to_a
data/lib/snmp/open.rb CHANGED
@@ -1,5 +1,5 @@
1
- require 'open3'
2
1
  require 'snmp/open/parser'
2
+ require 'snmp/open/command_reader'
3
3
 
4
4
  # Simple Network Management Protocol
5
5
  module SNMP
@@ -19,92 +19,39 @@ module SNMP
19
19
 
20
20
  # Open3-based wrapper for SNMP CLI commands
21
21
  class Open
22
- attr_reader :options
22
+ attr_reader :reader
23
23
 
24
- # see snmpcmd(1) for explanation of options
25
- OPTIONS = {
26
- version: '-v',
27
- auth_password: '-A',
28
- auth_protocol: '-a',
29
- community: '-c',
30
- context: '-n',
31
- numeric: '-On', # needed by parser, should always be enabled
32
- priv_password: '-X', # not recommended, see snmp.conf(5)
33
- priv_protocol: '-x',
34
- sec_level: '-l',
35
- sec_user: '-u',
36
- retries: '-r',
37
- timeout: '-t',
38
- host: nil
39
- }.freeze
40
-
41
- # +options+ accepts options dealing with making connections to the host,
42
- # including all of the options listed in the +OPTIONS+ constant hash. Other
43
- # options can be given as strings (or any object with a suitable +to_s+
44
- # method), e.g., these are equivalent:
45
- #
46
- # SNMP::Open.new(host: hostname, timeout: 3, '-m' => miblist)
47
- # SNMP::Open.new(hostname => nil, '-t' => '3', '-m' => miblist)
48
- #
24
+ # see CommandReader for a description of options
49
25
  def initialize(options = {})
50
- host = options.delete(:host) ||
51
- (raise ArgumentError, 'Host expected but not given')
52
- @host_options = merge_options(options)
53
- .merge('-On' => nil, host => nil)
54
- return if @host_options.key?(nil)
55
- end
56
-
57
- # Generate a CLI command string
58
- def cli(command, id = nil, options = {})
59
- command = case command
60
- when Symbol then "snmp#{command}"
61
- else command.to_s
62
- end
63
-
64
- [
65
- command,
66
- *options.map { |k, v| "#{k}#{v}" },
67
- *@host_options.map { |k, v| "#{k}#{v}" },
68
- *id
69
- ].join(' ')
26
+ @reader = options[:reader] || CommandReader.new(options)
70
27
  end
71
28
 
72
29
  # Perform an SNMP get using the "snmpget" command and parse the output
73
30
  def get(oids)
74
31
  return enum_for(:get, oids) unless block_given?
75
- texts = oids.map { |oid| capture_command(:get, oid) }
76
- Parser.new(oids).parse(texts).each { |arg, *| yield(arg) }
32
+ texts = oids.map { |oid| reader.capture(:get, oid) }
33
+ Parser.new(oids).parse(texts).first.each { |arg| yield(arg) }
77
34
  end
78
35
 
79
36
  # Perform an SNMP walk using the "snmpwalk" or "snmpbulkwalk" commands and
80
37
  # parse the output
81
38
  def walk(oids, **kwargs)
82
- kwargs = { bulk: true, non_repeaters: 0, max_repetitions: 10 }
83
- .merge(kwargs)
84
39
  return enum_for(:walk, oids, **kwargs) unless block_given?
40
+ bulk = kwargs.fetch(:bulk, true)
41
+ options = walk_options(bulk, **kwargs)
85
42
  cmd = bulk ? :bulkwalk : :walk
86
- options = bulk ? { '-Cn' => non_repeaters, '-Cr' => max_repetitions } : {}
87
- texts = oids.map { |oid| capture_command(cmd, oid, options) }
43
+ texts = oids.map { |oid| reader.capture(cmd, oid, options) }
88
44
  Parser.new(oids).parse(texts).each { |*args| yield(*args) }
89
45
  end
90
46
 
91
- private
92
-
93
- def capture_command(cmd, oid, options = {})
94
- out, err = Open3.capture3(cli(cmd, oid, options))
95
- raise err unless err.empty?
96
- out
97
- end
98
-
99
- def merge_options(options = {})
100
- options.each_pair.with_object({}) do |(key, value), opts|
101
- if OPTIONS.key?(key)
102
- opts[OPTIONS[key]] = value
103
- elsif key.is_a?(String)
104
- opts[key] = value
105
- else
106
- raise "Unknown option #{key}"
107
- end
47
+ def walk_options(bulk, **kwargs)
48
+ if bulk
49
+ {
50
+ '-Cn' => kwargs.fetch(:non_repeaters, 0),
51
+ '-Cr' => kwargs.fetch(:max_repetitions, 10)
52
+ }
53
+ else
54
+ {}
108
55
  end
109
56
  end
110
57
  end # class Open
@@ -0,0 +1,75 @@
1
+ require 'open3'
2
+
3
+ module SNMP
4
+ class Open
5
+ # Open3-based data source that executes an snmp* command and captures the
6
+ # output
7
+ class CommandReader
8
+ # see snmpcmd(1) for explanation of options
9
+ OPTIONS = {
10
+ version: '-v',
11
+ auth_password: '-A',
12
+ auth_protocol: '-a',
13
+ community: '-c',
14
+ context: '-n',
15
+ numeric: '-On', # needed by parser, should always be enabled
16
+ priv_password: '-X', # not recommended, see snmp.conf(5)
17
+ priv_protocol: '-x',
18
+ sec_level: '-l',
19
+ sec_user: '-u',
20
+ retries: '-r',
21
+ timeout: '-t',
22
+ host: nil
23
+ }.freeze
24
+
25
+ # +options+ accepts options dealing with making connections to the host,
26
+ # including all of the options listed in the +OPTIONS+ constant hash.
27
+ # Other options can be given as strings (or any object with a suitable
28
+ # +to_s+ method), e.g., these are equivalent:
29
+ #
30
+ # SNMP::Open.new(host: hostname, timeout: 3, '-m' => miblist)
31
+ # SNMP::Open.new(hostname => nil, '-t' => '3', '-m' => miblist)
32
+ #
33
+ def initialize(options)
34
+ host = options.delete(:host) ||
35
+ (raise ArgumentError, 'Host expected but not given')
36
+ @host_options = merge_options(options).merge('-On' => nil, host => nil)
37
+ end
38
+
39
+ def capture(cmd, oid, options = {})
40
+ out, err = Open3.capture3(cli(cmd, oid, options))
41
+ raise err unless err.empty?
42
+ out
43
+ end
44
+
45
+ # Generate a CLI command string
46
+ def cli(command, id = nil, options = {})
47
+ command = case command
48
+ when Symbol then "snmp#{command}"
49
+ else command.to_s
50
+ end
51
+
52
+ [
53
+ command,
54
+ *options.map { |k, v| "#{k}#{v}" },
55
+ *@host_options.map { |k, v| "#{k}#{v}" },
56
+ *id
57
+ ].join(' ')
58
+ end
59
+
60
+ private
61
+
62
+ def merge_options(options = {})
63
+ options.each_pair.with_object({}) do |(key, value), opts|
64
+ if OPTIONS.key?(key)
65
+ opts[OPTIONS[key]] = value
66
+ elsif key.is_a?(String)
67
+ opts[key] = value
68
+ else
69
+ raise "Unknown option #{key}"
70
+ end
71
+ end
72
+ end
73
+ end # class CommandReader
74
+ end # class Open
75
+ end # module SNMP
@@ -1,8 +1,8 @@
1
- require 'snmp/open/parser'
1
+ require 'snmp/open/command_reader'
2
2
 
3
3
  module SNMP
4
4
  class Open
5
- # Test double for SNMP::Open that reads from the filesystem instead of
5
+ # Test data source for SNMP::Open that reads from the filesystem instead of
6
6
  # running SNMP commands. Expects a 'walk' directory to be present if #walk
7
7
  # will be used, and a 'get' directory if #get will be used. Within each
8
8
  # directory, files named according to the numeric OIDs to be used are
@@ -12,37 +12,19 @@ module SNMP
12
12
  # Produces warnings describing an snmpbulkwalk or snmpget command that could
13
13
  # be used to generate a needed file, if the file is unavailable. Controlled
14
14
  # by the `warnings` option.
15
-
16
15
  class FileReader
17
16
  def initialize(directory, options = {})
18
17
  @directory = directory
19
18
  @warnings = options.delete(:warnings)
20
- @command_generator = SNMP::Open.new(options.merge(host: '$OPTIONS'))
21
- end
22
-
23
- def get(oids)
24
- return enum_for(:get, oids) unless block_given?
25
- texts = oids.map { |o| File.read(File.join(@directory, 'get', o)) }
26
- Parser.new(oids).parse(texts).each { |arg, *| yield(arg) }
27
- rescue Errno::ENOENT => err
28
- if @warnings
29
- oids.each do |oid|
30
- warn "#{@command_generator.cli(:get, oid)} > get/#{oid}"
31
- end
32
- end
33
- raise err
19
+ @command_generator =
20
+ SNMP::Open::CommandReader.new(options.merge(host: '$OPTIONS'))
34
21
  end
35
22
 
36
- def walk(oids, *rest)
37
- return enum_for(:walk, oids, *rest) unless block_given?
38
- texts = oids.map { |o| File.read(File.join(@directory, 'walk', o)) }
39
- Parser.new(oids).parse(texts).each { |*args| yield(*args) }
23
+ def capture(cmd, oid, _options = {})
24
+ outfile = File.join(cmd.to_s, oid)
25
+ File.read(File.join(@directory, outfile))
40
26
  rescue Errno::ENOENT => err
41
- if @warnings
42
- oids.each do |oid|
43
- warn "#{@command_generator.cli(:bulkwalk, oid)} > walk/#{oid}"
44
- end
45
- end
27
+ warn "#{@command_generator.cli(cmd, oid)} > #{outfile}" if @warnings
46
28
  raise err
47
29
  end
48
30
  end # class FileReader
@@ -13,6 +13,8 @@ module SNMP
13
13
  class Parser
14
14
  NOSUCHOBJECT_STR =
15
15
  'No Such Object available on this agent at this OID'.freeze
16
+ NOSUCHINSTANCE_STR =
17
+ 'No Such Instance currently exists at this OID'.freeze
16
18
 
17
19
  def initialize(oids)
18
20
  @oids = oids
@@ -21,7 +23,10 @@ module SNMP
21
23
  def parse(texts)
22
24
  columns = texts.map do |text|
23
25
  tokenized =
24
- text.gsub(NOSUCHOBJECT_STR, %("#{NOSUCHOBJECT_STR}")).shellsplit
26
+ text
27
+ .gsub(NOSUCHOBJECT_STR, %("#{NOSUCHOBJECT_STR}"))
28
+ .gsub(NOSUCHINSTANCE_STR, %("#{NOSUCHINSTANCE_STR}"))
29
+ .shellsplit
25
30
  parse_tokens(tokenized)
26
31
  end
27
32
 
@@ -80,12 +85,14 @@ module SNMP
80
85
  next_token = tokens.next
81
86
  type = next_token.match(/\A([A-Z]+):\z/) { |md| md[1] }
82
87
  type, value = parse_value(tokens, next_token, type)
83
- [type, convert_value(type, value)]
88
+ [type, Conversions.convert_value(type, value)]
84
89
  end
85
90
 
86
91
  def parse_value(tokens, token, type)
87
92
  if token == NOSUCHOBJECT_STR
88
93
  ['No Such Object', nil]
94
+ elsif token == NOSUCHINSTANCE_STR
95
+ ['No Such Instance', nil]
89
96
  elsif !type
90
97
  ['STRING', token]
91
98
  else
@@ -109,22 +116,25 @@ module SNMP
109
116
  @oids.zip(columns).map do |oid, column|
110
117
  indexes.map do |index|
111
118
  id = (oid == index ? index : "#{oid}.#{index}")
112
- column.find { |o| o.oid == id } || absent_value(id)
119
+ column.find { |o| o.oid == id } || Conversions.absent_value(id)
113
120
  end
114
121
  end
115
122
  end
116
123
 
117
- def convert_value(type, value)
118
- case type
119
- when 'INTEGER'
120
- value.to_i
121
- else
122
- value
124
+ # functions to generate value objects
125
+ module Conversions
126
+ module_function def convert_value(type, value)
127
+ case type
128
+ when 'INTEGER'
129
+ value.to_i
130
+ else
131
+ value
132
+ end
123
133
  end
124
- end
125
134
 
126
- def absent_value(id)
127
- Value.new(id, 'absent', nil)
135
+ module_function def absent_value(id)
136
+ Value.new(id, 'absent', nil)
137
+ end
128
138
  end
129
139
  end # class Parser
130
140
  end # class Open
@@ -1,5 +1,5 @@
1
1
  module Snmp
2
2
  module Open
3
- VERSION = '0.1.3'.freeze
3
+ VERSION = '0.1.4'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: snmp-open
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Miller
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-03-17 00:00:00.000000000 Z
11
+ date: 2017-03-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -83,8 +83,11 @@ files:
83
83
  - README.md
84
84
  - Rakefile
85
85
  - bin/console
86
+ - bin/get
86
87
  - bin/setup
88
+ - bin/walk
87
89
  - lib/snmp/open.rb
90
+ - lib/snmp/open/command_reader.rb
88
91
  - lib/snmp/open/file_reader.rb
89
92
  - lib/snmp/open/parser.rb
90
93
  - lib/snmp/open/version.rb