logstash-input-snmp 1.2.5 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -4
- data/CONTRIBUTORS +1 -0
- data/docs/index.asciidoc +106 -48
- data/lib/logstash/inputs/snmp/client.rb +1 -1
- data/lib/logstash/inputs/snmp.rb +104 -25
- data/logstash-input-snmp.gemspec +6 -2
- data/spec/inputs/integration/it_spec.rb +203 -0
- data/spec/inputs/snmp_spec.rb +151 -11
- metadata +56 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 747dbf68d6a90a7ab937bf8c64edef786b7d0c4c0ac2f97088401d1e842c2616
|
4
|
+
data.tar.gz: e53514a31810159796b3e80ee56f7aa6d66eba23ac781744fd9fbf0be83d4d9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa84badc45e056d01ca6a8668e3b5a2ddf89348339f49524b60b77843753cb7eb51fe2fcfe40d2d61dfc95523988f1fa6e11cc430692bbbe38d0014371c62278
|
7
|
+
data.tar.gz: 9e8e9895750a23892e99b2c198eef3e66f75de885e700dcea020d00356e843a022bd5dae6894a390f9f64faea40ea0f15310dbc7b34ef1fa96947b15d9d1a45b
|
data/CHANGELOG.md
CHANGED
@@ -1,14 +1,27 @@
|
|
1
|
+
## 1.3.0
|
2
|
+
- Feat: ECS compliance + optional target [#99](https://github.com/logstash-plugins/logstash-input-snmp/pull/99)
|
3
|
+
- Internal: update to Gradle 7 [#102](https://github.com/logstash-plugins/logstash-input-snmp/pull/102)
|
4
|
+
|
5
|
+
## 1.2.8
|
6
|
+
- Fixed interval handling to only sleep off the _remainder_ of the interval (if any), and to log a helpful warning when crawling the hosts takes longer than the configured interval [#100](https://github.com/logstash-plugins/logstash-input-snmp/pull/100). Fixes [#61](https://github.com/logstash-plugins/logstash-input-snmp/issues/61).
|
7
|
+
|
8
|
+
## 1.2.7
|
9
|
+
- Added integration tests to ensure SNMP server and IPv6 connections [#90](https://github.com/logstash-plugins/logstash-input-snmp/issues/90). Fixes[#87](https://github.com/logstash-plugins/logstash-input-snmp/issues/87).
|
10
|
+
|
11
|
+
## 1.2.6
|
12
|
+
- Docs: example on setting IPv6 hosts [#89](https://github.com/logstash-plugins/logstash-input-snmp/pull/89)
|
13
|
+
|
1
14
|
## 1.2.5
|
2
|
-
-
|
15
|
+
- Updated snmp4j library to v2.8.4 [#86](https://github.com/logstash-plugins/logstash-input-snmp/pull/86)
|
3
16
|
|
4
17
|
## 1.2.4
|
5
|
-
-
|
18
|
+
- Fixed: support SNMPv3 multiple identical security name with different credentials [#84](https://github.com/logstash-plugins/logstash-input-snmp/pull/84)
|
6
19
|
|
7
20
|
## 1.2.3
|
8
|
-
-
|
21
|
+
- Fixed: multithreading problem when using multiple snmp inputs with multiple v3 credentials [#80](https://github.com/logstash-plugins/logstash-input-snmp/pull/80)
|
9
22
|
|
10
23
|
## 1.2.2
|
11
|
-
-
|
24
|
+
- Refactor: scope and review java_imports [#72](https://github.com/logstash-plugins/logstash-input-snmp/pull/72)
|
12
25
|
|
13
26
|
## 1.2.1
|
14
27
|
- Fixed GAUGE32 integer overflow [#65](https://github.com/logstash-plugins/logstash-input-snmp/pull/65)
|
data/CONTRIBUTORS
CHANGED
@@ -4,6 +4,7 @@ reports, or in general have helped logstash along its way.
|
|
4
4
|
Contributors:
|
5
5
|
* Colin Surprenant - colin.surprenant@gmail.com
|
6
6
|
* Dan Major - axrayn@gmail.com
|
7
|
+
* Patrick Prugger - pprugger@gmx.at
|
7
8
|
|
8
9
|
Note: If you've sent us patches, bug reports, or otherwise contributed to
|
9
10
|
Logstash, and you aren't on the list above and want to be, please let us know
|
data/docs/index.asciidoc
CHANGED
@@ -26,6 +26,22 @@ to gather information related to the current state of the devices operation.
|
|
26
26
|
|
27
27
|
The SNMP input plugin supports SNMP v1, v2c, and v3 over UDP and TCP transport protocols.
|
28
28
|
|
29
|
+
[id="plugins-{type}s-{plugin}-ecs"]
|
30
|
+
==== Compatibility with the Elastic Common Schema (ECS)
|
31
|
+
|
32
|
+
Because SNMP data has specific field names based on OIDs, we recommend setting a <<plugins-{type}s-{plugin}-target>>.
|
33
|
+
Metadata fields follow a specific naming convention when <<plugins-{type}s-{plugin}-ecs_compatibility,ECS compatibility mode>> is enabled.
|
34
|
+
|
35
|
+
[cols="<l,<l,e,<e"]
|
36
|
+
|=======================================================================
|
37
|
+
|ECS disabled |ECS v1, v8 |Description
|
38
|
+
|[@metadata][host_protocol] |[@metadata][input][snmp][host][protocol] |The protocol used to retrieve data e.g. "udp"
|
39
|
+
|[@metadata][host_address] |[@metadata][input][snmp][host][address] |The host IP e.g. "192.168.1.1"
|
40
|
+
|[@metadata][host_port] |[@metadata][input][snmp][host][port] |The host's port e.g. "161"
|
41
|
+
|[@metadata][host_community] |[@metadata][input][snmp][host][community] |The configured community e.g. "public"
|
42
|
+
|[host] |[host][ip] |Same as `[@metadata][host_address]`, host's IP address
|
43
|
+
|=======================================================================
|
44
|
+
|
29
45
|
[id="plugins-{type}s-{plugin}-import-mibs"]
|
30
46
|
==== Importing MIBs
|
31
47
|
|
@@ -55,6 +71,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
|
|
55
71
|
[cols="<,<,<",options="header",]
|
56
72
|
|=======================================================================
|
57
73
|
|Setting |Input type|Required
|
74
|
+
| <<plugins-{type}s-{plugin}-ecs_compatibility>> |<<string,string>>|No
|
58
75
|
| <<plugins-{type}s-{plugin}-get>> |<<array,array>>|No
|
59
76
|
| <<plugins-{type}s-{plugin}-hosts>> |<<array,array>>|No
|
60
77
|
| <<plugins-{type}s-{plugin}-interval>> |<<number,number>>|No
|
@@ -63,6 +80,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
|
|
63
80
|
| <<plugins-{type}s-{plugin}-oid_path_length>> |<<number,number>>|No
|
64
81
|
| <<plugins-{type}s-{plugin}-walk>> |<<array,array>>|No
|
65
82
|
| <<plugins-{type}s-{plugin}-tables>> |<<array,array>>|No
|
83
|
+
| <<plugins-{type}s-{plugin}-target>> |<<string,string>>|No
|
66
84
|
|=======================================================================
|
67
85
|
|
68
86
|
==== SNMPv3 Authentication Options
|
@@ -86,14 +104,14 @@ Also see <<plugins-{type}s-{plugin}-common-options>> for a list of options suppo
|
|
86
104
|
input plugins.
|
87
105
|
|
88
106
|
[id="plugins-{type}s-{plugin}-get"]
|
89
|
-
===== `get`
|
107
|
+
===== `get`
|
108
|
+
|
109
|
+
* Value type is <<array,array>>
|
110
|
+
* There is no default value for this setting
|
90
111
|
|
91
112
|
Use the `get` option to query for scalar values for the given OID(s).
|
92
113
|
One or more OID(s) are specified as an array of strings of OID(s).
|
93
114
|
|
94
|
-
* Value type is <<array,array>>
|
95
|
-
* There is no default value for this setting
|
96
|
-
|
97
115
|
Example
|
98
116
|
[source,ruby]
|
99
117
|
-----
|
@@ -108,14 +126,14 @@ input {
|
|
108
126
|
[id="plugins-{type}s-{plugin}-hosts"]
|
109
127
|
===== `hosts`
|
110
128
|
|
129
|
+
* Value type is <<array,array>>
|
130
|
+
* There is no default value for this setting
|
131
|
+
|
111
132
|
The `hosts` option specifies the list of hosts to query the configured `get` and `walk` options.
|
112
133
|
|
113
134
|
Each host definition is a hash and must define the `host` key and value.
|
114
135
|
`host` must use the format `{tcp|udp}:{ip address}/{port}`, for example `host => "udp:127.0.0.1/161"`
|
115
136
|
|
116
|
-
* Value type is <<array,array>>
|
117
|
-
* There is no default value for this setting
|
118
|
-
|
119
137
|
Each host definition can optionally include the following keys and values:
|
120
138
|
|
121
139
|
* `community` the community string, default is `public`.
|
@@ -147,57 +165,71 @@ input {
|
|
147
165
|
}
|
148
166
|
-----
|
149
167
|
|
168
|
+
*Specifying IPv6 hosts*
|
169
|
+
|
170
|
+
[source,ruby]
|
171
|
+
-----
|
172
|
+
input {
|
173
|
+
snmp {
|
174
|
+
get => ["1.3.6.1.2.1.1.1.0"]
|
175
|
+
hosts => [{host => "udp:[::1]/161" community => "public"}, {host => "udp:[2001:db8::2:1]/161" community => "private"}]
|
176
|
+
}
|
177
|
+
}
|
178
|
+
-----
|
179
|
+
|
150
180
|
[id="plugins-{type}s-{plugin}-interval"]
|
151
|
-
===== `interval`
|
181
|
+
===== `interval`
|
152
182
|
|
153
|
-
|
183
|
+
* Value type is <<number,number>>
|
184
|
+
* Default value is `30`
|
154
185
|
|
155
|
-
|
156
|
-
|
186
|
+
The `interval` option specifies the polling interval in seconds.
|
187
|
+
If polling all configured hosts takes longer than this interval, a warning will be emitted to the logs.
|
157
188
|
|
158
189
|
[id="plugins-{type}s-{plugin}-mib_paths"]
|
159
|
-
===== `mib_paths`
|
190
|
+
===== `mib_paths`
|
160
191
|
|
161
|
-
|
162
|
-
|
192
|
+
* Value type is <<path,path>>
|
193
|
+
* There is no default value for this setting
|
163
194
|
|
164
|
-
|
165
|
-
|
195
|
+
The `mib_paths` option specifies the location of one or more imported MIB files.
|
196
|
+
The value can be either a dir path containing the imported MIB `.dic` files or a
|
197
|
+
file path to a single MIB `.dic` file.
|
166
198
|
|
167
199
|
This plugin includes the IETF MIBs.
|
168
200
|
If you require other MIBs, you need to import them. See <<plugins-{type}s-{plugin}-import-mibs>>.
|
169
201
|
|
170
202
|
[id="plugins-{type}s-{plugin}-oid_root_skip"]
|
171
|
-
===== `oid_root_skip`
|
203
|
+
===== `oid_root_skip`
|
204
|
+
|
205
|
+
* Value type is <<number,number>>
|
206
|
+
* Default value is `0`
|
172
207
|
|
173
208
|
The `oid_root_skip` option specifies the number of OID root digits to ignore in the event field name.
|
174
209
|
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`
|
175
210
|
which would result in a field name "1.1.1.0". Similarly when a MIB is used an OID such
|
176
211
|
"1.3.6.1.2.mib-2.system.sysDescr.0" would become "mib-2.system.sysDescr.0"
|
177
212
|
|
178
|
-
* Value type is <<number,number>>
|
179
|
-
* Default value is `0`
|
180
|
-
|
181
213
|
[id="plugins-{type}s-{plugin}-oid_path_length"]
|
182
214
|
===== `oid_path_length`
|
183
215
|
|
216
|
+
* Value type is <<number,number>>
|
217
|
+
* Default value is `0`
|
218
|
+
|
184
219
|
The `oid_path_length` option specifies the number of OID root digits to retain in the event field name.
|
185
220
|
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
221
|
which would result in a field name "1.0". Similarly when a MIB is used an OID such
|
187
222
|
"1.3.6.1.2.mib-2.system.sysDescr.0" would become "sysDescr.0"
|
188
223
|
|
189
|
-
* Value type is <<number,number>>
|
190
|
-
* Default value is `0`
|
191
|
-
|
192
224
|
[id="plugins-{type}s-{plugin}-walk"]
|
193
225
|
===== `walk`
|
194
226
|
|
227
|
+
* Value type is <<array,array>>
|
228
|
+
* There is no default value for this setting
|
229
|
+
|
195
230
|
Use the `walk` option to retrieve the subtree of information for the given OID(s).
|
196
231
|
One or more OID(s) are specified as an array of strings of OID(s).
|
197
232
|
|
198
|
-
* Value type is <<array,array>>
|
199
|
-
* There is no default value for this setting
|
200
|
-
|
201
233
|
Queries the subtree of information starting at the given OID(s).
|
202
234
|
|
203
235
|
Example
|
@@ -213,14 +245,14 @@ Example
|
|
213
245
|
[id="plugins-{type}s-{plugin}-tables"]
|
214
246
|
===== `tables`
|
215
247
|
|
248
|
+
* Value type is <<array,array>>
|
249
|
+
* There is no default value for this setting
|
250
|
+
* Results are returned under a field using the table name
|
251
|
+
|
216
252
|
The `tables` option is used to query for tabular values for the given column OID(s).
|
217
253
|
|
218
254
|
Each table definition is a hash and must define the name key and value and the columns to return.
|
219
255
|
|
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
256
|
*Specifying a single table*
|
225
257
|
|
226
258
|
[source,ruby]
|
@@ -254,10 +286,10 @@ These options are required only if you are using SNMPv3.
|
|
254
286
|
[id="plugins-{type}s-{plugin}-auth_pass"]
|
255
287
|
===== `auth_pass`
|
256
288
|
|
257
|
-
|
289
|
+
* Value type is <<password,password>>
|
290
|
+
* There is no default value for this setting
|
258
291
|
|
259
|
-
|
260
|
-
* There is no default value for this setting
|
292
|
+
The `auth_pass` option specifies the SNMPv3 authentication passphrase or password.
|
261
293
|
|
262
294
|
[id="plugins-{type}s-{plugin}-auth_protocol"]
|
263
295
|
===== `auth_protocol`
|
@@ -267,38 +299,66 @@ The `auth_protocol` option specifies the SNMPv3 authentication protocol or type
|
|
267
299
|
* Value can be any of: `md5`, `sha`, `sha2`, `hmac128sha224`, `hmac192sha256`, `hmac256sha384`, `hmac384sha512`
|
268
300
|
* There is no default value for this setting
|
269
301
|
|
302
|
+
[id="plugins-{type}s-{plugin}-ecs_compatibility"]
|
303
|
+
===== `ecs_compatibility`
|
304
|
+
|
305
|
+
* Value type is <<string,string>>
|
306
|
+
* Supported values are:
|
307
|
+
** `disabled`: does not use ECS-compatible field names (fields might be set at the root of the event)
|
308
|
+
** `v1`, `v8`: avoids field names that might conflict with Elastic Common Schema (for example, the `host` field)
|
309
|
+
* Default value depends on which version of Logstash is running:
|
310
|
+
** When Logstash provides a `pipeline.ecs_compatibility` setting, its value is used as the default
|
311
|
+
** Otherwise, the default value is `disabled`.
|
312
|
+
|
313
|
+
Controls this plugin's compatibility with the {ecs-ref}[Elastic Common Schema (ECS)].
|
314
|
+
|
270
315
|
[id="plugins-{type}s-{plugin}-priv_pass"]
|
271
316
|
===== `priv_pass`
|
272
317
|
|
273
|
-
|
318
|
+
* Value type is <<password,password>>
|
319
|
+
* There is no default value for this setting
|
274
320
|
|
275
|
-
|
276
|
-
* There is no default value for this setting
|
321
|
+
The `priv_pass` option specifies the SNMPv3 encryption password.
|
277
322
|
|
278
323
|
[id="plugins-{type}s-{plugin}-priv_protocol"]
|
279
324
|
===== `priv_protocol`
|
280
325
|
|
281
|
-
|
326
|
+
* Value can be any of: `des`, `3des`, `aes`, `aes128`, `aes192`, `aes256`
|
327
|
+
* Note that `aes` and `aes128` are equivalent
|
328
|
+
* There is no default value for this setting
|
282
329
|
|
283
|
-
|
284
|
-
* Note that `aes` and `aes128` are equivalent
|
285
|
-
* There is no default value for this setting
|
330
|
+
The `priv_protocol` option specifies the SNMPv3 privacy/encryption protocol.
|
286
331
|
|
287
332
|
[id="plugins-{type}s-{plugin}-security_name"]
|
288
333
|
===== `security_name`
|
289
334
|
|
290
|
-
|
335
|
+
* Value type is <<string,string>>
|
336
|
+
* There is no default value for this setting
|
291
337
|
|
292
|
-
|
293
|
-
* There is no default value for this setting
|
338
|
+
The `security_name` option specifies the SNMPv3 security name or user name.
|
294
339
|
|
295
340
|
[id="plugins-{type}s-{plugin}-security_level"]
|
296
341
|
===== `security_level`
|
297
342
|
|
298
|
-
|
343
|
+
* Value can be any of: `noAuthNoPriv`, `authNoPriv`, `authPriv`
|
344
|
+
* There is no default value for this setting
|
299
345
|
|
300
|
-
|
301
|
-
|
346
|
+
The `security_level` option specifies the SNMPv3 security level between
|
347
|
+
Authentication, No Privacy; Authentication, Privacy; or no Authentication, no Privacy.
|
348
|
+
|
349
|
+
[id="plugins-{type}s-{plugin}-target"]
|
350
|
+
===== `target`
|
351
|
+
|
352
|
+
* Value type is <<string,string>>
|
353
|
+
* There is no default value for this setting
|
354
|
+
|
355
|
+
The name of the field under which SNMP payloads are assigned.
|
356
|
+
If not specified data will be stored in the root of the event.
|
357
|
+
|
358
|
+
Setting a target is recommended when <<plugins-{type}s-{plugin}-ecs_compatibility>> is enabled.
|
359
|
+
|
360
|
+
[id="plugins-{type}s-{plugin}-examples"]
|
361
|
+
==== Configuration examples
|
302
362
|
|
303
363
|
*Specifying SNMPv3 settings*
|
304
364
|
|
@@ -319,8 +379,6 @@ input {
|
|
319
379
|
|
320
380
|
-----
|
321
381
|
|
322
|
-
==== More configuration examples
|
323
|
-
|
324
382
|
*Using both `get` and `walk` in the same poll cycle for each host(s)*
|
325
383
|
|
326
384
|
[source,ruby]
|
@@ -17,7 +17,7 @@ module LogStash
|
|
17
17
|
|
18
18
|
def initialize(protocol, address, port, community, version, retries, timeout, mib)
|
19
19
|
super(protocol, address, port, retries, timeout, mib)
|
20
|
-
raise(SnmpClientError, "SnmpClient is expecting
|
20
|
+
raise(SnmpClientError, "SnmpClient is expecting version '1' or '2c'") unless ["1", "2c"].include?(version.to_s)
|
21
21
|
|
22
22
|
@snmp = Snmp.new(create_transport(protocol))
|
23
23
|
@snmp.listen
|
data/lib/logstash/inputs/snmp.rb
CHANGED
@@ -7,11 +7,24 @@ require_relative "snmp/client"
|
|
7
7
|
require_relative "snmp/clientv3"
|
8
8
|
require_relative "snmp/mib"
|
9
9
|
|
10
|
+
require 'logstash/plugin_mixins/ecs_compatibility_support'
|
11
|
+
require 'logstash/plugin_mixins/ecs_compatibility_support/target_check'
|
12
|
+
require 'logstash/plugin_mixins/event_support/event_factory_adapter'
|
13
|
+
require 'logstash/plugin_mixins/validator_support/field_reference_validation_adapter'
|
14
|
+
|
10
15
|
# Generate a repeating message.
|
11
16
|
#
|
12
17
|
# This plugin is intented only as an example.
|
13
18
|
|
14
19
|
class LogStash::Inputs::Snmp < LogStash::Inputs::Base
|
20
|
+
|
21
|
+
include LogStash::PluginMixins::ECSCompatibilitySupport(:disabled, :v1, :v8 => :v1)
|
22
|
+
include LogStash::PluginMixins::ECSCompatibilitySupport::TargetCheck
|
23
|
+
|
24
|
+
include LogStash::PluginMixins::EventSupport::EventFactoryAdapter
|
25
|
+
|
26
|
+
extend LogStash::PluginMixins::ValidatorSupport::FieldReferenceValidationAdapter
|
27
|
+
|
15
28
|
config_name "snmp"
|
16
29
|
|
17
30
|
# List of OIDs for which we want to retrieve the scalar value
|
@@ -67,9 +80,6 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
|
|
67
80
|
# The default, `30`, means poll each host every 30 seconds.
|
68
81
|
config :interval, :validate => :number, :default => 30
|
69
82
|
|
70
|
-
# Add the default "host" field to the event.
|
71
|
-
config :add_field, :validate => :hash, :default => { "host" => "%{[@metadata][host_address]}" }
|
72
|
-
|
73
83
|
# SNMPv3 Credentials
|
74
84
|
#
|
75
85
|
# A single user can be configured and will be used for all defined SNMPv3 hosts.
|
@@ -94,9 +104,29 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
|
|
94
104
|
# The SNMPv3 security level can be Authentication, No Privacy; Authentication, Privacy; or no Authentication, no Privacy
|
95
105
|
config :security_level, :validate => ["noAuthNoPriv", "authNoPriv", "authPriv"]
|
96
106
|
|
107
|
+
# Defines a target field for placing fields.
|
108
|
+
# If this setting is omitted, data gets stored at the root (top level) of the event.
|
109
|
+
# The target is only relevant while decoding data into a new event.
|
110
|
+
config :target, :validate => :field_reference
|
111
|
+
|
97
112
|
BASE_MIB_PATH = ::File.join(__FILE__, "..", "..", "..", "mibs")
|
98
113
|
PROVIDED_MIB_PATHS = [::File.join(BASE_MIB_PATH, "logstash"), ::File.join(BASE_MIB_PATH, "ietf")].map { |path| ::File.expand_path(path) }
|
99
114
|
|
115
|
+
def initialize(params={})
|
116
|
+
super(params)
|
117
|
+
|
118
|
+
@host_protocol_field = ecs_select[disabled: '[@metadata][host_protocol]', v1: '[@metadata][input][snmp][host][protocol]']
|
119
|
+
@host_address_field = ecs_select[disabled: '[@metadata][host_address]', v1: '[@metadata][input][snmp][host][address]']
|
120
|
+
@host_port_field = ecs_select[disabled: '[@metadata][host_port]', v1: '[@metadata][input][snmp][host][port]']
|
121
|
+
@host_community_field = ecs_select[disabled: '[@metadata][host_community]', v1: '[@metadata][input][snmp][host][community]']
|
122
|
+
|
123
|
+
# Add the default "host" field to the event, for backwards compatibility, or host.ip in ecs mode
|
124
|
+
unless params.key?('add_field')
|
125
|
+
host_ip_field = ecs_select[disabled: "host", v1: "[host][ip]"]
|
126
|
+
@add_field = { host_ip_field => "%{#{@host_address_field}}" }
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
100
130
|
def register
|
101
131
|
validate_oids!
|
102
132
|
validate_hosts!
|
@@ -161,57 +191,62 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
|
|
161
191
|
end
|
162
192
|
|
163
193
|
def run(queue)
|
164
|
-
# for now a naive single threaded poller which sleeps
|
194
|
+
# for now a naive single threaded poller which sleeps off the remaining interval between
|
165
195
|
# each run. each run polls all the defined hosts for the get and walk options.
|
166
|
-
|
196
|
+
stoppable_interval_runner.every(@interval, "polling hosts") do
|
167
197
|
@client_definitions.each do |definition|
|
198
|
+
client = definition[:client]
|
168
199
|
result = {}
|
169
200
|
if !definition[:get].empty?
|
201
|
+
oids = definition[:get]
|
170
202
|
begin
|
171
|
-
result = result.merge(
|
203
|
+
result = result.merge(client.get(oids, @oid_root_skip, @oid_path_length))
|
172
204
|
rescue => e
|
173
|
-
logger.error("error invoking get operation
|
205
|
+
logger.error("error invoking get operation for OIDs: #{oids}, ignoring",
|
206
|
+
host: definition[:host_address], exception: e, backtrace: e.backtrace)
|
174
207
|
end
|
175
208
|
end
|
176
|
-
if
|
209
|
+
if !definition[:walk].empty?
|
177
210
|
definition[:walk].each do |oid|
|
178
211
|
begin
|
179
|
-
result = result.merge(
|
212
|
+
result = result.merge(client.walk(oid, @oid_root_skip, @oid_path_length))
|
180
213
|
rescue => e
|
181
|
-
logger.error("error invoking walk operation on OID: #{oid}, ignoring",
|
214
|
+
logger.error("error invoking walk operation on OID: #{oid}, ignoring",
|
215
|
+
host: definition[:host_address], exception: e, backtrace: e.backtrace)
|
182
216
|
end
|
183
217
|
end
|
184
218
|
end
|
185
219
|
|
186
|
-
if
|
220
|
+
if !Array(@tables).empty?
|
187
221
|
@tables.each do |table_entry|
|
188
222
|
begin
|
189
|
-
result = result.merge(
|
223
|
+
result = result.merge(client.table(table_entry, @oid_root_skip, @oid_path_length))
|
190
224
|
rescue => e
|
191
|
-
logger.error("error invoking table operation on OID: #{table_entry['name']}, ignoring",
|
225
|
+
logger.error("error invoking table operation on OID: #{table_entry['name']}, ignoring",
|
226
|
+
host: definition[:host_address], exception: e, backtrace: e.backtrace)
|
192
227
|
end
|
193
228
|
end
|
194
229
|
end
|
195
230
|
|
196
231
|
unless result.empty?
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
}
|
203
|
-
result["@metadata"] = metadata
|
204
|
-
|
205
|
-
event = LogStash::Event.new(result)
|
232
|
+
event = targeted_event_factory.new_event(result)
|
233
|
+
event.set(@host_protocol_field, definition[:host_protocol])
|
234
|
+
event.set(@host_address_field, definition[:host_address])
|
235
|
+
event.set(@host_port_field, definition[:host_port])
|
236
|
+
event.set(@host_community_field, definition[:host_community])
|
206
237
|
decorate(event)
|
207
238
|
queue << event
|
239
|
+
else
|
240
|
+
logger.debug? && logger.debug("no snmp data retrieved", host: definition[:host_address])
|
208
241
|
end
|
209
242
|
end
|
210
|
-
|
211
|
-
Stud.stoppable_sleep(@interval) { stop? }
|
212
243
|
end
|
213
244
|
end
|
214
245
|
|
246
|
+
def stoppable_interval_runner
|
247
|
+
StoppableIntervalRunner.new(self)
|
248
|
+
end
|
249
|
+
|
215
250
|
def close
|
216
251
|
@client_definitions.each do |definition|
|
217
252
|
begin
|
@@ -222,10 +257,13 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
|
|
222
257
|
end
|
223
258
|
end
|
224
259
|
|
260
|
+
def stop
|
261
|
+
end
|
262
|
+
|
225
263
|
private
|
226
264
|
|
227
265
|
OID_REGEX = /^\.?([0-9\.]+)$/
|
228
|
-
HOST_REGEX = /^(?<host_protocol
|
266
|
+
HOST_REGEX = /^(?<host_protocol>\w+):(?<host_address>.+)\/(?<host_port>\d+)$/i
|
229
267
|
VERSION_REGEX =/^1|2c|3$/
|
230
268
|
|
231
269
|
def validate_oids!
|
@@ -295,4 +333,45 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
|
|
295
333
|
def validate_strip!
|
296
334
|
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
|
297
335
|
end
|
336
|
+
|
337
|
+
##
|
338
|
+
# The StoppableIntervalRunner is capable of running a block of code at a
|
339
|
+
# repeating interval, while respecting the stop condition of the plugin.
|
340
|
+
class StoppableIntervalRunner
|
341
|
+
##
|
342
|
+
# @param plugin [#logger,#stop?]
|
343
|
+
def initialize(plugin)
|
344
|
+
@plugin = plugin
|
345
|
+
end
|
346
|
+
|
347
|
+
##
|
348
|
+
# Runs the provided block repeatedly using the provided interval.
|
349
|
+
# After executing the block, the remainder of the interval if any is slept off
|
350
|
+
# using an interruptible sleep.
|
351
|
+
# If no time remains, a warning is emitted to the logs.
|
352
|
+
#
|
353
|
+
# @param interval_seconds [Integer,Float]
|
354
|
+
# @param desc [String] (default: "operation"): a description to use when logging
|
355
|
+
# @yield
|
356
|
+
def every(interval_seconds, desc="operation", &block)
|
357
|
+
until @plugin.stop?
|
358
|
+
start_time = Time.now
|
359
|
+
|
360
|
+
yield
|
361
|
+
|
362
|
+
duration_seconds = Time.now - start_time
|
363
|
+
if duration_seconds >= interval_seconds
|
364
|
+
@plugin.logger.warn("#{desc} took longer than the configured interval", :interval_seconds => interval_seconds, :duration_seconds => duration_seconds.round(3))
|
365
|
+
else
|
366
|
+
remaining_interval = interval_seconds - duration_seconds
|
367
|
+
sleep(remaining_interval)
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# @api private
|
373
|
+
def sleep(duration)
|
374
|
+
Stud.stoppable_sleep(duration) { @plugin.stop? }
|
375
|
+
end
|
376
|
+
end
|
298
377
|
end
|
data/logstash-input-snmp.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'logstash-input-snmp'
|
3
|
-
s.version = '1.
|
3
|
+
s.version = '1.3.0'
|
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"
|
@@ -20,8 +20,12 @@ Gem::Specification.new do |s|
|
|
20
20
|
|
21
21
|
# Gem dependencies
|
22
22
|
s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
|
23
|
-
s.add_runtime_dependency '
|
23
|
+
s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~> 1.3'
|
24
|
+
s.add_runtime_dependency 'logstash-mixin-event_support', '~> 1.0'
|
25
|
+
s.add_runtime_dependency 'logstash-mixin-validator_support', '~> 1.0'
|
24
26
|
s.add_runtime_dependency 'logstash-codec-plain'
|
27
|
+
s.add_runtime_dependency 'stud', '>= 0.0.22', '< 0.1.0'
|
28
|
+
|
25
29
|
s.add_development_dependency 'logstash-devutils'
|
26
30
|
s.add_development_dependency 'rspec-wait'
|
27
31
|
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require "logstash/devutils/rspec/spec_helper"
|
2
|
+
require "logstash/inputs/snmp"
|
3
|
+
|
4
|
+
describe LogStash::Inputs::Snmp do
|
5
|
+
let(:config) { {"get" => ["1.3.6.1.2.1.1.1.0", "1.3.6.1.2.1.1.3.0", "1.3.6.1.2.1.1.5.0"]} }
|
6
|
+
let(:plugin) { LogStash::Inputs::Snmp.new(config)}
|
7
|
+
|
8
|
+
shared_examples "snmp plugin return single event" do
|
9
|
+
it "should have OID value" do
|
10
|
+
plugin.register
|
11
|
+
queue = []
|
12
|
+
stop_plugin_after_seconds(plugin)
|
13
|
+
plugin.run(queue)
|
14
|
+
plugin.close
|
15
|
+
event = queue.pop
|
16
|
+
|
17
|
+
expect(event).to be_a(LogStash::Event)
|
18
|
+
expect(event.get("iso.org.dod.internet.mgmt.mib-2.system.sysUpTime.sysUpTimeInstance")).to be_a Integer
|
19
|
+
expect(event.get("iso.org.dod.internet.mgmt.mib-2.system.sysName.0")).to be_a String
|
20
|
+
expect(event.get("iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0")).to be_a String
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
shared_examples "snmp plugin return one udp event and one tcp event" do |config|
|
25
|
+
it "should have one udp from snmp1 and one tcp from snmp2" do
|
26
|
+
events = input(config) { |_, queue| 2.times.collect { queue.pop } }
|
27
|
+
udp = 0; tcp = 0
|
28
|
+
events.each { |event|
|
29
|
+
if event.get("[@metadata][host_protocol]") == "udp"
|
30
|
+
udp += 1
|
31
|
+
expect(event.get("[@metadata][host_protocol]")).to eq("udp")
|
32
|
+
expect(event.get("[@metadata][host_address]")).to eq("snmp1")
|
33
|
+
expect(event.get("[@metadata][host_port]")).to eq("161")
|
34
|
+
else
|
35
|
+
tcp += 1
|
36
|
+
expect(event.get("[@metadata][host_protocol]")).to eq("tcp")
|
37
|
+
expect(event.get("[@metadata][host_address]")).to eq("snmp2")
|
38
|
+
expect(event.get("[@metadata][host_port]")).to eq("162")
|
39
|
+
end
|
40
|
+
}
|
41
|
+
expect(udp).to eq(1)
|
42
|
+
expect(tcp).to eq(1)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "against single snmp server with snmpv2 and udp", :integration => true do
|
47
|
+
let(:config) { super().merge({"hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}]})}
|
48
|
+
it_behaves_like "snmp plugin return single event"
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "against single server with snmpv3 and tcp", :integration => true do
|
52
|
+
let(:config) { super().merge({
|
53
|
+
"hosts" => [{"host" => "tcp:snmp1/161", "version" => "3"}],
|
54
|
+
"security_name" => "user_1",
|
55
|
+
"auth_protocol" => "sha",
|
56
|
+
"auth_pass" => "STrP@SSPhr@sE",
|
57
|
+
"priv_protocol" => "aes",
|
58
|
+
"priv_pass" => "STr0ngP@SSWRD161",
|
59
|
+
"security_level" => "authPriv"
|
60
|
+
})}
|
61
|
+
|
62
|
+
it_behaves_like "snmp plugin return single event"
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "invalid user against snmpv3 server", :integration => true do
|
66
|
+
let(:config) { super().merge({
|
67
|
+
"hosts" => [{"host" => "tcp:snmp1/161", "version" => "3"}],
|
68
|
+
"security_name" => "user_2",
|
69
|
+
"auth_protocol" => "sha",
|
70
|
+
"auth_pass" => "STrP@SSPhr@sE",
|
71
|
+
"priv_protocol" => "aes",
|
72
|
+
"priv_pass" => "STr0ngP@SSWRD161",
|
73
|
+
"security_level" => "authPriv"
|
74
|
+
})}
|
75
|
+
|
76
|
+
it "should have error log" do
|
77
|
+
expect(plugin.logger).to receive(:error).once
|
78
|
+
plugin.register
|
79
|
+
queue = []
|
80
|
+
stop_plugin_after_seconds(plugin)
|
81
|
+
plugin.run(queue)
|
82
|
+
plugin.close
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "single input plugin on single server with snmpv2 and mix of udp and tcp", :integration => true do
|
87
|
+
let(:config) { super().merge({"hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}, {"host" => "tcp:snmp1/161", "community" => "public"}]})}
|
88
|
+
it "should return two events " do
|
89
|
+
plugin.register
|
90
|
+
queue = []
|
91
|
+
stop_plugin_after_seconds(plugin)
|
92
|
+
plugin.run(queue)
|
93
|
+
plugin.close
|
94
|
+
|
95
|
+
host_cnt_snmp1 = queue.select {|event| event.get("host") == "snmp1"}.size
|
96
|
+
expect(queue.size).to eq(2)
|
97
|
+
expect(host_cnt_snmp1).to eq(2)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "single input plugin on multiple udp hosts", :integration => true do
|
102
|
+
let(:config) { super().merge({"hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}, {"host" => "udp:snmp2/162", "community" => "public"}]})}
|
103
|
+
it "should return two events, one per host" do
|
104
|
+
plugin.register
|
105
|
+
queue = []
|
106
|
+
stop_plugin_after_seconds(plugin)
|
107
|
+
plugin.run(queue)
|
108
|
+
plugin.close
|
109
|
+
|
110
|
+
hosts = queue.map { |event| event.get("host") }.sort
|
111
|
+
expect(queue.size).to eq(2)
|
112
|
+
expect(hosts).to eq(["snmp1", "snmp2"])
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "multiple pipelines and mix of udp tcp hosts", :integration => true do
|
117
|
+
let(:config) { {"get" => ["1.3.6.1.2.1.1.1.0"], "hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}]} }
|
118
|
+
let(:config2) { {"get" => ["1.3.6.1.2.1.1.1.0"], "hosts" => [{"host" => "tcp:snmp2/162", "community" => "public"}]} }
|
119
|
+
let(:plugin) { LogStash::Inputs::Snmp.new(config)}
|
120
|
+
let(:plugin2) { LogStash::Inputs::Snmp.new(config2)}
|
121
|
+
|
122
|
+
it "should return two events, one per host" do
|
123
|
+
plugin.register
|
124
|
+
plugin2.register
|
125
|
+
queue = []
|
126
|
+
queue2 = []
|
127
|
+
t = Thread.new {
|
128
|
+
stop_plugin_after_seconds(plugin)
|
129
|
+
plugin.run(queue)
|
130
|
+
}
|
131
|
+
t2 = Thread.new {
|
132
|
+
stop_plugin_after_seconds(plugin2)
|
133
|
+
plugin2.run(queue2)
|
134
|
+
}
|
135
|
+
t.join(2100)
|
136
|
+
t2.join(2100)
|
137
|
+
plugin.close
|
138
|
+
plugin2.close
|
139
|
+
|
140
|
+
hosts = [queue.pop, queue2.pop].map { |event| event.get("host") }.sort
|
141
|
+
expect(hosts).to eq(["snmp1", "snmp2"])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
describe "multiple plugin inputs and mix of udp tcp hosts", :integration => true do
|
146
|
+
config = <<-CONFIG
|
147
|
+
input {
|
148
|
+
snmp {
|
149
|
+
get => ["1.3.6.1.2.1.1.1.0"]
|
150
|
+
hosts => [{host => "udp:snmp1/161" community => "public"}]
|
151
|
+
}
|
152
|
+
snmp {
|
153
|
+
get => ["1.3.6.1.2.1.1.1.0"]
|
154
|
+
hosts => [{host => "tcp:snmp2/162" community => "public"}]
|
155
|
+
}
|
156
|
+
}
|
157
|
+
CONFIG
|
158
|
+
|
159
|
+
it_behaves_like "snmp plugin return one udp event and one tcp event", config
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "two plugins on different hosts with snmpv3 with same security name with different credentials and mix of udp and tcp", :integration => true do
|
163
|
+
config = <<-CONFIG
|
164
|
+
input {
|
165
|
+
snmp {
|
166
|
+
get => ["1.3.6.1.2.1.1.1.0"]
|
167
|
+
hosts => [{host => "udp:snmp1/161" version => "3"}]
|
168
|
+
security_name => "user_1"
|
169
|
+
auth_protocol => "sha"
|
170
|
+
auth_pass => "STrP@SSPhr@sE"
|
171
|
+
priv_protocol => "aes"
|
172
|
+
priv_pass => "STr0ngP@SSWRD161"
|
173
|
+
security_level => "authPriv"
|
174
|
+
}
|
175
|
+
snmp {
|
176
|
+
get => ["1.3.6.1.2.1.1.1.0"]
|
177
|
+
hosts => [{host => "tcp:snmp2/162" version => "3"}]
|
178
|
+
security_name => "user_1"
|
179
|
+
auth_protocol => "sha"
|
180
|
+
auth_pass => "STrP@SSPhr@sE"
|
181
|
+
priv_protocol => "aes"
|
182
|
+
priv_pass => "STr0ngP@SSWRD162"
|
183
|
+
security_level => "authPriv"
|
184
|
+
}
|
185
|
+
}
|
186
|
+
CONFIG
|
187
|
+
|
188
|
+
it_behaves_like "snmp plugin return one udp event and one tcp event", config
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "single host with tcp over ipv6", :integration => true do
|
192
|
+
let(:config) { super().merge({"hosts" => [{"host" => "tcp:[2001:3984:3989::161]/161"}]})}
|
193
|
+
it_behaves_like "snmp plugin return single event"
|
194
|
+
end
|
195
|
+
|
196
|
+
def stop_plugin_after_seconds(plugin)
|
197
|
+
Thread.new{
|
198
|
+
sleep(2)
|
199
|
+
plugin.do_stop
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
data/spec/inputs/snmp_spec.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require "logstash/devutils/rspec/spec_helper"
|
3
3
|
require "logstash/devutils/rspec/shared_examples"
|
4
|
+
require 'logstash/plugin_mixins/ecs_compatibility_support/spec_helper'
|
4
5
|
require "logstash/inputs/snmp"
|
5
6
|
|
6
|
-
describe LogStash::Inputs::Snmp do
|
7
|
+
describe LogStash::Inputs::Snmp, :ecs_compatibility_support do
|
8
|
+
|
7
9
|
let(:mock_client) { double("LogStash::SnmpClient") }
|
8
10
|
|
9
11
|
it_behaves_like "an interruptible input plugin" do
|
@@ -70,6 +72,10 @@ describe LogStash::Inputs::Snmp do
|
|
70
72
|
{"get" => ["1.0"], "hosts" => [{"host" => "udp:127.0.0.1/161", "version" => "1"}]},
|
71
73
|
{"get" => ["1.0"], "hosts" => [{"host" => "udp:127.0.0.1/161", "version" => "2c"}]},
|
72
74
|
{"get" => ["1.0"], "hosts" => [{"host" => "udp:127.0.0.1/161", "version" => "3"}], "security_name" => "v3user"},
|
75
|
+
|
76
|
+
{"get" => ["1.0"], "hosts" => [{"host" => "udp:[::1]/16100"}]},
|
77
|
+
{"get" => ["1.0"], "hosts" => [{"host" => "udp:[2001:db8:0:1:1:1:1:1]/16100"}]},
|
78
|
+
{"get" => ["1.0"], "hosts" => [{"host" => "udp:[2001:db8::2:1]/161"}]},
|
73
79
|
]
|
74
80
|
}
|
75
81
|
|
@@ -105,7 +111,7 @@ describe LogStash::Inputs::Snmp do
|
|
105
111
|
let(:valid_configs) {
|
106
112
|
[
|
107
113
|
{"get" => ["1.0"], "hosts" => [{"host" => "udp:127.0.0.1/161", "version" => "3"}], "security_name" => "ciscov3", "auth_protocol" => "sha", "auth_pass" => "myshapass", "priv_protocol" => "aes", "priv_pass" => "myprivpass", "security_level" => "authNoPriv"},
|
108
|
-
{"get" => ["1.0"], "hosts" => [{"host" => "udp:
|
114
|
+
{"get" => ["1.0"], "hosts" => [{"host" => "udp:[2001:db8:0:1:1:1:1:1]/1610", "version" => "3"}], "security_name" => "dellv3", "auth_protocol" => "md5", "auth_pass" => "myshapass", "priv_protocol" => "3des", "priv_pass" => "myprivpass", "security_level" => "authNoPriv"}
|
109
115
|
]
|
110
116
|
}
|
111
117
|
|
@@ -126,7 +132,12 @@ describe LogStash::Inputs::Snmp do
|
|
126
132
|
end
|
127
133
|
end
|
128
134
|
|
129
|
-
|
135
|
+
ecs_compatibility_matrix(:disabled, :v1, :v8) do |ecs_select|
|
136
|
+
|
137
|
+
before(:each) do
|
138
|
+
allow_any_instance_of(described_class).to receive(:ecs_compatibility).and_return(ecs_compatibility)
|
139
|
+
end
|
140
|
+
|
130
141
|
before do
|
131
142
|
expect(LogStash::SnmpClient).to receive(:new).and_return(mock_client)
|
132
143
|
expect(mock_client).to receive(:get).and_return({"foo" => "bar"})
|
@@ -139,25 +150,33 @@ describe LogStash::Inputs::Snmp do
|
|
139
150
|
input {
|
140
151
|
snmp {
|
141
152
|
get => ["1.3.6.1.2.1.1.1.0"]
|
142
|
-
hosts => [{host => "udp:127.0.0.1/161" community => "public"}]
|
153
|
+
hosts => [{ host => "udp:127.0.0.1/161" community => "public" }]
|
143
154
|
}
|
144
155
|
}
|
145
156
|
CONFIG
|
146
157
|
event = input(config) { |_, queue| queue.pop }
|
147
158
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
159
|
+
if ecs_select.active_mode == :disabled
|
160
|
+
expect(event.get("[@metadata][host_protocol]")).to eq("udp")
|
161
|
+
expect(event.get("[@metadata][host_address]")).to eq("127.0.0.1")
|
162
|
+
expect(event.get("[@metadata][host_port]")).to eq("161")
|
163
|
+
expect(event.get("[@metadata][host_community]")).to eq("public")
|
164
|
+
expect(event.get("host")).to eql("127.0.0.1")
|
165
|
+
else
|
166
|
+
expect(event.get("[@metadata][input][snmp][host][protocol]")).to eq("udp")
|
167
|
+
expect(event.get("[@metadata][input][snmp][host][address]")).to eq("127.0.0.1")
|
168
|
+
expect(event.get("[@metadata][input][snmp][host][port]")).to eq('161')
|
169
|
+
expect(event.get("[@metadata][input][snmp][host][community]")).to eq("public")
|
170
|
+
expect(event.get("host")).to eql('ip' => "127.0.0.1")
|
171
|
+
end
|
153
172
|
end
|
154
173
|
|
155
|
-
it "
|
174
|
+
it "should add custom host field (legacy metadata)" do
|
156
175
|
config = <<-CONFIG
|
157
176
|
input {
|
158
177
|
snmp {
|
159
178
|
get => ["1.3.6.1.2.1.1.1.0"]
|
160
|
-
hosts => [{host => "udp:127.0.0.1/161" community => "public"}]
|
179
|
+
hosts => [{ host => "udp:127.0.0.1/161" community => "public" }]
|
161
180
|
add_field => { host => "%{[@metadata][host_protocol]}:%{[@metadata][host_address]}/%{[@metadata][host_port]},%{[@metadata][host_community]}" }
|
162
181
|
}
|
163
182
|
}
|
@@ -165,6 +184,127 @@ describe LogStash::Inputs::Snmp do
|
|
165
184
|
event = input(config) { |_, queue| queue.pop }
|
166
185
|
|
167
186
|
expect(event.get("host")).to eq("udp:127.0.0.1/161,public")
|
187
|
+
end if ecs_select.active_mode == :disabled
|
188
|
+
|
189
|
+
it "should add custom host field (ECS mode)" do
|
190
|
+
config = <<-CONFIG
|
191
|
+
input {
|
192
|
+
snmp {
|
193
|
+
get => ["1.3.6.1.2.1.1.1.0"]
|
194
|
+
hosts => [{ host => "tcp:192.168.1.11/1161" }]
|
195
|
+
add_field => { "[host][formatted]" => "%{[@metadata][input][snmp][host][protocol]}://%{[@metadata][input][snmp][host][address]}:%{[@metadata][input][snmp][host][port]}" }
|
196
|
+
}
|
197
|
+
}
|
198
|
+
CONFIG
|
199
|
+
event = input(config) { |_, queue| queue.pop }
|
200
|
+
|
201
|
+
expect(event.get("host")).to eq('formatted' => "tcp://192.168.1.11:1161")
|
202
|
+
end if ecs_select.active_mode != :disabled
|
203
|
+
|
204
|
+
it "should target event data" do
|
205
|
+
config = <<-CONFIG
|
206
|
+
input {
|
207
|
+
snmp {
|
208
|
+
get => ["1.3.6.1.2.1.1.1.0"]
|
209
|
+
hosts => [{ host => "udp:127.0.0.1/161" community => "public" }]
|
210
|
+
target => "snmp_data"
|
211
|
+
}
|
212
|
+
}
|
213
|
+
CONFIG
|
214
|
+
event = input(config) { |_, queue| queue.pop }
|
215
|
+
|
216
|
+
expect( event.include?('foo') ).to be false
|
217
|
+
expect( event.get('[snmp_data]') ).to eql 'foo' => 'bar'
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context "StoppableIntervalRunner" do
|
222
|
+
let(:stop_holder) { Struct.new(:value).new(false) }
|
223
|
+
|
224
|
+
before(:each) do
|
225
|
+
allow(plugin).to receive(:stop?) { stop_holder.value }
|
226
|
+
end
|
227
|
+
|
228
|
+
let(:plugin) do
|
229
|
+
double("Plugin").tap do |dbl|
|
230
|
+
allow(dbl).to receive(:logger).and_return(double("Logger").as_null_object)
|
231
|
+
allow(dbl).to receive(:stop?) { stop_holder.value }
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
subject(:interval_runner) { LogStash::Inputs::Snmp::StoppableIntervalRunner.new(plugin) }
|
236
|
+
|
237
|
+
context "#every" do
|
238
|
+
context "when the plugin is stopped" do
|
239
|
+
let(:interval_seconds) { 2 }
|
240
|
+
it 'does not yield the block' do
|
241
|
+
stop_holder.value = true
|
242
|
+
expect { |yielder| interval_runner.every(interval_seconds, &yielder) }.to_not yield_control
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context "when the yield takes shorter than the interval" do
|
247
|
+
let(:duration_seconds) { 1 }
|
248
|
+
let(:interval_seconds) { 2 }
|
249
|
+
|
250
|
+
it 'sleeps off the remainder' do
|
251
|
+
allow(interval_runner).to receive(:sleep).and_call_original
|
252
|
+
|
253
|
+
interval_runner.every(interval_seconds) do
|
254
|
+
Kernel::sleep(duration_seconds) # non-stoppable
|
255
|
+
stop_holder.value = true # prevent re-runs
|
256
|
+
end
|
257
|
+
|
258
|
+
expect(interval_runner).to have_received(:sleep).with(a_value_within(0.1).of(1))
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context "when the yield takes longer than the interval" do
|
263
|
+
let(:duration_seconds) { 2 }
|
264
|
+
let(:interval_seconds) { 1 }
|
265
|
+
|
266
|
+
it 'logs a warning to the plugin' do
|
267
|
+
allow(interval_runner).to receive(:sleep).and_call_original
|
268
|
+
|
269
|
+
interval_runner.every(interval_seconds) do
|
270
|
+
Kernel::sleep(duration_seconds) # non-stoppable
|
271
|
+
stop_holder.value = true # prevent re-runs
|
272
|
+
end
|
273
|
+
|
274
|
+
expect(interval_runner).to_not have_received(:sleep)
|
275
|
+
|
276
|
+
expect(plugin.logger).to have_received(:warn).with(a_string_including("took longer"), a_hash_including(:interval_seconds => interval_seconds, :duration_seconds => a_value_within(0.1).of(duration_seconds)))
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'runs regularly until the plugin is stopped' do
|
281
|
+
timestamps = []
|
282
|
+
|
283
|
+
thread = Thread.new do
|
284
|
+
interval_runner.every(1) do
|
285
|
+
timestamps << Time.now
|
286
|
+
Kernel::sleep(Random::rand(0.8))
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
Kernel::sleep(5)
|
291
|
+
expect(thread).to be_alive
|
292
|
+
|
293
|
+
stop_holder.value = true
|
294
|
+
Kernel::sleep(1)
|
295
|
+
|
296
|
+
aggregate_failures do
|
297
|
+
expect(thread).to_not be_alive
|
298
|
+
expect(timestamps.count).to be_within(1).of(5)
|
299
|
+
|
300
|
+
timestamps.each_cons(2) do |previous, current|
|
301
|
+
# ensure each start time is very close to 1s after the previous.
|
302
|
+
expect(current - previous).to be_within(0.05).of(1)
|
303
|
+
end
|
304
|
+
|
305
|
+
thread.kill if thread.alive?
|
306
|
+
end
|
307
|
+
end
|
168
308
|
end
|
169
309
|
end
|
170
310
|
end
|
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.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Elasticsearch
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -33,23 +33,45 @@ dependencies:
|
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
requirement: !ruby/object:Gem::Requirement
|
35
35
|
requirements:
|
36
|
-
- - "
|
36
|
+
- - "~>"
|
37
37
|
- !ruby/object:Gem::Version
|
38
|
-
version:
|
39
|
-
|
38
|
+
version: '1.3'
|
39
|
+
name: logstash-mixin-ecs_compatibility_support
|
40
|
+
prerelease: false
|
41
|
+
type: :runtime
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
40
45
|
- !ruby/object:Gem::Version
|
41
|
-
version:
|
42
|
-
|
46
|
+
version: '1.3'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - "~>"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '1.0'
|
53
|
+
name: logstash-mixin-event_support
|
43
54
|
prerelease: false
|
44
55
|
type: :runtime
|
45
56
|
version_requirements: !ruby/object:Gem::Requirement
|
46
57
|
requirements:
|
47
|
-
- - "
|
58
|
+
- - "~>"
|
48
59
|
- !ruby/object:Gem::Version
|
49
|
-
version:
|
50
|
-
|
60
|
+
version: '1.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - "~>"
|
51
65
|
- !ruby/object:Gem::Version
|
52
|
-
version:
|
66
|
+
version: '1.0'
|
67
|
+
name: logstash-mixin-validator_support
|
68
|
+
prerelease: false
|
69
|
+
type: :runtime
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.0'
|
53
75
|
- !ruby/object:Gem::Dependency
|
54
76
|
requirement: !ruby/object:Gem::Requirement
|
55
77
|
requirements:
|
@@ -64,6 +86,26 @@ dependencies:
|
|
64
86
|
- - ">="
|
65
87
|
- !ruby/object:Gem::Version
|
66
88
|
version: '0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 0.0.22
|
95
|
+
- - "<"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 0.1.0
|
98
|
+
name: stud
|
99
|
+
prerelease: false
|
100
|
+
type: :runtime
|
101
|
+
version_requirements: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: 0.0.22
|
106
|
+
- - "<"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: 0.1.0
|
67
109
|
- !ruby/object:Gem::Dependency
|
68
110
|
requirement: !ruby/object:Gem::Requirement
|
69
111
|
requirements:
|
@@ -414,6 +456,7 @@ files:
|
|
414
456
|
- logstash-input-snmp.gemspec
|
415
457
|
- spec/fixtures/RFC1213-MIB.dic
|
416
458
|
- spec/fixtures/collision.dic
|
459
|
+
- spec/inputs/integration/it_spec.rb
|
417
460
|
- spec/inputs/snmp/base_client_spec.rb
|
418
461
|
- spec/inputs/snmp/mib_spec.rb
|
419
462
|
- spec/inputs/snmp_spec.rb
|
@@ -440,14 +483,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
440
483
|
- !ruby/object:Gem::Version
|
441
484
|
version: '0'
|
442
485
|
requirements: []
|
443
|
-
|
444
|
-
rubygems_version: 2.6.13
|
486
|
+
rubygems_version: 3.1.6
|
445
487
|
signing_key:
|
446
488
|
specification_version: 4
|
447
489
|
summary: SNMP input plugin
|
448
490
|
test_files:
|
449
491
|
- spec/fixtures/RFC1213-MIB.dic
|
450
492
|
- spec/fixtures/collision.dic
|
493
|
+
- spec/inputs/integration/it_spec.rb
|
451
494
|
- spec/inputs/snmp/base_client_spec.rb
|
452
495
|
- spec/inputs/snmp/mib_spec.rb
|
453
496
|
- spec/inputs/snmp_spec.rb
|