snmp-open 0.6.0 → 0.7.1

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
  SHA256:
3
- metadata.gz: 1362f32d8e43b509be4884b392c43689b9cba48c2e2d6f63c4912e645ba6fcc9
4
- data.tar.gz: d751cce9e00204ae0b0d131120338ce3b7beaf1bbfb2e71f114c60f8c9bfdfa8
3
+ metadata.gz: 0c647ac12c0055f8f71908c27d5460e3b220d2c265751987d7a08ac1b1179efa
4
+ data.tar.gz: 74dfcf665582e2d23f093a6e67b83e9def03c1b8939d9772b7a645e884f95dbe
5
5
  SHA512:
6
- metadata.gz: 51603c2ee2b8d5077be2dcc8f26b74ad174d09a31fe6b6879343d98b33bba5f30f6246cf2ac3999f3a3aa46fda3b5cf7f849310016de5725cbdd0574741af99c
7
- data.tar.gz: 2670576c9373c22e74e5bc7a118317ffdefb33d685ef654236e1ec5f369f9ac772d619ff7cfba3e8532ba52a5fc3b0d52bc67439cbeb96d84a08d6cc36f40819
6
+ metadata.gz: d8bd115d0a86d384dc860191bbb045e4903ab739ef1581041b6e030134345a60a3481a4f75554ddaf527587db8e72d0b544949590638606fabe0ea2ada23a77d
7
+ data.tar.gz: d505743d70310b076a783e1cdad8ab95a1a5776c6e8f5caf467c827791216896d76db3baf5fc9c4ca25519de6bf6336a7c6e27c3bff308e8eb65235c68ecf597
@@ -12,7 +12,7 @@ jobs:
12
12
  fail-fast: false
13
13
  matrix:
14
14
  os: [ubuntu-latest, macos-latest]
15
- ruby: [2.4, 2.5, 2.6, 2.7, jruby, truffleruby]
15
+ ruby: [2.4, 2.5, 2.6, 2.7, jruby]
16
16
  runs-on: ${{ matrix.os }}
17
17
  steps:
18
18
  - uses: actions/checkout@v2
data/.rubocop.yml CHANGED
@@ -1,2 +1,62 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+
1
4
  Style/CommentedKeyword:
2
5
  Enabled: false
6
+
7
+ Lint/MissingSuper:
8
+ Exclude:
9
+ - 'lib/snmp/open/parser/value_parser.rb'
10
+
11
+ Metrics/ClassLength:
12
+ Exclude:
13
+ - 'lib/snmp/open/parser.rb'
14
+
15
+ Style/AccessModifierDeclarations: {Enabled: false}
16
+ Style/IfUnlessModifier: {Enabled: false}
17
+ Style/FrozenStringLiteralComment: {Enabled: false}
18
+
19
+ Layout/SpaceBeforeBrackets: # (new in 1.7)
20
+ Enabled: true
21
+ Lint/AmbiguousAssignment: # (new in 1.7)
22
+ Enabled: true
23
+ Lint/DeprecatedConstants: # (new in 1.8)
24
+ Enabled: true
25
+ Lint/DuplicateBranch: # (new in 1.3)
26
+ Enabled: true
27
+ Lint/DuplicateRegexpCharacterClassElement: # (new in 1.1)
28
+ Enabled: true
29
+ Lint/EmptyBlock: # (new in 1.1)
30
+ Enabled: true
31
+ Lint/EmptyClass: # (new in 1.3)
32
+ Enabled: true
33
+ Lint/LambdaWithoutLiteralBlock: # (new in 1.8)
34
+ Enabled: true
35
+ Lint/NoReturnInBeginEndBlocks: # (new in 1.2)
36
+ Enabled: true
37
+ Lint/RedundantDirGlobSort: # (new in 1.8)
38
+ Enabled: true
39
+ Lint/ToEnumArguments: # (new in 1.1)
40
+ Enabled: true
41
+ Lint/UnexpectedBlockArity: # (new in 1.5)
42
+ Enabled: true
43
+ Lint/UnmodifiedReduceAccumulator: # (new in 1.1)
44
+ Enabled: true
45
+ Style/ArgumentsForwarding: # (new in 1.1)
46
+ Enabled: true
47
+ Style/CollectionCompact: # (new in 1.2)
48
+ Enabled: true
49
+ Style/DocumentDynamicEvalDefinition: # (new in 1.1)
50
+ Enabled: true
51
+ Style/EndlessMethod: # (new in 1.8)
52
+ Enabled: true
53
+ Style/HashExcept: # (new in 1.7)
54
+ Enabled: true
55
+ Style/NegatedIfElseCondition: # (new in 1.2)
56
+ Enabled: true
57
+ Style/NilLambda: # (new in 1.3)
58
+ Enabled: true
59
+ Style/RedundantArgument: # (new in 1.4)
60
+ Enabled: true
61
+ Style/SwapValues: # (new in 1.1)
62
+ Enabled: true
data/Gemfile CHANGED
@@ -5,4 +5,5 @@ group :guard do
5
5
  gem 'guard'
6
6
  gem 'guard-rspec'
7
7
  gem 'guard-rubocop'
8
+ gem 'rubocop', '~>1.0'
8
9
  end
@@ -121,8 +121,11 @@ module SNMP
121
121
  end # class CommandReader
122
122
 
123
123
  class CommandError < RuntimeError; end
124
+
124
125
  class CommandTimeoutError < CommandError; end
126
+
125
127
  class UnknownMIBError < CommandError; end
128
+
126
129
  class UnknownOIDError < CommandError; end
127
130
  end # class Open
128
131
  end # module SNMP
@@ -32,12 +32,12 @@ module SNMP
32
32
  mkdir(@directory, cmd.to_s) if @make_directories
33
33
  outfile = File.join(@directory, cmd.to_s, oid)
34
34
  File.read(outfile)
35
- rescue Errno::ENOENT => err
35
+ rescue Errno::ENOENT => e
36
36
  if @warnings
37
37
  warning = @warnings.call(@command_generator, cmd, oid, outfile)
38
38
  warn warning
39
39
  end
40
- raise err
40
+ raise e
41
41
  end
42
42
 
43
43
  def mkdir(base, cmd)
@@ -12,8 +12,10 @@ module SNMP
12
12
  'snmpbulkwalk' => '-Cc',
13
13
  'snmpwalk' => '-Cc'
14
14
  },
15
+ no_mib_name: '-Os',
15
16
  no_units: '-OU',
16
17
  non_symbolic: '-Oe',
18
+ non_symbolic_table_indexes: '-Ob',
17
19
  numeric: '-On',
18
20
  priv_password: '-X', # not recommended, see snmp.conf(5)
19
21
  priv_protocol: '-x',
@@ -24,9 +26,11 @@ module SNMP
24
26
  host: nil
25
27
  }.freeze
26
28
 
27
- # On some systems, SNMP command outputs will include symbolic values
28
- # and/or value units. The parser doesn't support these, so disable them.
29
+ # On some systems, SNMP command outputs will include symbolic values,
30
+ # table indexes, and/or value units. The parser doesn't support these, so
31
+ # disable them.
29
32
  REQUIRED_BY_PARSER = {
33
+ '-Ob' => nil,
30
34
  '-Oe' => nil,
31
35
  '-OU' => nil
32
36
  }.freeze
@@ -23,9 +23,11 @@ module SNMP
23
23
  class Bits < ValueParser
24
24
  def parse(tokens)
25
25
  return @parse if @parse
26
+
26
27
  bytes = []
27
28
  loop do
28
29
  break unless tokens.peek =~ /\A[0-9A-Za-z]{1,2}\z/
30
+
29
31
  bytes << tokens.next.to_i(16)
30
32
  end
31
33
  @parse = [@type, bytes]
@@ -50,9 +52,11 @@ module SNMP
50
52
  class HexString < ValueParser
51
53
  def parse(tokens)
52
54
  return @parse if @parse
55
+
53
56
  bytes = []
54
57
  loop do
55
58
  break unless tokens.peek =~ /\A[0-9A-Za-z]{2}\z/
59
+
56
60
  bytes << tokens.next
57
61
  end
58
62
  string = bytes.map { |b| b.to_i(16).chr }.join
@@ -72,6 +76,7 @@ module SNMP
72
76
  class Timeticks < ValueParser
73
77
  def parse(tokens)
74
78
  return @parse if @parse
79
+
75
80
  ticks = tokens.next.tr('()', '').to_i
76
81
 
77
82
  # consume tokens through one like 23:59:59.99
