snmp-open 0.3.0 → 0.6.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 +5 -5
- data/.github/workflows/ruby.yml +24 -0
- data/.rubocop.yml +62 -0
- data/Gemfile +1 -0
- data/lib/snmp/open.rb +4 -2
- data/lib/snmp/open/command_reader.rb +37 -34
- data/lib/snmp/open/file_reader.rb +24 -7
- data/lib/snmp/open/options.rb +41 -0
- data/lib/snmp/open/parser.rb +69 -38
- data/lib/snmp/open/parser/constants.rb +15 -0
- data/lib/snmp/open/parser/value_parser.rb +132 -0
- data/lib/snmp/open/version.rb +3 -3
- data/snmp-open.gemspec +6 -4
- metadata +18 -15
- data/.travis.yml +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2778b6bac09136b98ff5bfcdf5313f470d8468d556353f4e5466479624291383
|
4
|
+
data.tar.gz: b2f2a13ad5ad75e79116e33e63f2462e768b5850887180e9b94e1ff369c17bac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f61f7cbd63437b97d260e8bee251cfe45ca02a706564de5ae293859c5ab8feb80c253fdd67ba0fe93fd78b1df49370adca9a12cecac6b545456efdeccf8f4553
|
7
|
+
data.tar.gz: 816aba2a56ded47b4dadbe0fee21e339bfa423bd76bc6a4e1a2e5979dfc2048fa62124c23447e4d7144d6da06f153b48b04ff3374b5647fd05d0d94a9e8d8ba7
|
@@ -0,0 +1,24 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [ master ]
|
6
|
+
pull_request:
|
7
|
+
branches: [ master ]
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
test:
|
11
|
+
strategy:
|
12
|
+
fail-fast: false
|
13
|
+
matrix:
|
14
|
+
os: [ubuntu-latest, macos-latest]
|
15
|
+
ruby: [2.4, 2.5, 2.6, 2.7, jruby, truffleruby]
|
16
|
+
runs-on: ${{ matrix.os }}
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v2
|
19
|
+
- uses: ruby/setup-ruby@v1
|
20
|
+
with:
|
21
|
+
ruby-version: ${{ matrix.ruby }}
|
22
|
+
bundler-cache: true
|
23
|
+
- name: Run tests
|
24
|
+
run: bundle exec rake
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.4
|
3
|
+
|
4
|
+
Style/CommentedKeyword:
|
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
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
|
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
|
@@ -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 +
|
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 =
|
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
|
|
@@ -54,8 +30,7 @@ module SNMP
|
|
54
30
|
else
|
55
31
|
Open3.capture3(cli(cmd, oid, options))
|
56
32
|
end
|
57
|
-
|
58
|
-
raise CommandError, err.chomp unless err.empty?
|
33
|
+
raise_capture_errors(err)
|
59
34
|
out
|
60
35
|
end
|
61
36
|
|
@@ -66,6 +41,7 @@ module SNMP
|
|
66
41
|
[
|
67
42
|
command,
|
68
43
|
*options.map { |k, v| "#{k}#{v}" },
|
44
|
+
*oid_options(id),
|
69
45
|
*@host_options.map { |k, v| "#{k}#{v}" },
|
70
46
|
*@command_options.fetch(command, {}).map { |k, v| "#{k}#{v}" },
|
71
47
|
*id
|
@@ -74,11 +50,24 @@ module SNMP
|
|
74
50
|
|
75
51
|
private
|
76
52
|
|
53
|
+
def raise_capture_errors(err)
|
54
|
+
case err
|
55
|
+
when /^Cannot find module \(([^)]+)\)/
|
56
|
+
raise UnknownMIBError, "Unknown MIB: #{Regexp.last_match(1)}"
|
57
|
+
when /^(\S+): Unknown Object Identifier$/
|
58
|
+
raise UnknownOIDError, "Unknown OID: #{Regexp.last_match(1)}"
|
59
|
+
when /^timeout/i
|
60
|
+
raise CommandTimeoutError, err.chomp
|
61
|
+
when /./
|
62
|
+
raise CommandError, err.chomp
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
77
66
|
def merge_options(options = {})
|
78
67
|
options.each_pair.with_object({}) do |(key, value), opts|
|
79
|
-
if
|
80
|
-
opts[
|
81
|
-
(
|
68
|
+
if Options::MAP.key?(key)
|
69
|
+
opts[Options::MAP[key]] =
|
70
|
+
(Options::VALUES.fetch(key, {}).fetch(value, value) || next)
|
82
71
|
elsif key.is_a?(String)
|
83
72
|
opts[key] = value
|
84
73
|
else
|
@@ -87,6 +76,15 @@ module SNMP
|
|
87
76
|
end
|
88
77
|
end
|
89
78
|
|
79
|
+
# if the request OID is all-numeric, force numeric OID in the output
|
80
|
+
def oid_options(id)
|
81
|
+
if id =~ /[^0-9.]/
|
82
|
+
[]
|
83
|
+
else
|
84
|
+
['-On']
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
90
88
|
def normalize_command(command)
|
91
89
|
case command
|
92
90
|
when Symbol then "snmp#{command}"
|
@@ -123,6 +121,11 @@ module SNMP
|
|
123
121
|
end # class CommandReader
|
124
122
|
|
125
123
|
class CommandError < RuntimeError; end
|
124
|
+
|
126
125
|
class CommandTimeoutError < CommandError; end
|
126
|
+
|
127
|
+
class UnknownMIBError < CommandError; end
|
128
|
+
|
129
|
+
class UnknownOIDError < CommandError; end
|
127
130
|
end # class Open
|
128
131
|
end # module SNMP
|
@@ -13,19 +13,36 @@ module SNMP
|
|
13
13
|
# be used to generate a needed file, if the file is unavailable. Controlled
|
14
14
|
# by the `warnings` option.
|
15
15
|
class FileReader
|
16
|
+
DEFAULT_WARNING_FORMATTER = lambda { |gen, cmd, oid, outfile|
|
17
|
+
"#{gen.cli(cmd, oid)} > #{outfile}"
|
18
|
+
}
|
19
|
+
|
16
20
|
def initialize(directory, options = {})
|
17
21
|
@directory = directory
|
18
22
|
@warnings = options.delete(:warnings)
|
19
|
-
@
|
20
|
-
|
23
|
+
@make_directories = options.delete(:make_directories)
|
24
|
+
if @warnings && !@warnings.respond_to?(:call)
|
25
|
+
@warnings = DEFAULT_WARNING_FORMATTER
|
26
|
+
end
|
27
|
+
options[:host] ||= '$OPTIONS'
|
28
|
+
@command_generator = SNMP::Open::CommandReader.new(options)
|
21
29
|
end
|
22
30
|
|
23
31
|
def capture(cmd, oid, _options = {})
|
24
|
-
|
25
|
-
File.
|
26
|
-
|
27
|
-
|
28
|
-
|
32
|
+
mkdir(@directory, cmd.to_s) if @make_directories
|
33
|
+
outfile = File.join(@directory, cmd.to_s, oid)
|
34
|
+
File.read(outfile)
|
35
|
+
rescue Errno::ENOENT => e
|
36
|
+
if @warnings
|
37
|
+
warning = @warnings.call(@command_generator, cmd, oid, outfile)
|
38
|
+
warn warning
|
39
|
+
end
|
40
|
+
raise e
|
41
|
+
end
|
42
|
+
|
43
|
+
def mkdir(base, cmd)
|
44
|
+
Dir.mkdir(base) unless File.exist?(base)
|
45
|
+
Dir.mkdir(File.join(base, cmd)) unless File.exist?(File.join(base, cmd))
|
29
46
|
end
|
30
47
|
end # class FileReader
|
31
48
|
end # class Open
|
@@ -0,0 +1,41 @@
|
|
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 values
|
28
|
+
# and/or value units. The parser doesn't support these, so disable them.
|
29
|
+
REQUIRED_BY_PARSER = {
|
30
|
+
'-Oe' => nil,
|
31
|
+
'-OU' => nil
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
VALUES = {
|
35
|
+
no_check_increasing: {
|
36
|
+
true => ''
|
37
|
+
}.freeze
|
38
|
+
}.freeze
|
39
|
+
end # class Options
|
40
|
+
end # class Open
|
41
|
+
end # module SNMP
|
data/lib/snmp/open/parser.rb
CHANGED
@@ -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,38 @@ module SNMP
|
|
11
13
|
|
12
14
|
# convert SNMP command output into arrays
|
13
15
|
class Parser
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
include SNMP::Open::Parser::Constants
|
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
|
@@ -22,11 +52,8 @@ module SNMP
|
|
22
52
|
|
23
53
|
def parse(texts)
|
24
54
|
columns = texts.map do |text|
|
25
|
-
|
26
|
-
|
27
|
-
.gsub(NOSUCHOBJECT_STR, %("#{NOSUCHOBJECT_STR}"))
|
28
|
-
.gsub(NOSUCHINSTANCE_STR, %("#{NOSUCHINSTANCE_STR}"))
|
29
|
-
.shellsplit
|
55
|
+
clean = clean_input_text(text)
|
56
|
+
tokenized = clean.shellsplit
|
30
57
|
parse_tokens(tokenized)
|
31
58
|
end
|
32
59
|
|
@@ -35,7 +62,7 @@ module SNMP
|
|
35
62
|
|
36
63
|
private
|
37
64
|
|
38
|
-
def
|
65
|
+
def align(columns)
|
39
66
|
indexes = columns.first.map { |value| index_using_first_oid(value) }
|
40
67
|
hash = columns.flat_map { |row| row.map { |v| [v.oid, v] } }.to_h
|
41
68
|
|
@@ -47,6 +74,14 @@ module SNMP
|
|
47
74
|
end
|
48
75
|
end
|
49
76
|
|
77
|
+
def clean_input_text(text)
|
78
|
+
text
|
79
|
+
.gsub(/\r\n|\n\r|\r/, "\n")
|
80
|
+
.gsub(EMPTY_STRING_RE, %(\\1 = \\2: ""\n))
|
81
|
+
.gsub(STRING_RE, %(\\1 = \\2: "\\3"\n))
|
82
|
+
.gsub(Static::ANY_MESSAGE, Static::QUOTED_MESSAGES)
|
83
|
+
end
|
84
|
+
|
50
85
|
def index_using_first_oid(value)
|
51
86
|
base = @oids.first
|
52
87
|
|
@@ -69,34 +104,31 @@ module SNMP
|
|
69
104
|
|
70
105
|
objects
|
71
106
|
rescue StopIteration
|
72
|
-
|
107
|
+
objects
|
73
108
|
end
|
74
109
|
|
75
110
|
def parse_next_object(tokens)
|
76
111
|
oid = tokens.next.sub(/\A\./, '')
|
77
|
-
raise "Parse error at #{oid}" unless oid =~
|
112
|
+
raise "Parse error at #{oid}" unless oid =~ OID_RE
|
113
|
+
|
78
114
|
equals = tokens.next
|
79
115
|
raise "Parse error after #{oid}" unless equals == '='
|
116
|
+
|
80
117
|
type, value = parse_type(tokens)
|
81
118
|
Value.new(oid, type, value)
|
82
119
|
end
|
83
120
|
|
84
121
|
def parse_type(tokens)
|
85
|
-
|
86
|
-
type =
|
87
|
-
type,
|
88
|
-
[type, Conversions.convert_value(type, value)]
|
122
|
+
token = tokens.next
|
123
|
+
type = token.match(/\A([-A-Za-z]+[0-9]*):\z/) { |md| md[1] }
|
124
|
+
ValueParser.find(type, token).parse(tokens)
|
89
125
|
end
|
90
126
|
|
91
|
-
def
|
92
|
-
if
|
93
|
-
|
94
|
-
elsif token == NOSUCHINSTANCE_STR
|
95
|
-
['No Such Instance', nil]
|
96
|
-
elsif !type
|
97
|
-
['STRING', token]
|
127
|
+
def table(columns)
|
128
|
+
if columns.size == 1 && columns.all? { |column| column.size == 1 }
|
129
|
+
columns
|
98
130
|
else
|
99
|
-
|
131
|
+
align(columns)
|
100
132
|
end
|
101
133
|
end
|
102
134
|
|
@@ -116,25 +148,24 @@ module SNMP
|
|
116
148
|
@oids.zip(columns).map do |oid, column|
|
117
149
|
indexes.map do |index|
|
118
150
|
id = (oid == index ? index : "#{oid}.#{index}")
|
119
|
-
column.find { |o| o.oid == id } ||
|
151
|
+
column.find { |o| o.oid == id } || Value.new(id, 'absent', nil)
|
120
152
|
end
|
121
153
|
end
|
122
154
|
end
|
123
155
|
|
124
|
-
#
|
125
|
-
module
|
126
|
-
|
127
|
-
case type
|
128
|
-
when 'INTEGER'
|
129
|
-
value.to_i
|
130
|
-
else
|
131
|
-
value
|
132
|
-
end
|
133
|
-
end
|
156
|
+
# static messages from net-snmp commands
|
157
|
+
module Static
|
158
|
+
include SNMP::Open::Parser::Constants
|
134
159
|
|
135
|
-
|
136
|
-
|
137
|
-
|
160
|
+
MESSAGES = [
|
161
|
+
NOSUCHOBJECT_STR,
|
162
|
+
NOSUCHINSTANCE_STR,
|
163
|
+
NOMOREVARIABLES_STR
|
164
|
+
].freeze
|
165
|
+
|
166
|
+
ANY_MESSAGE = Regexp.union(*MESSAGES)
|
167
|
+
|
168
|
+
QUOTED_MESSAGES = MESSAGES.map { |v| [v, %("#{v}")] }.to_h.freeze
|
138
169
|
end
|
139
170
|
end # class Parser
|
140
171
|
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,132 @@
|
|
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
|
+
|
27
|
+
bytes = []
|
28
|
+
loop do
|
29
|
+
break unless tokens.peek =~ /\A[0-9A-Za-z]{1,2}\z/
|
30
|
+
|
31
|
+
bytes << tokens.next.to_i(16)
|
32
|
+
end
|
33
|
+
@parse = [@type, bytes]
|
34
|
+
end
|
35
|
+
end # class Bits < ValueParser
|
36
|
+
|
37
|
+
# parses objects with no explicit type
|
38
|
+
class Default < ValueParser
|
39
|
+
def initialize(_type, token)
|
40
|
+
@parse = ['STRING', token]
|
41
|
+
end
|
42
|
+
end # class Default
|
43
|
+
|
44
|
+
# parses integer-like objects
|
45
|
+
class Integer < ValueParser
|
46
|
+
def parse(tokens)
|
47
|
+
@parse ||= [@type, Integer(tokens.next)]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# parses objects identified like '= Hex-STRING:'
|
52
|
+
class HexString < ValueParser
|
53
|
+
def parse(tokens)
|
54
|
+
return @parse if @parse
|
55
|
+
|
56
|
+
bytes = []
|
57
|
+
loop do
|
58
|
+
break unless tokens.peek =~ /\A[0-9A-Za-z]{2}\z/
|
59
|
+
|
60
|
+
bytes << tokens.next
|
61
|
+
end
|
62
|
+
string = bytes.map { |b| b.to_i(16).chr }.join
|
63
|
+
@parse = [@type, string]
|
64
|
+
end
|
65
|
+
end # class HexString
|
66
|
+
|
67
|
+
# handles messages indicating the end of the response
|
68
|
+
class Stop < ValueParser
|
69
|
+
def parse(*)
|
70
|
+
raise StopIteration, @token
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# parses objects identified like '= Timeticks:'
|
75
|
+
# note that 1 second = 100 ticks
|
76
|
+
class Timeticks < ValueParser
|
77
|
+
def parse(tokens)
|
78
|
+
return @parse if @parse
|
79
|
+
|
80
|
+
ticks = tokens.next.tr('()', '').to_i
|
81
|
+
|
82
|
+
# consume tokens through one like 23:59:59.99
|
83
|
+
loop do
|
84
|
+
break if tokens.next =~ /\A\d\d:\d\d:\d\d.\d\d\z/
|
85
|
+
end
|
86
|
+
|
87
|
+
@parse = [@type, ticks]
|
88
|
+
end
|
89
|
+
end # class Timeticks
|
90
|
+
|
91
|
+
# handles objects not handled by any other parser
|
92
|
+
class Other < ValueParser
|
93
|
+
def parse(tokens)
|
94
|
+
@parse ||= [@type, tokens.next]
|
95
|
+
end
|
96
|
+
end # class Other
|
97
|
+
|
98
|
+
# handles NoSuchInstance
|
99
|
+
class NoSuchInstance < ValueParser
|
100
|
+
def initialize(*)
|
101
|
+
@parse = ['No Such Instance', nil]
|
102
|
+
end
|
103
|
+
end # class NoSuchInstance < ValueParser
|
104
|
+
|
105
|
+
# handles NoSuchObject
|
106
|
+
class NoSuchObject < ValueParser
|
107
|
+
def initialize(*)
|
108
|
+
@parse = ['No Such Object', nil]
|
109
|
+
end
|
110
|
+
end # class NoSuchObject < ValueParser
|
111
|
+
|
112
|
+
KNOWN_TOKENS = {
|
113
|
+
NOSUCHINSTANCE_STR => NoSuchInstance,
|
114
|
+
NOSUCHOBJECT_STR => NoSuchObject,
|
115
|
+
NOMOREVARIABLES_STR => Stop
|
116
|
+
}.freeze
|
117
|
+
|
118
|
+
KNOWN_TYPES = {
|
119
|
+
nil => Default,
|
120
|
+
'BITS' => Bits,
|
121
|
+
'INTEGER' => ValueParser::Integer,
|
122
|
+
'Gauge32' => ValueParser::Integer,
|
123
|
+
'Gauge64' => ValueParser::Integer,
|
124
|
+
'Counter32' => ValueParser::Integer,
|
125
|
+
'Counter64' => ValueParser::Integer,
|
126
|
+
'Hex-STRING' => HexString,
|
127
|
+
'Timeticks' => Timeticks
|
128
|
+
}.freeze
|
129
|
+
end # class ValueParser
|
130
|
+
end # class Parser
|
131
|
+
end # class Open
|
132
|
+
end # module SNMP
|
data/lib/snmp/open/version.rb
CHANGED
data/snmp-open.gemspec
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
lib = File.expand_path('
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
3
|
require 'snmp/open/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'snmp-open'
|
7
|
-
spec.version =
|
7
|
+
spec.version = SNMP::Open::VERSION
|
8
8
|
spec.authors = ['Ben Miller']
|
9
9
|
spec.email = ['bmiller@rackspace.com']
|
10
10
|
|
@@ -18,8 +18,10 @@ 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.
|
22
|
-
|
21
|
+
spec.required_ruby_version = '~> 2.4'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 2.2'
|
24
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
23
25
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
24
26
|
spec.add_development_dependency 'snmp'
|
25
27
|
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.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Miller
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.2'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '13.0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '13.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,16 +66,17 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
description:
|
69
|
+
description:
|
70
70
|
email:
|
71
71
|
- bmiller@rackspace.com
|
72
72
|
executables: []
|
73
73
|
extensions: []
|
74
74
|
extra_rdoc_files: []
|
75
75
|
files:
|
76
|
+
- ".github/workflows/ruby.yml"
|
76
77
|
- ".gitignore"
|
77
78
|
- ".rspec"
|
78
|
-
- ".
|
79
|
+
- ".rubocop.yml"
|
79
80
|
- CODE_OF_CONDUCT.md
|
80
81
|
- Gemfile
|
81
82
|
- Guardfile
|
@@ -89,30 +90,32 @@ files:
|
|
89
90
|
- lib/snmp/open.rb
|
90
91
|
- lib/snmp/open/command_reader.rb
|
91
92
|
- lib/snmp/open/file_reader.rb
|
93
|
+
- lib/snmp/open/options.rb
|
92
94
|
- lib/snmp/open/parser.rb
|
95
|
+
- lib/snmp/open/parser/constants.rb
|
96
|
+
- lib/snmp/open/parser/value_parser.rb
|
93
97
|
- lib/snmp/open/version.rb
|
94
98
|
- snmp-open.gemspec
|
95
99
|
homepage: https://github.com/bjmllr/snmp-open
|
96
100
|
licenses: []
|
97
101
|
metadata: {}
|
98
|
-
post_install_message:
|
102
|
+
post_install_message:
|
99
103
|
rdoc_options: []
|
100
104
|
require_paths:
|
101
105
|
- lib
|
102
106
|
required_ruby_version: !ruby/object:Gem::Requirement
|
103
107
|
requirements:
|
104
|
-
- - "
|
108
|
+
- - "~>"
|
105
109
|
- !ruby/object:Gem::Version
|
106
|
-
version: '
|
110
|
+
version: '2.4'
|
107
111
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
112
|
requirements:
|
109
113
|
- - ">="
|
110
114
|
- !ruby/object:Gem::Version
|
111
115
|
version: '0'
|
112
116
|
requirements: []
|
113
|
-
|
114
|
-
|
115
|
-
signing_key:
|
117
|
+
rubygems_version: 3.1.2
|
118
|
+
signing_key:
|
116
119
|
specification_version: 4
|
117
120
|
summary: Wrapper for command-line SNMP utilities
|
118
121
|
test_files: []
|