logstash-input-snmp 1.2.7 → 1.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 755490661919181ec1b348c1b2bade1300a1829be66206b52c203a230c7a00fd
4
- data.tar.gz: b05f137f91e3720ec567f620358c230b8ffe88f96c83d5158a9d05975b3e8ea6
3
+ metadata.gz: 4bcb5f73151249317b6f7b36c028a3f863e7d4a88ba6da977fbc88d255355364
4
+ data.tar.gz: 85ef765af5f93aefb92a8f40d9738101ada3b5173bb9b4b8de3d4ec21a406578
5
5
  SHA512:
6
- metadata.gz: a3af3dba5c77fa3b6bec973a23eb8e3e0f8260287a6aa974d4cde6dd323193086d9767b2725600fc6e9776445e65bcb09e4e85883ffde331fe9e403eeba68edd
7
- data.tar.gz: dfe8509aa8619a38616aeb23f534097f19ce06dd14f2c7d1bcb6874fcbdb14bf01fef88148b4cd06b20ed1fd766a03e81735cc96694ca6e28106ad4be649e0ab
6
+ metadata.gz: 423f10e23a58b04a72c47d70beb06053dedc8d07e1ec1e36c835ca783c6791bb6ccd12d701d3e50bd4ebe173185a4240ae21a93c36d7ecdf38bf4e2ae6d58f25
7
+ data.tar.gz: f452759e6c453d088b002721ae1520b1c22291e16ff609d8f308e57c3e9aa9dc29b008c809a9a6a76e76b00e68c0399fc96b63f23dc066aa403a682f19712f2e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## 1.2.8
2
+ - 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 [#61](https://github.com/logstash-plugins/logstash-input-snmp/issues/61)
3
+
1
4
  ## 1.2.7
2
5
  - Added integration tests to ensure SNMP server and IPv6 connections [#87](https://github.com/logstash-plugins/logstash-input-snmp/pull/87)
3
6
 
data/docs/index.asciidoc CHANGED
@@ -163,6 +163,7 @@ input {
163
163
  ===== `interval`
164
164
 
165
165
  The `interval` option specifies the polling interval in seconds.
166
+ If polling all configured hosts takes longer than this interval, a warning will be emitted to the logs.
166
167
 
167
168
  * Value type is <<number,number>>
168
169
  * Default value is `30`
@@ -161,9 +161,9 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
161
161
  end
162
162
 
163
163
  def run(queue)
164
- # for now a naive single threaded poller which sleeps for the given interval between
164
+ # for now a naive single threaded poller which sleeps off the remaining interval between
165
165
  # each run. each run polls all the defined hosts for the get and walk options.
166
- while !stop?
166
+ stoppable_interval_runner.every(@interval, "polling hosts") do
167
167
  @client_definitions.each do |definition|
168
168
  result = {}
169
169
  if !definition[:get].empty?
@@ -207,11 +207,13 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
207
207
  queue << event
208
208
  end
209
209
  end
210
-
211
- Stud.stoppable_sleep(@interval) { stop? }
212
210
  end
213
211
  end
214
212
 
213
+ def stoppable_interval_runner
214
+ StoppableIntervalRunner.new(self)
215
+ end
216
+
215
217
  def close
216
218
  @client_definitions.each do |definition|
217
219
  begin
@@ -222,6 +224,9 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
222
224
  end
223
225
  end
224
226
 
227
+ def stop
228
+ end
229
+
225
230
  private
226
231
 
227
232
  OID_REGEX = /^\.?([0-9\.]+)$/
@@ -295,4 +300,45 @@ class LogStash::Inputs::Snmp < LogStash::Inputs::Base
295
300
  def validate_strip!
296
301
  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
302
  end
303
+
304
+ ##
305
+ # The StoppableIntervalRunner is capable of running a block of code at a
306
+ # repeating interval, while respecting the stop condition of the plugin.
307
+ class StoppableIntervalRunner
308
+ ##
309
+ # @param plugin [#logger,#stop?]
310
+ def initialize(plugin)
311
+ @plugin = plugin
312
+ end
313
+
314
+ ##
315
+ # Runs the provided block repeatedly using the provided interval.
316
+ # After executing the block, the remainder of the interval if any is slept off
317
+ # using an interruptible sleep.
318
+ # If no time remains, a warning is emitted to the logs.
319
+ #
320
+ # @param interval_seconds [Integer,Float]
321
+ # @param desc [String] (default: "operation"): a description to use when logging
322
+ # @yield
323
+ def every(interval_seconds, desc="operation", &block)
324
+ until @plugin.stop?
325
+ start_time = Time.now
326
+
327
+ yield
328
+
329
+ duration_seconds = Time.now - start_time
330
+ if duration_seconds >= interval_seconds
331
+ @plugin.logger.warn("#{desc} took longer than the configured interval", :interval_seconds => interval_seconds, :duration_seconds => duration_seconds.round(3))
332
+ else
333
+ remaining_interval = interval_seconds - duration_seconds
334
+ sleep(remaining_interval)
335
+ end
336
+ end
337
+ end
338
+
339
+ # @api private
340
+ def sleep(duration)
341
+ Stud.stoppable_sleep(duration) { @plugin.stop? }
342
+ end
343
+ end
298
344
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-input-snmp'
3
- s.version = '1.2.7'
3
+ s.version = '1.2.8'
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"
@@ -44,12 +44,12 @@ describe LogStash::Inputs::Snmp do
44
44
  end
45
45
 
46
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"}]})}
47
+ let(:config) { super().merge({"hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}]})}
48
48
  it_behaves_like "snmp plugin return single event"
49
49
  end
50
50
 
51
51
  describe "against single server with snmpv3 and tcp", :integration => true do
52
- let(:config) { super.merge({
52
+ let(:config) { super().merge({
53
53
  "hosts" => [{"host" => "tcp:snmp1/161", "version" => "3"}],
54
54
  "security_name" => "user_1",
55
55
  "auth_protocol" => "sha",
@@ -63,7 +63,7 @@ describe LogStash::Inputs::Snmp do
63
63
  end
64
64
 
65
65
  describe "invalid user against snmpv3 server", :integration => true do
66
- let(:config) { super.merge({
66
+ let(:config) { super().merge({
67
67
  "hosts" => [{"host" => "tcp:snmp1/161", "version" => "3"}],
68
68
  "security_name" => "user_2",
69
69
  "auth_protocol" => "sha",
@@ -84,7 +84,7 @@ describe LogStash::Inputs::Snmp do
84
84
  end
85
85
 
86
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"}]})}
87
+ let(:config) { super().merge({"hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}, {"host" => "tcp:snmp1/161", "community" => "public"}]})}
88
88
  it "should return two events " do
89
89
  plugin.register
90
90
  queue = []
@@ -99,7 +99,7 @@ describe LogStash::Inputs::Snmp do
99
99
  end
100
100
 
101
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"}]})}
102
+ let(:config) { super().merge({"hosts" => [{"host" => "udp:snmp1/161", "community" => "public"}, {"host" => "udp:snmp2/162", "community" => "public"}]})}
103
103
  it "should return two events, one per host" do
104
104
  plugin.register
105
105
  queue = []
@@ -189,7 +189,7 @@ describe LogStash::Inputs::Snmp do
189
189
  end
190
190
 
191
191
  describe "single host with tcp over ipv6", :integration => true do
192
- let(:config) { super.merge({"hosts" => [{"host" => "tcp:[2001:3984:3989::161]/161"}]})}
192
+ let(:config) { super().merge({"hosts" => [{"host" => "tcp:[2001:3984:3989::161]/161"}]})}
193
193
  it_behaves_like "snmp plugin return single event"
194
194
  end
195
195
 
File without changes
@@ -171,5 +171,95 @@ describe LogStash::Inputs::Snmp do
171
171
  expect(event.get("host")).to eq("udp:127.0.0.1/161,public")
172
172
  end
173
173
  end
174
+
175
+ context "StoppableIntervalRunner" do
176
+ let(:stop_holder) { Struct.new(:value).new(false) }
177
+
178
+ before(:each) do
179
+ allow(plugin).to receive(:stop?) { stop_holder.value }
180
+ end
181
+
182
+ let(:plugin) do
183
+ double("Plugin").tap do |dbl|
184
+ allow(dbl).to receive(:logger).and_return(double("Logger").as_null_object)
185
+ allow(dbl).to receive(:stop?) { stop_holder.value }
186
+ end
187
+ end
188
+
189
+ subject(:interval_runner) { LogStash::Inputs::Snmp::StoppableIntervalRunner.new(plugin) }
190
+
191
+ context "#every" do
192
+ context "when the plugin is stopped" do
193
+ let(:interval_seconds) { 2 }
194
+ it 'does not yield the block' do
195
+ stop_holder.value = true
196
+ expect { |yielder| interval_runner.every(interval_seconds, &yielder) }.to_not yield_control
197
+ end
198
+ end
199
+
200
+ context "when the yield takes shorter than the interval" do
201
+ let(:duration_seconds) { 1 }
202
+ let(:interval_seconds) { 2 }
203
+
204
+ it 'sleeps off the remainder' do
205
+ allow(interval_runner).to receive(:sleep).and_call_original
206
+
207
+ interval_runner.every(interval_seconds) do
208
+ Kernel::sleep(duration_seconds) # non-stoppable
209
+ stop_holder.value = true # prevent re-runs
210
+ end
211
+
212
+ expect(interval_runner).to have_received(:sleep).with(a_value_within(0.1).of(1))
213
+ end
214
+ end
215
+
216
+ context "when the yield takes longer than the interval" do
217
+ let(:duration_seconds) { 2 }
218
+ let(:interval_seconds) { 1 }
219
+
220
+ it 'logs a warning to the plugin' do
221
+ allow(interval_runner).to receive(:sleep).and_call_original
222
+
223
+ interval_runner.every(interval_seconds) do
224
+ Kernel::sleep(duration_seconds) # non-stoppable
225
+ stop_holder.value = true # prevent re-runs
226
+ end
227
+
228
+ expect(interval_runner).to_not have_received(:sleep)
229
+
230
+ 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)))
231
+ end
232
+ end
233
+
234
+ it 'runs regularly until the plugin is stopped' do
235
+ timestamps = []
236
+
237
+ thread = Thread.new do
238
+ interval_runner.every(1) do
239
+ timestamps << Time.now
240
+ Kernel::sleep(Random::rand(0.8))
241
+ end
242
+ end
243
+
244
+ Kernel::sleep(5)
245
+ expect(thread).to be_alive
246
+
247
+ stop_holder.value = true
248
+ Kernel::sleep(1)
249
+
250
+ aggregate_failures do
251
+ expect(thread).to_not be_alive
252
+ expect(timestamps.count).to be_within(1).of(5)
253
+
254
+ timestamps.each_cons(2) do |previous, current|
255
+ # ensure each start time is very close to 1s after the previous.
256
+ expect(current - previous).to be_within(0.05).of(1)
257
+ end
258
+
259
+ thread.kill if thread.alive?
260
+ end
261
+ end
262
+ end
263
+ end
174
264
  end
175
265
 
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.7
4
+ version: 1.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elasticsearch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-05 00:00:00.000000000 Z
11
+ date: 2021-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -416,8 +416,10 @@ files:
416
416
  - spec/fixtures/collision.dic
417
417
  - spec/inputs/integration/it_spec.rb
418
418
  - spec/inputs/snmp/base_client_spec.rb
419
+ - spec/inputs/snmp/interval_runner_spec.rb
419
420
  - spec/inputs/snmp/mib_spec.rb
420
421
  - spec/inputs/snmp_spec.rb
422
+ - vendor/jar-dependencies/org/snmp4j/snmp4j/2.5.11/snmp4j-2.5.11.jar
421
423
  - vendor/jar-dependencies/org/snmp4j/snmp4j/2.8.4/snmp4j-2.8.4.jar
422
424
  homepage: http://www.elastic.co/guide/en/logstash/current/index.html
423
425
  licenses:
@@ -441,8 +443,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
441
443
  - !ruby/object:Gem::Version
442
444
  version: '0'
443
445
  requirements: []
444
- rubyforge_project:
445
- rubygems_version: 2.6.13
446
+ rubygems_version: 3.1.6
446
447
  signing_key:
447
448
  specification_version: 4
448
449
  summary: SNMP input plugin
@@ -451,5 +452,6 @@ test_files:
451
452
  - spec/fixtures/collision.dic
452
453
  - spec/inputs/integration/it_spec.rb
453
454
  - spec/inputs/snmp/base_client_spec.rb
455
+ - spec/inputs/snmp/interval_runner_spec.rb
454
456
  - spec/inputs/snmp/mib_spec.rb
455
457
  - spec/inputs/snmp_spec.rb