@@ -106,16 +111,16 @@ module SNMP
106
111
 
107
112
  KNOWN_TOKENS = {
108
113
  NOSUCHINSTANCE_STR => NoSuchInstance,
109
- NOSUCHOBJECT_STR => NoSuchObject,
114
+ NOSUCHOBJECT_STR => NoSuchObject,
110
115
  NOMOREVARIABLES_STR => Stop
111
116
  }.freeze
112
117
 
113
118
  KNOWN_TYPES = {
114
119
  nil => Default,
115
120
  'BITS' => Bits,
116
- 'INTEGER' => ValueParser::Integer,
117
- 'Gauge32' => ValueParser::Integer,
118
- 'Gauge64' => ValueParser::Integer,
121
+ 'INTEGER' => ValueParser::Integer,
122
+ 'Gauge32' => ValueParser::Integer,
123
+ 'Gauge64' => ValueParser::Integer,
119
124
  'Counter32' => ValueParser::Integer,
120
125
  'Counter64' => ValueParser::Integer,
121
126
  'Hex-STRING' => HexString,
@@ -14,7 +14,37 @@ module SNMP
14
14
  # convert SNMP command output into arrays
15
15
  class Parser
16
16
  include SNMP::Open::Parser::Constants
17
- OID_RE = Regexp.union(/\S+-MIB::\S+/, /[0-9\.]+/)
17
+ OID_RE = Regexp.union(/\S+-MIB::\S+/, /[0-9.]+/)
18
+
19
+ EMPTY_STRING_RE =
20
+ /^(#{OID_RE}) # capture group 1: OID
21
+ \s+=\s+
22
+ (Opaque|STRING) # capture group 2: Type
23
+ :\s*\n # value is always empty string
24
+ /x.freeze
25
+
26
+ STRING_RE =
27
+ /^(#{OID_RE}) # capture group 1: OID
28
+ \s+=\s+
29
+ (Opaque|STRING):\s+ # capture group 2: Type
30
+
31
+ ( # capture group 3: Value
32
+
33
+ (?!") # this pattern is for finding strings in need of
34
+ # quoting, so reject any strings that are already
35
+ # quoted
36
+
37
+ [^\n]* # first line of value
38
+
39
+ (\n # newline before each additional line of value
40
+ (?!
41
+ #{OID_RE} # additional lines of value are identified by not
42
+ \s+=\s+ # starting with "<OID> ="
43
+ )
44
+ [^\n]+ # additional lines of value
45
+ )*
46
+ )\n
47
+ /x.freeze
18
48
 
19
49
  def initialize(oids)
20
50
  @oids = oids
@@ -33,37 +63,34 @@ module SNMP
33
63
  private
34
64
 
35
65
  def align(columns)
36
- indexes = columns.first.map { |value| index_using_first_oid(value) }
66
+ indexes = indexes_from_columns(columns)
67
+ bases = bases_from_columns(columns)
37
68
  hash = columns.flat_map { |row| row.map { |v| [v.oid, v] } }.to_h
38
69
 
39
70
  indexes.map do |index|
40
- @oids.map do |base|
71
+ bases.map do |base, _|
41
72
  oid = [base, *index].join('.')
42
73
  hash.fetch(oid) { Value.new(oid, 'absent', nil) }
43
74
  end
44
75
  end
45
76
  end
46
77
 
78
+ def bases_from_columns(columns)
79
+ @oids
80
+ .zip(columns.map { |c| c&.first&.oid })
81
+ .map { |base, oid| base && oid && split_oid(base, oid) }
82
+ end
83
+
47
84
  def clean_input_text(text)
48
85
  text
49
86
  .gsub(/\r\n|\n\r|\r/, "\n")
50
- .gsub(/^(#{OID_RE})\s*=\s*(Opaque|STRING):\s*\n/,
51
- %(\\1 = \\2: ""\n))
52
- .gsub(/^(#{OID_RE}) = (Opaque|STRING): ((?!")[^\n]*)\n/,
53
- %(\\1 = \\2: "\\3"\n))
87
+ .gsub(EMPTY_STRING_RE, %(\\1 = \\2: ""\n))
88
+ .gsub(STRING_RE, %(\\1 = \\2: "\\3"\n))
54
89
  .gsub(Static::ANY_MESSAGE, Static::QUOTED_MESSAGES)
55
90
  end
56
91
 
57
- def index_using_first_oid(value)
58
- base = @oids.first
59
-
60
- if base == value.oid
61
- nil
62
- elsif value.oid.start_with?(base)
63
- value.oid.gsub(/\A#{base}\.?/, '')
64
- else
65
- raise "Received ID doesn't start with the given ID"
66
- end
92
+ def indexes_from_columns(columns)
93
+ columns.first.map { |value| split_oid(@oids.first, value.oid)[1] }
67
94
  end
68
95
 
69
96
  def parse_tokens(tokens)
@@ -82,8 +109,10 @@ module SNMP
82
109
  def parse_next_object(tokens)
83
110
  oid = tokens.next.sub(/\A\./, '')
84
111
  raise "Parse error at #{oid}" unless oid =~ OID_RE
112
+
85
113
  equals = tokens.next
86
114
  raise "Parse error after #{oid}" unless equals == '='
115
+
87
116
  type, value = parse_type(tokens)
88
117
  Value.new(oid, type, value)
89
118
  end
@@ -94,6 +123,21 @@ module SNMP
94
123
  ValueParser.find(type, token).parse(tokens)
95
124
  end
96
125
 
126
+ # split a complete OID into a base and index, given an expected base
127
+ # raises if the base isn't present
128
+ def split_oid(base, oid)
129
+ if base == oid
130
+ [base, nil]
131
+ elsif oid.start_with?(base)
132
+ [base, oid.sub(/\A#{base}\.?/, '')]
133
+ elsif base.include?('::') && !oid.include?('::')
134
+ alternate_base = base.sub(/\A[^:]+::/, '')
135
+ split_oid(alternate_base, oid)
136
+ else
137
+ raise "Received ID doesn't start with the given ID"
138
+ end
139
+ end
140
+
97
141
  def table(columns)
98
142
  if columns.size == 1 && columns.all? { |column| column.size == 1 }
99
143
  columns
@@ -1,5 +1,5 @@
1
1
  module SNMP
2
2
  class Open
3
- VERSION = '0.6.0'.freeze
3
+ VERSION = '0.7.1'.freeze
4
4
  end
5
5
  end
data/lib/snmp/open.rb CHANGED
@@ -27,16 +27,18 @@ module SNMP
27
27
  end
28
28
 
29
29
  # Perform an SNMP get using the "snmpget" command and parse the output
30
- def get(oids)
30
+ def get(oids, &block)
31
31
  return enum_for(:get, oids) unless block_given?
32
+
32
33
  texts = oids.map { |oid| reader.capture(:get, oid) }
33
- Parser.new(oids).parse(texts).fetch(0, []).each { |arg| yield(arg) }
34
+ Parser.new(oids).parse(texts).fetch(0, []).each(&block)
34
35
  end
35
36
 
36
37
  # Perform an SNMP walk using the "snmpwalk" or "snmpbulkwalk" commands and
37
38
  # parse the output
38
39
  def walk(oids, **kwargs)
39
40
  return enum_for(:walk, oids, **kwargs) unless block_given?
41
+
40
42
  bulk = kwargs.fetch(:bulk, true)
41
43
  options = walk_options(bulk, **kwargs)
42
44
  cmd = bulk ? :bulkwalk : :walk
data/snmp-open.gemspec CHANGED
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ['lib']
20
20
 
21
+ spec.required_ruby_version = '~> 2.4'
22
+
21
23
  spec.add_development_dependency 'bundler', '~> 2.2'
22
24
  spec.add_development_dependency 'rake', '~> 13.0'
23
25
  spec.add_development_dependency 'rspec', '~> 3.0'
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.6.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Miller
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-16 00:00:00.000000000 Z
11
+ date: 2022-03-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -105,16 +105,17 @@ require_paths:
105
105
  - lib
106
106
  required_ruby_version: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: '2.4'
111
111
  required_rubygems_version: !ruby/object:Gem::Requirement
112
112
  requirements:
113
113
  - - ">="
114
114
  - !ruby/object:Gem::Version
115
115
  version: '0'
116
116
  requirements: []
117
- rubygems_version: 3.1.2
117
+ rubyforge_project:
118
+ rubygems_version: 2.7.6.2
118
119
  signing_key:
119
120
  specification_version: 4
120
121
  summary: Wrapper for command-line SNMP utilities