logstash-input-snmp 1.0.1 → 1.2.3

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: 33e68f50e2d8dce7148c80ccc3a90029b4a0cc1f1039ae72a99be1525df17d89
4
- data.tar.gz: 75d01ff55607a5e0aa31251bcdb91d94d75c3009acae39ac82c4dfb0a09feaa4
3
+ metadata.gz: 131263a5b332d433de705dac607ddecd0920f33827c0419c7a837adc735c4dc0
4
+ data.tar.gz: d1b6e99a2c8340470833dc83e2d2bb756342c332cc36ba22ac90b93caa32149b
5
5
  SHA512:
6
- metadata.gz: e2e8ae4ba872e2f55451be69e62108572ff27ea9380eaa29330cd80a42dbfc39cba4bfb4a1f11ef6e087c66f8e4c0d927484a02f093c742c666c6ede031f8cfc
7
- data.tar.gz: 7347cab038cb7d1efbee0c8d60c5fe3a13dcedd2986f399ae150feeaa8758f34f0c0a32740f94685b13e172868a52d340f121bcdde81ffb193f9902985722932
6
+ metadata.gz: 8b7b2c722a7eb4d81492b8f39b2aa05fd60131f1871b89ec0b122fbaed398336924a5c805bd3885a45906b2ba43a41d9579fb5d5ab456d42cc8a32df93557e51
7
+ data.tar.gz: c2f3d2d67868fdd602117dab01f371c76b3e8ea7406438965395e088f73db06a4b589635cb57989e7d90e6ebf9a7f0fcee4aa96aa6592572fb48686d9c3abb4d
@@ -1,3 +1,23 @@
1
+ ## 1.2.3
2
+ - Fixed: multithreading problem when using multiple snmp inputs with multiple v3 credentials [#80](https://github.com/logstash-plugins/logstash-input-snmp/pull/80)
3
+
4
+ ## 1.2.2
5
+ - Refactor: scope and review java_imports [#72](https://github.com/logstash-plugins/logstash-input-snmp/pull/72)
6
+
7
+ ## 1.2.1
8
+ - Fixed GAUGE32 integer overflow [#65](https://github.com/logstash-plugins/logstash-input-snmp/pull/65)
9
+
10
+ ## 1.2.0
11
+ - Adding oid_path_length config option [#59](https://github.com/logstash-plugins/logstash-input-snmp/pull/59)
12
+ - Fixing bug with table support removing index value from OIDs [#60])https://github.com/logstash-plugins/logstash-input-snmp/issues/60)
13
+
14
+ ## 1.1.1
15
+ - Added information and other improvements to documentation [#57](https://github.com/logstash-plugins/logstash-input-snmp/pull/57)
16
+
17
+ ## 1.1.0
18
+ - Added support for querying SNMP tables [#49](https://github.com/logstash-plugins/logstash-input-snmp/pull/49)
19
+ - Changed three error messages in the base_client to include the target address for clarity in the logs.
20
+
1
21
  ## 1.0.1
2
22
  - Added no_codec condition to the documentation and bumped version [#39](https://github.com/logstash-plugins/logstash-input-snmp/pull/39)
3
23
  - Changed docs to improve options layout [#38](https://github.com/logstash-plugins/logstash-input-snmp/pull/38)
@@ -22,4 +42,3 @@
22
42
 
23
43
  ## 0.1.0.beta1
24
44
  - First beta version
25
-
@@ -3,6 +3,7 @@ reports, or in general have helped logstash along its way.
3
3
 
4
4
  Contributors:
5
5
  * Colin Surprenant - colin.surprenant@gmail.com
6
+ * Dan Major - axrayn@gmail.com
6
7
 
7
8
  Note: If you've sent us patches, bug reports, or otherwise contributed to
8
9
  Logstash, and you aren't on the list above and want to be, please let us know
data/LICENSE CHANGED
@@ -1,11 +1,202 @@
1
- Licensed under the Apache License, Version 2.0 (the "License");
2
- you may not use this file except in compliance with the License.
3
- You may obtain a copy of the License at
4
1
 
5
- http://www.apache.org/licenses/LICENSE-2.0
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
6
5
 
7
- Unless required by applicable law or agreed to in writing, software
8
- distributed under the License is distributed on an "AS IS" BASIS,
9
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
- See the License for the specific language governing permissions and
11
- limitations under the License.
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2020 Elastic and contributors
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
@@ -59,8 +59,10 @@ This plugin supports the following configuration options plus the <<plugins-{typ
59
59
  | <<plugins-{type}s-{plugin}-hosts>> |<<array,array>>|No
60
60
  | <<plugins-{type}s-{plugin}-interval>> |<<number,number>>|No
61
61
  | <<plugins-{type}s-{plugin}-mib_paths>> |<<path,path>>|No
62
- | <<plugins-{type}s-{plugin}-oid_root_skip>> |<<boolean,boolean>>|No
62
+ | <<plugins-{type}s-{plugin}-oid_root_skip>> |<<number,number>>|No
63
+ | <<plugins-{type}s-{plugin}-oid_path_length>> |<<number,number>>|No
63
64
  | <<plugins-{type}s-{plugin}-walk>> |<<array,array>>|No
65
+ | <<plugins-{type}s-{plugin}-tables>> |<<array,array>>|No
64
66
  |=======================================================================
65
67
 
66
68
  ==== SNMPv3 Authentication Options
@@ -168,7 +170,7 @@ If you require other MIBs, you need to import them. See <<plugins-{type}s-{plugi
168
170
  [id="plugins-{type}s-{plugin}-oid_root_skip"]
169
171
  ===== `oid_root_skip`
170
172
 
171
- The `oid_root_skip` option specifies the number of OID root digits to ignore in event field name.
173
+ The `oid_root_skip` option specifies the number of OID root digits to ignore in the event field name.
172
174
  For example, in a numeric OID like "1.3.6.1.2.1.1.1.0" the first 5 digits could be ignored by setting `oid_root_skip => 5`
173
175
  which would result in a field name "1.1.1.0". Similarly when a MIB is used an OID such
174
176
  "1.3.6.1.2.mib-2.system.sysDescr.0" would become "mib-2.system.sysDescr.0"
@@ -176,6 +178,17 @@ which would result in a field name "1.1.1.0". Similarly when a MIB is used an OI
176
178
  * Value type is <<number,number>>
177
179
  * Default value is `0`
178
180
 
181
+ [id="plugins-{type}s-{plugin}-oid_path_length"]
182
+ ===== `oid_path_length`
183
+
184
+ The `oid_path_length` option specifies the number of OID root digits to retain in the event field name.
185
+ For example, in a numeric OID like "1.3.6.1.2.1.1.1.0" the last 2 digits could be retained by setting `oid_path_length => 2`
186
+ which would result in a field name "1.0". Similarly when a MIB is used an OID such
187
+ "1.3.6.1.2.mib-2.system.sysDescr.0" would become "sysDescr.0"
188
+
189
+ * Value type is <<number,number>>
190
+ * Default value is `0`
191
+
179
192
  [id="plugins-{type}s-{plugin}-walk"]
180
193
  ===== `walk`
181
194
 
@@ -197,6 +210,41 @@ Example
197
210
  }
198
211
  -----
199
212
 
213
+ [id="plugins-{type}s-{plugin}-tables"]
214
+ ===== `tables`
215
+
216
+ The `tables` option is used to query for tabular values for the given column OID(s).
217
+
218
+ Each table definition is a hash and must define the name key and value and the columns to return.
219
+
220
+ * Value type is <<array,array>>
221
+ * There is no default value for this setting
222
+ * Results are returned under a field using the table name
223
+
224
+ *Specifying a single table*
225
+
226
+ [source,ruby]
227
+ -----
228
+ input {
229
+ snmp {
230
+ hosts => [{host => "udp:127.0.0.1/161" community => "public" version => "2c" retries => 2 timeout => 1000}]
231
+ tables => [ {"name" => "interfaces" "columns" => ["1.3.6.1.2.1.2.2.1.1", "1.3.6.1.2.1.2.2.1.2", "1.3.6.1.2.1.2.2.1.5"]} ]
232
+ }
233
+ }
234
+ -----
235
+
236
+ *Specifying multiple tables*
237
+
238
+ [source,ruby]
239
+ -----
240
+ input {
241
+ snmp {
242
+ get => ["1.3.6.1.2.1.1.1.0"]
243
+ tables => [ {"name" => "interfaces" "columns" => ["1.3.6.1.2.1.2.2.1.1", "1.3.6.1.2.1.2.2.1.2", "1.3.6.1.2.1.2.2.1.5"]}, {"name" => "ltmPoolStatTable" "columns" => ["1.3.6.1.4.1.3375.2.2.5.2.3.1.1", "1.3.6.1.4.1.3375.2.2.5.2.3.1.6"]} ]
244
+ }
245
+ }
246
+ -----
247
+
200
248
  ==== SNMPv3 Authentication Options
201
249
 
202
250
  A **single user** can be configured and will be used for all defined SNMPv3 hosts.
@@ -252,6 +300,25 @@ The `security_level` option specifies the SNMPv3 security level between Authenti
252
300
  * Value can be any of: `noAuthNoPriv`, `authNoPriv`, `authPriv`
253
301
  * There is no default value for this setting
254
302
 
303
+ *Specifying SNMPv3 settings*
304
+
305
+ [source,ruby]
306
+ -----
307
+ input {
308
+ snmp {
309
+ hosts => [{host => "udp:127.0.0.1/161" version => "3"}]
310
+ get => ["1.3.6.1.2.1.1.1.0"]
311
+ security_name => "mySecurityName"
312
+ auth_protocol => "sha"
313
+ auth_pass => "ShaPassword"
314
+ priv_protocol => "aes"
315
+ priv_pass => "AesPasword"
316
+ security_level => "authPriv"
317
+ }
318
+ }
319
+
320
+ -----
321
+
255
322
  ==== More configuration examples
256
323
 
257
324
  *Using both `get` and `walk` in the same poll cycle for each host(s)*
@@ -20,6 +20,9 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
20
20
  # List of OIDs for which we want to retrieve the subtree of information
21
21
  config :walk,:validate => :array # ["1.3.6.1.2.1.1.1.0"]
22
22
 
23
+ # List of tables to walk
24
+ config :tables, :validate => :array #[ {"name" => "interfaces" "columns" => ["1.3.6.1.2.1.2.2.1.1", "1.3.6.1.2.1.2.2.1.2", "1.3.6.1.2.1.2.2.1.5"]} ]
25
+
23
26
  # List of hosts to query the configured `get` and `walk` options.
24
27
  #
25
28
  # Each host definition is a hash and must define the `host` key and value.
@@ -53,6 +56,12 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
53
56
  # as "1.3.6.1.2.mib-2.system.sysDescr.0" would become "mib-2.system.sysDescr.0"
54
57
  config :oid_root_skip, :validate => :number, :default => 0
55
58
 
59
+ # number of OID tail digits to retain in event field name. For example, in a numeric OID
60
+ # like 1.3.6.1.2.1.1.1.0" the last 2 digits could be retained by setting oid_path_length => 2
61
+ # which would result in a field name "1.0". Similarly, when a MIB is used an OID such as
62
+ # "1.3.6.1.2.mib-2.system.sysDescr.0" would become "sysDescr.0"
63
+ config :oid_path_length, :validate => :number, :default => 0
64
+
56
65
  # Set polling interval in seconds
57
66
  #
58
67
  # The default, `30`, means poll each host every 30 seconds.
@@ -91,6 +100,8 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
91
100
  def register
92
101
  validate_oids!
93
102
  validate_hosts!
103
+ validate_tables!
104
+ validate_strip!
94
105
 
95
106
  mib = LogStash::SnmpMib.new
96
107
 
@@ -157,7 +168,7 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
157
168
  result = {}
158
169
  if !definition[:get].empty?
159
170
  begin
160
- result = result.merge(definition[:client].get(definition[:get], @oid_root_skip))
171
+ result = result.merge(definition[:client].get(definition[:get], @oid_root_skip, @oid_path_length))
161
172
  rescue => e
162
173
  logger.error("error invoking get operation on #{definition[:host_address]} for OIDs: #{definition[:get]}, ignoring", :exception => e, :backtrace => e.backtrace)
163
174
  end
@@ -165,13 +176,23 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
165
176
  if !definition[:walk].empty?
166
177
  definition[:walk].each do |oid|
167
178
  begin
168
- result = result.merge(definition[:client].walk(oid, @oid_root_skip))
179
+ result = result.merge(definition[:client].walk(oid, @oid_root_skip, @oid_path_length))
169
180
  rescue => e
170
181
  logger.error("error invoking walk operation on OID: #{oid}, ignoring", :exception => e, :backtrace => e.backtrace)
171
182
  end
172
183
  end
173
184
  end
174
185
 
186
+ if !Array(@tables).empty?
187
+ @tables.each do |table_entry|
188
+ begin
189
+ result = result.merge(definition[:client].table(table_entry, @oid_root_skip, @oid_path_length))
190
+ rescue => e
191
+ logger.error("error invoking table operation on OID: #{table_entry['name']}, ignoring", :exception => e, :backtrace => e.backtrace)
192
+ end
193
+ end
194
+ end
195
+
175
196
  unless result.empty?
176
197
  metadata = {
177
198
  "host_protocol" => definition[:host_protocol],
@@ -217,7 +238,20 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
217
238
  $1
218
239
  end
219
240
 
220
- raise(LogStash::ConfigurationError, "at least one get OID or one walk OID is required") if @get.empty? && @walk.empty?
241
+ if !@tables.nil?
242
+ @tables.each do |table_entry|
243
+ # Verify oids for valid pattern and get rid of any leading dot if present
244
+ columns = table_entry["columns"]
245
+ columns.each do |column|
246
+ unless column =~ OID_REGEX
247
+ raise(Logstash::ConfigurationError, "The table column oid '#{column}' is an invalid format")
248
+ end
249
+ end
250
+ $1
251
+ end
252
+ end
253
+
254
+ raise(LogStash::ConfigurationError, "at least one get OID, one walk OID, or one table OID is required") if @get.empty? && @walk.empty? && @tables.nil?
221
255
  end
222
256
 
223
257
  def validate_v3_user!
@@ -242,4 +276,16 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
242
276
  raise(LogStash::ConfigurationError, "each host definition must have a \"host\" option") if !host.is_a?(Hash) || host["host"].nil?
243
277
  end
244
278
  end
279
+
280
+ def validate_tables!
281
+ if !@tables.nil?
282
+ @tables.each do |table_entry|
283
+ raise(LogStash::ConfigurationError, "each table definition must have a \"name\" option") if !table_entry.is_a?(Hash) || table_entry["name"].nil?
284
+ end
285
+ end
286
+ end
287
+
288
+ def validate_strip!
289
+ raise(LogStash::ConfigurationError, "you can not specify both oid_root_skip and oid_path_length") if @oid_root_skip > 0 and @oid_path_length > 0
290
+ end
245
291
  end
@@ -3,48 +3,71 @@ require "logstash-input-snmp_jars.rb"
3
3
 
4
4
  require "logstash/util/loggable"
5
5
 
6
- java_import "org.snmp4j.CommunityTarget"
7
- java_import "org.snmp4j.PDU"
8
- java_import "org.snmp4j.ScopedPDU"
9
- java_import "org.snmp4j.Snmp"
10
- java_import "org.snmp4j.Target"
11
- java_import "org.snmp4j.TransportMapping"
12
- java_import "org.snmp4j.event.ResponseEvent"
13
- java_import "org.snmp4j.mp.SnmpConstants"
14
- java_import "org.snmp4j.smi.Address"
15
- java_import "org.snmp4j.smi.GenericAddress"
16
- java_import "org.snmp4j.smi.OID"
17
- java_import "org.snmp4j.smi.OctetString"
18
- java_import "org.snmp4j.smi.VariableBinding"
19
- java_import "org.snmp4j.transport.DefaultUdpTransportMapping"
20
- java_import "org.snmp4j.transport.DefaultTcpTransportMapping"
21
- java_import "org.snmp4j.util.TreeUtils"
22
- java_import "org.snmp4j.util.DefaultPDUFactory"
23
- java_import "org.snmp4j.asn1.BER"
24
-
25
6
  module LogStash
26
7
  class SnmpClientError < StandardError
27
8
  end
28
9
 
10
+ # Only one Snmp instance per transport should be created and listened on.
11
+ # The Snmp object is thread safe and can be shared across input threads and pipelines.
12
+ class SnmpFactory
13
+ java_import "org.snmp4j.Snmp"
14
+ java_import "org.snmp4j.transport.DefaultUdpTransportMapping"
15
+ java_import "org.snmp4j.transport.DefaultTcpTransportMapping"
16
+
17
+ include Singleton
18
+
19
+ def initialize
20
+ @lock = Mutex.new
21
+ @udp = nil
22
+ @tcp = nil
23
+ end
24
+
25
+ def udp
26
+ @lock.synchronize do
27
+ if @udp.nil?
28
+ @udp = Snmp.new(DefaultUdpTransportMapping.new)
29
+ @udp.listen
30
+ end
31
+ end
32
+ @udp
33
+ end
34
+
35
+ def tcp
36
+ @lock.synchronize do
37
+ if @tcp.nil?
38
+ @tcp = Snmp.new(DefaultTcpTransportMapping.new)
39
+ @tcp.listen
40
+ end
41
+ end
42
+ @tcp
43
+ end
44
+ end
45
+
29
46
  class BaseSnmpClient
47
+ java_import "org.snmp4j.TransportMapping"
48
+ java_import "org.snmp4j.mp.SnmpConstants"
49
+ java_import "org.snmp4j.smi.OID"
50
+ java_import "org.snmp4j.smi.VariableBinding"
51
+ java_import "org.snmp4j.util.TableUtils"
52
+ java_import "org.snmp4j.util.TreeUtils"
53
+ java_import "org.snmp4j.asn1.BER"
54
+
30
55
  include LogStash::Util::Loggable
31
56
 
32
57
  def initialize(protocol, address, port, retries, timeout, mib)
58
+ @mib = mib
59
+
33
60
  transport = case protocol.to_s
34
61
  when "udp"
35
- DefaultUdpTransportMapping.new
62
+ @snmp = SnmpFactory.instance.udp
36
63
  when "tcp"
37
- DefaultTcpTransportMapping.new
64
+ @snmp = SnmpFactory.instance.tcp
38
65
  else
39
66
  raise(SnmpClientError, "invalid transport protocol specified '#{protocol.to_s}', expecting 'udp' or 'tcp'")
40
- end
41
-
42
- @mib = mib
43
- @snmp = Snmp.new(transport)
44
- transport.listen()
67
+ end
45
68
  end
46
69
 
47
- def get(oids, strip_root = 0)
70
+ def get(oids, strip_root = 0, path_length = 0)
48
71
  pdu = get_pdu
49
72
  Array(oids).each { |oid| pdu.add(VariableBinding.new(OID.new(oid))) }
50
73
 
@@ -52,11 +75,11 @@ module LogStash
52
75
  return nil if response_event.nil?
53
76
 
54
77
  e = response_event.getError
55
- raise(SnmpClientError, "error sending snmp get request: #{e.inspect}, #{e.getMessage}") if e
78
+ raise(SnmpClientError, "error sending snmp get request to target #{@target.address}: #{e.inspect}, #{e.getMessage}") if e
56
79
 
57
80
  result = {}
58
81
  response_pdu = response_event.getResponse
59
- raise(SnmpClientError, "timeout sending snmp get request") if response_pdu.nil?
82
+ raise(SnmpClientError, "timeout sending snmp get request to target #{@target.address}") if response_pdu.nil?
60
83
 
61
84
  size = response_pdu.size
62
85
  (0..size - 1).each do |i|
@@ -65,14 +88,14 @@ module LogStash
65
88
  variable = variable_binding.getVariable
66
89
  value = coerce(variable)
67
90
 
68
- result[@mib.map_oid(oid, strip_root)] = value
91
+ result[@mib.map_oid(oid, strip_root, path_length)] = value
69
92
  end
70
93
 
71
94
  result
72
95
  end
73
96
 
74
97
 
75
- def walk(oid, strip_root = 0)
98
+ def walk(oid, strip_root = 0, path_length = 0)
76
99
  result = {}
77
100
 
78
101
  pdufactory = get_pdu_factory
@@ -85,7 +108,7 @@ module LogStash
85
108
 
86
109
  if event.isError
87
110
  # TODO: see if we can salvage non errored event here
88
- raise(SnmpClientError, "error sending snmp walk request: #{event.getErrorMessage}")
111
+ raise(SnmpClientError, "error sending snmp walk request to target #{@target.address}: #{event.getErrorMessage}")
89
112
  end
90
113
 
91
114
  var_bindings = event.getVariableBindings
@@ -98,13 +121,56 @@ module LogStash
98
121
  variable = var_binding.getVariable
99
122
  value = coerce(variable)
100
123
 
101
- result[@mib.map_oid(oid, strip_root)] = value
124
+ result[@mib.map_oid(oid, strip_root, path_length)] = value
102
125
  end
103
126
  end
104
127
 
105
128
  result
106
129
  end
107
130
 
131
+ def table(table, strip_root = 0, path_length = 0)
132
+ result = {}
133
+ rows = []
134
+ pdufactory = get_pdu_factory
135
+ table_name = table["name"]
136
+ tableUtils = TableUtils.new(@snmp, pdufactory)
137
+
138
+ colOID = Array.new
139
+ Array(table["columns"]).each { |oid| colOID << OID.new(oid) }
140
+
141
+ events = tableUtils.getTable(@target, colOID.to_java(org.snmp4j.smi.OID), nil, nil)
142
+
143
+ return nil if events.nil? || events.size == 0
144
+ events.each do |event|
145
+ next if event.nil?
146
+
147
+ if event.isError
148
+ # TODO: see if we can salvage non errored event here
149
+ raise(SnmpClientError, "error sending snmp table request to target #{@target.address}: #{event.getErrorMessage}")
150
+ end
151
+
152
+ row = {}
153
+ idx_val = event.getIndex.toString
154
+ row["index"] = idx_val
155
+
156
+ var_bindings = event.getColumns
157
+ next if var_bindings.nil? || var_bindings.size == 0
158
+
159
+ var_bindings.each do |var_binding|
160
+ next if var_binding.nil?
161
+
162
+ oid = var_binding.getOid.toString
163
+ variable = var_binding.getVariable
164
+ value = coerce(variable)
165
+ mapped_oid = @mib.map_oid(oid, strip_root, path_length).chomp('.'+idx_val)
166
+ row[mapped_oid] = value
167
+ end
168
+ rows << row
169
+ end
170
+ result[table_name] = rows
171
+ result
172
+ end
173
+
108
174
  protected
109
175
 
110
176
  NULL_STRING = "null".freeze
@@ -115,9 +181,9 @@ module LogStash
115
181
  case variable_syntax
116
182
  when BER::OCTETSTRING, BER::BITSTRING
117
183
  variable.toString
118
- when BER::TIMETICKS, BER::COUNTER, BER::COUNTER32, BER::COUNTER64
184
+ when BER::TIMETICKS, BER::COUNTER, BER::COUNTER32, BER::COUNTER64, BER::GAUGE, BER::GAUGE32
119
185
  variable.toLong
120
- when BER::INTEGER, BER::INTEGER32, BER::GAUGE, BER::GAUGE32
186
+ when BER::INTEGER, BER::INTEGER32
121
187
  variable.toInt
122
188
  when BER::IPADDRESS
123
189
  variable.toString
@@ -2,28 +2,19 @@ require "java"
2
2
  require "logstash-input-snmp_jars.rb"
3
3
  require_relative "base_client"
4
4
 
5
- java_import "org.snmp4j.CommunityTarget"
6
- java_import "org.snmp4j.PDU"
7
- java_import "org.snmp4j.ScopedPDU"
8
- java_import "org.snmp4j.Snmp"
9
- java_import "org.snmp4j.Target"
10
- java_import "org.snmp4j.TransportMapping"
11
- java_import "org.snmp4j.event.ResponseEvent"
12
- java_import "org.snmp4j.mp.SnmpConstants"
13
- java_import "org.snmp4j.smi.Address"
14
- java_import "org.snmp4j.smi.GenericAddress"
15
- java_import "org.snmp4j.smi.OID"
16
- java_import "org.snmp4j.smi.OctetString"
17
- java_import "org.snmp4j.smi.VariableBinding"
18
- java_import "org.snmp4j.transport.DefaultUdpTransportMapping"
19
- java_import "org.snmp4j.transport.DefaultTcpTransportMapping"
20
- java_import "org.snmp4j.util.TreeUtils"
21
- java_import "org.snmp4j.util.DefaultPDUFactory"
22
- java_import "org.snmp4j.asn1.BER"
23
-
24
5
  module LogStash
25
6
  class SnmpClient < BaseSnmpClient
26
7
 
8
+ java_import "org.snmp4j.CommunityTarget"
9
+ java_import "org.snmp4j.PDU"
10
+ java_import "org.snmp4j.ScopedPDU"
11
+ java_import "org.snmp4j.Snmp"
12
+ java_import "org.snmp4j.Target"
13
+ java_import "org.snmp4j.smi.Address"
14
+ java_import "org.snmp4j.smi.GenericAddress"
15
+ java_import "org.snmp4j.smi.OctetString"
16
+ java_import "org.snmp4j.util.DefaultPDUFactory"
17
+
27
18
  def initialize(protocol, address, port, community, version, retries, timeout, mib)
28
19
  super(protocol, address, port, retries, timeout, mib)
29
20
  raise(SnmpClientError, "SnmpClient is expecting verison '1' or '2c'") unless ["1", "2c"].include?(version.to_s)
@@ -1,43 +1,51 @@
1
1
  require 'java'
2
2
  require 'logstash-input-snmp_jars.rb'
3
3
 
4
+ module LogStash
4
5
 
5
- java_import 'org.snmp4j.PDU'
6
- java_import 'org.snmp4j.ScopedPDU'
7
- java_import 'org.snmp4j.Snmp'
8
- java_import 'org.snmp4j.Target'
9
- java_import 'org.snmp4j.TransportMapping'
10
- java_import 'org.snmp4j.event.ResponseEvent'
11
- java_import 'org.snmp4j.mp.MPv3'
12
- java_import 'org.snmp4j.mp.SnmpConstants'
13
- java_import 'org.snmp4j.security.AuthMD5'
14
- java_import 'org.snmp4j.security.AuthSHA'
15
- java_import 'org.snmp4j.security.AuthSHA2'
16
- java_import 'org.snmp4j.security.AuthHMAC128SHA224'
17
- java_import 'org.snmp4j.security.AuthHMAC192SHA256'
18
- java_import 'org.snmp4j.security.AuthHMAC256SHA384'
19
- java_import 'org.snmp4j.security.AuthHMAC384SHA512'
20
- java_import 'org.snmp4j.security.Priv3DES'
21
- java_import 'org.snmp4j.security.PrivDES'
22
- java_import 'org.snmp4j.security.PrivAES128'
23
- java_import 'org.snmp4j.security.PrivAES192'
24
- java_import 'org.snmp4j.security.PrivAES256'
25
- java_import 'org.snmp4j.security.SecurityModels'
26
- java_import 'org.snmp4j.security.SecurityProtocols'
27
- java_import 'org.snmp4j.security.USM'
28
- java_import 'org.snmp4j.security.UsmUser'
29
- java_import 'org.snmp4j.security.SecurityLevel'
30
- java_import 'org.snmp4j.smi.Address'
31
- java_import 'org.snmp4j.smi.GenericAddress'
32
- java_import 'org.snmp4j.smi.OID'
33
- java_import 'org.snmp4j.smi.OctetString'
34
- java_import 'org.snmp4j.transport.DefaultUdpTransportMapping'
35
- java_import 'org.snmp4j.transport.DefaultTcpTransportMapping'
36
- java_import 'org.snmp4j.UserTarget'
37
- java_import 'org.snmp4j.util.DefaultPDUFactory'
6
+ # Only a single USM instance should be created and added to the SecurityModels
7
+ class USMFactory
8
+ java_import 'org.snmp4j.security.SecurityProtocols'
9
+ java_import 'org.snmp4j.security.SecurityModels'
10
+ java_import 'org.snmp4j.security.USM'
11
+ java_import 'org.snmp4j.smi.OctetString'
12
+ java_import 'org.snmp4j.mp.MPv3'
13
+
14
+ include Singleton
15
+ attr_reader :usm
16
+
17
+ def initialize
18
+ @usm = USM.new(SecurityProtocols.getInstance, OctetString.new(MPv3.createLocalEngineID), 0)
19
+ SecurityModels.getInstance.addSecurityModel(@usm)
20
+ end
21
+ end
38
22
 
39
- module LogStash
40
23
  class SnmpClientV3 < BaseSnmpClient
24
+ java_import 'org.snmp4j.PDU'
25
+ java_import 'org.snmp4j.ScopedPDU'
26
+ java_import 'org.snmp4j.Snmp'
27
+ java_import 'org.snmp4j.Target'
28
+ java_import 'org.snmp4j.event.ResponseEvent'
29
+ java_import 'org.snmp4j.mp.SnmpConstants'
30
+ java_import 'org.snmp4j.security.AuthMD5'
31
+ java_import 'org.snmp4j.security.AuthSHA'
32
+ java_import 'org.snmp4j.security.AuthSHA2'
33
+ java_import 'org.snmp4j.security.AuthHMAC128SHA224'
34
+ java_import 'org.snmp4j.security.AuthHMAC192SHA256'
35
+ java_import 'org.snmp4j.security.AuthHMAC256SHA384'
36
+ java_import 'org.snmp4j.security.AuthHMAC384SHA512'
37
+ java_import 'org.snmp4j.security.Priv3DES'
38
+ java_import 'org.snmp4j.security.PrivDES'
39
+ java_import 'org.snmp4j.security.PrivAES128'
40
+ java_import 'org.snmp4j.security.PrivAES192'
41
+ java_import 'org.snmp4j.security.PrivAES256'
42
+ java_import 'org.snmp4j.security.UsmUser'
43
+ java_import 'org.snmp4j.security.SecurityLevel'
44
+ java_import 'org.snmp4j.smi.Address'
45
+ java_import 'org.snmp4j.smi.GenericAddress'
46
+ java_import 'org.snmp4j.smi.OctetString'
47
+ java_import 'org.snmp4j.UserTarget'
48
+ java_import 'org.snmp4j.util.DefaultPDUFactory'
41
49
 
42
50
  def initialize(protocol, address, port, retries, timeout, mib, security_name, auth_protocol, auth_pass, priv_protocol, priv_pass, security_level)
43
51
  super(protocol, address, port, retries, timeout, mib)
@@ -49,8 +57,8 @@ module LogStash
49
57
  auth_pass = auth_pass.nil? ? nil : OctetString.new(auth_pass)
50
58
  priv_pass = priv_pass.nil? ? nil : OctetString.new(priv_pass)
51
59
 
52
- usm = USM.new(SecurityProtocols.getInstance, OctetString.new(MPv3.createLocalEngineID), 0)
53
- SecurityModels.getInstance.addSecurityModel(usm)
60
+ # make sure the USM is initialized
61
+ USMFactory.instance
54
62
 
55
63
  @snmp.getUSM.addUser(UsmUser.new(security_name, auth_protocol, auth_pass, priv_protocol, priv_pass))
56
64
  @target = build_target("#{protocol}:#{address}/#{port}", security_name, security_level, retries, timeout)
@@ -69,7 +69,7 @@ module LogStash
69
69
  current.childs[last_node] = node
70
70
  end
71
71
 
72
- def map_oid(oid, strip_root = 0)
72
+ def map_oid(oid, strip_root = 0, path_length = 0)
73
73
  path = Oid.parse(oid)
74
74
 
75
75
  result = []
@@ -88,7 +88,13 @@ module LogStash
88
88
  result << node.name
89
89
  end
90
90
 
91
- result.drop(strip_root).join(".")
91
+ if strip_root > 0
92
+ result.drop(strip_root).join(".")
93
+ elsif path_length > 0
94
+ result.pop(path_length).join(".")
95
+ else
96
+ result.join(".")
97
+ end
92
98
  end
93
99
  end
94
100
 
@@ -136,8 +142,8 @@ module LogStash
136
142
  [module_name, nodes]
137
143
  end
138
144
 
139
- def map_oid(oid, strip_root = 0)
140
- @tree.map_oid(oid, strip_root)
145
+ def map_oid(oid, strip_root = 0, path_length = 0)
146
+ @tree.map_oid(oid, strip_root, path_length)
141
147
  end
142
148
 
143
149
  private
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-input-snmp'
3
- s.version = '1.0.1'
3
+ s.version = '1.2.3'
4
4
  s.licenses = ['Apache-2.0']
5
5
  s.summary = "SNMP input plugin"
6
6
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -2,11 +2,14 @@
2
2
  require "logstash/devutils/rspec/spec_helper"
3
3
  require "logstash/inputs/snmp/base_client"
4
4
 
5
- java_import "org.snmp4j.smi.AbstractVariable"
6
- java_import "org.snmp4j.smi.SMIConstants"
7
-
8
5
  module LogStash
9
6
 
7
+ java_import "org.snmp4j.asn1.BER"
8
+ java_import "org.snmp4j.smi.AbstractVariable"
9
+ java_import "org.snmp4j.smi.SMIConstants"
10
+ java_import "org.snmp4j.smi.Gauge32"
11
+ java_import "org.snmp4j.smi.Integer32"
12
+
10
13
  class TestableBaseSnmpClient < BaseSnmpClient
11
14
  def coerce(*args)
12
15
  super(*args)
@@ -33,6 +36,18 @@ module LogStash
33
36
  expect(subject).to receive(:logger).exactly(1).times.and_call_original
34
37
  expect(subject.coerce(v)).to eq("error: unknown variable syntax 130, EndOfMibView")
35
38
  end
39
+
40
+ it "should handle max unsigned 32 bits integer GAUGE32" do
41
+ MAX_UNSIGNED_INT_32 = 4294967295
42
+ v = Gauge32.new(MAX_UNSIGNED_INT_32)
43
+ expect(subject.coerce(v)).to eq(MAX_UNSIGNED_INT_32)
44
+ end
45
+
46
+ it "should handle max signed 32 bits integer INTEGER32" do
47
+ MAX_SIGNED_INT_32 = 2147483647
48
+ v = Integer32.new(MAX_SIGNED_INT_32)
49
+ expect(subject.coerce(v)).to eq(MAX_SIGNED_INT_32)
50
+ end
36
51
  end
37
52
  end
38
- end
53
+ end
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/devutils/rspec/shared_examples"
3
4
  require "logstash/inputs/snmp"
4
5
 
5
6
  describe LogStash::Inputs::Snmp do
@@ -22,8 +23,12 @@ describe LogStash::Inputs::Snmp do
22
23
  [
23
24
  {"get" => ["1.3.6.1.2.1.1.1.0"], "hosts" => [{"host" => "udp:127.0.0.1/161"}]},
24
25
  {"get" => [".1.3.6.1.2.1.1.1.0"], "hosts" => [{"host" => "udp:127.0.0.1/161"}]},
26
+ {"get" => [".1.3.6.1.2.1.1.1.0"], "hosts" => [{"host" => "udp:127.0.0.1/161"}], "oid_root_skip" => 2},
27
+ {"get" => [".1.3.6.1.2.1.1.1.0"], "hosts" => [{"host" => "udp:127.0.0.1/161"}], "oid_path_length" => 2},
25
28
  {"get" => ["1.3.6.1.2.1.1.1.0", "1.3.6.1.2.1.1"], "hosts" => [{"host" => "udp:127.0.0.1/161"}]},
26
29
  {"get" => ["1.3.6.1.2.1.1.1.0", ".1.3.6.1.2.1.1"], "hosts" => [{"host" => "udp:127.0.0.1/161"}]},
30
+ {"walk" => ["1.3.6.1.2.1.1.1"], "hosts" => [{"host" => "udp:127.0.0.1/161"}]},
31
+ {"tables" => [{"name" => "ltmPoolStatTable", "columns" => ["1.3.6.1.4.1.3375.2.2.5.2.3.1.1", "1.3.6.1.4.1.3375.2.2.5.2.3.1.6"]}], "hosts" => [{"host" => "udp:127.0.0.1/161"}]},
27
32
  ]
28
33
  }
29
34
 
@@ -33,6 +38,10 @@ describe LogStash::Inputs::Snmp do
33
38
  {"get" => ["test"], "hosts" => [{"host" => "udp:127.0.0.1/161"}]},
34
39
  {"get" => [], "hosts" => [{"host" => "udp:127.0.0.1/161"}]},
35
40
  {"get" => "foo", "hosts" => [{"host" => "udp:127.0.0.1/161"}]},
41
+ {"get" => [".1.3.6.1.2.1.1.1.0"], "hosts" => [{"host" => "udp:127.0.0.1/161"}], "oid_path_length" => "a" },
42
+ {"get" => [".1.3.6.1.2.1.1.1.0"], "hosts" => [{"host" => "udp:127.0.0.1/161"}], "oid_path_length" => 2, "oid_root_skip" => 2 },
43
+ {"walk" => "foo", "hosts" => [{"host" => "udp:127.0.0.1/161"}]},
44
+ {"tables" => [{"columns" => ["1.2.3.4", "4.3.2.1"]}], "hosts" => [{"host" => "udp:127.0.0.1/161"}]},
36
45
  ]
37
46
  }
38
47
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-snmp
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elasticsearch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-05 00:00:00.000000000 Z
11
+ date: 2020-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement