snmp-open 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: db7e18f61349f692d1543dcfe02ae4e2bc9bab34
4
- data.tar.gz: 37598b9ab5c8797bd2227eac71da7e8e945ddca7
3
+ metadata.gz: ed0ba9db497fbb007a69e702a2ed6321cfde3583
4
+ data.tar.gz: 9cbd977f12ae09f800fc0b27aa29fbad04bd0cd9
5
5
  SHA512:
6
- metadata.gz: f7dc1bf3650a77fcce40e30ed2c9d6a7ac4f248e91adfac6e1358f5455e06cb045f44be5d9167287a9de4aef13843147a14f41e6b5cf962840deef5f8ae57a09
7
- data.tar.gz: cf03e2bb2dc6509afe2991e8f0aaf02a4a262cefd0bc1ca733da93024d8006272177126e223aa861aaffda6ba750596bac90bb96a721fbde6d3467c919b173fe
6
+ metadata.gz: 74fb9d39823597e02d95f2029e14391d16851e29f0b20006ab595550e56207dc2f20a1bb3404aa118d25cf9e0a8016542c60be327bf43e45683bad890571aa7a
7
+ data.tar.gz: 6b29974a7a28fa197acb75647674f036f7ef5fad733de2f0ae7a5c780005717e17d8cb87fb4855f398768665ccf987432906d6ed6131f69546798725ac98f6f0
@@ -1,39 +1,13 @@
1
1
  require 'open3'
2
+ require 'snmp/open/options'
2
3
 
3
4
  module SNMP
4
5
  class Open
5
6
  # Open3-based data source that executes an snmp* command and captures the
6
7
  # output
7
8
  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
- no_check_increasing: {
16
- 'snmpbulkwalk' => '-Cc',
17
- 'snmpwalk' => '-Cc'
18
- },
19
- numeric: '-On', # needed by parser, should always be enabled
20
- priv_password: '-X', # not recommended, see snmp.conf(5)
21
- priv_protocol: '-x',
22
- sec_level: '-l',
23
- sec_user: '-u',
24
- retries: '-r',
25
- timeout: '-t',
26
- host: nil
27
- }.freeze
28
-
29
- OPTION_VALUES = {
30
- no_check_increasing: {
31
- true => ''
32
- }.freeze
33
- }.freeze
34
-
35
9
  # +options+ accepts options dealing with making connections to the host,
36
- # including all of the options listed in the +OPTIONS+ constant hash.
10
+ # including all of the options listed in the +Options::MAP+ constant hash.
37
11
  # Other options can be given as strings (or any object with a suitable
38
12
  # +to_s+ method), e.g., these are equivalent:
39
13
  #
@@ -44,7 +18,9 @@ module SNMP
44
18
  @env = options.delete(:env)
45
19
  host = options.delete(:host) ||
46
20
  (raise ArgumentError, 'Host expected but not given')
47
- opts = merge_options(options).merge('-On' => nil, host => nil)
21
+ opts = Options::REQUIRED_BY_PARSER
22
+ .merge(merge_options(options))
23
+ .merge(host => nil)
48
24
  @command_options, @host_options = partition_options(opts)
49
25
  end
50
26
 
@@ -76,9 +52,9 @@ module SNMP
76
52
 
77
53
  def merge_options(options = {})
78
54
  options.each_pair.with_object({}) do |(key, value), opts|
79
- if OPTIONS.key?(key)
80
- opts[OPTIONS[key]] =
81
- (OPTION_VALUES.fetch(key, {}).fetch(value, value) || next)
55
+ if Options::MAP.key?(key)
56
+ opts[Options::MAP[key]] =
57
+ (Options::VALUES.fetch(key, {}).fetch(value, value) || next)
82
58
  elsif key.is_a?(String)
83
59
  opts[key] = value
84
60
  else
@@ -0,0 +1,43 @@
1
+ module SNMP
2
+ class Open
3
+ class Options
4
+ # see snmpcmd(1) for explanation of options
5
+ MAP = {
6
+ version: '-v',
7
+ auth_password: '-A',
8
+ auth_protocol: '-a',
9
+ community: '-c',
10
+ context: '-n',
11
+ no_check_increasing: {
12
+ 'snmpbulkwalk' => '-Cc',
13
+ 'snmpwalk' => '-Cc'
14
+ },
15
+ no_units: '-OU',
16
+ non_symbolic: '-Oe',
17
+ numeric: '-On',
18
+ priv_password: '-X', # not recommended, see snmp.conf(5)
19
+ priv_protocol: '-x',
20
+ sec_level: '-l',
21
+ sec_user: '-u',
22
+ retries: '-r',
23
+ timeout: '-t',
24
+ host: nil
25
+ }.freeze
26
+
27
+ # On some systems, SNMP command outputs will include symbolic OID names,
28
+ # symbolic values, and/or value units. The parser doesn't support these,
29
+ # so disable them.
30
+ REQUIRED_BY_PARSER = {
31
+ '-Oe' => nil,
32
+ '-On' => nil,
33
+ '-OU' => nil
34
+ }.freeze
35
+
36
+ VALUES = {
37
+ no_check_increasing: {
38
+ true => ''
39
+ }.freeze
40
+ }.freeze
41
+ end # class Options
42
+ end # class Open
43
+ end # module SNMP
@@ -1,5 +1,7 @@
1
1
  require 'set'
2
2
  require 'shellwords'
3
+ require 'snmp/open/parser/constants'
4
+ require 'snmp/open/parser/value_parser'
3
5
 
4
6
  module SNMP
5
7
  class Open
@@ -11,10 +13,7 @@ module SNMP
11
13
 
12
14
  # convert SNMP command output into arrays
13
15
  class Parser
14
- NOSUCHOBJECT_STR =
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
+ include SNMP::Open::Parser::Constants
18
17
 
19
18
  def initialize(oids)
20
19
  @oids = oids
@@ -24,8 +23,9 @@ module SNMP
24
23
  columns = texts.map do |text|
25
24
  tokenized =
26
25
  text
27
- .gsub(NOSUCHOBJECT_STR, %("#{NOSUCHOBJECT_STR}"))
28
- .gsub(NOSUCHINSTANCE_STR, %("#{NOSUCHINSTANCE_STR}"))
26
+ .gsub(/^([0-9\.]+) = (Opaque|STRING): ((?!")[^\n]*)\n/,
27
+ %(\\1 = \\2: "\\3"\n))
28
+ .gsub(Static::ANY_MESSAGE, Static::QUOTED_MESSAGES)
29
29
  .shellsplit
30
30
  parse_tokens(tokenized)
31
31
  end
@@ -82,22 +82,9 @@ module SNMP
82
82
  end
83
83
 
84
84
  def parse_type(tokens)
85
- next_token = tokens.next
86
- type = next_token.match(/\A([A-Z]+):\z/) { |md| md[1] }
87
- type, value = parse_value(tokens, next_token, type)
88
- [type, Conversions.convert_value(type, value)]
89
- end
90
-
91
- def parse_value(tokens, token, type)
92
- if token == NOSUCHOBJECT_STR
93
- ['No Such Object', nil]
94
- elsif token == NOSUCHINSTANCE_STR
95
- ['No Such Instance', nil]
96
- elsif !type
97
- ['STRING', token]
98
- else
99
- [type, tokens.next]
100
- end
85
+ token = tokens.next
86
+ type = token.match(/\A([-A-Za-z]+[0-9]*):\z/) { |md| md[1] }
87
+ ValueParser.find(type, token).parse(tokens)
101
88
  end
102
89
 
103
90
  def table(columns)
@@ -124,25 +111,24 @@ module SNMP
124
111
  @oids.zip(columns).map do |oid, column|
125
112
  indexes.map do |index|
126
113
  id = (oid == index ? index : "#{oid}.#{index}")
127
- column.find { |o| o.oid == id } || Conversions.absent_value(id)
114
+ column.find { |o| o.oid == id } || Value.new(id, 'absent', nil)
128
115
  end
129
116
  end
130
117
  end
131
118
 
132
- # functions to generate value objects
133
- module Conversions
134
- module_function def convert_value(type, value)
135
- case type
136
- when 'INTEGER'
137
- value.to_i
138
- else
139
- value
140
- end
141
- end
119
+ # static messages from net-snmp commands
120
+ module Static
121
+ include SNMP::Open::Parser::Constants
142
122
 
143
- module_function def absent_value(id)
144
- Value.new(id, 'absent', nil)
145
- end
123
+ MESSAGES = [
124
+ NOSUCHOBJECT_STR,
125
+ NOSUCHINSTANCE_STR,
126
+ NOMOREVARIABLES_STR
127
+ ].freeze
128
+
129
+ ANY_MESSAGE = Regexp.union(*MESSAGES)
130
+
131
+ QUOTED_MESSAGES = MESSAGES.map { |v| [v, %("#{v}")] }.to_h.freeze
146
132
  end
147
133
  end # class Parser
148
134
  end # class Open
@@ -0,0 +1,15 @@
1
+ module SNMP
2
+ class Open
3
+ class Parser
4
+ module Constants
5
+ NOSUCHOBJECT_STR =
6
+ 'No Such Object available on this agent at this OID'.freeze
7
+ NOSUCHINSTANCE_STR =
8
+ 'No Such Instance currently exists at this OID'.freeze
9
+ NOMOREVARIABLES_STR =
10
+ 'No more variables left in this MIB View '\
11
+ '(It is past the end of the MIB tree)'.freeze
12
+ end # module Constants
13
+ end # class Parser
14
+ end # class Open
15
+ end # module SNMP
@@ -0,0 +1,127 @@
1
+ module SNMP
2
+ class Open
3
+ class Parser
4
+ # base class for value parsers
5
+ class ValueParser
6
+ include SNMP::Open::Parser::Constants
7
+
8
+ def self.find(type, token)
9
+ cls = KNOWN_TOKENS[token] || KNOWN_TYPES[type] || Other
10
+ cls.new(type, token)
11
+ end
12
+
13
+ def initialize(type, token)
14
+ @type = type
15
+ @token = token
16
+ end
17
+
18
+ def parse(*)
19
+ @parse
20
+ end
21
+
22
+ # parses BITS
23
+ class Bits < ValueParser
24
+ def parse(tokens)
25
+ return @parse if @parse
26
+ bytes = []
27
+ loop do
28
+ break unless tokens.peek =~ /\A[0-9A-Za-z]{1,2}\z/
29
+ bytes << tokens.next.to_i(16)
30
+ end
31
+ @parse = [@type, bytes]
32
+ end
33
+ end # class Bits < ValueParser
34
+
35
+ # parses objects with no explicit type
36
+ class Default < ValueParser
37
+ def initialize(_type, token)
38
+ @parse = ['STRING', token]
39
+ end
40
+ end # class Default
41
+
42
+ # parses integer-like objects
43
+ class Integer < ValueParser
44
+ def parse(tokens)
45
+ @parse ||= [@type, Integer(tokens.next)]
46
+ end
47
+ end
48
+
49
+ # parses objects identified like '= Hex-STRING:'
50
+ class HexString < ValueParser
51
+ def parse(tokens)
52
+ return @parse if @parse
53
+ bytes = []
54
+ loop do
55
+ break unless tokens.peek =~ /\A[0-9A-Za-z]{2}\z/
56
+ bytes << tokens.next
57
+ end
58
+ string = bytes.map { |b| b.to_i(16).chr }.join
59
+ @parse = [@type, string]
60
+ end
61
+ end # class HexString
62
+
63
+ # handles messages indicating the end of the response
64
+ class Stop < ValueParser
65
+ def parse(*)
66
+ raise StopIteration, @token
67
+ end
68
+ end
69
+
70
+ # parses objects identified like '= Timeticks:'
71
+ # note that 1 second = 100 ticks
72
+ class Timeticks < ValueParser
73
+ def parse(tokens)
74
+ return @parse if @parse
75
+ ticks = tokens.next.tr('()', '').to_i
76
+
77
+ # consume tokens through one like 23:59:59.99
78
+ loop do
79
+ break if tokens.next =~ /\A\d\d:\d\d:\d\d.\d\d\z/
80
+ end
81
+
82
+ @parse = [@type, ticks]
83
+ end
84
+ end # class Timeticks
85
+
86
+ # handles objects not handled by any other parser
87
+ class Other < ValueParser
88
+ def parse(tokens)
89
+ @parse ||= [@type, tokens.next]
90
+ end
91
+ end # class Other
92
+
93
+ # handles NoSuchInstance
94
+ class NoSuchInstance < ValueParser
95
+ def initialize(*)
96
+ @parse = ['No Such Instance', nil]
97
+ end
98
+ end # class NoSuchInstance < ValueParser
99
+
100
+ # handles NoSuchObject
101
+ class NoSuchObject < ValueParser
102
+ def initialize(*)
103
+ @parse = ['No Such Object', nil]
104
+ end
105
+ end # class NoSuchObject < ValueParser
106
+
107
+ KNOWN_TOKENS = {
108
+ NOSUCHINSTANCE_STR => NoSuchInstance,
109
+ NOSUCHOBJECT_STR => NoSuchObject,
110
+ NOMOREVARIABLES_STR => Stop
111
+ }.freeze
112
+
113
+ KNOWN_TYPES = {
114
+ nil => Default,
115
+ 'BITS' => Bits,
116
+ 'INTEGER' => ValueParser::Integer,
117
+ 'Gauge32' => ValueParser::Integer,
118
+ 'Gauge64' => ValueParser::Integer,
119
+ 'Counter32' => ValueParser::Integer,
120
+ 'Counter64' => ValueParser::Integer,
121
+ 'Hex-STRING' => HexString,
122
+ 'Timeticks' => Timeticks
123
+ }.freeze
124
+ end # class ValueParser
125
+ end # class Parser
126
+ end # class Open
127
+ end # module SNMP
@@ -1,5 +1,5 @@
1
- module Snmp
1
+ module SNMP
2
2
  module Open
3
- VERSION = '0.3.1'.freeze
3
+ VERSION = '0.4.0'.freeze
4
4
  end
5
5
  end
@@ -4,7 +4,7 @@ require 'snmp/open/version'
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'snmp-open'
7
- spec.version = Snmp::Open::VERSION
7
+ spec.version = SNMP::Open::VERSION
8
8
  spec.authors = ['Ben Miller']
9
9
  spec.email = ['bmiller@rackspace.com']
10
10
 
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.3.1
4
+ version: 0.4.0
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-09-28 00:00:00.000000000 Z
11
+ date: 2017-11-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -89,7 +89,10 @@ files:
89
89
  - lib/snmp/open.rb
90
90
  - lib/snmp/open/command_reader.rb
91
91
  - lib/snmp/open/file_reader.rb
92
+ - lib/snmp/open/options.rb
92
93
  - lib/snmp/open/parser.rb
94
+ - lib/snmp/open/parser/constants.rb
95
+ - lib/snmp/open/parser/value_parser.rb
93
96
  - lib/snmp/open/version.rb
94
97
  - snmp-open.gemspec
95
98
  homepage: https://github.com/bjmllr/snmp-open