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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 94fd9e076cacf58d8e7bc4a7a363d1ac7a04a41c389d33d8c48d47fd3314253f
4
- data.tar.gz: ec0a6543886329aa4ec9e774ee5bb7c2e39cf9e9ac76f830ff36e1f7b570f1a5
3
+ metadata.gz: 747dbf68d6a90a7ab937bf8c64edef786b7d0c4c0ac2f97088401d1e842c2616
4
+ data.tar.gz: e53514a31810159796b3e80ee56f7aa6d66eba23ac781744fd9fbf0be83d4d9a
5
5
  SHA512:
6
- metadata.gz: c717a3a347da825b2fb1ce88edaffbdf72ffa970d02db5cabeee78799d49ce8dacdefe2c8758d417a7ac10645483c73a38b19630ead1e04b93c5831c5098d6b7
7
- data.tar.gz: c28c57f4e5f2ef7093afad644e4dbccfb0a12681123dd3405ad52f9b55e4687bec85c5646b3135351ce9afe2e46d8839b2e3e5d09cb48e0ba0c2adaa7fdb7b93
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
- - Updated snmp4j library to v2.8.4 [#86](https://github.com/logstash-plugins/logstash-input-snmp/pull/86)
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
- - Fixed: support SNMPv3 multiple identical security name with different credentials [#84](https://github.com/logstash-plugins/logstash-input-snmp/pull/84)
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
- - Fixed: multithreading problem when using multiple snmp inputs with multiple v3 credentials [#80](https://github.com/logstash-plugins/logstash-input-snmp/pull/80)
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
- - Refactor: scope and review java_imports [#72](https://github.com/logstash-plugins/logstash-input-snmp/pull/72)
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
- The `interval` option specifies the polling interval in seconds.
183
+ * Value type is <<number,number>>
184
+ * Default value is `30`
154
185
 
155
- * Value type is <<number,number>>
156
- * Default value is `30`
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
- The `mib_paths` option specifies the location of one or more imported MIB files. The value can be either a dir path containing
162
- the imported MIB `.dic` files or a file path to a single MIB `.dic` file.
192
+ * Value type is <<path,path>>
193
+ * There is no default value for this setting
163
194
 
164
- * Value type is <<path,path>>
165
- * There is no default value for this setting
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
- The `auth_pass` option specifies the SNMPv3 authentication passphrase or password
289
+ * Value type is <<password,password>>
290
+ * There is no default value for this setting
258
291
 
259
- * Value type is <<password,password>>
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
- The `priv_pass` option specifies the SNMPv3 encryption password
318
+ * Value type is <<password,password>>
319
+ * There is no default value for this setting
274
320
 
275
- * Value type is <<password,password>>
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
- The `priv_protocol` option specifies the SNMPv3 privacy/encryption protocol.
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
- * Value can be any of: `des`, `3des`, `aes`, `aes128`, `aes192`, `aes256`
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
- The `security_name` option specifies the SNMPv3 security name or user name
335
+ * Value type is <<string,string>>
336
+ * There is no default value for this setting
291
337
 
292
- * Value type is <<string,string>>
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
- The `security_level` option specifies the SNMPv3 security level between Authentication, No Privacy; Authentication, Privacy; or no Authentication, no Privacy
343
+ * Value can be any of: `noAuthNoPriv`, `authNoPriv`, `authPriv`
344
+ * There is no default value for this setting
299
345
 
300
- * Value can be any of: `noAuthNoPriv`, `authNoPriv`, `authPriv`
301
- * There is no default value for this setting
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 verison '1' or '2c'") unless ["1", "2c"].include?(version.to_s)
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
@@ -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 for the given interval between
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
- while !stop?
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(definition[:client].get(definition[:get], @oid_root_skip, @oid_path_length))
203
+ result = result.merge(client.get(oids, @oid_root_skip, @oid_path_length))
172
204
  rescue => e
173
- logger.error("error invoking get operation on #{definition[:host_address]} for OIDs: #{definition[:get]}, ignoring", :exception => e, :backtrace => e.backtrace)
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 !definition[:walk].empty?
209
+ if !definition[:walk].empty?
177
210
  definition[:walk].each do |oid|
178
211
  begin
179
- result = result.merge(definition[:client].walk(oid, @oid_root_skip, @oid_path_length))
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", :exception => e, :backtrace => e.backtrace)
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 !Array(@tables).empty?
220
+ if !Array(@tables).empty?
187
221
  @tables.each do |table_entry|
188
222
  begin
189
- result = result.merge(definition[:client].table(table_entry, @oid_root_skip, @oid_path_length))
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", :exception => e, :backtrace => e.backtrace)
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
- metadata = {
198
- "host_protocol" => definition[:host_protocol],
199
- "host_address" => definition[:host_address],
200
- "host_port" => definition[:host_port],
201
- "host_community" => definition[:host_community],
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>udp|tcp):(?<host_address>.+)\/(?<host_port>\d+)$/i
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
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-input-snmp'
3
- s.version = '1.2.5'
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 'stud', '>= 0.0.22', '< 0.1.0'
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
@@ -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:127.0.0.1/161", "version" => "3"}], "security_name" => "dellv3", "auth_protocol" => "md5", "auth_pass" => "myshapass", "priv_protocol" => "3des", "priv_pass" => "myprivpass", "security_level" => "authNoPriv"}
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
- context "@metadata" do
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
- expect(event.get("[@metadata][host_protocol]")).to eq("udp")
149
- expect(event.get("[@metadata][host_address]")).to eq("127.0.0.1")
150
- expect(event.get("[@metadata][host_port]")).to eq("161")
151
- expect(event.get("[@metadata][host_community]")).to eq("public")
152
- expect(event.get("host")).to eq("127.0.0.1")
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 "shoud add custom host field" do
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.2.5
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: 2020-08-26 00:00:00.000000000 Z
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: 0.0.22
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: 0.1.0
42
- name: stud
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: 0.0.22
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: 0.1.0
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
- rubyforge_project:
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